vrshoot

view libs/assimp/ColladaParser.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file ColladaParser.cpp
43 * @brief Implementation of the Collada parser helper
44 */
46 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
49 #include "ColladaParser.h"
50 #include "fast_atof.h"
51 #include "ParsingUtils.h"
53 using namespace Assimp;
54 using namespace Assimp::Collada;
56 // ------------------------------------------------------------------------------------------------
57 // Constructor to be privately used by Importer
58 ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
59 : mFileName( pFile)
60 {
61 mRootNode = NULL;
62 mUnitSize = 1.0f;
63 mUpDirection = UP_Z;
65 // We assume the newest file format by default
66 mFormat = FV_1_5_n;
68 // open the file
69 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
70 if( file.get() == NULL)
71 throw DeadlyImportError( "Failed to open file " + pFile + ".");
73 // generate a XML reader for it
74 boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
75 mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
76 if( !mReader)
77 ThrowException( "Collada: Unable to open file.");
79 // start reading
80 ReadContents();
81 }
83 // ------------------------------------------------------------------------------------------------
84 // Destructor, private as well
85 ColladaParser::~ColladaParser()
86 {
87 delete mReader;
88 for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
89 delete it->second;
90 for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
91 delete it->second;
92 }
94 // ------------------------------------------------------------------------------------------------
95 // Read bool from text contents of current element
96 bool ColladaParser::ReadBoolFromTextContent()
97 {
98 const char* cur = GetTextContent();
99 return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur);
100 }
102 // ------------------------------------------------------------------------------------------------
103 // Read float from text contents of current element
104 float ColladaParser::ReadFloatFromTextContent()
105 {
106 const char* cur = GetTextContent();
107 return fast_atof(cur);
108 }
110 // ------------------------------------------------------------------------------------------------
111 // Reads the contents of the file
112 void ColladaParser::ReadContents()
113 {
114 while( mReader->read())
115 {
116 // handle the root element "COLLADA"
117 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
118 {
119 if( IsElement( "COLLADA"))
120 {
121 // check for 'version' attribute
122 const int attrib = TestAttribute("version");
123 if (attrib != -1) {
124 const char* version = mReader->getAttributeValue(attrib);
126 if (!::strncmp(version,"1.5",3)) {
127 mFormat = FV_1_5_n;
128 DefaultLogger::get()->debug("Collada schema version is 1.5.n");
129 }
130 else if (!::strncmp(version,"1.4",3)) {
131 mFormat = FV_1_4_n;
132 DefaultLogger::get()->debug("Collada schema version is 1.4.n");
133 }
134 else if (!::strncmp(version,"1.3",3)) {
135 mFormat = FV_1_3_n;
136 DefaultLogger::get()->debug("Collada schema version is 1.3.n");
137 }
138 }
140 ReadStructure();
141 } else
142 {
143 DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element <%s>.") % mReader->getNodeName()));
144 SkipElement();
145 }
146 } else
147 {
148 // skip everything else silently
149 }
150 }
151 }
153 // ------------------------------------------------------------------------------------------------
154 // Reads the structure of the file
155 void ColladaParser::ReadStructure()
156 {
157 while( mReader->read())
158 {
159 // beginning of elements
160 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
161 {
162 if( IsElement( "asset"))
163 ReadAssetInfo();
164 else if( IsElement( "library_animations"))
165 ReadAnimationLibrary();
166 else if( IsElement( "library_controllers"))
167 ReadControllerLibrary();
168 else if( IsElement( "library_images"))
169 ReadImageLibrary();
170 else if( IsElement( "library_materials"))
171 ReadMaterialLibrary();
172 else if( IsElement( "library_effects"))
173 ReadEffectLibrary();
174 else if( IsElement( "library_geometries"))
175 ReadGeometryLibrary();
176 else if( IsElement( "library_visual_scenes"))
177 ReadSceneLibrary();
178 else if( IsElement( "library_lights"))
179 ReadLightLibrary();
180 else if( IsElement( "library_cameras"))
181 ReadCameraLibrary();
182 else if( IsElement( "library_nodes"))
183 ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
184 else if( IsElement( "scene"))
185 ReadScene();
186 else
187 SkipElement();
188 }
189 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
190 {
191 break;
192 }
193 }
194 }
196 // ------------------------------------------------------------------------------------------------
197 // Reads asset informations such as coordinate system informations and legal blah
198 void ColladaParser::ReadAssetInfo()
199 {
200 if( mReader->isEmptyElement())
201 return;
203 while( mReader->read())
204 {
205 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
206 {
207 if( IsElement( "unit"))
208 {
209 // read unit data from the element's attributes
210 const int attrIndex = TestAttribute( "meter");
211 if (attrIndex == -1) {
212 mUnitSize = 1.f;
213 }
214 else {
215 mUnitSize = mReader->getAttributeValueAsFloat( attrIndex);
216 }
218 // consume the trailing stuff
219 if( !mReader->isEmptyElement())
220 SkipElement();
221 }
222 else if( IsElement( "up_axis"))
223 {
224 // read content, strip whitespace, compare
225 const char* content = GetTextContent();
226 if( strncmp( content, "X_UP", 4) == 0)
227 mUpDirection = UP_X;
228 else if( strncmp( content, "Y_UP", 4) == 0)
229 mUpDirection = UP_Y;
230 else
231 mUpDirection = UP_Z;
233 // check element end
234 TestClosing( "up_axis");
235 } else
236 {
237 SkipElement();
238 }
239 }
240 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
241 {
242 if( strcmp( mReader->getNodeName(), "asset") != 0)
243 ThrowException( "Expected end of <asset> element.");
245 break;
246 }
247 }
248 }
250 // ------------------------------------------------------------------------------------------------
251 // Reads the animation library
252 void ColladaParser::ReadAnimationLibrary()
253 {
254 if (mReader->isEmptyElement())
255 return;
257 while( mReader->read())
258 {
259 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
260 {
261 if( IsElement( "animation"))
262 {
263 // delegate the reading. Depending on the inner elements it will be a container or a anim channel
264 ReadAnimation( &mAnims);
265 } else
266 {
267 // ignore the rest
268 SkipElement();
269 }
270 }
271 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
272 {
273 if( strcmp( mReader->getNodeName(), "library_animations") != 0)
274 ThrowException( "Expected end of <library_animations> element.");
276 break;
277 }
278 }
279 }
281 // ------------------------------------------------------------------------------------------------
282 // Reads an animation into the given parent structure
283 void ColladaParser::ReadAnimation( Collada::Animation* pParent)
284 {
285 if( mReader->isEmptyElement())
286 return;
288 // an <animation> element may be a container for grouping sub-elements or an animation channel
289 // this is the channel collection by ID, in case it has channels
290 typedef std::map<std::string, AnimationChannel> ChannelMap;
291 ChannelMap channels;
292 // this is the anim container in case we're a container
293 Animation* anim = NULL;
295 // optional name given as an attribute
296 std::string animName;
297 int indexName = TestAttribute( "name");
298 int indexID = TestAttribute( "id");
299 if( indexName >= 0)
300 animName = mReader->getAttributeValue( indexName);
301 else if( indexID >= 0)
302 animName = mReader->getAttributeValue( indexID);
303 else
304 animName = "animation";
306 while( mReader->read())
307 {
308 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
309 {
310 // we have subanimations
311 if( IsElement( "animation"))
312 {
313 // create container from our element
314 if( !anim)
315 {
316 anim = new Animation;
317 anim->mName = animName;
318 pParent->mSubAnims.push_back( anim);
319 }
321 // recurse into the subelement
322 ReadAnimation( anim);
323 }
324 else if( IsElement( "source"))
325 {
326 // possible animation data - we'll never know. Better store it
327 ReadSource();
328 }
329 else if( IsElement( "sampler"))
330 {
331 // read the ID to assign the corresponding collada channel afterwards.
332 int indexID = GetAttribute( "id");
333 std::string id = mReader->getAttributeValue( indexID);
334 ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first;
336 // have it read into a channel
337 ReadAnimationSampler( newChannel->second);
338 }
339 else if( IsElement( "channel"))
340 {
341 // the binding element whose whole purpose is to provide the target to animate
342 // Thanks, Collada! A directly posted information would have been too simple, I guess.
343 // Better add another indirection to that! Can't have enough of those.
344 int indexTarget = GetAttribute( "target");
345 int indexSource = GetAttribute( "source");
346 const char* sourceId = mReader->getAttributeValue( indexSource);
347 if( sourceId[0] == '#')
348 sourceId++;
349 ChannelMap::iterator cit = channels.find( sourceId);
350 if( cit != channels.end())
351 cit->second.mTarget = mReader->getAttributeValue( indexTarget);
353 if( !mReader->isEmptyElement())
354 SkipElement();
355 }
356 else
357 {
358 // ignore the rest
359 SkipElement();
360 }
361 }
362 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
363 {
364 if( strcmp( mReader->getNodeName(), "animation") != 0)
365 ThrowException( "Expected end of <animation> element.");
367 break;
368 }
369 }
371 // it turned out to have channels - add them
372 if( !channels.empty())
373 {
374 // special filtering for stupid exporters packing each channel into a separate animation
375 if( channels.size() == 1)
376 {
377 pParent->mChannels.push_back( channels.begin()->second);
378 } else
379 {
380 // else create the animation, if not done yet, and store the channels
381 if( !anim)
382 {
383 anim = new Animation;
384 anim->mName = animName;
385 pParent->mSubAnims.push_back( anim);
386 }
387 for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
388 anim->mChannels.push_back( it->second);
389 }
390 }
391 }
393 // ------------------------------------------------------------------------------------------------
394 // Reads an animation sampler into the given anim channel
395 void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
396 {
397 while( mReader->read())
398 {
399 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
400 {
401 if( IsElement( "input"))
402 {
403 int indexSemantic = GetAttribute( "semantic");
404 const char* semantic = mReader->getAttributeValue( indexSemantic);
405 int indexSource = GetAttribute( "source");
406 const char* source = mReader->getAttributeValue( indexSource);
407 if( source[0] != '#')
408 ThrowException( "Unsupported URL format");
409 source++;
411 if( strcmp( semantic, "INPUT") == 0)
412 pChannel.mSourceTimes = source;
413 else if( strcmp( semantic, "OUTPUT") == 0)
414 pChannel.mSourceValues = source;
416 if( !mReader->isEmptyElement())
417 SkipElement();
418 }
419 else
420 {
421 // ignore the rest
422 SkipElement();
423 }
424 }
425 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
426 {
427 if( strcmp( mReader->getNodeName(), "sampler") != 0)
428 ThrowException( "Expected end of <sampler> element.");
430 break;
431 }
432 }
433 }
435 // ------------------------------------------------------------------------------------------------
436 // Reads the skeleton controller library
437 void ColladaParser::ReadControllerLibrary()
438 {
439 if (mReader->isEmptyElement())
440 return;
442 while( mReader->read())
443 {
444 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
445 {
446 if( IsElement( "controller"))
447 {
448 // read ID. Ask the spec if it's neccessary or optional... you might be surprised.
449 int attrID = GetAttribute( "id");
450 std::string id = mReader->getAttributeValue( attrID);
452 // create an entry and store it in the library under its ID
453 mControllerLibrary[id] = Controller();
455 // read on from there
456 ReadController( mControllerLibrary[id]);
457 } else
458 {
459 // ignore the rest
460 SkipElement();
461 }
462 }
463 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
464 {
465 if( strcmp( mReader->getNodeName(), "library_controllers") != 0)
466 ThrowException( "Expected end of <library_controllers> element.");
468 break;
469 }
470 }
471 }
473 // ------------------------------------------------------------------------------------------------
474 // Reads a controller into the given mesh structure
475 void ColladaParser::ReadController( Collada::Controller& pController)
476 {
477 while( mReader->read())
478 {
479 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
480 {
481 // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
482 if( IsElement( "morph"))
483 {
484 // should skip everything inside, so there's no danger of catching elements inbetween
485 SkipElement();
486 }
487 else if( IsElement( "skin"))
488 {
489 // read the mesh it refers to. According to the spec this could also be another
490 // controller, but I refuse to implement every single idea they've come up with
491 int sourceIndex = GetAttribute( "source");
492 pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
493 }
494 else if( IsElement( "bind_shape_matrix"))
495 {
496 // content is 16 floats to define a matrix... it seems to be important for some models
497 const char* content = GetTextContent();
499 // read the 16 floats
500 for( unsigned int a = 0; a < 16; a++)
501 {
502 // read a number
503 content = fast_atoreal_move<float>( content, pController.mBindShapeMatrix[a]);
504 // skip whitespace after it
505 SkipSpacesAndLineEnd( &content);
506 }
508 TestClosing( "bind_shape_matrix");
509 }
510 else if( IsElement( "source"))
511 {
512 // data array - we have specialists to handle this
513 ReadSource();
514 }
515 else if( IsElement( "joints"))
516 {
517 ReadControllerJoints( pController);
518 }
519 else if( IsElement( "vertex_weights"))
520 {
521 ReadControllerWeights( pController);
522 }
523 else
524 {
525 // ignore the rest
526 SkipElement();
527 }
528 }
529 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
530 {
531 if( strcmp( mReader->getNodeName(), "controller") == 0)
532 break;
533 else if( strcmp( mReader->getNodeName(), "skin") != 0)
534 ThrowException( "Expected end of <controller> element.");
535 }
536 }
537 }
539 // ------------------------------------------------------------------------------------------------
540 // Reads the joint definitions for the given controller
541 void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
542 {
543 while( mReader->read())
544 {
545 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
546 {
547 // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
548 if( IsElement( "input"))
549 {
550 int indexSemantic = GetAttribute( "semantic");
551 const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
552 int indexSource = GetAttribute( "source");
553 const char* attrSource = mReader->getAttributeValue( indexSource);
555 // local URLS always start with a '#'. We don't support global URLs
556 if( attrSource[0] != '#')
557 ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of <joints> data <input> element") % attrSource));
558 attrSource++;
560 // parse source URL to corresponding source
561 if( strcmp( attrSemantic, "JOINT") == 0)
562 pController.mJointNameSource = attrSource;
563 else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
564 pController.mJointOffsetMatrixSource = attrSource;
565 else
566 ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in <joints> data <input> element") % attrSemantic));
568 // skip inner data, if present
569 if( !mReader->isEmptyElement())
570 SkipElement();
571 }
572 else
573 {
574 // ignore the rest
575 SkipElement();
576 }
577 }
578 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
579 {
580 if( strcmp( mReader->getNodeName(), "joints") != 0)
581 ThrowException( "Expected end of <joints> element.");
583 break;
584 }
585 }
586 }
588 // ------------------------------------------------------------------------------------------------
589 // Reads the joint weights for the given controller
590 void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
591 {
592 // read vertex count from attributes and resize the array accordingly
593 int indexCount = GetAttribute( "count");
594 size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
595 pController.mWeightCounts.resize( vertexCount);
597 while( mReader->read())
598 {
599 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
600 {
601 // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
602 if( IsElement( "input") && vertexCount > 0 )
603 {
604 InputChannel channel;
606 int indexSemantic = GetAttribute( "semantic");
607 const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
608 int indexSource = GetAttribute( "source");
609 const char* attrSource = mReader->getAttributeValue( indexSource);
610 int indexOffset = TestAttribute( "offset");
611 if( indexOffset >= 0)
612 channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
614 // local URLS always start with a '#'. We don't support global URLs
615 if( attrSource[0] != '#')
616 ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of <vertex_weights> data <input> element") % attrSource));
617 channel.mAccessor = attrSource + 1;
619 // parse source URL to corresponding source
620 if( strcmp( attrSemantic, "JOINT") == 0)
621 pController.mWeightInputJoints = channel;
622 else if( strcmp( attrSemantic, "WEIGHT") == 0)
623 pController.mWeightInputWeights = channel;
624 else
625 ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in <vertex_weights> data <input> element") % attrSemantic));
627 // skip inner data, if present
628 if( !mReader->isEmptyElement())
629 SkipElement();
630 }
631 else if( IsElement( "vcount") && vertexCount > 0 )
632 {
633 // read weight count per vertex
634 const char* text = GetTextContent();
635 size_t numWeights = 0;
636 for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
637 {
638 if( *text == 0)
639 ThrowException( "Out of data while reading <vcount>");
641 *it = strtoul10( text, &text);
642 numWeights += *it;
643 SkipSpacesAndLineEnd( &text);
644 }
646 TestClosing( "vcount");
648 // reserve weight count
649 pController.mWeights.resize( numWeights);
650 }
651 else if( IsElement( "v") && vertexCount > 0 )
652 {
653 // read JointIndex - WeightIndex pairs
654 const char* text = GetTextContent();
656 for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
657 {
658 if( *text == 0)
659 ThrowException( "Out of data while reading <vertex_weights>");
660 it->first = strtoul10( text, &text);
661 SkipSpacesAndLineEnd( &text);
662 if( *text == 0)
663 ThrowException( "Out of data while reading <vertex_weights>");
664 it->second = strtoul10( text, &text);
665 SkipSpacesAndLineEnd( &text);
666 }
668 TestClosing( "v");
669 }
670 else
671 {
672 // ignore the rest
673 SkipElement();
674 }
675 }
676 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
677 {
678 if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
679 ThrowException( "Expected end of <vertex_weights> element.");
681 break;
682 }
683 }
684 }
686 // ------------------------------------------------------------------------------------------------
687 // Reads the image library contents
688 void ColladaParser::ReadImageLibrary()
689 {
690 if( mReader->isEmptyElement())
691 return;
693 while( mReader->read())
694 {
695 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
696 if( IsElement( "image"))
697 {
698 // read ID. Another entry which is "optional" by design but obligatory in reality
699 int attrID = GetAttribute( "id");
700 std::string id = mReader->getAttributeValue( attrID);
702 // create an entry and store it in the library under its ID
703 mImageLibrary[id] = Image();
705 // read on from there
706 ReadImage( mImageLibrary[id]);
707 } else
708 {
709 // ignore the rest
710 SkipElement();
711 }
712 }
713 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
714 if( strcmp( mReader->getNodeName(), "library_images") != 0)
715 ThrowException( "Expected end of <library_images> element.");
717 break;
718 }
719 }
720 }
722 // ------------------------------------------------------------------------------------------------
723 // Reads an image entry into the given image
724 void ColladaParser::ReadImage( Collada::Image& pImage)
725 {
726 while( mReader->read())
727 {
728 if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
729 // Need to run different code paths here, depending on the Collada XSD version
730 if (IsElement("image")) {
731 SkipElement();
732 }
733 else if( IsElement( "init_from"))
734 {
735 if (mFormat == FV_1_4_n)
736 {
737 // FIX: C4D exporter writes empty <init_from/> tags
738 if (!mReader->isEmptyElement()) {
739 // element content is filename - hopefully
740 const char* sz = TestTextContent();
741 if (sz)pImage.mFileName = sz;
742 TestClosing( "init_from");
743 }
744 if (!pImage.mFileName.length()) {
745 pImage.mFileName = "unknown_texture";
746 }
747 }
748 else if (mFormat == FV_1_5_n)
749 {
750 // make sure we skip over mip and array initializations, which
751 // we don't support, but which could confuse the loader if
752 // they're not skipped.
753 int attrib = TestAttribute("array_index");
754 if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
755 DefaultLogger::get()->warn("Collada: Ignoring texture array index");
756 continue;
757 }
759 attrib = TestAttribute("mip_index");
760 if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
761 DefaultLogger::get()->warn("Collada: Ignoring MIP map layer");
762 continue;
763 }
765 // TODO: correctly jump over cube and volume maps?
766 }
767 }
768 else if (mFormat == FV_1_5_n)
769 {
770 if( IsElement( "ref"))
771 {
772 // element content is filename - hopefully
773 const char* sz = TestTextContent();
774 if (sz)pImage.mFileName = sz;
775 TestClosing( "ref");
776 }
777 else if( IsElement( "hex") && !pImage.mFileName.length())
778 {
779 // embedded image. get format
780 const int attrib = TestAttribute("format");
781 if (-1 == attrib)
782 DefaultLogger::get()->warn("Collada: Unknown image file format");
783 else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
785 const char* data = GetTextContent();
787 // hexadecimal-encoded binary octets. First of all, find the
788 // required buffer size to reserve enough storage.
789 const char* cur = data;
790 while (!IsSpaceOrNewLine(*cur)) cur++;
792 const unsigned int size = (unsigned int)(cur-data) * 2;
793 pImage.mImageData.resize(size);
794 for (unsigned int i = 0; i < size;++i)
795 pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
797 TestClosing( "hex");
798 }
799 }
800 else
801 {
802 // ignore the rest
803 SkipElement();
804 }
805 }
806 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
807 if( strcmp( mReader->getNodeName(), "image") == 0)
808 break;
809 }
810 }
811 }
813 // ------------------------------------------------------------------------------------------------
814 // Reads the material library
815 void ColladaParser::ReadMaterialLibrary()
816 {
817 if( mReader->isEmptyElement())
818 return;
820 while( mReader->read())
821 {
822 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
823 {
824 if( IsElement( "material"))
825 {
826 // read ID. By now you propably know my opinion about this "specification"
827 int attrID = GetAttribute( "id");
828 std::string id = mReader->getAttributeValue( attrID);
830 // create an entry and store it in the library under its ID
831 ReadMaterial(mMaterialLibrary[id] = Material());
832 } else
833 {
834 // ignore the rest
835 SkipElement();
836 }
837 }
838 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
839 {
840 if( strcmp( mReader->getNodeName(), "library_materials") != 0)
841 ThrowException( "Expected end of <library_materials> element.");
843 break;
844 }
845 }
846 }
848 // ------------------------------------------------------------------------------------------------
849 // Reads the light library
850 void ColladaParser::ReadLightLibrary()
851 {
852 if( mReader->isEmptyElement())
853 return;
855 while( mReader->read())
856 {
857 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
858 if( IsElement( "light"))
859 {
860 // read ID. By now you propably know my opinion about this "specification"
861 int attrID = GetAttribute( "id");
862 std::string id = mReader->getAttributeValue( attrID);
864 // create an entry and store it in the library under its ID
865 ReadLight(mLightLibrary[id] = Light());
867 } else
868 {
869 // ignore the rest
870 SkipElement();
871 }
872 }
873 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
874 if( strcmp( mReader->getNodeName(), "library_lights") != 0)
875 ThrowException( "Expected end of <library_lights> element.");
877 break;
878 }
879 }
880 }
882 // ------------------------------------------------------------------------------------------------
883 // Reads the camera library
884 void ColladaParser::ReadCameraLibrary()
885 {
886 if( mReader->isEmptyElement())
887 return;
889 while( mReader->read())
890 {
891 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
892 if( IsElement( "camera"))
893 {
894 // read ID. By now you propably know my opinion about this "specification"
895 int attrID = GetAttribute( "id");
896 std::string id = mReader->getAttributeValue( attrID);
898 // create an entry and store it in the library under its ID
899 Camera& cam = mCameraLibrary[id];
900 attrID = TestAttribute( "name");
901 if (attrID != -1)
902 cam.mName = mReader->getAttributeValue( attrID);
904 ReadCamera(cam);
906 } else
907 {
908 // ignore the rest
909 SkipElement();
910 }
911 }
912 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
913 if( strcmp( mReader->getNodeName(), "library_cameras") != 0)
914 ThrowException( "Expected end of <library_cameras> element.");
916 break;
917 }
918 }
919 }
921 // ------------------------------------------------------------------------------------------------
922 // Reads a material entry into the given material
923 void ColladaParser::ReadMaterial( Collada::Material& pMaterial)
924 {
925 while( mReader->read())
926 {
927 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
928 if (IsElement("material")) {
929 SkipElement();
930 }
931 else if( IsElement( "instance_effect"))
932 {
933 // referred effect by URL
934 int attrUrl = GetAttribute( "url");
935 const char* url = mReader->getAttributeValue( attrUrl);
936 if( url[0] != '#')
937 ThrowException( "Unknown reference format");
939 pMaterial.mEffect = url+1;
941 SkipElement();
942 } else
943 {
944 // ignore the rest
945 SkipElement();
946 }
947 }
948 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
949 if( strcmp( mReader->getNodeName(), "material") != 0)
950 ThrowException( "Expected end of <material> element.");
952 break;
953 }
954 }
955 }
957 // ------------------------------------------------------------------------------------------------
958 // Reads a light entry into the given light
959 void ColladaParser::ReadLight( Collada::Light& pLight)
960 {
961 while( mReader->read())
962 {
963 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
964 if (IsElement("light")) {
965 SkipElement();
966 }
967 else if (IsElement("spot")) {
968 pLight.mType = aiLightSource_SPOT;
969 }
970 else if (IsElement("ambient")) {
971 pLight.mType = aiLightSource_AMBIENT;
972 }
973 else if (IsElement("directional")) {
974 pLight.mType = aiLightSource_DIRECTIONAL;
975 }
976 else if (IsElement("point")) {
977 pLight.mType = aiLightSource_POINT;
978 }
979 else if (IsElement("color")) {
980 // text content contains 3 floats
981 const char* content = GetTextContent();
983 content = fast_atoreal_move<float>( content, (float&)pLight.mColor.r);
984 SkipSpacesAndLineEnd( &content);
986 content = fast_atoreal_move<float>( content, (float&)pLight.mColor.g);
987 SkipSpacesAndLineEnd( &content);
989 content = fast_atoreal_move<float>( content, (float&)pLight.mColor.b);
990 SkipSpacesAndLineEnd( &content);
992 TestClosing( "color");
993 }
994 else if (IsElement("constant_attenuation")) {
995 pLight.mAttConstant = ReadFloatFromTextContent();
996 TestClosing("constant_attenuation");
997 }
998 else if (IsElement("linear_attenuation")) {
999 pLight.mAttLinear = ReadFloatFromTextContent();
1000 TestClosing("linear_attenuation");
1002 else if (IsElement("quadratic_attenuation")) {
1003 pLight.mAttQuadratic = ReadFloatFromTextContent();
1004 TestClosing("quadratic_attenuation");
1006 else if (IsElement("falloff_angle")) {
1007 pLight.mFalloffAngle = ReadFloatFromTextContent();
1008 TestClosing("falloff_angle");
1010 else if (IsElement("falloff_exponent")) {
1011 pLight.mFalloffExponent = ReadFloatFromTextContent();
1012 TestClosing("falloff_exponent");
1014 // FCOLLADA extensions
1015 // -------------------------------------------------------
1016 else if (IsElement("outer_cone")) {
1017 pLight.mOuterAngle = ReadFloatFromTextContent();
1018 TestClosing("outer_cone");
1020 // ... and this one is even deprecated
1021 else if (IsElement("penumbra_angle")) {
1022 pLight.mPenumbraAngle = ReadFloatFromTextContent();
1023 TestClosing("penumbra_angle");
1025 else if (IsElement("intensity")) {
1026 pLight.mIntensity = ReadFloatFromTextContent();
1027 TestClosing("intensity");
1029 else if (IsElement("falloff")) {
1030 pLight.mOuterAngle = ReadFloatFromTextContent();
1031 TestClosing("falloff");
1033 else if (IsElement("hotspot_beam")) {
1034 pLight.mFalloffAngle = ReadFloatFromTextContent();
1035 TestClosing("hotspot_beam");
1038 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1039 if( strcmp( mReader->getNodeName(), "light") == 0)
1040 break;
1045 // ------------------------------------------------------------------------------------------------
1046 // Reads a camera entry into the given light
1047 void ColladaParser::ReadCamera( Collada::Camera& pCamera)
1049 while( mReader->read())
1051 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1052 if (IsElement("camera")) {
1053 SkipElement();
1055 else if (IsElement("orthographic")) {
1056 pCamera.mOrtho = true;
1058 else if (IsElement("xfov") || IsElement("xmag")) {
1059 pCamera.mHorFov = ReadFloatFromTextContent();
1060 TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
1062 else if (IsElement("yfov") || IsElement("ymag")) {
1063 pCamera.mVerFov = ReadFloatFromTextContent();
1064 TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
1066 else if (IsElement("aspect_ratio")) {
1067 pCamera.mAspect = ReadFloatFromTextContent();
1068 TestClosing("aspect_ratio");
1070 else if (IsElement("znear")) {
1071 pCamera.mZNear = ReadFloatFromTextContent();
1072 TestClosing("znear");
1074 else if (IsElement("zfar")) {
1075 pCamera.mZFar = ReadFloatFromTextContent();
1076 TestClosing("zfar");
1079 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1080 if( strcmp( mReader->getNodeName(), "camera") == 0)
1081 break;
1086 // ------------------------------------------------------------------------------------------------
1087 // Reads the effect library
1088 void ColladaParser::ReadEffectLibrary()
1090 if (mReader->isEmptyElement()) {
1091 return;
1094 while( mReader->read())
1096 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1097 if( IsElement( "effect"))
1099 // read ID. Do I have to repeat my ranting about "optional" attributes?
1100 int attrID = GetAttribute( "id");
1101 std::string id = mReader->getAttributeValue( attrID);
1103 // create an entry and store it in the library under its ID
1104 mEffectLibrary[id] = Effect();
1105 // read on from there
1106 ReadEffect( mEffectLibrary[id]);
1107 } else
1109 // ignore the rest
1110 SkipElement();
1113 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1114 if( strcmp( mReader->getNodeName(), "library_effects") != 0)
1115 ThrowException( "Expected end of <library_effects> element.");
1117 break;
1122 // ------------------------------------------------------------------------------------------------
1123 // Reads an effect entry into the given effect
1124 void ColladaParser::ReadEffect( Collada::Effect& pEffect)
1126 // for the moment we don't support any other type of effect.
1127 while( mReader->read())
1129 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1131 if( IsElement( "profile_COMMON"))
1132 ReadEffectProfileCommon( pEffect);
1133 else
1134 SkipElement();
1136 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1138 if( strcmp( mReader->getNodeName(), "effect") != 0)
1139 ThrowException( "Expected end of <effect> element.");
1141 break;
1146 // ------------------------------------------------------------------------------------------------
1147 // Reads an COMMON effect profile
1148 void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
1150 while( mReader->read())
1152 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1154 if( IsElement( "newparam")) {
1155 // save ID
1156 int attrSID = GetAttribute( "sid");
1157 std::string sid = mReader->getAttributeValue( attrSID);
1158 pEffect.mParams[sid] = EffectParam();
1159 ReadEffectParam( pEffect.mParams[sid]);
1161 else if( IsElement( "technique") || IsElement( "extra"))
1163 // just syntactic sugar
1166 /* Shading modes */
1167 else if( IsElement( "phong"))
1168 pEffect.mShadeType = Shade_Phong;
1169 else if( IsElement( "constant"))
1170 pEffect.mShadeType = Shade_Constant;
1171 else if( IsElement( "lambert"))
1172 pEffect.mShadeType = Shade_Lambert;
1173 else if( IsElement( "blinn"))
1174 pEffect.mShadeType = Shade_Blinn;
1176 /* Color + texture properties */
1177 else if( IsElement( "emission"))
1178 ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive);
1179 else if( IsElement( "ambient"))
1180 ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient);
1181 else if( IsElement( "diffuse"))
1182 ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse);
1183 else if( IsElement( "specular"))
1184 ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular);
1185 else if( IsElement( "reflective")) {
1186 ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective);
1188 else if( IsElement( "transparent")) {
1189 ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent);
1191 else if( IsElement( "shininess"))
1192 ReadEffectFloat( pEffect.mShininess);
1193 else if( IsElement( "reflectivity"))
1194 ReadEffectFloat( pEffect.mReflectivity);
1196 /* Single scalar properties */
1197 else if( IsElement( "transparency"))
1198 ReadEffectFloat( pEffect.mTransparency);
1199 else if( IsElement( "index_of_refraction"))
1200 ReadEffectFloat( pEffect.mRefractIndex);
1202 // GOOGLEEARTH/OKINO extensions
1203 // -------------------------------------------------------
1204 else if( IsElement( "double_sided"))
1205 pEffect.mDoubleSided = ReadBoolFromTextContent();
1207 // FCOLLADA extensions
1208 // -------------------------------------------------------
1209 else if( IsElement( "bump")) {
1210 aiColor4D dummy;
1211 ReadEffectColor( dummy,pEffect.mTexBump);
1214 // MAX3D extensions
1215 // -------------------------------------------------------
1216 else if( IsElement( "wireframe")) {
1217 pEffect.mWireframe = ReadBoolFromTextContent();
1218 TestClosing( "wireframe");
1220 else if( IsElement( "faceted")) {
1221 pEffect.mFaceted = ReadBoolFromTextContent();
1222 TestClosing( "faceted");
1224 else
1226 // ignore the rest
1227 SkipElement();
1230 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1231 if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0)
1233 break;
1239 // ------------------------------------------------------------------------------------------------
1240 // Read texture wrapping + UV transform settings from a profile==Maya chunk
1241 void ColladaParser::ReadSamplerProperties( Sampler& out )
1243 if (mReader->isEmptyElement()) {
1244 return;
1247 while( mReader->read())
1249 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1251 // MAYA extensions
1252 // -------------------------------------------------------
1253 if( IsElement( "wrapU")) {
1254 out.mWrapU = ReadBoolFromTextContent();
1255 TestClosing( "wrapU");
1257 else if( IsElement( "wrapV")) {
1258 out.mWrapV = ReadBoolFromTextContent();
1259 TestClosing( "wrapV");
1261 else if( IsElement( "mirrorU")) {
1262 out.mMirrorU = ReadBoolFromTextContent();
1263 TestClosing( "mirrorU");
1265 else if( IsElement( "mirrorV")) {
1266 out.mMirrorV = ReadBoolFromTextContent();
1267 TestClosing( "mirrorV");
1269 else if( IsElement( "repeatU")) {
1270 out.mTransform.mScaling.x = ReadFloatFromTextContent();
1271 TestClosing( "repeatU");
1273 else if( IsElement( "repeatV")) {
1274 out.mTransform.mScaling.y = ReadFloatFromTextContent();
1275 TestClosing( "repeatV");
1277 else if( IsElement( "offsetU")) {
1278 out.mTransform.mTranslation.x = ReadFloatFromTextContent();
1279 TestClosing( "offsetU");
1281 else if( IsElement( "offsetV")) {
1282 out.mTransform.mTranslation.y = ReadFloatFromTextContent();
1283 TestClosing( "offsetV");
1285 else if( IsElement( "rotateUV")) {
1286 out.mTransform.mRotation = ReadFloatFromTextContent();
1287 TestClosing( "rotateUV");
1289 else if( IsElement( "blend_mode")) {
1291 const char* sz = GetTextContent();
1292 // http://www.feelingsoftware.com/content/view/55/72/lang,en/
1293 // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
1294 if (0 == ASSIMP_strincmp(sz,"ADD",3))
1295 out.mOp = aiTextureOp_Add;
1297 else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8))
1298 out.mOp = aiTextureOp_Subtract;
1300 else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8))
1301 out.mOp = aiTextureOp_Multiply;
1303 else {
1304 DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode");
1306 TestClosing( "blend_mode");
1308 // OKINO extensions
1309 // -------------------------------------------------------
1310 else if( IsElement( "weighting")) {
1311 out.mWeighting = ReadFloatFromTextContent();
1312 TestClosing( "weighting");
1314 else if( IsElement( "mix_with_previous_layer")) {
1315 out.mMixWithPrevious = ReadFloatFromTextContent();
1316 TestClosing( "mix_with_previous_layer");
1318 // MAX3D extensions
1319 // -------------------------------------------------------
1320 else if( IsElement( "amount")) {
1321 out.mWeighting = ReadFloatFromTextContent();
1322 TestClosing( "amount");
1325 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1326 if( strcmp( mReader->getNodeName(), "technique") == 0)
1327 break;
1332 // ------------------------------------------------------------------------------------------------
1333 // Reads an effect entry containing a color or a texture defining that color
1334 void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
1336 if (mReader->isEmptyElement())
1337 return;
1339 // Save current element name
1340 const std::string curElem = mReader->getNodeName();
1342 while( mReader->read())
1344 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1345 if( IsElement( "color"))
1347 // text content contains 4 floats
1348 const char* content = GetTextContent();
1350 content = fast_atoreal_move<float>( content, (float&)pColor.r);
1351 SkipSpacesAndLineEnd( &content);
1353 content = fast_atoreal_move<float>( content, (float&)pColor.g);
1354 SkipSpacesAndLineEnd( &content);
1356 content = fast_atoreal_move<float>( content, (float&)pColor.b);
1357 SkipSpacesAndLineEnd( &content);
1359 content = fast_atoreal_move<float>( content, (float&)pColor.a);
1360 SkipSpacesAndLineEnd( &content);
1361 TestClosing( "color");
1363 else if( IsElement( "texture"))
1365 // get name of source textur/sampler
1366 int attrTex = GetAttribute( "texture");
1367 pSampler.mName = mReader->getAttributeValue( attrTex);
1369 // get name of UV source channel. Specification demands it to be there, but some exporters
1370 // don't write it. It will be the default UV channel in case it's missing.
1371 attrTex = TestAttribute( "texcoord");
1372 if( attrTex >= 0 )
1373 pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
1374 //SkipElement();
1376 else if( IsElement( "technique"))
1378 const int _profile = GetAttribute( "profile");
1379 const char* profile = mReader->getAttributeValue( _profile );
1381 // Some extensions are quite useful ... ReadSamplerProperties processes
1382 // several extensions in MAYA, OKINO and MAX3D profiles.
1383 if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO"))
1385 // get more information on this sampler
1386 ReadSamplerProperties(pSampler);
1388 else SkipElement();
1390 else if( !IsElement( "extra"))
1392 // ignore the rest
1393 SkipElement();
1396 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
1397 if (mReader->getNodeName() == curElem)
1398 break;
1403 // ------------------------------------------------------------------------------------------------
1404 // Reads an effect entry containing a float
1405 void ColladaParser::ReadEffectFloat( float& pFloat)
1407 while( mReader->read())
1409 if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
1410 if( IsElement( "float"))
1412 // text content contains a single floats
1413 const char* content = GetTextContent();
1414 content = fast_atoreal_move<float>( content, pFloat);
1415 SkipSpacesAndLineEnd( &content);
1417 TestClosing( "float");
1418 } else
1420 // ignore the rest
1421 SkipElement();
1424 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
1425 break;
1430 // ------------------------------------------------------------------------------------------------
1431 // Reads an effect parameter specification of any kind
1432 void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam)
1434 while( mReader->read())
1436 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
1437 if( IsElement( "surface"))
1439 // image ID given inside <init_from> tags
1440 TestOpening( "init_from");
1441 const char* content = GetTextContent();
1442 pParam.mType = Param_Surface;
1443 pParam.mReference = content;
1444 TestClosing( "init_from");
1446 // don't care for remaining stuff
1447 SkipElement( "surface");
1449 else if( IsElement( "sampler2D"))
1451 // surface ID is given inside <source> tags
1452 TestOpening( "source");
1453 const char* content = GetTextContent();
1454 pParam.mType = Param_Sampler;
1455 pParam.mReference = content;
1456 TestClosing( "source");
1458 // don't care for remaining stuff
1459 SkipElement( "sampler2D");
1460 } else
1462 // ignore unknown element
1463 SkipElement();
1466 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
1467 break;
1472 // ------------------------------------------------------------------------------------------------
1473 // Reads the geometry library contents
1474 void ColladaParser::ReadGeometryLibrary()
1476 if( mReader->isEmptyElement())
1477 return;
1479 while( mReader->read())
1481 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1483 if( IsElement( "geometry"))
1485 // read ID. Another entry which is "optional" by design but obligatory in reality
1486 int indexID = GetAttribute( "id");
1487 std::string id = mReader->getAttributeValue( indexID);
1489 // TODO: (thom) support SIDs
1490 // ai_assert( TestAttribute( "sid") == -1);
1492 // create a mesh and store it in the library under its ID
1493 Mesh* mesh = new Mesh;
1494 mMeshLibrary[id] = mesh;
1496 // read on from there
1497 ReadGeometry( mesh);
1498 } else
1500 // ignore the rest
1501 SkipElement();
1504 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1506 if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
1507 ThrowException( "Expected end of <library_geometries> element.");
1509 break;
1514 // ------------------------------------------------------------------------------------------------
1515 // Reads a geometry from the geometry library.
1516 void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
1518 if( mReader->isEmptyElement())
1519 return;
1521 while( mReader->read())
1523 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1525 if( IsElement( "mesh"))
1527 // read on from there
1528 ReadMesh( pMesh);
1529 } else
1531 // ignore the rest
1532 SkipElement();
1535 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1537 if( strcmp( mReader->getNodeName(), "geometry") != 0)
1538 ThrowException( "Expected end of <geometry> element.");
1540 break;
1545 // ------------------------------------------------------------------------------------------------
1546 // Reads a mesh from the geometry library
1547 void ColladaParser::ReadMesh( Mesh* pMesh)
1549 if( mReader->isEmptyElement())
1550 return;
1552 while( mReader->read())
1554 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1556 if( IsElement( "source"))
1558 // we have professionals dealing with this
1559 ReadSource();
1561 else if( IsElement( "vertices"))
1563 // read per-vertex mesh data
1564 ReadVertexData( pMesh);
1566 else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
1567 || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
1569 // read per-index mesh data and faces setup
1570 ReadIndexData( pMesh);
1571 } else
1573 // ignore the rest
1574 SkipElement();
1577 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1579 if( strcmp( mReader->getNodeName(), "technique_common") == 0)
1581 // end of another meaningless element - read over it
1583 else if( strcmp( mReader->getNodeName(), "mesh") == 0)
1585 // end of <mesh> element - we're done here
1586 break;
1587 } else
1589 // everything else should be punished
1590 ThrowException( "Expected end of <mesh> element.");
1596 // ------------------------------------------------------------------------------------------------
1597 // Reads a source element
1598 void ColladaParser::ReadSource()
1600 int indexID = GetAttribute( "id");
1601 std::string sourceID = mReader->getAttributeValue( indexID);
1603 while( mReader->read())
1605 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1607 if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array"))
1609 ReadDataArray();
1611 else if( IsElement( "technique_common"))
1613 // I don't care for your profiles
1615 else if( IsElement( "accessor"))
1617 ReadAccessor( sourceID);
1618 } else
1620 // ignore the rest
1621 SkipElement();
1624 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1626 if( strcmp( mReader->getNodeName(), "source") == 0)
1628 // end of <source> - we're done
1629 break;
1631 else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
1633 // end of another meaningless element - read over it
1634 } else
1636 // everything else should be punished
1637 ThrowException( "Expected end of <source> element.");
1643 // ------------------------------------------------------------------------------------------------
1644 // Reads a data array holding a number of floats, and stores it in the global library
1645 void ColladaParser::ReadDataArray()
1647 std::string elmName = mReader->getNodeName();
1648 bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
1649 bool isEmptyElement = mReader->isEmptyElement();
1651 // read attributes
1652 int indexID = GetAttribute( "id");
1653 std::string id = mReader->getAttributeValue( indexID);
1654 int indexCount = GetAttribute( "count");
1655 unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
1656 if (count == 0) { return; } // some exporters write empty data arrays with count="0"
1657 const char* content = TestTextContent();
1659 // read values and store inside an array in the data library
1660 mDataLibrary[id] = Data();
1661 Data& data = mDataLibrary[id];
1662 data.mIsStringArray = isStringArray;
1664 // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
1665 if (content)
1667 if( isStringArray)
1669 data.mStrings.reserve( count);
1670 std::string s;
1672 for( unsigned int a = 0; a < count; a++)
1674 if( *content == 0)
1675 ThrowException( "Expected more values while reading IDREF_array contents.");
1677 s.clear();
1678 while( !IsSpaceOrNewLine( *content))
1679 s += *content++;
1680 data.mStrings.push_back( s);
1682 SkipSpacesAndLineEnd( &content);
1684 } else
1686 data.mValues.reserve( count);
1688 for( unsigned int a = 0; a < count; a++)
1690 if( *content == 0)
1691 ThrowException( "Expected more values while reading float_array contents.");
1693 float value;
1694 // read a number
1695 content = fast_atoreal_move<float>( content, value);
1696 data.mValues.push_back( value);
1697 // skip whitespace after it
1698 SkipSpacesAndLineEnd( &content);
1703 // test for closing tag
1704 if( !isEmptyElement )
1705 TestClosing( elmName.c_str());
1708 // ------------------------------------------------------------------------------------------------
1709 // Reads an accessor and stores it in the global library
1710 void ColladaParser::ReadAccessor( const std::string& pID)
1712 // read accessor attributes
1713 int attrSource = GetAttribute( "source");
1714 const char* source = mReader->getAttributeValue( attrSource);
1715 if( source[0] != '#')
1716 ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of <accessor> element.") % source));
1717 int attrCount = GetAttribute( "count");
1718 unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
1719 int attrOffset = TestAttribute( "offset");
1720 unsigned int offset = 0;
1721 if( attrOffset > -1)
1722 offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
1723 int attrStride = TestAttribute( "stride");
1724 unsigned int stride = 1;
1725 if( attrStride > -1)
1726 stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
1728 // store in the library under the given ID
1729 mAccessorLibrary[pID] = Accessor();
1730 Accessor& acc = mAccessorLibrary[pID];
1731 acc.mCount = count;
1732 acc.mOffset = offset;
1733 acc.mStride = stride;
1734 acc.mSource = source+1; // ignore the leading '#'
1735 acc.mSize = 0; // gets incremented with every param
1737 // and read the components
1738 while( mReader->read())
1740 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1742 if( IsElement( "param"))
1744 // read data param
1745 int attrName = TestAttribute( "name");
1746 std::string name;
1747 if( attrName > -1)
1749 name = mReader->getAttributeValue( attrName);
1751 // analyse for common type components and store it's sub-offset in the corresponding field
1753 /* Cartesian coordinates */
1754 if( name == "X") acc.mSubOffset[0] = acc.mParams.size();
1755 else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
1756 else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
1758 /* RGBA colors */
1759 else if( name == "R") acc.mSubOffset[0] = acc.mParams.size();
1760 else if( name == "G") acc.mSubOffset[1] = acc.mParams.size();
1761 else if( name == "B") acc.mSubOffset[2] = acc.mParams.size();
1762 else if( name == "A") acc.mSubOffset[3] = acc.mParams.size();
1764 /* UVWQ (STPQ) texture coordinates */
1765 else if( name == "S") acc.mSubOffset[0] = acc.mParams.size();
1766 else if( name == "T") acc.mSubOffset[1] = acc.mParams.size();
1767 else if( name == "P") acc.mSubOffset[2] = acc.mParams.size();
1768 // else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
1769 /* 4D uv coordinates are not supported in Assimp */
1771 /* Generic extra data, interpreted as UV data, too*/
1772 else if( name == "U") acc.mSubOffset[0] = acc.mParams.size();
1773 else if( name == "V") acc.mSubOffset[1] = acc.mParams.size();
1774 //else
1775 // DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name));
1778 // read data type
1779 int attrType = TestAttribute( "type");
1780 if( attrType > -1)
1782 // for the moment we only distinguish between a 4x4 matrix and anything else.
1783 // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
1784 // which should be tested for here.
1785 std::string type = mReader->getAttributeValue( attrType);
1786 if( type == "float4x4")
1787 acc.mSize += 16;
1788 else
1789 acc.mSize += 1;
1792 acc.mParams.push_back( name);
1794 // skip remaining stuff of this element, if any
1795 SkipElement();
1796 } else
1798 ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <accessor>") % mReader->getNodeName()));
1801 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1803 if( strcmp( mReader->getNodeName(), "accessor") != 0)
1804 ThrowException( "Expected end of <accessor> element.");
1805 break;
1810 // ------------------------------------------------------------------------------------------------
1811 // Reads input declarations of per-vertex mesh data into the given mesh
1812 void ColladaParser::ReadVertexData( Mesh* pMesh)
1814 // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
1815 int attrID= GetAttribute( "id");
1816 pMesh->mVertexID = mReader->getAttributeValue( attrID);
1818 // a number of <input> elements
1819 while( mReader->read())
1821 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1823 if( IsElement( "input"))
1825 ReadInputChannel( pMesh->mPerVertexData);
1826 } else
1828 ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <vertices>") % mReader->getNodeName()));
1831 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1833 if( strcmp( mReader->getNodeName(), "vertices") != 0)
1834 ThrowException( "Expected end of <vertices> element.");
1836 break;
1841 // ------------------------------------------------------------------------------------------------
1842 // Reads input declarations of per-index mesh data into the given mesh
1843 void ColladaParser::ReadIndexData( Mesh* pMesh)
1845 std::vector<size_t> vcount;
1846 std::vector<InputChannel> perIndexData;
1848 // read primitive count from the attribute
1849 int attrCount = GetAttribute( "count");
1850 size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
1852 // material subgroup
1853 int attrMaterial = TestAttribute( "material");
1854 SubMesh subgroup;
1855 if( attrMaterial > -1)
1856 subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
1857 subgroup.mNumFaces = numPrimitives;
1858 pMesh->mSubMeshes.push_back( subgroup);
1860 // distinguish between polys and triangles
1861 std::string elementName = mReader->getNodeName();
1862 PrimitiveType primType = Prim_Invalid;
1863 if( IsElement( "lines"))
1864 primType = Prim_Lines;
1865 else if( IsElement( "linestrips"))
1866 primType = Prim_LineStrip;
1867 else if( IsElement( "polygons"))
1868 primType = Prim_Polygon;
1869 else if( IsElement( "polylist"))
1870 primType = Prim_Polylist;
1871 else if( IsElement( "triangles"))
1872 primType = Prim_Triangles;
1873 else if( IsElement( "trifans"))
1874 primType = Prim_TriFans;
1875 else if( IsElement( "tristrips"))
1876 primType = Prim_TriStrips;
1878 ai_assert( primType != Prim_Invalid);
1880 // also a number of <input> elements, but in addition a <p> primitive collection and propably index counts for all primitives
1881 while( mReader->read())
1883 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
1885 if( IsElement( "input"))
1887 ReadInputChannel( perIndexData);
1889 else if( IsElement( "vcount"))
1891 if( !mReader->isEmptyElement())
1893 if (numPrimitives) // It is possible to define a mesh without any primitives
1895 // case <polylist> - specifies the number of indices for each polygon
1896 const char* content = GetTextContent();
1897 vcount.reserve( numPrimitives);
1898 for( unsigned int a = 0; a < numPrimitives; a++)
1900 if( *content == 0)
1901 ThrowException( "Expected more values while reading <vcount> contents.");
1902 // read a number
1903 vcount.push_back( (size_t) strtoul10( content, &content));
1904 // skip whitespace after it
1905 SkipSpacesAndLineEnd( &content);
1909 TestClosing( "vcount");
1912 else if( IsElement( "p"))
1914 if( !mReader->isEmptyElement())
1916 // now here the actual fun starts - these are the indices to construct the mesh data from
1917 ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
1919 } else
1921 ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName));
1924 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1926 if( mReader->getNodeName() != elementName)
1927 ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % elementName));
1929 break;
1934 // ------------------------------------------------------------------------------------------------
1935 // Reads a single input channel element and stores it in the given array, if valid
1936 void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
1938 InputChannel channel;
1940 // read semantic
1941 int attrSemantic = GetAttribute( "semantic");
1942 std::string semantic = mReader->getAttributeValue( attrSemantic);
1943 channel.mType = GetTypeForSemantic( semantic);
1945 // read source
1946 int attrSource = GetAttribute( "source");
1947 const char* source = mReader->getAttributeValue( attrSource);
1948 if( source[0] != '#')
1949 ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of <input> element.") % source));
1950 channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
1952 // read index offset, if per-index <input>
1953 int attrOffset = TestAttribute( "offset");
1954 if( attrOffset > -1)
1955 channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
1957 // read set if texture coordinates
1958 if(channel.mType == IT_Texcoord || channel.mType == IT_Color){
1959 int attrSet = TestAttribute("set");
1960 if(attrSet > -1){
1961 attrSet = mReader->getAttributeValueAsInt( attrSet);
1962 if(attrSet < 0)
1963 ThrowException( boost::str( boost::format( "Invalid index \"%i\" in set attribute of <input> element") % (attrSet)));
1965 channel.mIndex = attrSet;
1969 // store, if valid type
1970 if( channel.mType != IT_Invalid)
1971 poChannels.push_back( channel);
1973 // skip remaining stuff of this element, if any
1974 SkipElement();
1977 // ------------------------------------------------------------------------------------------------
1978 // Reads a <p> primitive index list and assembles the mesh data into the given mesh
1979 void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
1980 size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
1982 // determine number of indices coming per vertex
1983 // find the offset index for all per-vertex channels
1984 size_t numOffsets = 1;
1985 size_t perVertexOffset = SIZE_MAX; // invalid value
1986 BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels)
1988 numOffsets = std::max( numOffsets, channel.mOffset+1);
1989 if( channel.mType == IT_Vertex)
1990 perVertexOffset = channel.mOffset;
1993 // determine the expected number of indices
1994 size_t expectedPointCount = 0;
1995 switch( pPrimType)
1997 case Prim_Polylist:
1999 BOOST_FOREACH( size_t i, pVCount)
2000 expectedPointCount += i;
2001 break;
2003 case Prim_Lines:
2004 expectedPointCount = 2 * pNumPrimitives;
2005 break;
2006 case Prim_Triangles:
2007 expectedPointCount = 3 * pNumPrimitives;
2008 break;
2009 default:
2010 // other primitive types don't state the index count upfront... we need to guess
2011 break;
2014 // and read all indices into a temporary array
2015 std::vector<size_t> indices;
2016 if( expectedPointCount > 0)
2017 indices.reserve( expectedPointCount * numOffsets);
2019 if (pNumPrimitives > 0) // It is possible to not contain any indicies
2021 const char* content = GetTextContent();
2022 while( *content != 0)
2024 // read a value.
2025 // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
2026 int value = std::max( 0, strtol10( content, &content));
2027 indices.push_back( size_t( value));
2028 // skip whitespace after it
2029 SkipSpacesAndLineEnd( &content);
2033 // complain if the index count doesn't fit
2034 if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets)
2035 ThrowException( "Expected different index count in <p> element.");
2036 else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
2037 ThrowException( "Expected different index count in <p> element.");
2039 // find the data for all sources
2040 for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
2042 InputChannel& input = *it;
2043 if( input.mResolved)
2044 continue;
2046 // find accessor
2047 input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
2048 // resolve accessor's data pointer as well, if neccessary
2049 const Accessor* acc = input.mResolved;
2050 if( !acc->mData)
2051 acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
2053 // and the same for the per-index channels
2054 for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
2056 InputChannel& input = *it;
2057 if( input.mResolved)
2058 continue;
2060 // ignore vertex pointer, it doesn't refer to an accessor
2061 if( input.mType == IT_Vertex)
2063 // warn if the vertex channel does not refer to the <vertices> element in the same mesh
2064 if( input.mAccessor != pMesh->mVertexID)
2065 ThrowException( "Unsupported vertex referencing scheme.");
2066 continue;
2069 // find accessor
2070 input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
2071 // resolve accessor's data pointer as well, if neccessary
2072 const Accessor* acc = input.mResolved;
2073 if( !acc->mData)
2074 acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
2078 // now assemble vertex data according to those indices
2079 std::vector<size_t>::const_iterator idx = indices.begin();
2081 // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
2082 size_t numPrimitives = pNumPrimitives;
2083 if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
2084 numPrimitives = 1;
2086 pMesh->mFaceSize.reserve( numPrimitives);
2087 pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
2089 for( size_t a = 0; a < numPrimitives; a++)
2091 // determine number of points for this primitive
2092 size_t numPoints = 0;
2093 switch( pPrimType)
2095 case Prim_Lines:
2096 numPoints = 2;
2097 break;
2098 case Prim_Triangles:
2099 numPoints = 3;
2100 break;
2101 case Prim_Polylist:
2102 numPoints = pVCount[a];
2103 break;
2104 case Prim_TriFans:
2105 case Prim_Polygon:
2106 numPoints = indices.size() / numOffsets;
2107 break;
2108 default:
2109 // LineStrip and TriStrip not supported due to expected index unmangling
2110 ThrowException( "Unsupported primitive type.");
2111 break;
2114 // store the face size to later reconstruct the face from
2115 pMesh->mFaceSize.push_back( numPoints);
2117 // gather that number of vertices
2118 for( size_t b = 0; b < numPoints; b++)
2120 // read all indices for this vertex. Yes, in a hacky local array
2121 ai_assert( numOffsets < 20 && perVertexOffset < 20);
2122 size_t vindex[20];
2123 for( size_t offsets = 0; offsets < numOffsets; ++offsets)
2124 vindex[offsets] = *idx++;
2126 // extract per-vertex channels using the global per-vertex offset
2127 for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
2128 ExtractDataObjectFromChannel( *it, vindex[perVertexOffset], pMesh);
2129 // and extract per-index channels using there specified offset
2130 for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
2131 ExtractDataObjectFromChannel( *it, vindex[it->mOffset], pMesh);
2133 // store the vertex-data index for later assignment of bone vertex weights
2134 pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]);
2139 // if I ever get my hands on that guy who invented this steaming pile of indirection...
2140 TestClosing( "p");
2143 // ------------------------------------------------------------------------------------------------
2144 // Extracts a single object from an input channel and stores it in the appropriate mesh data array
2145 void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
2147 // ignore vertex referrer - we handle them that separate
2148 if( pInput.mType == IT_Vertex)
2149 return;
2151 const Accessor& acc = *pInput.mResolved;
2152 if( pLocalIndex >= acc.mCount)
2153 ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount));
2155 // get a pointer to the start of the data object referred to by the accessor and the local index
2156 const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
2158 // assemble according to the accessors component sub-offset list. We don't care, yet,
2159 // what kind of object exactly we're extracting here
2160 float obj[4];
2161 for( size_t c = 0; c < 4; ++c)
2162 obj[c] = dataObject[acc.mSubOffset[c]];
2164 // now we reinterpret it according to the type we're reading here
2165 switch( pInput.mType)
2167 case IT_Position: // ignore all position streams except 0 - there can be only one position
2168 if( pInput.mIndex == 0)
2169 pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2170 else
2171 DefaultLogger::get()->error("Collada: just one vertex position stream supported");
2172 break;
2173 case IT_Normal:
2174 // pad to current vertex count if necessary
2175 if( pMesh->mNormals.size() < pMesh->mPositions.size()-1)
2176 pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0));
2178 // ignore all normal streams except 0 - there can be only one normal
2179 if( pInput.mIndex == 0)
2180 pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2181 else
2182 DefaultLogger::get()->error("Collada: just one vertex normal stream supported");
2183 break;
2184 case IT_Tangent:
2185 // pad to current vertex count if necessary
2186 if( pMesh->mTangents.size() < pMesh->mPositions.size()-1)
2187 pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0));
2189 // ignore all tangent streams except 0 - there can be only one tangent
2190 if( pInput.mIndex == 0)
2191 pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2192 else
2193 DefaultLogger::get()->error("Collada: just one vertex tangent stream supported");
2194 break;
2195 case IT_Bitangent:
2196 // pad to current vertex count if necessary
2197 if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1)
2198 pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1));
2200 // ignore all bitangent streams except 0 - there can be only one bitangent
2201 if( pInput.mIndex == 0)
2202 pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
2203 else
2204 DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported");
2205 break;
2206 case IT_Texcoord:
2207 // up to 4 texture coord sets are fine, ignore the others
2208 if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
2210 // pad to current vertex count if necessary
2211 if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1)
2212 pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(),
2213 pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0));
2215 pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2]));
2216 if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
2217 pMesh->mNumUVComponents[pInput.mIndex]=3;
2218 } else
2220 DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping.");
2222 break;
2223 case IT_Color:
2224 // up to 4 color sets are fine, ignore the others
2225 if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
2227 // pad to current vertex count if necessary
2228 if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1)
2229 pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(),
2230 pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1));
2232 aiColor4D result(0, 0, 0, 1);
2233 for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
2235 result[i] = obj[pInput.mResolved->mSubOffset[i]];
2237 pMesh->mColors[pInput.mIndex].push_back(result);
2238 } else
2240 DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping.");
2243 break;
2244 default:
2245 // IT_Invalid and IT_Vertex
2246 ai_assert(false && "shouldn't ever get here");
2250 // ------------------------------------------------------------------------------------------------
2251 // Reads the library of node hierarchies and scene parts
2252 void ColladaParser::ReadSceneLibrary()
2254 if( mReader->isEmptyElement())
2255 return;
2257 while( mReader->read())
2259 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2261 // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
2262 if( IsElement( "visual_scene"))
2264 // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
2265 int indexID = GetAttribute( "id");
2266 const char* attrID = mReader->getAttributeValue( indexID);
2268 // read name if given.
2269 int indexName = TestAttribute( "name");
2270 const char* attrName = "unnamed";
2271 if( indexName > -1)
2272 attrName = mReader->getAttributeValue( indexName);
2274 // create a node and store it in the library under its ID
2275 Node* node = new Node;
2276 node->mID = attrID;
2277 node->mName = attrName;
2278 mNodeLibrary[node->mID] = node;
2280 ReadSceneNode( node);
2281 } else
2283 // ignore the rest
2284 SkipElement();
2287 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2289 if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
2290 //ThrowException( "Expected end of \"library_visual_scenes\" element.");
2292 break;
2297 // ------------------------------------------------------------------------------------------------
2298 // Reads a scene node's contents including children and stores it in the given node
2299 void ColladaParser::ReadSceneNode( Node* pNode)
2301 // quit immediately on <bla/> elements
2302 if( mReader->isEmptyElement())
2303 return;
2305 while( mReader->read())
2307 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2309 if( IsElement( "node"))
2311 Node* child = new Node;
2312 int attrID = TestAttribute( "id");
2313 if( attrID > -1)
2314 child->mID = mReader->getAttributeValue( attrID);
2315 int attrSID = TestAttribute( "sid");
2316 if( attrSID > -1)
2317 child->mSID = mReader->getAttributeValue( attrSID);
2319 int attrName = TestAttribute( "name");
2320 if( attrName > -1)
2321 child->mName = mReader->getAttributeValue( attrName);
2323 // TODO: (thom) support SIDs
2324 // ai_assert( TestAttribute( "sid") == -1);
2326 if (pNode)
2328 pNode->mChildren.push_back( child);
2329 child->mParent = pNode;
2331 else
2333 // no parent node given, probably called from <library_nodes> element.
2334 // create new node in node library
2335 mNodeLibrary[child->mID] = child;
2338 // read on recursively from there
2339 ReadSceneNode( child);
2340 continue;
2342 // For any further stuff we need a valid node to work on
2343 else if (!pNode)
2344 continue;
2346 if( IsElement( "lookat"))
2347 ReadNodeTransformation( pNode, TF_LOOKAT);
2348 else if( IsElement( "matrix"))
2349 ReadNodeTransformation( pNode, TF_MATRIX);
2350 else if( IsElement( "rotate"))
2351 ReadNodeTransformation( pNode, TF_ROTATE);
2352 else if( IsElement( "scale"))
2353 ReadNodeTransformation( pNode, TF_SCALE);
2354 else if( IsElement( "skew"))
2355 ReadNodeTransformation( pNode, TF_SKEW);
2356 else if( IsElement( "translate"))
2357 ReadNodeTransformation( pNode, TF_TRANSLATE);
2358 else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
2360 // ... scene evaluation or, in other words, postprocessing pipeline,
2361 // or, again in other words, a turing-complete description how to
2362 // render a Collada scene. The only thing that is interesting for
2363 // us is the primary camera.
2364 int attrId = TestAttribute("camera_node");
2365 if (-1 != attrId)
2367 const char* s = mReader->getAttributeValue(attrId);
2368 if (s[0] != '#')
2369 DefaultLogger::get()->error("Collada: Unresolved reference format of camera");
2370 else
2371 pNode->mPrimaryCamera = s+1;
2374 else if( IsElement( "instance_node"))
2376 // find the node in the library
2377 int attrID = TestAttribute( "url");
2378 if( attrID != -1)
2380 const char* s = mReader->getAttributeValue(attrID);
2381 if (s[0] != '#')
2382 DefaultLogger::get()->error("Collada: Unresolved reference format of node");
2383 else
2385 pNode->mNodeInstances.push_back(NodeInstance());
2386 pNode->mNodeInstances.back().mNode = s+1;
2390 else if( IsElement( "instance_geometry") || IsElement( "instance_controller"))
2392 // Reference to a mesh or controller, with possible material associations
2393 ReadNodeGeometry( pNode);
2395 else if( IsElement( "instance_light"))
2397 // Reference to a light, name given in 'url' attribute
2398 int attrID = TestAttribute("url");
2399 if (-1 == attrID)
2400 DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_light> element");
2401 else
2403 const char* url = mReader->getAttributeValue( attrID);
2404 if( url[0] != '#')
2405 ThrowException( "Unknown reference format in <instance_light> element");
2407 pNode->mLights.push_back(LightInstance());
2408 pNode->mLights.back().mLight = url+1;
2411 else if( IsElement( "instance_camera"))
2413 // Reference to a camera, name given in 'url' attribute
2414 int attrID = TestAttribute("url");
2415 if (-1 == attrID)
2416 DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_camera> element");
2417 else
2419 const char* url = mReader->getAttributeValue( attrID);
2420 if( url[0] != '#')
2421 ThrowException( "Unknown reference format in <instance_camera> element");
2423 pNode->mCameras.push_back(CameraInstance());
2424 pNode->mCameras.back().mCamera = url+1;
2427 else
2429 // skip everything else for the moment
2430 SkipElement();
2433 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
2434 break;
2439 // ------------------------------------------------------------------------------------------------
2440 // Reads a node transformation entry of the given type and adds it to the given node's transformation list.
2441 void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType)
2443 if( mReader->isEmptyElement())
2444 return;
2446 std::string tagName = mReader->getNodeName();
2448 Transform tf;
2449 tf.mType = pType;
2451 // read SID
2452 int indexSID = TestAttribute( "sid");
2453 if( indexSID >= 0)
2454 tf.mID = mReader->getAttributeValue( indexSID);
2456 // how many parameters to read per transformation type
2457 static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
2458 const char* content = GetTextContent();
2460 // read as many parameters and store in the transformation
2461 for( unsigned int a = 0; a < sNumParameters[pType]; a++)
2463 // read a number
2464 content = fast_atoreal_move<float>( content, tf.f[a]);
2465 // skip whitespace after it
2466 SkipSpacesAndLineEnd( &content);
2469 // place the transformation at the queue of the node
2470 pNode->mTransforms.push_back( tf);
2472 // and consume the closing tag
2473 TestClosing( tagName.c_str());
2476 // ------------------------------------------------------------------------------------------------
2477 // Processes bind_vertex_input and bind elements
2478 void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl)
2480 while( mReader->read())
2482 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
2483 if( IsElement( "bind_vertex_input"))
2485 Collada::InputSemanticMapEntry vn;
2487 // effect semantic
2488 int n = GetAttribute("semantic");
2489 std::string s = mReader->getAttributeValue(n);
2491 // input semantic
2492 n = GetAttribute("input_semantic");
2493 vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) );
2495 // index of input set
2496 n = TestAttribute("input_set");
2497 if (-1 != n)
2498 vn.mSet = mReader->getAttributeValueAsInt(n);
2500 tbl.mMap[s] = vn;
2502 else if( IsElement( "bind")) {
2503 DefaultLogger::get()->warn("Collada: Found unsupported <bind> element");
2506 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
2507 if( strcmp( mReader->getNodeName(), "instance_material") == 0)
2508 break;
2513 // ------------------------------------------------------------------------------------------------
2514 // Reads a mesh reference in a node and adds it to the node's mesh list
2515 void ColladaParser::ReadNodeGeometry( Node* pNode)
2517 // referred mesh is given as an attribute of the <instance_geometry> element
2518 int attrUrl = GetAttribute( "url");
2519 const char* url = mReader->getAttributeValue( attrUrl);
2520 if( url[0] != '#')
2521 ThrowException( "Unknown reference format");
2523 Collada::MeshInstance instance;
2524 instance.mMeshOrController = url+1; // skipping the leading #
2526 if( !mReader->isEmptyElement())
2528 // read material associations. Ignore additional elements inbetween
2529 while( mReader->read())
2531 if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
2533 if( IsElement( "instance_material"))
2535 // read ID of the geometry subgroup and the target material
2536 int attrGroup = GetAttribute( "symbol");
2537 std::string group = mReader->getAttributeValue( attrGroup);
2538 int attrMaterial = GetAttribute( "target");
2539 const char* urlMat = mReader->getAttributeValue( attrMaterial);
2540 Collada::SemanticMappingTable s;
2541 if( urlMat[0] == '#')
2542 urlMat++;
2544 s.mMatName = urlMat;
2546 // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
2547 if( !mReader->isEmptyElement())
2548 ReadMaterialVertexInputBinding(s);
2550 // store the association
2551 instance.mMaterials[group] = s;
2554 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2556 if( strcmp( mReader->getNodeName(), "instance_geometry") == 0
2557 || strcmp( mReader->getNodeName(), "instance_controller") == 0)
2558 break;
2563 // store it
2564 pNode->mMeshes.push_back( instance);
2567 // ------------------------------------------------------------------------------------------------
2568 // Reads the collada scene
2569 void ColladaParser::ReadScene()
2571 if( mReader->isEmptyElement())
2572 return;
2574 while( mReader->read())
2576 if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
2577 if( IsElement( "instance_visual_scene"))
2579 // should be the first and only occurence
2580 if( mRootNode)
2581 ThrowException( "Invalid scene containing multiple root nodes in <instance_visual_scene> element");
2583 // read the url of the scene to instance. Should be of format "#some_name"
2584 int urlIndex = GetAttribute( "url");
2585 const char* url = mReader->getAttributeValue( urlIndex);
2586 if( url[0] != '#')
2587 ThrowException( "Unknown reference format in <instance_visual_scene> element");
2589 // find the referred scene, skip the leading #
2590 NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
2591 if( sit == mNodeLibrary.end())
2592 ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
2593 mRootNode = sit->second;
2594 } else {
2595 SkipElement();
2598 else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
2599 break;
2604 // ------------------------------------------------------------------------------------------------
2605 // Aborts the file reading with an exception
2606 void ColladaParser::ThrowException( const std::string& pError) const
2608 throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError));
2611 // ------------------------------------------------------------------------------------------------
2612 // Skips all data until the end node of the current element
2613 void ColladaParser::SkipElement()
2615 // nothing to skip if it's an <element />
2616 if( mReader->isEmptyElement())
2617 return;
2619 // reroute
2620 SkipElement( mReader->getNodeName());
2623 // ------------------------------------------------------------------------------------------------
2624 // Skips all data until the end node of the given element
2625 void ColladaParser::SkipElement( const char* pElement)
2627 // copy the current node's name because it'a pointer to the reader's internal buffer,
2628 // which is going to change with the upcoming parsing
2629 std::string element = pElement;
2630 while( mReader->read())
2632 if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
2633 if( mReader->getNodeName() == element)
2634 break;
2638 // ------------------------------------------------------------------------------------------------
2639 // Tests for an opening element of the given name, throws an exception if not found
2640 void ColladaParser::TestOpening( const char* pName)
2642 // read element start
2643 if( !mReader->read())
2644 ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of <%s> element.") % pName));
2645 // whitespace in front is ok, just read again if found
2646 if( mReader->getNodeType() == irr::io::EXN_TEXT)
2647 if( !mReader->read())
2648 ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of <%s> element.") % pName));
2650 if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
2651 ThrowException( boost::str( boost::format( "Expected start of <%s> element.") % pName));
2654 // ------------------------------------------------------------------------------------------------
2655 // Tests for the closing tag of the given element, throws an exception if not found
2656 void ColladaParser::TestClosing( const char* pName)
2658 // check if we're already on the closing tag and return right away
2659 if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
2660 return;
2662 // if not, read some more
2663 if( !mReader->read())
2664 ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName));
2665 // whitespace in front is ok, just read again if found
2666 if( mReader->getNodeType() == irr::io::EXN_TEXT)
2667 if( !mReader->read())
2668 ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName));
2670 // but this has the be the closing tag, or we're lost
2671 if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
2672 ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % pName));
2675 // ------------------------------------------------------------------------------------------------
2676 // Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
2677 int ColladaParser::GetAttribute( const char* pAttr) const
2679 int index = TestAttribute( pAttr);
2680 if( index != -1)
2681 return index;
2683 // attribute not found -> throw an exception
2684 ThrowException( boost::str( boost::format( "Expected attribute \"%s\" for element <%s>.") % pAttr % mReader->getNodeName()));
2685 return -1;
2688 // ------------------------------------------------------------------------------------------------
2689 // Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
2690 int ColladaParser::TestAttribute( const char* pAttr) const
2692 for( int a = 0; a < mReader->getAttributeCount(); a++)
2693 if( strcmp( mReader->getAttributeName( a), pAttr) == 0)
2694 return a;
2696 return -1;
2699 // ------------------------------------------------------------------------------------------------
2700 // Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
2701 const char* ColladaParser::GetTextContent()
2703 const char* sz = TestTextContent();
2704 if(!sz) {
2705 ThrowException( "Invalid contents in element \"n\".");
2707 return sz;
2710 // ------------------------------------------------------------------------------------------------
2711 // Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
2712 const char* ColladaParser::TestTextContent()
2714 // present node should be the beginning of an element
2715 if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
2716 return NULL;
2718 // read contents of the element
2719 if( !mReader->read() )
2720 return NULL;
2721 if( mReader->getNodeType() != irr::io::EXN_TEXT)
2722 return NULL;
2724 // skip leading whitespace
2725 const char* text = mReader->getNodeData();
2726 SkipSpacesAndLineEnd( &text);
2728 return text;
2731 // ------------------------------------------------------------------------------------------------
2732 // Calculates the resulting transformation fromm all the given transform steps
2733 aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform>& pTransforms) const
2735 aiMatrix4x4 res;
2737 for( std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
2739 const Transform& tf = *it;
2740 switch( tf.mType)
2742 case TF_LOOKAT:
2744 aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]);
2745 aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]);
2746 aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize();
2747 aiVector3D dir = aiVector3D( dstPos - pos).Normalize();
2748 aiVector3D right = (dir ^ up).Normalize();
2750 res *= aiMatrix4x4(
2751 right.x, up.x, -dir.x, pos.x,
2752 right.y, up.y, -dir.y, pos.y,
2753 right.z, up.z, -dir.z, pos.z,
2754 0, 0, 0, 1);
2755 break;
2757 case TF_ROTATE:
2759 aiMatrix4x4 rot;
2760 float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f;
2761 aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
2762 aiMatrix4x4::Rotation( angle, axis, rot);
2763 res *= rot;
2764 break;
2766 case TF_TRANSLATE:
2768 aiMatrix4x4 trans;
2769 aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans);
2770 res *= trans;
2771 break;
2773 case TF_SCALE:
2775 aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
2776 0.0f, 0.0f, 0.0f, 1.0f);
2777 res *= scale;
2778 break;
2780 case TF_SKEW:
2781 // TODO: (thom)
2782 ai_assert( false);
2783 break;
2784 case TF_MATRIX:
2786 aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
2787 tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
2788 res *= mat;
2789 break;
2791 default:
2792 ai_assert( false);
2793 break;
2797 return res;
2800 // ------------------------------------------------------------------------------------------------
2801 // Determines the input data type for the given semantic string
2802 Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
2804 if( pSemantic == "POSITION")
2805 return IT_Position;
2806 else if( pSemantic == "TEXCOORD")
2807 return IT_Texcoord;
2808 else if( pSemantic == "NORMAL")
2809 return IT_Normal;
2810 else if( pSemantic == "COLOR")
2811 return IT_Color;
2812 else if( pSemantic == "VERTEX")
2813 return IT_Vertex;
2814 else if( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL")
2815 return IT_Bitangent;
2816 else if( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT")
2817 return IT_Tangent;
2819 DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic));
2820 return IT_Invalid;
2823 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER