Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimPreviewInstance.cpp
Marc Audy e697b581a9 Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 3252535)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================

Change 3228282 on 2016/12/08 by Aaron.McLeran

	Adding ability to fix up existing sound classes

	- Utility "soundclassfixup" console command renames sound classes which are packaged inside other sound classes accidentally as new uniquely named packages
	- Also removes code which was allowing "NewSoundClass" behavior in sound class graphs to populate with existing sound classes. Instead, it *always* creates a new sound class and warns if the sound class already exists. Connecting existing sound classes is instead going to be done through dragging them into the graph from the content browser or from the sound class node itself.

Change 3228774 on 2016/12/09 by Ori.Cohen

	Fix multi select being very slow in phat

	#JIRA UE-39559

Change 3229036 on 2016/12/09 by Marc.Audy

	Remove trivial overrides

Change 3229130 on 2016/12/09 by Aaron.McLeran

	Fixing build error.

	Moving new code from CL 3228282 into WITH_EDITOR block since it's an editor-only operation

Change 3229412 on 2016/12/09 by Aaron.McLeran

	Fixing 7.1 surround sound systems on PC by forcing them to load as 5.1.

	- We don't support 7.1 but 7.1 systems should at least behave as good as 5.1

Change 3229782 on 2016/12/09 by Marc.Audy

	Fixed crash when seamless travelling in PIE from levels other than the current editor level with a streaming sublevel shared with the current editor level (4.15)
	#jira UE-39407

Change 3229842 on 2016/12/09 by Marc.Audy

	Missing files for CL# 3229782

Change 3229905 on 2016/12/09 by Marc.Audy

	Check Owner has a valid world before tryign to access Scene (4.14.2)
	#jira UE-39560

Change 3229961 on 2016/12/09 by Aaron.McLeran

	UE-39650 Implementing  CL 3229894 in Dev-Framework

Change 3229964 on 2016/12/09 by Aaron.McLeran

	Removing redundant loop introduced from integration

Change 3230722 on 2016/12/12 by Lukasz.Furman

	fixed vislog macros for recording thick segments
	#ue4

Change 3230864 on 2016/12/12 by Lina.Halper

	Fix crash with deleting pose

	#jira:UE-39584

Change 3230893 on 2016/12/12 by Marc.Audy

	Support more default values in UHT for FVector: ForwardVector, RightVector, and single float FVector constructor

Change 3231189 on 2016/12/12 by Ori.Cohen

	Added bone name to the physics invalid operation warnings.

Change 3231420 on 2016/12/12 by James.Golding

	Support per-component skel mesh weight override
	#jira UEFW-240

Change 3231422 on 2016/12/12 by James.Golding

	Test map for per-component skin weights

Change 3231491 on 2016/12/12 by James.Golding

	Move , FPositionVertexBuffer and FStaticMeshVertexDataInterface into their own headers
	Move FStaticMeshVertexBuffer implementation into its own cpp

Change 3231590 on 2016/12/12 by mason.seay

	Changed to box collision

Change 3231900 on 2016/12/12 by Aaron.McLeran

	Switching to creating new master submixes rather than loading them

Change 3231909 on 2016/12/12 by James.Golding

	Fix Mac CIS in StaticMeshVertexBuffer.h

Change 3232157 on 2016/12/13 by Mieszko.Zielinski

	Fixed a silly bug in FBlackboardKeySelector::InitSelection resulting in the key selector picking first "ok-ish" value, even if it wasn't matching type filter #UE4

Change 3232162 on 2016/12/13 by Mieszko.Zielinski

	Fixed UNavigationSystem::bNavigationAutoUpdateEnabled getting ignored by recent addition to related condition in UNavigationSystem #UE4

Change 3232314 on 2016/12/13 by James.Golding

	Another attempt at fixing Mac CIS

Change 3232322 on 2016/12/13 by Lukasz.Furman

	fixed order of nav area application and low area filter
	#ue4

