c07ea6c
From 42e353f2a672baed485c03acf30529c77092ece9 Mon Sep 17 00:00:00 2001
c07ea6c
From: =?UTF-8?q?Antti=20M=C3=A4=C3=A4tt=C3=A4?= <antti.maatta@qt.io>
c07ea6c
Date: Thu, 12 Jan 2017 13:41:32 +0200
c07ea6c
Subject: [PATCH] Morph animation support for collada
c07ea6c
c07ea6c
---
c07ea6c
 code/CMakeLists.txt     |   2 +
c07ea6c
 code/ColladaHelper.h    |  31 +++++
c07ea6c
 code/ColladaLoader.cpp  | 310 ++++++++++++++++++++++++++++++++++++++++++------
c07ea6c
 code/ColladaLoader.h    |   5 +
c07ea6c
 code/ColladaParser.cpp  |  48 +++++++-
c07ea6c
 code/CreateAnimMesh.cpp |  92 ++++++++++++++
c07ea6c
 code/CreateAnimMesh.h   |  56 +++++++++
c07ea6c
 include/assimp/anim.h   |  95 +++++++++++++--
c07ea6c
 include/assimp/mesh.h   |  36 +++++-
c07ea6c
 9 files changed, 623 insertions(+), 52 deletions(-)
c07ea6c
 create mode 100644 code/CreateAnimMesh.cpp
c07ea6c
 create mode 100644 code/CreateAnimMesh.h
c07ea6c
c07ea6c
diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt
c07ea6c
index 35ecf50..e0747d6 100644
c07ea6c
--- a/code/CMakeLists.txt
c07ea6c
+++ b/code/CMakeLists.txt
c07ea6c
@@ -171,6 +171,8 @@ SET( Common_SRCS
c07ea6c
   Bitmap.h
c07ea6c
   XMLTools.h
c07ea6c
   Version.cpp
c07ea6c
+  CreateAnimMesh.h
c07ea6c
+  CreateAnimMesh.cpp
c07ea6c
 )
c07ea6c
 SOURCE_GROUP(Common FILES ${Common_SRCS})
c07ea6c
 
c07ea6c
diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h
c07ea6c
index 5a51642..5ff4904 100644
c07ea6c
--- a/code/ColladaHelper.h
c07ea6c
+++ b/code/ColladaHelper.h
c07ea6c
@@ -89,6 +89,21 @@ enum InputType
c07ea6c
     IT_Bitangent
c07ea6c
 };
c07ea6c
 
c07ea6c
+/** Supported controller types */
c07ea6c
+enum ControllerType
c07ea6c
+{
c07ea6c
+    Skin,
c07ea6c
+    Morph
c07ea6c
+};
c07ea6c
+
c07ea6c
+/** Supported morph methods */
c07ea6c
+enum MorphMethod
c07ea6c
+{
c07ea6c
+    Normalized,
c07ea6c
+    Relative
c07ea6c
+};
c07ea6c
+
c07ea6c
+
c07ea6c
 /** Contains all data for one of the different transformation types */
c07ea6c
 struct Transform
c07ea6c
 {
c07ea6c
@@ -380,6 +395,12 @@ enum PrimitiveType
c07ea6c
 /** A skeleton controller to deform a mesh with the use of joints */
c07ea6c
 struct Controller
c07ea6c
 {
c07ea6c
+    // controller type
c07ea6c
+    ControllerType mType;
c07ea6c
+
c07ea6c
+    // Morphing method if type is Morph
c07ea6c
+    MorphMethod mMethod;
c07ea6c
+
c07ea6c
     // the URL of the mesh deformed by the controller.
c07ea6c
     std::string mMeshId;
c07ea6c
 
c07ea6c
@@ -402,6 +423,9 @@ struct Controller
c07ea6c
 
c07ea6c
     // JointIndex-WeightIndex pairs for all vertices
c07ea6c
     std::vector< std::pair<size_t, size_t> > mWeights;
c07ea6c
+
c07ea6c
+    std::string mMorphTarget;
c07ea6c
+    std::string mMorphWeight;
c07ea6c
 };
c07ea6c
 
c07ea6c
 /** A collada material. Pretty much the only member is a reference to an effect. */
c07ea6c
@@ -577,6 +601,12 @@ struct AnimationChannel
c07ea6c
     std::string mSourceTimes;
c07ea6c
     /** Source URL of the value values. Collada calls them "output". */
c07ea6c
     std::string mSourceValues;
c07ea6c
+    /** Source URL of the IN_TANGENT semantic values. */
c07ea6c
+    std::string mInTanValues;
c07ea6c
+    /** Source URL of the OUT_TANGENT semantic values. */
c07ea6c
+    std::string mOutTanValues;
c07ea6c
+    /** Source URL of the INTERPOLATION semantic values. */
c07ea6c
+    std::string mInterpolationValues;
c07ea6c
 };
c07ea6c
 
c07ea6c
 /** An animation. Container for 0-x animation channels or 0-x animations */
c07ea6c
@@ -645,6 +675,7 @@ struct Animation
c07ea6c
 struct ChannelEntry
c07ea6c
 {
c07ea6c
     const Collada::AnimationChannel* mChannel; ///> the source channel
c07ea6c
+    std::string mTargetId;
c07ea6c
     std::string mTransformId;   // the ID of the transformation step of the node which is influenced
c07ea6c
     size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
c07ea6c
     size_t mSubElement; // starting index inside the transform data
c07ea6c
diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp
c07ea6c
index 6cc0f85..a3b6d6e 100644
c07ea6c
--- a/code/ColladaLoader.cpp
c07ea6c
+++ b/code/ColladaLoader.cpp
c07ea6c
@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
c07ea6c
 #include "ParsingUtils.h"
c07ea6c
 #include "SkeletonMeshBuilder.h"
c07ea6c
 #include "Defines.h"
c07ea6c
+#include "CreateAnimMesh.h"
c07ea6c
 
c07ea6c
 #include "time.h"
c07ea6c
 #include "math.h"
c07ea6c
@@ -148,6 +149,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
c07ea6c
     mMeshIndexByID.clear();
c07ea6c
     mMaterialIndexByName.clear();
c07ea6c
     mMeshes.clear();
c07ea6c
+    mTargetMeshes.clear();
c07ea6c
     newMats.clear();
c07ea6c
     mLights.clear();
c07ea6c
     mCameras.clear();
c07ea6c
@@ -562,6 +564,21 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
c07ea6c
 }
