From d06a2081889ac90ca0f028fa544a2331635dc838 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 26 Jul 2025 23:53:27 +0300 Subject: [PATCH 1/8] cleanup __skeleton library --- .../scripting/lua/libs/lib__skeleton.cpp | 97 +++++++++---------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/src/logic/scripting/lua/libs/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp index 2cb9c44e..208c3483 100644 --- a/src/logic/scripting/lua/libs/lib__skeleton.cpp +++ b/src/logic/scripting/lua/libs/lib__skeleton.cpp @@ -13,25 +13,30 @@ static int index_range_check( return static_cast(index); } -static int l_get_model(lua::State* L) { +static rigging::Skeleton* get_skeleton(lua::State* L) { if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto* rigConfig = skeleton.config; - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - const auto& modelOverride = skeleton.modelOverrides[index]; + return &entity->getSkeleton(); + } + return nullptr; +} + +static int l_get_model(lua::State* L) { + if (auto skeleton = get_skeleton(L)) { + auto& rigConfig = *skeleton->config; + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + const auto& modelOverride = skeleton->modelOverrides[index]; if (!modelOverride.model) { return lua::pushstring(L, modelOverride.name); } - return lua::pushstring(L, rigConfig->getBones()[index]->model.name); + return lua::pushstring(L, rigConfig.getBones()[index]->model.name); } return 0; } static int l_set_model(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - auto& modelOverride = skeleton.modelOverrides[index]; + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + auto& modelOverride = skeleton->modelOverrides[index]; if (lua::isnoneornil(L, 3)) { modelOverride = {"", nullptr, true}; } else { @@ -42,28 +47,25 @@ static int l_set_model(lua::State* L) { } static int l_get_matrix(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - return lua::pushmat4(L, skeleton.pose.matrices[index]); + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + return lua::pushmat4(L, skeleton->pose.matrices[index]); } return 0; } static int l_set_matrix(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - skeleton.pose.matrices[index] = lua::tomat4(L, 3); + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + skeleton->pose.matrices[index] = lua::tomat4(L, 3); } return 0; } static int l_get_texture(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - const auto& found = skeleton.textures.find(lua::require_string(L, 2)); - if (found != skeleton.textures.end()) { + if (auto skeleton = get_skeleton(L)) { + const auto& found = skeleton->textures.find(lua::require_string(L, 2)); + if (found != skeleton->textures.end()) { return lua::pushstring(L, found->second); } } @@ -71,18 +73,16 @@ static int l_get_texture(lua::State* L) { } static int l_set_texture(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.textures[lua::require_string(L, 2)] = + if (auto skeleton = get_skeleton(L)) { + skeleton->textures[lua::require_string(L, 2)] = lua::require_string(L, 3); } return 0; } static int l_index(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - if (auto bone = skeleton.config->find(lua::require_string(L, 2))) { + if (auto skeleton= get_skeleton(L)) { + if (auto bone = skeleton->config->find(lua::require_string(L, 2))) { return lua::pushinteger(L, bone->getIndex()); } } @@ -90,58 +90,52 @@ static int l_index(lua::State* L) { } static int l_is_visible(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); + if (auto skeleton = get_skeleton(L)) { if (!lua::isnoneornil(L, 2)) { - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - return lua::pushboolean(L, skeleton.flags.at(index).visible); + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + return lua::pushboolean(L, skeleton->flags.at(index).visible); } - return lua::pushboolean(L, skeleton.visible); + return lua::pushboolean(L, skeleton->visible); } return 0; } static int l_set_visible(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); + if (auto skeleton = get_skeleton(L)) { if (!lua::isnoneornil(L, 3)) { - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - skeleton.flags.at(index).visible = lua::toboolean(L, 3); + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + skeleton->flags.at(index).visible = lua::toboolean(L, 3); } else { - skeleton.visible = lua::toboolean(L, 2); + skeleton->visible = lua::toboolean(L, 2); } } return 0; } static int l_get_color(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - return lua::pushvec(L, skeleton.tint); + if (auto skeleton = get_skeleton(L)) { + return lua::pushvec(L, skeleton->tint); } return 0; } static int l_set_color(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.tint = lua::tovec3(L, 2); + if (auto skeleton = get_skeleton(L)) { + skeleton->tint = lua::tovec3(L, 2); } return 0; } static int l_is_interpolated(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - return lua::pushboolean(L, skeleton.interpolation.isEnabled()); + if (auto skeleton = get_skeleton(L)) { + return lua::pushboolean(L, skeleton->interpolation.isEnabled()); } return 0; } static int l_set_interpolated(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.interpolation.setEnabled(lua::toboolean(L, 2)); + if (auto skeleton = get_skeleton(L)) { + skeleton->interpolation.setEnabled(lua::toboolean(L, 2)); } return 0; } @@ -160,4 +154,5 @@ const luaL_Reg skeletonlib[] = { {"set_color", lua::wrap}, {"is_interpolated", lua::wrap}, {"set_interpolated", lua::wrap}, - {NULL, NULL}}; + {NULL, NULL} +}; From 5632c9ccfead7210bf2f71ccc523783a5f43787e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 02:45:25 +0300 Subject: [PATCH 2/8] add hand skeleton --- res/content/base/config/defaults.toml | 1 + res/content/base/skeletons/hand.json | 9 +++ src/content/Content.cpp | 8 +++ src/content/Content.hpp | 1 + src/graphics/render/HandsRenderer.cpp | 94 +++++++++++++++++++++++++ src/graphics/render/HandsRenderer.hpp | 32 +++++++++ src/graphics/render/NamedSkeletons.cpp | 23 ++++++ src/graphics/render/NamedSkeletons.hpp | 23 ++++++ src/graphics/render/WorldRenderer.cpp | 96 +++++++++----------------- src/graphics/render/WorldRenderer.hpp | 6 +- 10 files changed, 226 insertions(+), 67 deletions(-) create mode 100644 res/content/base/skeletons/hand.json create mode 100644 src/graphics/render/HandsRenderer.cpp create mode 100644 src/graphics/render/HandsRenderer.hpp create mode 100644 src/graphics/render/NamedSkeletons.cpp create mode 100644 src/graphics/render/NamedSkeletons.hpp diff --git a/res/content/base/config/defaults.toml b/res/content/base/config/defaults.toml index a099c198..650c24d7 100644 --- a/res/content/base/config/defaults.toml +++ b/res/content/base/config/defaults.toml @@ -1,2 +1,3 @@ generator = "base:demo" player-entity = "base:player" +hand-skeleton = "base:hand" diff --git a/res/content/base/skeletons/hand.json b/res/content/base/skeletons/hand.json new file mode 100644 index 00000000..8ff9d841 --- /dev/null +++ b/res/content/base/skeletons/hand.json @@ -0,0 +1,9 @@ +{ + "root": { + "nodes": [ + { + "name": "item" + } + ] + } +} diff --git a/src/content/Content.cpp b/src/content/Content.cpp index ce92be18..5ee68859 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -63,6 +63,14 @@ const rigging::SkeletonConfig* Content::getSkeleton(const std::string& id return found->second.get(); } +const rigging::SkeletonConfig& Content::requireSkeleton(const std::string& id) const { + auto skeleton = getSkeleton(id); + if (skeleton == nullptr) { + throw std::runtime_error("skeleton '" + id + "' not loaded"); + } + return *skeleton; +} + const BlockMaterial* Content::findBlockMaterial(const std::string& id) const { auto found = blockMaterials.find(id); if (found == blockMaterials.end()) { diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 539325a8..81e816b6 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -212,6 +212,7 @@ public: } const rigging::SkeletonConfig* getSkeleton(const std::string& id) const; + const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const; const BlockMaterial* findBlockMaterial(const std::string& id) const; const ContentPackRuntime* getPackRuntime(const std::string& id) const; ContentPackRuntime* getPackRuntime(const std::string& id); diff --git a/src/graphics/render/HandsRenderer.cpp b/src/graphics/render/HandsRenderer.cpp new file mode 100644 index 00000000..aa5666f1 --- /dev/null +++ b/src/graphics/render/HandsRenderer.cpp @@ -0,0 +1,94 @@ +#include "HandsRenderer.hpp" + +#include +#include + +#include "ModelBatch.hpp" +#include "assets/Assets.hpp" +#include "content/Content.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/commons/Model.hpp" +#include "items/Inventory.hpp" +#include "items/ItemStack.hpp" +#include "items/ItemDef.hpp" +#include "objects/Player.hpp" +#include "objects/rigging.hpp" +#include "world/Level.hpp" +#include "window/Camera.hpp" +#include "window/Window.hpp" + +using namespace rigging; + +HandsRenderer::HandsRenderer( + const Assets& assets, + const Level& level, + const Player& player, + ModelBatch& modelBatch, + std::shared_ptr skeleton +) + : assets(assets), + level(level), + player(player), + modelBatch(modelBatch), + skeleton(std::move(skeleton)) { +} + +void HandsRenderer::renderHands( + const Camera& camera, float delta +) { + // configure model matrix + const glm::vec3 itemOffset(0.06f, 0.035f, -0.1); + + static glm::mat4 prevRotation(1.0f); + + const float speed = 24.0f; + glm::mat4 matrix = glm::translate(glm::mat4(1.0f), itemOffset); + matrix = glm::scale(matrix, glm::vec3(0.1f)); + glm::mat4 rotation = camera.rotation; + + // rotation interpolation + glm::quat rot0 = glm::quat_cast(prevRotation); + glm::quat rot1 = glm::quat_cast(rotation); + glm::quat finalRot = + glm::slerp(rot0, rot1, static_cast(delta * speed)); + rotation = glm::mat4_cast(finalRot); + prevRotation = rotation; + + // building matrix + matrix = rotation * matrix * + glm::rotate( + glm::mat4(1.0f), -glm::pi() * 0.5f, glm::vec3(0, 1, 0) + ); + + // getting offset + glm::vec3 cameraRotation = player.getRotation(); + auto offset = -(camera.position - player.getPosition()); + float angle = glm::radians(cameraRotation.x - 90); + float cos = glm::cos(angle); + float sin = glm::sin(angle); + + float newX = offset.x * cos - offset.z * sin; + float newZ = offset.x * sin + offset.z * cos; + offset = glm::vec3(newX, offset.y, newZ); + matrix = matrix * glm::translate(glm::mat4(1.0f), offset); + + // get current chosen item + auto indices = level.content.getIndices(); + const auto& inventory = player.getInventory(); + int slot = player.getChosenSlot(); + const ItemStack& stack = inventory->getSlot(slot); + const auto& def = indices->items.require(stack.getItemId()); + + auto& skeleton = *this->skeleton; + const auto& config = *skeleton.config; + + auto itemBone = config.find("item"); + size_t itemBoneIndex = itemBone->getIndex(); + skeleton.modelOverrides.at(itemBoneIndex).model = assets.get(def.modelName); + skeleton.pose.matrices.at(itemBoneIndex) = matrix; + + // render + modelBatch.setLightsOffset(camera.position); + config.update(skeleton, glm::mat4(1.0f), glm::vec3()); + config.render(assets, modelBatch, skeleton, glm::mat4(1.0f), glm::vec3()); +} diff --git a/src/graphics/render/HandsRenderer.hpp b/src/graphics/render/HandsRenderer.hpp new file mode 100644 index 00000000..156035eb --- /dev/null +++ b/src/graphics/render/HandsRenderer.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +class Assets; +class Camera; +class Level; +class Player; +class ModelBatch; + +namespace rigging { + struct Skeleton; +} + +class HandsRenderer { +public: + HandsRenderer( + const Assets& assets, + const Level& level, + const Player& player, + ModelBatch& modelBatch, + std::shared_ptr skeleton + ); + + void renderHands(const Camera& camera, float delta); +private: + const Assets& assets; + const Level& level; + const Player& player; + ModelBatch& modelBatch; + std::shared_ptr skeleton; +}; diff --git a/src/graphics/render/NamedSkeletons.cpp b/src/graphics/render/NamedSkeletons.cpp new file mode 100644 index 00000000..35dd57ec --- /dev/null +++ b/src/graphics/render/NamedSkeletons.cpp @@ -0,0 +1,23 @@ +#include "NamedSkeletons.hpp" + +#include "objects/rigging.hpp" + +using namespace rigging; + +NamedSkeletons::NamedSkeletons() = default; + +std::shared_ptr NamedSkeletons::createSkeleton( + const std::string& name, const SkeletonConfig* config +) { + auto skeleton = std::make_shared(config); + skeletons[name] = skeleton; + return skeleton; +} + +rigging::Skeleton* NamedSkeletons::getSkeleton(const std::string& name) { + const auto& found = skeletons.find(name); + if (found == skeletons.end()) { + return nullptr; + } + return found->second.get(); +} diff --git a/src/graphics/render/NamedSkeletons.hpp b/src/graphics/render/NamedSkeletons.hpp new file mode 100644 index 00000000..4e4fb77a --- /dev/null +++ b/src/graphics/render/NamedSkeletons.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace rigging { + struct Skeleton; + class SkeletonConfig; +} + +class NamedSkeletons { +public: + NamedSkeletons(); + + std::shared_ptr createSkeleton( + const std::string& name, const rigging::SkeletonConfig* config + ); + + rigging::Skeleton* getSkeleton(const std::string& name); +private: + std::unordered_map> skeletons; +}; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index e8511fa5..594d39be 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -47,6 +47,8 @@ #include "BlockWrapsRenderer.hpp" #include "ParticlesRenderer.hpp" #include "PrecipitationRenderer.hpp" +#include "HandsRenderer.hpp" +#include "NamedSkeletons.hpp" #include "TextsRenderer.hpp" #include "ChunksRenderer.hpp" #include "GuidesRenderer.hpp" @@ -107,6 +109,19 @@ WorldRenderer::WorldRenderer( settings.graphics.skyboxResolution.get(), assets->require("skybox_gen") ); + + const auto& content = level.content; + skeletons = std::make_unique(); + const auto& skeletonConfig = content.requireSkeleton( + content.getDefaults()["hand-skeleton"].asString() + ); + hands = std::make_unique( + *assets, + level, + player, + *modelBatch, + skeletons->createSkeleton("hand", &skeletonConfig) + ); } WorldRenderer::~WorldRenderer() = default; @@ -273,70 +288,6 @@ void WorldRenderer::renderLines( } } -void WorldRenderer::renderHands( - const Camera& camera, float delta -) { - auto& entityShader = assets.require("entity"); - auto indices = level.content.getIndices(); - - // get current chosen item - const auto& inventory = player.getInventory(); - int slot = player.getChosenSlot(); - const ItemStack& stack = inventory->getSlot(slot); - const auto& def = indices->items.require(stack.getItemId()); - - // prepare modified HUD camera - Camera hudcam = camera; - hudcam.far = 10.0f; - hudcam.setFov(0.9f); - hudcam.position = {}; - - // configure model matrix - const glm::vec3 itemOffset(0.06f, 0.035f, -0.1); - - static glm::mat4 prevRotation(1.0f); - - const float speed = 24.0f; - glm::mat4 matrix = glm::translate(glm::mat4(1.0f), itemOffset); - matrix = glm::scale(matrix, glm::vec3(0.1f)); - glm::mat4 rotation = camera.rotation; - glm::quat rot0 = glm::quat_cast(prevRotation); - glm::quat rot1 = glm::quat_cast(rotation); - glm::quat finalRot = - glm::slerp(rot0, rot1, static_cast(delta * speed)); - rotation = glm::mat4_cast(finalRot); - matrix = rotation * matrix * - glm::rotate( - glm::mat4(1.0f), -glm::pi() * 0.5f, glm::vec3(0, 1, 0) - ); - prevRotation = rotation; - glm::vec3 cameraRotation = player.getRotation(); - auto offset = -(camera.position - player.getPosition()); - float angle = glm::radians(cameraRotation.x - 90); - float cos = glm::cos(angle); - float sin = glm::sin(angle); - - float newX = offset.x * cos - offset.z * sin; - float newZ = offset.x * sin + offset.z * cos; - offset = glm::vec3(newX, offset.y, newZ); - matrix = matrix * glm::translate(glm::mat4(1.0f), offset); - - // render - modelBatch->setLightsOffset(camera.position); - modelBatch->draw( - matrix, - glm::vec3(1.0f), - assets.get(def.modelName), - nullptr - ); - display::clearDepth(); - setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); - skybox->bind(); - modelBatch->render(); - modelBatch->setLightsOffset(glm::vec3()); - skybox->unbind(); -} - void WorldRenderer::generateShadowsMap( const Camera& camera, const DrawContext& pctx, @@ -568,7 +519,22 @@ void WorldRenderer::draw( DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); ctx.setCullFace(true); - renderHands(camera, delta); + + // prepare modified HUD camera + Camera hudcam = camera; + hudcam.far = 10.0f; + hudcam.setFov(0.9f); + hudcam.position = {}; + + hands->renderHands(camera, delta); + + display::clearDepth(); + setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); + + skybox->bind(); + modelBatch->render(); + modelBatch->setLightsOffset(glm::vec3()); + skybox->unbind(); } renderBlockOverlay(pctx); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 5d33f8cd..e53a5d80 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -20,6 +20,8 @@ class ChunksRenderer; class ParticlesRenderer; class BlockWrapsRenderer; class PrecipitationRenderer; +class HandsRenderer; +class NamedSkeletons; class GuidesRenderer; class TextsRenderer; class Shader; @@ -52,6 +54,7 @@ class WorldRenderer { std::unique_ptr modelBatch; std::unique_ptr guides; std::unique_ptr chunks; + std::unique_ptr hands; std::unique_ptr skybox; std::unique_ptr shadowMap; std::unique_ptr wideShadowMap; @@ -69,8 +72,6 @@ class WorldRenderer { /// @brief Render block selection lines void renderBlockSelection(); - - void renderHands(const Camera& camera, float delta); /// @brief Render lines (selection and debug) /// @param camera active camera @@ -100,6 +101,7 @@ public: std::unique_ptr texts; std::unique_ptr blockWraps; std::unique_ptr precipitation; + std::unique_ptr skeletons; static bool showChunkBorders; static bool showEntitiesDebug; From 567eec88c82a1924b526efed03dc751d80c4efa3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 16:04:25 +0300 Subject: [PATCH 3/8] add named skeletons support --- src/logic/scripting/lua/libs/lib__skeleton.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/logic/scripting/lua/libs/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp index 208c3483..ff4a0eba 100644 --- a/src/logic/scripting/lua/libs/lib__skeleton.cpp +++ b/src/logic/scripting/lua/libs/lib__skeleton.cpp @@ -1,6 +1,13 @@ #include "objects/rigging.hpp" #include "libentity.hpp" +#include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/NamedSkeletons.hpp" + +namespace scripting { + extern WorldRenderer* renderer; +} + static int index_range_check( const rigging::Skeleton& skeleton, lua::Integer index ) { @@ -14,6 +21,9 @@ static int index_range_check( } static rigging::Skeleton* get_skeleton(lua::State* L) { + if (lua::isstring(L, 1)) { + return scripting::renderer->skeletons->getSkeleton(lua::tostring(L, 1)); + } if (auto entity = get_entity(L, 1)) { return &entity->getSkeleton(); } From 6f1174131033a09c6f5d4669d0b2189672457ebb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 19:20:30 +0300 Subject: [PATCH 4/8] move hand item control to lua --- res/scripts/hud.lua | 39 +++++++++++++++++ src/graphics/render/HandsRenderer.cpp | 60 --------------------------- src/graphics/render/HandsRenderer.hpp | 6 --- src/graphics/render/WorldRenderer.cpp | 6 +-- 4 files changed, 40 insertions(+), 71 deletions(-) diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 3fd1375c..c3c872bd 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -82,3 +82,42 @@ function on_hud_open() configure_SSAO() end + +local prev_rotation = mat4.idt() + +function update_hand() + local skeleton = __skeleton + local pid = hud.get_player() + local invid, slot = player.get_inventory(pid) + local itemid = inventory.get(invid, slot) + + local cam = cameras.get("core:first-person") + local bone = skeleton.index("hand", "item") + + local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1) + + local rotation = cam:get_rot() + + local angle = player.get_rot() - 90 + local cos = math.cos(angle / (180 / math.pi)) + local sin = math.sin(angle / (180 / math.pi)) + + local newX = offset[1] * cos - offset[3] * sin + local newZ = offset[1] * sin + offset[3] * cos + + offset[1] = newX + offset[3] = newZ + + local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1}) + mat4.scale(mat, {0.1, 0.1, 0.1}, mat) + mat4.mul(rotation, mat, mat) + mat4.rotate(mat, {0, 1, 0}, -90, mat) + mat4.translate(mat, offset, mat) + + skeleton.set_matrix("hand", bone, mat) + skeleton.set_model("hand", bone, item.model_name(itemid)) +end + +function on_hud_render() + timeit(1, update_hand) +end diff --git a/src/graphics/render/HandsRenderer.cpp b/src/graphics/render/HandsRenderer.cpp index aa5666f1..db598a43 100644 --- a/src/graphics/render/HandsRenderer.cpp +++ b/src/graphics/render/HandsRenderer.cpp @@ -4,31 +4,19 @@ #include #include "ModelBatch.hpp" -#include "assets/Assets.hpp" #include "content/Content.hpp" -#include "graphics/core/Shader.hpp" #include "graphics/commons/Model.hpp" -#include "items/Inventory.hpp" -#include "items/ItemStack.hpp" -#include "items/ItemDef.hpp" -#include "objects/Player.hpp" #include "objects/rigging.hpp" -#include "world/Level.hpp" #include "window/Camera.hpp" -#include "window/Window.hpp" using namespace rigging; HandsRenderer::HandsRenderer( const Assets& assets, - const Level& level, - const Player& player, ModelBatch& modelBatch, std::shared_ptr skeleton ) : assets(assets), - level(level), - player(player), modelBatch(modelBatch), skeleton(std::move(skeleton)) { } @@ -36,56 +24,8 @@ HandsRenderer::HandsRenderer( void HandsRenderer::renderHands( const Camera& camera, float delta ) { - // configure model matrix - const glm::vec3 itemOffset(0.06f, 0.035f, -0.1); - - static glm::mat4 prevRotation(1.0f); - - const float speed = 24.0f; - glm::mat4 matrix = glm::translate(glm::mat4(1.0f), itemOffset); - matrix = glm::scale(matrix, glm::vec3(0.1f)); - glm::mat4 rotation = camera.rotation; - - // rotation interpolation - glm::quat rot0 = glm::quat_cast(prevRotation); - glm::quat rot1 = glm::quat_cast(rotation); - glm::quat finalRot = - glm::slerp(rot0, rot1, static_cast(delta * speed)); - rotation = glm::mat4_cast(finalRot); - prevRotation = rotation; - - // building matrix - matrix = rotation * matrix * - glm::rotate( - glm::mat4(1.0f), -glm::pi() * 0.5f, glm::vec3(0, 1, 0) - ); - - // getting offset - glm::vec3 cameraRotation = player.getRotation(); - auto offset = -(camera.position - player.getPosition()); - float angle = glm::radians(cameraRotation.x - 90); - float cos = glm::cos(angle); - float sin = glm::sin(angle); - - float newX = offset.x * cos - offset.z * sin; - float newZ = offset.x * sin + offset.z * cos; - offset = glm::vec3(newX, offset.y, newZ); - matrix = matrix * glm::translate(glm::mat4(1.0f), offset); - - // get current chosen item - auto indices = level.content.getIndices(); - const auto& inventory = player.getInventory(); - int slot = player.getChosenSlot(); - const ItemStack& stack = inventory->getSlot(slot); - const auto& def = indices->items.require(stack.getItemId()); - auto& skeleton = *this->skeleton; const auto& config = *skeleton.config; - - auto itemBone = config.find("item"); - size_t itemBoneIndex = itemBone->getIndex(); - skeleton.modelOverrides.at(itemBoneIndex).model = assets.get(def.modelName); - skeleton.pose.matrices.at(itemBoneIndex) = matrix; // render modelBatch.setLightsOffset(camera.position); diff --git a/src/graphics/render/HandsRenderer.hpp b/src/graphics/render/HandsRenderer.hpp index 156035eb..1aba08a2 100644 --- a/src/graphics/render/HandsRenderer.hpp +++ b/src/graphics/render/HandsRenderer.hpp @@ -4,8 +4,6 @@ class Assets; class Camera; -class Level; -class Player; class ModelBatch; namespace rigging { @@ -16,8 +14,6 @@ class HandsRenderer { public: HandsRenderer( const Assets& assets, - const Level& level, - const Player& player, ModelBatch& modelBatch, std::shared_ptr skeleton ); @@ -25,8 +21,6 @@ public: void renderHands(const Camera& camera, float delta); private: const Assets& assets; - const Level& level; - const Player& player; ModelBatch& modelBatch; std::shared_ptr skeleton; }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 594d39be..ba1e580b 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -116,11 +116,7 @@ WorldRenderer::WorldRenderer( content.getDefaults()["hand-skeleton"].asString() ); hands = std::make_unique( - *assets, - level, - player, - *modelBatch, - skeletons->createSkeleton("hand", &skeletonConfig) + *assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig) ); } From b991ca3c9aa4b5d33e6a7e73baad3f9fbb6ab87a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 21:38:09 +0300 Subject: [PATCH 5/8] add gfx.skeletons library --- res/scripts/hud.lua | 2 +- res/scripts/stdlib.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index c3c872bd..9111c794 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -86,7 +86,7 @@ end local prev_rotation = mat4.idt() function update_hand() - local skeleton = __skeleton + local skeleton = gfx.skeletons local pid = hud.get_player() local invid, slot = player.get_inventory(pid) local itemid = inventory.get(invid, slot) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 765d2c08..d4be90b3 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -396,6 +396,8 @@ function _rules.clear() end function __vc_on_hud_open() + gfx.skeletons = __skeleton + _rules.create("allow-cheats", true) _rules.create("allow-content-access", hud._is_content_access(), function(value) From b5a2f2ae7162c11b409ca89c50bab795038fd856 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 22:18:28 +0300 Subject: [PATCH 6/8] add gfx.skeletons library docs --- doc/en/scripting/builtins/libgfx-skeletons.md | 48 ++++++++++++++++++ doc/ru/scripting/builtins/libgfx-skeletons.md | 49 +++++++++++++++++++ res/scripts/hud.lua | 2 - res/scripts/hud_classes.lua | 20 ++++++++ .../scripting/lua/libs/lib__skeleton.cpp | 5 ++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 doc/en/scripting/builtins/libgfx-skeletons.md create mode 100644 doc/ru/scripting/builtins/libgfx-skeletons.md diff --git a/doc/en/scripting/builtins/libgfx-skeletons.md b/doc/en/scripting/builtins/libgfx-skeletons.md new file mode 100644 index 00000000..4146b4be --- /dev/null +++ b/doc/en/scripting/builtins/libgfx-skeletons.md @@ -0,0 +1,48 @@ +# gfx.skeletons library + +A library for working with named skeletons, such as 'hand', +used to control the hand and the carried item displayed in first-person view. +The set of functions is similar to the skeleton component of entities. + +The first argument to the function is the name of the skeleton. + +```lua +-- Returns an object wrapper over the skeleton +local skeleton = gfx.skeletons.get(name: str) + +-- Returns the index of the bone by name or nil +skeleton:index(name: str) -> int + +-- Returns the name of the model assigned to the bone with the specified index +skeleton:get_model(index: int) -> str + +-- Reassigns the model of the bone with the specified index +-- Resets to the original if you do not specify a name +skeleton:set_model(index: int, name: str) + +-- Returns the transformation matrix of the bone with the specified index +skeleton:get_matrix(index: int) -> mat4 + +-- Sets the transformation matrix of the bone with the specified index +skeleton:set_matrix(index: int, matrix: mat4) + +-- Returns the texture by key (dynamically assigned textures - '$name') +skeleton:get_texture(key: str) -> str + +-- Assigns a texture by key +skeleton:set_texture(key: str, value: str) + +-- Checks the visibility status of a bone by index +-- or the entire skeleton if index is not specified +skeleton:is_visible([optional] index: int) -> bool + +-- Sets the visibility status of a bone by index +-- or the entire skeleton if index is not specified +skeleton:set_visible([optional] index: int, status: bool) + +-- Returns the color of the entity +skeleton:get_color() -> vec3 + +-- Sets the color of the entity +skeleton:set_color(color: vec3) +``` diff --git a/doc/ru/scripting/builtins/libgfx-skeletons.md b/doc/ru/scripting/builtins/libgfx-skeletons.md new file mode 100644 index 00000000..0e80b8f9 --- /dev/null +++ b/doc/ru/scripting/builtins/libgfx-skeletons.md @@ -0,0 +1,49 @@ +# Библиотека gfx.skeletons + +Библиотека для работы с именованными скелетами, такими как 'hand', +использующийся для управления, отображаемыми при виде от первого лица, +рукой и переносимым предметом. Набор функций аналогичен компоненту skeleton +у сущностей. + +Первым аргументом в функции передаётся имя скелета. + +```lua +-- Возвращает объектную обёртку над скелетом +local skeleton = gfx.skeletons.get(name: str) + +-- Возвращает индекс кости по имени или nil +skeleton:index(name: str) -> int + +-- Возвращает имя модели, назначенной на кость с указанным индексом +skeleton:get_model(index: int) -> str + +-- Переназначает модель кости с указанным индексом +-- Сбрасывает до изначальной, если не указывать имя +skeleton:set_model(index: int, name: str) + +-- Возвращает матрицу трансформации кости с указанным индексом +skeleton:get_matrix(index: int) -> mat4 + +-- Устанавливает матрицу трансформации кости с указанным индексом +skeleton:set_matrix(index: int, matrix: mat4) + +-- Возвращает текстуру по ключу (динамически назначаемые текстуры - '$имя') +skeleton:get_texture(key: str) -> str + +-- Назначает текстуру по ключу +skeleton:set_texture(key: str, value: str) + +-- Проверяет статус видимости кости по индесу +-- или всего скелета, если индекс не указан +skeleton:is_visible([опционально] index: int) -> bool + +-- Устанавливает статус видимости кости по индексу +-- или всего скелета, если индекс не указан +skeleton:set_visible([опционально] index: int, status: bool) + +-- Возвращает цвет сущности +skeleton:get_color() -> vec3 + +-- Устанавливает цвет сущности +skeleton:set_color(color: vec3) +``` diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 9111c794..e189d098 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -83,8 +83,6 @@ function on_hud_open() configure_SSAO() end -local prev_rotation = mat4.idt() - function update_hand() local skeleton = gfx.skeletons local pid = hud.get_player() diff --git a/res/scripts/hud_classes.lua b/res/scripts/hud_classes.lua index dc373c6e..f8a28944 100644 --- a/res/scripts/hud_classes.lua +++ b/res/scripts/hud_classes.lua @@ -12,7 +12,27 @@ local Text3D = {__index={ update_settings=function(self, t) return gfx.text3d.update_settings(self.id, t) end, }} +local Skeleton = {__index={ + index=function(self, s) return gfx.skeletons.index(self.name, s) end, + get_model=function(self, i) return gfx.skeletons.get_model(self.name, i) end, + set_model=function(self, i, s) return gfx.skeletons.set_model(self.name, i, s) end, + get_matrix=function(self, i) return gfx.skeletons.get_matrix(self.name, i) end, + set_matrix=function(self, i, m) return gfx.skeletons.set_matrix(self.name, i, m) end, + get_texture=function(self, i) return gfx.skeletons.get_texture(self.name, i) end, + set_texture=function(self, i, s) return gfx.skeletons.set_texture(self.name, i, s) end, + is_visible=function(self, i) return gfx.skeletons.is_visible(self.name, i) end, + set_visible=function(self, i, b) return gfx.skeletons.set_visible(self.name, i, b) end, + get_color=function(self, i) return gfx.skeletons.get_color(self.name, i) end, + set_color=function(self, i, c) return gfx.skeletons.set_color(self.name, i, c) end, +}} + gfx.text3d.new = function(pos, text, preset, extension) local id = gfx.text3d.show(pos, text, preset, extension) return setmetatable({id=id}, Text3D) end + +gfx.skeletons.get = function(name) + if gfx.skeletons.exists(name) then + return setmetatable({name=name}, Skeleton) + end +end diff --git a/src/logic/scripting/lua/libs/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp index ff4a0eba..7272548c 100644 --- a/src/logic/scripting/lua/libs/lib__skeleton.cpp +++ b/src/logic/scripting/lua/libs/lib__skeleton.cpp @@ -150,6 +150,10 @@ static int l_set_interpolated(lua::State* L) { return 0; } +static int l_exists(lua::State* L) { + return lua::pushboolean(L, get_skeleton(L)); +} + const luaL_Reg skeletonlib[] = { {"get_model", lua::wrap}, {"set_model", lua::wrap}, @@ -164,5 +168,6 @@ const luaL_Reg skeletonlib[] = { {"set_color", lua::wrap}, {"is_interpolated", lua::wrap}, {"set_interpolated", lua::wrap}, + {"exists", lua::wrap}, {NULL, NULL} }; From 93c922be998105504d0362ca2eacdef30ba59ce5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 22:21:14 +0300 Subject: [PATCH 7/8] update doc/*/scripting.md --- doc/en/scripting.md | 1 + doc/ru/scripting.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/en/scripting.md b/doc/en/scripting.md index b9661cd3..05a89a26 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -20,6 +20,7 @@ Subsections: - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) - [gfx.particles](particles.md#gfxparticles-library) - [gfx.posteffects](scripting/builtins/libgfx-posteffects.md) + - [gfx.skeletons](scripting/builtins/libgfx-skeletons.md) - [gfx.text3d](3d-text.md#gfxtext3d-library) - [gfx.weather](scripting/builtins/libgfx-weather.md) - [gui](scripting/builtins/libgui.md) diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 6fb86dce..eaae3302 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -20,6 +20,7 @@ - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) - [gfx.particles](particles.md#библиотека-gfxparticles) - [gfx.posteffects](scripting/builtins/libgfx-posteffects.md) + - [gfx.skeletons](scripting/builtins/libgfx-skeletons.md) - [gfx.text3d](3d-text.md#библиотека-gfxtext3d) - [gfx.weather](scripting/builtins/libgfx-weather.md) - [gui](scripting/builtins/libgui.md) From ecf3b94d5375d6207492b86d10b1d42e1fd7c570 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 22:30:30 +0300 Subject: [PATCH 8/8] add hud.hand_controller --- doc/en/scripting/builtins/libhud.md | 3 +++ doc/ru/scripting/builtins/libhud.md | 3 +++ res/scripts/hud.lua | 8 ++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libhud.md b/doc/en/scripting/builtins/libhud.md index 06766422..58d78302 100644 --- a/doc/en/scripting/builtins/libhud.md +++ b/doc/en/scripting/builtins/libhud.md @@ -65,4 +65,7 @@ hud.is_inventory_open() -> bool -- Sets whether to allow pausing. If false, the pause menu will not pause the game. hud.set_allow_pause(flag: bool) + +-- Function that controls the named skeleton 'hand' (see gfx.skeletons) +hud.hand_controller: function() ``` diff --git a/doc/ru/scripting/builtins/libhud.md b/doc/ru/scripting/builtins/libhud.md index 311542cb..5f0856b3 100644 --- a/doc/ru/scripting/builtins/libhud.md +++ b/doc/ru/scripting/builtins/libhud.md @@ -68,4 +68,7 @@ hud.is_inventory_open() -> bool -- Устанавливает разрешение на паузу. При значении false меню паузы не приостанавливает игру. hud.set_allow_pause(flag: bool) + +-- Функция, управляющая именованным скелетом 'hand' (см. gfx.skeletons) +hud.hand_controller: function() ``` diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index e189d098..abd1eaf4 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -83,7 +83,7 @@ function on_hud_open() configure_SSAO() end -function update_hand() +local function update_hand() local skeleton = gfx.skeletons local pid = hud.get_player() local invid, slot = player.get_inventory(pid) @@ -117,5 +117,9 @@ function update_hand() end function on_hud_render() - timeit(1, update_hand) + if hud.hand_controller then + hud.hand_controller() + else + update_hand() + end end