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