c07ea6c
 
c07ea6c
 // ------------------------------------------------------------------------------------------------
c07ea6c
+// Find mesh from either meshes or morph target meshes
c07ea6c
+aiMesh *ColladaLoader::findMesh(std::string meshid)
c07ea6c
+{
c07ea6c
+    for (unsigned int i = 0; i < mMeshes.size(); i++)
c07ea6c
+        if (std::string(mMeshes[i]->mName.data) == meshid)
c07ea6c
+            return mMeshes[i];
c07ea6c
+
c07ea6c
+    for (unsigned int i = 0; i < mTargetMeshes.size(); i++)
c07ea6c
+        if (std::string(mTargetMeshes[i]->mName.data) == meshid)
c07ea6c
+            return mTargetMeshes[i];
c07ea6c
+
c07ea6c
+    return NULL;
c07ea6c
+}
c07ea6c
+
c07ea6c
+// ------------------------------------------------------------------------------------------------
c07ea6c
 // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
c07ea6c
 aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
c07ea6c
     const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
c07ea6c
@@ -646,8 +663,70 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
c07ea6c
             face.mIndices[b] = vertex++;
c07ea6c
     }
c07ea6c
 
c07ea6c
+    // create morph target meshes if any
c07ea6c
+    std::vector<aiMesh*> targetMeshes;
c07ea6c
+    std::vector<float> targetWeights;
c07ea6c
+    Collada::MorphMethod method;
c07ea6c
+
c07ea6c
+    for(std::map<std::string, Collada::Controller>::const_iterator it = pParser.mControllerLibrary.begin();
c07ea6c
+        it != pParser.mControllerLibrary.end(); it++)
c07ea6c
+    {
c07ea6c
+        const Collada::Controller &c = it->second;
c07ea6c
+        const Collada::Mesh* baseMesh = pParser.ResolveLibraryReference( pParser.mMeshLibrary, c.mMeshId);
c07ea6c
+
c07ea6c
+        if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName)
c07ea6c
+        {
c07ea6c
+            const Collada::Accessor& targetAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphTarget);
c07ea6c
+            const Collada::Accessor& weightAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphWeight);
c07ea6c
+            const Collada::Data& targetData = pParser.ResolveLibraryReference( pParser.mDataLibrary, targetAccessor.mSource);
c07ea6c
+            const Collada::Data& weightData = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightAccessor.mSource);
c07ea6c
+
c07ea6c
+            // take method
c07ea6c
+            method = c.mMethod;
c07ea6c
+
c07ea6c
+            if (!targetData.mIsStringArray)
c07ea6c
+                throw DeadlyImportError( "target data must contain id. ");
c07ea6c
+            if (weightData.mIsStringArray)
c07ea6c
+                throw DeadlyImportError( "target weight data must not be textual ");
c07ea6c
+
c07ea6c
+            for (unsigned int i = 0; i < targetData.mStrings.size(); ++i)
c07ea6c
+            {
c07ea6c
+                const Collada::Mesh* targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, targetData.mStrings.at(i));
c07ea6c
+
c07ea6c
+                aiMesh *aimesh = findMesh(targetMesh->mName);
c07ea6c
+                if (!aimesh)
c07ea6c
+                {
c07ea6c
+                    if (targetMesh->mSubMeshes.size() > 1)
c07ea6c
+                        throw DeadlyImportError( "Morhing target mesh must be a single");
c07ea6c
+                    aimesh = CreateMesh(pParser, targetMesh, targetMesh->mSubMeshes.at(0), NULL, 0, 0);
c07ea6c
+                    mTargetMeshes.push_back(aimesh);
c07ea6c
+                }
c07ea6c
+                targetMeshes.push_back(aimesh);
c07ea6c
+            }
c07ea6c
+            for (unsigned int i = 0; i < weightData.mValues.size(); ++i)
c07ea6c
+                targetWeights.push_back(weightData.mValues.at(i));
c07ea6c
+        }
c07ea6c
+    }
c07ea6c
+    if (targetMeshes.size() > 0 && targetWeights.size() == targetMeshes.size())
c07ea6c
+    {
c07ea6c
+        std::vector<aiAnimMesh*> animMeshes;
c07ea6c
+        for (unsigned int i = 0; i < targetMeshes.size(); i++)
c07ea6c
+        {
c07ea6c
+            aiAnimMesh *animMesh = aiCreateAnimMesh(targetMeshes.at(i));
c07ea6c
+            animMesh->mWeight = targetWeights[i];
c07ea6c
+            animMeshes.push_back(animMesh);
c07ea6c
+        }
c07ea6c
+        dstMesh->mMethod = (method == Collada::Relative)
c07ea6c
+                                ? aiMorphingMethod_MORPH_RELATIVE
c07ea6c
+                                : aiMorphingMethod_MORPH_NORMALIZED;
c07ea6c
+        dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()];
c07ea6c
+        dstMesh->mNumAnimMeshes = animMeshes.size();
c07ea6c
+        for (unsigned int i = 0; i < animMeshes.size(); i++)
c07ea6c
+            dstMesh->mAnimMeshes[i] = animMeshes.at(i);
c07ea6c
+    }
c07ea6c
+
c07ea6c
     // create bones if given
c07ea6c
-    if( pSrcController)
c07ea6c
+    if( pSrcController && pSrcController->mType == Collada::Skin)
c07ea6c
     {
c07ea6c
         // refuse if the vertex count does not match
c07ea6c
 //      if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
c07ea6c
@@ -946,6 +1025,68 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars
c07ea6c
         CreateAnimation( pScene, pParser, pSrcAnim, animName);
c07ea6c
 }
c07ea6c
 
