diff options
author | Antti Määttä <antti.maatta@qt.io> | 2016-10-10 12:13:55 +0300 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2017-01-27 11:18:36 +0000 |
commit | eda313e0855f94e317cd656ad9c83ef086e17625 (patch) | |
tree | 33c5a5edab80e51dc65dc9dfba1ca8293df9a4ef /src/3rdparty | |
parent | 4a94e68d5975b53f11ab2ab7755f6b944fc8b614 (diff) |
Morph animation loading support for Collada
Add morph animation support for collada and morph target loading.
Change-Id: I291cf1ff5e575729d0136cf0880e9ef780ac1600
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Diffstat (limited to 'src/3rdparty')
-rw-r--r-- | src/3rdparty/assimp/assimp.pri | 6 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/ColladaHelper.h | 36 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/ColladaLoader.cpp | 797 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/ColladaLoader.h | 5 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/ColladaParser.cpp | 50 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/CreateAnimMesh.cpp | 92 | ||||
-rw-r--r-- | src/3rdparty/assimp/code/CreateAnimMesh.h | 56 | ||||
-rw-r--r-- | src/3rdparty/assimp/include/assimp/anim.h | 83 | ||||
-rw-r--r-- | src/3rdparty/assimp/include/assimp/mesh.h | 34 | ||||
-rw-r--r-- | src/3rdparty/patches/0006-Morph-animation-loading-support-for-Collada.patch | 1493 |
10 files changed, 2359 insertions, 293 deletions
diff --git a/src/3rdparty/assimp/assimp.pri b/src/3rdparty/assimp/assimp.pri index d3282fb56..802a645fe 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 0e087bd21..2f05d46a7 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 2096e95ee..c8b9c10f2 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 4f22a51cd..3c993d100 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 a230b64e7..5369ee64c 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 000000000..094a414bf --- /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 000000000..c5ceb4028 --- /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 2ae1b605b..b32d749ba 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 9e82608a0..7e2ecc179 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" diff --git a/src/3rdparty/patches/0006-Morph-animation-loading-support-for-Collada.patch b/src/3rdparty/patches/0006-Morph-animation-loading-support-for-Collada.patch new file mode 100644 index 000000000..bd94f587c --- /dev/null +++ b/src/3rdparty/patches/0006-Morph-animation-loading-support-for-Collada.patch @@ -0,0 +1,1493 @@ +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 + |