nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file ColladaParser.cpp nuclear@0: * @brief Implementation of the Collada parser helper nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER nuclear@0: nuclear@0: #include "ColladaParser.h" nuclear@0: #include "fast_atof.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::Collada; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) nuclear@0: : mFileName( pFile) nuclear@0: { nuclear@0: mRootNode = NULL; nuclear@0: mUnitSize = 1.0f; nuclear@0: mUpDirection = UP_Z; nuclear@0: nuclear@0: // We assume the newest file format by default nuclear@0: mFormat = FV_1_5_n; nuclear@0: nuclear@0: // open the file nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile)); nuclear@0: if( file.get() == NULL) nuclear@0: throw DeadlyImportError( "Failed to open file " + pFile + "."); nuclear@0: nuclear@0: // generate a XML reader for it nuclear@0: boost::scoped_ptr mIOWrapper( new CIrrXML_IOStreamReader( file.get())); nuclear@0: mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); nuclear@0: if( !mReader) nuclear@0: ThrowException( "Collada: Unable to open file."); nuclear@0: nuclear@0: // start reading nuclear@0: ReadContents(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: ColladaParser::~ColladaParser() nuclear@0: { nuclear@0: delete mReader; nuclear@0: for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it) nuclear@0: delete it->second; nuclear@0: for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it) nuclear@0: delete it->second; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read bool from text contents of current element nuclear@0: bool ColladaParser::ReadBoolFromTextContent() nuclear@0: { nuclear@0: const char* cur = GetTextContent(); nuclear@0: return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read float from text contents of current element nuclear@0: float ColladaParser::ReadFloatFromTextContent() nuclear@0: { nuclear@0: const char* cur = GetTextContent(); nuclear@0: return fast_atof(cur); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the contents of the file nuclear@0: void ColladaParser::ReadContents() nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: // handle the root element "COLLADA" nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "COLLADA")) nuclear@0: { nuclear@0: // check for 'version' attribute nuclear@0: const int attrib = TestAttribute("version"); nuclear@0: if (attrib != -1) { nuclear@0: const char* version = mReader->getAttributeValue(attrib); nuclear@0: nuclear@0: if (!::strncmp(version,"1.5",3)) { nuclear@0: mFormat = FV_1_5_n; nuclear@0: DefaultLogger::get()->debug("Collada schema version is 1.5.n"); nuclear@0: } nuclear@0: else if (!::strncmp(version,"1.4",3)) { nuclear@0: mFormat = FV_1_4_n; nuclear@0: DefaultLogger::get()->debug("Collada schema version is 1.4.n"); nuclear@0: } nuclear@0: else if (!::strncmp(version,"1.3",3)) { nuclear@0: mFormat = FV_1_3_n; nuclear@0: DefaultLogger::get()->debug("Collada schema version is 1.3.n"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: ReadStructure(); nuclear@0: } else nuclear@0: { nuclear@0: DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element <%s>.") % mReader->getNodeName())); nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } else nuclear@0: { nuclear@0: // skip everything else silently nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the structure of the file nuclear@0: void ColladaParser::ReadStructure() nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: // beginning of elements nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "asset")) nuclear@0: ReadAssetInfo(); nuclear@0: else if( IsElement( "library_animations")) nuclear@0: ReadAnimationLibrary(); nuclear@0: else if( IsElement( "library_controllers")) nuclear@0: ReadControllerLibrary(); nuclear@0: else if( IsElement( "library_images")) nuclear@0: ReadImageLibrary(); nuclear@0: else if( IsElement( "library_materials")) nuclear@0: ReadMaterialLibrary(); nuclear@0: else if( IsElement( "library_effects")) nuclear@0: ReadEffectLibrary(); nuclear@0: else if( IsElement( "library_geometries")) nuclear@0: ReadGeometryLibrary(); nuclear@0: else if( IsElement( "library_visual_scenes")) nuclear@0: ReadSceneLibrary(); nuclear@0: else if( IsElement( "library_lights")) nuclear@0: ReadLightLibrary(); nuclear@0: else if( IsElement( "library_cameras")) nuclear@0: ReadCameraLibrary(); nuclear@0: else if( IsElement( "library_nodes")) nuclear@0: ReadSceneNode(NULL); /* some hacking to reuse this piece of code */ nuclear@0: else if( IsElement( "scene")) nuclear@0: ReadScene(); nuclear@0: else nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads asset informations such as coordinate system informations and legal blah nuclear@0: void ColladaParser::ReadAssetInfo() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "unit")) nuclear@0: { nuclear@0: // read unit data from the element's attributes nuclear@0: const int attrIndex = TestAttribute( "meter"); nuclear@0: if (attrIndex == -1) { nuclear@0: mUnitSize = 1.f; nuclear@0: } nuclear@0: else { nuclear@0: mUnitSize = mReader->getAttributeValueAsFloat( attrIndex); nuclear@0: } nuclear@0: nuclear@0: // consume the trailing stuff nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "up_axis")) nuclear@0: { nuclear@0: // read content, strip whitespace, compare nuclear@0: const char* content = GetTextContent(); nuclear@0: if( strncmp( content, "X_UP", 4) == 0) nuclear@0: mUpDirection = UP_X; nuclear@0: else if( strncmp( content, "Y_UP", 4) == 0) nuclear@0: mUpDirection = UP_Y; nuclear@0: else nuclear@0: mUpDirection = UP_Z; nuclear@0: nuclear@0: // check element end nuclear@0: TestClosing( "up_axis"); nuclear@0: } else nuclear@0: { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "asset") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the animation library nuclear@0: void ColladaParser::ReadAnimationLibrary() nuclear@0: { nuclear@0: if (mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "animation")) nuclear@0: { nuclear@0: // delegate the reading. Depending on the inner elements it will be a container or a anim channel nuclear@0: ReadAnimation( &mAnims); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "library_animations") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an animation into the given parent structure nuclear@0: void ColladaParser::ReadAnimation( Collada::Animation* pParent) nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: // an element may be a container for grouping sub-elements or an animation channel nuclear@0: // this is the channel collection by ID, in case it has channels nuclear@0: typedef std::map ChannelMap; nuclear@0: ChannelMap channels; nuclear@0: // this is the anim container in case we're a container nuclear@0: Animation* anim = NULL; nuclear@0: nuclear@0: // optional name given as an attribute nuclear@0: std::string animName; nuclear@0: int indexName = TestAttribute( "name"); nuclear@0: int indexID = TestAttribute( "id"); nuclear@0: if( indexName >= 0) nuclear@0: animName = mReader->getAttributeValue( indexName); nuclear@0: else if( indexID >= 0) nuclear@0: animName = mReader->getAttributeValue( indexID); nuclear@0: else nuclear@0: animName = "animation"; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: // we have subanimations nuclear@0: if( IsElement( "animation")) nuclear@0: { nuclear@0: // create container from our element nuclear@0: if( !anim) nuclear@0: { nuclear@0: anim = new Animation; nuclear@0: anim->mName = animName; nuclear@0: pParent->mSubAnims.push_back( anim); nuclear@0: } nuclear@0: nuclear@0: // recurse into the subelement nuclear@0: ReadAnimation( anim); nuclear@0: } nuclear@0: else if( IsElement( "source")) nuclear@0: { nuclear@0: // possible animation data - we'll never know. Better store it nuclear@0: ReadSource(); nuclear@0: } nuclear@0: else if( IsElement( "sampler")) nuclear@0: { nuclear@0: // read the ID to assign the corresponding collada channel afterwards. nuclear@0: int indexID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( indexID); nuclear@0: ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first; nuclear@0: nuclear@0: // have it read into a channel nuclear@0: ReadAnimationSampler( newChannel->second); nuclear@0: } nuclear@0: else if( IsElement( "channel")) nuclear@0: { nuclear@0: // the binding element whose whole purpose is to provide the target to animate nuclear@0: // Thanks, Collada! A directly posted information would have been too simple, I guess. nuclear@0: // Better add another indirection to that! Can't have enough of those. nuclear@0: int indexTarget = GetAttribute( "target"); nuclear@0: int indexSource = GetAttribute( "source"); nuclear@0: const char* sourceId = mReader->getAttributeValue( indexSource); nuclear@0: if( sourceId[0] == '#') nuclear@0: sourceId++; nuclear@0: ChannelMap::iterator cit = channels.find( sourceId); nuclear@0: if( cit != channels.end()) nuclear@0: cit->second.mTarget = mReader->getAttributeValue( indexTarget); nuclear@0: nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "animation") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // it turned out to have channels - add them nuclear@0: if( !channels.empty()) nuclear@0: { nuclear@0: // special filtering for stupid exporters packing each channel into a separate animation nuclear@0: if( channels.size() == 1) nuclear@0: { nuclear@0: pParent->mChannels.push_back( channels.begin()->second); nuclear@0: } else nuclear@0: { nuclear@0: // else create the animation, if not done yet, and store the channels nuclear@0: if( !anim) nuclear@0: { nuclear@0: anim = new Animation; nuclear@0: anim->mName = animName; nuclear@0: pParent->mSubAnims.push_back( anim); nuclear@0: } nuclear@0: for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) nuclear@0: anim->mChannels.push_back( it->second); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an animation sampler into the given anim channel nuclear@0: void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "input")) nuclear@0: { nuclear@0: int indexSemantic = GetAttribute( "semantic"); nuclear@0: const char* semantic = mReader->getAttributeValue( indexSemantic); nuclear@0: int indexSource = GetAttribute( "source"); nuclear@0: const char* source = mReader->getAttributeValue( indexSource); nuclear@0: if( source[0] != '#') nuclear@0: ThrowException( "Unsupported URL format"); nuclear@0: source++; nuclear@0: nuclear@0: if( strcmp( semantic, "INPUT") == 0) nuclear@0: pChannel.mSourceTimes = source; nuclear@0: else if( strcmp( semantic, "OUTPUT") == 0) nuclear@0: pChannel.mSourceValues = source; nuclear@0: nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "sampler") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the skeleton controller library nuclear@0: void ColladaParser::ReadControllerLibrary() nuclear@0: { nuclear@0: if (mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "controller")) nuclear@0: { nuclear@0: // read ID. Ask the spec if it's neccessary or optional... you might be surprised. nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: mControllerLibrary[id] = Controller(); nuclear@0: nuclear@0: // read on from there nuclear@0: ReadController( mControllerLibrary[id]); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "library_controllers") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a controller into the given mesh structure nuclear@0: void ColladaParser::ReadController( Collada::Controller& pController) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other nuclear@0: if( IsElement( "morph")) nuclear@0: { nuclear@0: // should skip everything inside, so there's no danger of catching elements inbetween nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "skin")) nuclear@0: { nuclear@0: // read the mesh it refers to. According to the spec this could also be another nuclear@0: // controller, but I refuse to implement every single idea they've come up with nuclear@0: int sourceIndex = GetAttribute( "source"); nuclear@0: pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1; nuclear@0: } nuclear@0: else if( IsElement( "bind_shape_matrix")) nuclear@0: { nuclear@0: // content is 16 floats to define a matrix... it seems to be important for some models nuclear@0: const char* content = GetTextContent(); nuclear@0: nuclear@0: // read the 16 floats nuclear@0: for( unsigned int a = 0; a < 16; a++) nuclear@0: { nuclear@0: // read a number nuclear@0: content = fast_atoreal_move( content, pController.mBindShapeMatrix[a]); nuclear@0: // skip whitespace after it nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: nuclear@0: TestClosing( "bind_shape_matrix"); nuclear@0: } nuclear@0: else if( IsElement( "source")) nuclear@0: { nuclear@0: // data array - we have specialists to handle this nuclear@0: ReadSource(); nuclear@0: } nuclear@0: else if( IsElement( "joints")) nuclear@0: { nuclear@0: ReadControllerJoints( pController); nuclear@0: } nuclear@0: else if( IsElement( "vertex_weights")) nuclear@0: { nuclear@0: ReadControllerWeights( pController); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "controller") == 0) nuclear@0: break; nuclear@0: else if( strcmp( mReader->getNodeName(), "skin") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the joint definitions for the given controller nuclear@0: void ColladaParser::ReadControllerJoints( Collada::Controller& pController) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX" nuclear@0: if( IsElement( "input")) nuclear@0: { nuclear@0: int indexSemantic = GetAttribute( "semantic"); nuclear@0: const char* attrSemantic = mReader->getAttributeValue( indexSemantic); nuclear@0: int indexSource = GetAttribute( "source"); nuclear@0: const char* attrSource = mReader->getAttributeValue( indexSource); nuclear@0: nuclear@0: // local URLS always start with a '#'. We don't support global URLs nuclear@0: if( attrSource[0] != '#') nuclear@0: ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); nuclear@0: attrSource++; nuclear@0: nuclear@0: // parse source URL to corresponding source nuclear@0: if( strcmp( attrSemantic, "JOINT") == 0) nuclear@0: pController.mJointNameSource = attrSource; nuclear@0: else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0) nuclear@0: pController.mJointOffsetMatrixSource = attrSource; nuclear@0: else nuclear@0: ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); nuclear@0: nuclear@0: // skip inner data, if present nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "joints") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the joint weights for the given controller nuclear@0: void ColladaParser::ReadControllerWeights( Collada::Controller& pController) nuclear@0: { nuclear@0: // read vertex count from attributes and resize the array accordingly nuclear@0: int indexCount = GetAttribute( "count"); nuclear@0: size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount); nuclear@0: pController.mWeightCounts.resize( vertexCount); nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT" nuclear@0: if( IsElement( "input") && vertexCount > 0 ) nuclear@0: { nuclear@0: InputChannel channel; nuclear@0: nuclear@0: int indexSemantic = GetAttribute( "semantic"); nuclear@0: const char* attrSemantic = mReader->getAttributeValue( indexSemantic); nuclear@0: int indexSource = GetAttribute( "source"); nuclear@0: const char* attrSource = mReader->getAttributeValue( indexSource); nuclear@0: int indexOffset = TestAttribute( "offset"); nuclear@0: if( indexOffset >= 0) nuclear@0: channel.mOffset = mReader->getAttributeValueAsInt( indexOffset); nuclear@0: nuclear@0: // local URLS always start with a '#'. We don't support global URLs nuclear@0: if( attrSource[0] != '#') nuclear@0: ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of data element") % attrSource)); nuclear@0: channel.mAccessor = attrSource + 1; nuclear@0: nuclear@0: // parse source URL to corresponding source nuclear@0: if( strcmp( attrSemantic, "JOINT") == 0) nuclear@0: pController.mWeightInputJoints = channel; nuclear@0: else if( strcmp( attrSemantic, "WEIGHT") == 0) nuclear@0: pController.mWeightInputWeights = channel; nuclear@0: else nuclear@0: ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in data element") % attrSemantic)); nuclear@0: nuclear@0: // skip inner data, if present nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "vcount") && vertexCount > 0 ) nuclear@0: { nuclear@0: // read weight count per vertex nuclear@0: const char* text = GetTextContent(); nuclear@0: size_t numWeights = 0; nuclear@0: for( std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) nuclear@0: { nuclear@0: if( *text == 0) nuclear@0: ThrowException( "Out of data while reading "); nuclear@0: nuclear@0: *it = strtoul10( text, &text); nuclear@0: numWeights += *it; nuclear@0: SkipSpacesAndLineEnd( &text); nuclear@0: } nuclear@0: nuclear@0: TestClosing( "vcount"); nuclear@0: nuclear@0: // reserve weight count nuclear@0: pController.mWeights.resize( numWeights); nuclear@0: } nuclear@0: else if( IsElement( "v") && vertexCount > 0 ) nuclear@0: { nuclear@0: // read JointIndex - WeightIndex pairs nuclear@0: const char* text = GetTextContent(); nuclear@0: nuclear@0: for( std::vector< std::pair >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) nuclear@0: { nuclear@0: if( *text == 0) nuclear@0: ThrowException( "Out of data while reading "); nuclear@0: it->first = strtoul10( text, &text); nuclear@0: SkipSpacesAndLineEnd( &text); nuclear@0: if( *text == 0) nuclear@0: ThrowException( "Out of data while reading "); nuclear@0: it->second = strtoul10( text, &text); nuclear@0: SkipSpacesAndLineEnd( &text); nuclear@0: } nuclear@0: nuclear@0: TestClosing( "v"); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "vertex_weights") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the image library contents nuclear@0: void ColladaParser::ReadImageLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "image")) nuclear@0: { nuclear@0: // read ID. Another entry which is "optional" by design but obligatory in reality nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: mImageLibrary[id] = Image(); nuclear@0: nuclear@0: // read on from there nuclear@0: ReadImage( mImageLibrary[id]); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "library_images") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an image entry into the given image nuclear@0: void ColladaParser::ReadImage( Collada::Image& pImage) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ nuclear@0: // Need to run different code paths here, depending on the Collada XSD version nuclear@0: if (IsElement("image")) { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "init_from")) nuclear@0: { nuclear@0: if (mFormat == FV_1_4_n) nuclear@0: { nuclear@0: // FIX: C4D exporter writes empty tags nuclear@0: if (!mReader->isEmptyElement()) { nuclear@0: // element content is filename - hopefully nuclear@0: const char* sz = TestTextContent(); nuclear@0: if (sz)pImage.mFileName = sz; nuclear@0: TestClosing( "init_from"); nuclear@0: } nuclear@0: if (!pImage.mFileName.length()) { nuclear@0: pImage.mFileName = "unknown_texture"; nuclear@0: } nuclear@0: } nuclear@0: else if (mFormat == FV_1_5_n) nuclear@0: { nuclear@0: // make sure we skip over mip and array initializations, which nuclear@0: // we don't support, but which could confuse the loader if nuclear@0: // they're not skipped. nuclear@0: int attrib = TestAttribute("array_index"); nuclear@0: if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { nuclear@0: DefaultLogger::get()->warn("Collada: Ignoring texture array index"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: attrib = TestAttribute("mip_index"); nuclear@0: if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) { nuclear@0: DefaultLogger::get()->warn("Collada: Ignoring MIP map layer"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // TODO: correctly jump over cube and volume maps? nuclear@0: } nuclear@0: } nuclear@0: else if (mFormat == FV_1_5_n) nuclear@0: { nuclear@0: if( IsElement( "ref")) nuclear@0: { nuclear@0: // element content is filename - hopefully nuclear@0: const char* sz = TestTextContent(); nuclear@0: if (sz)pImage.mFileName = sz; nuclear@0: TestClosing( "ref"); nuclear@0: } nuclear@0: else if( IsElement( "hex") && !pImage.mFileName.length()) nuclear@0: { nuclear@0: // embedded image. get format nuclear@0: const int attrib = TestAttribute("format"); nuclear@0: if (-1 == attrib) nuclear@0: DefaultLogger::get()->warn("Collada: Unknown image file format"); nuclear@0: else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib); nuclear@0: nuclear@0: const char* data = GetTextContent(); nuclear@0: nuclear@0: // hexadecimal-encoded binary octets. First of all, find the nuclear@0: // required buffer size to reserve enough storage. nuclear@0: const char* cur = data; nuclear@0: while (!IsSpaceOrNewLine(*cur)) cur++; nuclear@0: nuclear@0: const unsigned int size = (unsigned int)(cur-data) * 2; nuclear@0: pImage.mImageData.resize(size); nuclear@0: for (unsigned int i = 0; i < size;++i) nuclear@0: pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1)); nuclear@0: nuclear@0: TestClosing( "hex"); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "image") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the material library nuclear@0: void ColladaParser::ReadMaterialLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "material")) nuclear@0: { nuclear@0: // read ID. By now you propably know my opinion about this "specification" nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: ReadMaterial(mMaterialLibrary[id] = Material()); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "library_materials") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the light library nuclear@0: void ColladaParser::ReadLightLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "light")) nuclear@0: { nuclear@0: // read ID. By now you propably know my opinion about this "specification" nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: ReadLight(mLightLibrary[id] = Light()); nuclear@0: nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "library_lights") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the camera library nuclear@0: void ColladaParser::ReadCameraLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "camera")) nuclear@0: { nuclear@0: // read ID. By now you propably know my opinion about this "specification" nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: Camera& cam = mCameraLibrary[id]; nuclear@0: attrID = TestAttribute( "name"); nuclear@0: if (attrID != -1) nuclear@0: cam.mName = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: ReadCamera(cam); nuclear@0: nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "library_cameras") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a material entry into the given material nuclear@0: void ColladaParser::ReadMaterial( Collada::Material& pMaterial) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if (IsElement("material")) { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "instance_effect")) nuclear@0: { nuclear@0: // referred effect by URL nuclear@0: int attrUrl = GetAttribute( "url"); nuclear@0: const char* url = mReader->getAttributeValue( attrUrl); nuclear@0: if( url[0] != '#') nuclear@0: ThrowException( "Unknown reference format"); nuclear@0: nuclear@0: pMaterial.mEffect = url+1; nuclear@0: nuclear@0: SkipElement(); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "material") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a light entry into the given light nuclear@0: void ColladaParser::ReadLight( Collada::Light& pLight) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if (IsElement("light")) { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if (IsElement("spot")) { nuclear@0: pLight.mType = aiLightSource_SPOT; nuclear@0: } nuclear@0: else if (IsElement("ambient")) { nuclear@0: pLight.mType = aiLightSource_AMBIENT; nuclear@0: } nuclear@0: else if (IsElement("directional")) { nuclear@0: pLight.mType = aiLightSource_DIRECTIONAL; nuclear@0: } nuclear@0: else if (IsElement("point")) { nuclear@0: pLight.mType = aiLightSource_POINT; nuclear@0: } nuclear@0: else if (IsElement("color")) { nuclear@0: // text content contains 3 floats nuclear@0: const char* content = GetTextContent(); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pLight.mColor.r); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pLight.mColor.g); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pLight.mColor.b); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: TestClosing( "color"); nuclear@0: } nuclear@0: else if (IsElement("constant_attenuation")) { nuclear@0: pLight.mAttConstant = ReadFloatFromTextContent(); nuclear@0: TestClosing("constant_attenuation"); nuclear@0: } nuclear@0: else if (IsElement("linear_attenuation")) { nuclear@0: pLight.mAttLinear = ReadFloatFromTextContent(); nuclear@0: TestClosing("linear_attenuation"); nuclear@0: } nuclear@0: else if (IsElement("quadratic_attenuation")) { nuclear@0: pLight.mAttQuadratic = ReadFloatFromTextContent(); nuclear@0: TestClosing("quadratic_attenuation"); nuclear@0: } nuclear@0: else if (IsElement("falloff_angle")) { nuclear@0: pLight.mFalloffAngle = ReadFloatFromTextContent(); nuclear@0: TestClosing("falloff_angle"); nuclear@0: } nuclear@0: else if (IsElement("falloff_exponent")) { nuclear@0: pLight.mFalloffExponent = ReadFloatFromTextContent(); nuclear@0: TestClosing("falloff_exponent"); nuclear@0: } nuclear@0: // FCOLLADA extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if (IsElement("outer_cone")) { nuclear@0: pLight.mOuterAngle = ReadFloatFromTextContent(); nuclear@0: TestClosing("outer_cone"); nuclear@0: } nuclear@0: // ... and this one is even deprecated nuclear@0: else if (IsElement("penumbra_angle")) { nuclear@0: pLight.mPenumbraAngle = ReadFloatFromTextContent(); nuclear@0: TestClosing("penumbra_angle"); nuclear@0: } nuclear@0: else if (IsElement("intensity")) { nuclear@0: pLight.mIntensity = ReadFloatFromTextContent(); nuclear@0: TestClosing("intensity"); nuclear@0: } nuclear@0: else if (IsElement("falloff")) { nuclear@0: pLight.mOuterAngle = ReadFloatFromTextContent(); nuclear@0: TestClosing("falloff"); nuclear@0: } nuclear@0: else if (IsElement("hotspot_beam")) { nuclear@0: pLight.mFalloffAngle = ReadFloatFromTextContent(); nuclear@0: TestClosing("hotspot_beam"); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "light") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a camera entry into the given light nuclear@0: void ColladaParser::ReadCamera( Collada::Camera& pCamera) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if (IsElement("camera")) { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if (IsElement("orthographic")) { nuclear@0: pCamera.mOrtho = true; nuclear@0: } nuclear@0: else if (IsElement("xfov") || IsElement("xmag")) { nuclear@0: pCamera.mHorFov = ReadFloatFromTextContent(); nuclear@0: TestClosing((pCamera.mOrtho ? "xmag" : "xfov")); nuclear@0: } nuclear@0: else if (IsElement("yfov") || IsElement("ymag")) { nuclear@0: pCamera.mVerFov = ReadFloatFromTextContent(); nuclear@0: TestClosing((pCamera.mOrtho ? "ymag" : "yfov")); nuclear@0: } nuclear@0: else if (IsElement("aspect_ratio")) { nuclear@0: pCamera.mAspect = ReadFloatFromTextContent(); nuclear@0: TestClosing("aspect_ratio"); nuclear@0: } nuclear@0: else if (IsElement("znear")) { nuclear@0: pCamera.mZNear = ReadFloatFromTextContent(); nuclear@0: TestClosing("znear"); nuclear@0: } nuclear@0: else if (IsElement("zfar")) { nuclear@0: pCamera.mZFar = ReadFloatFromTextContent(); nuclear@0: TestClosing("zfar"); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "camera") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the effect library nuclear@0: void ColladaParser::ReadEffectLibrary() nuclear@0: { nuclear@0: if (mReader->isEmptyElement()) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "effect")) nuclear@0: { nuclear@0: // read ID. Do I have to repeat my ranting about "optional" attributes? nuclear@0: int attrID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // create an entry and store it in the library under its ID nuclear@0: mEffectLibrary[id] = Effect(); nuclear@0: // read on from there nuclear@0: ReadEffect( mEffectLibrary[id]); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "library_effects") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an effect entry into the given effect nuclear@0: void ColladaParser::ReadEffect( Collada::Effect& pEffect) nuclear@0: { nuclear@0: // for the moment we don't support any other type of effect. nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "profile_COMMON")) nuclear@0: ReadEffectProfileCommon( pEffect); nuclear@0: else nuclear@0: SkipElement(); nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "effect") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an COMMON effect profile nuclear@0: void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "newparam")) { nuclear@0: // save ID nuclear@0: int attrSID = GetAttribute( "sid"); nuclear@0: std::string sid = mReader->getAttributeValue( attrSID); nuclear@0: pEffect.mParams[sid] = EffectParam(); nuclear@0: ReadEffectParam( pEffect.mParams[sid]); nuclear@0: } nuclear@0: else if( IsElement( "technique") || IsElement( "extra")) nuclear@0: { nuclear@0: // just syntactic sugar nuclear@0: } nuclear@0: nuclear@0: /* Shading modes */ nuclear@0: else if( IsElement( "phong")) nuclear@0: pEffect.mShadeType = Shade_Phong; nuclear@0: else if( IsElement( "constant")) nuclear@0: pEffect.mShadeType = Shade_Constant; nuclear@0: else if( IsElement( "lambert")) nuclear@0: pEffect.mShadeType = Shade_Lambert; nuclear@0: else if( IsElement( "blinn")) nuclear@0: pEffect.mShadeType = Shade_Blinn; nuclear@0: nuclear@0: /* Color + texture properties */ nuclear@0: else if( IsElement( "emission")) nuclear@0: ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive); nuclear@0: else if( IsElement( "ambient")) nuclear@0: ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient); nuclear@0: else if( IsElement( "diffuse")) nuclear@0: ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse); nuclear@0: else if( IsElement( "specular")) nuclear@0: ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular); nuclear@0: else if( IsElement( "reflective")) { nuclear@0: ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective); nuclear@0: } nuclear@0: else if( IsElement( "transparent")) { nuclear@0: ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent); nuclear@0: } nuclear@0: else if( IsElement( "shininess")) nuclear@0: ReadEffectFloat( pEffect.mShininess); nuclear@0: else if( IsElement( "reflectivity")) nuclear@0: ReadEffectFloat( pEffect.mReflectivity); nuclear@0: nuclear@0: /* Single scalar properties */ nuclear@0: else if( IsElement( "transparency")) nuclear@0: ReadEffectFloat( pEffect.mTransparency); nuclear@0: else if( IsElement( "index_of_refraction")) nuclear@0: ReadEffectFloat( pEffect.mRefractIndex); nuclear@0: nuclear@0: // GOOGLEEARTH/OKINO extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if( IsElement( "double_sided")) nuclear@0: pEffect.mDoubleSided = ReadBoolFromTextContent(); nuclear@0: nuclear@0: // FCOLLADA extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if( IsElement( "bump")) { nuclear@0: aiColor4D dummy; nuclear@0: ReadEffectColor( dummy,pEffect.mTexBump); nuclear@0: } nuclear@0: nuclear@0: // MAX3D extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if( IsElement( "wireframe")) { nuclear@0: pEffect.mWireframe = ReadBoolFromTextContent(); nuclear@0: TestClosing( "wireframe"); nuclear@0: } nuclear@0: else if( IsElement( "faceted")) { nuclear@0: pEffect.mFaceted = ReadBoolFromTextContent(); nuclear@0: TestClosing( "faceted"); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0) nuclear@0: { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read texture wrapping + UV transform settings from a profile==Maya chunk nuclear@0: void ColladaParser::ReadSamplerProperties( Sampler& out ) nuclear@0: { nuclear@0: if (mReader->isEmptyElement()) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: nuclear@0: // MAYA extensions nuclear@0: // ------------------------------------------------------- nuclear@0: if( IsElement( "wrapU")) { nuclear@0: out.mWrapU = ReadBoolFromTextContent(); nuclear@0: TestClosing( "wrapU"); nuclear@0: } nuclear@0: else if( IsElement( "wrapV")) { nuclear@0: out.mWrapV = ReadBoolFromTextContent(); nuclear@0: TestClosing( "wrapV"); nuclear@0: } nuclear@0: else if( IsElement( "mirrorU")) { nuclear@0: out.mMirrorU = ReadBoolFromTextContent(); nuclear@0: TestClosing( "mirrorU"); nuclear@0: } nuclear@0: else if( IsElement( "mirrorV")) { nuclear@0: out.mMirrorV = ReadBoolFromTextContent(); nuclear@0: TestClosing( "mirrorV"); nuclear@0: } nuclear@0: else if( IsElement( "repeatU")) { nuclear@0: out.mTransform.mScaling.x = ReadFloatFromTextContent(); nuclear@0: TestClosing( "repeatU"); nuclear@0: } nuclear@0: else if( IsElement( "repeatV")) { nuclear@0: out.mTransform.mScaling.y = ReadFloatFromTextContent(); nuclear@0: TestClosing( "repeatV"); nuclear@0: } nuclear@0: else if( IsElement( "offsetU")) { nuclear@0: out.mTransform.mTranslation.x = ReadFloatFromTextContent(); nuclear@0: TestClosing( "offsetU"); nuclear@0: } nuclear@0: else if( IsElement( "offsetV")) { nuclear@0: out.mTransform.mTranslation.y = ReadFloatFromTextContent(); nuclear@0: TestClosing( "offsetV"); nuclear@0: } nuclear@0: else if( IsElement( "rotateUV")) { nuclear@0: out.mTransform.mRotation = ReadFloatFromTextContent(); nuclear@0: TestClosing( "rotateUV"); nuclear@0: } nuclear@0: else if( IsElement( "blend_mode")) { nuclear@0: nuclear@0: const char* sz = GetTextContent(); nuclear@0: // http://www.feelingsoftware.com/content/view/55/72/lang,en/ nuclear@0: // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE nuclear@0: if (0 == ASSIMP_strincmp(sz,"ADD",3)) nuclear@0: out.mOp = aiTextureOp_Add; nuclear@0: nuclear@0: else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) nuclear@0: out.mOp = aiTextureOp_Subtract; nuclear@0: nuclear@0: else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) nuclear@0: out.mOp = aiTextureOp_Multiply; nuclear@0: nuclear@0: else { nuclear@0: DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode"); nuclear@0: } nuclear@0: TestClosing( "blend_mode"); nuclear@0: } nuclear@0: // OKINO extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if( IsElement( "weighting")) { nuclear@0: out.mWeighting = ReadFloatFromTextContent(); nuclear@0: TestClosing( "weighting"); nuclear@0: } nuclear@0: else if( IsElement( "mix_with_previous_layer")) { nuclear@0: out.mMixWithPrevious = ReadFloatFromTextContent(); nuclear@0: TestClosing( "mix_with_previous_layer"); nuclear@0: } nuclear@0: // MAX3D extensions nuclear@0: // ------------------------------------------------------- nuclear@0: else if( IsElement( "amount")) { nuclear@0: out.mWeighting = ReadFloatFromTextContent(); nuclear@0: TestClosing( "amount"); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "technique") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an effect entry containing a color or a texture defining that color nuclear@0: void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) nuclear@0: { nuclear@0: if (mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: // Save current element name nuclear@0: const std::string curElem = mReader->getNodeName(); nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "color")) nuclear@0: { nuclear@0: // text content contains 4 floats nuclear@0: const char* content = GetTextContent(); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pColor.r); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pColor.g); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pColor.b); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: content = fast_atoreal_move( content, (float&)pColor.a); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: TestClosing( "color"); nuclear@0: } nuclear@0: else if( IsElement( "texture")) nuclear@0: { nuclear@0: // get name of source textur/sampler nuclear@0: int attrTex = GetAttribute( "texture"); nuclear@0: pSampler.mName = mReader->getAttributeValue( attrTex); nuclear@0: nuclear@0: // get name of UV source channel. Specification demands it to be there, but some exporters nuclear@0: // don't write it. It will be the default UV channel in case it's missing. nuclear@0: attrTex = TestAttribute( "texcoord"); nuclear@0: if( attrTex >= 0 ) nuclear@0: pSampler.mUVChannel = mReader->getAttributeValue( attrTex); nuclear@0: //SkipElement(); nuclear@0: } nuclear@0: else if( IsElement( "technique")) nuclear@0: { nuclear@0: const int _profile = GetAttribute( "profile"); nuclear@0: const char* profile = mReader->getAttributeValue( _profile ); nuclear@0: nuclear@0: // Some extensions are quite useful ... ReadSamplerProperties processes nuclear@0: // several extensions in MAYA, OKINO and MAX3D profiles. nuclear@0: if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO")) nuclear@0: { nuclear@0: // get more information on this sampler nuclear@0: ReadSamplerProperties(pSampler); nuclear@0: } nuclear@0: else SkipElement(); nuclear@0: } nuclear@0: else if( !IsElement( "extra")) nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ nuclear@0: if (mReader->getNodeName() == curElem) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an effect entry containing a float nuclear@0: void ColladaParser::ReadEffectFloat( float& pFloat) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT){ nuclear@0: if( IsElement( "float")) nuclear@0: { nuclear@0: // text content contains a single floats nuclear@0: const char* content = GetTextContent(); nuclear@0: content = fast_atoreal_move( content, pFloat); nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: nuclear@0: TestClosing( "float"); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an effect parameter specification of any kind nuclear@0: void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "surface")) nuclear@0: { nuclear@0: // image ID given inside tags nuclear@0: TestOpening( "init_from"); nuclear@0: const char* content = GetTextContent(); nuclear@0: pParam.mType = Param_Surface; nuclear@0: pParam.mReference = content; nuclear@0: TestClosing( "init_from"); nuclear@0: nuclear@0: // don't care for remaining stuff nuclear@0: SkipElement( "surface"); nuclear@0: } nuclear@0: else if( IsElement( "sampler2D")) nuclear@0: { nuclear@0: // surface ID is given inside tags nuclear@0: TestOpening( "source"); nuclear@0: const char* content = GetTextContent(); nuclear@0: pParam.mType = Param_Sampler; nuclear@0: pParam.mReference = content; nuclear@0: TestClosing( "source"); nuclear@0: nuclear@0: // don't care for remaining stuff nuclear@0: SkipElement( "sampler2D"); nuclear@0: } else nuclear@0: { nuclear@0: // ignore unknown element nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the geometry library contents nuclear@0: void ColladaParser::ReadGeometryLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "geometry")) nuclear@0: { nuclear@0: // read ID. Another entry which is "optional" by design but obligatory in reality nuclear@0: int indexID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( indexID); nuclear@0: nuclear@0: // TODO: (thom) support SIDs nuclear@0: // ai_assert( TestAttribute( "sid") == -1); nuclear@0: nuclear@0: // create a mesh and store it in the library under its ID nuclear@0: Mesh* mesh = new Mesh; nuclear@0: mMeshLibrary[id] = mesh; nuclear@0: nuclear@0: // read on from there nuclear@0: ReadGeometry( mesh); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "library_geometries") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a geometry from the geometry library. nuclear@0: void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "mesh")) nuclear@0: { nuclear@0: // read on from there nuclear@0: ReadMesh( pMesh); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "geometry") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a mesh from the geometry library nuclear@0: void ColladaParser::ReadMesh( Mesh* pMesh) nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "source")) nuclear@0: { nuclear@0: // we have professionals dealing with this nuclear@0: ReadSource(); nuclear@0: } nuclear@0: else if( IsElement( "vertices")) nuclear@0: { nuclear@0: // read per-vertex mesh data nuclear@0: ReadVertexData( pMesh); nuclear@0: } nuclear@0: else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips") nuclear@0: || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips")) nuclear@0: { nuclear@0: // read per-index mesh data and faces setup nuclear@0: ReadIndexData( pMesh); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "technique_common") == 0) nuclear@0: { nuclear@0: // end of another meaningless element - read over it nuclear@0: } nuclear@0: else if( strcmp( mReader->getNodeName(), "mesh") == 0) nuclear@0: { nuclear@0: // end of element - we're done here nuclear@0: break; nuclear@0: } else nuclear@0: { nuclear@0: // everything else should be punished nuclear@0: ThrowException( "Expected end of element."); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a source element nuclear@0: void ColladaParser::ReadSource() nuclear@0: { nuclear@0: int indexID = GetAttribute( "id"); nuclear@0: std::string sourceID = mReader->getAttributeValue( indexID); nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array")) nuclear@0: { nuclear@0: ReadDataArray(); nuclear@0: } nuclear@0: else if( IsElement( "technique_common")) nuclear@0: { nuclear@0: // I don't care for your profiles nuclear@0: } nuclear@0: else if( IsElement( "accessor")) nuclear@0: { nuclear@0: ReadAccessor( sourceID); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "source") == 0) nuclear@0: { nuclear@0: // end of - we're done nuclear@0: break; nuclear@0: } nuclear@0: else if( strcmp( mReader->getNodeName(), "technique_common") == 0) nuclear@0: { nuclear@0: // end of another meaningless element - read over it nuclear@0: } else nuclear@0: { nuclear@0: // everything else should be punished nuclear@0: ThrowException( "Expected end of element."); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a data array holding a number of floats, and stores it in the global library nuclear@0: void ColladaParser::ReadDataArray() nuclear@0: { nuclear@0: std::string elmName = mReader->getNodeName(); nuclear@0: bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array"); nuclear@0: bool isEmptyElement = mReader->isEmptyElement(); nuclear@0: nuclear@0: // read attributes nuclear@0: int indexID = GetAttribute( "id"); nuclear@0: std::string id = mReader->getAttributeValue( indexID); nuclear@0: int indexCount = GetAttribute( "count"); nuclear@0: unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount); nuclear@0: if (count == 0) { return; } // some exporters write empty data arrays with count="0" nuclear@0: const char* content = TestTextContent(); nuclear@0: nuclear@0: // read values and store inside an array in the data library nuclear@0: mDataLibrary[id] = Data(); nuclear@0: Data& data = mDataLibrary[id]; nuclear@0: data.mIsStringArray = isStringArray; nuclear@0: nuclear@0: // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them nuclear@0: if (content) nuclear@0: { nuclear@0: if( isStringArray) nuclear@0: { nuclear@0: data.mStrings.reserve( count); nuclear@0: std::string s; nuclear@0: nuclear@0: for( unsigned int a = 0; a < count; a++) nuclear@0: { nuclear@0: if( *content == 0) nuclear@0: ThrowException( "Expected more values while reading IDREF_array contents."); nuclear@0: nuclear@0: s.clear(); nuclear@0: while( !IsSpaceOrNewLine( *content)) nuclear@0: s += *content++; nuclear@0: data.mStrings.push_back( s); nuclear@0: nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: } else nuclear@0: { nuclear@0: data.mValues.reserve( count); nuclear@0: nuclear@0: for( unsigned int a = 0; a < count; a++) nuclear@0: { nuclear@0: if( *content == 0) nuclear@0: ThrowException( "Expected more values while reading float_array contents."); nuclear@0: nuclear@0: float value; nuclear@0: // read a number nuclear@0: content = fast_atoreal_move( content, value); nuclear@0: data.mValues.push_back( value); nuclear@0: // skip whitespace after it nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // test for closing tag nuclear@0: if( !isEmptyElement ) nuclear@0: TestClosing( elmName.c_str()); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads an accessor and stores it in the global library nuclear@0: void ColladaParser::ReadAccessor( const std::string& pID) nuclear@0: { nuclear@0: // read accessor attributes nuclear@0: int attrSource = GetAttribute( "source"); nuclear@0: const char* source = mReader->getAttributeValue( attrSource); nuclear@0: if( source[0] != '#') nuclear@0: ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); nuclear@0: int attrCount = GetAttribute( "count"); nuclear@0: unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount); nuclear@0: int attrOffset = TestAttribute( "offset"); nuclear@0: unsigned int offset = 0; nuclear@0: if( attrOffset > -1) nuclear@0: offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset); nuclear@0: int attrStride = TestAttribute( "stride"); nuclear@0: unsigned int stride = 1; nuclear@0: if( attrStride > -1) nuclear@0: stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride); nuclear@0: nuclear@0: // store in the library under the given ID nuclear@0: mAccessorLibrary[pID] = Accessor(); nuclear@0: Accessor& acc = mAccessorLibrary[pID]; nuclear@0: acc.mCount = count; nuclear@0: acc.mOffset = offset; nuclear@0: acc.mStride = stride; nuclear@0: acc.mSource = source+1; // ignore the leading '#' nuclear@0: acc.mSize = 0; // gets incremented with every param nuclear@0: nuclear@0: // and read the components nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "param")) nuclear@0: { nuclear@0: // read data param nuclear@0: int attrName = TestAttribute( "name"); nuclear@0: std::string name; nuclear@0: if( attrName > -1) nuclear@0: { nuclear@0: name = mReader->getAttributeValue( attrName); nuclear@0: nuclear@0: // analyse for common type components and store it's sub-offset in the corresponding field nuclear@0: nuclear@0: /* Cartesian coordinates */ nuclear@0: if( name == "X") acc.mSubOffset[0] = acc.mParams.size(); nuclear@0: else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size(); nuclear@0: else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size(); nuclear@0: nuclear@0: /* RGBA colors */ nuclear@0: else if( name == "R") acc.mSubOffset[0] = acc.mParams.size(); nuclear@0: else if( name == "G") acc.mSubOffset[1] = acc.mParams.size(); nuclear@0: else if( name == "B") acc.mSubOffset[2] = acc.mParams.size(); nuclear@0: else if( name == "A") acc.mSubOffset[3] = acc.mParams.size(); nuclear@0: nuclear@0: /* UVWQ (STPQ) texture coordinates */ nuclear@0: else if( name == "S") acc.mSubOffset[0] = acc.mParams.size(); nuclear@0: else if( name == "T") acc.mSubOffset[1] = acc.mParams.size(); nuclear@0: else if( name == "P") acc.mSubOffset[2] = acc.mParams.size(); nuclear@0: // else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size(); nuclear@0: /* 4D uv coordinates are not supported in Assimp */ nuclear@0: nuclear@0: /* Generic extra data, interpreted as UV data, too*/ nuclear@0: else if( name == "U") acc.mSubOffset[0] = acc.mParams.size(); nuclear@0: else if( name == "V") acc.mSubOffset[1] = acc.mParams.size(); nuclear@0: //else nuclear@0: // DefaultLogger::get()->warn( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name)); nuclear@0: } nuclear@0: nuclear@0: // read data type nuclear@0: int attrType = TestAttribute( "type"); nuclear@0: if( attrType > -1) nuclear@0: { nuclear@0: // for the moment we only distinguish between a 4x4 matrix and anything else. nuclear@0: // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types nuclear@0: // which should be tested for here. nuclear@0: std::string type = mReader->getAttributeValue( attrType); nuclear@0: if( type == "float4x4") nuclear@0: acc.mSize += 16; nuclear@0: else nuclear@0: acc.mSize += 1; nuclear@0: } nuclear@0: nuclear@0: acc.mParams.push_back( name); nuclear@0: nuclear@0: // skip remaining stuff of this element, if any nuclear@0: SkipElement(); nuclear@0: } else nuclear@0: { nuclear@0: ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "accessor") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads input declarations of per-vertex mesh data into the given mesh nuclear@0: void ColladaParser::ReadVertexData( Mesh* pMesh) nuclear@0: { nuclear@0: // extract the ID of the element. Not that we care, but to catch strange referencing schemes we should warn about nuclear@0: int attrID= GetAttribute( "id"); nuclear@0: pMesh->mVertexID = mReader->getAttributeValue( attrID); nuclear@0: nuclear@0: // a number of elements nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "input")) nuclear@0: { nuclear@0: ReadInputChannel( pMesh->mPerVertexData); nuclear@0: } else nuclear@0: { nuclear@0: ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag ") % mReader->getNodeName())); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "vertices") != 0) nuclear@0: ThrowException( "Expected end of element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads input declarations of per-index mesh data into the given mesh nuclear@0: void ColladaParser::ReadIndexData( Mesh* pMesh) nuclear@0: { nuclear@0: std::vector vcount; nuclear@0: std::vector perIndexData; nuclear@0: nuclear@0: // read primitive count from the attribute nuclear@0: int attrCount = GetAttribute( "count"); nuclear@0: size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount); nuclear@0: nuclear@0: // material subgroup nuclear@0: int attrMaterial = TestAttribute( "material"); nuclear@0: SubMesh subgroup; nuclear@0: if( attrMaterial > -1) nuclear@0: subgroup.mMaterial = mReader->getAttributeValue( attrMaterial); nuclear@0: subgroup.mNumFaces = numPrimitives; nuclear@0: pMesh->mSubMeshes.push_back( subgroup); nuclear@0: nuclear@0: // distinguish between polys and triangles nuclear@0: std::string elementName = mReader->getNodeName(); nuclear@0: PrimitiveType primType = Prim_Invalid; nuclear@0: if( IsElement( "lines")) nuclear@0: primType = Prim_Lines; nuclear@0: else if( IsElement( "linestrips")) nuclear@0: primType = Prim_LineStrip; nuclear@0: else if( IsElement( "polygons")) nuclear@0: primType = Prim_Polygon; nuclear@0: else if( IsElement( "polylist")) nuclear@0: primType = Prim_Polylist; nuclear@0: else if( IsElement( "triangles")) nuclear@0: primType = Prim_Triangles; nuclear@0: else if( IsElement( "trifans")) nuclear@0: primType = Prim_TriFans; nuclear@0: else if( IsElement( "tristrips")) nuclear@0: primType = Prim_TriStrips; nuclear@0: nuclear@0: ai_assert( primType != Prim_Invalid); nuclear@0: nuclear@0: // also a number of elements, but in addition a

