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,