|
@@ -48,6 +48,7 @@
|
|
|
#include <windows.h>
|
|
#include <windows.h>
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#include <assimp/config.h>
|
|
|
#include <assimp/cimport.h>
|
|
#include <assimp/cimport.h>
|
|
|
#include <assimp/scene.h>
|
|
#include <assimp/scene.h>
|
|
|
#include <assimp/postprocess.h>
|
|
#include <assimp/postprocess.h>
|
|
@@ -72,6 +73,7 @@ struct OutModel
|
|
|
PODVector<aiMesh*> meshes_;
|
|
PODVector<aiMesh*> meshes_;
|
|
|
PODVector<aiNode*> meshNodes_;
|
|
PODVector<aiNode*> meshNodes_;
|
|
|
PODVector<aiNode*> bones_;
|
|
PODVector<aiNode*> bones_;
|
|
|
|
|
+ PODVector<aiNode*> pivotlessBones_;
|
|
|
PODVector<aiAnimation*> animations_;
|
|
PODVector<aiAnimation*> animations_;
|
|
|
PODVector<float> boneRadii_;
|
|
PODVector<float> boneRadii_;
|
|
|
PODVector<BoundingBox> boneHitboxes_;
|
|
PODVector<BoundingBox> boneHitboxes_;
|
|
@@ -89,6 +91,56 @@ struct OutScene
|
|
|
PODVector<unsigned> nodeModelIndices_;
|
|
PODVector<unsigned> nodeModelIndices_;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// fbx transform chain
|
|
|
|
|
+enum TransformationComp
|
|
|
|
|
+{
|
|
|
|
|
+ TransformationComp_Translation = 0,
|
|
|
|
|
+ TransformationComp_RotationOffset,
|
|
|
|
|
+ TransformationComp_RotationPivot,
|
|
|
|
|
+ TransformationComp_PreRotation,
|
|
|
|
|
+ TransformationComp_Rotation,
|
|
|
|
|
+ TransformationComp_PostRotation,
|
|
|
|
|
+ TransformationComp_RotationPivotInverse,
|
|
|
|
|
+
|
|
|
|
|
+ TransformationComp_ScalingOffset,
|
|
|
|
|
+ TransformationComp_ScalingPivot,
|
|
|
|
|
+ TransformationComp_Scaling,
|
|
|
|
|
+
|
|
|
|
|
+ //*** not checking these
|
|
|
|
|
+ // they are typically flushed out in the fbxconverter, but there
|
|
|
|
|
+ // might be cases where they're not, hence, leaving them.
|
|
|
|
|
+ #ifdef EXT_TRANSFORMATION_CHECK
|
|
|
|
|
+ TransformationComp_ScalingPivotInverse,
|
|
|
|
|
+ TransformationComp_GeometricTranslation,
|
|
|
|
|
+ TransformationComp_GeometricRotation,
|
|
|
|
|
+ TransformationComp_GeometricScaling,
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ TransformationComp_MAXIMUM
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const char *transformSuffix[TransformationComp_MAXIMUM] =
|
|
|
|
|
+{
|
|
|
|
|
+ "Translation", // TransformationComp_Translation = 0,
|
|
|
|
|
+ "RotationOffset", // TransformationComp_RotationOffset,
|
|
|
|
|
+ "RotationPivot", // TransformationComp_RotationPivot,
|
|
|
|
|
+ "PreRotation", // TransformationComp_PreRotation,
|
|
|
|
|
+ "Rotation", // TransformationComp_Rotation,
|
|
|
|
|
+ "PostRotation", // TransformationComp_PostRotation,
|
|
|
|
|
+ "RotationPivotInverse", // TransformationComp_RotationPivotInverse,
|
|
|
|
|
+
|
|
|
|
|
+ "ScalingOffset", // TransformationComp_ScalingOffset,
|
|
|
|
|
+ "ScalingPivot", // TransformationComp_ScalingPivot,
|
|
|
|
|
+ "Scaling", // TransformationComp_Scaling,
|
|
|
|
|
+
|
|
|
|
|
+ #ifdef EXT_TRANSFORMATION_CHECK
|
|
|
|
|
+ "ScalingPivotInverse", // TransformationComp_ScalingPivotInverse,
|
|
|
|
|
+ "GeometricTranslation", // TransformationComp_GeometricTranslation,
|
|
|
|
|
+ "GeometricRotation", // TransformationComp_GeometricRotation,
|
|
|
|
|
+ "GeometricScaling", // TransformationComp_GeometricScaling,
|
|
|
|
|
+ #endif
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
static const unsigned MAX_CHANNELS = 4;
|
|
static const unsigned MAX_CHANNELS = 4;
|
|
|
|
|
|
|
|
SharedPtr<Context> context_(new Context());
|
|
SharedPtr<Context> context_(new Context());
|
|
@@ -129,6 +181,7 @@ float defaultTicksPerSecond_ = 4800.0f;
|
|
|
// For subset animation import usage
|
|
// For subset animation import usage
|
|
|
float importStartTime_ = 0.0f;
|
|
float importStartTime_ = 0.0f;
|
|
|
float importEndTime_ = 0.0f;
|
|
float importEndTime_ = 0.0f;
|
|
|
|
|
+bool suppressFbxPivotNodes_ = true;
|
|
|
|
|
|
|
|
int main(int argc, char** argv);
|
|
int main(int argc, char** argv);
|
|
|
void Run(const Vector<String>& arguments);
|
|
void Run(const Vector<String>& arguments);
|
|
@@ -191,6 +244,9 @@ Matrix3x4 ToMatrix3x4(const aiMatrix4x4& mat);
|
|
|
aiMatrix4x4 ToAIMatrix4x4(const Matrix3x4& mat);
|
|
aiMatrix4x4 ToAIMatrix4x4(const Matrix3x4& mat);
|
|
|
String SanitateAssetName(const String& name);
|
|
String SanitateAssetName(const String& name);
|
|
|
|
|
|
|
|
|
|
+unsigned GetPivotlessBoneIndex(OutModel& model, const String& boneName);
|
|
|
|
|
+void ExtrapolatePivotlessAnimation(OutModel* model);
|
|
|
|
|
+
|
|
|
int main(int argc, char** argv)
|
|
int main(int argc, char** argv)
|
|
|
{
|
|
{
|
|
|
Vector<String> arguments;
|
|
Vector<String> arguments;
|
|
@@ -255,6 +311,7 @@ void Run(const Vector<String>& arguments)
|
|
|
"-bp Move bones to bind pose before saving model\n"
|
|
"-bp Move bones to bind pose before saving model\n"
|
|
|
"-split <start> <end> (animation model only)\n"
|
|
"-split <start> <end> (animation model only)\n"
|
|
|
" Split animation, will only import from start frame to end frame\n"
|
|
" Split animation, will only import from start frame to end frame\n"
|
|
|
|
|
+ "-np Do not suppress $fbx pivot nodes (fbx files only)\n"
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -347,6 +404,11 @@ void Run(const Vector<String>& arguments)
|
|
|
case 'f':
|
|
case 'f':
|
|
|
flags &= ~aiProcess_FixInfacingNormals;
|
|
flags &= ~aiProcess_FixInfacingNormals;
|
|
|
break;
|
|
break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'p':
|
|
|
|
|
+ suppressFbxPivotNodes_ = false;
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else if (argument == "mb" && !value.Empty())
|
|
else if (argument == "mb" && !value.Empty())
|
|
@@ -445,7 +507,32 @@ void Run(const Vector<String>& arguments)
|
|
|
Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE, aiDefaultLogStream_STDOUT);
|
|
Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE, aiDefaultLogStream_STDOUT);
|
|
|
|
|
|
|
|
PrintLine("Reading file " + inFile);
|
|
PrintLine("Reading file " + inFile);
|
|
|
- scene_ = aiImportFile(GetNativePath(inFile).CString(), flags);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (suppressFbxPivotNodes_)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!inFile.EndsWith(".fbx", false))
|
|
|
|
|
+ suppressFbxPivotNodes_ = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // only do this for the "model" command. "anim" command extrapolates animation from the original bone definition
|
|
|
|
|
+ if (suppressFbxPivotNodes_ && command == "model")
|
|
|
|
|
+ {
|
|
|
|
|
+ PrintLine("Suppressing $fbx nodes");
|
|
|
|
|
+ aiPropertyStore *aiprops = aiCreatePropertyStore();
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, 1); //default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, 0); //default = false;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_MATERIALS, 1); //default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_CAMERAS, 1); //default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_LIGHTS, 1); //default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, 1); //default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_STRICT_MODE, 0); //default = false;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, 0); //**false, default = true;
|
|
|
|
|
+ aiSetImportPropertyInteger(aiprops, AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, 1);//default = true;
|
|
|
|
|
+
|
|
|
|
|
+ scene_ = aiImportFileExWithProperties(GetNativePath(inFile).CString(), flags, NULL, aiprops);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ scene_ = aiImportFile(GetNativePath(inFile).CString(), flags);
|
|
|
if (!scene_)
|
|
if (!scene_)
|
|
|
ErrorExit("Could not open or parse input file " + inFile + ": " + String(aiGetErrorString()));
|
|
ErrorExit("Could not open or parse input file " + inFile + ": " + String(aiGetErrorString()));
|
|
|
|
|
|
|
@@ -1151,6 +1238,10 @@ void BuildAndSaveModel(OutModel& model)
|
|
|
|
|
|
|
|
void BuildAndSaveAnimations(OutModel* model)
|
|
void BuildAndSaveAnimations(OutModel* model)
|
|
|
{
|
|
{
|
|
|
|
|
+ // extrapolate anim
|
|
|
|
|
+ ExtrapolatePivotlessAnimation(model);
|
|
|
|
|
+
|
|
|
|
|
+ // build and save anim
|
|
|
const PODVector<aiAnimation*>& animations = model ? model->animations_ : sceneAnimations_;
|
|
const PODVector<aiAnimation*>& animations = model ? model->animations_ : sceneAnimations_;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < animations.Size(); ++i)
|
|
for (unsigned i = 0; i < animations.Size(); ++i)
|
|
@@ -1209,14 +1300,32 @@ void BuildAndSaveAnimations(OutModel* model)
|
|
|
|
|
|
|
|
if (model)
|
|
if (model)
|
|
|
{
|
|
{
|
|
|
- unsigned boneIndex = GetBoneIndex(*model, channelName);
|
|
|
|
|
- if (boneIndex == M_MAX_UNSIGNED)
|
|
|
|
|
|
|
+ unsigned boneIndex;
|
|
|
|
|
+ unsigned pos = channelName.Find("_$AssimpFbx$");
|
|
|
|
|
+
|
|
|
|
|
+ if (!suppressFbxPivotNodes_ || pos == String::NPOS)
|
|
|
{
|
|
{
|
|
|
- PrintLine("Warning: skipping animation track " + channelName + " not found in model skeleton");
|
|
|
|
|
- outAnim->RemoveTrack(channelName);
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ boneIndex = GetBoneIndex(*model, channelName);
|
|
|
|
|
+ if (boneIndex == M_MAX_UNSIGNED)
|
|
|
|
|
+ {
|
|
|
|
|
+ PrintLine("Warning: skipping animation track " + channelName + " not found in model skeleton");
|
|
|
|
|
+ outAnim->RemoveTrack(channelName);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ boneNode = model->bones_[boneIndex];
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ channelName = channelName.Substring(0, pos);
|
|
|
|
|
+
|
|
|
|
|
+ // every first $fbx animation channel for a bone will consolidate other $fbx animation to a single channel
|
|
|
|
|
+ // skip subsequent $fbx animation channel for the same bone
|
|
|
|
|
+ if (outAnim->GetTrack(channelName) != NULL)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ boneIndex = GetPivotlessBoneIndex(*model, channelName);
|
|
|
|
|
+ boneNode = model->pivotlessBones_[boneIndex];
|
|
|
}
|
|
}
|
|
|
- boneNode = model->bones_[boneIndex];
|
|
|
|
|
isRootBone = boneIndex == 0;
|
|
isRootBone = boneIndex == 0;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
@@ -2500,3 +2609,299 @@ String SanitateAssetName(const String& name)
|
|
|
|
|
|
|
|
return fixedName;
|
|
return fixedName;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+unsigned GetPivotlessBoneIndex(OutModel& model, const String& boneName)
|
|
|
|
|
+{
|
|
|
|
|
+ for (unsigned i = 0; i < model.pivotlessBones_.Size(); ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (boneName == model.pivotlessBones_[i]->mName.data)
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ return M_MAX_UNSIGNED;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void FillChainTransforms(OutModel &model, aiMatrix4x4 *chain, const String& mainBoneName)
|
|
|
|
|
+{
|
|
|
|
|
+ for (unsigned j = 0; j < TransformationComp_MAXIMUM; ++j)
|
|
|
|
|
+ {
|
|
|
|
|
+ String transfBoneName = mainBoneName + "_$AssimpFbx$_" + String(transformSuffix[j]);
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned k = 0; k < model.bones_.Size(); ++k)
|
|
|
|
|
+ {
|
|
|
|
|
+ String boneName = String(model.bones_[k]->mName.data);
|
|
|
|
|
+
|
|
|
|
|
+ if (boneName == transfBoneName)
|
|
|
|
|
+ {
|
|
|
|
|
+ chain[j] = model.bones_[k]->mTransformation;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int InitAnimatedChainTransfromIndeces(aiAnimation* anim, const String& mainBoneName, int *channelIndeces)
|
|
|
|
|
+{
|
|
|
|
|
+ int numTransforms = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned j = 0; j < TransformationComp_MAXIMUM; ++j)
|
|
|
|
|
+ {
|
|
|
|
|
+ String transfBoneName = mainBoneName + "_$AssimpFbx$_" + String(transformSuffix[j]);
|
|
|
|
|
+ channelIndeces[j] = -1;
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned k = 0; k < anim->mNumChannels; ++k)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiNodeAnim* channel = anim->mChannels[k];
|
|
|
|
|
+ String channelName = FromAIString(channel->mNodeName);
|
|
|
|
|
+
|
|
|
|
|
+ if (channelName == transfBoneName)
|
|
|
|
|
+ {
|
|
|
|
|
+ numTransforms++;
|
|
|
|
|
+ channelIndeces[j] = k;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return numTransforms;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void ExpandAnimatedChannelKeys(aiAnimation* anim, unsigned mainChannel, int *channelIndeces)
|
|
|
|
|
+{
|
|
|
|
|
+ aiNodeAnim* channel = anim->mChannels[mainChannel];
|
|
|
|
|
+ unsigned int poskeyFrames = channel->mNumPositionKeys;
|
|
|
|
|
+ unsigned int rotkeyFrames = channel->mNumRotationKeys;
|
|
|
|
|
+ unsigned int scalekeyFrames = channel->mNumScalingKeys;
|
|
|
|
|
+
|
|
|
|
|
+ // get max key frames
|
|
|
|
|
+ for (unsigned i = 0; i < TransformationComp_MAXIMUM; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (channelIndeces[i] != -1 && i != mainChannel)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiNodeAnim* channel2 = anim->mChannels[i];
|
|
|
|
|
+
|
|
|
|
|
+ if (channel2->mNumPositionKeys > poskeyFrames)
|
|
|
|
|
+ poskeyFrames = channel2->mNumPositionKeys;
|
|
|
|
|
+ if (channel2->mNumRotationKeys > rotkeyFrames)
|
|
|
|
|
+ rotkeyFrames = channel2->mNumRotationKeys;
|
|
|
|
|
+ if (channel2->mNumScalingKeys > scalekeyFrames)
|
|
|
|
|
+ scalekeyFrames = channel2->mNumScalingKeys;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // resize and init vector key array
|
|
|
|
|
+ if (poskeyFrames > channel->mNumPositionKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiVectorKey* newKeys = new aiVectorKey[poskeyFrames];
|
|
|
|
|
+ for (unsigned i = 0; i < poskeyFrames; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (i < channel->mNumPositionKeys )
|
|
|
|
|
+ newKeys[i] = aiVectorKey(channel->mPositionKeys[i].mTime, channel->mPositionKeys[i].mValue);
|
|
|
|
|
+ else
|
|
|
|
|
+ newKeys[i].mValue = aiVector3D(0.0f,0.0f,0.0f);
|
|
|
|
|
+ }
|
|
|
|
|
+ delete [] channel->mPositionKeys;
|
|
|
|
|
+ channel->mPositionKeys = newKeys;
|
|
|
|
|
+ channel->mNumPositionKeys = poskeyFrames;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (rotkeyFrames > channel->mNumRotationKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiQuatKey* newKeys = new aiQuatKey[rotkeyFrames];
|
|
|
|
|
+ for (unsigned i = 0; i < rotkeyFrames; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (i < channel->mNumRotationKeys)
|
|
|
|
|
+ newKeys[i] = aiQuatKey(channel->mRotationKeys[i].mTime, channel->mRotationKeys[i].mValue);
|
|
|
|
|
+ else
|
|
|
|
|
+ newKeys[i].mValue = aiQuaternion();
|
|
|
|
|
+ }
|
|
|
|
|
+ delete [] channel->mRotationKeys;
|
|
|
|
|
+ channel->mRotationKeys = newKeys;
|
|
|
|
|
+ channel->mNumRotationKeys = rotkeyFrames;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (scalekeyFrames > channel->mNumScalingKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiVectorKey* newKeys = new aiVectorKey[scalekeyFrames];
|
|
|
|
|
+ for (unsigned i = 0; i < scalekeyFrames; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ if ( i < channel->mNumScalingKeys)
|
|
|
|
|
+ newKeys[i] = aiVectorKey(channel->mPositionKeys[i].mTime, channel->mPositionKeys[i].mValue);
|
|
|
|
|
+ else
|
|
|
|
|
+ newKeys[i].mValue = aiVector3D(1.0f,1.0f,1.0f);
|
|
|
|
|
+ }
|
|
|
|
|
+ delete [] channel->mScalingKeys;
|
|
|
|
|
+ channel->mScalingKeys = newKeys;
|
|
|
|
|
+ channel->mNumScalingKeys = scalekeyFrames;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void CreatePivotlessFbxBoneStruct(OutModel &model)
|
|
|
|
|
+{
|
|
|
|
|
+ // init
|
|
|
|
|
+ model.pivotlessBones_.Clear();
|
|
|
|
|
+ aiMatrix4x4 chain[TransformationComp_MAXIMUM];
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned i = 0; i < model.bones_.Size(); ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ String mainBoneName = String(model.bones_[i]->mName.data);
|
|
|
|
|
+
|
|
|
|
|
+ // skip $fbx nodes
|
|
|
|
|
+ if (mainBoneName.Find("$AssimpFbx$") != String::NPOS)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
|
|
|
|
|
+ FillChainTransforms(model, &chain[0], mainBoneName);
|
|
|
|
|
+
|
|
|
|
|
+ // calculate chained transform
|
|
|
|
|
+ aiMatrix4x4 finalTransform;
|
|
|
|
|
+ for (unsigned j = 0; j < TransformationComp_MAXIMUM; ++j)
|
|
|
|
|
+ finalTransform = finalTransform * chain[j];
|
|
|
|
|
+
|
|
|
|
|
+ // new bone node
|
|
|
|
|
+ aiNode *pnode = new aiNode;
|
|
|
|
|
+ pnode->mName = model.bones_[i]->mName;
|
|
|
|
|
+ pnode->mTransformation = finalTransform * model.bones_[i]->mTransformation;
|
|
|
|
|
+
|
|
|
|
|
+ model.pivotlessBones_.Push(pnode);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void ExtrapolatePivotlessAnimation(OutModel* model)
|
|
|
|
|
+{
|
|
|
|
|
+ if (suppressFbxPivotNodes_ && model)
|
|
|
|
|
+ {
|
|
|
|
|
+ PrintLine("Suppressing $fbx nodes");
|
|
|
|
|
+
|
|
|
|
|
+ // construct new bone structure from suppressed $fbx pivot nodes
|
|
|
|
|
+ CreatePivotlessFbxBoneStruct(*model);
|
|
|
|
|
+
|
|
|
|
|
+ // extrapolate anim
|
|
|
|
|
+ const PODVector<aiAnimation *> &animations = model->animations_;
|
|
|
|
|
+
|
|
|
|
|
+ #ifdef DBG_DUMP_ANIMFBXNODES
|
|
|
|
|
+ for (unsigned i = 0; i < animations.Size(); ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiAnimation* anim = animations[i];
|
|
|
|
|
+
|
|
|
|
|
+ PrintLine("Animation num channels=" + String(anim->mNumChannels));
|
|
|
|
|
+ for (unsigned j = 0; j < anim->mNumChannels; ++j)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiNodeAnim* channel = anim->mChannels[j];
|
|
|
|
|
+ String channelName = FromAIString(channel->mNodeName);
|
|
|
|
|
+ PrintLine(String(j) + String(" : ") + channelName);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned i = 0; i < animations.Size(); ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiAnimation* anim = animations[i];
|
|
|
|
|
+ Vector<String> mainBoneCompleteList;
|
|
|
|
|
+ mainBoneCompleteList.Clear();
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned j = 0; j < anim->mNumChannels; ++j)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiNodeAnim* channel = anim->mChannels[j];
|
|
|
|
|
+ String channelName = FromAIString(channel->mNodeName);
|
|
|
|
|
+ unsigned pos = channelName.Find("_$AssimpFbx$");
|
|
|
|
|
+
|
|
|
|
|
+ if (pos != String::NPOS)
|
|
|
|
|
+ {
|
|
|
|
|
+ // every first $fbx animation channel for a bone will consolidate other $fbx animation to a single channel
|
|
|
|
|
+ // skip subsequent $fbx animation channel for the same bone
|
|
|
|
|
+ String mainBoneName = channelName.Substring(0, pos);
|
|
|
|
|
+
|
|
|
|
|
+ if (mainBoneCompleteList.Find(mainBoneName) != mainBoneCompleteList.End())
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ mainBoneCompleteList.Push(mainBoneName);
|
|
|
|
|
+
|
|
|
|
|
+ // init chain and chain transfer indeces
|
|
|
|
|
+ unsigned boneIdx = GetBoneIndex(*model, mainBoneName);
|
|
|
|
|
+ aiMatrix4x4 mainboneTransform = model->bones_[boneIdx]->mTransformation;
|
|
|
|
|
+ aiMatrix4x4 chain[TransformationComp_MAXIMUM];
|
|
|
|
|
+ int channelIndeces[TransformationComp_MAXIMUM];
|
|
|
|
|
+ int numTransfIndeces = InitAnimatedChainTransfromIndeces(anim, mainBoneName, &channelIndeces[0]);
|
|
|
|
|
+
|
|
|
|
|
+ // expand key arrays
|
|
|
|
|
+ if (numTransfIndeces > 1)
|
|
|
|
|
+ ExpandAnimatedChannelKeys(anim, j, &channelIndeces[0]);
|
|
|
|
|
+
|
|
|
|
|
+ // fill chain transforms
|
|
|
|
|
+ std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
|
|
|
|
|
+ FillChainTransforms(*model, &chain[0], mainBoneName);
|
|
|
|
|
+
|
|
|
|
|
+ unsigned keyFrames = channel->mNumPositionKeys;
|
|
|
|
|
+ if (channel->mNumRotationKeys > keyFrames)
|
|
|
|
|
+ keyFrames = channel->mNumRotationKeys;
|
|
|
|
|
+ if (channel->mNumScalingKeys > keyFrames)
|
|
|
|
|
+ keyFrames = channel->mNumScalingKeys;
|
|
|
|
|
+
|
|
|
|
|
+ for (unsigned k = 0; k < keyFrames; ++k)
|
|
|
|
|
+ {
|
|
|
|
|
+ double frameTime = 0.0;
|
|
|
|
|
+ aiMatrix4x4 finalTransform;
|
|
|
|
|
+
|
|
|
|
|
+ // chain transform animated values
|
|
|
|
|
+ for (unsigned l = 0; l < TransformationComp_MAXIMUM; ++l)
|
|
|
|
|
+ {
|
|
|
|
|
+ // it's either chain transform or replaced by channel animation
|
|
|
|
|
+ if (channelIndeces[l] != -1)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiMatrix4x4 animtform;
|
|
|
|
|
+ aiMatrix4x4 transMat, scaleMat, rotMat;
|
|
|
|
|
+ aiNodeAnim* animchannel = anim->mChannels[channelIndeces[l]];
|
|
|
|
|
+
|
|
|
|
|
+ if (k < animchannel->mNumPositionKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiMatrix4x4::Translation(animchannel->mPositionKeys[k].mValue, transMat);
|
|
|
|
|
+ animtform = animtform * transMat;
|
|
|
|
|
+ frameTime = max(animchannel->mPositionKeys[k].mTime, frameTime);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (k < animchannel->mNumRotationKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ rotMat = aiMatrix4x4(animchannel->mRotationKeys[k].mValue.GetMatrix());
|
|
|
|
|
+ animtform = animtform * rotMat;
|
|
|
|
|
+ frameTime = max(animchannel->mRotationKeys[k].mTime, frameTime);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (k < animchannel->mNumScalingKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiMatrix4x4::Scaling(animchannel->mScalingKeys[k].mValue, scaleMat);
|
|
|
|
|
+ animtform = animtform * scaleMat;
|
|
|
|
|
+ frameTime = max(animchannel->mScalingKeys[k].mTime, frameTime);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ finalTransform = finalTransform * animtform;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ finalTransform = finalTransform * chain[l];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ aiVector3D animPos, animScale;
|
|
|
|
|
+ aiQuaternion animRot;
|
|
|
|
|
+ finalTransform = finalTransform * mainboneTransform;
|
|
|
|
|
+ finalTransform.Decompose(animScale, animRot, animPos);
|
|
|
|
|
+
|
|
|
|
|
+ // new values
|
|
|
|
|
+ if (k < channel->mNumPositionKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ channel->mPositionKeys[k].mValue = animPos;
|
|
|
|
|
+ channel->mPositionKeys[k].mTime = frameTime;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (k < channel->mNumRotationKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ channel->mRotationKeys[k].mValue = animRot;
|
|
|
|
|
+ channel->mRotationKeys[k].mTime = frameTime;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (k < channel->mNumScalingKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ channel->mScalingKeys[k].mValue = animScale;
|
|
|
|
|
+ channel->mScalingKeys[k].mTime = frameTime;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|