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
#rb none
#rnx
============================
MAJOR FEATURES & CHANGES
============================
Change 3783110 by Thomas.Sarkanen
Added support for logical negation when copying to array properties in the fast path
#jira UE-52043 - Anim fast path correctly doesn't handle copying to arrays of bools with logical negation
Change 3783112 by Thomas.Sarkanen
Fixed not being able to assign/unassign constraint and physical animation profiles from the context menu
#jira UE-50205 - Constraint cannot be assigned or unassigned from current profile when using "Unassign" from the context menu in the Skeleton Tree and Graph
Change 3783114 by Thomas.Sarkanen
Asset picker now only reports 'picks' on user interactions
In Ocean and other projects with lots of animation assets, the asset picker amortizes its filter queries over a number of frames. This causes the list to temporarily not include the currently selected item, which then gets reported to client code as a 'deselection'. To address this (and to maintain backwards compatibility) I've added a new delegate that gives more context for selections to the SAssetPicker, allowing us to not report selections that are 'Direct'.
#jira UE-46802 - Searching for an animation from the dropdown in persona causes it to instantly close
Change 3783118 by Thomas.Sarkanen
Added inline time/frame settings to notify context menu
#jira UE-45410 - Inline MontageSection/Notify Time Entry
Change 3783122 by Thomas.Sarkanen
Collision Response dropdown is now a checkbox
Added details customization to allow the enum to masquerate as a bool property
#jira UE-47916 - Drop down used for Collision Response enable/disable in Physics Asset Editor
Change 3783183 by Jurre.deBaare
Follow up fix for toggling post processing in the preview scene
Change 3783186 by Jurre.deBaare
Material Baking Options has two Mesh Settings sections
#fix Changed category names for to allow for two distinct categories (would have preferred to merge them, but am limited by the way detailsview handles multiple objects with containing same category)
#jira UE-52645
Change 3783188 by Jurre.deBaare
Duplicating:
"Simplygon Skelmesh "Regenerate" button remains for content that had Lods created in simplygon, even when it's not installed. On regenerating editor crashes - FPersonaMeshDetails::ApplyChanges()
#fix Ensure the regenerate button is disabled when there is no MeshReduction interface available
#jira UE-52641
Change 3783205 by Jurre.deBaare
Bounds are not shown correctly in Persona for imported alembic skeletal mesh
#fix Ensured that we do not add invalid of (0,0,0) bounds to outgoing mesh bounds
#jira UE-49338
Change 3783248 by Jurre.deBaare
Preview Scene Settings window is missing Search bar
#fix changed search bar flag on Details View
#jira UE-50063
Change 3783267 by Jurre.deBaare
Animation Modifiers: GetBonePosesForTime does not create valid data for GetBonePose
#fix changed the way we retrieve bone transforms, now take it directly from the raw data rather than from a pose
#jira UE-52057
Change 3783281 by Jurre.deBaare
Tool Tip issues in Animation Editor Preferences
#fix corrected typos in comments
#jira UE-51338
Change 3783373 by Thomas.Sarkanen
Added error-reporting to the profile name widgets
This means that the effect of renaming profiles the same as an existing one is clear (i.e. we deny it now).
#jira UE-48120 - Renaming Physics profiles the same as existing Physics profiles switches profiles and is confusing
Change 3783438 by Jurre.deBaare
Vertex paint fill tool fills all channels
#fix Ensure that the current channel fill selection in the Mesh Painter is also used for the fill tool, to allow the user masking out certain channels when using fill
#jira UE-49256
Change 3783583 by Thomas.Sarkanen
Correctly return whether a mesh section is shown or not when it has not been edited
This fixes skeletal->static mesh conversion
Change 3783598 by Thomas.Sarkanen
Fix multi-convex generastion for skeletal meshes
Some triangle indices were being missed out of the index buffer, so the mesh that the convex hull was generated from was corrupt. Removing an early-out fixes this.
#jira UE-52529 - Inaccurate Collision is Generated When Using Multi Convex Hull (PhysicsAsset Editor)
Change 3783615 by Jurre.deBaare
OpacityMask/Opacity bug in MeshUtilities
#fix ensured that material baking uses correct texture samples for Opacity Mask property
#misc deprecated all of this functionality as users should be using the MaterialBaking module
#jira UE-52382
Change 3783620 by Martin.Wilson
Fix crash due to oversampling animation during compression
#Jira UE-52713
Change 3783633 by Jurre.deBaare
Fix deprecation warnings on CIS
Change 3783636 by Benn.Gallagher
Fixed non-working tethers in clothing
Fixed clothing config not applying to active simulation after editing
Fixed and re-enabled accurate wind mode
#jira UE-50797, UE-43611
Change 3783637 by Benn.Gallagher
Github PR: Fix world to actor transform bug in anim dynamics
Fixed incorrect Actor-space calculations for simulation transforms and world vector transformations inside AnimDynamics highlighted when testing the above PR
#jira UE-48681
#3929
Change 3783638 by Benn.Gallagher
Fixed UBlendProfile properties not being correctly customized on anim nodes - and animation sequence references not being correctly filtered on anim nodes.
Change 3783660 by Danny.Bouimad
Fixing #UE-40686 Mass TranslatedMass Automated test, instead of decreasing tolrence I changed the content to preserve granularity.
Change 3783974 by Ori.Cohen
Refactor when sync components is called and how we pass the data into plugins. Simplifies how we handle physx data getting invalidated by UE4 during updates.
Also fixed crash when scene query returns a destructible component that's been destroyed, but final apex delete flush hasn't happened yet.
#jira UE-50215
Change 3784112 by Benn.Gallagher
Fixed subinstance nodes inside states failing to correctly create bridge variables on their skeleton class due to not correctly processing subgraphs in the anim blueprint compiler.
#jira UE-51000
Change 3784277 by Martin.Wilson
Fix socket name getting an appended _0
#jira UE-46625
Change 3785589 by Ori.Cohen
Fix cis
Change 3786336 by Martin.Wilson
Pushing skeleton to live link can now take source guid
-Message bus source pushes guid when sending skeleton
Change 3786778 by Martin.Wilson
Added ability for worker thread animation blueprint code to report to Message Log + added additive warning when playing an animation on an additive pose
#jira UE-49780
Change 3786847 by Martin.Wilson
Initialization and delta time for live link retargeter
#Jira UE-52112
Change 3786852 by Lina.Halper
Sequencer blending support
#jira: UE-52183
Change 3786924 by Lina.Halper
PR #4210: FIX: Incorrectly passing an unrelated bool rather than the expected pose ind. (Contributed by ruffenman)
Change 3787114 by Jurre.deBaare
Discrepancy in description of Preview Scene setting and keyboard shortcut
#fix Changed naming of the settings to match the Advanced Preview Scene panel
#jira UE-50060
Change 3787115 by Jurre.deBaare
Animation Editor Preferences do not update the preview scene
#fix Removed unused preference from PersonaOptions
#jira UE-51318
Change 3787117 by Jurre.deBaare
Off-by one error in frame time calculations
#fix Fixed up UAnimSequenceBase::GetTimeAtFrame and UAnimSequenceBase::GetFrameAtTime to return correct frame indices, similar to FAnimationRuntime::GetKeyIndicesFromTime
#jira UE-52037
Change 3787412 by Martin.Wilson
CIS Fix
Change 3787622 by Ethan.Geller
Include Google Resonance SDK.
Change 3787633 by Ethan.Geller
Promote AmibsonicsMixerPtr to FAudioDevice
Change 3788026 by Lina.Halper
Retarget source reference to soft object ptr
#jira: UE-48570
Change 3788252 by Ethan.Geller
Add blueprint functions for Resonance Global Reverb
Change 3788750 by Ethan.Geller
fix single file compile for Resonance plugin
Change 3788763 by Ethan.Geller
include IModularFeatures.h explicitly for incremental build
Change 3789108 by Martin.Wilson
Fix animations with scaled root bone generating incorrect root motion
#jira UE-52088
Change 3789642 by Martin.Wilson
Fix transition nodes from referencing the wrong state node due to non unique node guids being introduced by copy and paste
#jira UE-43891
Change 3790165 by Martin.Wilson
Fix marker sync position not being maintained across instant transitions
#jira UE-21355
Change 3790182 by Ethan.Geller
Final Resonance edits pass.
Change 3790184 by Lina.Halper
Fix issue with crash when montage is streamed out while event is queued.
https://udn.unrealengine.com/questions/404318/anim-montage-queued-montage-event-crash.html
Change 3790207 by dan.reynolds
#UE-50774 Updated AEOverviewReverb to not attempt to destroy Audio Component that has already been destroyed.
Change 3790215 by Martin.Wilson
CIS Fix
Change 3790953 by Ethan.Geller
#jira UE-53023 bypass filters when at max frequency for LPF, DC for HPF
Change 3791832 by Martin.Wilson
Don't load animations for preview tooltip in Persona
#jira UE-52118
Change 3792873 by David.Hill
Fix CIS. Remove timer from proxylod code.
Change 3793251 by David.Hill
ProxyLOD Thirdparty libs build cs files.
Update from ModuleRules.WindowsPlatform to ReadOnlyTargetRules.WindowsPlatform
-- The WindowsPlatform alias is deprecated in 4.18
Change 3793400 by Ethan.Geller
Update Resonance blueprint library to fit google naming conventions
Change 3794097 by Benn.Gallagher
Fixed clothing visualizations no longer functioning
#jira UE-52995
Change 3794250 by Danny.Bouimad
Regenerated ground truth on LODCurveLinkingTest1 and AnimatedCloth, expected change as a result of Ben fixing a bug.
Needed to update Owens cloth settings too.
Should resolve automation test CIS fails
Change 3794352 by David.Hill
ProxyLOD code:
Disable openvdb-centric warnings within the openvdb platform.h file.
C6326: Potential comparison of a constant with another constant
and add annotations
C28251: Inconsistent annotation for 'copysign'
also added a warning suppress for static analysis CA_SUPPRESS(6011) within the proxylod version of the simplifier.
Change 3794786 by Lina.Halper
Pose asset retarget source bug fix
#jira: UE-52429
Change 3794841 by Danny.Bouimad
Hopefully fixes the cloth automation CIS
Change 3795191 by Lina.Halper
Fix build issue
Change 3795486 by Ethan.Geller
re-enable android support for Oculus Audio
Change 3796162 by Danny.Bouimad
Third attempt to fix the cloth CIS error. Hopefuly this will solve it.
Change 3796311 by Martin.Wilson
Remove recompress animation from curve track actions. Allows smoother interaction on animations with slow recompress time.
#jira UE-51740
Change 3796321 by Thomas.Sarkanen
Duplicating CL 3770752 from 4.18:
Prevent crash when generating convex bodies fails
Note: speculative fix as the issue cannot be reproduced locally
#jira UE-52449 - [CrashReport] FPhysicsAssetUtils::CreateFromSkeletalMeshInternal()
Change 3797093 by Danny.Bouimad
Constrant node AnimBP Automated tests
Change 3797384 by Danny.Bouimad
Fixing CIS error caused by automated test lighting issue
Change 3800621 by Thomas.Sarkanen
Fix CIS: Shadowed variable warning
#jira UE-53253 - FMenuBuilder declaration shadows a local variable warning appears when building the editor on Linux
Change 3800690 by Danny.Bouimad
Checking in fix for CIS automation for ConstraintNode, set the screenshot tool to use BaseColor. This should fix the issue with the rendering fuzzyness
Change 3800874 by David.Hill
Clean up static analysis warnings
#jira: UE-53270
Change 3801227 by David.Hill
Allow proxylod to fail gracefully if the input mesh is way too big (e.g. sky sphere)
Added code to automatically compute the correct spatial sampling rate based on the geometry size, also allow the user to override.
#cl: UE-53155
Change 3801228 by David.Hill
UI: Mesh Proxy Dialog re-write. Make this more like MeshMerging, and share some code.
#cl UE-53155, UE-52787, UE-53106
Change 3801319 by Danny.Bouimad
Regenerated all the screen shots for the constraint tests.
Change 3801383 by Ethan.Geller
#jira UE-53311 fix additional #if PLATFORM_WINDOWS guards in Oculus Audio
Change 3801697 by Ethan.Geller
include AudioDevice.h directly to resolve FAudioDevice.
Change 3802180 by David.Hill
This should fix the Incremental UE4Editor Linux build.
Change 3802643 by David.Hill
ProxyLOD UI change. Add limits to the target screen size. They now reflect the values in the old version of the UI and the thirdparty tool.
#CL: UE-53313
Change 3802986 by Ethan.Geller
#jira UE-53330 Change vraudio to explicit library path
Change 3803448 by Danny.Bouimad
disabling constraint tests
Change 3803678 by Danny.Bouimad
#jira UE-53306 Fix
Change 3804333 by Ethan.Geller
#jira UE-53330 fix library paths for iOS on Resonance
Change 3804453 by David.Hill
Fix Shadow warning when compiling UE4Editor on linux:
FlattenedMaterials.
#CL: UE-53349
Change 3804510 by Lina.Halper
CIS warning on shadow vars
#jira: UE-53348, UE-53345
Change 3805451 by Lina.Halper
Fix build issue : Renamed variable -
https://ec-01.epicgames.net/commander/link/jobStepDetails/jobSteps/74095846?stepName=Incremental%20UnrealHeaderTool%20Win64&jobId=8173688&jobName=UE4%20Dev-AnimPhys%20-%20CL%203805429%20-%20Incremental%20Editor%20Win64&tabGroup=diagnosticHeader
Change 3805470 by Lina.Halper
Fix build issue
Change 3806524 by Martin.Wilson
Only use previous frame end position if it is valid for this frame
#jira UE-53414
Change 3792620 by David.Hill
Copying //UE4/Dev-ProxyLOD to Dev-AnimPhys-Minimal (//UE4/Dev-AnimPhys-Minimal)
Adding the ProxyLOD code to AnimPhys.
Change 3796059 by Thomas.Sarkanen
Persona viewport settings are now per-asset editor
This prevnets bone following (etc) being shared by all Persona asset editors
FOV & view type is no longer chared by all Persona asset editors
#jira UE-53005 - Viewport settings like bone following are shared between all animation sub-editors
[CL 3806814 by Marc Audy in Main branch]
1882 lines
62 KiB
C++
1882 lines
62 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimationBlueprintLibrary.h"
|
|
|
|
#include "Animation/AnimSequence.h"
|
|
#include "Animation/AnimationAsset.h"
|
|
#include "Animation/AnimMetaData.h"
|
|
#include "Animation/AnimNotifies/AnimNotifyState.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "Animation/AnimNotifies/AnimNotify.h"
|
|
#include "BonePose.h"
|
|
|
|
#include "AnimationRuntime.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogAnimationBlueprintLibrary, Verbose, All);
|
|
|
|
const FName UAnimationBlueprintLibrary::SmartContainerNames[(int32)ESmartNameContainerType::SNCT_MAX] = { USkeleton::AnimCurveMappingName, USkeleton::AnimTrackCurveMappingName };
|
|
|
|
void UAnimationBlueprintLibrary::GetNumFrames(const UAnimSequence* AnimationSequence, int32& NumFrames)
|
|
{
|
|
NumFrames = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
NumFrames = AnimationSequence->NumFrames;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetNumFrames"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationTrackNames(const UAnimSequence* AnimationSequence, TArray<FName>& TrackNames)
|
|
{
|
|
TrackNames.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
TrackNames.Append(AnimationSequence->AnimationTrackNames);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetAnimationTrackNames"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRawTrackPositionData(const UAnimSequence* AnimationSequence, const FName TrackName, TArray<FVector>& PositionData)
|
|
{
|
|
PositionData.Empty();
|
|
if (IsValidRawAnimationTrackName(AnimationSequence, TrackName))
|
|
{
|
|
const FRawAnimSequenceTrack& RawTrack = GetRawAnimationTrackByName(AnimationSequence, TrackName);
|
|
PositionData.Append(RawTrack.PosKeys);
|
|
}
|
|
else
|
|
{
|
|
const FString AnimSequenceName = (AnimationSequence != nullptr) ? AnimationSequence->GetName() : "Invalid Animation sequence";
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Raw Animation Track name %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimSequenceName );
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRawTrackRotationData(const UAnimSequence* AnimationSequence, const FName TrackName, TArray<FQuat>& RotationData)
|
|
{
|
|
RotationData.Empty();
|
|
if (IsValidRawAnimationTrackName(AnimationSequence, TrackName))
|
|
{
|
|
const FRawAnimSequenceTrack& RawTrack = GetRawAnimationTrackByName(AnimationSequence, TrackName);
|
|
RotationData.Append(RawTrack.RotKeys);
|
|
}
|
|
else
|
|
{
|
|
const FString AnimSequenceName = (AnimationSequence != nullptr) ? AnimationSequence->GetName() : "Invalid Animation sequence";
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Raw Animation Track name %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimSequenceName);
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRawTrackScaleData(const UAnimSequence* AnimationSequence, const FName TrackName, TArray<FVector>& ScaleData)
|
|
{
|
|
ScaleData.Empty();
|
|
if (IsValidRawAnimationTrackName(AnimationSequence, TrackName))
|
|
{
|
|
const FRawAnimSequenceTrack& RawTrack = GetRawAnimationTrackByName(AnimationSequence, TrackName);
|
|
ScaleData.Append(RawTrack.ScaleKeys);
|
|
}
|
|
else
|
|
{
|
|
const FString AnimSequenceName = (AnimationSequence != nullptr) ? AnimationSequence->GetName() : "Invalid Animation sequence";
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Raw Animation Track name %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimSequenceName);
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRawTrackData(const UAnimSequence* AnimationSequence, const FName TrackName, TArray<FVector>& PositionKeys, TArray<FQuat>& RotationKeys, TArray<FVector>& ScalingKeys)
|
|
{
|
|
PositionKeys.Empty();
|
|
RotationKeys.Empty();
|
|
ScalingKeys.Empty();
|
|
if (IsValidRawAnimationTrackName(AnimationSequence, TrackName))
|
|
{
|
|
const FRawAnimSequenceTrack& RawTrack = GetRawAnimationTrackByName(AnimationSequence, TrackName);
|
|
PositionKeys.Append(RawTrack.PosKeys);
|
|
RotationKeys.Append(RawTrack.RotKeys);
|
|
ScalingKeys.Append(RawTrack.ScaleKeys);
|
|
}
|
|
else
|
|
{
|
|
const FString AnimSequenceName = (AnimationSequence != nullptr) ? AnimationSequence->GetName() : "Invalid Animation sequence";
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Raw Animation Track name %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimSequenceName);
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsValidRawAnimationTrackName(const UAnimSequence* AnimationSequence, const FName TrackName)
|
|
{
|
|
bool bValidName = false;
|
|
|
|
if (AnimationSequence)
|
|
{
|
|
const int32 TrackIndex = AnimationSequence->AnimationTrackNames.IndexOfByKey(TrackName);
|
|
bValidName = (TrackIndex != INDEX_NONE) && AnimationSequence->RawAnimationData.IsValidIndex(TrackIndex);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for IsValidRawAnimationTrackName"));
|
|
}
|
|
|
|
return bValidName;
|
|
}
|
|
|
|
const FRawAnimSequenceTrack& UAnimationBlueprintLibrary::GetRawAnimationTrackByName(const UAnimSequence* AnimationSequence, const FName TrackName)
|
|
{
|
|
checkf(AnimationSequence, TEXT("Invalid Animation Sequence supplied for GetRawAnimationTrackByName"));
|
|
|
|
const int32 TrackIndex = AnimationSequence->AnimationTrackNames.IndexOfByKey(TrackName);
|
|
checkf(TrackIndex != INDEX_NONE, TEXT("Raw Animation Track %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimationSequence->GetName());
|
|
return AnimationSequence->GetRawAnimationTrack(TrackIndex);
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetCompressionScheme(const UAnimSequence* AnimationSequence, UAnimCompress*& CompressionScheme)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
CompressionScheme = AnimationSequence->CompressionScheme;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetCompressionScheme"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetCompressionScheme(UAnimSequence* AnimationSequence, UAnimCompress* CompressionScheme)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->CompressionScheme = CompressionScheme;
|
|
}
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetCompressionScheme"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAdditiveAnimationType(const UAnimSequence* AnimationSequence, TEnumAsByte<enum EAdditiveAnimationType>& AdditiveAnimationType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AdditiveAnimationType = AnimationSequence->AdditiveAnimType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetAdditiveAnimationType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetAdditiveAnimationType(UAnimSequence* AnimationSequence, const TEnumAsByte<enum EAdditiveAnimationType> AdditiveAnimationType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->AdditiveAnimType = AdditiveAnimationType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetAdditiveAnimationType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAdditiveBasePoseType(const UAnimSequence* AnimationSequence, TEnumAsByte<enum EAdditiveBasePoseType>& AdditiveBasePoseType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AdditiveBasePoseType = AnimationSequence->RefPoseType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetAdditiveBasePoseType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetAdditiveBasePoseType(UAnimSequence* AnimationSequence, const TEnumAsByte<enum EAdditiveBasePoseType> AdditiveBasePoseType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RefPoseType = AdditiveBasePoseType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetAdditiveBasePoseType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationInterpolationType(const UAnimSequence* AnimationSequence, EAnimInterpolationType& InterpolationType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
InterpolationType = AnimationSequence->Interpolation;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetAnimationInterpolationType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetAnimationInterpolationType(UAnimSequence* AnimationSequence, EAnimInterpolationType Type)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->Interpolation = Type;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetAnimationInterpolationType"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsRootMotionEnabled(const UAnimSequence* AnimationSequence)
|
|
{
|
|
bool bEnabled = false;
|
|
if (AnimationSequence)
|
|
{
|
|
bEnabled = AnimationSequence->bEnableRootMotion;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for IsRootMotionEnabled"));
|
|
}
|
|
|
|
return bEnabled;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetRootMotionEnabled(UAnimSequence* AnimationSequence, bool bEnabled)
|
|
{
|
|
bool bIsEnabled = false;
|
|
|
|
if (AnimationSequence)
|
|
{
|
|
bIsEnabled = AnimationSequence->bEnableRootMotion;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetRootMotionEnabled"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRootMotionLockType(const UAnimSequence* AnimationSequence, TEnumAsByte<ERootMotionRootLock::Type>& LockType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
LockType = AnimationSequence->RootMotionRootLock;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetRootMotionLockType"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetRootMotionLockType(UAnimSequence* AnimationSequence, TEnumAsByte<ERootMotionRootLock::Type> RootMotionLockType)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RootMotionRootLock = RootMotionLockType;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for SetRootMotionLockType"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsRootMotionLockForced(const UAnimSequence* AnimationSequence)
|
|
{
|
|
bool bIsLocked = false;
|
|
if (AnimationSequence)
|
|
{
|
|
bIsLocked = AnimationSequence->bForceRootLock;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for IsRootMotionLockForced"));
|
|
}
|
|
|
|
return bIsLocked;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetIsRootMotionLockForced(UAnimSequence* AnimationSequence, bool bForced)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->bForceRootLock = bForced;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for SetIsRootMotionLockForced"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationSyncMarkers(const UAnimSequence* AnimationSequence, TArray<FAnimSyncMarker>& Markers)
|
|
{
|
|
Markers.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
Markers = AnimationSequence->AuthoredSyncMarkers;;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetAnimationSyncMarkers"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetUniqueMarkerNames(const UAnimSequence* AnimationSequence, TArray<FName>& MarkerNames)
|
|
{
|
|
MarkerNames.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
MarkerNames = AnimationSequence->UniqueMarkerNames;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetUniqueMarkerNames"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddAnimationSyncMarker(UAnimSequence* AnimationSequence, FName MarkerName, float Time, FName TrackName)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, TrackName);
|
|
const bool bIsValidTime = IsValidTimeInternal(AnimationSequence, Time);
|
|
|
|
if (bIsValidTrackName && bIsValidTime)
|
|
{
|
|
FAnimSyncMarker NewMarker;
|
|
NewMarker.MarkerName = MarkerName;
|
|
NewMarker.Time = Time;
|
|
NewMarker.TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, TrackName);
|
|
|
|
AnimationSequence->AuthoredSyncMarkers.Add(NewMarker);
|
|
AnimationSequence->AnimNotifyTracks[NewMarker.TrackIndex].SyncMarkers.Add(&AnimationSequence->AuthoredSyncMarkers.Last());
|
|
|
|
AnimationSequence->RefreshSyncMarkerDataFromAuthored();
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
if (!bIsValidTrackName)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist in Animation Sequence %s"), *TrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
|
|
if (!bIsValidTime)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("%f is side of Animation Sequence %s range"), Time, *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddAnimationSyncMarker"));
|
|
}
|
|
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsValidAnimationSyncMarkerName(const UAnimSequence* AnimationSequence, FName MarkerName)
|
|
{
|
|
bool bIsValid = false;
|
|
if (AnimationSequence)
|
|
{
|
|
bIsValid = AnimationSequence->UniqueMarkerNames.Contains(MarkerName);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for IsValidAnimationSyncMarkerName"));
|
|
}
|
|
|
|
return bIsValid;
|
|
}
|
|
|
|
int32 UAnimationBlueprintLibrary::RemoveAnimationSyncMarkersByName(UAnimSequence* AnimationSequence, FName MarkerName)
|
|
{
|
|
int32 NumRemovedMarkers = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
NumRemovedMarkers = AnimationSequence->AuthoredSyncMarkers.RemoveAll(
|
|
[&](const FAnimSyncMarker& Marker)
|
|
{
|
|
return Marker.MarkerName == MarkerName;
|
|
});
|
|
|
|
AnimationSequence->RefreshSyncMarkerDataFromAuthored();
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAnimationSyncMarkersByName"));
|
|
}
|
|
|
|
return NumRemovedMarkers;
|
|
}
|
|
|
|
int32 UAnimationBlueprintLibrary::RemoveAnimationSyncMarkersByTrack(UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
int32 NumRemovedMarkers = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
const int32 TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
if (TrackIndex != INDEX_NONE)
|
|
{
|
|
NumRemovedMarkers = AnimationSequence->AuthoredSyncMarkers.RemoveAll(
|
|
[&](const FAnimSyncMarker& Marker)
|
|
{
|
|
return Marker.TrackIndex == TrackIndex;
|
|
});
|
|
|
|
AnimationSequence->RefreshSyncMarkerDataFromAuthored();
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAnimationSyncMarkersByTrack"));
|
|
}
|
|
|
|
return NumRemovedMarkers;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllAnimationSyncMarkers(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->AuthoredSyncMarkers.Empty();
|
|
AnimationSequence->RefreshSyncMarkerDataFromAuthored();
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAllAnimationSyncMarkers"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationNotifyEvents(const UAnimSequence* AnimationSequence, TArray<FAnimNotifyEvent>& NotifyEvents)
|
|
{
|
|
NotifyEvents.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
NotifyEvents = AnimationSequence->Notifies;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetAnimationNotifyEvents"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationNotifyEventNames(const UAnimSequence* AnimationSequence, TArray<FName>& EventNames)
|
|
{
|
|
EventNames.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
for (const FAnimNotifyEvent& Event : AnimationSequence->Notifies)
|
|
{
|
|
EventNames.AddUnique(Event.NotifyName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetAnimationNotifyEventNames"));
|
|
}
|
|
}
|
|
|
|
UAnimNotify* UAnimationBlueprintLibrary::AddAnimationNotifyEvent(UAnimSequence* AnimationSequence, FName NotifyTrackName, float StartTime, float Duration, TSubclassOf<UAnimNotifyState> NotifyClass)
|
|
{
|
|
UAnimNotify* Notify = nullptr;
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
const bool bIsValidTime = IsValidTimeInternal(AnimationSequence, StartTime);
|
|
|
|
if (bIsValidTrackName && bIsValidTime)
|
|
{
|
|
AnimationSequence->Notifies.AddDefaulted();
|
|
FAnimNotifyEvent& NewEvent = AnimationSequence->Notifies.Last();
|
|
|
|
NewEvent.NotifyName = NAME_None;
|
|
NewEvent.Link(AnimationSequence, StartTime);
|
|
NewEvent.TriggerTimeOffset = GetTriggerTimeOffsetForType(AnimationSequence->CalculateOffsetForNotify(StartTime));
|
|
NewEvent.TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
|
|
if (NotifyClass)
|
|
{
|
|
class UObject* AnimNotifyClass = NewObject<UObject>(AnimationSequence, NotifyClass, NAME_None, RF_Transactional);
|
|
NewEvent.NotifyStateClass = Cast<UAnimNotifyState>(AnimNotifyClass);
|
|
NewEvent.Notify = Cast<UAnimNotify>(AnimNotifyClass);
|
|
|
|
// Setup name and duration for new event
|
|
if (NewEvent.NotifyStateClass)
|
|
{
|
|
NewEvent.NotifyName = FName(*NewEvent.NotifyStateClass->GetNotifyName());
|
|
NewEvent.SetDuration(Duration);
|
|
NewEvent.EndLink.Link(AnimationSequence, NewEvent.EndLink.GetTime());
|
|
}
|
|
else
|
|
{
|
|
NewEvent.NotifyName = FName(*NewEvent.Notify->GetNotifyName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewEvent.Notify = NULL;
|
|
NewEvent.NotifyStateClass = NULL;
|
|
}
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
|
|
Notify = NewEvent.Notify;
|
|
}
|
|
else
|
|
{
|
|
if (!bIsValidTrackName)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
|
|
if (!bIsValidTime)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("%f is side of Animation Sequence %s range"), StartTime, *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddAnimationNotifyEvent"));
|
|
}
|
|
|
|
return Notify;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddAnimationNotifyEventObject(UAnimSequence* AnimationSequence, float StartTime, UAnimNotify* Notify, FName NotifyTrackName)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bValidNotify = Notify != nullptr;
|
|
const bool bValidOuter = bValidNotify && Notify->GetOuter() == AnimationSequence;
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
const bool bIsValidTime = IsValidTimeInternal(AnimationSequence, StartTime);
|
|
|
|
if (bValidNotify && bValidOuter && bIsValidTrackName && bIsValidTime)
|
|
{
|
|
AnimationSequence->Notifies.AddDefaulted();
|
|
FAnimNotifyEvent& NewEvent = AnimationSequence->Notifies.Last();
|
|
|
|
NewEvent.NotifyName = NAME_None;
|
|
NewEvent.Link(AnimationSequence, StartTime);
|
|
NewEvent.TriggerTimeOffset = GetTriggerTimeOffsetForType(AnimationSequence->CalculateOffsetForNotify(StartTime));
|
|
NewEvent.TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
NewEvent.NotifyStateClass = Cast<UAnimNotifyState>(Notify);
|
|
NewEvent.Notify = Notify;
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
if (!bValidNotify)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Notify in AddAnimationNotifyEventObject"));
|
|
}
|
|
|
|
if (!bValidOuter)
|
|
{
|
|
const FString NotifyName = bValidNotify ? Notify->GetName() : "Invalid Notify";
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify %s Outer is not %s"), *NotifyName, *AnimationSequence->GetName());
|
|
}
|
|
|
|
if (!bIsValidTrackName)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
|
|
if (!bIsValidTime)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("%f is side of Animation Sequence %s range"), StartTime, *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddAnimationNotifyEventObject"));
|
|
}
|
|
|
|
}
|
|
|
|
int32 UAnimationBlueprintLibrary::RemoveAnimationNotifyEventsByName(UAnimSequence* AnimationSequence, FName NotifyName)
|
|
{
|
|
int32 NumRemovedEvents = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
NumRemovedEvents = AnimationSequence->Notifies.RemoveAll(
|
|
[&](const FAnimNotifyEvent& Event)
|
|
{
|
|
return Event.NotifyName == NotifyName;
|
|
});
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAnimationNotifyEventsByName"));
|
|
}
|
|
|
|
return NumRemovedEvents;
|
|
}
|
|
|
|
int32 UAnimationBlueprintLibrary::RemoveAnimationNotifyEventsByTrack(UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
int32 NumRemovedEvents = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
if (bIsValidTrackName)
|
|
{
|
|
const int32 TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
NumRemovedEvents = AnimationSequence->Notifies.RemoveAll(
|
|
[&](const FAnimNotifyEvent& Event)
|
|
{
|
|
return Event.TrackIndex == TrackIndex;
|
|
});
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAnimationNotifyEventsByTrack"));
|
|
}
|
|
|
|
|
|
return NumRemovedEvents;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationNotifyTrackNames(const UAnimSequence* AnimationSequence, TArray<FName>& TrackNames)
|
|
{
|
|
TrackNames.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
for (const FAnimNotifyTrack& Track : AnimationSequence->AnimNotifyTracks)
|
|
{
|
|
TrackNames.AddUnique(Track.TrackName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetAnimationNotifyTrackNames"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddAnimationNotifyTrack(UAnimSequence* AnimationSequence, FName NotifyTrackName, FLinearColor TrackColor /*= FLinearColor::White*/)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bExistingTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
if (!bExistingTrackName)
|
|
{
|
|
FAnimNotifyTrack NewTrack;
|
|
NewTrack.TrackName = NotifyTrackName;
|
|
NewTrack.TrackColor = TrackColor;
|
|
AnimationSequence->AnimNotifyTracks.Add(NewTrack);
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s already exists on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddAnimationNotifyTrack"));
|
|
}
|
|
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAnimationNotifyTrack(UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const int32 TrackIndexToDelete = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
if (TrackIndexToDelete != INDEX_NONE)
|
|
{
|
|
// Remove all notifies and sync markers on the to-delete-track
|
|
AnimationSequence->Notifies.RemoveAll([&](const FAnimNotifyEvent& Notify) { return Notify.TrackIndex == TrackIndexToDelete; });
|
|
AnimationSequence->AuthoredSyncMarkers.RemoveAll([&](const FAnimSyncMarker& Marker) { return Marker.TrackIndex == TrackIndexToDelete; });
|
|
|
|
// Before track removal, make sure everything behind is fixed
|
|
for (FAnimNotifyEvent& Notify : AnimationSequence->Notifies)
|
|
{
|
|
if (Notify.TrackIndex > TrackIndexToDelete)
|
|
{
|
|
Notify.TrackIndex = Notify.TrackIndex - 1;
|
|
}
|
|
}
|
|
for (FAnimSyncMarker& SyncMarker : AnimationSequence->AuthoredSyncMarkers)
|
|
{
|
|
if (SyncMarker.TrackIndex > TrackIndexToDelete)
|
|
{
|
|
SyncMarker.TrackIndex = SyncMarker.TrackIndex - 1;
|
|
}
|
|
}
|
|
|
|
// Delete the track itself
|
|
AnimationSequence->AnimNotifyTracks.RemoveAt(TrackIndexToDelete);
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAnimationNotifyTrack"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllAnimationNotifyTracks(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->Notifies.Empty();
|
|
AnimationSequence->AuthoredSyncMarkers.Empty();
|
|
|
|
// Remove all but one notify tracks
|
|
AnimationSequence->AnimNotifyTracks.SetNum(1);
|
|
|
|
// Also remove all stale notifies and sync markers from only track
|
|
AnimationSequence->AnimNotifyTracks[0].Notifies.Empty();
|
|
AnimationSequence->AnimNotifyTracks[0].SyncMarkers.Empty();
|
|
|
|
// Refresh all cached data
|
|
AnimationSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAllAnimationNotifyTracks"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsValidAnimNotifyTrackName(const UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
bool bIsValid = false;
|
|
if (AnimationSequence)
|
|
{
|
|
bIsValid = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName) != INDEX_NONE;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for IsValidAnimNotifyTrackName"));
|
|
}
|
|
|
|
return bIsValid;
|
|
}
|
|
|
|
int32 UAnimationBlueprintLibrary::GetTrackIndexForAnimationNotifyTrackName(const UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
return AnimationSequence->AnimNotifyTracks.IndexOfByPredicate(
|
|
[&](const FAnimNotifyTrack& Track)
|
|
{
|
|
return Track.TrackName == NotifyTrackName;
|
|
});
|
|
}
|
|
|
|
const FAnimNotifyTrack& UAnimationBlueprintLibrary::GetNotifyTrackByName(const UAnimSequence* AnimationSequence, FName NotifyTrackName)
|
|
{
|
|
const int32 TrackIndex = GetTrackIndexForAnimationNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
checkf(TrackIndex != INDEX_NONE, TEXT("Notify Track %s does not exist on %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
return AnimationSequence->AnimNotifyTracks[TrackIndex];
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationSyncMarkersForTrack(const UAnimSequence* AnimationSequence, FName NotifyTrackName, TArray<FAnimSyncMarker>& Markers)
|
|
{
|
|
Markers.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
|
|
if (bIsValidTrackName)
|
|
{
|
|
const FAnimNotifyTrack& Track = GetNotifyTrackByName(AnimationSequence, NotifyTrackName);
|
|
Markers.Empty(Track.SyncMarkers.Num());
|
|
for (FAnimSyncMarker* Marker : Track.SyncMarkers)
|
|
{
|
|
Markers.Add(*Marker);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddVectorCurveKey"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetAnimationNotifyEventsForTrack(const UAnimSequence* AnimationSequence, FName NotifyTrackName, TArray<FAnimNotifyEvent>& Events)
|
|
{
|
|
Events.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
const bool bIsValidTrackName = IsValidAnimNotifyTrackName(AnimationSequence, NotifyTrackName);
|
|
|
|
if (bIsValidTrackName)
|
|
{
|
|
const FAnimNotifyTrack& Track = GetNotifyTrackByName(AnimationSequence, NotifyTrackName);
|
|
Events.Empty(Track.Notifies.Num());
|
|
for (FAnimNotifyEvent* Event : Track.Notifies)
|
|
{
|
|
Events.Add(*Event);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Animation Notify Track %s does not exist on Animation Sequence %s"), *NotifyTrackName.ToString(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddVectorCurveKey"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddCurve(UAnimSequence* AnimationSequence, FName CurveName, ERawCurveTrackTypes CurveType /*= RCT_Float*/, bool bMetaDataCurve /*= false*/)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
static const ESmartNameContainerType ContainerForCurveType[(int32)ERawCurveTrackTypes::RCT_MAX] = { ESmartNameContainerType::SNCT_CurveMapping, ESmartNameContainerType::SNCT_CurveMapping, ESmartNameContainerType::SNCT_TrackCurveMapping };
|
|
const ESmartNameContainerType CurveContainer = ContainerForCurveType[(int32)CurveType];
|
|
const int32 CurveFlags = bMetaDataCurve ? AACF_Metadata : AACF_DefaultCurve;
|
|
|
|
// Validate combination of curve types
|
|
|
|
// Only Float metadata curves are valid
|
|
const bool bValidMetaData = !bMetaDataCurve || (bMetaDataCurve && CurveType == ERawCurveTrackTypes::RCT_Float);
|
|
// Transform curves can only be added if the curve name exists as a bone on the skeleton
|
|
const bool bValidTransformCurveData = CurveType != ERawCurveTrackTypes::RCT_Transform || (AnimationSequence->GetSkeleton() && DoesBoneNameExistInternal(AnimationSequence->GetSkeleton(), CurveName));
|
|
|
|
if (bValidMetaData && bValidTransformCurveData )
|
|
{
|
|
// Add or retrieve the smartname
|
|
const bool bCurveAdded = AddCurveInternal(AnimationSequence, CurveName, SmartContainerNames[(int32)CurveContainer], CurveFlags, CurveType);
|
|
|
|
if (!bCurveAdded)
|
|
{
|
|
// Curve already existed
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Curve %s already exists on the Skeleton %s."), *CurveName.ToString(), *AnimationSequence->GetSkeleton()->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bValidMetaData)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Curve type to be create as metadata, currently only float curves are supported as metadata."));
|
|
}
|
|
|
|
if (!bValidTransformCurveData)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Transform Curve name, the supplied name %s does not exist on the Skeleton %s."), *CurveName.ToString(), AnimationSequence->GetSkeleton() ? *AnimationSequence->GetSkeleton()->GetName() : TEXT("Invalid Skeleton"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for AddCurve"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveCurve(UAnimSequence* AnimationSequence, FName CurveName, bool bRemoveNameFromSkeleton /*= false*/)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const FName ContainerName = RetrieveContainerNameForCurve(AnimationSequence, CurveName);
|
|
if (ContainerName != NAME_None)
|
|
{
|
|
const bool bCurveRemoved = RemoveCurveInternal(AnimationSequence, CurveName, ContainerName, bRemoveNameFromSkeleton);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Could not find SmartName Container for Curve Name %s while trying to remove the curve"), *CurveName.ToString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveCurve"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllCurveData(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RawCurveData.DeleteAllCurveData(ERawCurveTrackTypes::RCT_Float);
|
|
AnimationSequence->RawCurveData.DeleteAllCurveData(ERawCurveTrackTypes::RCT_Vector);
|
|
AnimationSequence->RawCurveData.DeleteAllCurveData(ERawCurveTrackTypes::RCT_Transform);
|
|
|
|
AnimationSequence->bNeedsRebake = true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAllCurveData"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddTransformationCurveKey(UAnimSequence* AnimationSequence, FName CurveName, const float Time, const FTransform& Transform)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<float> TimeArray;
|
|
TArray<FTransform> TransformArray;
|
|
|
|
TimeArray.Add(Time);
|
|
TransformArray.Add(Transform);
|
|
|
|
AddCurveKeysInternal<FTransform, FTransformCurve>(AnimationSequence, CurveName, TimeArray, TransformArray, ERawCurveTrackTypes::RCT_Transform);
|
|
AnimationSequence->bNeedsRebake = true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddTransformationCurveKey"));
|
|
}
|
|
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddTransformationCurveKeys(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<FTransform>& Transforms)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
if (Times.Num() == Transforms.Num())
|
|
{
|
|
AddCurveKeysInternal<FTransform, FTransformCurve>(AnimationSequence, CurveName, Times, Transforms, ERawCurveTrackTypes::RCT_Transform);
|
|
AnimationSequence->bNeedsRebake = true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Number of Time values %i does not match the number of Transforms %i in AddTransformationCurveKeys"), Times.Num(), Transforms.Num());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddTransformationCurveKeys"));
|
|
}
|
|
}
|
|
|
|
|
|
void UAnimationBlueprintLibrary::AddFloatCurveKey(UAnimSequence* AnimationSequence, FName CurveName, const float Time, const float Value)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<float> TimeArray;
|
|
TArray<float> ValueArray;
|
|
|
|
TimeArray.Add(Time);
|
|
ValueArray.Add(Value);
|
|
|
|
AddCurveKeysInternal<float, FFloatCurve>(AnimationSequence, CurveName, TimeArray, ValueArray, ERawCurveTrackTypes::RCT_Float);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddFloatCurveKey"));
|
|
}
|
|
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddFloatCurveKeys(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<float>& Values)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
if (Times.Num() == Values.Num())
|
|
{
|
|
AddCurveKeysInternal<float, FFloatCurve>(AnimationSequence, CurveName, Times, Values, ERawCurveTrackTypes::RCT_Float);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Number of Time values %i does not match the number of Values %i in AddFloatCurveKeys"), Times.Num(), Values.Num());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddFloatCurveKeys"));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddVectorCurveKey(UAnimSequence* AnimationSequence, FName CurveName, const float Time, const FVector Vector)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<float> TimeArray;
|
|
TArray<FVector> VectorArray;
|
|
|
|
TimeArray.Add(Time);
|
|
VectorArray.Add(Vector);
|
|
|
|
AddCurveKeysInternal<FVector, FVectorCurve>(AnimationSequence, CurveName, TimeArray, VectorArray, ERawCurveTrackTypes::RCT_Vector);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddVectorCurveKey"));
|
|
}
|
|
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddVectorCurveKeys(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<FVector>& Vectors)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
if (Times.Num() == Vectors.Num())
|
|
{
|
|
AddCurveKeysInternal<FVector, FVectorCurve>(AnimationSequence, CurveName, Times, Vectors, ERawCurveTrackTypes::RCT_Vector);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Number of Time values %i does not match the number of Vectors %i in AddVectorCurveKeys"), Times.Num(), Vectors.Num());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddVectorCurveKeys"));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
template <typename DataType, typename CurveClass>
|
|
void UAnimationBlueprintLibrary::AddCurveKeysInternal(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<DataType>& KeyData, ERawCurveTrackTypes CurveType)
|
|
{
|
|
checkf(Times.Num() == KeyData.Num(), TEXT("Not enough key data supplied"));
|
|
|
|
const FName ContainerName = RetrieveContainerNameForCurve(AnimationSequence, CurveName);
|
|
|
|
if (ContainerName != NAME_None)
|
|
{
|
|
// Retrieve smart name for curve
|
|
const FSmartName CurveSmartName = RetrieveSmartNameForCurve(AnimationSequence, CurveName, ContainerName);
|
|
|
|
// Retrieve the curve by name
|
|
CurveClass* Curve = static_cast<CurveClass*>(AnimationSequence->RawCurveData.GetCurveData(CurveSmartName.UID, CurveType));
|
|
if (Curve)
|
|
{
|
|
const int32 NumKeys = KeyData.Num();
|
|
for (int32 KeyIndex = 0; KeyIndex < NumKeys; ++KeyIndex)
|
|
{
|
|
Curve->UpdateOrAddKey(KeyData[KeyIndex], Times[KeyIndex]);
|
|
}
|
|
|
|
AnimationSequence->BakeTrackCurvesToRawAnimation();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::AddCurveInternal(UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName, int32 CurveFlags, ERawCurveTrackTypes SupportedCurveType)
|
|
{
|
|
// Add or retrieve the smart name
|
|
FSmartName SmartCurveName;
|
|
AnimationSequence->GetSkeleton()->AddSmartNameAndModify(ContainerName, CurveName, SmartCurveName);
|
|
|
|
bool bCurveAdded = false;
|
|
|
|
if (AnimationSequence->RawCurveData.GetCurveData(SmartCurveName.UID) == nullptr)
|
|
{
|
|
bCurveAdded = AnimationSequence->RawCurveData.AddCurveData(SmartCurveName, CurveFlags, SupportedCurveType);
|
|
}
|
|
else
|
|
{
|
|
// Curve already exists
|
|
}
|
|
|
|
return bCurveAdded;
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::RemoveCurveInternal(UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName, bool bRemoveNameFromSkeleton)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
bool bRemoved = false;
|
|
SmartName::UID_Type UID = AnimationSequence->GetSkeleton()->GetUIDByName(ContainerName, CurveName);
|
|
if (UID != SmartName::MaxUID)
|
|
{
|
|
FSmartName SmartCurveName;
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
checkf(Skeleton != nullptr, TEXT("Invalid Skeleton ptr"));
|
|
if (Skeleton->GetSmartNameByUID(ContainerName, UID, SmartCurveName))
|
|
{
|
|
if (ContainerName == USkeleton::AnimTrackCurveMappingName)
|
|
{
|
|
bRemoved = AnimationSequence->RawCurveData.DeleteCurveData(SmartCurveName, ERawCurveTrackTypes::RCT_Transform);
|
|
AnimationSequence->bNeedsRebake = true;
|
|
}
|
|
else
|
|
{
|
|
bRemoved = AnimationSequence->RawCurveData.DeleteCurveData(SmartCurveName, ERawCurveTrackTypes::RCT_Float);
|
|
bRemoved |= AnimationSequence->RawCurveData.DeleteCurveData(SmartCurveName, ERawCurveTrackTypes::RCT_Vector);
|
|
}
|
|
|
|
if (bRemoveNameFromSkeleton)
|
|
{
|
|
// Ensure we are eligible to do this
|
|
bool bValidToRemove = true;
|
|
|
|
if (ContainerName == USkeleton::AnimTrackCurveMappingName)
|
|
{
|
|
// Make sure we do not remove bone names
|
|
bValidToRemove = DoesBoneNameExistInternal(Skeleton, CurveName);
|
|
}
|
|
|
|
if (bValidToRemove)
|
|
{
|
|
Skeleton->RemoveSmartnameAndModify(ContainerName, UID);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Cannot remove Curve Name %s from Skeleton %s"), *CurveName.ToString(), *AnimationSequence->GetSkeleton()->GetName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Could not retrieve Smart Name for Curve Name %s from Skeleton %s"), *CurveName.ToString(), *AnimationSequence->GetSkeleton()->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Name does not exist on skeleton
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Could find for Curve Name %s in Skeleton %s"), *CurveName.ToString(), *AnimationSequence->GetSkeleton()->GetName());
|
|
}
|
|
|
|
return bRemoved;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::DoesBoneNameExist(UAnimSequence* AnimationSequence, FName BoneName, bool& bExists)
|
|
{
|
|
bExists = false;
|
|
if (AnimationSequence)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
bExists = DoesBoneNameExistInternal(Skeleton, BoneName);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("No Skeleton found for Animation Sequence %s"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for DoesBoneNameExist"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::DoesBoneNameExistInternal(USkeleton* Skeleton, FName BoneName)
|
|
{
|
|
checkf(Skeleton != nullptr, TEXT("Invalid Skeleton ptr"));
|
|
return Skeleton->GetUIDByName(USkeleton::AnimTrackCurveMappingName, BoneName) != SmartName::MaxUID;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetFloatKeys(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<float>& Values)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
GetCurveKeysInternal<float, FFloatCurve>(AnimationSequence, CurveName, Times, Values, ERawCurveTrackTypes::RCT_Float);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetFloatKeys"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetVectorKeys(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<FVector>& Values)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
GetCurveKeysInternal<FVector, FVectorCurve>(AnimationSequence, CurveName, Times, Values, ERawCurveTrackTypes::RCT_Vector);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetVectorKeys"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetTransformationKeys(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<FTransform>& Values)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
GetCurveKeysInternal<FTransform, FTransformCurve>(AnimationSequence, CurveName, Times, Values, ERawCurveTrackTypes::RCT_Transform);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetTransformationKeys"));
|
|
}
|
|
}
|
|
|
|
template <typename DataType, typename CurveClass>
|
|
void UAnimationBlueprintLibrary::GetCurveKeysInternal(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<DataType>& KeyData, ERawCurveTrackTypes CurveType)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
const FName ContainerName = RetrieveContainerNameForCurve(AnimationSequence, CurveName);
|
|
|
|
if (ContainerName != NAME_None)
|
|
{
|
|
// Retrieve smart name for curve
|
|
const FSmartName CurveSmartName = RetrieveSmartNameForCurve(AnimationSequence, CurveName, ContainerName);
|
|
|
|
// Retrieve the curve by name
|
|
CurveClass* Curve = static_cast<CurveClass*>(AnimationSequence->RawCurveData.GetCurveData(CurveSmartName.UID, CurveType));
|
|
if (Curve)
|
|
{
|
|
Curve->GetKeys(Times, KeyData);
|
|
checkf(Times.Num() == KeyData.Num(), TEXT("Invalid key data retrieved from curve"));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::DoesCurveExist(UAnimSequence* AnimationSequence, FName CurveName, ERawCurveTrackTypes CurveType)
|
|
{
|
|
bool bExistingCurve = false;
|
|
|
|
if (AnimationSequence)
|
|
{
|
|
FSmartName SmartName;
|
|
if (RetrieveSmartNameForCurve(AnimationSequence, CurveName, USkeleton::AnimTrackCurveMappingName, SmartName))
|
|
{
|
|
FAnimCurveBase* Curve = AnimationSequence->RawCurveData.GetCurveData(SmartName.UID, CurveType);
|
|
bExistingCurve = Curve != nullptr;
|
|
}
|
|
|
|
if (RetrieveSmartNameForCurve(AnimationSequence, CurveName, USkeleton::AnimCurveMappingName, SmartName))
|
|
{
|
|
FAnimCurveBase* Curve = AnimationSequence->RawCurveData.GetCurveData(SmartName.UID, CurveType);
|
|
bExistingCurve |= Curve != nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for DoesCurveExist"));
|
|
}
|
|
|
|
return bExistingCurve;
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::DoesSmartNameExist(UAnimSequence* AnimationSequence, FName Name)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
FSmartName SmartName;
|
|
return AnimationSequence->GetSkeleton()->GetSmartNameByName(USkeleton::AnimTrackCurveMappingName, Name, SmartName) ||
|
|
AnimationSequence->GetSkeleton()->GetSmartNameByName(USkeleton::AnimCurveMappingName, Name, SmartName);
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::RetrieveSmartNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName, FSmartName& SmartName)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
return AnimationSequence->GetSkeleton()->GetSmartNameByName(ContainerName, CurveName, SmartName);
|
|
}
|
|
|
|
FSmartName UAnimationBlueprintLibrary::RetrieveSmartNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName, FName ContainerName)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
FSmartName SmartCurveName;
|
|
AnimationSequence->GetSkeleton()->GetSmartNameByName(ContainerName, CurveName, SmartCurveName);
|
|
return SmartCurveName;
|
|
}
|
|
|
|
FName UAnimationBlueprintLibrary::RetrieveContainerNameForCurve(const UAnimSequence* AnimationSequence, FName CurveName)
|
|
{
|
|
checkf(AnimationSequence != nullptr, TEXT("Invalid Animation Sequence ptr"));
|
|
for (int32 Index = 0; Index < (int32)ESmartNameContainerType::SNCT_MAX; ++Index)
|
|
{
|
|
const FSmartNameMapping* CurveMapping = AnimationSequence->GetSkeleton()->GetSmartNameContainer(SmartContainerNames[Index]);
|
|
if (CurveMapping->Exists(CurveName))
|
|
{
|
|
return SmartContainerNames[Index];
|
|
}
|
|
}
|
|
|
|
return NAME_None;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddMetaData(UAnimSequence* AnimationSequence, TSubclassOf<UAnimMetaData> MetaDataClass, UAnimMetaData*& MetaDataInstance)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
MetaDataInstance = NewObject<UAnimMetaData>(AnimationSequence, MetaDataClass, NAME_None, RF_Transactional);
|
|
if (MetaDataInstance)
|
|
{
|
|
AnimationSequence->AddMetaData(MetaDataInstance);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Failed to create instance for %s"), *MetaDataClass->GetName());
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddMetaData"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddMetaDataObject(UAnimSequence* AnimationSequence, UAnimMetaData* MetaDataObject)
|
|
{
|
|
if (AnimationSequence && MetaDataObject)
|
|
{
|
|
if (MetaDataObject->GetOuter() == AnimationSequence)
|
|
{
|
|
AnimationSequence->AddMetaData(MetaDataObject);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Outer for MetaData Instance %s is not Animation Sequence %s"), *MetaDataObject->GetName(), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AnimationSequence)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for AddMetaDataObject"));
|
|
}
|
|
|
|
if (!MetaDataObject)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid MetaDataObject for AddMetaDataObject"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllMetaData(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->EmptyMetaData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveAllMetaData"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveMetaData(UAnimSequence* AnimationSequence, UAnimMetaData* MetaDataObject)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RemoveMetaData(MetaDataObject);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveMetaData"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveMetaDataOfClass(UAnimSequence* AnimationSequence, TSubclassOf<UAnimMetaData> MetaDataClass)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<UAnimMetaData*> MetaDataOfClass;
|
|
GetMetaDataOfClass(AnimationSequence, MetaDataClass, MetaDataOfClass);
|
|
AnimationSequence->RemoveMetaData(MetaDataOfClass);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for RemoveMetaDataOfClass"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetMetaData(const UAnimSequence* AnimationSequence, TArray<UAnimMetaData*>& MetaData)
|
|
{
|
|
MetaData.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
MetaData = AnimationSequence->GetMetaData();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetMetaData"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetMetaDataOfClass(const UAnimSequence* AnimationSequence, TSubclassOf<UAnimMetaData> MetaDataClass, TArray<UAnimMetaData*>& MetaDataOfClass)
|
|
{
|
|
MetaDataOfClass.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
const TArray<UAnimMetaData*>& MetaData = AnimationSequence->GetMetaData();
|
|
for (UAnimMetaData* MetaDataInstance : MetaData)
|
|
{
|
|
if (MetaDataInstance->GetClass() == *MetaDataClass)
|
|
{
|
|
MetaDataOfClass.Add(MetaDataInstance);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for GetMetaDataOfClass"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::ContainsMetaDataOfClass(const UAnimSequence* AnimationSequence, TSubclassOf<UAnimMetaData> MetaDataClass)
|
|
{
|
|
bool bContainsMetaData = false;
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<UAnimMetaData*> MetaData;
|
|
GetMetaData(AnimationSequence, MetaData);
|
|
bContainsMetaData = MetaData.FindByPredicate(
|
|
[&](UAnimMetaData* MetaDataObject)
|
|
{
|
|
return (MetaDataObject->GetClass() == *MetaDataClass);
|
|
}) != nullptr;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence for ContainsMetaDataOfClass"));
|
|
}
|
|
|
|
return bContainsMetaData;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetBonePoseForTime(const UAnimSequence* AnimationSequence, FName BoneName, float Time, bool bExtractRootMotion, FTransform& Pose)
|
|
{
|
|
Pose.SetIdentity();
|
|
if (AnimationSequence)
|
|
{
|
|
TArray<FName> BoneNameArray;
|
|
TArray<FTransform> PoseArray;
|
|
BoneNameArray.Add(BoneName);
|
|
GetBonePosesForTime(AnimationSequence, BoneNameArray, Time, bExtractRootMotion, PoseArray);
|
|
Pose = PoseArray[0];
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetBonePoseForTime"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetBonePoseForFrame(const UAnimSequence* AnimationSequence, FName BoneName, int32 Frame, bool bExtractRootMotion, FTransform& Pose)
|
|
{
|
|
Pose.SetIdentity();
|
|
if (AnimationSequence)
|
|
{
|
|
GetBonePoseForTime(AnimationSequence, BoneName, GetTimeAtFrameInternal(AnimationSequence, Frame), bExtractRootMotion, Pose);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetBonePoseForFrame"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetBonePosesForTime(const UAnimSequence* AnimationSequence, TArray<FName> BoneNames, float Time, bool bExtractRootMotion, TArray<FTransform>& Poses)
|
|
{
|
|
Poses.Empty(BoneNames.Num());
|
|
if (AnimationSequence)
|
|
{
|
|
Poses.AddDefaulted(BoneNames.Num());
|
|
|
|
// Need this for FCompactPose
|
|
FMemMark Mark(FMemStack::Get());
|
|
|
|
if (IsValidTimeInternal(AnimationSequence, Time))
|
|
{
|
|
if (BoneNames.Num())
|
|
{
|
|
for (int32 BoneNameIndex = 0; BoneNameIndex < BoneNames.Num(); ++BoneNameIndex)
|
|
{
|
|
const FName& BoneName = BoneNames[BoneNameIndex];
|
|
|
|
FTransform& Transform = Poses[BoneNameIndex];
|
|
if (IsValidRawAnimationTrackName(AnimationSequence, BoneName))
|
|
{
|
|
AnimationSequence->ExtractBoneTransform(GetRawAnimationTrackByName(AnimationSequence, BoneName), Transform, Time);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid bone name %s for Animation Sequence %s supplied for GetBonePosesForTime"), *BoneName.ToString(), *AnimationSequence->GetName());
|
|
Transform = FTransform::Identity;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Error, TEXT("Invalid or no bone names specified to retrieve poses given Animation Sequence %s in GetBonePosesForTime"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid time value %f for Animation Sequence %s supplied for GetBonePosesForTime"), Time, *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetBonePosesForTime"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetBonePosesForFrame(const UAnimSequence* AnimationSequence, TArray<FName> BoneNames, int32 Frame, bool bExtractRootMotion, TArray<FTransform>& Poses)
|
|
{
|
|
Poses.Empty(BoneNames.Num());
|
|
if (AnimationSequence)
|
|
{
|
|
GetBonePosesForTime(AnimationSequence, BoneNames, GetTimeAtFrameInternal(AnimationSequence, Frame), bExtractRootMotion, Poses);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetBonePosesForFrame"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::AddVirtualBone(const UAnimSequence* AnimationSequence, FName SourceBoneName, FName TargetBoneName, FName& VirtualBoneName)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
const bool bSourceBoneExists = DoesBoneNameExistInternal(Skeleton, SourceBoneName);
|
|
const bool bTargetBoneExists = DoesBoneNameExistInternal(Skeleton, TargetBoneName);
|
|
const bool bVirtualBoneDoesNotExist = !DoesVirtualBoneNameExistInternal(Skeleton, VirtualBoneName);
|
|
|
|
if (bSourceBoneExists && bTargetBoneExists && bVirtualBoneDoesNotExist)
|
|
{
|
|
const bool bAdded = Skeleton->AddNewVirtualBone(SourceBoneName, TargetBoneName, VirtualBoneName);
|
|
if (bAdded)
|
|
{
|
|
Skeleton->HandleSkeletonHierarchyChange();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Virtual bone between %s and %s already exists on Skeleton %s"), *SourceBoneName.ToString(), *TargetBoneName.ToString(), *Skeleton->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bSourceBoneExists)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Bone Name %s does not exist on Skeleton %s"), *SourceBoneName.ToString(), *Skeleton->GetName());
|
|
}
|
|
|
|
if (!bTargetBoneExists)
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Bone Name %s does not exist on Skeleton %s"), *TargetBoneName.ToString(), *Skeleton->GetName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("No Skeleton found for Animation Sequence %s"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for AddVirtualBone"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveVirtualBone(const UAnimSequence* AnimationSequence, FName VirtualBoneName)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
if (DoesVirtualBoneNameExistInternal(Skeleton, VirtualBoneName))
|
|
{
|
|
TArray<FName> BoneNameArray;
|
|
BoneNameArray.Add(VirtualBoneName);
|
|
Skeleton->RemoveVirtualBones(BoneNameArray);
|
|
Skeleton->HandleSkeletonHierarchyChange();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Virtual Bone Name %s already exists on Skeleton %s"), *VirtualBoneName.ToString(), *Skeleton->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("No Skeleton found for Animation Sequence %s"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for RemoveVirtualBone"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveVirtualBones(const UAnimSequence* AnimationSequence, TArray<FName> VirtualBoneNames)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
for (FName& VirtualBoneName : VirtualBoneNames)
|
|
{
|
|
if (!DoesVirtualBoneNameExistInternal(Skeleton, VirtualBoneName))
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Virtual Bone Name %s already exists on Skeleton %s"), *VirtualBoneName.ToString(), *Skeleton->GetName());
|
|
}
|
|
}
|
|
|
|
Skeleton->RemoveVirtualBones(VirtualBoneNames);
|
|
Skeleton->HandleSkeletonHierarchyChange();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("No Skeleton found for Animation Sequence %s"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for RemoveVirtualBones"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllVirtualBones(const UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
TArray<FName> VirtualBoneNames;
|
|
for (const FVirtualBone& VirtualBone : Skeleton->VirtualBones)
|
|
{
|
|
VirtualBoneNames.Add(VirtualBone.VirtualBoneName);
|
|
}
|
|
|
|
RemoveVirtualBones(AnimationSequence, VirtualBoneNames);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("No Skeleton found for Animation Sequence %s"), *AnimationSequence->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for RemoveAllVirtualBones"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::DoesVirtualBoneNameExistInternal(USkeleton* Skeleton, FName BoneName)
|
|
{
|
|
checkf(Skeleton != nullptr, TEXT("Invalid Skeleton ptr"));
|
|
return Skeleton->VirtualBones.IndexOfByPredicate([&](const FVirtualBone& VirtualBone) { return VirtualBone.VirtualBoneName == BoneName; }) != INDEX_NONE;
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetSequenceLength(const UAnimSequence* AnimationSequence, float& Length)
|
|
{
|
|
Length = 0.0f;
|
|
if (AnimationSequence)
|
|
{
|
|
Length = AnimationSequence->SequenceLength;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetSequenceLength"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetRateScale(const UAnimSequence* AnimationSequence, float& RateScale)
|
|
{
|
|
RateScale = 0.0f;
|
|
if (AnimationSequence)
|
|
{
|
|
RateScale = AnimationSequence->RateScale;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetRateScale"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::SetRateScale(UAnimSequence* AnimationSequence, float RateScale)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RateScale = RateScale;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for SetRateScale"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetFrameAtTime(const UAnimSequence* AnimationSequence, const float Time, int32& Frame)
|
|
{
|
|
Frame = 0;
|
|
if (AnimationSequence)
|
|
{
|
|
Frame = AnimationSequence->GetFrameAtTime(Time);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetFrameAtTime"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::GetTimeAtFrame(const UAnimSequence* AnimationSequence, const int32 Frame, float& Time)
|
|
{
|
|
Time = 0.0f;
|
|
if (AnimationSequence)
|
|
{
|
|
Time = GetTimeAtFrameInternal(AnimationSequence, Frame);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for GetTimeAtFrame"));
|
|
}
|
|
}
|
|
|
|
float UAnimationBlueprintLibrary::GetTimeAtFrameInternal(const UAnimSequence* AnimationSequence, const int32 Frame)
|
|
{
|
|
return AnimationSequence->GetTimeAtFrame(Frame);
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::IsValidTime(const UAnimSequence* AnimationSequence, const float Time, bool& IsValid)
|
|
{
|
|
IsValid = false;
|
|
if (AnimationSequence)
|
|
{
|
|
IsValid = IsValidTimeInternal(AnimationSequence, Time);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for IsValidTime"));
|
|
}
|
|
}
|
|
|
|
bool UAnimationBlueprintLibrary::IsValidTimeInternal(const UAnimSequence* AnimationSequence, const float Time)
|
|
{
|
|
return FMath::IsWithinInclusive(Time, 0.0f, AnimationSequence->SequenceLength);
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::FindBonePathToRoot(const UAnimSequence* AnimationSequence, FName BoneName, TArray<FName>& BonePath)
|
|
{
|
|
BonePath.Empty();
|
|
if (AnimationSequence)
|
|
{
|
|
BonePath.Add(BoneName);
|
|
int32 BoneIndex = AnimationSequence->GetSkeleton()->GetReferenceSkeleton().FindRawBoneIndex(BoneName);
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
while (BoneIndex != INDEX_NONE)
|
|
{
|
|
const int32 ParentBoneIndex = AnimationSequence->GetSkeleton()->GetReferenceSkeleton().GetRawParentIndex(BoneIndex);
|
|
if (ParentBoneIndex != INDEX_NONE)
|
|
{
|
|
BonePath.Add(AnimationSequence->GetSkeleton()->GetReferenceSkeleton().GetBoneName(ParentBoneIndex));
|
|
}
|
|
|
|
BoneIndex = ParentBoneIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Bone name %s not found in Skeleton %s"), *BoneName.ToString(), *AnimationSequence->GetSkeleton()->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Animation Sequence supplied for FindBonePathToRoot"));
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveBoneAnimation(UAnimSequence* AnimationSequence, FName BoneName, bool bIncludeChildren /*= true*/, bool bFinalize /*= true*/)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
const TArray<FName>& TrackNames = AnimationSequence->GetAnimationTrackNames();
|
|
const int32 TrackIndex = TrackNames.Find(BoneName);
|
|
if (TrackIndex != INDEX_NONE)
|
|
{
|
|
TArray<int32> TracksToRemove;
|
|
TracksToRemove.Add(TrackIndex);
|
|
|
|
// remove all children if required
|
|
if (bIncludeChildren)
|
|
{
|
|
USkeleton* Skeleton = AnimationSequence->GetSkeleton();
|
|
if (Skeleton)
|
|
{
|
|
const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton();
|
|
const int32 ParentBoneIndex = RefSkeleton.FindBoneIndex(BoneName);
|
|
|
|
// slow
|
|
for (int32 ChildTrackIndex = 0; ChildTrackIndex < TrackNames.Num(); ++ChildTrackIndex)
|
|
{
|
|
if (TrackIndex != ChildTrackIndex)
|
|
{
|
|
const int32 ChildBoneIndex = RefSkeleton.FindBoneIndex(TrackNames[ChildTrackIndex]);
|
|
if (RefSkeleton.BoneIsChildOf(ChildBoneIndex, ParentBoneIndex))
|
|
{
|
|
TracksToRemove.Add(ChildTrackIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TracksToRemove.Sort([](const int32& A, const int32& B) { return A < B; });
|
|
|
|
// go reverse since we're removing by index
|
|
for (int32 Index = TracksToRemove.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
AnimationSequence->RemoveTrack(TracksToRemove[Index]);
|
|
}
|
|
|
|
if (bFinalize)
|
|
{
|
|
AnimationSequence->PostProcessSequence();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// print warning with track index
|
|
UE_LOG(LogAnimationBlueprintLibrary, Warning, TEXT("Invalid Bone Name for the animation."));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::RemoveAllBoneAnimation(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->RemoveAllTracks();
|
|
}
|
|
}
|
|
|
|
void UAnimationBlueprintLibrary::FinalizeBoneAnimation(UAnimSequence* AnimationSequence)
|
|
{
|
|
if (AnimationSequence)
|
|
{
|
|
AnimationSequence->PostProcessSequence();
|
|
}
|
|
}
|
|
|
|
template void UAnimationBlueprintLibrary::AddCurveKeysInternal<float, FFloatCurve>(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<float>& KeyData, ERawCurveTrackTypes CurveType);
|
|
template void UAnimationBlueprintLibrary::AddCurveKeysInternal<FVector, FVectorCurve>(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<FVector>& KeyData, ERawCurveTrackTypes CurveType);
|
|
template void UAnimationBlueprintLibrary::AddCurveKeysInternal<FTransform, FTransformCurve>(UAnimSequence* AnimationSequence, FName CurveName, const TArray<float>& Times, const TArray<FTransform>& KeyData, ERawCurveTrackTypes CurveType);
|
|
|
|
template void UAnimationBlueprintLibrary::GetCurveKeysInternal<float, FFloatCurve>(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<float>& KeyData, ERawCurveTrackTypes CurveType);
|
|
template void UAnimationBlueprintLibrary::GetCurveKeysInternal<FVector, FVectorCurve>(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<FVector>& KeyData, ERawCurveTrackTypes CurveType);
|
|
template void UAnimationBlueprintLibrary::GetCurveKeysInternal<FTransform, FTransformCurve>(UAnimSequence* AnimationSequence, FName CurveName, TArray<float>& Times, TArray<FTransform>& KeyData, ERawCurveTrackTypes CurveType);
|
|
|