c07ea6c
+struct MorphTimeValues
c07ea6c
+{
c07ea6c
+    float mTime;
c07ea6c
+    struct key
c07ea6c
+    {
c07ea6c
+        float mWeight;
c07ea6c
+        unsigned int mValue;
c07ea6c
+    };
c07ea6c
+    std::vector<key> mKeys;
c07ea6c
+};
c07ea6c
+
c07ea6c
+void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value)
c07ea6c
+{
c07ea6c
+    MorphTimeValues::key k;
c07ea6c
+    k.mValue = value;
c07ea6c
+    k.mWeight = weight;
c07ea6c
+    if (values.size() == 0 || time < values[0].mTime)
c07ea6c
+    {
c07ea6c
+        MorphTimeValues val;
c07ea6c
+        val.mTime = time;
c07ea6c
+        val.mKeys.push_back(k);
c07ea6c
+        values.insert(values.begin(), val);
c07ea6c
+        return;
c07ea6c
+    }
c07ea6c
+    if (time > values.back().mTime)
c07ea6c
+    {
c07ea6c
+        MorphTimeValues val;
c07ea6c
+        val.mTime = time;
c07ea6c
+        val.mKeys.push_back(k);
c07ea6c
+        values.insert(values.end(), val);
c07ea6c
+        return;
c07ea6c
+    }
c07ea6c
+    for (unsigned int i = 0; i < values.size(); i++)
c07ea6c
+    {
c07ea6c
+        if (std::abs(time - values[i].mTime) < 1e-6f)
c07ea6c
+        {
c07ea6c
+            values[i].mKeys.push_back(k);
c07ea6c
+            return;
c07ea6c
+        } else if (time > values[i].mTime && time < values[i+1].mTime)
c07ea6c
+        {
c07ea6c
+            MorphTimeValues val;
c07ea6c
+            val.mTime = time;
c07ea6c
+            val.mKeys.push_back(k);
c07ea6c
+            values.insert(values.begin() + i, val);
c07ea6c
+            return;
c07ea6c
+        }
c07ea6c
+    }
c07ea6c
+    // should not get here
c07ea6c
+}
c07ea6c
+
c07ea6c
+float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value)
c07ea6c
+{
c07ea6c
+    for (unsigned int i = 0; i < values[key].mKeys.size(); i++)
c07ea6c
+    {
c07ea6c
+        if (values[key].mKeys[i].mValue == value)
c07ea6c
+            return values[key].mKeys[i].mWeight;
c07ea6c
+    }
c07ea6c
+    // no value at key found, try to interpolate if present at other keys. if not, return zero
c07ea6c
+    // TODO: interpolation
c07ea6c
+    return 0.0f;
c07ea6c
+}
c07ea6c
+
c07ea6c
 // ------------------------------------------------------------------------------------------------
c07ea6c
 // Constructs the animation for the given source anim
c07ea6c
 void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
c07ea6c
@@ -955,6 +1096,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
c07ea6c
     CollectNodes( pScene->mRootNode, nodes);
c07ea6c
 
c07ea6c
     std::vector<aiNodeAnim*> anims;
c07ea6c
+    std::vector<aiMeshMorphAnim*> morphAnims;
c07ea6c
+
c07ea6c
     for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
c07ea6c
     {
c07ea6c
         // find all the collada anim channels which refer to the current node
c07ea6c
@@ -978,7 +1121,20 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
c07ea6c
             // find the slash that separates the node name - there should be only one
c07ea6c
             std::string::size_type slashPos = srcChannel.mTarget.find( '/');
c07ea6c
             if( slashPos == std::string::npos)
c07ea6c
+            {
c07ea6c
+                std::string::size_type targetPos = srcChannel.mTarget.find(srcNode->mID);
c07ea6c
+                if (targetPos == std::string::npos)
c07ea6c
+                    continue;
c07ea6c
+
c07ea6c
+                // not node transform, but something else. store as unknown animation channel for now
c07ea6c
+                entry.mChannel = &(*cit);
c07ea6c
+                entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(),
c07ea6c
+                                        srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length());
c07ea6c
+                if (entry.mTargetId.front() == '-')
c07ea6c
+                    entry.mTargetId = entry.mTargetId.substr(1);
c07ea6c
+                entries.push_back(entry);
c07ea6c
                 continue;
c07ea6c
+            }
c07ea6c
             if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
c07ea6c
                 continue;
c07ea6c
             std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
c07ea6c
@@ -1058,8 +1214,14 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
c07ea6c
                 if( srcNode->mTransforms[a].mID == entry.mTransformId)
c07ea6c
                     entry.mTransformIndex = a;
c07ea6c
 
