diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp')
| -rw-r--r-- | NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp | 1480 |
1 files changed, 1480 insertions, 0 deletions
diff --git a/NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp b/NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp new file mode 100644 index 0000000..1f8b8cb --- /dev/null +++ b/NvCloth/samples/external/assimp-4.1.0/code/XFileParser.cpp @@ -0,0 +1,1480 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + 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 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +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 +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 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the XFile parser helper class */ + + +#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 + +// Magic identifier for MSZIP compressed data +#define MSZIP_MAGIC 0x4B43 +#define MSZIP_BLOCK 32786 + +// ------------------------------------------------------------------------------------------------ +// 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_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. +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( 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"); +#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; + + 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); + } +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. Destroys all imported data along with it +XFileParser::~XFileParser() +{ + // 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(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectTemplate() +{ + // parse a template data object. Currently not stored. + std::string name; + readHeadOfDataObject( &name); + + // read GUID + std::string guid = GetNextToken(); + + // read and ignore data members + bool running = true; + while ( running ) + { + std::string s = GetNextToken(); + + if( s == "}") + break; + + 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(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(); +} + +// ------------------------------------------------------------------------------------------------ +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++) + { + // read indices + unsigned int numIndices = ReadInt(); + 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(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* /*pMesh*/ ) +{ + readHeadOfDataObject(); + + /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt(); + /*unsigned int maxSkinWeightsPerFace =*/ ReadInt(); + /*unsigned int numBonesInMesh = */ReadInt(); + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh) +{ + readHeadOfDataObject(); + + // 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 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 b = 0; b < numIndices; b++) + face.mIndices.push_back( ReadInt()); + + TestForSeparator(); + } + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh) +{ + 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++]; + + 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(); + + 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(); +} + +// ------------------------------------------------------------------------------------------------ +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(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMaterial( Material* pMaterial) +{ + std::string matName; + readHeadOfDataObject( &matName); + if( matName.empty()) + matName = std::string( "material") + 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(); +} + +// ------------------------------------------------------------------------------------------------ +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(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +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(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +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( 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, "\\"); +} + +// ------------------------------------------------------------------------------------------------ +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; + } +} + +// ------------------------------------------------------------------------------------------------ +//! checks for closing curly brace +void XFileParser::CheckForClosingBrace() +{ + if( GetNextToken() != "}") + ThrowException( "Closing brace expected."); +} + +// ------------------------------------------------------------------------------------------------ +//! checks for one following semicolon +void XFileParser::CheckForSemicolon() +{ + if( mIsBinaryFormat) + return; + + if( GetNextToken() != ";") + ThrowException( "Semicolon expected."); +} + +// ------------------------------------------------------------------------------------------------ +//! checks for a separator char, either a ',' or a ';' +void XFileParser::CheckForSeparator() +{ + if( mIsBinaryFormat) + return; + + std::string token = GetNextToken(); + if( token != "," && token != ";") + ThrowException( "Separator character (';' or ',') expected."); +} + +// ------------------------------------------------------------------------------------------------ +// tests and possibly consumes a separator char, but does nothing if there was no separator +void XFileParser::TestForSeparator() +{ + if( mIsBinaryFormat) + return; + + FindNextNoneWhiteSpace(); + if( P >= End) + return; + + // test and skip + if( *P == ';' || *P == ',') + P++; +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::readHeadOfDataObject( std::string* poName) +{ + 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; +} + +// ------------------------------------------------------------------------------------------------ +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; + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::GetNextTokenAsString( std::string& poString) +{ + if( mIsBinaryFormat) + { + poString = GetNextToken(); + return; + } + + FindNextNoneWhiteSpace(); + if( P >= End) + ThrowException( "Unexpected end of file while parsing string"); + + if( *P != '"') + ThrowException( "Expected quotation mark."); + ++P; + + while( P < End && *P != '"') + poString.append( P++, 1); + + 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; +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ReadUntilEndOfLine() +{ + 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; +} + +// ------------------------------------------------------------------------------------------------ +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; +} + +// ------------------------------------------------------------------------------------------------ +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 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; + } +} + +// ------------------------------------------------------------------------------------------------ +ai_real 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) { + ai_real result = (ai_real) (*(double*) P); + P += 8; + return result; + } else { + P = End; + return 0; + } + } else + { + if( End - P >= 4) { + ai_real result = *(ai_real*) 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.0; + } else + if( strncmp( P, "1.#QNAN0", 8) == 0) + { + P += 8; + CheckForSeparator(); + return 0.0; + } + + ai_real result = 0.0; + P = fast_atoreal_move<ai_real>( P, result); + + CheckForSeparator(); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +aiVector2D XFileParser::ReadVector2() +{ + aiVector2D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + TestForSeparator(); + + return vector; +} + +// ------------------------------------------------------------------------------------------------ +aiVector3D XFileParser::ReadVector3() +{ + aiVector3D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + vector.z = ReadFloat(); + TestForSeparator(); + + return vector; +} + +// ------------------------------------------------------------------------------------------------ +aiColor4D XFileParser::ReadRGBA() +{ + 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(); + + return color; +} + +// ------------------------------------------------------------------------------------------------ +// Throws an exception with a line number and the given text. +AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) +{ + if( mIsBinaryFormat) + throw DeadlyImportError( pText); + else + throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << 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 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 |