2020-01-06 12:54:08 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ClothingSimulation.h"
# include "Components/SkeletalMeshComponent.h"
2022-10-26 12:57:32 -04:00
# include "Engine/SkeletalMesh.h"
2020-01-06 12:54:08 -05:00
# include "PhysicsEngine/PhysicsSettings.h"
//==============================================================================
// FClothingSimulationContextCommon
//==============================================================================
2020-10-29 13:38:15 -04:00
DEFINE_STAT ( STAT_ClothComputeNormals ) ;
DEFINE_STAT ( STAT_ClothInternalSolve ) ;
DEFINE_STAT ( STAT_ClothUpdateCollisions ) ;
DEFINE_STAT ( STAT_ClothSkinPhysMesh ) ;
DEFINE_STAT ( STAT_ClothFillContext ) ;
2020-01-06 12:54:08 -05:00
2020-01-10 15:54:40 -05:00
static TAutoConsoleVariable < float > GClothMaxDeltaTimeTeleportMultiplier (
TEXT ( " p.Cloth.MaxDeltaTimeTeleportMultiplier " ) ,
1.5f ,
TEXT ( " A multiplier of the MaxPhysicsDelta time at which we will automatically just teleport cloth to its new location \n " )
TEXT ( " default: 1.5 " ) ) ;
2023-09-29 12:37:14 -04:00
static TAutoConsoleVariable < float > GClothMaxVelocityScale (
TEXT ( " p.Cloth.MaxVelocityScale " ) ,
1.f ,
TEXT ( " The maximum amount of the component induced velocity allowed on all cloths. \n " )
TEXT ( " Use 1.0 for fully induced velocity(default), or use 0.0 for no induced velocity, and any other values in between for a reduced induced velocity. \n " )
TEXT ( " When set to 0.0, it also provides a way to force the clothing to simulate in local space. \n " )
TEXT ( " default: 1.0 " ) ) ;
2023-02-20 13:00:25 -05:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS // CachedPositions, CachedVelocities
2020-01-06 12:54:08 -05:00
FClothingSimulationContextCommon : : FClothingSimulationContextCommon ( )
: ComponentToWorld ( FTransform : : Identity )
, WorldGravity ( FVector : : ZeroVector )
, WindVelocity ( FVector : : ZeroVector )
2020-10-29 13:38:15 -04:00
, WindAdaption ( 0.f )
2020-01-06 12:54:08 -05:00
, DeltaSeconds ( 0.f )
2022-09-30 11:41:20 -04:00
, VelocityScale ( 1.f )
2020-01-10 15:54:40 -05:00
, TeleportMode ( EClothingTeleportMode : : None )
2020-03-11 14:37:58 -04:00
, MaxDistanceScale ( 1.f )
2020-08-14 06:18:22 -04:00
, PredictedLod ( INDEX_NONE )
2020-01-06 12:54:08 -05:00
{ }
2023-02-20 13:00:25 -05:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2020-01-06 12:54:08 -05:00
2023-02-20 13:00:25 -05:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS // CachedPositions, CachedVelocities
2020-01-06 12:54:08 -05:00
FClothingSimulationContextCommon : : ~ FClothingSimulationContextCommon ( )
{ }
2023-02-20 13:00:25 -05:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2020-01-06 12:54:08 -05:00
2021-06-07 20:09:45 -04:00
void FClothingSimulationContextCommon : : Fill ( const USkeletalMeshComponent * InComponent , float InDeltaSeconds , float InMaxPhysicsDelta , bool bIsInitialization )
2020-01-06 12:54:08 -05:00
{
2020-10-29 13:38:15 -04:00
SCOPE_CYCLE_COUNTER ( STAT_ClothFillContext ) ;
LLM_SCOPE ( ELLMTag : : SkeletalMesh ) ;
2020-01-06 12:54:08 -05:00
check ( InComponent ) ;
FillBoneTransforms ( InComponent ) ;
2021-06-07 20:09:45 -04:00
FillRefToLocals ( InComponent , bIsInitialization ) ;
2020-01-06 12:54:08 -05:00
FillComponentToWorld ( InComponent ) ;
FillWorldGravity ( InComponent ) ;
FillWindVelocity ( InComponent ) ;
FillDeltaSeconds ( InDeltaSeconds , InMaxPhysicsDelta ) ;
2020-01-10 15:54:40 -05:00
FillTeleportMode ( InComponent , InDeltaSeconds , InMaxPhysicsDelta ) ;
2020-03-11 12:14:43 -04:00
FillMaxDistanceScale ( InComponent ) ;
2024-08-27 16:25:50 -04:00
FillSolverGeometryScale ( InComponent ) ;
2020-08-14 06:18:22 -04:00
2021-03-11 10:57:49 -04:00
PredictedLod = InComponent - > GetPredictedLODLevel ( ) ;
2020-01-06 12:54:08 -05:00
}
void FClothingSimulationContextCommon : : FillBoneTransforms ( const USkeletalMeshComponent * InComponent )
{
2022-08-15 09:26:50 -04:00
const USkeletalMesh * const SkeletalMesh = InComponent - > GetSkeletalMeshAsset ( ) ;
2020-01-06 12:54:08 -05:00
2022-07-14 03:55:26 -04:00
if ( USkinnedMeshComponent * const LeaderComponent = InComponent - > LeaderPoseComponent . Get ( ) )
2020-01-06 12:54:08 -05:00
{
2022-07-14 03:55:26 -04:00
const TArray < int32 > & LeaderBoneMap = InComponent - > GetLeaderBoneMap ( ) ;
int32 NumBones = LeaderBoneMap . Num ( ) ;
2020-01-06 12:54:08 -05:00
if ( NumBones = = 0 )
{
if ( SkeletalMesh )
{
2022-07-14 03:55:26 -04:00
// This case indicates an invalid leader pose component (e.g. no skeletal mesh)
2020-11-25 11:17:08 -04:00
NumBones = SkeletalMesh - > GetRefSkeleton ( ) . GetNum ( ) ;
2020-01-06 12:54:08 -05:00
BoneTransforms . Empty ( NumBones ) ;
BoneTransforms . AddDefaulted ( NumBones ) ;
}
}
else
{
BoneTransforms . Reset ( NumBones ) ;
BoneTransforms . AddDefaulted ( NumBones ) ;
2022-07-14 03:55:26 -04:00
const TArray < FTransform > & LeaderTransforms = LeaderComponent - > GetComponentSpaceTransforms ( ) ;
2020-01-06 12:54:08 -05:00
for ( int32 BoneIndex = 0 ; BoneIndex < NumBones ; + + BoneIndex )
{
2022-07-14 03:55:26 -04:00
bool bFoundLeader = false ;
if ( LeaderBoneMap . IsValidIndex ( BoneIndex ) )
2020-01-06 12:54:08 -05:00
{
2022-07-14 03:55:26 -04:00
const int32 LeaderIndex = LeaderBoneMap [ BoneIndex ] ;
if ( LeaderIndex ! = INDEX_NONE & & LeaderIndex < LeaderTransforms . Num ( ) )
2020-01-06 12:54:08 -05:00
{
2022-07-14 03:55:26 -04:00
BoneTransforms [ BoneIndex ] = LeaderTransforms [ LeaderIndex ] ;
bFoundLeader = true ;
2020-01-06 12:54:08 -05:00
}
}
2022-07-14 03:55:26 -04:00
if ( ! bFoundLeader & & SkeletalMesh )
2020-01-06 12:54:08 -05:00
{
2020-11-25 11:17:08 -04:00
const int32 ParentIndex = SkeletalMesh - > GetRefSkeleton ( ) . GetParentIndex ( BoneIndex ) ;
2020-01-06 12:54:08 -05:00
BoneTransforms [ BoneIndex ] =
BoneTransforms . IsValidIndex ( ParentIndex ) & & ParentIndex < BoneIndex ?
2020-11-25 11:17:08 -04:00
BoneTransforms [ ParentIndex ] * SkeletalMesh - > GetRefSkeleton ( ) . GetRefBonePose ( ) [ BoneIndex ] :
SkeletalMesh - > GetRefSkeleton ( ) . GetRefBonePose ( ) [ BoneIndex ] ;
2020-01-06 12:54:08 -05:00
}
}
}
}
else
{
BoneTransforms = InComponent - > GetComponentSpaceTransforms ( ) ;
}
}
2021-06-07 20:09:45 -04:00
void FClothingSimulationContextCommon : : FillRefToLocals ( const USkeletalMeshComponent * InComponent , bool bIsInitialization )
2020-01-06 12:54:08 -05:00
{
RefToLocals . Reset ( ) ;
2021-06-07 20:09:45 -04:00
// Constraints are initialized using bone distances upon initialization, so fill out reference pose
if ( bIsInitialization )
{
2022-08-15 09:26:50 -04:00
const USkeletalMesh * const SkeletalMesh = InComponent - > GetSkeletalMeshAsset ( ) ;
2021-06-07 20:09:45 -04:00
if ( SkeletalMesh )
{
const int32 NumBones = SkeletalMesh - > GetRefSkeleton ( ) . GetNum ( ) ;
RefToLocals . AddUninitialized ( NumBones ) ;
for ( int32 BoneIndex = 0 ; BoneIndex < NumBones ; + + BoneIndex )
{
2022-01-27 06:25:37 -05:00
RefToLocals [ BoneIndex ] = FMatrix44f : : Identity ;
2021-06-07 20:09:45 -04:00
}
}
return ;
}
2021-03-11 10:57:49 -04:00
InComponent - > GetCurrentRefToLocalMatrices ( RefToLocals , InComponent - > GetPredictedLODLevel ( ) ) ;
2020-01-06 12:54:08 -05:00
}
void FClothingSimulationContextCommon : : FillComponentToWorld ( const USkeletalMeshComponent * InComponent )
{
ComponentToWorld = InComponent - > GetComponentTransform ( ) ;
}
void FClothingSimulationContextCommon : : FillWorldGravity ( const USkeletalMeshComponent * InComponent )
{
const UWorld * const ComponentWorld = InComponent - > GetWorld ( ) ;
check ( ComponentWorld ) ;
WorldGravity = FVector ( 0.f , 0.f , ComponentWorld - > GetGravityZ ( ) ) ;
}
void FClothingSimulationContextCommon : : FillWindVelocity ( const USkeletalMeshComponent * InComponent )
{
2020-09-25 13:44:05 -04:00
InComponent - > GetWindForCloth_GameThread ( WindVelocity , WindAdaption ) ;
2020-01-06 12:54:08 -05:00
}
void FClothingSimulationContextCommon : : FillDeltaSeconds ( float InDeltaSeconds , float InMaxPhysicsDelta )
{
DeltaSeconds = FMath : : Min ( InDeltaSeconds , InMaxPhysicsDelta ) ;
}
2020-01-10 15:54:40 -05:00
void FClothingSimulationContextCommon : : FillTeleportMode ( const USkeletalMeshComponent * InComponent , float InDeltaSeconds , float InMaxPhysicsDelta )
{
2024-09-10 10:26:02 -04:00
TeleportMode = ( InComponent - > ClothTeleportMode < EClothingTeleportMode : : Teleport & & ( InDeltaSeconds > InMaxPhysicsDelta * GClothMaxDeltaTimeTeleportMultiplier . GetValueOnGameThread ( ) ) ) ?
2020-01-10 15:54:40 -05:00
EClothingTeleportMode : : Teleport :
InComponent - > ClothTeleportMode ;
2022-09-30 11:41:20 -04:00
2023-09-29 12:37:14 -04:00
const float MaxVelocityScale = FMath : : Clamp ( GClothMaxVelocityScale . GetValueOnGameThread ( ) , 0.f , 1.f ) ;
const float ComponentVelocityScale = InComponent - > ClothVelocityScale ;
2022-10-20 06:10:03 -04:00
VelocityScale = ( TeleportMode = = EClothingTeleportMode : : None & & InDeltaSeconds > 0.f ) ?
2023-09-29 12:37:14 -04:00
FMath : : Clamp ( ComponentVelocityScale , 0.f , MaxVelocityScale ) * FMath : : Min ( InDeltaSeconds , InMaxPhysicsDelta ) / InDeltaSeconds : 0.f ;
2020-01-10 15:54:40 -05:00
}
2020-09-25 13:44:05 -04:00
void FClothingSimulationContextCommon : : FillMaxDistanceScale ( const USkeletalMeshComponent * InComponent )
2020-01-06 12:54:08 -05:00
{
2020-09-25 13:44:05 -04:00
MaxDistanceScale = InComponent - > GetClothMaxDistanceScale ( ) ;
2020-03-11 12:14:43 -04:00
}
2024-08-27 16:25:50 -04:00
void FClothingSimulationContextCommon : : FillSolverGeometryScale ( const USkeletalMeshComponent * InComponent )
{
SolverGeometryScale = InComponent - > ClothGeometryScale ;
}
2020-01-06 12:54:08 -05:00
//==============================================================================
// FClothingSimulationCommon
//==============================================================================
FClothingSimulationCommon : : FClothingSimulationCommon ( )
{
MaxPhysicsDelta = UPhysicsSettings : : Get ( ) - > MaxPhysicsDeltaTime ;
}
FClothingSimulationCommon : : ~ FClothingSimulationCommon ( )
{ }
void FClothingSimulationCommon : : FillContext ( USkeletalMeshComponent * InComponent , float InDeltaTime , IClothingSimulationContext * InOutContext )
2021-06-07 20:09:45 -04:00
{
// Deprecated version always fills RefToLocals with current animation results instead of using reference pose on initialization
const bool bIsInitialization = false ;
FillContext ( InComponent , InDeltaTime , InOutContext , bIsInitialization ) ;
}
void FClothingSimulationCommon : : FillContext ( USkeletalMeshComponent * InComponent , float InDeltaTime , IClothingSimulationContext * InOutContext , bool bIsInitialization )
2020-01-06 12:54:08 -05:00
{
check ( InOutContext ) ;
FClothingSimulationContextCommon * const Context = static_cast < FClothingSimulationContextCommon * > ( InOutContext ) ;
2021-06-07 20:09:45 -04:00
Context - > Fill ( InComponent , InDeltaTime , MaxPhysicsDelta , bIsInitialization ) ;
2020-01-06 12:54:08 -05:00
// Checking the component here to track rare issue leading to invalid contexts
2021-09-06 12:23:53 -04:00
if ( ! IsValid ( InComponent ) )
2020-01-06 12:54:08 -05:00
{
const AActor * const CompOwner = InComponent - > GetOwner ( ) ;
UE_LOG ( LogSkeletalMesh , Warning ,
TEXT ( " Attempting to fill a clothing simulation context for a PendingKill skeletal mesh component (Comp: %s, Actor: %s). "
" Pending kill skeletal mesh components should be unregistered before marked pending kill. " ) ,
* InComponent - > GetName ( ) , CompOwner ? * CompOwner - > GetName ( ) : TEXT ( " None " ) ) ;
// Make sure we clear this out to skip any attempted simulations
Context - > BoneTransforms . Reset ( ) ;
}
if ( Context - > BoneTransforms . Num ( ) = = 0 )
{
const AActor * const CompOwner = InComponent - > GetOwner ( ) ;
2022-07-14 03:55:26 -04:00
const USkinnedMeshComponent * const Leader = InComponent - > LeaderPoseComponent . Get ( ) ;
UE_LOG ( LogSkeletalMesh , Warning , TEXT ( " Attempting to fill a clothing simulation context for a skeletal mesh component that has zero bones (Comp: %s, Leader: %s, Actor: %s). " ) , * InComponent - > GetName ( ) , Leader ? * Leader - > GetName ( ) : TEXT ( " None " ) , CompOwner ? * CompOwner - > GetName ( ) : TEXT ( " None " ) ) ;
2020-01-06 12:54:08 -05:00
// Make sure we clear this out to skip any attempted simulations
Context - > BoneTransforms . Reset ( ) ;
}
}