Преглед изворни кода

Terrain patch stitching.
Limited terrain LOD levels to 4.
Do not count clipped sub-triangles toward the triangle limit in OcclusionBuffer.cpp.

Lasse Öörni пре 13 година
родитељ
комит
5df4edfe03

+ 10 - 2
Engine/Graphics/OcclusionBuffer.cpp

@@ -496,6 +496,7 @@ void OcclusionBuffer::DrawTriangle(Vector4* vertices)
 {
     unsigned clipMask = 0;
     unsigned andClipMask = 0;
+    bool drawOk = false;
     Vector3 projected[3];
     
     // Build the clip plane mask for the triangle
@@ -536,7 +537,10 @@ void OcclusionBuffer::DrawTriangle(Vector4* vertices)
         projected[2] = ViewportTransform(vertices[2]);
         
         if (CheckFacing(projected[0], projected[1], projected[2]))
+        {
             DrawTriangle2D(projected);
+            drawOk = true;
+        }
     }
     else
     {
@@ -570,10 +574,16 @@ void OcclusionBuffer::DrawTriangle(Vector4* vertices)
                 projected[2] = ViewportTransform(vertices[index + 2]);
                 
                 if (CheckFacing(projected[0], projected[1], projected[2]))
+                {
                     DrawTriangle2D(projected);
+                    drawOk = true;
+                }
             }
         }
     }
+    
+    if (drawOk)
+        ++numTriangles_;
 }
 
 void OcclusionBuffer::ClipVertices(const Vector4& plane, Vector4* vertices, bool* triangles, unsigned& numTriangles)
@@ -710,8 +720,6 @@ void OcclusionBuffer::DrawTriangle2D(const Vector3* vertices)
     int top, middle, bottom;
     bool middleIsRight;
     
-    numTriangles_++;
-    
     // Sort vertices in Y-direction
     if (vertices[0].y_ < vertices[1].y_)
     {

+ 161 - 63
Engine/Graphics/Terrain.cpp

@@ -45,10 +45,14 @@
 OBJECTTYPESTATIC(Terrain);
 
 static const Vector3 DEFAULT_SPACING(1.0f, 0.25f, 1.0f);
-static const unsigned MAX_LOD_LEVELS = 8;
+static const unsigned MAX_LOD_LEVELS = 4;
 static const int DEFAULT_PATCH_SIZE = 64;
 static const int MIN_PATCH_SIZE = 4;
 static const int MAX_PATCH_SIZE = 128;
+static const unsigned STITCH_NORTH = 1;
+static const unsigned STITCH_SOUTH = 2;
+static const unsigned STITCH_WEST = 4;
+static const unsigned STITCH_EAST = 8;
 
 Terrain::Terrain(Context* context) :
     Component(context),
@@ -369,15 +373,15 @@ float Terrain::GetHeight(const Vector3& worldPosition) const
 
 void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 {
-    unsigned vertexDataRow = patchSize_ + 1;
+    unsigned row = patchSize_ + 1;
     VertexBuffer* vertexBuffer = patch->GetVertexBuffer();
     Geometry* geometry = patch->GetGeometry();
     Geometry* maxLodGeometry = patch->GetMaxLodGeometry();
     
-    if (vertexBuffer->GetVertexCount() != vertexDataRow * vertexDataRow)
-        vertexBuffer->SetSize(vertexDataRow * vertexDataRow, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
+    if (vertexBuffer->GetVertexCount() != row * row)
+        vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     
-    SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[vertexDataRow * vertexDataRow * sizeof(Vector3)]);
+    SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
     
     float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount());
     float* positionData = (float*)cpuVertexData.Get();
@@ -445,11 +449,25 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 
 void Terrain::UpdatePatchLod(TerrainPatch* patch)
 {
-    /// \todo Use stitching
-    unsigned lodLevel = patch->GetLodLevel();
     Geometry* geometry = patch->GetGeometry();
-    if (lodLevel < drawRanges_.Size())
-        geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[lodLevel].first_, drawRanges_[lodLevel].second_);
+    
+    // All LOD levels except the coarsest have 16 versions for stitching
+    unsigned lodLevel = patch->GetLodLevel();
+    unsigned drawRangeIndex = lodLevel << 4;
+    if (lodLevel < numLodLevels_ - 1)
+    {
+        if (patch->GetNorthPatch() && patch->GetNorthPatch()->GetLodLevel() > lodLevel)
+            drawRangeIndex |= STITCH_NORTH;
+        if (patch->GetSouthPatch() && patch->GetSouthPatch()->GetLodLevel() > lodLevel)
+            drawRangeIndex |= STITCH_SOUTH;
+        if (patch->GetWestPatch() && patch->GetWestPatch()->GetLodLevel() > lodLevel)
+            drawRangeIndex |= STITCH_WEST;
+        if (patch->GetEastPatch() && patch->GetEastPatch()->GetLodLevel() > lodLevel)
+            drawRangeIndex |= STITCH_EAST;
+    }
+    
+    if (drawRangeIndex < drawRanges_.Size())
+        geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[drawRangeIndex].first_, drawRanges_[drawRangeIndex].second_);
 }
 
 void Terrain::SetMaterialAttr(ResourceRef value)
