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