2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2017-02-08 17:53:41 -05:00
2017-03-24 09:53:37 -04:00
# include "BoneControllers/AnimNode_RigidBody.h"
2017-02-08 17:53:41 -05:00
# include "AnimationRuntime.h"
# include "Animation/AnimInstanceProxy.h"
# include "PhysicsEngine/BodySetup.h"
# include "PhysicsEngine/PhysicsAsset.h"
# include "PhysicsEngine/PhysicsConstraintTemplate.h"
2017-11-09 18:22:55 -05:00
# include "GameFramework/PawnMovementComponent.h"
2018-07-31 02:23:26 -04:00
# include "Physics/PhysicsInterfaceCore.h"
# include "Physics/ImmediatePhysics/ImmediatePhysicsActorHandle.h"
2019-08-02 09:01:58 -04:00
# include "Physics/ImmediatePhysics/ImmediatePhysicsSimulation.h"
# include "Physics/ImmediatePhysics/ImmediatePhysicsStats.h"
2019-04-19 20:21:02 -04:00
# include "PhysicsEngine/PhysicsSettings.h"
2017-02-08 17:53:41 -05:00
# include "Logging/MessageLog.h"
2020-04-22 20:21:14 -04:00
# include "Logging/LogMacros.h"
2017-02-08 17:53:41 -05:00
2020-02-20 12:16:00 -05:00
//PRAGMA_DISABLE_OPTIMIZATION
2017-02-08 17:53:41 -05:00
/////////////////////////////////////////////////////
2019-08-02 09:01:58 -04:00
// FAnimNode_RigidBody
2017-02-08 17:53:41 -05:00
# define LOCTEXT_NAMESPACE "ImmediatePhysics"
2019-10-04 18:34:07 -04:00
DEFINE_STAT ( STAT_RigidBodyNodeInitTime ) ;
2019-11-07 15:11:17 -05:00
CSV_DECLARE_CATEGORY_MODULE_EXTERN ( ENGINE_API , Animation ) ;
2020-04-22 20:21:14 -04:00
# if UE_BUILD_SHIPPING || UE_BUILD_TEST
DECLARE_LOG_CATEGORY_EXTERN ( LogRBAN , Log , Warning ) ;
# else
DECLARE_LOG_CATEGORY_EXTERN ( LogRBAN , Log , All ) ;
# endif
DEFINE_LOG_CATEGORY ( LogRBAN ) ;
2020-08-11 01:36:57 -04:00
TAutoConsoleVariable < int32 > CVarEnableRigidBodyNode ( TEXT ( " p.RigidBodyNode " ) , 1 , TEXT ( " Enables/disables the whole rigid body node system. When disabled, avoids all allocations and runtime costs. Can be used to disable RB Nodes on low-end platforms. " ) , ECVF_Default ) ;
TAutoConsoleVariable < int32 > CVarEnableRigidBodyNodeSimulation ( TEXT ( " p.RigidBodyNode.EnableSimulation " ) , 1 , TEXT ( " Runtime Enable/Disable RB Node Simulation for debugging and testing (node is initialized and bodies and constraints are created, even when disabled.) " ) , ECVF_Default ) ;
2018-05-23 21:04:31 -04:00
TAutoConsoleVariable < int32 > CVarRigidBodyLODThreshold ( TEXT ( " p.RigidBodyLODThreshold " ) , - 1 , TEXT ( " Max LOD that rigid body node is allowed to run on. Provides a global threshold that overrides per-node the LODThreshold property. -1 means no override. " ) , ECVF_Scalability ) ;
2018-02-22 11:25:06 -05:00
2019-10-11 13:50:12 -04:00
int32 RBAN_MaxSubSteps = 4 ;
2019-11-15 13:44:29 -05:00
bool bRBAN_EnableTimeBasedReset = true ;
2020-06-23 18:40:00 -04:00
bool bRBAN_EnableComponentAcceleration = true ;
int32 RBAN_WorldObjectExpiry = 4 ;
2019-10-22 11:12:17 -04:00
FAutoConsoleVariableRef CVarRigidBodyNodeMaxSteps ( TEXT ( " p.RigidBodyNode.MaxSubSteps " ) , RBAN_MaxSubSteps , TEXT ( " Set the maximum number of simulation steps in the update loop " ) , ECVF_Default ) ;
2019-11-15 13:44:29 -05:00
FAutoConsoleVariableRef CVarRigidBodyNodeEnableTimeBasedReset ( TEXT ( " p.RigidBodyNode.EnableTimeBasedReset " ) , bRBAN_EnableTimeBasedReset , TEXT ( " If true, Rigid Body nodes are reset when they have not been updated for a while (default true) " ) , ECVF_Default ) ;
2020-06-23 18:40:00 -04:00
FAutoConsoleVariableRef CVarRigidBodyNodeEnableComponentAcceleration ( TEXT ( " p.RigidBodyNode.EnableComponentAcceleration " ) , bRBAN_EnableComponentAcceleration , TEXT ( " Enable/Disable the simple acceleration transfer system for component- or bone-space simulation " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeWorldObjectExpiry ( TEXT ( " p.RigidBodyNode.WorldObjectExpiry " ) , RBAN_WorldObjectExpiry , TEXT ( " World objects are removed from the simulation if not detected after this many tests " ) , ECVF_Default ) ;
// FSimSpaceSettings forced overrides for testing
bool bRBAN_SimSpace_EnableOverride = false ;
FSimSpaceSettings RBAN_SimSpaceOverride ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceOverride ( TEXT ( " p.RigidBodyNode.Space.Override " ) , bRBAN_SimSpace_EnableOverride , TEXT ( " Force-enable the advanced simulation space movement forces " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceMasterAlpha ( TEXT ( " p.RigidBodyNode.Space.MasterAlpha " ) , RBAN_SimSpaceOverride . MasterAlpha , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceVelScaleZ ( TEXT ( " p.RigidBodyNode.Space.VelocityScaleZ " ) , RBAN_SimSpaceOverride . VelocityScaleZ , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceMaxCompLinVel ( TEXT ( " p.RigidBodyNode.Space.MaxLinearVelocity " ) , RBAN_SimSpaceOverride . MaxLinearVelocity , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceMaxCompAngVel ( TEXT ( " p.RigidBodyNode.Space.MaxAngularVelocity " ) , RBAN_SimSpaceOverride . MaxAngularVelocity , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceMaxCompLinAcc ( TEXT ( " p.RigidBodyNode.Space.MaxLinearAcceleration " ) , RBAN_SimSpaceOverride . MaxLinearAcceleration , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceMaxCompAngAcc ( TEXT ( " p.RigidBodyNode.Space.MaxAngularAcceleration " ) , RBAN_SimSpaceOverride . MaxAngularAcceleration , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
2020-09-01 14:07:48 -04:00
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearDragX ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearDrag.X " ) , RBAN_SimSpaceOverride . ExternalLinearDragV . X , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearDragY ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearDrag.Y " ) , RBAN_SimSpaceOverride . ExternalLinearDragV . Y , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearDragZ ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearDrag.Z " ) , RBAN_SimSpaceOverride . ExternalLinearDragV . Z , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
2020-06-23 18:40:00 -04:00
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearVelocityX ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearVelocity.X " ) , RBAN_SimSpaceOverride . ExternalLinearVelocity . X , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearVelocityY ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearVelocity.Y " ) , RBAN_SimSpaceOverride . ExternalLinearVelocity . Y , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
FAutoConsoleVariableRef CVarRigidBodyNodeSpaceExternalLinearVelocityZ ( TEXT ( " p.RigidBodyNode.Space.ExternalLinearVelocity.Z " ) , RBAN_SimSpaceOverride . ExternalLinearVelocity . Z , TEXT ( " RBAN SimSpaceSettings overrides " ) , ECVF_Default ) ;
2020-11-24 18:42:39 -04:00
# if ENABLE_RBAN_PERF_LOGGING
static float RBAN_PerfWarningThreshold = 0.f ;
static FAutoConsoleVariableRef CVarRigidBodyNodePerfWarningThreshold (
TEXT ( " p.RigidBodyNode.PerfWarningThreshold " ) ,
RBAN_PerfWarningThreshold ,
TEXT ( " 0: disabled \n " )
TEXT ( " >0: Threshold (in ms) before printing RBAN performance warnings to log. " ) ,
ECVF_Default ) ;
static float RBAN_PerfWarningInterval = 5.f ;
static FAutoConsoleVariableRef CVarRigidBodyNodePerfWarningInterval (
TEXT ( " p.RigidBodyNode.PerfWarningInterval " ) ,
RBAN_PerfWarningInterval ,
TEXT ( " Time (in seconds) between warnings to prevent log spam. " ) ,
ECVF_Default ) ;
# endif
2020-06-23 18:40:00 -04:00
FSimSpaceSettings : : FSimSpaceSettings ( )
: MasterAlpha ( 0 )
, VelocityScaleZ ( 1 )
, MaxLinearVelocity ( 10000 )
, MaxAngularVelocity ( 10000 )
, MaxLinearAcceleration ( 10000 )
, MaxAngularAcceleration ( 10000 )
2020-09-01 14:07:48 -04:00
, ExternalLinearDrag_DEPRECATED ( 0 )
, ExternalLinearDragV ( FVector : : ZeroVector )
2020-06-23 18:40:00 -04:00
, ExternalLinearVelocity ( FVector : : ZeroVector )
, ExternalAngularVelocity ( FVector : : ZeroVector )
{
}
2020-09-01 14:07:48 -04:00
void FSimSpaceSettings : : PostSerialize ( const FArchive & Ar )
{
if ( Ar . IsLoading ( ) )
{
if ( ExternalLinearDrag_DEPRECATED ! = 0.0f )
{
ExternalLinearDragV = FVector ( ExternalLinearDrag_DEPRECATED , ExternalLinearDrag_DEPRECATED , ExternalLinearDrag_DEPRECATED ) ;
}
}
}
2019-10-11 13:50:12 -04:00
2017-05-12 11:21:11 -04:00
FAnimNode_RigidBody : : FAnimNode_RigidBody ( ) :
QueryParams ( NAME_None , FCollisionQueryParams : : GetUnknownStatId ( ) )
2017-02-08 17:53:41 -05:00
{
2020-09-01 14:07:48 -04:00
WorldTimeSeconds = 0.0f ;
LastEvalTimeSeconds = 0.0f ;
2019-12-12 20:53:50 -05:00
AccumulatedDeltaTime = 0.0f ;
2018-05-23 21:04:31 -04:00
ResetSimulatedTeleportType = ETeleportType : : None ;
2017-02-08 17:53:41 -05:00
PhysicsSimulation = nullptr ;
OverridePhysicsAsset = nullptr ;
2019-06-17 13:58:21 -04:00
UsePhysicsAsset = nullptr ;
2017-02-08 17:53:41 -05:00
bOverrideWorldGravity = false ;
2017-03-24 09:53:37 -04:00
CachedBoundsScale = 1.2f ;
2017-07-26 09:23:14 -04:00
SimulationSpace = ESimulationSpace : : ComponentSpace ;
ExternalForce = FVector : : ZeroVector ;
2018-11-14 19:05:13 -05:00
# if WITH_EDITORONLY_DATA
2017-07-26 09:23:14 -04:00
bComponentSpaceSimulation_DEPRECATED = true ;
2018-11-14 19:05:13 -05:00
# endif
2017-02-08 17:53:41 -05:00
OverrideWorldGravity = FVector : : ZeroVector ;
TotalMass = 0.f ;
2020-06-23 18:40:00 -04:00
CachedBounds . Center = FVector : : ZeroVector ;
2017-03-24 09:53:37 -04:00
CachedBounds . W = 0 ;
2019-12-12 20:53:50 -05:00
PhysScene = nullptr ;
2017-03-24 09:53:37 -04:00
UnsafeWorld = nullptr ;
2020-06-23 18:40:00 -04:00
UnsafeOwner = nullptr ;
2017-11-09 18:22:55 -05:00
bSimulationStarted = false ;
bCheckForBodyTransformInit = false ;
2018-05-23 21:04:31 -04:00
OverlapChannel = ECC_WorldStatic ;
bEnableWorldGeometry = false ;
bTransferBoneVelocities = false ;
bFreezeIncomingPoseOnStart = false ;
2019-01-08 11:38:48 -05:00
bClampLinearTranslationLimitToRefPose = false ;
2020-04-22 20:21:14 -04:00
WorldSpaceMinimumScale = 0.01f ;
2018-05-23 21:04:31 -04:00
PreviousTransform = CurrentTransform = FTransform : : Identity ;
PreviousComponentLinearVelocity = FVector : : ZeroVector ;
ComponentLinearAccScale = FVector : : ZeroVector ;
ComponentLinearVelScale = FVector : : ZeroVector ;
2018-09-25 10:11:35 -04:00
ComponentAppliedLinearAccClamp = FVector ( 10000 , 10000 , 10000 ) ;
2019-01-08 19:14:21 -05:00
bForceDisableCollisionBetweenConstraintBodies = false ;
2020-09-01 14:07:48 -04:00
EvaluationResetTime = 0.01f ;
2017-02-08 17:53:41 -05:00
}
2017-03-24 09:53:37 -04:00
FAnimNode_RigidBody : : ~ FAnimNode_RigidBody ( )
2017-02-08 17:53:41 -05:00
{
delete PhysicsSimulation ;
}
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : GatherDebugData ( FNodeDebugData & DebugData )
2017-02-08 17:53:41 -05:00
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( GatherDebugData )
2017-02-08 17:53:41 -05:00
FString DebugLine = DebugData . GetNodeName ( this ) ;
DebugLine + = " ( " ;
AddDebugNodeData ( DebugLine ) ;
DebugLine + = " ) " ;
DebugData . AddDebugItem ( DebugLine ) ;
2017-11-09 18:22:55 -05:00
const bool bUsingFrozenPose = bFreezeIncomingPoseOnStart & & bSimulationStarted & & ( CapturedFrozenPose . GetPose ( ) . GetNumBones ( ) > 0 ) ;
if ( ! bUsingFrozenPose )
{
ComponentPose . GatherDebugData ( DebugData ) ;
}
2017-02-08 17:53:41 -05:00
}
2019-10-02 17:27:26 -04:00
FTransform SpaceToWorldTransform ( ESimulationSpace Space , const FTransform & ComponentToWorld , const FTransform & BaseBoneTM )
{
switch ( Space )
{
case ESimulationSpace : : ComponentSpace :
return ComponentToWorld ;
case ESimulationSpace : : WorldSpace :
return FTransform : : Identity ;
case ESimulationSpace : : BaseBoneSpace :
return BaseBoneTM * ComponentToWorld ;
default :
return FTransform : : Identity ;
}
}
2018-04-12 16:57:51 -04:00
FVector WorldVectorToSpaceNoScale ( ESimulationSpace Space , const FVector & WorldDir , const FTransform & ComponentToWorld , const FTransform & BaseBoneTM )
2017-07-26 09:23:14 -04:00
{
switch ( Space )
{
case ESimulationSpace : : ComponentSpace : return ComponentToWorld . InverseTransformVectorNoScale ( WorldDir ) ;
case ESimulationSpace : : WorldSpace : return WorldDir ;
2018-04-12 16:57:51 -04:00
case ESimulationSpace : : BaseBoneSpace :
return BaseBoneTM . InverseTransformVectorNoScale ( ComponentToWorld . InverseTransformVectorNoScale ( WorldDir ) ) ;
2017-07-26 09:23:14 -04:00
default : return FVector : : ZeroVector ;
}
}
2018-04-12 16:57:51 -04:00
FVector WorldPositionToSpace ( ESimulationSpace Space , const FVector & WorldPoint , const FTransform & ComponentToWorld , const FTransform & BaseBoneTM )
2017-07-26 09:23:14 -04:00
{
switch ( Space )
{
case ESimulationSpace : : ComponentSpace : return ComponentToWorld . InverseTransformPosition ( WorldPoint ) ;
case ESimulationSpace : : WorldSpace : return WorldPoint ;
2018-04-12 16:57:51 -04:00
case ESimulationSpace : : BaseBoneSpace :
return BaseBoneTM . InverseTransformPosition ( ComponentToWorld . InverseTransformPosition ( WorldPoint ) ) ;
2017-07-26 09:23:14 -04:00
default : return FVector : : ZeroVector ;
}
}
2018-04-12 16:57:51 -04:00
FORCEINLINE_DEBUGGABLE FTransform ConvertCSTransformToSimSpace ( ESimulationSpace SimulationSpace , const FTransform & InCSTransform , const FTransform & ComponentToWorld , const FTransform & BaseBoneTM )
2017-11-09 18:22:55 -05:00
{
switch ( SimulationSpace )
{
case ESimulationSpace : : ComponentSpace : return InCSTransform ;
case ESimulationSpace : : WorldSpace : return InCSTransform * ComponentToWorld ;
2018-04-12 16:57:51 -04:00
case ESimulationSpace : : BaseBoneSpace : return InCSTransform . GetRelativeTransform ( BaseBoneTM ) ; break ;
2017-11-09 18:22:55 -05:00
default : ensureMsgf ( false , TEXT ( " Unsupported Simulation Space " ) ) ; return InCSTransform ;
}
}
2017-07-26 09:23:14 -04:00
2017-11-09 18:22:55 -05:00
void FAnimNode_RigidBody : : UpdateComponentPose_AnyThread ( const FAnimationUpdateContext & Context )
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( UpdateComponentPose_AnyThread )
2017-11-09 18:22:55 -05:00
// Only freeze update graph after initial update, as we want to get that pose through.
2018-05-23 21:04:31 -04:00
if ( bFreezeIncomingPoseOnStart & & bSimulationStarted & & ResetSimulatedTeleportType = = ETeleportType : : None )
2017-11-09 18:22:55 -05:00
{
// If we have a Frozen Pose captured,
// then we don't need to update the rest of the graph.
if ( CapturedFrozenPose . GetPose ( ) . GetNumBones ( ) > 0 )
{
}
else
{
// Create a new context with zero deltatime to freeze time in rest of the graph.
// This will be used to capture a frozen pose.
2019-09-17 19:12:19 -04:00
FAnimationUpdateContext FrozenContext = Context . FractionalWeightAndTime ( 1.f , 0.f ) ;
2017-11-09 18:22:55 -05:00
Super : : UpdateComponentPose_AnyThread ( FrozenContext ) ;
}
}
else
{
Super : : UpdateComponentPose_AnyThread ( Context ) ;
}
}
void FAnimNode_RigidBody : : EvaluateComponentPose_AnyThread ( FComponentSpacePoseContext & Output )
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( EvaluateComponentPose_AnyThread )
2017-11-09 18:22:55 -05:00
if ( bFreezeIncomingPoseOnStart & & bSimulationStarted )
{
// If we have a Frozen Pose captured, use it.
// Only after our intialize setup. As we need new pose for that.
2018-05-23 21:04:31 -04:00
if ( ResetSimulatedTeleportType = = ETeleportType : : None & & ( CapturedFrozenPose . GetPose ( ) . GetNumBones ( ) > 0 ) )
2017-11-09 18:22:55 -05:00
{
Output . Pose . CopyPose ( CapturedFrozenPose ) ;
Output . Curve . CopyFrom ( CapturedFrozenCurves ) ;
}
// Otherwise eval graph to capture it.
else
{
Super : : EvaluateComponentPose_AnyThread ( Output ) ;
CapturedFrozenPose . CopyPose ( Output . Pose ) ;
CapturedFrozenCurves . CopyFrom ( Output . Curve ) ;
}
}
else
{
Super : : EvaluateComponentPose_AnyThread ( Output ) ;
}
// Capture incoming pose if 'bTransferBoneVelocities' is set.
// That is, until simulation starts.
if ( bTransferBoneVelocities & & ! bSimulationStarted )
{
CapturedBoneVelocityPose . CopyPose ( Output . Pose ) ;
CapturedBoneVelocityPose . CopyAndAssignBoneContainer ( CapturedBoneVelocityBoneContainer ) ;
}
}
2018-04-12 16:57:51 -04:00
void FAnimNode_RigidBody : : InitializeNewBodyTransformsDuringSimulation ( FComponentSpacePoseContext & Output , const FTransform & ComponentTransform , const FTransform & BaseBoneTM )
2017-11-09 18:22:55 -05:00
{
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
const int32 BodyIndex = OutputData . BodyIndex ;
FBodyAnimData & BodyData = BodyAnimData [ BodyIndex ] ;
if ( ! BodyData . bBodyTransformInitialized )
{
BodyData . bBodyTransformInitialized = true ;
// If we have a parent body, we need to grab relative transforms to it.
if ( OutputData . ParentBodyIndex ! = INDEX_NONE )
{
ensure ( BodyAnimData [ OutputData . ParentBodyIndex ] . bBodyTransformInitialized ) ;
FTransform BodyRelativeTransform = FTransform : : Identity ;
2020-08-11 01:36:57 -04:00
for ( const FCompactPoseBoneIndex & CompactBoneIndex : OutputData . BoneIndicesToParentBody )
2017-11-09 18:22:55 -05:00
{
const FTransform & LocalSpaceTM = Output . Pose . GetLocalSpaceTransform ( CompactBoneIndex ) ;
BodyRelativeTransform = BodyRelativeTransform * LocalSpaceTM ;
}
const FTransform WSBodyTM = BodyRelativeTransform * Bodies [ OutputData . ParentBodyIndex ] - > GetWorldTransform ( ) ;
2020-05-04 16:37:52 -04:00
Bodies [ BodyIndex ] - > InitWorldTransform ( WSBodyTM ) ;
2019-04-05 19:59:27 -04:00
BodyAnimData [ BodyIndex ] . RefPoseLength = BodyRelativeTransform . GetLocation ( ) . Size ( ) ;
2017-11-09 18:22:55 -05:00
}
// If we don't have a parent body, then we can just grab the incoming pose in component space.
else
{
const FTransform & ComponentSpaceTM = Output . Pose . GetComponentSpaceTransform ( OutputData . CompactPoseBoneIndex ) ;
2018-04-12 16:57:51 -04:00
const FTransform BodyTM = ConvertCSTransformToSimSpace ( SimulationSpace , ComponentSpaceTM , ComponentTransform , BaseBoneTM ) ;
2017-11-09 18:22:55 -05:00
2020-05-04 16:37:52 -04:00
Bodies [ BodyIndex ] - > InitWorldTransform ( BodyTM ) ;
2017-11-09 18:22:55 -05:00
}
}
}
}
2020-06-23 18:40:00 -04:00
void FAnimNode_RigidBody : : InitSimulationSpace (
const FTransform & ComponentToWorld ,
const FTransform & BoneToComponent )
{
2020-10-22 19:19:16 -04:00
SimSpacePreviousComponentToWorld = ComponentToWorld ;
SimSpacePreviousBoneToComponent = BoneToComponent ;
SimSpacePreviousComponentLinearVelocity = FVector : : ZeroVector ;
SimSpacePreviousComponentAngularVelocity = FVector : : ZeroVector ;
SimSpacePreviousBoneLinearVelocity = FVector : : ZeroVector ;
SimSpacePreviousBoneAngularVelocity = FVector : : ZeroVector ;
2020-06-23 18:40:00 -04:00
}
void FAnimNode_RigidBody : : CalculateSimulationSpace (
ESimulationSpace Space ,
const FTransform & ComponentToWorld ,
const FTransform & BoneToComponent ,
const float Dt ,
const FSimSpaceSettings & Settings ,
FTransform & SpaceTransform ,
FVector & SpaceLinearVel ,
FVector & SpaceAngularVel ,
FVector & SpaceLinearAcc ,
FVector & SpaceAngularAcc )
{
// World-space transform of the simulation space
SpaceTransform = SpaceToWorldTransform ( Space , ComponentToWorld , BoneToComponent ) ;
SpaceLinearVel = FVector : : ZeroVector ;
SpaceAngularVel = FVector : : ZeroVector ;
SpaceLinearAcc = FVector : : ZeroVector ;
SpaceAngularAcc = FVector : : ZeroVector ;
// If the system is disabled, nothing else to do
if ( ( Settings . MasterAlpha = = 0.0f ) | | ( Dt < SMALL_NUMBER ) )
{
return ;
}
if ( Space = = ESimulationSpace : : WorldSpace )
{
SpaceLinearVel = Settings . ExternalLinearVelocity ;
SpaceAngularVel = Settings . ExternalAngularVelocity ;
return ;
}
// World-space component velocity and acceleration
2020-10-22 19:19:16 -04:00
FVector CompLinVel = Chaos : : FVec3 : : CalculateVelocity ( SimSpacePreviousComponentToWorld . GetTranslation ( ) , ComponentToWorld . GetTranslation ( ) , Dt ) ;
FVector CompAngVel = Chaos : : FRotation3 : : CalculateAngularVelocity ( SimSpacePreviousComponentToWorld . GetRotation ( ) , ComponentToWorld . GetRotation ( ) , Dt ) ;
FVector CompLinAcc = ( CompLinVel - SimSpacePreviousComponentLinearVelocity ) / Dt ;
FVector CompAngAcc = ( CompAngVel - SimSpacePreviousComponentAngularVelocity ) / Dt ;
SimSpacePreviousComponentToWorld = ComponentToWorld ;
SimSpacePreviousComponentLinearVelocity = CompLinVel ;
SimSpacePreviousComponentAngularVelocity = CompAngVel ;
2020-06-23 18:40:00 -04:00
if ( Space = = ESimulationSpace : : ComponentSpace )
{
CompLinVel . Z * = Settings . VelocityScaleZ ;
CompLinAcc . Z * = Settings . VelocityScaleZ ;
SpaceLinearVel = CompLinVel . GetClampedToMaxSize ( Settings . MaxLinearVelocity ) + Settings . ExternalLinearVelocity ;
SpaceAngularVel = CompAngVel . GetClampedToMaxSize ( Settings . MaxAngularVelocity ) + Settings . ExternalAngularVelocity ;
2020-09-01 14:07:48 -04:00
SpaceLinearAcc = CompLinAcc . GetClampedToMaxSize ( Settings . MaxLinearAcceleration ) ;
2020-06-23 18:40:00 -04:00
SpaceAngularAcc = CompAngAcc . GetClampedToMaxSize ( Settings . MaxAngularAcceleration ) ;
return ;
}
if ( Space = = ESimulationSpace : : BaseBoneSpace )
{
// World-space component-relative bone velocity and acceleration
2020-10-22 19:19:16 -04:00
FVector BoneLinVel = Chaos : : FVec3 : : CalculateVelocity ( SimSpacePreviousBoneToComponent . GetTranslation ( ) , BoneToComponent . GetTranslation ( ) , Dt ) ;
FVector BoneAngVel = Chaos : : FRotation3 : : CalculateAngularVelocity ( SimSpacePreviousBoneToComponent . GetRotation ( ) , BoneToComponent . GetRotation ( ) , Dt ) ;
2020-06-23 18:40:00 -04:00
BoneLinVel = ComponentToWorld . TransformVector ( BoneLinVel ) ;
BoneAngVel = ComponentToWorld . TransformVector ( BoneAngVel ) ;
2020-10-22 19:19:16 -04:00
FVector BoneLinAcc = ( BoneLinVel - SimSpacePreviousBoneLinearVelocity ) / Dt ;
FVector BoneAngAcc = ( BoneAngVel - SimSpacePreviousBoneAngularVelocity ) / Dt ;
SimSpacePreviousBoneToComponent = BoneToComponent ;
SimSpacePreviousBoneLinearVelocity = BoneLinVel ;
SimSpacePreviousBoneAngularVelocity = BoneAngVel ;
2020-06-23 18:40:00 -04:00
// World-space bone velocity and acceleration
FVector NetAngVel = CompAngVel + BoneAngVel ;
FVector NetAngAcc = CompAngAcc + BoneAngAcc ;
// If we limit the angular velocity, we also need to limit the component of linear velocity that comes from (angvel x offset)
float AngVelScale = 1.0f ;
float NetAngVelLenSq = NetAngVel . SizeSquared ( ) ;
if ( NetAngVelLenSq > FMath : : Square ( Settings . MaxAngularVelocity ) )
{
AngVelScale = Settings . MaxAngularVelocity * FMath : : InvSqrt ( NetAngVelLenSq ) ;
}
// Add the linear velocity and acceleration that comes from rotation of the space about the component
// NOTE: Component angular velocity constribution is scaled
FVector SpaceCompOffset = ComponentToWorld . TransformVector ( BoneToComponent . GetTranslation ( ) ) ;
FVector NetLinVel = CompLinVel + BoneLinVel + FVector : : CrossProduct ( AngVelScale * CompAngVel , SpaceCompOffset ) ;
FVector NetLinAcc = CompLinAcc + BoneLinAcc + FVector : : CrossProduct ( AngVelScale * CompAngAcc , SpaceCompOffset ) ;
NetLinVel . Z * = Settings . VelocityScaleZ ;
NetLinAcc . Z * = Settings . VelocityScaleZ ;
SpaceLinearVel = NetLinVel . GetClampedToMaxSize ( Settings . MaxLinearVelocity ) + Settings . ExternalLinearVelocity ;
SpaceAngularVel = NetAngVel . GetClampedToMaxSize ( Settings . MaxAngularVelocity ) + Settings . ExternalAngularVelocity ;
2020-09-01 14:07:48 -04:00
SpaceLinearAcc = NetLinAcc . GetClampedToMaxSize ( Settings . MaxLinearAcceleration ) ;
2020-06-23 18:40:00 -04:00
SpaceAngularAcc = NetAngAcc . GetClampedToMaxSize ( Settings . MaxAngularAcceleration ) ;
return ;
}
}
2017-11-09 18:22:55 -05:00
DECLARE_CYCLE_STAT ( TEXT ( " RigidBody_Eval " ) , STAT_RigidBody_Eval , STATGROUP_Anim ) ;
2019-08-02 09:01:58 -04:00
DECLARE_CYCLE_STAT ( TEXT ( " FAnimNode_RigidBody::EvaluateSkeletalControl_AnyThread " ) , STAT_ImmediateEvaluateSkeletalControl , STATGROUP_ImmediatePhysics ) ;
2017-02-08 17:53:41 -05:00
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : EvaluateSkeletalControl_AnyThread ( FComponentSpacePoseContext & Output , TArray < FBoneTransform > & OutBoneTransforms )
2017-02-08 17:53:41 -05:00
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( EvaluateSkeletalControl_AnyThread )
2017-11-09 18:22:55 -05:00
SCOPE_CYCLE_COUNTER ( STAT_RigidBody_Eval ) ;
2019-11-07 15:11:17 -05:00
CSV_SCOPED_TIMING_STAT ( Animation , RigidBodyEval ) ;
2019-06-17 13:58:21 -04:00
FScopeCycleCounterUObject AdditionalScope ( UsePhysicsAsset , GET_STATID ( STAT_RigidBody_Eval ) ) ;
2017-03-24 09:53:37 -04:00
SCOPE_CYCLE_COUNTER ( STAT_ImmediateEvaluateSkeletalControl ) ;
2019-08-02 09:01:58 -04:00
//SCOPED_NAMED_EVENT_TEXT("FAnimNode_RigidBody::EvaluateSkeletalControl_AnyThread", FColor::Magenta);
2017-02-08 17:53:41 -05:00
2020-08-11 01:36:57 -04:00
if ( CVarEnableRigidBodyNodeSimulation . GetValueOnAnyThread ( ) = = 0 )
{
return ;
}
2017-11-09 18:22:55 -05:00
const float DeltaSeconds = AccumulatedDeltaTime ;
AccumulatedDeltaTime = 0.f ;
2019-10-15 16:08:35 -04:00
if ( bEnabled & & PhysicsSimulation )
2017-02-08 17:53:41 -05:00
{
2020-11-24 18:42:39 -04:00
# if ENABLE_RBAN_PERF_LOGGING
double StartTime = - 1.f ;
if ( RBAN_PerfWarningThreshold > 0.f )
{
StartTime = FPlatformTime : : Seconds ( ) ;
}
# endif
2020-09-01 14:07:48 -04:00
2017-03-24 09:53:37 -04:00
const FBoneContainer & BoneContainer = Output . Pose . GetPose ( ) . GetBoneContainer ( ) ;
2017-07-26 09:23:14 -04:00
const FTransform CompWorldSpaceTM = Output . AnimInstanceProxy - > GetComponentTransform ( ) ;
2020-09-01 14:07:48 -04:00
bool bFirstEvalSinceReset = ! Output . AnimInstanceProxy - > GetEvaluationCounter ( ) . HasEverBeenUpdated ( ) ;
// First-frame initialization
if ( bFirstEvalSinceReset )
2018-05-23 21:04:31 -04:00
{
PreviousCompWorldSpaceTM = CompWorldSpaceTM ;
2020-09-01 14:07:48 -04:00
ResetSimulatedTeleportType = ETeleportType : : ResetPhysics ;
2018-05-23 21:04:31 -04:00
}
2017-02-08 17:53:41 -05:00
2020-09-01 14:07:48 -04:00
// See if we need to reset physics because too much time passed since our last update (e.g., because we we off-screen for a while),
// in which case the current sim state may be too far from the current anim pose. This is mostly a problem with world-space
// simulation, whereas bone- and component-space sims can be fairly robust against missing updates.
// Don't do this on first frame or if time-based reset is disabled.
if ( ( EvaluationResetTime > 0.0f ) & & ! bFirstEvalSinceReset )
{
// NOTE: under normal conditions, when this anim node is being serviced at the usual rate (which may not be every frame
// if URO is enabled), we expect that WorldTimeSeconds == (LastEvalTimeSeconds + DeltaSeconds). DeltaSeconds is the
// accumulated time since the last update, including frames dropped by URO, but not frames dropped because of
// being off-screen or LOD changes.
if ( WorldTimeSeconds - ( LastEvalTimeSeconds + DeltaSeconds ) > EvaluationResetTime )
{
UE_LOG ( LogRBAN , Verbose , TEXT ( " %s Time-Based Reset " ) , * Output . AnimInstanceProxy - > GetAnimInstanceName ( ) ) ;
ResetSimulatedTeleportType = ETeleportType : : ResetPhysics ;
}
}
// Update the evaluation time to the current time
LastEvalTimeSeconds = WorldTimeSeconds ;
2020-04-22 20:21:14 -04:00
// Disable simulation below minimum scale in world space mode. World space sim doesn't play nice with scale anyway - we do not scale joint offets or collision shapes.
if ( ( SimulationSpace = = ESimulationSpace : : WorldSpace ) & & ( CompWorldSpaceTM . GetScale3D ( ) . SizeSquared ( ) < WorldSpaceMinimumScale * WorldSpaceMinimumScale ) )
{
return ;
}
const FTransform BaseBoneTM = Output . Pose . GetComponentSpaceTransform ( BaseBoneRef . GetCompactPoseIndex ( BoneContainer ) ) ;
2019-10-02 17:27:26 -04:00
2017-11-09 18:22:55 -05:00
// Initialize potential new bodies because of LOD change.
2018-05-23 21:04:31 -04:00
if ( ResetSimulatedTeleportType = = ETeleportType : : None & & bCheckForBodyTransformInit )
2017-11-09 18:22:55 -05:00
{
bCheckForBodyTransformInit = false ;
2018-04-12 16:57:51 -04:00
InitializeNewBodyTransformsDuringSimulation ( Output , CompWorldSpaceTM , BaseBoneTM ) ;
2017-02-08 17:53:41 -05:00
}
2017-11-09 18:22:55 -05:00
// If time advances, update simulation
2018-05-23 21:04:31 -04:00
// Reset if necessary
2019-05-14 09:09:23 -04:00
bool bDynamicsReset = ( ResetSimulatedTeleportType ! = ETeleportType : : None ) ;
if ( bDynamicsReset )
2017-11-09 18:22:55 -05:00
{
2018-05-23 21:04:31 -04:00
// Capture bone velocities if we have captured a bone velocity pose.
if ( bTransferBoneVelocities & & ( CapturedBoneVelocityPose . GetPose ( ) . GetNumBones ( ) > 0 ) )
2017-11-09 18:22:55 -05:00
{
2018-05-23 21:04:31 -04:00
for ( const FOutputBoneData & OutputData : OutputBoneData )
2017-11-09 18:22:55 -05:00
{
2018-05-23 21:04:31 -04:00
const int32 BodyIndex = OutputData . BodyIndex ;
FBodyAnimData & BodyData = BodyAnimData [ BodyIndex ] ;
if ( BodyData . bIsSimulated )
2017-11-09 18:22:55 -05:00
{
2018-05-23 21:04:31 -04:00
const FCompactPoseBoneIndex NextCompactPoseBoneIndex = OutputData . CompactPoseBoneIndex ;
// Convert CompactPoseBoneIndex to SkeletonBoneIndex...
const int32 PoseSkeletonBoneIndex = BoneContainer . GetPoseToSkeletonBoneIndexArray ( ) [ NextCompactPoseBoneIndex . GetInt ( ) ] ;
// ... So we can convert to the captured pose CompactPoseBoneIndex.
// In case there was a LOD change, and poses are not compatible anymore.
const FCompactPoseBoneIndex PrevCompactPoseBoneIndex = CapturedBoneVelocityBoneContainer . GetCompactPoseIndexFromSkeletonIndex ( PoseSkeletonBoneIndex ) ;
2017-02-08 17:53:41 -05:00
2018-05-23 21:04:31 -04:00
if ( PrevCompactPoseBoneIndex ! = FCompactPoseBoneIndex ( INDEX_NONE ) )
2017-11-09 18:22:55 -05:00
{
2018-05-23 21:04:31 -04:00
const FTransform PrevCSTM = CapturedBoneVelocityPose . GetComponentSpaceTransform ( PrevCompactPoseBoneIndex ) ;
const FTransform NextCSTM = Output . Pose . GetComponentSpaceTransform ( NextCompactPoseBoneIndex ) ;
2017-02-08 17:53:41 -05:00
2018-05-23 21:04:31 -04:00
const FTransform PrevSSTM = ConvertCSTransformToSimSpace ( SimulationSpace , PrevCSTM , CompWorldSpaceTM , BaseBoneTM ) ;
const FTransform NextSSTM = ConvertCSTransformToSimSpace ( SimulationSpace , NextCSTM , CompWorldSpaceTM , BaseBoneTM ) ;
// Linear Velocity
if ( DeltaSeconds > 0.0f )
2017-11-09 18:22:55 -05:00
{
2018-11-14 19:05:13 -05:00
BodyData . TransferedBoneLinearVelocity = ( ( NextSSTM . GetLocation ( ) - PrevSSTM . GetLocation ( ) ) / DeltaSeconds ) ;
2017-11-09 18:22:55 -05:00
}
2018-05-23 21:04:31 -04:00
else
{
2018-11-14 19:05:13 -05:00
BodyData . TransferedBoneLinearVelocity = ( FVector : : ZeroVector ) ;
2018-05-23 21:04:31 -04:00
}
// Angular Velocity
const FQuat DeltaRotation = ( NextSSTM . GetRotation ( ) . Inverse ( ) * PrevSSTM . GetRotation ( ) ) ;
const float RotationAngle = DeltaRotation . GetAngle ( ) / DeltaSeconds ;
2018-11-14 19:05:13 -05:00
BodyData . TransferedBoneAngularVelocity = ( FQuat ( DeltaRotation . GetRotationAxis ( ) , RotationAngle ) ) ;
2017-11-09 18:22:55 -05:00
}
}
}
2018-05-23 21:04:31 -04:00
}
2017-11-09 18:22:55 -05:00
2018-05-23 21:04:31 -04:00
switch ( ResetSimulatedTeleportType )
{
case ETeleportType : : TeleportPhysics :
2017-11-09 18:22:55 -05:00
{
2020-04-22 20:21:14 -04:00
UE_LOG ( LogRBAN , Verbose , TEXT ( " %s TeleportPhysics (Scale: %f %f %f) " ) , * Output . AnimInstanceProxy - > GetAnimInstanceName ( ) , CompWorldSpaceTM . GetScale3D ( ) . X , CompWorldSpaceTM . GetScale3D ( ) . Y , CompWorldSpaceTM . GetScale3D ( ) . Z ) ;
2018-05-23 21:04:31 -04:00
// Teleport bodies.
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
const int32 BodyIndex = OutputData . BodyIndex ;
BodyAnimData [ BodyIndex ] . bBodyTransformInitialized = true ;
FTransform BodyTM = Bodies [ BodyIndex ] - > GetWorldTransform ( ) ;
FTransform ComponentSpaceTM ;
switch ( SimulationSpace )
{
case ESimulationSpace : : ComponentSpace : ComponentSpaceTM = BodyTM ; break ;
case ESimulationSpace : : WorldSpace : ComponentSpaceTM = BodyTM . GetRelativeTransform ( PreviousCompWorldSpaceTM ) ; break ;
case ESimulationSpace : : BaseBoneSpace : ComponentSpaceTM = BodyTM * BaseBoneTM ; break ;
default : ensureMsgf ( false , TEXT ( " Unsupported Simulation Space " ) ) ; ComponentSpaceTM = BodyTM ;
}
BodyTM = ConvertCSTransformToSimSpace ( SimulationSpace , ComponentSpaceTM , CompWorldSpaceTM , BaseBoneTM ) ;
Bodies [ BodyIndex ] - > SetWorldTransform ( BodyTM ) ;
2019-04-05 19:59:27 -04:00
if ( OutputData . ParentBodyIndex ! = INDEX_NONE )
{
BodyAnimData [ BodyIndex ] . RefPoseLength = BodyTM . GetRelativeTransform ( Bodies [ OutputData . ParentBodyIndex ] - > GetWorldTransform ( ) ) . GetLocation ( ) . Size ( ) ;
}
2018-05-23 21:04:31 -04:00
}
}
break ;
case ETeleportType : : ResetPhysics :
{
2020-04-22 20:21:14 -04:00
UE_LOG ( LogRBAN , Verbose , TEXT ( " %s ResetPhysics (Scale: %f %f %f) " ) , * Output . AnimInstanceProxy - > GetAnimInstanceName ( ) , CompWorldSpaceTM . GetScale3D ( ) . X , CompWorldSpaceTM . GetScale3D ( ) . Y , CompWorldSpaceTM . GetScale3D ( ) . Z ) ;
2020-06-23 18:40:00 -04:00
InitSimulationSpace ( CompWorldSpaceTM , BaseBoneTM ) ;
2018-05-23 21:04:31 -04:00
// Completely reset bodies.
2017-11-09 18:22:55 -05:00
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
const int32 BodyIndex = OutputData . BodyIndex ;
BodyAnimData [ BodyIndex ] . bBodyTransformInitialized = true ;
const FTransform & ComponentSpaceTM = Output . Pose . GetComponentSpaceTransform ( OutputData . CompactPoseBoneIndex ) ;
2018-04-12 16:57:51 -04:00
const FTransform BodyTM = ConvertCSTransformToSimSpace ( SimulationSpace , ComponentSpaceTM , CompWorldSpaceTM , BaseBoneTM ) ;
2020-05-04 16:37:52 -04:00
Bodies [ BodyIndex ] - > InitWorldTransform ( BodyTM ) ;
2019-04-05 19:59:27 -04:00
if ( OutputData . ParentBodyIndex ! = INDEX_NONE )
{
BodyAnimData [ BodyIndex ] . RefPoseLength = BodyTM . GetRelativeTransform ( Bodies [ OutputData . ParentBodyIndex ] - > GetWorldTransform ( ) ) . GetLocation ( ) . Size ( ) ;
}
2017-11-09 18:22:55 -05:00
}
}
2018-05-23 21:04:31 -04:00
break ;
2017-11-09 18:22:55 -05:00
}
2018-05-23 21:04:31 -04:00
// Always reset after a teleport
PreviousCompWorldSpaceTM = CompWorldSpaceTM ;
ResetSimulatedTeleportType = ETeleportType : : None ;
PreviousComponentLinearVelocity = FVector : : ZeroVector ;
}
// Only need to tick physics if we didn't reset and we have some time to simulate
2019-05-14 09:09:23 -04:00
if ( ( bSimulateAnimPhysicsAfterReset | | ! bDynamicsReset ) & & DeltaSeconds > AnimPhysicsMinDeltaTime )
2018-05-23 21:04:31 -04:00
{
// Transfer bone velocities previously captured.
if ( bTransferBoneVelocities & & ( CapturedBoneVelocityPose . GetPose ( ) . GetNumBones ( ) > 0 ) )
2017-11-09 18:22:55 -05:00
{
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
const int32 BodyIndex = OutputData . BodyIndex ;
2018-05-23 21:04:31 -04:00
const FBodyAnimData & BodyData = BodyAnimData [ BodyIndex ] ;
2017-11-09 18:22:55 -05:00
2018-05-23 21:04:31 -04:00
if ( BodyData . bIsSimulated )
{
ImmediatePhysics : : FActorHandle * Body = Bodies [ BodyIndex ] ;
2018-11-14 19:05:13 -05:00
Body - > SetLinearVelocity ( BodyData . TransferedBoneLinearVelocity ) ;
2018-05-23 21:04:31 -04:00
2018-11-14 19:05:13 -05:00
const FQuat AngularVelocity = BodyData . TransferedBoneAngularVelocity ;
2018-05-23 21:04:31 -04:00
Body - > SetAngularVelocity ( AngularVelocity . GetRotationAxis ( ) * AngularVelocity . GetAngle ( ) ) ;
2017-11-09 18:22:55 -05:00
}
}
2018-05-23 21:04:31 -04:00
// Free up our captured pose after it's been used.
CapturedBoneVelocityPose . Empty ( ) ;
}
2020-06-23 18:40:00 -04:00
else if ( ( SimulationSpace ! = ESimulationSpace : : WorldSpace ) & & bRBAN_EnableComponentAcceleration )
2018-05-23 21:04:31 -04:00
{
2020-10-22 19:19:16 -04:00
if ( ! ComponentLinearVelScale . IsNearlyZero ( ) | | ! ComponentLinearAccScale . IsNearlyZero ( ) )
2017-11-09 18:22:55 -05:00
{
2020-10-22 19:19:16 -04:00
// Calc linear velocity
const FVector ComponentDeltaLocation = CurrentTransform . GetTranslation ( ) - PreviousTransform . GetTranslation ( ) ;
const FVector ComponentLinearVelocity = ComponentDeltaLocation / DeltaSeconds ;
// Apply acceleration that opposed velocity (basically 'drag')
FVector ApplyLinearAcc = WorldVectorToSpaceNoScale ( SimulationSpace , - ComponentLinearVelocity , CompWorldSpaceTM , BaseBoneTM ) * ComponentLinearVelScale ;
2018-05-23 21:04:31 -04:00
2020-10-22 19:19:16 -04:00
// Calc linear acceleration
const FVector ComponentLinearAcceleration = ( ComponentLinearVelocity - PreviousComponentLinearVelocity ) / DeltaSeconds ;
PreviousComponentLinearVelocity = ComponentLinearVelocity ;
// Apply opposite acceleration to bodies
ApplyLinearAcc + = WorldVectorToSpaceNoScale ( SimulationSpace , - ComponentLinearAcceleration , CompWorldSpaceTM , BaseBoneTM ) * ComponentLinearAccScale ;
// Iterate over bodies
for ( const FOutputBoneData & OutputData : OutputBoneData )
2018-05-23 21:04:31 -04:00
{
2020-10-22 19:19:16 -04:00
const int32 BodyIndex = OutputData . BodyIndex ;
const FBodyAnimData & BodyData = BodyAnimData [ BodyIndex ] ;
2018-05-23 21:04:31 -04:00
2020-10-22 19:19:16 -04:00
if ( BodyData . bIsSimulated )
2018-05-23 21:04:31 -04:00
{
2020-10-22 19:19:16 -04:00
ImmediatePhysics : : FActorHandle * Body = Bodies [ BodyIndex ] ;
2018-05-23 21:04:31 -04:00
2020-10-22 19:19:16 -04:00
// Apply
const float BodyInvMass = Body - > GetInverseMass ( ) ;
if ( BodyInvMass > 0.f )
2018-05-23 21:04:31 -04:00
{
2020-10-22 19:19:16 -04:00
// Final desired acceleration to apply to body
FVector FinalBodyLinearAcc = ApplyLinearAcc ;
2018-05-23 21:04:31 -04:00
2020-10-22 19:19:16 -04:00
// Clamp if desired
if ( ! ComponentAppliedLinearAccClamp . IsNearlyZero ( ) )
{
FinalBodyLinearAcc = FinalBodyLinearAcc . BoundToBox ( - ComponentAppliedLinearAccClamp , ComponentAppliedLinearAccClamp ) ;
}
// Apply to body
Body - > AddForce ( FinalBodyLinearAcc / BodyInvMass ) ;
}
2018-05-23 21:04:31 -04:00
}
}
2017-11-09 18:22:55 -05:00
}
}
2018-05-23 21:04:31 -04:00
2019-10-11 13:50:12 -04:00
// @todo(ccaulfield): We should be interpolating kinematic targets for each sub-step below
2018-05-23 21:04:31 -04:00
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
const int32 BodyIndex = OutputData . BodyIndex ;
if ( ! BodyAnimData [ BodyIndex ] . bIsSimulated )
{
const FTransform & ComponentSpaceTM = Output . Pose . GetComponentSpaceTransform ( OutputData . CompactPoseBoneIndex ) ;
const FTransform BodyTM = ConvertCSTransformToSimSpace ( SimulationSpace , ComponentSpaceTM , CompWorldSpaceTM , BaseBoneTM ) ;
Bodies [ BodyIndex ] - > SetKinematicTarget ( BodyTM ) ;
}
}
2018-12-12 11:25:29 -05:00
2018-05-23 21:04:31 -04:00
UpdateWorldForces ( CompWorldSpaceTM , BaseBoneTM ) ;
const FVector SimSpaceGravity = WorldVectorToSpaceNoScale ( SimulationSpace , WorldSpaceGravity , CompWorldSpaceTM , BaseBoneTM ) ;
// Run simulation at a minimum of 30 FPS to prevent system from exploding.
// DeltaTime can be higher due to URO, so take multiple iterations in that case.
2019-10-11 13:50:12 -04:00
const int32 MaxSteps = RBAN_MaxSubSteps ;
2018-05-23 21:04:31 -04:00
const float MaxDeltaSeconds = 1.f / 30.f ;
2019-10-29 13:35:01 -04:00
# if !WITH_CHAOS
2019-10-11 13:50:12 -04:00
const int32 NumSteps = FMath : : Clamp ( FMath : : CeilToInt ( DeltaSeconds / MaxDeltaSeconds ) , 1 , MaxSteps ) ;
const float StepDeltaTime = DeltaSeconds / float ( NumSteps ) ;
for ( int32 Step = 1 ; Step < = NumSteps ; Step + + )
2018-05-23 21:04:31 -04:00
{
2018-12-12 11:25:29 -05:00
// We call the _AssumesLocked version here without a lock as the simulation is local to this node and we know
// we're not going to alter anything while this is running.
PhysicsSimulation - > Simulate_AssumesLocked ( StepDeltaTime , SimSpaceGravity ) ;
2018-05-23 21:04:31 -04:00
}
2019-10-29 13:35:01 -04:00
# else
2020-06-23 18:40:00 -04:00
FSimSpaceSettings * UseSimSpaceSettings = & SimSpaceSettings ;
if ( bRBAN_SimSpace_EnableOverride )
{
UseSimSpaceSettings = & RBAN_SimSpaceOverride ;
}
FTransform SimulationTransform ;
FVector SimulationLinearVelocity ;
FVector SimulationAngularVelocity ;
FVector SimulationLinearAcceleration ;
FVector SimulationAngularAcceleration ;
CalculateSimulationSpace (
SimulationSpace ,
CompWorldSpaceTM ,
BaseBoneTM ,
DeltaSeconds ,
* UseSimSpaceSettings ,
SimulationTransform ,
SimulationLinearVelocity ,
SimulationAngularVelocity ,
SimulationLinearAcceleration ,
SimulationAngularAcceleration ) ;
UpdateWorldObjects ( SimulationTransform ) ;
PhysicsSimulation - > UpdateSimulationSpace (
SimulationTransform ,
SimulationLinearVelocity ,
SimulationAngularVelocity ,
SimulationLinearAcceleration ,
SimulationAngularAcceleration ) ;
PhysicsSimulation - > SetSimulationSpaceSettings (
UseSimSpaceSettings - > MasterAlpha ,
2020-09-01 14:07:48 -04:00
UseSimSpaceSettings - > ExternalLinearDragV ) ;
2020-06-23 18:40:00 -04:00
2020-01-18 20:07:46 -05:00
PhysicsSimulation - > SetSolverIterations (
2020-04-01 14:24:17 -04:00
SolverIterations . FixedTimeStep ,
2020-01-18 20:07:46 -05:00
SolverIterations . SolverIterations ,
SolverIterations . JointIterations ,
SolverIterations . CollisionIterations ,
SolverIterations . SolverPushOutIterations ,
SolverIterations . JointPushOutIterations ,
2020-06-23 18:40:00 -04:00
SolverIterations . CollisionPushOutIterations ) ;
2020-01-18 20:07:46 -05:00
2019-10-29 13:35:01 -04:00
PhysicsSimulation - > Simulate_AssumesLocked ( DeltaSeconds , MaxDeltaSeconds , MaxSteps , SimSpaceGravity ) ;
# endif
2017-11-09 18:22:55 -05:00
}
2017-02-08 17:53:41 -05:00
//write back to animation system
for ( const FOutputBoneData & OutputData : OutputBoneData )
{
2017-11-09 18:22:55 -05:00
const int32 BodyIndex = OutputData . BodyIndex ;
if ( BodyAnimData [ BodyIndex ] . bIsSimulated )
2017-02-08 17:53:41 -05:00
{
2019-01-08 11:38:48 -05:00
FTransform BodyTM = Bodies [ BodyIndex ] - > GetWorldTransform ( ) ;
// if we clamp translation, we only do this when all linear translation are locked
//
2020-05-04 16:37:52 -04:00
// @todo(ccaulfield): this shouldn't be required with Chaos - projection should be handling it...
2019-01-08 11:38:48 -05:00
if ( bClampLinearTranslationLimitToRefPose
& & BodyAnimData [ BodyIndex ] . LinearXMotion = = ELinearConstraintMotion : : LCM_Locked
& & BodyAnimData [ BodyIndex ] . LinearYMotion = = ELinearConstraintMotion : : LCM_Locked
& & BodyAnimData [ BodyIndex ] . LinearZMotion = = ELinearConstraintMotion : : LCM_Locked )
{
// grab local space of length from ref pose
// we have linear limit value - see if that works
// calculate current local space from parent
// find parent transform
const int32 ParentBodyIndex = OutputData . ParentBodyIndex ;
FTransform ParentTransform = FTransform : : Identity ;
if ( ParentBodyIndex ! = INDEX_NONE )
{
ParentTransform = Bodies [ ParentBodyIndex ] - > GetWorldTransform ( ) ;
}
// get local transform
FTransform LocalTransform = BodyTM . GetRelativeTransform ( ParentTransform ) ;
const float CurrentLength = LocalTransform . GetTranslation ( ) . Size ( ) ;
// this is inconsistent with constraint. The actual linear limit is set by constraint
if ( ! FMath : : IsNearlyEqual ( CurrentLength , BodyAnimData [ BodyIndex ] . RefPoseLength , KINDA_SMALL_NUMBER ) )
{
float RefPoseLength = BodyAnimData [ BodyIndex ] . RefPoseLength ;
if ( CurrentLength > RefPoseLength )
{
float Scale = ( CurrentLength > KINDA_SMALL_NUMBER ) ? RefPoseLength / CurrentLength : 0.f ;
// we don't use 1.f here because 1.f can create pops based on float issue.
// so we only activate clamping when less than 90%
if ( Scale < 0.9f )
{
LocalTransform . ScaleTranslation ( Scale ) ;
BodyTM = LocalTransform * ParentTransform ;
Bodies [ BodyIndex ] - > SetWorldTransform ( BodyTM ) ;
}
}
}
}
2017-11-09 18:22:55 -05:00
FTransform ComponentSpaceTM ;
2017-07-26 09:23:14 -04:00
2017-11-09 18:22:55 -05:00
switch ( SimulationSpace )
{
case ESimulationSpace : : ComponentSpace : ComponentSpaceTM = BodyTM ; break ;
case ESimulationSpace : : WorldSpace : ComponentSpaceTM = BodyTM . GetRelativeTransform ( CompWorldSpaceTM ) ; break ;
2018-04-12 16:57:51 -04:00
case ESimulationSpace : : BaseBoneSpace : ComponentSpaceTM = BodyTM * BaseBoneTM ; break ;
2017-11-09 18:22:55 -05:00
default : ensureMsgf ( false , TEXT ( " Unsupported Simulation Space " ) ) ; ComponentSpaceTM = BodyTM ;
2017-02-08 17:53:41 -05:00
}
2017-11-09 18:22:55 -05:00
OutBoneTransforms . Add ( FBoneTransform ( OutputData . CompactPoseBoneIndex , ComponentSpaceTM ) ) ;
2017-02-08 17:53:41 -05:00
}
}
2018-05-23 21:04:31 -04:00
PreviousCompWorldSpaceTM = CompWorldSpaceTM ;
2020-11-24 18:42:39 -04:00
# if ENABLE_RBAN_PERF_LOGGING
if ( RBAN_PerfWarningThreshold > 0.f )
{
const double EndTime = FPlatformTime : : Seconds ( ) ;
const double ElapsedTimeMS = ( EndTime - StartTime ) * 1000.0 ;
if ( ElapsedTimeMS > RBAN_PerfWarningThreshold & & ( EndTime - LastPerfWarningTimeSeconds ) > RBAN_PerfWarningInterval )
{
check ( UsePhysicsAsset ) ;
LastPerfWarningTimeSeconds = EndTime ;
UE_LOG ( LogRBAN , Warning , TEXT ( " Exceeded Performance Budget: %s took %.02fms " ) , * UsePhysicsAsset - > GetName ( ) , ElapsedTimeMS ) ;
}
}
# endif
2017-02-08 17:53:41 -05:00
}
}
2017-03-24 09:53:37 -04:00
void ComputeBodyInsertionOrder ( TArray < FBoneIndexType > & InsertionOrder , const USkeletalMeshComponent & SKC )
2017-02-08 17:53:41 -05:00
{
2017-03-24 09:53:37 -04:00
//We want to ensure simulated bodies are sorted by LOD so that the first simulated bodies are at the highest LOD.
//Since LOD2 is a subset of LOD1 which is a subset of LOD0 we can change the number of simulated bodies without any reordering
//For this to work we must first insert all simulated bodies in the right order. We then insert all the kinematic bodies in the right order
InsertionOrder . Reset ( ) ;
2020-04-08 16:41:10 -04:00
if ( SKC . SkeletalMesh = = nullptr )
{
return ;
}
2017-10-13 11:32:28 -04:00
const int32 NumLODs = SKC . GetNumLODs ( ) ;
if ( NumLODs > 0 )
{
2017-03-24 09:53:37 -04:00
TArray < FBoneIndexType > RequiredBones0 ;
TArray < FBoneIndexType > ComponentSpaceTMs0 ;
SKC . ComputeRequiredBones ( RequiredBones0 , ComponentSpaceTMs0 , 0 , /*bIgnorePhysicsAsset=*/ true ) ;
2020-04-08 16:41:10 -04:00
TArray < bool > InSortedOrder ;
2020-11-25 11:17:08 -04:00
InSortedOrder . AddZeroed ( SKC . SkeletalMesh - > GetRefSkeleton ( ) . GetNum ( ) ) ;
2017-03-24 09:53:37 -04:00
auto MergeIndices = [ & InsertionOrder , & InSortedOrder ] ( const TArray < FBoneIndexType > & RequiredBones ) - > void
{
for ( FBoneIndexType BoneIdx : RequiredBones )
{
if ( ! InSortedOrder [ BoneIdx ] )
{
InsertionOrder . Add ( BoneIdx ) ;
}
InSortedOrder [ BoneIdx ] = true ;
}
} ;
for ( int32 LodIdx = NumLODs - 1 ; LodIdx > 0 ; - - LodIdx )
{
TArray < FBoneIndexType > RequiredBones ;
TArray < FBoneIndexType > ComponentSpaceTMs ;
SKC . ComputeRequiredBones ( RequiredBones , ComponentSpaceTMs , LodIdx , /*bIgnorePhysicsAsset=*/ true ) ;
MergeIndices ( RequiredBones ) ;
}
MergeIndices ( RequiredBones0 ) ;
}
2017-02-08 17:53:41 -05:00
}
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : InitPhysics ( const UAnimInstance * InAnimInstance )
2017-02-08 17:53:41 -05:00
{
2019-10-04 18:34:07 -04:00
SCOPE_CYCLE_COUNTER ( STAT_RigidBodyNodeInitTime ) ;
2019-10-15 16:08:35 -04:00
delete PhysicsSimulation ;
PhysicsSimulation = nullptr ;
2017-02-08 17:53:41 -05:00
const USkeletalMeshComponent * SkeletalMeshComp = InAnimInstance - > GetSkelMeshComponent ( ) ;
2017-11-09 18:22:55 -05:00
const USkeletalMesh * SkeletalMeshAsset = SkeletalMeshComp - > SkeletalMesh ;
2020-11-25 11:17:08 -04:00
const FReferenceSkeleton & SkelMeshRefSkel = SkeletalMeshAsset - > GetRefSkeleton ( ) ;
2020-11-12 18:18:02 -04:00
UsePhysicsAsset = OverridePhysicsAsset ? ToRawPtr ( OverridePhysicsAsset ) : InAnimInstance - > GetSkelMeshComponent ( ) - > GetPhysicsAsset ( ) ;
2017-11-09 18:22:55 -05:00
USkeleton * SkeletonAsset = InAnimInstance - > CurrentSkeleton ;
2020-11-25 11:17:08 -04:00
ensure ( SkeletonAsset = = SkeletalMeshAsset - > GetSkeleton ( ) ) ;
2017-11-09 18:22:55 -05:00
const int32 SkelMeshLinkupIndex = SkeletonAsset - > GetMeshLinkupIndex ( SkeletalMeshAsset ) ;
ensure ( SkelMeshLinkupIndex ! = INDEX_NONE ) ;
const FSkeletonToMeshLinkup & SkeletonToMeshLinkupTable = SkeletonAsset - > LinkupCache [ SkelMeshLinkupIndex ] ;
const TArray < int32 > & MeshToSkeletonBoneIndex = SkeletonToMeshLinkupTable . MeshToSkeletonTable ;
const int32 NumSkeletonBones = SkeletonAsset - > GetReferenceSkeleton ( ) . GetNum ( ) ;
SkeletonBoneIndexToBodyIndex . Reset ( NumSkeletonBones ) ;
SkeletonBoneIndexToBodyIndex . Init ( INDEX_NONE , NumSkeletonBones ) ;
2019-08-02 09:01:58 -04:00
PreviousTransform = SkeletalMeshComp - > GetComponentToWorld ( ) ;
2018-05-23 21:04:31 -04:00
2020-06-23 18:40:00 -04:00
ComponentsInSim . Reset ( ) ;
ComponentsInSimTick = 0 ;
2019-04-19 20:21:02 -04:00
if ( UPhysicsSettings * Settings = UPhysicsSettings : : Get ( ) )
{
AnimPhysicsMinDeltaTime = Settings - > AnimPhysicsMinDeltaTime ;
2019-05-14 09:09:23 -04:00
bSimulateAnimPhysicsAfterReset = Settings - > bSimulateAnimPhysicsAfterReset ;
2019-04-19 20:21:02 -04:00
}
else
{
AnimPhysicsMinDeltaTime = 0.f ;
2019-05-14 09:09:23 -04:00
bSimulateAnimPhysicsAfterReset = false ;
2019-04-19 20:21:02 -04:00
}
2019-10-15 16:08:35 -04:00
bEnabled = UsePhysicsAsset & & SkeletalMeshComp - > GetAllowRigidBodyAnimNode ( ) & & CVarEnableRigidBodyNode . GetValueOnAnyThread ( ) ! = 0 ;
if ( bEnabled )
2017-02-08 17:53:41 -05:00
{
2019-01-14 12:11:24 -05:00
PhysicsSimulation = new ImmediatePhysics : : FSimulation ( ) ;
2017-02-08 17:53:41 -05:00
const int32 NumBodies = UsePhysicsAsset - > SkeletalBodySetups . Num ( ) ;
Bodies . Empty ( NumBodies ) ;
2017-11-09 18:22:55 -05:00
BodyAnimData . Reset ( NumBodies ) ;
BodyAnimData . AddDefaulted ( NumBodies ) ;
2017-02-08 17:53:41 -05:00
TotalMass = 0.f ;
2019-08-02 09:01:58 -04:00
// Instantiate a FBodyInstance/FConstraintInstance set that will be cloned into the Immediate Physics sim.
2019-11-07 15:27:44 -05:00
// NOTE: We do not have a skeleton at the moment, so we have to use the ref pose
2017-03-24 09:53:37 -04:00
TArray < FBodyInstance * > HighLevelBodyInstances ;
TArray < FConstraintInstance * > HighLevelConstraintInstances ;
2019-11-07 15:27:44 -05:00
2019-10-04 18:11:17 -04:00
// Chaos relies on the initial pose to set up constraint positions
2019-11-07 15:27:44 -05:00
bool bCreateBodiesInRefPose = ( WITH_CHAOS ! = 0 ) ;
SkeletalMeshComp - > InstantiatePhysicsAssetRefPose (
* UsePhysicsAsset ,
SimulationSpace = = ESimulationSpace : : WorldSpace ? SkeletalMeshComp - > GetComponentToWorld ( ) . GetScale3D ( ) : FVector ( 1.f ) ,
HighLevelBodyInstances ,
HighLevelConstraintInstances ,
nullptr ,
nullptr ,
INDEX_NONE ,
FPhysicsAggregateHandle ( ) ,
bCreateBodiesInRefPose ) ;
2017-02-08 17:53:41 -05:00
2019-01-14 12:11:24 -05:00
TMap < FName , ImmediatePhysics : : FActorHandle * > NamesToHandles ;
TArray < ImmediatePhysics : : FActorHandle * > IgnoreCollisionActors ;
2017-02-08 17:53:41 -05:00
2017-03-24 09:53:37 -04:00
TArray < FBoneIndexType > InsertionOrder ;
ComputeBodyInsertionOrder ( InsertionOrder , * SkeletalMeshComp ) ;
2017-02-08 17:53:41 -05:00
2020-04-08 16:41:10 -04:00
// NOTE: NumBonesLOD0 may be less than NumBonesTotal, and it may be middle bones that are missing from LOD0.
// In this case, LOD0 bone indices may be >= NumBonesLOD0, but always < NumBonesTotal. Arrays indexed by
// bone index must be size NumBonesTotal.
2017-03-24 09:53:37 -04:00
const int32 NumBonesLOD0 = InsertionOrder . Num ( ) ;
2020-04-08 16:41:10 -04:00
const int32 NumBonesTotal = SkelMeshRefSkel . GetNum ( ) ;
2017-03-24 09:53:37 -04:00
2020-02-20 12:16:00 -05:00
// If our skeleton is not the one that was used to build the PhysicsAsset, some bodies may be missing, or rearranged.
// We need to map the original indices to the new bodies for use by the CollisionDisableTable.
// NOTE: This array is indexed by the original BodyInstance body index (BodyInstance->InstanceBodyIndex)
2019-01-14 12:11:24 -05:00
TArray < ImmediatePhysics : : FActorHandle * > BodyIndexToActorHandle ;
2020-02-20 12:16:00 -05:00
BodyIndexToActorHandle . AddZeroed ( HighLevelBodyInstances . Num ( ) ) ;
2017-03-24 09:53:37 -04:00
TArray < FBodyInstance * > BodiesSorted ;
2020-04-08 16:41:10 -04:00
BodiesSorted . AddZeroed ( NumBonesTotal ) ;
2017-03-24 09:53:37 -04:00
for ( FBodyInstance * BI : HighLevelBodyInstances )
2017-02-08 17:53:41 -05:00
{
2017-03-24 09:53:37 -04:00
if ( BI - > IsValidBodyInstance ( ) )
2017-02-08 17:53:41 -05:00
{
2017-03-24 09:53:37 -04:00
BodiesSorted [ BI - > InstanceBoneIndex ] = BI ;
2017-02-08 17:53:41 -05:00
}
}
2019-08-02 09:01:58 -04:00
// Create the immediate physics bodies
for ( FBoneIndexType InsertBone : InsertionOrder )
2017-03-24 09:53:37 -04:00
{
2019-08-02 09:01:58 -04:00
if ( FBodyInstance * BodyInstance = BodiesSorted [ InsertBone ] )
2017-03-24 09:53:37 -04:00
{
2019-08-02 09:01:58 -04:00
UBodySetup * BodySetup = UsePhysicsAsset - > SkeletalBodySetups [ BodyInstance - > InstanceBodyIndex ] ;
2017-03-24 09:53:37 -04:00
2019-08-02 09:01:58 -04:00
bool bSimulated = ( BodySetup - > PhysicsType = = EPhysicsType : : PhysType_Simulated ) ;
ImmediatePhysics : : EActorType ActorType = bSimulated ? ImmediatePhysics : : EActorType : : DynamicActor : ImmediatePhysics : : EActorType : : KinematicActor ;
ImmediatePhysics : : FActorHandle * NewBodyHandle = PhysicsSimulation - > CreateActor ( ActorType , BodyInstance , BodyInstance - > GetUnrealWorldTransform ( ) ) ;
if ( NewBodyHandle )
{
if ( bSimulated )
2017-03-24 09:53:37 -04:00
{
const float InvMass = NewBodyHandle - > GetInverseMass ( ) ;
TotalMass + = InvMass > 0.f ? 1.f / InvMass : 0.f ;
}
2019-08-02 09:01:58 -04:00
const int32 BodyIndex = Bodies . Add ( NewBodyHandle ) ;
const int32 SkeletonBoneIndex = MeshToSkeletonBoneIndex [ InsertBone ] ;
SkeletonBoneIndexToBodyIndex [ SkeletonBoneIndex ] = BodyIndex ;
BodyAnimData [ BodyIndex ] . bIsSimulated = bSimulated ;
NamesToHandles . Add ( BodySetup - > BoneName , NewBodyHandle ) ;
BodyIndexToActorHandle [ BodyInstance - > InstanceBodyIndex ] = NewBodyHandle ;
if ( BodySetup - > CollisionReponse = = EBodyCollisionResponse : : BodyCollision_Disabled )
2017-03-24 09:53:37 -04:00
{
2019-08-02 09:01:58 -04:00
IgnoreCollisionActors . Add ( NewBodyHandle ) ;
2017-03-24 09:53:37 -04:00
}
2019-12-19 23:33:50 -05:00
# if WITH_CHAOS
NewBodyHandle - > SetName ( BodySetup - > BoneName ) ;
# endif
2017-03-24 09:53:37 -04:00
}
}
2019-08-02 09:01:58 -04:00
}
2017-03-24 09:53:37 -04:00
//Insert joints so that they coincide body order. That is, if we stop simulating all bodies past some index, we can simply ignore joints past a corresponding index without any re-order
//For this to work we consider the most last inserted bone in each joint
TArray < int32 > InsertionOrderPerBone ;
2020-04-08 16:41:10 -04:00
InsertionOrderPerBone . AddUninitialized ( NumBonesTotal ) ;
2017-03-24 09:53:37 -04:00
for ( int32 Position = 0 ; Position < NumBonesLOD0 ; + + Position )
{
InsertionOrderPerBone [ InsertionOrder [ Position ] ] = Position ;
}
2017-11-09 18:22:55 -05:00
HighLevelConstraintInstances . Sort ( [ & InsertionOrderPerBone , & SkelMeshRefSkel ] ( const FConstraintInstance & LHS , const FConstraintInstance & RHS )
2017-03-24 09:53:37 -04:00
{
if ( LHS . IsValidConstraintInstance ( ) & & RHS . IsValidConstraintInstance ( ) )
{
2017-11-09 18:22:55 -05:00
const int32 BoneIdxLHS1 = SkelMeshRefSkel . FindBoneIndex ( LHS . ConstraintBone1 ) ;
const int32 BoneIdxLHS2 = SkelMeshRefSkel . FindBoneIndex ( LHS . ConstraintBone2 ) ;
2017-03-24 09:53:37 -04:00
2017-11-09 18:22:55 -05:00
const int32 BoneIdxRHS1 = SkelMeshRefSkel . FindBoneIndex ( RHS . ConstraintBone1 ) ;
const int32 BoneIdxRHS2 = SkelMeshRefSkel . FindBoneIndex ( RHS . ConstraintBone2 ) ;
2017-03-24 09:53:37 -04:00
const int32 MaxPositionLHS = FMath : : Max ( InsertionOrderPerBone [ BoneIdxLHS1 ] , InsertionOrderPerBone [ BoneIdxLHS2 ] ) ;
const int32 MaxPositionRHS = FMath : : Max ( InsertionOrderPerBone [ BoneIdxRHS1 ] , InsertionOrderPerBone [ BoneIdxRHS2 ] ) ;
return MaxPositionLHS < MaxPositionRHS ;
}
return false ;
} ) ;
2019-08-02 09:01:58 -04:00
2020-04-21 19:16:24 -04:00
TArray < ImmediatePhysics : : FSimulation : : FIgnorePair > IgnorePairs ;
2017-02-08 17:53:41 -05:00
if ( NamesToHandles . Num ( ) > 0 )
{
//constraints
for ( int32 ConstraintIdx = 0 ; ConstraintIdx < HighLevelConstraintInstances . Num ( ) ; + + ConstraintIdx )
{
2017-03-24 09:53:37 -04:00
FConstraintInstance * CI = HighLevelConstraintInstances [ ConstraintIdx ] ;
2019-01-14 12:11:24 -05:00
ImmediatePhysics : : FActorHandle * Body1Handle = NamesToHandles . FindRef ( CI - > ConstraintBone1 ) ;
ImmediatePhysics : : FActorHandle * Body2Handle = NamesToHandles . FindRef ( CI - > ConstraintBone2 ) ;
2017-02-08 17:53:41 -05:00
if ( Body1Handle & & Body2Handle )
{
if ( Body1Handle - > IsSimulated ( ) | | Body2Handle - > IsSimulated ( ) )
{
2019-08-02 09:01:58 -04:00
PhysicsSimulation - > CreateJoint ( CI , Body1Handle , Body2Handle ) ;
2019-01-08 14:00:07 -05:00
if ( bForceDisableCollisionBetweenConstraintBodies )
2019-01-08 11:38:48 -05:00
{
int32 BodyIndex1 = UsePhysicsAsset - > FindBodyIndex ( CI - > ConstraintBone1 ) ;
int32 BodyIndex2 = UsePhysicsAsset - > FindBodyIndex ( CI - > ConstraintBone2 ) ;
if ( BodyIndex1 ! = INDEX_NONE & & BodyIndex2 ! = INDEX_NONE )
{
UsePhysicsAsset - > DisableCollision ( BodyIndex1 , BodyIndex2 ) ;
}
}
int32 BodyIndex ;
if ( Bodies . Find ( Body1Handle , BodyIndex ) )
{
BodyAnimData [ BodyIndex ] . LinearXMotion = CI - > GetLinearXMotion ( ) ;
BodyAnimData [ BodyIndex ] . LinearYMotion = CI - > GetLinearYMotion ( ) ;
BodyAnimData [ BodyIndex ] . LinearZMotion = CI - > GetLinearZMotion ( ) ;
BodyAnimData [ BodyIndex ] . LinearLimit = CI - > GetLinearLimit ( ) ;
//set limit to ref pose
FTransform Body1Transform = Body1Handle - > GetWorldTransform ( ) ;
FTransform Body2Transform = Body2Handle - > GetWorldTransform ( ) ;
BodyAnimData [ BodyIndex ] . RefPoseLength = Body1Transform . GetRelativeTransform ( Body2Transform ) . GetLocation ( ) . Size ( ) ;
}
2020-04-21 19:16:24 -04:00
if ( CI - > IsCollisionDisabled ( ) )
{
ImmediatePhysics : : FSimulation : : FIgnorePair Pair ;
Pair . A = Body1Handle ;
Pair . B = Body2Handle ;
IgnorePairs . Add ( Pair ) ;
}
2017-02-08 17:53:41 -05:00
}
}
}
2018-05-23 21:04:31 -04:00
ResetSimulatedTeleportType = ETeleportType : : ResetPhysics ;
2017-02-08 17:53:41 -05:00
}
2020-06-23 18:40:00 -04:00
// Terminate all the constraint instances
for ( FConstraintInstance * CI : HighLevelConstraintInstances )
{
CI - > TermConstraint ( ) ;
delete CI ;
}
2018-07-31 02:23:26 -04:00
// Terminate all of the instances, cannot be done during insert or we may break constraint chains
for ( FBodyInstance * Instance : HighLevelBodyInstances )
{
if ( Instance - > IsValidBodyInstance ( ) )
{
Instance - > TermBody ( true ) ;
}
delete Instance ;
}
2020-06-23 18:40:00 -04:00
HighLevelConstraintInstances . Empty ( ) ;
2018-07-31 02:23:26 -04:00
HighLevelBodyInstances . Empty ( ) ;
BodiesSorted . Empty ( ) ;
2017-02-08 17:53:41 -05:00
const TMap < FRigidBodyIndexPair , bool > & DisableTable = UsePhysicsAsset - > CollisionDisableTable ;
for ( auto ConstItr = DisableTable . CreateConstIterator ( ) ; ConstItr ; + + ConstItr )
{
2020-02-20 12:16:00 -05:00
int32 IndexA = ConstItr . Key ( ) . Indices [ 0 ] ;
int32 IndexB = ConstItr . Key ( ) . Indices [ 1 ] ;
2020-02-21 18:08:39 -05:00
if ( ( IndexA < BodyIndexToActorHandle . Num ( ) ) & & ( IndexB < BodyIndexToActorHandle . Num ( ) ) )
2020-02-20 12:16:00 -05:00
{
if ( ( BodyIndexToActorHandle [ IndexA ] ! = nullptr ) & & ( BodyIndexToActorHandle [ IndexB ] ! = nullptr ) )
{
ImmediatePhysics : : FSimulation : : FIgnorePair Pair ;
Pair . A = BodyIndexToActorHandle [ IndexA ] ;
Pair . B = BodyIndexToActorHandle [ IndexB ] ;
IgnorePairs . Add ( Pair ) ;
}
}
2017-02-08 17:53:41 -05:00
}
PhysicsSimulation - > SetIgnoreCollisionPairTable ( IgnorePairs ) ;
PhysicsSimulation - > SetIgnoreCollisionActors ( IgnoreCollisionActors ) ;
2020-01-18 20:07:46 -05:00
# if WITH_CHAOS
SolverIterations = UsePhysicsAsset - > SolverIterations ;
PhysicsSimulation - > SetSolverIterations (
2020-04-01 14:24:17 -04:00
SolverIterations . FixedTimeStep ,
2020-01-18 20:07:46 -05:00
SolverIterations . SolverIterations ,
SolverIterations . JointIterations ,
SolverIterations . CollisionIterations ,
SolverIterations . SolverPushOutIterations ,
SolverIterations . JointPushOutIterations ,
SolverIterations . CollisionPushOutIterations
) ;
# endif
2017-10-06 04:43:18 -04:00
}
2017-02-08 17:53:41 -05:00
}
2019-08-02 09:01:58 -04:00
DECLARE_CYCLE_STAT ( TEXT ( " FAnimNode_RigidBody::UpdateWorldGeometry " ) , STAT_ImmediateUpdateWorldGeometry , STATGROUP_ImmediatePhysics ) ;
2017-02-08 17:53:41 -05:00
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : UpdateWorldGeometry ( const UWorld & World , const USkeletalMeshComponent & SKC )
2017-02-08 17:53:41 -05:00
{
SCOPE_CYCLE_COUNTER ( STAT_ImmediateUpdateWorldGeometry ) ;
2017-05-12 11:21:11 -04:00
QueryParams = FCollisionQueryParams ( SCENE_QUERY_STAT ( RagdollNodeFindGeometry ) , /*bTraceComplex=*/ false ) ;
2017-02-08 17:53:41 -05:00
# if WITH_EDITOR
if ( ! World . IsGameWorld ( ) )
{
QueryParams . MobilityType = EQueryMobilityType : : Any ; //If we're in some preview world trace against everything because things like the preview floor are not static
QueryParams . AddIgnoredComponent ( & SKC ) ;
}
else
# endif
{
QueryParams . MobilityType = EQueryMobilityType : : Static ; //We only want static actors
}
2020-06-23 18:40:00 -04:00
// Check for deleted world objects and flag for removal (later in anim task)
ExpireWorldObjects ( ) ;
2017-02-08 17:53:41 -05:00
2020-06-23 18:40:00 -04:00
// If we have moved outside of the bounds we checked for world objects we need to gather new world objects
FSphere Bounds = SKC . CalcBounds ( SKC . GetComponentToWorld ( ) ) . GetSphere ( ) ;
2017-03-24 09:53:37 -04:00
if ( ! Bounds . IsInside ( CachedBounds ) )
2017-02-08 17:53:41 -05:00
{
2017-03-24 09:53:37 -04:00
// Since the cached bounds are no longer valid, update them.
CachedBounds = Bounds ;
CachedBounds . W * = CachedBoundsScale ;
2020-06-23 18:40:00 -04:00
// Cache the PhysScene and World for use in UpdateWorldForces and CollectWorldObjects
// When these are non-null it is an indicator that we need to update the collected world objects list
2017-03-24 09:53:37 -04:00
PhysScene = World . GetPhysicsScene ( ) ;
UnsafeWorld = & World ;
2020-06-23 18:40:00 -04:00
UnsafeOwner = SKC . GetOwner ( ) ;
// A timer to track objects we haven't detected in a while
+ + ComponentsInSimTick ;
2017-02-08 17:53:41 -05:00
}
}
2019-08-02 09:01:58 -04:00
DECLARE_CYCLE_STAT ( TEXT ( " FAnimNode_RigidBody::UpdateWorldForces " ) , STAT_ImmediateUpdateWorldForces , STATGROUP_ImmediatePhysics ) ;
2017-02-08 17:53:41 -05:00
2018-04-12 16:57:51 -04:00
void FAnimNode_RigidBody : : UpdateWorldForces ( const FTransform & ComponentToWorld , const FTransform & BaseBoneTM )
2017-02-08 17:53:41 -05:00
{
SCOPE_CYCLE_COUNTER ( STAT_ImmediateUpdateWorldForces ) ;
if ( TotalMass > 0.f )
{
for ( const USkeletalMeshComponent : : FPendingRadialForces & PendingRadialForce : PendingRadialForces )
{
2018-04-12 16:57:51 -04:00
const FVector RadialForceOrigin = WorldPositionToSpace ( SimulationSpace , PendingRadialForce . Origin , ComponentToWorld , BaseBoneTM ) ;
2019-01-14 12:11:24 -05:00
for ( ImmediatePhysics : : FActorHandle * Body : Bodies )
2017-02-08 17:53:41 -05:00
{
const float InvMass = Body - > GetInverseMass ( ) ;
if ( InvMass > 0.f )
{
const float StrengthPerBody = PendingRadialForce . bIgnoreMass ? PendingRadialForce . Strength : PendingRadialForce . Strength / ( TotalMass * InvMass ) ;
2019-08-02 09:01:58 -04:00
ImmediatePhysics : : EForceType ForceType ;
2017-02-08 17:53:41 -05:00
if ( PendingRadialForce . Type = = USkeletalMeshComponent : : FPendingRadialForces : : AddImpulse )
{
2019-08-02 09:01:58 -04:00
ForceType = PendingRadialForce . bIgnoreMass ? ImmediatePhysics : : EForceType : : AddVelocity : ImmediatePhysics : : EForceType : : AddImpulse ;
2017-02-08 17:53:41 -05:00
}
else
{
2019-08-02 09:01:58 -04:00
ForceType = PendingRadialForce . bIgnoreMass ? ImmediatePhysics : : EForceType : : AddAcceleration : ImmediatePhysics : : EForceType : : AddForce ;
2017-02-08 17:53:41 -05:00
}
2017-07-26 09:23:14 -04:00
Body - > AddRadialForce ( RadialForceOrigin , StrengthPerBody , PendingRadialForce . Radius , PendingRadialForce . Falloff , ForceType ) ;
}
}
}
2017-02-08 17:53:41 -05:00
2017-07-26 09:23:14 -04:00
if ( ! ExternalForce . IsNearlyZero ( ) )
{
2018-04-12 16:57:51 -04:00
const FVector ExternalForceInSimSpace = WorldVectorToSpaceNoScale ( SimulationSpace , ExternalForce , ComponentToWorld , BaseBoneTM ) ;
2019-01-14 12:11:24 -05:00
for ( ImmediatePhysics : : FActorHandle * Body : Bodies )
2017-07-26 09:23:14 -04:00
{
const float InvMass = Body - > GetInverseMass ( ) ;
if ( InvMass > 0.f )
{
Body - > AddForce ( ExternalForceInSimSpace ) ;
2017-02-08 17:53:41 -05:00
}
}
}
}
}
2018-02-22 11:25:06 -05:00
bool FAnimNode_RigidBody : : NeedsDynamicReset ( ) const
{
return true ;
}
2018-05-23 21:04:31 -04:00
void FAnimNode_RigidBody : : ResetDynamics ( ETeleportType InTeleportType )
2018-02-22 11:25:06 -05:00
{
2018-05-23 21:04:31 -04:00
// This will be picked up next evaluate and reset our simulation.
// Teleport type can only go higher - i.e. if we have requested a reset, then a teleport will still reset fully
ResetSimulatedTeleportType = ( ( InTeleportType > ResetSimulatedTeleportType ) ? InTeleportType : ResetSimulatedTeleportType ) ;
2018-02-22 11:25:06 -05:00
}
2017-11-09 18:22:55 -05:00
DECLARE_CYCLE_STAT ( TEXT ( " RigidBody_PreUpdate " ) , STAT_RigidBody_PreUpdate , STATGROUP_Anim ) ;
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : PreUpdate ( const UAnimInstance * InAnimInstance )
2017-02-08 17:53:41 -05:00
{
2018-02-22 11:25:06 -05:00
// Don't update geometry if RBN is disabled
2019-10-15 16:08:35 -04:00
if ( ! bEnabled )
2018-02-22 11:25:06 -05:00
{
return ;
}
2017-11-09 18:22:55 -05:00
SCOPE_CYCLE_COUNTER ( STAT_RigidBody_PreUpdate ) ;
2017-02-08 17:53:41 -05:00
USkeletalMeshComponent * SKC = InAnimInstance - > GetSkelMeshComponent ( ) ;
2017-11-09 18:22:55 -05:00
APawn * PawnOwner = InAnimInstance - > TryGetPawnOwner ( ) ;
UPawnMovementComponent * MovementComp = PawnOwner ? PawnOwner - > GetMovementComponent ( ) : nullptr ;
2017-02-08 17:53:41 -05:00
2020-06-23 18:40:00 -04:00
# if WITH_EDITOR && !WITH_CHAOS
2017-07-26 09:23:14 -04:00
if ( bEnableWorldGeometry & & SimulationSpace ! = ESimulationSpace : : WorldSpace )
2017-02-08 17:53:41 -05:00
{
2017-07-26 09:23:14 -04:00
FMessageLog ( " PIE " ) . Warning ( FText : : Format ( LOCTEXT ( " WorldCollisionComponentSpace " , " Trying to use world collision without world space simulation for ''{0}''. This is not supported, please change SimulationSpace to WorldSpace " ) ,
2017-02-08 17:53:41 -05:00
FText : : FromString ( GetPathNameSafe ( SKC ) ) ) ) ;
}
# endif
2020-01-31 01:39:50 -05:00
UWorld * World = InAnimInstance - > GetWorld ( ) ;
if ( World )
2017-02-08 17:53:41 -05:00
{
2020-01-31 01:39:50 -05:00
WorldSpaceGravity = bOverrideWorldGravity ? OverrideWorldGravity : ( MovementComp ? FVector ( 0.f , 0.f , MovementComp - > GetGravityZ ( ) ) : FVector ( 0.f , 0.f , World - > GetGravityZ ( ) ) ) ;
2020-09-01 14:07:48 -04:00
2020-01-31 01:39:50 -05:00
if ( SKC )
2017-02-08 17:53:41 -05:00
{
2020-09-01 14:07:48 -04:00
// Store game time for use in parallel evaluation. This may be the totol time (inc pauses) or the time the game has been unpaused.
WorldTimeSeconds = SKC - > PrimaryComponentTick . bTickEvenWhenPaused ? World - > UnpausedTimeSeconds : World - > TimeSeconds ;
2020-06-23 18:40:00 -04:00
if ( PhysicsSimulation & & bEnableWorldGeometry )
2020-01-31 01:39:50 -05:00
{
UpdateWorldGeometry ( * World , * SKC ) ;
}
2017-02-08 17:53:41 -05:00
2020-01-31 01:39:50 -05:00
PendingRadialForces = SKC - > GetPendingRadialForces ( ) ;
2018-05-23 21:04:31 -04:00
2020-01-31 01:39:50 -05:00
PreviousTransform = CurrentTransform ;
CurrentTransform = SKC - > GetComponentToWorld ( ) ;
}
}
2017-02-08 17:53:41 -05:00
}
2018-05-23 21:04:31 -04:00
int32 FAnimNode_RigidBody : : GetLODThreshold ( ) const
{
if ( CVarRigidBodyLODThreshold . GetValueOnAnyThread ( ) ! = - 1 )
{
if ( LODThreshold ! = - 1 )
{
return FMath : : Min ( LODThreshold , CVarRigidBodyLODThreshold . GetValueOnAnyThread ( ) ) ;
}
else
{
return CVarRigidBodyLODThreshold . GetValueOnAnyThread ( ) ;
}
}
else
{
return LODThreshold ;
}
}
2017-11-09 18:22:55 -05:00
DECLARE_CYCLE_STAT ( TEXT ( " RigidBody_Update " ) , STAT_RigidBody_Update , STATGROUP_Anim ) ;
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : UpdateInternal ( const FAnimationUpdateContext & Context )
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( UpdateInternal )
2018-02-22 11:25:06 -05:00
// Avoid this work if RBN is disabled, as the results would be discarded
2019-10-15 16:08:35 -04:00
if ( ! bEnabled )
2018-02-22 11:25:06 -05:00
{
return ;
}
2019-08-02 09:01:58 -04:00
SCOPE_CYCLE_COUNTER ( STAT_RigidBody_Update ) ;
2017-11-09 18:22:55 -05:00
// Accumulate deltatime elapsed during update. To be used during evaluation.
AccumulatedDeltaTime + = Context . AnimInstanceProxy - > GetDeltaSeconds ( ) ;
2017-03-24 09:53:37 -04:00
if ( UnsafeWorld ! = nullptr )
2020-06-23 18:40:00 -04:00
{
2017-11-09 18:22:55 -05:00
// Node is valid to evaluate. Simulation is starting.
bSimulationStarted = true ;
2020-06-23 18:40:00 -04:00
}
2017-03-24 09:53:37 -04:00
2020-06-23 18:40:00 -04:00
// Remove expired objects from the sim
PurgeExpiredWorldObjects ( ) ;
// Find nearby world objects to add to the sim (gated on UnsafeWorld - see UpdateWorldGeometry)
CollectWorldObjects ( ) ;
// These get set again if our bounds change. Subsequent calls to CollectWorldObjects will early-out until then
UnsafeWorld = nullptr ;
UnsafeOwner = nullptr ;
PhysScene = nullptr ;
}
void FAnimNode_RigidBody : : CollectWorldObjects ( )
{
if ( ( UnsafeWorld ! = nullptr ) & & ( PhysScene ! = nullptr ) )
{
// @todo(ccaulfield): should this use CachedBounds?
2017-03-24 09:53:37 -04:00
TArray < FOverlapResult > Overlaps ;
2020-06-23 18:40:00 -04:00
UnsafeWorld - > OverlapMultiByChannel ( Overlaps , CachedBounds . Center , FQuat : : Identity , OverlapChannel , FCollisionShape : : MakeSphere ( CachedBounds . W ) , QueryParams , FCollisionResponseParams ( ECR_Overlap ) ) ;
2018-07-31 02:23:26 -04:00
2019-08-02 09:01:58 -04:00
// @todo(ccaulfield): is there an engine-independent way to do this?
# if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX
2018-12-12 11:25:29 -05:00
SCOPED_SCENE_READ_LOCK ( PhysScene ? PhysScene - > GetPxScene ( ) : nullptr ) ; //TODO: expose this part to the anim node
2019-08-02 09:01:58 -04:00
# endif
2017-03-24 09:53:37 -04:00
for ( const FOverlapResult & Overlap : Overlaps )
{
if ( UPrimitiveComponent * OverlapComp = Overlap . GetComponent ( ) )
{
2020-06-23 18:40:00 -04:00
FWorldObject * WorldObject = ComponentsInSim . Find ( OverlapComp ) ;
if ( WorldObject ! = nullptr )
2017-03-24 09:53:37 -04:00
{
2020-06-23 18:40:00 -04:00
// Existing object - reset its age
WorldObject - > LastSeenTick = ComponentsInSimTick ;
}
else
{
# if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX
2017-03-24 09:53:37 -04:00
ComponentsInSim . Add ( OverlapComp ) ;
2020-03-11 17:07:16 -04:00
// Not sure why this happens, adding check to fix crash in CheckRBN engine test.
if ( OverlapComp - > BodyInstance . BodySetup ! = nullptr )
{
PhysicsSimulation - > CreateActor ( ImmediatePhysics : : EActorType : : StaticActor , & OverlapComp - > BodyInstance , OverlapComp - > BodyInstance . GetUnrealWorldTransform ( ) ) ;
}
2020-06-23 18:40:00 -04:00
# elif WITH_CHAOS
// New object - add it to the sim
const bool bIsSelf = ( UnsafeOwner = = OverlapComp - > GetOwner ( ) ) ;
if ( ! bIsSelf )
{
// Create a kinematic actor. Not using Static as world-static objects may move in the simulation's frame of reference
ImmediatePhysics : : FActorHandle * ActorHandle = PhysicsSimulation - > CreateActor ( ImmediatePhysics : : EActorType : : KinematicActor , & OverlapComp - > BodyInstance , OverlapComp - > GetComponentTransform ( ) ) ;
PhysicsSimulation - > AddToCollidingPairs ( ActorHandle ) ;
ComponentsInSim . Add ( OverlapComp , FWorldObject ( ActorHandle , ComponentsInSimTick ) ) ;
}
# endif
2017-03-24 09:53:37 -04:00
}
}
}
}
}
2020-06-23 18:40:00 -04:00
// Flag invalid objects for purging
void FAnimNode_RigidBody : : ExpireWorldObjects ( )
{
# if WITH_CHAOS
// Invalidate deleted and expired world objects
TArray < const UPrimitiveComponent * > PrunedEntries ;
for ( auto & WorldEntry : ComponentsInSim )
{
const UPrimitiveComponent * WorldComp = WorldEntry . Key ;
FWorldObject & WorldObject = WorldEntry . Value ;
// Do we need to expire this object?
const int32 ExpireTickCount = RBAN_WorldObjectExpiry ;
bool bIsInvalid =
( ( ComponentsInSimTick - WorldObject . LastSeenTick ) > ExpireTickCount ) // Haven't seen this object for a while
| | ( WorldComp = = nullptr )
| | ( WorldComp - > IsPendingKill ( ) )
| | ( WorldComp - > GetBodyInstance ( ) = = nullptr )
| | ( ! WorldComp - > GetBodyInstance ( ) - > IsValidBodyInstance ( ) ) ;
// Remove from sim if necessary
if ( bIsInvalid )
{
WorldObject . bExpired = true ;
}
}
# endif
}
void FAnimNode_RigidBody : : PurgeExpiredWorldObjects ( )
{
# if WITH_CHAOS
// Destroy expired simulated objects
TArray < const UPrimitiveComponent * > PurgedEntries ;
for ( auto & WorldEntry : ComponentsInSim )
{
FWorldObject & WorldObject = WorldEntry . Value ;
if ( WorldObject . bExpired )
{
PhysicsSimulation - > DestroyActor ( WorldObject . ActorHandle ) ;
WorldObject . ActorHandle = nullptr ;
PurgedEntries . Add ( WorldEntry . Key ) ;
}
}
// Remove purged map entries
for ( const UPrimitiveComponent * PurgedEntry : PurgedEntries )
{
ComponentsInSim . Remove ( PurgedEntry ) ;
}
# endif
}
// Update the transforms of the world objects we added to the sim. This is required
// if we have a component- or bone-space simulation as even world-static objects
// will be moving in the simulation's frame of reference.
void FAnimNode_RigidBody : : UpdateWorldObjects ( const FTransform & SpaceTransform )
{
# if WITH_CHAOS
if ( SimulationSpace ! = ESimulationSpace : : WorldSpace )
{
for ( const auto & WorldEntry : ComponentsInSim )
{
const UPrimitiveComponent * OverlapComp = WorldEntry . Key ;
if ( OverlapComp ! = nullptr )
{
ImmediatePhysics : : FActorHandle * ActorHandle = WorldEntry . Value . ActorHandle ;
// Calculate the sim-space transform of this object
const FTransform CompWorldTransform = OverlapComp - > BodyInstance . GetUnrealWorldTransform ( ) ;
FTransform CompSpaceTransform ;
CompSpaceTransform . SetTranslation ( SpaceTransform . InverseTransformPosition ( CompWorldTransform . GetLocation ( ) ) ) ;
CompSpaceTransform . SetRotation ( SpaceTransform . InverseTransformRotation ( CompWorldTransform . GetRotation ( ) ) ) ;
CompSpaceTransform . SetScale3D ( FVector : : OneVector ) ; // TODO - sort out scale for world objects in local sim
// Update the sim's copy of the world object
ActorHandle - > SetWorldTransform ( CompSpaceTransform ) ;
}
}
}
# endif
}
2017-03-24 09:53:37 -04:00
void FAnimNode_RigidBody : : InitializeBoneReferences ( const FBoneContainer & RequiredBones )
2017-02-08 17:53:41 -05:00
{
2019-07-16 11:49:59 -04:00
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE ( InitializeBoneReferences )
2017-02-08 17:53:41 -05:00
/** We only need to update simulated bones and children of simulated bones*/
const int32 NumBodies = Bodies . Num ( ) ;
const TArray < FBoneIndexType > & RequiredBoneIndices = RequiredBones . GetBoneIndicesArray ( ) ;
const int32 NumRequiredBoneIndices = RequiredBoneIndices . Num ( ) ;
const FReferenceSkeleton & RefSkeleton = RequiredBones . GetReferenceSkeleton ( ) ;
2017-11-09 18:22:55 -05:00
OutputBoneData . Empty ( NumBodies ) ;
2017-02-08 17:53:41 -05:00
2017-03-24 09:53:37 -04:00
int32 NumSimulatedBodies = 0 ;
2018-04-12 16:57:51 -04:00
// if no name is entered, use root
if ( BaseBoneRef . BoneName = = NAME_None )
{
BaseBoneRef . BoneName = RefSkeleton . GetBoneName ( 0 ) ;
}
if ( BaseBoneRef . BoneName ! = NAME_None )
{
BaseBoneRef . Initialize ( RequiredBones ) ;
}
2017-07-26 09:23:14 -04:00
2019-11-08 10:00:08 -05:00
bool bHasInvalidBoneReference = false ;
for ( int32 Index = 0 ; Index < NumRequiredBoneIndices ; + + Index )
2017-02-08 17:53:41 -05:00
{
2017-11-09 18:22:55 -05:00
const FCompactPoseBoneIndex CompactPoseBoneIndex ( Index ) ;
const FBoneIndexType SkeletonBoneIndex = RequiredBones . GetSkeletonIndex ( CompactPoseBoneIndex ) ;
2019-11-08 10:00:08 -05:00
const FBoneIndexType IndexToBodyNum = SkeletonBoneIndexToBodyIndex . Num ( ) ;
// If we have a missing bone in our skeleton, we don't want to have an out of bounds access.
if ( SkeletonBoneIndex > = IndexToBodyNum )
{
bHasInvalidBoneReference = true ;
break ;
}
2017-11-09 18:22:55 -05:00
const int32 BodyIndex = SkeletonBoneIndexToBodyIndex [ SkeletonBoneIndex ] ;
2017-02-08 17:53:41 -05:00
2017-11-09 18:22:55 -05:00
if ( BodyIndex ! = INDEX_NONE )
2017-02-08 17:53:41 -05:00
{
2017-11-09 18:22:55 -05:00
//If we have a body we need to save it for later
FOutputBoneData * OutputData = new ( OutputBoneData ) FOutputBoneData ( ) ;
OutputData - > BodyIndex = BodyIndex ;
OutputData - > CompactPoseBoneIndex = CompactPoseBoneIndex ;
2017-02-08 17:53:41 -05:00
2017-11-09 18:22:55 -05:00
if ( BodyAnimData [ BodyIndex ] . bIsSimulated )
{
+ + NumSimulatedBodies ;
2017-02-08 17:53:41 -05:00
}
2017-11-09 18:22:55 -05:00
OutputData - > BoneIndicesToParentBody . Add ( CompactPoseBoneIndex ) ;
// Walk up parent chain until we find parent body.
OutputData - > ParentBodyIndex = INDEX_NONE ;
FCompactPoseBoneIndex CompactParentIndex = RequiredBones . GetParentBoneIndex ( CompactPoseBoneIndex ) ;
while ( CompactParentIndex ! = INDEX_NONE )
2017-02-08 17:53:41 -05:00
{
2017-11-09 18:22:55 -05:00
const FBoneIndexType SkeletonParentBoneIndex = RequiredBones . GetSkeletonIndex ( CompactParentIndex ) ;
2019-11-08 10:00:08 -05:00
// Must check our parent as well for a missing bone.
if ( SkeletonParentBoneIndex > = IndexToBodyNum )
{
bHasInvalidBoneReference = true ;
break ;
}
2017-11-09 18:22:55 -05:00
OutputData - > ParentBodyIndex = SkeletonBoneIndexToBodyIndex [ SkeletonParentBoneIndex ] ;
if ( OutputData - > ParentBodyIndex ! = INDEX_NONE )
{
break ;
}
2017-02-08 17:53:41 -05:00
2017-11-09 18:22:55 -05:00
OutputData - > BoneIndicesToParentBody . Add ( CompactParentIndex ) ;
CompactParentIndex = RequiredBones . GetParentBoneIndex ( CompactParentIndex ) ;
2017-02-08 17:53:41 -05:00
}
2019-11-08 10:00:08 -05:00
if ( bHasInvalidBoneReference )
{
break ;
}
2017-02-08 17:53:41 -05:00
}
}
2017-03-24 09:53:37 -04:00
2019-11-08 10:00:08 -05:00
if ( bHasInvalidBoneReference )
2017-03-24 09:53:37 -04:00
{
2019-11-08 10:00:08 -05:00
// If a bone was missing, let us know which asset it happened on, and clear our bone container to make the bad asset visible.
ensureMsgf ( false , TEXT ( " FAnimNode_RigidBody::InitializeBoneReferences: The Skeleton %s, is missing bones that SkeletalMesh %s needs. Skeleton might need to be resaved. " ) ,
* GetNameSafe ( RequiredBones . GetSkeletonAsset ( ) ) , * GetNameSafe ( RequiredBones . GetSkeletalMeshAsset ( ) ) ) ;
OutputBoneData . Empty ( ) ;
2017-03-24 09:53:37 -04:00
}
2019-11-08 10:00:08 -05:00
else
{
// New bodies potentially introduced with new LOD
// We'll have to initialize their transform.
bCheckForBodyTransformInit = true ;
2017-11-09 18:22:55 -05:00
2019-11-08 10:00:08 -05:00
if ( PhysicsSimulation )
{
PhysicsSimulation - > SetNumActiveBodies ( NumSimulatedBodies ) ;
}
// We're switching to a new LOD, this invalidates our captured poses.
CapturedFrozenPose . Empty ( ) ;
CapturedFrozenCurves . Empty ( ) ;
}
2017-02-08 17:53:41 -05:00
}
2019-12-19 23:33:50 -05:00
void FAnimNode_RigidBody : : AddImpulseAtLocation ( FVector Impulse , FVector Location , FName BoneName )
{
# if WITH_CHAOS
// Find the body. This is currently only used in the editor and will need optimizing if used in game
for ( int32 BodyIndex = 0 ; BodyIndex < Bodies . Num ( ) ; + + BodyIndex )
{
ImmediatePhysics : : FActorHandle * Body = Bodies [ BodyIndex ] ;
if ( Body - > GetName ( ) = = BoneName )
{
Body - > AddImpulseAtLocation ( Impulse , Location ) ;
}
}
# endif
}
2017-06-21 10:25:35 -04:00
void FAnimNode_RigidBody : : OnInitializeAnimInstance ( const FAnimInstanceProxy * InProxy , const UAnimInstance * InAnimInstance )
2017-02-08 17:53:41 -05:00
{
2017-06-21 10:25:35 -04:00
InitPhysics ( InAnimInstance ) ;
2017-02-08 17:53:41 -05:00
}
2018-11-14 19:05:13 -05:00
# if WITH_EDITORONLY_DATA
2017-07-26 09:23:14 -04:00
void FAnimNode_RigidBody : : PostSerialize ( const FArchive & Ar )
{
if ( bComponentSpaceSimulation_DEPRECATED = = false )
{
//If this is not the default value it means we have old content where we were simulating in world space
SimulationSpace = ESimulationSpace : : WorldSpace ;
bComponentSpaceSimulation_DEPRECATED = true ;
}
}
2018-11-14 19:05:13 -05:00
# endif
2017-07-26 09:23:14 -04:00
2019-02-19 14:41:41 -05:00
bool FAnimNode_RigidBody : : IsValidToEvaluate ( const USkeleton * Skeleton , const FBoneContainer & RequiredBones )
{
return BaseBoneRef . IsValidToEvaluate ( RequiredBones ) ;
}
2019-01-08 11:38:48 -05:00
# undef LOCTEXT_NAMESPACE