From 74204086ae963202acee7e0b4cc9e02dd9f91fbe Mon Sep 17 00:00:00 2001 From: ulfjorensen Date: Wed, 25 Mar 2009 21:10:28 +0000 Subject: [PATCH] - Collada loader now also loads skinned meshes, although still as static meshes. Preparation step, more to come. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@368 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/ColladaHelper.h | 38 +++- code/ColladaLoader.cpp | 57 ++++-- code/ColladaParser.cpp | 448 ++++++++++++++++++++++++++++++++++------- code/ColladaParser.h | 30 ++- 4 files changed, 479 insertions(+), 94 deletions(-) diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index ba6726b71..65f0491cb 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -186,13 +186,15 @@ struct SemanticMappingTable } }; -/** A reference to a mesh inside a node, including materials assigned to the various subgroups */ +/** A reference to a mesh inside a node, including materials assigned to the various subgroups. + * The ID refers to either a mesh or a controller which specifies the mesh + */ struct MeshInstance { - ///< ID of the mesh - std::string mMesh; + ///< ID of the mesh or controller to be instanced + std::string mMeshOrController; - ///< Map of materials by the subgroup ID they're applied to + ///< Map of materials by the subgroup ID they're applied to std::map mMaterials; }; @@ -255,10 +257,12 @@ struct Node } }; -/** Data source array */ +/** Data source array: either floats or strings */ struct Data { + bool mIsStringArray; std::vector mValues; + std::vector mStrings; }; /** Accessor to a data array */ @@ -347,6 +351,30 @@ enum PrimitiveType Prim_Polygon }; +/** A skeleton controller to deform a mesh with the use of joints */ +struct Controller +{ + // the URL of the mesh deformed by the controller. + std::string mMeshId; + + // accessor URL of the joint names + std::string mJointNameSource; + + // accessor URL of the joint inverse bind matrices + std::string mJointOffsetMatrixSource; + + // input channel: joint names. + InputChannel mWeightInputJoints; + // input channel: joint weights + InputChannel mWeightInputWeights; + + // Number of weights per vertex. + std::vector mWeightCounts; + + // JointIndex-WeightIndex pairs for all vertices + std::vector< std::pair > mWeights; +}; + /** A collada material. Pretty much the only member is a reference to an effect. */ struct Material { diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 7782069bb..392921a94 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -399,14 +399,33 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // add a mesh for each subgroup in each collada mesh BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes) { + const Collada::Mesh* srcMesh = NULL; + const Collada::Controller* srcController = NULL; + // find the referred mesh - ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMesh); + ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController); if( srcMeshIt == pParser.mMeshLibrary.end()) { - DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMesh)); - continue; + // if not found in the mesh-library, it might also be a controller referring to a mesh + ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController); + if( srcContrIt != pParser.mControllerLibrary.end()) + { + srcController = &srcContrIt->second; + srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId); + if( srcMeshIt != pParser.mMeshLibrary.end()) + srcMesh = srcMeshIt->second; + } + + if( !srcMesh) + { + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController)); + continue; + } + } else + { + // ID found in the mesh library -> direct reference to an unskinned mesh + srcMesh = srcMeshIt->second; } - const Collada::Mesh* srcMesh = srcMeshIt->second; // build a mesh for each of its subgroups size_t vertexStart = 0, faceStart = 0; @@ -424,7 +443,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll table = &meshMatIt->second; else { table = NULL; - DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMesh)); + DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMeshOrController)); } const std::string& meshMaterial = table ? table->mMatName : ""; @@ -451,7 +470,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll } // built lookup index of the Mesh-Submesh-Material combination - ColladaMeshIndex index( mid.mMesh, sm, meshMaterial); + ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial); // if we already have the mesh at the library, just add its index to the node's array std::map::const_iterator dstMeshIt = mMeshIndexByID.find( index); @@ -843,30 +862,33 @@ const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser // find the image referred by this name in the image library of the scene ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name); - if( imIt == pParser.mImageLibrary.end()) { + if( imIt == pParser.mImageLibrary.end()) + { throw new ImportErrorException( boost::str( boost::format( "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name)); } + static aiString result; // if this is an embedded texture image setup an aiTexture for it - if (imIt->second.mFileName.empty()) { - if (imIt->second.mImageData.empty()) { + if (imIt->second.mFileName.empty()) + { + if (imIt->second.mImageData.empty()) throw new ImportErrorException("Collada: Invalid texture, no data or file reference given"); - } + aiTexture* tex = new aiTexture(); // setup format hint - if (imIt->second.mEmbeddedFormat.length() > 3) { + if (imIt->second.mEmbeddedFormat.length() > 3) DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters"); - } - ::strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3); + + strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3); // and copy texture data tex->mHeight = 0; tex->mWidth = imIt->second.mImageData.size(); tex->pcData = (aiTexel*)new char[tex->mWidth]; - ::memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth); + memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth); // setup texture reference string result.data[0] = '*'; @@ -875,7 +897,8 @@ const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser // and add this texture to the list mTextures.push_back(tex); } - else { + else + { result.Set( imIt->second.mFileName ); ConvertPath(result); } @@ -890,10 +913,10 @@ void ColladaLoader::ConvertPath (aiString& ss) // For the moment we're just stripping the file:// away to make it work. // Windoes doesn't seem to be able to find stuff like // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' - if (0 == ::strncmp(ss.data,"file://",7)) + if (0 == strncmp(ss.data,"file://",7)) { ss.length -= 7; - ::memmove(ss.data,ss.data+7,ss.length); + memmove(ss.data,ss.data+7,ss.length); ss.data[ss.length] = '\0'; } } diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 85a3deb4d..609aef7ff 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -160,6 +160,8 @@ void ColladaParser::ReadStructure() if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "asset")) ReadAssetInfo(); + else if( IsElement( "library_controllers")) + ReadControllerLibrary(); else if( IsElement( "library_images")) ReadImageLibrary(); else if( IsElement( "library_materials")) @@ -233,6 +235,244 @@ void ColladaParser::ReadAssetInfo() } } +// ------------------------------------------------------------------------------------------------ +// Reads the skeleton controller library +void ColladaParser::ReadControllerLibrary() +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "controller")) + { + // read ID. Ask the spec if it's neccessary or optional... you might be surprised. + int attrID = GetAttribute( "id"); + std::string id = mReader->getAttributeValue( attrID); + + // create an entry and store it in the library under its ID + mControllerLibrary[id] = Controller(); + + // read on from there + ReadController( mControllerLibrary[id]); + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "library_controllers") != 0) + ThrowException( "Expected end of \"library_controllers\" element."); + + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a controller into the given mesh structure +void ColladaParser::ReadController( Collada::Controller& pController) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other + if( IsElement( "morph")) + { + // should skip everything inside, so there's no danger of catching elements inbetween + SkipElement(); + } + else if( IsElement( "skin")) + { + // read the mesh it refers to. According to the spec this could also be another + // controller, but I refuse to implement every bullshit idea they've come up with + int sourceIndex = GetAttribute( "source"); + pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1; + } + else if( IsElement( "bind_shape_matrix")) + { + // content is 16 floats to define some sort of matrix... I'm going to ignore this + // as long as I don't have a clue how to interpret it + SkipElement(); + } + else if( IsElement( "source")) + { + // data array - we have specialists to handle this + ReadSource(); + } + else if( IsElement( "joints")) + { + ReadControllerJoints( pController); + } + else if( IsElement( "vertex_weights")) + { + ReadControllerWeights( pController); + } + else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "controller") == 0) + break; + else if( strcmp( mReader->getNodeName(), "skin") != 0) + ThrowException( "Expected end of \"controller\" element."); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the joint definitions for the given controller +void ColladaParser::ReadControllerJoints( Collada::Controller& pController) +{ + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX" + if( IsElement( "input")) + { + int indexSemantic = GetAttribute( "semantic"); + const char* attrSemantic = mReader->getAttributeValue( indexSemantic); + int indexSource = GetAttribute( "source"); + const char* attrSource = mReader->getAttributeValue( indexSource); + + // local URLS always start with a '#'. We don't support global URLs + if( attrSource[0] != '#') + ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource)); + attrSource++; + + // parse source URL to corresponding source + if( strcmp( attrSemantic, "JOINT") == 0) + pController.mJointNameSource = attrSource; + else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0) + pController.mJointOffsetMatrixSource = attrSource; + else + ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in joint data") % attrSemantic)); + + // skip inner data, if present + if( !mReader->isEmptyElement()) + SkipElement(); + } + else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "joints") != 0) + ThrowException( "Expected end of \"joints\" element."); + + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the joint weights for the given controller +void ColladaParser::ReadControllerWeights( Collada::Controller& pController) +{ + // read vertex count from attributes and resize the array accordingly + int indexCount = GetAttribute( "count"); + size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount); + pController.mWeightCounts.resize( vertexCount); + + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT" + if( IsElement( "input")) + { + InputChannel channel; + + int indexSemantic = GetAttribute( "semantic"); + const char* attrSemantic = mReader->getAttributeValue( indexSemantic); + int indexSource = GetAttribute( "source"); + const char* attrSource = mReader->getAttributeValue( indexSource); + int indexOffset = TestAttribute( "offset"); + if( indexOffset >= 0) + channel.mOffset = mReader->getAttributeValueAsInt( indexOffset); + + // local URLS always start with a '#'. We don't support global URLs + if( attrSource[0] != '#') + ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource)); + channel.mAccessor = attrSource + 1; + + // parse source URL to corresponding source + if( strcmp( attrSemantic, "JOINT") == 0) + pController.mWeightInputJoints = channel; + else if( strcmp( attrSemantic, "WEIGHT") == 0) + pController.mWeightInputWeights = channel; + else + ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in vertex_weight data") % attrSemantic)); + + // skip inner data, if present + if( !mReader->isEmptyElement()) + SkipElement(); + } + else if( IsElement( "vcount")) + { + // read weight count per vertex + const char* text = GetTextContent(); + size_t numWeights = 0; + for( std::vector::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) + { + if( *text == 0) + ThrowException( "Out of data while reading vcount"); + + *it = strtol10( text, &text); + numWeights += *it; + SkipSpacesAndLineEnd( &text); + } + + TestClosing( "vcount"); + + // reserve weight count + pController.mWeights.resize( numWeights); + } + else if( IsElement( "v")) + { + // read JointIndex - WeightIndex pairs + const char* text = GetTextContent(); + + for( std::vector< std::pair >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) + { + if( *text == 0) + ThrowException( "Out of data while reading vertex_weights"); + it->first = strtol10( text, &text); + SkipSpacesAndLineEnd( &text); + if( *text == 0) + ThrowException( "Out of data while reading vertex_weights"); + it->second = strtol10( text, &text); + SkipSpacesAndLineEnd( &text); + } + + TestClosing( "v"); + } + else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "vertex_weights") != 0) + ThrowException( "Expected end of \"vertex_weights\" element."); + + break; + } + } +} + // ------------------------------------------------------------------------------------------------ // Reads the image library contents void ColladaParser::ReadImageLibrary() @@ -276,14 +516,15 @@ void ColladaParser::ReadImage( Collada::Image& pImage) // Need to run different code paths here, depending on the Collada XSD version if( IsElement( "init_from")) { - if (mFormat == FV_1_4_n) { + if (mFormat == FV_1_4_n) + { // element content is filename - hopefully const char* sz = TestTextContent(); if (sz)pImage.mFileName = sz; TestClosing( "init_from"); } - else if (mFormat == FV_1_5_n) { - + else if (mFormat == FV_1_5_n) + { // make sure we skip over mip and array initializations, which // we don't support, but which could confuse the loader if // they're not skipped. @@ -302,7 +543,8 @@ void ColladaParser::ReadImage( Collada::Image& pImage) // TODO: correctly jump over cube and volume maps? } } - else if (mFormat == FV_1_5_n) { + else if (mFormat == FV_1_5_n) + { if( IsElement( "ref")) { // element content is filename - hopefully @@ -327,13 +569,14 @@ void ColladaParser::ReadImage( Collada::Image& pImage) const unsigned int size = (unsigned int)(cur-data) * 2; pImage.mImageData.resize(size); - for (unsigned int i = 0; i < size;++i) { + for (unsigned int i = 0; i < size;++i) pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1)); - } + TestClosing( "hex"); } } - else { + else + { // ignore the rest SkipElement(); } @@ -351,7 +594,8 @@ void ColladaParser::ReadMaterialLibrary() { while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { if( IsElement( "material")) { // read ID. By now you propably know my opinion about this "specification" @@ -366,7 +610,8 @@ void ColladaParser::ReadMaterialLibrary() SkipElement(); } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { if( strcmp( mReader->getNodeName(), "library_materials") != 0) ThrowException( "Expected end of \"library_materials\" element."); @@ -999,7 +1244,7 @@ void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) { if( IsElement( "mesh")) { - // read on from there + // read on from there ReadMesh( pMesh); } else { @@ -1021,33 +1266,15 @@ void ColladaParser::ReadGeometry( Collada::Mesh* pMesh) // Reads a mesh from the geometry library void ColladaParser::ReadMesh( Mesh* pMesh) { - // I'm doing a dirty state parsing here because I don't want to open another submethod for it. - // There's a tag defining the name for the accessor inside, and possible a - // with it's own ID. This string contains the current source's ID if parsing is inside a element. - std::string presentSourceID; - while( mReader->read()) { if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { if( IsElement( "source")) { - // beginning of a source element - store ID for the inner elements - int attrID = GetAttribute( "id"); - presentSourceID = mReader->getAttributeValue( attrID); + // we have professionals dealing with this + ReadSource(); } - else if( IsElement( "float_array")) - { - ReadFloatArray(); - } - else if( IsElement( "technique_common")) - { - // I don't fucking care for your profiles bullshit - } - else if( IsElement( "accessor")) - { - ReadAccessor( presentSourceID); - } else if( IsElement( "vertices")) { // read per-vertex mesh data @@ -1066,12 +1293,7 @@ void ColladaParser::ReadMesh( Mesh* pMesh) } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "source") == 0) - { - // end of - reset present source ID - presentSourceID.clear(); - } - else if( strcmp( mReader->getNodeName(), "technique_common") == 0) + if( strcmp( mReader->getNodeName(), "technique_common") == 0) { // end of another meaningless element - read over it } @@ -1089,9 +1311,59 @@ void ColladaParser::ReadMesh( Mesh* pMesh) } // ------------------------------------------------------------------------------------------------ -// Reads a data array holding a number of floats, and stores it in the global library -void ColladaParser::ReadFloatArray() +// Reads a source element +void ColladaParser::ReadSource() { + int indexID = GetAttribute( "id"); + std::string sourceID = mReader->getAttributeValue( indexID); + + while( mReader->read()) + { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { + if( IsElement( "float_array") || IsElement( "IDREF_array")) + { + ReadDataArray(); + } + else if( IsElement( "technique_common")) + { + // I don't fucking care for your profiles bullshit + } + else if( IsElement( "accessor")) + { + ReadAccessor( sourceID); + } else + { + // ignore the rest + SkipElement(); + } + } + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "source") == 0) + { + // end of - we're done + break; + } + else if( strcmp( mReader->getNodeName(), "technique_common") == 0) + { + // end of another meaningless element - read over it + } else + { + // everything else should be punished + ThrowException( "Expected end of \"source\" element."); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads a data array holding a number of floats, and stores it in the global library +void ColladaParser::ReadDataArray() +{ + std::string elmName = mReader->getNodeName(); + bool isStringArray = (elmName == "IDREF_array"); + // read attributes int indexID = GetAttribute( "id"); std::string id = mReader->getAttributeValue( indexID); @@ -1102,22 +1374,45 @@ void ColladaParser::ReadFloatArray() // read values and store inside an array in the data library mDataLibrary[id] = Data(); Data& data = mDataLibrary[id]; - data.mValues.reserve( count); - for( unsigned int a = 0; a < count; a++) - { - if( *content == 0) - ThrowException( "Expected more values while reading float_array contents."); + data.mIsStringArray = isStringArray; - float value; - // read a number - content = fast_atof_move( content, value); - data.mValues.push_back( value); - // skip whitespace after it - SkipSpacesAndLineEnd( &content); + if( isStringArray) + { + data.mStrings.reserve( count); + std::string s; + + for( unsigned int a = 0; a < count; a++) + { + if( *content == 0) + ThrowException( "Expected more values while reading IDREF_array contents."); + + s.clear(); + while( !IsSpaceOrNewLine( *content)) + s += *content++; + data.mStrings.push_back( s); + + SkipSpacesAndLineEnd( &content); + } + } else + { + data.mValues.reserve( count); + + for( unsigned int a = 0; a < count; a++) + { + if( *content == 0) + ThrowException( "Expected more values while reading float_array contents."); + + float value; + // read a number + content = fast_atof_move( content, value); + data.mValues.push_back( value); + // skip whitespace after it + SkipSpacesAndLineEnd( &content); + } } // test for closing tag - TestClosing( "float_array"); + TestClosing( elmName.c_str()); } // ------------------------------------------------------------------------------------------------ @@ -1636,7 +1931,8 @@ void ColladaParser::ReadSceneNode( Node* pNode) while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { if( IsElement( "node")) { Node* child = new Node; @@ -1651,12 +1947,13 @@ void ColladaParser::ReadSceneNode( Node* pNode) // TODO: (thom) support SIDs // assert( TestAttribute( "sid") == -1); - if (pNode) { - + if (pNode) + { pNode->mChildren.push_back( child); child->mParent = pNode; } - else { + else + { // no parent node given, probably called from element. // create new node in node library mNodeLibrary[child->mID] = pNode = child; @@ -1689,36 +1986,44 @@ void ColladaParser::ReadSceneNode( Node* pNode) // render a Collada scene. The only thing that is interesting for // us is the primary camera. int attrId = TestAttribute("camera_node"); - if (-1 != attrId) { + if (-1 != attrId) + { const char* s = mReader->getAttributeValue(attrId); if (s[0] != '#') DefaultLogger::get()->error("Collada: Unresolved reference format of camera"); - else pNode->mPrimaryCamera = s+1; + else + pNode->mPrimaryCamera = s+1; } } - else if( IsElement( "instance_node")) { + else if( IsElement( "instance_node")) + { // find the node in the library int attrID = TestAttribute( "url"); - if( attrID != -1) { + if( attrID != -1) + { const char* s = mReader->getAttributeValue(attrID); if (s[0] != '#') DefaultLogger::get()->error("Collada: Unresolved reference format of node"); - else { + else + { pNode->mNodeInstances.push_back(NodeInstance()); pNode->mNodeInstances.back().mNode = s+1; } } } - else if( IsElement( "instance_geometry")) { - // Reference to a mesh, with possible material associations + else if( IsElement( "instance_geometry") || IsElement( "instance_controller")) + { + // Reference to a mesh or controller, with possible material associations ReadNodeGeometry( pNode); } - else if( IsElement( "instance_light")) { + else if( IsElement( "instance_light")) + { // Reference to a light, name given in 'url' attribute int attrID = TestAttribute("url"); if (-1 == attrID) DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else { + else + { const char* url = mReader->getAttributeValue( attrID); if( url[0] != '#') ThrowException( "Unknown reference format in element"); @@ -1727,12 +2032,14 @@ void ColladaParser::ReadSceneNode( Node* pNode) pNode->mLights.back().mLight = url+1; } } - else if( IsElement( "instance_camera")) { + else if( IsElement( "instance_camera")) + { // Reference to a camera, name given in 'url' attribute int attrID = TestAttribute("url"); if (-1 == attrID) DefaultLogger::get()->warn("Collada: Expected url attribute in element"); - else { + else + { const char* url = mReader->getAttributeValue( attrID); if( url[0] != '#') ThrowException( "Unknown reference format in element"); @@ -1829,14 +2136,15 @@ void ColladaParser::ReadNodeGeometry( Node* pNode) ThrowException( "Unknown reference format"); Collada::MeshInstance instance; - instance.mMesh = url+1; // skipping the leading # + instance.mMeshOrController = url+1; // skipping the leading # if( !mReader->isEmptyElement()) { // read material associations. Ignore additional elements inbetween while( mReader->read()) { - if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) + { if( IsElement( "instance_material")) { // read ID of the geometry subgroup and the target material @@ -1858,8 +2166,10 @@ void ColladaParser::ReadNodeGeometry( Node* pNode) instance.mMaterials[group] = s; } } - else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { - if( strcmp( mReader->getNodeName(), "instance_geometry") == 0) + else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) + { + if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 + || strcmp( mReader->getNodeName(), "instance_controller") == 0) break; } } diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 37b5876b8..6cf995894 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -48,7 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "irrXMLWrapper.h" #include "ColladaHelper.h" -namespace Assimp { +namespace Assimp +{ // ------------------------------------------------------------------------------------------ /** Parser helper class for the Collada loader. @@ -76,6 +77,18 @@ protected: /** Reads asset informations such as coordinate system informations and legal blah */ void ReadAssetInfo(); + /** Reads the skeleton controller library */ + void ReadControllerLibrary(); + + /** Reads a controller into the given mesh structure */ + void ReadController( Collada::Controller& pController); + + /** Reads the joint definitions for the given controller */ + void ReadControllerJoints( Collada::Controller& pController); + + /** Reads the joint weights for the given controller */ + void ReadControllerWeights( Collada::Controller& pController); + /** Reads the image library contents */ void ReadImageLibrary(); @@ -127,8 +140,15 @@ protected: /** Reads a mesh from the geometry library */ void ReadMesh( Collada::Mesh* pMesh); - /** Reads a data array holding a number of floats, and stores it in the global library */ - void ReadFloatArray(); + /** Reads a source element - a combination of raw data and an accessor defining + * things that should not be redefinable. Yes, that's another rant. + */ + void ReadSource(); + + /** Reads a data array holding a number of elements, and stores it in the global library. + * Currently supported are array of floats and arrays of strings. + */ + void ReadDataArray(); /** Reads an accessor and stores it in the global library under the given ID - * accessors use the ID of the parent element @@ -264,6 +284,10 @@ protected: typedef std::map CameraLibrary; CameraLibrary mCameraLibrary; + /** Controller library: joint controllers by ID */ + typedef std::map ControllerLibrary; + ControllerLibrary mControllerLibrary; + /** Pointer to the root node. Don't delete, it just points to one of the nodes in the node library. */ Collada::Node* mRootNode;