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