diff options
Diffstat (limited to 'thirdparty/assimp/code/PostProcessing')
9 files changed, 486 insertions, 33 deletions
diff --git a/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.cpp b/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.cpp new file mode 100644 index 0000000000..75daeb6b59 --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.cpp @@ -0,0 +1,268 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 "ArmaturePopulate.h" + +#include <assimp/BaseImporter.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <iostream> + +namespace Assimp { + +/// The default class constructor. +ArmaturePopulate::ArmaturePopulate() : BaseProcess() +{} + +/// The class destructor. +ArmaturePopulate::~ArmaturePopulate() +{} + +bool ArmaturePopulate::IsActive(unsigned int pFlags) const { + return (pFlags & aiProcess_PopulateArmatureData) != 0; +} + +void ArmaturePopulate::SetupProperties(const Importer *pImp) { + // do nothing +} + +void ArmaturePopulate::Execute(aiScene *out) { + + // Now convert all bone positions to the correct mOffsetMatrix + std::vector<aiBone *> bones; + std::vector<aiNode *> nodes; + std::map<aiBone *, aiNode *> bone_stack; + BuildBoneList(out->mRootNode, out->mRootNode, out, bones); + BuildNodeList(out->mRootNode, nodes); + + BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); + + ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size()); + + for (std::pair<aiBone *, aiNode *> kvp : bone_stack) { + aiBone *bone = kvp.first; + aiNode *bone_node = kvp.second; + ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str()); + // lcl transform grab - done in generate_nodes :) + + // bone->mOffsetMatrix = bone_node->mTransformation; + aiNode *armature = GetArmatureRoot(bone_node, bones); + + ai_assert(armature); + + // set up bone armature id + bone->mArmature = armature; + + // set this bone node to be referenced properly + ai_assert(bone_node); + bone->mNode = bone_node; + } +} + + +/* Reprocess all nodes to calculate bone transforms properly based on the REAL + * mOffsetMatrix not the local. */ +/* Before this would use mesh transforms which is wrong for bone transforms */ +/* Before this would work for simple character skeletons but not complex meshes + * with multiple origins */ +/* Source: sketch fab log cutter fbx */ +void ArmaturePopulate::BuildBoneList(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + std::vector<aiBone *> &bones) { + ai_assert(scene); + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + // check for bones + for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) { + ai_assert(child->mMeshes); + unsigned int mesh_index = child->mMeshes[meshId]; + aiMesh *mesh = scene->mMeshes[mesh_index]; + ai_assert(mesh); + + for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { + aiBone *bone = mesh->mBones[boneId]; + ai_assert(bone); + + // duplicate meshes exist with the same bones sometimes :) + // so this must be detected + if (std::find(bones.begin(), bones.end(), bone) == bones.end()) { + // add the element once + bones.push_back(bone); + } + } + + // find mesh and get bones + // then do recursive lookup for bones in root node hierarchy + } + + BuildBoneList(child, root_node, scene, bones); + } +} + +/* Prepare flat node list which can be used for non recursive lookups later */ +void ArmaturePopulate::BuildNodeList(const aiNode *current_node, + std::vector<aiNode *> &nodes) { + ai_assert(current_node); + + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + nodes.push_back(child); + + BuildNodeList(child, nodes); + } +} + +/* A bone stack allows us to have multiple armatures, with the same bone names + * A bone stack allows us also to retrieve bones true transform even with + * duplicate names :) + */ +void ArmaturePopulate::BuildBoneStack(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + const std::vector<aiBone *> &bones, + std::map<aiBone *, aiNode *> &bone_stack, + std::vector<aiNode *> &node_stack) { + ai_assert(scene); + ai_assert(root_node); + ai_assert(!node_stack.empty()); + + for (aiBone *bone : bones) { + ai_assert(bone); + aiNode *node = GetNodeFromStack(bone->mName, node_stack); + if (node == nullptr) { + node_stack.clear(); + BuildNodeList(root_node, node_stack); + ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str()); + + node = GetNodeFromStack(bone->mName, node_stack); + + if (!node) { + ASSIMP_LOG_ERROR("serious import issue node for bone was not detected"); + continue; + } + } + + ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); + + bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node)); + } +} + + +/* Returns the armature root node */ +/* This is required to be detected for a bone initially, it will recurse up + * until it cannot find another bone and return the node No known failure + * points. (yet) + */ +aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, + std::vector<aiBone *> &bone_list) { + while (bone_node) { + if (!IsBoneNode(bone_node->mName, bone_list)) { + ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); + return bone_node; + } + + bone_node = bone_node->mParent; + } + + ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!"); + + return nullptr; +} + + + +/* Simple IsBoneNode check if this could be a bone */ +bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, + std::vector<aiBone *> &bones) { + for (aiBone *bone : bones) { + if (bone->mName == bone_name) { + return true; + } + } + + return false; +} + +/* Pop this node by name from the stack if found */ +/* Used in multiple armature situations with duplicate node / bone names */ +/* Known flaw: cannot have nodes with bone names, will be fixed in later release + */ +/* (serious to be fixed) Known flaw: nodes which have more than one bone could + * be prematurely dropped from stack */ +aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, + std::vector<aiNode *> &nodes) { + std::vector<aiNode *>::iterator iter; + aiNode *found = nullptr; + for (iter = nodes.begin(); iter < nodes.end(); ++iter) { + aiNode *element = *iter; + ai_assert(element); + // node valid and node name matches + if (element->mName == node_name) { + found = element; + break; + } + } + + if (found != nullptr) { + ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str()); + // now pop the element from the node list + nodes.erase(iter); + + return found; + } + + // unique names can cause this problem + ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!"); + + return nullptr; +} + + + + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.h b/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.h new file mode 100644 index 0000000000..aa1ad7c80c --- /dev/null +++ b/thirdparty/assimp/code/PostProcessing/ArmaturePopulate.h @@ -0,0 +1,112 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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. + +---------------------------------------------------------------------- +*/ +#ifndef ARMATURE_POPULATE_H_ +#define ARMATURE_POPULATE_H_ + +#include "Common/BaseProcess.h" +#include <assimp/BaseImporter.h> +#include <vector> +#include <map> + + +struct aiNode; +struct aiBone; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Armature Populate: This is a post process designed + * To save you time when importing models into your game engines + * This was originally designed only for fbx but will work with other formats + * it is intended to auto populate aiBone data with armature and the aiNode + * This is very useful when dealing with skinned meshes + * or when dealing with many different skeletons + * It's off by default but recommend that you try it and use it + * It should reduce down any glue code you have in your + * importers + * You can contact RevoluPowered <gordon@gordonite.tech> + * For more info about this +*/ +class ASSIMP_API ArmaturePopulate : public BaseProcess { +public: + /// The default class constructor. + ArmaturePopulate(); + + /// The class destructor. + virtual ~ArmaturePopulate(); + + /// Overwritten, @see BaseProcess + virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess + virtual void Execute( aiScene* pScene ); + + static aiNode *GetArmatureRoot(aiNode *bone_node, + std::vector<aiBone *> &bone_list); + + static bool IsBoneNode(const aiString &bone_name, + std::vector<aiBone *> &bones); + + static aiNode *GetNodeFromStack(const aiString &node_name, + std::vector<aiNode *> &nodes); + + static void BuildNodeList(const aiNode *current_node, + std::vector<aiNode *> &nodes); + + static void BuildBoneList(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + std::vector<aiBone *> &bones); + + static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + const std::vector<aiBone *> &bones, + std::map<aiBone *, aiNode *> &bone_stack, + std::vector<aiNode *> &node_stack); +}; + +} // Namespace Assimp + + +#endif // SCALE_PROCESS_H_
\ No newline at end of file diff --git a/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp index bb571a551b..df4d44337d 100644 --- a/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp +++ b/thirdparty/assimp/code/PostProcessing/ComputeUVMappingProcess.cpp @@ -354,12 +354,12 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& } else if (axis * base_axis_z >= angle_epsilon) { FindMeshCenter(mesh, center, min, max); - diffu = max.y - min.y; - diffv = max.z - min.z; + diffu = max.x - min.x; + diffv = max.y - min.y; for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { const aiVector3D& pos = mesh->mVertices[pnt]; - out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0); + out[pnt].Set((pos.x - min.x) / diffu,(pos.y - min.y) / diffv,0.0); } } // slower code path in case the mapping axis is not one of the coordinate system axes diff --git a/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp index 433f042448..016884c6e7 100644 --- a/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp +++ b/thirdparty/assimp/code/PostProcessing/FindInvalidDataProcess.cpp @@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FindInvalidDataProcess.h" #include "ProcessHelper.h" -#include <assimp/Macros.h> #include <assimp/Exceptional.h> #include <assimp/qnan.h> diff --git a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp index 914ec05b46..f121fc60d3 100644 --- a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp +++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp @@ -431,31 +431,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) bone->mWeights = new aiVertexWeight[bone->mNumWeights]; memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); } - else { - - /* NOTE: - * - * In the algorithm above we're assuming that there are no vertices - * with a different bone weight setup at the same position. That wouldn't - * make sense, but it is not absolutely impossible. SkeletonMeshBuilder - * for example generates such input data if two skeleton points - * share the same position. Again this doesn't make sense but is - * reality for some model formats (MD5 for example uses these special - * nodes as attachment tags for its weapons). - * - * Then it is possible that a bone has no weights anymore .... as a quick - * workaround, we're just removing these bones. If they're animated, - * model geometry might be modified but at least there's no risk of a crash. - */ - delete bone; - --pMesh->mNumBones; - for (unsigned int n = a; n < pMesh->mNumBones; ++n) { - pMesh->mBones[n] = pMesh->mBones[n+1]; - } - - --a; - ASSIMP_LOG_WARN("Removing bone -> no weights remaining"); - } } return pMesh->mNumVertices; } diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp index 50ff5ed93d..41f50a5ba5 100644 --- a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp +++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp @@ -224,3 +224,32 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh) } return (pcMesh->mNumVertices != iOldNumVertices); } + + +// ------------------------------------------------------------------------------------------------ +bool IsMeshInVerboseFormat(const aiMesh* mesh) { + // avoid slow vector<bool> specialization + std::vector<unsigned int> seen(mesh->mNumVertices,0); + for(unsigned int i = 0; i < mesh->mNumFaces; ++i) { + const aiFace& f = mesh->mFaces[i]; + for(unsigned int j = 0; j < f.mNumIndices; ++j) { + if(++seen[f.mIndices[j]] == 2) { + // found a duplicate index + return false; + } + } + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool MakeVerboseFormatProcess::IsVerboseFormat(const aiScene* pScene) { + for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + if(!IsMeshInVerboseFormat(pScene->mMeshes[i])) { + return false; + } + } + + return true; +} diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h index 1adf8e2f69..8565d5933a 100644 --- a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h +++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h @@ -94,6 +94,13 @@ public: * @param pScene The imported data to work at. */ void Execute( aiScene* pScene); +public: + + // ------------------------------------------------------------------- + /** Checks whether the scene is already in verbose format. + * @param pScene The data to check. + * @return true if the scene is already in verbose format. */ + static bool IsVerboseFormat(const aiScene* pScene); private: diff --git a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp index 712fd6943d..75d1b6ef78 100644 --- a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp +++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.cpp @@ -538,13 +538,17 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation) { Validate(&pAnimation->mName); - // validate all materials - if (pAnimation->mNumChannels) + // validate all animations + if (pAnimation->mNumChannels || pAnimation->mNumMorphMeshChannels) { - if (!pAnimation->mChannels) { + if (!pAnimation->mChannels && pAnimation->mNumChannels) { ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", pAnimation->mNumChannels); } + if (!pAnimation->mMorphMeshChannels && pAnimation->mNumMorphMeshChannels) { + ReportError("aiAnimation::mMorphMeshChannels is NULL (aiAnimation::mNumMorphMeshChannels is %i)", + pAnimation->mNumMorphMeshChannels); + } for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) { if (!pAnimation->mChannels[i]) @@ -554,6 +558,15 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation) } Validate(pAnimation, pAnimation->mChannels[i]); } + for (unsigned int i = 0; i < pAnimation->mNumMorphMeshChannels;++i) + { + if (!pAnimation->mMorphMeshChannels[i]) + { + ReportError("aiAnimation::mMorphMeshChannels[%i] is NULL (aiAnimation::mNumMorphMeshChannels is %i)", + i, pAnimation->mNumMorphMeshChannels); + } + Validate(pAnimation, pAnimation->mMorphMeshChannels[i]); + } } else { ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); @@ -903,6 +916,48 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation, } } +void ValidateDSProcess::Validate( const aiAnimation* pAnimation, + const aiMeshMorphAnim* pMeshMorphAnim) +{ + Validate(&pMeshMorphAnim->mName); + + if (!pMeshMorphAnim->mNumKeys) { + ReportError("Empty mesh morph animation channel"); + } + + // otherwise check whether one of the keys exceeds the total duration of the animation + if (pMeshMorphAnim->mNumKeys) + { + if (!pMeshMorphAnim->mKeys) + { + ReportError("aiMeshMorphAnim::mKeys is NULL (aiMeshMorphAnim::mNumKeys is %i)", + pMeshMorphAnim->mNumKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pMeshMorphAnim->mNumKeys;++i) + { + // ScenePreprocessor will compute the duration if still the default value + // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, + // seems to be due the compilers register usage/width. + if (pAnimation->mDuration > 0. && pMeshMorphAnim->mKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pMeshMorphAnim->mKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pMeshMorphAnim->mKeys[i].mTime <= dLast) + { + ReportWarning("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is smaller " + "than aiMeshMorphAnim::mKeys[%i] (which is %.5f)",i, + (float)pMeshMorphAnim->mKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pMeshMorphAnim->mKeys[i].mTime; + } + } +} + // ------------------------------------------------------------------------------------------------ void ValidateDSProcess::Validate( const aiNode* pNode) { @@ -958,7 +1013,7 @@ void ValidateDSProcess::Validate( const aiString* pString) { if (pString->length > MAXLEN) { - ReportError("aiString::length is too large (%lu, maximum is %lu)", + ReportError("aiString::length is too large (%u, maximum is %lu)", pString->length,MAXLEN); } const char* sz = pString->data; diff --git a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h index 0b891ef414..7b309c9251 100644 --- a/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h +++ b/thirdparty/assimp/code/PostProcessing/ValidateDataStructure.h @@ -55,6 +55,7 @@ struct aiBone; struct aiMesh; struct aiAnimation; struct aiNodeAnim; +struct aiMeshMorphAnim; struct aiTexture; struct aiMaterial; struct aiNode; @@ -150,6 +151,13 @@ protected: void Validate( const aiAnimation* pAnimation, const aiNodeAnim* pBoneAnim); + /** Validates a mesh morph animation channel. + * @param pAnimation Input animation. + * @param pMeshMorphAnim Mesh morph animation channel. + * */ + void Validate( const aiAnimation* pAnimation, + const aiMeshMorphAnim* pMeshMorphAnim); + // ------------------------------------------------------------------- /** Validates a node and all of its subnodes * @param Node Input node*/ |