/** @file Implementation of the XFile parser helper class */ #include "XFileParser.h" #include "XFileHelper.h" #include "BaseImporter.h" #include "fast_atof.h" #include using namespace Assimp; using namespace Assimp::XFile; // ------------------------------------------------------------------------------------------------ // Constructor. Creates a data structure out of the XFile given in the memory block. XFileParser::XFileParser( const std::vector& pBuffer) { mMajorVersion = mMinorVersion = 0; mIsBinaryFormat = false; mBinaryNumCount = 0; P = End = NULL; mLineNumber = 0; mScene = NULL; // set up memory pointers P = &pBuffer.front(); End = P + pBuffer.size(); // check header if( strncmp( P, "xof ", 4) != 0) throw new ImportErrorException( "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); // read format if( strncmp( P + 8, "txt ", 4) == 0) mIsBinaryFormat = false; else if( strncmp( P + 8, "bin ", 4) == 0) mIsBinaryFormat = true; else ThrowException( "Unsupported xfile format"); // 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)); // start reading here P += 16; 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() { while( 1) { // 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? // os::Printer::log("} found in dataObject", ELL_WARNING); } else { // unknown format //os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING); 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 while(true) { 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. while(true) { 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 { // os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING); 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++) { 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()); CheckForSeparator(); } // here, other data objects may follow while(true) { 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 { //os::Printer::log("Unknown data object in mesh in x file", objectName.c_str(), ELL_WARNING); 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()); CheckForSeparator(); } CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh) { readHeadOfDataObject(); std::vector& 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(); std::vector& 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(); } CheckForSemicolon(); 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 == ';') ++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 while(true) { 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 { // os::Printer::log("Unknown data object in material list in x file", objectName.c_str(), ELL_WARNING); ParseUnknownDataObject(); } } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectMaterial( Material* pMaterial) { std::string matName; readHeadOfDataObject( &matName); 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 while(true) { 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( texname); } else { // os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING); 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; while(true) { 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 { // os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING); ParseUnknownDataObject(); } } } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectAnimation( Animation* pAnim) { readHeadOfDataObject(); AnimBone* banim = new AnimBone; pAnim->mAnims.push_back( banim); while(true) { 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 { //os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING); 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(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseDataObjectTextureFilename( std::string& pName) { readHeadOfDataObject(); GetNextTokenAsString( pName); CheckForClosingBrace(); } // ------------------------------------------------------------------------------------------------ void XFileParser::ParseUnknownDataObject() { // find opening delimiter while( true) { 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."); } // ------------------------------------------------------------------------------------------------ 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. unsigned int tok = ReadBinWord(); unsigned int len; // standalone tokens switch( tok) { case 1: // name token len = ReadBinDWord(); s = std::string(P, len); P += len; return s; case 2: // string token len = ReadBinDWord(); s = std::string(P, len); P += (len + 2); return s; case 3: // integer token P += 4; return ""; case 5: // GUID token P += 16; return ""; case 6: len = ReadBinDWord(); P += (len * 4); return ""; case 7: len = ReadBinDWord(); P += (len * mBinaryFloatSize); return ""; 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; while( true) { 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() { const unsigned char* q = (const unsigned char*) P; unsigned short tmp = q[0] | (q[1] << 8); P += 2; return tmp; } // ------------------------------------------------------------------------------------------------ unsigned int XFileParser::ReadBinDWord() { 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) { unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 if( tmp == 0x06) // array of ints follows mBinaryNumCount = ReadBinDWord(); else // single int follows mBinaryNumCount = 1; } --mBinaryNumCount; return ReadBinDWord(); } else { FindNextNoneWhiteSpace(); // 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; } } // ------------------------------------------------------------------------------------------------ float XFileParser::ReadFloat() { if( mIsBinaryFormat) { if( mBinaryNumCount == 0) { unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 if( tmp == 0x07) // array of floats following mBinaryNumCount = ReadBinDWord(); else // single float following mBinaryNumCount = 1; } --mBinaryNumCount; if( mBinaryFloatSize == 8) { float result = (float) (*(double*) P); P += 8; return result; } else { float result = *(float*) P; P += 4; return result; } } // text version FindNextNoneWhiteSpace(); // check for various special strings to allow reading files from faulty exporters // I mean you, Blender! if( strncmp( P, "-1.#IND00", 9) == 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_atof_move( P, result); CheckForSeparator(); return result; } // ------------------------------------------------------------------------------------------------ aiVector2D XFileParser::ReadVector2() { aiVector2D vector; vector.x = ReadFloat(); vector.y = ReadFloat(); CheckForSeparator(); return vector; } // ------------------------------------------------------------------------------------------------ aiVector3D XFileParser::ReadVector3() { aiVector3D vector; vector.x = ReadFloat(); vector.y = ReadFloat(); vector.z = ReadFloat(); CheckForSeparator(); return vector; } // ------------------------------------------------------------------------------------------------ aiColor4D XFileParser::ReadRGBA() { aiColor4D color; color.r = ReadFloat(); color.g = ReadFloat(); color.b = ReadFloat(); color.a = ReadFloat(); CheckForSeparator(); return color; } // ------------------------------------------------------------------------------------------------ aiColor3D XFileParser::ReadRGB() { aiColor3D color; color.r = ReadFloat(); color.g = ReadFloat(); color.b = ReadFloat(); CheckForSeparator(); return color; } // Throws an exception with a line number and the given text. void XFileParser::ThrowException( const std::string& pText) { if( mIsBinaryFormat) throw new ImportErrorException( pText); else throw new ImportErrorException( boost::str( boost::format( "Line %d: %s") % 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 inbetween. The 3DSMax kwXport plugin seems to produce this // mess in some cases if( pNode->mChildren.size() == 1) { 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(); // then kill it delete child; pNode->mChildren.clear(); } } // recurse for( unsigned int a = 0; a < pNode->mChildren.size(); a++) FilterHierarchy( pNode->mChildren[a]); }