diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index 686c5bfa2..937659a36 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -32,6 +32,7 @@ #include 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) { diff --git a/Engine/source/ts/assimp/assimpAppNode.h b/Engine/source/ts/assimp/assimpAppNode.h index 3fe04d43e..947cb894b 100644 --- a/Engine/source/ts/assimp/assimpAppNode.h +++ b/Engine/source/ts/assimp/assimpAppNode.h @@ -70,6 +70,7 @@ public: } static aiAnimation* sActiveSequence; + static F32 sTimeMultiplier; //----------------------------------------------------------------------- const char *getName() { return mName; } diff --git a/Engine/source/ts/assimp/assimpAppSequence.cpp b/Engine/source/ts/assimp/assimpAppSequence.cpp index 4b903d756..33d0cf9fb 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.cpp +++ b/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) diff --git a/Engine/source/ts/assimp/assimpAppSequence.h b/Engine/source/ts/assimp/assimpAppSequence.h index ee7d9c0ac..be5c11025 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.h +++ b/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: diff --git a/Templates/BaseGame/game/tools/gui/assimpImport.ed.gui b/Templates/BaseGame/game/tools/gui/assimpImport.ed.gui index 981c1f827..22794f3ad 100644 --- a/Templates/BaseGame/game/tools/gui/assimpImport.ed.gui +++ b/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); diff --git a/Templates/Full/game/tools/gui/assimpImport.ed.gui b/Templates/Full/game/tools/gui/assimpImport.ed.gui index 981c1f827..22794f3ad 100644 --- a/Templates/Full/game/tools/gui/assimpImport.ed.gui +++ b/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);