1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 /** @file ColladaParser.cpp
44  *  @brief Implementation of the Collada parser helper
45  */
46 
47 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
48 
49 #include <sstream>
50 #include <stdarg.h>
51 #include "ColladaParser.h"
52 #include "fast_atof.h"
53 #include "ParsingUtils.h"
54 #include "StringUtils.h"
55 #include <assimp/DefaultLogger.hpp>
56 #include <assimp/IOSystem.hpp>
57 #include <assimp/light.h>
58 #include "TinyFormatter.h"
59 
60 #include <memory>
61 
62 using namespace Assimp;
63 using namespace Assimp::Collada;
64 using namespace Assimp::Formatter;
65 
66 // ------------------------------------------------------------------------------------------------
67 // Constructor to be privately used by Importer
ColladaParser(IOSystem * pIOHandler,const std::string & pFile)68 ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
69     : mFileName( pFile )
70     , mReader( NULL )
71     , mDataLibrary()
72     , mAccessorLibrary()
73     , mMeshLibrary()
74     , mNodeLibrary()
75     , mImageLibrary()
76     , mEffectLibrary()
77     , mMaterialLibrary()
78     , mLightLibrary()
79     , mCameraLibrary()
80     , mControllerLibrary()
81     , mRootNode( NULL )
82     , mAnims()
83     , mUnitSize( 1.0f )
84     , mUpDirection( UP_Y )
85     , mFormat(FV_1_5_n )    // We assume the newest file format by default
86 {
87     // validate io-handler instance
88     if ( NULL == pIOHandler ) {
89         throw DeadlyImportError("IOSystem is NULL." );
90     }
91 
92     // open the file
93     std::unique_ptr<IOStream> file( pIOHandler->Open(pFile ) );
94     if (file.get() == NULL) {
95         throw DeadlyImportError( "Failed to open file " + pFile + "." );
96     }
97 
98     // generate a XML reader for it
99     std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
100     mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
101     if (!mReader) {
102         ThrowException("Collada: Unable to open file.");
103     }
104 
105     // start reading
106     ReadContents();
107 }
108 
109 // ------------------------------------------------------------------------------------------------
110 // Destructor, private as well
~ColladaParser()111 ColladaParser::~ColladaParser()
112 {
113     delete mReader;
114     for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
115         delete it->second;
116     for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
117         delete it->second;
118 }
119 
120 // ------------------------------------------------------------------------------------------------
121 // Read bool from text contents of current element
ReadBoolFromTextContent()122 bool ColladaParser::ReadBoolFromTextContent()
123 {
124     const char* cur = GetTextContent();
125     return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur);
126 }
127 
128 // ------------------------------------------------------------------------------------------------
129 // Read float from text contents of current element
ReadFloatFromTextContent()130 ai_real ColladaParser::ReadFloatFromTextContent()
131 {
132     const char* cur = GetTextContent();
133     return fast_atof(cur);
134 }
135 
136 // ------------------------------------------------------------------------------------------------
137 // Reads the contents of the file
ReadContents()138 void ColladaParser::ReadContents()
139 {
140     while( mReader->read())
141     {
142         // handle the root element "COLLADA"
143         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
144         {
145             if( IsElement( "COLLADA"))
146             {
147                 // check for 'version' attribute
148                 const int attrib = TestAttribute("version");
149                 if (attrib != -1) {
150                     const char* version = mReader->getAttributeValue(attrib);
151 
152                     if (!::strncmp(version,"1.5",3)) {
153                         mFormat =  FV_1_5_n;
154                         DefaultLogger::get()->debug("Collada schema version is 1.5.n");
155                     }
156                     else if (!::strncmp(version,"1.4",3)) {
157                         mFormat =  FV_1_4_n;
158                         DefaultLogger::get()->debug("Collada schema version is 1.4.n");
159                     }
160                     else if (!::strncmp(version,"1.3",3)) {
161                         mFormat =  FV_1_3_n;
162                         DefaultLogger::get()->debug("Collada schema version is 1.3.n");
163                     }
164                 }
165 
166                 ReadStructure();
167             } else
168             {
169                 DefaultLogger::get()->debug( format() << "Ignoring global element <" << mReader->getNodeName() << ">." );
170                 SkipElement();
171             }
172         } else
173         {
174             // skip everything else silently
175         }
176     }
177 }
178 
179 // ------------------------------------------------------------------------------------------------
180 // Reads the structure of the file
ReadStructure()181 void ColladaParser::ReadStructure()
182 {
183     while( mReader->read())
184     {
185         // beginning of elements
186         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
187         {
188             if( IsElement( "asset"))
189                 ReadAssetInfo();
190             else if( IsElement( "library_animations"))
191                 ReadAnimationLibrary();
192 			else if (IsElement("library_animation_clips"))
193 				ReadAnimationClipLibrary();
194             else if( IsElement( "library_controllers"))
195                 ReadControllerLibrary();
196             else if( IsElement( "library_images"))
197                 ReadImageLibrary();
198             else if( IsElement( "library_materials"))
199                 ReadMaterialLibrary();
200             else if( IsElement( "library_effects"))
201                 ReadEffectLibrary();
202             else if( IsElement( "library_geometries"))
203                 ReadGeometryLibrary();
204             else if( IsElement( "library_visual_scenes"))
205                 ReadSceneLibrary();
206             else if( IsElement( "library_lights"))
207                 ReadLightLibrary();
208             else if( IsElement( "library_cameras"))
209                 ReadCameraLibrary();
210             else if( IsElement( "library_nodes"))
211                 ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
212             else if( IsElement( "scene"))
213                 ReadScene();
214             else
215                 SkipElement();
216         }
217         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
218         {
219             break;
220         }
221     }
222 
223 	PostProcessRootAnimations();
224 }
225 
226 // ------------------------------------------------------------------------------------------------
227 // Reads asset information such as coordinate system information and legal blah
ReadAssetInfo()228 void ColladaParser::ReadAssetInfo()
229 {
230     if( mReader->isEmptyElement())
231         return;
232 
233     while( mReader->read())
234     {
235         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
236         {
237             if( IsElement( "unit"))
238             {
239                 // read unit data from the element's attributes
240                 const int attrIndex = TestAttribute( "meter");
241                 if (attrIndex == -1) {
242                     mUnitSize = 1.f;
243                 }
244                 else {
245                     mUnitSize = mReader->getAttributeValueAsFloat( attrIndex);
246                 }
247 
248                 // consume the trailing stuff
249                 if( !mReader->isEmptyElement())
250                     SkipElement();
251             }
252             else if( IsElement( "up_axis"))
253             {
254                 // read content, strip whitespace, compare
255                 const char* content = GetTextContent();
256                 if( strncmp( content, "X_UP", 4) == 0)
257                     mUpDirection = UP_X;
258                 else if( strncmp( content, "Z_UP", 4) == 0)
259                     mUpDirection = UP_Z;
260                 else
261                     mUpDirection = UP_Y;
262 
263                 // check element end
264                 TestClosing( "up_axis");
265             } else
266             {
267                 SkipElement();
268             }
269         }
270         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
271         {
272             if( strcmp( mReader->getNodeName(), "asset") != 0)
273                 ThrowException( "Expected end of <asset> element.");
274 
275             break;
276         }
277     }
278 }
279 
280 // ------------------------------------------------------------------------------------------------
281 // Reads the animation clips
ReadAnimationClipLibrary()282 void ColladaParser::ReadAnimationClipLibrary()
283 {
284 	if (mReader->isEmptyElement())
285 		return;
286 
287 	while (mReader->read())
288 	{
289 		if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
290 		{
291 			if (IsElement("animation_clip"))
292 			{
293 				// optional name given as an attribute
294 				std::string animName;
295 				int indexName = TestAttribute("name");
296 				int indexID = TestAttribute("id");
297 				if (indexName >= 0)
298 					animName = mReader->getAttributeValue(indexName);
299 				else if (indexID >= 0)
300 					animName = mReader->getAttributeValue(indexID);
301 				else
302 					animName = std::string("animation_") + to_string(mAnimationClipLibrary.size());
303 
304 				std::pair<std::string, std::vector<std::string> > clip;
305 
306 				clip.first = animName;
307 
308 				while (mReader->read())
309 				{
310 					if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
311 					{
312 						if (IsElement("instance_animation"))
313 						{
314 							int indexUrl = TestAttribute("url");
315 							if (indexUrl >= 0)
316 							{
317 								const char* url = mReader->getAttributeValue(indexUrl);
318 								if (url[0] != '#')
319 									ThrowException("Unknown reference format");
320 
321 								url++;
322 
323 								clip.second.push_back(url);
324 							}
325 						}
326 						else
327 						{
328 							// ignore the rest
329 							SkipElement();
330 						}
331 					}
332 					else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
333 					{
334 						if (strcmp(mReader->getNodeName(), "animation_clip") != 0)
335 							ThrowException("Expected end of <animation_clip> element.");
336 
337 						break;
338 					}
339 				}
340 
341 				if (clip.second.size() > 0)
342 				{
343 					mAnimationClipLibrary.push_back(clip);
344 				}
345 			}
346 			else
347 			{
348 				// ignore the rest
349 				SkipElement();
350 			}
351 		}
352 		else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
353 		{
354 			if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0)
355 				ThrowException("Expected end of <library_animation_clips> element.");
356 
357 			break;
358 		}
359 	}
360 }
361 
362 // ------------------------------------------------------------------------------------------------
363 // Re-build animations from animation clip library, if present, otherwise combine single-channel animations
PostProcessRootAnimations()364 void ColladaParser::PostProcessRootAnimations()
365 {
366 	if (mAnimationClipLibrary.size() > 0)
367 	{
368 		Animation temp;
369 
370 		for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it)
371 		{
372 			std::string clipName = it->first;
373 
374 			Animation *clip = new Animation();
375 			clip->mName = clipName;
376 
377 			temp.mSubAnims.push_back(clip);
378 
379 			for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a)
380 			{
381 				std::string animationID = *a;
382 
383 				AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
384 
385 				if (animation != mAnimationLibrary.end())
386 				{
387 					Animation *pSourceAnimation = animation->second;
388 
389 					pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
390 				}
391 			}
392 		}
393 
394 		mAnims = temp;
395 
396 		// Ensure no double deletes.
397 		temp.mSubAnims.clear();
398 	}
399 	else
400 	{
401 		mAnims.CombineSingleChannelAnimations();
402 	}
403 }
404 
405 // ------------------------------------------------------------------------------------------------
406 // Reads the animation library
ReadAnimationLibrary()407 void ColladaParser::ReadAnimationLibrary()
408 {
409     if (mReader->isEmptyElement())
410         return;
411 
412     while( mReader->read())
413     {
414         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
415         {
416             if( IsElement( "animation"))
417             {
418                 // delegate the reading. Depending on the inner elements it will be a container or a anim channel
419                 ReadAnimation( &mAnims);
420             } else
421             {
422                 // ignore the rest
423                 SkipElement();
424             }
425         }
426         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
427         {
428             if( strcmp( mReader->getNodeName(), "library_animations") != 0)
429                 ThrowException( "Expected end of <library_animations> element.");
430 
431             break;
432         }
433     }
434 }
435 
436 // ------------------------------------------------------------------------------------------------
437 // Reads an animation into the given parent structure
ReadAnimation(Collada::Animation * pParent)438 void ColladaParser::ReadAnimation( Collada::Animation* pParent)
439 {
440     if( mReader->isEmptyElement())
441         return;
442 
443     // an <animation> element may be a container for grouping sub-elements or an animation channel
444     // this is the channel collection by ID, in case it has channels
445     typedef std::map<std::string, AnimationChannel> ChannelMap;
446     ChannelMap channels;
447     // this is the anim container in case we're a container
448     Animation* anim = NULL;
449 
450     // optional name given as an attribute
451     std::string animName;
452 	std::string animID;
453     int indexName = TestAttribute( "name");
454     int indexID = TestAttribute( "id");
455 
456 	if (indexID >= 0)
457 		animID = mReader->getAttributeValue(indexID);
458 
459     if( indexName >= 0)
460         animName = mReader->getAttributeValue( indexName);
461     else if( indexID >= 0)
462         animName = animID;
463     else
464         animName = "animation";
465 
466     while( mReader->read())
467     {
468         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
469         {
470             // we have subanimations
471             if( IsElement( "animation"))
472             {
473                 // create container from our element
474                 if( !anim)
475                 {
476                     anim = new Animation;
477                     anim->mName = animName;
478                     pParent->mSubAnims.push_back( anim);
479                 }
480 
481                 // recurse into the subelement
482                 ReadAnimation( anim);
483             }
484             else if( IsElement( "source"))
485             {
486                 // possible animation data - we'll never know. Better store it
487                 ReadSource();
488             }
489             else if( IsElement( "sampler"))
490             {
491                 // read the ID to assign the corresponding collada channel afterwards.
492                 int indexID = GetAttribute( "id");
493                 std::string id = mReader->getAttributeValue( indexID);
494                 ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first;
495 
496                 // have it read into a channel
497                 ReadAnimationSampler( newChannel->second);
498             }
499             else if( IsElement( "channel"))
500             {
501                 // the binding element whose whole purpose is to provide the target to animate
502                 // Thanks, Collada! A directly posted information would have been too simple, I guess.
503                 // Better add another indirection to that! Can't have enough of those.
504                 int indexTarget = GetAttribute( "target");
505                 int indexSource = GetAttribute( "source");
506                 const char* sourceId = mReader->getAttributeValue( indexSource);
507                 if( sourceId[0] == '#')
508                     sourceId++;
509                 ChannelMap::iterator cit = channels.find( sourceId);
510                 if( cit != channels.end())
511                     cit->second.mTarget = mReader->getAttributeValue( indexTarget);
512 
513                 if( !mReader->isEmptyElement())
514                     SkipElement();
515             }
516             else
517             {
518                 // ignore the rest
519                 SkipElement();
520             }
521         }
522         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
523         {
524             if( strcmp( mReader->getNodeName(), "animation") != 0)
525                 ThrowException( "Expected end of <animation> element.");
526 
527             break;
528         }
529     }
530 
531     // it turned out to have channels - add them
532     if( !channels.empty())
533     {
534 		// FIXME: Is this essentially doing the same as "single-anim-node" codepath in
535 		//        ColladaLoader::StoreAnimations? For now, this has been deferred to after
536 		//        all animations and all clips have been read. Due to handling of
537 		//        <library_animation_clips> this cannot be done here, as the channel owner
538 		//        is lost, and some exporters make up animations by referring to multiple
539 		//        single-channel animations from an <instance_animation>.
540 /*
541         // special filtering for stupid exporters packing each channel into a separate animation
542         if( channels.size() == 1)
543         {
544             pParent->mChannels.push_back( channels.begin()->second);
545         } else
546 */
547         {
548             // else create the animation, if not done yet, and store the channels
549             if( !anim)
550             {
551                 anim = new Animation;
552                 anim->mName = animName;
553                 pParent->mSubAnims.push_back( anim);
554             }
555             for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
556                 anim->mChannels.push_back( it->second);
557 
558 			if (indexID >= 0)
559 			{
560 				mAnimationLibrary[animID] = anim;
561 			}
562         }
563     }
564 }
565 
566 // ------------------------------------------------------------------------------------------------
567 // Reads an animation sampler into the given anim channel
ReadAnimationSampler(Collada::AnimationChannel & pChannel)568 void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
569 {
570     while( mReader->read())
571     {
572         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
573         {
574             if( IsElement( "input"))
575             {
576                 int indexSemantic = GetAttribute( "semantic");
577                 const char* semantic = mReader->getAttributeValue( indexSemantic);
578                 int indexSource = GetAttribute( "source");
579                 const char* source = mReader->getAttributeValue( indexSource);
580                 if( source[0] != '#')
581                     ThrowException( "Unsupported URL format");
582                 source++;
583 
584                 if( strcmp( semantic, "INPUT") == 0)
585                     pChannel.mSourceTimes = source;
586                 else if( strcmp( semantic, "OUTPUT") == 0)
587                     pChannel.mSourceValues = source;
588                 else if( strcmp( semantic, "IN_TANGENT") == 0)
589                     pChannel.mInTanValues = source;
590                 else if( strcmp( semantic, "OUT_TANGENT") == 0)
591                     pChannel.mOutTanValues = source;
592                 else if( strcmp( semantic, "INTERPOLATION") == 0)
593                     pChannel.mInterpolationValues = source;
594 
595                 if( !mReader->isEmptyElement())
596                     SkipElement();
597             }
598             else
599             {
600                 // ignore the rest
601                 SkipElement();
602             }
603         }
604         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
605         {
606             if( strcmp( mReader->getNodeName(), "sampler") != 0)
607                 ThrowException( "Expected end of <sampler> element.");
608 
609             break;
610         }
611     }
612 }
613 
614 // ------------------------------------------------------------------------------------------------
615 // Reads the skeleton controller library
ReadControllerLibrary()616 void ColladaParser::ReadControllerLibrary()
617 {
618     if (mReader->isEmptyElement())
619         return;
620 
621     while( mReader->read())
622     {
623         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
624         {
625             if( IsElement( "controller"))
626             {
627                 // read ID. Ask the spec if it's necessary or optional... you might be surprised.
628                 int attrID = GetAttribute( "id");
629                 std::string id = mReader->getAttributeValue( attrID);
630 
631                 // create an entry and store it in the library under its ID
632                 mControllerLibrary[id] = Controller();
633 
634                 // read on from there
635                 ReadController( mControllerLibrary[id]);
636             } else
637             {
638                 // ignore the rest
639                 SkipElement();
640             }
641         }
642         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
643         {
644             if( strcmp( mReader->getNodeName(), "library_controllers") != 0)
645                 ThrowException( "Expected end of <library_controllers> element.");
646 
647             break;
648         }
649     }
650 }
651 
652 // ------------------------------------------------------------------------------------------------
653 // Reads a controller into the given mesh structure
ReadController(Collada::Controller & pController)654 void ColladaParser::ReadController( Collada::Controller& pController)
655 {
656     // initial values
657     pController.mType = Skin;
658     pController.mMethod = Normalized;
659     while( mReader->read())
660     {
661         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
662         {
663             // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
664             if( IsElement( "morph"))
665             {
666                 pController.mType = Morph;
667                 int baseIndex = GetAttribute("source");
668                 pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
669                 int methodIndex = GetAttribute("method");
670                 if (methodIndex > 0) {
671                     const char *method = mReader->getAttributeValue(methodIndex);
672                     if (strcmp(method, "RELATIVE") == 0)
673                         pController.mMethod = Relative;
674                 }
675             }
676             else if( IsElement( "skin"))
677             {
678                 // read the mesh it refers to. According to the spec this could also be another
679                 // controller, but I refuse to implement every single idea they've come up with
680                 int sourceIndex = GetAttribute( "source");
681                 pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
682             }
683             else if( IsElement( "bind_shape_matrix"))
684             {
685                 // content is 16 floats to define a matrix... it seems to be important for some models
686                 const char* content = GetTextContent();
687 
688                 // read the 16 floats
689                 for( unsigned int a = 0; a < 16; a++)
690                 {
691                     // read a number
692                     content = fast_atoreal_move<ai_real>( content, pController.mBindShapeMatrix[a]);
693                     // skip whitespace after it
694                     SkipSpacesAndLineEnd( &content);
695                 }
696 
697                 TestClosing( "bind_shape_matrix");
698             }
699             else if( IsElement( "source"))
700             {
701                 // data array - we have specialists to handle this
702                 ReadSource();
703             }
704             else if( IsElement( "joints"))
705             {
706                 ReadControllerJoints( pController);
707             }
708             else if( IsElement( "vertex_weights"))
709             {
710                 ReadControllerWeights( pController);
711             }
712             else if ( IsElement( "targets" ))
713             {
714                 while (mReader->read()) {
715                     if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
716                         if ( IsElement( "input")) {
717                             int semanticsIndex = GetAttribute("semantic");
718                             int sourceIndex = GetAttribute("source");
719 
720                             const char *semantics = mReader->getAttributeValue(semanticsIndex);
721                             const char *source = mReader->getAttributeValue(sourceIndex);
722                             if (strcmp(semantics, "MORPH_TARGET") == 0) {
723                                 pController.mMorphTarget = source + 1;
724                             }
725                             else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
726                             {
727                                 pController.mMorphWeight = source + 1;
728                             }
729                         }
730                     } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
731                         if( strcmp( mReader->getNodeName(), "targets") == 0)
732                             break;
733                         else
734                             ThrowException( "Expected end of <targets> element.");
735                     }
736                 }
737             }
738             else
739             {
740                 // ignore the rest
741                 SkipElement();
742             }
743         }
744         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
745         {
746             if( strcmp( mReader->getNodeName(), "controller") == 0)
747                 break;
748             else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0)
749                 ThrowException( "Expected end of <controller> element.");
750         }
751     }
752 }
753 
754 // ------------------------------------------------------------------------------------------------
755 // Reads the joint definitions for the given controller
ReadControllerJoints(Collada::Controller & pController)756 void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
757 {
758     while( mReader->read())
759     {
760         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
761         {
762             // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
763             if( IsElement( "input"))
764             {
765                 int indexSemantic = GetAttribute( "semantic");
766                 const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
767                 int indexSource = GetAttribute( "source");
768                 const char* attrSource = mReader->getAttributeValue( indexSource);
769 
770                 // local URLS always start with a '#'. We don't support global URLs
771                 if( attrSource[0] != '#')
772                     ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element" );
773                 attrSource++;
774 
775                 // parse source URL to corresponding source
776                 if( strcmp( attrSemantic, "JOINT") == 0)
777                     pController.mJointNameSource = attrSource;
778                 else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
779                     pController.mJointOffsetMatrixSource = attrSource;
780                 else
781                     ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element" );
782 
783                 // skip inner data, if present
784                 if( !mReader->isEmptyElement())
785                     SkipElement();
786             }
787             else
788             {
789                 // ignore the rest
790                 SkipElement();
791             }
792         }
793         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
794         {
795             if( strcmp( mReader->getNodeName(), "joints") != 0)
796                 ThrowException( "Expected end of <joints> element.");
797 
798             break;
799         }
800     }
801 }
802 
803 // ------------------------------------------------------------------------------------------------
804 // Reads the joint weights for the given controller
ReadControllerWeights(Collada::Controller & pController)805 void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
806 {
807     // read vertex count from attributes and resize the array accordingly
808     int indexCount = GetAttribute( "count");
809     size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
810     pController.mWeightCounts.resize( vertexCount);
811 
812     while( mReader->read())
813     {
814         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
815         {
816             // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
817             if( IsElement( "input") && vertexCount > 0 )
818             {
819                 InputChannel channel;
820 
821                 int indexSemantic = GetAttribute( "semantic");
822                 const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
823                 int indexSource = GetAttribute( "source");
824                 const char* attrSource = mReader->getAttributeValue( indexSource);
825                 int indexOffset = TestAttribute( "offset");
826                 if( indexOffset >= 0)
827                     channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
828 
829                 // local URLS always start with a '#'. We don't support global URLs
830                 if( attrSource[0] != '#')
831                     ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element" );
832                 channel.mAccessor = attrSource + 1;
833 
834                 // parse source URL to corresponding source
835                 if( strcmp( attrSemantic, "JOINT") == 0)
836                     pController.mWeightInputJoints = channel;
837                 else if( strcmp( attrSemantic, "WEIGHT") == 0)
838                     pController.mWeightInputWeights = channel;
839                 else
840                     ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element" );
841 
842                 // skip inner data, if present
843                 if( !mReader->isEmptyElement())
844                     SkipElement();
845             }
846             else if( IsElement( "vcount") && vertexCount > 0 )
847             {
848                 // read weight count per vertex
849                 const char* text = GetTextContent();
850                 size_t numWeights = 0;
851                 for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
852                 {
853                     if( *text == 0)
854                         ThrowException( "Out of data while reading <vcount>");
855 
856                     *it = strtoul10( text, &text);
857                     numWeights += *it;
858                     SkipSpacesAndLineEnd( &text);
859                 }
860 
861                 TestClosing( "vcount");
862 
863                 // reserve weight count
864                 pController.mWeights.resize( numWeights);
865             }
866             else if( IsElement( "v") && vertexCount > 0 )
867             {
868                 // read JointIndex - WeightIndex pairs
869                 const char* text = GetTextContent();
870 
871                 for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
872                 {
873                     if( *text == 0)
874                         ThrowException( "Out of data while reading <vertex_weights>");
875                     it->first = strtoul10( text, &text);
876                     SkipSpacesAndLineEnd( &text);
877                     if( *text == 0)
878                         ThrowException( "Out of data while reading <vertex_weights>");
879                     it->second = strtoul10( text, &text);
880                     SkipSpacesAndLineEnd( &text);
881                 }
882 
883                 TestClosing( "v");
884             }
885             else
886             {
887                 // ignore the rest
888                 SkipElement();
889             }
890         }
891         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
892         {
893             if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
894                 ThrowException( "Expected end of <vertex_weights> element.");
895 
896             break;
897         }
898     }
899 }
900 
901 // ------------------------------------------------------------------------------------------------
902 // Reads the image library contents
ReadImageLibrary()903 void ColladaParser::ReadImageLibrary()
904 {
905     if( mReader->isEmptyElement())
906         return;
907 
908     while( mReader->read())
909     {
910         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
911             if( IsElement( "image"))
912             {
913                 // read ID. Another entry which is "optional" by design but obligatory in reality
914                 int attrID = GetAttribute( "id");
915                 std::string id = mReader->getAttributeValue( attrID);
916 
917                 // create an entry and store it in the library under its ID
918                 mImageLibrary[id] = Image();
919 
920                 // read on from there
921                 ReadImage( mImageLibrary[id]);
922             } else
923             {
924                 // ignore the rest
925                 SkipElement();
926             }
927         }
928         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
929             if( strcmp( mReader->getNodeName(), "library_images") != 0)
930                 ThrowException( "Expected end of <library_images> element.");
931 
932             break;
933         }
934     }
935 }
936 
937 // ------------------------------------------------------------------------------------------------
938 // Reads an image entry into the given image
ReadImage(Collada::Image & pImage)939 void ColladaParser::ReadImage( Collada::Image& pImage)
940 {
941     while( mReader->read())
942     {
943         if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
944             // Need to run different code paths here, depending on the Collada XSD version
945             if (IsElement("image")) {
946                 SkipElement();
947             }
948             else if(  IsElement( "init_from"))
949             {
950                 if (mFormat == FV_1_4_n)
951                 {
952                     // FIX: C4D exporter writes empty <init_from/> tags
953                     if (!mReader->isEmptyElement()) {
954                         // element content is filename - hopefully
955                         const char* sz = TestTextContent();
956                         if (sz)pImage.mFileName = sz;
957                         TestClosing( "init_from");
958                     }
959                     if (!pImage.mFileName.length()) {
960                         pImage.mFileName = "unknown_texture";
961                     }
962                 }
963                 else if (mFormat == FV_1_5_n)
964                 {
965                     // make sure we skip over mip and array initializations, which
966                     // we don't support, but which could confuse the loader if
967                     // they're not skipped.
968                     int attrib = TestAttribute("array_index");
969                     if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
970                         DefaultLogger::get()->warn("Collada: Ignoring texture array index");
971                         continue;
972                     }
973 
974                     attrib = TestAttribute("mip_index");
975                     if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
976                         DefaultLogger::get()->warn("Collada: Ignoring MIP map layer");
977                         continue;
978                     }
979 
980                     // TODO: correctly jump over cube and volume maps?
981                 }
982             }
983             else if (mFormat == FV_1_5_n)
984             {
985                 if( IsElement( "ref"))
986                 {
987                     // element content is filename - hopefully
988                     const char* sz = TestTextContent();
989                     if (sz)pImage.mFileName = sz;
990                     TestClosing( "ref");
991                 }
992                 else if( IsElement( "hex") && !pImage.mFileName.length())
993                 {
994                     // embedded image. get format
995                     const int attrib = TestAttribute("format");
996                     if (-1 == attrib)
997                         DefaultLogger::get()->warn("Collada: Unknown image file format");
998                     else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
999 
1000                     const char* data = GetTextContent();
1001 
1002                     // hexadecimal-encoded binary octets. First of all, find the
1003                     // required buffer size to reserve enough storage.
1004                     const char* cur = data;
1005                     while (!IsSpaceOrNewLine(*cur)) cur++;
1006 
1007                     const unsigned int size = (unsigned int)(cur-data) * 2;
1008                     pImage.mImageData.resize(size);
1009                     for (unsigned int i = 0; i < size;++i)
1010                         pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
1011 
1012                     TestClosing( "hex");
1013                 }
1014             }
1015             else
1016             {
1017                 // ignore the rest
1018                 SkipElement();
1019             }
1020         }
1021         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1022             if( strcmp( mReader->getNodeName(), "image") == 0)
1023                 break;
1024         }
1025     }
1026 }
1027 
1028 // ------------------------------------------------------------------------------------------------
1029 // Reads the material library
ReadMaterialLibrary()1030 void ColladaParser::ReadMaterialLibrary()
1031 {
1032     if( mReader->isEmptyElement())
1033         return;
1034 
1035     std::map<std::string, int> names;
1036     while( mReader->read())
1037     {
1038         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1039         {
1040             if( IsElement( "material"))
1041             {
1042                 // read ID. By now you probably know my opinion about this "specification"
1043                 int attrID = GetAttribute( "id");
1044                 std::string id = mReader->getAttributeValue( attrID);
1045 
1046                 std::string name;
1047                 int attrName = TestAttribute("name");
1048                 if (attrName >= 0)
1049                     name = mReader->getAttributeValue( attrName);
1050 
1051                 // create an entry and store it in the library under its ID
1052                 mMaterialLibrary[id] = Material();
1053 
1054                 if( !name.empty())
1055                 {
1056                     std::map<std::string, int>::iterator it = names.find( name);
1057                     if( it != names.end())
1058                     {
1059                         std::ostringstream strStream;
1060                         strStream << ++it->second;
1061                         name.append( " " + strStream.str());
1062                     }
1063                     else
1064                     {
1065                         names[name] = 0;
1066                     }
1067 
1068                     mMaterialLibrary[id].mName = name;
1069                 }
1070 
1071                 ReadMaterial( mMaterialLibrary[id]);
1072             } else
1073             {
1074                 // ignore the rest
1075                 SkipElement();
1076             }
1077         }
1078         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1079         {
1080             if( strcmp( mReader->getNodeName(), "library_materials") != 0)
1081                 ThrowException( "Expected end of <library_materials> element.");
1082 
1083             break;
1084         }
1085     }
1086 }
1087 
1088 // ------------------------------------------------------------------------------------------------
1089 // Reads the light library
ReadLightLibrary()1090 void ColladaParser::ReadLightLibrary()
1091 {
1092     if( mReader->isEmptyElement())
1093         return;
1094 
1095     while( mReader->read())
1096     {
1097         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1098             if( IsElement( "light"))
1099             {
1100                 // read ID. By now you probably know my opinion about this "specification"
1101                 int attrID = GetAttribute( "id");
1102                 std::string id = mReader->getAttributeValue( attrID);
1103 
1104                 // create an entry and store it in the library under its ID
1105                 ReadLight(mLightLibrary[id] = Light());
1106 
1107             } else
1108             {
1109                 // ignore the rest
1110                 SkipElement();
1111             }
1112         }
1113         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
1114             if( strcmp( mReader->getNodeName(), "library_lights") != 0)
1115                 ThrowException( "Expected end of <library_lights> element.");
1116 
1117             break;
1118         }
1119     }
1120 }
1121 
1122 // ------------------------------------------------------------------------------------------------
1123 // Reads the camera library
ReadCameraLibrary()1124 void ColladaParser::ReadCameraLibrary()
1125 {
1126     if( mReader->isEmptyElement())
1127         return;
1128 
1129     while( mReader->read())
1130     {
1131         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1132             if( IsElement( "camera"))
1133             {
1134                 // read ID. By now you probably know my opinion about this "specification"
1135                 int attrID = GetAttribute( "id");
1136                 std::string id = mReader->getAttributeValue( attrID);
1137 
1138                 // create an entry and store it in the library under its ID
1139                 Camera& cam = mCameraLibrary[id];
1140                 attrID = TestAttribute( "name");
1141                 if (attrID != -1)
1142                     cam.mName = mReader->getAttributeValue( attrID);
1143 
1144                 ReadCamera(cam);
1145 
1146             } else
1147             {
1148                 // ignore the rest
1149                 SkipElement();
1150             }
1151         }
1152         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
1153             if( strcmp( mReader->getNodeName(), "library_cameras") != 0)
1154                 ThrowException( "Expected end of <library_cameras> element.");
1155 
1156             break;
1157         }
1158     }
1159 }
1160 
1161 // ------------------------------------------------------------------------------------------------
1162 // Reads a material entry into the given material
ReadMaterial(Collada::Material & pMaterial)1163 void ColladaParser::ReadMaterial( Collada::Material& pMaterial)
1164 {
1165     while( mReader->read())
1166     {
1167         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1168             if (IsElement("material")) {
1169                 SkipElement();
1170             }
1171             else if( IsElement( "instance_effect"))
1172             {
1173                 // referred effect by URL
1174                 int attrUrl = GetAttribute( "url");
1175                 const char* url = mReader->getAttributeValue( attrUrl);
1176                 if( url[0] != '#')
1177                     ThrowException( "Unknown reference format");
1178 
1179                 pMaterial.mEffect = url+1;
1180 
1181                 SkipElement();
1182             } else
1183             {
1184                 // ignore the rest
1185                 SkipElement();
1186             }
1187         }
1188         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1189             if( strcmp( mReader->getNodeName(), "material") != 0)
1190                 ThrowException( "Expected end of <material> element.");
1191 
1192             break;
1193         }
1194     }
1195 }
1196 
1197 // ------------------------------------------------------------------------------------------------
1198 // Reads a light entry into the given light
ReadLight(Collada::Light & pLight)1199 void ColladaParser::ReadLight( Collada::Light& pLight)
1200 {
1201     while( mReader->read())
1202     {
1203         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1204             if (IsElement("light")) {
1205                 SkipElement();
1206             }
1207             else if (IsElement("spot")) {
1208                 pLight.mType = aiLightSource_SPOT;
1209             }
1210             else if (IsElement("ambient")) {
1211                 pLight.mType = aiLightSource_AMBIENT;
1212             }
1213             else if (IsElement("directional")) {
1214                 pLight.mType = aiLightSource_DIRECTIONAL;
1215             }
1216             else if (IsElement("point")) {
1217                 pLight.mType = aiLightSource_POINT;
1218             }
1219             else if (IsElement("color")) {
1220                 // text content contains 3 floats
1221                 const char* content = GetTextContent();
1222 
1223                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.r);
1224                 SkipSpacesAndLineEnd( &content);
1225 
1226                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.g);
1227                 SkipSpacesAndLineEnd( &content);
1228 
1229                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.b);
1230                 SkipSpacesAndLineEnd( &content);
1231 
1232                 TestClosing( "color");
1233             }
1234             else if (IsElement("constant_attenuation")) {
1235                 pLight.mAttConstant = ReadFloatFromTextContent();
1236                 TestClosing("constant_attenuation");
1237             }
1238             else if (IsElement("linear_attenuation")) {
1239                 pLight.mAttLinear = ReadFloatFromTextContent();
1240                 TestClosing("linear_attenuation");
1241             }
1242             else if (IsElement("quadratic_attenuation")) {
1243                 pLight.mAttQuadratic = ReadFloatFromTextContent();
1244                 TestClosing("quadratic_attenuation");
1245             }
1246             else if (IsElement("falloff_angle")) {
1247                 pLight.mFalloffAngle = ReadFloatFromTextContent();
1248                 TestClosing("falloff_angle");
1249             }
1250             else if (IsElement("falloff_exponent")) {
1251                 pLight.mFalloffExponent = ReadFloatFromTextContent();
1252                 TestClosing("falloff_exponent");
1253             }
1254             // FCOLLADA extensions
1255             // -------------------------------------------------------
1256             else if (IsElement("outer_cone")) {
1257                 pLight.mOuterAngle = ReadFloatFromTextContent();
1258                 TestClosing("outer_cone");
1259             }
1260             // ... and this one is even deprecated
1261             else if (IsElement("penumbra_angle")) {
1262                 pLight.mPenumbraAngle = ReadFloatFromTextContent();
1263                 TestClosing("penumbra_angle");
1264             }
1265             else if (IsElement("intensity")) {
1266                 pLight.mIntensity = ReadFloatFromTextContent();
1267                 TestClosing("intensity");
1268             }
1269             else if (IsElement("falloff")) {
1270                 pLight.mOuterAngle = ReadFloatFromTextContent();
1271                 TestClosing("falloff");
1272             }
1273             else if (IsElement("hotspot_beam")) {
1274                 pLight.mFalloffAngle = ReadFloatFromTextContent();
1275                 TestClosing("hotspot_beam");
1276             }
1277             // OpenCOLLADA extensions
1278             // -------------------------------------------------------
1279             else if (IsElement("decay_falloff")) {
1280                 pLight.mOuterAngle = ReadFloatFromTextContent();
1281                 TestClosing("decay_falloff");
1282             }
1283         }
1284         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1285             if( strcmp( mReader->getNodeName(), "light") == 0)
1286                 break;
1287         }
1288     }
1289 }
1290 
1291 // ------------------------------------------------------------------------------------------------
1292 // Reads a camera entry into the given light
ReadCamera(Collada::Camera & pCamera)1293 void ColladaParser::ReadCamera( Collada::Camera& pCamera)
1294 {
1295     while( mReader->read())
1296     {
1297         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1298             if (IsElement("camera")) {
1299                 SkipElement();
1300             }
1301             else if (IsElement("orthographic")) {
1302                 pCamera.mOrtho = true;
1303             }
1304             else if (IsElement("xfov") || IsElement("xmag")) {
1305                 pCamera.mHorFov = ReadFloatFromTextContent();
1306                 TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
1307             }
1308             else if (IsElement("yfov") || IsElement("ymag")) {
1309                 pCamera.mVerFov = ReadFloatFromTextContent();
1310                 TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
1311             }
1312             else if (IsElement("aspect_ratio")) {
1313                 pCamera.mAspect = ReadFloatFromTextContent();
1314                 TestClosing("aspect_ratio");
1315             }
1316             else if (IsElement("znear")) {
1317                 pCamera.mZNear = ReadFloatFromTextContent();
1318                 TestClosing("znear");
1319             }
1320             else if (IsElement("zfar")) {
1321                 pCamera.mZFar = ReadFloatFromTextContent();
1322                 TestClosing("zfar");
1323             }
1324         }
1325         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1326             if( strcmp( mReader->getNodeName(), "camera") == 0)
1327                 break;
1328         }
1329     }
1330 }
1331 
1332 // ------------------------------------------------------------------------------------------------
1333 // Reads the effect library
ReadEffectLibrary()1334 void ColladaParser::ReadEffectLibrary()
1335 {
1336     if (mReader->isEmptyElement()) {
1337         return;
1338     }
1339 
1340     while( mReader->read())
1341     {
1342         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1343             if( IsElement( "effect"))
1344             {
1345                 // read ID. Do I have to repeat my ranting about "optional" attributes?
1346                 int attrID = GetAttribute( "id");
1347                 std::string id = mReader->getAttributeValue( attrID);
1348 
1349                 // create an entry and store it in the library under its ID
1350                 mEffectLibrary[id] = Effect();
1351                 // read on from there
1352                 ReadEffect( mEffectLibrary[id]);
1353             } else
1354             {
1355                 // ignore the rest
1356                 SkipElement();
1357             }
1358         }
1359         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1360             if( strcmp( mReader->getNodeName(), "library_effects") != 0)
1361                 ThrowException( "Expected end of <library_effects> element.");
1362 
1363             break;
1364         }
1365     }
1366 }
1367 
1368 // ------------------------------------------------------------------------------------------------
1369 // Reads an effect entry into the given effect
ReadEffect(Collada::Effect & pEffect)1370 void ColladaParser::ReadEffect( Collada::Effect& pEffect)
1371 {
1372     // for the moment we don't support any other type of effect.
1373     while( mReader->read())
1374     {
1375         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1376         {
1377             if( IsElement( "profile_COMMON"))
1378                 ReadEffectProfileCommon( pEffect);
1379             else
1380                 SkipElement();
1381         }
1382         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1383         {
1384             if( strcmp( mReader->getNodeName(), "effect") != 0)
1385                 ThrowException( "Expected end of <effect> element.");
1386 
1387             break;
1388         }
1389     }
1390 }
1391 
1392 // ------------------------------------------------------------------------------------------------
1393 // Reads an COMMON effect profile
ReadEffectProfileCommon(Collada::Effect & pEffect)1394 void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
1395 {
1396     while( mReader->read())
1397     {
1398         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1399         {
1400             if( IsElement( "newparam")) {
1401                 // save ID
1402                 int attrSID = GetAttribute( "sid");
1403                 std::string sid = mReader->getAttributeValue( attrSID);
1404                 pEffect.mParams[sid] = EffectParam();
1405                 ReadEffectParam( pEffect.mParams[sid]);
1406             }
1407             else if( IsElement( "technique") || IsElement( "extra"))
1408             {
1409                 // just syntactic sugar
1410             }
1411 
1412             else if( mFormat == FV_1_4_n && IsElement( "image"))
1413             {
1414                 // read ID. Another entry which is "optional" by design but obligatory in reality
1415                 int attrID = GetAttribute( "id");
1416                 std::string id = mReader->getAttributeValue( attrID);
1417 
1418                 // create an entry and store it in the library under its ID
1419                 mImageLibrary[id] = Image();
1420 
1421                 // read on from there
1422                 ReadImage( mImageLibrary[id]);
1423             }
1424 
1425             /* Shading modes */
1426             else if( IsElement( "phong"))
1427                 pEffect.mShadeType = Shade_Phong;
1428             else if( IsElement( "constant"))
1429                 pEffect.mShadeType = Shade_Constant;
1430             else if( IsElement( "lambert"))
1431                 pEffect.mShadeType = Shade_Lambert;
1432             else if( IsElement( "blinn"))
1433                 pEffect.mShadeType = Shade_Blinn;
1434 
1435             /* Color + texture properties */
1436             else if( IsElement( "emission"))
1437                 ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive);
1438             else if( IsElement( "ambient"))
1439                 ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient);
1440             else if( IsElement( "diffuse"))
1441                 ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse);
1442             else if( IsElement( "specular"))
1443                 ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular);
1444             else if( IsElement( "reflective")) {
1445                 ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective);
1446             }
1447             else if( IsElement( "transparent")) {
1448                 pEffect.mHasTransparency = true;
1449 
1450                 const char* opaque = mReader->getAttributeValueSafe("opaque");
1451 
1452                 if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
1453                     pEffect.mRGBTransparency = true;
1454                 }
1455 
1456                 // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
1457 				if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
1458 					pEffect.mInvertTransparency = true;
1459 				}
1460 
1461                 ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent);
1462             }
1463             else if( IsElement( "shininess"))
1464                 ReadEffectFloat( pEffect.mShininess);
1465             else if( IsElement( "reflectivity"))
1466                 ReadEffectFloat( pEffect.mReflectivity);
1467 
1468             /* Single scalar properties */
1469             else if( IsElement( "transparency"))
1470                 ReadEffectFloat( pEffect.mTransparency);
1471             else if( IsElement( "index_of_refraction"))
1472                 ReadEffectFloat( pEffect.mRefractIndex);
1473 
1474             // GOOGLEEARTH/OKINO extensions
1475             // -------------------------------------------------------
1476             else if( IsElement( "double_sided"))
1477                 pEffect.mDoubleSided = ReadBoolFromTextContent();
1478 
1479             // FCOLLADA extensions
1480             // -------------------------------------------------------
1481             else if( IsElement( "bump")) {
1482                 aiColor4D dummy;
1483                 ReadEffectColor( dummy,pEffect.mTexBump);
1484             }
1485 
1486             // MAX3D extensions
1487             // -------------------------------------------------------
1488             else if( IsElement( "wireframe"))   {
1489                 pEffect.mWireframe = ReadBoolFromTextContent();
1490                 TestClosing( "wireframe");
1491             }
1492             else if( IsElement( "faceted")) {
1493                 pEffect.mFaceted = ReadBoolFromTextContent();
1494                 TestClosing( "faceted");
1495             }
1496             else
1497             {
1498                 // ignore the rest
1499                 SkipElement();
1500             }
1501         }
1502         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1503             if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0)
1504             {
1505                 break;
1506             }
1507         }
1508     }
1509 }
1510 
1511 // ------------------------------------------------------------------------------------------------
1512 // Read texture wrapping + UV transform settings from a profile==Maya chunk
ReadSamplerProperties(Sampler & out)1513 void ColladaParser::ReadSamplerProperties( Sampler& out )
1514 {
1515     if (mReader->isEmptyElement()) {
1516         return;
1517     }
1518 
1519     while( mReader->read())
1520     {
1521         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1522 
1523             // MAYA extensions
1524             // -------------------------------------------------------
1525             if( IsElement( "wrapU"))        {
1526                 out.mWrapU = ReadBoolFromTextContent();
1527                 TestClosing( "wrapU");
1528             }
1529             else if( IsElement( "wrapV"))   {
1530                 out.mWrapV = ReadBoolFromTextContent();
1531                 TestClosing( "wrapV");
1532             }
1533             else if( IsElement( "mirrorU"))     {
1534                 out.mMirrorU = ReadBoolFromTextContent();
1535                 TestClosing( "mirrorU");
1536             }
1537             else if( IsElement( "mirrorV")) {
1538                 out.mMirrorV = ReadBoolFromTextContent();
1539                 TestClosing( "mirrorV");
1540             }
1541             else if( IsElement( "repeatU")) {
1542                 out.mTransform.mScaling.x = ReadFloatFromTextContent();
1543                 TestClosing( "repeatU");
1544             }
1545             else if( IsElement( "repeatV")) {
1546                 out.mTransform.mScaling.y = ReadFloatFromTextContent();
1547                 TestClosing( "repeatV");
1548             }
1549             else if( IsElement( "offsetU")) {
1550                 out.mTransform.mTranslation.x = ReadFloatFromTextContent();
1551                 TestClosing( "offsetU");
1552             }
1553             else if( IsElement( "offsetV")) {
1554                 out.mTransform.mTranslation.y = ReadFloatFromTextContent();
1555                 TestClosing( "offsetV");
1556             }
1557             else if( IsElement( "rotateUV"))    {
1558                 out.mTransform.mRotation = ReadFloatFromTextContent();
1559                 TestClosing( "rotateUV");
1560             }
1561             else if( IsElement( "blend_mode"))  {
1562 
1563                 const char* sz = GetTextContent();
1564                 // http://www.feelingsoftware.com/content/view/55/72/lang,en/
1565                 // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
1566                 if (0 == ASSIMP_strincmp(sz,"ADD",3))
1567                     out.mOp = aiTextureOp_Add;
1568 
1569                 else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8))
1570                     out.mOp = aiTextureOp_Subtract;
1571 
1572                 else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8))
1573                     out.mOp = aiTextureOp_Multiply;
1574 
1575                 else  {
1576                     DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode");
1577                 }
1578                 TestClosing( "blend_mode");
1579             }
1580             // OKINO extensions
1581             // -------------------------------------------------------
1582             else if( IsElement( "weighting"))   {
1583                 out.mWeighting = ReadFloatFromTextContent();
1584                 TestClosing( "weighting");
1585             }
1586             else if( IsElement( "mix_with_previous_layer")) {
1587                 out.mMixWithPrevious = ReadFloatFromTextContent();
1588                 TestClosing( "mix_with_previous_layer");
1589             }
1590             // MAX3D extensions
1591             // -------------------------------------------------------
1592             else if( IsElement( "amount"))  {
1593                 out.mWeighting = ReadFloatFromTextContent();
1594                 TestClosing( "amount");
1595             }
1596         }
1597         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1598             if( strcmp( mReader->getNodeName(), "technique") == 0)
1599                 break;
1600         }
1601     }
1602 }
1603 
1604 // ------------------------------------------------------------------------------------------------
1605 // Reads an effect entry containing a color or a texture defining that color
ReadEffectColor(aiColor4D & pColor,Sampler & pSampler)1606 void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
1607 {
1608     if (mReader->isEmptyElement())
1609         return;
1610 
1611     // Save current element name
1612     const std::string curElem = mReader->getNodeName();
1613 
1614     while( mReader->read())
1615     {
1616         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1617             if( IsElement( "color"))
1618             {
1619                 // text content contains 4 floats
1620                 const char* content = GetTextContent();
1621 
1622                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.r);
1623                 SkipSpacesAndLineEnd( &content);
1624 
1625                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.g);
1626                 SkipSpacesAndLineEnd( &content);
1627 
1628                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.b);
1629                 SkipSpacesAndLineEnd( &content);
1630 
1631                 content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.a);
1632                 SkipSpacesAndLineEnd( &content);
1633                 TestClosing( "color");
1634             }
1635             else if( IsElement( "texture"))
1636             {
1637                 // get name of source texture/sampler
1638                 int attrTex = GetAttribute( "texture");
1639                 pSampler.mName = mReader->getAttributeValue( attrTex);
1640 
1641                 // get name of UV source channel. Specification demands it to be there, but some exporters
1642                 // don't write it. It will be the default UV channel in case it's missing.
1643                 attrTex = TestAttribute( "texcoord");
1644                 if( attrTex >= 0 )
1645                     pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
1646                 //SkipElement();
1647 
1648                 // as we've read texture, the color needs to be 1,1,1,1
1649                 pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
1650             }
1651             else if( IsElement( "technique"))
1652             {
1653                 const int _profile = GetAttribute( "profile");
1654                 const char* profile = mReader->getAttributeValue( _profile );
1655 
1656                 // Some extensions are quite useful ... ReadSamplerProperties processes
1657                 // several extensions in MAYA, OKINO and MAX3D profiles.
1658                 if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO"))
1659                 {
1660                     // get more information on this sampler
1661                     ReadSamplerProperties(pSampler);
1662                 }
1663                 else SkipElement();
1664             }
1665             else if( !IsElement( "extra"))
1666             {
1667                 // ignore the rest
1668                 SkipElement();
1669             }
1670         }
1671         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
1672             if (mReader->getNodeName() == curElem)
1673                 break;
1674         }
1675     }
1676 }
1677 
1678 // ------------------------------------------------------------------------------------------------
1679 // Reads an effect entry containing a float
ReadEffectFloat(ai_real & pFloat)1680 void ColladaParser::ReadEffectFloat( ai_real& pFloat)
1681 {
1682     while( mReader->read())
1683     {
1684         if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
1685             if( IsElement( "float"))
1686             {
1687                 // text content contains a single floats
1688                 const char* content = GetTextContent();
1689                 content = fast_atoreal_move<ai_real>( content, pFloat);
1690                 SkipSpacesAndLineEnd( &content);
1691 
1692                 TestClosing( "float");
1693             } else
1694             {
1695                 // ignore the rest
1696                 SkipElement();
1697             }
1698         }
1699         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
1700             break;
1701         }
1702     }
1703 }
1704 
1705 // ------------------------------------------------------------------------------------------------
1706 // Reads an effect parameter specification of any kind
ReadEffectParam(Collada::EffectParam & pParam)1707 void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam)
1708 {
1709     while( mReader->read())
1710     {
1711         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1712             if( IsElement( "surface"))
1713             {
1714                 // image ID given inside <init_from> tags
1715                 TestOpening( "init_from");
1716                 const char* content = GetTextContent();
1717                 pParam.mType = Param_Surface;
1718                 pParam.mReference = content;
1719                 TestClosing( "init_from");
1720 
1721                 // don't care for remaining stuff
1722                 SkipElement( "surface");
1723             }
1724             else if( IsElement( "sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat))
1725             {
1726                 // surface ID is given inside <source> tags
1727                 TestOpening( "source");
1728                 const char* content = GetTextContent();
1729                 pParam.mType = Param_Sampler;
1730                 pParam.mReference = content;
1731                 TestClosing( "source");
1732 
1733                 // don't care for remaining stuff
1734                 SkipElement( "sampler2D");
1735             }
1736             else if( IsElement( "sampler2D"))
1737             {
1738                 // surface ID is given inside <instance_image> tags
1739                 TestOpening( "instance_image");
1740                 int attrURL = GetAttribute("url");
1741                 const char* url = mReader->getAttributeValue( attrURL);
1742                 if( url[0] != '#')
1743                     ThrowException( "Unsupported URL format in instance_image");
1744                 url++;
1745                 pParam.mType = Param_Sampler;
1746                 pParam.mReference = url;
1747                 SkipElement( "sampler2D");
1748             } else
1749             {
1750                 // ignore unknown element
1751                 SkipElement();
1752             }
1753         }
1754         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1755             break;
1756         }
1757     }
1758 }
1759 
1760 // ------------------------------------------------------------------------------------------------
1761 // Reads the geometry library contents
ReadGeometryLibrary()1762 void ColladaParser::ReadGeometryLibrary()
1763 {
1764     if( mReader->isEmptyElement())
1765         return;
1766 
1767     while( mReader->read())
1768     {
1769         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1770         {
1771             if( IsElement( "geometry"))
1772             {
1773                 // read ID. Another entry which is "optional" by design but obligatory in reality
1774                 int indexID = GetAttribute( "id");
1775                 std::string id = mReader->getAttributeValue( indexID);
1776 
1777                 // TODO: (thom) support SIDs
1778                 // ai_assert( TestAttribute( "sid") == -1);
1779 
1780                 // create a mesh and store it in the library under its ID
1781                 Mesh* mesh = new Mesh;
1782                 mMeshLibrary[id] = mesh;
1783 
1784                 // read the mesh name if it exists
1785                 const int nameIndex = TestAttribute("name");
1786                 if(nameIndex != -1)
1787                 {
1788                     mesh->mName = mReader->getAttributeValue(nameIndex);
1789                 }
1790 
1791                 // read on from there
1792                 ReadGeometry( mesh);
1793             } else
1794             {
1795                 // ignore the rest
1796                 SkipElement();
1797             }
1798         }
1799         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1800         {
1801             if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
1802                 ThrowException( "Expected end of <library_geometries> element.");
1803 
1804             break;
1805         }
1806     }
1807 }
1808 
1809 // ------------------------------------------------------------------------------------------------
1810 // Reads a geometry from the geometry library.
ReadGeometry(Collada::Mesh * pMesh)1811 void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
1812 {
1813     if( mReader->isEmptyElement())
1814         return;
1815 
1816     while( mReader->read())
1817     {
1818         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1819         {
1820             if( IsElement( "mesh"))
1821             {
1822                 // read on from there
1823                 ReadMesh( pMesh);
1824             } else
1825             {
1826                 // ignore the rest
1827                 SkipElement();
1828             }
1829         }
1830         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1831         {
1832             if( strcmp( mReader->getNodeName(), "geometry") != 0)
1833                 ThrowException( "Expected end of <geometry> element.");
1834 
1835             break;
1836         }
1837     }
1838 }
1839 
1840 // ------------------------------------------------------------------------------------------------
1841 // Reads a mesh from the geometry library
ReadMesh(Mesh * pMesh)1842 void ColladaParser::ReadMesh( Mesh* pMesh)
1843 {
1844     if( mReader->isEmptyElement())
1845         return;
1846 
1847     while( mReader->read())
1848     {
1849         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1850         {
1851             if( IsElement( "source"))
1852             {
1853                 // we have professionals dealing with this
1854                 ReadSource();
1855             }
1856             else if( IsElement( "vertices"))
1857             {
1858                 // read per-vertex mesh data
1859                 ReadVertexData( pMesh);
1860             }
1861             else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
1862                 || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
1863             {
1864                 // read per-index mesh data and faces setup
1865                 ReadIndexData( pMesh);
1866             } else
1867             {
1868                 // ignore the restf
1869                 SkipElement();
1870             }
1871         }
1872         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1873         {
1874             if( strcmp( mReader->getNodeName(), "technique_common") == 0)
1875             {
1876                 // end of another meaningless element - read over it
1877             }
1878             else if( strcmp( mReader->getNodeName(), "mesh") == 0)
1879             {
1880                 // end of <mesh> element - we're done here
1881                 break;
1882             } else
1883             {
1884                 // everything else should be punished
1885                 ThrowException( "Expected end of <mesh> element.");
1886             }
1887         }
1888     }
1889 }
1890 
1891 // ------------------------------------------------------------------------------------------------
1892 // Reads a source element
ReadSource()1893 void ColladaParser::ReadSource()
1894 {
1895     int indexID = GetAttribute( "id");
1896     std::string sourceID = mReader->getAttributeValue( indexID);
1897 
1898     while( mReader->read())
1899     {
1900         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1901         {
1902             if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array"))
1903             {
1904                 ReadDataArray();
1905             }
1906             else if( IsElement( "technique_common"))
1907             {
1908                 // I don't care for your profiles
1909             }
1910             else if( IsElement( "accessor"))
1911             {
1912                 ReadAccessor( sourceID);
1913             } else
1914             {
1915                 // ignore the rest
1916                 SkipElement();
1917             }
1918         }
1919         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1920         {
1921             if( strcmp( mReader->getNodeName(), "source") == 0)
1922             {
1923                 // end of <source> - we're done
1924                 break;
1925             }
1926             else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
1927             {
1928                 // end of another meaningless element - read over it
1929             } else
1930             {
1931                 // everything else should be punished
1932                 ThrowException( "Expected end of <source> element.");
1933             }
1934         }
1935     }
1936 }
1937 
1938 // ------------------------------------------------------------------------------------------------
1939 // Reads a data array holding a number of floats, and stores it in the global library
ReadDataArray()1940 void ColladaParser::ReadDataArray()
1941 {
1942     std::string elmName = mReader->getNodeName();
1943     bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
1944   bool isEmptyElement = mReader->isEmptyElement();
1945 
1946     // read attributes
1947     int indexID = GetAttribute( "id");
1948     std::string id = mReader->getAttributeValue( indexID);
1949     int indexCount = GetAttribute( "count");
1950     unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
1951     const char* content = TestTextContent();
1952 
1953   // read values and store inside an array in the data library
1954   mDataLibrary[id] = Data();
1955   Data& data = mDataLibrary[id];
1956   data.mIsStringArray = isStringArray;
1957 
1958   // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
1959   if (content)
1960   {
1961         if( isStringArray)
1962         {
1963             data.mStrings.reserve( count);
1964             std::string s;
1965 
1966             for( unsigned int a = 0; a < count; a++)
1967             {
1968                 if( *content == 0)
1969                     ThrowException( "Expected more values while reading IDREF_array contents.");
1970 
1971                 s.clear();
1972                 while( !IsSpaceOrNewLine( *content))
1973                     s += *content++;
1974                 data.mStrings.push_back( s);
1975 
1976                 SkipSpacesAndLineEnd( &content);
1977             }
1978         } else
1979         {
1980             data.mValues.reserve( count);
1981 
1982             for( unsigned int a = 0; a < count; a++)
1983             {
1984                 if( *content == 0)
1985                     ThrowException( "Expected more values while reading float_array contents.");
1986 
1987                 ai_real value;
1988                 // read a number
1989                 content = fast_atoreal_move<ai_real>( content, value);
1990                 data.mValues.push_back( value);
1991                 // skip whitespace after it
1992                 SkipSpacesAndLineEnd( &content);
1993             }
1994         }
1995     }
1996 
1997   // test for closing tag
1998   if( !isEmptyElement )
1999     TestClosing( elmName.c_str());
2000 }
2001 
2002 // ------------------------------------------------------------------------------------------------
2003 // Reads an accessor and stores it in the global library
ReadAccessor(const std::string & pID)2004 void ColladaParser::ReadAccessor( const std::string& pID)
2005 {
2006     // read accessor attributes
2007     int attrSource = GetAttribute( "source");
2008     const char* source = mReader->getAttributeValue( attrSource);
2009     if( source[0] != '#')
2010         ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element." );
2011     int attrCount = GetAttribute( "count");
2012     unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
2013     int attrOffset = TestAttribute( "offset");
2014     unsigned int offset = 0;
2015     if( attrOffset > -1)
2016         offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
2017     int attrStride = TestAttribute( "stride");
2018     unsigned int stride = 1;
2019     if( attrStride > -1)
2020         stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
2021 
2022     // store in the library under the given ID
2023     mAccessorLibrary[pID] = Accessor();
2024     Accessor& acc = mAccessorLibrary[pID];
2025     acc.mCount = count;
2026     acc.mOffset = offset;
2027     acc.mStride = stride;
2028     acc.mSource = source+1; // ignore the leading '#'
2029     acc.mSize = 0; // gets incremented with every param
2030 
2031     // and read the components
2032     while( mReader->read())
2033     {
2034         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2035         {
2036             if( IsElement( "param"))
2037             {
2038                 // read data param
2039                 int attrName = TestAttribute( "name");
2040                 std::string name;
2041                 if( attrName > -1)
2042                 {
2043                     name = mReader->getAttributeValue( attrName);
2044 
2045                     // analyse for common type components and store it's sub-offset in the corresponding field
2046 
2047                     /* Cartesian coordinates */
2048                     if( name == "X") acc.mSubOffset[0] = acc.mParams.size();
2049                     else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
2050                     else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
2051 
2052                     /* RGBA colors */
2053                     else if( name == "R") acc.mSubOffset[0] = acc.mParams.size();
2054                     else if( name == "G") acc.mSubOffset[1] = acc.mParams.size();
2055                     else if( name == "B") acc.mSubOffset[2] = acc.mParams.size();
2056                     else if( name == "A") acc.mSubOffset[3] = acc.mParams.size();
2057 
2058                     /* UVWQ (STPQ) texture coordinates */
2059                     else if( name == "S") acc.mSubOffset[0] = acc.mParams.size();
2060                     else if( name == "T") acc.mSubOffset[1] = acc.mParams.size();
2061                     else if( name == "P") acc.mSubOffset[2] = acc.mParams.size();
2062                 //  else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
2063                     /* 4D uv coordinates are not supported in Assimp */
2064 
2065                     /* Generic extra data, interpreted as UV data, too*/
2066                     else if( name == "U") acc.mSubOffset[0] = acc.mParams.size();
2067                     else if( name == "V") acc.mSubOffset[1] = acc.mParams.size();
2068                     //else
2069                     //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
2070                 }
2071 
2072                 // read data type
2073                 int attrType = TestAttribute( "type");
2074                 if( attrType > -1)
2075                 {
2076                     // for the moment we only distinguish between a 4x4 matrix and anything else.
2077                     // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
2078                     // which should be tested for here.
2079                     std::string type = mReader->getAttributeValue( attrType);
2080                     if( type == "float4x4")
2081                         acc.mSize += 16;
2082                     else
2083                         acc.mSize += 1;
2084                 }
2085 
2086                 acc.mParams.push_back( name);
2087 
2088                 // skip remaining stuff of this element, if any
2089                 SkipElement();
2090             } else
2091             {
2092                 ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>" );
2093             }
2094         }
2095         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2096         {
2097             if( strcmp( mReader->getNodeName(), "accessor") != 0)
2098                 ThrowException( "Expected end of <accessor> element.");
2099             break;
2100         }
2101     }
2102 }
2103 
2104 // ------------------------------------------------------------------------------------------------
2105 // Reads input declarations of per-vertex mesh data into the given mesh
ReadVertexData(Mesh * pMesh)2106 void ColladaParser::ReadVertexData( Mesh* pMesh)
2107 {
2108     // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
2109     int attrID= GetAttribute( "id");
2110     pMesh->mVertexID = mReader->getAttributeValue( attrID);
2111 
2112     // a number of <input> elements
2113     while( mReader->read())
2114     {
2115         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2116         {
2117             if( IsElement( "input"))
2118             {
2119                 ReadInputChannel( pMesh->mPerVertexData);
2120             } else
2121             {
2122                 ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>" );
2123             }
2124         }
2125         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2126         {
2127             if( strcmp( mReader->getNodeName(), "vertices") != 0)
2128                 ThrowException( "Expected end of <vertices> element.");
2129 
2130             break;
2131         }
2132     }
2133 }
2134 
2135 // ------------------------------------------------------------------------------------------------
2136 // Reads input declarations of per-index mesh data into the given mesh
ReadIndexData(Mesh * pMesh)2137 void ColladaParser::ReadIndexData( Mesh* pMesh)
2138 {
2139     std::vector<size_t> vcount;
2140     std::vector<InputChannel> perIndexData;
2141 
2142     // read primitive count from the attribute
2143     int attrCount = GetAttribute( "count");
2144     size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
2145     // some mesh types (e.g. tristrips) don't specify primitive count upfront,
2146     // so we need to sum up the actual number of primitives while we read the <p>-tags
2147     size_t actualPrimitives = 0;
2148 
2149     // material subgroup
2150     int attrMaterial = TestAttribute( "material");
2151     SubMesh subgroup;
2152     if( attrMaterial > -1)
2153         subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
2154 
2155     // distinguish between polys and triangles
2156     std::string elementName = mReader->getNodeName();
2157     PrimitiveType primType = Prim_Invalid;
2158     if( IsElement( "lines"))
2159         primType = Prim_Lines;
2160     else if( IsElement( "linestrips"))
2161         primType = Prim_LineStrip;
2162     else if( IsElement( "polygons"))
2163         primType = Prim_Polygon;
2164     else if( IsElement( "polylist"))
2165         primType = Prim_Polylist;
2166     else if( IsElement( "triangles"))
2167         primType = Prim_Triangles;
2168     else if( IsElement( "trifans"))
2169         primType = Prim_TriFans;
2170     else if( IsElement( "tristrips"))
2171         primType = Prim_TriStrips;
2172 
2173     ai_assert( primType != Prim_Invalid);
2174 
2175     // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
2176     while( mReader->read())
2177     {
2178         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2179         {
2180             if( IsElement( "input"))
2181             {
2182                 ReadInputChannel( perIndexData);
2183             }
2184             else if( IsElement( "vcount"))
2185             {
2186                 if( !mReader->isEmptyElement())
2187                 {
2188                     if (numPrimitives)  // It is possible to define a mesh without any primitives
2189                     {
2190                         // case <polylist> - specifies the number of indices for each polygon
2191                         const char* content = GetTextContent();
2192                         vcount.reserve( numPrimitives);
2193                         for( unsigned int a = 0; a < numPrimitives; a++)
2194                         {
2195                             if( *content == 0)
2196                                 ThrowException( "Expected more values while reading <vcount> contents.");
2197                             // read a number
2198                             vcount.push_back( (size_t) strtoul10( content, &content));
2199                             // skip whitespace after it
2200                             SkipSpacesAndLineEnd( &content);
2201                         }
2202                     }
2203 
2204                     TestClosing( "vcount");
2205                 }
2206             }
2207             else if( IsElement( "p"))
2208             {
2209                 if( !mReader->isEmptyElement())
2210                 {
2211                     // now here the actual fun starts - these are the indices to construct the mesh data from
2212                     actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
2213                 }
2214             }
2215             else if (IsElement("extra"))
2216             {
2217                 SkipElement("extra");
2218             } else if ( IsElement("ph")) {
2219                 SkipElement("ph");
2220             } else {
2221                 ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">" );
2222             }
2223         }
2224         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2225         {
2226             if( mReader->getNodeName() != elementName)
2227                 ThrowException( format() << "Expected end of <" << elementName << "> element." );
2228 
2229             break;
2230         }
2231     }
2232 
2233 #ifdef ASSIMP_BUILD_DEBUG
2234 	if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
2235         primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
2236         ai_assert(actualPrimitives == numPrimitives);
2237     }
2238 #endif
2239 
2240     // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
2241     subgroup.mNumFaces = actualPrimitives;
2242     pMesh->mSubMeshes.push_back(subgroup);
2243 }
2244 
2245 // ------------------------------------------------------------------------------------------------
2246 // Reads a single input channel element and stores it in the given array, if valid
ReadInputChannel(std::vector<InputChannel> & poChannels)2247 void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
2248 {
2249     InputChannel channel;
2250 
2251     // read semantic
2252     int attrSemantic = GetAttribute( "semantic");
2253     std::string semantic = mReader->getAttributeValue( attrSemantic);
2254     channel.mType = GetTypeForSemantic( semantic);
2255 
2256     // read source
2257     int attrSource = GetAttribute( "source");
2258     const char* source = mReader->getAttributeValue( attrSource);
2259     if( source[0] != '#')
2260         ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element." );
2261     channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
2262 
2263     // read index offset, if per-index <input>
2264     int attrOffset = TestAttribute( "offset");
2265     if( attrOffset > -1)
2266         channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
2267 
2268     // read set if texture coordinates
2269     if(channel.mType == IT_Texcoord || channel.mType == IT_Color){
2270         int attrSet = TestAttribute("set");
2271         if(attrSet > -1){
2272             attrSet = mReader->getAttributeValueAsInt( attrSet);
2273             if(attrSet < 0)
2274                 ThrowException( format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element" );
2275 
2276             channel.mIndex = attrSet;
2277         }
2278     }
2279 
2280     // store, if valid type
2281     if( channel.mType != IT_Invalid)
2282         poChannels.push_back( channel);
2283 
2284     // skip remaining stuff of this element, if any
2285     SkipElement();
2286 }
2287 
2288 // ------------------------------------------------------------------------------------------------
2289 // Reads a <p> primitive index list and assembles the mesh data into the given mesh
ReadPrimitives(Mesh * pMesh,std::vector<InputChannel> & pPerIndexChannels,size_t pNumPrimitives,const std::vector<size_t> & pVCount,PrimitiveType pPrimType)2290 size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
2291     size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
2292 {
2293     // determine number of indices coming per vertex
2294     // find the offset index for all per-vertex channels
2295     size_t numOffsets = 1;
2296     size_t perVertexOffset = SIZE_MAX; // invalid value
2297     for( const InputChannel& channel : pPerIndexChannels)
2298     {
2299         numOffsets = std::max( numOffsets, channel.mOffset+1);
2300         if( channel.mType == IT_Vertex)
2301             perVertexOffset = channel.mOffset;
2302     }
2303 
2304     // determine the expected number of indices
2305     size_t expectedPointCount = 0;
2306     switch( pPrimType)
2307     {
2308         case Prim_Polylist:
2309         {
2310             for( size_t i : pVCount)
2311                 expectedPointCount += i;
2312             break;
2313         }
2314         case Prim_Lines:
2315             expectedPointCount = 2 * pNumPrimitives;
2316             break;
2317         case Prim_Triangles:
2318             expectedPointCount = 3 * pNumPrimitives;
2319             break;
2320         default:
2321             // other primitive types don't state the index count upfront... we need to guess
2322             break;
2323     }
2324 
2325     // and read all indices into a temporary array
2326     std::vector<size_t> indices;
2327     if( expectedPointCount > 0)
2328         indices.reserve( expectedPointCount * numOffsets);
2329 
2330     if (pNumPrimitives > 0) // It is possible to not contain any indices
2331     {
2332         const char* content = GetTextContent();
2333         while( *content != 0)
2334         {
2335             // read a value.
2336             // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
2337             int value = std::max( 0, strtol10( content, &content));
2338             indices.push_back( size_t( value));
2339             // skip whitespace after it
2340             SkipSpacesAndLineEnd( &content);
2341         }
2342     }
2343 
2344 	// complain if the index count doesn't fit
2345     if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
2346         if (pPrimType == Prim_Lines) {
2347             // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
2348             ReportWarning( "Expected different index count in <p> element, %d instead of %d.", indices.size(), expectedPointCount * numOffsets);
2349             pNumPrimitives = (indices.size() / numOffsets) / 2;
2350         } else
2351             ThrowException( "Expected different index count in <p> element.");
2352 
2353     } else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
2354 		ThrowException( "Expected different index count in <p> element.");
2355 
2356 	// find the data for all sources
2357     for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
2358     {
2359         InputChannel& input = *it;
2360         if( input.mResolved)
2361             continue;
2362 
2363         // find accessor
2364         input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
2365         // resolve accessor's data pointer as well, if necessary
2366         const Accessor* acc = input.mResolved;
2367         if( !acc->mData)
2368             acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
2369     }
2370     // and the same for the per-index channels
2371     for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
2372     {
2373         InputChannel& input = *it;
2374         if( input.mResolved)
2375             continue;
2376 
2377         // ignore vertex pointer, it doesn't refer to an accessor
2378         if( input.mType == IT_Vertex)
2379         {
2380             // warn if the vertex channel does not refer to the <vertices> element in the same mesh
2381             if( input.mAccessor != pMesh->mVertexID)
2382                 ThrowException( "Unsupported vertex referencing scheme.");
2383             continue;
2384         }
2385 
2386         // find accessor
2387         input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
2388         // resolve accessor's data pointer as well, if necessary
2389         const Accessor* acc = input.mResolved;
2390         if( !acc->mData)
2391             acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
2392     }
2393 
2394     // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
2395     size_t numPrimitives = pNumPrimitives;
2396     if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
2397         numPrimitives = 1;
2398     // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
2399     if ( pPrimType == Prim_TriStrips){
2400         size_t numberOfVertices = indices.size() / numOffsets;
2401         numPrimitives = numberOfVertices - 2;
2402     }
2403     if (pPrimType == Prim_LineStrip) {
2404         size_t numberOfVertices = indices.size() / numOffsets;
2405         numPrimitives = numberOfVertices - 1;
2406     }
2407 
2408     pMesh->mFaceSize.reserve( numPrimitives);
2409     pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
2410 
2411     size_t polylistStartVertex = 0;
2412     for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
2413     {
2414         // determine number of points for this primitive
2415         size_t numPoints = 0;
2416         switch( pPrimType)
2417         {
2418             case Prim_Lines:
2419                 numPoints = 2;
2420                 for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
2421                     CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2422                 break;
2423             case Prim_LineStrip:
2424                 numPoints = 2;
2425                 for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
2426                     CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2427                 break;
2428             case Prim_Triangles:
2429                 numPoints = 3;
2430                 for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
2431                     CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2432                 break;
2433             case Prim_TriStrips:
2434                 numPoints = 3;
2435                 ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2436                 break;
2437             case Prim_Polylist:
2438                 numPoints = pVCount[currentPrimitive];
2439                 for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
2440                     CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
2441                 polylistStartVertex += numPoints;
2442                 break;
2443             case Prim_TriFans:
2444             case Prim_Polygon:
2445                 numPoints = indices.size() / numOffsets;
2446                 for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
2447                     CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2448                 break;
2449             default:
2450                 // LineStrip is not supported due to expected index unmangling
2451                 ThrowException( "Unsupported primitive type.");
2452                 break;
2453         }
2454 
2455         // store the face size to later reconstruct the face from
2456         pMesh->mFaceSize.push_back( numPoints);
2457     }
2458 
2459     // if I ever get my hands on that guy who invented this steaming pile of indirection...
2460     TestClosing( "p");
2461     return numPrimitives;
2462 }
2463 
2464 ///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels.
2465 ///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
2466 ///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
CopyVertex(size_t currentVertex,size_t numOffsets,size_t numPoints,size_t perVertexOffset,Mesh * pMesh,std::vector<InputChannel> & pPerIndexChannels,size_t currentPrimitive,const std::vector<size_t> & indices)2467 void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
2468     // calculate the base offset of the vertex whose attributes we ant to copy
2469     size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
2470 
2471     // don't overrun the boundaries of the index list
2472     ai_assert((baseOffset + numOffsets - 1) < indices.size());
2473 
2474     // extract per-vertex channels using the global per-vertex offset
2475     for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
2476         ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
2477     // and extract per-index channels using there specified offset
2478     for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
2479         ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
2480 
2481     // store the vertex-data index for later assignment of bone vertex weights
2482     pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
2483 }
2484 
ReadPrimTriStrips(size_t numOffsets,size_t perVertexOffset,Mesh * pMesh,std::vector<InputChannel> & pPerIndexChannels,size_t currentPrimitive,const std::vector<size_t> & indices)2485 void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
2486     if (currentPrimitive % 2 != 0){
2487         //odd tristrip triangles need their indices mangled, to preserve winding direction
2488         CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2489         CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2490         CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2491     }
2492     else {//for non tristrips or even tristrip triangles
2493         CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2494         CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2495         CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
2496     }
2497 }
2498 
2499 // ------------------------------------------------------------------------------------------------
2500 // Extracts a single object from an input channel and stores it in the appropriate mesh data array
ExtractDataObjectFromChannel(const InputChannel & pInput,size_t pLocalIndex,Mesh * pMesh)2501 void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
2502 {
2503     // ignore vertex referrer - we handle them that separate
2504     if( pInput.mType == IT_Vertex)
2505         return;
2506 
2507     const Accessor& acc = *pInput.mResolved;
2508     if( pLocalIndex >= acc.mCount)
2509         ThrowException( format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification" );
2510 
2511     // get a pointer to the start of the data object referred to by the accessor and the local index
2512     const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
2513 
2514     // assemble according to the accessors component sub-offset list. We don't care, yet,
2515     // what kind of object exactly we're extracting here
2516     ai_real obj[4];
2517     for( size_t c = 0; c < 4; ++c)
2518         obj[c] = dataObject[acc.mSubOffset[c]];
2519 
2520     // now we reinterpret it according to the type we're reading here
2521     switch( pInput.mType)
2522     {
2523         case IT_Position: // ignore all position streams except 0 - there can be only one position
2524             if( pInput.mIndex == 0)
2525                 pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2526             else
2527                 DefaultLogger::get()->error("Collada: just one vertex position stream supported");
2528             break;
2529         case IT_Normal:
2530             // pad to current vertex count if necessary
2531             if( pMesh->mNormals.size() < pMesh->mPositions.size()-1)
2532                 pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0));
2533 
2534             // ignore all normal streams except 0 - there can be only one normal
2535             if( pInput.mIndex == 0)
2536                 pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2537             else
2538                 DefaultLogger::get()->error("Collada: just one vertex normal stream supported");
2539             break;
2540         case IT_Tangent:
2541             // pad to current vertex count if necessary
2542             if( pMesh->mTangents.size() < pMesh->mPositions.size()-1)
2543                 pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0));
2544 
2545             // ignore all tangent streams except 0 - there can be only one tangent
2546             if( pInput.mIndex == 0)
2547                 pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2548             else
2549                 DefaultLogger::get()->error("Collada: just one vertex tangent stream supported");
2550             break;
2551         case IT_Bitangent:
2552             // pad to current vertex count if necessary
2553             if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1)
2554                 pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1));
2555 
2556             // ignore all bitangent streams except 0 - there can be only one bitangent
2557             if( pInput.mIndex == 0)
2558                 pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2559             else
2560                 DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported");
2561             break;
2562         case IT_Texcoord:
2563             // up to 4 texture coord sets are fine, ignore the others
2564             if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
2565             {
2566                 // pad to current vertex count if necessary
2567                 if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1)
2568                     pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(),
2569                         pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0));
2570 
2571                 pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2]));
2572                 if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
2573                     pMesh->mNumUVComponents[pInput.mIndex]=3;
2574             }   else
2575             {
2576                 DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping.");
2577             }
2578             break;
2579         case IT_Color:
2580             // up to 4 color sets are fine, ignore the others
2581             if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
2582             {
2583                 // pad to current vertex count if necessary
2584                 if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1)
2585                     pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(),
2586                         pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1));
2587 
2588                 aiColor4D result(0, 0, 0, 1);
2589                 for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
2590                 {
2591                     result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
2592                 }
2593                 pMesh->mColors[pInput.mIndex].push_back(result);
2594             } else
2595             {
2596                 DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping.");
2597             }
2598 
2599             break;
2600         default:
2601             // IT_Invalid and IT_Vertex
2602             ai_assert(false && "shouldn't ever get here");
2603     }
2604 }
2605 
2606 // ------------------------------------------------------------------------------------------------
2607 // Reads the library of node hierarchies and scene parts
ReadSceneLibrary()2608 void ColladaParser::ReadSceneLibrary()
2609 {
2610     if( mReader->isEmptyElement())
2611         return;
2612 
2613     while( mReader->read())
2614     {
2615         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2616         {
2617             // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
2618             if( IsElement( "visual_scene"))
2619             {
2620                 // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
2621                 int indexID = GetAttribute( "id");
2622                 const char* attrID = mReader->getAttributeValue( indexID);
2623 
2624                 // read name if given.
2625                 int indexName = TestAttribute( "name");
2626                 const char* attrName = "unnamed";
2627                 if( indexName > -1)
2628                     attrName = mReader->getAttributeValue( indexName);
2629 
2630                 // create a node and store it in the library under its ID
2631                 Node* node = new Node;
2632                 node->mID = attrID;
2633                 node->mName = attrName;
2634                 mNodeLibrary[node->mID] = node;
2635 
2636                 ReadSceneNode( node);
2637             } else
2638             {
2639                 // ignore the rest
2640                 SkipElement();
2641             }
2642         }
2643         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2644         {
2645             if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
2646                 //ThrowException( "Expected end of \"library_visual_scenes\" element.");
2647 
2648             break;
2649         }
2650     }
2651 }
2652 
2653 // ------------------------------------------------------------------------------------------------
2654 // Reads a scene node's contents including children and stores it in the given node
ReadSceneNode(Node * pNode)2655 void ColladaParser::ReadSceneNode( Node* pNode)
2656 {
2657     // quit immediately on <bla/> elements
2658     if( mReader->isEmptyElement())
2659         return;
2660 
2661     while( mReader->read())
2662     {
2663         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2664         {
2665             if( IsElement( "node"))
2666             {
2667                 Node* child = new Node;
2668                 int attrID = TestAttribute( "id");
2669                 if( attrID > -1)
2670                     child->mID = mReader->getAttributeValue( attrID);
2671                 int attrSID = TestAttribute( "sid");
2672                 if( attrSID > -1)
2673                     child->mSID = mReader->getAttributeValue( attrSID);
2674 
2675                 int attrName = TestAttribute( "name");
2676                 if( attrName > -1)
2677                     child->mName = mReader->getAttributeValue( attrName);
2678 
2679                 // TODO: (thom) support SIDs
2680                 // ai_assert( TestAttribute( "sid") == -1);
2681 
2682                 if (pNode)
2683                 {
2684                     pNode->mChildren.push_back( child);
2685                     child->mParent = pNode;
2686                 }
2687                 else
2688                 {
2689                     // no parent node given, probably called from <library_nodes> element.
2690                     // create new node in node library
2691                     mNodeLibrary[child->mID] = child;
2692                 }
2693 
2694                 // read on recursively from there
2695                 ReadSceneNode( child);
2696                 continue;
2697             }
2698             // For any further stuff we need a valid node to work on
2699             else if (!pNode)
2700                 continue;
2701 
2702             if( IsElement( "lookat"))
2703                 ReadNodeTransformation( pNode, TF_LOOKAT);
2704             else if( IsElement( "matrix"))
2705                 ReadNodeTransformation( pNode, TF_MATRIX);
2706             else if( IsElement( "rotate"))
2707                 ReadNodeTransformation( pNode, TF_ROTATE);
2708             else if( IsElement( "scale"))
2709                 ReadNodeTransformation( pNode, TF_SCALE);
2710             else if( IsElement( "skew"))
2711                 ReadNodeTransformation( pNode, TF_SKEW);
2712             else if( IsElement( "translate"))
2713                 ReadNodeTransformation( pNode, TF_TRANSLATE);
2714             else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
2715             {
2716                 // ... scene evaluation or, in other words, postprocessing pipeline,
2717                 // or, again in other words, a turing-complete description how to
2718                 // render a Collada scene. The only thing that is interesting for
2719                 // us is the primary camera.
2720                 int attrId = TestAttribute("camera_node");
2721                 if (-1 != attrId)
2722                 {
2723                     const char* s = mReader->getAttributeValue(attrId);
2724                     if (s[0] != '#')
2725                         DefaultLogger::get()->error("Collada: Unresolved reference format of camera");
2726                     else
2727                         pNode->mPrimaryCamera = s+1;
2728                 }
2729             }
2730             else if( IsElement( "instance_node"))
2731             {
2732                 // find the node in the library
2733                 int attrID = TestAttribute( "url");
2734                 if( attrID != -1)
2735                 {
2736                     const char* s = mReader->getAttributeValue(attrID);
2737                     if (s[0] != '#')
2738                         DefaultLogger::get()->error("Collada: Unresolved reference format of node");
2739                     else
2740                     {
2741                         pNode->mNodeInstances.push_back(NodeInstance());
2742                         pNode->mNodeInstances.back().mNode = s+1;
2743                     }
2744                 }
2745             }
2746             else if( IsElement( "instance_geometry") || IsElement( "instance_controller"))
2747             {
2748                 // Reference to a mesh or controller, with possible material associations
2749                 ReadNodeGeometry( pNode);
2750             }
2751             else if( IsElement( "instance_light"))
2752             {
2753                 // Reference to a light, name given in 'url' attribute
2754                 int attrID = TestAttribute("url");
2755                 if (-1 == attrID)
2756                     DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_light> element");
2757                 else
2758                 {
2759                     const char* url = mReader->getAttributeValue( attrID);
2760                     if( url[0] != '#')
2761                         ThrowException( "Unknown reference format in <instance_light> element");
2762 
2763                     pNode->mLights.push_back(LightInstance());
2764                     pNode->mLights.back().mLight = url+1;
2765                 }
2766             }
2767             else if( IsElement( "instance_camera"))
2768             {
2769                 // Reference to a camera, name given in 'url' attribute
2770                 int attrID = TestAttribute("url");
2771                 if (-1 == attrID)
2772                     DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_camera> element");
2773                 else
2774                 {
2775                     const char* url = mReader->getAttributeValue( attrID);
2776                     if( url[0] != '#')
2777                         ThrowException( "Unknown reference format in <instance_camera> element");
2778 
2779                     pNode->mCameras.push_back(CameraInstance());
2780                     pNode->mCameras.back().mCamera = url+1;
2781                 }
2782             }
2783             else
2784             {
2785                 // skip everything else for the moment
2786                 SkipElement();
2787             }
2788         }
2789         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
2790             break;
2791         }
2792     }
2793 }
2794 
2795 // ------------------------------------------------------------------------------------------------
2796 // Reads a node transformation entry of the given type and adds it to the given node's transformation list.
ReadNodeTransformation(Node * pNode,TransformType pType)2797 void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType)
2798 {
2799     if( mReader->isEmptyElement())
2800         return;
2801 
2802     std::string tagName = mReader->getNodeName();
2803 
2804     Transform tf;
2805     tf.mType = pType;
2806 
2807     // read SID
2808     int indexSID = TestAttribute( "sid");
2809     if( indexSID >= 0)
2810         tf.mID = mReader->getAttributeValue( indexSID);
2811 
2812     // how many parameters to read per transformation type
2813     static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
2814     const char* content = GetTextContent();
2815 
2816     // read as many parameters and store in the transformation
2817     for( unsigned int a = 0; a < sNumParameters[pType]; a++)
2818     {
2819         // read a number
2820         content = fast_atoreal_move<ai_real>( content, tf.f[a]);
2821         // skip whitespace after it
2822         SkipSpacesAndLineEnd( &content);
2823     }
2824 
2825     // place the transformation at the queue of the node
2826     pNode->mTransforms.push_back( tf);
2827 
2828     // and consume the closing tag
2829     TestClosing( tagName.c_str());
2830 }
2831 
2832 // ------------------------------------------------------------------------------------------------
2833 // Processes bind_vertex_input and bind elements
ReadMaterialVertexInputBinding(Collada::SemanticMappingTable & tbl)2834 void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl)
2835 {
2836     while( mReader->read())
2837     {
2838         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
2839             if( IsElement( "bind_vertex_input"))
2840             {
2841                 Collada::InputSemanticMapEntry vn;
2842 
2843                 // effect semantic
2844                 int n = GetAttribute("semantic");
2845                 std::string s = mReader->getAttributeValue(n);
2846 
2847                 // input semantic
2848                 n = GetAttribute("input_semantic");
2849                 vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) );
2850 
2851                 // index of input set
2852                 n = TestAttribute("input_set");
2853                 if (-1 != n)
2854                     vn.mSet = mReader->getAttributeValueAsInt(n);
2855 
2856                 tbl.mMap[s] = vn;
2857             }
2858             else if( IsElement( "bind")) {
2859                 DefaultLogger::get()->warn("Collada: Found unsupported <bind> element");
2860             }
2861         }
2862         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
2863             if( strcmp( mReader->getNodeName(), "instance_material") == 0)
2864                 break;
2865         }
2866     }
2867 }
2868 
2869 // ------------------------------------------------------------------------------------------------
2870 // Reads a mesh reference in a node and adds it to the node's mesh list
ReadNodeGeometry(Node * pNode)2871 void ColladaParser::ReadNodeGeometry( Node* pNode)
2872 {
2873     // referred mesh is given as an attribute of the <instance_geometry> element
2874     int attrUrl = GetAttribute( "url");
2875     const char* url = mReader->getAttributeValue( attrUrl);
2876     if( url[0] != '#')
2877         ThrowException( "Unknown reference format");
2878 
2879     Collada::MeshInstance instance;
2880     instance.mMeshOrController = url+1; // skipping the leading #
2881 
2882     if( !mReader->isEmptyElement())
2883     {
2884         // read material associations. Ignore additional elements in between
2885         while( mReader->read())
2886         {
2887             if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2888             {
2889                 if( IsElement( "instance_material"))
2890                 {
2891                     // read ID of the geometry subgroup and the target material
2892                     int attrGroup = GetAttribute( "symbol");
2893                     std::string group = mReader->getAttributeValue( attrGroup);
2894                     int attrMaterial = GetAttribute( "target");
2895                     const char* urlMat = mReader->getAttributeValue( attrMaterial);
2896                     Collada::SemanticMappingTable s;
2897                     if( urlMat[0] == '#')
2898                         urlMat++;
2899 
2900                     s.mMatName = urlMat;
2901 
2902                     // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
2903                     if( !mReader->isEmptyElement())
2904                         ReadMaterialVertexInputBinding(s);
2905 
2906                     // store the association
2907                     instance.mMaterials[group] = s;
2908                 }
2909             }
2910             else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2911             {
2912                 if( strcmp( mReader->getNodeName(), "instance_geometry") == 0
2913                     || strcmp( mReader->getNodeName(), "instance_controller") == 0)
2914                     break;
2915             }
2916         }
2917     }
2918 
2919     // store it
2920     pNode->mMeshes.push_back( instance);
2921 }
2922 
2923 // ------------------------------------------------------------------------------------------------
2924 // Reads the collada scene
ReadScene()2925 void ColladaParser::ReadScene()
2926 {
2927     if( mReader->isEmptyElement())
2928         return;
2929 
2930     while( mReader->read())
2931     {
2932         if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
2933             if( IsElement( "instance_visual_scene"))
2934             {
2935                 // should be the first and only occurrence
2936                 if( mRootNode)
2937                     ThrowException( "Invalid scene containing multiple root nodes in <instance_visual_scene> element");
2938 
2939                 // read the url of the scene to instance. Should be of format "#some_name"
2940                 int urlIndex = GetAttribute( "url");
2941                 const char* url = mReader->getAttributeValue( urlIndex);
2942                 if( url[0] != '#')
2943                     ThrowException( "Unknown reference format in <instance_visual_scene> element");
2944 
2945                 // find the referred scene, skip the leading #
2946                 NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
2947                 if( sit == mNodeLibrary.end())
2948                     ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
2949                 mRootNode = sit->second;
2950             } else  {
2951                 SkipElement();
2952             }
2953         }
2954         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
2955             break;
2956         }
2957     }
2958 }
2959 
2960 // ------------------------------------------------------------------------------------------------
2961 // Aborts the file reading with an exception
ThrowException(const std::string & pError) const2962 AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const
2963 {
2964     throw DeadlyImportError( format() << "Collada: " << mFileName << " - " << pError );
2965 }
ReportWarning(const char * msg,...)2966 void ColladaParser::ReportWarning(const char* msg,...)
2967 {
2968     ai_assert(NULL != msg);
2969 
2970     va_list args;
2971     va_start(args,msg);
2972 
2973     char szBuffer[3000];
2974     const int iLen = vsprintf(szBuffer,msg,args);
2975     ai_assert(iLen > 0);
2976 
2977     va_end(args);
2978     DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
2979 }
2980 
2981 
2982 // ------------------------------------------------------------------------------------------------
2983 // Skips all data until the end node of the current element
SkipElement()2984 void ColladaParser::SkipElement()
2985 {
2986     // nothing to skip if it's an <element />
2987     if( mReader->isEmptyElement())
2988         return;
2989 
2990     // reroute
2991     SkipElement( mReader->getNodeName());
2992 }
2993 
2994 // ------------------------------------------------------------------------------------------------
2995 // Skips all data until the end node of the given element
SkipElement(const char * pElement)2996 void ColladaParser::SkipElement( const char* pElement)
2997 {
2998     // copy the current node's name because it'a pointer to the reader's internal buffer,
2999     // which is going to change with the upcoming parsing
3000     std::string element = pElement;
3001     while( mReader->read())
3002     {
3003         if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
3004             if( mReader->getNodeName() == element)
3005                 break;
3006     }
3007 }
3008 
3009 // ------------------------------------------------------------------------------------------------
3010 // Tests for an opening element of the given name, throws an exception if not found
TestOpening(const char * pName)3011 void ColladaParser::TestOpening( const char* pName)
3012 {
3013     // read element start
3014     if( !mReader->read())
3015         ThrowException( format() << "Unexpected end of file while beginning of <" << pName << "> element." );
3016     // whitespace in front is ok, just read again if found
3017     if( mReader->getNodeType() == irr::io::EXN_TEXT)
3018         if( !mReader->read())
3019             ThrowException( format() << "Unexpected end of file while reading beginning of <" << pName << "> element." );
3020 
3021     if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
3022         ThrowException( format() << "Expected start of <" << pName << "> element." );
3023 }
3024 
3025 // ------------------------------------------------------------------------------------------------
3026 // Tests for the closing tag of the given element, throws an exception if not found
TestClosing(const char * pName)3027 void ColladaParser::TestClosing( const char* pName)
3028 {
3029     // check if we're already on the closing tag and return right away
3030     if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
3031         return;
3032 
3033     // if not, read some more
3034     if( !mReader->read())
3035         ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
3036     // whitespace in front is ok, just read again if found
3037     if( mReader->getNodeType() == irr::io::EXN_TEXT)
3038         if( !mReader->read())
3039             ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
3040 
3041     // but this has the be the closing tag, or we're lost
3042     if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
3043         ThrowException( format() << "Expected end of <" << pName << "> element." );
3044 }
3045 
3046 // ------------------------------------------------------------------------------------------------
3047 // Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
GetAttribute(const char * pAttr) const3048 int ColladaParser::GetAttribute( const char* pAttr) const
3049 {
3050     int index = TestAttribute( pAttr);
3051     if( index != -1)
3052         return index;
3053 
3054     // attribute not found -> throw an exception
3055     ThrowException( format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">." );
3056     return -1;
3057 }
3058 
3059 // ------------------------------------------------------------------------------------------------
3060 // Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
TestAttribute(const char * pAttr) const3061 int ColladaParser::TestAttribute( const char* pAttr) const
3062 {
3063     for( int a = 0; a < mReader->getAttributeCount(); a++)
3064         if( strcmp( mReader->getAttributeName( a), pAttr) == 0)
3065             return a;
3066 
3067     return -1;
3068 }
3069 
3070 // ------------------------------------------------------------------------------------------------
3071 // Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
GetTextContent()3072 const char* ColladaParser::GetTextContent()
3073 {
3074     const char* sz = TestTextContent();
3075     if(!sz) {
3076         ThrowException( "Invalid contents in element \"n\".");
3077     }
3078     return sz;
3079 }
3080 
3081 // ------------------------------------------------------------------------------------------------
3082 // Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
TestTextContent()3083 const char* ColladaParser::TestTextContent()
3084 {
3085     // present node should be the beginning of an element
3086     if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
3087         return NULL;
3088 
3089     // read contents of the element
3090     if( !mReader->read() )
3091         return NULL;
3092     if( mReader->getNodeType() != irr::io::EXN_TEXT)
3093         return NULL;
3094 
3095     // skip leading whitespace
3096     const char* text = mReader->getNodeData();
3097     SkipSpacesAndLineEnd( &text);
3098 
3099     return text;
3100 }
3101 
3102 // ------------------------------------------------------------------------------------------------
3103 // Calculates the resulting transformation fromm all the given transform steps
CalculateResultTransform(const std::vector<Transform> & pTransforms) const3104 aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform>& pTransforms) const
3105 {
3106     aiMatrix4x4 res;
3107 
3108     for( std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
3109     {
3110         const Transform& tf = *it;
3111         switch( tf.mType)
3112         {
3113             case TF_LOOKAT:
3114       {
3115         aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]);
3116         aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]);
3117         aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize();
3118         aiVector3D dir = aiVector3D( dstPos - pos).Normalize();
3119         aiVector3D right = (dir ^ up).Normalize();
3120 
3121         res *= aiMatrix4x4(
3122           right.x, up.x, -dir.x, pos.x,
3123           right.y, up.y, -dir.y, pos.y,
3124           right.z, up.z, -dir.z, pos.z,
3125           0, 0, 0, 1);
3126                 break;
3127       }
3128             case TF_ROTATE:
3129             {
3130                 aiMatrix4x4 rot;
3131                 ai_real angle = tf.f[3] * ai_real( AI_MATH_PI) / ai_real( 180.0 );
3132                 aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
3133                 aiMatrix4x4::Rotation( angle, axis, rot);
3134                 res *= rot;
3135                 break;
3136             }
3137             case TF_TRANSLATE:
3138             {
3139                 aiMatrix4x4 trans;
3140                 aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans);
3141                 res *= trans;
3142                 break;
3143             }
3144             case TF_SCALE:
3145             {
3146                 aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
3147                     0.0f, 0.0f, 0.0f, 1.0f);
3148                 res *= scale;
3149                 break;
3150             }
3151             case TF_SKEW:
3152                 // TODO: (thom)
3153                 ai_assert( false);
3154                 break;
3155             case TF_MATRIX:
3156             {
3157                 aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
3158                     tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
3159                 res *= mat;
3160                 break;
3161             }
3162             default:
3163                 ai_assert( false);
3164                 break;
3165         }
3166     }
3167 
3168     return res;
3169 }
3170 
3171 // ------------------------------------------------------------------------------------------------
3172 // Determines the input data type for the given semantic string
GetTypeForSemantic(const std::string & semantic)3173 Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& semantic)
3174 {
3175     if ( semantic.empty() ) {
3176         DefaultLogger::get()->warn( format() << "Vertex input type is empty." );
3177         return IT_Invalid;
3178     }
3179 
3180     if( semantic == "POSITION")
3181         return IT_Position;
3182     else if( semantic == "TEXCOORD")
3183         return IT_Texcoord;
3184     else if( semantic == "NORMAL")
3185         return IT_Normal;
3186     else if( semantic == "COLOR")
3187         return IT_Color;
3188     else if( semantic == "VERTEX")
3189         return IT_Vertex;
3190     else if( semantic == "BINORMAL" || semantic ==  "TEXBINORMAL")
3191         return IT_Bitangent;
3192     else if( semantic == "TANGENT" || semantic == "TEXTANGENT")
3193         return IT_Tangent;
3194 
3195     DefaultLogger::get()->warn( format() << "Unknown vertex input type \"" << semantic << "\". Ignoring." );
3196     return IT_Invalid;
3197 }
3198 
3199 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
3200