Change 3232364 on 2016/12/13 by Thomas.Sarkanen

	Spline IK node

	Added new runtime & graph node to deform bones along a spline. Added edit mode to work with in the BP editor.
	Spline is specified within the node using control points. External spline could come later.
	Currently very expensive to evaluate as it regenerates the transformed spline and PWLA each frame.

	#jira UEFW-249 - Add spline IK node

Change 3232589 on 2016/12/13 by Thomas.Sarkanen

	Fixed non-editor builds

Change 3232654 on 2016/12/13 by Marc.Audy

	Don't rerun construction scripts when an actor has seamless traveled from another level (4.15)
	#jira UE-39699

Change 3232690 on 2016/12/13 by Martin.Wilson

	Remove unused member

Change 3232691 on 2016/12/13 by Martin.Wilson

	Virtual bone additions:

	1) Rename support
	2) Ability to chain virtual bones (Have a virtual bone that is a child of another virtual bone)

	#jira UE-39710

Change 3232782 on 2016/12/13 by Danny.Bouimad

	Adding Test Content

Change 3232843 on 2016/12/13 by danny.bouimad

	More Updates

Change 3232981 on 2016/12/13 by Marc.Audy

	Fix CIS issues

Change 3233075 on 2016/12/13 by mason.seay

	SplineIK asset for bug report

Change 3233124 on 2016/12/13 by Ori.Cohen

	Added mass automation tests.

Change 3233265 on 2016/12/13 by Ben.Marsh

	Build: Add support for building Orion and Fortnite precompiled binaries from Dev-Framework.

Change 3233365 on 2016/12/13 by mason.seay

	Resaving with non-empty engine version

Change 3233532 on 2016/12/13 by mason.seay

	Level blueprint clean up

Change 3233571 on 2016/12/13 by Ben.Marsh

	Set up paths for precompiled binaries.

Change 3233601 on 2016/12/13 by Ben.Marsh

	Build: Use the code CL rather than latest CL for precompiled binaries.

Change 3234402 on 2016/12/14 by Ori.Cohen

	Physics: Fixed line traces not working properly in editor worlds when physics substepping was enabled (UE-36408)
	- Substepping relies on interpolating transforms over frames, but only game worlds will be ticked, so we now disallow this feature in non-game worlds.
	#jira UE-36408

Change 3234415 on 2016/12/14 by Ori.Cohen

	Fix CIS

Change 3234574 on 2016/12/14 by Thomas.Sarkanen

	Fix crash when IK chain is inverted

	#jira UE-39720 - Crash compiling animation blueprint with Spline IK node

Change 3234882 on 2016/12/14 by Ori.Cohen

	Fixed teleport not working for physical animation component

Change 3234971 on 2016/12/14 by Aaron.McLeran

	Fix for omni-directional sounds in audio mixer

Change 3235251 on 2016/12/14 by mason.seay

	Assets for proposed functional testing

Change 3235492 on 2016/12/14 by Ori.Cohen

	Undo previous bad normal fix and remove wheel width compensation. This leads to bad normals when thick tires roll over the edge leading to instability.

	#JIRA UE-38710

Change 3236398 on 2016/12/15 by Marc.Audy

	(4.15) Add new object flag RF_NeedInitialization to indicate that ~FObjectInitalizer and PostInitProperties have not been executed for the object
	Do not allow Modify calls on Objects that have not been initialized
	#jira UE-39731

Change 3236413 on 2016/12/15 by Lukasz.Furman

	added EQS profiler
	#ue4

Change 3236418 on 2016/12/15 by Lukasz.Furman

	changed log verbosity in navmesh geometry export function
	#jira UE-39809
	#3039

Change 3236508 on 2016/12/15 by Ori.Cohen

	Allow vehicles to override inertia tensor after any mass properties have changed

	#JIRA UE-39566

Change 3236573 on 2016/12/15 by Ori.Cohen

	Fix manipulation tool not working properly with welded components

Change 3236577 on 2016/12/15 by Ori.Cohen

	Improve physics asset body creation so that it merges small bones and turns off collision between initially overlapping bodies.

Change 3236580 on 2016/12/15 by Ori.Cohen

	Improve mass computation for physics shapes (ignore trimesh which introduces error)

