/src/assimp/code/PostProcessing/ArmaturePopulate.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2026, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above |
14 | | copyright notice, this list of conditions and the |
15 | | following disclaimer. |
16 | | |
17 | | * Redistributions in binary form must reproduce the above |
18 | | copyright notice, this list of conditions and the |
19 | | following disclaimer in the documentation and/or other |
20 | | materials provided with the distribution. |
21 | | |
22 | | * Neither the name of the assimp team, nor the names of its |
23 | | contributors may be used to endorse or promote products |
24 | | derived from this software without specific prior |
25 | | written permission of the assimp team. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | ---------------------------------------------------------------------- |
39 | | */ |
40 | | #include "ArmaturePopulate.h" |
41 | | |
42 | | #include <assimp/BaseImporter.h> |
43 | | #include <assimp/DefaultLogger.hpp> |
44 | | #include <assimp/postprocess.h> |
45 | | #include <assimp/scene.h> |
46 | | |
47 | | namespace Assimp { |
48 | | |
49 | 0 | static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones) { |
50 | 0 | for (aiBone *bone : bones) { |
51 | 0 | if (bone->mName == bone_name) { |
52 | 0 | return true; |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | 0 | return false; |
57 | 0 | } |
58 | | |
59 | 11.3k | bool ArmaturePopulate::IsActive(unsigned int pFlags) const { |
60 | 11.3k | return (pFlags & aiProcess_PopulateArmatureData) != 0; |
61 | 11.3k | } |
62 | | |
63 | 0 | void ArmaturePopulate::SetupProperties(const Importer *) { |
64 | | // do nothing |
65 | 0 | } |
66 | | |
67 | 0 | void ArmaturePopulate::Execute(aiScene *out) { |
68 | | |
69 | | // Now convert all bone positions to the correct mOffsetMatrix |
70 | 0 | std::vector<aiBone *> bones; |
71 | 0 | std::vector<aiNode *> nodes; |
72 | 0 | std::map<aiBone *, aiNode *> bone_stack; |
73 | 0 | BuildBoneList(out->mRootNode, out->mRootNode, out, bones); |
74 | 0 | BuildNodeList(out->mRootNode, nodes); |
75 | |
|
76 | 0 | BuildBoneStack(out->mRootNode, out, bones, bone_stack, nodes); |
77 | |
|
78 | 0 | ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size()); |
79 | |
|
80 | 0 | for (std::pair<aiBone *, aiNode *> kvp : bone_stack) { |
81 | 0 | aiBone *bone = kvp.first; |
82 | 0 | aiNode *bone_node = kvp.second; |
83 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str()); |
84 | | |
85 | | // lcl transform grab - done in generate_nodes :) |
86 | 0 | aiNode *armature = GetArmatureRoot(bone_node, bones); |
87 | |
|
88 | 0 | ai_assert(armature); |
89 | |
|
90 | 0 | #ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS |
91 | | // set up bone armature id |
92 | 0 | bone->mArmature = armature; |
93 | | |
94 | | // set this bone node to be referenced properly |
95 | 0 | ai_assert(bone_node); |
96 | 0 | bone->mNode = bone_node; |
97 | 0 | #endif |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | |
102 | | // Reprocess all nodes to calculate bone transforms properly based on the REAL |
103 | | // mOffsetMatrix not the local. |
104 | | // Before this would use mesh transforms which is wrong for bone transforms |
105 | | // Before this would work for simple character skeletons but not complex meshes |
106 | | // with multiple origins |
107 | | // Source: sketch fab log cutter fbx |
108 | | void ArmaturePopulate::BuildBoneList(aiNode *current_node, |
109 | | const aiNode *root_node, |
110 | | const aiScene *scene, |
111 | 0 | std::vector<aiBone *> &bones) { |
112 | 0 | ai_assert(scene); |
113 | 0 | for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { |
114 | 0 | aiNode *child = current_node->mChildren[nodeId]; |
115 | 0 | ai_assert(child); |
116 | | |
117 | | // check for bones |
118 | 0 | for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) { |
119 | 0 | ai_assert(child->mMeshes); |
120 | 0 | unsigned int mesh_index = child->mMeshes[meshId]; |
121 | 0 | aiMesh *mesh = scene->mMeshes[mesh_index]; |
122 | 0 | ai_assert(mesh); |
123 | |
|
124 | 0 | for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { |
125 | 0 | aiBone *bone = mesh->mBones[boneId]; |
126 | 0 | ai_assert(nullptr != bone); |
127 | | |
128 | | // duplicate mehes exist with the same bones sometimes :) |
129 | | // so this must be detected |
130 | 0 | if (std::find(bones.begin(), bones.end(), bone) == bones.end()) { |
131 | | // add the element once |
132 | 0 | bones.emplace_back(bone); |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | | // find mesh and get bones |
137 | | // then do recursive lookup for bones in root node hierarchy |
138 | 0 | } |
139 | |
|
140 | 0 | BuildBoneList(child, root_node, scene, bones); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | // Prepare flat node list which can be used for non recursive lookups later |
145 | | void ArmaturePopulate::BuildNodeList(const aiNode *current_node, |
146 | 0 | std::vector<aiNode *> &nodes) { |
147 | 0 | ai_assert(nullptr != current_node); |
148 | |
|
149 | 0 | for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { |
150 | 0 | aiNode *child = current_node->mChildren[nodeId]; |
151 | 0 | ai_assert(child); |
152 | |
|
153 | 0 | if (child->mNumMeshes == 0) { |
154 | 0 | nodes.emplace_back(child); |
155 | 0 | } |
156 | |
|
157 | 0 | BuildNodeList(child, nodes); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | // A bone stack allows us to have multiple armatures, with the same bone names |
162 | | // A bone stack allows us also to retrieve bones true transform even with |
163 | | // duplicate names :) |
164 | | void ArmaturePopulate::BuildBoneStack(const aiNode *root_node, |
165 | | const aiScene*, |
166 | | const std::vector<aiBone *> &bones, |
167 | | std::map<aiBone *, aiNode *> &bone_stack, |
168 | 0 | std::vector<aiNode *> &node_stack) { |
169 | 0 | if (node_stack.empty()) { |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | ai_assert(nullptr != root_node); |
173 | |
|
174 | 0 | for (aiBone *bone : bones) { |
175 | 0 | ai_assert(bone); |
176 | 0 | aiNode *node = GetNodeFromStack(bone->mName, node_stack); |
177 | 0 | if (node == nullptr) { |
178 | 0 | node_stack.clear(); |
179 | 0 | BuildNodeList(root_node, node_stack); |
180 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("Resetting bone stack: nullptr element ", bone->mName.C_Str()); |
181 | |
|
182 | 0 | node = GetNodeFromStack(bone->mName, node_stack); |
183 | |
|
184 | 0 | if (nullptr == node) { |
185 | 0 | ASSIMP_LOG_ERROR("serious import issue node for bone was not detected"); |
186 | 0 | continue; |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); |
191 | |
|
192 | 0 | bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node)); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | // Returns the armature root node |
197 | | // This is required to be detected for a bone initially, it will recurse up |
198 | | // until it cannot find another bone and return the node No known failure |
199 | | // points. (yet) |
200 | 0 | aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone *> &bone_list) { |
201 | 0 | while (nullptr != bone_node) { |
202 | 0 | if (!IsBoneNode(bone_node->mName, bone_list)) { |
203 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); |
204 | 0 | return bone_node; |
205 | 0 | } |
206 | | |
207 | 0 | bone_node = bone_node->mParent; |
208 | 0 | } |
209 | | |
210 | 0 | ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!"); |
211 | |
|
212 | 0 | return nullptr; |
213 | 0 | } |
214 | | |
215 | | // Pop this node by name from the stack if found |
216 | | // Used in multiple armature situations with duplicate node / bone names |
217 | | // Known flaw: cannot have nodes with bone names, will be fixed in later release |
218 | | // (serious to be fixed) Known flaw: nodes which have more than one bone could |
219 | | // be prematurely dropped from stack |
220 | | aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, |
221 | 0 | std::vector<aiNode *> &nodes) { |
222 | 0 | std::vector<aiNode *>::iterator iter; |
223 | 0 | aiNode *found = nullptr; |
224 | 0 | for (iter = nodes.begin(); iter < nodes.end(); ++iter) { |
225 | 0 | aiNode *element = *iter; |
226 | 0 | ai_assert(nullptr != element); |
227 | | // node valid and node name matches |
228 | 0 | if (element->mName == node_name) { |
229 | 0 | found = element; |
230 | 0 | break; |
231 | 0 | } |
232 | 0 | } |
233 | |
|
234 | 0 | if (found != nullptr) { |
235 | 0 | ASSIMP_LOG_INFO("Removed node from stack: ", found->mName.C_Str()); |
236 | | // now pop the element from the node list |
237 | 0 | nodes.erase(iter); |
238 | |
|
239 | 0 | return found; |
240 | 0 | } |
241 | | |
242 | | // unique names can cause this problem |
243 | 0 | ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!"); |
244 | |
|
245 | 0 | return nullptr; |
246 | 0 | } |
247 | | |
248 | | } // Namespace Assimp |