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