You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2775736 on 2015/11/20 by Richard.Hinckley
Fix for Paper2D issue with repeated imports in one edutor session. Paper2D import process now creates a new importer at the end. This prevents the sprite sheet import process from leaving frame data around, causing subsequent imports (including imports of different sprite sheets) to include this data inappropriately.
#codereview michael.noland
Change 2776352 on 2015/11/20 by Zak.Middleton
#ue4 - Avoid useless DetachFromParent() for the same pending AttachParent during registration. Added missing UpdateOverlaps() when detaching from object simulating physics.
#rb Marc.Audy, Ori.Cohen
#codereview James.Golding
Change 2776401 on 2015/11/20 by Mieszko.Zielinski
Implemented a way to do batched points projection to navmesh, where every point can declare a custom projection box #UE4
The biggest advantage here is that projection box is independent from projected point - no more manual offsetting of projected point to achieve "100uu up and 500uu down"-like functionality
#jira UE-23705
#rb Lukasz.Furman
Change 2777450 on 2015/11/23 by Martin.Wilson
Bake additive data into animations during cooking to avoid doing additive calculations and extra pose extraction and blending at runtime
#rb Thomas.Sarkanen
Change 2777698 on 2015/11/23 by Mieszko.Zielinski
Gameplay debugging tools fixes #UE4
Fixes:
- made newly added logs respect Log Visualizer's filters
- added handling of invalid data when trying to draw EGameplayDebuggerShapeElement::Cylinder in AGameplayDebuggingHUDComponent::DrawPerception. This is a patch, root cause to be found.
- fixed Log Visualizer resetting it's data while trying to serialize invalid objects. This is a patch, root cause to be addressed.
In addition
- while at it removed bunch of 'auto' and 'class' keywords from the files I've touched
#rb Lukasz.Furman
Change 2777762 on 2015/11/23 by Mieszko.Zielinski
Removed BlackboardComponent's functionality deprecated since 4.7 #UE4
#rb Lukasz.Furman
Change 2777839 on 2015/11/23 by Zak.Middleton
#ue4 - Wrap all vector library calls to math functions through our FMath versions, so they benefit from fixes or improvements therein. Added Exp2() function.
#rb Laurent.Delayen
Change 2777840 on 2015/11/23 by Zak.Middleton
#ue4 - Fix up uses of library math functions to go through our FMath namespace.
#rb Laurent.Delayen
Change 2778287 on 2015/11/23 by Stan.Melax
deprecation of FCollisionQueryParams(bool)
See 2774707 description for the whole story
#OR-9936
#codereview marc.audy
Changes to kite will have to be in a separate check-in
I couldn't submit to all files from the framework branch addition fixes have their files are shelved in cl 2778299
Change 2778507 on 2015/11/23 by Marc.Audy
Eliminate spurious cook warnings for known missing packages
#rb Michael.Noland
Change 2778546 on 2015/11/23 by Aaron.McLeran
Moving occlusion feature settings from audio component to sound attenuation settings struct.
- Sound attenuation setting struct is used for all sounds that do 3d spatialization so it make sense for the occlusion feature settings to be there.
- Kept old low-pass frequency filter setting values on audio component (where HighFrequencyAttenuation used to be)
#rb Zak.Middleton
Change 2778664 on 2015/11/23 by Zak.Middleton
#ue4 - Clarify some comparison functions (IsZero, IsNearlyZero, Equals) in FRotator to explain that they compare as orientations, not other interpretations such as rotational speed, winding, etc.
#rb Aaron.Mcleran
#codereview Frank.Gigliotti
Change 2779335 on 2015/11/24 by Mieszko.Zielinski
Another VisualLog patch to avoid crashing due to a core bug that remains to be investigated #UE4
Again, the core bug here is related visual log trying to serialize invalid objects on a regular basis.
#rb Lukasz.Furman
Change 2779338 on 2015/11/24 by Benn.Gallagher
Fixed crash in Persona when focus is taken from a different window
#jira UE-22516
#rb Ben.Cosh
Change 2779375 on 2015/11/24 by Benn.Gallagher
Fix for deadlock in destructibles. Aquiring actor buffer without releasing causes an infinite wait on next aquire.
#rb Ori.Cohen
Change 2779753 on 2015/11/24 by Zak.Middleton
#ue4 - FMath::Atan2() no longer calls atan2f() because of some compiler or library bugs causing it to randomly return NaN for valid input. It now uses a high-precision minimax approximation instead, measured to be 2x faster than the stock C version.
#rb Brian.Karis
Change 2779853 on 2015/11/24 by Marc.Audy
1105 lines
34 KiB
C++
1105 lines
34 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "AnimGraphPrivatePCH.h"
|
|
#include "AnimPreviewInstance.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "ScopedTransaction.h"
|
|
#endif
|
|
|
|
#define LOCTEXT_NAMESPACE "AnimPreviewInstance"
|
|
|
|
void FAnimPreviewInstanceProxy::Initialize(UAnimInstance* InAnimInstance)
|
|
{
|
|
FAnimSingleNodeInstanceProxy::Initialize(InAnimInstance);
|
|
|
|
bSetKey = false;
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::ResetModifiedBone(bool bCurveController)
|
|
{
|
|
TArray<FAnimNode_ModifyBone>& Controllers = (bCurveController)?CurveBoneControllers : BoneControllers;
|
|
Controllers.Empty();
|
|
}
|
|
|
|
FAnimNode_ModifyBone* FAnimPreviewInstanceProxy::FindModifiedBone(const FName& InBoneName, bool bCurveController)
|
|
{
|
|
TArray<FAnimNode_ModifyBone>& Controllers = (bCurveController)?CurveBoneControllers : BoneControllers;
|
|
|
|
return Controllers.FindByPredicate(
|
|
[InBoneName](const FAnimNode_ModifyBone& InController) -> bool
|
|
{
|
|
return InController.BoneToModify.BoneName == InBoneName;
|
|
}
|
|
);
|
|
}
|
|
|
|
FAnimNode_ModifyBone& FAnimPreviewInstanceProxy::ModifyBone(const FName& InBoneName, bool bCurveController)
|
|
{
|
|
FAnimNode_ModifyBone* SingleBoneController = FindModifiedBone(InBoneName, bCurveController);
|
|
TArray<FAnimNode_ModifyBone>& Controllers = (bCurveController)?CurveBoneControllers : BoneControllers;
|
|
|
|
if(SingleBoneController == nullptr)
|
|
{
|
|
int32 NewIndex = Controllers.Add(FAnimNode_ModifyBone());
|
|
SingleBoneController = &Controllers[NewIndex];
|
|
}
|
|
|
|
SingleBoneController->BoneToModify.BoneName = InBoneName;
|
|
|
|
if (bCurveController)
|
|
{
|
|
SingleBoneController->TranslationMode = BMM_Additive;
|
|
SingleBoneController->TranslationSpace = BCS_BoneSpace;
|
|
|
|
SingleBoneController->RotationMode = BMM_Additive;
|
|
SingleBoneController->RotationSpace = BCS_BoneSpace;
|
|
|
|
SingleBoneController->ScaleMode = BMM_Additive;
|
|
SingleBoneController->ScaleSpace = BCS_BoneSpace;
|
|
}
|
|
else
|
|
{
|
|
SingleBoneController->TranslationMode = BMM_Replace;
|
|
SingleBoneController->TranslationSpace = BCS_BoneSpace;
|
|
|
|
SingleBoneController->RotationMode = BMM_Replace;
|
|
SingleBoneController->RotationSpace = BCS_BoneSpace;
|
|
|
|
SingleBoneController->ScaleMode = BMM_Replace;
|
|
SingleBoneController->ScaleSpace = BCS_BoneSpace;
|
|
}
|
|
|
|
return *SingleBoneController;
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::RemoveBoneModification(const FName& InBoneName, bool bCurveController)
|
|
{
|
|
TArray<FAnimNode_ModifyBone>& Controllers = (bCurveController)?CurveBoneControllers : BoneControllers;
|
|
Controllers.RemoveAll(
|
|
[InBoneName](const FAnimNode_ModifyBone& InController)
|
|
{
|
|
return InController.BoneToModify.BoneName == InBoneName;
|
|
}
|
|
);
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::Update(float DeltaSeconds)
|
|
{
|
|
// we cant update on a worker thread here because of the key delegate needing to be fired
|
|
check(IsInGameThread());
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
if(bForceRetargetBasePose)
|
|
{
|
|
// nothing to be done here
|
|
return;
|
|
}
|
|
#endif // #if WITH_EDITORONLY_DATA
|
|
|
|
FAnimSingleNodeInstanceProxy::Update(DeltaSeconds);
|
|
}
|
|
|
|
bool FAnimPreviewInstanceProxy::Evaluate(FPoseContext& Output)
|
|
{
|
|
// we cant evaluate on a worker thread here because of the key delegate needing to be fired
|
|
check(IsInGameThread());
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
if(bForceRetargetBasePose)
|
|
{
|
|
USkeletalMeshComponent* MeshComponent = Output.AnimInstanceProxy->GetSkelMeshComponent();
|
|
if(MeshComponent && MeshComponent->SkeletalMesh)
|
|
{
|
|
FAnimationRuntime::FillWithRetargetBaseRefPose(Output.Pose, GetSkelMeshComponent()->SkeletalMesh);
|
|
}
|
|
else
|
|
{
|
|
// ideally we'll return just ref pose, but not sure if this will work with LODs
|
|
Output.Pose.ResetToRefPose();
|
|
}
|
|
}
|
|
else
|
|
#endif // #if WITH_EDITORONLY_DATA
|
|
{
|
|
FAnimSingleNodeInstanceProxy::Evaluate(Output);
|
|
}
|
|
|
|
if (bEnableControllers)
|
|
{
|
|
UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent>(GetSkelMeshComponent());
|
|
|
|
if(Component && GetSkeleton())
|
|
{
|
|
// update curve controllers
|
|
UpdateCurveController();
|
|
|
|
// create bone controllers from
|
|
if(BoneControllers.Num() > 0 || CurveBoneControllers.Num() > 0)
|
|
{
|
|
FPoseContext PreController(Output), PostController(Output);
|
|
// if set key is true, we should save pre controller local space transform
|
|
// so that we can calculate the delta correctly
|
|
if(bSetKey)
|
|
{
|
|
PreController = Output;
|
|
}
|
|
|
|
FCSPose<FCompactPose> OutMeshPose;
|
|
OutMeshPose.InitPose(Output.Pose);
|
|
|
|
// apply curve data first
|
|
ApplyBoneControllers(Component, CurveBoneControllers, OutMeshPose);
|
|
|
|
// and now apply bone controllers data
|
|
// it is possible they can be overlapping, but then bone controllers will overwrite
|
|
ApplyBoneControllers(Component, BoneControllers, OutMeshPose);
|
|
|
|
// convert back to local @todo check this
|
|
OutMeshPose.ConvertToLocalPoses(Output.Pose);
|
|
|
|
if(bSetKey)
|
|
{
|
|
// now we have post controller, and calculate delta now
|
|
PostController = Output;
|
|
SetKeyImplementation(PreController.Pose, PostController.Pose);
|
|
}
|
|
}
|
|
// if any other bone is selected, still go for set key even if nothing changed
|
|
else if(Component->BonesOfInterest.Num() > 0)
|
|
{
|
|
if(bSetKey)
|
|
{
|
|
// in this case, pose is same
|
|
SetKeyImplementation(Output.Pose, Output.Pose);
|
|
}
|
|
}
|
|
}
|
|
|
|
// we should unset here, just in case somebody clicks the key when it's not valid
|
|
if(bSetKey)
|
|
{
|
|
bSetKey = false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::RefreshCurveBoneControllers()
|
|
{
|
|
// go through all curves and see if it has Transform Curve
|
|
// if so, find what bone that belong to and create BoneMOdifier for them
|
|
UAnimSequence* CurrentSequence = Cast<UAnimSequence>(GetCurrentAsset());
|
|
|
|
CurveBoneControllers.Empty();
|
|
|
|
// do not apply if BakedAnimation is on
|
|
if(CurrentSequence)
|
|
{
|
|
// make sure if this needs source update
|
|
if ( !CurrentSequence->DoesContainTransformCurves() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetRequiredBones().SetUseSourceData(true);
|
|
|
|
TArray<FTransformCurve>& Curves = CurrentSequence->RawCurveData.TransformCurves;
|
|
const FSmartNameMapping* NameMapping = GetSkeleton()->GetSmartNameContainer(USkeleton::AnimTrackCurveMappingName);
|
|
|
|
for (auto& Curve : Curves)
|
|
{
|
|
// skip if disabled
|
|
if (Curve.GetCurveTypeFlag(ACF_Disabled))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// add bone modifier
|
|
FName CurveName;
|
|
NameMapping->GetName(Curve.CurveUid, CurveName);
|
|
|
|
// @TODO: this is going to be issue. If they don't save skeleton with it, we don't have name at all?
|
|
if (CurveName == NAME_None)
|
|
{
|
|
FSmartNameMapping::UID NewUID;
|
|
GetSkeleton()->AddSmartNameAndModify(USkeleton::AnimTrackCurveMappingName, Curve.LastObservedName, NewUID);
|
|
Curve.CurveUid = NewUID;
|
|
|
|
CurveName = Curve.LastObservedName;
|
|
}
|
|
|
|
FName BoneName = CurveName;
|
|
if (BoneName != NAME_None && GetSkeleton()->GetReferenceSkeleton().FindBoneIndex(BoneName) != INDEX_NONE)
|
|
{
|
|
ModifyBone(BoneName, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::UpdateCurveController()
|
|
{
|
|
// evaluate the curve data first
|
|
UAnimSequenceBase* CurrentSequence = Cast<UAnimSequenceBase>(GetCurrentAsset());
|
|
|
|
if (CurrentSequence && GetSkeleton())
|
|
{
|
|
TMap<FName, FTransform> ActiveCurves;
|
|
CurrentSequence->RawCurveData.EvaluateTransformCurveData(GetSkeleton(), ActiveCurves, GetCurrentTime(), 1.f);
|
|
|
|
// make sure those curves exists in the bone controller, otherwise problem
|
|
if ( ActiveCurves.Num() > 0 )
|
|
{
|
|
for(auto& SingleBoneController : CurveBoneControllers)
|
|
{
|
|
// make sure the curve exists
|
|
FName CurveName = SingleBoneController.BoneToModify.BoneName;
|
|
|
|
// we should add extra key to front and back whenever animation length changes or so.
|
|
// animation length change requires to bake down animation first
|
|
// this will make sure all the keys that were embedded at the start/end will automatically be backed to the data
|
|
const FTransform* Value = ActiveCurves.Find(CurveName);
|
|
if (Value)
|
|
{
|
|
// apply this change
|
|
SingleBoneController.Translation = Value->GetTranslation();
|
|
SingleBoneController.Scale = Value->GetScale3D();
|
|
// sasd we're converting twice
|
|
SingleBoneController.Rotation = Value->GetRotation().Rotator();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// should match
|
|
ensure (CurveBoneControllers.Num() == 0);
|
|
CurveBoneControllers.Empty();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::ApplyBoneControllers(USkeletalMeshComponent* Component, TArray<FAnimNode_ModifyBone> &InBoneControllers, FCSPose<FCompactPose>& OutMeshPose)
|
|
{
|
|
for(auto& SingleBoneController : InBoneControllers)
|
|
{
|
|
SingleBoneController.BoneToModify.BoneIndex = GetRequiredBones().GetPoseBoneIndexForBoneName(SingleBoneController.BoneToModify.BoneName);
|
|
if(SingleBoneController.BoneToModify.BoneIndex != INDEX_NONE)
|
|
{
|
|
TArray<FBoneTransform> BoneTransforms;
|
|
SingleBoneController.EvaluateBoneTransforms(Component, OutMeshPose, BoneTransforms);
|
|
if(BoneTransforms.Num() > 0)
|
|
{
|
|
OutMeshPose.LocalBlendCSBoneTransforms(BoneTransforms, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::SetKeyImplementation(const FCompactPose& PreControllerInLocalSpace, const FCompactPose& PostControllerInLocalSpace)
|
|
{
|
|
#if WITH_EDITOR
|
|
// evaluate the curve data first
|
|
UAnimSequence* CurrentSequence = Cast<UAnimSequence>(GetCurrentAsset());
|
|
UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent> (GetSkelMeshComponent());
|
|
|
|
if(CurrentSequence && GetSkeleton() && Component && Component->SkeletalMesh)
|
|
{
|
|
FScopedTransaction ScopedTransaction(LOCTEXT("SetKey", "Set Key"));
|
|
CurrentSequence->Modify(true);
|
|
GetAnimInstanceObject()->Modify();
|
|
|
|
TArray<FName> BonesToModify;
|
|
// need to get component transform first. Depending on when this gets called, the transform is not up-to-date.
|
|
// first look at the bonecontrollers, and convert each bone controller to transform curve key
|
|
// and add new curvebonecontrollers with additive data type
|
|
// clear bone controller data
|
|
for(auto& SingleBoneController : BoneControllers)
|
|
{
|
|
// find bone name, and just get transform of the bone in local space
|
|
// and get the additive data
|
|
// find if this already exists, then just add curve data only
|
|
FName BoneName = SingleBoneController.BoneToModify.BoneName;
|
|
// now convert data
|
|
const FMeshPoseBoneIndex MeshBoneIndex(Component->GetBoneIndex(BoneName));
|
|
const FCompactPoseBoneIndex BoneIndex = GetRequiredBones().MakeCompactPoseIndex(MeshBoneIndex);
|
|
FTransform LocalTransform = PostControllerInLocalSpace[BoneIndex];
|
|
|
|
// now we have LocalTransform and get additive data
|
|
FTransform AdditiveTransform = LocalTransform.GetRelativeTransform(PreControllerInLocalSpace[BoneIndex]);
|
|
AddKeyToSequence(CurrentSequence, GetCurrentTime(), BoneName, AdditiveTransform);
|
|
|
|
BonesToModify.Add(BoneName);
|
|
}
|
|
|
|
// see if the bone is selected right now and if that is added - if bone is selected, we should add identity key to it.
|
|
if ( Component->BonesOfInterest.Num() > 0 )
|
|
{
|
|
// if they're selected, we should add to the modifyBone list even if they're not modified, so that they can key that point.
|
|
// first make sure those are added
|
|
// if not added, make sure to set the key for them
|
|
for (const auto& BoneIndex : Component->BonesOfInterest)
|
|
{
|
|
FName BoneName = Component->GetBoneName(BoneIndex);
|
|
// if it's not on BonesToModify, add identity here.
|
|
if (!BonesToModify.Contains(BoneName))
|
|
{
|
|
AddKeyToSequence(CurrentSequence, GetCurrentTime(), BoneName, FTransform::Identity);
|
|
}
|
|
}
|
|
}
|
|
|
|
ResetModifiedBone(false);
|
|
|
|
OnSetKeyCompleteDelegate.ExecuteIfBound();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FAnimPreviewInstanceProxy::AddKeyToSequence(UAnimSequence* Sequence, float Time, const FName& BoneName, const FTransform& AdditiveTransform)
|
|
{
|
|
Sequence->AddKeyToSequence(Time, BoneName, AdditiveTransform);
|
|
|
|
// now add to the controller
|
|
// find if it exists in CurveBoneController
|
|
// make sure you add it there
|
|
ModifyBone(BoneName, true);
|
|
|
|
GetRequiredBones().SetUseSourceData(true);
|
|
}
|
|
|
|
UAnimPreviewInstance::UAnimPreviewInstance(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
RootMotionMode = ERootMotionMode::RootMotionFromEverything;
|
|
bCanUseParallelUpdateAnimation = false;
|
|
}
|
|
|
|
static FArchive& operator<<(FArchive& Ar, FAnimNode_ModifyBone& ModifyBone)
|
|
{
|
|
FAnimNode_ModifyBone::StaticStruct()->SerializeItem(Ar, &ModifyBone, nullptr);
|
|
return Ar;
|
|
}
|
|
|
|
void UAnimPreviewInstance::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
if(Ar.IsTransacting())
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
Ar << Proxy.GetBoneControllers();
|
|
Ar << Proxy.GetCurveBoneControllers();
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::NativeInitializeAnimation()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
// Cache our play state from the previous animation otherwise set to play
|
|
bool bCachedIsPlaying = (Proxy.GetCurrentAsset() != NULL) ? Proxy.IsPlaying() : true;
|
|
|
|
Super::NativeInitializeAnimation();
|
|
|
|
Proxy.SetPlaying(bCachedIsPlaying);
|
|
|
|
Proxy.RefreshCurveBoneControllers();
|
|
}
|
|
|
|
FAnimNode_ModifyBone* UAnimPreviewInstance::FindModifiedBone(const FName& InBoneName, bool bCurveController/*=false*/)
|
|
{
|
|
return GetProxyOnGameThread<FAnimPreviewInstanceProxy>().FindModifiedBone(InBoneName, bCurveController);
|
|
}
|
|
|
|
FAnimNode_ModifyBone& UAnimPreviewInstance::ModifyBone(const FName& InBoneName, bool bCurveController/*=false*/)
|
|
{
|
|
return GetProxyOnGameThread<FAnimPreviewInstanceProxy>().ModifyBone(InBoneName, bCurveController);
|
|
}
|
|
|
|
void UAnimPreviewInstance::RemoveBoneModification(const FName& InBoneName, bool bCurveController/*=false*/)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().RemoveBoneModification(InBoneName, bCurveController);
|
|
}
|
|
|
|
void UAnimPreviewInstance::ResetModifiedBone(bool bCurveController/*=false*/)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().ResetModifiedBone(bCurveController);
|
|
}
|
|
|
|
void UAnimPreviewInstance::SetKey(FSimpleDelegate InOnSetKeyCompleteDelegate)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetKey(InOnSetKeyCompleteDelegate);
|
|
}
|
|
|
|
void UAnimPreviewInstance::RefreshCurveBoneControllers()
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().RefreshCurveBoneControllers();
|
|
}
|
|
|
|
/** Set SkeletalControl Alpha**/
|
|
void UAnimPreviewInstance::SetSkeletalControlAlpha(float InSkeletalControlAlpha)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetSkeletalControlAlpha(InSkeletalControlAlpha);
|
|
}
|
|
|
|
UAnimSequence* UAnimPreviewInstance::GetAnimSequence()
|
|
{
|
|
return Cast<UAnimSequence>(GetCurrentAsset());
|
|
}
|
|
|
|
void UAnimPreviewInstance::RestartMontage(UAnimMontage* Montage, FName FromSection)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (Montage == Proxy.GetCurrentAsset())
|
|
{
|
|
MontagePreviewType = EMPT_Normal;
|
|
// since this is preview, we would like not to blend in
|
|
// just hard stop here
|
|
Montage_Stop(0.0f, Montage);
|
|
Montage_Play(Montage, Proxy.GetPlayRate());
|
|
if (FromSection != NAME_None)
|
|
{
|
|
Montage_JumpToSection(FromSection);
|
|
}
|
|
MontagePreview_SetLoopNormal(Proxy.IsLooping(), Montage->GetSectionIndex(FromSection));
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::SetAnimationAsset(UAnimationAsset* NewAsset, bool bIsLooping, float InPlayRate)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
// make sure to turn that off before setting new asset
|
|
Proxy.GetRequiredBones().SetUseSourceData(false);
|
|
|
|
Super::SetAnimationAsset(NewAsset, bIsLooping, InPlayRate);
|
|
RootMotionMode = Cast<UAnimMontage>(Proxy.GetCurrentAsset()) != NULL ? ERootMotionMode::RootMotionFromMontagesOnly : ERootMotionMode::RootMotionFromEverything;
|
|
|
|
// should re sync up curve bone controllers from new asset
|
|
Proxy.RefreshCurveBoneControllers();
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetLooping(bool bIsLooping)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
Proxy.SetLooping(bIsLooping);
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
switch (MontagePreviewType)
|
|
{
|
|
case EMPT_AllSections:
|
|
MontagePreview_SetLoopAllSections(Proxy.IsLooping());
|
|
break;
|
|
case EMPT_Normal:
|
|
default:
|
|
MontagePreview_SetLoopNormal(Proxy.IsLooping());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetPlaying(bool bIsPlaying)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
Proxy.SetPlaying(bIsPlaying);
|
|
|
|
if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance())
|
|
{
|
|
CurMontageInstance->bPlaying = Proxy.IsPlaying();
|
|
}
|
|
else if (Proxy.IsPlaying())
|
|
{
|
|
UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset());
|
|
if (Montage)
|
|
{
|
|
switch (MontagePreviewType)
|
|
{
|
|
case EMPT_AllSections:
|
|
MontagePreview_PreviewAllSections();
|
|
break;
|
|
case EMPT_Normal:
|
|
default:
|
|
MontagePreview_PreviewNormal();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetReverse(bool bInReverse)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
Super::SetReverse(bInReverse);
|
|
|
|
if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance())
|
|
{
|
|
// copy the current playrate
|
|
CurMontageInstance->SetPlayRate(Proxy.GetPlayRate());
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_Restart()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
switch (MontagePreviewType)
|
|
{
|
|
case EMPT_AllSections:
|
|
MontagePreview_PreviewAllSections();
|
|
break;
|
|
case EMPT_Normal:
|
|
default:
|
|
MontagePreview_PreviewNormal();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_StepForward()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
bool bWasPlaying = IsPlayingMontage() && (Proxy.IsLooping() || Proxy.IsPlaying()); // we need to handle non-looped case separately, even if paused during playthrough
|
|
MontagePreview_SetReverse(false);
|
|
if (! bWasPlaying)
|
|
{
|
|
if (! Proxy.IsLooping())
|
|
{
|
|
float StoppedAt = Proxy.GetCurrentTime();
|
|
if (! bWasPlaying)
|
|
{
|
|
// play montage but at last known location
|
|
MontagePreview_Restart();
|
|
SetPosition(StoppedAt, false);
|
|
}
|
|
int32 LastPreviewSectionIdx = MontagePreview_FindLastSection(MontagePreviewStartSectionIdx);
|
|
if (FMath::Abs(Proxy.GetCurrentTime() - (Montage->CompositeSections[LastPreviewSectionIdx].GetTime() + Montage->GetSectionLength(LastPreviewSectionIdx))) <= MontagePreview_CalculateStepLength())
|
|
{
|
|
// we're at the end, jump right to the end
|
|
Montage_JumpToSectionsEnd(Montage->GetSectionName(LastPreviewSectionIdx));
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
return; // can't go further than beginning of this
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MontagePreview_Restart();
|
|
}
|
|
}
|
|
MontagePreview_SetPlaying(true);
|
|
|
|
// Advance a single frame, leaving it paused afterwards
|
|
int32 NumFrames = Montage->GetNumberOfFrames();
|
|
// Add DELTA to prefer next frame when we're close to the boundary
|
|
float CurrentFraction = Proxy.GetCurrentTime() / Montage->SequenceLength + DELTA;
|
|
float NextFrame = FMath::Clamp<float>(FMath::FloorToFloat(CurrentFraction * NumFrames) + 1.0f, 0, NumFrames);
|
|
float NewTime = Montage->SequenceLength * (NextFrame / NumFrames);
|
|
|
|
GetSkelMeshComponent()->GlobalAnimRateScale = 1.0f;
|
|
GetSkelMeshComponent()->TickAnimation(NewTime - Proxy.GetCurrentTime(), false);
|
|
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_StepBackward()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
bool bWasPlaying = IsPlayingMontage() && (Proxy.IsLooping() || Proxy.IsPlaying()); // we need to handle non-looped case separately, even if paused during playthrough
|
|
MontagePreview_SetReverse(true);
|
|
if (! bWasPlaying)
|
|
{
|
|
if (! Proxy.IsLooping())
|
|
{
|
|
float StoppedAt = Proxy.GetCurrentTime();
|
|
if (! bWasPlaying)
|
|
{
|
|
// play montage but at last known location
|
|
MontagePreview_Restart();
|
|
SetPosition(StoppedAt, false);
|
|
}
|
|
int32 LastPreviewSectionIdx = MontagePreview_FindLastSection(MontagePreviewStartSectionIdx);
|
|
if (FMath::Abs(Proxy.GetCurrentTime() - (Montage->CompositeSections[LastPreviewSectionIdx].GetTime() + Montage->GetSectionLength(LastPreviewSectionIdx))) <= MontagePreview_CalculateStepLength())
|
|
{
|
|
// special case as we could stop at the end of our last section which is also beginning of following section - we don't want to get stuck there, but be inside of our starting section
|
|
Montage_JumpToSection(Montage->GetSectionName(LastPreviewSectionIdx));
|
|
}
|
|
else if (FMath::Abs(Proxy.GetCurrentTime() - Montage->CompositeSections[MontagePreviewStartSectionIdx].GetTime()) <= MontagePreview_CalculateStepLength())
|
|
{
|
|
// we're at the end of playing backward, jump right to the end
|
|
Montage_JumpToSectionsEnd(Montage->GetSectionName(MontagePreviewStartSectionIdx));
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
return; // can't go further than beginning of first section
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MontagePreview_Restart();
|
|
}
|
|
}
|
|
MontagePreview_SetPlaying(true);
|
|
|
|
// Advance a single frame, leaving it paused afterwards
|
|
int32 NumFrames = Montage->GetNumberOfFrames();
|
|
// Add DELTA to prefer next frame when we're close to the boundary
|
|
float CurrentFraction = Proxy.GetCurrentTime() / Montage->SequenceLength + DELTA;
|
|
float NextFrame = FMath::Clamp<float>(FMath::FloorToFloat(CurrentFraction * NumFrames) - 1.0f, 0, NumFrames);
|
|
float NewTime = Montage->SequenceLength * (NextFrame / NumFrames);
|
|
|
|
GetSkelMeshComponent()->GlobalAnimRateScale = 1.0f;
|
|
GetSkelMeshComponent()->TickAnimation(FMath::Abs(NewTime - Proxy.GetCurrentTime()), false);
|
|
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
}
|
|
|
|
float UAnimPreviewInstance::MontagePreview_CalculateStepLength()
|
|
{
|
|
return 1.0f / 30.0f;
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_JumpToStart()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 SectionIdx = 0;
|
|
if (MontagePreviewType == EMPT_Normal)
|
|
{
|
|
SectionIdx = MontagePreviewStartSectionIdx;
|
|
}
|
|
// TODO hack - Montage_JumpToSection requires montage being played
|
|
bool bWasPlaying = IsPlayingMontage();
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_Restart();
|
|
}
|
|
if (Proxy.GetPlayRate() < 0.f)
|
|
{
|
|
Montage_JumpToSectionsEnd(Montage->GetSectionName(SectionIdx));
|
|
}
|
|
else
|
|
{
|
|
Montage_JumpToSection(Montage->GetSectionName(SectionIdx));
|
|
}
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_JumpToEnd()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 SectionIdx = 0;
|
|
if (MontagePreviewType == EMPT_Normal)
|
|
{
|
|
SectionIdx = MontagePreviewStartSectionIdx;
|
|
}
|
|
// TODO hack - Montage_JumpToSectionsEnd requires montage being played
|
|
bool bWasPlaying = IsPlayingMontage();
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_Restart();
|
|
}
|
|
if (Proxy.GetPlayRate() < 0.f)
|
|
{
|
|
Montage_JumpToSection(Montage->GetSectionName(MontagePreview_FindLastSection(SectionIdx)));
|
|
}
|
|
else
|
|
{
|
|
Montage_JumpToSectionsEnd(Montage->GetSectionName(MontagePreview_FindLastSection(SectionIdx)));
|
|
}
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_JumpToPreviewStart()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 SectionIdx = 0;
|
|
if (MontagePreviewType == EMPT_Normal)
|
|
{
|
|
SectionIdx = MontagePreviewStartSectionIdx;
|
|
}
|
|
// TODO hack - Montage_JumpToSectionsEnd requires montage being played
|
|
bool bWasPlaying = IsPlayingMontage();
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_Restart();
|
|
}
|
|
Montage_JumpToSection(Montage->GetSectionName(Proxy.GetPlayRate() > 0.f? SectionIdx : MontagePreview_FindLastSection(SectionIdx)));
|
|
if (! bWasPlaying)
|
|
{
|
|
MontagePreview_SetPlaying(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_JumpToPosition(float NewPosition)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
SetPosition(NewPosition, false);
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
// this section will be first
|
|
int32 NewMontagePreviewStartSectionIdx = MontagePreview_FindFirstSectionAsInMontage(Montage->GetSectionIndexFromPosition(NewPosition));
|
|
if (MontagePreviewStartSectionIdx != NewMontagePreviewStartSectionIdx &&
|
|
MontagePreviewType == EMPT_Normal)
|
|
{
|
|
MontagePreviewStartSectionIdx = NewMontagePreviewStartSectionIdx;
|
|
}
|
|
// setup looping to match normal playback
|
|
MontagePreview_SetLooping(Proxy.IsLooping());
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_RemoveBlendOut()
|
|
{
|
|
if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance())
|
|
{
|
|
CurMontageInstance->DefaultBlendTimeMultiplier = 0.0f;
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_PreviewNormal(int32 FromSectionIdx)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 PreviewFromSection = FromSectionIdx;
|
|
if (FromSectionIdx != INDEX_NONE)
|
|
{
|
|
MontagePreviewStartSectionIdx = MontagePreview_FindFirstSectionAsInMontage(FromSectionIdx);
|
|
}
|
|
else
|
|
{
|
|
FromSectionIdx = MontagePreviewStartSectionIdx;
|
|
PreviewFromSection = MontagePreviewStartSectionIdx;
|
|
}
|
|
MontagePreviewType = EMPT_Normal;
|
|
// since this is preview, we would like not to blend in
|
|
// just hard stop here
|
|
Montage_Stop(0.0f, Montage);
|
|
Montage_Play(Montage, Proxy.GetPlayRate());
|
|
MontagePreview_SetLoopNormal(Proxy.IsLooping(), FromSectionIdx);
|
|
Montage_JumpToSection(Montage->GetSectionName(PreviewFromSection));
|
|
MontagePreview_RemoveBlendOut();
|
|
Proxy.SetPlaying(true);
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_PreviewAllSections()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset());
|
|
if (Montage && Montage->SequenceLength > 0.f)
|
|
{
|
|
MontagePreviewType = EMPT_AllSections;
|
|
// since this is preview, we would like not to blend in
|
|
// just hard stop here
|
|
Montage_Stop(0.0f, Montage);
|
|
Montage_Play(Montage, Proxy.GetPlayRate());
|
|
MontagePreview_SetLoopAllSections(Proxy.IsLooping());
|
|
MontagePreview_JumpToPreviewStart();
|
|
MontagePreview_RemoveBlendOut();
|
|
Proxy.SetPlaying(true);
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetLoopNormal(bool bIsLooping, int32 PreferSectionIdx)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
MontagePreview_ResetSectionsOrder();
|
|
|
|
if (PreferSectionIdx == INDEX_NONE)
|
|
{
|
|
PreferSectionIdx = Montage->GetSectionIndexFromPosition(Proxy.GetCurrentTime());
|
|
}
|
|
int32 TotalSection = Montage->CompositeSections.Num();
|
|
if (TotalSection > 0)
|
|
{
|
|
int PreferedInChain = TotalSection;
|
|
TArray<bool> AlreadyUsed;
|
|
AlreadyUsed.AddZeroed(TotalSection);
|
|
while (true)
|
|
{
|
|
// find first not already used section
|
|
int32 NotUsedIdx = 0;
|
|
while (NotUsedIdx < TotalSection)
|
|
{
|
|
if (! AlreadyUsed[NotUsedIdx])
|
|
{
|
|
break;
|
|
}
|
|
++ NotUsedIdx;
|
|
}
|
|
if (NotUsedIdx >= TotalSection)
|
|
{
|
|
break;
|
|
}
|
|
// find if this is one we're looking for closest to starting one
|
|
int32 CurSectionIdx = NotUsedIdx;
|
|
int32 InChain = 0;
|
|
while (true)
|
|
{
|
|
// find first that contains this
|
|
if (CurSectionIdx == PreferSectionIdx &&
|
|
InChain < PreferedInChain)
|
|
{
|
|
PreferedInChain = InChain;
|
|
PreferSectionIdx = NotUsedIdx;
|
|
}
|
|
AlreadyUsed[CurSectionIdx] = true;
|
|
FName NextSection = Montage->CompositeSections[CurSectionIdx].NextSectionName;
|
|
CurSectionIdx = Montage->GetSectionIndex(NextSection);
|
|
if (CurSectionIdx == INDEX_NONE || AlreadyUsed[CurSectionIdx]) // break loops
|
|
{
|
|
break;
|
|
}
|
|
++ InChain;
|
|
}
|
|
// loop this section
|
|
SetMontageLoop(Montage, Proxy.IsLooping(), Montage->CompositeSections[NotUsedIdx].SectionName);
|
|
}
|
|
if (Montage->CompositeSections.IsValidIndex(PreferSectionIdx))
|
|
{
|
|
SetMontageLoop(Montage, Proxy.IsLooping(), Montage->CompositeSections[PreferSectionIdx].SectionName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetLoopAllSetupSections(bool bIsLooping)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
MontagePreview_ResetSectionsOrder();
|
|
|
|
int32 TotalSection = Montage->CompositeSections.Num();
|
|
if (TotalSection > 0)
|
|
{
|
|
FName FirstSection = Montage->CompositeSections[0].SectionName;
|
|
FName PreviousSection = FirstSection;
|
|
TArray<bool> AlreadyUsed;
|
|
AlreadyUsed.AddZeroed(TotalSection);
|
|
while (true)
|
|
{
|
|
// find first not already used section
|
|
int32 NotUsedIdx = 0;
|
|
while (NotUsedIdx < TotalSection)
|
|
{
|
|
if (! AlreadyUsed[NotUsedIdx])
|
|
{
|
|
break;
|
|
}
|
|
++ NotUsedIdx;
|
|
}
|
|
if (NotUsedIdx >= TotalSection)
|
|
{
|
|
break;
|
|
}
|
|
// go through all connected to join them into one big chain
|
|
int CurSectionIdx = NotUsedIdx;
|
|
while (true)
|
|
{
|
|
AlreadyUsed[CurSectionIdx] = true;
|
|
FName CurrentSection = Montage->CompositeSections[CurSectionIdx].SectionName;
|
|
Montage_SetNextSection(PreviousSection, CurrentSection);
|
|
PreviousSection = CurrentSection;
|
|
|
|
FName NextSection = Montage->CompositeSections[CurSectionIdx].NextSectionName;
|
|
CurSectionIdx = Montage->GetSectionIndex(NextSection);
|
|
if (CurSectionIdx == INDEX_NONE || AlreadyUsed[CurSectionIdx]) // break loops
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (Proxy.IsLooping())
|
|
{
|
|
// and loop all
|
|
Montage_SetNextSection(PreviousSection, FirstSection);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_SetLoopAllSections(bool bIsLooping)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 TotalSection = Montage->CompositeSections.Num();
|
|
if (TotalSection > 0)
|
|
{
|
|
if (Proxy.IsLooping())
|
|
{
|
|
for (int i = 0; i < TotalSection; ++ i)
|
|
{
|
|
Montage_SetNextSection(Montage->CompositeSections[i].SectionName, Montage->CompositeSections[(i+1) % TotalSection].SectionName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < TotalSection - 1; ++ i)
|
|
{
|
|
Montage_SetNextSection(Montage->CompositeSections[i].SectionName, Montage->CompositeSections[i+1].SectionName);
|
|
}
|
|
Montage_SetNextSection(Montage->CompositeSections[TotalSection - 1].SectionName, NAME_None);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::MontagePreview_ResetSectionsOrder()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
int32 TotalSection = Montage->CompositeSections.Num();
|
|
// restore to default
|
|
for (int i = 0; i < TotalSection; ++ i)
|
|
{
|
|
Montage_SetNextSection(Montage->CompositeSections[i].SectionName, Montage->CompositeSections[i].NextSectionName);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 UAnimPreviewInstance::MontagePreview_FindFirstSectionAsInMontage(int32 ForSectionIdx)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
int32 ResultIdx = ForSectionIdx;
|
|
// Montage does not have looping set up, so it should be valid and it gets
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
TArray<bool> AlreadyVisited;
|
|
AlreadyVisited.AddZeroed(Montage->CompositeSections.Num());
|
|
bool bFoundResult = false;
|
|
while (! bFoundResult)
|
|
{
|
|
int32 UnusedSectionIdx = INDEX_NONE;
|
|
for (int32 Idx = 0; Idx < Montage->CompositeSections.Num(); ++ Idx)
|
|
{
|
|
if (! AlreadyVisited[Idx])
|
|
{
|
|
UnusedSectionIdx = Idx;
|
|
break;
|
|
}
|
|
}
|
|
if (UnusedSectionIdx == INDEX_NONE)
|
|
{
|
|
break;
|
|
}
|
|
// check if this has ForSectionIdx
|
|
int32 CurrentSectionIdx = UnusedSectionIdx;
|
|
while (CurrentSectionIdx != INDEX_NONE && ! AlreadyVisited[CurrentSectionIdx])
|
|
{
|
|
if (CurrentSectionIdx == ForSectionIdx)
|
|
{
|
|
ResultIdx = UnusedSectionIdx;
|
|
bFoundResult = true;
|
|
break;
|
|
}
|
|
AlreadyVisited[CurrentSectionIdx] = true;
|
|
FName NextSection = Montage->CompositeSections[CurrentSectionIdx].NextSectionName;
|
|
CurrentSectionIdx = Montage->GetSectionIndex(NextSection);
|
|
}
|
|
}
|
|
}
|
|
return ResultIdx;
|
|
}
|
|
|
|
int32 UAnimPreviewInstance::MontagePreview_FindLastSection(int32 StartSectionIdx)
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
int32 ResultIdx = StartSectionIdx;
|
|
if (UAnimMontage* Montage = Cast<UAnimMontage>(Proxy.GetCurrentAsset()))
|
|
{
|
|
if (FAnimMontageInstance* CurMontageInstance = GetActiveMontageInstance())
|
|
{
|
|
int32 TotalSection = Montage->CompositeSections.Num();
|
|
if (TotalSection > 0)
|
|
{
|
|
TArray<bool> AlreadyVisited;
|
|
AlreadyVisited.AddZeroed(TotalSection);
|
|
int32 CurrentSectionIdx = StartSectionIdx;
|
|
while (CurrentSectionIdx != INDEX_NONE && ! AlreadyVisited[CurrentSectionIdx])
|
|
{
|
|
AlreadyVisited[CurrentSectionIdx] = true;
|
|
ResultIdx = CurrentSectionIdx;
|
|
CurrentSectionIdx = CurMontageInstance->GetNextSectionID(CurrentSectionIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ResultIdx;
|
|
}
|
|
|
|
void UAnimPreviewInstance::BakeAnimation()
|
|
{
|
|
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
|
|
if (UAnimSequence* Sequence = Cast<UAnimSequence>(Proxy.GetCurrentAsset()))
|
|
{
|
|
FScopedTransaction ScopedTransaction(LOCTEXT("BakeAnimation", "Bake Animation"));
|
|
Sequence->Modify(true);
|
|
Sequence->BakeTrackCurvesToRawAnimation();
|
|
}
|
|
}
|
|
|
|
void UAnimPreviewInstance::EnableControllers(bool bEnable)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().EnableControllers(bEnable);
|
|
}
|
|
|
|
void UAnimPreviewInstance::SetForceRetargetBasePose(bool bInForceRetargetBasePose)
|
|
{
|
|
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetForceRetargetBasePose(bInForceRetargetBasePose);
|
|
}
|
|
|
|
bool UAnimPreviewInstance::GetForceRetargetBasePose() const
|
|
{
|
|
return GetProxyOnGameThread<FAnimPreviewInstanceProxy>().GetForceRetargetBasePose();
|
|
}
|
|
|
|
FAnimInstanceProxy* UAnimPreviewInstance::CreateAnimInstanceProxy()
|
|
{
|
|
return new FAnimPreviewInstanceProxy(this);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |