60 if (!
model || !skeleton) {
61 throw Exception(
"AnimSkin.fromModel: null model/skeleton");
63 const aiMesh *
mesh =
model->getMesh(meshIndex);
65 throw Exception(
"AnimSkin.fromModel: invalid mesh index %d", meshIndex);
67 if (!
mesh->HasBones() ||
mesh->mNumBones == 0) {
68 throw Exception(
"AnimSkin.fromModel: mesh %d has no bones", meshIndex);
70 if (
mesh->mNumVertices == 0 || !
mesh->mVertices) {
71 throw Exception(
"AnimSkin.fromModel: mesh %d has no vertices", meshIndex);
75 skin->vertexCount_ =
static_cast<int>(
mesh->mNumVertices);
76 skin->bindPos_.resize(
static_cast<size_t>(skin->vertexCount_) * 3u);
77 for (
int v = 0;
v < skin->vertexCount_; ++
v) {
78 const aiVector3D &
p =
mesh->mVertices[
v];
79 skin->bindPos_[
static_cast<size_t>(
v) * 3u + 0] =
p.x;
80 skin->bindPos_[
static_cast<size_t>(
v) * 3u + 1] =
p.y;
81 skin->bindPos_[
static_cast<size_t>(
v) * 3u + 2] =
p.z;
85 std::vector<int> meshBoneToSkin(
mesh->mNumBones, -1);
87 for (
unsigned b = 0;
b <
mesh->mNumBones; ++
b) {
88 const aiBone *bone =
mesh->mBones[
b];
90 const std::string
name = bone->mName.C_Str();
92 if (skBone < 0)
continue;
93 meshBoneToSkin[
b] =
static_cast<int>(skin->skeletonBone_.size());
94 skin->skeletonBone_.push_back(skBone);
95 skin->skinBoneNames_.push_back(
name);
96 skin->inverseBind_.push_back(fromAiMatrix(bone->mOffsetMatrix));
101 throw Exception(
"AnimSkin.fromModel: no mesh bones matched skeleton names");
109 std::vector<std::vector<Acc>> acc(
static_cast<size_t>(skin->vertexCount_));
111 for (
unsigned b = 0;
b <
mesh->mNumBones; ++
b) {
112 const int skinBone = meshBoneToSkin[
b];
113 if (skinBone < 0)
continue;
114 const aiBone *bone =
mesh->mBones[
b];
116 for (
unsigned w = 0;
w < bone->mNumWeights; ++
w) {
117 const aiVertexWeight &vw = bone->mWeights[
w];
118 if (vw.mVertexId >=
mesh->mNumVertices)
continue;
119 if (vw.mWeight <= 0.f)
continue;
120 acc[vw.mVertexId].push_back(Acc{skinBone, vw.mWeight});
124 skin->influences_.assign(
static_cast<size_t>(skin->vertexCount_) *
kMaxInfluences, Influence{});
125 for (
int v = 0;
v < skin->vertexCount_; ++
v) {
126 auto &list = acc[
static_cast<size_t>(
v)];
127 std::sort(list.begin(), list.end(),
128 [](
const Acc &
a,
const Acc &
b) { return a.weight > b.weight; });
130 std::unordered_map<int, float> merged;
131 for (
const Acc &
a : list) {
132 if (
a.bone < 0)
continue;
133 merged[
a.bone] +=
a.weight;
136 for (
const auto &kv : merged) list.push_back(Acc{kv.first, kv.second});
137 std::sort(list.begin(), list.end(),
138 [](
const Acc &
a,
const Acc &
b) { return a.weight > b.weight; });
141 const int take = std::min(
kMaxInfluences,
static_cast<int>(list.size()));
142 for (
int i = 0; i < take; ++i) sum += list[static_cast<size_t>(i)].weight;
143 const float inv = (sum > 1e-8f) ? (1.f / sum) : 0.f;
144 for (
int i = 0; i < take; ++i) {
145 Influence &dst = skin->influences_[
static_cast<size_t>(
v) *
kMaxInfluences + i];
146 dst.bone = list[
static_cast<size_t>(i)].bone;
147 dst.weight = list[
static_cast<size_t>(i)].weight * inv;
207void AnimSkin::skinPositions(
const AnimPose *pose,
float *outPosXYZ)
const {
208 if (!pose)
throw Exception(
"AnimSkin.skinPositions: pose is null");
209 if (!outPosXYZ)
throw Exception(
"AnimSkin.skinPositions: outPosXYZ is null");
210 if (vertexCount_ <= 0)
return;
213 std::vector<Mat4> skinMats(inverseBind_.size());
214 for (
size_t j = 0; j < inverseBind_.size(); ++j) {
215 const int skelBone = skeletonBone_[j];
216 const Mat4 world = Mat4::fromTRS(pose->
world(skelBone));
217 skinMats[j] = Mat4::mul(world, inverseBind_[j]);
220 for (
int v = 0;
v < vertexCount_; ++
v) {
221 const float bx = bindPos_[
static_cast<size_t>(
v) * 3u + 0];
222 const float by = bindPos_[
static_cast<size_t>(
v) * 3u + 1];
223 const float bz = bindPos_[
static_cast<size_t>(
v) * 3u + 2];
224 float ox = 0.f, oy = 0.f, oz = 0.f;
226 for (
int i = 0; i < kMaxInfluences; ++i) {
227 const Influence &inf =
228 influences_[
static_cast<size_t>(
v) * kMaxInfluences + i];
229 if (inf.bone < 0 || inf.weight <= 0.f)
continue;
231 skinMats[
static_cast<size_t>(inf.bone)].transformPoint(bx, by, bz,
px, py, pz);
232 ox +=
px * inf.weight;
233 oy += py * inf.weight;
234 oz += pz * inf.weight;
242 outPosXYZ[
static_cast<size_t>(
v) * 3u + 0] = ox;
243 outPosXYZ[
static_cast<size_t>(
v) * 3u + 1] = oy;
244 outPosXYZ[
static_cast<size_t>(
v) * 3u + 2] = oz;
248bool AnimSkin::skinPositionsTo(
const AnimPose *pose, std::vector<float> &outPosXYZ)
const {
249 if (!pose || vertexCount_ <= 0)
return false;
250 outPosXYZ.resize(
static_cast<size_t>(vertexCount_) * 3u);
251 skinPositions(pose, outPosXYZ.data());
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...