Fix last frame importing from animations to correctly loop
This commit is contained in:
@@ -160,7 +160,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
{
|
{
|
||||||
if (_preview != null)
|
if (_preview != null)
|
||||||
{
|
{
|
||||||
frame = Mathf.Clamp(frame, 0, DurationFrames - 1);
|
frame = Mathf.Clamp(frame, 0, DurationFrames);
|
||||||
var time = frame / FramesPerSecond;
|
var time = frame / FramesPerSecond;
|
||||||
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
|
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
|
||||||
if (!_preview.PlayAnimation)
|
if (!_preview.PlayAnimation)
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ float GetAnimSamplePos(float length, Animation* anim, float pos, float speed)
|
|||||||
return animPos;
|
return animPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE void GetAnimSamplePos(bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, float& pos, float& prevPos)
|
FORCE_INLINE void GetAnimPos(bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, float& pos, float& prevPos)
|
||||||
{
|
{
|
||||||
// Calculate actual time position within the animation node (defined by length and loop mode)
|
// Calculate actual time position within the animation node (defined by length and loop mode)
|
||||||
pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
||||||
@@ -407,7 +407,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
const float frameRateMatchScale = (float)(nestedAnimSpeed / anim->Data.FramesPerSecond);
|
const float frameRateMatchScale = (float)(nestedAnimSpeed / anim->Data.FramesPerSecond);
|
||||||
nestedAnimPos = nestedAnimPos * frameRateMatchScale;
|
nestedAnimPos = nestedAnimPos * frameRateMatchScale;
|
||||||
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
|
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
|
||||||
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
GetAnimPos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
||||||
|
|
||||||
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode, usedNodes);
|
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode, usedNodes);
|
||||||
}
|
}
|
||||||
@@ -590,7 +590,7 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float
|
|||||||
return Value::Null;
|
return Value::Null;
|
||||||
|
|
||||||
float pos, prevPos;
|
float pos, prevPos;
|
||||||
GetAnimSamplePos(loop, length, startTimePos, prevTimePos, newTimePos, pos, prevPos);
|
GetAnimPos(loop, length, startTimePos, prevTimePos, newTimePos, pos, prevPos);
|
||||||
|
|
||||||
const auto nodes = node->GetNodes(this);
|
const auto nodes = node->GetNodes(this);
|
||||||
InitNodes(nodes);
|
InitNodes(nodes);
|
||||||
@@ -615,7 +615,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
|||||||
return Value::Null;
|
return Value::Null;
|
||||||
|
|
||||||
float pos, prevPos;
|
float pos, prevPos;
|
||||||
GetAnimSamplePos(loop, length, startTimePos, prevTimePos, newTimePos, pos, prevPos);
|
GetAnimPos(loop, length, startTimePos, prevTimePos, newTimePos, pos, prevPos);
|
||||||
|
|
||||||
// Sample the animations with blending
|
// Sample the animations with blending
|
||||||
const auto nodes = node->GetNodes(this);
|
const auto nodes = node->GetNodes(this);
|
||||||
@@ -638,8 +638,8 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
|||||||
|
|
||||||
// Get actual animation position (includes looping and start offset)
|
// Get actual animation position (includes looping and start offset)
|
||||||
float posA, prevPosA, posB, prevPosB;
|
float posA, prevPosA, posB, prevPosB;
|
||||||
GetAnimSamplePos(loop, a.Length, startTimePos, a.PrevTimePos, a.TimePos, posA, prevPosA);
|
GetAnimPos(loop, a.Length, startTimePos, a.PrevTimePos, a.TimePos, posA, prevPosA);
|
||||||
GetAnimSamplePos(loop, b.Length, startTimePos, b.PrevTimePos, b.TimePos, posB, prevPosB);
|
GetAnimPos(loop, b.Length, startTimePos, b.PrevTimePos, b.TimePos, posB, prevPosB);
|
||||||
|
|
||||||
// Sample the animations with blending
|
// Sample the animations with blending
|
||||||
const auto nodes = node->GetNodes(this);
|
const auto nodes = node->GetNodes(this);
|
||||||
@@ -663,9 +663,9 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
|||||||
|
|
||||||
// Get actual animation position (includes looping and start offset)
|
// Get actual animation position (includes looping and start offset)
|
||||||
float posA, prevPosA, posB, prevPosB, posC, prevPosC;
|
float posA, prevPosA, posB, prevPosB, posC, prevPosC;
|
||||||
GetAnimSamplePos(loop, a.Length, startTimePos, a.PrevTimePos, a.TimePos, posA, prevPosA);
|
GetAnimPos(loop, a.Length, startTimePos, a.PrevTimePos, a.TimePos, posA, prevPosA);
|
||||||
GetAnimSamplePos(loop, b.Length, startTimePos, b.PrevTimePos, b.TimePos, posB, prevPosB);
|
GetAnimPos(loop, b.Length, startTimePos, b.PrevTimePos, b.TimePos, posB, prevPosB);
|
||||||
GetAnimSamplePos(loop, c.Length, startTimePos, c.PrevTimePos, c.TimePos, posC, prevPosC);
|
GetAnimPos(loop, c.Length, startTimePos, c.PrevTimePos, c.TimePos, posC, prevPosC);
|
||||||
|
|
||||||
// Sample the animations with blending
|
// Sample the animations with blending
|
||||||
const auto nodes = node->GetNodes(this);
|
const auto nodes = node->GetNodes(this);
|
||||||
|
|||||||
@@ -565,6 +565,14 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interpolates between two quaternions, using spherical linear interpolation.
|
||||||
|
static Quaternion Slerp(const Quaternion& start, const Quaternion& end, float amount)
|
||||||
|
{
|
||||||
|
Quaternion result;
|
||||||
|
Slerp(start, end, amount, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Interpolates between two quaternions, using spherical linear interpolation.
|
// Interpolates between two quaternions, using spherical linear interpolation.
|
||||||
static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
|
static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "Engine/Platform/File.h"
|
#include "Engine/Platform/File.h"
|
||||||
|
|
||||||
#define OPEN_FBX_CONVERT_SPACE 1
|
#define OPEN_FBX_CONVERT_SPACE 1
|
||||||
|
#define OPEN_FBX_NAME_SIZE 256
|
||||||
#if BUILD_DEBUG
|
#if BUILD_DEBUG
|
||||||
#define OPEN_FBX_GET_CACHE_LIST(arrayName, varName, size) data.arrayName.Resize(size, false); auto& varName = data.arrayName
|
#define OPEN_FBX_GET_CACHE_LIST(arrayName, varName, size) data.arrayName.Resize(size, false); auto& varName = data.arrayName
|
||||||
#else
|
#else
|
||||||
@@ -204,7 +205,7 @@ struct OpenFbxImporterData
|
|||||||
ofbx::DataView aFilename = tex->getRelativeFileName();
|
ofbx::DataView aFilename = tex->getRelativeFileName();
|
||||||
if (aFilename == "")
|
if (aFilename == "")
|
||||||
aFilename = tex->getFileName();
|
aFilename = tex->getFileName();
|
||||||
char filenameData[256];
|
char filenameData[OPEN_FBX_NAME_SIZE];
|
||||||
aFilename.toString(filenameData);
|
aFilename.toString(filenameData);
|
||||||
const String filename(filenameData);
|
const String filename(filenameData);
|
||||||
String path;
|
String path;
|
||||||
@@ -498,7 +499,6 @@ bool ImportBones(OpenFbxImporterData& data, String& errorMsg)
|
|||||||
|
|
||||||
// Add bone
|
// Add bone
|
||||||
boneIndex = data.Bones.Count();
|
boneIndex = data.Bones.Count();
|
||||||
data.Bones.EnsureCapacity(256);
|
|
||||||
data.Bones.Resize(boneIndex + 1);
|
data.Bones.Resize(boneIndex + 1);
|
||||||
auto& bone = data.Bones[boneIndex];
|
auto& bone = data.Bones[boneIndex];
|
||||||
|
|
||||||
@@ -1089,7 +1089,6 @@ struct AnimInfo
|
|||||||
double TimeEnd;
|
double TimeEnd;
|
||||||
double Duration;
|
double Duration;
|
||||||
int32 FramesCount;
|
int32 FramesCount;
|
||||||
float SamplingPeriod;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Frame
|
struct Frame
|
||||||
@@ -1122,7 +1121,7 @@ void ExtractKeyframeScale(const ofbx::Object* bone, ofbx::DVec3& trans, const Fr
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curve, AnimInfo& info, void (*ExtractKeyframe)(const ofbx::Object*, ofbx::DVec3&, const Frame&, T&))
|
void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curve, AnimInfo& info, void (*extractKeyframe)(const ofbx::Object*, ofbx::DVec3&, const Frame&, T&))
|
||||||
{
|
{
|
||||||
if (curveNode == nullptr)
|
if (curveNode == nullptr)
|
||||||
return;
|
return;
|
||||||
@@ -1136,12 +1135,11 @@ void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curv
|
|||||||
for (int32 i = 0; i < info.FramesCount; i++)
|
for (int32 i = 0; i < info.FramesCount; i++)
|
||||||
{
|
{
|
||||||
auto& key = keyframes[i];
|
auto& key = keyframes[i];
|
||||||
const double t = info.TimeStart + ((double)i / info.FramesCount) * info.Duration;
|
const double alpha = (double)i / (info.FramesCount - 1);
|
||||||
|
const double t = Math::Lerp(info.TimeStart, info.TimeEnd, alpha);
|
||||||
key.Time = (float)i;
|
key.Time = (float)i;
|
||||||
|
|
||||||
ofbx::DVec3 trans = curveNode->getNodeLocalTransform(t);
|
ofbx::DVec3 trans = curveNode->getNodeLocalTransform(t);
|
||||||
ExtractKeyframe(bone, trans, localFrame, key.Value);
|
extractKeyframe(bone, trans, localFrame, key.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1179,7 +1177,7 @@ void ImportAnimation(int32 index, ModelData& data, OpenFbxImporterData& importer
|
|||||||
auto& animation = data.Animations.AddOne();
|
auto& animation = data.Animations.AddOne();
|
||||||
animation.Duration = (double)(int32)(localDuration * frameRate + 0.5f);
|
animation.Duration = (double)(int32)(localDuration * frameRate + 0.5f);
|
||||||
animation.FramesPerSecond = frameRate;
|
animation.FramesPerSecond = frameRate;
|
||||||
char nameData[256];
|
char nameData[OPEN_FBX_NAME_SIZE];
|
||||||
takeInfo->name.toString(nameData);
|
takeInfo->name.toString(nameData);
|
||||||
animation.Name = nameData;
|
animation.Name = nameData;
|
||||||
animation.Name = animation.Name.TrimTrailing();
|
animation.Name = animation.Name.TrimTrailing();
|
||||||
@@ -1190,8 +1188,7 @@ void ImportAnimation(int32 index, ModelData& data, OpenFbxImporterData& importer
|
|||||||
info.TimeStart = takeInfo->local_time_from;
|
info.TimeStart = takeInfo->local_time_from;
|
||||||
info.TimeEnd = takeInfo->local_time_to;
|
info.TimeEnd = takeInfo->local_time_to;
|
||||||
info.Duration = localDuration;
|
info.Duration = localDuration;
|
||||||
info.FramesCount = (int32)animation.Duration;
|
info.FramesCount = (int32)animation.Duration + 1;
|
||||||
info.SamplingPeriod = 1.0f / frameRate;
|
|
||||||
|
|
||||||
// Import curves
|
// Import curves
|
||||||
for (int32 i = 0; i < animatedNodes.Count(); i++)
|
for (int32 i = 0; i < animatedNodes.Count(); i++)
|
||||||
@@ -1319,7 +1316,7 @@ bool ModelTool::ImportDataOpenFBX(const String& path, ModelData& data, Options&
|
|||||||
{
|
{
|
||||||
const ofbx::DataView aEmbedded = scene->getEmbeddedData(i);
|
const ofbx::DataView aEmbedded = scene->getEmbeddedData(i);
|
||||||
ofbx::DataView aFilename = scene->getEmbeddedFilename(i);
|
ofbx::DataView aFilename = scene->getEmbeddedFilename(i);
|
||||||
char filenameData[256];
|
char filenameData[OPEN_FBX_NAME_SIZE];
|
||||||
aFilename.toString(filenameData);
|
aFilename.toString(filenameData);
|
||||||
if (outputPath.IsEmpty())
|
if (outputPath.IsEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user