Mapgen: Performance improvement and fixes for updateLiquid (#4065)
authorMillersMan <MillersMan@users.noreply.github.com>
Sat, 4 Jun 2016 23:30:36 +0000 (01:30 +0200)
committerkwolekr <kwolekr@minetest.net>
Sat, 4 Jun 2016 23:32:54 +0000 (19:32 -0400)
- Adds only ~100 nodes per chunk to trans_liquid with similar processing time
- Adds liquid nodes themselves instead of potential solid nodes below them
- CONTENT_IGNORE nodes are interpreted as if they continue their neighborhood
- This allows liquid columns to span multiple chunks without being interrupted
- NOTE: Expects an one-node border in generation chunk without liquid changes

src/mapgen.cpp
src/mapgen.h

index 32f7e29eb1912b2b80995b62fd3e1cec0c65a7bf..b5c48a471586058a4d6475f557428fdcd0449d16 100644 (file)
@@ -213,27 +213,86 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
        //printf("updateHeightmap: %dus\n", t.stop());
 }
 
+inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
+{
+       u32 vi_neg_x = vi;
+       vm->m_area.add_x(em, vi_neg_x, -1);
+       if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
+               if (c_nx.floodable && !c_nx.isLiquid())
+                       return true;
+       }
+       u32 vi_pos_x = vi;
+       vm->m_area.add_x(em, vi_pos_x, +1);
+       if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
+               if (c_px.floodable && !c_px.isLiquid())
+                       return true;
+       }
+       u32 vi_neg_z = vi;
+       vm->m_area.add_z(em, vi_neg_z, -1);
+       if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
+               if (c_nz.floodable && !c_nz.isLiquid())
+                       return true;
+       }
+       u32 vi_pos_z = vi;
+       vm->m_area.add_z(em, vi_pos_z, +1);
+       if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
+               if (c_pz.floodable && !c_pz.isLiquid())
+                       return true;
+       }
+       return false;
+}
 
 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
 {
-       bool isliquid, wasliquid;
+       bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
        v3s16 em  = vm->m_area.getExtent();
 
-       for (s16 z = nmin.Z; z <= nmax.Z; z++) {
-               for (s16 x = nmin.X; x <= nmax.X; x++) {
-                       wasliquid = true;
-
-                       u32 i = vm->m_area.index(x, nmax.Y, z);
-                       for (s16 y = nmax.Y; y >= nmin.Y; y--) {
-                               isliquid = ndef->get(vm->m_data[i]).isLiquid();
-
-                               // there was a change between liquid and nonliquid, add to queue.
-                               if (isliquid != wasliquid)
+       for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
+       for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
+               wasignored = true;
+               wasliquid = false;
+               waschecked = false;
+               waspushed = false;
+
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               for (s16 y = nmax.Y; y >= nmin.Y; y--) {
+                       isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
+                       isliquid = ndef->get(vm->m_data[vi]).isLiquid();
+
+                       if (isignored || wasignored || isliquid == wasliquid) {
+                               // Neither topmost node of liquid column nor topmost node below column
+                               waschecked = false;
+                               waspushed = false;
+                       } else if (isliquid) {
+                               // This is the topmost node in the column
+                               bool ispushed = false;
+                               if (isLiquidHorizontallyFlowable(vi, em)) {
                                        trans_liquid->push_back(v3s16(x, y, z));
-
-                               wasliquid = isliquid;
-                               vm->m_area.add_y(em, i, -1);
+                                       ispushed = true;
+                               }
+                               // Remember waschecked and waspushed to avoid repeated
+                               // checks/pushes in case the column consists of only this node
+                               waschecked = true;
+                               waspushed = ispushed;
+                       } else {
+                               // This is the topmost node below a liquid column
+                               u32 vi_above = vi;
+                               vm->m_area.add_y(em, vi_above, 1);
+                               if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
+                                               (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
+                                       // Push back the lowest node in the column which is one
+                                       // node above this one
+                                       trans_liquid->push_back(v3s16(x, y + 1, z));
+                               }
                        }
+
+                       wasliquid = isliquid;
+                       wasignored = isignored;
+                       vm->m_area.add_y(em, vi, -1);
                }
        }
 }
index f673007b53c1ac522af555598e42a79372f2894a..90ac84bd8cffbfdfcd9cafd861d4ec00f9fc6bc9 100644 (file)
@@ -199,6 +199,10 @@ public:
        virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
 
 private:
+       // isLiquidHorizontallyFlowable() is a helper function for updateLiquid()
+       // that checks whether there are floodable nodes without liquid beneath
+       // the node at index vi.
+       inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em);
        DISABLE_CLASS_COPY(Mapgen);
 };