@@ -621,46 +639,152 @@ void Terrain::CreateGeometry()
 
 void Terrain::CreateIndexData()
 {
-    /// \todo Create stitching combinations
-    unsigned totalIndexSize = 0;
-    unsigned vertexDataRow = patchSize_ + 1;
-    
-    for (unsigned i = 0; i < numLodLevels_; ++i)
-    {
-        unsigned lodPatchSize = patchSize_ >> i;
-        totalIndexSize += lodPatchSize * lodPatchSize * 6;
-    }
-    
+    PODVector<unsigned short> indices;
     drawRanges_.Clear();
-    indexBuffer_->SetSize(totalIndexSize, false);
-    unsigned short* indexData = (unsigned short*)indexBuffer_->Lock(0, indexBuffer_->GetIndexCount());
+    unsigned row = patchSize_ + 1;
     
-    if (indexData)
+    for (unsigned i = 0; i < numLodLevels_; ++i)
     {
-        unsigned index = 0;
+        unsigned combinations = (i < numLodLevels_ - 1) ? 16 : 1;
+        int skip = 1 << i;
         
-        for (unsigned i = 0; i < numLodLevels_; ++i)
+        for (unsigned j = 0; j < combinations; ++j)
         {
-            unsigned indexStart = index;
-            int skip = 1 << i;
+            unsigned indexStart = indices.Size();
+            
+            int zStart = 0;
+            int xStart = 0;
+            int zEnd = patchSize_;
+            int xEnd = patchSize_;
+            
+            if (j & STITCH_NORTH)
+                zEnd -= skip;
+            if (j & STITCH_SOUTH)
+                zStart += skip;
+            if (j & STITCH_WEST)
+                xStart += skip;
+            if (j & STITCH_EAST)
+                xEnd -= skip;
+            
+            // Build the main grid
+            for (int z = zStart; z < zEnd; z += skip)
+            {
+                for (int x = xStart; x < xEnd; x += skip)
+                {
+                    indices.Push((z + skip) * row + x);
+                    indices.Push(z * row + x + skip);
+                    indices.Push(z * row + x);
+                    indices.Push((z + skip) * row + x);
+                    indices.Push((z + skip) * row + x + skip);
+                    indices.Push(z * row + x + skip);
+                }
+            }
             
-            for (int z = 0; z < patchSize_; z += skip)
+            // Build the north edge
+            if (j & STITCH_NORTH)
             {
-                for (int x = 0; x < patchSize_; x += skip)
+                int z = patchSize_ - skip;
+                for (int x = 0; x < patchSize_; x += skip * 2)
                 {
-                    *indexData++ = x + (z + skip) * vertexDataRow;
-                    *indexData++ = x + z * vertexDataRow + skip;
-                    *indexData++ = x + z * vertexDataRow;
-                    *indexData++ = x + (z + skip) * vertexDataRow;
-                    *indexData++ = x + (z + skip) * vertexDataRow + skip;
-                    *indexData++ = x + z * vertexDataRow + skip;
-                    index += 6;
+                    if (x > 0 || (j & STITCH_WEST) == 0)
+                    {
+                        indices.Push((z + skip) * row + x);
+                        indices.Push(z * row + x + skip);
+                        indices.Push(z * row + x);
+                    }
+                    indices.Push((z + skip) * row + x);
+                    indices.Push((z + skip) * row + x + 2 * skip);
+                    indices.Push(z * row + x + skip);
+                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
+                    {
+                        indices.Push((z + skip) * row + x + 2 * skip);
+                        indices.Push(z * row + x + 2 * skip);
+                        indices.Push(z * row + x + skip);
+                    }
                 }
             }
             
-            drawRanges_.Push(MakePair(indexStart, index - indexStart));
+            // Build the south edge
+            if (j & STITCH_SOUTH)
+            {
+                int z = 0;
+                for (int x = 0; x < patchSize_; x += skip * 2)
+                {
+                    if (x > 0 || (j & STITCH_WEST) == 0)
+                    {
+                        indices.Push((z + skip) * row + x);
+                        indices.Push((z + skip) * row + x + skip);
+                        indices.Push(z * row + x);
+                    }
+                    indices.Push(z * row + x);
+                    indices.Push((z + skip) * row + x + skip);
+                    indices.Push(z * row + x + 2 * skip);
+                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
+                    {
+                        indices.Push((z + skip) * row + x + skip);
+                        indices.Push((z + skip) * row + x + 2 * skip);
+                        indices.Push(z * row + x + 2 * skip);
+                    }
+                }
+            }
+            
+            // Build the west edge
+            if (j & STITCH_WEST)
+            {
+                int x = 0;
+                for (int z = 0; z < patchSize_; z += skip * 2)
+                {
+                    if (z > 0 || (j & STITCH_SOUTH) == 0)
+                    {
+                        indices.Push(z * row + x);
+                        indices.Push((z + skip) * row + x + skip);
+                        indices.Push(z * row + x + skip);
+                    }
+                    indices.Push((z + 2 * skip) * row + x);
+                    indices.Push((z + skip) * row + x + skip);
+                    indices.Push(z * row + x);
+                    if (x < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
+                    {
+                        indices.Push((z + 2 * skip) * row + x);
+                        indices.Push((z + 2 * skip) * row + x + skip);
+                        indices.Push((z + skip) * row + x + skip);
+                    }
+                }
+            }
+            
+            // Build the east edge
+            if (j & STITCH_EAST)
+            {
+                int x = patchSize_ - skip;
+                for (int z = 0; z < patchSize_; z += skip * 2)
+                {
+                    if (z > 0 || (j & STITCH_SOUTH) == 0)
+                    {
+                        indices.Push(z * row + x);
+                        indices.Push((z + skip) * row + x);
+                        indices.Push(z * row + x + skip);
+                    }
+                    indices.Push((z + skip) * row + x);
+                    indices.Push((z + 2 * skip) * row + x + skip);
+                    indices.Push(z * row + x + skip);
+                    if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
+                    {
+                        indices.Push((z + skip) * row + x);
+                        indices.Push((z + 2 * skip) * row + x);
+                        indices.Push((z + 2 * skip) * row + x + skip);
+                    }
+                }
+            }
+            
+            drawRanges_.Push(MakePair(indexStart, indices.Size() - indexStart));
         }
-        
+    }
+    
+    indexBuffer_->SetSize(indices.Size(), false);
+    unsigned short* indexData = (unsigned short*)indexBuffer_->Lock(0, indices.Size());
+    if (indexData)
+    {
+        memcpy(indexData, &indices[0], indices.Size() * sizeof(unsigned short));
         indexBuffer_->Unlock();
     }
 }
@@ -771,32 +895,6 @@ void Terrain::SetNeighbors(TerrainPatch* patch)
         GetPatch(coords.x_ - 1, coords.y_), GetPatch(coords.x_ + 1, coords.y_));
 }
 
