- added another boost dependency.

- further work on the collada loader. Should load static geometry correctly now, but still not properly integrated
- added some Collada sample files. The license is hopefully fine with this.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@245 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
This commit is contained in:
ulfjorensen
2008-11-20 17:42:55 +00:00
parent 01ceeeb00b
commit fe3c008301
15 changed files with 2313 additions and 5 deletions

View File

@@ -72,6 +72,8 @@ ColladaParser::~ColladaParser()
delete mReader;
for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
delete it->second;
for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
delete it->second;
}
// ------------------------------------------------------------------------------------------------
@@ -163,6 +165,9 @@ void ColladaParser::ReadAssetInfo()
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "asset") != 0)
ThrowException( "Expected end of \"asset\" element.");
break;
}
}
@@ -172,9 +177,493 @@ void ColladaParser::ReadAssetInfo()
// Reads the geometry library contents
void ColladaParser::ReadGeometryLibrary()
{
while( mReader->read())
{
if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if( IsElement( "geometry"))
{
// read ID. Another entry which is "optional" by design but obligatory in reality
int indexID = GetAttribute( "id");
std::string id = mReader->getAttributeValue( indexID);
// TODO: (thom) support SIDs
assert( TestAttribute( "sid") == -1);
// a <geometry> always contains a single <mesh> element inside, so we just skip that element in advance
TestOpening( "mesh");
// create a mesh and store it in the library under its ID
Mesh* mesh = new Mesh;
mMeshLibrary[id] = mesh;
// read on from there
ReadMesh( mesh);
// check for the closing tag of the outer <geometry" element, the inner closing of <mesh> has been consumed by ReadMesh()
TestClosing( "geometry");
} else
{
// ignore the rest
SkipElement();
}
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
ThrowException( "Expected end of \"library_geometries\" element.");
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a mesh from the geometry library
void ColladaParser::ReadMesh( Mesh* pMesh)
{
// I'm doing a dirty state parsing here because I don't want to open another submethod for it.
// There's a <source> tag defining the name for the accessor inside, and possible a <float_array>
// with it's own ID. This string contains the current source's ID if parsing is inside a <source> element.
std::string presentSourceID;
while( mReader->read())
{
if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if( IsElement( "source"))
{
// beginning of a source element - store ID for the inner elements
int attrID = GetAttribute( "id");
presentSourceID = mReader->getAttributeValue( attrID);
}
else if( IsElement( "float_array"))
{
ReadFloatArray();
}
else if( IsElement( "technique_common"))
{
// I don't fucking care for your profiles bullshit
}
else if( IsElement( "accessor"))
{
ReadAccessor( presentSourceID);
}
else if( IsElement( "vertices"))
{
// read per-vertex mesh data
ReadVertexData( pMesh);
}
else if( IsElement( "polylist") || IsElement( "triangles"))
{
// read per-index mesh data and faces setup
ReadIndexData( pMesh);
} else
{
// ignore the rest
SkipElement();
}
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "source") == 0)
{
// end of <source> - reset present source ID
presentSourceID.clear();
}
else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
{
// end of another meaningless element - read over it
}
else if( strcmp( mReader->getNodeName(), "mesh") == 0)
{
// end of <mesh> element - we're done here
break;
} else
{
// everything else should be punished
ThrowException( "Expected end of \"mesh\" element.");
}
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a data array holding a number of floats, and stores it in the global library
void ColladaParser::ReadFloatArray()
{
// read attributes
int indexID = GetAttribute( "id");
std::string id = mReader->getAttributeValue( indexID);
int indexCount = GetAttribute( "count");
unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
const char* content = GetTextContent();
// read values and store inside an array in the data library
mDataLibrary[id] = Data();
Data& data = mDataLibrary[id];
data.mValues.reserve( count);
for( unsigned int a = 0; a < count; a++)
{
if( *content == 0)
ThrowException( "Expected more values while reading float_array contents.");
float value;
// read a number
content = fast_atof_move( content, value);
data.mValues.push_back( value);
// skip whitespace after it
SkipSpacesAndLineEnd( &content);
}
// test for closing tag
TestClosing( "float_array");
}
// ------------------------------------------------------------------------------------------------
// Reads an accessor and stores it in the global library
void ColladaParser::ReadAccessor( const std::string& pID)
{
// read accessor attributes
int attrSource = GetAttribute( "source");
const char* source = mReader->getAttributeValue( attrSource);
if( source[0] != '#')
ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
int attrCount = GetAttribute( "count");
unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
int attrOffset = TestAttribute( "offset");
unsigned int offset = 0;
if( attrOffset > -1)
offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
int attrStride = TestAttribute( "stride");
unsigned int stride = 1;
if( attrStride > -1)
stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
// store in the library under the given ID
mAccessorLibrary[pID] = Accessor();
Accessor& acc = mAccessorLibrary[pID];
acc.mCount = count;
acc.mOffset = offset;
acc.mStride = stride;
acc.mSource = source+1; // ignore the leading '#'
// and read the components
while( mReader->read())
{
if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if( IsElement( "param"))
{
// read data param
int attrName = TestAttribute( "name");
std::string name;
if( attrName > -1)
{
name = mReader->getAttributeValue( attrName);
// analyse for common type components and store it's sub-offset in the corresponding field
if( name == "X") acc.mSubOffset[0] = acc.mParams.size();
else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
else if( name == "R") acc.mSubOffset[0] = acc.mParams.size();
else if( name == "G") acc.mSubOffset[1] = acc.mParams.size();
else if( name == "B") acc.mSubOffset[2] = acc.mParams.size();
else if( name == "A") acc.mSubOffset[3] = acc.mParams.size();
else if( name == "S") acc.mSubOffset[0] = acc.mParams.size();
else if( name == "T") acc.mSubOffset[1] = acc.mParams.size();
else
DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name));
}
acc.mParams.push_back( name);
// skip remaining stuff of this element, if any
SkipElement();
} else
{
ThrowException( "Unexpected sub element in tag \"accessor\".");
}
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "accessor") != 0)
ThrowException( "Expected end of \"accessor\" element.");
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads input declarations of per-vertex mesh data into the given mesh
void ColladaParser::ReadVertexData( Mesh* pMesh)
{
// extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
int attrID= GetAttribute( "id");
pMesh->mVertexID = mReader->getAttributeValue( attrID);
// a number of <input> elements
while( mReader->read())
{
if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if( IsElement( "input"))
{
ReadInputChannel( pMesh->mPerVertexData);
} else
{
ThrowException( "Unexpected sub element in tag \"vertices\".");
}
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "vertices") != 0)
ThrowException( "Expected end of \"vertices\" element.");
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads input declarations of per-index mesh data into the given mesh
void ColladaParser::ReadIndexData( Mesh* pMesh)
{
std::vector<size_t> vcount;
std::vector<InputChannel> perIndexData;
// read primitive count from the attribute
int attrCount = GetAttribute( "count");
size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
// distinguish between polys and triangles
std::string elementName = mReader->getNodeName();
bool isPolylist = IsElement( "polylist");
// also a number of <input> elements, but in addition a <p> primitive collection and propably index counts for all primitives
while( mReader->read())
{
if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
{
if( IsElement( "input"))
{
ReadInputChannel( perIndexData);
}
else if( IsElement( "vcount"))
{
// case <polylist> - specifies the number of indices for each polygon
const char* content = GetTextContent();
vcount.reserve( numPrimitives);
for( unsigned int a = 0; a < numPrimitives; a++)
{
if( *content == 0)
ThrowException( "Expected more values while reading vcount contents.");
// read a number
vcount.push_back( (size_t) strtol10( content, &content));
// skip whitespace after it
SkipSpacesAndLineEnd( &content);
}
TestClosing( "vcount");
}
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);
} else
{
ThrowException( "Unexpected sub element in tag \"vertices\".");
}
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( mReader->getNodeName() != elementName)
ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % elementName));
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Reads a single input channel element and stores it in the given array, if valid
void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
{
InputChannel channel;
// read semantic
int attrSemantic = GetAttribute( "semantic");
std::string semantic = mReader->getAttributeValue( attrSemantic);
channel.mType = GetTypeForSemantic( semantic);
// read source
int attrSource = GetAttribute( "source");
const char* source = mReader->getAttributeValue( attrSource);
if( source[0] != '#')
ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
// read index offset, if per-index <input>
int attrOffset = TestAttribute( "offset");
if( attrOffset > -1)
channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
// store, if valid type
if( channel.mType != IT_Invalid)
poChannels.push_back( channel);
// skip remaining stuff of this element, if any
SkipElement();
}
// ------------------------------------------------------------------------------------------------
// 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)
{
// determine number of indices coming per vertex
// find the offset index for all per-vertex channels
size_t numOffsets = 1;
size_t perVertexOffset = -1; // invalid value
BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels)
{
numOffsets = std::max( numOffsets, channel.mOffset+1);
if( channel.mType == IT_Vertex)
perVertexOffset = channel.mOffset;
}
// determine the expected number of indices
size_t expectedPointCount = 0;
if( pIsPolylist)
{
BOOST_FOREACH( size_t i, pVCount)
expectedPointCount += i;
} else
{
// everything triangles
expectedPointCount = 3 * pNumPrimitives;
}
// and read all indices into a temporary array
std::vector<size_t> indices( expectedPointCount * numOffsets);
const char* content = GetTextContent();
BOOST_FOREACH( size_t& value, indices)
{
if( *content == 0)
ThrowException( "Expected more values while reading primitive indices.");
// read a value in place
value = strtol10( content, &content);
// skip whitespace after it
SkipSpacesAndLineEnd( &content);
}
// find the data for all sources
BOOST_FOREACH( InputChannel& input, pMesh->mPerVertexData)
{
// find accessor
input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
// resolve accessor's data pointer as well, if neccessary
const Accessor* acc = input.mResolved;
if( !acc->mData)
acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
}
// and the same for the per-index channels
BOOST_FOREACH( InputChannel& input, pPerIndexChannels)
{
// ignore vertex pointer, it doesn't refer to an accessor
if( input.mType == IT_Vertex)
{
// warn if the vertex channel does not refer to the <vertices> element in the same mesh
if( input.mAccessor != pMesh->mVertexID)
ThrowException( "Unsupported vertex referencing scheme. I fucking hate Collada.");
continue;
}
// find accessor
input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
// resolve accessor's data pointer as well, if neccessary
const Accessor* acc = input.mResolved;
if( !acc->mData)
acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
}
// 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++)
{
// determine number of points for this primitive
size_t numPoints = 3;
if( pIsPolylist)
numPoints = pVCount[a];
// store the face size to later reconstruct the face from
pMesh->mFaceSize.push_back( numPoints);
// gather that number of vertices
for( size_t b = 0; b < numPoints; b++)
{
// read all indices for this vertex. Yes, in a hacky static array
assert( numOffsets < 20);
static size_t vindex[20];
for( size_t offsets = 0; offsets < numOffsets; ++offsets)
vindex[offsets] = *idx++;
// extract per-vertex channels using the global per-vertex offset
BOOST_FOREACH( const InputChannel& input, pMesh->mPerVertexData)
ExtractDataObjectFromChannel( input, vindex[perVertexOffset], pMesh);
// and extract per-index channels using there specified offset
BOOST_FOREACH( const InputChannel& input, pPerIndexChannels)
ExtractDataObjectFromChannel( input, vindex[input.mOffset], pMesh);
}
}
// if I ever get my hands on that guy how invented this steaming pile of indirection...
TestClosing( "p");
}
// ------------------------------------------------------------------------------------------------
// Extracts a single object from an input channel and stores it in the appropriate mesh data array
void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
{
// ignore vertex referrer - we handle them that separate
if( pInput.mType == IT_Vertex)
return;
const Accessor& acc = *pInput.mResolved;
if( pLocalIndex >= acc.mCount)
ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount));
// get a pointer to the start of the data object referred to by the accessor and the local index
const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
// assemble according to the accessors component sub-offset list. We don't care, yet, what kind of object exactly we're extracting here
float obj[4];
for( size_t c = 0; c < 4; ++c)
obj[c] = dataObject[acc.mSubOffset[c]];
// now we reinterpret it according to the type we're reading here
switch( pInput.mType)
{
case IT_Position: // ignore all position streams except 0 - there can be only one position
if( pInput.mIndex == 0)
pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
break;
case IT_Normal: // ignore all normal streams except 0 - there can be only one normal
if( pInput.mIndex == 0)
pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
break;
case IT_Texcoord: // up to 4 texture coord sets are fine, ignore the others
if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
pMesh->mTexCoords[pInput.mIndex].push_back( aiVector2D( obj[0], obj[1]));
break;
case IT_Color: // up to 4 color sets are fine, ignore the others
if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
pMesh->mColors[pInput.mIndex].push_back( aiColor4D( obj[0], obj[1], obj[2], obj[3]));
break;
}
}
// ------------------------------------------------------------------------------------------------
// Reads the library of node hierarchies and scene parts
void ColladaParser::ReadSceneLibrary()
@@ -214,6 +703,9 @@ void ColladaParser::ReadSceneLibrary()
}
else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
{
if( strcmp( mReader->getNodeName(), "library_visual_scenes") != 0)
ThrowException( "Expected end of \"library_visual_scenes\" element.");
break;
}
}
@@ -371,6 +863,22 @@ void ColladaParser::SkipElement()
}
}
// ------------------------------------------------------------------------------------------------
// Tests for an opening element of the given name, throws an exception if not found
void ColladaParser::TestOpening( const char* pName)
{
// read element start
if( !mReader->read())
ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of \"%s\" element.") % pName));
// whitespace in front is ok, just read again if found
if( mReader->getNodeType() == irr::io::EXN_TEXT)
if( !mReader->read())
ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of \"%s\" element.") % pName));
if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
ThrowException( boost::str( boost::format( "Expected start of \"%s\" element.") % pName));
}
// ------------------------------------------------------------------------------------------------
// Tests for the closing tag of the given element, throws an exception if not found
void ColladaParser::TestClosing( const char* pName)
@@ -378,6 +886,11 @@ void ColladaParser::TestClosing( const char* pName)
// read closing tag
if( !mReader->read())
ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
// whitespace in front is ok, just read again if found
if( mReader->getNodeType() == irr::io::EXN_TEXT)
if( !mReader->read())
ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % pName));
}
@@ -482,3 +995,22 @@ aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform
return res;
}
// ------------------------------------------------------------------------------------------------
// Determines the input data type for the given semantic string
ColladaParser::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
{
if( pSemantic == "POSITION")
return IT_Position;
else if( pSemantic == "TEXCOORD")
return IT_Texcoord;
else if( pSemantic == "NORMAL")
return IT_Normal;
else if( pSemantic == "COLOR")
return IT_Color;
else if( pSemantic == "VERTEX")
return IT_Vertex;
DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic));
return IT_Invalid;
}