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