Files
UnrealEngine/Engine/Source/Runtime/Experimental/Chaos/Public/PhysicsProxy/GeometryCollectionPhysicsProxy.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

853 lines
36 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollectionProxyData.h"
#include "Chaos/Framework/PhysicsProxy.h"
#include "GeometryCollection/ManagedArray.h"
#include "GeometryCollection/GeometryCollectionCollisionStructureManager.h"
#include "Chaos/CollisionFilterData.h"
#include "Chaos/Framework/BufferedData.h"
#include "Chaos/GeometryParticlesfwd.h"
#include "Chaos/ParticleHandle.h"
#include "Chaos/ParticleHandleFwd.h"
#include "Chaos/PhysicsObject.h"
#include "Containers/Array.h"
#include "PBDRigidsSolver.h"
#include "Chaos/Defines.h"
#include "Chaos/GeometryParticlesfwd.h"
namespace Chaos
{
template <typename T> class TSerializablePtr;
class FErrorReporter;
struct FClusterCreationParameters;
struct FDirtyGeometryCollectionData;
class FPBDCollisionConstraints;
class FPBDRigidsEvolutionBase;
}
/**
* index abstraction for HitResults
* this allows regular bones and internal clusters to be represented by a unique int32 index that can be passed to HitResults
* and in return understood by the geometry collection physics proxy
*/
struct FGeometryCollectionItemIndex
{
public:
static FGeometryCollectionItemIndex CreateInternalClusterItemIndex(int32 ClusterUniqueIdx)
{
return FGeometryCollectionItemIndex(ClusterUniqueIdx, true);
}
static FGeometryCollectionItemIndex CreateTransformItemIndex(int32 TransformIndex)
{
return FGeometryCollectionItemIndex(TransformIndex, false);
}
static FGeometryCollectionItemIndex CreateFromExistingItemIndex(int32 ItemIndex)
{
FGeometryCollectionItemIndex Result(INDEX_NONE, false);
Result.ItemIndex = ItemIndex;
return Result;
}
static FGeometryCollectionItemIndex CreateInvalidItemIndex()
{
return FGeometryCollectionItemIndex(INDEX_NONE, false);
}
public:
FGeometryCollectionItemIndex(const FGeometryCollectionItemIndex& Other)
: ItemIndex(Other.ItemIndex)
{}
bool IsInternalCluster() const
{
return ItemIndex < INDEX_NONE;
}
int32 GetInternalClusterIndex() const
{
check(IsInternalCluster());
return (ItemIndex - InternalClusterBaseIndex);
}
int32 GetTransformIndex() const
{
check(!IsInternalCluster());
return ItemIndex;
}
bool IsValid() const
{
return ItemIndex != INDEX_NONE;
}
int32 GetItemIndex() const
{
return ItemIndex;
}
bool operator==(const FGeometryCollectionItemIndex& Other) const
{
return ItemIndex == Other.ItemIndex;
}
private:
static const int32 InternalClusterBaseIndex = TNumericLimits<int32>::Lowest();
FGeometryCollectionItemIndex(int32 Index, bool bInternalCluster)
: ItemIndex(INDEX_NONE)
{
if (Index > INDEX_NONE)
{
ItemIndex = Index + (bInternalCluster? InternalClusterBaseIndex: 0);
}
}
int32 ItemIndex;
};
class FStubGeometryCollectionData : public Chaos::FParticleData
{
public:
typedef Chaos::FParticleData Base;
FStubGeometryCollectionData(const FGeometryCollectionResults* DataIn=nullptr)
: Base(Chaos::EParticleType::GeometryCollection)
, Data(DataIn)
{}
void Reset()
{
Base::Reset(); // Sets Type to EParticleType::Static
}
const FGeometryCollectionResults* GetStateData() const { return Data; }
private:
const FGeometryCollectionResults* Data;
};
/**
* Class to manage sharing data between the game thread and the simulation thread
* (which may not be different than the game thread) for a \c FGeometryDynamicCollection.
*/
class FGeometryCollectionPhysicsProxy : public TPhysicsProxy<FGeometryCollectionPhysicsProxy, FStubGeometryCollectionData, FGeometryCollectionProxyTimestamp>
{
public:
typedef TPhysicsProxy<FGeometryCollectionPhysicsProxy, FStubGeometryCollectionData, FGeometryCollectionProxyTimestamp> Base;
typedef FCollisionStructureManager::FSimplicial FSimplicial;
typedef Chaos::TPBDRigidParticle<Chaos::FReal, 3> FParticle;
typedef Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3> FParticleHandle;
typedef Chaos::TPBDRigidClusteredParticleHandle<Chaos::FReal, 3> FClusterHandle;
/** Proxy publics */
using IPhysicsProxyBase::GetSolver;
FGeometryCollectionPhysicsProxy() = delete;
/**
* \p InOwner
* \p InDynamicCollection game thread owned geometry collection.
* \p InInitFunc callback invoked from \c Initialize().
* \p InCacheSyncFunc callback invoked from \c PullFromPhysicsState().
* \p InFinalSyncFunc callback invoked from \c SyncBeforeDestory().
*/
UE_DEPRECATED(5.6, "Use the constructor that use a shared ref for the game thread collection")
CHAOS_API FGeometryCollectionPhysicsProxy(
UObject* InOwner,
FGeometryDynamicCollection& GameThreadCollection,
const FSimulationParameters& SimulationParameters,
FCollisionFilterData InSimFilter,
FCollisionFilterData InQueryFilter,
FGuid InCollectorGuid = FGuid::NewGuid(),
const Chaos::EMultiBufferMode BufferMode=Chaos::EMultiBufferMode::TripleGuarded);
CHAOS_API FGeometryCollectionPhysicsProxy(
UObject* InOwner,
TSharedRef<FGeometryDynamicCollection> GameThreadCollection,
const FSimulationParameters& SimulationParameters,
FCollisionFilterData InSimFilter,
FCollisionFilterData InQueryFilter,
FGuid InCollectorGuid = FGuid::NewGuid(),
const Chaos::EMultiBufferMode BufferMode = Chaos::EMultiBufferMode::TripleGuarded);
CHAOS_API virtual ~FGeometryCollectionPhysicsProxy();
/**
* Construct \c PTDynamicCollection, copying attributes from the game thread,
* and prepare for simulation.
*/
CHAOS_API void Initialize(Chaos::FPBDRigidsEvolutionBase* Evolution);
void Reset() { }
bool IsInitializedOnPhysicsThread() const { return bIsInitializedOnPhysicsThread; }
/**
* Finish initialization on the physics thread.
*
* Called by solver command registered by \c FPBDRigidsSolver::RegisterObject().
*/
CHAOS_API void InitializeBodiesPT(
Chaos::FPBDRigidsSolver* RigidsSolver,
typename Chaos::FPBDRigidsSolver::FParticlesType& Particles);
/** */
static CHAOS_API void InitializeDynamicCollection(FGeometryDynamicCollection& DynamicCollection, const FGeometryCollection& RestCollection, const FSimulationParameters& Params);
/** */
bool IsSimulating() const { return Parameters.Simulating; }
/**
* Pushes current game thread particle state into the \c GameToPhysInterchange.
*
* Redirects to \c BufferGameState(), and returns nullptr as this class manages
* data transport to the physics thread itself, without allocating memory.
*/
Chaos::FParticleData* NewData() { BufferGameState(); return nullptr; }
CHAOS_API void BufferGameState();
/** Called at the end of \c FPBDRigidsSolver::PushPhysicsStateExec(). */
void ClearAccumulatedData() {}
/** Push PT state into the \c PhysToGameInterchange. */
CHAOS_API void BufferPhysicsResults_Internal(Chaos::FPBDRigidsSolver* CurrentSolver, Chaos::FDirtyGeometryCollectionData& BufferData);
/** Push GT state into the \c PhysToGameInterchange for async physics */
CHAOS_API void BufferPhysicsResults_External(Chaos::FDirtyGeometryCollectionData& BufferData);
/** Push data from the game thread to the physics thread */
CHAOS_API void PushStateOnGameThread(Chaos::FPBDRigidsSolver* InSolver);
/** apply the state changes on the physics thread */
CHAOS_API void PushToPhysicsState();
/** Does nothing as \c BufferPhysicsResults() already did this. */
CHAOS_API void FlipBuffer();
/**
* Pulls data out of the PhysToGameInterchange and updates \c GTDynamicCollection.
* Called from FPhysScene_ChaosInterface::SyncBodies(), NOT the solver.
*/
CHAOS_API bool PullFromPhysicsState(const Chaos::FDirtyGeometryCollectionData& BufferData, const int32 SolverSyncTimestamp, const Chaos::FDirtyGeometryCollectionData* NextPullData = nullptr, const Chaos::FRealSingle* Alpha = nullptr, const Chaos::FDirtyRigidParticleReplicationErrorData* Error = nullptr, const Chaos::FReal AsyncFixedTimeStep = 0);
bool IsDirty() { return false; }
static constexpr EPhysicsProxyType ConcreteType() { return EPhysicsProxyType::GeometryCollectionType; }
CHAOS_API void SyncBeforeDestroy();
CHAOS_API void OnRemoveFromSolver(Chaos::FPBDRigidsSolver *RBDSolver);
CHAOS_API void OnRemoveFromScene();
CHAOS_API void OnUnregisteredFromSolver();
void SetCollisionParticlesPerObjectFraction(float CollisionParticlesPerObjectFractionIn)
{CollisionParticlesPerObjectFraction = CollisionParticlesPerObjectFractionIn;}
UE_DEPRECATED(5.4, "Use GetSolverClusterHandle_Internal instead")
TArray<FClusterHandle*>& GetSolverClusterHandles() { return SolverClusterHandles; }
UE_DEPRECATED(5.4, "Use GetParticle_Internal instead")
TArray<FClusterHandle*>& GetSolverParticleHandles() { return SolverParticleHandles; }
FClusterHandle* GetSolverClusterHandle_Internal(int32 Index) const
{
const int32 ParticleIndex = FromTransformToParticleIndex[Index];
if (ParticleIndex != INDEX_NONE)
{
return SolverClusterHandles[ParticleIndex];
}
return nullptr;
}
const FGeometryCollectionResults* GetConsumerResultsGT() const
{ return PhysToGameInterchange.PeekConsumerBuffer(); }
/** Enqueue a field \p Command to be processed by \c ProcessCommands() or \c FieldForcesUpdateCallback(). Game thread only */
CHAOS_API void BufferFieldCommand_External(FFieldSystemCommand&& Command);
UE_DEPRECATED(5.5, "Use BufferFieldCommand_Internal instead when calling on the physics thread or the _external version when calling on the gamethread")
CHAOS_API void BufferCommand(Chaos::FPBDRigidsSolver* RigidsSolver, const FFieldSystemCommand& Command);
/** Enqueue a field \p Command to be processed by \c ProcessCommands() or \c FieldForcesUpdateCallback(). Physics thread only*/
CHAOS_API void BufferFieldCommand_Internal(Chaos::FPBDRigidsSolver* RigidsSolver, const FFieldSystemCommand& Command);
CHAOS_API void FieldForcesUpdateCallback(Chaos::FPBDRigidsSolver* RigidSolver);
CHAOS_API void FieldParameterUpdateCallback(Chaos::FPBDRigidsSolver* RigidSolver, const bool bUpdateViews = true);
static CHAOS_API bool NeedToInitializeSharedCollisionStructures(const FGeometryCollection& RestCollection);
static CHAOS_API void InitializeSharedCollisionStructures(Chaos::FErrorReporter& ErrorReporter, FGeometryCollection& RestCollection, const FSharedSimulationParameters& SharedParams);
void UpdateKinematicBodiesCallback(const FParticlesType& InParticles, const float InDt, const float InTime, FKinematicProxy& InKinematicProxy) {}
void StartFrameCallback(const float InDt, const float InTime) {}
void EndFrameCallback(const float InDt) {}
void BindParticleCallbackMapping(Chaos::TArrayCollectionArray<PhysicsProxyWrapper>& PhysicsProxyReverseMap, Chaos::TArrayCollectionArray<int32>& ParticleIDReverseMap) {}
void CreateRigidBodyCallback(FParticlesType& InOutParticles) {}
void DisableCollisionsCallback(TSet<TTuple<int32, int32>>& InPairs) {}
void AddForceCallback(FParticlesType& InParticles, const float InDt, const int32 InIndex) {}
bool IsGTCollectionDirty() const { return GameThreadCollection.IsDirty(); }
// set the world transform ( this needs to be called on the game thread )
CHAOS_API void SetWorldTransform_External(const FTransform& WorldTransform);
const FTransform& GetPreviousWorldTransform_External() const { return PreviousWorldTransform_External; }
const FTransform& GetWorldTransform_External() { return WorldTransform_External; }
// todo(chaos): Remove this and move to a cook time approach of the SM data based on the GC property
// Set whether the GC should be using collision from the Static Mesh or the GC itself for game thread traces ( this needs to be called on the game thread )
CHAOS_API void SetUseStaticMeshCollisionForTraces_External(bool bInUseStaticMeshCollisionForTraces);
UE_DEPRECATED(5.4, "Use GetParticle_Internal instead")
const TArray<FClusterHandle*> GetParticles() const
{
return SolverParticleHandles;
}
const TArray<FClusterHandle*>GetUnorderedParticles_Internal() const
{
return SolverParticleHandles;
}
FClusterHandle* GetParticle_Internal(int32 Index) const
{
if (FromTransformToParticleIndex.IsValidIndex(Index))
{
const int32 ParticleIndex = FromTransformToParticleIndex[Index];
if (SolverParticleHandles.IsValidIndex(ParticleIndex))
{
return SolverParticleHandles[ParticleIndex];
}
}
return nullptr;
}
const FSimulationParameters& GetSimParameters() const
{
return Parameters;
}
FSimulationParameters& GetSimParameters()
{
return Parameters;
}
FGeometryDynamicCollection& GetPhysicsCollection()
{
return PhysicsThreadCollection;
}
FGeometryDynamicCollection& GetExternalCollection()
{
return GameThreadCollection;
}
UE_DEPRECATED(5.4, "Use GetUnorderedParticles_External instead")
TArray<TUniquePtr<FParticle>>& GetExternalParticles()
{
return GTParticles;
}
TArray<TUniquePtr<FParticle>>& GetUnorderedParticles_External()
{
return GTParticles;
}
CHAOS_API FParticle* GetParticleByIndex_External(int32 Index);
CHAOS_API const FParticle* GetParticleByIndex_External(int32 Index) const;
CHAOS_API FParticleHandle* GetParticleByIndex_Internal(int32 Index);
CHAOS_API const FParticleHandle* GetParticleByIndex_Internal(int32 Index) const;
FParticle* GetInitialRootParticle_External() { return GetParticleByIndex_External(Parameters.InitialRootIndex); }
const FParticle* GetInitialRootParticle_External() const { return GetParticleByIndex_External(Parameters.InitialRootIndex); }
FParticleHandle* GetInitialRootParticle_Internal() { return GetParticleByIndex_Internal(Parameters.InitialRootIndex); }
const FParticleHandle* GetInitialRootParticle_Internal() const { return GetParticleByIndex_Internal(Parameters.InitialRootIndex); }
/**
* * Get all the geometry collection particle handles based on the processing resolution
*/
CHAOS_API void GetRelevantParticleHandles(
TArray<Chaos::TGeometryParticleHandle<Chaos::FReal, 3>*>& Handles,
const Chaos::FPBDRigidsSolver* RigidSolver,
EFieldResolutionType ResolutionType);
/**
* Get all the geometry collection particle handles filtered by object state
*/
CHAOS_API void GetFilteredParticleHandles(
TArray<Chaos::TGeometryParticleHandle<Chaos::FReal, 3>*>& Handles,
const Chaos::FPBDRigidsSolver* RigidSolver,
const EFieldFilterType FilterType,
const EFieldObjectType ObjectType);
/* Implemented so we can construct TAccelerationStructureHandle. */
virtual void* GetHandleUnsafe() const override { return nullptr; }
int32 GetTransformGroupIndexFromHandle(const FParticleHandle* Handle) const
{
if (const int32* TransformGroupIndex = HandleToTransformGroupIndex.Find(Handle))
{
return *TransformGroupIndex;
}
return INDEX_NONE;
}
void GetTransformGroupIndicesFromHandles(const TArray<FParticleHandle*> Handles, TArray<int32>& IndicesOut) const
{
IndicesOut.SetNumUninitialized(Handles.Num());
for (int32 HandleIndex = 0; HandleIndex < Handles.Num(); ++HandleIndex)
{
IndicesOut[HandleIndex] = INDEX_NONE;
if (const int32* TransformGroupIndex = HandleToTransformGroupIndex.Find(Handles[HandleIndex]))
{
IndicesOut[HandleIndex] = (*TransformGroupIndex);
}
}
}
FGeometryCollectionItemIndex GetInternalClusterParentItemIndex_External(int32 ChildTransformIndex) const
{
// first find the GTParticle matching the Transform index
if (ChildTransformIndex >= 0 && ChildTransformIndex < GTParticles.Num())
{
const TUniquePtr<FParticle>& ChildGTParticle = GTParticles[ChildTransformIndex];
if (const FGTParticleIndices* Indices = GTParticleToIndices.Find(ChildGTParticle.Get()))
{
if (Indices->InternalClusterUniqueId != INDEX_NONE)
{
return FGeometryCollectionItemIndex::CreateInternalClusterItemIndex(Indices->InternalClusterUniqueId);
}
}
}
return FGeometryCollectionItemIndex::CreateInvalidItemIndex();
}
const TArray<int32>* FindInternalClusterChildrenTransformIndices_External(FGeometryCollectionItemIndex ItemIndex) const
{
if (ensure(ItemIndex.IsInternalCluster()))
{
return InternalClusterUniqueIdxToChildrenTransformIndices.Find(ItemIndex.GetInternalClusterIndex());
}
return nullptr;
}
FGeometryCollectionItemIndex GetItemIndexFromGTParticle_External(const FParticle* GTPParticle) const
{
// internal cluster have no representation on the GT, so we use the child GT particle to find the matching internal cluster unique index
if (const FGTParticleIndices* Indices = GTParticleToIndices.Find(GTPParticle))
{
if (Indices->InternalClusterUniqueId != INDEX_NONE)
{
return FGeometryCollectionItemIndex::CreateInternalClusterItemIndex(Indices->InternalClusterUniqueId);
}
// regular particle that has a matching transform index
if (Indices->TransformGroupIndex != INDEX_NONE)
{
return FGeometryCollectionItemIndex::CreateTransformItemIndex(Indices->TransformGroupIndex);
}
}
return FGeometryCollectionItemIndex::CreateInvalidItemIndex();
}
FGeometryCollectionItemIndex GetItemIndexFromGTParticleNoInternalCluster_External(const FParticle* GTPParticle) const
{
if (const FGTParticleIndices* Indices = GTParticleToIndices.Find(GTPParticle))
{
if (Indices->TransformGroupIndex != INDEX_NONE)
{
return FGeometryCollectionItemIndex::CreateTransformItemIndex(Indices->TransformGroupIndex);
}
}
return FGeometryCollectionItemIndex::CreateInvalidItemIndex();
}
CHAOS_API FName GetTransformName_External(FGeometryCollectionItemIndex ItemIndex) const;
bool GetIsObjectDynamic() const { return IsObjectDynamic; }
CHAOS_API void DisableParticles_External(TArray<int32>&& TransformGroupIndices);
CHAOS_API void ApplyForceAt_External(FVector Force, FVector WorldLocation);
CHAOS_API void ApplyImpulseAt_External(FVector Force, FVector WorldLocation);
CHAOS_API void BreakClusters_External(TArray<FGeometryCollectionItemIndex>&& ItemIndices);
CHAOS_API void BreakActiveClusters_External();
CHAOS_API void SetAnchoredByIndex_External(int32 Index, bool bAnchored);
CHAOS_API void SetAnchoredByTransformedBox_External(const FBox& Box, const FTransform& Transform, bool bAnchored, int32 MaxLevel = INDEX_NONE);
CHAOS_API void RemoveAllAnchors_External();
CHAOS_API void ApplyExternalStrain_External(FGeometryCollectionItemIndex ItemIndex, const FVector& WorldLocation, float Radius, int32 PropagationDepth, float PropagationFactor, float StrainValue);
CHAOS_API void ApplyInternalStrain_External(FGeometryCollectionItemIndex ItemIndex, const FVector& WorldLocation, float Radius, int32 PropagationDepth, float PropagationFactor, float StrainValue);
CHAOS_API void ApplyBreakingLinearVelocity_External(FGeometryCollectionItemIndex ItemIndex, const FVector& LinearVelocity);
CHAOS_API void ApplyBreakingAngularVelocity_External(FGeometryCollectionItemIndex ItemIndex, const FVector& AngularVelocity);
CHAOS_API void ApplyLinearVelocity_External(FGeometryCollectionItemIndex ItemIndex, const FVector& LinearVelocity);
CHAOS_API void ApplyAngularVelocity_External(FGeometryCollectionItemIndex ItemIndex, const FVector& AngularVelocity);
CHAOS_API void SetProxyDirty_External();
CHAOS_API void SetEnableDamageFromCollision_External(bool bEnable);
CHAOS_API void SetNotifyBreakings_External(bool bNotify);
CHAOS_API void SetNotifyRemovals_External(bool bNotify);
CHAOS_API void SetNotifyCrumblings_External(bool bNotify, bool bIncludeChildren);
CHAOS_API void SetNotifyGlobalBreakings_External(bool bNotify);
CHAOS_API void SetNotifyGlobalRemovals_External(bool bNotify);
CHAOS_API void SetNotifyGlobalCrumblings_External(bool bNotify, bool bIncludeChildren);
CHAOS_API float ComputeMaterialBasedDamageThreshold_Internal(Chaos::FPBDRigidClusteredParticleHandle& ClusteredParticle) const;
FProxyInterpolationBase* GetInterpolationData() { return InterpolationData.Get(); }
const FProxyInterpolationBase* GetInterpolationData() const { return InterpolationData.Get(); }
enum class EReplicationMode: uint8
{
Unknown,
Server,
Client,
};
void SetReplicationMode(EReplicationMode Mode) { ReplicationMode = Mode; }
EReplicationMode GetReplicationMode() const { return ReplicationMode; }
UE_DEPRECATED(5.7, "Use the overload that takes a FCombinedShapeFilterData instead") CHAOS_API void UpdateFilterData_External(const FCollisionFilterData& NewSimFilter, const FCollisionFilterData& NewQueryFilter);
CHAOS_API void UpdateFilterData_External(const Chaos::Filter::FCombinedShapeFilterData& NewShapeFilter);
struct FParticleCollisionFilterData
{
int32 ParticleIndex = INDEX_NONE;
bool bIsValid = false;
bool bQueryEnabled = false;
bool bSimEnabled = false;
FCollisionFilterData QueryFilter;
FCollisionFilterData SimFilter;
};
CHAOS_API void UpdatePerParticleFilterData_External(const TArray<FParticleCollisionFilterData>& Data);
CHAOS_API void SetDamageThresholds_External(const TArray<float>& DamageThresholds);
CHAOS_API void SetDamagePropagationData_External(bool bEnabled, float BreakDamagePropagationFactor, float ShockDamagePropagationFactor);
CHAOS_API void SetDamageModel_External(EDamageModelTypeEnum DamageModel);
CHAOS_API void SetUseMaterialDamageModifiers_External(bool bUseMaterialDamageModifiers);
CHAOS_API void SetMaterialOverrideMassScaleMultiplier_External(float InMultiplier);
CHAOS_API void SetEnableGravity_External(bool EnableGravity_External);
CHAOS_API void SetGravityGroupIndex_External(int32 GravityGroupIndex);
CHAOS_API void SetOneWayInteractionLevel_External(int32 OneWayInteractionLevel);
CHAOS_API void SetPhysicsMaterial_External(const Chaos::FMaterialHandle& MaterialHandle);
/**
* Traverses the parents of TransformGroupIdx counting number of levels,
* and sets levels array value for TransformGroupIdx and its parents if not yet initialized.
* If level is already set, retrieve stored level.
* Uninitialized level array should be correct size and defaulted to zeros.
*/
static CHAOS_API int32 CalculateAndSetLevel(int32 TransformGroupIdx, const TManagedArray<int32>& Parent, TManagedArray<int32>& Levels);
void SetPostPhysicsSyncCallback(TFunction<void()> Callback)
{
PostPhysicsSyncCallback = Callback;
}
void SetPostParticlesCreatedCallback(TFunction<void()> Callback)
{
PostParticlesCreatedCallback = Callback;
}
CHAOS_API FClusterHandle* GetInitialRootHandle_Internal() const;
CHAOS_API TArray<Chaos::FPhysicsObjectHandle> GetAllPhysicsObjects() const ;
CHAOS_API TArray<Chaos::FPhysicsObjectHandle> GetAllPhysicsObjectIncludingNulls() const;
CHAOS_API Chaos::FPhysicsObjectHandle GetPhysicsObjectByIndex(int32 Index) const;
CHAOS_API void RebaseAllGameThreadCollectionTransformsOnNewWorldTransform_External();
UE_DEPRECATED(5.4, "Use GetNumTransforms instead")
int32 GetNumParticles() const { return NumTransforms; }
int32 GetNumTransforms() const { return NumTransforms; }
// todo(chaos): Remove this and move to a cook time approach of the SM data based on the GC property
using FCreateTraceCollisionGeometryCallback = TFunction<void(const FTransform& InToLocal, TArray<Chaos::FImplicitObjectPtr>& OutGeoms, Chaos::FShapesArray& OutShapes)>;
void SetCreateTraceCollisionGeometryCallback(FCreateTraceCollisionGeometryCallback InCreateGeometryCallback) { CreateTraceCollisionGeometryCallback = InCreateGeometryCallback; }
CHAOS_API void CreateChildrenGeometry_Internal();
int32 GetFromParticleToTransformIndex(int32 Index) const { check(FromParticleToTransformIndex.IsValidIndex(Index)); return FromParticleToTransformIndex[Index]; }
bool GetSkipChildToParentUpdateWhenInClusterUnion() const { return bSkipChildToParentUpdateWhenInClusterUnion; };
void SetSkipChildToParentUpdateWhenInClusterUnion(bool bValue) { bSkipChildToParentUpdateWhenInClusterUnion = bValue; };
protected:
bool RebaseParticleGameThreadCollectionTransformOnNewWorldTransform_External(int32 ParticleIndex, const TManagedArray<FTransform>& MassToLocal, bool bIsComponentTransformScaled, const FTransform& ComponentScaleTransform);
CHAOS_API float ComputeMaterialBasedDamageThreshold_Internal(int32 TransformIndex) const;
/**
* Compute user defined damage threshold for a specific transform
* this account for component level damage threshold as well as size specific ones
* @param DynamicCollection dynamic collection to use
* @param TransformIndex index of the transform to compute the threshold for
* #return damage threshold value
*/
CHAOS_API float ComputeUserDefinedDamageThreshold_Internal(int32 TransformIndex) const;
/** adjust scalar mass to account for per component scale properties ( from material override and world transform scale ) */
CHAOS_API float AdjustMassForScale(float Mass) const;
/** adjust inertia to account for per component scale properties ( from material override and world transform scale ) */
CHAOS_API Chaos::FVec3f AdjustInertiaForScale(const Chaos::FVec3f& Inertia) const;
CHAOS_API Chaos::TPBDGeometryCollectionParticleHandle<Chaos::FReal, 3>* BuildNonClusters_Internal(const uint32 CollectionClusterIndex, Chaos::FPBDRigidsSolver* RigidsSolver, float Mass, Chaos::FVec3f Inertia, const Chaos::FUniqueIdx* ExistingIndex);
/**
* Build a physics thread cluster parent particle.
* \p CollectionClusterIndex - the source geometry collection transform index.
* \p ChildHandles - physics particle handles of the cluster children.
* \p ChildTransformGroupIndices - geometry collection indices of the children.
* \P Parameters - uh, yeah... Other parameters.
*/
CHAOS_API Chaos::FPBDRigidClusteredParticleHandle* BuildClusters_Internal(
const uint32 CollectionClusterIndex,
TArray<Chaos::FPBDRigidParticleHandle*>& ChildHandles,
const TArray<int32>& ChildTransformGroupIndices,
const Chaos::FClusterCreationParameters & Parameters,
const Chaos::FUniqueIdx* ExistingIndex);
CHAOS_API void SetSleepingState(const Chaos::FPBDRigidsSolver& RigidsSolver);
CHAOS_API void DirtyAllParticles(const Chaos::FPBDRigidsSolver& RigidsSolver);
/**
* Traverses the parents of \p TransformIndex in \p GeometryCollection, counting
* the number of levels until the next parent is \c INDEX_NONE.
*/
static CHAOS_API int32 CalculateHierarchyLevel(const FGeometryDynamicCollection& DynamicCollection, int32 TransformIndex);
CHAOS_API void SetClusteredParticleKinematicTarget_Internal(Chaos::FPBDRigidClusteredParticleHandle* Handle, const FTransform& WorldTransform);
CHAOS_API void PrepareBufferData(Chaos::FDirtyGeometryCollectionData& BufferData, const FGeometryDynamicCollection& ThreadCollection, Chaos::FReal SolverLastDt = 0.0);
CHAOS_API void CreateNonClusteredParticles(Chaos::FPBDRigidsSolver* RigidsSolver, const FGeometryCollection& RestCollection, const FGeometryDynamicCollection& DynamicCollection);
CHAOS_API Chaos::FPBDRigidClusteredParticleHandle* FindClusteredParticleHandleByItemIndex_Internal(FGeometryCollectionItemIndex ItemIndex) const;
CHAOS_API void UpdateDamageThreshold_Internal();
/** Scale the cluster particles geometry (creates if necessary an additional TImplicitObjectScaled object into the implicits hierarchy) */
CHAOS_API void ScaleClusterGeometry_Internal(const FVector& WorldScale);
CHAOS_API void SetWorldTransform_Internal(const FTransform& WorldTransform, bool bInSkipChildToParentUpdateWhenInClusterUnion = false);
UE_DEPRECATED(5.7, "Use the overload that takes a FCombinedShapeFilterData instead") CHAOS_API void SetFilterData_Internal(const FCollisionFilterData& NewSimFilter, const FCollisionFilterData& NewQueryFilter);
CHAOS_API void SetFilterData_Internal(const Chaos::Filter::FCombinedShapeFilterData& NewShapeFilter);
CHAOS_API void SetPerParticleFilterData_Internal(const TArray<FParticleCollisionFilterData>& PerParticleData);
CHAOS_API void SetDamagePropagationData_Internal(bool bEnabled, float BreakDamagePropagationFactor, float ShockDamagePropagationFactor);
CHAOS_API void SetEnableGravity_Internal(bool bEnabled);
CHAOS_API void SetDamageThresholds_Internal(const TArray<float>& DamageThresholds);
CHAOS_API void SetDamageModel_Internal(EDamageModelTypeEnum DamageModel);
CHAOS_API void SetUseMaterialDamageModifiers_Internal(bool bUseMaterialDamageModifiers);
CHAOS_API void SetMaterialOverrideMassScaleMultiplier_Internal(float InMultiplier);
CHAOS_API void SetGravityGroupIndex_Internal(int32 GravityGroupIndex);
CHAOS_API void SetOneWayInteractionLevel_Internal(int32 InOneWayInteractionLevel);
CHAOS_API void SetPhysicsMaterial_Internal(const Chaos::FMaterialHandle& MaterialHandle);
private:
static TBitArray<> CalculateClustersToCreateFromChildren(const FGeometryDynamicCollection& DynamicCollection, int32 NumTransforms);
static int32 CalculateEffectiveParticles(const FGeometryDynamicCollection& DynamicCollection, int32 NumTransform, int32 MaxSimulatedLevel, bool bEnableClustering, const UObject* Owner, TBitArray<>& EffectiveParticles);
void CreateGTParticles(TManagedArray<Chaos::FImplicitObjectPtr>& Implicits, Chaos::FPBDRigidsEvolutionBase* Evolution, bool bInitializeRootOnly);
void CreateChildrenGeometry_External();
void SyncParticles_External();
/**
* Since geometry collections only buffer data that has changed, when PullFromPhysicsState is given both PrevData and NextData it must
* examine *both* PrevData and NextData for data about a particle (since that particle's data coudl be in PrevData and not NextData).
* PullNonInterpolatableDataFromSinglePhysicsState will do the work to pull the non-interpolatable data (which could include X/R/V/W in certain scenarios)
* to the game thread.
*
* @param BufferData The buffered physics data to pull data from (could be PrevData or NextData).
* @param bForcePullXRVW Whether or not to pull interpolatable data as non-interpolatable data (i.e. X/R/V/W). This happens only when NextData doesn't exist.
* @param Seen The bit array of the previously handled FDirtyGeometryCollectionData (should be null in the case of handling NextData, and should be the TBitArray on NextData when handling PrevData).
* @return A boolean indicating whether or not a change to the GT state was detected.
*/
bool PullNonInterpolatableDataFromSinglePhysicsState(const Chaos::FDirtyGeometryCollectionData& BufferData, bool bForcePullXRVW, const TBitArray<>* Seen);
FSimulationParameters Parameters;
TArray<Chaos::FPhysicsObjectUniquePtr> PhysicsObjects;
// todo : we should probably keep a simulation parameter copy on the game thread instead
FTransform WorldTransform_External;
FTransform PreviousWorldTransform_External;
//
// Proxy State Information
//
int32 NumTransforms;
int32 NumEffectiveParticles;
int32 BaseParticleIndex;
// Per object collision fraction.
float CollisionParticlesPerObjectFraction;
/** structure that contains the necessary information for processing fields */
struct FFieldData
{
/** field command to execute */
TArray<FFieldSystemCommand> Commands;
/** Field Datas stored during evaluation */
FFieldExecutionDatas ExecutionDatas;
};
// data is allocated on demand on the physics thread only ( see GetOrCreateFieldData_Internal )
TUniquePtr<FFieldData> FieldData_Internal;
FFieldData& GetOrCreateFieldData_Internal();
EReplicationMode ReplicationMode = EReplicationMode::Unknown;
uint8 bIsGameThreadWorldTransformDirty : 1;
uint8 bHasBuiltGeometryOnPT : 1;
uint8 bHasBuiltGeometryOnGT : 1;
/* set to true once InitializeBodiesPT has been called*/
bool bIsInitializedOnPhysicsThread : 1 = false;
//
// Buffer Results State Information
//
bool IsObjectDynamic : 1; // Records current dynamic state
bool IsObjectLoading : 1; // Indicate when loaded
bool IsObjectDeleting : 1; // Indicate when pending deletion
/** when true and part of a cluster union this will skip the Child to parent update part */
bool bSkipChildToParentUpdateWhenInClusterUnion : 1;
TArray<FParticleHandle*> SolverClusterID;
TArray<FClusterHandle*> SolverClusterHandles; // make a TArray of the base clase with type
TArray<FClusterHandle*> SolverParticleHandles;// make a TArray of base class and join with above
TMap<FParticleHandle*, int32> HandleToTransformGroupIndex;
TMap<int32, FClusterHandle*> UniqueIdxToInternalClusterHandle;
TArray<Chaos::FUniqueIdx> UniqueIdxs;
TArray<int32> FromParticleToTransformIndex;
TArray<int32> FromTransformToParticleIndex;
TBitArray<> EffectiveParticles;
// Game thread particles
TArray<TUniquePtr<FParticle>> GTParticles;
struct FGTParticleIndices
{
/** Correponding transform index of the GTparticle */
int32 TransformGroupIndex = INDEX_NONE;
/** unique index of the parent the GT particle is a child of internal cluster */
int32 InternalClusterUniqueId = INDEX_NONE;
};
TMap<FParticle*, FGTParticleIndices> GTParticleToIndices;
TMap<int32, TArray<int32>> InternalClusterUniqueIdxToChildrenTransformIndices;
// These are read on both threads and should not be changed
const FCollisionFilterData SimFilter;
const FCollisionFilterData QueryFilter;
// todo(chaos): Remove this and move to a cook time approach of the SM data based on the GC property
FCreateTraceCollisionGeometryCallback CreateTraceCollisionGeometryCallback;
// called after we sync the physics thread data ( called on the game thread )
TFunction<void()> PostPhysicsSyncCallback;
TFunction<void()> PostParticlesCreatedCallback;
// The Simulation data is copied between the game and physics thread. It is
// expected that the two data sets will diverge, based on how the simulation
// uses the data, but at the start of the simulation the PhysicsThreadCollection
// is a deep copy from the GameThreadCollection.
FGeometryDynamicCollection PhysicsThreadCollection;
FGeometryDynamicCollection& GameThreadCollection; // now initialize from the SharedRef passed in the constructor
const TSharedPtr<FGeometryDynamicCollection> GameThreadCollectionSharedPtr; // shoul dnever be null
// Currently this is using triple buffers for game-physics and
// physics-game thread communication, but not for any reason other than this
// is the only implementation we currently have of a guarded buffer - a buffer
// that tracks it's own state, rather than having other mechanisms determine
// whether or not the contents of the buffer have been updated. A double
// buffer would probably be fine, as that seems to be the assumption the logic
// currently managing the exchange is built upon. However, I believe that
// logic locks, and the triple buffer would enable a decoupled lock-free
// paradigm, at least for this component of the handshake.
Chaos::FGuardedTripleBuffer<FGeometryCollectionResults> PhysToGameInterchange;
TUniquePtr<FProxyInterpolationBase> InterpolationData;
public:
/** Get or create a derived FProxyInterpolationBase that handles render interpolation error corrections */
template<typename ErrorDataType>
ErrorDataType* GetOrCreateErrorInterpolationData()
{
if (!InterpolationData.IsValid())
{
InterpolationData = MakeUnique<ErrorDataType>();
}
else if (InterpolationData.Get()->GetInterpolationType() != ErrorDataType::InterpolationType)
{
InterpolationData = MakeUnique<ErrorDataType>(InterpolationData.Get()->GetPullDataInterpIdx_External(), InterpolationData.Get()->GetInterpChannel_External());
}
return static_cast<ErrorDataType*>(InterpolationData.Get());
}
private:
#if WITH_EDITORONLY_DATA
// this is used as a unique ID when collecting data from runtime
FGuid CollectorGuid;
#endif
};
/**
* the watcher collects runtime data about
* damage on each piece of the geometry collection
*/
struct FDamageCollector
{
public:
struct FDamageData
{
float DamageThreshold= 0;
float MaxDamages = 0;
bool bIsBroken = false;
};
CHAOS_API void Reset(int32 NumTransforms);
int32 Num() const { return DamageData.Num(); }
const FDamageData& operator[](int32 TransformIndex) const
{
static FDamageData DefaultDamageData;
if (DamageData.IsValidIndex(TransformIndex))
return DamageData[TransformIndex];
return DefaultDamageData;
}
CHAOS_API void SampleDamage(int32 TransformIndex, float Damage, float DamageThreshold);
private:
TArray<FDamageData> DamageData;
};
struct FRuntimeDataCollector
{
public:
CHAOS_API void Clear();
CHAOS_API void AddCollector(const FGuid& Guid, int32 TransformNum);
CHAOS_API void RemoveCollector(const FGuid& Guid);
CHAOS_API FDamageCollector* Find(const FGuid& Guid);
static CHAOS_API FRuntimeDataCollector& GetInstance();
private:
// collectors by geometry collection Guids
TMap<FGuid,FDamageCollector> Collectors;
};
CHAOS_API TUniquePtr<Chaos::FTriangleMesh> CreateTriangleMesh(const int32 FaceStart,const int32 FaceCount,const TManagedArray<bool>& Visible,const TManagedArray<FIntVector>& Indices, bool bRotateWinding = true);
CHAOS_API void BuildSimulationData(Chaos::FErrorReporter& ErrorReporter, FGeometryCollection& GeometryCollection, const FSharedSimulationParameters& SharedParams);