Files
UnrealEngine/Engine/Plugins/Animation/MotionWarping/Source/MotionWarping/Private/RootMotionModifier.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

673 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RootMotionModifier.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/World.h"
#include "MotionWarpingComponent.h"
#include "MotionWarpingAdapter.h"
#include "DrawDebugHelpers.h"
#include "UObject/FortniteMainBranchObjectVersion.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(RootMotionModifier)
// FMotionWarpingTarget
///////////////////////////////////////////////////////////////
FMotionWarpingTarget::FMotionWarpingTarget(const FName& InName, const USceneComponent* InComp, FName InBoneName, bool bInFollowComponent, EWarpTargetLocationOffsetDirection InLocationOffsetDirection, const AActor* InAvatarActor, const FVector& InLocOffset, const FRotator& InRotOffset)
{
if (ensure(InComp))
{
Name = InName;
Component = InComp;
BoneName = InBoneName;
bFollowComponent = bInFollowComponent;
LocationOffset = InLocOffset;
RotationOffset = InRotOffset;
AvatarActor = InAvatarActor;
LocationOffsetDirection = InLocationOffsetDirection;
FTransform Transform = FTransform::Identity;
if (BoneName != NAME_None)
{
Transform = FMotionWarpingTarget::GetTargetTransformFromComponent(InComp, InBoneName);
}
else
{
Transform = InComp->GetComponentTransform();
}
CacheOffset(Transform);
RecalculateOffset(Transform);
Location = Transform.GetLocation();
Rotation = Transform.Rotator();
}
}
FMotionWarpingTarget::FMotionWarpingTarget(const FName& InName, const USceneComponent* InComp, FName InBoneName, bool bInFollowComponent, const FVector& InLocOffset, const FRotator& InRotOffset)
: FMotionWarpingTarget(InName, InComp, InBoneName, bInFollowComponent, EWarpTargetLocationOffsetDirection::TargetsForwardVector, nullptr, InLocOffset, InRotOffset)
{
}
FTransform FMotionWarpingTarget::GetTargetTransformFromComponent(const USceneComponent* Comp, const FName& BoneName)
{
if (Comp == nullptr)
{
UE_LOG(LogMotionWarping, Warning, TEXT("FMotionWarpingTarget::GetTargetTransformFromComponent: Invalid Component"));
return FTransform::Identity;
}
if (Comp->DoesSocketExist(BoneName) == false)
{
UE_LOG(LogMotionWarping, Warning, TEXT("FMotionWarpingTarget::GetTargetTransformFromComponent: Invalid Bone or Socket. Comp: %s Owner: %s BoneName: %s"),
*GetNameSafe(Comp), *GetNameSafe(Comp->GetOwner()), *BoneName.ToString());
return Comp->GetComponentTransform();
}
return Comp->GetSocketTransform(BoneName);
}
void URootMotionModifier_Warp::Serialize(FArchive& Ar)
{
// Handle change of default blend type
Ar.UsingCustomVersion(FFortniteMainBranchObjectVersion::GUID);
if (Ar.IsLoading())
{
const int32 CustomVersion = Ar.CustomVer(FFortniteMainBranchObjectVersion::GUID);
if (CustomVersion < FFortniteMainBranchObjectVersion::ChangeDefaultAlphaBlendType)
{
// Switch the default back to Linear so old data remains the same
// Important: this is done before loading so if data has changed from default it still works
AddTranslationEasingFunc = EAlphaBlendOption::Linear;
}
}
Super::Serialize(Ar);
}
FTransform FMotionWarpingTarget::GetTargetTrasform() const
{
if (Component.IsValid() && bFollowComponent)
{
FTransform Transform = FTransform::Identity;
if (BoneName != NAME_None)
{
Transform = FMotionWarpingTarget::GetTargetTransformFromComponent(Component.Get(), BoneName);
}
else
{
Transform = Component->GetComponentTransform();
}
RecalculateOffset(Transform);
return FTransform(Transform.GetRotation(), Transform.GetLocation());
}
return FTransform(Rotation, Location);
}
/* Because vector from target to owner changes during warping, offset needs to be cached. */
void FMotionWarpingTarget::CacheOffset(const FTransform& InTransform)
{
// Forward offset doesn't need to be cached if it's the only one used. Otherwise, cache it too.
bCacheForwardOffset =
LocationOffsetDirection == EWarpTargetLocationOffsetDirection::VectorFromTargetToOwner &&
LocationOffset.X > SMALL_NUMBER &&
(LocationOffset.Y > SMALL_NUMBER || LocationOffset.Z > SMALL_NUMBER);
if (LocationOffsetDirection == EWarpTargetLocationOffsetDirection::VectorFromTargetToOwner)
{
if (AvatarActor.IsValid())
{
const FVector ContextVector = (AvatarActor->GetActorLocation() - InTransform.GetLocation()).GetSafeNormal();
const FVector RightVector = -FVector::CrossProduct(ContextVector, FVector::UpVector);
if (bCacheForwardOffset)
{
CachedForwardOffset = (ContextVector * LocationOffset.X);
}
CachedRightOffset = RightVector * LocationOffset.Y;
CachedUpOffset = -FVector::CrossProduct(RightVector, ContextVector) * LocationOffset.Z;
}
else
{
UE_LOG(LogMotionWarping, Warning, TEXT("Motion warping offset is set to VectorFromTargetToOwner but avator actor is invalid"))
}
}
}
void FMotionWarpingTarget::RecalculateOffset(FTransform& InTransform) const
{
FVector Offset = FVector::ZeroVector;
switch (LocationOffsetDirection)
{
case EWarpTargetLocationOffsetDirection::TargetsForwardVector:
Offset = LocationOffset;
break;
case EWarpTargetLocationOffsetDirection::VectorFromTargetToOwner:
if (AvatarActor.IsValid())
{
if (bCacheForwardOffset)
{
Offset = Component->GetComponentTransform().Inverse().TransformVector(CachedForwardOffset + CachedUpOffset + CachedRightOffset);
}
else
{
const FVector ContextVector = (AvatarActor->GetActorLocation() - InTransform.GetLocation()).GetSafeNormal();
const FVector ForwardOffset = (ContextVector * LocationOffset.X);
Offset = Component->GetComponentTransform().Inverse().TransformVector(ForwardOffset + CachedUpOffset + CachedRightOffset);
}
}
else
{
UE_LOG(LogMotionWarping, Warning, TEXT("Motion warping offset is set to VectorFromOwnerToTarget but avator actor is invalid"))
}
break;
case EWarpTargetLocationOffsetDirection::WorldSpace:
Offset = Component->GetComponentTransform().Inverse().TransformVector(LocationOffset);
break;
default:
checkNoEntry();
}
InTransform = FTransform(RotationOffset, Offset) * InTransform;
}
// URootMotionModifier
///////////////////////////////////////////////////////////////
URootMotionModifier::URootMotionModifier(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UMotionWarpingComponent* URootMotionModifier::GetOwnerComponent() const
{
return Cast<UMotionWarpingComponent>(GetOuter());
}
UMotionWarpingBaseAdapter* URootMotionModifier::GetOwnerAdapter() const
{
UMotionWarpingComponent* OwnerComp = GetOwnerComponent();
return OwnerComp ? OwnerComp->GetOwnerAdapter() : nullptr;
}
AActor* URootMotionModifier::GetActorOwner() const
{
if (UMotionWarpingBaseAdapter* OwnerAdapter = GetOwnerAdapter())
{
return OwnerAdapter->GetActor();
}
return nullptr;
}
ACharacter* URootMotionModifier::GetCharacterOwner() const
{
return Cast<ACharacter>(GetActorOwner());
}
void URootMotionModifier::Update(const FMotionWarpingUpdateContext& Context)
{
const AActor* ActorOwner = GetActorOwner();
if (ActorOwner == nullptr)
{
return;
}
// Mark for removal if our animation is not relevant anymore
if (!Context.Animation.IsValid() || Context.Animation.Get() != Animation)
{
UE_LOG(LogMotionWarping, Verbose, TEXT("MotionWarping: Marking RootMotionModifier for removal. Reason: Animation is not valid. Char: %s Current Animation: %s. Window: Animation: %s [%f %f] [%f %f]"),
*GetNameSafe(ActorOwner), *GetNameSafe(Context.Animation.Get()), *GetNameSafe(Animation.Get()), StartTime, EndTime, PreviousPosition, CurrentPosition);
SetState(ERootMotionModifierState::MarkedForRemoval);
return;
}
// Update playback times and weight
PreviousPosition = Context.PreviousPosition;
CurrentPosition = Context.CurrentPosition;
Weight = Context.Weight;
PlayRate = Context.PlayRate;
// Mark for removal if the animation already passed the warping window
if (PreviousPosition >= EndTime)
{
UE_LOG(LogMotionWarping, Verbose, TEXT("MotionWarping: Marking RootMotionModifier for removal. Reason: Window has ended. Char: %s Animation: %s [%f %f] [%f %f]"),
*GetNameSafe(ActorOwner), *GetNameSafe(Animation.Get()), StartTime, EndTime, PreviousPosition, CurrentPosition);
SetState(ERootMotionModifierState::MarkedForRemoval);
return;
}
// Mark for removal if we jumped to a position outside the warping window
if (State == ERootMotionModifierState::Active && PreviousPosition < EndTime && (CurrentPosition > EndTime || CurrentPosition < StartTime))
{
const float ExpectedDelta = Context.DeltaSeconds * Context.PlayRate;
const float ActualDelta = CurrentPosition - PreviousPosition;
if (!FMath::IsNearlyZero(FMath::Abs(ActualDelta - ExpectedDelta), KINDA_SMALL_NUMBER))
{
UE_LOG(LogMotionWarping, Verbose, TEXT("MotionWarping: Marking RootMotionModifier for removal. Reason: CurrentPosition manually changed. PrevPos: %f CurrPos: %f DeltaTime: %f ExpectedDelta: %f ActualDelta: %f"),
PreviousPosition, CurrentPosition, Context.DeltaSeconds, ExpectedDelta, ActualDelta);
SetState(ERootMotionModifierState::MarkedForRemoval);
return;
}
}
// Check if we are inside the warping window
if (PreviousPosition >= StartTime && PreviousPosition < EndTime)
{
// If we were waiting, switch to active
if (GetState() == ERootMotionModifierState::Waiting)
{
SetState(ERootMotionModifierState::Active);
}
}
if (State == ERootMotionModifierState::Active)
{
if (UMotionWarpingComponent* OwnerComp = GetOwnerComponent())
{
OnUpdateDelegate.ExecuteIfBound(OwnerComp, this);
}
}
}
void URootMotionModifier::SetState(ERootMotionModifierState NewState)
{
if (State != NewState)
{
ERootMotionModifierState LastState = State;
State = NewState;
OnStateChanged(LastState);
}
}
void URootMotionModifier::OnStateChanged(ERootMotionModifierState LastState)
{
if (UMotionWarpingComponent* OwnerComp = GetOwnerComponent())
{
if (LastState != ERootMotionModifierState::Active && State == ERootMotionModifierState::Active)
{
const UMotionWarpingBaseAdapter* OwnerAdapter = GetOwnerAdapter();
checkf(OwnerAdapter, TEXT("Root motion modifiers expect an owner and adapter"));
const FVector CurrentLocation = OwnerAdapter->GetVisualRootLocation();
const FQuat CurrentRotation = OwnerAdapter->GetActor()->GetActorQuat();
ActualStartTime = PreviousPosition;
StartTransform = FTransform(CurrentRotation, CurrentLocation);
TotalRootMotionWithinWindow = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(Animation.Get(), StartTime, EndTime);
OnActivateDelegate.ExecuteIfBound(OwnerComp, this);
}
else if (LastState == ERootMotionModifierState::Active && (State == ERootMotionModifierState::Disabled || State == ERootMotionModifierState::MarkedForRemoval))
{
OnDeactivateDelegate.ExecuteIfBound(OwnerComp, this);
}
}
}
URootMotionModifier_Warp::URootMotionModifier_Warp(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void URootMotionModifier_Warp::Update(const FMotionWarpingUpdateContext& Context)
{
// Update playback times and state
Super::Update(Context);
// Cache sync point transform and trigger OnTargetTransformChanged if needed
const UMotionWarpingComponent* OwnerComp = GetOwnerComponent();
if (OwnerComp && GetState() == ERootMotionModifierState::Active)
{
const FMotionWarpingTarget* WarpTargetPtr = OwnerComp->FindWarpTarget(WarpTargetName);
// Disable if there is no target for us
if (WarpTargetPtr == nullptr)
{
UE_LOG(LogMotionWarping, Verbose, TEXT("MotionWarping: Marking RootMotionModifier as Disabled. Reason: Invalid Warp Target (%s). Char: %s Animation: %s [%f %f] [%f %f]"),
*WarpTargetName.ToString(), *GetNameSafe(OwnerComp->GetOwner()), *GetNameSafe(Animation.Get()), StartTime, EndTime, PreviousPosition, CurrentPosition);
SetState(ERootMotionModifierState::Disabled);
return;
}
bRootMotionPaused = WarpTargetPtr->bRootMotionPaused;
bWarpingPaused = WarpTargetPtr->bWarpingPaused;
// Get the warp point sent by the game
FTransform WarpPointTransformGame = WarpTargetPtr->GetTargetTrasform();
// Cache the rotation offset to later apply when rotation warping
RotationOffset = WarpTargetPtr->RotationOffset.Quaternion();
// Initialize our target transform (where the root should end at the end of the window) with the warp point sent by the game
FTransform TargetTransform = WarpPointTransformGame;
// Check if a warp point is defined in the animation. If so, we need to extract it and offset the target transform
// the same amount the root bone is offset from the warp point in the animation
if (WarpPointAnimProvider != EWarpPointAnimProvider::None)
{
if (!CachedOffsetFromWarpPoint.IsSet())
{
if (const UMotionWarpingBaseAdapter* OwnerAdapter = GetOwnerAdapter())
{
if (WarpPointAnimProvider == EWarpPointAnimProvider::Static)
{
CachedOffsetFromWarpPoint = UMotionWarpingUtilities::CalculateRootTransformRelativeToWarpPointAtTime(*OwnerAdapter, GetAnimation(), EndTime, WarpPointAnimTransform);
}
else if (WarpPointAnimProvider == EWarpPointAnimProvider::Bone)
{
CachedOffsetFromWarpPoint = UMotionWarpingUtilities::CalculateRootTransformRelativeToWarpPointAtTime(*OwnerAdapter, GetAnimation(), EndTime, WarpPointAnimBoneName);
}
}
}
// Update Target Transform based on the offset between the root and the warp point in the animation
TargetTransform = CachedOffsetFromWarpPoint.GetValue() * WarpPointTransformGame;
}
CachedTargetTransform *= RootMotionRemainingAfterNotify.Inverse();
if (!CachedTargetTransform.Equals(TargetTransform))
{
CachedTargetTransform = TargetTransform;
OnTargetTransformChanged();
}
}
}
void URootMotionModifier_Warp::OnTargetTransformChanged()
{
if (const UMotionWarpingBaseAdapter* WarpingAdapter = GetOwnerAdapter())
{
ActualStartTime = PreviousPosition;
const FQuat CurrentRotation = WarpingAdapter->GetActor()->GetActorQuat();
const FVector CurrentLocation = WarpingAdapter->GetVisualRootLocation();
StartTransform = FTransform(CurrentRotation, CurrentLocation);
}
}
void URootMotionModifier_Warp::OnStateChanged(ERootMotionModifierState LastState)
{
Super::OnStateChanged(LastState);
if (bSubtractRemainingRootMotion)
{
RootMotionRemainingAfterNotify = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(Animation.Get(), EndTime, Animation.Get()->GetPlayLength());
}
}
FQuat URootMotionModifier_Warp::GetTargetRotation() const
{
switch (RotationType)
{
case EMotionWarpRotationType::Default:
return CachedTargetTransform.GetRotation();
case EMotionWarpRotationType::Facing:
if (const AActor* ActorOwner = GetActorOwner())
{
const FTransform& ActorTransform = ActorOwner->GetActorTransform();
const FVector ToSyncPoint = (CachedTargetTransform.GetLocation() - ActorTransform.GetLocation()).GetSafeNormal2D();
return RotationOffset * FRotationMatrix::MakeFromXZ(ToSyncPoint, FVector::UpVector).ToQuat();
}
break;
case EMotionWarpRotationType::OppositeDefault:
return FRotationMatrix::MakeFromXZ(CachedTargetTransform.GetRotation().GetForwardVector() * -1, CachedTargetTransform.GetRotation().GetUpVector()).ToQuat();
case EMotionWarpRotationType::OppositeFacing:
if (const AActor* ActorOwner = GetActorOwner())
{
const FTransform& ActorTransform = ActorOwner->GetActorTransform();
const FVector ToSyncPoint = (ActorTransform.GetLocation() - CachedTargetTransform.GetLocation()).GetSafeNormal2D();
return RotationOffset * FRotationMatrix::MakeFromXZ(ToSyncPoint, FVector::UpVector).ToQuat();
}
break;
default:
checkNoEntry();
}
return FQuat::Identity;
}
FQuat URootMotionModifier_Warp::WarpRotation(const FTransform& RootMotionDelta, const FTransform& RootMotionTotal, float DeltaSeconds)
{
if (bRootMotionPaused)
{
return FQuat::Identity;
}
if (bWarpingPaused)
{
return RootMotionDelta.GetRotation();
}
FQuat CurrentRotation;
FQuat TargetRotation;
if (const UMotionWarpingBaseAdapter* WarpingAdapter = GetOwnerAdapter())
{
CurrentRotation = WarpingAdapter->GetActor()->GetActorQuat() * WarpingAdapter->GetBaseVisualRotationOffset();
TargetRotation = CurrentRotation.Inverse() * (GetTargetRotation() * WarpingAdapter->GetBaseVisualRotationOffset() * RootMotionRemainingAfterNotify.GetRotation().Inverse());
}
else
{
// No owner, no warping possible
return FQuat::Identity;
}
const FQuat TotalRootMotionRotation = RootMotionTotal.GetRotation();
if (RotationMethod == EMotionWarpRotationMethod::Scale)
{
FRotator TotalRotator(TotalRootMotionRotation);
FRotator TargetRotator(TargetRotation);
const double YawDiff = FMath::FindDeltaAngleDegrees(TotalRotator.Yaw, TargetRotator.Yaw);
const double PitchDiff = FMath::FindDeltaAngleDegrees(TotalRotator.Pitch, TargetRotator.Pitch);
// To properly compute scale factor target rotation needs to be relative to total rotation.
// To avoid cases like 170 & -170 resulting in -1 scale factor rather than 1.11.
const double YawScale = FMath::IsNearlyZero(TotalRotator.Yaw) ? 0.0 : (TotalRotator.Yaw + YawDiff) / TotalRotator.Yaw;
const double PitchScale = FMath::IsNearlyZero(TotalRotator.Pitch) ? 0.0 : (TotalRotator.Pitch + PitchDiff) / TotalRotator.Pitch;
const float MaxRotation = WarpMaxRotationRate * DeltaSeconds;
FRotator ScaledDeltaRotation(RootMotionDelta.GetRotation());
ScaledDeltaRotation.Yaw = FMath::Clamp(ScaledDeltaRotation.Yaw * YawScale, -MaxRotation, MaxRotation);
ScaledDeltaRotation.Pitch = FMath::Clamp(ScaledDeltaRotation.Pitch * PitchScale, -MaxRotation, MaxRotation);
return ScaledDeltaRotation.Quaternion();
}
const float TimeRemaining = (EndTime - PreviousPosition) * WarpRotationTimeMultiplier;
const float PlayRateAdjustedDeltaSeconds = DeltaSeconds * PlayRate;
const float Alpha = FMath::Clamp(PlayRateAdjustedDeltaSeconds / TimeRemaining, 0.f, 1.f);
FQuat TargetRotThisFrame = FQuat::Slerp(TotalRootMotionRotation, TargetRotation, Alpha);
if (RotationMethod != EMotionWarpRotationMethod::Slerp)
{
const float AngleDeltaThisFrame = TotalRootMotionRotation.AngularDistance(TargetRotThisFrame);
const float MaxAngleDelta = FMath::Abs(FMath::DegreesToRadians(PlayRateAdjustedDeltaSeconds * WarpMaxRotationRate));
const float TotalAngleDelta = TotalRootMotionRotation.AngularDistance(TargetRotation);
if (RotationMethod == EMotionWarpRotationMethod::ConstantRate && (TotalAngleDelta <= MaxAngleDelta))
{
TargetRotThisFrame = TargetRotation;
}
else if ((AngleDeltaThisFrame > MaxAngleDelta) || RotationMethod == EMotionWarpRotationMethod::ConstantRate)
{
const FVector CrossProduct = FVector::CrossProduct(TotalRootMotionRotation.Vector(), TargetRotation.Vector());
const float SignDirection = FMath::Sign(CrossProduct.Z);
const FQuat ClampedRotationThisFrame = FQuat(FVector(0, 0, 1), MaxAngleDelta * SignDirection);
TargetRotThisFrame = ClampedRotationThisFrame;
}
}
const FQuat DeltaOut = TargetRotThisFrame * TotalRootMotionRotation.Inverse();
return (DeltaOut * RootMotionDelta.GetRotation());
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
void URootMotionModifier_Warp::PrintLog(const FString& Name, const FTransform& OriginalRootMotion, const FTransform& WarpedRootMotion) const
{
const AActor* ActorOwner = nullptr;
FVector CurrentLocation;
USkeletalMeshComponent* SkelMesh = nullptr;
if (const UMotionWarpingBaseAdapter* WarpingAdapter = GetOwnerAdapter())
{
ActorOwner = WarpingAdapter->GetActor();
SkelMesh = WarpingAdapter->GetMesh();
CurrentLocation = WarpingAdapter->GetVisualRootLocation();
}
if (ActorOwner && SkelMesh)
{
const FVector CurrentToTarget = (GetTargetLocation() - CurrentLocation).GetSafeNormal2D();
const FVector FutureLocation = CurrentLocation + (SkelMesh->ConvertLocalRootMotionToWorld(WarpedRootMotion)).GetTranslation();
const FRotator CurrentRotation = ActorOwner->GetActorRotation();
const FRotator FutureRotation = (WarpedRootMotion.GetRotation() * ActorOwner->GetActorQuat()).Rotator();
const float Dot = FVector::DotProduct(ActorOwner->GetActorForwardVector(), CurrentToTarget);
const float CurrentDist2D = FVector::Dist2D(GetTargetLocation(), CurrentLocation);
const float FutureDist2D = FVector::Dist2D(GetTargetLocation(), FutureLocation);
const float DeltaSeconds = ActorOwner->GetWorld()->GetDeltaSeconds();
const float Speed = WarpedRootMotion.GetTranslation().Size() / DeltaSeconds;
const float EndTimeOffset = CurrentPosition - EndTime;
UE_LOG(LogMotionWarping, Log, TEXT("%s NetMode: %d Char: %s Anim: %s Win: [%f %f][%f %f] DT: %f WT: %f ETOffset: %f Dist2D: %f Z: %f FDist2D: %f FZ: %f Dot: %f Delta: %s (%f) FDelta: %s (%f) Speed: %f Loc: %s FLoc: %s Rot: %s FRot: %s"),
*Name, (int32)ActorOwner->GetWorld()->GetNetMode(), *GetNameSafe(ActorOwner), *GetNameSafe(Animation.Get()), StartTime, EndTime, PreviousPosition, CurrentPosition, DeltaSeconds, ActorOwner->GetWorld()->GetTimeSeconds(), EndTimeOffset,
CurrentDist2D, (GetTargetLocation().Z - CurrentLocation.Z), FutureDist2D, (GetTargetLocation().Z - FutureLocation.Z), Dot,
*OriginalRootMotion.GetTranslation().ToString(), OriginalRootMotion.GetTranslation().Size(), *WarpedRootMotion.GetTranslation().ToString(), WarpedRootMotion.GetTranslation().Size(), Speed,
*CurrentLocation.ToString(), *FutureLocation.ToString(), *CurrentRotation.ToCompactString(), *FutureRotation.ToCompactString());
}
}
#endif
// URootMotionModifier_SimpleWarp
///////////////////////////////////////////////////////////////
UDEPRECATED_RootMotionModifier_SimpleWarp::UDEPRECATED_RootMotionModifier_SimpleWarp(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FTransform UDEPRECATED_RootMotionModifier_SimpleWarp::ProcessRootMotion(const FTransform& InRootMotion, float DeltaSeconds)
{
const ACharacter* CharacterOwner = nullptr;
if (const UMotionWarpingBaseAdapter* Adapter = GetOwnerAdapter())
{
CharacterOwner = Cast<ACharacter>(Adapter->GetActor());
}
if (CharacterOwner == nullptr)
{
return InRootMotion;
}
const FTransform& CharacterTransform = CharacterOwner->GetActorTransform();
FTransform FinalRootMotion = InRootMotion;
const FTransform RootMotionTotal = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(Animation.Get(), PreviousPosition, EndTime);
if (bWarpTranslation)
{
FVector DeltaTranslation = InRootMotion.GetTranslation();
const FTransform RootMotionDelta = UMotionWarpingUtilities::ExtractRootMotionFromAnimation(Animation.Get(), PreviousPosition, FMath::Min(CurrentPosition, EndTime));
const float HorizontalDelta = RootMotionDelta.GetTranslation().Size2D();
const float HorizontalTarget = FVector::Dist2D(CharacterTransform.GetLocation(), GetTargetLocation());
const float HorizontalOriginal = RootMotionTotal.GetTranslation().Size2D();
const float HorizontalTranslationWarped = !FMath::IsNearlyZero(HorizontalOriginal) ? ((HorizontalDelta * HorizontalTarget) / HorizontalOriginal) : 0.f;
const FTransform MeshRelativeTransform = FTransform(CharacterOwner->GetBaseRotationOffset(), CharacterOwner->GetBaseTranslationOffset());
const FTransform MeshTransform = MeshRelativeTransform * CharacterOwner->GetActorTransform();
DeltaTranslation = MeshTransform.InverseTransformPositionNoScale(GetTargetLocation()).GetSafeNormal2D() * HorizontalTranslationWarped;
if (!bIgnoreZAxis)
{
const float CapsuleHalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
const FVector CapsuleBottomLocation = (CharacterOwner->GetActorLocation() - FVector(0.f, 0.f, CapsuleHalfHeight));
const float VerticalDelta = RootMotionDelta.GetTranslation().Z;
const float VerticalTarget = GetTargetLocation().Z - CapsuleBottomLocation.Z;
const float VerticalOriginal = RootMotionTotal.GetTranslation().Z;
const float VerticalTranslationWarped = !FMath::IsNearlyZero(VerticalOriginal) ? ((VerticalDelta * VerticalTarget) / VerticalOriginal) : 0.f;
DeltaTranslation.Z = VerticalTranslationWarped;
}
else
{
DeltaTranslation.Z = InRootMotion.GetTranslation().Z;
}
FinalRootMotion.SetTranslation(DeltaTranslation);
}
if (bWarpRotation)
{
const FQuat WarpedRotation = WarpRotation(InRootMotion, RootMotionTotal, DeltaSeconds);
FinalRootMotion.SetRotation(WarpedRotation);
}
// Debug
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
const int32 DebugLevel = FMotionWarpingCVars::CVarMotionWarpingDebug.GetValueOnGameThread();
if (DebugLevel == 1 || DebugLevel == 3)
{
PrintLog(TEXT("SimpleWarp"), InRootMotion, FinalRootMotion);
}
if (DebugLevel == 2 || DebugLevel == 3)
{
const float DrawDebugDuration = FMotionWarpingCVars::CVarMotionWarpingDrawDebugDuration.GetValueOnGameThread();
DrawDebugCoordinateSystem(CharacterOwner->GetWorld(), GetTargetLocation(), GetTargetRotator(), 50.f, false, DrawDebugDuration, 0, 1.f);
}
#endif
return FinalRootMotion;
}
// URootMotionModifier_Scale
///////////////////////////////////////////////////////////////
URootMotionModifier_Scale* URootMotionModifier_Scale::AddRootMotionModifierScale(UMotionWarpingComponent* InMotionWarpingComp, const UAnimSequenceBase* InAnimation, float InStartTime, float InEndTime, FVector InScale)
{
if (ensureAlways(InMotionWarpingComp))
{
URootMotionModifier_Scale* NewModifier = NewObject<URootMotionModifier_Scale>(InMotionWarpingComp);
NewModifier->Animation = InAnimation;
NewModifier->StartTime = InStartTime;
NewModifier->EndTime = InEndTime;
NewModifier->Scale = InScale;
InMotionWarpingComp->AddModifier(NewModifier);
return NewModifier;
}
return nullptr;
}