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

