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