c07ea6c
-            if( entry.mTransformIndex == SIZE_MAX) {
c07ea6c
-                continue;
c07ea6c
+            if( entry.mTransformIndex == SIZE_MAX)
c07ea6c
+            {
c07ea6c
+                if (entry.mTransformId.find("morph-weights") != std::string::npos)
c07ea6c
+                {
c07ea6c
+                    entry.mTargetId = entry.mTransformId;
c07ea6c
+                    entry.mTransformId = "";
c07ea6c
+                } else
c07ea6c
+                    continue;
c07ea6c
             }
c07ea6c
 
c07ea6c
             entry.mChannel = &(*cit);
c07ea6c
@@ -1203,49 +1365,125 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
c07ea6c
 //      ai_assert( resultTrafos.size() > 0);
c07ea6c
 
c07ea6c
         // build an animation channel for the given node out of these trafo keys
c07ea6c
-    if( !resultTrafos.empty() )
c07ea6c
-    {
c07ea6c
-          aiNodeAnim* dstAnim = new aiNodeAnim;
c07ea6c
-          dstAnim->mNodeName = nodeName;
c07ea6c
-          dstAnim->mNumPositionKeys = resultTrafos.size();
c07ea6c
-          dstAnim->mNumRotationKeys= resultTrafos.size();
c07ea6c
-          dstAnim->mNumScalingKeys = resultTrafos.size();
c07ea6c
-          dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
c07ea6c
-          dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
c07ea6c
-          dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
c07ea6c
-
c07ea6c
-          for( size_t a = 0; a < resultTrafos.size(); ++a)
c07ea6c
-          {
c07ea6c
-              aiMatrix4x4 mat = resultTrafos[a];
c07ea6c
-              double time = double( mat.d4); // remember? time is stored in mat.d4
c07ea6c
-        mat.d4 = 1.0f;
c07ea6c
-
c07ea6c
-              dstAnim->mPositionKeys[a].mTime = time;
c07ea6c
-              dstAnim->mRotationKeys[a].mTime = time;
c07ea6c
-              dstAnim->mScalingKeys[a].mTime = time;
c07ea6c
-              mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
c07ea6c
-          }
c07ea6c
+        if( !resultTrafos.empty() )
c07ea6c
+        {
c07ea6c
+              aiNodeAnim* dstAnim = new aiNodeAnim;
c07ea6c
+              dstAnim->mNodeName = nodeName;
c07ea6c
+              dstAnim->mNumPositionKeys = resultTrafos.size();
c07ea6c
+              dstAnim->mNumRotationKeys= resultTrafos.size();
c07ea6c
+              dstAnim->mNumScalingKeys = resultTrafos.size();
c07ea6c
+              dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
c07ea6c
+              dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
c07ea6c
+              dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
c07ea6c
+
c07ea6c
+              for( size_t a = 0; a < resultTrafos.size(); ++a)
c07ea6c
+              {
c07ea6c
+                  aiMatrix4x4 mat = resultTrafos[a];
c07ea6c
+                  double time = double( mat.d4); // remember? time is stored in mat.d4
c07ea6c
+                    mat.d4 = 1.0f;
c07ea6c
+
c07ea6c
+                  dstAnim->mPositionKeys[a].mTime = time;
c07ea6c
+                  dstAnim->mRotationKeys[a].mTime = time;
c07ea6c
+                  dstAnim->mScalingKeys[a].mTime = time;
c07ea6c
+                  mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
c07ea6c
+              }
c07ea6c
 
c07ea6c
-          anims.push_back( dstAnim);
c07ea6c
-    } else
c07ea6c
-    {
c07ea6c
-      DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
c07ea6c
-    }
c07ea6c
+              anims.push_back( dstAnim);
c07ea6c
+        } else
c07ea6c
+        {
c07ea6c
+          DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
c07ea6c
+        }
c07ea6c
+
c07ea6c
+        if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
c07ea6c
+        {
c07ea6c
+            std::vector<Collada::ChannelEntry> morphChannels;
c07ea6c
+            for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
c07ea6c
+            {
c07ea6c
+                Collada::ChannelEntry& e = *it;
c07ea6c
+
c07ea6c
+                // skip non-transform types
c07ea6c
+                if (e.mTargetId.empty())
c07ea6c
+                    continue;
c07ea6c
+
c07ea6c
+                if (e.mTargetId.find("morph-weights") != std::string::npos)
c07ea6c
+                    morphChannels.push_back(e);
c07ea6c
+            }
c07ea6c
+            if (morphChannels.size() > 0)
c07ea6c
+            {
c07ea6c
+                // either 1) morph weight animation count should contain morph target count channels
c07ea6c
+                // or     2) one channel with morph target count arrays
c07ea6c
+                // assume first
c07ea6c
+
c07ea6c
+                aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim;
c07ea6c
+                morphAnim->mName.Set(nodeName);
c07ea6c
+
c07ea6c
+                std::vector<MorphTimeValues> morphTimeValues;
c07ea6c
+
c07ea6c
+                int morphAnimChannelIndex = 0;
c07ea6c
+                for( std::vector<Collada::ChannelEntry>::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it)
c07ea6c
+                {
c07ea6c
+                    Collada::ChannelEntry& e = *it;
c07ea6c
+                    std::string::size_type apos = e.mTargetId.find('(');
c07ea6c
+                    std::string::size_type bpos = e.mTargetId.find(')');
c07ea6c
+                    if (apos == std::string::npos || bpos == std::string::npos)
c07ea6c
+                        // unknown way to specify weight -> ignore this animation
c07ea6c
+                        continue;
c07ea6c
+
c07ea6c
+                    // weight target can be in format Weight_M_N, Weight_N, WeightN, or some other way
c07ea6c
+                    // we ignore the name and just assume the channels are in the right order
c07ea6c
+                    for (unsigned int i = 0; i < e.mTimeData->mValues.size(); i++)
c07ea6c
+                        insertMorphTimeValue(morphTimeValues, e.mTimeData->mValues.at(i), e.mValueData->mValues.at(i), morphAnimChannelIndex);
c07ea6c
+
c07ea6c
+                    ++morphAnimChannelIndex;
c07ea6c
+                }
c07ea6c
+
c07ea6c
+                morphAnim->mNumKeys = morphTimeValues.size();
c07ea6c
+                morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys];
c07ea6c
+                for (unsigned int key = 0; key < morphAnim->mNumKeys; key++)
c07ea6c
+                {
c07ea6c
+                    morphAnim->mKeys[key].mNumValuesAndWeights = morphChannels.size();
c07ea6c
+                    morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()];
c07ea6c
+                    morphAnim->mKeys[key].mWeights = new double [morphChannels.size()];
c07ea6c
+
c07ea6c
+                    morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime;
c07ea6c
+                    for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); valueIndex++)
c07ea6c
+                    {
c07ea6c
+                        morphAnim->mKeys[key].mValues[valueIndex] = valueIndex;
c07ea6c
+                        morphAnim->mKeys[key].mWeights[valueIndex] = getWeightAtKey(morphTimeValues, key, valueIndex);
c07ea6c
+                    }
c07ea6c
+                }
c07ea6c
+
c07ea6c
+                morphAnims.push_back(morphAnim);
c07ea6c
+            }
c07ea6c
+        }
c07ea6c
     }
c07ea6c
 
