载入中...
搜索中...
未找到
AnimSkinFromModel.cpp
浏览该文件的文档.
3
4#include "common/Exception.h"
5#include "model3d/ModelData.h"
6
7#include <assimp/matrix4x4.h>
8#include <assimp/mesh.h>
9#include <assimp/scene.h>
10
11#include <algorithm>
12#include <unordered_map>
13#include <vector>
14
15namespace eve::animation {
16
17namespace {
18
19Mat4 fromAiMatrix(const aiMatrix4x4& m) {
20 // Assimp is row-major; convert to column-major Mat4.
21 Mat4 out;
22 out.m[0] = m.a1;
23 out.m[1] = m.b1;
24 out.m[2] = m.c1;
25 out.m[3] = m.d1;
26 out.m[4] = m.a2;
27 out.m[5] = m.b2;
28 out.m[6] = m.c2;
29 out.m[7] = m.d2;
30 out.m[8] = m.a3;
31 out.m[9] = m.b3;
32 out.m[10] = m.c3;
33 out.m[11] = m.d3;
34 out.m[12] = m.a4;
35 out.m[13] = m.b4;
36 out.m[14] = m.c4;
37 out.m[15] = m.d4;
38 return out;
39}
40
41} // namespace
42
43AnimSkin* AnimSkin::fromModel(const model3d::ModelData* model, int meshIndex, const AnimSkeleton* skeleton) {
44 if (!model || !skeleton) {
45 throw Exception("AnimSkin.fromModel: null model/skeleton");
46 }
47 const aiMesh* mesh = model->getMesh(meshIndex);
48 if (!mesh) {
49 throw Exception("AnimSkin.fromModel: invalid mesh index %d", meshIndex);
50 }
51 if (!mesh->HasBones() || mesh->mNumBones == 0) {
52 throw Exception("AnimSkin.fromModel: mesh %d has no bones", meshIndex);
53 }
54 if (mesh->mNumVertices == 0 || !mesh->mVertices) {
55 throw Exception("AnimSkin.fromModel: mesh %d has no vertices", meshIndex);
56 }
57
58 auto* skin = new AnimSkin();
59 skin->vertexCount_ = static_cast<int>(mesh->mNumVertices);
60 skin->bindPos_.resize(static_cast<size_t>(skin->vertexCount_) * 3u);
61 for (int v = 0; v < skin->vertexCount_; ++v) {
62 const aiVector3D& p = mesh->mVertices[v];
63 skin->bindPos_[static_cast<size_t>(v) * 3u + 0] = p.x;
64 skin->bindPos_[static_cast<size_t>(v) * 3u + 1] = p.y;
65 skin->bindPos_[static_cast<size_t>(v) * 3u + 2] = p.z;
66 }
67 if (mesh->HasNormals() && mesh->mNormals) {
68 skin->bindNrm_.resize(static_cast<size_t>(skin->vertexCount_) * 3u);
69 for (int v = 0; v < skin->vertexCount_; ++v) {
70 const aiVector3D& n = mesh->mNormals[v];
71 skin->bindNrm_[static_cast<size_t>(v) * 3u + 0] = n.x;
72 skin->bindNrm_[static_cast<size_t>(v) * 3u + 1] = n.y;
73 skin->bindNrm_[static_cast<size_t>(v) * 3u + 2] = n.z;
74 }
75 }
76
77 // Gather skin joints that map onto the skeleton by name.
78 std::vector<int> meshBoneToSkin(mesh->mNumBones, -1);
79 int matched = 0;
80 for (unsigned b = 0; b < mesh->mNumBones; ++b) {
81 const aiBone* bone = mesh->mBones[b];
82 if (!bone) continue;
83 const std::string name = bone->mName.C_Str();
84 const int skBone = skeleton->findBone(name);
85 if (skBone < 0) continue;
86 meshBoneToSkin[b] = static_cast<int>(skin->skeletonBone_.size());
87 skin->skeletonBone_.push_back(skBone);
88 skin->skinBoneNames_.push_back(name);
89 skin->inverseBind_.push_back(fromAiMatrix(bone->mOffsetMatrix));
90 ++matched;
91 }
92 if (matched == 0) {
93 delete skin;
94 throw Exception("AnimSkin.fromModel: no mesh bones matched skeleton names");
95 }
96
97 // Accumulate all influences then keep the top-k per vertex (renormalize).
98 struct Acc {
99 int bone = -1;
100 float weight = 0.f;
101 };
102 std::vector<std::vector<Acc>> acc(static_cast<size_t>(skin->vertexCount_));
103
104 for (unsigned b = 0; b < mesh->mNumBones; ++b) {
105 const int skinBone = meshBoneToSkin[b];
106 if (skinBone < 0) continue;
107 const aiBone* bone = mesh->mBones[b];
108 if (!bone) continue;
109 for (unsigned w = 0; w < bone->mNumWeights; ++w) {
110 const aiVertexWeight& vw = bone->mWeights[w];
111 if (vw.mVertexId >= mesh->mNumVertices) continue;
112 if (vw.mWeight <= 0.f) continue;
113 acc[vw.mVertexId].push_back(Acc{skinBone, vw.mWeight});
114 }
115 }
116
117 skin->influences_.assign(static_cast<size_t>(skin->vertexCount_) * kMaxInfluences, Influence{});
118 for (int v = 0; v < skin->vertexCount_; ++v) {
119 auto& list = acc[static_cast<size_t>(v)];
120 std::sort(list.begin(), list.end(), [](const Acc& a, const Acc& b) { return a.weight > b.weight; });
121 // Merge duplicate bone entries.
122 std::unordered_map<int, float> merged;
123 for (const Acc& a : list) {
124 if (a.bone < 0) continue;
125 merged[a.bone] += a.weight;
126 }
127 list.clear();
128 for (const auto& kv : merged) list.push_back(Acc{kv.first, kv.second});
129 std::sort(list.begin(), list.end(), [](const Acc& a, const Acc& b) { return a.weight > b.weight; });
130
131 float sum = 0.f;
132 const int take = std::min(kMaxInfluences, static_cast<int>(list.size()));
133 for (int i = 0; i < take; ++i) sum += list[static_cast<size_t>(i)].weight;
134 const float inv = (sum > 1e-8f) ? (1.f / sum) : 0.f;
135 for (int i = 0; i < take; ++i) {
136 Influence& dst = skin->influences_[static_cast<size_t>(v) * kMaxInfluences + i];
137 dst.bone = list[static_cast<size_t>(i)].bone;
138 dst.weight = list[static_cast<size_t>(i)].weight * inv;
139 }
140 // Vertices with no weights stay at bind (weight sum 0 → identity path in skin).
141 }
142
143 return skin;
144}
145
146} // namespace eve::animation
float w
Definition AnimClip.cpp:738
glm::vec4 p[6]
glm::vec3 n
Definition Grass.cpp:63
float v
std::uint32_t bone
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Mesh * mesh
glm::mat4 model
float m[16]
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int findBone(const std::string &name) const
Finds bone.
CPU linear-blend skinning binding for one mesh against an AnimSkeleton.
Definition AnimSkin.h:47
static AnimSkin * fromModel(const model3d::ModelData *model, int meshIndex, const AnimSkeleton *skeleton)
Build skin binding for meshIndex on model, mapping bone names onto skeleton. Throws if the mesh has n...
AnimSkin()=default
Anim skin.
static constexpr int kMaxInfluences
Definition AnimSkin.h:49
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:39