Change 3236581 on 2016/12/15 by Ori.Cohen

	Fix incorrect inertia tensor computation for cubes (was being doubled by mistake).

Change 3236809 on 2016/12/15 by Lukasz.Furman

	compilation fix: missing headers in EnvQueryManager

Change 3237187 on 2016/12/15 by Lukasz.Furman

	compilation fix: missing defines in EnvQueryInstance

Change 3237423 on 2016/12/15 by Aaron.McLeran

	Audio mixer: Allow center channel panning as a project setting.

	- To better support previous audio engine behavior, allow audio mixer to pan audio to center channel via audio settings.

Change 3237639 on 2016/12/15 by Aaron.McLeran

	Audio mixer stat tracking

Change 3237646 on 2016/12/15 by dan.reynolds

	MIDI Test Assets:

	General MIDITestBP

	MPKmini2 Child BP

	MPKmini2 Wrap Map

Change 3238148 on 2016/12/16 by Lukasz.Furman

	fixed crash in EQS profiler
	copy of CL# 3238145

Change 3238708 on 2016/12/16 by Marc.Audy

	(4.15) Don't unload and then reload streaming levels that are marked to be hidden.
	#jira UE-39883

Change 3238799 on 2016/12/16 by Lina.Halper

	Potential fix + more info on crash on copying curve for WEX

Change 3239559 on 2016/12/19 by Ori.Cohen

	Guard against infinitely thin geometry which fixes some nans

Change 3239728 on 2016/12/19 by Marc.Audy

	Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3239536

Change 3239735 on 2016/12/19 by Jon.Nabozny

	Set 'p.MoveIgnoreFirstBlockingOverlap' to be enabled by default (3158732). This causes collision behavior to remain unchanged unless people opt in to the new behavior.
	Adjust Bot_RandomLocations default health to 100 from 0. This prevents death by hits from non-projectiles.
	4.15

	#jira UE-39387

Change 3239765 on 2016/12/19 by Jon.Nabozny

	Fix FPredictProjectilePathParams to use a valid default value for TraceChannel.
	This requires the use of a new bool bTraceWithChannel which is enabled by default.
	4.15

	#JIRA UE-39726

Change 3239810 on 2016/12/19 by Marc.Audy

	Avoid duplicate GetWorldSettings call

Change 3239826 on 2016/12/19 by Lukasz.Furman

	fixed crashes in gameplay debugger's draw delegate handling
	copy of 3234768, 3239819
	#ue4

Change 3239894 on 2016/12/19 by Richard.Hinckley

	Improving UInterface template files for "New C++ Class" feature. We now use GENERATED_BODY macros and don't need an empty constructor in the .cpp file.

Change 3239957 on 2016/12/19 by Aaron.McLeran

	UE-39924 Fix for crash when duplicating sound cue assets in content browser

	Checking for null before casting

Change 3239983 on 2016/12/19 by Mieszko.Zielinski

	Fixed injecting dynamic BTs not as expected when there's more than one injection point #UE4

Change 3240177 on 2016/12/19 by Mieszko.Zielinski

	Fix for AI agents hand-placed on levels not getting their PathFollowingComponent.MyNavData set properly #UE4

Change 3240488 on 2016/12/19 by Aaron.McLeran

	UE-39924 Fix for crash when duplicating sound cue assets in content browser

	More fixes!

Change 3240512 on 2016/12/19 by dan.reynolds

	AEOverview Update:

	- Created support for single level loads (sub-maps now auto generate lights and a staging platform when loaded individually vs. via AEOverviewMain)

	This will allow developers to load single levels functionally without adding lights or other assets to make them work.

Change 3240518 on 2016/12/19 by dan.reynolds

	AEOverview Update:

	- Added test for Multichannel 2D Reverb

Change 3240875 on 2016/12/20 by mason.seay

	Gameplay Tag Functional Tests

Change 3240876 on 2016/12/20 by dan.reynolds

	AEOverview Fix

	- Fixed miss targeted menu items (updated prefixes)

