- Bugfix: ColladaLoader node rotation was miscalculated

- ColladaLoader now supports more primitive types
- ColladaLoader now honours the up-vector specification
- moved Collada temporary structures to separate Helper header
- fast_atof_move() now also eats localized numbers with comma separator (e.g. 0,3210) correctly

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@251 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
ulfjorensen 2008-11-26 15:45:34 +00:00
parent edc8eda808
commit 85a487f6b8
7 changed files with 298 additions and 152 deletions

View File

@ -0,0 +1,172 @@
/** Helper structures for the Collada loader */
/*
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.
----------------------------------------------------------------------
*/
#ifndef AI_COLLADAHELPER_H_INC
#define AI_COLLADAHELPER_H_INC
namespace Assimp
{
namespace Collada
{
/** Transformation types that can be applied to a node */
enum TransformType
{
TF_LOOKAT,
TF_ROTATE,
TF_TRANSLATE,
TF_SCALE,
TF_SKEW,
TF_MATRIX
};
/** Contains all data for one of the different transformation types */
struct Transform
{
TransformType mType;
float f[16]; ///< Interpretation of data depends on the type of the transformation
};
/** A node in a scene hierarchy */
struct Node
{
std::string mName;
std::string mID;
Node* mParent;
std::vector<Node*> mChildren;
/** Operations in order to calculate the resulting transformation to parent. */
std::vector<Transform> mTransforms;
std::vector<std::string> mMeshes; ///< Meshes at this node
Node() { mParent = NULL; }
~Node() { for( std::vector<Node*>::iterator it = mChildren.begin(); it != mChildren.end(); ++it) delete *it; }
};
/** Data source array */
struct Data
{
std::vector<float> mValues;
};
/** Accessor to a data array */
struct Accessor
{
size_t mCount; // in number of objects
size_t mOffset; // in number of values
size_t mStride; // Stride in number of values
std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on.
// For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
std::string mSource; // URL of the source array
mutable const Data* mData; // Pointer to the source array, if resolved. NULL else
Accessor()
{
mCount = 0; mOffset = 0; mStride = 0; mData = NULL;
mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
}
};
/** A single face in a mesh */
struct Face
{
std::vector<size_t> mIndices;
};
/** Different types of input data to a vertex or face */
enum InputType
{
IT_Invalid,
IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
IT_Position,
IT_Normal,
IT_Texcoord,
IT_Color
};
/** An input channel for mesh data, referring to a single accessor */
struct InputChannel
{
InputType mType; // Type of the data
size_t mIndex; // Optional index, if multiple sets of the same data type are given
size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
std::string mAccessor; // ID of the accessor where to read the actual values from.
mutable const Accessor* mResolved; // Pointer to the accessor, if resolved. NULL else
InputChannel() { mType = IT_Invalid; mIndex = 0; mOffset = 0; mResolved = NULL; }
};
/** Contains data for a single mesh */
struct Mesh
{
std::string mVertexID; // just to check if there's some sophisticated addressing involved... which we don't support, and therefore should warn about.
std::vector<InputChannel> mPerVertexData; // Vertex data addressed by vertex indices
// actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
std::vector<aiVector3D> mPositions;
std::vector<aiVector3D> mNormals;
std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
// Faces. Stored are only the number of vertices for each face. 1 == point, 2 == line, 3 == triangle, 4+ == poly
std::vector<size_t> mFaceSize;
};
/** Which type of primitives the ReadPrimitives() function is going to read */
enum PrimitiveType
{
Prim_Invalid,
Prim_Lines,
Prim_LineStrip,
Prim_Triangles,
Prim_TriStrips,
Prim_TriFans,
Prim_Polylist,
Prim_Polygon
};
} // end of namespace Collada
} // end of namespace Assimp
#endif // AI_COLLADAHELPER_H_INC

View File

