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