// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimNode_RigLogic.h" #include "Components/SkeletalMeshComponent.h" #include "DNAAsset.h" #include "DNAIndexMapping.h" #include "DNAReader.h" #include "Engine/SkeletalMesh.h" #include "RigLogic.h" #include "RigInstance.h" #include "SharedRigRuntimeContext.h" #include "Animation/AnimInstanceProxy.h" #include "HAL/LowLevelMemTracker.h" #include "Animation/MorphTarget.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_RigLogic) LLM_DEFINE_TAG(Animation_RigLogic); DEFINE_LOG_CATEGORY(LogRigLogicAnimNode); static constexpr uint16 ATTR_COUNT_PER_JOINT = 10; FAnimNode_RigLogic::FAnimNode_RigLogic() : RigInstance(nullptr) { } FAnimNode_RigLogic::~FAnimNode_RigLogic() { if (RigInstance != nullptr) { delete RigInstance; RigInstance = nullptr; } } void FAnimNode_RigLogic::Initialize_AnyThread(const FAnimationInitializeContext& Context) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_Initialize_AnyThread); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::Initialize_AnyThread"); #endif // CPUPROFILERTRACE_ENABLED LLM_SCOPE_BYNAME(TEXT("Animation/RigLogic")); AnimSequence.Initialize(Context); } void FAnimNode_RigLogic::CacheVariableJointAttributes(const FBoneContainer& RequiredBones) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_CacheVariableJointAttributes); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::CacheVariableJointAttributes"); #endif // CPUPROFILERTRACE_ENABLED LLM_SCOPE_BYNAME(TEXT("Animation/RigLogic")); // Populate mapping of DNA joint indices to CompactPoseBoneIndex entries (used in updating joints with results from RigLogic) const uint16 CurrentLOD = RigInstance->GetLOD(); TArrayView VariableJointIndices = LocalRigRuntimeContext->VariableJointIndicesPerLOD[CurrentLOD].Values; FCachedJointMapping& CurrentLODJointMapping = LocalJointMappingsPerLOD[CurrentLOD]; auto& JointsMapDNAIndicesToCompactPoseBoneIndices = CurrentLODJointMapping.JointsMapDNAIndicesToCompactPoseBoneIndices; JointsMapDNAIndicesToCompactPoseBoneIndices.Empty(); JointsMapDNAIndicesToCompactPoseBoneIndices.Reserve(VariableJointIndices.Num()); for (const uint16 JointIndex : VariableJointIndices) { const FMeshPoseBoneIndex MeshPoseBoneIndex = LocalDNAIndexMapping->JointsMapDNAIndicesToMeshPoseBoneIndices[JointIndex]; const FCompactPoseBoneIndex CompactPoseBoneIndex = RequiredBones.MakeCompactPoseIndex(MeshPoseBoneIndex); if (CompactPoseBoneIndex != INDEX_NONE) { JointsMapDNAIndicesToCompactPoseBoneIndices.Add({JointIndex, CompactPoseBoneIndex}); } } } void FAnimNode_RigLogic::CacheDriverJoints(const FBoneContainer& RequiredBones) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_CacheDriverJoints); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::CacheDriverJoints"); #endif // CPUPROFILERTRACE_ENABLED LLM_SCOPE_BYNAME(TEXT("Animation/RigLogic")); const uint16 CurrentLOD = RigInstance->GetLOD(); FCachedJointMapping& CurrentLODJointMapping = LocalJointMappingsPerLOD[CurrentLOD]; auto& SparseDriverJointsToControlAttributesMap = CurrentLODJointMapping.SparseDriverJointsToControlAttributesMap; auto& DenseDriverJointsToControlAttributesMap = CurrentLODJointMapping.DenseDriverJointsToControlAttributesMap; // Populate driver joint to raw control attribute mapping (used to feed RigLogic with inputs from the joint hierarchy) SparseDriverJointsToControlAttributesMap.Empty(); DenseDriverJointsToControlAttributesMap.Empty(); DenseDriverJointsToControlAttributesMap.Reserve(LocalDNAIndexMapping->DriverJointsToControlAttributesMap.Num()); // Sparse mapping will likely remain empty so no reservation happens for (const auto& Mapping : LocalDNAIndexMapping->DriverJointsToControlAttributesMap) { const FCompactPoseBoneIndex CompactPoseBoneIndex = RequiredBones.MakeCompactPoseIndex(Mapping.MeshPoseBoneIndex); if (CompactPoseBoneIndex != INDEX_NONE) { if ((Mapping.RotationX != INDEX_NONE) && (Mapping.RotationY != INDEX_NONE) && (Mapping.RotationZ != INDEX_NONE) && (Mapping.RotationW != INDEX_NONE)) { DenseDriverJointsToControlAttributesMap.Add({CompactPoseBoneIndex, Mapping.DNAJointIndex, Mapping.RotationX, Mapping.RotationY, Mapping.RotationZ, Mapping.RotationW}); } else { SparseDriverJointsToControlAttributesMap.Add({CompactPoseBoneIndex, Mapping.DNAJointIndex, Mapping.RotationX, Mapping.RotationY, Mapping.RotationZ, Mapping.RotationW}); } } } } void FAnimNode_RigLogic::CachePoseCurvesToRigLogicControlsMap(const FPoseContext& InputContext, const FCachedIndexedCurve& IndexedCurves, TArray& Indices) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_CachePoseCurvesToRigLogicControlsMap); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::CachePoseCurvesToRigLogicControlsMap"); #endif // CPUPROFILERTRACE_ENABLED LLM_SCOPE_BYNAME(TEXT("Animation/RigLogic")); Indices.Init(INDEX_NONE, InputContext.Curve.Num()); int32 CurveIndex = 0; InputContext.Curve.ForEachElement([&](const UE::Anim::FCurveElement& InCurveElement) { IndexedCurves.ForEachElement([&](const UE::Anim::FCurveElementIndexed& InControlAttributeCurveElement) { if (InCurveElement.Name == InControlAttributeCurveElement.Name) { Indices[CurveIndex] = InControlAttributeCurveElement.Index; } }); ++CurveIndex; }); } void FAnimNode_RigLogic::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_CacheBones_AnyThread); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::CacheBones_AnyThread"); #endif // CPUPROFILERTRACE_ENABLED LLM_SCOPE_BYNAME(TEXT("Animation/RigLogic")); AnimSequence.CacheBones(Context); USkeletalMeshComponent* SkeletalMeshComponent = Context.AnimInstanceProxy->GetSkelMeshComponent(); if (SkeletalMeshComponent == nullptr) { return; } USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->GetSkeletalMeshAsset(); if (SkeletalMesh == nullptr) { return; } USkeleton* Skeleton = Context.AnimInstanceProxy->GetSkeleton(); if (Skeleton == nullptr) { return; } UDNAAsset* DNAAsset = Cast(SkeletalMesh->GetAssetUserDataOfClass(UDNAAsset::StaticClass())); if (DNAAsset == nullptr) { return; } TSharedPtr SharedRigRuntimeContext = DNAAsset->GetRigRuntimeContext(); if (!SharedRigRuntimeContext.IsValid()) { return; } if (LocalRigRuntimeContext != SharedRigRuntimeContext) { LocalRigRuntimeContext = SharedRigRuntimeContext; if (RigInstance != nullptr) { delete RigInstance; } RigInstance = new FRigInstance(LocalRigRuntimeContext->RigLogic.Get()); } RigInstance->SetLOD(Context.AnimInstanceProxy->GetLODLevel()); TSharedPtr SharedDNAIndexMapping = DNAAsset->GetDNAIndexMapping(Skeleton, SkeletalMesh); if (LocalDNAIndexMapping != SharedDNAIndexMapping) { const uint16 LODCount = LocalRigRuntimeContext->RigLogic->GetLODCount(); LocalDNAIndexMapping = SharedDNAIndexMapping; LocalJointMappingsPerLOD.Empty(); LocalJointMappingsPerLOD.SetNum(LODCount); PoseCurvesToRigLogicControlsMap.Reset(LODCount); PoseCurvesToRigLogicControlsMap.AddDefaulted(LODCount); } // CacheBones is called on LOD switches as well, in which case compact pose bone indices must be remapped const FBoneContainer& RequiredBones = Context.AnimInstanceProxy->GetRequiredBones(); if (RequiredBones.IsValid()) { const uint16 CurrentLOD = RigInstance->GetLOD(); FCachedJointMapping& CurrentLODJointMapping = LocalJointMappingsPerLOD[CurrentLOD]; // Lazily initialize and cache mappings for each LOD as they are requested const int32 BoneCountForLOD = RequiredBones.GetCompactPoseNumBones(); if (CurrentLODJointMapping.BoneCount != BoneCountForLOD) { const FRigLogicConfiguration& RigLogicConfig = LocalRigRuntimeContext->RigLogic->GetConfiguration(); if (RigLogicConfig.LoadJoints) { CacheVariableJointAttributes(RequiredBones); } if (RigLogicConfig.LoadTwistSwingBehavior || RigLogicConfig.LoadRBFBehavior) { CacheDriverJoints(RequiredBones); } CurrentLODJointMapping.BoneCount = BoneCountForLOD; } } } void FAnimNode_RigLogic::Update_AnyThread(const FAnimationUpdateContext& Context) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_Update_AnyThread); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::Update_AnyThread"); #endif // CPUPROFILERTRACE_ENABLED GetEvaluateGraphExposedInputs().Execute(Context); AnimSequence.Update(Context); } void FAnimNode_RigLogic::Evaluate_AnyThread(FPoseContext& OutputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_Evaluate_AnyThread); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::Evaluate_AnyThread"); #endif // CPUPROFILERTRACE_ENABLED AnimSequence.Evaluate(OutputContext); if (!LocalRigRuntimeContext.IsValid() || !LocalDNAIndexMapping.IsValid()) { return; } if (!IsLODEnabled(OutputContext.AnimInstanceProxy)) { return; } const FRigLogicConfiguration& RigLogicConfig = LocalRigRuntimeContext->RigLogic->GetConfiguration(); UpdateControlCurves(OutputContext); CalculateRigLogic(); if (RigLogicConfig.LoadJoints) { UpdateJoints(OutputContext); } if (RigLogicConfig.LoadBlendShapes) { UpdateBlendShapeCurves(OutputContext); } if (RigLogicConfig.LoadAnimatedMaps) { UpdateAnimMapCurves(OutputContext); } #if STATS LocalRigRuntimeContext->RigLogic->CollectCalculationStats(RigInstance); #endif // STATS } void FAnimNode_RigLogic::GatherDebugData(FNodeDebugData& DebugData) { AnimSequence.GatherDebugData(DebugData); } void FAnimNode_RigLogic::UpdateRawControls(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateRawControls); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateRawControls"); #endif // CPUPROFILERTRACE_ENABLED // Combine control attribute curve with input curve to get indexed curve to apply to rig // Curve elements that dont have a control mapping will have INDEX_NONE as their index UE::Anim::FNamedValueArrayUtils::Union(InputContext.Curve, LocalDNAIndexMapping->ControlAttributeCurves, [this](const UE::Anim::FCurveElement& InCurveElement, const UE::Anim::FCurveElementIndexed& InControlAttributeCurveElement, UE::Anim::ENamedValueUnionFlags InFlags) { if (InControlAttributeCurveElement.Index != INDEX_NONE) { RigInstance->SetRawControl(InControlAttributeCurveElement.Index, FMath::Clamp(InCurveElement.Value, 0.0, 1.0)); } }); } void FAnimNode_RigLogic::UpdateRawControlsCached(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateRawControlsCached); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateRawControlsCached"); #endif // CPUPROFILERTRACE_ENABLED const uint16 CurrentLOD = RigInstance->GetLOD(); auto& RawControlIndices = PoseCurvesToRigLogicControlsMap[CurrentLOD].RawControlIndices; if (RawControlIndices.Num() != InputContext.Curve.Num()) { CachePoseCurvesToRigLogicControlsMap(InputContext, LocalDNAIndexMapping->ControlAttributeCurves, RawControlIndices); } int32 CurveIndex = 0; InputContext.Curve.ForEachElement([&](const UE::Anim::FCurveElement& InCurveElement) { const int32 ControlIndex = RawControlIndices[CurveIndex]; if (ControlIndex != INDEX_NONE) { RigInstance->SetRawControl(ControlIndex, FMath::Clamp(InCurveElement.Value, 0.0, 1.0)); } ++CurveIndex; }); } void FAnimNode_RigLogic::UpdateSparseDriverJointDrivenControlCurves(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateSparseDriverJointDrivenControlCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateSparseDriverJointDrivenControlCurves"); #endif // CPUPROFILERTRACE_ENABLED const uint16 CurrentLOD = RigInstance->GetLOD(); const auto& SparseDriverJointsToControlAttributesMap = LocalJointMappingsPerLOD[CurrentLOD].SparseDriverJointsToControlAttributesMap; TArrayView InverseNeutralJointRotations = LocalRigRuntimeContext->InverseNeutralJointRotations; // The sparse mapping is NOT guaranteed to supply all quaternion attributes, so checks for each attribute mapping are present for (int32 MappingIndex = 0; MappingIndex < SparseDriverJointsToControlAttributesMap.Num(); ++MappingIndex) { const FCompactPoseBoneControlAttributeMapping& Mapping = SparseDriverJointsToControlAttributesMap[MappingIndex]; const FTransform& CompactPose = InputContext.Pose[Mapping.CompactPoseBoneIndex]; // Translation and Scale is currently not used here, so to avoid the overhead of checking them, they are simply ignored. // Should the need arise to use them as well, this code will need adjustment. const FQuat AbsPoseRotation = CompactPose.GetRotation(); const FQuat InvNeutralRotation = InverseNeutralJointRotations[Mapping.DNAJointIndex]; const FQuat DeltaPoseRotation = InvNeutralRotation * AbsPoseRotation; if (Mapping.RotationX != INDEX_NONE) { RigInstance->SetRawControl(Mapping.RotationX, DeltaPoseRotation.X); } if (Mapping.RotationY != INDEX_NONE) { RigInstance->SetRawControl(Mapping.RotationY, DeltaPoseRotation.Y); } if (Mapping.RotationZ != INDEX_NONE) { RigInstance->SetRawControl(Mapping.RotationZ, DeltaPoseRotation.Z); } if (Mapping.RotationW != INDEX_NONE) { RigInstance->SetRawControl(Mapping.RotationW, DeltaPoseRotation.W); } } } void FAnimNode_RigLogic::UpdateDenseDriverJointDrivenControlCurves(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateDenseDriverJointDrivenControlCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateDenseDriverJointDrivenControlCurves"); #endif // CPUPROFILERTRACE_ENABLED const uint16 CurrentLOD = RigInstance->GetLOD(); const auto& DenseDriverJointsToControlAttributesMap = LocalJointMappingsPerLOD[CurrentLOD].DenseDriverJointsToControlAttributesMap; TArrayView InverseNeutralJointRotations = LocalRigRuntimeContext->InverseNeutralJointRotations; // The dense mapping is guaranteed to supply all quaternion attributes, so NO checks for each attribute mapping are present for (int32 MappingIndex = 0; MappingIndex < DenseDriverJointsToControlAttributesMap.Num(); ++MappingIndex) { const FCompactPoseBoneControlAttributeMapping& Mapping = DenseDriverJointsToControlAttributesMap[MappingIndex]; const FTransform& CompactPose = InputContext.Pose[Mapping.CompactPoseBoneIndex]; // Translation and Scale is currently not used here, so to avoid the overhead of checking them, they are simply ignored. // Should the need arise to use them as well, this code will need adjustment. const FQuat AbsPoseRotation = CompactPose.GetRotation(); const FQuat InvNeutralRotation = InverseNeutralJointRotations[Mapping.DNAJointIndex]; const FQuat DeltaPoseRotation = InvNeutralRotation * AbsPoseRotation; RigInstance->SetRawControl(Mapping.RotationX, DeltaPoseRotation.X); RigInstance->SetRawControl(Mapping.RotationY, DeltaPoseRotation.Y); RigInstance->SetRawControl(Mapping.RotationZ, DeltaPoseRotation.Z); RigInstance->SetRawControl(Mapping.RotationW, DeltaPoseRotation.W); } } void FAnimNode_RigLogic::UpdateNeuralNetworkMaskCurves(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateNeuralNetworkMaskCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateNeuralNetworkMaskCurves"); #endif // CPUPROFILERTRACE_ENABLED if (RigInstance->GetNeuralNetworkCount() != 0) { UE::Anim::FNamedValueArrayUtils::Union(InputContext.Curve, LocalDNAIndexMapping->NeuralNetworkMaskCurves, [this](const UE::Anim::FCurveElement& InCurveElement, const UE::Anim::FCurveElementIndexed& InControlAttributeCurveElement, UE::Anim::ENamedValueUnionFlags InFlags) { if (InControlAttributeCurveElement.Index != INDEX_NONE) { RigInstance->SetNeuralNetworkMask(InControlAttributeCurveElement.Index, InCurveElement.Value); } }); } } void FAnimNode_RigLogic::UpdateNeuralNetworkMaskCurvesCached(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateNeuralNetworkMaskCurvesCached); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateNeuralNetworkMaskCurvesCached"); #endif // CPUPROFILERTRACE_ENABLED if (RigInstance->GetNeuralNetworkCount() != 0) { const uint16 CurrentLOD = RigInstance->GetLOD(); auto& NeuralNetworkMaskIndices = PoseCurvesToRigLogicControlsMap[CurrentLOD].NeuralNetworkMaskIndices; if (NeuralNetworkMaskIndices.Num() != InputContext.Curve.Num()) { CachePoseCurvesToRigLogicControlsMap(InputContext, LocalDNAIndexMapping->NeuralNetworkMaskCurves, NeuralNetworkMaskIndices); } int32 CurveIndex = 0; InputContext.Curve.ForEachElement([&](const UE::Anim::FCurveElement& InCurveElement) { const int32 ControlIndex = NeuralNetworkMaskIndices[CurveIndex]; if (ControlIndex != INDEX_NONE) { RigInstance->SetNeuralNetworkMask(ControlIndex, InCurveElement.Value); } ++CurveIndex; }); } } void FAnimNode_RigLogic::UpdateControlCurves(const FPoseContext& InputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateControlCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateControlCurves"); #endif // CPUPROFILERTRACE_ENABLED const FRigLogicConfiguration& RigLogicConfig = LocalRigRuntimeContext->RigLogic->GetConfiguration(); if (CacheAnimCurveNames) { UpdateRawControlsCached(InputContext); if (RigLogicConfig.LoadMachineLearnedBehavior) { UpdateNeuralNetworkMaskCurvesCached(InputContext); } } else { UpdateRawControls(InputContext); if (RigLogicConfig.LoadMachineLearnedBehavior) { UpdateNeuralNetworkMaskCurves(InputContext); } } if (RigLogicConfig.LoadRBFBehavior || RigLogicConfig.LoadTwistSwingBehavior) { UpdateSparseDriverJointDrivenControlCurves(InputContext); UpdateDenseDriverJointDrivenControlCurves(InputContext); } } void FAnimNode_RigLogic::CalculateRigLogic() { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_CalculateRigLogic); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::CalculateRigLogic"); #endif // CPUPROFILERTRACE_ENABLED FRigLogic* RigLogic = LocalRigRuntimeContext->RigLogic.Get(); // RigLogic has Null evaluators for each class of computations, so no explicit checks are necessary here // based on the chosen configuration, as no extra work will be performed if not needed. RigLogic->CalculateMachineLearnedBehaviorControls(RigInstance); RigLogic->CalculateRBFControls(RigInstance); RigLogic->CalculateControls(RigInstance); RigLogic->CalculateJoints(RigInstance); RigLogic->CalculateBlendShapes(RigInstance); RigLogic->CalculateAnimatedMaps(RigInstance); } void FAnimNode_RigLogic::UpdateJoints(FPoseContext& OutputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateJoints); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateJoints"); #endif // CPUPROFILERTRACE_ENABLED const uint16 LOD = RigInstance->GetLOD(); TArrayView DeltaJointValues = RigInstance->GetJointOutputs(); TArrayView NeutralJointValues = LocalRigRuntimeContext->RigLogic->GetNeutralJointValues(); const float* N = NeutralJointValues.GetData(); const float* D = DeltaJointValues.GetData(); const auto& JointsMapDNAIndicesToCompactPoseBoneIndices = LocalJointMappingsPerLOD[LOD].JointsMapDNAIndicesToCompactPoseBoneIndices; for (const FJointCompactPoseBoneMapping& Mapping : JointsMapDNAIndicesToCompactPoseBoneIndices) { const uint16 AttrIndex = Mapping.JointIndex * ATTR_COUNT_PER_JOINT; FTransform& CompactPose = OutputContext.Pose[Mapping.CompactPoseBoneIndex]; CompactPose.SetTranslation(FVector((N[AttrIndex + 0] + D[AttrIndex + 0]), (N[AttrIndex + 1] + D[AttrIndex + 1]), (N[AttrIndex + 2] + D[AttrIndex + 2]))); CompactPose.SetRotation(FQuat(N[AttrIndex + 3], N[AttrIndex + 4], N[AttrIndex + 5], N[AttrIndex + 6]) * FQuat(D[AttrIndex + 3], D[AttrIndex + 4], D[AttrIndex + 5], D[AttrIndex + 6])); CompactPose.SetScale3D(FVector((N[AttrIndex + 7] + D[AttrIndex + 7]), (N[AttrIndex + 8] + D[AttrIndex + 8]), (N[AttrIndex + 9] + D[AttrIndex + 9]))); } } void FAnimNode_RigLogic::UpdateBlendShapeCurves(FPoseContext& OutputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateBlendShapeCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateBlendShapeCurves"); #endif // CPUPROFILERTRACE_ENABLED const uint16 LOD = RigInstance->GetLOD(); TArrayView BlendShapeValues = RigInstance->GetBlendShapeOutputs(); const FDNAIndexMapping::FCachedIndexedCurve& MorphTargetCurve = LocalDNAIndexMapping->MorphTargetCurvesPerLOD[LOD]; UE::Anim::FNamedValueArrayUtils::Union(OutputContext.Curve, MorphTargetCurve, [&BlendShapeValues](UE::Anim::FCurveElement& InOutResult, const UE::Anim::FCurveElementIndexed& InSource, UE::Anim::ENamedValueUnionFlags InFlags) { if (BlendShapeValues.IsValidIndex(InSource.Index)) { InOutResult.Value = BlendShapeValues[InSource.Index]; InOutResult.Flags |= UE::Anim::ECurveElementFlags::MorphTarget; } }); } void FAnimNode_RigLogic::UpdateAnimMapCurves(FPoseContext& OutputContext) { #if STATS DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() QUICK_SCOPE_CYCLE_COUNTER(STAT_AnimNode_RigLogic_UpdateAnimMapCurves); #endif // STATS #if CPUPROFILERTRACE_ENABLED TRACE_CPUPROFILER_EVENT_SCOPE_STR("FAnimNode_RigLogic::UpdateAnimMapCurves"); #endif // CPUPROFILERTRACE_ENABLED const uint16 LOD = RigInstance->GetLOD(); TArrayView AnimMapOutputs = RigInstance->GetAnimatedMapOutputs(); const FDNAIndexMapping::FCachedIndexedCurve& MaskMultiplierCurve = LocalDNAIndexMapping->MaskMultiplierCurvesPerLOD[LOD]; UE::Anim::FNamedValueArrayUtils::Union(OutputContext.Curve, MaskMultiplierCurve, [&AnimMapOutputs](UE::Anim::FCurveElement& InOutResult, const UE::Anim::FCurveElementIndexed& InSource, UE::Anim::ENamedValueUnionFlags InFlags) { if (AnimMapOutputs.IsValidIndex(InSource.Index)) { InOutResult.Value = AnimMapOutputs[InSource.Index]; InOutResult.Flags |= UE::Anim::ECurveElementFlags::Material; } }); }