Merge pull request #572 from MihailRis/hand-skeleton

Hand skeleton
This commit is contained in:
MihailRis
2025-07-27 22:45:37 +03:00
committed by GitHub
20 changed files with 385 additions and 118 deletions
+8
View File
@@ -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()) {
+1
View File
@@ -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);
+34
View File
@@ -0,0 +1,34 @@
#include "HandsRenderer.hpp"
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "ModelBatch.hpp"
#include "content/Content.hpp"
#include "graphics/commons/Model.hpp"
#include "objects/rigging.hpp"
#include "window/Camera.hpp"
using namespace rigging;
HandsRenderer::HandsRenderer(
const Assets& assets,
ModelBatch& modelBatch,
std::shared_ptr<Skeleton> skeleton
)
: assets(assets),
modelBatch(modelBatch),
skeleton(std::move(skeleton)) {
}
void HandsRenderer::renderHands(
const Camera& camera, float delta
) {
auto& skeleton = *this->skeleton;
const auto& config = *skeleton.config;
// 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());
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <memory>
class Assets;
class Camera;
class ModelBatch;
namespace rigging {
struct Skeleton;
}
class HandsRenderer {
public:
HandsRenderer(
const Assets& assets,
ModelBatch& modelBatch,
std::shared_ptr<rigging::Skeleton> skeleton
);
void renderHands(const Camera& camera, float delta);
private:
const Assets& assets;
ModelBatch& modelBatch;
std::shared_ptr<rigging::Skeleton> skeleton;
};
+23
View File
@@ -0,0 +1,23 @@
#include "NamedSkeletons.hpp"
#include "objects/rigging.hpp"
using namespace rigging;
NamedSkeletons::NamedSkeletons() = default;
std::shared_ptr<rigging::Skeleton> NamedSkeletons::createSkeleton(
const std::string& name, const SkeletonConfig* config
) {
auto skeleton = std::make_shared<Skeleton>(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();
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
namespace rigging {
struct Skeleton;
class SkeletonConfig;
}
class NamedSkeletons {
public:
NamedSkeletons();
std::shared_ptr<rigging::Skeleton> createSkeleton(
const std::string& name, const rigging::SkeletonConfig* config
);
rigging::Skeleton* getSkeleton(const std::string& name);
private:
std::unordered_map<std::string, std::shared_ptr<rigging::Skeleton>> skeletons;
};
+27 -65
View File
@@ -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,15 @@ WorldRenderer::WorldRenderer(
settings.graphics.skyboxResolution.get(),
assets->require<Shader>("skybox_gen")
);
const auto& content = level.content;
skeletons = std::make_unique<NamedSkeletons>();
const auto& skeletonConfig = content.requireSkeleton(
content.getDefaults()["hand-skeleton"].asString()
);
hands = std::make_unique<HandsRenderer>(
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
);
}
WorldRenderer::~WorldRenderer() = default;
@@ -273,70 +284,6 @@ void WorldRenderer::renderLines(
}
}
void WorldRenderer::renderHands(
const Camera& camera, float delta
) {
auto& entityShader = assets.require<Shader>("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<float>(delta * speed));
rotation = glm::mat4_cast(finalRot);
matrix = rotation * matrix *
glm::rotate(
glm::mat4(1.0f), -glm::pi<float>() * 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<model::Model>(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 +515,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);
+4 -2
View File
@@ -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> modelBatch;
std::unique_ptr<GuidesRenderer> guides;
std::unique_ptr<ChunksRenderer> chunks;
std::unique_ptr<HandsRenderer> hands;
std::unique_ptr<Skybox> skybox;
std::unique_ptr<ShadowMap> shadowMap;
std::unique_ptr<ShadowMap> 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<TextsRenderer> texts;
std::unique_ptr<BlockWrapsRenderer> blockWraps;
std::unique_ptr<PrecipitationRenderer> precipitation;
std::unique_ptr<NamedSkeletons> skeletons;
static bool showChunkBorders;
static bool showEntitiesDebug;
+61 -51
View File
@@ -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
) {
@@ -13,25 +20,33 @@ static int index_range_check(
return static_cast<int>(index);
}
static int l_get_model(lua::State* L) {
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)) {
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 +57,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 +83,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,62 +100,60 @@ 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;
}
static int l_exists(lua::State* L) {
return lua::pushboolean(L, get_skeleton(L));
}
const luaL_Reg skeletonlib[] = {
{"get_model", lua::wrap<l_get_model>},
{"set_model", lua::wrap<l_set_model>},
@@ -160,4 +168,6 @@ const luaL_Reg skeletonlib[] = {
{"set_color", lua::wrap<l_set_color>},
{"is_interpolated", lua::wrap<l_is_interpolated>},
{"set_interpolated", lua::wrap<l_set_interpolated>},
{NULL, NULL}};
{"exists", lua::wrap<l_exists>},
{NULL, NULL}
};