Morph animation support for collada

pull/1134/head
Antti Määttä 2017-01-12 13:41:32 +02:00
parent a97a4fb03b
commit 9621dff027
9 changed files with 615 additions and 44 deletions

View File

@ -173,6 +173,8 @@ SET( Common_SRCS
XMLTools.h XMLTools.h
Version.cpp Version.cpp
IOStreamBuffer.h IOStreamBuffer.h
CreateAnimMesh.h
CreateAnimMesh.cpp
) )
SOURCE_GROUP(Common FILES ${Common_SRCS}) SOURCE_GROUP(Common FILES ${Common_SRCS})

View File

@ -89,6 +89,21 @@ enum InputType
IT_Bitangent 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 */ /** Contains all data for one of the different transformation types */
struct Transform struct Transform
{ {
@ -380,6 +395,12 @@ enum PrimitiveType
/** A skeleton controller to deform a mesh with the use of joints */ /** A skeleton controller to deform a mesh with the use of joints */
struct Controller struct Controller
{ {
// controller type
ControllerType mType;
// Morphing method if type is Morph
MorphMethod mMethod;
// the URL of the mesh deformed by the controller. // the URL of the mesh deformed by the controller.
std::string mMeshId; std::string mMeshId;
@ -402,6 +423,9 @@ struct Controller
// JointIndex-WeightIndex pairs for all vertices // JointIndex-WeightIndex pairs for all vertices
std::vector< std::pair<size_t, size_t> > mWeights; 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. */ /** A collada material. Pretty much the only member is a reference to an effect. */
@ -577,6 +601,12 @@ struct AnimationChannel
std::string mSourceTimes; std::string mSourceTimes;
/** Source URL of the value values. Collada calls them "output". */ /** Source URL of the value values. Collada calls them "output". */
std::string mSourceValues; 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 */ /** An animation. Container for 0-x animation channels or 0-x animations */
@ -645,6 +675,7 @@ struct Animation
struct ChannelEntry struct ChannelEntry
{ {
const Collada::AnimationChannel* mChannel; ///> the source channel 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 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 mTransformIndex; // Index into the node's transform chain to apply the channel to
size_t mSubElement; // starting index inside the transform data size_t mSubElement; // starting index inside the transform data

View File

@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ParsingUtils.h" #include "ParsingUtils.h"
#include "SkeletonMeshBuilder.h" #include "SkeletonMeshBuilder.h"
#include "Defines.h" #include "Defines.h"
#include "CreateAnimMesh.h"
#include "time.h" #include "time.h"
#include "math.h" #include "math.h"
@ -150,6 +151,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
mMeshIndexByID.clear(); mMeshIndexByID.clear();
mMaterialIndexByName.clear(); mMaterialIndexByName.clear();
mMeshes.clear(); mMeshes.clear();
mTargetMeshes.clear();
newMats.clear(); newMats.clear();
mLights.clear(); mLights.clear();
mCameras.clear(); mCameras.clear();
@ -571,6 +573,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 // 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, aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
@ -656,8 +673,70 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
face.mIndices[b] = static_cast<unsigned int>(vertex++); face.mIndices[b] = static_cast<unsigned int>(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 // create bones if given
if( pSrcController) if( pSrcController && pSrcController->mType == Collada::Skin)
{ {
// refuse if the vertex count does not match // refuse if the vertex count does not match
// if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
@ -956,6 +1035,68 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars
CreateAnimation( pScene, pParser, pSrcAnim, animName); 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 // Constructs the animation for the given source anim
void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName) void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
@ -965,6 +1106,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
CollectNodes( pScene->mRootNode, nodes); CollectNodes( pScene->mRootNode, nodes);
std::vector<aiNodeAnim*> anims; std::vector<aiNodeAnim*> anims;
std::vector<aiMeshMorphAnim*> morphAnims;
for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit) 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 // find all the collada anim channels which refer to the current node
@ -988,7 +1131,20 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
// find the slash that separates the node name - there should be only one // find the slash that separates the node name - there should be only one
std::string::size_type slashPos = srcChannel.mTarget.find( '/'); std::string::size_type slashPos = srcChannel.mTarget.find( '/');
if( slashPos == std::string::npos) 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; continue;
}
if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
continue; continue;
std::string targetID = srcChannel.mTarget.substr( 0, slashPos); std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
@ -1068,8 +1224,14 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
if( srcNode->mTransforms[a].mID == entry.mTransformId) if( srcNode->mTransforms[a].mID == entry.mTransformId)
entry.mTransformIndex = a; entry.mTransformIndex = a;
if( entry.mTransformIndex == SIZE_MAX) { if( entry.mTransformIndex == SIZE_MAX)
continue; {
if (entry.mTransformId.find("morph-weights") != std::string::npos)
{
entry.mTargetId = entry.mTransformId;
entry.mTransformId = "";
} else
continue;
} }
entry.mChannel = &(*cit); entry.mChannel = &(*cit);
@ -1213,49 +1375,125 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
// ai_assert( resultTrafos.size() > 0); // ai_assert( resultTrafos.size() > 0);
// build an animation channel for the given node out of these trafo keys // build an animation channel for the given node out of these trafo keys
if( !resultTrafos.empty() ) if( !resultTrafos.empty() )
{ {
aiNodeAnim* dstAnim = new aiNodeAnim; aiNodeAnim* dstAnim = new aiNodeAnim;
dstAnim->mNodeName = nodeName; dstAnim->mNodeName = nodeName;
dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size()); dstAnim->mNumPositionKeys = resultTrafos.size();
dstAnim->mNumRotationKeys= static_cast<unsigned int>(resultTrafos.size()); dstAnim->mNumRotationKeys= resultTrafos.size();
dstAnim->mNumScalingKeys = static_cast<unsigned int>(resultTrafos.size()); dstAnim->mNumScalingKeys = resultTrafos.size();
dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
for( size_t a = 0; a < resultTrafos.size(); ++a) for( size_t a = 0; a < resultTrafos.size(); ++a)
{ {
aiMatrix4x4 mat = resultTrafos[a]; aiMatrix4x4 mat = resultTrafos[a];
double time = double( mat.d4); // remember? time is stored in mat.d4 double time = double( mat.d4); // remember? time is stored in mat.d4
mat.d4 = 1.0f; mat.d4 = 1.0f;
dstAnim->mPositionKeys[a].mTime = time; dstAnim->mPositionKeys[a].mTime = time;
dstAnim->mRotationKeys[a].mTime = time; dstAnim->mRotationKeys[a].mTime = time;
dstAnim->mScalingKeys[a].mTime = time; dstAnim->mScalingKeys[a].mTime = time;
mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
} }
anims.push_back( dstAnim); anims.push_back( dstAnim);
} else } else
{ {
DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); 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; aiAnimation* anim = new aiAnimation;
anim->mName.Set( pName); anim->mName.Set( pName);
anim->mNumChannels = static_cast<unsigned int>(anims.size()); anim->mNumChannels = anims.size();
anim->mChannels = new aiNodeAnim*[anims.size()]; if (anim->mNumChannels > 0)
std::copy( anims.begin(), anims.end(), anim->mChannels); {
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; anim->mDuration = 0.0f;
for( size_t a = 0; a < anims.size(); ++a) 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]->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]->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]->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; anim->mTicksPerSecond = 1;
mAnims.push_back( anim); mAnims.push_back( anim);

View File

@ -118,6 +118,8 @@ protected:
void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
aiNode* pTarget); aiNode* pTarget);
aiMesh *findMesh(std::string meshid);
/** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */ /** 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, aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace); const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace);
@ -224,6 +226,9 @@ protected:
/** Accumulated meshes for the target scene */ /** Accumulated meshes for the target scene */
std::vector<aiMesh*> mMeshes; std::vector<aiMesh*> mMeshes;
/** Accumulated morph target meshes */
std::vector<aiMesh*> mTargetMeshes;
/** Temporary material list */ /** Temporary material list */
std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats; std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats;

View File

@ -585,6 +585,12 @@ void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
pChannel.mSourceTimes = source; pChannel.mSourceTimes = source;
else if( strcmp( semantic, "OUTPUT") == 0) else if( strcmp( semantic, "OUTPUT") == 0)
pChannel.mSourceValues = source; 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()) if( !mReader->isEmptyElement())
SkipElement(); SkipElement();
@ -647,6 +653,9 @@ void ColladaParser::ReadControllerLibrary()
// Reads a controller into the given mesh structure // Reads a controller into the given mesh structure
void ColladaParser::ReadController( Collada::Controller& pController) void ColladaParser::ReadController( Collada::Controller& pController)
{ {
// initial values
pController.mType = Skin;
pController.mMethod = Normalized;
while( mReader->read()) while( mReader->read())
{ {
if( mReader->getNodeType() == irr::io::EXN_ELEMENT) if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
@ -654,8 +663,15 @@ void ColladaParser::ReadController( Collada::Controller& pController)
// two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
if( IsElement( "morph")) if( IsElement( "morph"))
{ {
// should skip everything inside, so there's no danger of catching elements in between pController.mType = Morph;
SkipElement(); 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")) else if( IsElement( "skin"))
{ {
@ -693,6 +709,32 @@ void ColladaParser::ReadController( Collada::Controller& pController)
{ {
ReadControllerWeights( 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 else
{ {
// ignore the rest // ignore the rest
@ -703,7 +745,7 @@ void ColladaParser::ReadController( Collada::Controller& pController)
{ {
if( strcmp( mReader->getNodeName(), "controller") == 0) if( strcmp( mReader->getNodeName(), "controller") == 0)
break; 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."); ThrowException( "Expected end of <controller> element.");
} }
} }

View File

@ -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

View File

@ -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

View File

@ -190,6 +190,39 @@ struct aiMeshKey
#endif #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 /** Defines how an animation channel behaves outside the defined time
* range. This corresponds to aiNodeAnim::mPreState and * range. This corresponds to aiNodeAnim::mPreState and
@ -340,6 +373,37 @@ struct aiMeshAnim
#endif #endif
}; };
// ---------------------------------------------------------------------------
/** 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 /** 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.*/ * each node affected by the animation a separate series of data is given.*/
@ -372,6 +436,14 @@ struct aiAnimation {
* The array is mNumMeshChannels in size. */ * The array is mNumMeshChannels in size. */
C_STRUCT aiMeshAnim** mMeshChannels; 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 #ifdef __cplusplus
aiAnimation() aiAnimation()
: mDuration(-1.) : mDuration(-1.)
@ -379,7 +451,9 @@ struct aiAnimation {
, mNumChannels(0) , mNumChannels(0)
, mChannels(NULL) , mChannels(NULL)
, mNumMeshChannels(0) , mNumMeshChannels(0)
, mMeshChannels(NULL) { , mMeshChannels(NULL)
, mNumMorphMeshChannels(0)
, mMorphMeshChannels(NULL) {
// empty // empty
} }
@ -399,6 +473,11 @@ struct aiAnimation {
delete [] mMeshChannels; delete [] mMeshChannels;
} }
if (mNumMorphMeshChannels && mMorphMeshChannels) {
for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) {
delete mMorphMeshChannels[a];
}
}
} }
#endif // __cplusplus #endif // __cplusplus
}; };

View File

@ -378,6 +378,9 @@ struct aiAnimMesh
*/ */
unsigned int mNumVertices; unsigned int mNumVertices;
/** Weight of the AnimMesh. */
float mWeight;
#ifdef __cplusplus #ifdef __cplusplus
aiAnimMesh() aiAnimMesh()
@ -446,6 +449,27 @@ struct aiAnimMesh
#endif #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. /** @brief A mesh represents a geometry or model with a single material.
@ -600,14 +624,17 @@ struct aiMesh
C_STRUCT aiString mName; 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; 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 * 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; C_STRUCT aiAnimMesh** mAnimMeshes;
/** Method of morphing when animeshes are specified. */
unsigned int mMethod;
#ifdef __cplusplus #ifdef __cplusplus
@ -733,7 +760,6 @@ struct aiMesh
#endif // __cplusplus #endif // __cplusplus
}; };
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif //! extern "C" #endif //! extern "C"