Browse Source

Sequence timing options.
This commit deals with the problem that the keyframe timestamps are not standardized. Seconds, milliseconds and ticks are used depending on the import format. There is no metadata or property that specifies the format used, so the option is exposed to the user as part of the import options gui.

OTHGMars 6 years ago
parent
commit
70bbad8411

+ 4 - 3
Engine/source/ts/assimp/assimpAppNode.cpp

@@ -32,6 +32,7 @@
 #include <assimp/types.h>
 
 aiAnimation* AssimpAppNode::sActiveSequence = NULL;
+F32 AssimpAppNode::sTimeMultiplier = 1.0f;
 
 AssimpAppNode::AssimpAppNode(const struct aiScene* scene, const struct aiNode* node, AssimpAppNode* parent)
 :  mInvertMeshes(false),
@@ -132,7 +133,7 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
             F32 lastT = 0.0;
             for (U32 key = 0; key < nodeAnim->mNumPositionKeys; ++key)
             {
-               F32 curT = (F32)nodeAnim->mPositionKeys[key].mTime;
+               F32 curT = sTimeMultiplier * (F32)nodeAnim->mPositionKeys[key].mTime;
                curPos.set(nodeAnim->mPositionKeys[key].mValue.x, nodeAnim->mPositionKeys[key].mValue.y, nodeAnim->mPositionKeys[key].mValue.z);
                if (curT > t)
                {
@@ -161,7 +162,7 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
             F32 lastT = 0.0;
             for (U32 key = 0; key < nodeAnim->mNumRotationKeys; ++key)
             {
-               F32 curT = (F32)nodeAnim->mRotationKeys[key].mTime;
+               F32 curT = sTimeMultiplier * (F32)nodeAnim->mRotationKeys[key].mTime;
                curRot.set(nodeAnim->mRotationKeys[key].mValue.x, nodeAnim->mRotationKeys[key].mValue.y,
                   nodeAnim->mRotationKeys[key].mValue.z, nodeAnim->mRotationKeys[key].mValue.w);
                if (curT > t)
@@ -190,7 +191,7 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
             F32 lastT = 0.0;
             for (U32 key = 0; key < nodeAnim->mNumScalingKeys; ++key)
             {
-               F32 curT = (F32)nodeAnim->mScalingKeys[key].mTime;
+               F32 curT = sTimeMultiplier * (F32)nodeAnim->mScalingKeys[key].mTime;
                curScale.set(nodeAnim->mScalingKeys[key].mValue.x, nodeAnim->mScalingKeys[key].mValue.y, nodeAnim->mScalingKeys[key].mValue.z);
                if (curT > t)
                {

+ 1 - 0
Engine/source/ts/assimp/assimpAppNode.h

@@ -70,6 +70,7 @@ public:
    }
 
    static aiAnimation* sActiveSequence;
+   static F32 sTimeMultiplier;
 
    //-----------------------------------------------------------------------
    const char *getName() { return mName; }

+ 27 - 9
Engine/source/ts/assimp/assimpAppSequence.cpp

@@ -20,19 +20,19 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
    if (mSequenceName.isEmpty())
       mSequenceName = "ambient";
 
-   // From: http://sir-kimmi.de/assimp/lib_html/data.html#anims
-   // An aiAnimation has a duration. The duration as well as all time stamps are given in ticks.
-   // To get the correct timing, all time stamp thus have to be divided by aiAnimation::mTicksPerSecond.
-   // Beware, though, that certain combinations of file format and exporter don't always store this
-   // information in the exported file. In this case, mTicksPerSecond is set to 0 to indicate the lack of knowledge.
    fps = (mAnim->mTicksPerSecond > 0) ? mAnim->mTicksPerSecond : 30.0f;
 
+   U32 maxKeys = 0;
    F32 maxEndTime = 0;
-   F32 minFrameTime = 1000.0f;
+   F32 minFrameTime = 100000.0f;
    // Detect the frame rate (minimum time between keyframes) and max sequence time
    for (U32 i = 0; i < mAnim->mNumChannels; ++i)
    {
       aiNodeAnim *nodeAnim = mAnim->mChannels[i];
+      maxKeys = getMax(maxKeys, nodeAnim->mNumPositionKeys);
+      maxKeys = getMax(maxKeys, nodeAnim->mNumRotationKeys);
+      maxKeys = getMax(maxKeys, nodeAnim->mNumScalingKeys);
+
       if (nodeAnim->mNumPositionKeys)
          maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys-1].mTime);
       if (nodeAnim->mNumRotationKeys)
@@ -57,9 +57,24 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
       }
    }
 
-   fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps;
-   fps = mClamp(fps, TSShapeLoader::MinFrameRate, TSShapeLoader::MaxFrameRate);
-   seqEnd = maxEndTime;
+   S32 timeFactor = Con::getIntVariable("$Assimp::AnimTiming", 1);
+   S32 fpsRequest = Con::getIntVariable("$Assimp::AnimFPS", 30);
+   if (timeFactor == 0)
+   {  // Timing specified in frames
+      fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
+      maxKeys = getMax(maxKeys, (U32)maxEndTime);  // Keys won't be assigned for every frame.
+      seqEnd = maxKeys / fps;
+      mTimeMultiplier = 1.0f / fps;
+   }
+   else
+   {  // Timing specified in seconds or ms depending on format
+      timeFactor = mClamp(timeFactor, 1, 1000);
+      minFrameTime /= (F32)timeFactor;
+      maxEndTime /= (F32)timeFactor;
+      fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps;
+      seqEnd = maxEndTime;
+      mTimeMultiplier = 1.0f / timeFactor;
+   }
 }
 
 AssimpAppSequence::~AssimpAppSequence()
