Sfoglia il codice sorgente

improve world generator speed to 20 chunks per second

Kolja Strohm 13 ore fa
parent
commit
c1c3d235f7

+ 142 - 55
FactoryCraft/Chunk.cpp

@@ -423,79 +423,166 @@ void Chunk::api(Framework::StreamReader* zRequest,
 
 void Chunk::initializeLightning()
 {
-    // TODO: initialize only daylight with a more efficient algorithm here and
-    // add all light sources to the light update thread when the chunk is added
-    // to the map
-    unsigned char dayLight[6] = {255, 255, 255, 0, 0, 0};
-    unsigned char noLight[6] = {0, 0, 0, 0, 0, 0};
-    while (true)
+    unsigned char dayLight[3] = {255, 255, 255};
+    unsigned char noLight[3] = {0, 0, 0};
+    unsigned short visited[CHUNK_SIZE][WORLD_HEIGHT];
+    memset(visited, 0, sizeof(visited));
+    int minZ = 0;
+    int goUps = 0;
+    for (int z = WORLD_HEIGHT - 1; z >= 0; z--)
     {
-        bool changes = false;
-        for (int z = WORLD_HEIGHT - 1; z >= 0; z--)
+        minZ = z;
+        unsigned char max[3] = {0, 0, 0};
+        bool allVisited = 1;
+        for (int x = 0; x < CHUNK_SIZE; x++)
         {
-            for (int x = 0; x < CHUNK_SIZE; x++)
+            for (int y = 0; y < CHUNK_SIZE; y++)
             {
-                for (int y = 0; y < CHUNK_SIZE; y++)
+                if (visited[y][z] & (1 << x)) continue;
+                unsigned char* lightAbove
+                    = z == WORLD_HEIGHT - 1
+                        ? dayLight
+                        : getLightData(Framework::Vec3<int>(x, y, z + 1));
+                if (lightAbove[0] | lightAbove[1] | lightAbove[2])
                 {
-                    int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
+                    visited[y][z] |= 1 << x;
                     unsigned char* light
                         = getLightData(Framework::Vec3<int>(x, y, z));
-                    unsigned char newLight[6] = {0, 0, 0, 0, 0, 0};
-                    for (int i = 0; i < 6; i++)
-                    {
-                        unsigned char* neighborLeight;
-                        Framework::Vec3<int> neighborPos
-                            = Framework::Vec3<int>(x, y, z)
-                            + getDirection(getDirectionFromIndex(i));
-                        if (neighborPos.z < 0 || neighborPos.x < 0
-                            || neighborPos.y < 0 || neighborPos.x >= CHUNK_SIZE
-                            || neighborPos.y >= CHUNK_SIZE)
-                        {
-                            neighborLeight = noLight;
-                        }
-                        else if (neighborPos.z >= WORLD_HEIGHT)
-                        {
-                            neighborLeight = dayLight;
-                        }
-                        else
-                        {
-                            neighborLeight = getLightData(neighborPos);
-                        }
-                        for (int j = 0; j < 3; j++)
-                            newLight[j] = (unsigned char)MAX(newLight[j],
-                                i == getDirectionIndex(TOP)
-                                    ? neighborLeight[j]
-                                    : (unsigned char)((float)neighborLeight[j]
-                                                      * 0.8f));
-                        for (int j = 3; j < 6; j++)
-                            newLight[j] = (unsigned char)MAX(newLight[j],
-                                (unsigned char)((float)neighborLeight[j]
-                                                * 0.85f));
-                    }
+                    int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
                     const Block* current
                         = blocks[index]
                             ? blocks[index]
                             : Game::INSTANCE->zBlockType(blockIds[index])
                                   ->zDefault();
-                    // add own light emission
-                    for (int j = 3; j < 6; j++)
-                        newLight[j] = (unsigned char)MAX(newLight[j],
-                            current->getLightEmisionColor()[j - 3]);
-                    current->filterPassingLight(newLight);
-                    current->filterPassingLight(newLight + 3);
-                    for (int i = 0; i < 6; i++)
+                    light[0] = lightAbove[0];
+                    light[1] = lightAbove[1];
+                    light[2] = lightAbove[2];
+                    current->filterPassingLight(light);
+                    max[0] = MAX(max[0], lightAbove[0]);
+                    max[1] = MAX(max[1], lightAbove[1]);
+                    max[2] = MAX(max[2], lightAbove[2]);
+                }
+                else
+                {
+                    allVisited = 0;
+                }
+            }
+        }
+        if (!(max[0] | max[1] | max[2])) break;
+        if (!allVisited)
+        {
+            bool goUp = 1;
+            while (goUp)
+            {
+                goUp = 0;
+                bool changes = 1;
+                while (changes)
+                {
+                    changes = 0;
+                    for (int x = 0; x < CHUNK_SIZE; x++)
                     {
-                        if (newLight[i] != light[i])
+                        for (int y = 0; y < CHUNK_SIZE; y++)
                         {
-                            changes = 1;
-                            memcpy(light, newLight, 6);
-                            break;
+                            int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
+                            unsigned char* light
+                                = getLightData(Framework::Vec3<int>(x, y, z));
+                            const Block* current
+                                = blocks[index]
+                                    ? blocks[index]
+                                    : Game::INSTANCE
+                                          ->zBlockType(blockIds[index])
+                                          ->zDefault();
+                            unsigned char newLight[3] = {0, 0, 0};
+                            for (int i = 0; i < 4; i++)
+                            {
+                                Framework::Vec3<int> neighborPos
+                                    = Framework::Vec3<int>(x, y, z)
+                                    + getDirection(getDirectionFromIndex(i));
+                                if (neighborPos.x < 0 || neighborPos.y < 0
+                                    || neighborPos.x >= CHUNK_SIZE
+                                    || neighborPos.y >= CHUNK_SIZE)
+                                {
+                                    continue;
+                                }
+                                unsigned char* neighborLeight
+                                    = getLightData(neighborPos);
+                                for (int j = 0; j < 3; j++)
+                                {
+                                    newLight[j] = (unsigned char)MAX(
+                                        newLight[j],
+                                        (unsigned char)((float)neighborLeight[j]
+                                                        * 0.8f));
+                                }
+                            }
+                            current->filterPassingLight(newLight);
+                            if (newLight[0] > light[0] || newLight[1] > light[1]
+                                || newLight[2] > light[2])
+                            {
+                                changes = 1;
+                                light[0] = MAX(light[0], newLight[0]);
+                                light[1] = MAX(light[1], newLight[1]);
+                                light[2] = MAX(light[2], newLight[2]);
+                                if (z < WORLD_HEIGHT - 1
+                                    && !(visited[y][z + 1] & (1 << x)))
+                                {
+                                    unsigned char* lightAbove = getLightData(
+                                        Framework::Vec3<int>(x, y, z + 1));
+                                    newLight[0]
+                                        = (unsigned char)(light[0] * 0.8f);
+                                    newLight[1]
+                                        = (unsigned char)(light[1] * 0.8f);
+                                    newLight[2]
+                                        = (unsigned char)(light[2] * 0.8f);
+                                    const Block* above
+                                        = blocks[index - 1]
+                                            ? blocks[index - 1]
+                                            : Game::INSTANCE
+                                                  ->zBlockType(
+                                                      blockIds[index - 1])
+                                                  ->zDefault();
+                                    above->filterPassingLight(newLight);
+                                    if (newLight[0] > lightAbove[0]
+                                        || newLight[1] > lightAbove[1]
+                                        || newLight[2] > lightAbove[2])
+                                    {
+                                        lightAbove[0]
+                                            = MAX(lightAbove[0], newLight[0]);
+                                        lightAbove[1]
+                                            = MAX(lightAbove[1], newLight[1]);
+                                        lightAbove[2]
+                                            = MAX(lightAbove[2], newLight[2]);
+                                        visited[y][z + 1] |= 1 << x;
+                                        goUp = 1;
+                                    }
+                                }
+                            }
                         }
                     }
                 }
+                if (goUp)
+                {
+                    z++;
+                    goUps++;
+                }
             }
         }
-        if (!changes) break;
+    }
+    Framework::Logging::debug() << "goUps: " << goUps << " minZ: " << minZ;
+}
+
+void Chunk::updateLightSources()
+{
+    Dimension* zDim = Game::INSTANCE->zDimension(dimensionId);
+    for (int i : lightSources)
+    {
+        int x = (i / WORLD_HEIGHT) / CHUNK_SIZE;
+        int y = (i / WORLD_HEIGHT) % CHUNK_SIZE;
+        int z = i % WORLD_HEIGHT;
+        Framework::Vec3<int> pos = {x, y, z};
+        zDim->updateLightning(
+            Framework::Vec3<int>(x + this->location.x - CHUNK_SIZE / 2,
+                y + this->location.y - CHUNK_SIZE / 2,
+                z));
     }
 }
 

+ 1 - 0
FactoryCraft/Chunk.h

@@ -62,6 +62,7 @@ public:
         Entity* zSource,
         DoLaterHandler& laterHandler);
     void initializeLightning();
