From 374fdb10776eb9463c4019e94688b85775244522 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Wed, 11 Feb 2026 14:31:46 +0100 Subject: [PATCH] Skinning --- CMakeLists.txt | 2 ++ src/assets/mesh_builder.hpp | 4 +-- src/assets/model.cpp | 21 +++++++++++++-- src/assets/model.hpp | 4 ++- src/game/character.cpp | 1 + src/game/openworld.cpp | 1 - src/gameview/characterview.cpp | 47 +++++++++++++++++++++++++--------- src/gameview/characterview.hpp | 5 ++++ src/gameview/skinning_ubo.cpp | 23 +++++++++++++++++ src/gameview/skinning_ubo.hpp | 22 ++++++++++++++++ src/gfx/draw_list.hpp | 4 ++- src/gfx/renderer.cpp | 8 ++++++ src/gfx/shader_defs.hpp | 2 +- src/gfx/shader_sources.cpp | 28 +++++++++++++++----- 14 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 src/gameview/skinning_ubo.cpp create mode 100644 src/gameview/skinning_ubo.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 87d9fd7..43990e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ set(CLIENT_ONLY_SOURCES "src/gameview/client_session.cpp" "src/gameview/entityview.hpp" "src/gameview/entityview.cpp" + "src/gameview/skinning_ubo.hpp" + "src/gameview/skinning_ubo.cpp" "src/gameview/vehicleview.hpp" "src/gameview/vehicleview.cpp" "src/gameview/worldview.hpp" diff --git a/src/assets/mesh_builder.hpp b/src/assets/mesh_builder.hpp index 2be9f83..765f7bd 100644 --- a/src/assets/mesh_builder.hpp +++ b/src/assets/mesh_builder.hpp @@ -11,8 +11,8 @@ namespace assets struct MeshVertexBoneInfluence { - int bone_index; - float weight; + int bone_index = -1; + float weight = 0.0f; }; struct MeshVertex diff --git a/src/assets/model.cpp b/src/assets/model.cpp index e0b8a93..b5b7f8c 100644 --- a/src/assets/model.cpp +++ b/src/assets/model.cpp @@ -27,7 +27,15 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri v.uv.y = 1.0f - v.uv.y; // FLIP FOR GL - // TODO: LUV & bone data + if (model->skeleton_) + { + size_t num_bones = 0; + iss >> num_bones; + for (size_t i = 0; i < num_bones; ++i) + { + iss >> v.bones[i].bone_index >> v.bones[i].weight; + } + } mb.AddVertex(v); ) @@ -48,6 +56,7 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri t.vert[0] = indices[0]; t.vert[1] = indices[1]; t.vert[2] = indices[2]; + mb.AddTriangle(t); ) @@ -97,7 +106,7 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri std::shared_ptr texture; if (!texture_name.empty()) { - texture = CacheManager::GetTexture("data/" + surface_name + ".png"); + texture = CacheManager::GetTexture("data/" + texture_name + ".png"); } mb.BeginSurface(sflags, surface_name, texture); @@ -111,10 +120,18 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri { temp_hull = std::make_unique(); } + else if (command == "skeleton") + { + std::string skel_name; + iss >> skel_name; + model->skeleton_ = CacheManager::GetSkeleton("data/" + skel_name + ".sk"); + CLIENT_ONLY(mb.SetMeshFlag(gfx::MF_SKELETAL)); + } else { throw std::runtime_error("Unknown command in model file: " + command); } + // TODO: skeleton }); diff --git a/src/assets/model.hpp b/src/assets/model.hpp index 5d57c50..9ae103b 100644 --- a/src/assets/model.hpp +++ b/src/assets/model.hpp @@ -3,8 +3,8 @@ #include #include +#include "skeleton.hpp" #include "utils/defs.hpp" - #include "collision/trianglemesh.hpp" #ifdef CLIENT @@ -43,6 +43,7 @@ public: const collision::TriangleMesh* GetColMesh() const { return cmesh_.get(); } btCollisionShape* GetColShape() const { return cshape_.get(); } + const std::shared_ptr& GetSkeleton() const { return skeleton_; } CLIENT_ONLY(const std::shared_ptr& GetMesh() const { return mesh_; }) private: @@ -50,6 +51,7 @@ private: // std::vector cshapes_; std::unique_ptr cshape_; + std::shared_ptr skeleton_; CLIENT_ONLY(std::shared_ptr mesh_;) }; diff --git a/src/game/character.cpp b/src/game/character.cpp index c5043e9..be380c8 100644 --- a/src/game/character.cpp +++ b/src/game/character.cpp @@ -48,6 +48,7 @@ void game::Character::Update() auto bt_trans = bt_ghost_.getWorldTransform(); root_.local.SetBtTransform(bt_trans); + root_.local.position.z -= shape_.height * 0.5f + shape_.radius - 0.05f; // foot pos UpdateMovement(); SendUpdateMsg(); diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index 24e2ed3..26198de 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -214,7 +214,6 @@ void game::OpenWorld::PlayerInput(Player& player, PlayerInputType type, bool ena void game::OpenWorld::PlayerViewAnglesChanged(Player& player, float yaw, float pitch) { auto character = player_characters_.at(&player); - std::cout << "player aiming " << yaw << " " << pitch <SetForwardYaw(yaw); } diff --git a/src/gameview/characterview.cpp b/src/gameview/characterview.cpp index 943ca99..3fc2f62 100644 --- a/src/gameview/characterview.cpp +++ b/src/gameview/characterview.cpp @@ -1,11 +1,14 @@ #include "characterview.hpp" #include "assets/cache.hpp" +#include "assets/model.hpp" #include "net/utils.hpp" -game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg) : EntityView(world, msg) +game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg) : EntityView(world, msg), ubo_(sk_) { - - sk_ = SkeletonInstance(assets::CacheManager::GetSkeleton("data/human.sk"), &root_); + basemodel_ = assets::CacheManager::GetModel("data/human.mdl"); + sk_ = SkeletonInstance(basemodel_->GetSkeleton(), &root_); + ubo_.Update(); + ubo_valid_ = true; } bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage& msg) @@ -26,6 +29,7 @@ void game::view::CharacterView::Update(const UpdateInfo& info) sk_.ApplySkelAnim(*anim, info.time, 1.0f); root_.UpdateMatrix(); sk_.UpdateBoneMatrices(); + ubo_valid_ = false; } void game::view::CharacterView::Draw(const DrawArgs& args) @@ -40,17 +44,35 @@ void game::view::CharacterView::Draw(const DrawArgs& args) end = start + glm::vec3(glm::cos(yaw_), glm::sin(yaw_), 0.0f) * 0.5f; args.dlist.AddBeam(start, end, 0xFF007700, 0.05f); - // draw bones debug - const auto& bone_nodes = sk_.GetBoneNodes(); - for (const auto& bone_node : bone_nodes) + //// draw bones debug + //const auto& bone_nodes = sk_.GetBoneNodes(); + //for (const auto& bone_node : bone_nodes) + //{ + // if (!bone_node.parent) + // continue; + + // glm::vec3 p0 = bone_node.parent->matrix[3]; + // glm::vec3 p1 = bone_node.matrix[3]; + + // args.dlist.AddBeam(p0, p1, 0xFF00EEEE, 0.01f); + //} + + // draw human + + if (!ubo_valid_) { - if (!bone_node.parent) - continue; + ubo_.Update(); + ubo_valid_ = true; + } - glm::vec3 p0 = bone_node.parent->matrix[3]; - glm::vec3 p1 = bone_node.matrix[3]; - - args.dlist.AddBeam(p0, p1, 0xFF00EEEE, 0.01f); + const auto& mesh = *basemodel_->GetMesh(); + for (const auto& surface : mesh.surfaces) + { + gfx::DrawSurfaceCmd cmd; + cmd.surface = &surface; + cmd.matrices = &root_.matrix; + cmd.skinning = &ubo_; + args.dlist.AddSurface(cmd); } } @@ -61,6 +83,7 @@ bool game::view::CharacterView::ProcessUpdateMsg(net::InMessage& msg) return false; net::DecodePosition(posq, root_.local.position); + root_.local.rotation = glm::rotate(glm::quat(1.0f, 0.0f, 0.0f, 0.0f), yaw_ + glm::pi() * 0.5f, glm::vec3(0, 0, 1)); return true; } diff --git a/src/gameview/characterview.hpp b/src/gameview/characterview.hpp index 89dcd34..fd423e0 100644 --- a/src/gameview/characterview.hpp +++ b/src/gameview/characterview.hpp @@ -1,7 +1,9 @@ #pragma once #include "entityview.hpp" +#include "assets/model.hpp" #include "game/skeletoninstance.hpp" +#include "skinning_ubo.hpp" namespace game::view { @@ -24,7 +26,10 @@ private: private: float yaw_ = 0.0f; + std::shared_ptr basemodel_; SkeletonInstance sk_; + SkinningUBO ubo_; + bool ubo_valid_ = false; }; diff --git a/src/gameview/skinning_ubo.cpp b/src/gameview/skinning_ubo.cpp new file mode 100644 index 0000000..7a035ae --- /dev/null +++ b/src/gameview/skinning_ubo.cpp @@ -0,0 +1,23 @@ +#include "skinning_ubo.hpp" +#include "gfx/shader_defs.hpp" + +game::view::SkinningUBO::SkinningUBO(const SkeletonInstance& sk) : sk_(sk) +{ +} + +void game::view::SkinningUBO::Update() +{ + static glm::mat4 skin_mats[SD_MAX_BONES]; + + const auto& skeleton = sk_.GetSkeleton(); + size_t num_mats = std::min(skeleton->GetNumBones(), static_cast(SD_MAX_BONES)); + + for (size_t i = 0; i < num_mats; ++i) + { + const auto& bone = skeleton->GetBone(i); + const TransformNode& node = sk_.GetBoneNode(i); + skin_mats[i] = node.matrix * bone.inv_bind_matrix; + } + + SetData(skin_mats, num_mats); +} diff --git a/src/gameview/skinning_ubo.hpp b/src/gameview/skinning_ubo.hpp new file mode 100644 index 0000000..b7cf46d --- /dev/null +++ b/src/gameview/skinning_ubo.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "gfx/uniform_buffer.hpp" +#include "game/skeletoninstance.hpp" + +namespace game::view +{ + +class SkinningUBO : public gfx::UniformBuffer +{ +public: + SkinningUBO(const SkeletonInstance& sk); + void Update(); + +private: + const SkeletonInstance& sk_; + +}; + + + +} \ No newline at end of file diff --git a/src/gfx/draw_list.hpp b/src/gfx/draw_list.hpp index e1f72e3..0788ee9 100644 --- a/src/gfx/draw_list.hpp +++ b/src/gfx/draw_list.hpp @@ -5,6 +5,7 @@ #include "assets/skeleton.hpp" #include "hud.hpp" #include "surface.hpp" +#include "uniform_buffer.hpp" namespace gfx { @@ -12,11 +13,12 @@ namespace gfx struct DrawSurfaceCmd { const Surface* surface = nullptr; - const glm::mat4* matrices = nullptr; // model matrix, continues in array of matrices for skeletal meshes + const glm::mat4* matrices = nullptr; // model matrix const glm::vec4* color = nullptr; // optional tint uint32_t first = 0; // first triangle index uint32_t count = 0; // num triangles float dist = 0.0f; // distance to camera - for transparnt sorting + const UniformBuffer* skinning = nullptr; // skinning matrices for skeletal meshes }; struct DrawBeamCmd diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index b11b053..d3346b7 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -167,6 +167,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi // cache to eliminate fake state changes const gfx::Texture* last_texture = nullptr; const gfx::VertexArray* last_vao = nullptr; + const gfx::UniformBuffer* last_skin = nullptr; InvalidateShaders(); // enable depth test @@ -290,6 +291,13 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi last_texture = surface->texture.get(); } + // bind skinning UBO + if (cmd.skinning && last_skin != cmd.skinning) + { + glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId()); + last_skin = cmd.skinning; + } + // bind VAO if (last_vao != surface->va.get()) { diff --git a/src/gfx/shader_defs.hpp b/src/gfx/shader_defs.hpp index 23efe72..b09d14b 100644 --- a/src/gfx/shader_defs.hpp +++ b/src/gfx/shader_defs.hpp @@ -1,7 +1,7 @@ #pragma once #define SD_MAX_LIGHTS 4 -#define SD_MAX_BONES 256 +#define SD_MAX_BONES 128 #define SHF_CULL_ALPHA 1 #define SHF_BACKGROUND 2 \ No newline at end of file diff --git a/src/gfx/shader_sources.cpp b/src/gfx/shader_sources.cpp index d12503c..85e8c23 100644 --- a/src/gfx/shader_sources.cpp +++ b/src/gfx/shader_sources.cpp @@ -141,6 +141,7 @@ SHADER_HEADER R"GLSL( layout (location = 0) in vec3 a_pos; layout (location = 1) in vec3 a_normal; +layout (location = 2) in vec4 a_color; layout (location = 3) in vec2 a_uv; layout (location = 5) in ivec4 a_bone_ids; layout (location = 6) in vec4 a_bone_weights; @@ -156,7 +157,6 @@ COMPUTE_LIGHTS_GLSL R"GLSL( out vec2 v_uv; - out vec3 v_color; void main() { @@ -168,13 +168,12 @@ void main() { } } - vec4 world_pos = u_model * bone_transform * vec4(a_pos, 1.0); - vec3 world_normal = normalize(mat3(u_model) * mat3(bone_transform) * a_normal); + vec4 world_pos = bone_transform * vec4(a_pos, 1.0); + vec3 world_normal = normalize(mat3(bone_transform) * a_normal); gl_Position = u_view_proj * world_pos; v_uv = a_uv; - - v_color = ComputeLights(world_pos.xyz, world_normal); + v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb; } )GLSL", @@ -182,16 +181,33 @@ void main() { SHADER_HEADER R"GLSL( in vec2 v_uv; - in vec3 v_color; +#define SHF_CULL_ALPHA 1 +#define SHF_BACKGROUND 2 + uniform sampler2D u_tex; +uniform vec4 u_color; +uniform int u_flags; layout (location = 0) out vec4 o_color; void main() { o_color = vec4(texture(u_tex, v_uv)); + + if ((u_flags & SHF_CULL_ALPHA) > 0) + { + if (o_color.a < 0.5) + discard; + } + else if ((u_flags & SHF_BACKGROUND) > 0) + { + // blend with bg + o_color = mix(u_color, o_color, o_color.a); + } + o_color.rgb *= v_color; // Apply vertex color + //o_color = vec4(1.0, 0.0, 0.0, 1.0); } )GLSL",