-unsigned Terrain::GetDrawRangeIndex(unsigned lod, unsigned northLod, unsigned southLod, unsigned westLod, unsigned eastLod)
-{
-    /*
-    // If neighbor patches have more accurate LOD, no need to perform stitching
-    northLod = Max((int)lod, (int)northLod);
-    southLod = Max((int)lod, (int)southLod);
-    westLod = Max((int)lod, (int)westLod);
-    eastLod = Max((int)lod, (int)eastLod);
-    
-    unsigned index = 0;
-    
-    // Skip higher LOD levels
-    for (unsigned i = 0; i < lod; ++i)
-        index += 16;
-    
-    // Each LOD level can stitch to max. 1 level coarser LOD to reduce amount of draw range combinations
-    index += northLod > lod ? 1 : 0;
-    index += southLod > lod ? 2 : 0;
-    index += westLod > lod ? 4 : 0;
-    index += eastLod > lod ? 8 : 0;
-    
-    return index;
-    */
-    return lod;
-}
-
 bool Terrain::SetHeightMapInternal(Image* image, bool recreateNow)
 {
     if (image && image->IsCompressed())

+ 0 - 2
Engine/Graphics/Terrain.h

@@ -156,8 +156,6 @@ private:
     void CalculateLodErrors(TerrainPatch* patch);
     /// Set neighbors for a patch.
     void SetNeighbors(TerrainPatch* patch);
-    /// Get draw range index for a given LOD and neighbor LODs.
-    unsigned GetDrawRangeIndex(unsigned lod, unsigned northLod, unsigned southLod, unsigned westLod, unsigned eastLod);
     /// Set heightmap image and optionally recreate the geometry immediately. Return true if successful.
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     /// Handle heightmap image reload finished.

+ 1 - 1
Engine/Graphics/TerrainPatch.cpp

@@ -178,7 +178,7 @@ void TerrainPatch::UpdateGeometry(const FrameInfo& frame)
 
 UpdateGeometryType TerrainPatch::GetUpdateGeometryType()
 {
-    // If any of the neighbour patches have changed LOD, must update stitching
+    // If any of the neighbor patches have changed LOD, must also update own LOD because of stitching
     if (vertexBuffer_->IsDataLost())
         return UPDATE_MAIN_THREAD;
     else if (lodDirty_ || (north_ && north_->lodDirty_) || (south_ && south_->lodDirty_) || (west_ && west_->lodDirty_) ||