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