c07ea6c
-    if( !anims.empty())
c07ea6c
+    if( !anims.empty() || !morphAnims.empty())
c07ea6c
     {
c07ea6c
         aiAnimation* anim = new aiAnimation;
c07ea6c
         anim->mName.Set( pName);
c07ea6c
         anim->mNumChannels = anims.size();
c07ea6c
-        anim->mChannels = new aiNodeAnim*[anims.size()];
c07ea6c
-        std::copy( anims.begin(), anims.end(), anim->mChannels);
c07ea6c
+        if (anim->mNumChannels > 0)
c07ea6c
+        {
c07ea6c
+            anim->mChannels = new aiNodeAnim*[anims.size()];
c07ea6c
+            std::copy( anims.begin(), anims.end(), anim->mChannels);
c07ea6c
+        }
c07ea6c
+        anim->mNumMorphMeshChannels = morphAnims.size();
c07ea6c
+        if (anim->mNumMorphMeshChannels > 0)
c07ea6c
+        {
c07ea6c
+            anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels];
c07ea6c
+            std::copy( morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels);
c07ea6c
+        }
c07ea6c
         anim->mDuration = 0.0f;
c07ea6c
         for( size_t a = 0; a < anims.size(); ++a)
c07ea6c
         {
c07ea6c
-            anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
c07ea6c
-            anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
c07ea6c
-            anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
c07ea6c
+                anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
c07ea6c
+                anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
c07ea6c
+                anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
c07ea6c
+        }
c07ea6c
+        for (size_t a = 0; a < morphAnims.size(); ++a)
c07ea6c
+        {
c07ea6c
+            anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys-1].mTime);
c07ea6c
         }
c07ea6c
         anim->mTicksPerSecond = 1;
c07ea6c
         mAnims.push_back( anim);
c07ea6c
diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h
c07ea6c
index 313608c..fc26532 100644
c07ea6c
--- a/code/ColladaLoader.h
c07ea6c
+++ b/code/ColladaLoader.h
c07ea6c
@@ -117,6 +117,8 @@ protected:
c07ea6c
     /** Builds meshes for the given node and references them */
c07ea6c
     void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
c07ea6c
         aiNode* pTarget);
c07ea6c
+		
c07ea6c
+    aiMesh *findMesh(std::string meshid);
c07ea6c
 
c07ea6c
     /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
c07ea6c
     aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
c07ea6c
@@ -223,6 +225,9 @@ protected:
c07ea6c
 
c07ea6c
     /** Accumulated meshes for the target scene */
c07ea6c
     std::vector<aiMesh*> mMeshes;
c07ea6c
+	
c07ea6c
+    /** Accumulated morph target meshes */
c07ea6c
+    std::vector<aiMesh*> mTargetMeshes;
c07ea6c
 
c07ea6c
     /** Temporary material list */
c07ea6c
     std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats;
c07ea6c
diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp
c07ea6c
index 527e2ed..ce87476 100644
c07ea6c
--- a/code/ColladaParser.cpp
c07ea6c
+++ b/code/ColladaParser.cpp
c07ea6c
@@ -586,6 +586,12 @@ void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
c07ea6c
                     pChannel.mSourceTimes = source;
c07ea6c
                 else if( strcmp( semantic, "OUTPUT") == 0)
c07ea6c
                     pChannel.mSourceValues = source;
c07ea6c
+                else if( strcmp( semantic, "IN_TANGENT") == 0)
c07ea6c
+                    pChannel.mInTanValues = source;
c07ea6c
+                else if( strcmp( semantic, "OUT_TANGENT") == 0)
c07ea6c
+                    pChannel.mOutTanValues = source;
c07ea6c
+                else if( strcmp( semantic, "INTERPOLATION") == 0)
c07ea6c
+                    pChannel.mInterpolationValues = source;
c07ea6c
 
c07ea6c
                 if( !mReader->isEmptyElement())
c07ea6c
                     SkipElement();
c07ea6c
@@ -648,6 +654,9 @@ void ColladaParser::ReadControllerLibrary()
c07ea6c
 // Reads a controller into the given mesh structure
c07ea6c
 void ColladaParser::ReadController( Collada::Controller& pController)
c07ea6c
 {
c07ea6c
+    // initial values
c07ea6c
+    pController.mType = Skin;
c07ea6c
+    pController.mMethod = Normalized;
c07ea6c
     while( mReader->read())
c07ea6c
     {
c07ea6c
         if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
c07ea6c
@@ -655,8 +664,15 @@ void ColladaParser::ReadController( Collada::Controller& pController)
c07ea6c
             // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
c07ea6c
             if( IsElement( "morph"))
c07ea6c
             {
c07ea6c
-                // should skip everything inside, so there's no danger of catching elements inbetween
c07ea6c
-                SkipElement();
c07ea6c
+                pController.mType = Morph;
c07ea6c
+                int baseIndex = GetAttribute("source");
c07ea6c
+                pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
c07ea6c
+                int methodIndex = GetAttribute("method");
c07ea6c
+                if (methodIndex > 0) {
c07ea6c
+                    const char *method = mReader->getAttributeValue(methodIndex);
c07ea6c
+                    if (strcmp(method, "RELATIVE") == 0)
c07ea6c
+                        pController.mMethod = Relative;
c07ea6c
+                }
c07ea6c
             }
c07ea6c
             else if( IsElement( "skin"))
c07ea6c
             {
c07ea6c
@@ -694,6 +710,32 @@ void ColladaParser::ReadController( Collada::Controller& pController)
c07ea6c
             {
c07ea6c
                 ReadControllerWeights( pController);
c07ea6c
             }
c07ea6c
+            else if ( IsElement( "targets" ))
c07ea6c
+            {
c07ea6c
+                while (mReader->read()) {
c07ea6c
+                    if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
c07ea6c
+                        if ( IsElement( "input")) {
c07ea6c
+                            int semanticsIndex = GetAttribute("semantic");
c07ea6c
+                            int sourceIndex = GetAttribute("source");
c07ea6c
+
c07ea6c
+                            const char *semantics = mReader->getAttributeValue(semanticsIndex);
c07ea6c
+                            const char *source = mReader->getAttributeValue(sourceIndex);
c07ea6c
+                            if (strcmp(semantics, "MORPH_TARGET") == 0) {
c07ea6c
+                                pController.mMorphTarget = source + 1;
c07ea6c
+                            }
c07ea6c
+                            else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
c07ea6c
+                            {
c07ea6c
+                                pController.mMorphWeight = source + 1;
c07ea6c
+                            }
c07ea6c
+                        }
c07ea6c
+                    } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
c07ea6c
+                        if( strcmp( mReader->getNodeName(), "targets") == 0)
c07ea6c
+                            break;
c07ea6c
+                        else
c07ea6c
+                            ThrowException( "Expected end of <targets> element.");
c07ea6c
+                    }
c07ea6c
+                }
c07ea6c
+            }
c07ea6c
             else
c07ea6c
             {
c07ea6c
                 // ignore the rest
c07ea6c
@@ -704,7 +746,7 @@ void ColladaParser::ReadController( Collada::Controller& pController)
c07ea6c
         {
c07ea6c
             if( strcmp( mReader->getNodeName(), "controller") == 0)
c07ea6c
                 break;
c07ea6c
-            else if( strcmp( mReader->getNodeName(), "skin") != 0)
c07ea6c
+            else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0)
c07ea6c
                 ThrowException( "Expected end of <controller> element.");
c07ea6c
         }
c07ea6c
     }
