diff --git a/res/content/base/blocks/wooden_door.json b/res/content/base/blocks/wooden_door.json new file mode 100644 index 00000000..9a1eb666 --- /dev/null +++ b/res/content/base/blocks/wooden_door.json @@ -0,0 +1,17 @@ +{ + "material": "base:wood", + "texture-faces": [ + "wooden_door_side", + "wooden_door_side", + "wooden_door_top", + "wooden_door_top", + "wooden_door", + "wooden_door" + ], + "light-passing": true, + "sky-light-passing": true, + "size": [1, 2, 1], + "rotation": "pane", + "model": "aabb", + "hitbox": [0.0, 0.0, 0.8, 1.0, 2.0, 0.2] +} diff --git a/res/content/base/content.json b/res/content/base/content.json index ef2f084b..0e6dc07f 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -25,6 +25,7 @@ "pane", "pipe", "lightbulb", - "torch" + "torch", + "wooden_door" ] } \ No newline at end of file diff --git a/res/content/base/preload.json b/res/content/base/preload.json new file mode 100644 index 00000000..801938a6 --- /dev/null +++ b/res/content/base/preload.json @@ -0,0 +1,6 @@ +{ + "sounds": [ + "blocks/door_open", + "blocks/door_close" + ] +} diff --git a/res/content/base/scripts/wooden_door.lua b/res/content/base/scripts/wooden_door.lua new file mode 100644 index 00000000..b922464d --- /dev/null +++ b/res/content/base/scripts/wooden_door.lua @@ -0,0 +1,13 @@ +function on_interact(x, y, z) + local inc = 1 + if block.get_user_bits(x, y, z, 0, 1) > 0 then + inc = 3 + block.set_user_bits(x, y, z, 0, 1, 0) + audio.play_sound("blocks/door_close", x+0.5, y+1, z+0.5, 1, 1) + else + block.set_user_bits(x, y, z, 0, 1, 1) + audio.play_sound("blocks/door_open", x+0.5, y+1, z+0.5, 1, 1) + end + block.set_rotation(x, y, z, (block.get_rotation(x, y, z) + inc) % 4) + return true +end diff --git a/res/content/base/sounds/blocks/door_close.ogg b/res/content/base/sounds/blocks/door_close.ogg new file mode 100644 index 00000000..53ead574 Binary files /dev/null and b/res/content/base/sounds/blocks/door_close.ogg differ diff --git a/res/content/base/sounds/blocks/door_open.ogg b/res/content/base/sounds/blocks/door_open.ogg new file mode 100644 index 00000000..77aadf80 Binary files /dev/null and b/res/content/base/sounds/blocks/door_open.ogg differ diff --git a/res/content/base/textures/blocks/wooden_door.png b/res/content/base/textures/blocks/wooden_door.png new file mode 100644 index 00000000..ad651ac6 Binary files /dev/null and b/res/content/base/textures/blocks/wooden_door.png differ diff --git a/res/content/base/textures/blocks/wooden_door_side.png b/res/content/base/textures/blocks/wooden_door_side.png new file mode 100644 index 00000000..9201f08c Binary files /dev/null and b/res/content/base/textures/blocks/wooden_door_side.png differ diff --git a/res/content/base/textures/blocks/wooden_door_top.png b/res/content/base/textures/blocks/wooden_door_top.png new file mode 100644 index 00000000..96e2a73d Binary files /dev/null and b/res/content/base/textures/blocks/wooden_door_top.png differ 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..68cb44d7 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -137,16 +137,14 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat def.model = BlockModel::custom; if (root->has("model-primitives")) { loadCustomBlockModel(def, root->map("model-primitives")); - } - else { - std::cerr << "ERROR occured while block " - << name << " parsed: no \"model-primitives\" found" << std::endl; + } else { + logger.error() << name << ": no 'model-primitives' found"; } } else if (model == "X") def.model = BlockModel::xsprite; else if (model == "none") def.model = BlockModel::none; else { - std::cerr << "unknown model " << model << std::endl; + logger.error() << "unknown model " << model; def.model = BlockModel::none; } @@ -156,12 +154,12 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat std::string profile = "none"; root->str("rotation", profile); def.rotatable = profile != "none"; - if (profile == "pipe") { + if (profile == BlockRotProfile::PIPE_NAME) { def.rotations = BlockRotProfile::PIPE; - } else if (profile == "pane") { + } else if (profile == BlockRotProfile::PANE_NAME) { def.rotations = BlockRotProfile::PANE; } else if (profile != "none") { - std::cerr << "unknown rotation profile " << profile << std::endl; + logger.error() << "unknown rotation profile " << profile; def.rotatable = false; } @@ -175,29 +173,37 @@ 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); @@ -254,8 +260,8 @@ void ContentLoader::loadCustomBlockModel(Block& def, dynamic::Map* primitives) { /* Parse tetragon to points */ auto tgonobj = modeltetragons->list(i); glm::vec3 p1(tgonobj->num(0), tgonobj->num(1), tgonobj->num(2)), - xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)), - yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8)); + xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)), + yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8)); def.modelExtraPoints.push_back(p1); def.modelExtraPoints.push_back(p1+xw); def.modelExtraPoints.push_back(p1+xw+yh); @@ -279,7 +285,7 @@ void ContentLoader::loadItem(ItemDef& def, const std::string& name, const fs::pa } else if (iconTypeStr == "sprite") { def.iconType = item_icon_type::sprite; } else if (iconTypeStr.length()){ - std::cerr << "unknown icon type" << iconTypeStr << std::endl; + logger.error() << name << ": unknown icon type" << iconTypeStr; } root->str("icon", def.icon); root->str("placing-block", def.placingBlock); diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index a278b3e3..24caea8d 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -75,20 +75,21 @@ std::shared_ptr create_debug_panel( L" visible: "+std::to_wstring(level->chunks->visible); })); panel->add(create_label([=](){ + const auto& vox = player->selection.vox; std::wstringstream stream; - stream << "r:" << player->selectedVoxel.state.rotation << " s:" - << player->selectedVoxel.state.segment << " u:" - << std::bitset<8>(player->selectedVoxel.state.userbits); - if (player->selectedVoxel.id == BLOCK_VOID) { + stream << "r:" << vox.state.rotation << " s:" + << std::bitset<3>(vox.state.segment) << " u:" + << std::bitset<8>(vox.state.userbits); + if (vox.id == BLOCK_VOID) { return std::wstring {L"block: -"}; } else { - return L"block: "+std::to_wstring(player->selectedVoxel.id)+ + return L"block: "+std::to_wstring(vox.id)+ L" "+stream.str(); } })); panel->add(create_label([=](){ auto* indices = level->content->getIndices(); - if (auto def = indices->getBlockDef(player->selectedVoxel.id)) { + if (auto def = indices->getBlockDef(player->selection.vox.id)) { return L"name: " + util::str2wstr_utf8(def->name); } else { return std::wstring {L"name: void"}; diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index c68bae97..378dc4e7 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -27,6 +27,8 @@ BlocksRenderer::BlocksRenderer( const ContentGfxCache* cache, const EngineSettings* settings ) : content(content), + vertexBuffer(std::make_unique(capacity)), + indexBuffer(std::make_unique(capacity)), vertexOffset(0), indexOffset(0), indexSize(0), @@ -34,16 +36,14 @@ BlocksRenderer::BlocksRenderer( cache(cache), settings(settings) { - vertexBuffer = new float[capacity]; - indexBuffer = new int[capacity]; - voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2); + voxelsBuffer = std::make_unique( + CHUNK_W + voxelBufferPadding*2, + CHUNK_H, + CHUNK_D + voxelBufferPadding*2); blockDefsCache = content->getIndices()->getBlockDefs(); } BlocksRenderer::~BlocksRenderer() { - delete voxelsBuffer; - delete[] vertexBuffer; - delete[] indexBuffer; } /* Basic vertex add method */ @@ -60,10 +60,10 @@ void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& lig uint32_t integer; } compressed; - compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24; - compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16; - compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8; - compressed.integer |= (uint32_t(light.a * 255) & 0xff); + compressed.integer = (static_cast(light.r * 255) & 0xff) << 24; + compressed.integer |= (static_cast(light.g * 255) & 0xff) << 16; + compressed.integer |= (static_cast(light.b * 255) & 0xff) << 8; + compressed.integer |= (static_cast(light.a * 255) & 0xff); vertexBuffer[vertexOffset++] = compressed.floating; } @@ -78,15 +78,17 @@ void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) { indexOffset += 4; } -/* Add face with precalculated lights */ -void BlocksRenderer::face(const vec3& coord, - float w, float h, float d, - const vec3& axisX, - const vec3& axisY, - const vec3& axisZ, - const UVRegion& region, - const vec4(&lights)[4], - const vec4& tint) { +/// @brief Add face with precalculated lights +void BlocksRenderer::face( + const vec3& coord, + float w, float h, float d, + const vec3& axisX, + const vec3& axisY, + const vec3& axisZ, + const UVRegion& region, + const vec4(&lights)[4], + const vec4& tint +) { if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { overflow = true; return; @@ -102,23 +104,27 @@ void BlocksRenderer::face(const vec3& coord, index(0, 1, 3, 1, 2, 3); } -void BlocksRenderer::vertex(const vec3& coord, - float u, float v, - const vec4& tint, - const vec3& axisX, - const vec3& axisY, - const vec3& axisZ) { +void BlocksRenderer::vertex( + const vec3& coord, + float u, float v, + const vec4& tint, + const vec3& axisX, + const vec3& axisY, + const vec3& axisZ +) { vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f; vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY); vertex(coord, u, v, light * tint); } -void BlocksRenderer::face(const vec3& coord, - const vec3& X, - const vec3& Y, - const vec3& Z, - const UVRegion& region, - bool lights) { +void BlocksRenderer::face( + const vec3& coord, + const vec3& X, + const vec3& Y, + const vec3& Z, + const UVRegion& region, + bool lights +) { if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { overflow = true; return; @@ -148,14 +154,13 @@ void BlocksRenderer::face(const vec3& coord, index(0, 1, 2, 0, 2, 3); } -void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1, - const vec3& p2, const vec3& p3, const vec3& p4, - const vec3& X, - const vec3& Y, - const vec3& Z, - const UVRegion& texreg, - bool lights) { - +void BlocksRenderer::tetragonicFace( + const vec3& coord, + const vec3& p1, const vec3& p2, const vec3& p3, const vec3& p4, + const vec3& X, const vec3& Y, const vec3& Z, + const UVRegion& texreg, + bool lights +) { const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z; const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z; const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z; @@ -182,17 +187,19 @@ void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1, index(0, 1, 3, 1, 2, 3); } -void BlocksRenderer::blockXSprite(int x, int y, int z, - const vec3& size, - const UVRegion& texface1, - const UVRegion& texface2, - float spread) { - vec4 lights[]{ - pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) }; - +void BlocksRenderer::blockXSprite( + int x, int y, int z, + const vec3& size, + const UVRegion& texface1, + const UVRegion& texface2, + float spread +) { + vec4 lights[] { + pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) + }; int rand = ((x * z + y) ^ (z * y - x)) * (z + y); float xs = ((float)(char)rand / 512) * spread; @@ -218,7 +225,7 @@ void BlocksRenderer::blockXSprite(int x, int y, int z, // HINT: texture faces order: {east, west, bottom, top, south, north} -/* AABB blocks render method */ +/// @brief AABB blocks render method void BlocksRenderer::blockAABB( const ivec3& icoord, const UVRegion(&texfaces)[6], @@ -259,8 +266,9 @@ void BlocksRenderer::blockAABB( face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east } -void BlocksRenderer::blockCustomModel(const ivec3& icoord, - const Block* block, ubyte rotation, bool lights) { +void BlocksRenderer::blockCustomModel( + const ivec3& icoord, const Block* block, ubyte rotation, bool lights +) { vec3 X(1, 0, 0); vec3 Y(0, 1, 0); vec3 Z(0, 0, 1); @@ -347,8 +355,9 @@ bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) const { blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z); - if (id == BLOCK_VOID) + if (id == BLOCK_VOID) { return false; + } const Block& block = *blockDefsCache[id]; if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) { return true; @@ -360,8 +369,9 @@ bool BlocksRenderer::isOpenForLight(int x, int y, int z) const { blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z); - if (id == BLOCK_VOID) + if (id == BLOCK_VOID) { return false; + } const Block& block = *blockDefsCache[id]; if (block.lightPassing) { return true; @@ -371,15 +381,13 @@ bool BlocksRenderer::isOpenForLight(int x, int y, int z) const { vec4 BlocksRenderer::pickLight(int x, int y, int z) const { if (isOpenForLight(x, y, z)) { - light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, - y, + light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z); return vec4(Lightmap::extract(light, 0) / 15.0f, - Lightmap::extract(light, 1) / 15.0f, - Lightmap::extract(light, 2) / 15.0f, - Lightmap::extract(light, 3) / 15.0f); - } - else { + Lightmap::extract(light, 1) / 15.0f, + Lightmap::extract(light, 2) / 15.0f, + Lightmap::extract(light, 3) / 15.0f); + } else { return vec4(0.0f); } } @@ -391,17 +399,20 @@ vec4 BlocksRenderer::pickLight(const ivec3& coord) const { vec4 BlocksRenderer::pickSoftLight(const ivec3& coord, const ivec3& right, const ivec3& up) const { - return ( - pickLight(coord) + - pickLight(coord - right) + - pickLight(coord - right - up) + - pickLight(coord - up)) * 0.25f; + return (pickLight(coord) + + pickLight(coord - right) + + pickLight(coord - right - up) + + pickLight(coord - up)) * 0.25f; } vec4 BlocksRenderer::pickSoftLight(float x, float y, float z, - const ivec3& right, - const ivec3& up) const { - return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up); + const ivec3& right, + const ivec3& up) const { + return pickSoftLight({ + static_cast(round(x)), + static_cast(round(y)), + static_cast(round(z))}, + right, up); } void BlocksRenderer::render(const voxel* voxels) { @@ -411,48 +422,55 @@ 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), - cache->getRegion(id, 3), - cache->getRegion(id, 4), - cache->getRegion(id, 5)}; + } + const UVRegion texfaces[6] { + cache->getRegion(id, 0), + cache->getRegion(id, 1), + cache->getRegion(id, 2), + cache->getRegion(id, 3), + cache->getRegion(id, 4), + cache->getRegion(id, 5) + }; int x = i % CHUNK_W; int y = i / (CHUNK_D * CHUNK_W); int z = (i / CHUNK_D) % CHUNK_W; switch (def.model) { - case BlockModel::block: - blockCube(x, y, z, texfaces, &def, vox.state, !def.rt.emissive); - break; - case BlockModel::xsprite: { - blockXSprite(x, y, z, vec3(1.0f), - texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); - break; + case BlockModel::block: + blockCube(x, y, z, texfaces, &def, vox.state, !def.rt.emissive); + break; + case BlockModel::xsprite: { + blockXSprite(x, y, z, vec3(1.0f), + texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); + break; + } + case BlockModel::aabb: { + blockAABB(ivec3(x,y,z), texfaces, &def, vox.state.rotation, !def.rt.emissive); + break; + } + case BlockModel::custom: { + blockCustomModel(ivec3(x, y, z), &def, vox.state.rotation, !def.rt.emissive); + break; + } + default: + break; } - case BlockModel::aabb: { - blockAABB(ivec3(x,y,z), texfaces, &def, vox.state.rotation, !def.rt.emissive); - break; - } - case BlockModel::custom: { - blockCustomModel(ivec3(x, y, z), &def, vox.state.rotation, !def.rt.emissive); - break; - } - default: - break; - } - if (overflow) + if (overflow) { return; + } } } } void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) { this->chunk = chunk; - voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1); - chunks->getVoxels(voxelsBuffer, settings->graphics.backlight.get()); + voxelsBuffer->setPosition( + chunk->x * CHUNK_W - voxelBufferPadding, 0, + chunk->z * CHUNK_D - voxelBufferPadding); + chunks->getVoxels(voxelsBuffer.get(), settings->graphics.backlight.get()); overflow = false; vertexOffset = 0; indexOffset = indexSize = 0; @@ -463,7 +481,9 @@ void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) { std::shared_ptr BlocksRenderer::createMesh() { const vattr attrs[]{ {3}, {2}, {1}, {0} }; size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; - return std::make_shared(vertexBuffer, vcount, indexBuffer, indexSize, attrs); + return std::make_shared( + vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs + ); } std::shared_ptr BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) { @@ -472,5 +492,5 @@ std::shared_ptr BlocksRenderer::render(const Chunk* chunk, const ChunksSto } VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { - return voxelsBuffer; + return voxelsBuffer.get(); } diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 244d4674..e31fd163 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "../../voxels/voxel.hpp" #include "../../typedefs.hpp" @@ -22,16 +23,15 @@ class BlocksRenderer { static const glm::vec3 SUN_VECTOR; static const uint VERTEX_SIZE; const Content* const content; - float* vertexBuffer; - int* indexBuffer; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; size_t vertexOffset; size_t indexOffset, indexSize; size_t capacity; - + int voxelBufferPadding = 2; bool overflow = false; - const Chunk* chunk = nullptr; - VoxelsVolume* voxelsBuffer; + std::unique_ptr voxelsBuffer; const Block* const* blockDefsCache; const ContentGfxCache* const cache; @@ -40,36 +40,41 @@ class BlocksRenderer { void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light); void index(int a, int b, int c, int d, int e, int f); - void vertex(const glm::vec3& coord, float u, float v, - const glm::vec4& brightness, - const glm::vec3& axisX, - const glm::vec3& axisY, - const glm::vec3& axisZ); - - void face(const glm::vec3& coord, float w, float h, float d, + void vertex( + const glm::vec3& coord, float u, float v, + const glm::vec4& brightness, + const glm::vec3& axisX, + const glm::vec3& axisY, + const glm::vec3& axisZ + ); + void face( + const glm::vec3& coord, + float w, float h, float d, const glm::vec3& axisX, const glm::vec3& axisY, const glm::vec3& axisZ, const UVRegion& region, const glm::vec4(&lights)[4], - const glm::vec4& tint); - - void face(const glm::vec3& coord, + const glm::vec4& tint + ); + void face( + const glm::vec3& coord, const glm::vec3& axisX, const glm::vec3& axisY, const glm::vec3& axisZ, const UVRegion& region, - bool lights); - - void tetragonicFace(const glm::vec3& coord, + bool lights + ); + void tetragonicFace( + const glm::vec3& coord, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3, const glm::vec3& p4, const glm::vec3& X, const glm::vec3& Y, const glm::vec3& Z, const UVRegion& texreg, - bool lights); - + bool lights + ); void blockCube( int x, int y, int z, const UVRegion(&faces)[6], diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 8d02a750..795e4c0d 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -195,15 +195,16 @@ void WorldRenderer::renderLevel( } void WorldRenderer::renderBlockSelection(Camera* camera, Shader* linesShader) { + const auto& selection = player->selection; auto indices = level->content->getIndices(); - blockid_t id = PlayerController::selectedBlockId; + blockid_t id = selection.vox.id; auto block = indices->getBlockDef(id); - const glm::ivec3 pos = player->selectedBlockPosition; - const glm::vec3 point = PlayerController::selectedPointPosition; - const glm::vec3 norm = PlayerController::selectedBlockNormal; + const glm::ivec3 pos = player->selection.position; + const glm::vec3 point = selection.hitPosition; + const glm::vec3 norm = selection.normal; const std::vector& hitboxes = block->rotatable - ? block->rt.hitboxes[PlayerController::selectedBlockRotation] + ? block->rt.hitboxes[selection.vox.state.rotation] : block->hitboxes; linesShader->use(); @@ -305,7 +306,7 @@ void WorldRenderer::draw( ctx.setCullFace(true); renderLevel(ctx, camera, settings); // Selected block - if (PlayerController::selectedBlockId != -1 && hudVisible){ + if (player->selection.vox.id != BLOCK_VOID && hudVisible){ renderBlockSelection(camera, linesShader); } } 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 a9fcdbb5..583be194 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -170,11 +170,6 @@ void CameraControl::update(const PlayerInput& input, float delta, Chunks* chunks } } -glm::vec3 PlayerController::selectedPointPosition; -glm::ivec3 PlayerController::selectedBlockNormal; -int PlayerController::selectedBlockId = -1; -int PlayerController::selectedBlockRotation = 0; - PlayerController::PlayerController( Level* level, const EngineSettings& settings, @@ -252,8 +247,8 @@ void PlayerController::update(float delta, bool input, bool pause) { if (input) { updateInteraction(); } else { - selectedBlockId = -1; - selectedBlockRotation = 0; + player->selection.vox.id = BLOCK_VOID; + player->selection.vox.state.rotation = 0; } } @@ -295,7 +290,7 @@ void PlayerController::updateControls(float delta){ player->updateInput(level, input, delta); } -static int determine_rotation(Block* def, glm::ivec3& norm, glm::vec3& camDir) { +static int determine_rotation(Block* def, const glm::ivec3& norm, glm::vec3& camDir) { if (def && def->rotatable){ const std::string& name = def->rotations.name; if (name == "pipe") { @@ -336,25 +331,11 @@ static void pick_block(ContentIndices* indices, Chunks* chunks, Player* player, } } -// TODO: refactor this nesting nest -void PlayerController::updateInteraction(){ +voxel* PlayerController::updateSelection(float maxDistance) { auto indices = level->content->getIndices(); - Chunks* chunks = level->chunks.get(); - Lighting* lighting = level->lighting.get(); - Camera* camera = player->camera.get(); - - bool xkey = Events::pressed(keycode::X); - bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || - (xkey && Events::active(BIND_PLAYER_ATTACK)); - bool rclick = Events::jactive(BIND_PLAYER_BUILD) || - (xkey && Events::active(BIND_PLAYER_BUILD)); - float maxDistance = 10.0f; - if (xkey) { - maxDistance *= 20.0f; - } - auto inventory = player->getInventory(); - const ItemStack& stack = inventory->getSlot(player->getChosenSlot()); - ItemDef* item = indices->getItemDef(stack.getItemId()); + auto chunks = level->chunks.get(); + auto camera = player->camera.get(); + auto& selection = player->selection; glm::vec3 end; glm::ivec3 iend; @@ -365,95 +346,134 @@ void PlayerController::updateInteraction(){ maxDistance, end, norm, iend ); - if (vox != nullptr) { - player->selectedVoxel = *vox; - selectedBlockId = vox->id; - selectedBlockRotation = vox->state.rotation; - player->selectedBlockPosition = iend; - selectedPointPosition = end; - selectedBlockNormal = norm; - int x = iend.x; - int y = iend.y; - int z = iend.z; - - Block* def = indices->getBlockDef(item->rt.placingBlock); - blockstate state {}; - 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)) - return; - } - - Block* target = indices->getBlockDef(vox->id); - if (lclick && target->breakable){ - onBlockInteraction( - glm::ivec3(x, y, z), target, - BlockInteraction::destruction - ); - blocksController->breakBlock(player.get(), target, x, y, z); - } - if (rclick && !input.shift) { - bool preventDefault = false; - if (item->rt.funcsset.on_use_on_block) { - preventDefault = scripting::on_item_use_on_block(player.get(), item, x, y, z); - } else if (item->rt.funcsset.on_use) { - preventDefault = scripting::on_item_use(player.get(), item); - } - if (preventDefault) { - return; - } - } - if (def && rclick){ - if (!input.shift && target->rt.funcsset.oninteract) { - if (scripting::on_block_interact(player.get(), target, x, y, z)) - return; - } - if (!target->replaceable){ - 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; - } - } - vox = chunks->get(x, y, z); - blockid_t chosenBlock = def->rt.id; - if (vox && (target = indices->getBlockDef(vox->id))->replaceable) { - if (!level->physics->isBlockInside(x,y,z,def,state, player->hitbox.get()) - || !def->obstacle){ - if (def->grounded && !chunks->isSolidBlock(x, y-1, z)) { - chosenBlock = 0; - } - if (chosenBlock != vox->id && chosenBlock) { - onBlockInteraction( - glm::ivec3(x, y, z), def, - BlockInteraction::placing - ); - chunks->set(x, y, z, chosenBlock, state); - lighting->onBlockSet(x,y,z, chosenBlock); - if (def->rt.funcsset.onplaced) { - scripting::on_block_placed(player.get(), def, x, y, z); - } - blocksController->updateSides(x, y, z); - } - } - } - } - if (Events::jactive(BIND_PLAYER_PICK)) { - pick_block(indices, chunks, player.get(), x, y, z); + if (vox == nullptr) { + selection.vox = {BLOCK_VOID, {}}; + return nullptr; + } + blockstate selectedState = vox->state; + selection.vox = *vox; + selection.actualPosition = iend; + if (selectedState.segment) { + selection.position = chunks->seekOrigin( + iend, indices->getBlockDef(selection.vox.id), selectedState + ); + auto origin = chunks->get(iend); + if (origin && origin->id != vox->id) { + chunks->set(iend.x, iend.y, iend.z, 0, {}); + return updateSelection(maxDistance); } } else { - selectedBlockId = -1; - selectedBlockRotation = 0; - player->selectedVoxel.id = BLOCK_VOID; - if (rclick) { - if (item->rt.funcsset.on_use) { - scripting::on_item_use(player.get(), item); - } + selection.position = iend; + } + selection.hitPosition = end; + selection.normal = norm; + return vox; +} + +void PlayerController::processRightClick(Block* def, Block* target) { + const auto& selection = player->selection; + auto chunks = level->chunks.get(); + auto camera = player->camera.get(); + auto lighting = level->lighting.get(); + + blockstate state {}; + state.rotation = determine_rotation(def, selection.normal, camera->dir); + + if (!input.shift && target->rt.funcsset.oninteract) { + if (scripting::on_block_interact(player.get(), target, selection.position)) { + return; } } + auto coord = selection.actualPosition; + if (!target->replaceable){ + coord += selection.normal; + } else if (def->rotations.name == BlockRotProfile::PIPE_NAME) { + state.rotation = BLOCK_DIR_UP; + } + blockid_t chosenBlock = def->rt.id; + + if (def->obstacle && level->physics->isBlockInside( + coord.x, coord.y, coord.z, def,state, player->hitbox.get())) { + return; + } + auto vox = chunks->get(coord); + if (vox == nullptr) { + return; + } + if (!chunks->checkReplaceability(def, state, coord)) { + return; + } + if (def->grounded && !chunks->isSolidBlock(coord.x, coord.y-1, coord.z)) { + return; + } + if (chosenBlock != vox->id && chosenBlock) { + onBlockInteraction(coord, def, BlockInteraction::placing); + chunks->set(coord.x, coord.y, coord.z, chosenBlock, state); + lighting->onBlockSet(coord.x, coord.y, coord.z, chosenBlock); + if (def->rt.funcsset.onplaced) { + scripting::on_block_placed(player.get(), def, coord.x, coord.y, coord.z); + } + blocksController->updateSides(coord.x, coord.y, coord.z); + } +} + +void PlayerController::updateInteraction() { + auto indices = level->content->getIndices(); + auto chunks = level->chunks.get(); + const auto& selection = player->selection; + + bool xkey = Events::pressed(keycode::X); + bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || (xkey && Events::active(BIND_PLAYER_ATTACK)); + bool rclick = Events::jactive(BIND_PLAYER_BUILD) || (xkey && Events::active(BIND_PLAYER_BUILD)); + float maxDistance = xkey ? 200.0f : 10.0f; + + auto inventory = player->getInventory(); + const ItemStack& stack = inventory->getSlot(player->getChosenSlot()); + ItemDef* item = indices->getItemDef(stack.getItemId()); + + auto vox = updateSelection(maxDistance); + if (vox == nullptr) { + if (rclick && item->rt.funcsset.on_use) { + scripting::on_item_use(player.get(), item); + } + return; + } + + auto iend = selection.position; + if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) { + if (scripting::on_item_break_block(player.get(), item, iend.x, iend.y, iend.z)) { + return; + } + } + auto target = indices->getBlockDef(vox->id); + if (lclick && target->breakable){ + onBlockInteraction( + iend, target, + BlockInteraction::destruction + ); + blocksController->breakBlock(player.get(), target, iend.x, iend.y, iend.z); + } + if (rclick && !input.shift) { + bool preventDefault = false; + if (item->rt.funcsset.on_use_on_block) { + preventDefault = scripting::on_item_use_on_block( + player.get(), item, iend.x, iend.y, iend.z + ); + } else if (item->rt.funcsset.on_use) { + preventDefault = scripting::on_item_use(player.get(), item); + } + if (preventDefault) { + return; + } + } + auto def = indices->getBlockDef(item->rt.placingBlock); + if (def && rclick) { + processRightClick(def, target); + } + if (Events::jactive(BIND_PLAYER_PICK)) { + auto coord = selection.actualPosition; + pick_block(indices, chunks, player.get(), coord.x, coord.y, coord.z); + } } Player* PlayerController::getPlayer() { diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp index 76d04b79..e1a09b99 100644 --- a/src/logic/PlayerController.hpp +++ b/src/logic/PlayerController.hpp @@ -76,12 +76,10 @@ class PlayerController { float stepsTimer = 0.0f; void onFootstep(); void updateFootsteps(float delta); -public: - static glm::ivec3 selectedBlockNormal; - static glm::vec3 selectedPointPosition; - static int selectedBlockId; - static int selectedBlockRotation; + void processRightClick(Block* def, Block* target); + voxel* updateSelection(float maxDistance); +public: PlayerController( Level* level, const EngineSettings& settings, diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp index 22e76b84..8bdafb22 100644 --- a/src/logic/scripting/lua/libblock.cpp +++ b/src/logic/scripting/lua/libblock.cpp @@ -13,29 +13,32 @@ #include "../../../content/Content.hpp" #include "../../../logic/BlocksController.hpp" -int l_block_name(lua_State* L) { +static Block* require_block(lua_State* L) { auto indices = scripting::content->getIndices(); lua_Integer id = lua_tointeger(L, 1); if (static_cast(id) >= indices->countBlockDefs()) { - return 0; + return nullptr; } - auto def = indices->getBlockDef(id); - lua_pushstring(L, def->name.c_str()); - return 1; + return indices->getBlockDef(id); } -int l_block_material(lua_State* L) { - auto indices = scripting::content->getIndices(); - lua_Integer id = lua_tointeger(L, 1); - if (static_cast(id) >= indices->countBlockDefs()) { - return 0; +static int l_name(lua_State* L) { + if (auto def = require_block(L)) { + lua_pushstring(L, def->name.c_str()); + return 1; } - auto def = indices->getBlockDef(id); - lua_pushstring(L, def->material.c_str()); - return 1; + return 0; } -int l_is_solid_at(lua_State* L) { +static int l_material(lua_State* L) { + if (auto def = require_block(L)) { + lua_pushstring(L, def->material.c_str()); + return 1; + } + return 0; +} + +static int l_is_solid_at(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -44,18 +47,53 @@ int l_is_solid_at(lua_State* L) { return 1; } -int l_blocks_count(lua_State* L) { +static int l_count(lua_State* L) { lua_pushinteger(L, scripting::indices->countBlockDefs()); return 1; } -int l_block_index(lua_State* L) { +static int l_index(lua_State* L) { std::string name = lua_tostring(L, 1); lua_pushinteger(L, scripting::content->requireBlock(name).rt.id); return 1; } -int l_set_block(lua_State* L) { +static int l_is_extended(lua_State* L) { + if (auto def = require_block(L)) { + lua_pushboolean(L, def->rt.extended); + return 1; + } + return 0; +} + +static int l_get_size(lua_State* L) { + if (auto def = require_block(L)) { + return lua::pushivec3(L, def->size.x, def->size.y, def->size.z); + } + return 0; +} + +static int l_is_segment(lua_State* L) { + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); + auto vox = scripting::level->chunks->get(x, y, z); + + lua_pushboolean(L, vox->state.segment); + return 1; +} + +static int l_seek_origin(lua_State* L) { + lua_Integer x = lua_tointeger(L, 1); + lua_Integer y = lua_tointeger(L, 2); + lua_Integer z = lua_tointeger(L, 3); + auto vox = scripting::level->chunks->get(x, y, z); + auto def = scripting::indices->getBlockDef(vox->id); + + return lua::pushivec3(L, scripting::level->chunks->seekOrigin({x, y, z}, def, vox->state)); +} + +static int l_set(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -76,7 +114,7 @@ int l_set_block(lua_State* L) { return 0; } -int l_get_block(lua_State* L) { +static int l_get(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -86,7 +124,7 @@ int l_get_block(lua_State* L) { return 1; } -int l_get_block_x(lua_State* L) { +static int l_get_x(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -103,7 +141,7 @@ int l_get_block_x(lua_State* L) { } } -int l_get_block_y(lua_State* L) { +static int l_get_y(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -120,7 +158,7 @@ int l_get_block_y(lua_State* L) { } } -int l_get_block_z(lua_State* L) { +static int l_get_z(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -137,7 +175,7 @@ int l_get_block_z(lua_State* L) { } } -int l_get_block_rotation(lua_State* L) { +static int l_get_rotation(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -147,21 +185,16 @@ int l_get_block_rotation(lua_State* L) { return 1; } -int l_set_block_rotation(lua_State* L) { +static int l_set_rotation(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); lua_Integer value = lua_tointeger(L, 4); - voxel* vox = scripting::level->chunks->get(x, y, z); - if (vox == nullptr) { - return 0; - } - vox->state.rotation = value; - scripting::level->chunks->getChunkByVoxel(x, y, z)->setModifiedAndUnsaved(); + scripting::level->chunks->setRotation(x, y, z, value); return 0; } -int l_get_block_states(lua_State* L) { +static int l_get_states(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -171,7 +204,7 @@ int l_get_block_states(lua_State* L) { return 1; } -int l_set_block_states(lua_State* L) { +static int l_set_states(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -187,7 +220,7 @@ int l_set_block_states(lua_State* L) { return 0; } -int l_get_block_user_bits(lua_State* L) { +static int l_get_user_bits(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -205,7 +238,7 @@ int l_get_block_user_bits(lua_State* L) { return 1; } -int l_set_block_user_bits(lua_State* L) { +static int l_set_user_bits(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -228,7 +261,7 @@ int l_set_block_user_bits(lua_State* L) { return 0; } -int l_is_replaceable_at(lua_State* L) { +static int l_is_replaceable_at(lua_State* L) { lua_Integer x = lua_tointeger(L, 1); lua_Integer y = lua_tointeger(L, 2); lua_Integer z = lua_tointeger(L, 3); @@ -237,35 +270,36 @@ int l_is_replaceable_at(lua_State* L) { return 1; } -int l_block_caption(lua_State* L) { - auto indices = scripting::content->getIndices(); - lua_Integer id = lua_tointeger(L, 1); - if (static_cast(id) >= indices->countBlockDefs()) { - return 0; +static int l_caption(lua_State* L) { + if (auto def = require_block(L)) { + lua_pushstring(L, def->caption.c_str()); + return 1; } - auto def = indices->getBlockDef(id); - lua_pushstring(L, def->caption.c_str()); - return 1; + return 0; } const luaL_Reg blocklib [] = { - {"index", lua_wrap_errors}, - {"name", lua_wrap_errors}, - {"material", lua_wrap_errors}, - {"caption", lua_wrap_errors}, - {"defs_count", lua_wrap_errors}, + {"index", lua_wrap_errors}, + {"name", lua_wrap_errors}, + {"material", lua_wrap_errors}, + {"caption", lua_wrap_errors}, + {"defs_count", lua_wrap_errors}, {"is_solid_at", lua_wrap_errors}, {"is_replaceable_at", lua_wrap_errors}, - {"set", lua_wrap_errors}, - {"get", lua_wrap_errors}, - {"get_X", lua_wrap_errors}, - {"get_Y", lua_wrap_errors}, - {"get_Z", lua_wrap_errors}, - {"get_states", lua_wrap_errors}, - {"set_states", lua_wrap_errors}, - {"get_rotation", lua_wrap_errors}, - {"set_rotation", lua_wrap_errors}, - {"get_user_bits", lua_wrap_errors}, - {"set_user_bits", lua_wrap_errors}, + {"set", lua_wrap_errors}, + {"get", lua_wrap_errors}, + {"get_X", lua_wrap_errors}, + {"get_Y", lua_wrap_errors}, + {"get_Z", lua_wrap_errors}, + {"get_states", lua_wrap_errors}, + {"set_states", lua_wrap_errors}, + {"get_rotation", lua_wrap_errors}, + {"set_rotation", lua_wrap_errors}, + {"get_user_bits", lua_wrap_errors}, + {"set_user_bits", lua_wrap_errors}, + {"is_extended", lua_wrap_errors}, + {"get_size", lua_wrap_errors}, + {"is_segment", lua_wrap_errors}, + {"seek_origin", lua_wrap_errors}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/libplayer.cpp b/src/logic/scripting/lua/libplayer.cpp index 045aae37..fde82362 100644 --- a/src/logic/scripting/lua/libplayer.cpp +++ b/src/logic/scripting/lua/libplayer.cpp @@ -134,10 +134,10 @@ static int l_player_set_noclip(lua_State* L) { static int l_player_get_selected_block(lua_State* L) { if (auto player = get_player(L, 1)) { - if (player->selectedVoxel.id == BLOCK_VOID) { + if (player->selection.vox.id == BLOCK_VOID) { return 0; } - const glm::ivec3 pos = player->selectedBlockPosition; + const glm::ivec3 pos = player->selection.position; lua_pushinteger(L, pos.x); lua_pushinteger(L, pos.y); lua_pushinteger(L, pos.z); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 0c4de9f3..799bd3d4 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -21,6 +21,13 @@ namespace lua { return 3; } + inline int pushivec3(lua_State* L, glm::ivec3 vec) { + lua_pushinteger(L, vec.x); + lua_pushinteger(L, vec.y); + lua_pushinteger(L, vec.z); + return 3; + } + inline int pushvec3(lua_State* L, glm::vec3 vec) { lua_pushnumber(L, vec.x); lua_pushnumber(L, vec.y); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index c24d9860..493b6f91 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -192,10 +192,10 @@ void scripting::on_block_broken(Player* player, const Block* block, int x, int y }); } -bool scripting::on_block_interact(Player* player, const Block* block, int x, int y, int z) { +bool scripting::on_block_interact(Player* player, const Block* block, glm::ivec3 pos) { std::string name = block->name + ".interact"; - return state->emit_event(name, [x, y, z, player] (lua::LuaState* state) { - state->pushivec3(x, y, z); + return state->emit_event(name, [pos, player] (lua::LuaState* state) { + state->pushivec3(pos.x, pos.y, pos.z); state->pushinteger(player->getId()); return 4; }); diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 14e678d4..6b7f9b3d 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -58,7 +58,7 @@ namespace scripting { void random_update_block(const Block* block, int x, int y, int z); void on_block_placed(Player* player, const Block* block, int x, int y, int z); void on_block_broken(Player* player, const Block* block, int x, int y, int z); - bool on_block_interact(Player* player, const Block* block, int x, int y, int z); + bool on_block_interact(Player* player, const Block* block, glm::ivec3 pos); /// @brief Called on RMB click with the item selected /// @return true if prevents default action diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index 81ba856b..40f57ccc 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -3,22 +3,27 @@ #include -// Axis Aligned Bounding Box +/// @brief Axis Aligned Bounding Box struct AABB { glm::vec3 a {0.0f}; glm::vec3 b {1.0f}; - /* Get AABB point with minimal x,y,z */ + AABB() {} + + AABB(glm::vec3 size) : a(0.0f), b(size) { + } + + /// @brief Get AABB point with minimal x,y,z inline glm::vec3 min() const { return glm::min(a, b); } - /* Get AABB point with minimal x,y,z */ + /// @brief Get AABB point with minimal x,y,z inline glm::vec3 max() const { return glm::max(a, b); } - /* Get AABB dimensions: width, height, depth */ + /// @brief Get AABB dimensions: width, height, depth inline glm::vec3 size() const { return glm::vec3( fabs(b.x - a.x), @@ -31,14 +36,14 @@ struct AABB { return (a + b) * 0.5f; } - /* Multiply AABB size from center */ + /// @brief Multiply AABB size from center inline void scale(const glm::vec3 mul) { glm::vec3 center = (a + b) * 0.5f; a = (a - center) * mul + center; b = (b - center) * mul + center; } - /* Multiply AABB size from given origin */ + /// @brief Multiply AABB size from given origin inline void scale(const glm::vec3 mul, const glm::vec3 orig) { glm::vec3 beg = min(); glm::vec3 end = max(); @@ -47,7 +52,7 @@ struct AABB { b = (b - center) * mul + center; } - /* Check if given point is inside */ + /// @brief Check if given point is inside inline bool contains(const glm::vec3 pos) const { const glm::vec3 p = min(); const glm::vec3 s = size(); diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index 0afeda5c..fdae39b5 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -32,6 +32,14 @@ struct PlayerInput { bool flight : 1; }; +struct BlockSelection { + voxel vox {0, {}}; + glm::ivec3 position {}; + glm::ivec3 actualPosition {}; + glm::ivec3 normal {}; + glm::vec3 hitPosition; +}; + class Player : public Object, public Serializable { float speed; int chosenSlot; @@ -44,9 +52,8 @@ public: std::shared_ptr currentCamera; std::unique_ptr hitbox; bool debug = false; - voxel selectedVoxel {0, {}}; glm::vec3 cam {}; - glm::ivec3 selectedBlockPosition {}; + BlockSelection selection {}; Player(glm::vec3 position, float speed, std::shared_ptr inv); ~Player(); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 2d3d1577..d3d95d7c 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; @@ -62,6 +62,9 @@ struct BlockRotProfile { /// @brief Doors, signs and other panes static const BlockRotProfile PANE; + + static inline std::string PIPE_NAME = "pipe"; + static inline std::string PANE_NAME = "pane"; }; enum class BlockModel { @@ -110,6 +113,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 +180,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]; @@ -191,4 +199,4 @@ public: Block(const Block&) = delete; }; -#endif /* VOXELS_BLOCK_HPP_ */ +#endif // VOXELS_BLOCK_HPP_ diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 2805c94b..413cab85 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -22,7 +22,7 @@ Chunks::Chunks( WorldFiles* wfile, LevelEvents* events, const Content* content -) : contentIds(content->getIndices()), +) : indices(content->getIndices()), chunks(w*d), chunksSecond(w*d), w(w), d(d), ox(ox), oz(oz), @@ -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); @@ -65,13 +65,18 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z){ return ∅ } } - const Block* def = contentIds->getBlockDef(v->id); + const Block* def = indices->getBlockDef(v->id); if (def->obstacle) { + glm::ivec3 offset {}; + if (v->state.segment) { + glm::ivec3 point(ix, iy, iz); + offset = seekOrigin(point, def, v->state) - point; + } const auto& boxes = def->rotatable ? def->rt.hitboxes[v->state.rotation] : def->hitboxes; for (const auto& hitbox : boxes) { - if (hitbox.contains({x - ix, y - iy, z - iz})) { + if (hitbox.contains({x - ix - offset.x, y - iy - offset.y, z - iz - offset.z})) { return &hitbox; } } @@ -83,21 +88,21 @@ bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return contentIds->getBlockDef(v->id)->rt.solid; + return indices->getBlockDef(v->id)->rt.solid; } bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return contentIds->getBlockDef(v->id)->replaceable; + return indices->getBlockDef(v->id)->replaceable; } bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return contentIds->getBlockDef(v->id)->obstacle; + return indices->getBlockDef(v->id)->obstacle; } ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel){ @@ -153,33 +158,217 @@ Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) { Chunk* Chunks::getChunk(int x, int z){ x -= ox; z -= oz; - if (x < 0 || z < 0 || x >= int(w) || z >= int(d)) + if (x < 0 || z < 0 || x >= static_cast(w) || z >= static_cast(d)) { return nullptr; + } return chunks[z * w + x].get(); } -void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) { - if (y < 0 || y >= CHUNK_H) +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, {}); + } + } + } +} + +static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { + return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); +} + +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 = segment_to_int(sx, sy, sz); + + 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); + } + } + } +} + +bool Chunks::checkReplaceability(const Block* def, blockstate state, 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++) { + blockstate segState = state; + segState.segment = segment_to_int(sx, sy, sz); + + 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->getBlockDef(vox->id); + if (!target->replaceable && vox->id != ignore) { + return false; + } + } else { + return false; + } + } + } + } + return true; +} + +void Chunks::setRotationExtended( + Block* def, blockstate state, 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 = 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); + 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, {}); + } + } + } + } +} + +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->getBlockDef(vox->id); + if (!def->rotatable || vox->state.rotation == index) { + return; + } + if (def->rt.extended) { + setRotationExtended(def, vox->state, {x, y, z}, index); + } else { + vox->state.rotation = index; + auto chunk = getChunkByVoxel(x, y, z); + chunk->setModifiedAndUnsaved(); + } +} + +void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) { + 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 = indices->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 = indices->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; @@ -242,14 +431,14 @@ voxel* Chunks::rayCast( if (voxel == nullptr){ return nullptr; } - const Block* def = contentIds->getBlockDef(voxel->id); + const auto def = indices->getBlockDef(voxel->id); if (def->selectable){ end.x = px + t * dx; end.y = py + t * dy; end.z = pz + t * dz; - iend.x = ix; - iend.y = iy; - iend.z = iz; + iend.x = ix; + iend.y = iy; + iend.z = iz; if (!def->rt.solid) { const std::vector& hitboxes = def->rotatable @@ -261,7 +450,14 @@ voxel* Chunks::rayCast( bool hit = false; - for (const auto& box : hitboxes) { + 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) { @@ -359,7 +555,7 @@ glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDis if (voxel == nullptr) { return glm::vec3(px + t * dx, py + t * dy, pz + t * dz); } - const Block* def = contentIds->getBlockDef(voxel->id); + const auto def = indices->getBlockDef(voxel->id); if (def->obstacle) { if (!def->rt.solid) { const std::vector& hitboxes = def->rotatable @@ -370,9 +566,14 @@ glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDis glm::ivec3 norm; Ray ray(start, dir); + glm::ivec3 offset {}; + if (voxel->state.segment) { + offset = seekOrigin({ix, iy, iz}, def, voxel->state) - glm::ivec3(ix, iy, iz); + } + for (const auto& box : hitboxes) { // norm is dummy now, can be inefficient - if (ray.intersectAABB(glm::ivec3(ix, iy, iz), box, maxDist, norm, distance) > RayRelation::None) { + if (ray.intersectAABB(glm::ivec3(ix, iy, iz)+offset, box, maxDist, norm, distance) > RayRelation::None) { return start + (dir * glm::vec3(distance)); } } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index abeceae1..fb8185d4 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -17,10 +17,15 @@ class ContentIndices; class Chunk; class WorldFiles; class LevelEvents; +class Block; /* Player-centred chunks matrix */ class Chunks { - const ContentIndices* const contentIds; + const ContentIndices* const indices; + + 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); + void setRotationExtended(Block* def, blockstate state, glm::ivec3 origin, uint8_t rotation); public: std::vector> chunks; std::vector> chunksSecond; @@ -40,11 +45,32 @@ 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); + /// @brief Seek for the extended block origin position + /// @param pos segment block position + /// @param def segment block definition + /// @param state segment block state + /// @return origin block position or `pos` if block is not extended + glm::ivec3 seekOrigin(glm::ivec3 pos, const Block* def, blockstate state); + + /// @brief Check if required zone is replaceable + /// @param def definition of the block that requires a replaceable zone + /// @param state the block state + /// @param coord position of the zone start + /// @param ignore ignored block id (will be counted as replaceable) + bool checkReplaceability(const Block* def, blockstate state, glm::ivec3 coord, blockid_t ignore=0); + + void setRotation(int32_t x, int32_t y, int32_t z, uint8_t rotation); + voxel* rayCast( glm::vec3 start, glm::vec3 dir, diff --git a/src/voxels/voxel.hpp b/src/voxels/voxel.hpp index 9cfaf698..460a6a4f 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 : 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); +/// @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) << 6 | 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) & 0b111), + static_cast((i >> 6) & 0b11), static_cast((i >> 8) & 0xFF) }; }