diff options
Diffstat (limited to 'src/3rdparty/assimp/code/XFileParser.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/XFileParser.cpp | 2355 |
1 files changed, 1183 insertions, 1172 deletions
diff --git a/src/3rdparty/assimp/code/XFileParser.cpp b/src/3rdparty/assimp/code/XFileParser.cpp index a89b12d95..a820955a8 100644 --- a/src/3rdparty/assimp/code/XFileParser.cpp +++ b/src/3rdparty/assimp/code/XFileParser.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2016, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -25,39 +25,46 @@ conditions are met: derived from this software without specific prior written permission of the assimp team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file Implementation of the XFile parser helper class */ -#include "AssimpPCH.h" + #ifndef ASSIMP_BUILD_NO_X_IMPORTER #include "XFileParser.h" #include "XFileHelper.h" #include "fast_atof.h" +#include "Exceptional.h" +#include "TinyFormatter.h" +#include "ByteSwapper.h" +#include "StringUtils.h" +#include <assimp/DefaultLogger.hpp> + using namespace Assimp; using namespace Assimp::XFile; +using namespace Assimp::Formatter; #ifndef ASSIMP_BUILD_NO_COMPRESSED_X -# ifdef ASSIMP_BUILD_NO_OWN_ZLIB -# include <zlib.h> -# else -# include "../contrib/zlib/zlib.h" -# endif +# ifdef ASSIMP_BUILD_NO_OWN_ZLIB +# include <zlib.h> +# else +# include "../contrib/zlib/zlib.h" +# endif // Magic identifier for MSZIP compressed data #define MSZIP_MAGIC 0x4B43 @@ -65,950 +72,954 @@ using namespace Assimp::XFile; // ------------------------------------------------------------------------------------------------ // Dummy memory wrappers for use with zlib -static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) { - return ::operator new(items*size); +static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) { + return ::operator new(items*size); } -static void dummy_free (void* /*opaque*/, void* address) { - return ::operator delete(address); +static void dummy_free (void* /*opaque*/, void* address) { + return ::operator delete(address); } #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X // ------------------------------------------------------------------------------------------------ -// Constructor. Creates a data structure out of the XFile given in the memory block. +// Constructor. Creates a data structure out of the XFile given in the memory block. XFileParser::XFileParser( const std::vector<char>& pBuffer) { - mMajorVersion = mMinorVersion = 0; - mIsBinaryFormat = false; - mBinaryNumCount = 0; - P = End = NULL; - mLineNumber = 0; - mScene = NULL; - - // vector to store uncompressed file for INFLATE'd X files - std::vector<char> uncompressed; - - // set up memory pointers - P = &pBuffer.front(); - End = P + pBuffer.size() - 1; - - // check header - if( strncmp( P, "xof ", 4) != 0) - throw DeadlyImportError( "Header mismatch, file is not an XFile."); - - // read version. It comes in a four byte format such as "0302" - mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); - mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); - - bool compressed = false; - - // txt - pure ASCII text format - if( strncmp( P + 8, "txt ", 4) == 0) - mIsBinaryFormat = false; - - // bin - Binary format - else if( strncmp( P + 8, "bin ", 4) == 0) - mIsBinaryFormat = true; - - // tzip - Inflate compressed text format - else if( strncmp( P + 8, "tzip", 4) == 0) - { - mIsBinaryFormat = false; - compressed = true; - } - // bzip - Inflate compressed binary format - else if( strncmp( P + 8, "bzip", 4) == 0) - { - mIsBinaryFormat = true; - compressed = true; - } - else ThrowException( boost::str(boost::format("Unsupported xfile format '%c%c%c%c'") - % P[8] % P[9] % P[10] % P[11])); - - // float size - mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 - + (unsigned int)(P[13] - 48) * 100 - + (unsigned int)(P[14] - 48) * 10 - + (unsigned int)(P[15] - 48); - - if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) - ThrowException( boost::str( boost::format( "Unknown float size %1% specified in xfile header.") - % mBinaryFloatSize)); - - // The x format specifies size in bits, but we work in bytes - mBinaryFloatSize /= 8; - - P += 16; - - // If this is a compressed X file, apply the inflate algorithm to it - if (compressed) - { + mMajorVersion = mMinorVersion = 0; + mIsBinaryFormat = false; + mBinaryNumCount = 0; + P = End = NULL; + mLineNumber = 0; + mScene = NULL; + + // vector to store uncompressed file for INFLATE'd X files + std::vector<char> uncompressed; + + // set up memory pointers + P = &pBuffer.front(); + End = P + pBuffer.size() - 1; + + // check header + if( strncmp( P, "xof ", 4) != 0) + throw DeadlyImportError( "Header mismatch, file is not an XFile."); + + // read version. It comes in a four byte format such as "0302" + mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); + mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); + + bool compressed = false; + + // txt - pure ASCII text format + if( strncmp( P + 8, "txt ", 4) == 0) + mIsBinaryFormat = false; + + // bin - Binary format + else if( strncmp( P + 8, "bin ", 4) == 0) + mIsBinaryFormat = true; + + // tzip - Inflate compressed text format + else if( strncmp( P + 8, "tzip", 4) == 0) + { + mIsBinaryFormat = false; + compressed = true; + } + // bzip - Inflate compressed binary format + else if( strncmp( P + 8, "bzip", 4) == 0) + { + mIsBinaryFormat = true; + compressed = true; + } + else ThrowException( format() << "Unsupported xfile format '" << + P[8] << P[9] << P[10] << P[11] << "'"); + + // float size + mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 + + (unsigned int)(P[13] - 48) * 100 + + (unsigned int)(P[14] - 48) * 10 + + (unsigned int)(P[15] - 48); + + if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) + ThrowException( format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header." ); + + // The x format specifies size in bits, but we work in bytes + mBinaryFloatSize /= 8; + + P += 16; + + // If this is a compressed X file, apply the inflate algorithm to it + if (compressed) + { #ifdef ASSIMP_BUILD_NO_COMPRESSED_X - throw DeadlyImportError("Assimp was built without compressed X support"); + throw DeadlyImportError("Assimp was built without compressed X support"); #else - /* /////////////////////////////////////////////////////////////////////// - * COMPRESSED X FILE FORMAT - * /////////////////////////////////////////////////////////////////////// - * [xhead] - * 2 major - * 2 minor - * 4 type // bzip,tzip - * [mszip_master_head] - * 4 unkn // checksum? - * 2 unkn // flags? (seems to be constant) - * [mszip_head] - * 2 ofs // offset to next section - * 2 magic // 'CK' - * ... ofs bytes of data - * ... next mszip_head - * - * http://www.kdedevelopers.org/node/3181 has been very helpful. - * /////////////////////////////////////////////////////////////////////// - */ - - // build a zlib stream - z_stream stream; - stream.opaque = NULL; - stream.zalloc = &dummy_alloc; - stream.zfree = &dummy_free; - stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII); - - // initialize the inflation algorithm - ::inflateInit2(&stream, -MAX_WBITS); - - // skip unknown data (checksum, flags?) - P += 6; - - // First find out how much storage we'll need. Count sections. - const char* P1 = P; - unsigned int est_out = 0; - - while (P1 + 3 < End) - { - // read next offset - uint16_t ofs = *((uint16_t*)P1); - AI_SWAP2(ofs); P1 += 2; - - if (ofs >= MSZIP_BLOCK) - throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block"); - - // check magic word - uint16_t magic = *((uint16_t*)P1); - AI_SWAP2(magic); P1 += 2; - - if (magic != MSZIP_MAGIC) - throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header"); - - // and advance to the next offset - P1 += ofs; - est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size - } - - // Allocate storage and terminating zero and do the actual uncompressing - uncompressed.resize(est_out + 1); - char* out = &uncompressed.front(); - while (P + 3 < End) - { - uint16_t ofs = *((uint16_t*)P); - AI_SWAP2(ofs); - P += 4; - - // push data to the stream - stream.next_in = (Bytef*)P; - stream.avail_in = ofs; - stream.next_out = (Bytef*)out; - stream.avail_out = MSZIP_BLOCK; - - // and decompress the data .... - int ret = ::inflate( &stream, Z_SYNC_FLUSH ); - if (ret != Z_OK && ret != Z_STREAM_END) - throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data"); - - ::inflateReset( &stream ); - ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out ); - - // and advance to the next offset - out += MSZIP_BLOCK - stream.avail_out; - P += ofs; - } - - // terminate zlib - ::inflateEnd(&stream); - - // ok, update pointers to point to the uncompressed file data - P = &uncompressed[0]; - End = out; - - // FIXME: we don't need the compressed data anymore, could release - // it already for better memory usage. Consider breaking const-co. - DefaultLogger::get()->info("Successfully decompressed MSZIP-compressed file"); + /* /////////////////////////////////////////////////////////////////////// + * COMPRESSED X FILE FORMAT + * /////////////////////////////////////////////////////////////////////// + * [xhead] + * 2 major + * 2 minor + * 4 type // bzip,tzip + * [mszip_master_head] + * 4 unkn // checksum? + * 2 unkn // flags? (seems to be constant) + * [mszip_head] + * 2 ofs // offset to next section + * 2 magic // 'CK' + * ... ofs bytes of data + * ... next mszip_head + * + * http://www.kdedevelopers.org/node/3181 has been very helpful. + * /////////////////////////////////////////////////////////////////////// + */ + + // build a zlib stream + z_stream stream; + stream.opaque = NULL; + stream.zalloc = &dummy_alloc; + stream.zfree = &dummy_free; + stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII); + + // initialize the inflation algorithm + ::inflateInit2(&stream, -MAX_WBITS); + + // skip unknown data (checksum, flags?) + P += 6; + + // First find out how much storage we'll need. Count sections. + const char* P1 = P; + unsigned int est_out = 0; + + while (P1 + 3 < End) + { + // read next offset + uint16_t ofs = *((uint16_t*)P1); + AI_SWAP2(ofs); P1 += 2; + + if (ofs >= MSZIP_BLOCK) + throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block"); + + // check magic word + uint16_t magic = *((uint16_t*)P1); + AI_SWAP2(magic); P1 += 2; + + if (magic != MSZIP_MAGIC) + throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header"); + + // and advance to the next offset + P1 += ofs; + est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size + } + + // Allocate storage and terminating zero and do the actual uncompressing + uncompressed.resize(est_out + 1); + char* out = &uncompressed.front(); + while (P + 3 < End) + { + uint16_t ofs = *((uint16_t*)P); + AI_SWAP2(ofs); + P += 4; + + if (P + ofs > End + 2) { + throw DeadlyImportError("X: Unexpected EOF in compressed chunk"); + } + + // push data to the stream + stream.next_in = (Bytef*)P; + stream.avail_in = ofs; + stream.next_out = (Bytef*)out; + stream.avail_out = MSZIP_BLOCK; + + // and decompress the data .... + int ret = ::inflate( &stream, Z_SYNC_FLUSH ); + if (ret != Z_OK && ret != Z_STREAM_END) + throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data"); + + ::inflateReset( &stream ); + ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out ); + + // and advance to the next offset + out += MSZIP_BLOCK - stream.avail_out; + P += ofs; + } + + // terminate zlib + ::inflateEnd(&stream); + + // ok, update pointers to point to the uncompressed file data + P = &uncompressed[0]; + End = out; + + // FIXME: we don't need the compressed data anymore, could release + // it already for better memory usage. Consider breaking const-co. + DefaultLogger::get()->info("Successfully decompressed MSZIP-compressed file"); #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X - } - else - { - // start reading here - ReadUntilEndOfLine(); - } - - mScene = new Scene; - ParseFile(); - - // filter the imported hierarchy for some degenerated cases - if( mScene->mRootNode) { - FilterHierarchy( mScene->mRootNode); - } + } + else + { + // start reading here + ReadUntilEndOfLine(); + } + + mScene = new Scene; + ParseFile(); + + // filter the imported hierarchy for some degenerated cases + if( mScene->mRootNode) { + FilterHierarchy( mScene->mRootNode); + } } // ------------------------------------------------------------------------------------------------ -// Destructor. Destroys all imported data along with it +// Destructor. Destroys all imported data along with it XFileParser::~XFileParser() { - // kill everything we created - delete mScene; + // kill everything we created + delete mScene; } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseFile() { - bool running = true; - while( running ) - { - // read name of next object - std::string objectName = GetNextToken(); - if (objectName.length() == 0) - break; - - // parse specific object - if( objectName == "template") - ParseDataObjectTemplate(); - else - if( objectName == "Frame") - ParseDataObjectFrame( NULL); - else - if( objectName == "Mesh") - { - // some meshes have no frames at all - Mesh* mesh = new Mesh; - ParseDataObjectMesh( mesh); - mScene->mGlobalMeshes.push_back( mesh); - } else - if( objectName == "AnimTicksPerSecond") - ParseDataObjectAnimTicksPerSecond(); - else - if( objectName == "AnimationSet") - ParseDataObjectAnimationSet(); - else - if( objectName == "Material") - { - // Material outside of a mesh or node - Material material; - ParseDataObjectMaterial( &material); - mScene->mGlobalMaterials.push_back( material); - } else - if( objectName == "}") - { - // whatever? - DefaultLogger::get()->warn("} found in dataObject"); - } else - { - // unknown format - DefaultLogger::get()->warn("Unknown data object in animation of .x file"); - ParseUnknownDataObject(); - } - } + bool running = true; + while( running ) + { + // read name of next object + std::string objectName = GetNextToken(); + if (objectName.length() == 0) + break; + + // parse specific object + if( objectName == "template") + ParseDataObjectTemplate(); + else + if( objectName == "Frame") + ParseDataObjectFrame( NULL); + else + if( objectName == "Mesh") + { + // some meshes have no frames at all + Mesh* mesh = new Mesh; + ParseDataObjectMesh( mesh); + mScene->mGlobalMeshes.push_back( mesh); + } else + if( objectName == "AnimTicksPerSecond") + ParseDataObjectAnimTicksPerSecond(); + else + if( objectName == "AnimationSet") + ParseDataObjectAnimationSet(); + else + if( objectName == "Material") + { + // Material outside of a mesh or node + Material material; + ParseDataObjectMaterial( &material); + mScene->mGlobalMaterials.push_back( material); + } else + if( objectName == "}") + { + // whatever? + DefaultLogger::get()->warn("} found in dataObject"); + } else + { + // unknown format + DefaultLogger::get()->warn("Unknown data object in animation of .x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectTemplate() { - // parse a template data object. Currently not stored. - std::string name; - readHeadOfDataObject( &name); + // parse a template data object. Currently not stored. + std::string name; + readHeadOfDataObject( &name); - // read GUID - std::string guid = GetNextToken(); + // read GUID + std::string guid = GetNextToken(); - // read and ignore data members - bool running = true; - while ( running ) - { - std::string s = GetNextToken(); + // read and ignore data members + bool running = true; + while ( running ) + { + std::string s = GetNextToken(); - if( s == "}") - break; + if( s == "}") + break; - if( s.length() == 0) - ThrowException( "Unexpected end of file reached while parsing template definition"); - } + if( s.length() == 0) + ThrowException( "Unexpected end of file reached while parsing template definition"); + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectFrame( Node* pParent) { - // A coordinate frame, or "frame of reference." The Frame template - // is open and can contain any object. The Direct3D extensions (D3DX) - // mesh-loading functions recognize Mesh, FrameTransformMatrix, and - // Frame template instances as child objects when loading a Frame - // instance. - std::string name; - readHeadOfDataObject(&name); - - // create a named node and place it at its parent, if given - Node* node = new Node( pParent); - node->mName = name; - if( pParent) - { - pParent->mChildren.push_back( node); - } else - { - // there might be multiple root nodes - if( mScene->mRootNode != NULL) - { - // place a dummy root if not there - if( mScene->mRootNode->mName != "$dummy_root") - { - Node* exroot = mScene->mRootNode; - mScene->mRootNode = new Node( NULL); - mScene->mRootNode->mName = "$dummy_root"; - mScene->mRootNode->mChildren.push_back( exroot); - exroot->mParent = mScene->mRootNode; - } - // put the new node as its child instead - mScene->mRootNode->mChildren.push_back( node); - node->mParent = mScene->mRootNode; - } else - { - // it's the first node imported. place it as root - mScene->mRootNode = node; - } - } - - // Now inside a frame. - // read tokens until closing brace is reached. - bool running = true; - while ( running ) - { - std::string objectName = GetNextToken(); - if (objectName.size() == 0) - ThrowException( "Unexpected end of file reached while parsing frame"); - - if( objectName == "}") - break; // frame finished - else - if( objectName == "Frame") - ParseDataObjectFrame( node); // child frame - else - if( objectName == "FrameTransformMatrix") - ParseDataObjectTransformationMatrix( node->mTrafoMatrix); - else - if( objectName == "Mesh") - { - Mesh* mesh = new Mesh; - node->mMeshes.push_back( mesh); - ParseDataObjectMesh( mesh); - } else - { - DefaultLogger::get()->warn("Unknown data object in frame in x file"); - ParseUnknownDataObject(); - } - } + // A coordinate frame, or "frame of reference." The Frame template + // is open and can contain any object. The Direct3D extensions (D3DX) + // mesh-loading functions recognize Mesh, FrameTransformMatrix, and + // Frame template instances as child objects when loading a Frame + // instance. + std::string name; + readHeadOfDataObject(&name); + + // create a named node and place it at its parent, if given + Node* node = new Node( pParent); + node->mName = name; + if( pParent) + { + pParent->mChildren.push_back( node); + } else + { + // there might be multiple root nodes + if( mScene->mRootNode != NULL) + { + // place a dummy root if not there + if( mScene->mRootNode->mName != "$dummy_root") + { + Node* exroot = mScene->mRootNode; + mScene->mRootNode = new Node( NULL); + mScene->mRootNode->mName = "$dummy_root"; + mScene->mRootNode->mChildren.push_back( exroot); + exroot->mParent = mScene->mRootNode; + } + // put the new node as its child instead + mScene->mRootNode->mChildren.push_back( node); + node->mParent = mScene->mRootNode; + } else + { + // it's the first node imported. place it as root + mScene->mRootNode = node; + } + } + + // Now inside a frame. + // read tokens until closing brace is reached. + bool running = true; + while ( running ) + { + std::string objectName = GetNextToken(); + if (objectName.size() == 0) + ThrowException( "Unexpected end of file reached while parsing frame"); + + if( objectName == "}") + break; // frame finished + else + if( objectName == "Frame") + ParseDataObjectFrame( node); // child frame + else + if( objectName == "FrameTransformMatrix") + ParseDataObjectTransformationMatrix( node->mTrafoMatrix); + else + if( objectName == "Mesh") + { + Mesh* mesh = new Mesh(name); + node->mMeshes.push_back( mesh); + ParseDataObjectMesh( mesh); + } else + { + DefaultLogger::get()->warn("Unknown data object in frame in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix) { - // read header, we're not interested if it has a name - readHeadOfDataObject(); - - // read its components - pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat(); - pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat(); - pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat(); - pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat(); - pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat(); - pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat(); - pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat(); - pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat(); - - // trailing symbols - CheckForSemicolon(); - CheckForClosingBrace(); + // read header, we're not interested if it has a name + readHeadOfDataObject(); + + // read its components + pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat(); + pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat(); + pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat(); + pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat(); + pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat(); + pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat(); + pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat(); + pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat(); + + // trailing symbols + CheckForSemicolon(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) { - std::string name; - readHeadOfDataObject( &name); - - // read vertex count - unsigned int numVertices = ReadInt(); - pMesh->mPositions.resize( numVertices); - - // read vertices - for( unsigned int a = 0; a < numVertices; a++) - pMesh->mPositions[a] = ReadVector3(); - - // read position faces - unsigned int numPosFaces = ReadInt(); - pMesh->mPosFaces.resize( numPosFaces); - for( unsigned int a = 0; a < numPosFaces; a++) - { - unsigned int numIndices = ReadInt(); - if( numIndices < 3) - ThrowException( boost::str( boost::format( "Invalid index count %1% for face %2%.") % numIndices % a)); - - // read indices - Face& face = pMesh->mPosFaces[a]; - for( unsigned int b = 0; b < numIndices; b++) - face.mIndices.push_back( ReadInt()); - TestForSeparator(); - } - - // here, other data objects may follow - bool running = true; - while ( running ) - { - std::string objectName = GetNextToken(); - - if( objectName.size() == 0) - ThrowException( "Unexpected end of file while parsing mesh structure"); - else - if( objectName == "}") - break; // mesh finished - else - if( objectName == "MeshNormals") - ParseDataObjectMeshNormals( pMesh); - else - if( objectName == "MeshTextureCoords") - ParseDataObjectMeshTextureCoords( pMesh); - else - if( objectName == "MeshVertexColors") - ParseDataObjectMeshVertexColors( pMesh); - else - if( objectName == "MeshMaterialList") - ParseDataObjectMeshMaterialList( pMesh); - else - if( objectName == "VertexDuplicationIndices") - ParseUnknownDataObject(); // we'll ignore vertex duplication indices - else - if( objectName == "XSkinMeshHeader") - ParseDataObjectSkinMeshHeader( pMesh); - else - if( objectName == "SkinWeights") - ParseDataObjectSkinWeights( pMesh); - else - { - DefaultLogger::get()->warn("Unknown data object in mesh in x file"); - ParseUnknownDataObject(); - } - } + std::string name; + readHeadOfDataObject( &name); + + // read vertex count + unsigned int numVertices = ReadInt(); + pMesh->mPositions.resize( numVertices); + + // read vertices + for( unsigned int a = 0; a < numVertices; a++) + pMesh->mPositions[a] = ReadVector3(); + + // read position faces + unsigned int numPosFaces = ReadInt(); + pMesh->mPosFaces.resize( numPosFaces); + for( unsigned int a = 0; a < numPosFaces; a++) + { + unsigned int numIndices = ReadInt(); + if( numIndices < 3) { + ThrowException( format() << "Invalid index count " << numIndices << " for face " << a << "." ); + } + + // read indices + Face& face = pMesh->mPosFaces[a]; + for( unsigned int b = 0; b < numIndices; b++) + face.mIndices.push_back( ReadInt()); + TestForSeparator(); + } + + // here, other data objects may follow + bool running = true; + while ( running ) + { + std::string objectName = GetNextToken(); + + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh structure"); + else + if( objectName == "}") + break; // mesh finished + else + if( objectName == "MeshNormals") + ParseDataObjectMeshNormals( pMesh); + else + if( objectName == "MeshTextureCoords") + ParseDataObjectMeshTextureCoords( pMesh); + else + if( objectName == "MeshVertexColors") + ParseDataObjectMeshVertexColors( pMesh); + else + if( objectName == "MeshMaterialList") + ParseDataObjectMeshMaterialList( pMesh); + else + if( objectName == "VertexDuplicationIndices") + ParseUnknownDataObject(); // we'll ignore vertex duplication indices + else + if( objectName == "XSkinMeshHeader") + ParseDataObjectSkinMeshHeader( pMesh); + else + if( objectName == "SkinWeights") + ParseDataObjectSkinWeights( pMesh); + else + { + DefaultLogger::get()->warn("Unknown data object in mesh in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) { - readHeadOfDataObject(); - - std::string transformNodeName; - GetNextTokenAsString( transformNodeName); - - pMesh->mBones.push_back( Bone()); - Bone& bone = pMesh->mBones.back(); - bone.mName = transformNodeName; - - // read vertex weights - unsigned int numWeights = ReadInt(); - bone.mWeights.reserve( numWeights); - - for( unsigned int a = 0; a < numWeights; a++) - { - BoneWeight weight; - weight.mVertex = ReadInt(); - bone.mWeights.push_back( weight); - } - - // read vertex weights - for( unsigned int a = 0; a < numWeights; a++) - bone.mWeights[a].mWeight = ReadFloat(); - - // read matrix offset - bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat(); - bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat(); - bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat(); - bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat(); - bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat(); - bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat(); - bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat(); - bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat(); - - CheckForSemicolon(); - CheckForClosingBrace(); + readHeadOfDataObject(); + + std::string transformNodeName; + GetNextTokenAsString( transformNodeName); + + pMesh->mBones.push_back( Bone()); + Bone& bone = pMesh->mBones.back(); + bone.mName = transformNodeName; + + // read vertex weights + unsigned int numWeights = ReadInt(); + bone.mWeights.reserve( numWeights); + + for( unsigned int a = 0; a < numWeights; a++) + { + BoneWeight weight; + weight.mVertex = ReadInt(); + bone.mWeights.push_back( weight); + } + + // read vertex weights + for( unsigned int a = 0; a < numWeights; a++) + bone.mWeights[a].mWeight = ReadFloat(); + + // read matrix offset + bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat(); + bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat(); + bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat(); + bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat(); + bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat(); + bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat(); + bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat(); + bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat(); + + CheckForSemicolon(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* /*pMesh*/ ) { - readHeadOfDataObject(); + readHeadOfDataObject(); - /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt(); - /*unsigned int maxSkinWeightsPerFace =*/ ReadInt(); - /*unsigned int numBonesInMesh = */ReadInt(); + /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt(); + /*unsigned int maxSkinWeightsPerFace =*/ ReadInt(); + /*unsigned int numBonesInMesh = */ReadInt(); - CheckForClosingBrace(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh) { - readHeadOfDataObject(); + readHeadOfDataObject(); - // read count - unsigned int numNormals = ReadInt(); - pMesh->mNormals.resize( numNormals); + // read count + unsigned int numNormals = ReadInt(); + pMesh->mNormals.resize( numNormals); - // read normal vectors - for( unsigned int a = 0; a < numNormals; a++) - pMesh->mNormals[a] = ReadVector3(); + // read normal vectors + for( unsigned int a = 0; a < numNormals; a++) + pMesh->mNormals[a] = ReadVector3(); - // read normal indices - unsigned int numFaces = ReadInt(); - if( numFaces != pMesh->mPosFaces.size()) - ThrowException( "Normal face count does not match vertex face count."); + // read normal indices + unsigned int numFaces = ReadInt(); + if( numFaces != pMesh->mPosFaces.size()) + ThrowException( "Normal face count does not match vertex face count."); - for( unsigned int a = 0; a < numFaces; a++) - { - unsigned int numIndices = ReadInt(); - pMesh->mNormFaces.push_back( Face()); - Face& face = pMesh->mNormFaces.back(); + for( unsigned int a = 0; a < numFaces; a++) + { + unsigned int numIndices = ReadInt(); + pMesh->mNormFaces.push_back( Face()); + Face& face = pMesh->mNormFaces.back(); - for( unsigned int b = 0; b < numIndices; b++) - face.mIndices.push_back( ReadInt()); + for( unsigned int b = 0; b < numIndices; b++) + face.mIndices.push_back( ReadInt()); - TestForSeparator(); - } + TestForSeparator(); + } - CheckForClosingBrace(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh) { - readHeadOfDataObject(); - if( pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS) - ThrowException( "Too many sets of texture coordinates"); + readHeadOfDataObject(); + if( pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS) + ThrowException( "Too many sets of texture coordinates"); - std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++]; + std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++]; - unsigned int numCoords = ReadInt(); - if( numCoords != pMesh->mPositions.size()) - ThrowException( "Texture coord count does not match vertex count"); + unsigned int numCoords = ReadInt(); + if( numCoords != pMesh->mPositions.size()) + ThrowException( "Texture coord count does not match vertex count"); - coords.resize( numCoords); - for( unsigned int a = 0; a < numCoords; a++) - coords[a] = ReadVector2(); + coords.resize( numCoords); + for( unsigned int a = 0; a < numCoords; a++) + coords[a] = ReadVector2(); - CheckForClosingBrace(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh) { - readHeadOfDataObject(); - if( pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS) - ThrowException( "Too many colorsets"); - std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++]; - - unsigned int numColors = ReadInt(); - if( numColors != pMesh->mPositions.size()) - ThrowException( "Vertex color count does not match vertex count"); - - colors.resize( numColors, aiColor4D( 0, 0, 0, 1)); - for( unsigned int a = 0; a < numColors; a++) - { - unsigned int index = ReadInt(); - if( index >= pMesh->mPositions.size()) - ThrowException( "Vertex color index out of bounds"); - - colors[index] = ReadRGBA(); - // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma. - // Ignore gracefully. - if( !mIsBinaryFormat) - { - FindNextNoneWhiteSpace(); - if( *P == ';' || *P == ',') - P++; - } - } - - CheckForClosingBrace(); + readHeadOfDataObject(); + if( pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS) + ThrowException( "Too many colorsets"); + std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++]; + + unsigned int numColors = ReadInt(); + if( numColors != pMesh->mPositions.size()) + ThrowException( "Vertex color count does not match vertex count"); + + colors.resize( numColors, aiColor4D( 0, 0, 0, 1)); + for( unsigned int a = 0; a < numColors; a++) + { + unsigned int index = ReadInt(); + if( index >= pMesh->mPositions.size()) + ThrowException( "Vertex color index out of bounds"); + + colors[index] = ReadRGBA(); + // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma. + // Ignore gracefully. + if( !mIsBinaryFormat) + { + FindNextNoneWhiteSpace(); + if( *P == ';' || *P == ',') + P++; + } + } + + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh) { - readHeadOfDataObject(); - - // read material count - /*unsigned int numMaterials =*/ ReadInt(); - // read non triangulated face material index count - unsigned int numMatIndices = ReadInt(); - - // some models have a material index count of 1... to be able to read them we - // replicate this single material index on every face - if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1) - ThrowException( "Per-Face material index count does not match face count."); - - // read per-face material indices - for( unsigned int a = 0; a < numMatIndices; a++) - pMesh->mFaceMaterials.push_back( ReadInt()); - - // in version 03.02, the face indices end with two semicolons. - // commented out version check, as version 03.03 exported from blender also has 2 semicolons - if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) - { - if(P < End && *P == ';') - ++P; - } - - // if there was only a single material index, replicate it on all faces - while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size()) - pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front()); - - // read following data objects - bool running = true; - while ( running ) - { - std::string objectName = GetNextToken(); - if( objectName.size() == 0) - ThrowException( "Unexpected end of file while parsing mesh material list."); - else - if( objectName == "}") - break; // material list finished - else - if( objectName == "{") - { - // template materials - std::string matName = GetNextToken(); - Material material; - material.mIsReference = true; - material.mName = matName; - pMesh->mMaterials.push_back( material); - - CheckForClosingBrace(); // skip } - } else - if( objectName == "Material") - { - pMesh->mMaterials.push_back( Material()); - ParseDataObjectMaterial( &pMesh->mMaterials.back()); - } else - if( objectName == ";") - { - // ignore - } else - { - DefaultLogger::get()->warn("Unknown data object in material list in x file"); - ParseUnknownDataObject(); - } - } + readHeadOfDataObject(); + + // read material count + /*unsigned int numMaterials =*/ ReadInt(); + // read non triangulated face material index count + unsigned int numMatIndices = ReadInt(); + + // some models have a material index count of 1... to be able to read them we + // replicate this single material index on every face + if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1) + ThrowException( "Per-Face material index count does not match face count."); + + // read per-face material indices + for( unsigned int a = 0; a < numMatIndices; a++) + pMesh->mFaceMaterials.push_back( ReadInt()); + + // in version 03.02, the face indices end with two semicolons. + // commented out version check, as version 03.03 exported from blender also has 2 semicolons + if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) + { + if(P < End && *P == ';') + ++P; + } + + // if there was only a single material index, replicate it on all faces + while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size()) + pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front()); + + // read following data objects + bool running = true; + while ( running ) + { + std::string objectName = GetNextToken(); + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh material list."); + else + if( objectName == "}") + break; // material list finished + else + if( objectName == "{") + { + // template materials + std::string matName = GetNextToken(); + Material material; + material.mIsReference = true; + material.mName = matName; + pMesh->mMaterials.push_back( material); + + CheckForClosingBrace(); // skip } + } else + if( objectName == "Material") + { + pMesh->mMaterials.push_back( Material()); + ParseDataObjectMaterial( &pMesh->mMaterials.back()); + } else + if( objectName == ";") + { + // ignore + } else + { + DefaultLogger::get()->warn("Unknown data object in material list in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMaterial( Material* pMaterial) { - std::string matName; - readHeadOfDataObject( &matName); - if( matName.empty()) - matName = std::string( "material") + boost::lexical_cast<std::string>( mLineNumber); - pMaterial->mName = matName; - pMaterial->mIsReference = false; - - // read material values - pMaterial->mDiffuse = ReadRGBA(); - pMaterial->mSpecularExponent = ReadFloat(); - pMaterial->mSpecular = ReadRGB(); - pMaterial->mEmissive = ReadRGB(); - - // read other data objects - bool running = true; - while ( running ) - { - std::string objectName = GetNextToken(); - if( objectName.size() == 0) - ThrowException( "Unexpected end of file while parsing mesh material"); - else - if( objectName == "}") - break; // material finished - else - if( objectName == "TextureFilename" || objectName == "TextureFileName") - { - // some exporters write "TextureFileName" instead. - std::string texname; - ParseDataObjectTextureFilename( texname); - pMaterial->mTextures.push_back( TexEntry( texname)); - } else - if( objectName == "NormalmapFilename" || objectName == "NormalmapFileName") - { - // one exporter writes out the normal map in a separate filename tag - std::string texname; - ParseDataObjectTextureFilename( texname); - pMaterial->mTextures.push_back( TexEntry( texname, true)); - } else - { - DefaultLogger::get()->warn("Unknown data object in material in x file"); - ParseUnknownDataObject(); - } - } + std::string matName; + readHeadOfDataObject( &matName); + if( matName.empty()) + matName = std::string( "material") + std::to_string( mLineNumber ); + pMaterial->mName = matName; + pMaterial->mIsReference = false; + + // read material values + pMaterial->mDiffuse = ReadRGBA(); + pMaterial->mSpecularExponent = ReadFloat(); + pMaterial->mSpecular = ReadRGB(); + pMaterial->mEmissive = ReadRGB(); + + // read other data objects + bool running = true; + while ( running ) + { + std::string objectName = GetNextToken(); + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh material"); + else + if( objectName == "}") + break; // material finished + else + if( objectName == "TextureFilename" || objectName == "TextureFileName") + { + // some exporters write "TextureFileName" instead. + std::string texname; + ParseDataObjectTextureFilename( texname); + pMaterial->mTextures.push_back( TexEntry( texname)); + } else + if( objectName == "NormalmapFilename" || objectName == "NormalmapFileName") + { + // one exporter writes out the normal map in a separate filename tag + std::string texname; + ParseDataObjectTextureFilename( texname); + pMaterial->mTextures.push_back( TexEntry( texname, true)); + } else + { + DefaultLogger::get()->warn("Unknown data object in material in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectAnimTicksPerSecond() { - readHeadOfDataObject(); - mScene->mAnimTicksPerSecond = ReadInt(); - CheckForClosingBrace(); + readHeadOfDataObject(); + mScene->mAnimTicksPerSecond = ReadInt(); + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectAnimationSet() { - std::string animName; - readHeadOfDataObject( &animName); - - Animation* anim = new Animation; - mScene->mAnims.push_back( anim); - anim->mName = animName; - - bool running = true; - while ( running ) - { - std::string objectName = GetNextToken(); - if( objectName.length() == 0) - ThrowException( "Unexpected end of file while parsing animation set."); - else - if( objectName == "}") - break; // animation set finished - else - if( objectName == "Animation") - ParseDataObjectAnimation( anim); - else - { - DefaultLogger::get()->warn("Unknown data object in animation set in x file"); - ParseUnknownDataObject(); - } - } + std::string animName; + readHeadOfDataObject( &animName); + + Animation* anim = new Animation; + mScene->mAnims.push_back( anim); + anim->mName = animName; + + bool running = true; + while ( running ) + { + std::string objectName = GetNextToken(); + if( objectName.length() == 0) + ThrowException( "Unexpected end of file while parsing animation set."); + else + if( objectName == "}") + break; // animation set finished + else + if( objectName == "Animation") + ParseDataObjectAnimation( anim); + else + { + DefaultLogger::get()->warn("Unknown data object in animation set in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectAnimation( Animation* pAnim) { - readHeadOfDataObject(); - AnimBone* banim = new AnimBone; - pAnim->mAnims.push_back( banim); - - bool running = true; - while( running ) - { - std::string objectName = GetNextToken(); - - if( objectName.length() == 0) - ThrowException( "Unexpected end of file while parsing animation."); - else - if( objectName == "}") - break; // animation finished - else - if( objectName == "AnimationKey") - ParseDataObjectAnimationKey( banim); - else - if( objectName == "AnimationOptions") - ParseUnknownDataObject(); // not interested - else - if( objectName == "{") - { - // read frame name - banim->mBoneName = GetNextToken(); - CheckForClosingBrace(); - } else - { - DefaultLogger::get()->warn("Unknown data object in animation in x file"); - ParseUnknownDataObject(); - } - } + readHeadOfDataObject(); + AnimBone* banim = new AnimBone; + pAnim->mAnims.push_back( banim); + + bool running = true; + while( running ) + { + std::string objectName = GetNextToken(); + + if( objectName.length() == 0) + ThrowException( "Unexpected end of file while parsing animation."); + else + if( objectName == "}") + break; // animation finished + else + if( objectName == "AnimationKey") + ParseDataObjectAnimationKey( banim); + else + if( objectName == "AnimationOptions") + ParseUnknownDataObject(); // not interested + else + if( objectName == "{") + { + // read frame name + banim->mBoneName = GetNextToken(); + CheckForClosingBrace(); + } else + { + DefaultLogger::get()->warn("Unknown data object in animation in x file"); + ParseUnknownDataObject(); + } + } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone) { - readHeadOfDataObject(); - - // read key type - unsigned int keyType = ReadInt(); - - // read number of keys - unsigned int numKeys = ReadInt(); - - for( unsigned int a = 0; a < numKeys; a++) - { - // read time - unsigned int time = ReadInt(); - - // read keys - switch( keyType) - { - case 0: // rotation quaternion - { - // read count - if( ReadInt() != 4) - ThrowException( "Invalid number of arguments for quaternion key in animation"); - - aiQuatKey key; - key.mTime = double( time); - key.mValue.w = ReadFloat(); - key.mValue.x = ReadFloat(); - key.mValue.y = ReadFloat(); - key.mValue.z = ReadFloat(); - pAnimBone->mRotKeys.push_back( key); - - CheckForSemicolon(); - break; - } - - case 1: // scale vector - case 2: // position vector - { - // read count - if( ReadInt() != 3) - ThrowException( "Invalid number of arguments for vector key in animation"); - - aiVectorKey key; - key.mTime = double( time); - key.mValue = ReadVector3(); - - if( keyType == 2) - pAnimBone->mPosKeys.push_back( key); - else - pAnimBone->mScaleKeys.push_back( key); - - break; - } - - case 3: // combined transformation matrix - case 4: // denoted both as 3 or as 4 - { - // read count - if( ReadInt() != 16) - ThrowException( "Invalid number of arguments for matrix key in animation"); - - // read matrix - MatrixKey key; - key.mTime = double( time); - key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat(); - key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat(); - key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat(); - key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat(); - key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat(); - key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat(); - key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat(); - key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat(); - pAnimBone->mTrafoKeys.push_back( key); - - CheckForSemicolon(); - break; - } - - default: - ThrowException( boost::str( boost::format( "Unknown key type %1% in animation.") % keyType)); - break; - } // end switch - - // key separator - CheckForSeparator(); - } - - CheckForClosingBrace(); + readHeadOfDataObject(); + + // read key type + unsigned int keyType = ReadInt(); + + // read number of keys + unsigned int numKeys = ReadInt(); + + for( unsigned int a = 0; a < numKeys; a++) + { + // read time + unsigned int time = ReadInt(); + + // read keys + switch( keyType) + { + case 0: // rotation quaternion + { + // read count + if( ReadInt() != 4) + ThrowException( "Invalid number of arguments for quaternion key in animation"); + + aiQuatKey key; + key.mTime = double( time); + key.mValue.w = ReadFloat(); + key.mValue.x = ReadFloat(); + key.mValue.y = ReadFloat(); + key.mValue.z = ReadFloat(); + pAnimBone->mRotKeys.push_back( key); + + CheckForSemicolon(); + break; + } + + case 1: // scale vector + case 2: // position vector + { + // read count + if( ReadInt() != 3) + ThrowException( "Invalid number of arguments for vector key in animation"); + + aiVectorKey key; + key.mTime = double( time); + key.mValue = ReadVector3(); + + if( keyType == 2) + pAnimBone->mPosKeys.push_back( key); + else + pAnimBone->mScaleKeys.push_back( key); + + break; + } + + case 3: // combined transformation matrix + case 4: // denoted both as 3 or as 4 + { + // read count + if( ReadInt() != 16) + ThrowException( "Invalid number of arguments for matrix key in animation"); + + // read matrix + MatrixKey key; + key.mTime = double( time); + key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat(); + key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat(); + key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat(); + key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat(); + key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat(); + key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat(); + key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat(); + key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat(); + pAnimBone->mTrafoKeys.push_back( key); + + CheckForSemicolon(); + break; + } + + default: + ThrowException( format() << "Unknown key type " << keyType << " in animation." ); + break; + } // end switch + + // key separator + CheckForSeparator(); + } + + CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectTextureFilename( std::string& pName) { - readHeadOfDataObject(); - GetNextTokenAsString( pName); - CheckForClosingBrace(); - - // FIX: some files (e.g. AnimationTest.x) have "" as texture file name - if (!pName.length()) - { - DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture."); - } - - // some exporters write double backslash paths out. We simply replace them if we find them - while( pName.find( "\\\\") != std::string::npos) - pName.replace( pName.find( "\\\\"), 2, "\\"); + readHeadOfDataObject(); + GetNextTokenAsString( pName); + CheckForClosingBrace(); + + // FIX: some files (e.g. AnimationTest.x) have "" as texture file name + if (!pName.length()) + { + DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture."); + } + + // some exporters write double backslash paths out. We simply replace them if we find them + while( pName.find( "\\\\") != std::string::npos) + pName.replace( pName.find( "\\\\"), 2, "\\"); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseUnknownDataObject() { - // find opening delimiter - bool running = true; - while( running ) - { - std::string t = GetNextToken(); - if( t.length() == 0) - ThrowException( "Unexpected end of file while parsing unknown segment."); - - if( t == "{") - break; - } - - unsigned int counter = 1; - - // parse until closing delimiter - while( counter > 0) - { - std::string t = GetNextToken(); - - if( t.length() == 0) - ThrowException( "Unexpected end of file while parsing unknown segment."); - - if( t == "{") - ++counter; - else - if( t == "}") - --counter; - } + // find opening delimiter + bool running = true; + while( running ) + { + std::string t = GetNextToken(); + if( t.length() == 0) + ThrowException( "Unexpected end of file while parsing unknown segment."); + + if( t == "{") + break; + } + + unsigned int counter = 1; + + // parse until closing delimiter + while( counter > 0) + { + std::string t = GetNextToken(); + + if( t.length() == 0) + ThrowException( "Unexpected end of file while parsing unknown segment."); + + if( t == "{") + ++counter; + else + if( t == "}") + --counter; + } } // ------------------------------------------------------------------------------------------------ //! checks for closing curly brace void XFileParser::CheckForClosingBrace() { - if( GetNextToken() != "}") - ThrowException( "Closing brace expected."); + if( GetNextToken() != "}") + ThrowException( "Closing brace expected."); } // ------------------------------------------------------------------------------------------------ //! checks for one following semicolon void XFileParser::CheckForSemicolon() { - if( mIsBinaryFormat) - return; + if( mIsBinaryFormat) + return; - if( GetNextToken() != ";") - ThrowException( "Semicolon expected."); + if( GetNextToken() != ";") + ThrowException( "Semicolon expected."); } // ------------------------------------------------------------------------------------------------ //! checks for a separator char, either a ',' or a ';' void XFileParser::CheckForSeparator() { - if( mIsBinaryFormat) - return; + if( mIsBinaryFormat) + return; - std::string token = GetNextToken(); - if( token != "," && token != ";") - ThrowException( "Separator character (';' or ',') expected."); + std::string token = GetNextToken(); + if( token != "," && token != ";") + ThrowException( "Separator character (';' or ',') expected."); } // ------------------------------------------------------------------------------------------------ @@ -1030,410 +1041,410 @@ void XFileParser::TestForSeparator() // ------------------------------------------------------------------------------------------------ void XFileParser::readHeadOfDataObject( std::string* poName) { - std::string nameOrBrace = GetNextToken(); - if( nameOrBrace != "{") - { - if( poName) - *poName = nameOrBrace; - - if( GetNextToken() != "{") - ThrowException( "Opening brace expected."); - } + std::string nameOrBrace = GetNextToken(); + if( nameOrBrace != "{") + { + if( poName) + *poName = nameOrBrace; + + if( GetNextToken() != "{") + ThrowException( "Opening brace expected."); + } } // ------------------------------------------------------------------------------------------------ std::string XFileParser::GetNextToken() { - std::string s; - - // process binary-formatted file - if( mIsBinaryFormat) - { - // in binary mode it will only return NAME and STRING token - // and (correctly) skip over other tokens. - - if( End - P < 2) return s; - unsigned int tok = ReadBinWord(); - unsigned int len; - - // standalone tokens - switch( tok) - { - case 1: - // name token - if( End - P < 4) return s; - len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += len; - return s; - case 2: - // string token - if( End - P < 4) return s; - len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += (len + 2); - return s; - case 3: - // integer token - P += 4; - return "<integer>"; - case 5: - // GUID token - P += 16; - return "<guid>"; - case 6: - if( End - P < 4) return s; - len = ReadBinDWord(); - P += (len * 4); - return "<int_list>"; - case 7: - if( End - P < 4) return s; - len = ReadBinDWord(); - P += (len * mBinaryFloatSize); - return "<flt_list>"; - case 0x0a: - return "{"; - case 0x0b: - return "}"; - case 0x0c: - return "("; - case 0x0d: - return ")"; - case 0x0e: - return "["; - case 0x0f: - return "]"; - case 0x10: - return "<"; - case 0x11: - return ">"; - case 0x12: - return "."; - case 0x13: - return ","; - case 0x14: - return ";"; - case 0x1f: - return "template"; - case 0x28: - return "WORD"; - case 0x29: - return "DWORD"; - case 0x2a: - return "FLOAT"; - case 0x2b: - return "DOUBLE"; - case 0x2c: - return "CHAR"; - case 0x2d: - return "UCHAR"; - case 0x2e: - return "SWORD"; - case 0x2f: - return "SDWORD"; - case 0x30: - return "void"; - case 0x31: - return "string"; - case 0x32: - return "unicode"; - case 0x33: - return "cstring"; - case 0x34: - return "array"; - } - } - // process text-formatted file - else - { - FindNextNoneWhiteSpace(); - if( P >= End) - return s; - - while( (P < End) && !isspace( (unsigned char) *P)) - { - // either keep token delimiters when already holding a token, or return if first valid char - if( *P == ';' || *P == '}' || *P == '{' || *P == ',') - { - if( !s.size()) - s.append( P++, 1); - break; // stop for delimiter - } - s.append( P++, 1); - } - } - return s; + std::string s; + + // process binary-formatted file + if( mIsBinaryFormat) + { + // in binary mode it will only return NAME and STRING token + // and (correctly) skip over other tokens. + + if( End - P < 2) return s; + unsigned int tok = ReadBinWord(); + unsigned int len; + + // standalone tokens + switch( tok) + { + case 1: + // name token + if( End - P < 4) return s; + len = ReadBinDWord(); + if( End - P < int(len)) return s; + s = std::string(P, len); + P += len; + return s; + case 2: + // string token + if( End - P < 4) return s; + len = ReadBinDWord(); + if( End - P < int(len)) return s; + s = std::string(P, len); + P += (len + 2); + return s; + case 3: + // integer token + P += 4; + return "<integer>"; + case 5: + // GUID token + P += 16; + return "<guid>"; + case 6: + if( End - P < 4) return s; + len = ReadBinDWord(); + P += (len * 4); + return "<int_list>"; + case 7: + if( End - P < 4) return s; + len = ReadBinDWord(); + P += (len * mBinaryFloatSize); + return "<flt_list>"; + case 0x0a: + return "{"; + case 0x0b: + return "}"; + case 0x0c: + return "("; + case 0x0d: + return ")"; + case 0x0e: + return "["; + case 0x0f: + return "]"; + case 0x10: + return "<"; + case 0x11: + return ">"; + case 0x12: + return "."; + case 0x13: + return ","; + case 0x14: + return ";"; + case 0x1f: + return "template"; + case 0x28: + return "WORD"; + case 0x29: + return "DWORD"; + case 0x2a: + return "FLOAT"; + case 0x2b: + return "DOUBLE"; + case 0x2c: + return "CHAR"; + case 0x2d: + return "UCHAR"; + case 0x2e: + return "SWORD"; + case 0x2f: + return "SDWORD"; + case 0x30: + return "void"; + case 0x31: + return "string"; + case 0x32: + return "unicode"; + case 0x33: + return "cstring"; + case 0x34: + return "array"; + } + } + // process text-formatted file + else + { + FindNextNoneWhiteSpace(); + if( P >= End) + return s; + + while( (P < End) && !isspace( (unsigned char) *P)) + { + // either keep token delimiters when already holding a token, or return if first valid char + if( *P == ';' || *P == '}' || *P == '{' || *P == ',') + { + if( !s.size()) + s.append( P++, 1); + break; // stop for delimiter + } + s.append( P++, 1); + } + } + return s; } // ------------------------------------------------------------------------------------------------ void XFileParser::FindNextNoneWhiteSpace() { - if( mIsBinaryFormat) - return; - - bool running = true; - while( running ) - { - while( P < End && isspace( (unsigned char) *P)) - { - if( *P == '\n') - mLineNumber++; - ++P; - } - - if( P >= End) - return; - - // check if this is a comment - if( (P[0] == '/' && P[1] == '/') || P[0] == '#') - ReadUntilEndOfLine(); - else - break; - } + if( mIsBinaryFormat) + return; + + bool running = true; + while( running ) + { + while( P < End && isspace( (unsigned char) *P)) + { + if( *P == '\n') + mLineNumber++; + ++P; + } + + if( P >= End) + return; + + // check if this is a comment + if( (P[0] == '/' && P[1] == '/') || P[0] == '#') + ReadUntilEndOfLine(); + else + break; + } } // ------------------------------------------------------------------------------------------------ void XFileParser::GetNextTokenAsString( std::string& poString) { - if( mIsBinaryFormat) - { - poString = GetNextToken(); - return; - } + if( mIsBinaryFormat) + { + poString = GetNextToken(); + return; + } - FindNextNoneWhiteSpace(); - if( P >= End) - ThrowException( "Unexpected end of file while parsing string"); + FindNextNoneWhiteSpace(); + if( P >= End) + ThrowException( "Unexpected end of file while parsing string"); - if( *P != '"') - ThrowException( "Expected quotation mark."); - ++P; + if( *P != '"') + ThrowException( "Expected quotation mark."); + ++P; - while( P < End && *P != '"') - poString.append( P++, 1); + while( P < End && *P != '"') + poString.append( P++, 1); - if( P >= End-1) - ThrowException( "Unexpected end of file while parsing string"); + if( P >= End-1) + ThrowException( "Unexpected end of file while parsing string"); - if( P[1] != ';' || P[0] != '"') - ThrowException( "Expected quotation mark and semicolon at the end of a string."); - P+=2; + if( P[1] != ';' || P[0] != '"') + ThrowException( "Expected quotation mark and semicolon at the end of a string."); + P+=2; } // ------------------------------------------------------------------------------------------------ void XFileParser::ReadUntilEndOfLine() { - if( mIsBinaryFormat) - return; - - while( P < End) - { - if( *P == '\n' || *P == '\r') - { - ++P; mLineNumber++; - return; - } - - ++P; - } + if( mIsBinaryFormat) + return; + + while( P < End) + { + if( *P == '\n' || *P == '\r') + { + ++P; mLineNumber++; + return; + } + + ++P; + } } // ------------------------------------------------------------------------------------------------ unsigned short XFileParser::ReadBinWord() { - ai_assert(End - P >= 2); - const unsigned char* q = (const unsigned char*) P; - unsigned short tmp = q[0] | (q[1] << 8); - P += 2; - return tmp; + ai_assert(End - P >= 2); + const unsigned char* q = (const unsigned char*) P; + unsigned short tmp = q[0] | (q[1] << 8); + P += 2; + return tmp; } // ------------------------------------------------------------------------------------------------ unsigned int XFileParser::ReadBinDWord() { - ai_assert(End - P >= 4); - const unsigned char* q = (const unsigned char*) P; - unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); - P += 4; - return tmp; + ai_assert(End - P >= 4); + const unsigned char* q = (const unsigned char*) P; + unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); + P += 4; + return tmp; } // ------------------------------------------------------------------------------------------------ unsigned int XFileParser::ReadInt() { - if( mIsBinaryFormat) - { - if( mBinaryNumCount == 0 && End - P >= 2) - { - unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 - if( tmp == 0x06 && End - P >= 4) // array of ints follows - mBinaryNumCount = ReadBinDWord(); - else // single int follows - mBinaryNumCount = 1; - } - - --mBinaryNumCount; - if ( End - P >= 4) { - return ReadBinDWord(); - } else { - P = End; - return 0; - } - } else - { - FindNextNoneWhiteSpace(); - - // TODO: consider using strtol10 instead??? - - // check preceeding minus sign - bool isNegative = false; - if( *P == '-') - { - isNegative = true; - P++; - } - - // at least one digit expected - if( !isdigit( *P)) - ThrowException( "Number expected."); - - // read digits - unsigned int number = 0; - while( P < End) - { - if( !isdigit( *P)) - break; - number = number * 10 + (*P - 48); - P++; - } - - CheckForSeparator(); - return isNegative ? ((unsigned int) -int( number)) : number; - } + if( mIsBinaryFormat) + { + if( mBinaryNumCount == 0 && End - P >= 2) + { + unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 + if( tmp == 0x06 && End - P >= 4) // array of ints follows + mBinaryNumCount = ReadBinDWord(); + else // single int follows + mBinaryNumCount = 1; + } + + --mBinaryNumCount; + if ( End - P >= 4) { + return ReadBinDWord(); + } else { + P = End; + return 0; + } + } else + { + FindNextNoneWhiteSpace(); + + // TODO: consider using strtol10 instead??? + + // check preceding minus sign + bool isNegative = false; + if( *P == '-') + { + isNegative = true; + P++; + } + + // at least one digit expected + if( !isdigit( *P)) + ThrowException( "Number expected."); + + // read digits + unsigned int number = 0; + while( P < End) + { + if( !isdigit( *P)) + break; + number = number * 10 + (*P - 48); + P++; + } + + CheckForSeparator(); + return isNegative ? ((unsigned int) -int( number)) : number; + } } // ------------------------------------------------------------------------------------------------ float XFileParser::ReadFloat() { - if( mIsBinaryFormat) - { - if( mBinaryNumCount == 0 && End - P >= 2) - { - unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 - if( tmp == 0x07 && End - P >= 4) // array of floats following - mBinaryNumCount = ReadBinDWord(); - else // single float following - mBinaryNumCount = 1; - } - - --mBinaryNumCount; - if( mBinaryFloatSize == 8) - { - if( End - P >= 8) { - float result = (float) (*(double*) P); - P += 8; - return result; - } else { - P = End; - return 0; - } - } else - { - if( End - P >= 4) { - float result = *(float*) P; - P += 4; - return result; - } else { - P = End; - return 0; - } - } - } - - // text version - FindNextNoneWhiteSpace(); - // check for various special strings to allow reading files from faulty exporters - // I mean you, Blender! - // Reading is safe because of the terminating zero - if( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0) - { - P += 9; - CheckForSeparator(); - return 0.0f; - } else - if( strncmp( P, "1.#QNAN0", 8) == 0) - { - P += 8; - CheckForSeparator(); - return 0.0f; - } - - float result = 0.0f; - P = fast_atoreal_move<float>( P, result); - - CheckForSeparator(); - - return result; + if( mIsBinaryFormat) + { + if( mBinaryNumCount == 0 && End - P >= 2) + { + unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 + if( tmp == 0x07 && End - P >= 4) // array of floats following + mBinaryNumCount = ReadBinDWord(); + else // single float following + mBinaryNumCount = 1; + } + + --mBinaryNumCount; + if( mBinaryFloatSize == 8) + { + if( End - P >= 8) { + float result = (float) (*(double*) P); + P += 8; + return result; + } else { + P = End; + return 0; + } + } else + { + if( End - P >= 4) { + float result = *(float*) P; + P += 4; + return result; + } else { + P = End; + return 0; + } + } + } + + // text version + FindNextNoneWhiteSpace(); + // check for various special strings to allow reading files from faulty exporters + // I mean you, Blender! + // Reading is safe because of the terminating zero + if( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0) + { + P += 9; + CheckForSeparator(); + return 0.0f; + } else + if( strncmp( P, "1.#QNAN0", 8) == 0) + { + P += 8; + CheckForSeparator(); + return 0.0f; + } + + float result = 0.0f; + P = fast_atoreal_move<float>( P, result); + + CheckForSeparator(); + + return result; } // ------------------------------------------------------------------------------------------------ aiVector2D XFileParser::ReadVector2() { - aiVector2D vector; - vector.x = ReadFloat(); - vector.y = ReadFloat(); - TestForSeparator(); + aiVector2D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + TestForSeparator(); - return vector; + return vector; } // ------------------------------------------------------------------------------------------------ aiVector3D XFileParser::ReadVector3() { - aiVector3D vector; - vector.x = ReadFloat(); - vector.y = ReadFloat(); - vector.z = ReadFloat(); - TestForSeparator(); + aiVector3D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + vector.z = ReadFloat(); + TestForSeparator(); - return vector; + return vector; } // ------------------------------------------------------------------------------------------------ aiColor4D XFileParser::ReadRGBA() { - aiColor4D color; - color.r = ReadFloat(); - color.g = ReadFloat(); - color.b = ReadFloat(); - color.a = ReadFloat(); - TestForSeparator(); - - return color; + aiColor4D color; + color.r = ReadFloat(); + color.g = ReadFloat(); + color.b = ReadFloat(); + color.a = ReadFloat(); + TestForSeparator(); + + return color; } // ------------------------------------------------------------------------------------------------ aiColor3D XFileParser::ReadRGB() { - aiColor3D color; - color.r = ReadFloat(); - color.g = ReadFloat(); - color.b = ReadFloat(); - TestForSeparator(); + aiColor3D color; + color.r = ReadFloat(); + color.g = ReadFloat(); + color.b = ReadFloat(); + TestForSeparator(); - return color; + return color; } // ------------------------------------------------------------------------------------------------ // Throws an exception with a line number and the given text. -void XFileParser::ThrowException( const std::string& pText) +AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) { - if( mIsBinaryFormat) - throw DeadlyImportError( pText); - else - throw DeadlyImportError( boost::str( boost::format( "Line %d: %s") % mLineNumber % pText)); + if( mIsBinaryFormat) + throw DeadlyImportError( pText); + else + throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << pText ); } @@ -1441,31 +1452,31 @@ void XFileParser::ThrowException( const std::string& pText) // Filters the imported hierarchy for some degenerated cases that some exporters produce. void XFileParser::FilterHierarchy( XFile::Node* pNode) { - // if the node has just a single unnamed child containing a mesh, remove - // the anonymous node inbetween. The 3DSMax kwXport plugin seems to produce this - // mess in some cases - if( pNode->mChildren.size() == 1 && pNode->mMeshes.empty() ) - { - XFile::Node* child = pNode->mChildren.front(); - if( child->mName.length() == 0 && child->mMeshes.size() > 0) - { - // transfer its meshes to us - for( unsigned int a = 0; a < child->mMeshes.size(); a++) - pNode->mMeshes.push_back( child->mMeshes[a]); - child->mMeshes.clear(); - - // transfer the transform as well - pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix; - - // then kill it - delete child; - pNode->mChildren.clear(); - } - } - - // recurse - for( unsigned int a = 0; a < pNode->mChildren.size(); a++) - FilterHierarchy( pNode->mChildren[a]); + // if the node has just a single unnamed child containing a mesh, remove + // the anonymous node between. The 3DSMax kwXport plugin seems to produce this + // mess in some cases + if( pNode->mChildren.size() == 1 && pNode->mMeshes.empty() ) + { + XFile::Node* child = pNode->mChildren.front(); + if( child->mName.length() == 0 && child->mMeshes.size() > 0) + { + // transfer its meshes to us + for( unsigned int a = 0; a < child->mMeshes.size(); a++) + pNode->mMeshes.push_back( child->mMeshes[a]); + child->mMeshes.clear(); + + // transfer the transform as well + pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix; + + // then kill it + delete child; + pNode->mChildren.clear(); + } + } + + // recurse + for( unsigned int a = 0; a < pNode->mChildren.size(); a++) + FilterHierarchy( pNode->mChildren[a]); } #endif // !! ASSIMP_BUILD_NO_X_IMPORTER |