From ed9cf8800aea07c169719a3efddae7020e6be7e9 Mon Sep 17 00:00:00 2001 From: Eliot Byte Date: Thu, 23 Oct 2025 12:29:04 +0300 Subject: [PATCH 1/5] fix correct line generation algorithm and bounds calculation (#657) * fix correct line generation algorithm and bounds calculation - Fix closest_point_on_segment algorithm using proper dot product projection - Correct loop bounds calculation for radius=0 lines - Use precise integer arithmetic to avoid rounding errors - Resolves missing blocks in vertical line structures * fix ai hallucinations * polishing closest_point_on_segment * refactor: extract dot product calculation --- src/maths/util.hpp | 24 +++++++++++++++++------- src/world/generator/WorldGenerator.cpp | 6 +++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/maths/util.hpp b/src/maths/util.hpp index 00345942..2aa3605c 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -118,6 +118,11 @@ namespace util { return x * x + y * y + z * z; } + /// @return integer dot product of two vectors + inline int dot(const glm::ivec3& a, const glm::ivec3& b) { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + /// @brief Find nearest point on segment to given /// @param a segment point A /// @param b segment point B @@ -144,12 +149,17 @@ namespace util { inline glm::ivec3 closest_point_on_segment( const glm::ivec3& a, const glm::ivec3& b, const glm::ivec3& point ) { - auto vec = b - a; - float da = distance2(point, a); - float db = distance2(point, b); - float len = length2(vec); - float t = (((da - db) / len) * 0.5f + 0.5f); - t = std::min(1.0f, std::max(0.0f, t)); - return a + glm::ivec3(glm::vec3(vec) * t); + glm::ivec3 vec = b - a; + int len2 = length2(vec); + + if (len2 == 0) return a; + + glm::ivec3 ap = point - a; + int dot_product = dot(ap, vec); + + float t = static_cast(dot_product) / static_cast(len2); + t = glm::clamp(t, 0.0f, 1.0f); + + return a + glm::ivec3(glm::round(glm::vec3(vec) * t)); } } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index e2bf4a86..ccd52db1 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -547,13 +547,13 @@ void WorldGenerator::generateLine( auto b = line.b; int minX = std::max(0, std::min(a.x-radius-cgx, b.x-radius-cgx)); - int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx)); + int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx) + 1); int minZ = std::max(0, std::min(a.z-radius-cgz, b.z-radius-cgz)); - int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz)); + int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz) + 1); int minY = std::max(0, std::min(a.y-radius, b.y-radius)); - int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius)); + int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius) + 1); for (int y = minY; y < maxY; y++) { for (int z = minZ; z < maxZ; z++) { From 2a1d2f9354ee2ab623aca9d0059c0a676395d6d0 Mon Sep 17 00:00:00 2001 From: eliotbyte Date: Tue, 28 Oct 2025 23:09:28 +0300 Subject: [PATCH 2/5] fix enable per-variant custom model caching --- src/frontend/ContentGfxCache.cpp | 6 +++--- src/frontend/ContentGfxCache.hpp | 8 ++++++-- src/graphics/render/BlocksPreview.cpp | 2 +- src/graphics/render/BlocksRenderer.cpp | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/frontend/ContentGfxCache.cpp b/src/frontend/ContentGfxCache.cpp index 47443aff..20b7cdaa 100644 --- a/src/frontend/ContentGfxCache.cpp +++ b/src/frontend/ContentGfxCache.cpp @@ -62,7 +62,7 @@ void ContentGfxCache::refreshVariant( } } } - models[def.rt.id] = std::move(model); + models[modelKey(def.rt.id, variantIndex)] = std::move(model); } } @@ -94,8 +94,8 @@ void ContentGfxCache::refresh() { ContentGfxCache::~ContentGfxCache() = default; -const model::Model& ContentGfxCache::getModel(blockid_t id) const { - const auto& found = models.find(id); +const model::Model& ContentGfxCache::getModel(blockid_t id, uint8_t variant) const { + const auto& found = models.find(modelKey(id, variant)); if (found == models.end()) { throw std::runtime_error("model not found"); } diff --git a/src/frontend/ContentGfxCache.hpp b/src/frontend/ContentGfxCache.hpp index 8eda04e5..2d2bacaa 100644 --- a/src/frontend/ContentGfxCache.hpp +++ b/src/frontend/ContentGfxCache.hpp @@ -27,7 +27,11 @@ class ContentGfxCache { // array of block sides uv regions (6 per block) std::unique_ptr sideregions; - std::unordered_map models; + std::unordered_map models; + + static inline uint64_t modelKey(blockid_t id, uint8_t variant) { + return (uint64_t(id) << 8) | uint64_t(variant & 0xFF); + } void refreshVariant( const Block& def, @@ -53,7 +57,7 @@ public: return sideregions[getRegionIndex(id, variant, side, !dense)]; } - const model::Model& getModel(blockid_t id) const; + const model::Model& getModel(blockid_t id, uint8_t variant) const; void refresh(const Block& block, const Atlas& atlas); diff --git a/src/graphics/render/BlocksPreview.cpp b/src/graphics/render/BlocksPreview.cpp index 9a1bf97d..4b8bfee9 100644 --- a/src/graphics/render/BlocksPreview.cpp +++ b/src/graphics/render/BlocksPreview.cpp @@ -67,7 +67,7 @@ std::unique_ptr BlocksPreview::draw( glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f); offset.y += (1.0f - hitbox).y * 0.5f; shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset)); - const auto& model = cache.getModel(def.rt.id); + const auto& model = cache.getModel(def.rt.id, 0); for (const auto& mesh : model.meshes) { for (const auto& vertex : mesh.vertices) { diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 1f14d2c7..6df7967e 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -308,7 +308,7 @@ void BlocksRenderer::blockCustomModel( Z = orient.axes[2]; } - const auto& model = cache.getModel(block.rt.id); + const auto& model = cache.getModel(block.rt.id, block.getVariantIndex(states.userbits)); for (const auto& mesh : model.meshes) { if (vertexCount + mesh.vertices.size() >= capacity) { overflow = true; From cd6b73f1eb5268043d4b6abfa9e11d8bc1403116 Mon Sep 17 00:00:00 2001 From: eliotbyte Date: Tue, 28 Oct 2025 23:09:48 +0300 Subject: [PATCH 3/5] docs: add custom model variant examples to block-propertie --- doc/en/block-properties.md | 26 ++++++++++++++++++++++++++ doc/ru/block-properties.md | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md index 1244da99..82811b63 100644 --- a/doc/en/block-properties.md +++ b/doc/en/block-properties.md @@ -98,6 +98,32 @@ Properties available for variance: Variants are managed via `block.set_variant(x, y, z, index)`. +### Custom model variants (geometry switching) + +You can use different custom models for different variants. Provide a separate `model-name` for each variant that needs different geometry. The renderer caches geometry per (block id, variant). + +The base model (specified in root) becomes variant 0. The variants array maps to indices 1+. + +Example (default + two custom variants): +```json +{ + "model": "custom", + "model-name": "stairs_middle", + "state-based": { + "bits": 4, + "variants": [ + { "model": "custom", "model-name": "stairs_left" }, + { "model": "custom", "model-name": "stairs_right" } + ] + } +} +``` + +In this example: +- Variant 0 = `stairs_middle` (from root) +- Variant 1 = `stairs_left` (from variants[0]) +- Variant 2 = `stairs_right` (from variants[1]) + ## Lighting ### *emission* diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 0003cb92..4569cc49 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -106,6 +106,32 @@ Управление состоянием производится через `block.set_variant(x, y, z, index)`. +### Кастомные модели по вариантам (переключение геометрии) + +Для custom-моделей можно переключать геометрию по варианту. Укажите отдельный `model-name` в нужных вариантах — рендерер кэширует геометрию по паре (id блока, вариант). + +Базовая модель (из корня) становится вариантом 0. Массив variants соответствует индексам 1+. + +Пример (база + два кастомных варианта): +```json +{ + "model": "custom", + "model-name": "stairs_middle", + "state-based": { + "bits": 4, + "variants": [ + { "model": "custom", "model-name": "stairs_left" }, + { "model": "custom", "model-name": "stairs_right" } + ] + } +} +``` + +В этом примере: +- Вариант 0 = `stairs_middle` (из корня) +- Вариант 1 = `stairs_left` (из variants[0]) +- Вариант 2 = `stairs_right` (из variants[1]) + ## Освещение ### Излучение - *emission*: From 934c5f194e89b8cca738929e67d0c2640ac2610f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 4 Nov 2025 15:50:52 +0300 Subject: [PATCH 4/5] fix: on_hud_render is not documented --- doc/en/scripting/events.md | 6 ++++++ doc/ru/scripting/events.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index 712dcb8c..fce3abfb 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -190,6 +190,12 @@ function on_hud_open(playerid: int) Called after world open. +```lua +function on_hud_render() +``` + +Called every frame. Used for client-side tasks such as animation and camera control. + ```lua function on_hud_close(playerid: int) ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 4ca3fda5..73a6bc0c 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -190,6 +190,12 @@ function on_hud_open(playerid: int) Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы. +```lua +function on_hud_render() +``` + +Вызывается каждый кадр. Используется для клиентских задач, таких как анимация, управление камерой. + ```lua function on_hud_close(playerid: int) ``` From 652f652e556b64401e17b10c749c9952ce90182c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 4 Nov 2025 16:02:11 +0300 Subject: [PATCH 5/5] fix: player.get_camera, set_camera are not documented --- doc/en/scripting/builtins/libplayer.md | 12 ++++++++++++ doc/ru/scripting/builtins/libplayer.md | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/doc/en/scripting/builtins/libplayer.md b/doc/en/scripting/builtins/libplayer.md index fa799198..48004674 100644 --- a/doc/en/scripting/builtins/libplayer.md +++ b/doc/en/scripting/builtins/libplayer.md @@ -112,6 +112,18 @@ player.get_name(playerid: int) -> str Player name setter and getter +```lua +player.get_camera(playerid: int) -> int +``` + +Returns the index of the player's current camera. + +```lua +player.set_camera(playerid: int, camera_index: int) +``` + +Switches the player's camera. See [cameras](libcameras.md). + ```lua player.set_selected_slot(playerid: int, slotid: int) ``` diff --git a/doc/ru/scripting/builtins/libplayer.md b/doc/ru/scripting/builtins/libplayer.md index 2228f42c..87381193 100644 --- a/doc/ru/scripting/builtins/libplayer.md +++ b/doc/ru/scripting/builtins/libplayer.md @@ -112,6 +112,18 @@ player.get_name(playerid: int) -> str Сеттер и геттер имени игрока +```lua +player.get_camera(playerid: int) -> int +``` + +Возвращает индекс текущей камеры игрока + +```lua +player.set_camera(playerid: int, camera_index: int) +``` + +Переключает камеру игрока. См. [камеры](libcameras.md). + ```lua player.set_selected_slot(playerid: int, slotid: int) ```