Change 3240923 on 2016/12/20 by Lukasz.Furman

	fixed memory corruption in template A* solver
	copy of CL# 3240898
	#ue4

Change 3241661 on 2016/12/21 by Thomas.Sarkanen

	Fix mesh-customized sockets not showing up by default in 'Active' socket filter mode

	#jira UE-39938 - Cannot edit mesh sockets

Change 3241964 on 2016/12/21 by Wes.Hunt

	Remove QoSReporter from CrashReportClient
	#tests editor debug gpf and verify crash is sent.

Change 3241996 on 2016/12/21 by Wes.Hunt

	Add @Owner tags to all analytics events in all our games #jira AN-805
	* Added default owners to most events. Tracked down authors of some events.
	* Added skeleton docs for many missing locations (just added @Name and @Owner so analytics folks can see the name and who to talk to in the doc webpage).
	* verified this checkin contains changes to comments ONLY.
	#tests compiled Orion and QAGame.

Change 3242825 on 2016/12/22 by Lukasz.Furman

	fixed order of behavior tree execution indices for PIE debugging
	#jira UE-39922

Change 3242860 on 2016/12/22 by mason.seay

	Functional tests for timer

Change 3243188 on 2016/12/22 by dan.reynolds

	AEOverview Update

	- Created viewport bookmarks on each sub-map for individual testing consistency

	- Updated EQ and Reverb effect parameters to work with new Audio Mixer Effects

Change 3243192 on 2016/12/22 by dan.reynolds

	AEOverview Lighting Fix

Change 3243507 on 2016/12/23 by dan.reynolds

	AEOverview Moved to Maps\Framework\Audio\

	+ redirector clean up, resaves, etc.

Change 3243553 on 2016/12/24 by Aaron.McLeran

	Bringing fixes to dev-framework from odin

	3240517
	3240476
	3240473
	3240412
	3240315
	3240220
	3240194

Change 3243567 on 2016/12/24 by Aaron.McLeran

	Fixing build.
	Adding #include for FConfigCacheIni

Change 3244466 on 2017/01/01 by Mieszko.Zielinski

	Removed FGameplayDebuggerDebugDrawDelegateHelper::InitDelegateHelper implementation that was failing a check without any explanation or comment #UE4

	#jira UE-40069

Change 3244471 on 2017/01/01 by Aaron.McLeran

	Bringing fixes to dev-framework from odin

	3244469
	3244467
	3243743

Change 3244639 on 2017/01/03 by Jurre.deBaare

	CIS error fix

Change 3244748 on 2017/01/03 by Jurre.deBaare

	Crash while using the Delete Button in the HLOD Outliner while a Generated Proxy Mesh is opened in the Static Mesh Editor
	#fix Unify path for both delete cluster options in the outliner UI
	#jira UE-40066

Change 3245338 on 2017/01/03 by Aaron.McLeran

	Getting rid of shadowed variable.

Change 3245816 on 2017/01/03 by Aaron.McLeran

	Synth component and DSP objects

	- New synth component wraps an audio component and procedural sound wave to make generating synthesis much much easier
	- Bunch of changes and improvements to DSP objects for real-time synthesis.
	- New polyphonic virtual analog synthesizer

Change 3246146 on 2017/01/04 by Ben.Marsh

	Move precompiled binaries into the Private-Binaries stream.

Change 3246283 on 2017/01/04 by Marc.Audy

	Fix CIS warnings

Change 3246457 on 2017/01/04 by Aaron.McLeran

	Fixing static analysis warnings

Change 3246519 on 2017/01/04 by Benn.Gallagher

	Fix for serialization mismatch on skeletal mesh source model.

Change 3247193 on 2017/01/04 by Dan.Reynolds

	Adding new DSP utility

Change 3247769 on 2017/01/05 by Marc.Audy

	Remove inaccurate comment

Change 3248068 on 2017/01/05 by dan.reynolds

	AEOverview Fix

	- Shortening long path name (Multichannel sub-directories) and fixing up redirectors

