Browse Source

Fix incorrect partial terrain update when smoothing was used. Improve worst-case performance for partial terrain update by only tracking one rectangular region that encloses the changed area.

Lasse Öörni 10 years ago
parent
commit
e8d4a05ab8
2 changed files with 44 additions and 40 deletions
  1. 44 38
      Source/Urho3D/Graphics/Terrain.cpp
  2. 0 2
      Source/Urho3D/Graphics/Terrain.h

+ 44 - 38
Source/Urho3D/Graphics/Terrain.cpp

@@ -54,6 +54,26 @@ static const unsigned STITCH_SOUTH = 2;
 static const unsigned STITCH_WEST = 4;
 static const unsigned STITCH_WEST = 4;
 static const unsigned STITCH_EAST = 8;
 static const unsigned STITCH_EAST = 8;
 
 
+inline void GrowUpdateRegion(IntRect& updateRegion, int x, int y)
+{
+    if (updateRegion.left_ < 0)
+    {
+        updateRegion.left_ = updateRegion.right_ = x;
+        updateRegion.top_ = updateRegion.bottom_ + y;
+    }
+    else
+    {
+        if (x < updateRegion.left_)
+            updateRegion.left_ = x;
+        if (x > updateRegion.right_)
+            updateRegion.right_ = x;
+        if (y < updateRegion.top_)
+            updateRegion.top_ = y;
+        if (y > updateRegion.bottom_)
+            updateRegion.bottom_ = y;
+    }
+}
+
 Terrain::Terrain(Context* context) :
 Terrain::Terrain(Context* context) :
     Component(context),
     Component(context),
     indexBuffer_(new IndexBuffer(context)),
     indexBuffer_(new IndexBuffer(context)),
@@ -684,6 +704,7 @@ void Terrain::CreateGeometry()
         float* dest = smoothing_ ? sourceHeightData_ : heightData_;
         float* dest = smoothing_ ? sourceHeightData_ : heightData_;
         unsigned imgComps = heightMap_->GetComponents();
         unsigned imgComps = heightMap_->GetComponents();
         unsigned imgRow = heightMap_->GetWidth() * imgComps;
         unsigned imgRow = heightMap_->GetWidth() * imgComps;
+        IntRect updateRegion(-1, -1, -1, -1);
 
 
         if (imgComps == 1)
         if (imgComps == 1)
         {
         {
@@ -701,16 +722,8 @@ void Terrain::CreateGeometry()
                     {
                     {
                         if (*dest != newHeight)
                         if (*dest != newHeight)
                         {
                         {
-                            if (!smoothing_)
-                                MarkPatchesDirty(dirtyPatches, x, z);
-                            else
-                            {
-                                for (int z1 = z - 1; z1 <= z + 1; ++z1)
-                                    for (int x1 = x - 1; x1 <= z + 1; ++x1)
-                                        MarkPatchesDirty(dirtyPatches, x1, z1);
-                            }
-
                             *dest = newHeight;
                             *dest = newHeight;
+                            GrowUpdateRegion(updateRegion, x, z);
                         }
                         }
                     }
                     }
 
 
@@ -736,16 +749,8 @@ void Terrain::CreateGeometry()
                     {
                     {
                         if (*dest != newHeight)
                         if (*dest != newHeight)
                         {
                         {
-                            if (!smoothing_)
-                                MarkPatchesDirty(dirtyPatches, x, z);
-                            else
-                            {
-                                for (int z1 = z - 1; z1 <= z + 1; ++z1)
-                                    for (int x1 = x - 1; x1 <= z + 1; ++x1)
-                                        MarkPatchesDirty(dirtyPatches, x1, z1);
-                            }
-
                             *dest = newHeight;
                             *dest = newHeight;
+                            GrowUpdateRegion(updateRegion, x, z);
                         }
                         }
                     }
                     }
 
 
@@ -754,6 +759,27 @@ void Terrain::CreateGeometry()
             }
             }
         }
         }
 
 
+        // If updating a region of the heightmap, check which patches change
+        if (!updateAll)
+        {
+            int lodExpand = 1 << numLodLevels_;
+            // Expand the right & bottom 1 pixel more, as patches share vertices at the edge
+            updateRegion.left_ -= lodExpand;
+            updateRegion.right_ += lodExpand + 1;
+            updateRegion.top_ -= lodExpand;
+            updateRegion.bottom_ += lodExpand + 1;
+            
+            int sX = Max(updateRegion.left_ / patchSize_, 0);
+            int eX = Min(updateRegion.right_ / patchSize_, numPatches_.x_ - 1);
+            int sY = Max(updateRegion.top_ / patchSize_, 0);
+            int eY = Min(updateRegion.bottom_ / patchSize_, numPatches_.y_ - 1);
+            for (int y = sY; y <= eY; ++y)
+            {
+                for (int x = sX; x <= eX; ++x)
+                    dirtyPatches[y * numPatches_.x_ + x] = true;
+            }
+        }
+
         patches_.Reserve(numPatches_.x_ * numPatches_.y_);
         patches_.Reserve(numPatches_.x_ * numPatches_.y_);
 
 
         bool enabled = IsEnabledEffective();
         bool enabled = IsEnabledEffective();
@@ -1176,24 +1202,4 @@ void Terrain::HandleHeightMapReloadFinished(StringHash eventType, VariantMap& ev
     CreateGeometry();
     CreateGeometry();
 }
 }
 
 
-void Terrain::MarkPatchesDirty(PODVector<bool>& dirtyPatches, int x, int z)
-{
-    x = Clamp(x, 0, numVertices_.x_);
-    z = Clamp(z, 0, numVertices_.y_);
-
-    // A point on the heightmap can potentially belong to multiple patches; dirty all that are applicable
-    int pZ = z / patchSize_;
-    int vZ = z % patchSize_;
-    int pX = x / patchSize_;
-    int vX = x % patchSize_;
-    if (pZ < numPatches_.y_ && pX < numPatches_.x_)
-        dirtyPatches[pZ * numPatches_.x_ + pX] = true;
-    if (vX == 0 && pX > 0 && pZ < numPatches_.y_)
-        dirtyPatches[pZ * numPatches_.x_ + pX - 1] = true;
-    if (vZ == 0 && pZ > 0 && pX < numPatches_.y_)
-        dirtyPatches[(pZ - 1) * numPatches_.x_ + pX] = true;
-    if (vX == 0 && vZ == 0 && pX > 0 && pZ > 0)
-        dirtyPatches[(pZ - 1) * numPatches_.x_ + pX - 1] = true;
-}
-
 }
 }

+ 0 - 2
Source/Urho3D/Graphics/Terrain.h

@@ -175,8 +175,6 @@ private:
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     /// Handle heightmap image reload finished.
     /// Handle heightmap image reload finished.
     void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
     void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
-    /// Mark patch(es) dirty based on location. Used when checking the heightmap image for changes.
-    void MarkPatchesDirty(PODVector<bool>& dirtyPatches, int x, int z);
 
 
     /// Shared index buffer.
     /// Shared index buffer.
     SharedPtr<IndexBuffer> indexBuffer_;
     SharedPtr<IndexBuffer> indexBuffer_;