postman09
(postman09)
October 28, 2020, 10:31pm
259
Hi guys! I planned to share my solution, but I have no time to complete it, so I’m here to share a part of my solution. Many people ask me how I get time by distance from curve. Code below is not perfect, but I’m not a C++ programmer This code works only if you compress curve with “Uniform Indexable” compression type!
#include "Animation/AnimInstanceBase.h"
#include "Animation/AnimCurveCompressionCodec_UniformIndexable.h"
DEFINE_LOG_CATEGORY(LogAnimInstanceBase)
float UAnimInstanceBase::GetCurveTime(const UAnimSequence* *AnimationSequence*, const FName *CurveName*, const float *CurveValue*)
{
if (AnimationSequence == nullptr)
{
UE_LOG(LogAnimInstanceBase, Error, TEXT("Invalid Animation Sequence ptr"));
return 0.0f;
}
* // Get curve SmartName*
FSmartName CurveSmartName;
AnimationSequence->GetSkeleton()->GetSmartNameByName(USkeleton::AnimCurveMappingName, CurveName, CurveSmartName);
* // Create a buffered access to times and values in curve*
const FAnimCurveBufferAccess CurveBuffer = FAnimCurveBufferAccess(AnimationSequence, CurveSmartName.UID);
* // The number of elements in curve*
const int NumSamples = static_cast<int>(CurveBuffer.GetNumSamples());
const int LastIndex = NumSamples - 1;
if (NumSamples < 2)
{
return 0.0f;
}
* // Corner cases*
if (CurveValue <= CurveBuffer.GetValue(0))
{
return CurveBuffer.GetTime(0);
}
if (CurveValue >= CurveBuffer.GetValue(LastIndex))
{
return CurveBuffer.GetTime(LastIndex);
}
* // Binary search*
int32 NextIndex = 1;
int32 Count = LastIndex - NextIndex;
while (Count > 0)
{
const int32 Step = Count / 2;
const int32 Middle = NextIndex + Step;
if (CurveValue > CurveBuffer.GetValue(Middle))
{
NextIndex = Middle + 1;
Count -= Step + 1;
}
else
{
Count = Step;
}
}
const int32 PrevIndex = NextIndex - 1;
const float PrevCurveValue = CurveBuffer.GetValue(PrevIndex);
const float NextCurveValue = CurveBuffer.GetValue(NextIndex);
const float PrevCurveTime = CurveBuffer.GetTime(PrevIndex);
const float NextCurveTime = CurveBuffer.GetTime(NextIndex);
* // Find time by two nearest known points on the curve*
const float Diff = NextCurveValue - PrevCurveValue;
const float Alpha = !FMath::IsNearlyZero(Diff) ? (CurveValue - PrevCurveValue) / Diff : 0.0f;
return FMath::Lerp(PrevCurveTime, NextCurveTime, Alpha);
}
P.S.: If you know how to improve code or found a bug, please let me know.
P.P.S: same in gist Get time by value from curve compressed with Uniform Indexable · GitHub
Thats not bad but if there arn’t alot of data points in the curve it won’t be that accurate since you are just lerping. The actual code for interpolation between points is much more sophisticated and takes into account tangents and what not, and still works with few data points.
What I did was just rite a quick animation modifier to swap the time and distance axis and save it as another curve so I could use the normal get float curve value function. That way I can use Epics black magic and not have to worry about the curve value being accurate.