From 5632c9ccfead7210bf2f71ccc523783a5f43787e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Jul 2025 02:45:25 +0300 Subject: [PATCH] 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;