diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp new file mode 100644 index 000000000..9d5dbd313 --- /dev/null +++ b/code/ColladaLoader.cpp @@ -0,0 +1,116 @@ +/** Implementation of 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. +--------------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" +#include "../include/aiAnim.h" +#include "ColladaLoader.h" +#include "ColladaParser.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ColladaLoader::ColladaLoader() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ColladaLoader::~ColladaLoader() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler) const +{ + // check file extension + std::string::size_type pos = pFile.find_last_of('.'); + // no file extension - can't read + if( pos == std::string::npos) + return false; + std::string extension = pFile.substr( pos); + for( std::string::iterator it = extension.begin(); it != extension.end(); ++it) + *it = tolower( *it); + + if( extension == ".dae") + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + mFileName = pFile; + + // parse the input file + ColladaParser parser( pFile); + + // build the node hierarchy from it + pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode); + + pScene->mFlags = AI_SCENE_FLAGS_INCOMPLETE; +} + +// ------------------------------------------------------------------------------------------------ +// Recursively constructs a scene node for the given parser node and returns it. +aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const ColladaParser::Node* pNode) +{ + // create a node for it + aiNode* node = new aiNode( pNode->mName); + + // calculate the transformation matrix for it + node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms); + + // add children + node->mNumChildren = pNode->mChildren.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + for( unsigned int a = 0; a < pNode->mChildren.size(); a++) + { + node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]); + node->mChildren[a]->mParent = node; + } + + return node; +} diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h new file mode 100644 index 000000000..7ccbb0df6 --- /dev/null +++ b/code/ColladaLoader.h @@ -0,0 +1,95 @@ +/** Defines the collada loader class */ + +/* +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_COLLADALOADER_H_INC +#define AI_COLLADALOADER_H_INC + +#include "BaseImporter.h" +#include "ColladaParser.h" + +namespace Assimp +{ + +/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing + * more useless stuff, so I limited the data to what I think is useful for games. +*/ +class ColladaLoader : public BaseImporter +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + ColladaLoader(); + + /** Destructor, private as well */ + ~ColladaLoader(); + +public: + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + +protected: + /** Called by Importer::GetExtensionList() for each loaded importer. + * See BaseImporter::GetExtensionList() for details + */ + void GetExtensionList( std::string& append) + { + append.append("*.dae"); + } + + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + 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); + +protected: + /** Filename, for a verbose error message */ + std::string mFileName; +}; + +} // end of namespace Assimp + +#endif // AI_COLLADALOADER_H_INC diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp new file mode 100644 index 000000000..02beb3d16 --- /dev/null +++ b/code/ColladaParser.cpp @@ -0,0 +1,484 @@ +/** Implementation of the Collada parser helper*/ +/* +--------------------------------------------------------------------------- +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. +--------------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" +#include "ColladaParser.h" +#include "fast_atof.h" +#include "ParsingUtils.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ColladaParser::ColladaParser( const std::string& pFile) + : mFileName( pFile) +{ + mRootNode = NULL; + mUnitSize = 1.0f; + mUpDirection = UP_Z; + + // generate a XML reader for it + mReader = irr::io::createIrrXMLReader( pFile.c_str()); + if( !mReader) + ThrowException( "Unable to open file."); + + // start reading + ReadContents(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ColladaParser::~ColladaParser() +{ + delete mReader; + for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) + delete it->second; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the contents of the file +void ColladaParser::ReadContents() +{ + while( mReader->read()) + { + // handle the root element "COLLADA" + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "COLLADA")) + { + ReadStructure(); + } else + { + DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element \"%s\".") % mReader->getNodeName())); + SkipElement(); + } + } else + { + // skip everything else silently + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the structure of the file +void ColladaParser::ReadStructure() +{ + while( mReader->read()) + { + // beginning of elements + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "asset")) + ReadAssetInfo(); + else if( IsElement( "library_geometries")) + ReadGeometryLibrary(); + else if( IsElement( "library_visual_scenes")) + ReadSceneLibrary(); + else if( IsElement( "scene")) + ReadScene(); + else + SkipElement(); + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads asset informations such as coordinate system informations and legal blah +void ColladaParser::ReadAssetInfo() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "unit")) + { + // read unit data from the element's attributes + int attrIndex = GetAttribute( "meter"); + mUnitSize = mReader->getAttributeValueAsFloat( attrIndex); + + // consume the trailing stuff + if( !mReader->isEmptyElement()) + SkipElement(); + } + else if( IsElement( "up_axis")) + { + // read content, strip whitespace, compare + const char* content = GetTextContent(); + if( strncmp( content, "X_UP", 4) == 0) + mUpDirection = UP_X; + else if( strncmp( content, "Y_UP", 4) == 0) + mUpDirection = UP_Y; + else + mUpDirection = UP_Z; + + // check element end + TestClosing( "up_axis"); + } else + { + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the geometry library contents +void ColladaParser::ReadGeometryLibrary() +{ + SkipElement(); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the library of node hierarchies and scene parts +void ColladaParser::ReadSceneLibrary() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + // a visual scene - generate root node under its ID and let ReadNode() do the recursive work + if( IsElement( "visual_scene")) + { + // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then? + int indexID = GetAttribute( "id"); + const char* attrID = mReader->getAttributeValue( indexID); + + // read name if given. + int indexName = TestAttribute( "name"); + const char* attrName = "unnamed"; + if( indexName > -1) + attrName = mReader->getAttributeValue( indexName); + + // TODO: (thom) support SIDs + assert( TestAttribute( "sid") == -1); + + // create a node and store it in the library under its ID + Node* node = new Node; + node->mID = attrID; + node->mName = attrName; + mNodeLibrary[node->mID] = node; + + ReadSceneNode( node); + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a scene node's contents including children and stores it in the given node +void ColladaParser::ReadSceneNode( Node* pNode) +{ + // quit immediately on elements + if( mReader->isEmptyElement()) + return; + + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "lookat")) + ReadNodeTransformation( pNode, TF_LOOKAT); + else if( IsElement( "matrix")) + ReadNodeTransformation( pNode, TF_MATRIX); + else if( IsElement( "rotate")) + ReadNodeTransformation( pNode, TF_ROTATE); + else if( IsElement( "scale")) + ReadNodeTransformation( pNode, TF_SCALE); + else if( IsElement( "skew")) + ReadNodeTransformation( pNode, TF_SKEW); + else if( IsElement( "translate")) + ReadNodeTransformation( pNode, TF_TRANSLATE); + else if( IsElement( "node")) + { + Node* child = new Node; + int attrID = TestAttribute( "id"); + if( attrID > -1) + child->mID = mReader->getAttributeValue( attrID); + + int attrName = TestAttribute( "name"); + if( attrName > -1) + child->mName = mReader->getAttributeValue( attrName); + + // TODO: (thom) support SIDs + assert( TestAttribute( "sid") == -1); + + pNode->mChildren.push_back( child); + child->mParent = pNode; + + // read on recursively from there + ReadSceneNode( child); + } else if( IsElement( "instance_node")) + { + // test for it, in case we need to implement it + assert( false); + SkipElement(); + } else + { + // skip everything else for the moment + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a node transformation entry of the given type and adds it to the given node's transformation list. +void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) +{ + std::string tagName = mReader->getNodeName(); + + // how many parameters to read per transformation type + static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; + const char* content = GetTextContent(); + + // read as many parameters and store in the transformation + Transform tf; + tf.mType = pType; + for( unsigned int a = 0; a < sNumParameters[pType]; a++) + { + // read a number + content = fast_atof_move( content, tf.f[a]); + // skip whitespace after it + SkipSpacesAndLineEnd( &content); + } + + // place the transformation at the queue of the node + pNode->mTransforms.push_back( tf); + + // and consum the closing tag + TestClosing( tagName.c_str()); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the collada scene +void ColladaParser::ReadScene() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "instance_visual_scene")) + { + // should be the first and only occurence + if( mRootNode) + ThrowException( "Invalid scene containing multiple root nodes"); + + // read the url of the scene to instance. Should be of format "#some_name" + int urlIndex = GetAttribute( "url"); + const char* url = mReader->getAttributeValue( urlIndex); + if( url[0] != '#') + ThrowException( "Unknown reference format"); + + // find the referred scene, skip the leading # + NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1); + if( sit == mNodeLibrary.end()) + ThrowException( boost::str( boost::format( "Unable to resolve visual_scene reference \"%s\".") % url)); + mRootNode = sit->second; + } else + { + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Aborts the file reading with an exception +void ColladaParser::ThrowException( const std::string& pError) const +{ + throw new ImportErrorException( boost::str( boost::format( "%s - %s") % mFileName % pError)); +} + +// ------------------------------------------------------------------------------------------------ +// Skips all data until the end node of the current element +void ColladaParser::SkipElement() +{ + // nothing to skip if it's an + if( mReader->isEmptyElement()) + return; + + // copy the current node's name because it'a pointer to the reader's internal buffer, + // which is going to change with the upcoming parsing + std::string element = mReader->getNodeName(); + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + if( mReader->getNodeName() == element) + break; + } +} + +// ------------------------------------------------------------------------------------------------ +// Tests for the closing tag of the given element, throws an exception if not found +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)); + if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0) + ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % pName)); +} + +// ------------------------------------------------------------------------------------------------ +// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes +int ColladaParser::GetAttribute( const char* pAttr) const +{ + int index = TestAttribute( pAttr); + if( index != -1) + return index; + + // attribute not found -> throw an exception + ThrowException( boost::str( boost::format( "Expected attribute \"%s\" at element \"%s\".") % pAttr % mReader->getNodeName())); + return -1; +} + +// ------------------------------------------------------------------------------------------------ +// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found +int ColladaParser::TestAttribute( const char* pAttr) const +{ + for( int a = 0; a < mReader->getAttributeCount(); a++) + if( strcmp( mReader->getAttributeName( a), pAttr) == 0) + return a; + + return -1; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. +const char* ColladaParser::GetTextContent() +{ + // present node should be the beginning of an element + if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) + ThrowException( "Expected opening element"); + + // read contents of the element + if( !mReader->read()) + ThrowException( "Unexpected end of file while reading asset up_axis element."); + if( mReader->getNodeType() != irr::io::EXN_TEXT) + ThrowException( "Invalid contents in element \"up_axis\"."); + + // skip leading whitespace + const char* text = mReader->getNodeData(); + SkipSpacesAndLineEnd( &text); + + return text; +} + +// ------------------------------------------------------------------------------------------------ +// Calculates the resulting transformation fromm all the given transform steps +aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector& pTransforms) const +{ + aiMatrix4x4 res; + + for( std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) + { + const Transform& tf = *it; + switch( tf.mType) + { + case TF_LOOKAT: + // TODO: (thom) + assert( false); + break; + case TF_ROTATE: + { + aiMatrix4x4 rot; + aiMatrix4x4::Rotation( tf.f[3], aiVector3D( tf.f[0], tf.f[1], tf.f[2]), rot); + res *= rot; + break; + } + case TF_TRANSLATE: + { + aiMatrix4x4 trans; + aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans); + res *= trans; + break; + } + case TF_SCALE: + { + aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + res *= scale; + break; + } + case TF_SKEW: + // TODO: (thom) + assert( false); + break; + case TF_MATRIX: + { + aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7], + tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]); + res *= mat; + break; + } + default: + assert( false); + break; + } + } + + return res; +} diff --git a/code/ColladaParser.h b/code/ColladaParser.h new file mode 100644 index 000000000..177e2580f --- /dev/null +++ b/code/ColladaParser.h @@ -0,0 +1,194 @@ +/** Defines the parser helper class 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_COLLADAPARSER_H_INC +#define AI_COLLADAPARSER_H_INC + +#include "./irrXML/irrXMLWrapper.h" + +namespace Assimp +{ + +/** Parser helper class for the Collada loader. Does all the XML reading and builds internal data structures from it, + * but leaves the resolving of all the references to the loader. +*/ +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 mChildren; + + /** Operations in order to calculate the resulting transformation to parent. */ + std::vector mTransforms; + + Node() { mParent = NULL; } + ~Node() { for( std::vector::iterator it = mChildren.begin(); it != mChildren.end(); ++it) delete *it; } + }; + + /** Data source array */ + struct Data + { + std::vector mValues; + }; + + /** Accessor to a data array */ + struct Accessor + { + unsigned int mCount; // in number of objects + unsigned int mOffset; // in number of values + unsigned int mStride; // Stride in number of values + std::string mSource; // URL of the source array + }; + + /** Contains data for a single mesh */ + struct Mesh + { + + }; + +protected: + /** Constructor from XML file */ + ColladaParser( const std::string& pFile); + + /** Destructor */ + ~ColladaParser(); + + /** Reads the contents of the file */ + void ReadContents(); + + /** Reads the structure of the file */ + void ReadStructure(); + + /** Reads asset informations such as coordinate system informations and legal blah */ + void ReadAssetInfo(); + + /** Reads the geometry library contents */ + void ReadGeometryLibrary(); + + /** Reads a mesh from the geometry library */ + void ReadGeometry(); + + /** 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); + + /** 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); + + /** Reads the collada scene */ + void ReadScene(); + +protected: + /** Aborts the file reading with an exception */ + void ThrowException( const std::string& pError) const; + + /** Skips all data until the end node of the current element */ + void SkipElement(); + + /** Compares the current xml element name to the given string and returns true if equal */ + bool IsElement( const char* pName) const { assert( mReader->getNodeType() == irr::io::EXN_ELEMENT); return strcmp( mReader->getNodeName(), pName) == 0; } + + /** Tests for the closing tag of the given element, throws an exception if not found */ + void TestClosing( const char* pName); + + /** Checks the present element for the presence of the attribute, returns its index or throws an exception if not found */ + int GetAttribute( const char* pAttr) const; + + /** Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes */ + int TestAttribute( const char* pAttr) const; + + /** Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. */ + const char* GetTextContent(); + + /** Calculates the resulting transformation fromm all the given transform steps */ + aiMatrix4x4 CalculateResultTransform( const std::vector& pTransforms) const; + +protected: + /** Filename, for a verbose error message */ + std::string mFileName; + + /** XML reader */ + irr::io::IrrXMLReader* mReader; + + /** node library: root node of the hierarchy part by ID */ + typedef std::map 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; + + /** Size unit: how large compared to a meter */ + float mUnitSize; + + /** Which is the up vector */ + enum { UP_X, UP_Y, UP_Z } mUpDirection; +}; + +} // end of namespace Assimp + +#endif // AI_COLLADAPARSER_H_INC diff --git a/code/Importer.cpp b/code/Importer.cpp index 513795095..cdfe25d6c 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -129,6 +129,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_BUILD_NO_B3D_IMPORTER # include "B3DImporter.h" #endif +#ifndef AI_BUILD_NO_COLLADA_IMPORTER +# include "ColladaLoader.h" +#endif // PostProcess-Steps @@ -283,6 +286,9 @@ Importer::Importer() : #if (!defined AI_BUILD_NO_B3D_IMPORTER) mImporter.push_back( new B3DImporter()); #endif +#if (!defined AI_BUILD_NO_COLLADA_IMPORTER) + mImporter.push_back( new ColladaLoader()); +#endif // add an instance of each post processing step here in the order // of sequence it is executed. steps that are added here are not validated - diff --git a/include/aiMatrix4x4.h b/include/aiMatrix4x4.h index aef13d77a..5ba02468d 100644 --- a/include/aiMatrix4x4.h +++ b/include/aiMatrix4x4.h @@ -142,6 +142,14 @@ struct aiMatrix4x4 */ static aiMatrix4x4& RotationZ(float a, aiMatrix4x4& out); + /** Returns a rotation matrix for a rotation around an arbitrary axis. + * @param a Rotation angle, in radians + * @param axis Rotation axis, should be a normalized vector. + * @param out Receives the output matrix + * \return Reference to the output matrix + */ + static aiMatrix4x4& Rotation(float a, const aiVector3D& axis, aiMatrix4x4& out); + /** \brief Returns a translation matrix * \param v Translation vector * \param out Receives the output matrix diff --git a/include/aiMatrix4x4.inl b/include/aiMatrix4x4.inl index 04294a667..6fcd0dce0 100644 --- a/include/aiMatrix4x4.inl +++ b/include/aiMatrix4x4.inl @@ -282,6 +282,25 @@ inline aiMatrix4x4& aiMatrix4x4::RotationZ(float a, aiMatrix4x4& out) out.a2 = -(out.b1 = sin(a)); return out; } + +// --------------------------------------------------------------------------- +// Returns a rotation matrix for a rotation around an arbitrary axis. +inline aiMatrix4x4& aiMatrix4x4::Rotation( float a, const aiVector3D& axis, aiMatrix4x4& out) +{ + float c = cos( a), s = sin( a), t = 1 - c; + float x = axis.x, y = axis.y, z = axis.z; + + // Many thanks to MathWorld and Wikipedia + out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y; + out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x; + out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c; + out.a4 = out.b4 = out.c4 = 0.0f; + out.d1 = out.d2 = out.d3 = 0.0f; + out.d4 = 1.0f; + + return out; +} + // --------------------------------------------------------------------------- inline aiMatrix4x4& aiMatrix4x4::Translation( const aiVector3D& v, aiMatrix4x4& out) { diff --git a/workspaces/vc8/assimp.sln b/workspaces/vc8/assimp.sln index 948c9fc3e..633a238c9 100644 --- a/workspaces/vc8/assimp.sln +++ b/workspaces/vc8/assimp.sln @@ -76,30 +76,21 @@ Global {5691E159-2D9B-407F-971F-EA5C592DC524}.Release|x64.ActiveCfg = Release|x64 {5691E159-2D9B-407F-971F-EA5C592DC524}.Release|x64.Build.0 = Release|x64 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug_DLL|Win32.ActiveCfg = Debug_DLL|Win32 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug_DLL|Win32.Build.0 = Debug_DLL|Win32 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug|Win32.ActiveCfg = Debug|Win32 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug|x64.ActiveCfg = Debug|x64 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Debug|x64.Build.0 = Debug|x64 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release_DLL|Win32.ActiveCfg = Release_DLL|Win32 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release_DLL|Win32.Build.0 = Release_DLL|Win32 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release_DLL|x64.Build.0 = Release_DLL|x64 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release|Win32.ActiveCfg = Release|Win32 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release|Win32.Build.0 = Release|Win32 {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release|x64.ActiveCfg = Release|x64 - {9B9D1C90-8A03-409A-B547-AE7B48B90F1A}.Release|x64.Build.0 = Release|x64 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Debug_DLL|Win32.ActiveCfg = Debug|Win32 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Debug_DLL|x64.ActiveCfg = Debug|x64 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Debug|Win32.ActiveCfg = Debug|Win32 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Debug|x64.ActiveCfg = Debug|x64 - {FE78BFBA-4BA5-457D-8602-B800D498102D}.Debug|x64.Build.0 = Debug|x64 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Release_DLL|Win32.ActiveCfg = Release|Win32 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Release_DLL|x64.ActiveCfg = Release|x64 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Release|Win32.ActiveCfg = Release|Win32 {FE78BFBA-4BA5-457D-8602-B800D498102D}.Release|x64.ActiveCfg = Release|x64 - {FE78BFBA-4BA5-457D-8602-B800D498102D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/workspaces/vc8/assimp.vcproj b/workspaces/vc8/assimp.vcproj index 5d1a25735..845a98353 100644 --- a/workspaces/vc8/assimp.vcproj +++ b/workspaces/vc8/assimp.vcproj @@ -1422,6 +1422,26 @@ > + + + + + + + + + +