+    void updateLightSources();
 
     Framework::Either<Block*, int> zBlockAt(
         Framework::Vec3<int> cLocation) const;

+ 45 - 25
FactoryCraft/Dimension.cpp

@@ -510,7 +510,11 @@ void Dimension::setChunk(Chunk* chunk, Punkt center)
     }
     if (chunk) chunk->onLoaded();
     laterHandler.execute();
-    if (chunk) updateLightAtChunkBorders(center);
+    if (chunk)
+    {
+        updateLightAtChunkBorders(center);
+        chunk->updateLightSources();
+    }
 }
 
 void Dimension::save(Text worldDir) const
@@ -728,34 +732,50 @@ void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
         Logging::warning()
             << "light calculation queue is over 300000 blocks long";
     }
+    bool xn = zChunk(chunkCenter - Punkt(CHUNK_SIZE, 0)) != 0;
+    bool xp = zChunk(chunkCenter + Punkt(CHUNK_SIZE, 0)) != 0;
+    bool yn = zChunk(chunkCenter - Punkt(0, CHUNK_SIZE)) != 0;
+    bool yp = zChunk(chunkCenter + Punkt(0, CHUNK_SIZE)) != 0;
     for (int i = WORLD_HEIGHT - 1; i >= 0; i--)
     {
         for (int j = 0; j < CHUNK_SIZE; j++)
         {
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 - 1,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2 - 1,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y - CHUNK_SIZE / 2 - 1,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y - CHUNK_SIZE / 2,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y + CHUNK_SIZE / 2 - 1,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y + CHUNK_SIZE / 2,
-                i));
+            if (xn)
+            {
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 - 1,
+                    chunkCenter.y - CHUNK_SIZE / 2 + j,
+                    i));
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2,
+                    chunkCenter.y - CHUNK_SIZE / 2 + j,
+                    i));
+            }
+            if (xp)
+            {
+                updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2 - 1,
+                    chunkCenter.y - CHUNK_SIZE / 2 + j,
+                    i));
+                updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2,
+                    chunkCenter.y - CHUNK_SIZE / 2 + j,
+                    i));
+            }
+            if (yn)
+            {
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
+                    chunkCenter.y - CHUNK_SIZE / 2 - 1,
+                    i));
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
+                    chunkCenter.y - CHUNK_SIZE / 2,
+                    i));
+            }
+            if (yp)
+            {
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
+                    chunkCenter.y + CHUNK_SIZE / 2 - 1,
+                    i));
+                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
+                    chunkCenter.y + CHUNK_SIZE / 2,
+                    i));
+            }
         }
     }
 }

