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