From cdfc485ed44a704412fa3ec76982d7b109aef598 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 00:02:51 +0300 Subject: [PATCH 01/16] update blockstate structure --- src/voxels/Block.hpp | 4 ++-- src/voxels/voxel.hpp | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 2d3d1577..e19c1dc6 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -9,7 +9,7 @@ #include "../maths/UVRegion.hpp" #include "../typedefs.hpp" -#define BLOCK_ITEM_SUFFIX ".item" +inline std::string BLOCK_ITEM_SUFFIX = ".item"; inline constexpr uint FACE_MX = 0; inline constexpr uint FACE_PX = 1; @@ -191,4 +191,4 @@ public: Block(const Block&) = delete; }; -#endif /* VOXELS_BLOCK_HPP_ */ +#endif // VOXELS_BLOCK_HPP_ diff --git a/src/voxels/voxel.hpp b/src/voxels/voxel.hpp index 9cfaf698..0e19415f 100644 --- a/src/voxels/voxel.hpp +++ b/src/voxels/voxel.hpp @@ -11,25 +11,27 @@ inline constexpr int BLOCK_DIR_UP = 0x4; inline constexpr int BLOCK_DIR_DOWN = 0x5; struct blockstate { - uint8_t rotation : 3; - uint8_t segment : 2; // planned to 0.22 - uint8_t reserved : 3; - uint8_t userbits : 8; + uint8_t rotation : 3; // block rotation index + uint8_t segment : 1; // segment block bit + uint8_t reserved : 4; // reserved bits + uint8_t userbits : 8; // bits for use in block script }; static_assert (sizeof(blockstate) == 2); +/// @brief blockstate cast to an integer (optimized out in most cases) inline constexpr blockstate_t blockstate2int(blockstate b) { return static_cast(b.rotation) | static_cast(b.segment) << 3 | - static_cast(b.reserved) << 5 | + static_cast(b.reserved) << 4 | static_cast(b.userbits) << 8; } +/// @brief integer cast to a blockstate (optimized out in most cases) inline constexpr blockstate int2blockstate(blockstate_t i) { return { static_cast(i & 0b111), - static_cast((i >> 3) & 0b11), - static_cast((i >> 5) & 0b111), + static_cast((i >> 3) & 0b1), + static_cast((i >> 4) & 0b1111), static_cast((i >> 8) & 0xFF) }; } From 853f7fc9d17ef7f3992e8e1b3f5fd7aaa7f8fcc4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 09:57:01 +0300 Subject: [PATCH 02/16] update blockstate.segment size --- src/voxels/voxel.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/voxels/voxel.hpp b/src/voxels/voxel.hpp index 0e19415f..460a6a4f 100644 --- a/src/voxels/voxel.hpp +++ b/src/voxels/voxel.hpp @@ -12,8 +12,8 @@ inline constexpr int BLOCK_DIR_DOWN = 0x5; struct blockstate { uint8_t rotation : 3; // block rotation index - uint8_t segment : 1; // segment block bit - uint8_t reserved : 4; // reserved bits + uint8_t segment : 3; // segment block bits + uint8_t reserved : 2; // reserved bits uint8_t userbits : 8; // bits for use in block script }; static_assert (sizeof(blockstate) == 2); @@ -22,7 +22,7 @@ static_assert (sizeof(blockstate) == 2); inline constexpr blockstate_t blockstate2int(blockstate b) { return static_cast(b.rotation) | static_cast(b.segment) << 3 | - static_cast(b.reserved) << 4 | + static_cast(b.reserved) << 6 | static_cast(b.userbits) << 8; } @@ -30,8 +30,8 @@ inline constexpr blockstate_t blockstate2int(blockstate b) { inline constexpr blockstate int2blockstate(blockstate_t i) { return { static_cast(i & 0b111), - static_cast((i >> 3) & 0b1), - static_cast((i >> 4) & 0b1111), + static_cast((i >> 3) & 0b111), + static_cast((i >> 6) & 0b11), static_cast((i >> 8) & 0xFF) }; } From f181086d2a19630b54af16ff4a4e10c858c0a268 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 11:00:53 +0300 Subject: [PATCH 03/16] update debug_panel --- src/frontend/debug_panel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index a278b3e3..958b2f02 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -77,7 +77,7 @@ std::shared_ptr create_debug_panel( panel->add(create_label([=](){ std::wstringstream stream; stream << "r:" << player->selectedVoxel.state.rotation << " s:" - << player->selectedVoxel.state.segment << " u:" + << std::bitset<3>(player->selectedVoxel.state.segment) << " u:" << std::bitset<8>(player->selectedVoxel.state.userbits); if (player->selectedVoxel.id == BLOCK_VOID) { return std::wstring {L"block: -"}; From 8aa6d5ee2d7838b3fa7647f868ed30ec2f16ba89 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 16:58:05 +0300 Subject: [PATCH 04/16] add extended blocks (block size property) --- src/content/ContentBuilder.cpp | 1 + src/content/ContentLoader.cpp | 37 +++++++---- src/graphics/render/BlocksRenderer.cpp | 4 +- src/logic/BlocksController.cpp | 9 ++- src/logic/PlayerController.cpp | 15 +++-- src/maths/aabb.hpp | 5 ++ src/voxels/Block.hpp | 5 ++ src/voxels/Chunks.cpp | 88 ++++++++++++++++++++++++-- src/voxels/Chunks.hpp | 12 +++- 9 files changed, 146 insertions(+), 30 deletions(-) diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index cda2ee3b..a660ca9a 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -62,6 +62,7 @@ std::unique_ptr ContentBuilder::build() { def.rt.id = blockDefsIndices.size(); def.rt.emissive = *reinterpret_cast(def.emission); def.rt.solid = def.model == BlockModel::block; + def.rt.extended = def.size.x > 1 || def.size.y > 1 || def.size.z > 1; if (def.rotatable) { for (uint i = 0; i < BlockRotProfile::MAX_COUNT; i++) { diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index fb04c1b1..50f07dac 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -175,29 +175,38 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat def.hitboxes[i].b = glm::vec3(box->num(3), box->num(4), box->num(5)); def.hitboxes[i].b += def.hitboxes[i].a; } + } else if (auto boxarr = root->list("hitbox")){ + AABB aabb; + aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2)); + aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5)); + aabb.b += aabb.a; + def.hitboxes = { aabb }; + } else if (!def.modelBoxes.empty()) { + def.hitboxes = def.modelBoxes; } else { - boxarr = root->list("hitbox"); - if (boxarr) { - AABB aabb; - aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2)); - aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5)); - aabb.b += aabb.a; - def.hitboxes = { aabb }; - } else if (!def.modelBoxes.empty()) { - def.hitboxes = def.modelBoxes; - } else { - def.hitboxes = { AABB() }; - } + def.hitboxes = { AABB() }; } + // block light emission [r, g, b] where r,g,b in range [0..15] - auto emissionarr = root->list("emission"); - if (emissionarr) { + if (auto emissionarr = root->list("emission")) { def.emission[0] = emissionarr->num(0); def.emission[1] = emissionarr->num(1); def.emission[2] = emissionarr->num(2); } + // block size + if (auto sizearr = root->list("size")) { + def.size.x = sizearr->num(0); + def.size.y = sizearr->num(1); + def.size.z = sizearr->num(2); + if (def.model == BlockModel::block && + (def.size.x != 1 || def.size.y != 1 || def.size.z != 1)) { + def.model = BlockModel::aabb; + def.hitboxes = {AABB(def.size)}; + } + } + // primitive properties root->flag("obstacle", def.obstacle); root->flag("replaceable", def.replaceable); diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 9420d070..578b198d 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -414,9 +414,11 @@ void BlocksRenderer::render(const voxel* voxels) { for (int i = begin; i < end; i++) { const voxel& vox = voxels[i]; blockid_t id = vox.id; + blockstate state = vox.state; const Block& def = *blockDefsCache[id]; - if (id == 0 || def.drawGroup != drawGroup) + if (id == 0 || def.drawGroup != drawGroup || state.segment) { continue; + } const UVRegion texfaces[6]{ cache->getRegion(id, 0), cache->getRegion(id, 1), cache->getRegion(id, 2), diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 9ddf12b8..4748e7e3 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -130,11 +130,13 @@ void BlocksController::randomTick(int tickid, int parts) { for (uint z = padding; z < d-padding; z++){ for (uint x = padding; x < w-padding; x++){ int index = z * w + x; - if ((index + tickid) % parts != 0) + if ((index + tickid) % parts != 0) { continue; + } auto& chunk = chunks->chunks[index]; - if (chunk == nullptr || !chunk->flags.lighted) + if (chunk == nullptr || !chunk->flags.lighted) { continue; + } for (int s = 0; s < segments; s++) { for (int i = 0; i < 4; i++) { int bx = random.rand() % CHUNK_W; @@ -146,7 +148,8 @@ void BlocksController::randomTick(int tickid, int parts) { scripting::random_update_block( block, chunk->x * CHUNK_W + bx, by, - chunk->z * CHUNK_D + bz); + chunk->z * CHUNK_D + bz + ); } } } diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 74fa97a5..ec4b63df 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -364,9 +364,15 @@ void PlayerController::updateInteraction(){ end, norm, iend ); if (vox != nullptr) { + blockstate selectedState = vox->state; player->selectedVoxel = *vox; selectedBlockId = vox->id; selectedBlockRotation = vox->state.rotation; + if (selectedState.segment) { + iend = chunks->seekOrigin( + iend, indices->getBlockDef(selectedBlockId), selectedState + ); + } player->selectedBlockPosition = iend; selectedPointPosition = end; selectedBlockNormal = norm; @@ -379,8 +385,9 @@ void PlayerController::updateInteraction(){ state.rotation = determine_rotation(def, norm, camera->dir); if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) { - if (scripting::on_item_break_block(player.get(), item, x, y, z)) + if (scripting::on_item_break_block(player.get(), item, x, y, z)) { return; + } } Block* target = indices->getBlockDef(vox->id); @@ -411,10 +418,8 @@ void PlayerController::updateInteraction(){ x = (iend.x)+(norm.x); y = (iend.y)+(norm.y); z = (iend.z)+(norm.z); - } else { - if (def->rotations.name == "pipe") { - state.rotation = BLOCK_DIR_UP; - } + } else if (def->rotations.name == "pipe") { + state.rotation = BLOCK_DIR_UP; } vox = chunks->get(x, y, z); blockid_t chosenBlock = def->rt.id; diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index 81ba856b..49177113 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -8,6 +8,11 @@ struct AABB { glm::vec3 a {0.0f}; glm::vec3 b {1.0f}; + AABB() {} + + AABB(glm::vec3 size) : a(0.0f), b(size) { + } + /* Get AABB point with minimal x,y,z */ inline glm::vec3 min() const { return glm::min(a, b); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index e19c1dc6..cdf997c0 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -110,6 +110,8 @@ public: /// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive clouds) uint8_t emission[4] {0, 0, 0, 0}; + glm::i8vec3 size {1, 1, 1}; + /// @brief Influences visible block sides for transparent blocks uint8_t drawGroup = 0; @@ -175,6 +177,9 @@ public: /// @brief does the block emit any lights bool emissive = false; + + // @brief block size is greather than 1x1x1 + bool extended = false; /// @brief set of hitboxes sets with all coord-systems precalculated std::vector hitboxes[BlockRotProfile::MAX_COUNT]; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 2805c94b..b4107832 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -33,7 +33,7 @@ Chunks::Chunks( chunksCount = 0; } -voxel* Chunks::get(int32_t x, int32_t y, int32_t z) { +voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { x -= ox * CHUNK_W; z -= oz * CHUNK_D; int cx = floordiv(x, CHUNK_W); @@ -158,28 +158,104 @@ Chunk* Chunks::getChunk(int x, int z){ return chunks[z * w + x].get(); } +glm::ivec3 Chunks::seekOrigin(glm::ivec3 pos, const Block* def, blockstate state) { + const auto& rotation = def->rotations.variants[state.rotation]; + auto segment = state.segment; + while (true) { + if (!segment) { + return pos; + } + if (segment & 1) pos -= rotation.axisX; + if (segment & 2) pos -= rotation.axisY; + if (segment & 4) pos -= rotation.axisZ; + + if (auto* voxel = get(pos.x, pos.y, pos.z)) { + segment = voxel->state.segment; + } else { + return pos; + } + } +} + +void Chunks::eraseSegments(const Block* def, blockstate state, int x, int y, int z) { + const auto& rotation = def->rotations.variants[state.rotation]; + for (int sy = 0; sy < def->size.y; sy++) { + for (int sz = 0; sz < def->size.z; sz++) { + for (int sx = 0; sx < def->size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(pos.x, pos.y, pos.z, 0, {}); + } + } + } +} + +void Chunks::repairSegments(const Block* def, blockstate state, int x, int y, int z) { + const auto& rotation = def->rotations.variants[state.rotation]; + const auto id = def->rt.id; + const auto size = def->size; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + blockstate segState = state; + segState.segment = ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); + + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(pos.x, pos.y, pos.z, id, segState); + } + } + } +} + void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) { - if (y < 0 || y >= CHUNK_H) + if (y < 0 || y >= CHUNK_H) { return; + } + int32_t gx = x; + int32_t gz = z; x -= ox * CHUNK_W; z -= oz * CHUNK_D; int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cz < 0 || cx >= int(w) || cz >= int(d)) + if (cx < 0 || cz < 0 || cx >= static_cast(w) || cz >= static_cast(d)) { return; + } Chunk* chunk = chunks[cz * w + cx].get(); - if (chunk == nullptr) + if (chunk == nullptr) { return; + } int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; + // block finalization voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; - auto def = contentIds->getBlockDef(vox.id); - if (def->inventorySize == 0) + auto prevdef = contentIds->getBlockDef(vox.id); + if (prevdef->inventorySize == 0) { chunk->removeBlockInventory(lx, y, lz); + } + if (prevdef->rt.extended && !vox.state.segment) { + eraseSegments(prevdef, vox.state, gx, y, gz); + } + + // block initialization + auto newdef = contentIds->getBlockDef(id); vox.id = id; vox.state = state; chunk->setModifiedAndUnsaved(); + if (!state.segment && newdef->rt.extended) { + repairSegments(newdef, state, gx, y, gz); + } if (y < chunk->bottom) chunk->bottom = y; else if (y + 1 > chunk->top) chunk->top = y + 1; diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index abeceae1..e73bf025 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -17,10 +17,14 @@ class ContentIndices; class Chunk; class WorldFiles; class LevelEvents; +class Block; /* Player-centred chunks matrix */ class Chunks { const ContentIndices* const contentIds; + + void eraseSegments(const Block* def, blockstate state, int x, int y, int z); + void repairSegments(const Block* def, blockstate state, int x, int y, int z); public: std::vector> chunks; std::vector> chunksSecond; @@ -40,10 +44,16 @@ public: Chunk* getChunk(int32_t x, int32_t z); Chunk* getChunkByVoxel(int32_t x, int32_t y, int32_t z); - voxel* get(int32_t x, int32_t y, int32_t z); + voxel* get(int32_t x, int32_t y, int32_t z) const; + + inline voxel* get(glm::ivec3 pos) { + return get(pos.x, pos.y, pos.z); + } + light_t getLight(int32_t x, int32_t y, int32_t z); ubyte getLight(int32_t x, int32_t y, int32_t z, int channel); void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state); + glm::ivec3 seekOrigin(glm::ivec3 pos, const Block* def, blockstate state); voxel* rayCast( glm::vec3 start, From c2aa4e3c267b9a1209faa1bfe02337a7675eaad3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 20:22:52 +0300 Subject: [PATCH 05/16] fix: ancient aabb blocks rendering bug (lighting) --- src/graphics/render/BlocksRenderer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 578b198d..72a0d87f 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -126,7 +126,7 @@ void BlocksRenderer::face(const vec3& coord, float s = 0.5f; if (lights) { - float d = glm::dot(Z, SUN_VECTOR); + float d = glm::dot(glm::normalize(Z), SUN_VECTOR); d = 0.8f + d * 0.2f; vec3 axisX = glm::normalize(X); @@ -229,13 +229,11 @@ void BlocksRenderer::blockAABB( if (block->hitboxes.empty()) { return; } - AABB hitbox = block->hitboxes[0]; for (const auto& box : block->hitboxes) { hitbox.a = glm::min(hitbox.a, box.a); hitbox.b = glm::max(hitbox.b, box.b); } - vec3 size = hitbox.size(); vec3 X(1, 0, 0); vec3 Y(0, 1, 0); @@ -249,7 +247,6 @@ void BlocksRenderer::blockAABB( Z = orient.axisZ; orient.transform(hitbox); } - coord = vec3(icoord) - vec3(0.5f) + hitbox.center(); face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north From ee47213dbbf2e9071e13d4a56bb3b5de5dfb4ce6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Jun 2024 21:42:44 +0300 Subject: [PATCH 06/16] add temporary test block --- res/content/base/blocks/door.json | 15 ++++++++++++++ res/content/base/content.json | 9 +++++---- res/content/base/textures/blocks/door.png | Bin 0 -> 8418 bytes .../base/textures/blocks/door_side.png | Bin 0 -> 6856 bytes res/content/base/textures/blocks/door_top.png | Bin 0 -> 6556 bytes src/frontend/debug_panel.cpp | 19 ++++++++++++++++++ src/logic/PlayerController.cpp | 5 +++++ src/objects/Player.hpp | 1 + 8 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 res/content/base/blocks/door.json create mode 100644 res/content/base/textures/blocks/door.png create mode 100644 res/content/base/textures/blocks/door_side.png create mode 100644 res/content/base/textures/blocks/door_top.png diff --git a/res/content/base/blocks/door.json b/res/content/base/blocks/door.json new file mode 100644 index 00000000..db12e660 --- /dev/null +++ b/res/content/base/blocks/door.json @@ -0,0 +1,15 @@ +{ + "material": "base:wood", + "texture-faces": [ + "door_side", + "door_side", + "door_top", + "door_top", + "door", + "door" + ], + "light-passing": true, + "sky-light-passing": true, + "size": [1, 2, 1], + "rotation": "pipe" +} diff --git a/res/content/base/content.json b/res/content/base/content.json index ef2f084b..09fc42f1 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -1,7 +1,4 @@ { - "items": [ - "bazalt_breaker" - ], "blocks": [ "dirt", "grass_block", @@ -25,6 +22,10 @@ "pane", "pipe", "lightbulb", - "torch" + "torch", + "door" + ], + "items": [ + "bazalt_breaker" ] } \ No newline at end of file diff --git a/res/content/base/textures/blocks/door.png b/res/content/base/textures/blocks/door.png new file mode 100644 index 0000000000000000000000000000000000000000..ad651ac66efe6de06134de64963dc0442048785f GIT binary patch literal 8418 zcmeHLc|276`=7dHOKGv)Y-37PjydPdJ`rMK?1T!*nVB<8mSJYBl}L(|3MoRjMOqZH zv>>$6LW`2ps>NEiP>FsA-EO^p-+%7w-rHZz>vd+%`Fx(|{du0}{d_*pGbi4`&SI+E zY&jebHQAH$m{nd)0m;KkA z$ZoyK2dMj{U-rd#Y7V_kIN*JtQ}dik2Xs%%j8U$6xjL%6Bv>HqZvnOqfAZ|gEP9~( z0eH|fuw_?Xh4tie9m$)Nki?J8S$!`}2JB@Y`n0YW%5RegHH%7fjI(exq4f1-sw#=4 zZWlD($L}5>`jxxCSm>@}WY(*)v;XB#)5!GKYh43cH@KZE%GtemP$4``?t#ktVR7A> zmZzlB{DUSZZykB+e$IWHn*IyxGf_t63$&`t%k%2)JM?cV4zBeLtZn3OJT=SO<4$w@ z&`4HLGl#wCu+^OGwB9EVMYcw@ou8E|n%R`Wvggq2@On?tr*566HwJt+uPwfMeIZO{ zZDSqz+^ZBBe379k2YD)dc>9K?BdtcuU{sLgmo|r+>{fd|*x&!UjISzrdVGtebYl+; z%^j(k^l=`O^jReNO1GOcDV~(<*>g;&GO&kk)ZM6Py(DG3BT0o9 zr|!CZ*XCG{X8PPa8m+o%Rhy;+XGXlrt#(z4mxGCRqAtDESD1zPDSay zvM$FP+}z*ZcMHg7*Sos=manuoN+xIf=9Tw9tzA){?{||bwRbzYSI_L?ZAS~W^^N9> z3vJSv9e#!O?t<5>krn6L*PI|W11&;h?!WjrQ*bxC-eq&Y`qSZcbh?lF2fXpRIOBDVLo)i!D)UmcWK34;YFMPJ z-QD#ExQ982yxV%)ORdZGZNY;X_I0JM)?7esqEcZ- z_>!YvyaTUs`>M`$A5&=~&WK9qoBLPtIBs zt4FA;;WVlGJ+N-Ej(<~ar?Jz1-FAbM*X1*qxyg5HSYgGr#09ToD{3Wz;LNHm{#IJs zid86+tfB9TAO---f`8wsH*XjA{gy+J6EAbw0E(AXli)_-Z6(_H< zswv$2XhqQM`_(!|m6c~~*0;tbT~_o=JNDs)b6U7pvnAS>(Q#Di&DJ;Oo$wWx;O@pv zx_NJVE43SR>Td6HbYW^rR75(f(*z68Bv=b~w=^8xbyKM+MUzf#7fa(7BzUH%?yK^h zRb3rXEp`>X)bU@sBtyv+(wXJMS4--&W5{aAiyrYq=g!QoS$0%xGMu>hp-%a>&h|jB zE$Pc#qxZ`^Y~BCa^5R~-&Qgz=VGS?0Uut#LR@2#ITU;)+FwR%SUZPv_-fq@e3(E*< zprR~nC$G&>P%xZ2B|dY5)xfR^Fa=s4zhMD+#>&if?a-mZCobxr98*hjiq7^<`^48# zJdx~C?KojiccO97ia3)}8Hdka^Jb763u@$|N``FWpGU6-Rqm#qa*>mkeN}VT_hV7j zyq0+eGaX;9bsD+vsyy2!?V?~4BVmF4UVWlg{+w6sF;|Bhk;DUMRVx#MVk$PZ1|7iJ zsj~-9nfGNx4<~T4Q$AQQ@MTGrTCbum$zJ`kbwoweZxY=hZd)7)Gs8QK zmqM%nW~|?3WO>O%{mSZP#o5m4`g-j<4=`6wR|>kfK=Htmqe^bU;JNo^K6iH>K1-K7 zHCftN`NTAXy7SDlavPsZ#}&pTA52L3P;vM+p&{mFWLfFp*|pL##WU-36Csm&f70%_ zgtX4Bs$ndZjWWpe^>E>~m29hnZ$F&hvLi1P%DO-Dc>f{y+LiL%@s~+ALI399=3}-^ z&&%x95!Onb#k!i5wvCr}cq})JeCZLm@_E_}QfB&`#OVpK%C2sEm29_%CDD(sVZNU7 z`wo{n_l#nvSjxL=t5xgTZdiNdFg8t6&X=mhTV}%pQTKW5JulV{=l89m7`#i ze%|USPI^nFW%TI>kIEJ`K5DA)o;^H_1JpzbonemKaX3YJ0oI$Wu(e@wh5iNzPsl+H z!u$iV-UWvNn!)0Rkqr*f3eoRE-YeZOK>wq^Cicmb+fNTH) ztS~_cOjs<3Hx%*sOegcDUm>t3BZ7}a638SHLqkIiLP-Wfkv9=yFc?G-Cc-d)NdV&T z00|NX1c>L1LX2aWqhhW|5GWA{1Ms7m2uBz!F(MGKdHkRD`3KtCeuEDXe~khb4`LV+ zNQ4YPqQ5`!dkwLK6@r0$HRyk8h@G((kLZMog~1{&$_haPBy+xp;Bmj{2L_A$#@yj? ziKrjykBN%0RiPi2v|!sheA5^Wfw#ava7+se_79R00spUH{b1YZ%$Pgh8-l5S!~H?} zPv6IcF)3SHrn!(CJZc`>+=wu`K9eWp3V6)1TRH{dabZ3MpwM9mAoC#v;BYWfC=7&4 zMqv_ypzwE4>;SO@3E-lmP#Cy@0K;J*2py$BJOH9%jFKr7DnQ351~`0>M5i!#FozF) z2VpM~U|EUyeQ(t$6c2-f=@c%7OhN$!r7{3ANTC4;heHGCJOl*EFoHt#acg*7=2D@^ zAHnDp_#@sZF)+Y;Y>m-?Gff@XMg-UZ{AH{qG*2;rF%8TYrkMyuBtnt1 zQ0Qkw7&Qq$syUY4c*Aj0ECpgrB78LCzvlchG-&*3d<*yq#-{N2v9x6(-0?-kNC?Us zTL{w|pW^x;0p2LKzrQBbpW}l6K^7mR&>19<51=p)Ba6=k0S1ps0x+&%6yfnf5IYWj zSXeCNOF|J5YU+)7ig|-2(3m%Py|GZv|4};B2OW(Q2$KK^0w9Vr1T#SflR{nyl9(V! zApZ9*l6iD4jmF^tBs!M|V1zQTBmxo47?8^4(s>}AOaIa6f9@hgX2L(wMVN-rNg$aF zkjMxPAX8xwKo~SGzyq;8f}%VKq5YdK(wsqviSY+PKaWKcl|n{oJUZqgiUMRZ2Ld>B z5(U7@6^RTn=p-IAeiVG$2LFgfhBL%qk||6G{&_Ak_M0EYr$b%cx1VG#M}{``5iz>qTx zGs#pYO!|2)a<~v5q%ueV8KqLNjR%qd1|1>+R0<#D(I7r%+<4viM_Kvv{NZDyz$k|b zKny+ytKEDqc98JM00$&P5EWyOLqq>9%3uIyIKv>5LSa&V;x6N%JcI)CH~>t-zQ`Pu z3vjq}GPdbRSi6B0KN|Pnsd!`nqyiw_86shQ3|91hWYLf~+QpIq_SA{zvZPz3WaQLk-%Uv*;&8u}vdvAMEvvKd-SG85RjYft)~1Qy+veNUpF>YaXISCnjW-}5EsBY@8IY$)Do9!Wnxu-xRx9QCtrJ9cQd1D zt*1)8jha|_$^N%puVV+pvj*n`Z0L^;R%c!5mpM4OcC-BzarlR`_J=&B{%F!TSVSOg zK6%1FG0}d)-dg3>tu0CEDOpvO_g?Y!Zd+;Z7@Boli8#1Tabrkn);`%uFRv-~ z4Dab`I=|XnCcYN7v&+dXUUMUqIk}l$r5fX8KSWlQ!aaV%Fj@W7?~lVv*d_~(M^4tB ztQ!O2UKk#HcpNG4Ir;mv-`X#1Vf8+`vFA?t$leb3jfBv*VU+$kB1g0Dpu)VuMHV2HC%Vk%+-2Y z(*w7U2`Te@0CA!IwdqR;yEe|P6Dd5rdAcOk_-dP>XJTKW!m|S5g@XQytMu8o7#elU z_ZW7*cW6@EF0{%fIA$cxwpN{I!=I8HyR>}cOU3*&*8KPm#qJXZImrw`I-I$=^~|$~ zcyFV|Fhu;?G-EFAy>zlz({Iy!T=28CkqW&0xU*$J@kz@spY9Axl?{1kbXB$Q)kXOS z!GWu`)V+vWv`AVno8Dr2!toMMs=|%1m2;V->AV1CTIg*oWWB zO`@ZrU0O#>Efsam*-O$_E=`VV<3-~pdD$dNAL^Nyuc&^7EK_jxlwn{(x|im%-4k`@ z#%}Zo{{!Re)GC2!L*5|o3Qm@F%T&(v#HyAZr9+CVwp0a%RQy9cU25s5 z+Si_-rec`C;`-~meQWV+hauMT`nNk;H5K;S{idmv)$Ns6?L|NI+{Y^G5=YZis^2ZG z?v70Iyo|w0tGoA_l~>I!hKtkB^m>KnS+9{t)Ms3Q4h7}+n(PnHZ|H^i9~Y9+UMRl5 z9>Xax=}6kaBGhyb_p%ghQXTsfEVWe=TjZGt}q5TS# z2Bo>h9O{nzG_?H5^SaeTVXWFaJ$oy)kL+Oe k;#?g=A-6W`S9i%MYI&P&QeFEb>YT=6>%eqh~s?%xZbz__-g%2KA*tld49j|^ZWkh!(}VO0^8bm zwHF8kZ52U&;oxrz@Da7P1HbXZr(*>I;kES0XmdD|Dl!=LYE2R^GN&1M5pU6`1p>>N z6?0?eR(5xsSTWdt?r=rW(yQOQ{y4iES6{q2Cg=LX(bu!7-#Xa2o&V-yuZACgEvxAy zQC$u%N;VY=2R*2ENvOTD^YYs3s|RJqJxDuR{%p@T&v#d~_q_V-^sO$>t5zbI^{vn3-6LeJUguDu9eed`fxbBFG8yZLjdLO8SafZn-oHa1H4fhgV8tS8pBnDL zXhJmRwmfg{RC@&$w%I>Darl+?-+jAIJZ8wP-}82zT(@?_nJw!Q=SSmsp*pQc!0f02 z$J85&E^POo7h95YV?*6-uiO|z!rDc|o!eiG#f5W)g+ig&?vpOb30>r8s`dtkEH7Ih zvFZ8SwA`S^EF#;oYa=w+L>9J9oct9?t|mUj-)(1R%VG<7Jqj1 zTvz3?NnPWX8;6gI@YN?uKb=#W-CZWwJ7|LU_OEQe#5@X@MQq5e{NePasWEwu9{2(% zSS5-kHZ7@D4SLjlVdb+<+m2Ru(mh#K8JyqXR$Nh>8K}J)pAft6cFg?zFUFnU_D9yu zJkL`BrTsSUj<}c8xgcl2MQmi%vd;^|vE5zz9Yn&CE#u~qOZzNxnx6GoIi>Z9!T0hi z2CR2Ka{AE4g7iwyBAb?4iC^Z3nRF5&C4vOcPdK@CK3v2WB(4}ZzU6eumHb~+M|PFZ z(cW&C>Uw7{Y!|^u7ShTS)fXMFv61ce&A$7{7+19GuiE~%Si7q4)Au{w8e2b2*v|d> z@*z_@JdOU{P1j}ESzkfh`fWLfz8-bF-C5t^5u1)wkwxY9L+6}3;6#hHeN|neC9Z3F zo(sNLJYsU}dZ@}X&*A8ArNOwV=lHlk*X^IcC)`YS-C_HDlVh>dn&`!yFVFgW#BP_Y zz$If^T--4vt9|S&#lmME`7N*e&+J-1?RlrocPJMJ_c*gaU^7wU>#J1w`o1W6Q0Tdt zv%G@#db{k8izxE%-7dw+`LJ?vpYJErBRWo2x*fOQK+hlc*bUPJg%5LSSw3#O>y&R> zb&8MaHms%3UE#jdyA^-=2S166NN~Qgdu0C$=SQ8}A)GK`mL}?KkEPeQE|^-n&#d-$ z+@3G=F*Ek#(ZZ>N6~Vhq8Rz$wmY4J>tLayFttzVUGhv4Ko}(;ek!7*PcuY>-`E=4? zpQ!Az?7)y;MEBgwJI<>huifZ#_wdFA!AG1YW$({$=nWmVUm4)(<1!FmTr+XYptsh{i@)Yi?@qI^n@19_6JXSJInL8R4(S67LO{$%@kH5q)i*lvZ~QnYsN0J*#_- z6uP+B?oB%wd*70}v1r?vGVF1KO?HBDIe~W=(6M3^p#m8%OUgA3?zluc~&PzN94~0VwzI=ivDBZ|Mq=!Ya>4_}GiTn4n z_q5OeK+BsMkwu%NGtm|=aU(7bo~_GLv8YkRoaiNv4poYL^+sNVNiYcv`CBw8sJNfK z$kWKF>2SY*W(e@jOPpXf8)&ICH8oX|DwF7q@lu4MC@G9eQ4|6akSR@PW-O4-l!Ar ze+mF;Hs~KUOp#!eNyB-QKH13f{wci9?EX3g$G+4zBpZ_&>2R!+PvW&e)C5jN-Z(N) z5vqKtVRb>gMr&x)0?vLz(yURxa@HHRS$7)gylx0ke~J5s^b78d!ayoCl=joJ$yW0e zeqLhh`820zH5}deNa75~qG}w%Nfd!FHNrqD6$lDWF)YTTGKS&N*Ps+SlbO-6ycG(7 zOEdsSMsX}BCmD$3c?QBLRt1q1gF!4ss8lQht2u&v4dNT424p3Z^tx46C=NiOB+lZP zjE5LrPC*!q6A+_P5fI5SFpQxLkC08)a4a35H)AxMlMa2ZUZ1Vy|OisFqXkcn1I1eVB} zaIImX0U6*}#+s)9piu*Gp?!@!W7ZoZ_4*_)vDGAzRkJa@MV?JT3DTH=M4C0@n{z&b zAKCP+sRWWVjawp7W7^UT+jNMDN#VK1gMePs7MsB6;(1Wt%?b5lUGopYQo}e&$zV0a zqZ|t2&B74H$z_llmZ3busbLtjgEtN~>DA^`#>o4`1EzotNT5bGq8^Q|?D?j2Y65Td z6O76r1c4Ad5VjRg5go=Y?B+EeV3KXh z$CnO#cZvu`qaTnWN-(4h#xO{RF$9FkQ5a$iT~Y^~3Q&C=Nylf`L#L#0`UaHAEsD0jY2V%n_C$ zQJ8smJ}-I;ibSF)jmc?L_Teakd8UTtlnlanISvXBmO&JW$RIhchB*RJ1LLT7r}*Lc z$W$0f;hcQnQ9`C>-cMFy5G;paG7`)J9H;UBqUb5L-eX_Pcu(p7+ya^e zUc2~$N!hdpF4W+DE`51HZyuqb@%@`W&6j|G^9YgX;~{V5-;eJ4=&rZ&z*`A_%&w2_ zdMgjSmGH;xdT;KsfAxxw*Ma~0Q^5U;HixvXs}5j zaQM{vu@NlH?F<%MnH8b_t*Y!=I}rHRll()lRIKpxiR@8!_0O}RLLaA=e^s9?EKKyy zm^n@8=DbQfcFXWnuKYJ;!}`LL`7mvlYE zHU=NRe8<(}3-27*-13Ld1^sXMsPos*Y)8R{z@o6T<2ZXdY+F!9ds(@(cLOGHaleQ2H)-r0|F)KExw!+ k0iAm?+_UYB+PLSBgilj;$rN4_cUWmF{KNbTz8*gAKc1GHMF0Q* literal 0 HcmV?d00001 diff --git a/res/content/base/textures/blocks/door_top.png b/res/content/base/textures/blocks/door_top.png new file mode 100644 index 0000000000000000000000000000000000000000..96e2a73d550e68b7ba4e3ec5147e776c5b28435c GIT binary patch literal 6556 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 ZW~O>i#H=uUo4uK(Z0vaN;xRF4{{^IkJFWl# literal 0 HcmV?d00001 diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 958b2f02..91067211 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -31,6 +31,13 @@ static std::shared_ptr