@@ -69,7 +84,10 @@ AssimpAppSequence::~AssimpAppSequence()
 void AssimpAppSequence::setActive(bool active)
 {
    if (active)
+   {
       AssimpAppNode::sActiveSequence = mAnim;
+      AssimpAppNode::sTimeMultiplier = mTimeMultiplier;
+   }
    else
    {
       if (AssimpAppNode::sActiveSequence == mAnim)

+ 1 - 0
Engine/source/ts/assimp/assimpAppSequence.h

@@ -25,6 +25,7 @@ class AssimpAppSequence : public AppSequence
    String   mSequenceName;
    F32      seqStart;
    F32      seqEnd;
+   F32      mTimeMultiplier; // The factor needed to convert the sequence data timestamp to seconds
 
 public:
 

+ 115 - 1
Templates/BaseGame/game/tools/gui/assimpImport.ed.gui

@@ -393,6 +393,107 @@
          canSaveDynamicFields = "0";
       };
 
+      new GuiTextCtrl() {
+         text = "Animation Timing:";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextRightProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "10 311";
+         Extent = "85 16";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         canSaveDynamicFields = "0";
+      };
+      new GuiPopUpMenuCtrl() {
+         maxPopupHeight = "200";
+         sbUsesNAColor = "0";
+         reverseTextList = "0";
+         bitmapBounds = "16 16";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiPopUpMenuProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "100 310";
+         Extent = "86 18";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         ToolTip = "Select the timing units used in the animation data.";
+         hovertime = "1000";
+         internalName = "animTiming";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextCtrl() {
+         text = "FPS:";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextRightProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "200 311";
+         Extent = "20 16";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextEditCtrl() {
+         historySize = "0";
+         password = "0";
+         tabComplete = "0";
+         sinkAllKeyEvents = "0";
+         passwordMask = "*";
+         text = "2";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextEditProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "225 310";
+         Extent = "26 18";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         ToolTip = "Frames per second for all animations when Animation Timing type is Frames (5 - 60)";
+         hovertime = "1000";
+         internalName = "animFPS";
+         canSaveDynamicFields = "0";
+      };
+
       new GuiTextCtrl() {
          text = "LOD";
          maxLength = "1024";
@@ -665,6 +766,10 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
       $Assimp::FindDegenerates = true;
       $Assimp::FindInvalidData = true;
       $Assimp::JoinIdenticalVertices = true;
+      $Assimp::FlipNormals = false;
+      
+      $Assimp::AnimTiming = 1; // Seconds
+      $Assimp::AnimFPS = 30;   // Framerate when timing is frames.
    }
 
    %this-->upAxis.clear();
@@ -678,9 +783,15 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
    %this-->lodType.add("SingleSize", 1);
    %this-->lodType.add("TrailingNumber", 2);
    %this-->lodType.setSelected($Assimp::lodType);
-
    %this-->singleDetailSize.text = $Assimp::singleDetailSize;
 
+   %this-->animTiming.clear();
+   %this-->animTiming.add("Frames", 0);
+   %this-->animTiming.add("Seconds", 1);
+   %this-->animTiming.add("Milliseconds", 1000);
+   %this-->animTiming.setSelected($Assimp::AnimTiming);
+   %this-->animFPS.text = $Assimp::AnimFPS;
+
    //Triangulate is a default(currently mandatory) behavior
    $Assimp::Triangulate = true;
 
@@ -703,6 +814,9 @@ function AssimpImportDlg::onOK(%this)
    $Assimp::lodType = %this-->lodType.getSelected();
    $Assimp::singleDetailSize = %this-->singleDetailSize.getText();
 
+   $Assimp::AnimTiming = %this-->animTiming.getSelected();
+   $Assimp::AnimFPS = %this-->animFPS.getText();
+
    // Load the shape (always from the DAE)
    $assimp::forceLoad = true;
    eval(%this.cmd);

+ 115 - 1
Templates/Full/game/tools/gui/assimpImport.ed.gui

@@ -393,6 +393,107 @@
          canSaveDynamicFields = "0";
       };
 
+      new GuiTextCtrl() {
+         text = "Animation Timing:";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextRightProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "10 311";
+         Extent = "85 16";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         canSaveDynamicFields = "0";
+      };
+      new GuiPopUpMenuCtrl() {
+         maxPopupHeight = "200";
+         sbUsesNAColor = "0";
+         reverseTextList = "0";
+         bitmapBounds = "16 16";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiPopUpMenuProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "100 310";
+         Extent = "86 18";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         ToolTip = "Select the timing units used in the animation data.";
+         hovertime = "1000";
+         internalName = "animTiming";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextCtrl() {
+         text = "FPS:";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextRightProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "200 311";
+         Extent = "20 16";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextEditCtrl() {
+         historySize = "0";
+         password = "0";
+         tabComplete = "0";
+         sinkAllKeyEvents = "0";
+         passwordMask = "*";
+         text = "2";
+         maxLength = "1024";
+         Margin = "0 0 0 0";
+         Padding = "0 0 0 0";
+         AnchorTop = "1";
+         AnchorBottom = "0";
+         AnchorLeft = "1";
+         AnchorRight = "0";
+         isContainer = "0";
+         Profile = "ToolsGuiTextEditProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         position = "225 310";
+         Extent = "26 18";
+         MinExtent = "8 2";
+         canSave = "1";
+         Visible = "1";
+         tooltipprofile = "ToolsGuiToolTipProfile";
+         ToolTip = "Frames per second for all animations when Animation Timing type is Frames (5 - 60)";
+         hovertime = "1000";
+         internalName = "animFPS";
+         canSaveDynamicFields = "0";
+      };
+
       new GuiTextCtrl() {
          text = "LOD";
          maxLength = "1024";
@@ -665,6 +766,10 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
       $Assimp::FindDegenerates = true;
       $Assimp::FindInvalidData = true;
       $Assimp::JoinIdenticalVertices = true;
+      $Assimp::FlipNormals = false;
+      
+      $Assimp::AnimTiming = 1; // Seconds
+      $Assimp::AnimFPS = 30;   // Framerate when timing is frames.
    }
 
    %this-->upAxis.clear();
@@ -678,9 +783,15 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
    %this-->lodType.add("SingleSize", 1);
    %this-->lodType.add("TrailingNumber", 2);
    %this-->lodType.setSelected($Assimp::lodType);
-
    %this-->singleDetailSize.text = $Assimp::singleDetailSize;
 
+   %this-->animTiming.clear();
+   %this-->animTiming.add("Frames", 0);
+   %this-->animTiming.add("Seconds", 1);
+   %this-->animTiming.add("Milliseconds", 1000);
+   %this-->animTiming.setSelected($Assimp::AnimTiming);
+   %this-->animFPS.text = $Assimp::AnimFPS;
+
    //Triangulate is a default(currently mandatory) behavior
    $Assimp::Triangulate = true;
 
@@ -703,6 +814,9 @@ function AssimpImportDlg::onOK(%this)
    $Assimp::lodType = %this-->lodType.getSelected();
    $Assimp::singleDetailSize = %this-->singleDetailSize.getText();
 
+   $Assimp::AnimTiming = %this-->animTiming.getSelected();
+   $Assimp::AnimFPS = %this-->animFPS.getText();
+
    // Load the shape (always from the DAE)
    $assimp::forceLoad = true;
    eval(%this.cmd);