Radium Engine  1.5.20
Loading...
Searching...
No Matches
AssimpHandleDataLoader.cpp
1#include <IO/AssimpLoader/AssimpHandleDataLoader.hpp>
2
3#include <assimp/mesh.h>
4#include <assimp/scene.h>
5#include <queue>
6#include <set>
7
8#include <Core/Asset/HandleData.hpp>
9#include <Core/Utils/Log.hpp>
10
11#include <IO/AssimpLoader/AssimpWrapper.hpp>
12
13namespace Ra {
14namespace IO {
15
16using namespace Core::Utils; // log
17using namespace Core::Asset;
18
20AssimpHandleDataLoader::AssimpHandleDataLoader( const bool VERBOSE_MODE ) :
21 DataLoader<HandleData>( VERBOSE_MODE ) {}
22
24AssimpHandleDataLoader::~AssimpHandleDataLoader() = default;
25
27
28void AssimpHandleDataLoader::loadData( const aiScene* scene,
30 data.clear();
31
32 if ( scene == nullptr ) {
33 LOG( logDEBUG ) << "AssimpHandleDataLoader : scene is nullptr.";
34 return;
35 }
36
37 if ( m_verbose ) {
38 LOG( logDEBUG ) << "File contains handle.";
39 LOG( logDEBUG ) << "Handle Loading begin...";
40 }
41
42 loadHandleData( scene, data );
43
44 if ( m_verbose ) { LOG( logDEBUG ) << "Handle Loading end.\n"; }
45}
46
48
49namespace {
50
51aiNode* findMeshNode( aiNode* node, const aiScene* scene, const aiString& meshName ) {
52 // look inside the node
53 for ( uint i = 0; i < node->mNumMeshes; ++i ) {
54 if ( scene->mMeshes[node->mMeshes[i]]->mName == meshName ) { return node; }
55 }
56 // repeat on children
57 for ( uint i = 0; i < node->mNumChildren; ++i ) {
58 aiNode* n = findMeshNode( node->mChildren[i], scene, meshName );
59 if ( n != nullptr ) { return n; }
60 }
61 return nullptr;
62}
63
64void initMarks( const aiNode* node, std::map<std::string, bool>& flags, bool flag = false ) {
65 flags[assimpToCore( node->mName )] = flag;
66 for ( uint i = 0; i < node->mNumChildren; ++i ) {
67 initMarks( node->mChildren[i], flags, flag );
68 }
69}
70
71void markParents( aiNode* node,
72 const aiScene* scene,
73 const std::vector<aiNode*>& meshParents,
75 const std::string nodeName = assimpToCore( node->mName );
76 if ( flag[nodeName] ) {
77 // skip already visited up-tree.
78 return;
79 }
80 flag[nodeName] = true;
81 // check if node is in the mesh hierarchy
82 auto it = std::find_if(
83 meshParents.begin(), meshParents.end(), [node]( const auto& n ) { return n == node; } );
84 if ( it != meshParents.end() ) {
85 flag[nodeName] = false;
86 return;
87 }
88 // otherwise mark parents
89 if ( node->mParent != nullptr ) { markParents( node->mParent, scene, meshParents, flag ); }
90}
91
92} // namespace
93
95void AssimpHandleDataLoader::loadHandleData(
96 const aiScene* scene,
98 // list mesh names according to GeometryLoader naming
99 std::set<std::string> meshNames;
100
101 // initialize need flag on all scene nodes
103 initMarks( scene->mRootNode, needNode );
104
105 std::vector<std::vector<aiNode*>> meshParents( scene->mNumMeshes );
106
107 // load the HandleComponentData for all meshes
109 for ( uint n = 0; n < scene->mNumMeshes; ++n ) {
110 const aiMesh* mesh = scene->mMeshes[n];
111 // fetch mesh name as registered by the GeometryLoader
112 std::string meshName = assimpToCore( mesh->mName );
113 while ( meshNames.find( meshName ) != meshNames.end() ) {
114 meshName.append( "_" );
115 }
116 meshNames.insert( meshName );
117
118 // if bound to no skeleton, skip
119 if ( !mesh->HasBones() ) { continue; }
120
121 // get mesh node parents
122 aiNode* meshNode = findMeshNode( scene->mRootNode, scene, mesh->mName );
123 while ( meshNode != nullptr ) {
124 meshParents[n].push_back( meshNode );
125 meshNode = meshNode->mParent;
126 }
127
128 // deal with skeleton
129 for ( uint j = 0; j < mesh->mNumBones; ++j ) {
130 const aiBone* bone = mesh->mBones[j];
131 // fetch bone data
132 const std::string boneName = assimpToCore( bone->mName );
133 // if data doesn't exist yet, create it
134 mapBone2Data[boneName].m_name = boneName;
135 // fill skinning weights and offset matrix for this mesh
136 loadHandleComponentDataWeights( bone, meshName, mapBone2Data[boneName] );
137 // deal with hierarchy
138 aiNode* node = scene->mRootNode->FindNode( bone->mName );
139 if ( node == nullptr ) { continue; }
140 // mark parents as needed up to mesh node relative
141 markParents( node, scene, meshParents[n], needNode );
142 // check children since end bones may not have weights
143 if ( node->mNumChildren == 1 ) {
144 const aiNode* child = node->mChildren[0];
145 const std::string childName = assimpToCore( child->mName );
146 // mark as needed
147 needNode[childName] = true;
148 // if data doesn't exist yet, create it
149 mapBone2Data[childName].m_name = childName;
150 }
151 }
152 }
153
154 // also add bone nodes for skeletons not attached to a mesh
155 auto rootNode = scene->mRootNode;
156 for ( uint i = 0; i < rootNode->mNumChildren; ++i ) {
157 auto node = rootNode->mChildren[i];
158 if ( needNode[assimpToCore( node->mName )] ) { continue; }
159 // check not up from a mesh
160 auto res =
161 std::find_if( meshParents.begin(), meshParents.end(), [node]( const auto& nodes ) {
162 return std::find_if( nodes.begin(), nodes.end(), [node]( const auto& n ) {
163 return n == node;
164 } ) != nodes.end();
165 } );
166 if ( res != meshParents.end() ) { continue; }
167 // mark subtree
168 initMarks( node, needNode, true );
169 }
170
171 // load hierarchy for needed bones
173 for ( const auto& n : needNode ) {
174 if ( !n.second ) { continue; }
175 // if data doesn't exist, create it (bone with no weight)
176 mapBone2Data[n.first].m_name = n.first;
177 // link to children
178 const aiNode* node = scene->mRootNode->FindNode( aiString( n.first ) );
179 for ( uint j = 0; j < node->mNumChildren; ++j ) {
180 const std::string childName = assimpToCore( node->mChildren[j]->mName );
181 if ( needNode.at( childName ) ) {
182 // if data doesn't exist, create it (bone with no weight)
183 mapBone2Data[childName].m_name = childName;
184 // register parenthood
185 edgeList.push_back( { n.first, childName } );
186 }
187 }
188 }
189
190 // load bone frame once all are registered
191 for ( auto& bone : mapBone2Data ) {
192 loadHandleComponentDataFrame( scene, aiString( bone.first ), bone.second );
193 }
194
195 // find roots and leaves
197 for ( const auto& node : needNode ) {
198 if ( node.second ) { roots.insert( node.first ); }
199 }
200 std::set<std::string> leaves = roots;
201 for ( auto edge : edgeList ) {
202 roots.erase( edge.second );
203 leaves.erase( edge.first );
204 }
205
206 // build one HandleData per root
207 for ( auto root : roots ) {
208 HandleData* handle = new HandleData();
209 handle->setType( HandleData::SKELETON );
210 handle->setName( root );
211
212 Ra::Core::Transform frame = Ra::Core::Transform::Identity();
213 aiNode* node = scene->mRootNode->FindNode( aiString( root ) );
214 while ( node->mParent != nullptr ) {
215 node = node->mParent;
216 frame = assimpToCore( node->mTransformation ) * frame;
217 }
218 handle->setFrame( frame );
219
220 // get list of bones and edges for this skeleton
222 fillHandleData( root, edgeList, mapBone2Data, nameTable, handle );
223 handle->setNameTable( nameTable );
224
225 // check if need additional end bones
226 bool needEndBone = false;
227 for ( const std::string& leaf : leaves ) {
228 if ( nameTable.find( leaf ) != nameTable.end() ) {
229 const auto& handleComponentData = mapBone2Data[leaf];
230 for ( const auto& mesh : handleComponentData.m_weights ) {
231 if ( mesh.second.size() != 0 ) {
232 needEndBone = true;
233 break;
234 }
235 }
236 if ( needEndBone ) { break; }
237 }
238 }
239 handle->needEndNodes( needEndBone );
240
241 if ( handle->isSkeleton() && !handle->hasEdges() ) {
242 // Do not load empty skeleton
243 delete handle;
244 }
245 else {
246 // register the HandleData
247 data.emplace_back( handle );
248 if ( m_verbose ) { handle->displayInfo(); }
249 }
250 }
251}
252
253void AssimpHandleDataLoader::loadHandleComponentDataFrame( const aiScene* scene,
254 const aiString& boneName,
255 HandleComponentData& data ) const {
256 // fetch global transform
257 data.m_frame.setIdentity();
258 aiNode* node = scene->mRootNode->FindNode( boneName );
259 while ( node != nullptr ) {
260 data.m_frame = assimpToCore( node->mTransformation ) * data.m_frame;
261 node = node->mParent;
262 }
263}
264
265void AssimpHandleDataLoader::loadHandleComponentDataWeights( const aiBone* bone,
266 const std::string& meshName,
267 HandleComponentData& data ) const {
268 // fetch skinning weigthts
269 for ( uint j = 0; j < bone->mNumWeights; ++j ) {
270 std::pair<uint, Scalar> weight( bone->mWeights[j].mVertexId, bone->mWeights[j].mWeight );
271 data.m_weights[meshName].push_back( weight );
272 }
273 data.m_bindMatrices[meshName] = assimpToCore( bone->mOffsetMatrix );
274}
275
276void AssimpHandleDataLoader::fillHandleData(
277 const std::string& node,
281 HandleData* data ) const {
282 // register the HandleComponentData for the bone
283 nameTable[node] = uint( data->getComponentData().size() );
284 data->getComponentData().push_back( mapBone2Data.at( node ) );
285 // bind meshes bound to the bone
286 for ( const auto& mesh : mapBone2Data.at( node ).m_weights ) {
287 data->addBindMesh( mesh.first );
288 }
289 // go through children
290 for ( const auto& edge : edgeList ) {
291 if ( edge.first == node ) {
292 fillHandleData( edge.second, edgeList, mapBone2Data, nameTable, data );
293 data->getEdgeData().push_back(
294 { nameTable.at( edge.first ), nameTable.at( edge.second ) } );
295 }
296 }
297}
298
299} // namespace IO
300} // namespace Ra
T append(T... args)
T at(T... args)
T begin(T... args)
T end(T... args)
T erase(T... args)
T find_if(T... args)
T insert(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T push_back(T... args)