Files
UnrealEngineUWP/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionParticlesData.cpp
benn gallagher 5c655ca2c4 Reimplemented solver actors for Chaos geometry collections.
- Reimplemented TaskGraph tick mode to correctly handle multiple worlds (this involves ticking global command queues outside of world tick and mapping worlds to solver lists for each world tick so we can correctly handle multiple worlds)
 - Audited global commands that had an explicit solver capture and moved them to solver commands
 - Updated headless tests to new API for solver flags
#rb
#jira

#ROBOMERGE-SOURCE: CL 11604093 in //UE4/Release-4.25/... via CL 11604130
#ROBOMERGE-BOT: RELEASE (Release-4.25Plus -> Main) (v655-11596533)

[CL 11604166 by benn gallagher in Main branch]
2020-02-25 10:44:05 -05:00

272 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "GeometryCollection/GeometryCollectionParticlesData.h"
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
#include "PhysicsSolver.h"
#include "Chaos/Sphere.h"
#include "Chaos/Box.h"
#include "Chaos/ImplicitObjectTransformed.h"
#include "Chaos/ImplicitObjectUnion.h"
#include "Chaos/Levelset.h"
#include "ChaosSolversModule.h"
#include "GeometryCollection/ManagedArray.h"
DEFINE_LOG_CATEGORY_STATIC(LogGeometryCollectionParticlesData, Log, All);
template<class T, int d>
TGeometryCollectionParticlesData<T, d>::TGeometryCollectionParticlesData()
: ChaosModule(FModuleManager::Get().GetModulePtr<FChaosSolversModule>("ChaosSolvers"))
, BufferedData()
, PhysicsSyncCount(0)
, GameSyncCount(MAX_int32)
, SyncFrame(MAX_uint64)
{
}
template<class T, int d>
//void TGeometryCollectionParticlesData<T, d>::Sync(const Chaos::FPhysicsSolver* Solver, const TManagedArray<int32>& RigidBodyIds)
void TGeometryCollectionParticlesData<T, d>::Sync(Chaos::FPhysicsSolver* Solver, const TManagedArray<FGuid>& RigidBodyIds)
{
// No point in calling twice the sync function within the same frame
if (!ensureMsgf(SyncFrame != GFrameCounter, TEXT("Sync should not happen twice during the same tick.")))
{
return;
}
SyncFrame = GFrameCounter;
// Check for newly transferred data
const int32 SyncCount = PhysicsSyncCount.GetValue();
if(GameSyncCount != SyncCount)
{
// Received some new data, so safe to flip the buffers
BufferedData.Flip();
// Is a new command worth sending?
const FData& ConstData = BufferedData.GetPhysicsDataForRead();
if (ConstData.RequiredDataFlags.FindFirstSetBit() != INDEX_NONE && Solver && RigidBodyIds.Num() > 0)
{
// Update sync count so that the next flip will only happen once the sync command has completed
GameSyncCount = SyncCount;
// Send sync command
Chaos::IDispatcher* const PhysicsDispatcher = ChaosModule->GetDispatcher();
check(PhysicsDispatcher);
PhysicsDispatcher->EnqueueCommandImmediate(Solver, [this, &RigidBodyIds](Chaos::FPhysicsSolver* InSolver)
{
// Iterate through all data
FData& Data = BufferedData.GetPhysicsDataForWrite();
for (int32 DataIndex = 0; DataIndex < uint32(EGeometryCollectionParticlesData::Count); ++DataIndex)
{
// Only sync required infos
if (Data.RequiredDataFlags[DataIndex])
{
Data.Copy(EGeometryCollectionParticlesData(DataIndex), InSolver, RigidBodyIds);
}
else
{
Data.Reset(EGeometryCollectionParticlesData(DataIndex));
}
}
// Update synced flags
Data.SyncedDataFlags = Data.RequiredDataFlags;
// Signal the new data has arrived and that it's safe to flip the buffers
PhysicsSyncCount.Increment();
});
}
}
// Clear flags ready for next set of requests
FData& Data = BufferedData.GetGameDataForWrite();
Data.RequiredDataFlags = FDataFlags();
UE_LOG(LogGeometryCollectionParticlesData, Verbose, TEXT("Synced particles data at frame %lu: \n%s."), SyncFrame, *ToString(0));
}
template<class T, int d>
void TGeometryCollectionParticlesData<T, d>::FData::SetAllDataSyncFlag() const
{
for (int32 DataIndex = 0; DataIndex < uint32(EGeometryCollectionParticlesData::Count); ++DataIndex)
{
RequiredDataFlags[DataIndex] = true;
}
}
template<class T, int d>
void TGeometryCollectionParticlesData<T, d>::FData::Reset(EGeometryCollectionParticlesData Data)
{
switch (Data)
{
default: break;
case EGeometryCollectionParticlesData::X : X .Resize(0); break;
case EGeometryCollectionParticlesData::R : R .Resize(0); break;
case EGeometryCollectionParticlesData::Geometry : Geometry .Resize(0); break;
case EGeometryCollectionParticlesData::GeometryType : GeometryType .Resize(0); break;
case EGeometryCollectionParticlesData::GeometryIsConvex : GeometryIsConvex .Resize(0); break;
case EGeometryCollectionParticlesData::GeometryHasBoundingBox: GeometryHasBoundingBox.Resize(0); break;
case EGeometryCollectionParticlesData::GeometryBoxMin : GeometryBoxMin .Resize(0); break;
case EGeometryCollectionParticlesData::GeometryBoxMax : GeometryBoxMax .Resize(0); break;
case EGeometryCollectionParticlesData::GeometrySphereCenter : GeometrySphereCenter .Resize(0); break;
case EGeometryCollectionParticlesData::GeometrySphereRadius : GeometrySphereRadius .Resize(0); break;
case EGeometryCollectionParticlesData::GeometryLevelSetGrid : GeometryLevelSetGrid .Resize(0); break;
case EGeometryCollectionParticlesData::V : V .Resize(0); break;
case EGeometryCollectionParticlesData::W : W .Resize(0); break;
case EGeometryCollectionParticlesData::F : F .Resize(0); break;
case EGeometryCollectionParticlesData::Torque : Torque .Resize(0); break;
case EGeometryCollectionParticlesData::I : I .Resize(0); break;
case EGeometryCollectionParticlesData::InvI : InvI .Resize(0); break;
case EGeometryCollectionParticlesData::M : M .Resize(0); break;
case EGeometryCollectionParticlesData::InvM : InvM .Resize(0); break;
case EGeometryCollectionParticlesData::CollisionParticlesSize: CollisionParticlesSize.Resize(0); break;
case EGeometryCollectionParticlesData::Disabled : Disabled .Resize(0); break;
case EGeometryCollectionParticlesData::Sleeping : Sleeping .Resize(0); break;
case EGeometryCollectionParticlesData::Island : Island .Resize(0); break;
case EGeometryCollectionParticlesData::P : P .Resize(0); break;
case EGeometryCollectionParticlesData::Q : Q .Resize(0); break;
case EGeometryCollectionParticlesData::PreV : PreV .Resize(0); break;
case EGeometryCollectionParticlesData::PreW : PreW .Resize(0); break;
case EGeometryCollectionParticlesData::ConnectivityEdges : ConnectivityEdges .Resize(0); break;
case EGeometryCollectionParticlesData::ChildToParentMap : ChildToParentMap .Resize(0); break;
}
}
template<class T, int d>
void TGeometryCollectionParticlesData<T, d>::FData::Copy(EGeometryCollectionParticlesData Data, const Chaos::FPhysicsSolver* Solver, const TManagedArray<FGuid>& RigidBodyIds)
{
check(Solver);
#if TODO_REIMPLEMENT_GET_RIGID_PARTICLES
// 10.31.2019 Ryan - This code uses the RigidBodyIds as indices, and that's not going to work anymore.
// We need to figure that out...
const Chaos::TPBDRigidParticles<T, d>& Particles = Solver->GetRigidParticles();
const Chaos::TPBDRigidClustering<FPBDRigidsEvolution, FPBDCollisionConstraints, T, d>& Clustering = Solver->GetRigidClustering();
// Lambdas used to flatten the particle structure
// Can't rely on FImplicitObject::GetType, as it returns Unknown
// TODO: Would there be something to fix in TImplicitObject so we can use this simpler line of code instead?:
// auto GetType = [](const Chaos::FImplicitObject* Geometry) { return Geometry ? Geometry->GetType(): Chaos::ImplicitObjectType::Unknown; };
auto GetType = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject)
{
if (ImplicitObject)
{
if (ImplicitObject->template GetObject<Chaos::TSphere <T, d>>()) { return Chaos::ImplicitObjectType::Sphere ; }
else if (ImplicitObject->template GetObject<Chaos::TAABB <T, d>>()) { return Chaos::ImplicitObjectType::Box ; }
else if (ImplicitObject->template GetObject<Chaos::TPlane <T, d>>()) { return Chaos::ImplicitObjectType::Plane ; }
else if (ImplicitObject->template GetObject<Chaos::TImplicitObjectTransformed<T, d>>()) { return Chaos::ImplicitObjectType::Transformed; }
else if (ImplicitObject->template GetObject<Chaos::FImplicitObjectUnion>()) { return Chaos::ImplicitObjectType::Union ; }
else if (ImplicitObject->template GetObject<Chaos::TLevelSet <T, d>>()) { return Chaos::ImplicitObjectType::LevelSet ; }
}
return Chaos::ImplicitObjectType::Unknown;
};
auto IsConvex = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { return ImplicitObject ? ImplicitObject->IsConvex (): false; };
auto HasBoundingBox = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { return ImplicitObject ? ImplicitObject->HasBoundingBox(): false; };
auto BoxMin = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { const Chaos::TAABB <T, d>* Box ; return ImplicitObject && (Box = ImplicitObject->template GetObject<Chaos::TAABB <T, d>>()) != nullptr ? Box ->Min (): Chaos::TVector<T, d>(T(0)); };
auto BoxMax = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { const Chaos::TAABB <T, d>* Box ; return ImplicitObject && (Box = ImplicitObject->template GetObject<Chaos::TAABB <T, d>>()) != nullptr ? Box ->Max (): Chaos::TVector<T, d>(T(0)); };
auto SphereCenter = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { const Chaos::TSphere <T, d>* Sphere ; return ImplicitObject && (Sphere = ImplicitObject->template GetObject<Chaos::TSphere <T, d>>()) != nullptr ? Sphere ->GetCenter (): Chaos::TVector<T, d>(T(0)); };
auto SphereRadius = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { const Chaos::TSphere <T, d>* Sphere ; return ImplicitObject && (Sphere = ImplicitObject->template GetObject<Chaos::TSphere <T, d>>()) != nullptr ? Sphere ->GetRadius (): T(0); };
auto LevelSetGrid = [](Chaos::TSerializablePtr<Chaos::FImplicitObject> ImplicitObject) { const Chaos::TLevelSet<T, d>* LevelSet; return ImplicitObject && (LevelSet = ImplicitObject->template GetObject<Chaos::TLevelSet<T, d>>()) != nullptr ? LevelSet->GetGrid(): Chaos::TUniformGrid<T, d>(); };
// Data type copy for all particles
const int32 Count = RigidBodyIds.Num();
switch (Data)
{
default: break;
case EGeometryCollectionParticlesData::X : X .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { X [i] = Particles.X (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::R : R .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { R [i] = Particles.R (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Geometry : Geometry .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Geometry [i] = Particles.Geometry (RigidBodyIds[i]).Get() ; } } break;
case EGeometryCollectionParticlesData::GeometryType : GeometryType .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryType [i] = GetType (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometryIsConvex : GeometryIsConvex .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryIsConvex [i] = IsConvex (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometryHasBoundingBox: GeometryHasBoundingBox.Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryHasBoundingBox[i] = HasBoundingBox(Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometryBoxMin : GeometryBoxMin .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryBoxMin [i] = BoxMin (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometryBoxMax : GeometryBoxMax .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryBoxMax [i] = BoxMax (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometrySphereCenter : GeometrySphereCenter .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometrySphereCenter [i] = SphereCenter (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometrySphereRadius : GeometrySphereRadius .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometrySphereRadius [i] = SphereRadius (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::GeometryLevelSetGrid : GeometryLevelSetGrid .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { GeometryLevelSetGrid [i] = LevelSetGrid (Particles.Geometry(RigidBodyIds[i])); } } break;
case EGeometryCollectionParticlesData::V : V .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { V [i] = Particles.V (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::W : W .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { W [i] = Particles.W (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::F : F .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { F [i] = Particles.F (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Torque : Torque .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Torque [i] = Particles.Torque (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::I : I .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { I [i] = Particles.I (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::InvI : InvI .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { InvI [i] = Particles.InvI (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::M : M .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { M [i] = Particles.M (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::InvM : InvM .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { InvM [i] = Particles.InvM (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::CollisionParticlesSize: CollisionParticlesSize.Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { CollisionParticlesSize[i] = Particles.CollisionParticlesSize (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Disabled : Disabled .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Disabled [i] = Particles.Disabled (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Sleeping : Sleeping .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Sleeping [i] = Particles.Sleeping (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Island : Island .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Island [i] = Particles.Island (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::P : P .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { P [i] = Particles.P (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::Q : Q .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { Q [i] = Particles.Q (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::PreV : PreV .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { PreV [i] = Particles.PreV (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::PreW : PreW .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { PreW [i] = Particles.PreW (RigidBodyIds[i]) ; } } break;
case EGeometryCollectionParticlesData::ConnectivityEdges : ConnectivityEdges .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { ConnectivityEdges [i] = Clustering.GetConnectivityEdges()[RigidBodyIds[i]] ; } } break;
case EGeometryCollectionParticlesData::ChildToParentMap : ChildToParentMap .Resize(Count); for (int32 i = 0; i < Count; ++i) { if (RigidBodyIds[i] != INDEX_NONE) { ChildToParentMap [i] = Clustering.GetChildToParentMap ()[RigidBodyIds[i]] ; } } break;
}
#endif
}
template<class T, int d>
FString TGeometryCollectionParticlesData<T, d>::FData::ToString(int32 Index, const TCHAR* Separator) const
{
auto TypeText = [](Chaos::EImplicitObjectType Type)
{
switch (Type)
{
default:
case Chaos::ImplicitObjectType::Unknown : return TEXT("Unknown" ); break;
case Chaos::ImplicitObjectType::Box : return TEXT("Box" ); break;
case Chaos::ImplicitObjectType::LevelSet : return TEXT("LevelSet" ); break;
case Chaos::ImplicitObjectType::Plane : return TEXT("Plane" ); break;
case Chaos::ImplicitObjectType::Sphere : return TEXT("Sphere" ); break;
case Chaos::ImplicitObjectType::Transformed: return TEXT("Transformed"); break;
case Chaos::ImplicitObjectType::Union : return TEXT("Union" ); break;
}
};
auto GridString = [](const Chaos::TUniformGrid<T, d>& Grid)
{
return FString::Printf(TEXT("Counts X=%d Y=%d Z=%d, Dx %s, MinCorner %s, MaxCorner %s"), Grid.Counts().X, Grid.Counts().Y, Grid.Counts().Z, *Grid.Dx().ToString(), *Grid.MinCorner().ToString(), *Grid.MaxCorner().ToString());
};
FString String;
const TCHAR* Sep = TEXT("");
if (HasSyncedData(EGeometryCollectionParticlesData::X )) { String += FString::Printf(TEXT( "X: %s" ), * X [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::R )) { String += FString::Printf(TEXT("%sR: %s" ), Sep, * R [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryType )) { String += FString::Printf(TEXT("%sGeometryType: %s" ), Sep, TypeText (GeometryType [Index]) ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryIsConvex )) { String += FString::Printf(TEXT("%sGeometryIsConvex: %d" ), Sep, GeometryIsConvex [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryHasBoundingBox)) { String += FString::Printf(TEXT("%sGeometryHasBoundingBox: %d"), Sep, GeometryHasBoundingBox[Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryBoxMin )) { String += FString::Printf(TEXT("%sGeometryBoxMin: %s" ), Sep, * GeometryBoxMin [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryBoxMax )) { String += FString::Printf(TEXT("%sGeometryBoxMax: %s" ), Sep, * GeometryBoxMax [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometrySphereCenter )) { String += FString::Printf(TEXT("%sGeometrySphereCenter: %s" ), Sep, * GeometrySphereCenter [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometrySphereRadius )) { String += FString::Printf(TEXT("%sGeometrySphereRadius: %f" ), Sep, GeometrySphereRadius [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::GeometryLevelSetGrid )) { String += FString::Printf(TEXT("%sGeometryLevelSetGrid: %s" ), Sep, *GridString(GeometryLevelSetGrid [Index]) ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::V )) { String += FString::Printf(TEXT("%sV: %s" ), Sep, * V [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::W )) { String += FString::Printf(TEXT("%sW: %s" ), Sep, * W [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::F )) { String += FString::Printf(TEXT("%sF: %s" ), Sep, * F [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::Torque )) { String += FString::Printf(TEXT("%sTorque: %s" ), Sep, * Torque [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::I )) { String += FString::Printf(TEXT("%sI: %s" ), Sep, * I [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::InvI )) { String += FString::Printf(TEXT("%sInvI: %s" ), Sep, * InvI [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::M )) { String += FString::Printf(TEXT("%sM: %f" ), Sep, M [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::InvM )) { String += FString::Printf(TEXT("%sInvM: %f" ), Sep, InvM [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::CollisionParticlesSize)) { String += FString::Printf(TEXT("%sCollisionParticlesSize: %d"), Sep, CollisionParticlesSize[Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::Disabled )) { String += FString::Printf(TEXT("%sDisabled: %d" ), Sep, Disabled [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::Sleeping )) { String += FString::Printf(TEXT("%sSleeping: %d" ), Sep, Sleeping [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::Island )) { String += FString::Printf(TEXT("%sIsland: %d" ), Sep, Island [Index] ); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::P )) { String += FString::Printf(TEXT("%sP: %s" ), Sep, * P [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::Q )) { String += FString::Printf(TEXT("%sQ: %s" ), Sep, * Q [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::PreV )) { String += FString::Printf(TEXT("%sPreV: %s" ), Sep, * PreV [Index].ToString()); Sep = Separator; }
if (HasSyncedData(EGeometryCollectionParticlesData::PreW )) { String += FString::Printf(TEXT("%sPreW: %s" ), Sep, * PreW [Index].ToString()); }
return String;
}
// Current chaos particles type
template class TGeometryCollectionParticlesData<float, 3>;
#endif // #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)