c07ea6c
diff --git a/code/CreateAnimMesh.cpp b/code/CreateAnimMesh.cpp
c07ea6c
new file mode 100644
c07ea6c
index 0000000..094a414
c07ea6c
--- /dev/null
c07ea6c
+++ b/code/CreateAnimMesh.cpp
c07ea6c
@@ -0,0 +1,92 @@
c07ea6c
+/*
c07ea6c
+---------------------------------------------------------------------------
c07ea6c
+Open Asset Import Library (assimp)
c07ea6c
+---------------------------------------------------------------------------
c07ea6c
+
c07ea6c
+Copyright (C) 2016 The Qt Company Ltd.
c07ea6c
+Copyright (c) 2006-2012, assimp team
c07ea6c
+
c07ea6c
+All rights reserved.
c07ea6c
+
c07ea6c
+Redistribution and use of this software in source and binary forms,
c07ea6c
+with or without modification, are permitted provided that the following
c07ea6c
+conditions are met:
c07ea6c
+
c07ea6c
+* Redistributions of source code must retain the above
c07ea6c
+copyright notice, this list of conditions and the
c07ea6c
+following disclaimer.
c07ea6c
+
c07ea6c
+* Redistributions in binary form must reproduce the above
c07ea6c
+copyright notice, this list of conditions and the
c07ea6c
+following disclaimer in the documentation and/or other
c07ea6c
+materials provided with the distribution.
c07ea6c
+
c07ea6c
+* Neither the name of the assimp team, nor the names of its
c07ea6c
+contributors may be used to endorse or promote products
c07ea6c
+derived from this software without specific prior
c07ea6c
+written permission of the assimp team.
c07ea6c
+
c07ea6c
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
c07ea6c
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
c07ea6c
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
c07ea6c
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
c07ea6c
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
c07ea6c
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
c07ea6c
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
c07ea6c
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
c07ea6c
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
c07ea6c
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
c07ea6c
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
c07ea6c
+---------------------------------------------------------------------------
c07ea6c
+*/
c07ea6c
+
c07ea6c
+#include "CreateAnimMesh.h"
c07ea6c
+
c07ea6c
+namespace Assimp    {
c07ea6c
+
c07ea6c
+aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
c07ea6c
+{
c07ea6c
+    aiAnimMesh *animesh = new aiAnimMesh;
c07ea6c
+    animesh->mVertices = NULL;
c07ea6c
+    animesh->mNormals = NULL;
c07ea6c
+    animesh->mTangents = NULL;
c07ea6c
+    animesh->mBitangents = NULL;
c07ea6c
+    animesh->mNumVertices = mesh->mNumVertices;
c07ea6c
+    if (mesh->mVertices) {
c07ea6c
+        animesh->mVertices = new aiVector3D[animesh->mNumVertices];
c07ea6c
+        std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D));
c07ea6c
+    }
c07ea6c
+    if (mesh->mNormals) {
c07ea6c
+        animesh->mNormals = new aiVector3D[animesh->mNumVertices];
c07ea6c
+        std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D));
c07ea6c
+    }
c07ea6c
+    if (mesh->mTangents) {
c07ea6c
+        animesh->mTangents = new aiVector3D[animesh->mNumVertices];
c07ea6c
+        std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D));
c07ea6c
+    }
c07ea6c
+    if (mesh->mBitangents) {
c07ea6c
+        animesh->mBitangents = new aiVector3D[animesh->mNumVertices];
c07ea6c
+        std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D));
c07ea6c
+    }
c07ea6c
+
c07ea6c
+    for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
c07ea6c
+        if (mesh->mColors[i]) {
c07ea6c
+            animesh->mColors[i] = new aiColor4D[animesh->mNumVertices];
c07ea6c
+            std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D));
c07ea6c
+        } else {
c07ea6c
+            animesh->mColors[i] = NULL;
c07ea6c
+        }
c07ea6c
+    }
c07ea6c
+
c07ea6c
+    for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
c07ea6c
+        if (mesh->mTextureCoords[i]) {
c07ea6c
+            animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices];
c07ea6c
+            std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D));
c07ea6c
+        } else {
c07ea6c
+            animesh->mTextureCoords[i] = NULL;
c07ea6c
+        }
c07ea6c
+    }
c07ea6c
+    return animesh;
c07ea6c
+}
c07ea6c
+
c07ea6c
+} // end of namespace Assimp
c07ea6c
diff --git a/code/CreateAnimMesh.h b/code/CreateAnimMesh.h
c07ea6c
new file mode 100644
c07ea6c
index 0000000..c5ceb40
c07ea6c
--- /dev/null
c07ea6c
+++ b/code/CreateAnimMesh.h
c07ea6c
@@ -0,0 +1,56 @@
c07ea6c
+/*
c07ea6c
+Open Asset Import Library (assimp)
c07ea6c
+----------------------------------------------------------------------
c07ea6c
+
c07ea6c
+Copyright (c) 2006-2016, assimp team
c07ea6c
+All rights reserved.
c07ea6c
+
c07ea6c
+Redistribution and use of this software in source and binary forms,
c07ea6c
+with or without modification, are permitted provided that the
c07ea6c
+following conditions are met:
c07ea6c
+
c07ea6c
+* Redistributions of source code must retain the above
c07ea6c
+  copyright notice, this list of conditions and the
c07ea6c
+  following disclaimer.
c07ea6c
+
c07ea6c
+* Redistributions in binary form must reproduce the above
c07ea6c
+  copyright notice, this list of conditions and the
c07ea6c
+  following disclaimer in the documentation and/or other
c07ea6c
+  materials provided with the distribution.
c07ea6c
+
c07ea6c
+* Neither the name of the assimp team, nor the names of its
c07ea6c
+  contributors may be used to endorse or promote products
c07ea6c
+  derived from this software without specific prior
c07ea6c
+  written permission of the assimp team.
c07ea6c
+
c07ea6c
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
c07ea6c
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
c07ea6c
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
c07ea6c
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
c07ea6c
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
c07ea6c
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
c07ea6c
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
c07ea6c
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
c07ea6c
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
c07ea6c
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
c07ea6c
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
c07ea6c
+
c07ea6c
+----------------------------------------------------------------------
c07ea6c
+*/
c07ea6c
+
c07ea6c
+/** @file CreateAnimMesh.h
c07ea6c
+ *  Create AnimMesh from Mesh
c07ea6c
+ */
c07ea6c
+#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H
c07ea6c
+#define INCLUDED_AI_CREATE_ANIM_MESH_H
c07ea6c
+
c07ea6c
+#include <assimp/mesh.h>
c07ea6c
+
c07ea6c
+namespace Assimp    {
c07ea6c
+
c07ea6c
+/** Create aiAnimMesh from aiMesh. */
c07ea6c
+aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh);
c07ea6c
+
c07ea6c
+} // end of namespace Assimp
c07ea6c
+#endif // INCLUDED_AI_CREATE_ANIM_MESH_H
c07ea6c
+
c07ea6c
diff --git a/include/assimp/anim.h b/include/assimp/anim.h
c07ea6c
index af59417..7b4dff8 100644
c07ea6c
--- a/include/assimp/anim.h
c07ea6c
+++ b/include/assimp/anim.h
c07ea6c
@@ -182,6 +182,39 @@ struct aiMeshKey
c07ea6c
 };
