You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb kriss.gossart #preflight 62aafc9ada0af39a4783930a [CL 20686007 by Josie Yang in ue5-main branch]
224 lines
8.2 KiB
C++
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();
|
|
}
|
|
}
|