44 if (!
model || !skeleton) {
45 throw Exception(
"AnimSkin.fromModel: null model/skeleton");
47 const aiMesh*
mesh =
model->getMesh(meshIndex);
49 throw Exception(
"AnimSkin.fromModel: invalid mesh index %d", meshIndex);
51 if (!
mesh->HasBones() ||
mesh->mNumBones == 0) {
52 throw Exception(
"AnimSkin.fromModel: mesh %d has no bones", meshIndex);
54 if (
mesh->mNumVertices == 0 || !
mesh->mVertices) {
55 throw Exception(
"AnimSkin.fromModel: mesh %d has no vertices", meshIndex);
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;
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;
78 std::vector<int> meshBoneToSkin(
mesh->mNumBones, -1);
80 for (
unsigned b = 0;
b <
mesh->mNumBones; ++
b) {
83 const std::string
name =
bone->mName.C_Str();
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));
94 throw Exception(
"AnimSkin.fromModel: no mesh bones matched skeleton names");
102 std::vector<std::vector<Acc>> acc(
static_cast<size_t>(skin->vertexCount_));
104 for (
unsigned b = 0;
b <
mesh->mNumBones; ++
b) {
105 const int skinBone = meshBoneToSkin[
b];
106 if (skinBone < 0)
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});
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; });
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;
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; });
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;
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...