+ 42 - 11
FactoryCraft/GeneratorRule.cpp

@@ -7,7 +7,9 @@ GeneratorRule::GeneratorRule()
       noiseConfig(0),
       noise(0),
       threshold(0.f),
-      condition(0)
+      condition(0),
+      bottomLayer(""),
+      topLayer("")
 {}
 
 GeneratorRule::~GeneratorRule()
@@ -29,9 +31,15 @@ void GeneratorRule::initialize(JExpressionMemory* zMemory)
 bool GeneratorRule::checkCondition(
     int x, int y, int z, JExpressionMemory* zMemory)
 {
-    return (!noise
-               || noise->getNoise((double)x, (double)y, (double)z) <= threshold)
-        && condition->getValue(zMemory);
+    if ((topLayer.getLength() && y > zMemory->getFloatVariable(topLayer))
+        || (bottomLayer.getLength()
+            && y < zMemory->getFloatVariable(bottomLayer)))
+    {
+        return false;
+    }
+    return (!condition || condition->getValue(zMemory))
+        && (!noise
+            || noise->getNoise((double)x, (double)y, (double)z) <= threshold);
 }
 
 Framework::Either<Block*, int> GeneratorRule::generateBlock(
@@ -40,9 +48,10 @@ Framework::Either<Block*, int> GeneratorRule::generateBlock(
     return createBlock(x, y, z, dimensionId);
 }
 
-void GeneratorRule::setNoiseConfig(Framework::JSON::JSONObject* noiseConfig) {
+void GeneratorRule::setNoiseConfig(Framework::JSON::JSONObject* noiseConfig)
+{
     if (this->noiseConfig) this->noiseConfig->release();
-	this->noiseConfig = noiseConfig;
+    this->noiseConfig = noiseConfig;
 }
 
 Framework::JSON::JSONObject* GeneratorRule::zNoiseConfig() const
@@ -50,7 +59,8 @@ Framework::JSON::JSONObject* GeneratorRule::zNoiseConfig() const
     return noiseConfig;
 }
 
-void GeneratorRule::setThreshold(float threshold) {
+void GeneratorRule::setThreshold(float threshold)
+{
     this->threshold = threshold;
 }
 
@@ -59,12 +69,33 @@ float GeneratorRule::getThreshold() const
     return threshold;
 }
 
-void GeneratorRule::setCondition(JBoolExpression* condition) {
-	if (this->condition) this->condition->release();
-	this->condition = condition;
+void GeneratorRule::setCondition(JBoolExpression* condition)
+{
+    if (this->condition) this->condition->release();
+    this->condition = condition;
 }
 
 JBoolExpression* GeneratorRule::zCondition() const
 {
     return condition;
-}
+}
+
+void GeneratorRule::setBottomLayer(Framework::Text bottomLayer)
+{
+    this->bottomLayer = bottomLayer;
+}
+
+Framework::Text GeneratorRule::getBottomLayer() const
+{
+    return bottomLayer;
+}
+
+void GeneratorRule::setTopLayer(Framework::Text topLayer)
+{
+    this->topLayer = topLayer;
+}
+
+Framework::Text GeneratorRule::getTopLayer() const
+{
+    return topLayer;
+}

+ 45 - 10
FactoryCraft/GeneratorRule.h

@@ -15,6 +15,8 @@ private:
     Noise* noise;
     float threshold;
     JBoolExpression* condition;
+    Framework::Text bottomLayer;
+    Framework::Text topLayer;
 
 protected:
     virtual Framework::Either<Block*, int> createBlock(
@@ -30,13 +32,16 @@ public:
     bool checkCondition(int x, int y, int z, JExpressionMemory* zMemory);
     Framework::Either<Block*, int> generateBlock(
         int x, int y, int z, int dimensionId);
-
     void setNoiseConfig(Framework::JSON::JSONObject* noiseConfig);
     Framework::JSON::JSONObject* zNoiseConfig() const;
     void setThreshold(float threshold);
     float getThreshold() const;
     void setCondition(JBoolExpression* condition);
     JBoolExpression* zCondition() const;
+    void setBottomLayer(Framework::Text bottomLayer);
+    Framework::Text getBottomLayer() const;
+    void setTopLayer(Framework::Text topLayer);
+    Framework::Text getTopLayer() const;
 };
 
 template<typename S> class GeneratorRuleFactory
@@ -60,9 +65,22 @@ public:
             zRule->setThreshold(
                 (float)zJson->zValue("threshold")->asNumber()->getNumber());
         }