c07ea6c
 
c07ea6c
 // ---------------------------------------------------------------------------
c07ea6c
+/** Binds a morph anim mesh to a specific point in time. */
c07ea6c
+struct aiMeshMorphKey
c07ea6c
+{
c07ea6c
+    /** The time of this key */
c07ea6c
+    double mTime;
c07ea6c
+
c07ea6c
+    /** The values and weights at the time of this key */
c07ea6c
+    unsigned int *mValues;
c07ea6c
+    double *mWeights;
c07ea6c
+
c07ea6c
+    /** The number of values and weights */
c07ea6c
+    unsigned int mNumValuesAndWeights;
c07ea6c
+#ifdef __cplusplus
c07ea6c
+	aiMeshMorphKey()
c07ea6c
+		: mTime(0.0)
c07ea6c
+		, mValues(NULL)
c07ea6c
+		, mWeights(NULL)
c07ea6c
+		, mNumValuesAndWeights(0)
c07ea6c
+	{
c07ea6c
+
c07ea6c
+	}
c07ea6c
+
c07ea6c
+    ~aiMeshMorphKey()
c07ea6c
+    {
c07ea6c
+        if (mNumValuesAndWeights && mValues && mWeights) {
c07ea6c
+            delete [] mValues;
c07ea6c
+            delete [] mWeights;
c07ea6c
+        }
c07ea6c
+    }
c07ea6c
+#endif
c07ea6c
+};
c07ea6c
+
c07ea6c
+// ---------------------------------------------------------------------------
c07ea6c
 /** Defines how an animation channel behaves outside the defined time
c07ea6c
  *  range. This corresponds to aiNodeAnim::mPreState and
c07ea6c
  *  aiNodeAnim::mPostState.*/
c07ea6c
@@ -335,7 +368,38 @@ struct aiMeshAnim
c07ea6c
 };
c07ea6c
 
c07ea6c
 // ---------------------------------------------------------------------------
c07ea6c
-/** An animation consists of keyframe data for a number of nodes. For
c07ea6c
+/** Describes a morphing animation of a given mesh. */
c07ea6c
+struct aiMeshMorphAnim
c07ea6c
+{
c07ea6c
+    /** Name of the mesh to be animated. An empty string is not allowed,
c07ea6c
+     *  animated meshes need to be named (not necessarily uniquely,
c07ea6c
+     *  the name can basically serve as wildcard to select a group
c07ea6c
+     *  of meshes with similar animation setup)*/
c07ea6c
+    C_STRUCT aiString mName;
c07ea6c
+
c07ea6c
+    /** Size of the #mKeys array. Must be 1, at least. */
c07ea6c
+    unsigned int mNumKeys;
c07ea6c
+
c07ea6c
+    /** Key frames of the animation. May not be NULL. */
c07ea6c
+    C_STRUCT aiMeshMorphKey* mKeys;
c07ea6c
+
c07ea6c
+#ifdef __cplusplus
c07ea6c
+
c07ea6c
+    aiMeshMorphAnim()
c07ea6c
+        : mNumKeys()
c07ea6c
+        , mKeys()
c07ea6c
+    {}
c07ea6c
+
c07ea6c
+    ~aiMeshMorphAnim()
c07ea6c
+    {
c07ea6c
+        delete[] mKeys;
c07ea6c
+    }
c07ea6c
+
c07ea6c
+#endif
c07ea6c
+};
c07ea6c
+
c07ea6c
+// ---------------------------------------------------------------------------
c07ea6c
+/** An animation consists of key-frame data for a number of nodes. For
c07ea6c
  *  each node affected by the animation a separate series of data is given.*/
c07ea6c
 struct aiAnimation
