From fb3f201dfc2b9814c65d1fc6c02d5330d604f059 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:32:52 +0300 Subject: [PATCH] move setRotation, checkReplaceability, raycast logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 39 ++-- src/voxels/Chunks.cpp | 229 +--------------------- src/voxels/blocks_agent.cpp | 172 ++++++++++++++++ src/voxels/blocks_agent.hpp | 155 +++++++++++++++ 4 files changed, 359 insertions(+), 236 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 743c042b..c0bf1ef6 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -98,7 +98,9 @@ static int l_set(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!level->chunks->get(x, y, z)) { + int cx = floordiv(x); + int cz = floordiv(z); + if (!blocks_agent::get_chunk(*level->chunksStorage, cx, cz)) { return 0; } blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); @@ -183,7 +185,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - level->chunks->setRotation(x, y, z, value); + blocks_agent::set_rotation(*level->chunksStorage, x, y, z, value); return 0; } @@ -234,7 +236,9 @@ static int l_get_user_bits(lua::State* L) { auto origin = blocks_agent::seek_origin( *level->chunksStorage, {x, y, z}, def, vox->state ); - vox = level->chunks->get(origin.x, origin.y, origin.z); + vox = blocks_agent::get( + *level->chunksStorage, origin.x, origin.y, origin.z + ); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -245,6 +249,7 @@ static int l_get_user_bits(lua::State* L) { } static int l_set_user_bits(lua::State* L) { + auto& chunks = *level->chunksStorage; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -254,18 +259,19 @@ static int l_set_user_bits(lua::State* L) { size_t mask = ((1 << bits) - 1) << offset; auto value = (lua::tointeger(L, 6) << offset) & mask; - auto chunk = level->chunks->getChunkByVoxel(x, y, z); - if (chunk == nullptr) { - return 0; - } - auto vox = level->chunks->get(x, y, z); - if (vox == nullptr) { + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = blocks_agent::get_chunk(chunks, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + auto vox = &chunk->voxels[vox_index(lx, y, lz)]; const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { - auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state); - vox = level->chunks->get(origin); + auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); + vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return 0; } @@ -410,8 +416,15 @@ static int l_raycast(lua::State* L) { glm::vec3 end; glm::ivec3 normal; glm::ivec3 iend; - if (auto voxel = level->chunks->rayCast( - start, dir, maxDistance, end, normal, iend, filteredBlocks + if (auto voxel = blocks_agent::raycast( + *level->chunksStorage, + start, + dir, + maxDistance, + end, + normal, + iend, + filteredBlocks )) { if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) { lua::pushvalue(L, 4); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index a56c3581..8d5231a6 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -181,109 +181,17 @@ bool Chunks::checkReplaceability( const glm::ivec3& origin, blockid_t ignore ) { - const auto& rotation = def.rotations.variants[state.rotation]; - 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++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - if (auto vox = get(pos.x, pos.y, pos.z)) { - auto& target = indices->blocks.require(vox->id); - if (!target.replaceable && vox->id != ignore) { - return false; - } - } else { - return false; - } - } - } - } - return true; + return blocks_agent::check_replaceability(*this, def, state, origin, ignore); } void Chunks::setRotationExtended( const Block& def, blockstate state, const glm::ivec3& origin, uint8_t index ) { - auto newstate = state; - newstate.rotation = index; - - // unable to rotate block (cause: obstacles) - if (!checkReplaceability(def, newstate, origin, def.rt.id)) { - return; - } - - const auto& rotation = def.rotations.variants[index]; - const auto size = def.size; - std::vector segmentBlocks; - - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - - blockstate segState = newstate; - segState.segment = blocks_agent::segment_to_int(sx, sy, sz); - - auto vox = get(pos); - // checked for nullptr by checkReplaceability - if (vox->id != def.rt.id) { - set(pos.x, pos.y, pos.z, def.rt.id, segState); - } else { - vox->state = segState; - auto chunk = getChunkByVoxel(pos.x, pos.y, pos.z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - segmentBlocks.emplace_back(pos); - } - } - } - } - const auto& prevRotation = def.rotations.variants[state.rotation]; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += prevRotation.axisX * sx; - pos += prevRotation.axisY * sy; - pos += prevRotation.axisZ * sz; - if (std::find( - segmentBlocks.begin(), segmentBlocks.end(), pos - ) == segmentBlocks.end()) { - set(pos.x, pos.y, pos.z, 0, {}); - } - } - } - } + blocks_agent::set_rotation_extended(*this, def, state, origin, index); } void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) { - if (index >= BlockRotProfile::MAX_COUNT) { - return; - } - auto vox = get(x, y, z); - if (vox == nullptr) { - return; - } - auto& def = indices->blocks.require(vox->id); - if (!def.rotatable || vox->state.rotation == index) { - return; - } - if (def.rt.extended) { - auto origin = seekOrigin({x, y, z}, def, vox->state); - vox = get(origin); - setRotationExtended(def, vox->state, origin, index); - } else { - vox->state.rotation = index; - auto chunk = getChunkByVoxel(x, y, z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - } + return blocks_agent::set_rotation(*this, x, y, z, index); } void Chunks::set( @@ -301,134 +209,9 @@ voxel* Chunks::rayCast( glm::ivec3& iend, std::set filter ) const { - float px = start.x; - float py = start.y; - float pz = start.z; - - float dx = dir.x; - float dy = dir.y; - float dz = dir.z; - - float t = 0.0f; - int ix = std::floor(px); - int iy = std::floor(py); - int iz = std::floor(pz); - - int stepx = (dx > 0.0f) ? 1 : -1; - int stepy = (dy > 0.0f) ? 1 : -1; - int stepz = (dz > 0.0f) ? 1 : -1; - - constexpr float infinity = std::numeric_limits::infinity(); - constexpr float epsilon = 1e-6f; // 0.000001 - float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); - float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); - float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); - - float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); - float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); - float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); - - float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; - float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; - float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; - - int steppedIndex = -1; - - while (t <= maxDist) { - voxel* voxel = get(ix, iy, iz); - if (voxel == nullptr) { - return nullptr; - } - - const auto& def = indices->blocks.require(voxel->id); - if ((filter.empty() && def.selectable) || - (!filter.empty() && filter.find(def.rt.id) == filter.end())) { - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - iend.x = ix; - iend.y = iy; - iend.z = iz; - - if (!def.rt.solid) { - const std::vector& hitboxes = - def.rotatable ? def.rt.hitboxes[voxel->state.rotation] - : def.hitboxes; - - scalar_t distance = maxDist; - Ray ray(start, dir); - - bool hit = false; - - glm::vec3 offset {}; - if (voxel->state.segment) { - offset = seekOrigin(iend, def, voxel->state) - iend; - } - - for (auto box : hitboxes) { - box.a += offset; - box.b += offset; - scalar_t boxDistance; - glm::ivec3 boxNorm; - if (ray.intersectAABB( - iend, box, maxDist, boxNorm, boxDistance - ) > RayRelation::None && - boxDistance < distance) { - hit = true; - distance = boxDistance; - norm = boxNorm; - end = start + (dir * glm::vec3(distance)); - } - } - - if (hit) return voxel; - } else { - iend.x = ix; - iend.y = iy; - iend.z = iz; - - norm.x = norm.y = norm.z = 0; - if (steppedIndex == 0) norm.x = -stepx; - if (steppedIndex == 1) norm.y = -stepy; - if (steppedIndex == 2) norm.z = -stepz; - return voxel; - } - } - if (txMax < tyMax) { - if (txMax < tzMax) { - ix += stepx; - t = txMax; - txMax += txDelta; - steppedIndex = 0; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } else { - if (tyMax < tzMax) { - iy += stepy; - t = tyMax; - tyMax += tyDelta; - steppedIndex = 1; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } - } - iend.x = ix; - iend.y = iy; - iend.z = iz; - - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - norm.x = norm.y = norm.z = 0; - return nullptr; + return blocks_agent::raycast( + *this, start, dir, maxDist, end, norm, iend, std::move(filter) + ); } glm::vec3 Chunks::rayCastToObstacle( diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 53cef38d..ab39ea29 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -1,5 +1,9 @@ #include "blocks_agent.hpp" +#include "maths/rays.hpp" + +#include + using namespace blocks_agent; template @@ -93,3 +97,171 @@ void blocks_agent::set( ) { set_block(chunks, x, y, z, id, state); } + +template +static inline voxel* raycast_blocks( + const Storage& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + const auto& blocks = chunks.getContentIndices().blocks; + float px = start.x; + float py = start.y; + float pz = start.z; + + float dx = dir.x; + float dy = dir.y; + float dz = dir.z; + + float t = 0.0f; + int ix = std::floor(px); + int iy = std::floor(py); + int iz = std::floor(pz); + + int stepx = (dx > 0.0f) ? 1 : -1; + int stepy = (dy > 0.0f) ? 1 : -1; + int stepz = (dz > 0.0f) ? 1 : -1; + + constexpr float infinity = std::numeric_limits::infinity(); + constexpr float epsilon = 1e-6f; // 0.000001 + float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); + float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); + float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); + + float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); + float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); + float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); + + float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; + float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; + float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; + + int steppedIndex = -1; + + while (t <= maxDist) { + voxel* voxel = get(chunks, ix, iy, iz); + if (voxel == nullptr) { + return nullptr; + } + + const auto& def = blocks.require(voxel->id); + if ((filter.empty() && def.selectable) || + (!filter.empty() && filter.find(def.rt.id) == filter.end())) { + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + iend.x = ix; + iend.y = iy; + iend.z = iz; + + if (!def.rt.solid) { + const std::vector& hitboxes = + def.rotatable ? def.rt.hitboxes[voxel->state.rotation] + : def.hitboxes; + + scalar_t distance = maxDist; + Ray ray(start, dir); + + bool hit = false; + + glm::vec3 offset {}; + if (voxel->state.segment) { + offset = seek_origin(chunks, iend, def, voxel->state) - iend; + } + + for (auto box : hitboxes) { + box.a += offset; + box.b += offset; + scalar_t boxDistance; + glm::ivec3 boxNorm; + if (ray.intersectAABB( + iend, box, maxDist, boxNorm, boxDistance + ) > RayRelation::None && + boxDistance < distance) { + hit = true; + distance = boxDistance; + norm = boxNorm; + end = start + (dir * glm::vec3(distance)); + } + } + + if (hit) return voxel; + } else { + iend.x = ix; + iend.y = iy; + iend.z = iz; + + norm.x = norm.y = norm.z = 0; + if (steppedIndex == 0) norm.x = -stepx; + if (steppedIndex == 1) norm.y = -stepy; + if (steppedIndex == 2) norm.z = -stepz; + return voxel; + } + } + if (txMax < tyMax) { + if (txMax < tzMax) { + ix += stepx; + t = txMax; + txMax += txDelta; + steppedIndex = 0; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } else { + if (tyMax < tzMax) { + iy += stepy; + t = tyMax; + tyMax += tyDelta; + steppedIndex = 1; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } + } + iend.x = ix; + iend.y = iy; + iend.z = iz; + + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + norm.x = norm.y = norm.z = 0; + return nullptr; +} + +voxel* blocks_agent::raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} + +voxel* blocks_agent::raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 3ce5b49b..3bad256f 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -10,6 +10,7 @@ #include "content/Content.hpp" #include "maths/voxmaths.hpp" +#include #include #include @@ -160,4 +161,158 @@ inline glm::ivec3 seek_origin( } } +template +inline bool check_replaceability( + const Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + blockid_t ignore +) { + const auto& blocks = chunks.getContentIndices().blocks; + const auto& rotation = def.rotations.variants[state.rotation]; + 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++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + if (auto vox = get(chunks, pos.x, pos.y, pos.z)) { + auto& target = blocks.require(vox->id); + if (!target.replaceable && vox->id != ignore) { + return false; + } + } else { + return false; + } + } + } + } + return true; +} + +/// @brief Set rotation to an extended block +/// @tparam Storage chunks storage +/// @param def block definition +/// @param state current block state +/// @param origin extended block origin +/// @param index target rotation index +template +inline void set_rotation_extended( + Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + uint8_t index +) { + auto newstate = state; + newstate.rotation = index; + + // unable to rotate block (cause: obstacles) + if (!check_replaceability(chunks, def, newstate, origin, def.rt.id)) { + return; + } + + const auto& rotation = def.rotations.variants[index]; + const auto size = def.size; + std::vector segmentBlocks; + + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + + blockstate segState = newstate; + segState.segment = segment_to_int(sx, sy, sz); + + auto vox = get(chunks, pos.x, pos.y, pos.z); + // checked for nullptr by checkReplaceability + if (vox->id != def.rt.id) { + set(chunks, pos.x, pos.y, pos.z, def.rt.id, segState); + } else { + vox->state = segState; + int cx = floordiv(pos.x); + int cz = floordiv(pos.z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + segmentBlocks.emplace_back(pos); + } + } + } + } + const auto& prevRotation = def.rotations.variants[state.rotation]; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += prevRotation.axisX * sx; + pos += prevRotation.axisY * sy; + pos += prevRotation.axisZ * sz; + if (std::find( + segmentBlocks.begin(), segmentBlocks.end(), pos + ) == segmentBlocks.end()) { + set(chunks, pos.x, pos.y, pos.z, 0, {}); + } + } + } + } +} + +template +inline void set_rotation( + Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index +) { + if (index >= BlockRotProfile::MAX_COUNT) { + return; + } + auto vox = get(chunks, x, y, z); + if (vox == nullptr) { + return; + } + const auto& def = chunks.getContentIndices().blocks.require(vox->id); + if (!def.rotatable || vox->state.rotation == index) { + return; + } + if (def.rt.extended) { + auto origin = seek_origin(chunks, {x, y, z}, def, vox->state); + vox = get(chunks, origin.x, origin.y, origin.z); + set_rotation_extended(chunks, def, vox->state, origin, index); + } else { + vox->state.rotation = index; + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + } +} + +voxel* raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + +voxel* raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + } // blocks_agent