-        zRule->setCondition(
-            Game::INSTANCE->zTypeRegistry()->fromJson<JBoolExpression>(
-                zJson->zValue("condition")));
+        if (zJson->hasValue("condition"))
+        {
+            zRule->setCondition(
+                Game::INSTANCE->zTypeRegistry()->fromJson<JBoolExpression>(
+                    zJson->zValue("condition")));
+        }
+        if (zJson->hasValue("bottomLayer"))
+        {
+            zRule->setBottomLayer(
+                (float)zJson->zValue("bottomLayer")->asString()->getString());
+        }
+        if (zJson->hasValue("topLayer"))
+        {
+            zRule->setTopLayer(
+                (float)zJson->zValue("topLayer")->asString()->getString());
+        }
         return result;
     }
 
@@ -78,27 +96,44 @@ public:
         }
         result->addValue("threshold",
             new Framework::JSON::JSONNumber(zRule->getThreshold()));
-        result->addValue("condition",
-            Game::INSTANCE->zTypeRegistry()->toJson(
-                dynamic_cast<Framework::JSON::JSONValue*>(
-                    zRule->zCondition()->getThis())));
+        if (zRule->zCondition())
+        {
+            result->addValue("condition",
+                Game::INSTANCE->zTypeRegistry()->toJson(
+                    dynamic_cast<Framework::JSON::JSONValue*>(
+                        zRule->zCondition()->getThis())));
+        }
+
+        result->addValue("bottomLayer",
+            new Framework::JSON::JSONString(zRule->getBottomLayer()));
+        result->addValue(
+            "topLayer", new Framework::JSON::JSONString(zRule->getTopLayer()));
         return result;
     }
 
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override
+
     {
         return builder
             ->withRequiredAttribute(
                 "noise", JNoise::getValidator(true), false, true)
             ->withRequiredAttribute("condition",
                 Game::INSTANCE->zTypeRegistry()
-                    ->getValidator<JBoolExpression>())
+                    ->getValidator<JBoolExpression>(),
+                false,
+                true)
             ->withRequiredNumber("threshold")
             ->whichIsOptional()
             ->whichIsGreaterOrEqual(0.0)
             ->whichIsLessOrEqual(1.0)