c07ea6c
 {
c07ea6c
@@ -367,15 +431,25 @@ struct aiAnimation
c07ea6c
      *  The array is mNumMeshChannels in size. */
c07ea6c
     C_STRUCT aiMeshAnim** mMeshChannels;
c07ea6c
 
c07ea6c
+    /** The number of mesh animation channels. Each channel affects
c07ea6c
+     *  a single mesh and defines morphing animation. */
c07ea6c
+    unsigned int mNumMorphMeshChannels;
c07ea6c
+
c07ea6c
+    /** The morph mesh animation channels. Each channel affects a single mesh.
c07ea6c
+     *  The array is mNumMorphMeshChannels in size. */
c07ea6c
+    C_STRUCT aiMeshMorphAnim **mMorphMeshChannels;
c07ea6c
+
c07ea6c
 #ifdef __cplusplus
c07ea6c
     aiAnimation()
c07ea6c
-        : mDuration(-1.)
c07ea6c
-        , mTicksPerSecond()
c07ea6c
-        , mNumChannels()
c07ea6c
-        , mChannels()
c07ea6c
-        , mNumMeshChannels()
c07ea6c
-        , mMeshChannels()
c07ea6c
-    {
c07ea6c
+    : mDuration(-1.)
c07ea6c
+    , mTicksPerSecond(0.)
c07ea6c
+    , mNumChannels(0)
c07ea6c
+    , mChannels(NULL)
c07ea6c
+    , mNumMeshChannels(0)
c07ea6c
+    , mMeshChannels(NULL)
c07ea6c
+    , mNumMorphMeshChannels(0)
c07ea6c
+    , mMorphMeshChannels(NULL) {
c07ea6c
+        // empty
c07ea6c
     }
c07ea6c
 
c07ea6c
     ~aiAnimation()
c07ea6c
@@ -395,6 +469,11 @@ struct aiAnimation
c07ea6c
 
c07ea6c
         delete [] mMeshChannels;
c07ea6c
         }
c07ea6c
+        if (mNumMorphMeshChannels && mMorphMeshChannels) {
c07ea6c
+                for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) {
c07ea6c
+                        delete mMorphMeshChannels[a];
c07ea6c
+                }
c07ea6c
+        }
c07ea6c
     }
c07ea6c
 #endif // __cplusplus
c07ea6c
 };
c07ea6c
diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h
c07ea6c
index a76a2d7..2ebc14d 100644
c07ea6c
--- a/include/assimp/mesh.h
c07ea6c
+++ b/include/assimp/mesh.h
c07ea6c
@@ -377,6 +377,9 @@ struct aiAnimMesh
c07ea6c
      */
c07ea6c
     unsigned int mNumVertices;
c07ea6c
 
c07ea6c
+/** Weight of the AnimMesh. */
c07ea6c
+    float mWeight;
c07ea6c
+
c07ea6c
 #ifdef __cplusplus
c07ea6c
 
c07ea6c
     aiAnimMesh()
c07ea6c
@@ -445,6 +448,27 @@ struct aiAnimMesh
c07ea6c
 #endif
c07ea6c
 };
c07ea6c
 
c07ea6c
+// ---------------------------------------------------------------------------
c07ea6c
+/** @brief Enumerates the methods of mesh morphing supported by Assimp.
c07ea6c
+ */
c07ea6c
+enum aiMorphingMethod
c07ea6c
+{
c07ea6c
+    /** Interpolation between morph targets */
c07ea6c
+    aiMorphingMethod_VERTEX_BLEND       = 0x1,
c07ea6c
+
c07ea6c
+    /** Normalized morphing between morph targets  */
c07ea6c
+    aiMorphingMethod_MORPH_NORMALIZED   = 0x2,
c07ea6c
+
c07ea6c
+    /** Relative morphing between morph targets  */
c07ea6c
+    aiMorphingMethod_MORPH_RELATIVE     = 0x3,
c07ea6c
+
c07ea6c
+    /** This value is not used. It is just here to force the
c07ea6c
+     *  compiler to map this enum to a 32 Bit integer.
c07ea6c
+     */
c07ea6c
+#ifndef SWIG
c07ea6c
+    _aiMorphingMethod_Force32Bit = INT_MAX
c07ea6c
+#endif
c07ea6c
+}; //! enum aiMorphingMethod
c07ea6c
 
c07ea6c
 // ---------------------------------------------------------------------------
c07ea6c
 /** @brief A mesh represents a geometry or model with a single material.
c07ea6c
@@ -599,15 +623,18 @@ struct aiMesh
c07ea6c
     C_STRUCT aiString mName;
c07ea6c
 
c07ea6c
 
c07ea6c
-    /** NOT CURRENTLY IN USE. The number of attachment meshes */
c07ea6c
+    /** The number of attachment meshes. Note! Currently only works with Collada loader. */
c07ea6c
     unsigned int mNumAnimMeshes;
c07ea6c
 
c07ea6c
-    /** NOT CURRENTLY IN USE. Attachment meshes for this mesh, for vertex-based animation.
c07ea6c
+    /** Attachment meshes for this mesh, for vertex-based animation.
c07ea6c
      *  Attachment meshes carry replacement data for some of the
c07ea6c
-     *  mesh'es vertex components (usually positions, normals). */
c07ea6c
+     *  mesh'es vertex components (usually positions, normals).
c07ea6c
+     *  Note! Currently only works with Collada loader.*/
c07ea6c
     C_STRUCT aiAnimMesh** mAnimMeshes;
c07ea6c
 
c07ea6c
-
c07ea6c
+    /** Method of morphing when animeshes are specified. */
c07ea6c
+    unsigned int mMethod;
c07ea6c
+	
c07ea6c
 #ifdef __cplusplus
c07ea6c
 
c07ea6c
     //! Default constructor. Initializes all members to 0
c07ea6c
@@ -732,7 +759,6 @@ struct aiMesh
c07ea6c
 #endif // __cplusplus
c07ea6c
 };
c07ea6c
 
c07ea6c
-
c07ea6c
 #ifdef __cplusplus
c07ea6c
 }
c07ea6c
 #endif //! extern "C"
c07ea6c
-- 
c07ea6c
2.9.3
c07ea6c