@ -90,6 +90,21 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
// build the node hierarchy from it
pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
// Convert to Z_UP, if different orientation
if( parser.mUpDirection == ColladaParser::UP_X)
pScene->mRootNode->mTransformation *= aiMatrix4x4(
0, -1, 0, 0,
0, 0, -1, 0,
1, 0, 0, 0,
0, 0, 0, 1);
else if( parser.mUpDirection == ColladaParser::UP_Y)
pScene->mRootNode->mTransformation *= aiMatrix4x4(
1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1);
// store all meshes
StoreSceneMeshes( pScene);
@ -113,7 +128,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
// ------------------------------------------------------------------------------------------------
// Recursively constructs a scene node for the given parser node and returns it.
aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const ColladaParser::Node* pNode)
aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
{
// create a node for it
aiNode* node = new aiNode( pNode->mName);
@ -138,7 +153,7 @@ aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Colla
// ------------------------------------------------------------------------------------------------
// Builds meshes for the given node and references them
void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const ColladaParser::Node* pNode, aiNode* pTarget)
void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
{
// accumulated mesh references by this node
std::vector<size_t> newMeshRefs;
@ -163,7 +178,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
{
// else we have to add the mesh to the collection and store its newly assigned index at the node
aiMesh* dstMesh = new aiMesh;
const ColladaParser::Mesh* srcMesh = srcMeshIt->second;
const Collada::Mesh* srcMesh = srcMeshIt->second;
// copy positions
dstMesh->mNumVertices = srcMesh->mPositions.size();

View File

@ -83,10 +83,10 @@ protected:
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
/** Recursively constructs a scene node for the given parser node and returns it. */
aiNode* BuildHierarchy( const ColladaParser& pParser, const ColladaParser::Node* pNode);
aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
/** Builds meshes for the given node and references them */
void BuildMeshesForNode( const ColladaParser& pParser, const ColladaParser::Node* pNode, aiNode* pTarget);
void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget);
/** Stores all meshes in the given scene */
void StoreSceneMeshes( aiScene* pScene);

View File

@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ParsingUtils.h"
using namespace Assimp;
using namespace Assimp::Collada;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
@ -253,7 +254,8 @@ void ColladaParser::ReadMesh( Mesh* pMesh)
// read per-vertex mesh data
ReadVertexData( pMesh);
}
else if( IsElement( "polylist") || IsElement( "triangles"))
else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
|| IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
{
// read per-index mesh data and faces setup
ReadIndexData( pMesh);
@ -438,7 +440,23 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
// distinguish between polys and triangles
std::string elementName = mReader->getNodeName();
bool isPolylist = IsElement( "polylist");
PrimitiveType primType = Prim_Invalid;
if( IsElement( "lines"))
primType = Prim_Lines;
else if( IsElement( "linestrips"))
primType = Prim_LineStrip;
else if( IsElement( "polygons"))
primType = Prim_Polygon;
else if( IsElement( "polylist"))
primType = Prim_Polylist;
else if( IsElement( "triangles"))
primType = Prim_Triangles;
else if( IsElement( "trifans"))
primType = Prim_TriFans;
else if( IsElement( "tristrips"))
primType = Prim_TriStrips;
assert( primType != Prim_Invalid);
// also a number of <input> elements, but in addition a <p> primitive collection and propably index counts for all primitives
while( mReader->read())
@ -469,7 +487,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
else if( IsElement( "p"))
{
// now here the actual fun starts - these are the indices to construct the mesh data from
ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, isPolylist);
ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
} else
{
ThrowException( "Unexpected sub element in tag \"vertices\".");
@ -519,7 +537,7 @@ void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
// ------------------------------------------------------------------------------------------------
// Reads a <p> primitive index list and assembles the mesh data into the given mesh
void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
size_t pNumPrimitives, const std::vector<size_t>& pVCount, bool pIsPolylist)
size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
{
// determine number of indices coming per vertex
// find the offset index for all per-vertex channels
@ -534,32 +552,52 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
// determine the expected number of indices
size_t expectedPointCount = 0;
if( pIsPolylist)
switch( pPrimType)
{
BOOST_FOREACH( size_t i, pVCount)
expectedPointCount += i;
} else
{
// everything triangles
expectedPointCount = 3 * pNumPrimitives;
case Prim_Polylist:
{
BOOST_FOREACH( size_t i, pVCount)
expectedPointCount += i;
break;
}
case Prim_Lines:
expectedPointCount = 2 * pNumPrimitives;
break;
case Prim_Triangles:
expectedPointCount = 3 * pNumPrimitives;
break;
default:
// other primitive types don't state the index count upfront... we need to guess
break;
}
// and read all indices into a temporary array
std::vector<size_t> indices( expectedPointCount * numOffsets);
std::vector<size_t> indices;
if( expectedPointCount > 0)
indices.reserve( expectedPointCount * numOffsets);
const char* content = GetTextContent();
BOOST_FOREACH( size_t& value, indices)
while( *content != 0)
{
if( *content == 0)
ThrowException( "Expected more values while reading primitive indices.");
// read a value in place
value = strtol10( content, &content);
// read a value
unsigned int value = strtol10( content, &content);
indices.push_back( size_t( value));
// skip whitespace after it
SkipSpacesAndLineEnd( &content);
}
// complain if the index count doesn't fit
if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets)
ThrowException( "Expected different index count in <p> element.");
else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
ThrowException( "Expected different index count in <p> element.");
// find the data for all sources
BOOST_FOREACH( InputChannel& input, pMesh->mPerVertexData)
{
if( input.mResolved)
continue;
// find accessor
input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
// resolve accessor's data pointer as well, if neccessary
@ -570,6 +608,9 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
// and the same for the per-index channels
BOOST_FOREACH( InputChannel& input, pPerIndexChannels)
{
if( input.mResolved)
continue;
// ignore vertex pointer, it doesn't refer to an accessor
if( input.mType == IT_Vertex)
{
@ -590,12 +631,28 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
// now assemble vertex data according to those indices
std::vector<size_t>::const_iterator idx = indices.begin();
for( size_t a = 0; a < pNumPrimitives; a++)
// For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
size_t numPrimitives = pNumPrimitives;
if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
numPrimitives = 1;
for( size_t a = 0; a < numPrimitives; a++)
{
// determine number of points for this primitive
size_t numPoints = 3;
if( pIsPolylist)
numPoints = pVCount[a];
size_t numPoints = 0;
switch( pPrimType)
{
case Prim_Lines: numPoints = 2; break;
case Prim_Triangles: numPoints = 3; break;
case Prim_Polylist: numPoints = pVCount[a]; break;
case Prim_TriFans:
case Prim_Polygon: numPoints = indices.size() / numOffsets; break;
default:
// LineStrip and TriStrip not supported due to expected index unmangling
ThrowException( "Unsupported primitive type.");
break;
}
// store the face size to later reconstruct the face from
pMesh->mFaceSize.push_back( numPoints);
@ -618,7 +675,7 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
}
}
// if I ever get my hands on that guy how invented this steaming pile of indirection...
// if I ever get my hands on that guy who invented this steaming pile of indirection...
TestClosing( "p");
}
@ -979,7 +1036,9 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform
case TF_ROTATE:
{
aiMatrix4x4 rot;
aiMatrix4x4::Rotation( tf.f[3], aiVector3D( tf.f[0], tf.f[1], tf.f[2]), rot);
float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f;
aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
aiMatrix4x4::Rotation( angle, axis, rot);
res *= rot;
break;
}
@ -1019,7 +1078,7 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform
// ------------------------------------------------------------------------------------------------
// Determines the input data type for the given semantic string
ColladaParser::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
{
if( pSemantic == "POSITION")
return IT_Position;

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_COLLADAPARSER_H_INC
#include "./irrXML/irrXMLWrapper.h"
#include "ColladaHelper.h"
namespace Assimp
{
@ -54,111 +55,6 @@ namespace Assimp
class ColladaParser
{
friend class ColladaLoader;
public:
/** Transformation types that can be applied to a node */
enum TransformType
{
TF_LOOKAT,
TF_ROTATE,
TF_TRANSLATE,
TF_SCALE,
TF_SKEW,
TF_MATRIX
};
/** Contains all data for one of the different transformation types */
struct Transform
{
TransformType mType;
float f[16]; ///< Interpretation of data depends on the type of the transformation
};
/** A node in a scene hierarchy */
struct Node
{
std::string mName;
std::string mID;
Node* mParent;
std::vector<Node*> mChildren;
/** Operations in order to calculate the resulting transformation to parent. */
std::vector<Transform> mTransforms;
std::vector<std::string> mMeshes; ///< Meshes at this node
Node() { mParent = NULL; }
~Node() { for( std::vector<Node*>::iterator it = mChildren.begin(); it != mChildren.end(); ++it) delete *it; }
};
/** Data source array */
struct Data
{
std::vector<float> mValues;
};
/** Accessor to a data array */
struct Accessor
{
size_t mCount; // in number of objects
size_t mOffset; // in number of values
size_t mStride; // Stride in number of values
std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on.
// For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
std::string mSource; // URL of the source array
mutable const Data* mData; // Pointer to the source array, if resolved. NULL else
Accessor()
{
mCount = 0; mOffset = 0; mStride = 0; mData = NULL;
mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
}
};
/** A single face in a mesh */
struct Face
{
std::vector<size_t> mIndices;
};
/** Different types of input data to a vertex or face */
enum InputType
{
IT_Invalid,
IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
IT_Position,
IT_Normal,
IT_Texcoord,
IT_Color
};
/** An input channel for mesh data, referring to a single accessor */
struct InputChannel
{
InputType mType; // Type of the data
size_t mIndex; // Optional index, if multiple sets of the same data type are given
size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
std::string mAccessor; // ID of the accessor where to read the actual values from.
mutable const Accessor* mResolved; // Pointer to the accessor, if resolved. NULL else
InputChannel() { mType = IT_Invalid; mIndex = 0; mOffset = 0; mResolved = NULL; }
};
/** Contains data for a single mesh */
struct Mesh
{
std::string mVertexID; // just to check if there's some sophisticated addressing involved... which we don't support, and therefore should warn about.
std::vector<InputChannel> mPerVertexData; // Vertex data addressed by vertex indices
// actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
std::vector<aiVector3D> mPositions;
std::vector<aiVector3D> mNormals;
std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
// Faces. Stored are only the number of vertices for each face. 1 == point, 2 == line, 3 == triangle, 4+ == poly
std::vector<size_t> mFaceSize;
};
protected:
/** Constructor from XML file */
@ -180,7 +76,7 @@ protected:
void ReadGeometryLibrary();
/** Reads a mesh from the geometry library */
void ReadMesh( Mesh* pMesh);
void ReadMesh( Collada::Mesh* pMesh);
/** Reads a data array holding a number of floats, and stores it in the global library */
void ReadFloatArray();
@ -191,32 +87,32 @@ protected:
void ReadAccessor( const std::string& pID);
/** Reads input declarations of per-vertex mesh data into the given mesh */
void ReadVertexData( Mesh* pMesh);
void ReadVertexData( Collada::Mesh* pMesh);
/** Reads input declarations of per-index mesh data into the given mesh */
void ReadIndexData( Mesh* pMesh);
void ReadIndexData( Collada::Mesh* pMesh);
/** Reads a single input channel element and stores it in the given array, if valid */
void ReadInputChannel( std::vector<InputChannel>& poChannels);
void ReadInputChannel( std::vector<Collada::InputChannel>& poChannels);
/** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
void ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
size_t pNumPrimitives, const std::vector<size_t>& pVCount, bool pIsPolylist);
void ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
size_t pNumPrimitives, const std::vector<size_t>& pVCount, Collada::PrimitiveType pPrimType);
/** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
void ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh);
void ExtractDataObjectFromChannel( const Collada::InputChannel& pInput, size_t pLocalIndex, Collada::Mesh* pMesh);
/** Reads the library of node hierarchies and scene parts */
void ReadSceneLibrary();
/** Reads a scene node's contents including children and stores it in the given node */
void ReadSceneNode( Node* pNode);
void ReadSceneNode( Collada::Node* pNode);
/** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
void ReadNodeTransformation( Node* pNode, TransformType pType);
void ReadNodeTransformation( Collada::Node* pNode, Collada::TransformType pType);
/** Reads a mesh reference in a node and adds it to the node's mesh list */
void ReadNodeGeometry( Node* pNode);
void ReadNodeGeometry( Collada::Node* pNode);
/** Reads the collada scene */
void ReadScene();
@ -247,10 +143,10 @@ protected:
const char* GetTextContent();
/** Calculates the resulting transformation fromm all the given transform steps */
aiMatrix4x4 CalculateResultTransform( const std::vector<Transform>& pTransforms) const;
aiMatrix4x4 CalculateResultTransform( const std::vector<Collada::Transform>& pTransforms) const;
/** Determines the input data type for the given semantic string */
InputType GetTypeForSemantic( const std::string& pSemantic);
Collada::InputType GetTypeForSemantic( const std::string& pSemantic);
/** Finds the item in the given library by its reference, throws if not found */
template <typename Type> const Type& ResolveLibraryReference( const std::map<std::string, Type>& pLibrary, const std::string& pURL) const;
@ -263,23 +159,23 @@ protected:
irr::io::IrrXMLReader* mReader;
/** All data arrays found in the file by ID. Might be referred to by actually everyone. Collada, you are a steaming pile of indirection. */
typedef std::map<std::string, Data> DataLibrary;
typedef std::map<std::string, Collada::Data> DataLibrary;
DataLibrary mDataLibrary;
/** Same for accessors which define how the data in a data array is accessed. */
typedef std::map<std::string, Accessor> AccessorLibrary;
typedef std::map<std::string, Collada::Accessor> AccessorLibrary;
AccessorLibrary mAccessorLibrary;
/** Mesh library: mesh by ID */
typedef std::map<std::string, Mesh*> MeshLibrary;
typedef std::map<std::string, Collada::Mesh*> MeshLibrary;
MeshLibrary mMeshLibrary;
/** node library: root node of the hierarchy part by ID */
typedef std::map<std::string, Node*> NodeLibrary;
typedef std::map<std::string, Collada::Node*> NodeLibrary;
NodeLibrary mNodeLibrary;
/** Pointer to the root node. Don't delete, it just points to one of the nodes in the node library. */
Node* mRootNode;
Collada::Node* mRootNode;
/** Size unit: how large compared to a meter */
float mUnitSize;

View File

@ -194,7 +194,7 @@ inline const char* fast_atof_move( const char* c, float& out)
else if (*c=='+')++c;
f = (float) strtol10_64 ( c, &c );
if (*c == '.')
if (*c == '.' || (c[0] == ',' && isdigit( c[1])))
{
++c;

View File

@ -1437,6 +1437,10 @@
<Filter
Name="Collada"
>
<File
RelativePath="..\..\code\ColladaHelper.h"
>
</File>
<File
RelativePath="..\..\code\ColladaLoader.cpp"
>