Browse Source

added assetimporter feature to suppress $fbx nodes

Lumak 9 years ago
parent
commit
dbd6465169
1 changed files with 412 additions and 7 deletions
  1. 412 7
      Source/Tools/AssetImporter/AssetImporter.cpp

+ 412 - 7
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -48,6 +48,7 @@
 #include <windows.h>
 #endif
 
+#include <assimp/config.h>
 #include <assimp/cimport.h>
 #include <assimp/scene.h>
 #include <assimp/postprocess.h>
@@ -72,6 +73,7 @@ struct OutModel
     PODVector<aiMesh*> meshes_;
     PODVector<aiNode*> meshNodes_;
     PODVector<aiNode*> bones_;
+    PODVector<aiNode*> pivotlessBones_;
     PODVector<aiAnimation*> animations_;
     PODVector<float> boneRadii_;
     PODVector<BoundingBox> boneHitboxes_;
@@ -89,6 +91,56 @@ struct OutScene
     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;
 
 SharedPtr<Context> context_(new Context());
@@ -129,6 +181,7 @@ float defaultTicksPerSecond_ = 4800.0f;
 // For subset animation import usage
 float importStartTime_ = 0.0f;
 float importEndTime_ = 0.0f;
+bool suppressFbxPivotNodes_ = true;
 
 int main(int argc, char** argv);
 void Run(const Vector<String>& arguments);
@@ -191,6 +244,9 @@ Matrix3x4 ToMatrix3x4(const aiMatrix4x4& mat);
 aiMatrix4x4 ToAIMatrix4x4(const Matrix3x4& mat);
 String SanitateAssetName(const String& name);
 
+unsigned GetPivotlessBoneIndex(OutModel& model, const String& boneName);
+void ExtrapolatePivotlessAnimation(OutModel* model);
+
 int main(int argc, char** argv)
 {
     Vector<String> arguments;
@@ -255,6 +311,7 @@ void Run(const Vector<String>& arguments)
             "-bp         Move bones to bind pose before saving model\n"
             "-split <start> <end> (animation model only)\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':
                     flags &= ~aiProcess_FixInfacingNormals;
                     break;
+
+                case 'p':
+                        suppressFbxPivotNodes_ = false;
+                    break;
+
                 }
             }
             else if (argument == "mb" && !value.Empty())
@@ -445,7 +507,32 @@ void Run(const Vector<String>& arguments)
             Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE, aiDefaultLogStream_STDOUT);
 
         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_)
             ErrorExit("Could not open or parse input file " + inFile + ": " + String(aiGetErrorString()));
 
@@ -1151,6 +1238,10 @@ void BuildAndSaveModel(OutModel& model)
 
 void BuildAndSaveAnimations(OutModel* model)
 {
+    // extrapolate anim
+    ExtrapolatePivotlessAnimation(model);
+
+    // build and save anim
     const PODVector<aiAnimation*>& animations = model ? model->animations_ : sceneAnimations_;
 
     for (unsigned i = 0; i < animations.Size(); ++i)
@@ -1209,14 +1300,32 @@ void BuildAndSaveAnimations(OutModel* 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;
             }
             else
@@ -2500,3 +2609,299 @@ String SanitateAssetName(const String& name)
 
     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;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+