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