2021-05-12 07:33:41 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "AnimPose.h"
2022-08-30 23:03:03 -04:00
2021-05-12 16:49:24 -04:00
# include "Animation/AnimBlueprint.h"
# include "Animation/AnimBlueprintGeneratedClass.h"
2022-08-30 23:03:03 -04:00
# include "Animation/AnimCurveTypes.h"
# include "Animation/AnimInstance.h"
# include "Animation/AnimNode_LinkedInputPose.h"
2021-09-07 05:02:01 -04:00
# include "Animation/AnimSequence.h"
2022-08-30 23:03:03 -04:00
# include "Animation/AnimSequenceBase.h"
# include "Animation/AnimationAsset.h"
# include "Animation/AttributesRuntime.h"
# include "Animation/Skeleton.h"
# include "Animation/SmartName.h"
# include "AnimationBlueprintLibrary.h"
# include "AnimationRuntime.h"
# include "BoneContainer.h"
# include "BoneIndices.h"
# include "BonePose.h"
# include "Components/SkeletalMeshComponent.h"
# include "Containers/BitArray.h"
# include "Containers/UnrealString.h"
# include "CoreTypes.h"
# include "Engine/SkeletalMesh.h"
# include "HAL/PlatformCrt.h"
# include "Logging/LogCategory.h"
# include "Logging/LogMacros.h"
# include "Math/TransformVectorized.h"
# include "Misc/AssertionMacros.h"
# include "Misc/MemStack.h"
# include "PreviewScene.h"
# include "ReferenceSkeleton.h"
2023-05-26 13:05:06 -04:00
# include "Engine/SkeletalMeshSocket.h"
2022-08-30 23:03:03 -04:00
# include "Templates/Casts.h"
# include "Trace/Detail/Channel.h"
# include "UObject/Object.h"
2021-05-12 07:33:41 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogAnimationPoseScripting , Verbose , All ) ;
void FAnimPose : : Init ( const FBoneContainer & InBoneContainer )
{
Reset ( ) ;
2022-01-04 11:25:06 -05:00
2022-04-06 10:45:20 -04:00
const FReferenceSkeleton & RefSkeleton = InBoneContainer . GetSkeletonAsset ( ) - > GetReferenceSkeleton ( ) ;
2022-01-04 11:25:06 -05:00
2022-04-06 10:45:20 -04:00
for ( const FBoneIndexType BoneIndex : InBoneContainer . GetBoneIndicesArray ( ) )
2021-05-12 07:33:41 -04:00
{
2021-05-12 12:09:21 -04:00
const FCompactPoseBoneIndex CompactIndex ( BoneIndex ) ;
2022-04-06 10:45:20 -04:00
const FCompactPoseBoneIndex CompactParentIndex = InBoneContainer . GetParentBoneIndex ( CompactIndex ) ;
2021-05-12 07:33:41 -04:00
2022-04-06 10:45:20 -04:00
const int32 SkeletonBoneIndex = InBoneContainer . GetSkeletonIndex ( CompactIndex ) ;
2021-05-12 12:09:21 -04:00
if ( SkeletonBoneIndex ! = INDEX_NONE )
{
2022-04-06 10:45:20 -04:00
const int32 ParentBoneIndex = CompactParentIndex . GetInt ( ) ! = INDEX_NONE ? InBoneContainer . GetSkeletonIndex ( CompactParentIndex ) : INDEX_NONE ;
2021-05-12 07:33:41 -04:00
2021-05-12 12:09:21 -04:00
BoneIndices . Add ( SkeletonBoneIndex ) ;
ParentBoneIndices . Add ( ParentBoneIndex ) ;
BoneNames . Add ( RefSkeleton . GetBoneName ( SkeletonBoneIndex ) ) ;
2022-04-06 10:45:20 -04:00
RefLocalSpacePoses . Add ( InBoneContainer . GetRefPoseTransform ( FCompactPoseBoneIndex ( BoneIndex ) ) ) ;
2021-05-12 12:09:21 -04:00
}
2021-05-12 07:33:41 -04:00
}
TArray < bool > Processed ;
Processed . SetNumZeroed ( BoneNames . Num ( ) ) ;
RefWorldSpacePoses . SetNum ( BoneNames . Num ( ) ) ;
for ( int32 EntryIndex = 0 ; EntryIndex < BoneNames . Num ( ) ; + + EntryIndex )
{
const int32 ParentIndex = ParentBoneIndices [ EntryIndex ] ;
const int32 TransformIndex = BoneIndices . IndexOfByKey ( ParentIndex ) ;
if ( TransformIndex ! = INDEX_NONE )
{
ensure ( Processed [ TransformIndex ] ) ;
RefWorldSpacePoses [ EntryIndex ] = RefLocalSpacePoses [ EntryIndex ] * RefWorldSpacePoses [ TransformIndex ] ;
}
else
{
RefWorldSpacePoses [ EntryIndex ] = RefLocalSpacePoses [ EntryIndex ] ;
}
Processed [ EntryIndex ] = true ;
}
}
void FAnimPose : : GetPose ( FCompactPose & InOutCompactPose ) const
{
if ( IsValid ( ) )
{
for ( int32 Index = 0 ; Index < BoneNames . Num ( ) ; + + Index )
{
const FName & BoneName = BoneNames [ Index ] ;
2023-07-20 07:41:53 -04:00
const int32 MeshBoneIndex = InOutCompactPose . GetBoneContainer ( ) . GetPoseBoneIndexForBoneName ( BoneName ) ;
const FCompactPoseBoneIndex PoseBoneIndex = InOutCompactPose . GetBoneContainer ( ) . MakeCompactPoseIndex ( FMeshPoseBoneIndex ( MeshBoneIndex ) ) ;
2021-05-12 07:33:41 -04:00
if ( PoseBoneIndex ! = INDEX_NONE )
{
InOutCompactPose [ PoseBoneIndex ] = LocalSpacePoses [ Index ] ;
}
}
}
}
void FAnimPose : : SetPose ( USkeletalMeshComponent * Component )
{
if ( IsInitialized ( ) )
{
const FBoneContainer & ContextBoneContainer = Component - > GetAnimInstance ( ) - > GetRequiredBones ( ) ;
LocalSpacePoses . SetNum ( RefLocalSpacePoses . Num ( ) ) ;
TArray < FTransform > BoneSpaceTransforms = Component - > GetBoneSpaceTransforms ( ) ;
for ( const FBoneIndexType BoneIndex : ContextBoneContainer . GetBoneIndicesArray ( ) )
{
const int32 SkeletonBoneIndex = ContextBoneContainer . GetSkeletonIndex ( FCompactPoseBoneIndex ( BoneIndex ) ) ;
LocalSpacePoses [ BoneIndices . IndexOfByKey ( SkeletonBoneIndex ) ] = BoneSpaceTransforms [ BoneIndex ] ;
}
ensure ( LocalSpacePoses . Num ( ) = = RefLocalSpacePoses . Num ( ) ) ;
GenerateWorldSpaceTransforms ( ) ;
}
}
void FAnimPose : : GenerateWorldSpaceTransforms ( )
{
if ( IsPopulated ( ) )
{
TArray < bool > Processed ;
Processed . SetNumZeroed ( BoneNames . Num ( ) ) ;
WorldSpacePoses . SetNum ( BoneNames . Num ( ) ) ;
for ( int32 EntryIndex = 0 ; EntryIndex < BoneNames . Num ( ) ; + + EntryIndex )
{
const int32 ParentIndex = ParentBoneIndices [ EntryIndex ] ;
const int32 TransformIndex = BoneIndices . IndexOfByKey ( ParentIndex ) ;
if ( TransformIndex ! = INDEX_NONE )
{
ensure ( Processed [ TransformIndex ] ) ;
WorldSpacePoses [ EntryIndex ] = LocalSpacePoses [ EntryIndex ] * WorldSpacePoses [ TransformIndex ] ;
}
else
{
WorldSpacePoses [ EntryIndex ] = LocalSpacePoses [ EntryIndex ] ;
}
Processed [ EntryIndex ] = true ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Anim Pose was not previously populated " ) ) ;
}
}
2022-04-06 10:45:20 -04:00
void FAnimPose : : SetPose ( const FAnimationPoseData & PoseData )
2021-05-12 07:33:41 -04:00
{
2022-04-06 10:45:20 -04:00
const FCompactPose & CompactPose = PoseData . GetPose ( ) ;
2021-05-12 07:33:41 -04:00
if ( IsInitialized ( ) )
{
const FBoneContainer & ContextBoneContainer = CompactPose . GetBoneContainer ( ) ;
LocalSpacePoses . SetNum ( RefLocalSpacePoses . Num ( ) ) ;
for ( const FCompactPoseBoneIndex BoneIndex : CompactPose . ForEachBoneIndex ( ) )
{
const int32 SkeletonBoneIndex = ContextBoneContainer . GetSkeletonIndex ( BoneIndex ) ;
LocalSpacePoses [ BoneIndices . IndexOfByKey ( SkeletonBoneIndex ) ] = CompactPose [ BoneIndex ] ;
}
ensure ( LocalSpacePoses . Num ( ) = = RefLocalSpacePoses . Num ( ) ) ;
GenerateWorldSpaceTransforms ( ) ;
2022-04-06 10:45:20 -04:00
const FBlendedCurve & Curve = PoseData . GetCurve ( ) ;
Animation Curve Runtime & Editor Improvements
Runtime notes:
- Removes 'smart name' usage across the animation systems.
- Changed curve blending from a uniform array (sized per skeleton) to a sparse array of sorted named values. Blends and other combiners are performed using a dual iteration 'tape merge'.
- Skeleton curves are no longer guaranteed to cover all curve names that can be found at runtime.
Editor notes:
- Curve metadata (flags, bone links etc.) is still present on the skeleton, but can also now exist on a skeletal mesh
- Curve metadata (for morph targets) is still populated on import
- Curves can now be used arbitrarily at runtime
New features:
- New Find/Replace dialog that allows for batch-replacing curves and notifies across all of a project's assets
- New curve debugger tab in various Persona editors that allows for viewing curve values live. This also now allows viewing curves for specific pose watches.
- Pose watches now output curve tracks to the Rewind Debugger
#rb Jurre.deBaare,Nicholas.Frechette,Sara.Schvartzman,Helge.Mathee,Kiaran.Ritchie,Jaime.Cifuentes,Martin.Wilson,Keith.Yerex,Andrean.Franc (and more!)
#jira UE-167776
#jira UE-173716
#jira UE-110407
#preflight 63fc98c81206d91a2bc3ab90
#preflight 63f3ad4f81646f1f24c240c2
[CL 24421496 by Thomas Sarkanen in ue5-main branch]
2023-02-27 07:20:58 -05:00
Curve . ForEachElement ( [ & CurveNames = CurveNames , & CurveValues = CurveValues ] ( const UE : : Anim : : FCurveElement & InElement )
2022-04-06 10:45:20 -04:00
{
Animation Curve Runtime & Editor Improvements
Runtime notes:
- Removes 'smart name' usage across the animation systems.
- Changed curve blending from a uniform array (sized per skeleton) to a sparse array of sorted named values. Blends and other combiners are performed using a dual iteration 'tape merge'.
- Skeleton curves are no longer guaranteed to cover all curve names that can be found at runtime.
Editor notes:
- Curve metadata (flags, bone links etc.) is still present on the skeleton, but can also now exist on a skeletal mesh
- Curve metadata (for morph targets) is still populated on import
- Curves can now be used arbitrarily at runtime
New features:
- New Find/Replace dialog that allows for batch-replacing curves and notifies across all of a project's assets
- New curve debugger tab in various Persona editors that allows for viewing curve values live. This also now allows viewing curves for specific pose watches.
- Pose watches now output curve tracks to the Rewind Debugger
#rb Jurre.deBaare,Nicholas.Frechette,Sara.Schvartzman,Helge.Mathee,Kiaran.Ritchie,Jaime.Cifuentes,Martin.Wilson,Keith.Yerex,Andrean.Franc (and more!)
#jira UE-167776
#jira UE-173716
#jira UE-110407
#preflight 63fc98c81206d91a2bc3ab90
#preflight 63f3ad4f81646f1f24c240c2
[CL 24421496 by Thomas Sarkanen in ue5-main branch]
2023-02-27 07:20:58 -05:00
CurveNames . Add ( InElement . Name ) ;
CurveValues . Add ( InElement . Value ) ;
} ) ;
2023-05-26 13:05:06 -04:00
TArray < USkeletalMeshSocket * > Sockets ;
const USkeleton * Skeleton = ContextBoneContainer . GetSkeletonAsset ( ) ;
const USkeletalMesh * SkeletalMesh = ContextBoneContainer . GetSkeletalMeshAsset ( ) ;
if ( SkeletalMesh )
{
Sockets = SkeletalMesh - > GetActiveSocketList ( ) ;
}
else if ( Skeleton )
{
Sockets = Skeleton - > Sockets ;
}
for ( const USkeletalMeshSocket * Socket : Sockets )
{
const int32 PoseBoneIndex = ContextBoneContainer . GetPoseBoneIndexForBoneName ( Socket - > BoneName ) ;
if ( PoseBoneIndex ! = INDEX_NONE )
{
SocketNames . Add ( Socket - > SocketName ) ;
SocketParentBoneNames . Add ( Socket - > BoneName ) ;
SocketTransforms . Add ( Socket - > GetSocketLocalTransform ( ) ) ;
}
}
2021-05-12 07:33:41 -04:00
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Anim Pose was not previously initialized " ) ) ;
}
}
void FAnimPose : : SetToRefPose ( )
{
if ( IsInitialized ( ) )
{
LocalSpacePoses = RefLocalSpacePoses ;
WorldSpacePoses = RefWorldSpacePoses ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Anim Pose was not previously initialized " ) ) ;
}
}
bool FAnimPose : : IsValid ( ) const
{
const int32 ExpectedNumBones = BoneNames . Num ( ) ;
bool bIsValid = ExpectedNumBones ! = 0 ;
bIsValid & = BoneIndices . Num ( ) = = ExpectedNumBones ;
bIsValid & = ParentBoneIndices . Num ( ) = = ExpectedNumBones ;
bIsValid & = LocalSpacePoses . Num ( ) = = ExpectedNumBones ;
bIsValid & = WorldSpacePoses . Num ( ) = = ExpectedNumBones ;
bIsValid & = RefLocalSpacePoses . Num ( ) = = ExpectedNumBones ;
bIsValid & = RefWorldSpacePoses . Num ( ) = = ExpectedNumBones ;
return bIsValid ;
}
void FAnimPose : : Reset ( )
{
BoneNames . Empty ( ) ;
BoneIndices . Empty ( ) ;
ParentBoneIndices . Empty ( ) ;
LocalSpacePoses . Empty ( ) ;
WorldSpacePoses . Empty ( ) ;
RefLocalSpacePoses . Empty ( ) ;
RefWorldSpacePoses . Empty ( ) ;
}
bool UAnimPoseExtensions : : IsValid ( const FAnimPose & Pose )
{
return Pose . IsValid ( ) ;
}
void UAnimPoseExtensions : : GetBoneNames ( const FAnimPose & Pose , TArray < FName > & Bones )
{
Bones . Append ( Pose . BoneNames ) ;
}
2022-01-04 11:25:06 -05:00
const FTransform & UAnimPoseExtensions : : GetBonePose ( const FAnimPose & Pose , FName BoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
2021-05-12 07:33:41 -04:00
{
if ( Pose . IsValid ( ) )
{
const int32 BoneIndex = Pose . BoneNames . IndexOfByKey ( BoneName ) ;
if ( BoneIndex ! = INDEX_NONE )
{
return Space = = EAnimPoseSpaces : : Local ? Pose . LocalSpacePoses [ BoneIndex ] : Pose . WorldSpacePoses [ BoneIndex ] ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s was found " ) , * BoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
void UAnimPoseExtensions : : SetBonePose ( FAnimPose & Pose , FTransform Transform , FName BoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
{
if ( Pose . IsValid ( ) )
{
const int32 BoneIndex = Pose . BoneNames . IndexOfByKey ( BoneName ) ;
if ( BoneIndex ! = INDEX_NONE )
{
if ( Space = = EAnimPoseSpaces : : Local )
{
Pose . LocalSpacePoses [ BoneIndex ] = Transform ;
}
else if ( Space = = EAnimPoseSpaces : : World )
{
const int32 ParentIndex = Pose . ParentBoneIndices [ BoneIndex ] ;
const FTransform ParentTransformWS = ParentIndex ! = INDEX_NONE ? Pose . WorldSpacePoses [ ParentIndex ] : FTransform : : Identity ;
Pose . LocalSpacePoses [ BoneIndex ] = Transform . GetRelativeTransform ( ParentTransformWS ) ;
}
Pose . GenerateWorldSpaceTransforms ( ) ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s was found " ) , * BoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
}
2022-01-04 11:25:06 -05:00
const FTransform & UAnimPoseExtensions : : GetRefBonePose ( const FAnimPose & Pose , FName BoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
2021-05-12 07:33:41 -04:00
{
if ( Pose . IsValid ( ) )
{
const int32 BoneIndex = Pose . BoneNames . IndexOfByKey ( BoneName ) ;
if ( BoneIndex ! = INDEX_NONE )
{
return Space = = EAnimPoseSpaces : : Local ? Pose . RefLocalSpacePoses [ BoneIndex ] : Pose . RefWorldSpacePoses [ BoneIndex ] ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s was found " ) , * BoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
FTransform UAnimPoseExtensions : : GetRelativeTransform ( const FAnimPose & Pose , FName FromBoneName , FName ToBoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
{
if ( Pose . IsValid ( ) )
{
const int32 FromBoneIndex = Pose . BoneNames . IndexOfByKey ( FromBoneName ) ;
const int32 ToBoneIndex = Pose . BoneNames . IndexOfByKey ( ToBoneName ) ;
if ( FromBoneIndex ! = INDEX_NONE & & ToBoneIndex ! = INDEX_NONE )
{
const FTransform & From = Space = = EAnimPoseSpaces : : Local ? Pose . LocalSpacePoses [ FromBoneIndex ] : Pose . WorldSpacePoses [ FromBoneIndex ] ;
const FTransform & To = Space = = EAnimPoseSpaces : : Local ? Pose . LocalSpacePoses [ ToBoneIndex ] : Pose . WorldSpacePoses [ ToBoneIndex ] ;
const FTransform Relative = To . GetRelativeTransform ( From ) ;
return Relative ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s or %s was found " ) , * FromBoneName . ToString ( ) , * ToBoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
FTransform UAnimPoseExtensions : : GetRelativeToRefPoseTransform ( const FAnimPose & Pose , FName BoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
{
if ( Pose . IsValid ( ) )
{
const int32 BoneIndex = Pose . BoneNames . IndexOfByKey ( BoneName ) ;
if ( BoneIndex ! = INDEX_NONE )
{
const FTransform & From = Space = = EAnimPoseSpaces : : Local ? Pose . RefLocalSpacePoses [ BoneIndex ] : Pose . RefWorldSpacePoses [ BoneIndex ] ;
const FTransform & To = Space = = EAnimPoseSpaces : : Local ? Pose . LocalSpacePoses [ BoneIndex ] : Pose . WorldSpacePoses [ BoneIndex ] ;
return To . GetRelativeTransform ( From ) ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s was found " ) , * BoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
FTransform UAnimPoseExtensions : : GetRefPoseRelativeTransform ( const FAnimPose & Pose , FName FromBoneName , FName ToBoneName , EAnimPoseSpaces Space /*= EAnimPoseSpaces::Local*/ )
{
if ( Pose . IsValid ( ) )
{
const int32 FromBoneIndex = Pose . BoneNames . IndexOfByKey ( FromBoneName ) ;
const int32 ToBoneIndex = Pose . BoneNames . IndexOfByKey ( ToBoneName ) ;
if ( FromBoneIndex ! = INDEX_NONE & & ToBoneIndex ! = INDEX_NONE )
{
const FTransform & From = Space = = EAnimPoseSpaces : : Local ? Pose . RefLocalSpacePoses [ FromBoneIndex ] : Pose . RefWorldSpacePoses [ FromBoneIndex ] ;
const FTransform & To = Space = = EAnimPoseSpaces : : Local ? Pose . RefLocalSpacePoses [ ToBoneIndex ] : Pose . RefWorldSpacePoses [ ToBoneIndex ] ;
const FTransform Relative = From . GetRelativeTransform ( To ) ;
return Relative ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No bone with name %s or %s was found " ) , * FromBoneName . ToString ( ) , * ToBoneName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
2023-05-26 13:05:06 -04:00
void UAnimPoseExtensions : : GetSocketNames ( const FAnimPose & Pose , TArray < FName > & Sockets )
{
if ( Pose . IsValid ( ) )
{
Sockets = Pose . SocketNames ;
}
}
FTransform UAnimPoseExtensions : : GetSocketPose ( const FAnimPose & Pose , FName SocketName , EAnimPoseSpaces Space )
{
if ( Pose . IsValid ( ) )
{
const int32 SocketIndex = Pose . SocketNames . IndexOfByKey ( SocketName ) ;
if ( SocketIndex ! = INDEX_NONE )
{
const int32 BoneIndex = Pose . BoneNames . IndexOfByKey ( Pose . SocketParentBoneNames [ SocketIndex ] ) ;
const FTransform BoneTransform = Space = = EAnimPoseSpaces : : Local ? Pose . LocalSpacePoses [ BoneIndex ] : Pose . WorldSpacePoses [ BoneIndex ] ;
return Pose . SocketTransforms [ SocketIndex ] * BoneTransform ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " No socket with name %s was found " ) , * SocketName . ToString ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
return FTransform : : Identity ;
}
2021-05-12 07:33:41 -04:00
void UAnimPoseExtensions : : EvaluateAnimationBlueprintWithInputPose ( const FAnimPose & Pose , USkeletalMesh * TargetSkeletalMesh , UAnimBlueprint * AnimationBlueprint , FAnimPose & OutPose )
{
if ( Pose . IsValid ( ) )
{
if ( TargetSkeletalMesh )
{
if ( AnimationBlueprint )
{
UAnimBlueprintGeneratedClass * AnimGeneratedClass = AnimationBlueprint - > GetAnimBlueprintGeneratedClass ( ) ;
if ( AnimGeneratedClass )
{
if ( AnimGeneratedClass - > TargetSkeleton = = TargetSkeletalMesh - > GetSkeleton ( ) )
{
FMemMark Mark ( FMemStack : : Get ( ) ) ;
FPreviewScene PreviewScene ;
USkeletalMeshComponent * Component = NewObject < USkeletalMeshComponent > ( ) ;
Component - > SetSkeletalMesh ( TargetSkeletalMesh ) ;
Component - > SetAnimInstanceClass ( AnimationBlueprint - > GetAnimBlueprintGeneratedClass ( ) ) ;
PreviewScene . AddComponent ( Component , FTransform : : Identity ) ;
if ( UAnimInstance * AnimInstance = Component - > GetAnimInstance ( ) )
{
if ( FAnimNode_LinkedInputPose * InputNode = AnimInstance - > GetLinkedInputPoseNode ( ) )
{
2021-05-12 12:09:21 -04:00
const FBoneContainer & BoneContainer = AnimInstance - > GetRequiredBones ( ) ;
InputNode - > CachedInputPose . SetBoneContainer ( & BoneContainer ) ;
InputNode - > CachedInputCurve . InitFrom ( BoneContainer ) ;
InputNode - > CachedInputPose . ResetToRefPose ( ) ;
// Copy bone transform from input pose using skeleton index mapping
for ( FCompactPoseBoneIndex CompactIndex : InputNode - > CachedInputPose . ForEachBoneIndex ( ) )
{
const int32 SkeletonIndex = BoneContainer . GetSkeletonIndex ( CompactIndex ) ;
if ( SkeletonIndex ! = INDEX_NONE )
{
const int32 Index = Pose . BoneIndices . IndexOfByKey ( SkeletonIndex ) ;
if ( Index ! = INDEX_NONE )
{
InputNode - > CachedInputPose [ CompactIndex ] = Pose . LocalSpacePoses [ Index ] ;
}
}
}
2021-05-12 07:33:41 -04:00
OutPose . Init ( AnimInstance - > GetRequiredBones ( ) ) ;
Component - > InitAnim ( true ) ;
Component - > RefreshBoneTransforms ( ) ;
const TArray < FTransform > & LocalSpaceTransforms = Component - > GetBoneSpaceTransforms ( ) ;
OutPose . SetPose ( Component ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Failed to retrieve Input Pose Node from Animation Graph %s " ) , * AnimationBlueprint - > GetName ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Animation Blueprint target Skeleton %s does not match Target Skeletal Mesh its Skeleton %s " ) , * AnimGeneratedClass - > TargetSkeleton - > GetName ( ) , * TargetSkeletalMesh - > GetSkeleton ( ) - > GetName ( ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Failed to retrieve Animation Blueprint generated class " ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Invalid Animation Blueprint " ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Invalid Target Skeletal Mesh " ) ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Provided Pose is not valid " ) ) ;
}
}
void UAnimPoseExtensions : : GetReferencePose ( USkeleton * Skeleton , FAnimPose & OutPose )
{
if ( Skeleton )
{
const FReferenceSkeleton & RefSkeleton = Skeleton - > GetReferenceSkeleton ( ) ;
TArray < FBoneIndexType > RequiredBoneIndexArray ;
RequiredBoneIndexArray . AddUninitialized ( RefSkeleton . GetNum ( ) ) ;
for ( int32 BoneIndex = 0 ; BoneIndex < RequiredBoneIndexArray . Num ( ) ; + + BoneIndex )
{
2022-10-20 07:23:59 -04:00
RequiredBoneIndexArray [ BoneIndex ] = IntCastChecked < FBoneIndexType > ( BoneIndex ) ;
2021-05-12 07:33:41 -04:00
}
FBoneContainer RequiredBones ;
Animation Curve Runtime & Editor Improvements
Runtime notes:
- Removes 'smart name' usage across the animation systems.
- Changed curve blending from a uniform array (sized per skeleton) to a sparse array of sorted named values. Blends and other combiners are performed using a dual iteration 'tape merge'.
- Skeleton curves are no longer guaranteed to cover all curve names that can be found at runtime.
Editor notes:
- Curve metadata (flags, bone links etc.) is still present on the skeleton, but can also now exist on a skeletal mesh
- Curve metadata (for morph targets) is still populated on import
- Curves can now be used arbitrarily at runtime
New features:
- New Find/Replace dialog that allows for batch-replacing curves and notifies across all of a project's assets
- New curve debugger tab in various Persona editors that allows for viewing curve values live. This also now allows viewing curves for specific pose watches.
- Pose watches now output curve tracks to the Rewind Debugger
#rb Jurre.deBaare,Nicholas.Frechette,Sara.Schvartzman,Helge.Mathee,Kiaran.Ritchie,Jaime.Cifuentes,Martin.Wilson,Keith.Yerex,Andrean.Franc (and more!)
#jira UE-167776
#jira UE-173716
#jira UE-110407
#preflight 63fc98c81206d91a2bc3ab90
#preflight 63f3ad4f81646f1f24c240c2
[CL 24421496 by Thomas Sarkanen in ue5-main branch]
2023-02-27 07:20:58 -05:00
RequiredBones . InitializeTo ( RequiredBoneIndexArray , UE : : Anim : : FCurveFilterSettings ( UE : : Anim : : ECurveFilterMode : : DisallowAll ) , * Skeleton ) ;
2021-05-12 07:33:41 -04:00
OutPose . Init ( RequiredBones ) ;
OutPose . SetToRefPose ( ) ;
}
else
{
UE_LOG ( LogAnimationPoseScripting , Error , TEXT ( " Invalid Skeleton provided " ) ) ;
}
}
2022-04-06 10:45:20 -04:00
void UAnimPoseExtensions : : GetCurveNames ( const FAnimPose & Pose , TArray < FName > & Curves )
{
Curves . Append ( Pose . CurveNames ) ;
}
float UAnimPoseExtensions : : GetCurveWeight ( const FAnimPose & Pose , const FName & CurveName )
{
float CurveValue = 0.f ;
const int32 CurveIndex = Pose . CurveNames . IndexOfByKey ( CurveName ) ;
if ( CurveIndex ! = INDEX_NONE )
{
CurveValue = Pose . CurveValues [ CurveIndex ] ;
}
return CurveValue ;
}
2021-05-12 07:33:41 -04:00
void UAnimPoseExtensions : : GetAnimPoseAtFrame ( const UAnimSequenceBase * AnimationSequenceBase , int32 FrameIndex , FAnimPoseEvaluationOptions EvaluationOptions , FAnimPose & Pose )
{
2023-05-26 13:05:06 -04:00
if ( AnimationSequenceBase )
{
const double Time = AnimationSequenceBase - > GetDataModel ( ) - > GetFrameRate ( ) . AsSeconds ( FrameIndex ) ;
GetAnimPoseAtTime ( AnimationSequenceBase , Time , EvaluationOptions , Pose ) ;
}
2021-05-12 07:33:41 -04:00
}
Reintroducing AnimationDataModel refactor with additional memory and cooktime optimizations.
**Animation Data Model load/cook time and memory optimizations**
- Added SetKeysOnly to FMovieSceneFloatChannel, allowing to only set value/frames values and skip allocating key handles
- Added EvaluateWithCache to TMovieSceneCurveChannelImpl, allowing to evaluate multiple curves using a caching data structure holding the frame-indices resulting from the FrameTime -> indexes calculationg
- UFKControlRig::GetControlName/GetControlTargetName, added thread_local mapping cache and optimized FName generation
- Made the sequencer bone curve keys 'sparse' again, if constant it just sets the default value for the curve otherwise it is still uniform
- Only resample keys when compressing, and clear them on any significant model changes and when save-during-cook has been completed
- Routed ::Presave for AnimSequencerDataModel directly through to UMovieSceneSignedObject to skip compiling cooked sequencer that that will/should never cooked and packaged
- Added some significant pass-by-reference changes to AnimSequencerModel APIs (passing large arrays by value previously) :doh:
- Optimized GenerateLegacyBoneData
Original CL description:
**New**
- Engine
- IAnimationDataModel interface
- AnimationData plugin
- AnimSequencerDataModel, sequencer based implementation of IAnimationDataModel
- AnimSequencerDataController, controller for above (implementation of IAnimationDataController)
- Added FCompressedRichCurve::PopulateCurve, allowing users to convert back to a FRichCurve for validation/debugging
- Added DefaultFrameRate to AnimationSettings, this replaces the hardcoded 30FPS in code
- Added ::GetKeyIndicesFromTime which takes FFrameRate
- Added AnimSequenceBase::OnAnimModelLoaded, this is required for correct postloading behaviour of data model (specifically AnimSequencerDataModel, as it depends on data from its outer ? anim sequence)
- Added IAnimationDataModel ::Evaluate which now takes responsibility for evaluating raw animation bone, curve and attribute data. And moved all trackbased evaluation code into AnimDataModel.cpp
- Added a.ForceEvalRawData allowing to force evaluation of source data
- Added a.SkipDDC to force compressing animation data locally
**Changed**
- Reparent UAnimDataModel to IAnimationDataModel interface, and rejig behaviour
- AnimSequenceBase now reference AnimDataModel as IAnimationDataModel
- Upgrade path for legacy to IAnimationDataModel and existing UAnimDataModel to IAnimationDataModel through IAnimationDataController::PopulateWithExisting
- IAnimationDataModel data is now frame(number/rate/time) based rather than seconds/keyindices. This enforces frameborder aligned data from now on.
- Moved RichCurve evaluation code from RichCurve.cpp to separate file and consolidated all instances of copy-pasta behaviour
- Updated/removed deprecated EngineTests around AnimSequences
- Changed many instances to use a FFrameTime together with a known FFrameRate versus seconds and sequencelength in float involving frame \/ time calculations (including instances of FAnimKeyHelper)
- Switched anim sequence evaluation to use double in Extraction context for time value and patched up places with static\_cast\<double\>
- FRichCurve::Eval
- Make sure we always evaluate keys when T >= Key0.Time and T <= KeyN.Time to deal with crazy userweighted tangents
- Fixed WeightedKeyDataAdapter::GetKeyInterpMode/GetKeyTangentWeightMode retrieving invalid values, as it was indexing according to KeyIndex rather than KeyIndex\*2. This made it so that values were misinterpreted.
- Fixed WeightedEvalForTwoKeys for compressed data retrieving GetKeyInterpMode and GetKeyTangentWeightMode using the wrong index value. As they are not indexed using KeyDataHandle type.
- Fixed issue where compression could crash when containing zero-key scale additivebase track (tries to retrieve)
- Replaced instances of NULL with nullptr
- Moved required decompression information into FAnimSequenceDecompressionContext and reimplemented decompression within UE::Anim::Decompression namespace and new file
- Deprecated ::GetRawDataGuid and replaced with GetDataModel()->GenerateGuid()
- Fixed UAnimStreamable evaluation/compression (previously broken with MVC refactor)
- Updated Animation exporting pipeline to be frame-based rather than seconds
- Deprecated RetargetPose, replaced with FRetargetingScope (used within AnimModel::Evaluate)
- Fixed BaseAdditiveAnimation array become invalid/incorrect when removing zero-additve tracks during compression
- Updated EngineTest animation sequence related tests to new APIs (while maintaining deprecated path testing as well for now)
- AnimDataController / Animation Sequence tests now generate a USkeleton for the transient animation sequence (used to perform the test on)
**Removed**
- Unused file/class AnimData/AnimDataNotifyCollector.h
#rb Thomas.Sarkanen, Mike.Zyracki
#jira UE-131296
#preflight 631f6897a9331c69c3a34d1e
[CL 21980597 by Jurre deBaare in ue5-main branch]
2022-09-13 06:41:15 -04:00
void UAnimPoseExtensions : : GetAnimPoseAtTime ( const UAnimSequenceBase * AnimationSequenceBase , double Time , FAnimPoseEvaluationOptions EvaluationOptions , FAnimPose & Pose )
2022-04-06 10:45:20 -04:00
{
TArray < FAnimPose > InOutPoses ;
GetAnimPoseAtTimeIntervals ( AnimationSequenceBase , { Time } , EvaluationOptions , InOutPoses ) ;
if ( InOutPoses . Num ( ) )
{
ensure ( InOutPoses . Num ( ) = = 1 ) ;
Pose = InOutPoses [ 0 ] ;
}
}
Reintroducing AnimationDataModel refactor with additional memory and cooktime optimizations.
**Animation Data Model load/cook time and memory optimizations**
- Added SetKeysOnly to FMovieSceneFloatChannel, allowing to only set value/frames values and skip allocating key handles
- Added EvaluateWithCache to TMovieSceneCurveChannelImpl, allowing to evaluate multiple curves using a caching data structure holding the frame-indices resulting from the FrameTime -> indexes calculationg
- UFKControlRig::GetControlName/GetControlTargetName, added thread_local mapping cache and optimized FName generation
- Made the sequencer bone curve keys 'sparse' again, if constant it just sets the default value for the curve otherwise it is still uniform
- Only resample keys when compressing, and clear them on any significant model changes and when save-during-cook has been completed
- Routed ::Presave for AnimSequencerDataModel directly through to UMovieSceneSignedObject to skip compiling cooked sequencer that that will/should never cooked and packaged
- Added some significant pass-by-reference changes to AnimSequencerModel APIs (passing large arrays by value previously) :doh:
- Optimized GenerateLegacyBoneData
Original CL description:
**New**
- Engine
- IAnimationDataModel interface
- AnimationData plugin
- AnimSequencerDataModel, sequencer based implementation of IAnimationDataModel
- AnimSequencerDataController, controller for above (implementation of IAnimationDataController)
- Added FCompressedRichCurve::PopulateCurve, allowing users to convert back to a FRichCurve for validation/debugging
- Added DefaultFrameRate to AnimationSettings, this replaces the hardcoded 30FPS in code
- Added ::GetKeyIndicesFromTime which takes FFrameRate
- Added AnimSequenceBase::OnAnimModelLoaded, this is required for correct postloading behaviour of data model (specifically AnimSequencerDataModel, as it depends on data from its outer ? anim sequence)
- Added IAnimationDataModel ::Evaluate which now takes responsibility for evaluating raw animation bone, curve and attribute data. And moved all trackbased evaluation code into AnimDataModel.cpp
- Added a.ForceEvalRawData allowing to force evaluation of source data
- Added a.SkipDDC to force compressing animation data locally
**Changed**
- Reparent UAnimDataModel to IAnimationDataModel interface, and rejig behaviour
- AnimSequenceBase now reference AnimDataModel as IAnimationDataModel
- Upgrade path for legacy to IAnimationDataModel and existing UAnimDataModel to IAnimationDataModel through IAnimationDataController::PopulateWithExisting
- IAnimationDataModel data is now frame(number/rate/time) based rather than seconds/keyindices. This enforces frameborder aligned data from now on.
- Moved RichCurve evaluation code from RichCurve.cpp to separate file and consolidated all instances of copy-pasta behaviour
- Updated/removed deprecated EngineTests around AnimSequences
- Changed many instances to use a FFrameTime together with a known FFrameRate versus seconds and sequencelength in float involving frame \/ time calculations (including instances of FAnimKeyHelper)
- Switched anim sequence evaluation to use double in Extraction context for time value and patched up places with static\_cast\<double\>
- FRichCurve::Eval
- Make sure we always evaluate keys when T >= Key0.Time and T <= KeyN.Time to deal with crazy userweighted tangents
- Fixed WeightedKeyDataAdapter::GetKeyInterpMode/GetKeyTangentWeightMode retrieving invalid values, as it was indexing according to KeyIndex rather than KeyIndex\*2. This made it so that values were misinterpreted.
- Fixed WeightedEvalForTwoKeys for compressed data retrieving GetKeyInterpMode and GetKeyTangentWeightMode using the wrong index value. As they are not indexed using KeyDataHandle type.
- Fixed issue where compression could crash when containing zero-key scale additivebase track (tries to retrieve)
- Replaced instances of NULL with nullptr
- Moved required decompression information into FAnimSequenceDecompressionContext and reimplemented decompression within UE::Anim::Decompression namespace and new file
- Deprecated ::GetRawDataGuid and replaced with GetDataModel()->GenerateGuid()
- Fixed UAnimStreamable evaluation/compression (previously broken with MVC refactor)
- Updated Animation exporting pipeline to be frame-based rather than seconds
- Deprecated RetargetPose, replaced with FRetargetingScope (used within AnimModel::Evaluate)
- Fixed BaseAdditiveAnimation array become invalid/incorrect when removing zero-additve tracks during compression
- Updated EngineTest animation sequence related tests to new APIs (while maintaining deprecated path testing as well for now)
- AnimDataController / Animation Sequence tests now generate a USkeleton for the transient animation sequence (used to perform the test on)
**Removed**
- Unused file/class AnimData/AnimDataNotifyCollector.h
#rb Thomas.Sarkanen, Mike.Zyracki
#jira UE-131296
#preflight 631f6897a9331c69c3a34d1e
[CL 21980597 by Jurre deBaare in ue5-main branch]
2022-09-13 06:41:15 -04:00
void UAnimPoseExtensions : : GetAnimPoseAtTimeIntervals ( const UAnimSequenceBase * AnimationSequenceBase , TArray < double > TimeIntervals , FAnimPoseEvaluationOptions EvaluationOptions , TArray < FAnimPose > & InOutPoses )
2021-05-12 07:33:41 -04:00
{
if ( AnimationSequenceBase & & AnimationSequenceBase - > GetSkeleton ( ) )
{
FMemMark Mark ( FMemStack : : Get ( ) ) ;
2022-04-06 10:45:20 -04:00
// asset to use for retarget proportions (can be either USkeletalMesh or USkeleton)
UObject * AssetToUse ;
int32 NumRequiredBones ;
if ( EvaluationOptions . OptionalSkeletalMesh )
{
AssetToUse = CastChecked < UObject > ( EvaluationOptions . OptionalSkeletalMesh ) ;
NumRequiredBones = EvaluationOptions . OptionalSkeletalMesh - > GetRefSkeleton ( ) . GetNum ( ) ;
2021-05-12 07:33:41 -04:00
}
else
{
2022-04-06 10:45:20 -04:00
AssetToUse = CastChecked < UObject > ( AnimationSequenceBase - > GetSkeleton ( ) ) ;
NumRequiredBones = AnimationSequenceBase - > GetSkeleton ( ) - > GetReferenceSkeleton ( ) . GetNum ( ) ;
}
TArray < FBoneIndexType > RequiredBoneIndexArray ;
RequiredBoneIndexArray . AddUninitialized ( NumRequiredBones ) ;
for ( int32 BoneIndex = 0 ; BoneIndex < RequiredBoneIndexArray . Num ( ) ; + + BoneIndex )
{
2022-10-20 07:23:59 -04:00
RequiredBoneIndexArray [ BoneIndex ] = static_cast < FBoneIndexType > ( BoneIndex ) ;
2022-04-06 10:45:20 -04:00
}
FBoneContainer RequiredBones ;
Animation Curve Runtime & Editor Improvements
Runtime notes:
- Removes 'smart name' usage across the animation systems.
- Changed curve blending from a uniform array (sized per skeleton) to a sparse array of sorted named values. Blends and other combiners are performed using a dual iteration 'tape merge'.
- Skeleton curves are no longer guaranteed to cover all curve names that can be found at runtime.
Editor notes:
- Curve metadata (flags, bone links etc.) is still present on the skeleton, but can also now exist on a skeletal mesh
- Curve metadata (for morph targets) is still populated on import
- Curves can now be used arbitrarily at runtime
New features:
- New Find/Replace dialog that allows for batch-replacing curves and notifies across all of a project's assets
- New curve debugger tab in various Persona editors that allows for viewing curve values live. This also now allows viewing curves for specific pose watches.
- Pose watches now output curve tracks to the Rewind Debugger
#rb Jurre.deBaare,Nicholas.Frechette,Sara.Schvartzman,Helge.Mathee,Kiaran.Ritchie,Jaime.Cifuentes,Martin.Wilson,Keith.Yerex,Andrean.Franc (and more!)
#jira UE-167776
#jira UE-173716
#jira UE-110407
#preflight 63fc98c81206d91a2bc3ab90
#preflight 63f3ad4f81646f1f24c240c2
[CL 24421496 by Thomas Sarkanen in ue5-main branch]
2023-02-27 07:20:58 -05:00
RequiredBones . InitializeTo ( RequiredBoneIndexArray , UE : : Anim : : FCurveFilterSettings ( EvaluationOptions . bEvaluateCurves ? UE : : Anim : : ECurveFilterMode : : None : UE : : Anim : : ECurveFilterMode : : DisallowAll ) , * AssetToUse ) ;
2022-04-06 10:45:20 -04:00
RequiredBones . SetUseRAWData ( EvaluationOptions . EvaluationType = = EAnimDataEvalType : : Raw ) ;
RequiredBones . SetUseSourceData ( EvaluationOptions . EvaluationType = = EAnimDataEvalType : : Source ) ;
RequiredBones . SetDisableRetargeting ( ! EvaluationOptions . bShouldRetarget ) ;
FCompactPose CompactPose ;
FBlendedCurve Curve ;
UE : : Anim : : FStackAttributeContainer Attributes ;
FAnimationPoseData PoseData ( CompactPose , Curve , Attributes ) ;
Reintroducing AnimationDataModel refactor with additional memory and cooktime optimizations.
**Animation Data Model load/cook time and memory optimizations**
- Added SetKeysOnly to FMovieSceneFloatChannel, allowing to only set value/frames values and skip allocating key handles
- Added EvaluateWithCache to TMovieSceneCurveChannelImpl, allowing to evaluate multiple curves using a caching data structure holding the frame-indices resulting from the FrameTime -> indexes calculationg
- UFKControlRig::GetControlName/GetControlTargetName, added thread_local mapping cache and optimized FName generation
- Made the sequencer bone curve keys 'sparse' again, if constant it just sets the default value for the curve otherwise it is still uniform
- Only resample keys when compressing, and clear them on any significant model changes and when save-during-cook has been completed
- Routed ::Presave for AnimSequencerDataModel directly through to UMovieSceneSignedObject to skip compiling cooked sequencer that that will/should never cooked and packaged
- Added some significant pass-by-reference changes to AnimSequencerModel APIs (passing large arrays by value previously) :doh:
- Optimized GenerateLegacyBoneData
Original CL description:
**New**
- Engine
- IAnimationDataModel interface
- AnimationData plugin
- AnimSequencerDataModel, sequencer based implementation of IAnimationDataModel
- AnimSequencerDataController, controller for above (implementation of IAnimationDataController)
- Added FCompressedRichCurve::PopulateCurve, allowing users to convert back to a FRichCurve for validation/debugging
- Added DefaultFrameRate to AnimationSettings, this replaces the hardcoded 30FPS in code
- Added ::GetKeyIndicesFromTime which takes FFrameRate
- Added AnimSequenceBase::OnAnimModelLoaded, this is required for correct postloading behaviour of data model (specifically AnimSequencerDataModel, as it depends on data from its outer ? anim sequence)
- Added IAnimationDataModel ::Evaluate which now takes responsibility for evaluating raw animation bone, curve and attribute data. And moved all trackbased evaluation code into AnimDataModel.cpp
- Added a.ForceEvalRawData allowing to force evaluation of source data
- Added a.SkipDDC to force compressing animation data locally
**Changed**
- Reparent UAnimDataModel to IAnimationDataModel interface, and rejig behaviour
- AnimSequenceBase now reference AnimDataModel as IAnimationDataModel
- Upgrade path for legacy to IAnimationDataModel and existing UAnimDataModel to IAnimationDataModel through IAnimationDataController::PopulateWithExisting
- IAnimationDataModel data is now frame(number/rate/time) based rather than seconds/keyindices. This enforces frameborder aligned data from now on.
- Moved RichCurve evaluation code from RichCurve.cpp to separate file and consolidated all instances of copy-pasta behaviour
- Updated/removed deprecated EngineTests around AnimSequences
- Changed many instances to use a FFrameTime together with a known FFrameRate versus seconds and sequencelength in float involving frame \/ time calculations (including instances of FAnimKeyHelper)
- Switched anim sequence evaluation to use double in Extraction context for time value and patched up places with static\_cast\<double\>
- FRichCurve::Eval
- Make sure we always evaluate keys when T >= Key0.Time and T <= KeyN.Time to deal with crazy userweighted tangents
- Fixed WeightedKeyDataAdapter::GetKeyInterpMode/GetKeyTangentWeightMode retrieving invalid values, as it was indexing according to KeyIndex rather than KeyIndex\*2. This made it so that values were misinterpreted.
- Fixed WeightedEvalForTwoKeys for compressed data retrieving GetKeyInterpMode and GetKeyTangentWeightMode using the wrong index value. As they are not indexed using KeyDataHandle type.
- Fixed issue where compression could crash when containing zero-key scale additivebase track (tries to retrieve)
- Replaced instances of NULL with nullptr
- Moved required decompression information into FAnimSequenceDecompressionContext and reimplemented decompression within UE::Anim::Decompression namespace and new file
- Deprecated ::GetRawDataGuid and replaced with GetDataModel()->GenerateGuid()
- Fixed UAnimStreamable evaluation/compression (previously broken with MVC refactor)
- Updated Animation exporting pipeline to be frame-based rather than seconds
- Deprecated RetargetPose, replaced with FRetargetingScope (used within AnimModel::Evaluate)
- Fixed BaseAdditiveAnimation array become invalid/incorrect when removing zero-additve tracks during compression
- Updated EngineTest animation sequence related tests to new APIs (while maintaining deprecated path testing as well for now)
- AnimDataController / Animation Sequence tests now generate a USkeleton for the transient animation sequence (used to perform the test on)
**Removed**
- Unused file/class AnimData/AnimDataNotifyCollector.h
#rb Thomas.Sarkanen, Mike.Zyracki
#jira UE-131296
#preflight 631f6897a9331c69c3a34d1e
[CL 21980597 by Jurre deBaare in ue5-main branch]
2022-09-13 06:41:15 -04:00
FAnimExtractContext Context ( 0.0 , EvaluationOptions . bExtractRootMotion ) ;
2023-08-02 05:38:23 -04:00
# if WITH_EDITOR
Context . bIgnoreRootLock = EvaluationOptions . bIncorporateRootMotionIntoPose ;
# endif // WITH_EDITOR
2022-04-06 10:45:20 -04:00
FCompactPose BasePose ;
BasePose . SetBoneContainer ( & RequiredBones ) ;
CompactPose . SetBoneContainer ( & RequiredBones ) ;
Curve . InitFrom ( RequiredBones ) ;
FAnimPose Pose ;
Pose . Init ( RequiredBones ) ;
for ( int32 Index = 0 ; Index < TimeIntervals . Num ( ) ; + + Index )
{
Reintroducing AnimationDataModel refactor with additional memory and cooktime optimizations.
**Animation Data Model load/cook time and memory optimizations**
- Added SetKeysOnly to FMovieSceneFloatChannel, allowing to only set value/frames values and skip allocating key handles
- Added EvaluateWithCache to TMovieSceneCurveChannelImpl, allowing to evaluate multiple curves using a caching data structure holding the frame-indices resulting from the FrameTime -> indexes calculationg
- UFKControlRig::GetControlName/GetControlTargetName, added thread_local mapping cache and optimized FName generation
- Made the sequencer bone curve keys 'sparse' again, if constant it just sets the default value for the curve otherwise it is still uniform
- Only resample keys when compressing, and clear them on any significant model changes and when save-during-cook has been completed
- Routed ::Presave for AnimSequencerDataModel directly through to UMovieSceneSignedObject to skip compiling cooked sequencer that that will/should never cooked and packaged
- Added some significant pass-by-reference changes to AnimSequencerModel APIs (passing large arrays by value previously) :doh:
- Optimized GenerateLegacyBoneData
Original CL description:
**New**
- Engine
- IAnimationDataModel interface
- AnimationData plugin
- AnimSequencerDataModel, sequencer based implementation of IAnimationDataModel
- AnimSequencerDataController, controller for above (implementation of IAnimationDataController)
- Added FCompressedRichCurve::PopulateCurve, allowing users to convert back to a FRichCurve for validation/debugging
- Added DefaultFrameRate to AnimationSettings, this replaces the hardcoded 30FPS in code
- Added ::GetKeyIndicesFromTime which takes FFrameRate
- Added AnimSequenceBase::OnAnimModelLoaded, this is required for correct postloading behaviour of data model (specifically AnimSequencerDataModel, as it depends on data from its outer ? anim sequence)
- Added IAnimationDataModel ::Evaluate which now takes responsibility for evaluating raw animation bone, curve and attribute data. And moved all trackbased evaluation code into AnimDataModel.cpp
- Added a.ForceEvalRawData allowing to force evaluation of source data
- Added a.SkipDDC to force compressing animation data locally
**Changed**
- Reparent UAnimDataModel to IAnimationDataModel interface, and rejig behaviour
- AnimSequenceBase now reference AnimDataModel as IAnimationDataModel
- Upgrade path for legacy to IAnimationDataModel and existing UAnimDataModel to IAnimationDataModel through IAnimationDataController::PopulateWithExisting
- IAnimationDataModel data is now frame(number/rate/time) based rather than seconds/keyindices. This enforces frameborder aligned data from now on.
- Moved RichCurve evaluation code from RichCurve.cpp to separate file and consolidated all instances of copy-pasta behaviour
- Updated/removed deprecated EngineTests around AnimSequences
- Changed many instances to use a FFrameTime together with a known FFrameRate versus seconds and sequencelength in float involving frame \/ time calculations (including instances of FAnimKeyHelper)
- Switched anim sequence evaluation to use double in Extraction context for time value and patched up places with static\_cast\<double\>
- FRichCurve::Eval
- Make sure we always evaluate keys when T >= Key0.Time and T <= KeyN.Time to deal with crazy userweighted tangents
- Fixed WeightedKeyDataAdapter::GetKeyInterpMode/GetKeyTangentWeightMode retrieving invalid values, as it was indexing according to KeyIndex rather than KeyIndex\*2. This made it so that values were misinterpreted.
- Fixed WeightedEvalForTwoKeys for compressed data retrieving GetKeyInterpMode and GetKeyTangentWeightMode using the wrong index value. As they are not indexed using KeyDataHandle type.
- Fixed issue where compression could crash when containing zero-key scale additivebase track (tries to retrieve)
- Replaced instances of NULL with nullptr
- Moved required decompression information into FAnimSequenceDecompressionContext and reimplemented decompression within UE::Anim::Decompression namespace and new file
- Deprecated ::GetRawDataGuid and replaced with GetDataModel()->GenerateGuid()
- Fixed UAnimStreamable evaluation/compression (previously broken with MVC refactor)
- Updated Animation exporting pipeline to be frame-based rather than seconds
- Deprecated RetargetPose, replaced with FRetargetingScope (used within AnimModel::Evaluate)
- Fixed BaseAdditiveAnimation array become invalid/incorrect when removing zero-additve tracks during compression
- Updated EngineTest animation sequence related tests to new APIs (while maintaining deprecated path testing as well for now)
- AnimDataController / Animation Sequence tests now generate a USkeleton for the transient animation sequence (used to perform the test on)
**Removed**
- Unused file/class AnimData/AnimDataNotifyCollector.h
#rb Thomas.Sarkanen, Mike.Zyracki
#jira UE-131296
#preflight 631f6897a9331c69c3a34d1e
[CL 21980597 by Jurre deBaare in ue5-main branch]
2022-09-13 06:41:15 -04:00
const double EvalInterval = TimeIntervals [ Index ] ;
2022-04-06 10:45:20 -04:00
bool bValidTime = false ;
2022-10-20 07:23:59 -04:00
UAnimationBlueprintLibrary : : IsValidTime ( AnimationSequenceBase , static_cast < float > ( EvalInterval ) , bValidTime ) ;
2022-04-06 10:45:20 -04:00
ensure ( bValidTime ) ;
Context . CurrentTime = EvalInterval ;
FAnimPose & FramePose = InOutPoses . AddDefaulted_GetRef ( ) ;
FramePose = Pose ;
Curve . InitFrom ( RequiredBones ) ;
if ( bValidTime )
{
if ( AnimationSequenceBase - > IsValidAdditive ( ) )
{
CompactPose . ResetToAdditiveIdentity ( ) ;
AnimationSequenceBase - > GetAnimationPose ( PoseData , Context ) ;
if ( EvaluationOptions . bRetrieveAdditiveAsFullPose )
{
const UAnimSequence * AnimSequence = Cast < const UAnimSequence > ( AnimationSequenceBase ) ;
FBlendedCurve BaseCurve ;
BaseCurve . InitFrom ( RequiredBones ) ;
UE : : Anim : : FStackAttributeContainer BaseAttributes ;
FAnimationPoseData BasePoseData ( BasePose , BaseCurve , BaseAttributes ) ;
AnimSequence - > GetAdditiveBasePose ( BasePoseData , Context ) ;
FAnimationRuntime : : AccumulateAdditivePose ( BasePoseData , PoseData , 1.f , AnimSequence - > GetAdditiveAnimType ( ) ) ;
BasePose . NormalizeRotations ( ) ;
FramePose . SetPose ( BasePoseData ) ;
}
else
{
FramePose . SetPose ( PoseData ) ;
}
}
else
{
CompactPose . ResetToRefPose ( ) ;
AnimationSequenceBase - > GetAnimationPose ( PoseData , Context ) ;
FramePose . SetPose ( PoseData ) ;
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Invalid time value %f for Animation Sequence %s supplied for GetBonePosesForTime " ) , EvalInterval , * AnimationSequenceBase - > GetName ( ) ) ;
}
2021-05-12 07:33:41 -04:00
}
}
else
{
UE_LOG ( LogAnimationPoseScripting , Warning , TEXT ( " Invalid Animation Sequence supplied for GetBonePosesForTime " ) ) ;
}
2021-05-12 12:09:21 -04:00
}