| From 9b2ce69f44e8ab60b48b3230fc4c0506aa156e40 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Antti=20M=C3=A4=C3=A4tt=C3=A4?= <antti.maatta@qt.io> |
| Date: Mon, 10 Oct 2016 12:13:55 +0300 |
| Subject: [PATCH] Morph animation loading support for Collada |
| |
| Add morph animation support for collada and morph target loading. |
| |
| Change-Id: I291cf1ff5e575729d0136cf0880e9ef780ac1600 |
| --- |
| src/3rdparty/assimp/assimp.pri | 6 +- |
| src/3rdparty/assimp/code/ColladaHelper.h | 36 +- |
| src/3rdparty/assimp/code/ColladaLoader.cpp | 797 ++++++++++++++++++---------- |
| src/3rdparty/assimp/code/ColladaLoader.h | 5 + |
| src/3rdparty/assimp/code/ColladaParser.cpp | 50 +- |
| src/3rdparty/assimp/code/CreateAnimMesh.cpp | 92 ++++ |
| src/3rdparty/assimp/code/CreateAnimMesh.h | 56 ++ |
| src/3rdparty/assimp/include/assimp/anim.h | 83 ++- |
| src/3rdparty/assimp/include/assimp/mesh.h | 34 +- |
| 9 files changed, 866 insertions(+), 293 deletions(-) |
| create mode 100644 src/3rdparty/assimp/code/CreateAnimMesh.cpp |
| create mode 100644 src/3rdparty/assimp/code/CreateAnimMesh.h |
| |
| diff --git a/src/3rdparty/assimp/assimp.pri b/src/3rdparty/assimp/assimp.pri |
| index d3282fb..802a645 100644 |
| --- a/src/3rdparty/assimp/assimp.pri |
| +++ b/src/3rdparty/assimp/assimp.pri |
| @@ -318,7 +318,8 @@ HEADERS += revision.h \ |
| code/STEPFileEncoding.h \ |
| code/OgreBinarySerializer.h \ |
| code/OgreStructs.h \ |
| - code/OgreXmlSerializer.h |
| + code/OgreXmlSerializer.h \ |
| + code/CreateAnimMesh.h |
| |
| SOURCES += code/3DSConverter.cpp \ |
| code/3DSLoader.cpp \ |
| @@ -470,7 +471,8 @@ SOURCES += code/3DSConverter.cpp \ |
| code/STEPFileEncoding.cpp \ |
| code/OgreBinarySerializer.cpp \ |
| code/OgreStructs.cpp \ |
| - code/OgreXmlSerializer.cpp |
| + code/OgreXmlSerializer.cpp \ |
| + code/CreateAnimMesh.cpp |
| |
| |
| |
| diff --git a/src/3rdparty/assimp/code/ColladaHelper.h b/src/3rdparty/assimp/code/ColladaHelper.h |
| index 0e087bd..2f05d46 100644 |
| --- a/src/3rdparty/assimp/code/ColladaHelper.h |
| +++ b/src/3rdparty/assimp/code/ColladaHelper.h |
| @@ -79,6 +79,20 @@ enum InputType |
| IT_Bitangent |
| }; |
| |
| +/** Supported controller types */ |
| +enum ControllerType |
| +{ |
| + Skin, |
| + Morph |
| +}; |
| + |
| +/** Supported morph methods */ |
| +enum MorphMethod |
| +{ |
| + Normalized, |
| + Relative |
| +}; |
| + |
| /** Contains all data for one of the different transformation types */ |
| struct Transform |
| { |
| @@ -366,17 +380,23 @@ enum PrimitiveType |
| Prim_Polygon |
| }; |
| |
| -/** A skeleton controller to deform a mesh with the use of joints */ |
| +/** A skeleton or morph controller to deform a mesh with the use of joints or morph targets */ |
| struct Controller |
| { |
| + // controller type |
| + ControllerType mType; |
| + |
| + // Morphing method if type is Morph |
| + MorphMethod mMethod; |
| + |
| // the URL of the mesh deformed by the controller. |
| std::string mMeshId; |
| |
| // accessor URL of the joint names |
| std::string mJointNameSource; |
| |
| - ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases |
| - float mBindShapeMatrix[16]; |
| + ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases |
| + float mBindShapeMatrix[16]; |
| |
| // accessor URL of the joint inverse bind matrices |
| std::string mJointOffsetMatrixSource; |
| @@ -391,6 +411,9 @@ struct Controller |
| |
| // JointIndex-WeightIndex pairs for all vertices |
| std::vector< std::pair<size_t, size_t> > mWeights; |
| + |
| + std::string mMorphTarget; |
| + std::string mMorphWeight; |
| }; |
| |
| /** A collada material. Pretty much the only member is a reference to an effect. */ |
| @@ -559,6 +582,12 @@ struct AnimationChannel |
| std::string mSourceTimes; |
| /** Source URL of the value values. Collada calls them "output". */ |
| std::string mSourceValues; |
| + /** Source URL of the IN_TANGENT semantic values. */ |
| + std::string mInTanValues; |
| + /** Source URL of the OUT_TANGENT semantic values. */ |
| + std::string mOutTanValues; |
| + /** Source URL of the INTERPOLATION semantic values. */ |
| + std::string mInterpolationValues; |
| }; |
| |
| /** An animation. Container for 0-x animation channels or 0-x animations */ |
| @@ -585,6 +614,7 @@ struct Animation |
| struct ChannelEntry |
| { |
| const Collada::AnimationChannel* mChannel; ///> the source channel |
| + std::string mTargetId; |
| std::string mTransformId; // the ID of the transformation step of the node which is influenced |
| size_t mTransformIndex; // Index into the node's transform chain to apply the channel to |
| size_t mSubElement; // starting index inside the transform data |
| diff --git a/src/3rdparty/assimp/code/ColladaLoader.cpp b/src/3rdparty/assimp/code/ColladaLoader.cpp |
| index 2096e95..c8b9c10 100644 |
| --- a/src/3rdparty/assimp/code/ColladaLoader.cpp |
| +++ b/src/3rdparty/assimp/code/ColladaLoader.cpp |
| @@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| #include "fast_atof.h" |
| #include "ParsingUtils.h" |
| #include "SkeletonMeshBuilder.h" |
| +#include "CreateAnimMesh.h" |
| |
| #include "time.h" |
| |
| @@ -125,15 +126,16 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I |
| { |
| mFileName = pFile; |
| |
| - // clean all member arrays - just for safety, it should work even if we did not |
| - mMeshIndexByID.clear(); |
| - mMaterialIndexByName.clear(); |
| - mMeshes.clear(); |
| - newMats.clear(); |
| - mLights.clear(); |
| - mCameras.clear(); |
| - mTextures.clear(); |
| - mAnims.clear(); |
| + // clean all member arrays - just for safety, it should work even if we did not |
| + mMeshIndexByID.clear(); |
| + mMaterialIndexByName.clear(); |
| + mMeshes.clear(); |
| + mTargetMeshes.clear(); |
| + newMats.clear(); |
| + mLights.clear(); |
| + mCameras.clear(); |
| + mTextures.clear(); |
| + mAnims.clear(); |
| |
| // parse the input file |
| ColladaParser parser( pIOHandler, pFile); |
| @@ -540,6 +542,21 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| +// Find mesh from either meshes or morph target meshes |
| +aiMesh *ColladaLoader::findMesh(std::string meshid) |
| +{ |
| + for (unsigned int i = 0; i < mMeshes.size(); i++) |
| + if (std::string(mMeshes[i]->mName.data) == meshid) |
| + return mMeshes[i]; |
| + |
| + for (unsigned int i = 0; i < mTargetMeshes.size(); i++) |
| + if (std::string(mTargetMeshes[i]->mName.data) == meshid) |
| + return mTargetMeshes[i]; |
| + |
| + return NULL; |
| +} |
| + |
| +// ------------------------------------------------------------------------------------------------ |
| // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh |
| aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, |
| const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) |
| @@ -624,149 +641,211 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: |
| face.mIndices[b] = vertex++; |
| } |
| |
| + // create morph target meshes if any |
| + std::vector<aiMesh*> targetMeshes; |
| + std::vector<float> targetWeights; |
| + Collada::MorphMethod method; |
| + |
| + for(std::map<std::string, Collada::Controller>::const_iterator it = pParser.mControllerLibrary.begin(); |
| + it != pParser.mControllerLibrary.end(); it++) |
| + { |
| + const Collada::Controller &c = it->second; |
| + const Collada::Mesh* baseMesh = pParser.ResolveLibraryReference( pParser.mMeshLibrary, c.mMeshId); |
| + |
| + if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName) |
| + { |
| + const Collada::Accessor& targetAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphTarget); |
| + const Collada::Accessor& weightAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphWeight); |
| + const Collada::Data& targetData = pParser.ResolveLibraryReference( pParser.mDataLibrary, targetAccessor.mSource); |
| + const Collada::Data& weightData = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightAccessor.mSource); |
| + |
| + // take method |
| + method = c.mMethod; |
| + |
| + if (!targetData.mIsStringArray) |
| + throw DeadlyImportError( "target data must contain id. "); |
| + if (weightData.mIsStringArray) |
| + throw DeadlyImportError( "target weight data must not be textual "); |
| + |
| + for (unsigned int i = 0; i < targetData.mStrings.size(); ++i) |
| + { |
| + const Collada::Mesh* targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, targetData.mStrings.at(i)); |
| + |
| + aiMesh *aimesh = findMesh(targetMesh->mName); |
| + if (!aimesh) |
| + { |
| + if (targetMesh->mSubMeshes.size() > 1) |
| + throw DeadlyImportError( "Morhing target mesh must be a single"); |
| + aimesh = CreateMesh(pParser, targetMesh, targetMesh->mSubMeshes.at(0), NULL, 0, 0); |
| + mTargetMeshes.push_back(aimesh); |
| + } |
| + targetMeshes.push_back(aimesh); |
| + } |
| + for (unsigned int i = 0; i < weightData.mValues.size(); ++i) |
| + targetWeights.push_back(weightData.mValues.at(i)); |
| + } |
| + } |
| + if (targetMeshes.size() > 0 && targetWeights.size() == targetMeshes.size()) |
| + { |
| + std::vector<aiAnimMesh*> animMeshes; |
| + for (unsigned int i = 0; i < targetMeshes.size(); i++) |
| + { |
| + aiAnimMesh *animMesh = aiCreateAnimMesh(targetMeshes.at(i)); |
| + animMesh->mWeight = targetWeights[i]; |
| + animMeshes.push_back(animMesh); |
| + } |
| + dstMesh->mMethod = (method == Collada::Relative) |
| + ? aiMorphingMethod_MORPH_RELATIVE |
| + : aiMorphingMethod_MORPH_NORMALIZED; |
| + dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()]; |
| + dstMesh->mNumAnimMeshes = animMeshes.size(); |
| + for (unsigned int i = 0; i < animMeshes.size(); i++) |
| + dstMesh->mAnimMeshes[i] = animMeshes.at(i); |
| + } |
| + |
| // create bones if given |
| - if( pSrcController) |
| + if( pSrcController && pSrcController->mType == Collada::Skin) |
| { |
| // refuse if the vertex count does not match |
| // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) |
| // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); |
| |
| - // resolve references - joint names |
| - const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); |
| - const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource); |
| - // joint offset matrices |
| - const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); |
| - const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource); |
| - // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider |
| - const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); |
| - if( &weightNamesAcc != &jointNamesAcc) |
| - throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author."); |
| - // vertex weights |
| - const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); |
| - const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource); |
| - |
| - if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) |
| - throw DeadlyImportError( "Data type mismatch while resolving mesh joints"); |
| - // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex |
| - if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) |
| - throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. "); |
| - |
| - // create containers to collect the weights for each bone |
| - size_t numBones = jointNames.mStrings.size(); |
| - std::vector<std::vector<aiVertexWeight> > dstBones( numBones); |
| - |
| - // build a temporary array of pointers to the start of each vertex's weights |
| - typedef std::vector< std::pair<size_t, size_t> > IndexPairVector; |
| - std::vector<IndexPairVector::const_iterator> weightStartPerVertex; |
| - weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end()); |
| - |
| - IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); |
| - for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) |
| - { |
| - weightStartPerVertex[a] = pit; |
| - pit += pSrcController->mWeightCounts[a]; |
| - } |
| - |
| - // now for each vertex put the corresponding vertex weights into each bone's weight collection |
| - for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) |
| - { |
| - // which position index was responsible for this vertex? that's also the index by which |
| - // the controller assigns the vertex weights |
| - size_t orgIndex = pSrcMesh->mFacePosIndices[a]; |
| - // find the vertex weights for this vertex |
| - IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; |
| - size_t pairCount = pSrcController->mWeightCounts[orgIndex]; |
| - |
| - for( size_t b = 0; b < pairCount; ++b, ++iit) |
| - { |
| - size_t jointIndex = iit->first; |
| - size_t vertexIndex = iit->second; |
| - |
| - float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0); |
| - |
| - // one day I gonna kill that XSI Collada exporter |
| - if( weight > 0.0f) |
| - { |
| - aiVertexWeight w; |
| - w.mVertexId = a - pStartVertex; |
| - w.mWeight = weight; |
| - dstBones[jointIndex].push_back( w); |
| - } |
| - } |
| - } |
| - |
| - // count the number of bones which influence vertices of the current submesh |
| - size_t numRemainingBones = 0; |
| - for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) |
| - if( it->size() > 0) |
| - numRemainingBones++; |
| - |
| - // create bone array and copy bone weights one by one |
| - dstMesh->mNumBones = numRemainingBones; |
| - dstMesh->mBones = new aiBone*[numRemainingBones]; |
| - size_t boneCount = 0; |
| - for( size_t a = 0; a < numBones; ++a) |
| - { |
| - // omit bones without weights |
| - if( dstBones[a].size() == 0) |
| - continue; |
| + // resolve references - joint names |
| + const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); |
| + const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource); |
| + // joint offset matrices |
| + const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); |
| + const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource); |
| + // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider |
| + const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); |
| + if( &weightNamesAcc != &jointNamesAcc) |
| + throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author."); |
| + // vertex weights |
| + const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); |
| + const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource); |
| + |
| + if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) |
| + throw DeadlyImportError( "Data type mismatch while resolving mesh joints"); |
| + // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex |
| + if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) |
| + throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. "); |
| + |
| + // create containers to collect the weights for each bone |
| + size_t numBones = jointNames.mStrings.size(); |
| + std::vector<std::vector<aiVertexWeight> > dstBones( numBones); |
| + |
| + // build a temporary array of pointers to the start of each vertex's weights |
| + typedef std::vector< std::pair<size_t, size_t> > IndexPairVector; |
| + std::vector<IndexPairVector::const_iterator> weightStartPerVertex; |
| + weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end()); |
| + |
| + IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); |
| + for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) |
| + { |
| + weightStartPerVertex[a] = pit; |
| + pit += pSrcController->mWeightCounts[a]; |
| + } |
| |
| - // create bone with its weights |
| - aiBone* bone = new aiBone; |
| - bone->mName = ReadString( jointNamesAcc, jointNames, a); |
| - bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0); |
| - bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1); |
| - bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2); |
| - bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3); |
| - bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4); |
| - bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5); |
| - bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6); |
| - bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7); |
| - bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8); |
| - bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9); |
| - bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10); |
| - bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11); |
| - bone->mNumWeights = dstBones[a].size(); |
| - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; |
| - std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); |
| - |
| - // apply bind shape matrix to offset matrix |
| - aiMatrix4x4 bindShapeMatrix; |
| - bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0]; |
| - bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1]; |
| - bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2]; |
| - bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3]; |
| - bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4]; |
| - bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5]; |
| - bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6]; |
| - bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7]; |
| - bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8]; |
| - bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9]; |
| - bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10]; |
| - bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11]; |
| - bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12]; |
| - bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13]; |
| - bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14]; |
| - bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15]; |
| - bone->mOffsetMatrix *= bindShapeMatrix; |
| - |
| - // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name. |
| - // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID, |
| - // and replace the bone's name by the node's name so that the user can use the standard |
| - // find-by-name method to associate nodes with bones. |
| - const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data); |
| - if( !bnode) |
| - bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data); |
| - |
| - // assign the name that we would have assigned for the source node |
| - if( bnode) |
| - bone->mName.Set( FindNameForNode( bnode)); |
| - else |
| - DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data)); |
| + // now for each vertex put the corresponding vertex weights into each bone's weight collection |
| + for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) |
| + { |
| + // which position index was responsible for this vertex? that's also the index by which |
| + // the controller assigns the vertex weights |
| + size_t orgIndex = pSrcMesh->mFacePosIndices[a]; |
| + // find the vertex weights for this vertex |
| + IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; |
| + size_t pairCount = pSrcController->mWeightCounts[orgIndex]; |
| + |
| + for( size_t b = 0; b < pairCount; ++b, ++iit) |
| + { |
| + size_t jointIndex = iit->first; |
| + size_t vertexIndex = iit->second; |
| + |
| + float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0); |
| + |
| + // one day I gonna kill that XSI Collada exporter |
| + if( weight > 0.0f) |
| + { |
| + aiVertexWeight w; |
| + w.mVertexId = a - pStartVertex; |
| + w.mWeight = weight; |
| + dstBones[jointIndex].push_back( w); |
| + } |
| + } |
| + } |
| |
| - // and insert bone |
| - dstMesh->mBones[boneCount++] = bone; |
| - } |
| + // count the number of bones which influence vertices of the current submesh |
| + size_t numRemainingBones = 0; |
| + for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) |
| + if( it->size() > 0) |
| + numRemainingBones++; |
| + |
| + // create bone array and copy bone weights one by one |
| + dstMesh->mNumBones = numRemainingBones; |
| + dstMesh->mBones = new aiBone*[numRemainingBones]; |
| + size_t boneCount = 0; |
| + for( size_t a = 0; a < numBones; ++a) |
| + { |
| + // omit bones without weights |
| + if( dstBones[a].size() == 0) |
| + continue; |
| + |
| + // create bone with its weights |
| + aiBone* bone = new aiBone; |
| + bone->mName = ReadString( jointNamesAcc, jointNames, a); |
| + bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0); |
| + bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1); |
| + bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2); |
| + bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3); |
| + bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4); |
| + bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5); |
| + bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6); |
| + bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7); |
| + bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8); |
| + bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9); |
| + bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10); |
| + bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11); |
| + bone->mNumWeights = dstBones[a].size(); |
| + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; |
| + std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); |
| + |
| + // apply bind shape matrix to offset matrix |
| + aiMatrix4x4 bindShapeMatrix; |
| + bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0]; |
| + bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1]; |
| + bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2]; |
| + bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3]; |
| + bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4]; |
| + bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5]; |
| + bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6]; |
| + bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7]; |
| + bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8]; |
| + bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9]; |
| + bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10]; |
| + bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11]; |
| + bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12]; |
| + bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13]; |
| + bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14]; |
| + bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15]; |
| + bone->mOffsetMatrix *= bindShapeMatrix; |
| + |
| + // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name. |
| + // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID, |
| + // and replace the bone's name by the node's name so that the user can use the standard |
| + // find-by-name method to associate nodes with bones. |
| + const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data); |
| + if( !bnode) |
| + bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data); |
| + |
| + // assign the name that we would have assigned for the source node |
| + if( bnode) |
| + bone->mName.Set( FindNameForNode( bnode)); |
| + else |
| + DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data)); |
| + |
| + // and insert bone |
| + dstMesh->mBones[boneCount++] = bone; |
| + } |
| } |
| |
| return dstMesh; |
| @@ -924,15 +1003,79 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars |
| CreateAnimation( pScene, pParser, pSrcAnim, animName); |
| } |
| |
| +struct MorphTimeValues |
| +{ |
| + float mTime; |
| + struct key |
| + { |
| + float mWeight; |
| + unsigned int mValue; |
| + }; |
| + std::vector<key> mKeys; |
| +}; |
| + |
| +void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value) |
| +{ |
| + MorphTimeValues::key k; |
| + k.mValue = value; |
| + k.mWeight = weight; |
| + if (values.size() == 0 || time < values[0].mTime) |
| + { |
| + MorphTimeValues val; |
| + val.mTime = time; |
| + val.mKeys.push_back(k); |
| + values.insert(values.begin(), val); |
| + return; |
| + } |
| + if (time > values.back().mTime) |
| + { |
| + MorphTimeValues val; |
| + val.mTime = time; |
| + val.mKeys.push_back(k); |
| + values.insert(values.end(), val); |
| + return; |
| + } |
| + for (unsigned int i = 0; i < values.size(); i++) |
| + { |
| + if (std::abs(time - values[i].mTime) < 1e-6f) |
| + { |
| + values[i].mKeys.push_back(k); |
| + return; |
| + } else if (time > values[i].mTime && time < values[i+1].mTime) |
| + { |
| + MorphTimeValues val; |
| + val.mTime = time; |
| + val.mKeys.push_back(k); |
| + values.insert(values.begin() + i, val); |
| + return; |
| + } |
| + } |
| + // should not get here |
| +} |
| + |
| +float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value) |
| +{ |
| + for (unsigned int i = 0; i < values[key].mKeys.size(); i++) |
| + { |
| + if (values[key].mKeys[i].mValue == value) |
| + return values[key].mKeys[i].mWeight; |
| + } |
| + // no value at key found, try to interpolate if present at other keys. if not, return zero |
| + // TODO: interpolation |
| + return 0.0f; |
| +} |
| + |
| // ------------------------------------------------------------------------------------------------ |
| // Constructs the animation for the given source anim |
| void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName) |
| { |
| - // collect a list of animatable nodes |
| - std::vector<const aiNode*> nodes; |
| - CollectNodes( pScene->mRootNode, nodes); |
| + // collect a list of animatable nodes |
| + std::vector<const aiNode*> nodes; |
| + CollectNodes( pScene->mRootNode, nodes); |
| + |
| + std::vector<aiNodeAnim*> anims; |
| + std::vector<aiMeshMorphAnim*> morphAnims; |
| |
| - std::vector<aiNodeAnim*> anims; |
| for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) |
| { |
| // find all the collada anim channels which refer to the current node |
| @@ -955,8 +1098,20 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars |
| // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others |
| // find the slash that separates the node name - there should be only one |
| std::string::size_type slashPos = srcChannel.mTarget.find( '/'); |
| - if( slashPos == std::string::npos) |
| - continue; |
| + if( slashPos == std::string::npos) { |
| + std::string::size_type targetPos = srcChannel.mTarget.find(srcNode->mID); |
| + if (targetPos == std::string::npos) |
| + continue; |
| + |
| + // not node transform, but something else. store as unknown animation channel for now |
| + entry.mChannel = &(*cit); |
| + entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(), |
| + srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length()); |
| + if (entry.mTargetId.front() == '-') |
| + entry.mTargetId = entry.mTargetId.substr(1); |
| + entries.push_back(entry); |
| + continue; |
| + } |
| if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) |
| continue; |
| std::string targetID = srcChannel.mTarget.substr( 0, slashPos); |
| @@ -995,9 +1150,15 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars |
| if( srcNode->mTransforms[a].mID == entry.mTransformId) |
| entry.mTransformIndex = a; |
| |
| - if( entry.mTransformIndex == SIZE_MAX) { |
| - continue; |
| - } |
| + if( entry.mTransformIndex == SIZE_MAX) |
| + { |
| + if (entry.mTransformId.find("morph-weights") != std::string::npos) |
| + { |
| + entry.mTargetId = entry.mTransformId; |
| + entry.mTransformId = ""; |
| + } else |
| + continue; |
| + } |
| |
| entry.mChannel = &(*cit); |
| entries.push_back( entry); |
| @@ -1021,143 +1182,219 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars |
| if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) |
| throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); |
| |
| - if( e.mTimeAccessor->mCount > 0 ) |
| - { |
| - // find bounding times |
| - startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); |
| - endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); |
| - } |
| - } |
| + if( e.mTimeAccessor->mCount > 0 ) |
| + { |
| + // find bounding times |
| + startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); |
| + endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); |
| + } |
| + } |
| |
| - std::vector<aiMatrix4x4> resultTrafos; |
| - if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) |
| - { |
| - // create a local transformation chain of the node's transforms |
| - std::vector<Collada::Transform> transforms = srcNode->mTransforms; |
| - |
| - // now for every unique point in time, find or interpolate the key values for that time |
| - // and apply them to the transform chain. Then the node's present transformation can be calculated. |
| - float time = startTime; |
| - while( 1) |
| - { |
| - for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) |
| - { |
| - Collada::ChannelEntry& e = *it; |
| - |
| - // find the keyframe behind the current point in time |
| - size_t pos = 0; |
| - float postTime = 0.f; |
| - while( 1) |
| - { |
| - if( pos >= e.mTimeAccessor->mCount) |
| - break; |
| - postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); |
| - if( postTime >= time) |
| - break; |
| - ++pos; |
| - } |
| - |
| - pos = std::min( pos, e.mTimeAccessor->mCount-1); |
| - |
| - // read values from there |
| - float temp[16]; |
| - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) |
| - temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); |
| - |
| - // if not exactly at the key time, interpolate with previous value set |
| - if( postTime > time && pos > 0) |
| - { |
| - float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); |
| - float factor = (time - postTime) / (preTime - postTime); |
| - |
| - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) |
| - { |
| - float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); |
| - temp[c] += (v - temp[c]) * factor; |
| - } |
| - } |
| - |
| - // Apply values to current transformation |
| - std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); |
| - } |
| - |
| - // Calculate resulting transformation |
| - aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); |
| - |
| - // out of lazyness: we store the time in matrix.d4 |
| - mat.d4 = time; |
| - resultTrafos.push_back( mat); |
| - |
| - // find next point in time to evaluate. That's the closest frame larger than the current in any channel |
| - float nextTime = 1e20f; |
| - for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) |
| - { |
| - Collada::ChannelEntry& e = *it; |
| - |
| - // find the next time value larger than the current |
| - size_t pos = 0; |
| - while( pos < e.mTimeAccessor->mCount) |
| - { |
| - float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); |
| - if( t > time) |
| - { |
| - nextTime = std::min( nextTime, t); |
| - break; |
| - } |
| - ++pos; |
| - } |
| - } |
| - |
| - // no more keys on any channel after the current time -> we're done |
| - if( nextTime > 1e19) |
| - break; |
| - |
| - // else construct next keyframe at this following time point |
| - time = nextTime; |
| - } |
| - } |
| + std::vector<aiMatrix4x4> resultTrafos; |
| + if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) |
| + { |
| + // create a local transformation chain of the node's transforms |
| + std::vector<Collada::Transform> transforms = srcNode->mTransforms; |
| + |
| + // now for every unique point in time, find or interpolate the key values for that time |
| + // and apply them to the transform chain. Then the node's present transformation can be calculated. |
| + float time = startTime; |
| + while( 1) |
| + { |
| + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) |
| + { |
| + Collada::ChannelEntry& e = *it; |
| + |
| + // skip non-transform types |
| + if (e.mTransformId.empty()) |
| + continue; |
| + |
| + // find the keyframe behind the current point in time |
| + size_t pos = 0; |
| + float postTime = 0.f; |
| + while( 1) |
| + { |
| + if( pos >= e.mTimeAccessor->mCount) |
| + break; |
| + postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); |
| + if( postTime >= time) |
| + break; |
| + ++pos; |
| + } |
| + |
| + pos = std::min( pos, e.mTimeAccessor->mCount-1); |
| + |
| + // read values from there |
| + float temp[16]; |
| + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) |
| + temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); |
| + |
| + // if not exactly at the key time, interpolate with previous value set |
| + if( postTime > time && pos > 0) |
| + { |
| + float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); |
| + float factor = (time - postTime) / (preTime - postTime); |
| + |
| + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) |
| + { |
| + float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); |
| + temp[c] += (v - temp[c]) * factor; |
| + } |
| + } |
| + |
| + // Apply values to current transformation |
| + std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); |
| + } |
| + |
| + // Calculate resulting transformation |
| + aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); |
| + |
| + // out of lazyness: we store the time in matrix.d4 |
| + mat.d4 = time; |
| + resultTrafos.push_back( mat); |
| + |
| + // find next point in time to evaluate. That's the closest frame larger than the current in any channel |
| + float nextTime = 1e20f; |
| + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) |
| + { |
| + Collada::ChannelEntry& e = *it; |
| + |
| + // find the next time value larger than the current |
| + size_t pos = 0; |
| + while( pos < e.mTimeAccessor->mCount) |
| + { |
| + float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); |
| + if( t > time) |
| + { |
| + nextTime = std::min( nextTime, t); |
| + break; |
| + } |
| + ++pos; |
| + } |
| + } |
| + |
| + // no more keys on any channel after the current time -> we're done |
| + if( nextTime > 1e19) |
| + break; |
| + |
| + // else construct next keyframe at this following time point |
| + time = nextTime; |
| + } |
| + } |
| |
| // there should be some keyframes, but we aren't that fixated on valid input data |
| // ai_assert( resultTrafos.size() > 0); |
| |
| // build an animation channel for the given node out of these trafo keys |
| - if( !resultTrafos.empty() ) |
| - { |
| - aiNodeAnim* dstAnim = new aiNodeAnim; |
| - dstAnim->mNodeName = nodeName; |
| - dstAnim->mNumPositionKeys = resultTrafos.size(); |
| - dstAnim->mNumRotationKeys= resultTrafos.size(); |
| - dstAnim->mNumScalingKeys = resultTrafos.size(); |
| - dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; |
| - dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; |
| - dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; |
| - |
| - for( size_t a = 0; a < resultTrafos.size(); ++a) |
| - { |
| - aiMatrix4x4 mat = resultTrafos[a]; |
| - double time = double( mat.d4); // remember? time is stored in mat.d4 |
| - mat.d4 = 1.0f; |
| - |
| - dstAnim->mPositionKeys[a].mTime = time; |
| - dstAnim->mRotationKeys[a].mTime = time; |
| - dstAnim->mScalingKeys[a].mTime = time; |
| - mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); |
| - } |
| - |
| - anims.push_back( dstAnim); |
| - } else |
| - { |
| - DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); |
| - } |
| + if( !resultTrafos.empty() ) |
| + { |
| + aiNodeAnim* dstAnim = new aiNodeAnim; |
| + dstAnim->mNodeName = nodeName; |
| + dstAnim->mNumPositionKeys = resultTrafos.size(); |
| + dstAnim->mNumRotationKeys= resultTrafos.size(); |
| + dstAnim->mNumScalingKeys = resultTrafos.size(); |
| + dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; |
| + dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; |
| + dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; |
| + |
| + for( size_t a = 0; a < resultTrafos.size(); ++a) |
| + { |
| + aiMatrix4x4 mat = resultTrafos[a]; |
| + double time = double( mat.d4); // remember? time is stored in mat.d4 |
| + mat.d4 = 1.0f; |
| + |
| + dstAnim->mPositionKeys[a].mTime = time; |
| + dstAnim->mRotationKeys[a].mTime = time; |
| + dstAnim->mScalingKeys[a].mTime = time; |
| + mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); |
| + } |
| + |
| + anims.push_back( dstAnim); |
| + } else |
| + { |
| + DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); |
| + } |
| + |
| + if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) |
| + { |
| + std::vector<Collada::ChannelEntry> morphChannels; |
| + for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) |
| + { |
| + Collada::ChannelEntry& e = *it; |
| + |
| + // skip non-transform types |
| + if (e.mTargetId.empty()) |
| + continue; |
| + |
| + if (e.mTargetId.find("morph-weights") != std::string::npos) |
| + morphChannels.push_back(e); |
| + } |
| + if (morphChannels.size() > 0) |
| + { |
| + // either 1) morph weight animation count should contain morph target count channels |
| + // or 2) one channel with morph target count arrays |
| + // assume first |
| + |
| + aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim; |
| + morphAnim->mName.Set(nodeName); |
| + |
| + std::vector<MorphTimeValues> morphTimeValues; |
| + |
| + int morphAnimChannelIndex = 0; |
| + for( std::vector<Collada::ChannelEntry>::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it) |
| + { |
| + Collada::ChannelEntry& e = *it; |
| + std::string::size_type apos = e.mTargetId.find('('); |
| + std::string::size_type bpos = e.mTargetId.find(')'); |
| + if (apos == std::string::npos || bpos == std::string::npos) |
| + // unknown way to specify weight -> ignore this animation |
| + continue; |
| + |
| + // weight target can be in format Weight_M_N, Weight_N, WeightN, or some other way |
| + // we ignore the name and just assume the channels are in the right order |
| + for (unsigned int i = 0; i < e.mTimeData->mValues.size(); i++) |
| + insertMorphTimeValue(morphTimeValues, e.mTimeData->mValues.at(i), e.mValueData->mValues.at(i), morphAnimChannelIndex); |
| + |
| + ++morphAnimChannelIndex; |
| + } |
| + |
| + morphAnim->mNumKeys = morphTimeValues.size(); |
| + morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys]; |
| + for (unsigned int key = 0; key < morphAnim->mNumKeys; key++) |
| + { |
| + morphAnim->mKeys[key].mNumValuesAndWeights = morphChannels.size(); |
| + morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()]; |
| + morphAnim->mKeys[key].mWeights = new double [morphChannels.size()]; |
| + |
| + morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime; |
| + for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); valueIndex++) |
| + { |
| + morphAnim->mKeys[key].mValues[valueIndex] = valueIndex; |
| + morphAnim->mKeys[key].mWeights[valueIndex] = getWeightAtKey(morphTimeValues, key, valueIndex); |
| + } |
| + } |
| + |
| + morphAnims.push_back(morphAnim); |
| + } |
| + } |
| } |
| |
| - if( !anims.empty()) |
| + if( !anims.empty() || !morphAnims.empty()) |
| { |
| aiAnimation* anim = new aiAnimation; |
| anim->mName.Set( pName); |
| anim->mNumChannels = anims.size(); |
| - anim->mChannels = new aiNodeAnim*[anims.size()]; |
| - std::copy( anims.begin(), anims.end(), anim->mChannels); |
| + if (anim->mNumChannels > 0) |
| + { |
| + anim->mChannels = new aiNodeAnim*[anims.size()]; |
| + std::copy( anims.begin(), anims.end(), anim->mChannels); |
| + } |
| + anim->mNumMorphMeshChannels = morphAnims.size(); |
| + if (anim->mNumMorphMeshChannels > 0) |
| + { |
| + anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels]; |
| + std::copy( morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels); |
| + } |
| anim->mDuration = 0.0f; |
| for( size_t a = 0; a < anims.size(); ++a) |
| { |
| @@ -1165,6 +1402,10 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars |
| anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime); |
| anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime); |
| } |
| + for (size_t a = 0; a < morphAnims.size(); ++a) |
| + { |
| + anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys-1].mTime); |
| + } |
| anim->mTicksPerSecond = 1; |
| mAnims.push_back( anim); |
| } |
| diff --git a/src/3rdparty/assimp/code/ColladaLoader.h b/src/3rdparty/assimp/code/ColladaLoader.h |
| index 4f22a51..3c993d1 100644 |
| --- a/src/3rdparty/assimp/code/ColladaLoader.h |
| +++ b/src/3rdparty/assimp/code/ColladaLoader.h |
| @@ -112,6 +112,8 @@ protected: |
| void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, |
| aiNode* pTarget); |
| |
| + aiMesh *findMesh(std::string meshid); |
| + |
| /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */ |
| aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, |
| const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace); |
| @@ -218,6 +220,9 @@ protected: |
| /** Accumulated meshes for the target scene */ |
| std::vector<aiMesh*> mMeshes; |
| |
| + /** Accumulated morph target meshes */ |
| + std::vector<aiMesh*> mTargetMeshes; |
| + |
| /** Temporary material list */ |
| std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats; |
| |
| diff --git a/src/3rdparty/assimp/code/ColladaParser.cpp b/src/3rdparty/assimp/code/ColladaParser.cpp |
| index a230b64..5369ee6 100644 |
| --- a/src/3rdparty/assimp/code/ColladaParser.cpp |
| +++ b/src/3rdparty/assimp/code/ColladaParser.cpp |
| @@ -412,6 +412,12 @@ void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel) |
| pChannel.mSourceTimes = source; |
| else if( strcmp( semantic, "OUTPUT") == 0) |
| pChannel.mSourceValues = source; |
| + else if( strcmp( semantic, "IN_TANGENT") == 0) |
| + pChannel.mInTanValues = source; |
| + else if( strcmp( semantic, "OUT_TANGENT") == 0) |
| + pChannel.mOutTanValues = source; |
| + else if( strcmp( semantic, "INTERPOLATION") == 0) |
| + pChannel.mInterpolationValues = source; |
| |
| if( !mReader->isEmptyElement()) |
| SkipElement(); |
| @@ -474,6 +480,9 @@ void ColladaParser::ReadControllerLibrary() |
| // Reads a controller into the given mesh structure |
| void ColladaParser::ReadController( Collada::Controller& pController) |
| { |
| + // initial values |
| + pController.mType = Skin; |
| + pController.mMethod = Normalized; |
| while( mReader->read()) |
| { |
| if( mReader->getNodeType() == irr::io::EXN_ELEMENT) |
| @@ -481,8 +490,15 @@ void ColladaParser::ReadController( Collada::Controller& pController) |
| // 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(); |
| + pController.mType = Morph; |
| + int baseIndex = GetAttribute("source"); |
| + pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1; |
| + int methodIndex = GetAttribute("method"); |
| + if (methodIndex > 0) { |
| + const char *method = mReader->getAttributeValue(methodIndex); |
| + if (strcmp(method, "RELATIVE") == 0) |
| + pController.mMethod = Relative; |
| + } |
| } |
| else if( IsElement( "skin")) |
| { |
| @@ -519,7 +535,33 @@ void ColladaParser::ReadController( Collada::Controller& pController) |
| else if( IsElement( "vertex_weights")) |
| { |
| ReadControllerWeights( pController); |
| - } |
| + } |
| + else if ( IsElement( "targets" )) |
| + { |
| + while (mReader->read()) { |
| + if( mReader->getNodeType() == irr::io::EXN_ELEMENT) { |
| + if ( IsElement( "input")) { |
| + int semanticsIndex = GetAttribute("semantic"); |
| + int sourceIndex = GetAttribute("source"); |
| + |
| + const char *semantics = mReader->getAttributeValue(semanticsIndex); |
| + const char *source = mReader->getAttributeValue(sourceIndex); |
| + if (strcmp(semantics, "MORPH_TARGET") == 0) { |
| + pController.mMorphTarget = source + 1; |
| + } |
| + else if (strcmp(semantics, "MORPH_WEIGHT") == 0) |
| + { |
| + pController.mMorphWeight = source + 1; |
| + } |
| + } |
| + } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) { |
| + if( strcmp( mReader->getNodeName(), "targets") == 0) |
| + break; |
| + else |
| + ThrowException( "Expected end of <targets> element."); |
| + } |
| + } |
| + } |
| else |
| { |
| // ignore the rest |
| @@ -530,7 +572,7 @@ void ColladaParser::ReadController( Collada::Controller& pController) |
| { |
| if( strcmp( mReader->getNodeName(), "controller") == 0) |
| break; |
| - else if( strcmp( mReader->getNodeName(), "skin") != 0) |
| + else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0) |
| ThrowException( "Expected end of <controller> element."); |
| } |
| } |
| diff --git a/src/3rdparty/assimp/code/CreateAnimMesh.cpp b/src/3rdparty/assimp/code/CreateAnimMesh.cpp |
| new file mode 100644 |
| index 0000000..094a414 |
| --- /dev/null |
| +++ b/src/3rdparty/assimp/code/CreateAnimMesh.cpp |
| @@ -0,0 +1,92 @@ |
| +/* |
| +--------------------------------------------------------------------------- |
| +Open Asset Import Library (assimp) |
| +--------------------------------------------------------------------------- |
| + |
| +Copyright (C) 2016 The Qt Company Ltd. |
| +Copyright (c) 2006-2012, assimp team |
| + |
| +All rights reserved. |
| + |
| +Redistribution and use of this software in source and binary forms, |
| +with or without modification, are permitted provided that the following |
| +conditions are met: |
| + |
| +* Redistributions of source code must retain the above |
| +copyright notice, this list of conditions and the |
| +following disclaimer. |
| + |
| +* Redistributions in binary form must reproduce the above |
| +copyright notice, this list of conditions and the |
| +following disclaimer in the documentation and/or other |
| +materials provided with the distribution. |
| + |
| +* Neither the name of the assimp team, nor the names of its |
| +contributors may be used to endorse or promote products |
| +derived from this software without specific prior |
| +written permission of the assimp team. |
| + |
| +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| +--------------------------------------------------------------------------- |
| +*/ |
| + |
| +#include "CreateAnimMesh.h" |
| + |
| +namespace Assimp { |
| + |
| +aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) |
| +{ |
| + aiAnimMesh *animesh = new aiAnimMesh; |
| + animesh->mVertices = NULL; |
| + animesh->mNormals = NULL; |
| + animesh->mTangents = NULL; |
| + animesh->mBitangents = NULL; |
| + animesh->mNumVertices = mesh->mNumVertices; |
| + if (mesh->mVertices) { |
| + animesh->mVertices = new aiVector3D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); |
| + } |
| + if (mesh->mNormals) { |
| + animesh->mNormals = new aiVector3D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); |
| + } |
| + if (mesh->mTangents) { |
| + animesh->mTangents = new aiVector3D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D)); |
| + } |
| + if (mesh->mBitangents) { |
| + animesh->mBitangents = new aiVector3D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D)); |
| + } |
| + |
| + for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { |
| + if (mesh->mColors[i]) { |
| + animesh->mColors[i] = new aiColor4D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D)); |
| + } else { |
| + animesh->mColors[i] = NULL; |
| + } |
| + } |
| + |
| + for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { |
| + if (mesh->mTextureCoords[i]) { |
| + animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices]; |
| + std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D)); |
| + } else { |
| + animesh->mTextureCoords[i] = NULL; |
| + } |
| + } |
| + return animesh; |
| +} |
| + |
| +} // end of namespace Assimp |
| diff --git a/src/3rdparty/assimp/code/CreateAnimMesh.h b/src/3rdparty/assimp/code/CreateAnimMesh.h |
| new file mode 100644 |
| index 0000000..c5ceb40 |
| --- /dev/null |
| +++ b/src/3rdparty/assimp/code/CreateAnimMesh.h |
| @@ -0,0 +1,56 @@ |
| +/* |
| +Open Asset Import Library (assimp) |
| +---------------------------------------------------------------------- |
| + |
| +Copyright (c) 2006-2016, assimp team |
| +All rights reserved. |
| + |
| +Redistribution and use of this software in source and binary forms, |
| +with or without modification, are permitted provided that the |
| +following conditions are met: |
| + |
| +* Redistributions of source code must retain the above |
| + copyright notice, this list of conditions and the |
| + following disclaimer. |
| + |
| +* Redistributions in binary form must reproduce the above |
| + copyright notice, this list of conditions and the |
| + following disclaimer in the documentation and/or other |
| + materials provided with the distribution. |
| + |
| +* Neither the name of the assimp team, nor the names of its |
| + contributors may be used to endorse or promote products |
| + derived from this software without specific prior |
| + written permission of the assimp team. |
| + |
| +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + |
| +---------------------------------------------------------------------- |
| +*/ |
| + |
| +/** @file CreateAnimMesh.h |
| + * Create AnimMesh from Mesh |
| + */ |
| +#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H |
| +#define INCLUDED_AI_CREATE_ANIM_MESH_H |
| + |
| +#include <assimp/mesh.h> |
| + |
| +namespace Assimp { |
| + |
| +/** Create aiAnimMesh from aiMesh. */ |
| +aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh); |
| + |
| +} // end of namespace Assimp |
| +#endif // INCLUDED_AI_CREATE_ANIM_MESH_H |
| + |
| diff --git a/src/3rdparty/assimp/include/assimp/anim.h b/src/3rdparty/assimp/include/assimp/anim.h |
| index 2ae1b60..b32d749 100644 |
| --- a/src/3rdparty/assimp/include/assimp/anim.h |
| +++ b/src/3rdparty/assimp/include/assimp/anim.h |
| @@ -181,6 +181,40 @@ struct aiMeshKey |
| #endif |
| }; |
| |
| + |
| +// --------------------------------------------------------------------------- |
| +/** Binds a morph anim mesh to a specific point in time. */ |
| +struct aiMeshMorphKey |
| +{ |
| + /** The time of this key */ |
| + double mTime; |
| + |
| + /** The values and weights at the time of this key */ |
| + unsigned int *mValues; |
| + double *mWeights; |
| + |
| + /** The number of values and weights */ |
| + unsigned int mNumValuesAndWeights; |
| +#ifdef __cplusplus |
| + aiMeshMorphKey() |
| + : mTime(0.0) |
| + , mValues(NULL) |
| + , mWeights(NULL) |
| + , mNumValuesAndWeights(0) |
| + { |
| + |
| + } |
| + |
| + ~aiMeshMorphKey() |
| + { |
| + if (mNumValuesAndWeights && mValues && mWeights) { |
| + delete [] mValues; |
| + delete [] mWeights; |
| + } |
| + } |
| +#endif |
| +}; |
| + |
| // --------------------------------------------------------------------------- |
| /** Defines how an animation channel behaves outside the defined time |
| * range. This corresponds to aiNodeAnim::mPreState and |
| @@ -203,8 +237,6 @@ enum aiAnimBehaviour |
| * time is t, use the value at (t-n) % (|m-n|).*/ |
| aiAnimBehaviour_REPEAT = 0x3, |
| |
| - |
| - |
| /** This value is not used, it is just here to force the |
| * the compiler to map this enum to a 32 Bit integer */ |
| #ifndef SWIG |
| @@ -335,6 +367,37 @@ struct aiMeshAnim |
| }; |
| |
| // --------------------------------------------------------------------------- |
| +/** Describes a morphing animation of a given mesh. */ |
| +struct aiMeshMorphAnim |
| +{ |
| + /** Name of the mesh to be animated. An empty string is not allowed, |
| + * animated meshes need to be named (not necessarily uniquely, |
| + * the name can basically serve as wildcard to select a group |
| + * of meshes with similar animation setup)*/ |
| + C_STRUCT aiString mName; |
| + |
| + /** Size of the #mKeys array. Must be 1, at least. */ |
| + unsigned int mNumKeys; |
| + |
| + /** Key frames of the animation. May not be NULL. */ |
| + C_STRUCT aiMeshMorphKey* mKeys; |
| + |
| +#ifdef __cplusplus |
| + |
| + aiMeshMorphAnim() |
| + : mNumKeys() |
| + , mKeys() |
| + {} |
| + |
| + ~aiMeshMorphAnim() |
| + { |
| + delete[] mKeys; |
| + } |
| + |
| +#endif |
| +}; |
| + |
| +// --------------------------------------------------------------------------- |
| /** An animation consists of keyframe data for a number of nodes. For |
| * each node affected by the animation a separate series of data is given.*/ |
| struct aiAnimation |
| @@ -367,6 +430,15 @@ struct aiAnimation |
| * The array is mNumMeshChannels in size. */ |
| C_STRUCT aiMeshAnim** mMeshChannels; |
| |
| + |
| + /** The number of mesh animation channels. Each channel affects |
| + * a single mesh and defines morphing animation. */ |
| + unsigned int mNumMorphMeshChannels; |
| + |
| + /** The morph mesh animation channels. Each channel affects a single mesh. |
| + * The array is mNumMorphMeshChannels in size. */ |
| + C_STRUCT aiMeshMorphAnim **mMorphMeshChannels; |
| + |
| #ifdef __cplusplus |
| aiAnimation() |
| : mDuration(-1.) |
| @@ -375,6 +447,8 @@ struct aiAnimation |
| , mChannels() |
| , mNumMeshChannels() |
| , mMeshChannels() |
| + , mNumMorphMeshChannels() |
| + , mMorphMeshChannels() |
| { |
| } |
| |
| @@ -395,6 +469,11 @@ struct aiAnimation |
| |
| delete [] mMeshChannels; |
| } |
| + if (mNumMorphMeshChannels && mMorphMeshChannels) { |
| + for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) { |
| + delete mMorphMeshChannels[a]; |
| + } |
| + } |
| } |
| #endif // __cplusplus |
| }; |
| diff --git a/src/3rdparty/assimp/include/assimp/mesh.h b/src/3rdparty/assimp/include/assimp/mesh.h |
| index 9e82608..7e2ecc1 100644 |
| --- a/src/3rdparty/assimp/include/assimp/mesh.h |
| +++ b/src/3rdparty/assimp/include/assimp/mesh.h |
| @@ -376,6 +376,9 @@ struct aiAnimMesh |
| */ |
| unsigned int mNumVertices; |
| |
| + /** Weight of the AnimMesh. */ |
| + float mWeight; |
| + |
| #ifdef __cplusplus |
| |
| aiAnimMesh() |
| @@ -444,6 +447,27 @@ struct aiAnimMesh |
| #endif |
| }; |
| |
| +// --------------------------------------------------------------------------- |
| +/** @brief Enumerates the methods of mesh morphing supported by Assimp. |
| + */ |
| +enum aiMorphingMethod |
| +{ |
| + /** Interpolation between morph targets */ |
| + aiMorphingMethod_VERTEX_BLEND = 0x1, |
| + |
| + /** Normalized morphing between morph targets */ |
| + aiMorphingMethod_MORPH_NORMALIZED = 0x2, |
| + |
| + /** Relative morphing between morph targets */ |
| + aiMorphingMethod_MORPH_RELATIVE = 0x3, |
| + |
| + /** This value is not used. It is just here to force the |
| + * compiler to map this enum to a 32 Bit integer. |
| + */ |
| +#ifndef SWIG |
| + _aiMorphingMethod_Force32Bit = INT_MAX |
| +#endif |
| +}; //! enum aiMorphingMethod |
| |
| // --------------------------------------------------------------------------- |
| /** @brief A mesh represents a geometry or model with a single material. |
| @@ -598,14 +622,17 @@ struct aiMesh |
| C_STRUCT aiString mName; |
| |
| |
| - /** NOT CURRENTLY IN USE. The number of attachment meshes */ |
| + /** The number of attachment meshes. Note! Currently only works with Collada loader. */ |
| unsigned int mNumAnimMeshes; |
| |
| - /** NOT CURRENTLY IN USE. Attachment meshes for this mesh, for vertex-based animation. |
| + /** Attachment meshes for this mesh, for vertex-based animation. |
| * Attachment meshes carry replacement data for some of the |
| - * mesh'es vertex components (usually positions, normals). */ |
| + * mesh'es vertex components (usually positions, normals). |
| + * Note! Currently only works with Collada loader.*/ |
| C_STRUCT aiAnimMesh** mAnimMeshes; |
| |
| + /** Method of morphing when animeshes are specified. */ |
| + unsigned int mMethod; |
| |
| #ifdef __cplusplus |
| |
| @@ -732,7 +759,6 @@ struct aiMesh |
| #endif // __cplusplus |
| }; |
| |
| - |
| #ifdef __cplusplus |
| } |
| #endif //! extern "C" |
| -- |
| 2.7.1.windows.1 |
| |