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