Change 3248251 on 2017/01/05 by Jon.Nabozny

	Fix uninitialized PropertyColor in BillboardComponent.

Change 3249305 on 2017/01/06 by James.Golding

	Fix FColorVertexBuffer copy constructor if source buffer is not initialised
	#jira UE-40242

Change 3249639 on 2017/01/06 by Jon.Nabozny

	Fix K2Node_CallFunction tool tip generation crash.

	#JIRA UE-40307

Change 3249716 on 2017/01/06 by Aaron.McLeran

	Minor changes to DSP objects

	Deciding on a method to pass parameters from BP to synth components.

Change 3249909 on 2017/01/06 by James.Golding

	Change USkinnedMeshComponent::GetSkinWeightBuffer to not require a MeshObject to return valid weight buffer
	Make VertInfluencedByActiveBoneTyped not crash if weight buffer is null
	#jira UE-40289

Change 3249931 on 2017/01/06 by Aaron.McLeran

	Bring CL 3244528 from Odin to Dev-Framework

Change 3250012 on 2017/01/06 by Aaron.McLeran

	Changing how synth params work

	- Removing base-class parameter getters/setters, removing OnParameterChange virtual function
	- Added SynthCommand function to help setting synth params on audio render thread from game thread
	- Refactored Synth1Component to use new system

Change 3250084 on 2017/01/06 by Aaron.McLeran

	Adding preset struct and adding noise to oscillator

Change 3250257 on 2017/01/07 by Aaron.McLeran

	Checking in stub for new synthesis plugin to put synthesis instances.

Change 3250264 on 2017/01/07 by Aaron.McLeran

	Moving synthesis code to new synthesis plugin

Change 3250313 on 2017/01/07 by Aaron.McLeran

	Fixing CIS static analysis warning on include cycle

Change 3250353 on 2017/01/08 by Aaron.McLeran

	Various audio mixer/dsp refinements

	-Simplying envelope code to just be a straightforward case statement
	-Added sample value lerping code for Amp object to avoid zippering when running at control-rate sample rates
	-Changed source manager wrapping code to always set NextFrameIndex to -1 in the edge case of the next being out of range, but current not being out of range. It should always be -1.
	-Added a console var to toggle enabling sample checks for tracking down sample bugs
	-Added data table row subclass to EpicSynth1Component preset struct

Change 3250382 on 2017/01/08 by Aaron.McLeran

	Bringing ODIN-3977 fix to dev-framework

Change 3250435 on 2017/01/08 by Aaron.McLeran

	Adding ability to set note durations for synth component

	Removing OnNoteOn/OnNoteOff events since derived synth components may or may not deal with notes.

Change 3250443 on 2017/01/08 by Aaron.McLeran

	Fixing CIS, removing console variable code.

Change 3250445 on 2017/01/08 by Aaron.McLeran

	Attempted fix for crash on existing PIE

Change 3250446 on 2017/01/08 by dan.reynolds

	Updated MidiSynthTestBP for new Note On Note Off functions

Change 3250447 on 2017/01/08 by dan.reynolds

	MidiListener and MidiSynthTestBP Updated to use Duration argument (MidiListener set default value to -1.0f )

Change 3250455 on 2017/01/08 by Aaron.McLeran

	Adding critical section so stopping a source voice and processing source voice can't happen at same time.

Change 3250465 on 2017/01/08 by Aaron.McLeran

	Fixing NaNs in sine approximations

Change 3250466 on 2017/01/08 by Aaron.McLeran

	Adding new music utility.

	- Changing scale indicies to be 1-based (music oriented)
	- Adding new function to get chord note from a mode

Change 3250467 on 2017/01/08 by Aaron.McLeran

	Undoing change to FastSin parabolic sine approximation

	- was not dividing by zero!

Change 3250468 on 2017/01/08 by Aaron.McLeran

	Adding ability to get a direct virtual function callback for procedural sound waves

	-Using the UE4 delegate function was not safe in the audio rendering thread and would sometimes not actually get called. Switched to a more direct and simple override, avoids some buffer copies and is more simple.

	-Updated synth component code to use the new method.

