Files
UnrealEngineUWP/Engine/Source/Runtime/ClothingSystemRuntimeCommon/Private/ClothingSimulation.cpp
Josie Yang 6b15506e58 Replace direct access to SkeletalMesh object from USkinnedMeshComponent with GetSkeletalMesh function
#rb kriss.gossart
#preflight 62aafc9ada0af39a4783930a

[CL 20686007 by Josie Yang in ue5-main branch]
2022-06-16 09:14:04 -04:00

224 lines
8.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ClothingSimulation.h"
#include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/PhysicsSettings.h"
//==============================================================================
// FClothingSimulationContextCommon
//==============================================================================
DEFINE_STAT(STAT_ClothComputeNormals);
DEFINE_STAT(STAT_ClothInternalSolve);
DEFINE_STAT(STAT_ClothUpdateCollisions);
DEFINE_STAT(STAT_ClothSkinPhysMesh);
DEFINE_STAT(STAT_ClothFillContext);
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"));
FClothingSimulationContextCommon::FClothingSimulationContextCommon()
: ComponentToWorld(FTransform::Identity)
, WorldGravity(FVector::ZeroVector)
, WindVelocity(FVector::ZeroVector)
, WindAdaption(0.f)
, DeltaSeconds(0.f)
, TeleportMode(EClothingTeleportMode::None)
, MaxDistanceScale(1.f)
, PredictedLod(INDEX_NONE)
{}
FClothingSimulationContextCommon::~FClothingSimulationContextCommon()
{}
void FClothingSimulationContextCommon::Fill(const USkeletalMeshComponent* InComponent, float InDeltaSeconds, float InMaxPhysicsDelta)
{
// Deprecated version always fills RefToLocals with current animation results instead of using reference pose on initialization
const bool bIsInitialization = false;
Fill(InComponent, InDeltaSeconds, InMaxPhysicsDelta, bIsInitialization);
}
void FClothingSimulationContextCommon::Fill(const USkeletalMeshComponent* InComponent, float InDeltaSeconds, float InMaxPhysicsDelta, bool bIsInitialization)
{
SCOPE_CYCLE_COUNTER(STAT_ClothFillContext);
LLM_SCOPE(ELLMTag::SkeletalMesh);
check(InComponent);
FillBoneTransforms(InComponent);
FillRefToLocals(InComponent, bIsInitialization);
FillComponentToWorld(InComponent);
FillWorldGravity(InComponent);
FillWindVelocity(InComponent);
FillDeltaSeconds(InDeltaSeconds, InMaxPhysicsDelta);
FillTeleportMode(InComponent, InDeltaSeconds, InMaxPhysicsDelta);
FillMaxDistanceScale(InComponent);
PredictedLod = InComponent->GetPredictedLODLevel();
}
void FClothingSimulationContextCommon::FillBoneTransforms(const USkeletalMeshComponent* InComponent)
{
const USkeletalMesh* const SkeletalMesh = InComponent->GetSkeletalMesh();
if (USkinnedMeshComponent* const MasterComponent = InComponent->MasterPoseComponent.Get())
{
const TArray<int32>& MasterBoneMap = InComponent->GetMasterBoneMap();
int32 NumBones = MasterBoneMap.Num();
if (NumBones == 0)
{
if (SkeletalMesh)
{
// This case indicates an invalid master pose component (e.g. no skeletal mesh)
NumBones = SkeletalMesh->GetRefSkeleton().GetNum();
BoneTransforms.Empty(NumBones);
BoneTransforms.AddDefaulted(NumBones);
}
}
else
{
BoneTransforms.Reset(NumBones);
BoneTransforms.AddDefaulted(NumBones);
const TArray<FTransform>& MasterTransforms = MasterComponent->GetComponentSpaceTransforms();
for (int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex)
{
bool bFoundMaster = false;
if (MasterBoneMap.IsValidIndex(BoneIndex))
{
const int32 MasterIndex = MasterBoneMap[BoneIndex];
if (MasterIndex != INDEX_NONE && MasterIndex < MasterTransforms.Num())
{
BoneTransforms[BoneIndex] = MasterTransforms[MasterIndex];
bFoundMaster = true;
}
}
if (!bFoundMaster && SkeletalMesh)
{
const int32 ParentIndex = SkeletalMesh->GetRefSkeleton().GetParentIndex(BoneIndex);
BoneTransforms[BoneIndex] =
BoneTransforms.IsValidIndex(ParentIndex) && ParentIndex < BoneIndex ?
BoneTransforms[ParentIndex] * SkeletalMesh->GetRefSkeleton().GetRefBonePose()[BoneIndex] :
SkeletalMesh->GetRefSkeleton().GetRefBonePose()[BoneIndex];
}
}
}
}
else
{
BoneTransforms = InComponent->GetComponentSpaceTransforms();
}
}
void FClothingSimulationContextCommon::FillRefToLocals(const USkeletalMeshComponent* InComponent, bool bIsInitialization)
{
RefToLocals.Reset();
// Constraints are initialized using bone distances upon initialization, so fill out reference pose
if (bIsInitialization)
{
const USkeletalMesh* const SkeletalMesh = InComponent->GetSkeletalMesh();
if (SkeletalMesh)
{
const int32 NumBones = SkeletalMesh->GetRefSkeleton().GetNum();
RefToLocals.AddUninitialized(NumBones);
for (int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex)
{
RefToLocals[BoneIndex] = FMatrix44f::Identity;
}
}
return;
}
InComponent->GetCurrentRefToLocalMatrices(RefToLocals, InComponent->GetPredictedLODLevel());
}
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)
{
InComponent->GetWindForCloth_GameThread(WindVelocity, WindAdaption);
}
void FClothingSimulationContextCommon::FillDeltaSeconds(float InDeltaSeconds, float InMaxPhysicsDelta)
{
DeltaSeconds = FMath::Min(InDeltaSeconds, InMaxPhysicsDelta);
}
void FClothingSimulationContextCommon::FillTeleportMode(const USkeletalMeshComponent* InComponent, float InDeltaSeconds, float InMaxPhysicsDelta)
{
TeleportMode = (InDeltaSeconds > InMaxPhysicsDelta * GClothMaxDeltaTimeTeleportMultiplier.GetValueOnGameThread()) ?
EClothingTeleportMode::Teleport :
InComponent->ClothTeleportMode;
}
void FClothingSimulationContextCommon::FillMaxDistanceScale(const USkeletalMeshComponent* InComponent)
{
MaxDistanceScale = InComponent->GetClothMaxDistanceScale();
}
//==============================================================================
// FClothingSimulationCommon
//==============================================================================
FClothingSimulationCommon::FClothingSimulationCommon()
{
MaxPhysicsDelta = UPhysicsSettings::Get()->MaxPhysicsDeltaTime;
}
FClothingSimulationCommon::~FClothingSimulationCommon()
{}
void FClothingSimulationCommon::FillContext(USkeletalMeshComponent* InComponent, float InDeltaTime, IClothingSimulationContext* InOutContext)
{
// 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)
{
check(InOutContext);
FClothingSimulationContextCommon* const Context = static_cast<FClothingSimulationContextCommon*>(InOutContext);
Context->Fill(InComponent, InDeltaTime, MaxPhysicsDelta, bIsInitialization);
// Checking the component here to track rare issue leading to invalid contexts
if (!IsValid(InComponent))
{
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();
const USkinnedMeshComponent* const Master = InComponent->MasterPoseComponent.Get();
UE_LOG(LogSkeletalMesh, Warning, TEXT("Attempting to fill a clothing simulation context for a skeletal mesh component that has zero bones (Comp: %s, Master: %s, Actor: %s)."), *InComponent->GetName(), Master ? *Master->GetName() : TEXT("None"), CompOwner ? *CompOwner->GetName() : TEXT("None"));
// Make sure we clear this out to skip any attempted simulations
Context->BoneTransforms.Reset();
}
}