Added AC-loader, WIP version. PLY loader is now able to load models from blender, test model added. Refactoring. Added FindInvalidData step. Added support for precompiled headers, the release builds in VC8 are configued to use PCH now. Added separate makefile for mingw, no -FPic warning anymore, -clear works now. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@176 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
1242 lines
34 KiB
C++
1242 lines
34 KiB
C++
/*
|
|
---------------------------------------------------------------------------
|
|
Open Asset Import Library (ASSIMP)
|
|
---------------------------------------------------------------------------
|
|
|
|
Copyright (c) 2006-2008, ASSIMP Development 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 Development 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 */
|
|
|
|
#include "AssimpPCH.h"
|
|
|
|
#include "XFileParser.h"
|
|
#include "XFileHelper.h"
|
|
#include "BaseImporter.h"
|
|
#include "fast_atof.h"
|
|
|
|
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<char>& 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( 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));
|
|
|
|
// 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?
|
|
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
|
|
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
|
|
{
|
|
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++)
|
|
{
|
|
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
|
|
{
|
|
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());
|
|
|
|
CheckForSeparator();
|
|
}
|
|
|
|
CheckForClosingBrace();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh)
|
|
{
|
|
readHeadOfDataObject();
|
|
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();
|
|
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();
|
|
CheckForSeparator();
|
|
}
|
|
|
|
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
|
|
{
|
|
DefaultLogger::get()->warn("Unknown data object in material list in x file");
|
|
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( 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;
|
|
|
|
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
|
|
{
|
|
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);
|
|
|
|
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
|
|
{
|
|
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();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
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
|
|
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 "<integer>";
|
|
case 5:
|
|
// GUID token
|
|
P += 16;
|
|
return "<guid>";
|
|
case 6:
|
|
len = ReadBinDWord();
|
|
P += (len * 4);
|
|
return "<int_list>";
|
|
case 7:
|
|
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;
|
|
|
|
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]);
|
|
}
|