Change 3250470 on 2017/01/08 by Aaron.McLeran

	Fixing note on duration

Change 3250479 on 2017/01/08 by Aaron.McLeran

	Fixing pan in the amp dsp object

Change 3252179 on 2017/01/10 by Mieszko.Zielinski

	Fallout fix after removal of BlackboardKeyUtils::CalculateComparisonResult declaration from the AIModule #UE4

Change 3252498 on 2017/01/10 by Marc.Audy

	Fix non-unity compile errors

[CL 3252563 by Marc Audy in Main branch]
2017-01-10 14:09:16 -05:00

1147 lines
35 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "AnimPreviewInstance.h"
#include "Animation/DebugSkelMeshComponent.h"
#include "AnimationRuntime.h"
#if WITH_EDITOR
#include "ScopedTransaction.h"
#endif
#define LOCTEXT_NAMESPACE "AnimPreviewInstance"
void FAnimPreviewInstanceProxy::Initialize(UAnimInstance* InAnimInstance)
{
FAnimSingleNodeInstanceProxy::Initialize(InAnimInstance);
bSetKey = false;
// link up our curve post-process mini-graph
PoseBlendNode.SourcePose.SetLinkNode(&CurveSource);
CurveSource.SourcePose.SetLinkNode(&SingleNode);
// initialize node manually
FAnimationInitializeContext InitContext(this);
// since curve source is linked to pose blend node, it won't need initialize
PoseBlendNode.Initialize(InitContext);
}
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
if (UPoseAsset* PoseAsset = Cast<UPoseAsset>(CurrentAsset))
{
PoseBlendNode.PoseAsset = PoseAsset;
FAnimationUpdateContext UpdateContext(this, DeltaSeconds);
PoseBlendNode.Update(UpdateContext);
}
else
{
FAnimSingleNodeInstanceProxy::Update(DeltaSeconds);
}
}
void FAnimPreviewInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
FAnimSingleNodeInstanceProxy::PreUpdate(InAnimInstance, DeltaSeconds);
if (!bForceRetargetBasePose)
{
CurveSource.PreUpdate(InAnimInstance);
}
}
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
{
if (UPoseAsset* PoseAsset = Cast<UPoseAsset>(CurrentAsset))
{
PoseBlendNode.Evaluate(Output);
}
else
{
FAnimSingleNodeInstanceProxy::Evaluate(Output);
}
}
if (bEnableControllers)
{
UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent>(GetSkelMeshComponent());
if(Component)
{
// 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(UAnimationAsset* AssetToRefreshFrom)
{
// go through all curves and see if it has Transform Curve
// if so, find what bone that belong to and create BoneMOdifier for them
check(!CurrentAsset || CurrentAsset == AssetToRefreshFrom);
UAnimSequence* CurrentSequence = Cast<UAnimSequence>(AssetToRefreshFrom);
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;
USkeleton* MySkeleton = CurrentSequence->GetSkeleton();
for (auto& Curve : Curves)
{
// skip if disabled
if (Curve.GetCurveTypeFlag(ACF_Disabled))
{
continue;
}
// add bone modifier
FName BoneName = Curve.Name.DisplayName;
if (BoneName != NAME_None && MySkeleton->GetReferenceSkeleton().FindBoneIndex(BoneName) != INDEX_NONE)
{
ModifyBone(BoneName, true);
}
}
}
}
void FAnimPreviewInstanceProxy::UpdateCurveController()
{
// evaluate the curve data first
UAnimSequenceBase* CurrentSequence = Cast<UAnimSequenceBase>(CurrentAsset);
USkeleton* PreviewSkeleton = (CurrentSequence) ? CurrentSequence->GetSkeleton() : nullptr;
if (CurrentSequence && PreviewSkeleton)
{
TMap<FName, FTransform> ActiveCurves;
CurrentSequence->RawCurveData.EvaluateTransformCurveData(PreviewSkeleton, 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)
{
if(USkeletalMesh* SkelMesh = Component->SkeletalMesh)
{
if(USkeleton* LocalSkeleton = SkelMesh->Skeleton)
{
for (auto& SingleBoneController : InBoneControllers)
{
SingleBoneController.BoneToModify.BoneIndex = GetRequiredBones().GetPoseBoneIndexForBoneName(SingleBoneController.BoneToModify.BoneName);
TArray<FBoneTransform> BoneTransforms;
if (SingleBoneController.IsValidToEvaluate(LocalSkeleton, OutMeshPose.GetPose().GetBoneContainer()))
{
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>(CurrentAsset);
UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent> (GetSkelMeshComponent());
USkeleton* PreviewSkeleton = (CurrentSequence) ? CurrentSequence->GetSkeleton() : nullptr;
if(CurrentSequence && PreviewSkeleton && 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;
bUseMultiThreadedAnimationUpdate = 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 = (CurrentAsset != nullptr) ? Proxy.IsPlaying() : true;
Super::NativeInitializeAnimation();
Proxy.SetPlaying(bCachedIsPlaying);
Proxy.RefreshCurveBoneControllers(CurrentAsset);
}
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::SetKey()
{
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetKey();
}
void UAnimPreviewInstance::SetKeyCompleteDelegate(FSimpleDelegate InOnSetKeyCompleteDelegate)
{
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetKeyCompleteDelegate(InOnSetKeyCompleteDelegate);
}
void UAnimPreviewInstance::RefreshCurveBoneControllers()
{
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().RefreshCurveBoneControllers(CurrentAsset);
}
/** Set SkeletalControl Alpha**/
void UAnimPreviewInstance::SetSkeletalControlAlpha(float InSkeletalControlAlpha)
{
GetProxyOnGameThread<FAnimPreviewInstanceProxy>().SetSkeletalControlAlpha(InSkeletalControlAlpha);
}
UAnimSequence* UAnimPreviewInstance::GetAnimSequence()
{
return Cast<UAnimSequence>(CurrentAsset);
}
void UAnimPreviewInstance::RestartMontage(UAnimMontage* Montage, FName FromSection)
{
if (Montage == CurrentAsset)
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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>(CurrentAsset) != nullptr ? ERootMotionMode::RootMotionFromMontagesOnly : ERootMotionMode::RootMotionFromEverything;
// should re sync up curve bone controllers from new asset
Proxy.RefreshCurveBoneControllers(CurrentAsset);
}
void UAnimPreviewInstance::MontagePreview_SetLooping(bool bIsLooping)
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
Proxy.SetLooping(bIsLooping);
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
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>(CurrentAsset);
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>(CurrentAsset))
{
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>(CurrentAsset))
{
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()
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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()
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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()
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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()
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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)
{
SetPosition(NewPosition, false);
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
// 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, bool bPlay)
{
UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset);
if (Montage && Montage->SequenceLength > 0.0f)
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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(bPlay);
FAnimMontageInstance* MontageInstance = GetActiveMontageInstance();
if (MontageInstance)
{
MontageInstance->SetWeight(1.0f);
MontageInstance->bPlaying = Proxy.IsPlaying();
}
}
}
void UAnimPreviewInstance::MontagePreview_PreviewAllSections(bool bPlay)
{
UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset);
if (Montage && Montage->SequenceLength > 0.0f)
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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(bPlay);
FAnimMontageInstance* MontageInstance = GetActiveMontageInstance();
if (MontageInstance)
{
MontageInstance->SetWeight(1.0f);
MontageInstance->bPlaying = Proxy.IsPlaying();
}
}
}
void UAnimPreviewInstance::MontagePreview_SetLoopNormal(bool bIsLooping, int32 PreferSectionIdx)
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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)
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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)
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
FAnimPreviewInstanceProxy& Proxy = GetProxyOnGameThread<FAnimPreviewInstanceProxy>();
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()
{
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
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)
{
int32 ResultIdx = ForSectionIdx;
// Montage does not have looping set up, so it should be valid and it gets
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
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)
{
int32 ResultIdx = StartSectionIdx;
if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset))
{
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::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