1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2015, assimp team
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 following
12 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 /** @file Implementation of the Collada loader */
43
44
45 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
46
47 #include "../include/assimp/anim.h"
48 #include "../include/assimp/scene.h"
49 #include "ColladaLoader.h"
50 #include "ColladaParser.h"
51
52 #include "fast_atof.h"
53 #include "ParsingUtils.h"
54 #include "SkeletonMeshBuilder.h"
55 #include "Defines.h"
56
57 #include "time.h"
58 #include <boost/foreach.hpp>
59 #include "../include/assimp/DefaultLogger.hpp"
60 #include "../include/assimp/Importer.hpp"
61 #include <numeric>
62 #include "Defines.h"
63
64
65 using namespace Assimp;
66
67 static const aiImporterDesc desc = {
68 "Collada Importer",
69 "",
70 "",
71 "http://collada.org",
72 aiImporterFlags_SupportTextFlavour,
73 1,
74 3,
75 1,
76 5,
77 "dae"
78 };
79
80
81 // ------------------------------------------------------------------------------------------------
82 // Constructor to be privately used by Importer
ColladaLoader()83 ColladaLoader::ColladaLoader()
84 : noSkeletonMesh()
85 , ignoreUpDirection(false)
86 , invertTransparency(false)
87 , mNodeNameCounter()
88 {}
89
90 // ------------------------------------------------------------------------------------------------
91 // Destructor, private as well
~ColladaLoader()92 ColladaLoader::~ColladaLoader()
93 {}
94
95 // ------------------------------------------------------------------------------------------------
96 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const97 bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
98 {
99 // check file extension
100 std::string extension = GetExtension(pFile);
101
102 if( extension == "dae")
103 return true;
104
105 // XML - too generic, we need to open the file and search for typical keywords
106 if( extension == "xml" || !extension.length() || checkSig) {
107 /* If CanRead() is called in order to check whether we
108 * support a specific file extension in general pIOHandler
109 * might be NULL and it's our duty to return true here.
110 */
111 if (!pIOHandler)return true;
112 const char* tokens[] = {"collada"};
113 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
114 }
115 return false;
116 }
117
118 // ------------------------------------------------------------------------------------------------
SetupProperties(const Importer * pImp)119 void ColladaLoader::SetupProperties(const Importer* pImp)
120 {
121 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
122 ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0;
123 invertTransparency = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_INVERT_TRANSPARENCY,0) != 0;
124 }
125
126
127 // ------------------------------------------------------------------------------------------------
128 // Get file extension list
GetInfo() const129 const aiImporterDesc* ColladaLoader::GetInfo () const
130 {
131 return &desc;
132 }
133
134 // ------------------------------------------------------------------------------------------------
135 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)136 void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
137 {
138 mFileName = pFile;
139
140 // clean all member arrays - just for safety, it should work even if we did not
141 mMeshIndexByID.clear();
142 mMaterialIndexByName.clear();
143 mMeshes.clear();
144 newMats.clear();
145 mLights.clear();
146 mCameras.clear();
147 mTextures.clear();
148 mAnims.clear();
149
150 // parse the input file
151 ColladaParser parser( pIOHandler, pFile);
152
153 if( !parser.mRootNode)
154 throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
155
156 // reserve some storage to avoid unnecessary reallocs
157 newMats.reserve(parser.mMaterialLibrary.size()*2);
158 mMeshes.reserve(parser.mMeshLibrary.size()*2);
159
160 mCameras.reserve(parser.mCameraLibrary.size());
161 mLights.reserve(parser.mLightLibrary.size());
162
163 // create the materials first, for the meshes to find
164 BuildMaterials( parser, pScene);
165
166 // build the node hierarchy from it
167 pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
168
169 // ... then fill the materials with the now adjusted settings
170 FillMaterials(parser, pScene);
171
172 // Apply unitsize scale calculation
173 pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0,
174 0, parser.mUnitSize, 0, 0,
175 0, 0, parser.mUnitSize, 0,
176 0, 0, 0, 1);
177 if( !ignoreUpDirection ) {
178 // Convert to Y_UP, if different orientation
179 if( parser.mUpDirection == ColladaParser::UP_X)
180 pScene->mRootNode->mTransformation *= aiMatrix4x4(
181 0, -1, 0, 0,
182 1, 0, 0, 0,
183 0, 0, 1, 0,
184 0, 0, 0, 1);
185 else if( parser.mUpDirection == ColladaParser::UP_Z)
186 pScene->mRootNode->mTransformation *= aiMatrix4x4(
187 1, 0, 0, 0,
188 0, 0, 1, 0,
189 0, -1, 0, 0,
190 0, 0, 0, 1);
191 }
192 // store all meshes
193 StoreSceneMeshes( pScene);
194
195 // store all materials
196 StoreSceneMaterials( pScene);
197
198 // store all lights
199 StoreSceneLights( pScene);
200
201 // store all cameras
202 StoreSceneCameras( pScene);
203
204 // store all animations
205 StoreAnimations( pScene, parser);
206
207
208 // If no meshes have been loaded, it's probably just an animated skeleton.
209 if (!pScene->mNumMeshes) {
210
211 if (!noSkeletonMesh) {
212 SkeletonMeshBuilder hero(pScene);
213 }
214 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
215 }
216 }
217
218 // ------------------------------------------------------------------------------------------------
219 // Recursively constructs a scene node for the given parser node and returns it.
BuildHierarchy(const ColladaParser & pParser,const Collada::Node * pNode)220 aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
221 {
222 // create a node for it
223 aiNode* node = new aiNode();
224
225 // find a name for the new node. It's more complicated than you might think
226 node->mName.Set( FindNameForNode( pNode));
227
228 // calculate the transformation matrix for it
229 node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
230
231 // now resolve node instances
232 std::vector<const Collada::Node*> instances;
233 ResolveNodeInstances(pParser,pNode,instances);
234
235 // add children. first the *real* ones
236 node->mNumChildren = pNode->mChildren.size()+instances.size();
237 node->mChildren = new aiNode*[node->mNumChildren];
238
239 for( size_t a = 0; a < pNode->mChildren.size(); a++)
240 {
241 node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
242 node->mChildren[a]->mParent = node;
243 }
244
245 // ... and finally the resolved node instances
246 for( size_t a = 0; a < instances.size(); a++)
247 {
248 node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
249 node->mChildren[pNode->mChildren.size() + a]->mParent = node;
250 }
251
252 // construct meshes
253 BuildMeshesForNode( pParser, pNode, node);
254
255 // construct cameras
256 BuildCamerasForNode(pParser, pNode, node);
257
258 // construct lights
259 BuildLightsForNode(pParser, pNode, node);
260 return node;
261 }
262
263 // ------------------------------------------------------------------------------------------------
264 // Resolve node instances
ResolveNodeInstances(const ColladaParser & pParser,const Collada::Node * pNode,std::vector<const Collada::Node * > & resolved)265 void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
266 std::vector<const Collada::Node*>& resolved)
267 {
268 // reserve enough storage
269 resolved.reserve(pNode->mNodeInstances.size());
270
271 // ... and iterate through all nodes to be instanced as children of pNode
272 for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
273 end = pNode->mNodeInstances.end(); it != end; ++it)
274 {
275 // find the corresponding node in the library
276 const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode);
277 const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
278
279 // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
280 // need to check for both name and ID to catch all. To avoid breaking valid files,
281 // the workaround is only enabled when the first attempt to resolve the node has failed.
282 if (!nd) {
283 nd = FindNode(pParser.mRootNode,(*it).mNode);
284 }
285 if (!nd)
286 DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
287
288 else {
289 // attach this node to the list of children
290 resolved.push_back(nd);
291 }
292 }
293 }
294
295 // ------------------------------------------------------------------------------------------------
296 // Resolve UV channels
ApplyVertexToEffectSemanticMapping(Collada::Sampler & sampler,const Collada::SemanticMappingTable & table)297 void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
298 const Collada::SemanticMappingTable& table)
299 {
300 std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
301 if (it != table.mMap.end()) {
302 if (it->second.mType != Collada::IT_Texcoord)
303 DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
304
305 sampler.mUVId = it->second.mSet;
306 }
307 }
308
309 // ------------------------------------------------------------------------------------------------
310 // Builds lights for the given node and references them
BuildLightsForNode(const ColladaParser & pParser,const Collada::Node * pNode,aiNode * pTarget)311 void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
312 {
313 BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
314 {
315 // find the referred light
316 ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
317 if( srcLightIt == pParser.mLightLibrary.end())
318 {
319 DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
320 continue;
321 }
322 const Collada::Light* srcLight = &srcLightIt->second;
323
324 // now fill our ai data structure
325 aiLight* out = new aiLight();
326 out->mName = pTarget->mName;
327 out->mType = (aiLightSourceType)srcLight->mType;
328
329 // collada lights point in -Z by default, rest is specified in node transform
330 out->mDirection = aiVector3D(0.f,0.f,-1.f);
331
332 out->mAttenuationConstant = srcLight->mAttConstant;
333 out->mAttenuationLinear = srcLight->mAttLinear;
334 out->mAttenuationQuadratic = srcLight->mAttQuadratic;
335
336 out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
337 if (out->mType == aiLightSource_AMBIENT) {
338 out->mColorDiffuse = out->mColorSpecular = aiColor3D(0, 0, 0);
339 out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
340 }
341 else {
342 // collada doesn't differentiate between these color types
343 out->mColorDiffuse = out->mColorSpecular = srcLight->mColor*srcLight->mIntensity;
344 out->mColorAmbient = aiColor3D(0, 0, 0);
345 }
346
347 // convert falloff angle and falloff exponent in our representation, if given
348 if (out->mType == aiLightSource_SPOT) {
349
350 out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
351
352 // ... some extension magic.
353 if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
354 {
355 // ... some deprecation magic.
356 if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
357 {
358 // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
359 // epsilon chosen to be 0.1
360 out->mAngleOuterCone = std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
361 out->mAngleInnerCone;
362 }
363 else {
364 out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
365 if (out->mAngleOuterCone < out->mAngleInnerCone)
366 std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
367 }
368 }
369 else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
370 }
371
372 // add to light list
373 mLights.push_back(out);
374 }
375 }
376
377 // ------------------------------------------------------------------------------------------------
378 // Builds cameras for the given node and references them
BuildCamerasForNode(const ColladaParser & pParser,const Collada::Node * pNode,aiNode * pTarget)379 void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
380 {
381 BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
382 {
383 // find the referred light
384 ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
385 if( srcCameraIt == pParser.mCameraLibrary.end())
386 {
387 DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
388 continue;
389 }
390 const Collada::Camera* srcCamera = &srcCameraIt->second;
391
392 // orthographic cameras not yet supported in Assimp
393 if (srcCamera->mOrtho) {
394 DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
395 }
396
397 // now fill our ai data structure
398 aiCamera* out = new aiCamera();
399 out->mName = pTarget->mName;
400
401 // collada cameras point in -Z by default, rest is specified in node transform
402 out->mLookAt = aiVector3D(0.f,0.f,-1.f);
403
404 // near/far z is already ok
405 out->mClipPlaneFar = srcCamera->mZFar;
406 out->mClipPlaneNear = srcCamera->mZNear;
407
408 // ... but for the rest some values are optional
409 // and we need to compute the others in any combination.
410 if (srcCamera->mAspect != 10e10f)
411 out->mAspect = srcCamera->mAspect;
412
413 if (srcCamera->mHorFov != 10e10f) {
414 out->mHorizontalFOV = srcCamera->mHorFov;
415
416 if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
417 out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
418 tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
419 }
420 }
421 else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
422 out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect *
423 tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
424 }
425
426 // Collada uses degrees, we use radians
427 out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
428
429 // add to camera list
430 mCameras.push_back(out);
431 }
432 }
433
434 // ------------------------------------------------------------------------------------------------
435 // Builds meshes for the given node and references them
BuildMeshesForNode(const ColladaParser & pParser,const Collada::Node * pNode,aiNode * pTarget)436 void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
437 {
438 // accumulated mesh references by this node
439 std::vector<size_t> newMeshRefs;
440 newMeshRefs.reserve(pNode->mMeshes.size());
441
442 // add a mesh for each subgroup in each collada mesh
443 BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
444 {
445 const Collada::Mesh* srcMesh = NULL;
446 const Collada::Controller* srcController = NULL;
447
448 // find the referred mesh
449 ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
450 if( srcMeshIt == pParser.mMeshLibrary.end())
451 {
452 // if not found in the mesh-library, it might also be a controller referring to a mesh
453 ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
454 if( srcContrIt != pParser.mControllerLibrary.end())
455 {
456 srcController = &srcContrIt->second;
457 srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
458 if( srcMeshIt != pParser.mMeshLibrary.end())
459 srcMesh = srcMeshIt->second;
460 }
461
462 if( !srcMesh)
463 {
464 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
465 continue;
466 }
467 } else
468 {
469 // ID found in the mesh library -> direct reference to an unskinned mesh
470 srcMesh = srcMeshIt->second;
471 }
472
473 // build a mesh for each of its subgroups
474 size_t vertexStart = 0, faceStart = 0;
475 for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
476 {
477 const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
478 if( submesh.mNumFaces == 0)
479 continue;
480
481 // find material assigned to this submesh
482 std::string meshMaterial;
483 std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
484
485 const Collada::SemanticMappingTable* table = NULL;
486 if( meshMatIt != mid.mMaterials.end())
487 {
488 table = &meshMatIt->second;
489 meshMaterial = table->mMatName;
490 }
491 else
492 {
493 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup <%s> in geometry <%s>.") % submesh.mMaterial % mid.mMeshOrController));
494 if( !mid.mMaterials.empty() )
495 meshMaterial = mid.mMaterials.begin()->second.mMatName;
496 }
497
498 // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
499 // given. The only mapping stuff which we do actually support is the UV channel.
500 std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
501 unsigned int matIdx;
502 if( matIt != mMaterialIndexByName.end())
503 matIdx = matIt->second;
504 else
505 matIdx = 0;
506
507 if (table && !table->mMap.empty() ) {
508 std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
509
510 // Iterate through all texture channels assigned to the effect and
511 // check whether we have mapping information for it.
512 ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
513 ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
514 ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
515 ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
516 ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
517 ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
518 }
519
520 // built lookup index of the Mesh-Submesh-Material combination
521 ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
522
523 // if we already have the mesh at the library, just add its index to the node's array
524 std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
525 if( dstMeshIt != mMeshIndexByID.end()) {
526 newMeshRefs.push_back( dstMeshIt->second);
527 }
528 else
529 {
530 // else we have to add the mesh to the collection and store its newly assigned index at the node
531 aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
532
533 // store the mesh, and store its new index in the node
534 newMeshRefs.push_back( mMeshes.size());
535 mMeshIndexByID[index] = mMeshes.size();
536 mMeshes.push_back( dstMesh);
537 vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
538
539 // assign the material index
540 dstMesh->mMaterialIndex = matIdx;
541 if(dstMesh->mName.length == 0)
542 {
543 dstMesh->mName = mid.mMeshOrController;
544 }
545 }
546 }
547 }
548
549 // now place all mesh references we gathered in the target node
550 pTarget->mNumMeshes = newMeshRefs.size();
551 if( newMeshRefs.size())
552 {
553 pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
554 std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
555 }
556 }
557
558 // ------------------------------------------------------------------------------------------------
559 // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
CreateMesh(const ColladaParser & pParser,const Collada::Mesh * pSrcMesh,const Collada::SubMesh & pSubMesh,const Collada::Controller * pSrcController,size_t pStartVertex,size_t pStartFace)560 aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
561 const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
562 {
563 aiMesh* dstMesh = new aiMesh;
564
565 dstMesh->mName = pSrcMesh->mName;
566
567 // count the vertices addressed by its faces
568 const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
569 pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0);
570
571 // copy positions
572 dstMesh->mNumVertices = numVertices;
573 dstMesh->mVertices = new aiVector3D[numVertices];
574 std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
575 pStartVertex + numVertices, dstMesh->mVertices);
576
577 // normals, if given. HACK: (thom) Due to the glorious Collada spec we never
578 // know if we have the same number of normals as there are positions. So we
579 // also ignore any vertex attribute if it has a different count
580 if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
581 {
582 dstMesh->mNormals = new aiVector3D[numVertices];
583 std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
584 pStartVertex + numVertices, dstMesh->mNormals);
585 }
586
587 // tangents, if given.
588 if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
589 {
590 dstMesh->mTangents = new aiVector3D[numVertices];
591 std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
592 pStartVertex + numVertices, dstMesh->mTangents);
593 }
594
595 // bitangents, if given.
596 if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
597 {
598 dstMesh->mBitangents = new aiVector3D[numVertices];
599 std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
600 pStartVertex + numVertices, dstMesh->mBitangents);
601 }
602
603 // same for texturecoords, as many as we have
604 // empty slots are not allowed, need to pack and adjust UV indexes accordingly
605 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
606 {
607 if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
608 {
609 dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
610 for( size_t b = 0; b < numVertices; ++b)
611 dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
612
613 dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
614 ++real;
615 }
616 }
617
618 // same for vertex colors, as many as we have. again the same packing to avoid empty slots
619 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
620 {
621 if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
622 {
623 dstMesh->mColors[real] = new aiColor4D[numVertices];
624 std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
625 ++real;
626 }
627 }
628
629 // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
630 size_t vertex = 0;
631 dstMesh->mNumFaces = pSubMesh.mNumFaces;
632 dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
633 for( size_t a = 0; a < dstMesh->mNumFaces; ++a)
634 {
635 size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
636 aiFace& face = dstMesh->mFaces[a];
637 face.mNumIndices = s;
638 face.mIndices = new unsigned int[s];
639 for( size_t b = 0; b < s; ++b)
640 face.mIndices[b] = vertex++;
641 }
642
643 // create bones if given
644 if( pSrcController)
645 {
646 // refuse if the vertex count does not match
647 // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
648 // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
649
650 // resolve references - joint names
651 const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
652 const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
653 // joint offset matrices
654 const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
655 const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
656 // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider
657 const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
658 if( &weightNamesAcc != &jointNamesAcc)
659 throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author.");
660 // vertex weights
661 const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
662 const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
663
664 if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
665 throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
666 // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
667 if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
668 throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. ");
669
670 // create containers to collect the weights for each bone
671 size_t numBones = jointNames.mStrings.size();
672 std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
673
674 // build a temporary array of pointers to the start of each vertex's weights
675 typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
676 std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
677 weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end());
678
679 IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
680 for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
681 {
682 weightStartPerVertex[a] = pit;
683 pit += pSrcController->mWeightCounts[a];
684 }
685
686 // now for each vertex put the corresponding vertex weights into each bone's weight collection
687 for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
688 {
689 // which position index was responsible for this vertex? that's also the index by which
690 // the controller assigns the vertex weights
691 size_t orgIndex = pSrcMesh->mFacePosIndices[a];
692 // find the vertex weights for this vertex
693 IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
694 size_t pairCount = pSrcController->mWeightCounts[orgIndex];
695
696 for( size_t b = 0; b < pairCount; ++b, ++iit)
697 {
698 size_t jointIndex = iit->first;
699 size_t vertexIndex = iit->second;
700
701 float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
702
703 // one day I gonna kill that XSI Collada exporter
704 if( weight > 0.0f)
705 {
706 aiVertexWeight w;
707 w.mVertexId = a - pStartVertex;
708 w.mWeight = weight;
709 dstBones[jointIndex].push_back( w);
710 }
711 }
712 }
713
714 // count the number of bones which influence vertices of the current submesh
715 size_t numRemainingBones = 0;
716 for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
717 if( it->size() > 0)
718 numRemainingBones++;
719
720 // create bone array and copy bone weights one by one
721 dstMesh->mNumBones = numRemainingBones;
722 dstMesh->mBones = new aiBone*[numRemainingBones];
723 size_t boneCount = 0;
724 for( size_t a = 0; a < numBones; ++a)
725 {
726 // omit bones without weights
727 if( dstBones[a].size() == 0)
728 continue;
729
730 // create bone with its weights
731 aiBone* bone = new aiBone;
732 bone->mName = ReadString( jointNamesAcc, jointNames, a);
733 bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
734 bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
735 bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
736 bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
737 bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
738 bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
739 bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
740 bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
741 bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
742 bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
743 bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
744 bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
745 bone->mNumWeights = dstBones[a].size();
746 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
747 std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
748
749 // apply bind shape matrix to offset matrix
750 aiMatrix4x4 bindShapeMatrix;
751 bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
752 bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
753 bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
754 bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
755 bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
756 bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
757 bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
758 bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
759 bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
760 bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
761 bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
762 bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
763 bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
764 bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
765 bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
766 bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
767 bone->mOffsetMatrix *= bindShapeMatrix;
768
769 // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name.
770 // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID,
771 // and replace the bone's name by the node's name so that the user can use the standard
772 // find-by-name method to associate nodes with bones.
773 const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
774 if( !bnode)
775 bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
776
777 // assign the name that we would have assigned for the source node
778 if( bnode)
779 bone->mName.Set( FindNameForNode( bnode));
780 else
781 DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data));
782
783 // and insert bone
784 dstMesh->mBones[boneCount++] = bone;
785 }
786 }
787
788 return dstMesh;
789 }
790
791 // ------------------------------------------------------------------------------------------------
792 // Stores all meshes in the given scene
StoreSceneMeshes(aiScene * pScene)793 void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
794 {
795 pScene->mNumMeshes = mMeshes.size();
796 if( mMeshes.size() > 0)
797 {
798 pScene->mMeshes = new aiMesh*[mMeshes.size()];
799 std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
800 mMeshes.clear();
801 }
802 }
803
804 // ------------------------------------------------------------------------------------------------
805 // Stores all cameras in the given scene
StoreSceneCameras(aiScene * pScene)806 void ColladaLoader::StoreSceneCameras( aiScene* pScene)
807 {
808 pScene->mNumCameras = mCameras.size();
809 if( mCameras.size() > 0)
810 {
811 pScene->mCameras = new aiCamera*[mCameras.size()];
812 std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
813 mCameras.clear();
814 }
815 }
816
817 // ------------------------------------------------------------------------------------------------
818 // Stores all lights in the given scene
StoreSceneLights(aiScene * pScene)819 void ColladaLoader::StoreSceneLights( aiScene* pScene)
820 {
821 pScene->mNumLights = mLights.size();
822 if( mLights.size() > 0)
823 {
824 pScene->mLights = new aiLight*[mLights.size()];
825 std::copy( mLights.begin(), mLights.end(), pScene->mLights);
826 mLights.clear();
827 }
828 }
829
830 // ------------------------------------------------------------------------------------------------
831 // Stores all textures in the given scene
StoreSceneTextures(aiScene * pScene)832 void ColladaLoader::StoreSceneTextures( aiScene* pScene)
833 {
834 pScene->mNumTextures = mTextures.size();
835 if( mTextures.size() > 0)
836 {
837 pScene->mTextures = new aiTexture*[mTextures.size()];
838 std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
839 mTextures.clear();
840 }
841 }
842
843 // ------------------------------------------------------------------------------------------------
844 // Stores all materials in the given scene
StoreSceneMaterials(aiScene * pScene)845 void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
846 {
847 pScene->mNumMaterials = newMats.size();
848
849 if (newMats.size() > 0) {
850 pScene->mMaterials = new aiMaterial*[newMats.size()];
851 for (unsigned int i = 0; i < newMats.size();++i)
852 pScene->mMaterials[i] = newMats[i].second;
853
854 newMats.clear();
855 }
856 }
857
858 // ------------------------------------------------------------------------------------------------
859 // Stores all animations
StoreAnimations(aiScene * pScene,const ColladaParser & pParser)860 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
861 {
862 // recursivly collect all animations from the collada scene
863 StoreAnimations( pScene, pParser, &pParser.mAnims, "");
864
865 // catch special case: many animations with the same length, each affecting only a single node.
866 // we need to unite all those single-node-anims to a proper combined animation
867 for( size_t a = 0; a < mAnims.size(); ++a)
868 {
869 aiAnimation* templateAnim = mAnims[a];
870 if( templateAnim->mNumChannels == 1)
871 {
872 // search for other single-channel-anims with the same duration
873 std::vector<size_t> collectedAnimIndices;
874 for( size_t b = a+1; b < mAnims.size(); ++b)
875 {
876 aiAnimation* other = mAnims[b];
877 if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
878 collectedAnimIndices.push_back( b);
879 }
880
881 // if there are other animations which fit the template anim, combine all channels into a single anim
882 if( !collectedAnimIndices.empty() )
883 {
884 aiAnimation* combinedAnim = new aiAnimation();
885 combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
886 combinedAnim->mDuration = templateAnim->mDuration;
887 combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
888 combinedAnim->mNumChannels = collectedAnimIndices.size() + 1;
889 combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
890 // add the template anim as first channel by moving its aiNodeAnim to the combined animation
891 combinedAnim->mChannels[0] = templateAnim->mChannels[0];
892 templateAnim->mChannels[0] = NULL;
893 delete templateAnim;
894 // combined animation replaces template animation in the anim array
895 mAnims[a] = combinedAnim;
896
897 // move the memory of all other anims to the combined anim and erase them from the source anims
898 for( size_t b = 0; b < collectedAnimIndices.size(); ++b)
899 {
900 aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
901 combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
902 srcAnimation->mChannels[0] = NULL;
903 delete srcAnimation;
904 }
905
906 // in a second go, delete all the single-channel-anims that we've stripped from their channels
907 // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one
908 while( !collectedAnimIndices.empty() )
909 {
910 mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
911 collectedAnimIndices.pop_back();
912 }
913 }
914 }
915 }
916
917 // now store all anims in the scene
918 if( !mAnims.empty())
919 {
920 pScene->mNumAnimations = mAnims.size();
921 pScene->mAnimations = new aiAnimation*[mAnims.size()];
922 std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
923 }
924
925 mAnims.clear();
926 }
927
928 // ------------------------------------------------------------------------------------------------
929 // Constructs the animations for the given source anim
StoreAnimations(aiScene * pScene,const ColladaParser & pParser,const Collada::Animation * pSrcAnim,const std::string & pPrefix)930 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string &pPrefix)
931 {
932 std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
933
934 // create nested animations, if given
935 for( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
936 StoreAnimations( pScene, pParser, *it, animName);
937
938 // create animation channels, if any
939 if( !pSrcAnim->mChannels.empty())
940 CreateAnimation( pScene, pParser, pSrcAnim, animName);
941 }
942
943 // ------------------------------------------------------------------------------------------------
944 // Constructs the animation for the given source anim
CreateAnimation(aiScene * pScene,const ColladaParser & pParser,const Collada::Animation * pSrcAnim,const std::string & pName)945 void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
946 {
947 // collect a list of animatable nodes
948 std::vector<const aiNode*> nodes;
949 CollectNodes( pScene->mRootNode, nodes);
950
951 std::vector<aiNodeAnim*> anims;
952 for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
953 {
954 // find all the collada anim channels which refer to the current node
955 std::vector<Collada::ChannelEntry> entries;
956 std::string nodeName = (*nit)->mName.data;
957
958 // find the collada node corresponding to the aiNode
959 const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
960 // ai_assert( srcNode != NULL);
961 if( !srcNode)
962 continue;
963
964 // now check all channels if they affect the current node
965 for( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
966 cit != pSrcAnim->mChannels.end(); ++cit)
967 {
968 const Collada::AnimationChannel& srcChannel = *cit;
969 Collada::ChannelEntry entry;
970
971 // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others
972 // find the slash that separates the node name - there should be only one
973 std::string::size_type slashPos = srcChannel.mTarget.find( '/');
974 if( slashPos == std::string::npos)
975 continue;
976 if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
977 continue;
978 std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
979 if( targetID != srcNode->mID)
980 continue;
981
982 // find the dot that separates the transformID - there should be only one or zero
983 std::string::size_type dotPos = srcChannel.mTarget.find( '.');
984 if( dotPos != std::string::npos)
985 {
986 if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
987 continue;
988
989 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
990
991 std::string subElement = srcChannel.mTarget.substr( dotPos+1);
992 if( subElement == "ANGLE")
993 entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle
994 else if( subElement == "X")
995 entry.mSubElement = 0;
996 else if( subElement == "Y")
997 entry.mSubElement = 1;
998 else if( subElement == "Z")
999 entry.mSubElement = 2;
1000 else
1001 DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement <%s>. Ignoring") % subElement));
1002 } else
1003 {
1004 // no subelement following, transformId is remaining string
1005 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
1006 }
1007
1008 std::string::size_type bracketPos = srcChannel.mTarget.find('(');
1009 if (bracketPos != std::string::npos)
1010 {
1011 entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1);
1012 std::string subElement = srcChannel.mTarget.substr(bracketPos);
1013
1014 if (subElement == "(0)(0)")
1015 entry.mSubElement = 0;
1016 else if (subElement == "(1)(0)")
1017 entry.mSubElement = 1;
1018 else if (subElement == "(2)(0)")
1019 entry.mSubElement = 2;
1020 else if (subElement == "(3)(0)")
1021 entry.mSubElement = 3;
1022 else if (subElement == "(0)(1)")
1023 entry.mSubElement = 4;
1024 else if (subElement == "(1)(1)")
1025 entry.mSubElement = 5;
1026 else if (subElement == "(2)(1)")
1027 entry.mSubElement = 6;
1028 else if (subElement == "(3)(1)")
1029 entry.mSubElement = 7;
1030 else if (subElement == "(0)(2)")
1031 entry.mSubElement = 8;
1032 else if (subElement == "(1)(2)")
1033 entry.mSubElement = 9;
1034 else if (subElement == "(2)(2)")
1035 entry.mSubElement = 10;
1036 else if (subElement == "(3)(2)")
1037 entry.mSubElement = 11;
1038 else if (subElement == "(0)(3)")
1039 entry.mSubElement = 12;
1040 else if (subElement == "(1)(3)")
1041 entry.mSubElement = 13;
1042 else if (subElement == "(2)(3)")
1043 entry.mSubElement = 14;
1044 else if (subElement == "(3)(3)")
1045 entry.mSubElement = 15;
1046
1047 }
1048
1049 // determine which transform step is affected by this channel
1050 entry.mTransformIndex = SIZE_MAX;
1051 for( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
1052 if( srcNode->mTransforms[a].mID == entry.mTransformId)
1053 entry.mTransformIndex = a;
1054
1055 if( entry.mTransformIndex == SIZE_MAX) {
1056 continue;
1057 }
1058
1059 entry.mChannel = &(*cit);
1060 entries.push_back( entry);
1061 }
1062
1063 // if there's no channel affecting the current node, we skip it
1064 if( entries.empty())
1065 continue;
1066
1067 // resolve the data pointers for all anim channels. Find the minimum time while we're at it
1068 float startTime = 1e20f, endTime = -1e20f;
1069 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
1070 {
1071 Collada::ChannelEntry& e = *it;
1072 e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
1073 e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
1074 e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
1075 e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
1076
1077 // time count and value count must match
1078 if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
1079 throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
1080
1081 if( e.mTimeAccessor->mCount > 0 )
1082 {
1083 // find bounding times
1084 startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
1085 endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
1086 }
1087 }
1088
1089 std::vector<aiMatrix4x4> resultTrafos;
1090 if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
1091 {
1092 // create a local transformation chain of the node's transforms
1093 std::vector<Collada::Transform> transforms = srcNode->mTransforms;
1094
1095 // now for every unique point in time, find or interpolate the key values for that time
1096 // and apply them to the transform chain. Then the node's present transformation can be calculated.
1097 float time = startTime;
1098 while( 1)
1099 {
1100 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
1101 {
1102 Collada::ChannelEntry& e = *it;
1103
1104 // find the keyframe behind the current point in time
1105 size_t pos = 0;
1106 float postTime = 0.f;
1107 while( 1)
1108 {
1109 if( pos >= e.mTimeAccessor->mCount)
1110 break;
1111 postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
1112 if( postTime >= time)
1113 break;
1114 ++pos;
1115 }
1116
1117 pos = std::min( pos, e.mTimeAccessor->mCount-1);
1118
1119 // read values from there
1120 float temp[16];
1121 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
1122 temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
1123
1124 // if not exactly at the key time, interpolate with previous value set
1125 if( postTime > time && pos > 0)
1126 {
1127 float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
1128 float factor = (time - postTime) / (preTime - postTime);
1129
1130 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
1131 {
1132 float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
1133 temp[c] += (v - temp[c]) * factor;
1134 }
1135 }
1136
1137 // Apply values to current transformation
1138 std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
1139 }
1140
1141 // Calculate resulting transformation
1142 aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
1143
1144 // out of lazyness: we store the time in matrix.d4
1145 mat.d4 = time;
1146 resultTrafos.push_back( mat);
1147
1148 // find next point in time to evaluate. That's the closest frame larger than the current in any channel
1149 float nextTime = 1e20f;
1150 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
1151 {
1152 Collada::ChannelEntry& e = *it;
1153
1154 // find the next time value larger than the current
1155 size_t pos = 0;
1156 while( pos < e.mTimeAccessor->mCount)
1157 {
1158 float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
1159 if( t > time)
1160 {
1161 nextTime = std::min( nextTime, t);
1162 break;
1163 }
1164 ++pos;
1165 }
1166 }
1167
1168 // no more keys on any channel after the current time -> we're done
1169 if( nextTime > 1e19)
1170 break;
1171
1172 // else construct next keyframe at this following time point
1173 time = nextTime;
1174 }
1175 }
1176
1177 // there should be some keyframes, but we aren't that fixated on valid input data
1178 // ai_assert( resultTrafos.size() > 0);
1179
1180 // build an animation channel for the given node out of these trafo keys
1181 if( !resultTrafos.empty() )
1182 {
1183 aiNodeAnim* dstAnim = new aiNodeAnim;
1184 dstAnim->mNodeName = nodeName;
1185 dstAnim->mNumPositionKeys = resultTrafos.size();
1186 dstAnim->mNumRotationKeys= resultTrafos.size();
1187 dstAnim->mNumScalingKeys = resultTrafos.size();
1188 dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
1189 dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
1190 dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
1191
1192 for( size_t a = 0; a < resultTrafos.size(); ++a)
1193 {
1194 aiMatrix4x4 mat = resultTrafos[a];
1195 double time = double( mat.d4); // remember? time is stored in mat.d4
1196 mat.d4 = 1.0f;
1197
1198 dstAnim->mPositionKeys[a].mTime = time;
1199 dstAnim->mRotationKeys[a].mTime = time;
1200 dstAnim->mScalingKeys[a].mTime = time;
1201 mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
1202 }
1203
1204 anims.push_back( dstAnim);
1205 } else
1206 {
1207 DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
1208 }
1209 }
1210
1211 if( !anims.empty())
1212 {
1213 aiAnimation* anim = new aiAnimation;
1214 anim->mName.Set( pName);
1215 anim->mNumChannels = anims.size();
1216 anim->mChannels = new aiNodeAnim*[anims.size()];
1217 std::copy( anims.begin(), anims.end(), anim->mChannels);
1218 anim->mDuration = 0.0f;
1219 for( size_t a = 0; a < anims.size(); ++a)
1220 {
1221 anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
1222 anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
1223 anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
1224 }
1225 anim->mTicksPerSecond = 1;
1226 mAnims.push_back( anim);
1227 }
1228 }
1229
1230 // ------------------------------------------------------------------------------------------------
1231 // Add a texture to a material structure
AddTexture(aiMaterial & mat,const ColladaParser & pParser,const Collada::Effect & effect,const Collada::Sampler & sampler,aiTextureType type,unsigned int idx)1232 void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser,
1233 const Collada::Effect& effect,
1234 const Collada::Sampler& sampler,
1235 aiTextureType type, unsigned int idx)
1236 {
1237 // first of all, basic file name
1238 const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
1239 mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
1240
1241 // mapping mode
1242 int map = aiTextureMapMode_Clamp;
1243 if (sampler.mWrapU)
1244 map = aiTextureMapMode_Wrap;
1245 if (sampler.mWrapU && sampler.mMirrorU)
1246 map = aiTextureMapMode_Mirror;
1247
1248 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
1249
1250 map = aiTextureMapMode_Clamp;
1251 if (sampler.mWrapV)
1252 map = aiTextureMapMode_Wrap;
1253 if (sampler.mWrapV && sampler.mMirrorV)
1254 map = aiTextureMapMode_Mirror;
1255
1256 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
1257
1258 // UV transformation
1259 mat.AddProperty(&sampler.mTransform, 1,
1260 _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
1261
1262 // Blend mode
1263 mat.AddProperty((int*)&sampler.mOp , 1,
1264 _AI_MATKEY_TEXBLEND_BASE, type, idx);
1265
1266 // Blend factor
1267 mat.AddProperty((float*)&sampler.mWeighting , 1,
1268 _AI_MATKEY_TEXBLEND_BASE, type, idx);
1269
1270 // UV source index ... if we didn't resolve the mapping, it is actually just
1271 // a guess but it works in most cases. We search for the frst occurence of a
1272 // number in the channel name. We assume it is the zero-based index into the
1273 // UV channel array of all corresponding meshes. It could also be one-based
1274 // for some exporters, but we won't care of it unless someone complains about.
1275 if (sampler.mUVId != UINT_MAX)
1276 map = sampler.mUVId;
1277 else {
1278 map = -1;
1279 for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
1280 if (IsNumeric(*it)) {
1281 map = strtoul10(&(*it));
1282 break;
1283 }
1284 }
1285 if (-1 == map) {
1286 DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
1287 map = 0;
1288 }
1289 }
1290 mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
1291 }
1292
1293 // ------------------------------------------------------------------------------------------------
1294 // Fills materials from the collada material definitions
FillMaterials(const ColladaParser & pParser,aiScene *)1295 void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/)
1296 {
1297 for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
1298 end = newMats.end(); it != end; ++it)
1299 {
1300 aiMaterial& mat = (aiMaterial&)*it->second;
1301 Collada::Effect& effect = *it->first;
1302
1303 // resolve shading mode
1304 int shadeMode;
1305 if (effect.mFaceted) /* fixme */
1306 shadeMode = aiShadingMode_Flat;
1307 else {
1308 switch( effect.mShadeType)
1309 {
1310 case Collada::Shade_Constant:
1311 shadeMode = aiShadingMode_NoShading;
1312 break;
1313 case Collada::Shade_Lambert:
1314 shadeMode = aiShadingMode_Gouraud;
1315 break;
1316 case Collada::Shade_Blinn:
1317 shadeMode = aiShadingMode_Blinn;
1318 break;
1319 case Collada::Shade_Phong:
1320 shadeMode = aiShadingMode_Phong;
1321 break;
1322
1323 default:
1324 DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
1325 shadeMode = aiShadingMode_Gouraud;
1326 break;
1327 }
1328 }
1329 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
1330
1331 // double-sided?
1332 shadeMode = effect.mDoubleSided;
1333 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
1334
1335 // wireframe?
1336 shadeMode = effect.mWireframe;
1337 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
1338
1339 // add material colors
1340 mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
1341 mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
1342 mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
1343 mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
1344 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
1345 mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
1346
1347 // scalar properties
1348 mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
1349 mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
1350 mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
1351
1352 // transparency, a very hard one. seemingly not all files are following the
1353 // specification here (1.0 transparency => completly opaque)...
1354 // therefore, we let the opportunity for the user to manually invert
1355 // the transparency if necessary and we add preliminary support for RGB_ZERO mode
1356 if(effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) {
1357 // Trying some support for RGB_ZERO mode
1358 if(effect.mRGBTransparency) {
1359 effect.mTransparency = 1.f - effect.mTransparent.a;
1360 }
1361
1362 // Global option
1363 if(invertTransparency) {
1364 effect.mTransparency = 1.f - effect.mTransparency;
1365 }
1366
1367 // Is the material finally transparent ?
1368 if (effect.mHasTransparency || effect.mTransparency < 1.f) {
1369 mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
1370 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
1371 }
1372 }
1373
1374 // add textures, if given
1375 if( !effect.mTexAmbient.mName.empty())
1376 /* It is merely a lightmap */
1377 AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
1378
1379 if( !effect.mTexEmissive.mName.empty())
1380 AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
1381
1382 if( !effect.mTexSpecular.mName.empty())
1383 AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
1384
1385 if( !effect.mTexDiffuse.mName.empty())
1386 AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
1387
1388 if( !effect.mTexBump.mName.empty())
1389 AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS);
1390
1391 if( !effect.mTexTransparent.mName.empty())
1392 AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
1393
1394 if( !effect.mTexReflective.mName.empty())
1395 AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
1396 }
1397 }
1398
1399 // ------------------------------------------------------------------------------------------------
1400 // Constructs materials from the collada material definitions
BuildMaterials(ColladaParser & pParser,aiScene *)1401 void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/)
1402 {
1403 newMats.reserve(pParser.mMaterialLibrary.size());
1404
1405 for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
1406 {
1407 const Collada::Material& material = matIt->second;
1408 // a material is only a reference to an effect
1409 ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
1410 if( effIt == pParser.mEffectLibrary.end())
1411 continue;
1412 Collada::Effect& effect = effIt->second;
1413
1414 // create material
1415 aiMaterial* mat = new aiMaterial;
1416 aiString name( material.mName.empty() ? matIt->first : material.mName );
1417 mat->AddProperty(&name,AI_MATKEY_NAME);
1418
1419 // store the material
1420 mMaterialIndexByName[matIt->first] = newMats.size();
1421 newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &effect,mat) );
1422 }
1423 // ScenePreprocessor generates a default material automatically if none is there.
1424 // All further code here in this loader works well without a valid material so
1425 // we can safely let it to ScenePreprocessor.
1426 #if 0
1427 if( newMats.size() == 0)
1428 {
1429 aiMaterial* mat = new aiMaterial;
1430 aiString name( AI_DEFAULT_MATERIAL_NAME );
1431 mat->AddProperty( &name, AI_MATKEY_NAME);
1432
1433 const int shadeMode = aiShadingMode_Phong;
1434 mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
1435 aiColor4D colAmbient( 0.2f, 0.2f, 0.2f, 1.0f), colDiffuse( 0.8f, 0.8f, 0.8f, 1.0f), colSpecular( 0.5f, 0.5f, 0.5f, 0.5f);
1436 mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
1437 mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
1438 mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
1439 const float specExp = 5.0f;
1440 mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
1441 }
1442 #endif
1443 }
1444
1445 // ------------------------------------------------------------------------------------------------
1446 // Resolves the texture name for the given effect texture entry
FindFilenameForEffectTexture(const ColladaParser & pParser,const Collada::Effect & pEffect,const std::string & pName)1447 aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
1448 const Collada::Effect& pEffect, const std::string& pName)
1449 {
1450 // recurse through the param references until we end up at an image
1451 std::string name = pName;
1452 while( 1)
1453 {
1454 // the given string is a param entry. Find it
1455 Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
1456 // if not found, we're at the end of the recursion. The resulting string should be the image ID
1457 if( it == pEffect.mParams.end())
1458 break;
1459
1460 // else recurse on
1461 name = it->second.mReference;
1462 }
1463
1464 // find the image referred by this name in the image library of the scene
1465 ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
1466 if( imIt == pParser.mImageLibrary.end())
1467 {
1468 throw DeadlyImportError( boost::str( boost::format(
1469 "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
1470 }
1471
1472 aiString result;
1473
1474 // if this is an embedded texture image setup an aiTexture for it
1475 if (imIt->second.mFileName.empty())
1476 {
1477 if (imIt->second.mImageData.empty()) {
1478 throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
1479 }
1480
1481 aiTexture* tex = new aiTexture();
1482
1483 // setup format hint
1484 if (imIt->second.mEmbeddedFormat.length() > 3) {
1485 DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
1486 }
1487 strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
1488
1489 // and copy texture data
1490 tex->mHeight = 0;
1491 tex->mWidth = imIt->second.mImageData.size();
1492 tex->pcData = (aiTexel*)new char[tex->mWidth];
1493 memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
1494
1495 // setup texture reference string
1496 result.data[0] = '*';
1497 result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
1498
1499 // and add this texture to the list
1500 mTextures.push_back(tex);
1501 }
1502 else
1503 {
1504 result.Set( imIt->second.mFileName );
1505 ConvertPath(result);
1506 }
1507 return result;
1508 }
1509
1510 // ------------------------------------------------------------------------------------------------
1511 // Convert a path read from a collada file to the usual representation
ConvertPath(aiString & ss)1512 void ColladaLoader::ConvertPath (aiString& ss)
1513 {
1514 // TODO: collada spec, p 22. Handle URI correctly.
1515 // For the moment we're just stripping the file:// away to make it work.
1516 // Windoes doesn't seem to be able to find stuff like
1517 // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
1518 if (0 == strncmp(ss.data,"file://",7))
1519 {
1520 ss.length -= 7;
1521 memmove(ss.data,ss.data+7,ss.length);
1522 ss.data[ss.length] = '\0';
1523 }
1524
1525 // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
1526 // I need to filter it without destroying linux paths starting with "/somewhere"
1527 if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' )
1528 {
1529 ss.length--;
1530 memmove( ss.data, ss.data+1, ss.length);
1531 ss.data[ss.length] = 0;
1532 }
1533
1534 // find and convert all %xy special chars
1535 char* out = ss.data;
1536 for( const char* it = ss.data; it != ss.data + ss.length; /**/ )
1537 {
1538 if( *it == '%' && (it + 3) < ss.data + ss.length )
1539 {
1540 // separate the number to avoid dragging in chars from behind into the parsing
1541 char mychar[3] = { it[1], it[2], 0 };
1542 size_t nbr = strtoul16( mychar);
1543 it += 3;
1544 *out++ = (char)(nbr & 0xFF);
1545 } else
1546 {
1547 *out++ = *it++;
1548 }
1549 }
1550
1551 // adjust length and terminator of the shortened string
1552 *out = 0;
1553 ss.length = (ptrdiff_t) (out - ss.data);
1554 }
1555
1556 // ------------------------------------------------------------------------------------------------
1557 // Reads a float value from an accessor and its data array.
ReadFloat(const Collada::Accessor & pAccessor,const Collada::Data & pData,size_t pIndex,size_t pOffset) const1558 float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
1559 {
1560 // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller
1561 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
1562 ai_assert( pos < pData.mValues.size());
1563 return pData.mValues[pos];
1564 }
1565
1566 // ------------------------------------------------------------------------------------------------
1567 // Reads a string value from an accessor and its data array.
ReadString(const Collada::Accessor & pAccessor,const Collada::Data & pData,size_t pIndex) const1568 const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
1569 {
1570 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
1571 ai_assert( pos < pData.mStrings.size());
1572 return pData.mStrings[pos];
1573 }
1574
1575 // ------------------------------------------------------------------------------------------------
1576 // Collects all nodes into the given array
CollectNodes(const aiNode * pNode,std::vector<const aiNode * > & poNodes) const1577 void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
1578 {
1579 poNodes.push_back( pNode);
1580
1581 for( size_t a = 0; a < pNode->mNumChildren; ++a)
1582 CollectNodes( pNode->mChildren[a], poNodes);
1583 }
1584
1585 // ------------------------------------------------------------------------------------------------
1586 // Finds a node in the collada scene by the given name
FindNode(const Collada::Node * pNode,const std::string & pName) const1587 const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
1588 {
1589 if( pNode->mName == pName || pNode->mID == pName)
1590 return pNode;
1591
1592 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
1593 {
1594 const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
1595 if( node)
1596 return node;
1597 }
1598
1599 return NULL;
1600 }
1601
1602 // ------------------------------------------------------------------------------------------------
1603 // Finds a node in the collada scene by the given SID
FindNodeBySID(const Collada::Node * pNode,const std::string & pSID) const1604 const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
1605 {
1606 if( pNode->mSID == pSID)
1607 return pNode;
1608
1609 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
1610 {
1611 const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
1612 if( node)
1613 return node;
1614 }
1615
1616 return NULL;
1617 }
1618
1619 // ------------------------------------------------------------------------------------------------
1620 // Finds a proper name for a node derived from the collada-node's properties
FindNameForNode(const Collada::Node * pNode)1621 std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
1622 {
1623 // now setup the name of the node. We take the name if not empty, otherwise the collada ID
1624 // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
1625 if (!pNode->mName.empty() && pNode->mName != "untitled")
1626 return pNode->mName;
1627 else if (!pNode->mID.empty())
1628 return pNode->mID;
1629 else if (!pNode->mSID.empty())
1630 return pNode->mSID;
1631 else
1632 {
1633 // No need to worry. Unnamed nodes are no problem at all, except
1634 // if cameras or lights need to be assigned to them.
1635 return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++);
1636 }
1637 }
1638
1639 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
1640