1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, assimp team
7
8
9
10 All rights reserved.
11
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15
16 * Redistributions of source code must retain the above
17 copyright notice, this list of conditions and the
18 following disclaimer.
19
20 * Redistributions in binary form must reproduce the above
21 copyright notice, this list of conditions and the
22 following disclaimer in the documentation and/or other
23 materials provided with the distribution.
24
25 * Neither the name of the assimp team, nor the names of its
26 contributors may be used to endorse or promote products
27 derived from this software without specific prior
28 written permission of the assimp team.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43
44 /** @file Implementation of the XFile parser helper class */
45
46
47 #ifndef ASSIMP_BUILD_NO_X_IMPORTER
48
49 #include "XFileParser.h"
50 #include "XFileHelper.h"
51 #include <assimp/fast_atof.h>
52 #include <assimp/Exceptional.h>
53 #include <assimp/TinyFormatter.h>
54 #include <assimp/ByteSwapper.h>
55 #include <assimp/StringUtils.h>
56 #include <assimp/DefaultLogger.hpp>
57
58
59 using namespace Assimp;
60 using namespace Assimp::XFile;
61 using namespace Assimp::Formatter;
62
63 #ifndef ASSIMP_BUILD_NO_COMPRESSED_X
64
65 # ifdef ASSIMP_BUILD_NO_OWN_ZLIB
66 # include <zlib.h>
67 # else
68 # include "../contrib/zlib/zlib.h"
69 # endif
70
71 // Magic identifier for MSZIP compressed data
72 #define MSZIP_MAGIC 0x4B43
73 #define MSZIP_BLOCK 32786
74
75 // ------------------------------------------------------------------------------------------------
76 // Dummy memory wrappers for use with zlib
dummy_alloc(void *,unsigned int items,unsigned int size)77 static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) {
78 return ::operator new(items*size);
79 }
80
dummy_free(void *,void * address)81 static void dummy_free (void* /*opaque*/, void* address) {
82 return ::operator delete(address);
83 }
84
85 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
86
87 // ------------------------------------------------------------------------------------------------
88 // Constructor. Creates a data structure out of the XFile given in the memory block.
XFileParser(const std::vector<char> & pBuffer)89 XFileParser::XFileParser( const std::vector<char>& pBuffer)
90 : mMajorVersion( 0 )
91 , mMinorVersion( 0 )
92 , mIsBinaryFormat( false )
93 , mBinaryNumCount( 0 )
94 , mP( nullptr )
95 , mEnd( nullptr )
96 , mLineNumber( 0 )
97 , mScene( nullptr ) {
98 // vector to store uncompressed file for INFLATE'd X files
99 std::vector<char> uncompressed;
100
101 // set up memory pointers
102 mP = &pBuffer.front();
103 mEnd = mP + pBuffer.size() - 1;
104
105 // check header
106 if ( 0 != strncmp( mP, "xof ", 4 ) ) {
107 throw DeadlyImportError( "Header mismatch, file is not an XFile." );
108 }
109
110 // read version. It comes in a four byte format such as "0302"
111 mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48);
112 mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48);
113
114 bool compressed = false;
115
116 // txt - pure ASCII text format
117 if( strncmp( mP + 8, "txt ", 4) == 0)
118 mIsBinaryFormat = false;
119
120 // bin - Binary format
121 else if( strncmp( mP + 8, "bin ", 4) == 0)
122 mIsBinaryFormat = true;
123
124 // tzip - Inflate compressed text format
125 else if( strncmp( mP + 8, "tzip", 4) == 0)
126 {
127 mIsBinaryFormat = false;
128 compressed = true;
129 }
130 // bzip - Inflate compressed binary format
131 else if( strncmp( mP + 8, "bzip", 4) == 0)
132 {
133 mIsBinaryFormat = true;
134 compressed = true;
135 }
136 else ThrowException( format() << "Unsupported xfile format '" <<
137 mP[8] << mP[9] << mP[10] << mP[11] << "'");
138
139 // float size
140 mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000
141 + (unsigned int)(mP[13] - 48) * 100
142 + (unsigned int)(mP[14] - 48) * 10
143 + (unsigned int)(mP[15] - 48);
144
145 if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
146 ThrowException( format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header." );
147
148 // The x format specifies size in bits, but we work in bytes
149 mBinaryFloatSize /= 8;
150
151 mP += 16;
152
153 // If this is a compressed X file, apply the inflate algorithm to it
154 if (compressed)
155 {
156 #ifdef ASSIMP_BUILD_NO_COMPRESSED_X
157 throw DeadlyImportError("Assimp was built without compressed X support");
158 #else
159 /* ///////////////////////////////////////////////////////////////////////
160 * COMPRESSED X FILE FORMAT
161 * ///////////////////////////////////////////////////////////////////////
162 * [xhead]
163 * 2 major
164 * 2 minor
165 * 4 type // bzip,tzip
166 * [mszip_master_head]
167 * 4 unkn // checksum?
168 * 2 unkn // flags? (seems to be constant)
169 * [mszip_head]
170 * 2 ofs // offset to next section
171 * 2 magic // 'CK'
172 * ... ofs bytes of data
173 * ... next mszip_head
174 *
175 * http://www.kdedevelopers.org/node/3181 has been very helpful.
176 * ///////////////////////////////////////////////////////////////////////
177 */
178
179 // build a zlib stream
180 z_stream stream;
181 stream.opaque = NULL;
182 stream.zalloc = &dummy_alloc;
183 stream.zfree = &dummy_free;
184 stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII);
185
186 // initialize the inflation algorithm
187 ::inflateInit2(&stream, -MAX_WBITS);
188
189 // skip unknown data (checksum, flags?)
190 mP += 6;
191
192 // First find out how much storage we'll need. Count sections.
193 const char* P1 = mP;
194 unsigned int est_out = 0;
195
196 while (P1 + 3 < mEnd)
197 {
198 // read next offset
199 uint16_t ofs = *((uint16_t*)P1);
200 AI_SWAP2(ofs); P1 += 2;
201
202 if (ofs >= MSZIP_BLOCK)
203 throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block");
204
205 // check magic word
206 uint16_t magic = *((uint16_t*)P1);
207 AI_SWAP2(magic); P1 += 2;
208
209 if (magic != MSZIP_MAGIC)
210 throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header");
211
212 // and advance to the next offset
213 P1 += ofs;
214 est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size
215 }
216
217 // Allocate storage and terminating zero and do the actual uncompressing
218 uncompressed.resize(est_out + 1);
219 char* out = &uncompressed.front();
220 while (mP + 3 < mEnd)
221 {
222 uint16_t ofs = *((uint16_t*)mP);
223 AI_SWAP2(ofs);
224 mP += 4;
225
226 if (mP + ofs > mEnd + 2) {
227 throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
228 }
229
230 // push data to the stream
231 stream.next_in = (Bytef*)mP;
232 stream.avail_in = ofs;
233 stream.next_out = (Bytef*)out;
234 stream.avail_out = MSZIP_BLOCK;
235
236 // and decompress the data ....
237 int ret = ::inflate( &stream, Z_SYNC_FLUSH );
238 if (ret != Z_OK && ret != Z_STREAM_END)
239 throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
240
241 ::inflateReset( &stream );
242 ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out );
243
244 // and advance to the next offset
245 out += MSZIP_BLOCK - stream.avail_out;
246 mP += ofs;
247 }
248
249 // terminate zlib
250 ::inflateEnd(&stream);
251
252 // ok, update pointers to point to the uncompressed file data
253 mP = &uncompressed[0];
254 mEnd = out;
255
256 // FIXME: we don't need the compressed data anymore, could release
257 // it already for better memory usage. Consider breaking const-co.
258 ASSIMP_LOG_INFO("Successfully decompressed MSZIP-compressed file");
259 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
260 }
261 else
262 {
263 // start reading here
264 ReadUntilEndOfLine();
265 }
266
267 mScene = new Scene;
268 ParseFile();
269
270 // filter the imported hierarchy for some degenerated cases
271 if( mScene->mRootNode) {
272 FilterHierarchy( mScene->mRootNode);
273 }
274 }
275
276 // ------------------------------------------------------------------------------------------------
277 // Destructor. Destroys all imported data along with it
~XFileParser()278 XFileParser::~XFileParser()
279 {
280 // kill everything we created
281 delete mScene;
282 }
283
284 // ------------------------------------------------------------------------------------------------
ParseFile()285 void XFileParser::ParseFile()
286 {
287 bool running = true;
288 while( running )
289 {
290 // read name of next object
291 std::string objectName = GetNextToken();
292 if (objectName.length() == 0)
293 break;
294
295 // parse specific object
296 if( objectName == "template")
297 ParseDataObjectTemplate();
298 else
299 if( objectName == "Frame")
300 ParseDataObjectFrame( NULL);
301 else
302 if( objectName == "Mesh")
303 {
304 // some meshes have no frames at all
305 Mesh* mesh = new Mesh;
306 ParseDataObjectMesh( mesh);
307 mScene->mGlobalMeshes.push_back( mesh);
308 } else
309 if( objectName == "AnimTicksPerSecond")
310 ParseDataObjectAnimTicksPerSecond();
311 else
312 if( objectName == "AnimationSet")
313 ParseDataObjectAnimationSet();
314 else
315 if( objectName == "Material")
316 {
317 // Material outside of a mesh or node
318 Material material;
319 ParseDataObjectMaterial( &material);
320 mScene->mGlobalMaterials.push_back( material);
321 } else
322 if( objectName == "}")
323 {
324 // whatever?
325 ASSIMP_LOG_WARN("} found in dataObject");
326 } else
327 {
328 // unknown format
329 ASSIMP_LOG_WARN("Unknown data object in animation of .x file");
330 ParseUnknownDataObject();
331 }
332 }
333 }
334
335 // ------------------------------------------------------------------------------------------------
ParseDataObjectTemplate()336 void XFileParser::ParseDataObjectTemplate()
337 {
338 // parse a template data object. Currently not stored.
339 std::string name;
340 readHeadOfDataObject( &name);
341
342 // read GUID
343 std::string guid = GetNextToken();
344
345 // read and ignore data members
346 bool running = true;
347 while ( running )
348 {
349 std::string s = GetNextToken();
350
351 if( s == "}")
352 break;
353
354 if( s.length() == 0)
355 ThrowException( "Unexpected end of file reached while parsing template definition");
356 }
357 }
358
359 // ------------------------------------------------------------------------------------------------
ParseDataObjectFrame(Node * pParent)360 void XFileParser::ParseDataObjectFrame( Node* pParent)
361 {
362 // A coordinate frame, or "frame of reference." The Frame template
363 // is open and can contain any object. The Direct3D extensions (D3DX)
364 // mesh-loading functions recognize Mesh, FrameTransformMatrix, and
365 // Frame template instances as child objects when loading a Frame
366 // instance.
367 std::string name;
368 readHeadOfDataObject(&name);
369
370 // create a named node and place it at its parent, if given
371 Node* node = new Node( pParent);
372 node->mName = name;
373 if( pParent)
374 {
375 pParent->mChildren.push_back( node);
376 } else
377 {
378 // there might be multiple root nodes
379 if( mScene->mRootNode != NULL)
380 {
381 // place a dummy root if not there
382 if( mScene->mRootNode->mName != "$dummy_root")
383 {
384 Node* exroot = mScene->mRootNode;
385 mScene->mRootNode = new Node( NULL);
386 mScene->mRootNode->mName = "$dummy_root";
387 mScene->mRootNode->mChildren.push_back( exroot);
388 exroot->mParent = mScene->mRootNode;
389 }
390 // put the new node as its child instead
391 mScene->mRootNode->mChildren.push_back( node);
392 node->mParent = mScene->mRootNode;
393 } else
394 {
395 // it's the first node imported. place it as root
396 mScene->mRootNode = node;
397 }
398 }
399
400 // Now inside a frame.
401 // read tokens until closing brace is reached.
402 bool running = true;
403 while ( running )
404 {
405 std::string objectName = GetNextToken();
406 if (objectName.size() == 0)
407 ThrowException( "Unexpected end of file reached while parsing frame");
408
409 if( objectName == "}")
410 break; // frame finished
411 else
412 if( objectName == "Frame")
413 ParseDataObjectFrame( node); // child frame
414 else
415 if( objectName == "FrameTransformMatrix")
416 ParseDataObjectTransformationMatrix( node->mTrafoMatrix);
417 else
418 if( objectName == "Mesh")
419 {
420 Mesh* mesh = new Mesh(name);
421 node->mMeshes.push_back( mesh);
422 ParseDataObjectMesh( mesh);
423 } else
424 {
425 ASSIMP_LOG_WARN("Unknown data object in frame in x file");
426 ParseUnknownDataObject();
427 }
428 }
429 }
430
431 // ------------------------------------------------------------------------------------------------
ParseDataObjectTransformationMatrix(aiMatrix4x4 & pMatrix)432 void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix)
433 {
434 // read header, we're not interested if it has a name
435 readHeadOfDataObject();
436
437 // read its components
438 pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat();
439 pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat();
440 pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat();
441 pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat();
442 pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat();
443 pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat();
444 pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat();
445 pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat();
446
447 // trailing symbols
448 CheckForSemicolon();
449 CheckForClosingBrace();
450 }
451
452 // ------------------------------------------------------------------------------------------------
ParseDataObjectMesh(Mesh * pMesh)453 void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
454 {
455 std::string name;
456 readHeadOfDataObject( &name);
457
458 // read vertex count
459 unsigned int numVertices = ReadInt();
460 pMesh->mPositions.resize( numVertices);
461
462 // read vertices
463 for( unsigned int a = 0; a < numVertices; a++)
464 pMesh->mPositions[a] = ReadVector3();
465
466 // read position faces
467 unsigned int numPosFaces = ReadInt();
468 pMesh->mPosFaces.resize( numPosFaces);
469 for( unsigned int a = 0; a < numPosFaces; ++a) {
470 // read indices
471 unsigned int numIndices = ReadInt();
472 Face& face = pMesh->mPosFaces[a];
473 for (unsigned int b = 0; b < numIndices; ++b) {
474 const int idx( ReadInt() );
475 if ( static_cast<unsigned int>( idx ) <= numVertices ) {
476 face.mIndices.push_back( idx );
477 }
478 }
479 TestForSeparator();
480 }
481
482 // here, other data objects may follow
483 bool running = true;
484 while ( running ) {
485 std::string objectName = GetNextToken();
486
487 if( objectName.empty() )
488 ThrowException( "Unexpected end of file while parsing mesh structure");
489 else
490 if( objectName == "}")
491 break; // mesh finished
492 else
493 if( objectName == "MeshNormals")
494 ParseDataObjectMeshNormals( pMesh);
495 else
496 if( objectName == "MeshTextureCoords")
497 ParseDataObjectMeshTextureCoords( pMesh);
498 else
499 if( objectName == "MeshVertexColors")
500 ParseDataObjectMeshVertexColors( pMesh);
501 else
502 if( objectName == "MeshMaterialList")
503 ParseDataObjectMeshMaterialList( pMesh);
504 else
505 if( objectName == "VertexDuplicationIndices")
506 ParseUnknownDataObject(); // we'll ignore vertex duplication indices
507 else
508 if( objectName == "XSkinMeshHeader")
509 ParseDataObjectSkinMeshHeader( pMesh);
510 else
511 if( objectName == "SkinWeights")
512 ParseDataObjectSkinWeights( pMesh);
513 else
514 {
515 ASSIMP_LOG_WARN("Unknown data object in mesh in x file");
516 ParseUnknownDataObject();
517 }
518 }
519 }
520
521 // ------------------------------------------------------------------------------------------------
ParseDataObjectSkinWeights(Mesh * pMesh)522 void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) {
523 if ( nullptr == pMesh ) {
524 return;
525 }
526 readHeadOfDataObject();
527
528 std::string transformNodeName;
529 GetNextTokenAsString( transformNodeName);
530
531 pMesh->mBones.push_back( Bone());
532 Bone& bone = pMesh->mBones.back();
533 bone.mName = transformNodeName;
534
535 // read vertex weights
536 unsigned int numWeights = ReadInt();
537 bone.mWeights.reserve( numWeights);
538
539 for( unsigned int a = 0; a < numWeights; a++)
540 {
541 BoneWeight weight;
542 weight.mVertex = ReadInt();
543 bone.mWeights.push_back( weight);
544 }
545
546 // read vertex weights
547 for( unsigned int a = 0; a < numWeights; a++)
548 bone.mWeights[a].mWeight = ReadFloat();
549
550 // read matrix offset
551 bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat();
552 bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat();
553 bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat();
554 bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat();
555 bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat();
556 bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat();
557 bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat();
558 bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat();
559
560 CheckForSemicolon();
561 CheckForClosingBrace();
562 }
563
564 // ------------------------------------------------------------------------------------------------
ParseDataObjectSkinMeshHeader(Mesh *)565 void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* /*pMesh*/ )
566 {
567 readHeadOfDataObject();
568
569 /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt();
570 /*unsigned int maxSkinWeightsPerFace =*/ ReadInt();
571 /*unsigned int numBonesInMesh = */ReadInt();
572
573 CheckForClosingBrace();
574 }
575
576 // ------------------------------------------------------------------------------------------------
ParseDataObjectMeshNormals(Mesh * pMesh)577 void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh)
578 {
579 readHeadOfDataObject();
580
581 // read count
582 unsigned int numNormals = ReadInt();
583 pMesh->mNormals.resize( numNormals);
584
585 // read normal vectors
586 for( unsigned int a = 0; a < numNormals; ++a) {
587 pMesh->mNormals[a] = ReadVector3();
588 }
589
590 // read normal indices
591 unsigned int numFaces = ReadInt();
592 if( numFaces != pMesh->mPosFaces.size()) {
593 ThrowException( "Normal face count does not match vertex face count.");
594 }
595
596 // do not crah when no face definitions are there
597 if (numFaces > 0) {
598 // normal face creation
599 pMesh->mNormFaces.resize( numFaces );
600 for( unsigned int a = 0; a < numFaces; ++a ) {
601 unsigned int numIndices = ReadInt();
602 pMesh->mNormFaces[a] = Face();
603 Face& face = pMesh->mNormFaces[a];
604 for( unsigned int b = 0; b < numIndices; ++b ) {
605 face.mIndices.push_back( ReadInt());
606 }
607
608 TestForSeparator();
609 }
610 }
611
612 CheckForClosingBrace();
613 }
614
615 // ------------------------------------------------------------------------------------------------
ParseDataObjectMeshTextureCoords(Mesh * pMesh)616 void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh)
617 {
618 readHeadOfDataObject();
619 if( pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS)
620 ThrowException( "Too many sets of texture coordinates");
621
622 std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++];
623
624 unsigned int numCoords = ReadInt();
625 if( numCoords != pMesh->mPositions.size())
626 ThrowException( "Texture coord count does not match vertex count");
627
628 coords.resize( numCoords);
629 for( unsigned int a = 0; a < numCoords; a++)
630 coords[a] = ReadVector2();
631
632 CheckForClosingBrace();
633 }
634
635 // ------------------------------------------------------------------------------------------------
ParseDataObjectMeshVertexColors(Mesh * pMesh)636 void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh)
637 {
638 readHeadOfDataObject();
639 if( pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS)
640 ThrowException( "Too many colorsets");
641 std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++];
642
643 unsigned int numColors = ReadInt();
644 if( numColors != pMesh->mPositions.size())
645 ThrowException( "Vertex color count does not match vertex count");
646
647 colors.resize( numColors, aiColor4D( 0, 0, 0, 1));
648 for( unsigned int a = 0; a < numColors; a++)
649 {
650 unsigned int index = ReadInt();
651 if( index >= pMesh->mPositions.size())
652 ThrowException( "Vertex color index out of bounds");
653
654 colors[index] = ReadRGBA();
655 // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma.
656 // Ignore gracefully.
657 if( !mIsBinaryFormat)
658 {
659 FindNextNoneWhiteSpace();
660 if( *mP == ';' || *mP == ',')
661 mP++;
662 }
663 }
664
665 CheckForClosingBrace();
666 }
667
668 // ------------------------------------------------------------------------------------------------
ParseDataObjectMeshMaterialList(Mesh * pMesh)669 void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
670 {
671 readHeadOfDataObject();
672
673 // read material count
674 /*unsigned int numMaterials =*/ ReadInt();
675 // read non triangulated face material index count
676 unsigned int numMatIndices = ReadInt();
677
678 // some models have a material index count of 1... to be able to read them we
679 // replicate this single material index on every face
680 if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
681 ThrowException( "Per-Face material index count does not match face count.");
682
683 // read per-face material indices
684 for( unsigned int a = 0; a < numMatIndices; a++)
685 pMesh->mFaceMaterials.push_back( ReadInt());
686
687 // in version 03.02, the face indices end with two semicolons.
688 // commented out version check, as version 03.03 exported from blender also has 2 semicolons
689 if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
690 {
691 if(mP < mEnd && *mP == ';')
692 ++mP;
693 }
694
695 // if there was only a single material index, replicate it on all faces
696 while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
697 pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front());
698
699 // read following data objects
700 bool running = true;
701 while ( running )
702 {
703 std::string objectName = GetNextToken();
704 if( objectName.size() == 0)
705 ThrowException( "Unexpected end of file while parsing mesh material list.");
706 else
707 if( objectName == "}")
708 break; // material list finished
709 else
710 if( objectName == "{")
711 {
712 // template materials
713 std::string matName = GetNextToken();
714 Material material;
715 material.mIsReference = true;
716 material.mName = matName;
717 pMesh->mMaterials.push_back( material);
718
719 CheckForClosingBrace(); // skip }
720 } else
721 if( objectName == "Material")
722 {
723 pMesh->mMaterials.push_back( Material());
724 ParseDataObjectMaterial( &pMesh->mMaterials.back());
725 } else
726 if( objectName == ";")
727 {
728 // ignore
729 } else
730 {
731 ASSIMP_LOG_WARN("Unknown data object in material list in x file");
732 ParseUnknownDataObject();
733 }
734 }
735 }
736
737 // ------------------------------------------------------------------------------------------------
ParseDataObjectMaterial(Material * pMaterial)738 void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
739 {
740 std::string matName;
741 readHeadOfDataObject( &matName);
742 if( matName.empty())
743 matName = std::string( "material") + to_string( mLineNumber );
744 pMaterial->mName = matName;
745 pMaterial->mIsReference = false;
746
747 // read material values
748 pMaterial->mDiffuse = ReadRGBA();
749 pMaterial->mSpecularExponent = ReadFloat();
750 pMaterial->mSpecular = ReadRGB();
751 pMaterial->mEmissive = ReadRGB();
752
753 // read other data objects
754 bool running = true;
755 while ( running )
756 {
757 std::string objectName = GetNextToken();
758 if( objectName.size() == 0)
759 ThrowException( "Unexpected end of file while parsing mesh material");
760 else
761 if( objectName == "}")
762 break; // material finished
763 else
764 if( objectName == "TextureFilename" || objectName == "TextureFileName")
765 {
766 // some exporters write "TextureFileName" instead.
767 std::string texname;
768 ParseDataObjectTextureFilename( texname);
769 pMaterial->mTextures.push_back( TexEntry( texname));
770 } else
771 if( objectName == "NormalmapFilename" || objectName == "NormalmapFileName")
772 {
773 // one exporter writes out the normal map in a separate filename tag
774 std::string texname;
775 ParseDataObjectTextureFilename( texname);
776 pMaterial->mTextures.push_back( TexEntry( texname, true));
777 } else
778 {
779 ASSIMP_LOG_WARN("Unknown data object in material in x file");
780 ParseUnknownDataObject();
781 }
782 }
783 }
784
785 // ------------------------------------------------------------------------------------------------
ParseDataObjectAnimTicksPerSecond()786 void XFileParser::ParseDataObjectAnimTicksPerSecond()
787 {
788 readHeadOfDataObject();
789 mScene->mAnimTicksPerSecond = ReadInt();
790 CheckForClosingBrace();
791 }
792
793 // ------------------------------------------------------------------------------------------------
ParseDataObjectAnimationSet()794 void XFileParser::ParseDataObjectAnimationSet()
795 {
796 std::string animName;
797 readHeadOfDataObject( &animName);
798
799 Animation* anim = new Animation;
800 mScene->mAnims.push_back( anim);
801 anim->mName = animName;
802
803 bool running = true;
804 while ( running )
805 {
806 std::string objectName = GetNextToken();
807 if( objectName.length() == 0)
808 ThrowException( "Unexpected end of file while parsing animation set.");
809 else
810 if( objectName == "}")
811 break; // animation set finished
812 else
813 if( objectName == "Animation")
814 ParseDataObjectAnimation( anim);
815 else
816 {
817 ASSIMP_LOG_WARN("Unknown data object in animation set in x file");
818 ParseUnknownDataObject();
819 }
820 }
821 }
822
823 // ------------------------------------------------------------------------------------------------
ParseDataObjectAnimation(Animation * pAnim)824 void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
825 {
826 readHeadOfDataObject();
827 AnimBone* banim = new AnimBone;
828 pAnim->mAnims.push_back( banim);
829
830 bool running = true;
831 while( running )
832 {
833 std::string objectName = GetNextToken();
834
835 if( objectName.length() == 0)
836 ThrowException( "Unexpected end of file while parsing animation.");
837 else
838 if( objectName == "}")
839 break; // animation finished
840 else
841 if( objectName == "AnimationKey")
842 ParseDataObjectAnimationKey( banim);
843 else
844 if( objectName == "AnimationOptions")
845 ParseUnknownDataObject(); // not interested
846 else
847 if( objectName == "{")
848 {
849 // read frame name
850 banim->mBoneName = GetNextToken();
851 CheckForClosingBrace();
852 } else
853 {
854 ASSIMP_LOG_WARN("Unknown data object in animation in x file");
855 ParseUnknownDataObject();
856 }
857 }
858 }
859
860 // ------------------------------------------------------------------------------------------------
ParseDataObjectAnimationKey(AnimBone * pAnimBone)861 void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone)
862 {
863 readHeadOfDataObject();
864
865 // read key type
866 unsigned int keyType = ReadInt();
867
868 // read number of keys
869 unsigned int numKeys = ReadInt();
870
871 for( unsigned int a = 0; a < numKeys; a++)
872 {
873 // read time
874 unsigned int time = ReadInt();
875
876 // read keys
877 switch( keyType)
878 {
879 case 0: // rotation quaternion
880 {
881 // read count
882 if( ReadInt() != 4)
883 ThrowException( "Invalid number of arguments for quaternion key in animation");
884
885 aiQuatKey key;
886 key.mTime = double( time);
887 key.mValue.w = ReadFloat();
888 key.mValue.x = ReadFloat();
889 key.mValue.y = ReadFloat();
890 key.mValue.z = ReadFloat();
891 pAnimBone->mRotKeys.push_back( key);
892
893 CheckForSemicolon();
894 break;
895 }
896
897 case 1: // scale vector
898 case 2: // position vector
899 {
900 // read count
901 if( ReadInt() != 3)
902 ThrowException( "Invalid number of arguments for vector key in animation");
903
904 aiVectorKey key;
905 key.mTime = double( time);
906 key.mValue = ReadVector3();
907
908 if( keyType == 2)
909 pAnimBone->mPosKeys.push_back( key);
910 else
911 pAnimBone->mScaleKeys.push_back( key);
912
913 break;
914 }
915
916 case 3: // combined transformation matrix
917 case 4: // denoted both as 3 or as 4
918 {
919 // read count
920 if( ReadInt() != 16)
921 ThrowException( "Invalid number of arguments for matrix key in animation");
922
923 // read matrix
924 MatrixKey key;
925 key.mTime = double( time);
926 key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat();
927 key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat();
928 key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat();
929 key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat();
930 key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat();
931 key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat();
932 key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat();
933 key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat();
934 pAnimBone->mTrafoKeys.push_back( key);
935
936 CheckForSemicolon();
937 break;
938 }
939
940 default:
941 ThrowException( format() << "Unknown key type " << keyType << " in animation." );
942 break;
943 } // end switch
944
945 // key separator
946 CheckForSeparator();
947 }
948
949 CheckForClosingBrace();
950 }
951
952 // ------------------------------------------------------------------------------------------------
ParseDataObjectTextureFilename(std::string & pName)953 void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
954 {
955 readHeadOfDataObject();
956 GetNextTokenAsString( pName);
957 CheckForClosingBrace();
958
959 // FIX: some files (e.g. AnimationTest.x) have "" as texture file name
960 if (!pName.length())
961 {
962 ASSIMP_LOG_WARN("Length of texture file name is zero. Skipping this texture.");
963 }
964
965 // some exporters write double backslash paths out. We simply replace them if we find them
966 while( pName.find( "\\\\") != std::string::npos)
967 pName.replace( pName.find( "\\\\"), 2, "\\");
968 }
969
970 // ------------------------------------------------------------------------------------------------
ParseUnknownDataObject()971 void XFileParser::ParseUnknownDataObject()
972 {
973 // find opening delimiter
974 bool running = true;
975 while( running )
976 {
977 std::string t = GetNextToken();
978 if( t.length() == 0)
979 ThrowException( "Unexpected end of file while parsing unknown segment.");
980
981 if( t == "{")
982 break;
983 }
984
985 unsigned int counter = 1;
986
987 // parse until closing delimiter
988 while( counter > 0)
989 {
990 std::string t = GetNextToken();
991
992 if( t.length() == 0)
993 ThrowException( "Unexpected end of file while parsing unknown segment.");
994
995 if( t == "{")
996 ++counter;
997 else
998 if( t == "}")
999 --counter;
1000 }
1001 }
1002
1003 // ------------------------------------------------------------------------------------------------
1004 //! checks for closing curly brace
CheckForClosingBrace()1005 void XFileParser::CheckForClosingBrace()
1006 {
1007 if( GetNextToken() != "}")
1008 ThrowException( "Closing brace expected.");
1009 }
1010
1011 // ------------------------------------------------------------------------------------------------
1012 //! checks for one following semicolon
CheckForSemicolon()1013 void XFileParser::CheckForSemicolon()
1014 {
1015 if( mIsBinaryFormat)
1016 return;
1017
1018 if( GetNextToken() != ";")
1019 ThrowException( "Semicolon expected.");
1020 }
1021
1022 // ------------------------------------------------------------------------------------------------
1023 //! checks for a separator char, either a ',' or a ';'
CheckForSeparator()1024 void XFileParser::CheckForSeparator()
1025 {
1026 if( mIsBinaryFormat)
1027 return;
1028
1029 std::string token = GetNextToken();
1030 if( token != "," && token != ";")
1031 ThrowException( "Separator character (';' or ',') expected.");
1032 }
1033
1034 // ------------------------------------------------------------------------------------------------
1035 // tests and possibly consumes a separator char, but does nothing if there was no separator
TestForSeparator()1036 void XFileParser::TestForSeparator()
1037 {
1038 if( mIsBinaryFormat)
1039 return;
1040
1041 FindNextNoneWhiteSpace();
1042 if( mP >= mEnd)
1043 return;
1044
1045 // test and skip
1046 if( *mP == ';' || *mP == ',')
1047 mP++;
1048 }
1049
1050 // ------------------------------------------------------------------------------------------------
readHeadOfDataObject(std::string * poName)1051 void XFileParser::readHeadOfDataObject( std::string* poName)
1052 {
1053 std::string nameOrBrace = GetNextToken();
1054 if( nameOrBrace != "{")
1055 {
1056 if( poName)
1057 *poName = nameOrBrace;
1058
1059 if ( GetNextToken() != "{" ) {
1060 delete mScene;
1061 ThrowException( "Opening brace expected." );
1062 }
1063 }
1064 }
1065
1066 // ------------------------------------------------------------------------------------------------
GetNextToken()1067 std::string XFileParser::GetNextToken() {
1068 std::string s;
1069
1070 // process binary-formatted file
1071 if( mIsBinaryFormat) {
1072 // in binary mode it will only return NAME and STRING token
1073 // and (correctly) skip over other tokens.
1074 if ( mEnd - mP < 2 ) {
1075 return s;
1076 }
1077 unsigned int tok = ReadBinWord();
1078 unsigned int len;
1079
1080 // standalone tokens
1081 switch( tok ) {
1082 case 1: {
1083 // name token
1084 if ( mEnd - mP < 4 ) {
1085 return s;
1086 }
1087 len = ReadBinDWord();
1088 const int bounds = int( mEnd - mP );
1089 const int iLen = int( len );
1090 if ( iLen < 0 ) {
1091 return s;
1092 }
1093 if ( bounds < iLen ) {
1094 return s;
1095 }
1096 s = std::string( mP, len );
1097 mP += len;
1098 }
1099 return s;
1100
1101 case 2:
1102 // string token
1103 if( mEnd - mP < 4) return s;
1104 len = ReadBinDWord();
1105 if( mEnd - mP < int(len)) return s;
1106 s = std::string(mP, len);
1107 mP += (len + 2);
1108 return s;
1109 case 3:
1110 // integer token
1111 mP += 4;
1112 return "<integer>";
1113 case 5:
1114 // GUID token
1115 mP += 16;
1116 return "<guid>";
1117 case 6:
1118 if( mEnd - mP < 4) return s;
1119 len = ReadBinDWord();
1120 mP += (len * 4);
1121 return "<int_list>";
1122 case 7:
1123 if( mEnd - mP < 4) return s;
1124 len = ReadBinDWord();
1125 mP += (len * mBinaryFloatSize);
1126 return "<flt_list>";
1127 case 0x0a:
1128 return "{";
1129 case 0x0b:
1130 return "}";
1131 case 0x0c:
1132 return "(";
1133 case 0x0d:
1134 return ")";
1135 case 0x0e:
1136 return "[";
1137 case 0x0f:
1138 return "]";
1139 case 0x10:
1140 return "<";
1141 case 0x11:
1142 return ">";
1143 case 0x12:
1144 return ".";
1145 case 0x13:
1146 return ",";
1147 case 0x14:
1148 return ";";
1149 case 0x1f:
1150 return "template";
1151 case 0x28:
1152 return "WORD";
1153 case 0x29:
1154 return "DWORD";
1155 case 0x2a:
1156 return "FLOAT";
1157 case 0x2b:
1158 return "DOUBLE";
1159 case 0x2c:
1160 return "CHAR";
1161 case 0x2d:
1162 return "UCHAR";
1163 case 0x2e:
1164 return "SWORD";
1165 case 0x2f:
1166 return "SDWORD";
1167 case 0x30:
1168 return "void";
1169 case 0x31:
1170 return "string";
1171 case 0x32:
1172 return "unicode";
1173 case 0x33:
1174 return "cstring";
1175 case 0x34:
1176 return "array";
1177 }
1178 }
1179 // process text-formatted file
1180 else
1181 {
1182 FindNextNoneWhiteSpace();
1183 if( mP >= mEnd)
1184 return s;
1185
1186 while( (mP < mEnd) && !isspace( (unsigned char) *mP))
1187 {
1188 // either keep token delimiters when already holding a token, or return if first valid char
1189 if( *mP == ';' || *mP == '}' || *mP == '{' || *mP == ',')
1190 {
1191 if( !s.size())
1192 s.append( mP++, 1);
1193 break; // stop for delimiter
1194 }
1195 s.append( mP++, 1);
1196 }
1197 }
1198 return s;
1199 }
1200
1201 // ------------------------------------------------------------------------------------------------
FindNextNoneWhiteSpace()1202 void XFileParser::FindNextNoneWhiteSpace()
1203 {
1204 if( mIsBinaryFormat)
1205 return;
1206
1207 bool running = true;
1208 while( running )
1209 {
1210 while( mP < mEnd && isspace( (unsigned char) *mP))
1211 {
1212 if( *mP == '\n')
1213 mLineNumber++;
1214 ++mP;
1215 }
1216
1217 if( mP >= mEnd)
1218 return;
1219
1220 // check if this is a comment
1221 if( (mP[0] == '/' && mP[1] == '/') || mP[0] == '#')
1222 ReadUntilEndOfLine();
1223 else
1224 break;
1225 }
1226 }
1227
1228 // ------------------------------------------------------------------------------------------------
GetNextTokenAsString(std::string & poString)1229 void XFileParser::GetNextTokenAsString( std::string& poString)
1230 {
1231 if( mIsBinaryFormat)
1232 {
1233 poString = GetNextToken();
1234 return;
1235 }
1236
1237 FindNextNoneWhiteSpace();
1238 if ( mP >= mEnd ) {
1239 delete mScene;
1240 ThrowException( "Unexpected end of file while parsing string" );
1241 }
1242
1243 if ( *mP != '"' ) {
1244 delete mScene;
1245 ThrowException( "Expected quotation mark." );
1246 }
1247 ++mP;
1248
1249 while( mP < mEnd && *mP != '"')
1250 poString.append( mP++, 1);
1251
1252 if ( mP >= mEnd - 1 ) {
1253 delete mScene;
1254 ThrowException( "Unexpected end of file while parsing string" );
1255 }
1256
1257 if ( mP[ 1 ] != ';' || mP[ 0 ] != '"' ) {
1258 delete mScene;
1259 ThrowException( "Expected quotation mark and semicolon at the end of a string." );
1260 }
1261 mP+=2;
1262 }
1263
1264 // ------------------------------------------------------------------------------------------------
ReadUntilEndOfLine()1265 void XFileParser::ReadUntilEndOfLine()
1266 {
1267 if( mIsBinaryFormat)
1268 return;
1269
1270 while( mP < mEnd)
1271 {
1272 if( *mP == '\n' || *mP == '\r')
1273 {
1274 ++mP; mLineNumber++;
1275 return;
1276 }
1277
1278 ++mP;
1279 }
1280 }
1281
1282 // ------------------------------------------------------------------------------------------------
ReadBinWord()1283 unsigned short XFileParser::ReadBinWord()
1284 {
1285 ai_assert(mEnd - mP >= 2);
1286 const unsigned char* q = (const unsigned char*) mP;
1287 unsigned short tmp = q[0] | (q[1] << 8);
1288 mP += 2;
1289 return tmp;
1290 }
1291
1292 // ------------------------------------------------------------------------------------------------
ReadBinDWord()1293 unsigned int XFileParser::ReadBinDWord() {
1294 ai_assert(mEnd - mP >= 4);
1295
1296 const unsigned char* q = (const unsigned char*) mP;
1297 unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
1298 mP += 4;
1299 return tmp;
1300 }
1301
1302 // ------------------------------------------------------------------------------------------------
ReadInt()1303 unsigned int XFileParser::ReadInt()
1304 {
1305 if( mIsBinaryFormat)
1306 {
1307 if( mBinaryNumCount == 0 && mEnd - mP >= 2)
1308 {
1309 unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
1310 if( tmp == 0x06 && mEnd - mP >= 4) // array of ints follows
1311 mBinaryNumCount = ReadBinDWord();
1312 else // single int follows
1313 mBinaryNumCount = 1;
1314 }
1315
1316 --mBinaryNumCount;
1317 const size_t len( mEnd - mP );
1318 if ( len >= 4) {
1319 return ReadBinDWord();
1320 } else {
1321 mP = mEnd;
1322 return 0;
1323 }
1324 } else
1325 {
1326 FindNextNoneWhiteSpace();
1327
1328 // TODO: consider using strtol10 instead???
1329
1330 // check preceding minus sign
1331 bool isNegative = false;
1332 if( *mP == '-')
1333 {
1334 isNegative = true;
1335 mP++;
1336 }
1337
1338 // at least one digit expected
1339 if( !isdigit( *mP))
1340 ThrowException( "Number expected.");
1341
1342 // read digits
1343 unsigned int number = 0;
1344 while( mP < mEnd)
1345 {
1346 if( !isdigit( *mP))
1347 break;
1348 number = number * 10 + (*mP - 48);
1349 mP++;
1350 }
1351
1352 CheckForSeparator();
1353
1354 return isNegative ? ((unsigned int) -int( number)) : number;
1355 }
1356 }
1357
1358 // ------------------------------------------------------------------------------------------------
ReadFloat()1359 ai_real XFileParser::ReadFloat()
1360 {
1361 if( mIsBinaryFormat)
1362 {
1363 if( mBinaryNumCount == 0 && mEnd - mP >= 2)
1364 {
1365 unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
1366 if( tmp == 0x07 && mEnd - mP >= 4) // array of floats following
1367 mBinaryNumCount = ReadBinDWord();
1368 else // single float following
1369 mBinaryNumCount = 1;
1370 }
1371
1372 --mBinaryNumCount;
1373 if( mBinaryFloatSize == 8) {
1374 if( mEnd - mP >= 8) {
1375 double res;
1376 ::memcpy( &res, mP, 8 );
1377 mP += 8;
1378 const ai_real result( static_cast<ai_real>( res ) );
1379 return result;
1380 } else {
1381 mP = mEnd;
1382 return 0;
1383 }
1384 } else {
1385 if( mEnd - mP >= 4) {
1386 ai_real result;
1387 ::memcpy( &result, mP, 4 );
1388 mP += 4;
1389 return result;
1390 } else {
1391 mP = mEnd;
1392 return 0;
1393 }
1394 }
1395 }
1396
1397 // text version
1398 FindNextNoneWhiteSpace();
1399 // check for various special strings to allow reading files from faulty exporters
1400 // I mean you, Blender!
1401 // Reading is safe because of the terminating zero
1402 if( strncmp( mP, "-1.#IND00", 9) == 0 || strncmp( mP, "1.#IND00", 8) == 0)
1403 {
1404 mP += 9;
1405 CheckForSeparator();
1406 return 0.0;
1407 } else
1408 if( strncmp( mP, "1.#QNAN0", 8) == 0)
1409 {
1410 mP += 8;
1411 CheckForSeparator();
1412 return 0.0;
1413 }
1414
1415 ai_real result = 0.0;
1416 mP = fast_atoreal_move<ai_real>( mP, result);
1417
1418 CheckForSeparator();
1419
1420 return result;
1421 }
1422
1423 // ------------------------------------------------------------------------------------------------
ReadVector2()1424 aiVector2D XFileParser::ReadVector2()
1425 {
1426 aiVector2D vector;
1427 vector.x = ReadFloat();
1428 vector.y = ReadFloat();
1429 TestForSeparator();
1430
1431 return vector;
1432 }
1433
1434 // ------------------------------------------------------------------------------------------------
ReadVector3()1435 aiVector3D XFileParser::ReadVector3()
1436 {
1437 aiVector3D vector;
1438 vector.x = ReadFloat();
1439 vector.y = ReadFloat();
1440 vector.z = ReadFloat();
1441 TestForSeparator();
1442
1443 return vector;
1444 }
1445
1446 // ------------------------------------------------------------------------------------------------
ReadRGBA()1447 aiColor4D XFileParser::ReadRGBA()
1448 {
1449 aiColor4D color;
1450 color.r = ReadFloat();
1451 color.g = ReadFloat();
1452 color.b = ReadFloat();
1453 color.a = ReadFloat();
1454 TestForSeparator();
1455
1456 return color;
1457 }
1458
1459 // ------------------------------------------------------------------------------------------------
ReadRGB()1460 aiColor3D XFileParser::ReadRGB()
1461 {
1462 aiColor3D color;
1463 color.r = ReadFloat();
1464 color.g = ReadFloat();
1465 color.b = ReadFloat();
1466 TestForSeparator();
1467
1468 return color;
1469 }
1470
1471 // ------------------------------------------------------------------------------------------------
1472 // Throws an exception with a line number and the given text.
ThrowException(const std::string & pText)1473 AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) {
1474 if ( mIsBinaryFormat ) {
1475 throw DeadlyImportError( pText );
1476 } else {
1477 throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << pText );
1478 }
1479 }
1480
1481 // ------------------------------------------------------------------------------------------------
1482 // Filters the imported hierarchy for some degenerated cases that some exporters produce.
FilterHierarchy(XFile::Node * pNode)1483 void XFileParser::FilterHierarchy( XFile::Node* pNode)
1484 {
1485 // if the node has just a single unnamed child containing a mesh, remove
1486 // the anonymous node between. The 3DSMax kwXport plugin seems to produce this
1487 // mess in some cases
1488 if( pNode->mChildren.size() == 1 && pNode->mMeshes.empty() )
1489 {
1490 XFile::Node* child = pNode->mChildren.front();
1491 if( child->mName.length() == 0 && child->mMeshes.size() > 0)
1492 {
1493 // transfer its meshes to us
1494 for( unsigned int a = 0; a < child->mMeshes.size(); a++)
1495 pNode->mMeshes.push_back( child->mMeshes[a]);
1496 child->mMeshes.clear();
1497
1498 // transfer the transform as well
1499 pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix;
1500
1501 // then kill it
1502 delete child;
1503 pNode->mChildren.clear();
1504 }
1505 }
1506
1507 // recurse
1508 for( unsigned int a = 0; a < pNode->mChildren.size(); a++)
1509 FilterHierarchy( pNode->mChildren[a]);
1510 }
1511
1512 #endif // !! ASSIMP_BUILD_NO_X_IMPORTER
1513