Files
UnrealEngine/Engine/Plugins/Animation/PoseSearch/Source/Runtime/Private/PoseSearchTrajectoryLibrary.cpp
2025-05-18 13:04:45 +08:00

905 lines
38 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PoseSearch/PoseSearchTrajectoryLibrary.h"
#include "PoseSearch/PoseSearchTrajectoryPredictor.h"
#include "Animation/AnimInstanceProxy.h"
#include "Animation/AnimInstance.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/Engine.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "PoseSearch/PoseSearchDefines.h"
#include "Kismet/KismetMathLibrary.h"
#include "Animation/TrajectoryTypes.h"
bool FPoseSearchTrajectoryData::UpdateData(
float DeltaTime,
const FAnimInstanceProxy& AnimInstanceProxy,
FDerived& TrajectoryDataDerived,
FState& TrajectoryDataState) const
{
return UpdateData(DeltaTime, AnimInstanceProxy.GetAnimInstanceObject(), TrajectoryDataDerived, TrajectoryDataState);
}
bool FPoseSearchTrajectoryData::UpdateData(
float DeltaTime,
const UObject* Context,
FDerived& TrajectoryDataDerived,
FState& TrajectoryDataState) const
{
const ACharacter* Character = Cast<ACharacter>(Context);
if (!Character)
{
if (const UAnimInstance* AnimInstance = Cast<UAnimInstance>(Context))
{
Character = Cast<ACharacter>(AnimInstance->GetOwningActor());
}
else if (const UActorComponent* AnimNextComponent = Cast<UActorComponent>(Context))
{
Character = Cast<ACharacter>(AnimNextComponent->GetOwner());
}
if (!Character)
{
return false;
}
}
const UCharacterMovementComponent* MoveComp = Character->GetCharacterMovement();
const USkeletalMeshComponent* MeshComp = Character->GetMesh();
if (!MoveComp || !MeshComp)
{
return false;
}
TrajectoryDataDerived.MaxSpeed = FMath::Max(MoveComp->GetMaxSpeed() * MoveComp->GetAnalogInputModifier(), MoveComp->GetMinAnalogSpeed());
TrajectoryDataDerived.BrakingDeceleration = FMath::Max(0.f, MoveComp->GetMaxBrakingDeceleration());
TrajectoryDataDerived.BrakingSubStepTime = MoveComp->BrakingSubStepTime;
TrajectoryDataDerived.bOrientRotationToMovement = MoveComp->bOrientRotationToMovement;
TrajectoryDataDerived.Velocity = MoveComp->Velocity;
TrajectoryDataDerived.Acceleration = MoveComp->GetCurrentAcceleration();
TrajectoryDataDerived.bStepGroundPrediction = !MoveComp->IsFalling() && !MoveComp->IsFlying();
if (TrajectoryDataDerived.Acceleration.IsZero())
{
TrajectoryDataDerived.Friction = MoveComp->bUseSeparateBrakingFriction ? MoveComp->BrakingFriction : MoveComp->GroundFriction;
const float FrictionFactor = FMath::Max(0.f, MoveComp->BrakingFrictionFactor);
TrajectoryDataDerived.Friction = FMath::Max(0.f, TrajectoryDataDerived.Friction * FrictionFactor);
}
else
{
TrajectoryDataDerived.Friction = MoveComp->GroundFriction;
}
const float DesiredControllerYaw = Character->GetViewRotation().Yaw;
const float DesiredYawDelta = DesiredControllerYaw - TrajectoryDataState.DesiredControllerYawLastUpdate;
TrajectoryDataState.DesiredControllerYawLastUpdate = DesiredControllerYaw;
if (DeltaTime > UE_SMALL_NUMBER)
{
// An AnimInstance might call this during an AnimBP recompile with 0 delta time, so we don't update ControllerYawRate
TrajectoryDataDerived.ControllerYawRate = FRotator::NormalizeAxis(DesiredYawDelta) / DeltaTime;
if (MaxControllerYawRate >= 0.f)
{
TrajectoryDataDerived.ControllerYawRate = FMath::Sign(TrajectoryDataDerived.ControllerYawRate) * FMath::Min(FMath::Abs(TrajectoryDataDerived.ControllerYawRate), MaxControllerYawRate);
}
}
TrajectoryDataDerived.Position = MeshComp->GetComponentLocation();
TrajectoryDataDerived.MeshCompRelativeRotation = MeshComp->GetRelativeRotation().Quaternion();
if (TrajectoryDataDerived.bOrientRotationToMovement)
{
TrajectoryDataDerived.Facing = MeshComp->GetComponentRotation().Quaternion();
}
else
{
TrajectoryDataDerived.Facing = FQuat::MakeFromRotator(FRotator(0,TrajectoryDataState.DesiredControllerYawLastUpdate,0)) * TrajectoryDataDerived.MeshCompRelativeRotation;
}
return true;
}
FVector FPoseSearchTrajectoryData::StepCharacterMovementGroundPrediction(
float DeltaTime,
const FVector& InVelocity,
const FVector& InAcceleration,
const FDerived& TrajectoryDataDerived) const
{
FVector OutVelocity = InVelocity;
// Braking logic is copied from UCharacterMovementComponent::ApplyVelocityBraking()
if (InAcceleration.IsZero())
{
if (InVelocity.IsZero())
{
return FVector::ZeroVector;
}
const bool bZeroFriction = (TrajectoryDataDerived.Friction == 0.f);
const bool bZeroBraking = (TrajectoryDataDerived.BrakingDeceleration == 0.f);
if (bZeroFriction && bZeroBraking)
{
return InVelocity;
}
float RemainingTime = DeltaTime;
const float MaxTimeStep = FMath::Clamp(TrajectoryDataDerived.BrakingSubStepTime, 1.0f / 75.0f, 1.0f / 20.0f);
const FVector PrevLinearVelocity = OutVelocity;
const FVector RevAccel = (bZeroBraking ? FVector::ZeroVector : (-TrajectoryDataDerived.BrakingDeceleration * OutVelocity.GetSafeNormal()));
// Decelerate to brake to a stop
while (RemainingTime >= UCharacterMovementComponent::MIN_TICK_TIME)
{
// Zero friction uses constant deceleration, so no need for iteration.
const float dt = ((RemainingTime > MaxTimeStep && !bZeroFriction) ? FMath::Min(MaxTimeStep, RemainingTime * 0.5f) : RemainingTime);
RemainingTime -= dt;
// apply friction and braking
OutVelocity = OutVelocity + ((-TrajectoryDataDerived.Friction) * OutVelocity + RevAccel) * dt;
// Don't reverse direction
if ((OutVelocity | PrevLinearVelocity) <= 0.f)
{
OutVelocity = FVector::ZeroVector;
return OutVelocity;
}
}
// Clamp to zero if nearly zero, or if below min threshold and braking
const float VSizeSq = OutVelocity.SizeSquared();
if (VSizeSq <= KINDA_SMALL_NUMBER || (!bZeroBraking && VSizeSq <= FMath::Square(UCharacterMovementComponent::BRAKE_TO_STOP_VELOCITY)))
{
OutVelocity = FVector::ZeroVector;
}
}
// Acceleration logic is copied from UCharacterMovementComponent::CalcVelocity
else
{
const FVector AccelDir = InAcceleration.GetSafeNormal();
const float VelSize = OutVelocity.Size();
OutVelocity = OutVelocity - (OutVelocity - AccelDir * VelSize) * FMath::Min(DeltaTime * TrajectoryDataDerived.Friction, 1.f);
OutVelocity += InAcceleration * DeltaTime;
OutVelocity = OutVelocity.GetClampedToMaxSize(TrajectoryDataDerived.MaxSpeed);
}
return OutVelocity;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::InitTrajectorySamples(
FPoseSearchQueryTrajectory& Trajectory,
const FPoseSearchTrajectoryData& TrajectoryData,
const FPoseSearchTrajectoryData::FDerived& TrajectoryDataDerived,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
InitTrajectorySamples(
Trajectory,
TrajectoryDataDerived.Position, TrajectoryDataDerived.Facing,
TrajectoryDataSampling,
DeltaTime );
}
// Deprecated
void UPoseSearchTrajectoryLibrary::InitTrajectorySamples(
FPoseSearchQueryTrajectory& Trajectory,
const FPoseSearchTrajectoryData& TrajectoryData, // Unused parameter
FVector DefaultPosition, FQuat DefaultFacing,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
InitTrajectorySamples(
Trajectory,
DefaultPosition,
DefaultFacing,
TrajectoryDataSampling,
DeltaTime);
}
// Deprecated
void UPoseSearchTrajectoryLibrary::InitTrajectorySamples(
FPoseSearchQueryTrajectory& Trajectory,
FVector DefaultPosition, FQuat DefaultFacing,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
FTransformTrajectory TransformTrajectory = Trajectory;
InitTrajectorySamples(TransformTrajectory, DefaultPosition, DefaultFacing, TrajectoryDataSampling, DeltaTime);
Trajectory = TransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::InitTrajectorySamples(FTransformTrajectory& Trajectory, FVector DefaultPosition, FQuat DefaultFacing, const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling, float DeltaTime)
{
const int32 NumHistorySamples = TrajectoryDataSampling.NumHistorySamples;
const int32 NumPredictionSamples = TrajectoryDataSampling.NumPredictionSamples;
// History + current sample + prediction
const int32 TotalNumSamples = NumHistorySamples + 1 + NumPredictionSamples;
if (Trajectory.Samples.Num() != TotalNumSamples)
{
Trajectory.Samples.SetNumUninitialized(TotalNumSamples);
// Initialize history samples
const float SecondsPerHistorySample = FMath::Max(TrajectoryDataSampling.SecondsPerHistorySample, 0.f);
for (int32 i = 0; i < NumHistorySamples; ++i)
{
Trajectory.Samples[i].Position = DefaultPosition;
Trajectory.Samples[i].Facing = DefaultFacing;
Trajectory.Samples[i].TimeInSeconds = SecondsPerHistorySample * (i - NumHistorySamples - 1);
}
// Initialize current sample and prediction
const float SecondsPerPredictionSample = FMath::Max(TrajectoryDataSampling.SecondsPerPredictionSample, 0.f);
for (int32 i = NumHistorySamples; i < Trajectory.Samples.Num(); ++i)
{
Trajectory.Samples[i].Position = DefaultPosition;
Trajectory.Samples[i].Facing = DefaultFacing;
Trajectory.Samples[i].TimeInSeconds = SecondsPerPredictionSample * (i - NumHistorySamples) + DeltaTime;
}
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::UpdateHistory_TransformHistory(
FPoseSearchQueryTrajectory& Trajectory,
const FPoseSearchTrajectoryData& TrajectoryData, // Unused parameter
FVector CurrentPosition,
FVector CurrentVelocity,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
UpdateHistory_TransformHistory(
Trajectory,
CurrentPosition,
CurrentVelocity,
TrajectoryDataSampling,
DeltaTime);
}
// Deprecated
void UPoseSearchTrajectoryLibrary::UpdateHistory_TransformHistory(
FPoseSearchQueryTrajectory& Trajectory,
FVector CurrentPosition,
FVector CurrentVelocity,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
FTransformTrajectory TransformTrajectory = Trajectory;
UpdateHistory_TransformHistory(TransformTrajectory, CurrentPosition, CurrentVelocity, TrajectoryDataSampling, DeltaTime);
Trajectory = TransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::UpdateHistory_TransformHistory(FTransformTrajectory& Trajectory,
FVector CurrentPosition,
FVector CurrentVelocity,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime,
float CurrentTime)
{
const int32 NumHistorySamples = TrajectoryDataSampling.NumHistorySamples;
if (NumHistorySamples > 0)
{
const float SecondsPerHistorySample = TrajectoryDataSampling.SecondsPerHistorySample;
check(NumHistorySamples <= Trajectory.Samples.Num());
// Trajectory.Samples[NumHistorySamples] is last frame position! (assuming this is called every frame)
const FVector CurrentTranslationFromMover = CurrentVelocity * DeltaTime;
const FVector TranslationSinceLastFrame = CurrentPosition - Trajectory.Samples[NumHistorySamples].Position;
const FVector InferredGroundTranslation = TranslationSinceLastFrame - CurrentTranslationFromMover;
// Shift history Samples when it's time to record a new one.
if (SecondsPerHistorySample <= 0.f || FMath::Abs(Trajectory.Samples[NumHistorySamples - 1].TimeInSeconds) >= SecondsPerHistorySample)
{
for (int32 Index = 0; Index < NumHistorySamples - 1; ++Index)
{
Trajectory.Samples[Index].TimeInSeconds = Trajectory.Samples[Index + 1].TimeInSeconds - DeltaTime;
Trajectory.Samples[Index].Position = Trajectory.Samples[Index + 1].Position + InferredGroundTranslation;
Trajectory.Samples[Index].Facing = Trajectory.Samples[Index + 1].Facing;
}
// Adding a new history record
// Note we add current frame at delta time in the future since t = 0 should be the previous frame
Trajectory.Samples[NumHistorySamples - 1].TimeInSeconds = CurrentTime;
Trajectory.Samples[NumHistorySamples - 1].Position = CurrentPosition;
Trajectory.Samples[NumHistorySamples - 1].Facing = Trajectory.Samples[NumHistorySamples].Facing;
}
else
{
// Didn't record a new history position, update timers and shift by ground translation
for (int32 Index = 0; Index < NumHistorySamples; ++Index)
{
Trajectory.Samples[Index].TimeInSeconds -= DeltaTime;
Trajectory.Samples[Index].Position += InferredGroundTranslation;
}
}
}
}
void UPoseSearchTrajectoryLibrary::UpdateHistory_WorldSpace(FTransformTrajectory& Trajectory,
FVector CurrentPosition,
FQuat CurrentRotation,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime,
float CurrentTime)
{
const int32 NumHistorySamples = TrajectoryDataSampling.NumHistorySamples;
if (NumHistorySamples > 0)
{
const float SecondsPerHistorySample = TrajectoryDataSampling.SecondsPerHistorySample;
check(NumHistorySamples <= Trajectory.Samples.Num());
// Shift history Samples when it's time to record a new one.
if (SecondsPerHistorySample <= 0.f || FMath::Abs(Trajectory.Samples[NumHistorySamples - 1].TimeInSeconds) >= SecondsPerHistorySample)
{
for (int32 Index = 0; Index < NumHistorySamples - 1; ++Index)
{
Trajectory.Samples[Index].TimeInSeconds = Trajectory.Samples[Index + 1].TimeInSeconds - DeltaTime;
Trajectory.Samples[Index].Position = Trajectory.Samples[Index + 1].Position;
Trajectory.Samples[Index].Facing = Trajectory.Samples[Index + 1].Facing;
}
// Adding a new history record
// Note we add current frame at delta time in the future since t = 0 should be the previous frame
Trajectory.Samples[NumHistorySamples - 1].TimeInSeconds = CurrentTime;
Trajectory.Samples[NumHistorySamples - 1].Position = CurrentPosition;
Trajectory.Samples[NumHistorySamples - 1].Facing = CurrentRotation;
}
else
{
// Didn't record a new history position, update timers and shift by ground translation
for (int32 Index = 0; Index < NumHistorySamples; ++Index)
{
Trajectory.Samples[Index].TimeInSeconds -= DeltaTime;
}
}
}
}
FVector UPoseSearchTrajectoryLibrary::RemapVectorMagnitudeWithCurve(
const FVector& Vector,
bool bUseCurve,
const FRuntimeFloatCurve& Curve)
{
if (bUseCurve)
{
const float Length = Vector.Length();
if (Length > UE_KINDA_SMALL_NUMBER)
{
const float RemappedLength = Curve.GetRichCurveConst()->Eval(Length);
return Vector * (RemappedLength / Length);
}
}
return Vector;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::UpdatePrediction_SimulateCharacterMovement(
FPoseSearchQueryTrajectory& Trajectory,
const FPoseSearchTrajectoryData& TrajectoryData,
const FPoseSearchTrajectoryData::FDerived& TrajectoryDataDerived,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling,
float DeltaTime)
{
FTransformTrajectory TransformTrajectory = Trajectory;
UpdatePrediction_SimulateCharacterMovement(TransformTrajectory, TrajectoryData, TrajectoryDataDerived, TrajectoryDataSampling, DeltaTime);
Trajectory = TransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::UpdatePrediction_SimulateCharacterMovement(FTransformTrajectory& Trajectory,
const FPoseSearchTrajectoryData& TrajectoryData, const FPoseSearchTrajectoryData::FDerived& TrajectoryDataDerived,
const FPoseSearchTrajectoryData::FSampling& TrajectoryDataSampling, float DeltaTime)
{
FVector CurrentPositionWS = TrajectoryDataDerived.Position;
FVector CurrentVelocityWS = RemapVectorMagnitudeWithCurve(TrajectoryDataDerived.Velocity, TrajectoryData.bUseSpeedRemappingCurve, TrajectoryData.SpeedRemappingCurve);
FVector CurrentAccelerationWS = RemapVectorMagnitudeWithCurve(TrajectoryDataDerived.Acceleration, TrajectoryData.bUseAccelerationRemappingCurve, TrajectoryData.AccelerationRemappingCurve);
// Bending CurrentVelocityWS towards CurrentAccelerationWS
if (TrajectoryData.BendVelocityTowardsAcceleration > UE_KINDA_SMALL_NUMBER && !CurrentAccelerationWS.IsNearlyZero())
{
const float CurrentSpeed = CurrentVelocityWS.Length();
const FVector VelocityWSAlongAcceleration = CurrentAccelerationWS.GetUnsafeNormal() * CurrentSpeed;
if (TrajectoryData.BendVelocityTowardsAcceleration < 1.f - UE_KINDA_SMALL_NUMBER)
{
CurrentVelocityWS = FMath::Lerp(CurrentVelocityWS, VelocityWSAlongAcceleration, TrajectoryData.BendVelocityTowardsAcceleration);
const float NewLength = CurrentVelocityWS.Length();
if (NewLength > UE_KINDA_SMALL_NUMBER)
{
CurrentVelocityWS *= CurrentSpeed / NewLength;
}
else
{
// @todo: consider setting the CurrentVelocityWS = VelocityWSAlongAcceleration if vel and acc are in opposite directions
}
}
else
{
CurrentVelocityWS = VelocityWSAlongAcceleration;
}
}
FQuat CurrentFacingWS = TrajectoryDataDerived.Facing;
const int32 NumHistorySamples = TrajectoryDataSampling.NumHistorySamples;
const float SecondsPerPredictionSample = TrajectoryDataSampling.SecondsPerPredictionSample;
const FQuat ControllerRotationPerStep = FQuat::MakeFromEuler(FVector(0.f, 0.f, TrajectoryDataDerived.ControllerYawRate * SecondsPerPredictionSample));
float AccumulatedSeconds = DeltaTime;
const int32 LastIndex = Trajectory.Samples.Num() - 1;
if (NumHistorySamples <= LastIndex)
{
for (int32 Index = NumHistorySamples; ; ++Index)
{
Trajectory.Samples[Index].Position = CurrentPositionWS;
Trajectory.Samples[Index].Facing = CurrentFacingWS;
Trajectory.Samples[Index].TimeInSeconds = AccumulatedSeconds;
if (Index == LastIndex)
{
break;
}
CurrentPositionWS += CurrentVelocityWS * SecondsPerPredictionSample;
AccumulatedSeconds += SecondsPerPredictionSample;
if (TrajectoryDataDerived.bStepGroundPrediction)
{
CurrentAccelerationWS = RemapVectorMagnitudeWithCurve(ControllerRotationPerStep * CurrentAccelerationWS,
TrajectoryData.bUseAccelerationRemappingCurve, TrajectoryData.AccelerationRemappingCurve);
const FVector NewVelocityWS = TrajectoryData.StepCharacterMovementGroundPrediction(SecondsPerPredictionSample, CurrentVelocityWS, CurrentAccelerationWS, TrajectoryDataDerived);
CurrentVelocityWS = RemapVectorMagnitudeWithCurve(NewVelocityWS, TrajectoryData.bUseSpeedRemappingCurve, TrajectoryData.SpeedRemappingCurve);
// Account for the controller (e.g. the camera) rotating.
CurrentFacingWS = ControllerRotationPerStep * CurrentFacingWS;
if (TrajectoryDataDerived.bOrientRotationToMovement && !CurrentAccelerationWS.IsNearlyZero())
{
// Rotate towards acceleration.
const FVector CurrentAccelerationCS = TrajectoryDataDerived.MeshCompRelativeRotation.RotateVector(CurrentAccelerationWS);
CurrentFacingWS = FMath::QInterpConstantTo(CurrentFacingWS, CurrentAccelerationCS.ToOrientationQuat(), SecondsPerPredictionSample, TrajectoryData.RotateTowardsMovementSpeed);
}
}
}
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::PoseSearchGenerateTrajectory(
const UObject* Context,
UPARAM(ref) const FPoseSearchTrajectoryData& InTrajectoryData,
float InDeltaTime,
UPARAM(ref) FPoseSearchQueryTrajectory& InOutTrajectory,
UPARAM(ref) float& InOutDesiredControllerYawLastUpdate,
FPoseSearchQueryTrajectory& OutTrajectory,
float InHistorySamplingInterval,
int32 InTrajectoryHistoryCount,
float InPredictionSamplingInterval,
int32 InTrajectoryPredictionCount)
{
FTransformTrajectory InOutTransformTrajectory = InOutTrajectory;
FTransformTrajectory OutTransformTrajectory;
PoseSearchGenerateTransformTrajectory(Context, InTrajectoryData, InDeltaTime, InOutTransformTrajectory, InOutDesiredControllerYawLastUpdate,
OutTransformTrajectory, InHistorySamplingInterval, InTrajectoryHistoryCount, InPredictionSamplingInterval, InTrajectoryPredictionCount);
OutTrajectory = OutTransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::PoseSearchGenerateTransformTrajectory(const UObject* InContext, const FPoseSearchTrajectoryData& InTrajectoryData,
float InDeltaTime, FTransformTrajectory& InOutTrajectory, float& InOutDesiredControllerYawLastUpdate, FTransformTrajectory& OutTrajectory,
float InHistorySamplingInterval, int32 InTrajectoryHistoryCount, float InPredictionSamplingInterval, int32 InTrajectoryPredictionCount)
{
FPoseSearchTrajectoryData::FSampling TrajectoryDataSampling;
TrajectoryDataSampling.NumHistorySamples = InTrajectoryHistoryCount;
TrajectoryDataSampling.SecondsPerHistorySample = InHistorySamplingInterval;
TrajectoryDataSampling.NumPredictionSamples = InTrajectoryPredictionCount;
TrajectoryDataSampling.SecondsPerPredictionSample = InPredictionSamplingInterval;
FPoseSearchTrajectoryData::FState TrajectoryDataState;
TrajectoryDataState.DesiredControllerYawLastUpdate = InOutDesiredControllerYawLastUpdate;
FPoseSearchTrajectoryData::FDerived TrajectoryDataDerived;
InTrajectoryData.UpdateData(InDeltaTime, InContext, TrajectoryDataDerived, TrajectoryDataState);
InitTrajectorySamples(InOutTrajectory, TrajectoryDataDerived.Position, TrajectoryDataDerived.Facing, TrajectoryDataSampling, InDeltaTime);
UpdateHistory_TransformHistory(InOutTrajectory, TrajectoryDataDerived.Position, TrajectoryDataDerived.Velocity, TrajectoryDataSampling, InDeltaTime);
UpdatePrediction_SimulateCharacterMovement(InOutTrajectory, InTrajectoryData, TrajectoryDataDerived, TrajectoryDataSampling, InDeltaTime);
InOutDesiredControllerYawLastUpdate = TrajectoryDataState.DesiredControllerYawLastUpdate;
OutTrajectory = InOutTrajectory;
}
void UPoseSearchTrajectoryLibrary::PoseSearchGeneratePredictorTransformTrajectory(UObject* InPredictor,
const FPoseSearchTrajectoryData& InTrajectoryData, float InDeltaTime, FTransformTrajectory& InOutTrajectory,
float& InOutDesiredControllerYawLastUpdate, FTransformTrajectory& OutTrajectory, float InHistorySamplingInterval,
int32 InTrajectoryHistoryCount, float InPredictionSamplingInterval, int32 InTrajectoryPredictionCount)
{
IPoseSearchTrajectoryPredictorInterface* Predictor = Cast<IPoseSearchTrajectoryPredictorInterface>(InPredictor);
if (Predictor != nullptr)
{
PoseSearchGenerateTransformTrajectoryWithPredictor(InPredictor,
InDeltaTime,
InOutTrajectory,
InOutDesiredControllerYawLastUpdate,
OutTrajectory,
InHistorySamplingInterval,
InTrajectoryHistoryCount,
InPredictionSamplingInterval,
InTrajectoryPredictionCount);
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::PoseSearchGeneratePredictorTrajectory(
UObject* InPredictor, // must implement IPoseSearchTrajectoryPredictorInterface
UPARAM(ref) const FPoseSearchTrajectoryData& InTrajectoryData,
float InDeltaTime,
UPARAM(ref) FPoseSearchQueryTrajectory& InOutTrajectory,
UPARAM(ref) float& InOutDesiredControllerYawLastUpdate,
FPoseSearchQueryTrajectory& OutTrajectory,
float InHistorySamplingInterval,
int32 InTrajectoryHistoryCount,
float InPredictionSamplingInterval,
int32 InTrajectoryPredictionCount)
{
IPoseSearchTrajectoryPredictorInterface* predictor = Cast<IPoseSearchTrajectoryPredictorInterface>(InPredictor);
if (predictor != nullptr)
{
PoseSearchGenerateTrajectoryWithPredictor(InPredictor,
InDeltaTime,
InOutTrajectory,
InOutDesiredControllerYawLastUpdate,
OutTrajectory,
InHistorySamplingInterval,
InTrajectoryHistoryCount,
InPredictionSamplingInterval,
InTrajectoryPredictionCount);
}
}
// Deprecated
void UPoseSearchTrajectoryLibrary::PoseSearchGenerateTrajectoryWithPredictor(
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> InPredictor, // must implement IPoseSearchTrajectoryPredictorInterface
float InDeltaTime,
UPARAM(ref) FPoseSearchQueryTrajectory& InOutTrajectory,
UPARAM(ref) float& InOutDesiredControllerYawLastUpdate,
FPoseSearchQueryTrajectory& OutTrajectory,
float InHistorySamplingInterval,
int32 InTrajectoryHistoryCount,
float InPredictionSamplingInterval,
int32 InTrajectoryPredictionCount)
{
FTransformTrajectory InOutTransformTrajectory = InOutTrajectory;
FTransformTrajectory OutTransformTrajectory;
PoseSearchGenerateTransformTrajectoryWithPredictor(InPredictor, InDeltaTime, InOutTransformTrajectory, InOutDesiredControllerYawLastUpdate,
OutTransformTrajectory, InHistorySamplingInterval, InTrajectoryHistoryCount, InPredictionSamplingInterval, InTrajectoryPredictionCount);
OutTrajectory = OutTransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::PoseSearchGenerateTransformTrajectoryWithPredictor(TScriptInterface<IPoseSearchTrajectoryPredictorInterface> InPredictor,
float InDeltaTime, FTransformTrajectory& InOutTrajectory, float& InOutDesiredControllerYawLastUpdate, FTransformTrajectory& OutTrajectory,
float InHistorySamplingInterval, int32 InTrajectoryHistoryCount, float InPredictionSamplingInterval, int32 InTrajectoryPredictionCount)
{
FPoseSearchTrajectoryData::FSampling TrajectoryDataSampling;
TrajectoryDataSampling.NumHistorySamples = InTrajectoryHistoryCount;
TrajectoryDataSampling.SecondsPerHistorySample = InHistorySamplingInterval;
TrajectoryDataSampling.NumPredictionSamples = InTrajectoryPredictionCount;
TrajectoryDataSampling.SecondsPerPredictionSample = InPredictionSamplingInterval;
// TODO: handle controller yaw
//TrajectoryDataState.DesiredControllerYawLastUpdate = InOutDesiredControllerYawLastUpdate;
FVector CurrentPosition = FVector::ZeroVector;
FVector CurrentVelocity = FVector::ZeroVector;
FQuat CurrentFacing = FQuat::Identity;
if (InPredictor != nullptr)
{
InPredictor->GetCurrentState(CurrentPosition, CurrentFacing, CurrentVelocity);
}
InitTrajectorySamples(InOutTrajectory, CurrentPosition, CurrentFacing, TrajectoryDataSampling, InDeltaTime);
UpdateHistory_TransformHistory(InOutTrajectory, CurrentPosition, CurrentVelocity, TrajectoryDataSampling, InDeltaTime);
if (InPredictor != nullptr)
{
InPredictor->Predict(InOutTrajectory, InTrajectoryPredictionCount + 1, InPredictionSamplingInterval, InTrajectoryHistoryCount);
}
//InOutDesiredControllerYawLastUpdate = TrajectoryDataState.DesiredControllerYawLastUpdate;
OutTrajectory = InOutTrajectory;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::HandleTrajectoryWorldCollisions(const UObject* WorldContextObject, const UAnimInstance* AnimInstance, UPARAM(ref) const FPoseSearchQueryTrajectory& InTrajectory, bool bApplyGravity, float FloorCollisionsOffset, FPoseSearchQueryTrajectory& OutTrajectory, FPoseSearchTrajectory_WorldCollisionResults& CollisionResult,
ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, bool bIgnoreSelf, float MaxObstacleHeight, FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime)
{
FTransformTrajectory InTransformTrajectory = InTrajectory;
FTransformTrajectory OutTransformTrajectory;
HandleTransformTrajectoryWorldCollisions(WorldContextObject, AnimInstance, InTransformTrajectory, bApplyGravity,
FloorCollisionsOffset, OutTransformTrajectory, CollisionResult, TraceChannel, bTraceComplex, ActorsToIgnore,
DrawDebugType, bIgnoreSelf, MaxObstacleHeight, TraceColor, TraceHitColor, DrawTime);
OutTrajectory = OutTransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::HandleTransformTrajectoryWorldCollisions(const UObject* WorldContextObject, const UAnimInstance* AnimInstance,
const FTransformTrajectory& InTrajectory, bool bApplyGravity, float FloorCollisionsOffset, FTransformTrajectory& OutTrajectory,
FPoseSearchTrajectory_WorldCollisionResults& CollisionResult, ETraceTypeQuery TraceChannel, bool bTraceComplex,
const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, bool bIgnoreSelf, float MaxObstacleHeight, FLinearColor TraceColor,
FLinearColor TraceHitColor, float DrawTime)
{
FVector StartingVelocity = FVector::ZeroVector;
FVector GravityAccel = FVector::ZeroVector;
if (bApplyGravity && AnimInstance)
{
if (const ACharacter* Character = Cast<ACharacter>(AnimInstance->GetOwningActor()))
{
if (const UCharacterMovementComponent* MoveComp = Character->GetCharacterMovement())
{
GravityAccel = MoveComp->GetGravityDirection() * -MoveComp->GetGravityZ();
StartingVelocity = Character->GetVelocity();
}
}
}
HandleTransformTrajectoryWorldCollisionsWithGravity(WorldContextObject, InTrajectory, StartingVelocity, bApplyGravity, GravityAccel,
FloorCollisionsOffset, OutTrajectory, CollisionResult, TraceChannel, bTraceComplex, ActorsToIgnore,
DrawDebugType, bIgnoreSelf, MaxObstacleHeight, TraceColor, TraceHitColor, DrawTime);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::HandleTrajectoryWorldCollisionsWithGravity(const UObject* WorldContextObject,
UPARAM(ref) const FPoseSearchQueryTrajectory& InTrajectory, FVector StartingVelocity, bool bApplyGravity, FVector GravityAccel, float FloorCollisionsOffset, FPoseSearchQueryTrajectory& OutTrajectory, FPoseSearchTrajectory_WorldCollisionResults& CollisionResult,
ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, bool bIgnoreSelf, float MaxObstacleHeight, FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime)
{
FTransformTrajectory InTransformTrajectory = InTrajectory;
FTransformTrajectory OutTransformTrajectory;
HandleTransformTrajectoryWorldCollisionsWithGravity(WorldContextObject, InTransformTrajectory, StartingVelocity, bApplyGravity, GravityAccel,
FloorCollisionsOffset, OutTransformTrajectory, CollisionResult, TraceChannel, bTraceComplex, ActorsToIgnore,
DrawDebugType, bIgnoreSelf, MaxObstacleHeight, TraceColor, TraceHitColor, DrawTime);
OutTrajectory = OutTransformTrajectory;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::HandleTransformTrajectoryWorldCollisionsWithGravity(const UObject* WorldContextObject,
const FTransformTrajectory& InTrajectory, FVector StartingVelocity, bool bApplyGravity, FVector GravityAccel, float FloorCollisionsOffset,
FTransformTrajectory& OutTrajectory, FPoseSearchTrajectory_WorldCollisionResults& CollisionResult, ETraceTypeQuery TraceChannel,
bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, bool bIgnoreSelf, float MaxObstacleHeight,
FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime)
{
OutTrajectory = InTrajectory;
TArray<FTransformTrajectorySample>& Samples = OutTrajectory.Samples;
const int32 NumSamples = Samples.Num();
FVector GravityDirection = FVector::ZeroVector;
float GravityZ = 0.f;
float InitialVelocityZ = StartingVelocity.Z;
if (bApplyGravity && !GravityAccel.IsNearlyZero())
{
GravityAccel.ToDirectionAndLength(GravityDirection, GravityZ);
GravityZ = -GravityZ;
const FVector VelocityOnGravityAxis = StartingVelocity.ProjectOnTo(GravityDirection);
InitialVelocityZ = VelocityOnGravityAxis.Length() * -FMath::Sign(GravityDirection.Dot(VelocityOnGravityAxis));
}
CollisionResult.TimeToLand = OutTrajectory.Samples.Last().TimeInSeconds;
if (!FMath::IsNearlyZero(GravityZ))
{
FVector LastImpactPoint;
FVector LastImpactNormal;
bool bIsLastImpactValid = false;
bool bIsFirstFall = true;
const FVector Gravity = GravityDirection * -GravityZ;
float FreeFallAccumulatedSeconds = 0.f;
for (int32 SampleIndex = 1; SampleIndex < NumSamples; ++SampleIndex)
{
FTransformTrajectorySample& Sample = Samples[SampleIndex];
if (Sample.TimeInSeconds > 0.f)
{
const int32 PrevSampleIndex = SampleIndex - 1;
const FTransformTrajectorySample& PrevSample = Samples[PrevSampleIndex];
FreeFallAccumulatedSeconds += Sample.TimeInSeconds - PrevSample.TimeInSeconds;
if (bIsLastImpactValid)
{
const FPlane GroundPlane = FPlane(PrevSample.Position, -GravityDirection);
Sample.Position = FPlane::PointPlaneProject(Sample.Position, GroundPlane);
}
// applying gravity
const FVector FreeFallOffset = Gravity * (0.5f * FreeFallAccumulatedSeconds * FreeFallAccumulatedSeconds);
Sample.Position += FreeFallOffset;
FHitResult HitResult;
if (FloorCollisionsOffset > 0.f && UKismetSystemLibrary::LineTraceSingle(WorldContextObject, Sample.Position + (GravityDirection * -MaxObstacleHeight), Sample.Position, TraceChannel, bTraceComplex, ActorsToIgnore, DrawDebugType, HitResult, bIgnoreSelf, TraceColor, TraceHitColor, DrawTime))
{
// Only allow our trace to move trajectory along gravity direction.
LastImpactPoint = UKismetMathLibrary::FindClosestPointOnLine(HitResult.ImpactPoint, Sample.Position, GravityDirection);
LastImpactNormal = HitResult.Normal;
bIsLastImpactValid = true;
Sample.Position = LastImpactPoint - GravityDirection * FloorCollisionsOffset;
if (bIsFirstFall)
{
const float InitialHeight = OutTrajectory.GetSampleAtTime(0.0f).Position.Z;
const float FinalHeight = Sample.Position.Z;
const float FallHeight = FMath::Abs(FinalHeight - InitialHeight);
bIsFirstFall = false;
CollisionResult.TimeToLand = (InitialVelocityZ / -GravityZ) + ((FMath::Sqrt(FMath::Square(InitialVelocityZ) + (2.f * -GravityZ * FallHeight))) / -GravityZ);
CollisionResult.LandSpeed = InitialVelocityZ + GravityZ * CollisionResult.TimeToLand;
}
FreeFallAccumulatedSeconds = 0.f;
}
}
}
}
else if (FloorCollisionsOffset > 0.f)
{
for (int32 SampleIndex = 0; SampleIndex < NumSamples; ++SampleIndex)
{
FTransformTrajectorySample& Sample = OutTrajectory.Samples[SampleIndex];
if (Sample.TimeInSeconds > 0.f)
{
FHitResult HitResult;
if (UKismetSystemLibrary::LineTraceSingle(WorldContextObject, Sample.Position + FVector::UpVector * 3000.f, Sample.Position, TraceChannel, bTraceComplex, ActorsToIgnore, DrawDebugType, HitResult, bIgnoreSelf, TraceColor, TraceHitColor, DrawTime))
{
Sample.Position.Z = HitResult.ImpactPoint.Z + FloorCollisionsOffset;
}
}
}
}
CollisionResult.LandSpeed = InitialVelocityZ + GravityZ * CollisionResult.TimeToLand;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// Deprecated
void UPoseSearchTrajectoryLibrary::GetTrajectorySampleAtTime(UPARAM(ref) const FPoseSearchQueryTrajectory& InTrajectory, float Time, FPoseSearchQueryTrajectorySample& OutTrajectorySample, bool bExtrapolate)
{
OutTrajectorySample = InTrajectory.GetSampleAtTime(Time, bExtrapolate);
}
// Deprecated
void UPoseSearchTrajectoryLibrary::GetTrajectoryVelocity(UPARAM(ref) const FPoseSearchQueryTrajectory& InTrajectory, float Time1, float Time2, FVector& OutVelocity, bool bExtrapolate)
{
GetTransformTrajectoryVelocity(FTransformTrajectory(InTrajectory), Time1, Time2, OutVelocity, bExtrapolate);
}
// Deprecated
void UPoseSearchTrajectoryLibrary::GetTrajectoryAngularVelocity(const FPoseSearchQueryTrajectory& InTrajectory, float Time1, float Time2, FVector& OutAngularVelocity, bool bExtrapolate /*= false*/)
{
GetTransformTrajectoryAngularVelocity(FTransformTrajectory(InTrajectory), Time1, Time2, OutAngularVelocity, bExtrapolate);
}
// Deprecated
FTransform UPoseSearchTrajectoryLibrary::GetTransform(const FPoseSearchQueryTrajectorySample& InTrajectorySample)
{
return InTrajectorySample.GetTransform();
}
// Deprecated
void UPoseSearchTrajectoryLibrary::DrawTrajectory(const UObject* WorldContextObject, const FPoseSearchQueryTrajectory& InTrajectory, const float DebugThickness, float HeightOffset)
{
DrawTransformTrajectory(WorldContextObject, FTransformTrajectory(InTrajectory), DebugThickness, HeightOffset);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void UPoseSearchTrajectoryLibrary::GetTransformTrajectorySampleAtTime(const FTransformTrajectory& InTrajectory, float Time,
FTransformTrajectorySample& OutTrajectorySample, bool bExtrapolate)
{
OutTrajectorySample = InTrajectory.GetSampleAtTime(Time, bExtrapolate);
}
void UPoseSearchTrajectoryLibrary::GetTransformTrajectoryVelocity(const FTransformTrajectory& InTrajectory, float Time1, float Time2, FVector& OutVelocity,
bool bExtrapolate)
{
if (FMath::IsNearlyEqual(Time1, Time2))
{
UE_LOG(LogPoseSearch, Warning, TEXT("UPoseSearchTrajectoryLibrary::GetTrajectoryVelocity - Time1 is same as Time2. Invalid time horizon."));
OutVelocity = FVector::ZeroVector;
return;
}
FTransformTrajectorySample Sample1 = InTrajectory.GetSampleAtTime(Time1, bExtrapolate);
FTransformTrajectorySample Sample2 = InTrajectory.GetSampleAtTime(Time2, bExtrapolate);
OutVelocity = (Sample2.Position - Sample1.Position) / (Time2 - Time1);
}
void UPoseSearchTrajectoryLibrary::GetTransformTrajectoryAngularVelocity(const FTransformTrajectory& InTrajectory, float Time1, float Time2,
FVector& OutAngularVelocity, bool bExtrapolate)
{
if (FMath::IsNearlyEqual(Time1, Time2))
{
UE_LOG(LogPoseSearch, Warning, TEXT("UPoseSearchTrajectoryLibrary::GetTrajectoryAngularVelocity - Time1 is same as Time2. Invalid time horizon."));
OutAngularVelocity = FVector::ZeroVector;
return;
}
FTransformTrajectorySample Sample1 = InTrajectory.GetSampleAtTime(Time1, bExtrapolate);
FTransformTrajectorySample Sample2 = InTrajectory.GetSampleAtTime(Time2, bExtrapolate);
const FQuat DeltaRotation = (Sample2.Facing * Sample1.Facing.Inverse()).GetShortestArcWith(FQuat::Identity);
const FVector AngularVelocityInRadians = DeltaRotation.ToRotationVector() / (Time2 - Time1);
OutAngularVelocity = FVector(
FMath::RadiansToDegrees(AngularVelocityInRadians.X),
FMath::RadiansToDegrees(AngularVelocityInRadians.Y),
FMath::RadiansToDegrees(AngularVelocityInRadians.Z));
}
FTransform UPoseSearchTrajectoryLibrary::GetTransformTrajectorySampleTransform(const FTransformTrajectorySample& InTrajectorySample)
{
return InTrajectorySample.GetTransform();
}
void UPoseSearchTrajectoryLibrary::DrawTransformTrajectory(const UObject* WorldContextObject, const FTransformTrajectory& InTrajectory,
const float DebugThickness, float HeightOffset)
{
#if ENABLE_ANIM_DEBUG
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
{
UTransformTrajectoryBlueprintLibrary::DebugDrawTrajectory(InTrajectory, World, DebugThickness, HeightOffset);
}
#endif // ENABLE_ANIM_DEBUG
}