blob: bd94f587c1e717c9be36e2b27256d70311f76841 [file] [log] [blame]
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