From 42e353f2a672baed485c03acf30529c77092ece9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antti=20M=C3=A4=C3=A4tt=C3=A4?= <antti.maatta@qt.io>
Date: Thu, 12 Jan 2017 13:41:32 +0200
Subject: [PATCH] Morph animation support for collada
---
code/CMakeLists.txt | 2 +
code/ColladaHelper.h | 31 +++++
code/ColladaLoader.cpp | 310 ++++++++++++++++++++++++++++++++++++++++++------
code/ColladaLoader.h | 5 +
code/ColladaParser.cpp | 48 +++++++-
code/CreateAnimMesh.cpp | 92 ++++++++++++++
code/CreateAnimMesh.h | 56 +++++++++
include/assimp/anim.h | 95 +++++++++++++--
include/assimp/mesh.h | 36 +++++-
9 files changed, 623 insertions(+), 52 deletions(-)
create mode 100644 code/CreateAnimMesh.cpp
create mode 100644 code/CreateAnimMesh.h
diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt
index 35ecf50..e0747d6 100644
--- a/code/CMakeLists.txt
+++ b/code/CMakeLists.txt
@@ -171,6 +171,8 @@ SET( Common_SRCS
Bitmap.h
XMLTools.h
Version.cpp
+ CreateAnimMesh.h
+ CreateAnimMesh.cpp
)
SOURCE_GROUP(Common FILES ${Common_SRCS})
diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h
index 5a51642..5ff4904 100644
--- a/code/ColladaHelper.h
+++ b/code/ColladaHelper.h
@@ -89,6 +89,21 @@ 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
{
@@ -380,6 +395,12 @@ enum PrimitiveType
/** A skeleton controller to deform a mesh with the use of joints */
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;
@@ -402,6 +423,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. */
@@ -577,6 +601,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 */
@@ -645,6 +675,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/code/ColladaLoader.cpp b/code/ColladaLoader.cpp
index 6cc0f85..a3b6d6e 100644
--- a/code/ColladaLoader.cpp
+++ b/code/ColladaLoader.cpp
@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ParsingUtils.h"
#include "SkeletonMeshBuilder.h"
#include "Defines.h"
+#include "CreateAnimMesh.h"
#include "time.h"
#include "math.h"
@@ -148,6 +149,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
mMeshIndexByID.clear();
mMaterialIndexByName.clear();
mMeshes.clear();
+ mTargetMeshes.clear();
newMats.clear();
mLights.clear();
mCameras.clear();
@@ -562,6 +564,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)
@@ -646,8 +663,70 @@ 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)
@@ -946,6 +1025,68 @@ 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)
@@ -955,6 +1096,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
CollectNodes( pScene->mRootNode, nodes);
std::vector<aiNodeAnim*> anims;
+ std::vector<aiMeshMorphAnim*> morphAnims;
+
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
@@ -978,7 +1121,20 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
// 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)
+ {
+ 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);
@@ -1058,8 +1214,14 @@ 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);
@@ -1203,49 +1365,125 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
// 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);
- }
+ 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.");
- }
+ 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)
{
- anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
- 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);
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
+ 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/code/ColladaLoader.h b/code/ColladaLoader.h
index 313608c..fc26532 100644
--- a/code/ColladaLoader.h
+++ b/code/ColladaLoader.h
@@ -117,6 +117,8 @@ protected:
/** Builds meshes for the given node and references them */
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,
@@ -223,6 +225,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/code/ColladaParser.cpp b/code/ColladaParser.cpp
index 527e2ed..ce87476 100644
--- a/code/ColladaParser.cpp
+++ b/code/ColladaParser.cpp
@@ -586,6 +586,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();
@@ -648,6 +654,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)
@@ -655,8 +664,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"))
{
@@ -694,6 +710,32 @@ void ColladaParser::ReadController( Collada::Controller& pController)
{
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
@@ -704,7 +746,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/code/CreateAnimMesh.cpp b/code/CreateAnimMesh.cpp
new file mode 100644
index 0000000..094a414
--- /dev/null
+++ b/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/code/CreateAnimMesh.h b/code/CreateAnimMesh.h
new file mode 100644
index 0000000..c5ceb40
--- /dev/null
+++ b/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/include/assimp/anim.h b/include/assimp/anim.h
index af59417..7b4dff8 100644
--- a/include/assimp/anim.h
+++ b/include/assimp/anim.h
@@ -182,6 +182,39 @@ struct aiMeshKey
};
// ---------------------------------------------------------------------------
+/** 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
* aiNodeAnim::mPostState.*/
@@ -335,7 +368,38 @@ struct aiMeshAnim
};
// ---------------------------------------------------------------------------
-/** An animation consists of keyframe data for a number of nodes. For
+/** 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 key-frame data for a number of nodes. For
* each node affected by the animation a separate series of data is given.*/
struct aiAnimation
{
@@ -367,15 +431,25 @@ 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.)
- , mTicksPerSecond()
- , mNumChannels()
- , mChannels()
- , mNumMeshChannels()
- , mMeshChannels()
- {
+ : mDuration(-1.)
+ , mTicksPerSecond(0.)
+ , mNumChannels(0)
+ , mChannels(NULL)
+ , mNumMeshChannels(0)
+ , mMeshChannels(NULL)
+ , mNumMorphMeshChannels(0)
+ , mMorphMeshChannels(NULL) {
+ // empty
}
~aiAnimation()
@@ -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/include/assimp/mesh.h b/include/assimp/mesh.h
index a76a2d7..2ebc14d 100644
--- a/include/assimp/mesh.h
+++ b/include/assimp/mesh.h
@@ -377,6 +377,9 @@ struct aiAnimMesh
*/
unsigned int mNumVertices;
+/** Weight of the AnimMesh. */
+ float mWeight;
+
#ifdef __cplusplus
aiAnimMesh()
@@ -445,6 +448,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.
@@ -599,15 +623,18 @@ 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
//! Default constructor. Initializes all members to 0
@@ -732,7 +759,6 @@ struct aiMesh
#endif // __cplusplus
};
-
#ifdef __cplusplus
}
#endif //! extern "C"
--
2.9.3