-            ->finishNumber();
+            ->finishNumber()
+            ->withRequiredString("topLayer")
+            ->whichIsOptional()
+            ->finishString()
+            ->withRequiredString("bottomLayer")
+            ->whichIsOptional()
+            ->finishString();
     }
 
 protected:

+ 1 - 0
FactoryCraft/JsonExpression.cpp

@@ -633,6 +633,7 @@ bool JFloatOperatorBoolExpression::getValue(JExpressionMemory* zMemory)
         if (!first) val &= accumulator(last, current);
         first = 0;
         last = current;
+        if (!val) break;
     }
     return val;
 }

+ 40 - 91
Windows Version/data/generator/overworld.json

@@ -367,37 +367,22 @@
                     {
                         "type": "blockInstance",
                         "blockType": "Water",
+                        "bottomLayer": "h",
                         "condition": {
-                            "type": "operator",
-                            "operator": "&&",
+                            "type": "comparsion",
+                            "operator": ">=i",
                             "values": [
                                 {
-                                    "type": "comparsion",
-                                    "operator": ">=i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "h"
-                                        }
-                                    ]
+                                    "type": "constant",
+                                    "value": 199
                                 },
                                 {
-                                    "type": "comparsion",
-                                    "operator": "<i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "constant",
-                                            "value": 200
-                                        }
-                                    ]
+                                    "type": "variable",
+                                    "name": "z"
+                                },
+                                {
+                                    "type": "variable",
+                                    "name": "h"
                                 }
                             ]
                         }
@@ -405,6 +390,7 @@
                     {
                         "type": "blockType",
                         "blockType": "Air",
+                        "bottomLayer": "h",
                         "condition": {
                             "type": "comparsion",
                             "operator": ">i",
@@ -441,6 +427,8 @@
                             }
                         },
                         "threshold": 0.25,
+                        "topLayer": "h",
+                        "bottomLayer": "h",
                         "condition": {
                             "type": "operator",
                             "operator": "&&",
@@ -510,6 +498,7 @@
                             }
                         },
                         "threshold": 0.35,
+                        "topLayer": "h",
                         "condition": {
                             "type": "comparsion",
                             "operator": "<i",
@@ -547,6 +536,7 @@
                             }
                         },
                         "threshold": 0.35,
+                        "topLayer": "h",
                         "condition": {
                             "type": "comparsion",
                             "operator": "<i",
@@ -584,96 +574,55 @@
                             }
                         },
                         "threshold": 0.35,
+                        "topLayer": "h",
+                        "bottomLayer": "u",
                         "condition": {
-                            "type": "operator",
-                            "operator": "&&",
+                            "type": "comparsion",
+                            "operator": "<i",
                             "values": [
                                 {
-                                    "type": "comparsion",
-                                    "operator": "<i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "h"
-                                        }
-                                    ]
+                                    "type": "variable",
+                                    "name": "u"
                                 },
                                 {
-                                    "type": "comparsion",
-                                    "operator": ">i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "u"
-                                        }
-                                    ]
-                                }
-                            ]
-                        }
-                    },
-                    {
-                        "type": "blockType",
-                        "blockType": "Dirt",
-                        "condition": {
-                            "type": "operator",
-                            "operator": "&&",
-                            "values": [
-                                {
-                                    "type": "comparsion",
-                                    "operator": "<i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "h"
-                                        }
-                                    ]
+                                    "type": "variable",
+                                    "name": "z"
                                 },
                                 {
-                                    "type": "comparsion",
-                                    "operator": ">i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "u"
-                                        }
-                                    ]
+                                    "type": "variable",
+                                    "name": "h"
                                 }
                             ]
                         }
                     },
                     {
                         "type": "blockType",
-                        "blockType": "Stone",
+                        "blockType": "Dirt",
+                        "topLayer": "h",
+                        "bottomLayer": "u",
                         "condition": {
                             "type": "comparsion",
-                            "operator": "<=i",
+                            "operator": "<i",
                             "values": [
+                                {
+                                    "type": "variable",
+                                    "name": "u"
+                                },
                                 {
                                     "type": "variable",
                                     "name": "z"
                                 },
                                 {
                                     "type": "variable",
-                                    "name": "u"
+                                    "name": "h"
                                 }
                             ]
                         }
+                    },
+                    {
+                        "type": "blockType",
+                        "blockType": "Stone",
+                        "topLayer": "u"
                     }
                 ],
                 "condition": {