primitive collection and propably index counts for all primitives nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "input")) nuclear@0: { nuclear@0: ReadInputChannel( perIndexData); nuclear@0: } nuclear@0: else if( IsElement( "vcount")) nuclear@0: { nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: { nuclear@0: if (numPrimitives) // It is possible to define a mesh without any primitives nuclear@0: { nuclear@0: // case - specifies the number of indices for each polygon nuclear@0: const char* content = GetTextContent(); nuclear@0: vcount.reserve( numPrimitives); nuclear@0: for( unsigned int a = 0; a < numPrimitives; a++) nuclear@0: { nuclear@0: if( *content == 0) nuclear@0: ThrowException( "Expected more values while reading contents."); nuclear@0: // read a number nuclear@0: vcount.push_back( (size_t) strtoul10( content, &content)); nuclear@0: // skip whitespace after it nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: TestClosing( "vcount"); nuclear@0: } nuclear@0: } nuclear@0: else if( IsElement( "p")) nuclear@0: { nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: { nuclear@0: // now here the actual fun starts - these are the indices to construct the mesh data from nuclear@0: ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType); nuclear@0: } nuclear@0: } else nuclear@0: { nuclear@0: ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName)); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( mReader->getNodeName() != elementName) nuclear@0: ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % elementName)); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a single input channel element and stores it in the given array, if valid nuclear@0: void ColladaParser::ReadInputChannel( std::vector& poChannels) nuclear@0: { nuclear@0: InputChannel channel; nuclear@0: nuclear@0: // read semantic nuclear@0: int attrSemantic = GetAttribute( "semantic"); nuclear@0: std::string semantic = mReader->getAttributeValue( attrSemantic); nuclear@0: channel.mType = GetTypeForSemantic( semantic); nuclear@0: nuclear@0: // read source nuclear@0: int attrSource = GetAttribute( "source"); nuclear@0: const char* source = mReader->getAttributeValue( attrSource); nuclear@0: if( source[0] != '#') nuclear@0: ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of element.") % source)); nuclear@0: channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only nuclear@0: nuclear@0: // read index offset, if per-index nuclear@0: int attrOffset = TestAttribute( "offset"); nuclear@0: if( attrOffset > -1) nuclear@0: channel.mOffset = mReader->getAttributeValueAsInt( attrOffset); nuclear@0: nuclear@0: // read set if texture coordinates nuclear@0: if(channel.mType == IT_Texcoord || channel.mType == IT_Color){ nuclear@0: int attrSet = TestAttribute("set"); nuclear@0: if(attrSet > -1){ nuclear@0: attrSet = mReader->getAttributeValueAsInt( attrSet); nuclear@0: if(attrSet < 0) nuclear@0: ThrowException( boost::str( boost::format( "Invalid index \"%i\" in set attribute of element") % (attrSet))); nuclear@0: nuclear@0: channel.mIndex = attrSet; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // store, if valid type nuclear@0: if( channel.mType != IT_Invalid) nuclear@0: poChannels.push_back( channel); nuclear@0: nuclear@0: // skip remaining stuff of this element, if any nuclear@0: SkipElement(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a

primitive index list and assembles the mesh data into the given mesh nuclear@0: void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, nuclear@0: size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) nuclear@0: { nuclear@0: // determine number of indices coming per vertex nuclear@0: // find the offset index for all per-vertex channels nuclear@0: size_t numOffsets = 1; nuclear@0: size_t perVertexOffset = SIZE_MAX; // invalid value nuclear@0: BOOST_FOREACH( const InputChannel& channel, pPerIndexChannels) nuclear@0: { nuclear@0: numOffsets = std::max( numOffsets, channel.mOffset+1); nuclear@0: if( channel.mType == IT_Vertex) nuclear@0: perVertexOffset = channel.mOffset; nuclear@0: } nuclear@0: nuclear@0: // determine the expected number of indices nuclear@0: size_t expectedPointCount = 0; nuclear@0: switch( pPrimType) nuclear@0: { nuclear@0: case Prim_Polylist: nuclear@0: { nuclear@0: BOOST_FOREACH( size_t i, pVCount) nuclear@0: expectedPointCount += i; nuclear@0: break; nuclear@0: } nuclear@0: case Prim_Lines: nuclear@0: expectedPointCount = 2 * pNumPrimitives; nuclear@0: break; nuclear@0: case Prim_Triangles: nuclear@0: expectedPointCount = 3 * pNumPrimitives; nuclear@0: break; nuclear@0: default: nuclear@0: // other primitive types don't state the index count upfront... we need to guess nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: // and read all indices into a temporary array nuclear@0: std::vector indices; nuclear@0: if( expectedPointCount > 0) nuclear@0: indices.reserve( expectedPointCount * numOffsets); nuclear@0: nuclear@0: if (pNumPrimitives > 0) // It is possible to not contain any indicies nuclear@0: { nuclear@0: const char* content = GetTextContent(); nuclear@0: while( *content != 0) nuclear@0: { nuclear@0: // read a value. nuclear@0: // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. nuclear@0: int value = std::max( 0, strtol10( content, &content)); nuclear@0: indices.push_back( size_t( value)); nuclear@0: // skip whitespace after it nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // complain if the index count doesn't fit nuclear@0: if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) nuclear@0: ThrowException( "Expected different index count in

element."); nuclear@0: else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0) nuclear@0: ThrowException( "Expected different index count in

element."); nuclear@0: nuclear@0: // find the data for all sources nuclear@0: for( std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) nuclear@0: { nuclear@0: InputChannel& input = *it; nuclear@0: if( input.mResolved) nuclear@0: continue; nuclear@0: nuclear@0: // find accessor nuclear@0: input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); nuclear@0: // resolve accessor's data pointer as well, if neccessary nuclear@0: const Accessor* acc = input.mResolved; nuclear@0: if( !acc->mData) nuclear@0: acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); nuclear@0: } nuclear@0: // and the same for the per-index channels nuclear@0: for( std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) nuclear@0: { nuclear@0: InputChannel& input = *it; nuclear@0: if( input.mResolved) nuclear@0: continue; nuclear@0: nuclear@0: // ignore vertex pointer, it doesn't refer to an accessor nuclear@0: if( input.mType == IT_Vertex) nuclear@0: { nuclear@0: // warn if the vertex channel does not refer to the element in the same mesh nuclear@0: if( input.mAccessor != pMesh->mVertexID) nuclear@0: ThrowException( "Unsupported vertex referencing scheme."); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // find accessor nuclear@0: input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor); nuclear@0: // resolve accessor's data pointer as well, if neccessary nuclear@0: const Accessor* acc = input.mResolved; nuclear@0: if( !acc->mData) nuclear@0: acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // now assemble vertex data according to those indices nuclear@0: std::vector::const_iterator idx = indices.begin(); nuclear@0: nuclear@0: // For continued primitives, the given count does not come all in one

, but only one primitive per

nuclear@0: size_t numPrimitives = pNumPrimitives; nuclear@0: if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) nuclear@0: numPrimitives = 1; nuclear@0: nuclear@0: pMesh->mFaceSize.reserve( numPrimitives); nuclear@0: pMesh->mFacePosIndices.reserve( indices.size() / numOffsets); nuclear@0: nuclear@0: for( size_t a = 0; a < numPrimitives; a++) nuclear@0: { nuclear@0: // determine number of points for this primitive nuclear@0: size_t numPoints = 0; nuclear@0: switch( pPrimType) nuclear@0: { nuclear@0: case Prim_Lines: nuclear@0: numPoints = 2; nuclear@0: break; nuclear@0: case Prim_Triangles: nuclear@0: numPoints = 3; nuclear@0: break; nuclear@0: case Prim_Polylist: nuclear@0: numPoints = pVCount[a]; nuclear@0: break; nuclear@0: case Prim_TriFans: nuclear@0: case Prim_Polygon: nuclear@0: numPoints = indices.size() / numOffsets; nuclear@0: break; nuclear@0: default: nuclear@0: // LineStrip and TriStrip not supported due to expected index unmangling nuclear@0: ThrowException( "Unsupported primitive type."); nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: // store the face size to later reconstruct the face from nuclear@0: pMesh->mFaceSize.push_back( numPoints); nuclear@0: nuclear@0: // gather that number of vertices nuclear@0: for( size_t b = 0; b < numPoints; b++) nuclear@0: { nuclear@0: // read all indices for this vertex. Yes, in a hacky local array nuclear@0: ai_assert( numOffsets < 20 && perVertexOffset < 20); nuclear@0: size_t vindex[20]; nuclear@0: for( size_t offsets = 0; offsets < numOffsets; ++offsets) nuclear@0: vindex[offsets] = *idx++; nuclear@0: nuclear@0: // extract per-vertex channels using the global per-vertex offset nuclear@0: for( std::vector::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it) nuclear@0: ExtractDataObjectFromChannel( *it, vindex[perVertexOffset], pMesh); nuclear@0: // and extract per-index channels using there specified offset nuclear@0: for( std::vector::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) nuclear@0: ExtractDataObjectFromChannel( *it, vindex[it->mOffset], pMesh); nuclear@0: nuclear@0: // store the vertex-data index for later assignment of bone vertex weights nuclear@0: pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // if I ever get my hands on that guy who invented this steaming pile of indirection... nuclear@0: TestClosing( "p"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Extracts a single object from an input channel and stores it in the appropriate mesh data array nuclear@0: void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh) nuclear@0: { nuclear@0: // ignore vertex referrer - we handle them that separate nuclear@0: if( pInput.mType == IT_Vertex) nuclear@0: return; nuclear@0: nuclear@0: const Accessor& acc = *pInput.mResolved; nuclear@0: if( pLocalIndex >= acc.mCount) nuclear@0: ThrowException( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount)); nuclear@0: nuclear@0: // get a pointer to the start of the data object referred to by the accessor and the local index nuclear@0: const float* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride; nuclear@0: nuclear@0: // assemble according to the accessors component sub-offset list. We don't care, yet, nuclear@0: // what kind of object exactly we're extracting here nuclear@0: float obj[4]; nuclear@0: for( size_t c = 0; c < 4; ++c) nuclear@0: obj[c] = dataObject[acc.mSubOffset[c]]; nuclear@0: nuclear@0: // now we reinterpret it according to the type we're reading here nuclear@0: switch( pInput.mType) nuclear@0: { nuclear@0: case IT_Position: // ignore all position streams except 0 - there can be only one position nuclear@0: if( pInput.mIndex == 0) nuclear@0: pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2])); nuclear@0: else nuclear@0: DefaultLogger::get()->error("Collada: just one vertex position stream supported"); nuclear@0: break; nuclear@0: case IT_Normal: nuclear@0: // pad to current vertex count if necessary nuclear@0: if( pMesh->mNormals.size() < pMesh->mPositions.size()-1) nuclear@0: pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0)); nuclear@0: nuclear@0: // ignore all normal streams except 0 - there can be only one normal nuclear@0: if( pInput.mIndex == 0) nuclear@0: pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2])); nuclear@0: else nuclear@0: DefaultLogger::get()->error("Collada: just one vertex normal stream supported"); nuclear@0: break; nuclear@0: case IT_Tangent: nuclear@0: // pad to current vertex count if necessary nuclear@0: if( pMesh->mTangents.size() < pMesh->mPositions.size()-1) nuclear@0: pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0)); nuclear@0: nuclear@0: // ignore all tangent streams except 0 - there can be only one tangent nuclear@0: if( pInput.mIndex == 0) nuclear@0: pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); nuclear@0: else nuclear@0: DefaultLogger::get()->error("Collada: just one vertex tangent stream supported"); nuclear@0: break; nuclear@0: case IT_Bitangent: nuclear@0: // pad to current vertex count if necessary nuclear@0: if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1) nuclear@0: pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1)); nuclear@0: nuclear@0: // ignore all bitangent streams except 0 - there can be only one bitangent nuclear@0: if( pInput.mIndex == 0) nuclear@0: pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2])); nuclear@0: else nuclear@0: DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported"); nuclear@0: break; nuclear@0: case IT_Texcoord: nuclear@0: // up to 4 texture coord sets are fine, ignore the others nuclear@0: if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) nuclear@0: { nuclear@0: // pad to current vertex count if necessary nuclear@0: if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1) nuclear@0: pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(), nuclear@0: pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0)); nuclear@0: nuclear@0: pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2])); nuclear@0: if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */ nuclear@0: pMesh->mNumUVComponents[pInput.mIndex]=3; nuclear@0: } else nuclear@0: { nuclear@0: DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping."); nuclear@0: } nuclear@0: break; nuclear@0: case IT_Color: nuclear@0: // up to 4 color sets are fine, ignore the others nuclear@0: if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) nuclear@0: { nuclear@0: // pad to current vertex count if necessary nuclear@0: if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1) nuclear@0: pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(), nuclear@0: pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1)); nuclear@0: nuclear@0: aiColor4D result(0, 0, 0, 1); nuclear@0: for (size_t i = 0; i < pInput.mResolved->mSize; ++i) nuclear@0: { nuclear@0: result[i] = obj[pInput.mResolved->mSubOffset[i]]; nuclear@0: } nuclear@0: pMesh->mColors[pInput.mIndex].push_back(result); nuclear@0: } else nuclear@0: { nuclear@0: DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping."); nuclear@0: } nuclear@0: nuclear@0: break; nuclear@0: default: nuclear@0: // IT_Invalid and IT_Vertex nuclear@0: ai_assert(false && "shouldn't ever get here"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the library of node hierarchies and scene parts nuclear@0: void ColladaParser::ReadSceneLibrary() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: // a visual scene - generate root node under its ID and let ReadNode() do the recursive work nuclear@0: if( IsElement( "visual_scene")) nuclear@0: { nuclear@0: // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then? nuclear@0: int indexID = GetAttribute( "id"); nuclear@0: const char* attrID = mReader->getAttributeValue( indexID); nuclear@0: nuclear@0: // read name if given. nuclear@0: int indexName = TestAttribute( "name"); nuclear@0: const char* attrName = "unnamed"; nuclear@0: if( indexName > -1) nuclear@0: attrName = mReader->getAttributeValue( indexName); nuclear@0: nuclear@0: // create a node and store it in the library under its ID nuclear@0: Node* node = new Node; nuclear@0: node->mID = attrID; nuclear@0: node->mName = attrName; nuclear@0: mNodeLibrary[node->mID] = node; nuclear@0: nuclear@0: ReadSceneNode( node); nuclear@0: } else nuclear@0: { nuclear@0: // ignore the rest nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0) nuclear@0: //ThrowException( "Expected end of \"library_visual_scenes\" element."); nuclear@0: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a scene node's contents including children and stores it in the given node nuclear@0: void ColladaParser::ReadSceneNode( Node* pNode) nuclear@0: { nuclear@0: // quit immediately on elements nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "node")) nuclear@0: { nuclear@0: Node* child = new Node; nuclear@0: int attrID = TestAttribute( "id"); nuclear@0: if( attrID > -1) nuclear@0: child->mID = mReader->getAttributeValue( attrID); nuclear@0: int attrSID = TestAttribute( "sid"); nuclear@0: if( attrSID > -1) nuclear@0: child->mSID = mReader->getAttributeValue( attrSID); nuclear@0: nuclear@0: int attrName = TestAttribute( "name"); nuclear@0: if( attrName > -1) nuclear@0: child->mName = mReader->getAttributeValue( attrName); nuclear@0: nuclear@0: // TODO: (thom) support SIDs nuclear@0: // ai_assert( TestAttribute( "sid") == -1); nuclear@0: nuclear@0: if (pNode) nuclear@0: { nuclear@0: pNode->mChildren.push_back( child); nuclear@0: child->mParent = pNode; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // no parent node given, probably called from element. nuclear@0: // create new node in node library nuclear@0: mNodeLibrary[child->mID] = child; nuclear@0: } nuclear@0: nuclear@0: // read on recursively from there nuclear@0: ReadSceneNode( child); nuclear@0: continue; nuclear@0: } nuclear@0: // For any further stuff we need a valid node to work on nuclear@0: else if (!pNode) nuclear@0: continue; nuclear@0: nuclear@0: if( IsElement( "lookat")) nuclear@0: ReadNodeTransformation( pNode, TF_LOOKAT); nuclear@0: else if( IsElement( "matrix")) nuclear@0: ReadNodeTransformation( pNode, TF_MATRIX); nuclear@0: else if( IsElement( "rotate")) nuclear@0: ReadNodeTransformation( pNode, TF_ROTATE); nuclear@0: else if( IsElement( "scale")) nuclear@0: ReadNodeTransformation( pNode, TF_SCALE); nuclear@0: else if( IsElement( "skew")) nuclear@0: ReadNodeTransformation( pNode, TF_SKEW); nuclear@0: else if( IsElement( "translate")) nuclear@0: ReadNodeTransformation( pNode, TF_TRANSLATE); nuclear@0: else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length()) nuclear@0: { nuclear@0: // ... scene evaluation or, in other words, postprocessing pipeline, nuclear@0: // or, again in other words, a turing-complete description how to nuclear@0: // render a Collada scene. The only thing that is interesting for nuclear@0: // us is the primary camera. nuclear@0: int attrId = TestAttribute("camera_node"); nuclear@0: if (-1 != attrId) nuclear@0: { nuclear@0: const char* s = mReader->getAttributeValue(attrId); nuclear@0: if (s[0] != '#') nuclear@0: DefaultLogger::get()->error("Collada: Unresolved reference format of camera"); nuclear@0: else nuclear@0: pNode->mPrimaryCamera = s+1; nuclear@0: } nuclear@0: } nuclear@0: else if( IsElement( "instance_node")) nuclear@0: { nuclear@0: // find the node in the library nuclear@0: int attrID = TestAttribute( "url"); nuclear@0: if( attrID != -1) nuclear@0: { nuclear@0: const char* s = mReader->getAttributeValue(attrID); nuclear@0: if (s[0] != '#') nuclear@0: DefaultLogger::get()->error("Collada: Unresolved reference format of node"); nuclear@0: else nuclear@0: { nuclear@0: pNode->mNodeInstances.push_back(NodeInstance()); nuclear@0: pNode->mNodeInstances.back().mNode = s+1; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else if( IsElement( "instance_geometry") || IsElement( "instance_controller")) nuclear@0: { nuclear@0: // Reference to a mesh or controller, with possible material associations nuclear@0: ReadNodeGeometry( pNode); nuclear@0: } nuclear@0: else if( IsElement( "instance_light")) nuclear@0: { nuclear@0: // Reference to a light, name given in 'url' attribute nuclear@0: int attrID = TestAttribute("url"); nuclear@0: if (-1 == attrID) nuclear@0: DefaultLogger::get()->warn("Collada: Expected url attribute in element"); nuclear@0: else nuclear@0: { nuclear@0: const char* url = mReader->getAttributeValue( attrID); nuclear@0: if( url[0] != '#') nuclear@0: ThrowException( "Unknown reference format in element"); nuclear@0: nuclear@0: pNode->mLights.push_back(LightInstance()); nuclear@0: pNode->mLights.back().mLight = url+1; nuclear@0: } nuclear@0: } nuclear@0: else if( IsElement( "instance_camera")) nuclear@0: { nuclear@0: // Reference to a camera, name given in 'url' attribute nuclear@0: int attrID = TestAttribute("url"); nuclear@0: if (-1 == attrID) nuclear@0: DefaultLogger::get()->warn("Collada: Expected url attribute in element"); nuclear@0: else nuclear@0: { nuclear@0: const char* url = mReader->getAttributeValue( attrID); nuclear@0: if( url[0] != '#') nuclear@0: ThrowException( "Unknown reference format in element"); nuclear@0: nuclear@0: pNode->mCameras.push_back(CameraInstance()); nuclear@0: pNode->mCameras.back().mCamera = url+1; nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // skip everything else for the moment nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a node transformation entry of the given type and adds it to the given node's transformation list. nuclear@0: void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType) nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: std::string tagName = mReader->getNodeName(); nuclear@0: nuclear@0: Transform tf; nuclear@0: tf.mType = pType; nuclear@0: nuclear@0: // read SID nuclear@0: int indexSID = TestAttribute( "sid"); nuclear@0: if( indexSID >= 0) nuclear@0: tf.mID = mReader->getAttributeValue( indexSID); nuclear@0: nuclear@0: // how many parameters to read per transformation type nuclear@0: static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 }; nuclear@0: const char* content = GetTextContent(); nuclear@0: nuclear@0: // read as many parameters and store in the transformation nuclear@0: for( unsigned int a = 0; a < sNumParameters[pType]; a++) nuclear@0: { nuclear@0: // read a number nuclear@0: content = fast_atoreal_move( content, tf.f[a]); nuclear@0: // skip whitespace after it nuclear@0: SkipSpacesAndLineEnd( &content); nuclear@0: } nuclear@0: nuclear@0: // place the transformation at the queue of the node nuclear@0: pNode->mTransforms.push_back( tf); nuclear@0: nuclear@0: // and consume the closing tag nuclear@0: TestClosing( tagName.c_str()); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Processes bind_vertex_input and bind elements nuclear@0: void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl) nuclear@0: { nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "bind_vertex_input")) nuclear@0: { nuclear@0: Collada::InputSemanticMapEntry vn; nuclear@0: nuclear@0: // effect semantic nuclear@0: int n = GetAttribute("semantic"); nuclear@0: std::string s = mReader->getAttributeValue(n); nuclear@0: nuclear@0: // input semantic nuclear@0: n = GetAttribute("input_semantic"); nuclear@0: vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) ); nuclear@0: nuclear@0: // index of input set nuclear@0: n = TestAttribute("input_set"); nuclear@0: if (-1 != n) nuclear@0: vn.mSet = mReader->getAttributeValueAsInt(n); nuclear@0: nuclear@0: tbl.mMap[s] = vn; nuclear@0: } nuclear@0: else if( IsElement( "bind")) { nuclear@0: DefaultLogger::get()->warn("Collada: Found unsupported element"); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { nuclear@0: if( strcmp( mReader->getNodeName(), "instance_material") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a mesh reference in a node and adds it to the node's mesh list nuclear@0: void ColladaParser::ReadNodeGeometry( Node* pNode) nuclear@0: { nuclear@0: // referred mesh is given as an attribute of the element nuclear@0: int attrUrl = GetAttribute( "url"); nuclear@0: const char* url = mReader->getAttributeValue( attrUrl); nuclear@0: if( url[0] != '#') nuclear@0: ThrowException( "Unknown reference format"); nuclear@0: nuclear@0: Collada::MeshInstance instance; nuclear@0: instance.mMeshOrController = url+1; // skipping the leading # nuclear@0: nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: { nuclear@0: // read material associations. Ignore additional elements inbetween nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) nuclear@0: { nuclear@0: if( IsElement( "instance_material")) nuclear@0: { nuclear@0: // read ID of the geometry subgroup and the target material nuclear@0: int attrGroup = GetAttribute( "symbol"); nuclear@0: std::string group = mReader->getAttributeValue( attrGroup); nuclear@0: int attrMaterial = GetAttribute( "target"); nuclear@0: const char* urlMat = mReader->getAttributeValue( attrMaterial); nuclear@0: Collada::SemanticMappingTable s; nuclear@0: if( urlMat[0] == '#') nuclear@0: urlMat++; nuclear@0: nuclear@0: s.mMatName = urlMat; nuclear@0: nuclear@0: // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff nuclear@0: if( !mReader->isEmptyElement()) nuclear@0: ReadMaterialVertexInputBinding(s); nuclear@0: nuclear@0: // store the association nuclear@0: instance.mMaterials[group] = s; nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: { nuclear@0: if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 nuclear@0: || strcmp( mReader->getNodeName(), "instance_controller") == 0) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // store it nuclear@0: pNode->mMeshes.push_back( instance); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the collada scene nuclear@0: void ColladaParser::ReadScene() nuclear@0: { nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { nuclear@0: if( IsElement( "instance_visual_scene")) nuclear@0: { nuclear@0: // should be the first and only occurence nuclear@0: if( mRootNode) nuclear@0: ThrowException( "Invalid scene containing multiple root nodes in element"); nuclear@0: nuclear@0: // read the url of the scene to instance. Should be of format "#some_name" nuclear@0: int urlIndex = GetAttribute( "url"); nuclear@0: const char* url = mReader->getAttributeValue( urlIndex); nuclear@0: if( url[0] != '#') nuclear@0: ThrowException( "Unknown reference format in element"); nuclear@0: nuclear@0: // find the referred scene, skip the leading # nuclear@0: NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1); nuclear@0: if( sit == mNodeLibrary.end()) nuclear@0: ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in element."); nuclear@0: mRootNode = sit->second; nuclear@0: } else { nuclear@0: SkipElement(); nuclear@0: } nuclear@0: } nuclear@0: else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){ nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Aborts the file reading with an exception nuclear@0: void ColladaParser::ThrowException( const std::string& pError) const nuclear@0: { nuclear@0: throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Skips all data until the end node of the current element nuclear@0: void ColladaParser::SkipElement() nuclear@0: { nuclear@0: // nothing to skip if it's an nuclear@0: if( mReader->isEmptyElement()) nuclear@0: return; nuclear@0: nuclear@0: // reroute nuclear@0: SkipElement( mReader->getNodeName()); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Skips all data until the end node of the given element nuclear@0: void ColladaParser::SkipElement( const char* pElement) nuclear@0: { nuclear@0: // copy the current node's name because it'a pointer to the reader's internal buffer, nuclear@0: // which is going to change with the upcoming parsing nuclear@0: std::string element = pElement; nuclear@0: while( mReader->read()) nuclear@0: { nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) nuclear@0: if( mReader->getNodeName() == element) nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Tests for an opening element of the given name, throws an exception if not found nuclear@0: void ColladaParser::TestOpening( const char* pName) nuclear@0: { nuclear@0: // read element start nuclear@0: if( !mReader->read()) nuclear@0: ThrowException( boost::str( boost::format( "Unexpected end of file while beginning of <%s> element.") % pName)); nuclear@0: // whitespace in front is ok, just read again if found nuclear@0: if( mReader->getNodeType() == irr::io::EXN_TEXT) nuclear@0: if( !mReader->read()) nuclear@0: ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of <%s> element.") % pName)); nuclear@0: nuclear@0: if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0) nuclear@0: ThrowException( boost::str( boost::format( "Expected start of <%s> element.") % pName)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Tests for the closing tag of the given element, throws an exception if not found nuclear@0: void ColladaParser::TestClosing( const char* pName) nuclear@0: { nuclear@0: // check if we're already on the closing tag and return right away nuclear@0: if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0) nuclear@0: return; nuclear@0: nuclear@0: // if not, read some more nuclear@0: if( !mReader->read()) nuclear@0: ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); nuclear@0: // whitespace in front is ok, just read again if found nuclear@0: if( mReader->getNodeType() == irr::io::EXN_TEXT) nuclear@0: if( !mReader->read()) nuclear@0: ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of <%s> element.") % pName)); nuclear@0: nuclear@0: // but this has the be the closing tag, or we're lost nuclear@0: if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0) nuclear@0: ThrowException( boost::str( boost::format( "Expected end of <%s> element.") % pName)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes nuclear@0: int ColladaParser::GetAttribute( const char* pAttr) const nuclear@0: { nuclear@0: int index = TestAttribute( pAttr); nuclear@0: if( index != -1) nuclear@0: return index; nuclear@0: nuclear@0: // attribute not found -> throw an exception nuclear@0: ThrowException( boost::str( boost::format( "Expected attribute \"%s\" for element <%s>.") % pAttr % mReader->getNodeName())); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Tests the present element for the presence of one attribute, returns its index or throws an exception if not found nuclear@0: int ColladaParser::TestAttribute( const char* pAttr) const nuclear@0: { nuclear@0: for( int a = 0; a < mReader->getAttributeCount(); a++) nuclear@0: if( strcmp( mReader->getAttributeName( a), pAttr) == 0) nuclear@0: return a; nuclear@0: nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the text contents of an element, throws an exception if not given. Skips leading whitespace. nuclear@0: const char* ColladaParser::GetTextContent() nuclear@0: { nuclear@0: const char* sz = TestTextContent(); nuclear@0: if(!sz) { nuclear@0: ThrowException( "Invalid contents in element \"n\"."); nuclear@0: } nuclear@0: return sz; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads the text contents of an element, returns NULL if not given. Skips leading whitespace. nuclear@0: const char* ColladaParser::TestTextContent() nuclear@0: { nuclear@0: // present node should be the beginning of an element nuclear@0: if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement()) nuclear@0: return NULL; nuclear@0: nuclear@0: // read contents of the element nuclear@0: if( !mReader->read() ) nuclear@0: return NULL; nuclear@0: if( mReader->getNodeType() != irr::io::EXN_TEXT) nuclear@0: return NULL; nuclear@0: nuclear@0: // skip leading whitespace nuclear@0: const char* text = mReader->getNodeData(); nuclear@0: SkipSpacesAndLineEnd( &text); nuclear@0: nuclear@0: return text; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Calculates the resulting transformation fromm all the given transform steps nuclear@0: aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector& pTransforms) const nuclear@0: { nuclear@0: aiMatrix4x4 res; nuclear@0: nuclear@0: for( std::vector::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) nuclear@0: { nuclear@0: const Transform& tf = *it; nuclear@0: switch( tf.mType) nuclear@0: { nuclear@0: case TF_LOOKAT: nuclear@0: { nuclear@0: aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]); nuclear@0: aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]); nuclear@0: aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize(); nuclear@0: aiVector3D dir = aiVector3D( dstPos - pos).Normalize(); nuclear@0: aiVector3D right = (dir ^ up).Normalize(); nuclear@0: nuclear@0: res *= aiMatrix4x4( nuclear@0: right.x, up.x, -dir.x, pos.x, nuclear@0: right.y, up.y, -dir.y, pos.y, nuclear@0: right.z, up.z, -dir.z, pos.z, nuclear@0: 0, 0, 0, 1); nuclear@0: break; nuclear@0: } nuclear@0: case TF_ROTATE: nuclear@0: { nuclear@0: aiMatrix4x4 rot; nuclear@0: float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f; nuclear@0: aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]); nuclear@0: aiMatrix4x4::Rotation( angle, axis, rot); nuclear@0: res *= rot; nuclear@0: break; nuclear@0: } nuclear@0: case TF_TRANSLATE: nuclear@0: { nuclear@0: aiMatrix4x4 trans; nuclear@0: aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans); nuclear@0: res *= trans; nuclear@0: break; nuclear@0: } nuclear@0: case TF_SCALE: nuclear@0: { nuclear@0: 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, nuclear@0: 0.0f, 0.0f, 0.0f, 1.0f); nuclear@0: res *= scale; nuclear@0: break; nuclear@0: } nuclear@0: case TF_SKEW: nuclear@0: // TODO: (thom) nuclear@0: ai_assert( false); nuclear@0: break; nuclear@0: case TF_MATRIX: nuclear@0: { nuclear@0: 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], nuclear@0: tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]); nuclear@0: res *= mat; nuclear@0: break; nuclear@0: } nuclear@0: default: nuclear@0: ai_assert( false); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Determines the input data type for the given semantic string nuclear@0: Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic) nuclear@0: { nuclear@0: if( pSemantic == "POSITION") nuclear@0: return IT_Position; nuclear@0: else if( pSemantic == "TEXCOORD") nuclear@0: return IT_Texcoord; nuclear@0: else if( pSemantic == "NORMAL") nuclear@0: return IT_Normal; nuclear@0: else if( pSemantic == "COLOR") nuclear@0: return IT_Color; nuclear@0: else if( pSemantic == "VERTEX") nuclear@0: return IT_Vertex; nuclear@0: else if( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL") nuclear@0: return IT_Bitangent; nuclear@0: else if( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT") nuclear@0: return IT_Tangent; nuclear@0: nuclear@0: DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic)); nuclear@0: return IT_Invalid; nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER