vrshoot

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

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