Files
UnrealEngine/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestUtility.cpp
2025-05-18 13:04:45 +08:00

748 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeadlessChaosTestUtility.h"
#include "HeadlessChaos.h"
#include "Chaos/Box.h"
#include "Chaos/Convex.h"
#include "Chaos/ErrorReporter.h"
#include "Chaos/Levelset.h"
#include "Chaos/ParticleHandle.h"
#include "Chaos/PBDRigidParticles.h"
#include "Chaos/PBDRigidsEvolutionGBF.h"
#include "Chaos/Plane.h"
#include "Chaos/Sphere.h"
#include "Chaos/Cylinder.h"
#include "Chaos/TaperedCylinder.h"
#include "Chaos/Utilities.h"
namespace ChaosTest {
using namespace Chaos;
int32 AppendAnalyticSphere(FPBDRigidParticles& InParticles, FReal Scale)
{
InParticles.AddParticles(1);
int32 RigidBodyIndex = InParticles.Size() - 1;
InParticles.SetX(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetV(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetR(RigidBodyIndex, FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
InParticles.SetW(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetP(RigidBodyIndex, InParticles.GetX(RigidBodyIndex));
InParticles.SetQ(RigidBodyIndex, InParticles.GetR(RigidBodyIndex));
InParticles.M(RigidBodyIndex) = 1.0;
InParticles.InvM(RigidBodyIndex) = 1.0;
InParticles.I(RigidBodyIndex) = TVec3<FRealSingle>(1, 1, 1);
InParticles.InvI(RigidBodyIndex) = TVec3<FRealSingle>(1, 1, 1);
InParticles.SetGeometry(RigidBodyIndex, MakeImplicitObjectPtr<Chaos::FSphere>(FVec3(0), Scale));
InParticles.SetObjectState(RigidBodyIndex, EObjectStateType::Dynamic);
return RigidBodyIndex;
}
int32 AppendAnalyticBox(FPBDRigidParticles& InParticles, FVec3 Scale)
{
InParticles.AddParticles(1);
int32 RigidBodyIndex = InParticles.Size() - 1;
InParticles.SetX(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetV(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetR(RigidBodyIndex, FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
InParticles.SetW(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetP(RigidBodyIndex, InParticles.GetX(RigidBodyIndex));
InParticles.SetQ(RigidBodyIndex, InParticles.GetR(RigidBodyIndex));
InParticles.M(RigidBodyIndex) = 1.0;
InParticles.InvM(RigidBodyIndex) = 1.0;
InParticles.I(RigidBodyIndex) = TVec3<FRealSingle>(1, 1, 1);
InParticles.InvI(RigidBodyIndex) = TVec3<FRealSingle>(1, 1, 1);
InParticles.SetGeometry(RigidBodyIndex, MakeImplicitObjectPtr<TBox<FReal, 3>>(-Scale / 2.0, Scale / 2.0));
InParticles.SetObjectState(RigidBodyIndex, EObjectStateType::Dynamic);
return RigidBodyIndex;
}
void InitAnalyticBox2(FKinematicGeometryParticleHandle* Particle, FVec3 Scale)
{
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
Particle->SetGeometry(MakeImplicitObjectPtr<TBox<FReal, 3>>(-Scale / 2.0, Scale / 2.0));
FPBDRigidParticleHandle* DynamicParticle = Particle->CastToRigidParticle();
if(DynamicParticle && DynamicParticle->ObjectState() == EObjectStateType::Dynamic)
{
DynamicParticle->SetP(Particle->GetX());
DynamicParticle->SetQ(Particle->GetR());
DynamicParticle->M() = 1.0;
DynamicParticle->InvM() = 1.0;
DynamicParticle->I() = TVec3<FRealSingle>(1, 1, 1);
DynamicParticle->InvI() = TVec3<FRealSingle>(1, 1, 1);
}
}
int32 AppendParticleBox(FPBDRigidParticles& InParticles, FVec3 Scale, TArray<TVec3<int32>>* elements)
{
InParticles.AddParticles(1);
int32 RigidBodyIndex = InParticles.Size() - 1;
InParticles.SetX(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetV(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetR(RigidBodyIndex, FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
InParticles.SetW(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetP(RigidBodyIndex, InParticles.GetX(RigidBodyIndex));
InParticles.SetQ(RigidBodyIndex, InParticles.GetR(RigidBodyIndex));
check(Scale.X == Scale.Y && Scale.X == Scale.Z);
FReal ScaleSq = Scale.X * Scale.X;
InParticles.M(RigidBodyIndex) = 1.0;
InParticles.InvM(RigidBodyIndex) = 1.0;
InParticles.I(RigidBodyIndex) = TVec3<FRealSingle>(ScaleSq / 6.0, ScaleSq / 6.0, ScaleSq / 6.0);
InParticles.InvI(RigidBodyIndex) = TVec3<FRealSingle>(6.0 / ScaleSq, 6.0 / ScaleSq, 6.0 / ScaleSq);
InParticles.SetGeometry(RigidBodyIndex, MakeImplicitObjectPtr<TBox<FReal, 3>>(-Scale / 2.0, Scale / 2.0));
InParticles.SetObjectState(RigidBodyIndex, EObjectStateType::Dynamic);
int32 CollisionIndex = 0;
InParticles.CollisionParticlesInitIfNeeded(RigidBodyIndex);
InParticles.CollisionParticles(RigidBodyIndex)->AddParticles(8);
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, -Scale[1] / 2.0, -Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, -Scale[1] / 2.0, -Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, +Scale[1] / 2.0, -Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, +Scale[1] / 2.0, -Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, -Scale[1] / 2.0, +Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, -Scale[1] / 2.0, +Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, +Scale[1] / 2.0, +Scale[2] / 2.0));
InParticles.CollisionParticles(RigidBodyIndex)->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, +Scale[1] / 2.0, +Scale[2] / 2.0));
if (elements != nullptr)
{
/*
//cw
elements->Add(TVec3<int32>(4, 1, 5)); // Front
elements->Add(TVec3<int32>(1, 4, 0));
elements->Add(TVec3<int32>(7, 2, 6)); // Back
elements->Add(TVec3<int32>(2, 7, 3));
elements->Add(TVec3<int32>(6, 0, 4)); // Right
elements->Add(TVec3<int32>(0, 6, 2));
elements->Add(TVec3<int32>(5, 3, 7)); // Left
elements->Add(TVec3<int32>(3, 5, 1));
elements->Add(TVec3<int32>(6, 5, 7)); // Top
elements->Add(TVec3<int32>(5, 6, 4));
elements->Add(TVec3<int32>(0, 2, 1)); // Front
elements->Add(TVec3<int32>(2, 0, 3));
*/
//ccw
elements->Add(TVec3<int32>(1,4,5)); // Front
elements->Add(TVec3<int32>(4,1,0));
elements->Add(TVec3<int32>(2,7,6)); // Back
elements->Add(TVec3<int32>(7,2,3));
elements->Add(TVec3<int32>(0,6,4)); // Right
elements->Add(TVec3<int32>(6,0,2));
elements->Add(TVec3<int32>(3,5,7)); // Left
elements->Add(TVec3<int32>(5,3,1));
elements->Add(TVec3<int32>(5,6,7)); // Top
elements->Add(TVec3<int32>(6,5,4));
elements->Add(TVec3<int32>(2,0,1)); // Front
elements->Add(TVec3<int32>(0,2,3));
}
return RigidBodyIndex;
}
void InitDynamicParticleBox2(FPBDRigidParticleHandle* Particle, const FVec3& Scale, FReal Margin, TArray<TVector<int32, 3>>* OutElements)
{
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
Particle->SetP(Particle->GetX());
Particle->SetQ(Particle->GetR());
// Assume unit mass - this gets scaled externally by the actual mass
const FVec3 ScaleSq = Scale * Scale;
Particle->M() = 1.0;
Particle->InvM() = 1.0;
Particle->I() = (1.0 / 12.0) * TVec3<FRealSingle>(ScaleSq.Y + ScaleSq.Z, ScaleSq.X + ScaleSq.Z, ScaleSq.X + ScaleSq.Y);
Particle->InvI() = TVec3<FRealSingle>(6.0 / ScaleSq);
Particle->SetGeometry(MakeImplicitObjectPtr<TBox<FReal, 3>>(-Scale / 2.0, Scale / 2.0, Margin));
int32 CollisionIndex = 0;
Particle->CollisionParticlesInitIfNeeded();
Particle->CollisionParticles()->AddParticles(8);
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, -Scale[1] / 2.0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, -Scale[1] / 2.0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, +Scale[1] / 2.0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, +Scale[1] / 2.0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, -Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, -Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, +Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, +Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->SetLocalBounds(Particle->GetGeometry()->BoundingBox());
Particle->UpdateWorldSpaceState(FRigidTransform3::Identity, FVec3(0));
Particle->SetHasBounds(true);
if (OutElements != nullptr)
{
/*
//cw
OutElements->Add(TVec3<int32>(4, 1, 5)); // Front
OutElements->Add(TVec3<int32>(1, 4, 0));
OutElements->Add(TVec3<int32>(7, 2, 6)); // Back
OutElements->Add(TVec3<int32>(2, 7, 3));
OutElements->Add(TVec3<int32>(6, 0, 4)); // Right
OutElements->Add(TVec3<int32>(0, 6, 2));
OutElements->Add(TVec3<int32>(5, 3, 7)); // Left
OutElements->Add(TVec3<int32>(3, 5, 1));
OutElements->Add(TVec3<int32>(6, 5, 7)); // Top
OutElements->Add(TVec3<int32>(5, 6, 4));
OutElements->Add(TVec3<int32>(0, 2, 1)); // Front
OutElements->Add(TVec3<int32>(2, 0, 3));
*/
//ccw
OutElements->Add(TVec3<int32>(1, 4, 5)); // Front
OutElements->Add(TVec3<int32>(4, 1, 0));
OutElements->Add(TVec3<int32>(2, 7, 6)); // Back
OutElements->Add(TVec3<int32>(7, 2, 3));
OutElements->Add(TVec3<int32>(0, 6, 4)); // Right
OutElements->Add(TVec3<int32>(6, 0, 2));
OutElements->Add(TVec3<int32>(3, 5, 7)); // Left
OutElements->Add(TVec3<int32>(5, 3, 1));
OutElements->Add(TVec3<int32>(5, 6, 7)); // Top
OutElements->Add(TVec3<int32>(6, 5, 4));
OutElements->Add(TVec3<int32>(2, 0, 1)); // Front
OutElements->Add(TVec3<int32>(0, 2, 3));
}
::ChaosTest::SetParticleSimDataToCollide({ Particle});
}
void InitDynamicParticleSphere2(FPBDRigidParticleHandle* Particle, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
Particle->SetP(Particle->GetX());
Particle->SetQ(Particle->GetR());
check(Scale.X == Scale.Y && Scale.X == Scale.Z);
FReal ScaleSq = Scale.X * Scale.X;
Particle->M() = 1.0;
Particle->InvM() = 1.0;
Particle->I() = TVec3<FRealSingle>(ScaleSq / 6.0);
Particle->InvI() = TVec3<FRealSingle>(6.0 / ScaleSq);
Particle->SetGeometry(MakeImplicitObjectPtr<Chaos::FSphere>(FVec3(0), Scale.X / 2.0));
int32 CollisionIndex = 0;
Particle->CollisionParticlesInitIfNeeded();
Particle->CollisionParticles()->AddParticles(6);
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, 0, 0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, 0, 0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, -Scale[1] / 2.0, 0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, +Scale[1] / 2.0, 0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, 0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, 0, +Scale[2] / 2.0));
if (OutElements != nullptr)
{
/*
//cw
OutElements->Add(TVec3<int32>(4, 1, 5)); // Front
OutElements->Add(TVec3<int32>(1, 4, 0));
OutElements->Add(TVec3<int32>(7, 2, 6)); // Back
OutElements->Add(TVec3<int32>(2, 7, 3));
OutElements->Add(TVec3<int32>(6, 0, 4)); // Right
OutElements->Add(TVec3<int32>(0, 6, 2));
OutElements->Add(TVec3<int32>(5, 3, 7)); // Left
OutElements->Add(TVec3<int32>(3, 5, 1));
OutElements->Add(TVec3<int32>(6, 5, 7)); // Top
OutElements->Add(TVec3<int32>(5, 6, 4));
OutElements->Add(TVec3<int32>(0, 2, 1)); // Front
OutElements->Add(TVec3<int32>(2, 0, 3));
*/
//ccw
OutElements->Add(TVec3<int32>(1, 4, 5)); // Front
OutElements->Add(TVec3<int32>(4, 1, 0));
OutElements->Add(TVec3<int32>(2, 7, 6)); // Back
OutElements->Add(TVec3<int32>(7, 2, 3));
OutElements->Add(TVec3<int32>(0, 6, 4)); // Right
OutElements->Add(TVec3<int32>(6, 0, 2));
OutElements->Add(TVec3<int32>(3, 5, 7)); // Left
OutElements->Add(TVec3<int32>(5, 3, 1));
OutElements->Add(TVec3<int32>(5, 6, 7)); // Top
OutElements->Add(TVec3<int32>(6, 5, 4));
OutElements->Add(TVec3<int32>(2, 0, 1)); // Front
OutElements->Add(TVec3<int32>(0, 2, 3));
}
}
void InitDynamicParticleCylinder2(FPBDRigidParticleHandle* Particle, const FVec3& Scale, TArray<TVec3<int32>>* OutElements, bool Tapered)
{
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
Particle->SetP(Particle->GetX());
Particle->SetQ(Particle->GetR());
check(Scale.X == Scale.Y && Scale.X == Scale.Z);
FReal ScaleSq = Scale.X * Scale.X;
Particle->M() = 1.0;
Particle->InvM() = 1.0;
Particle->I() = TVec3<FRealSingle>(ScaleSq / 6.0);
Particle->InvI() = TVec3<FRealSingle>(6.0 / ScaleSq);
if (Tapered)
{
Particle->SetGeometry(MakeImplicitObjectPtr<FTaperedCylinder>(FVec3(0, 0, Scale.X / 2.0), FVec3(0, 0, -Scale.X / 2.0), Scale.X / 2.0, Scale.X / 2.0));
}
else
{
Particle->SetGeometry(MakeImplicitObjectPtr<FCylinder>(FVec3(0, 0, Scale.X / 2.0), FVec3(0, 0, -Scale.X / 2.0), Scale.X / 2.0));
}
int32 CollisionIndex = 0;
Particle->CollisionParticlesInitIfNeeded();
Particle->CollisionParticles()->AddParticles(8);
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, 0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(-Scale[0] / 2.0, 0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, 0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(+Scale[0] / 2.0, 0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, -Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, -Scale[1] / 2.0, -Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, +Scale[1] / 2.0, +Scale[2] / 2.0));
Particle->CollisionParticles()->SetX(CollisionIndex++, FVec3(0, +Scale[1] / 2.0, -Scale[2] / 2.0));
if (OutElements != nullptr)
{
/*
//cw
OutElements->Add(TVec3<int32>(4, 1, 5)); // Front
OutElements->Add(TVec3<int32>(1, 4, 0));
OutElements->Add(TVec3<int32>(7, 2, 6)); // Back
OutElements->Add(TVec3<int32>(2, 7, 3));
OutElements->Add(TVec3<int32>(6, 0, 4)); // Right
OutElements->Add(TVec3<int32>(0, 6, 2));
OutElements->Add(TVec3<int32>(5, 3, 7)); // Left
OutElements->Add(TVec3<int32>(3, 5, 1));
OutElements->Add(TVec3<int32>(6, 5, 7)); // Top
OutElements->Add(TVec3<int32>(5, 6, 4));
OutElements->Add(TVec3<int32>(0, 2, 1)); // Front
OutElements->Add(TVec3<int32>(2, 0, 3));
*/
//ccw
OutElements->Add(TVec3<int32>(1, 4, 5)); // Front
OutElements->Add(TVec3<int32>(4, 1, 0));
OutElements->Add(TVec3<int32>(2, 7, 6)); // Back
OutElements->Add(TVec3<int32>(7, 2, 3));
OutElements->Add(TVec3<int32>(0, 6, 4)); // Right
OutElements->Add(TVec3<int32>(6, 0, 2));
OutElements->Add(TVec3<int32>(3, 5, 7)); // Left
OutElements->Add(TVec3<int32>(5, 3, 1));
OutElements->Add(TVec3<int32>(5, 6, 7)); // Top
OutElements->Add(TVec3<int32>(6, 5, 4));
OutElements->Add(TVec3<int32>(2, 0, 1)); // Front
OutElements->Add(TVec3<int32>(0, 2, 3));
}
}
FPBDRigidParticleHandle* AppendDynamicParticleBox(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
InitDynamicParticleBox2(Particles[0], Scale, 0.0f, OutElements);
return Particles[0];
}
// Create a particle with box collision of specified size and margin (size includes margin)
FPBDRigidParticleHandle* AppendDynamicParticleBoxMargin(FPBDRigidsSOAs& SOAs, const FVec3& Scale, FReal Margin, TArray<TVec3<int32>>* OutElements)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
InitDynamicParticleBox2(Particles[0], Scale, Margin, OutElements);
return Particles[0];
}
FPBDRigidParticleHandle* AppendDynamicParticleSphere(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
InitDynamicParticleSphere2(Particles[0], Scale, OutElements);
return Particles[0];
}
FPBDRigidParticleHandle* AppendDynamicParticleCylinder(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
InitDynamicParticleCylinder2(Particles[0], Scale, OutElements, false);
return Particles[0];
}
FPBDRigidParticleHandle* AppendDynamicParticleTaperedCylinder(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
InitDynamicParticleCylinder2(Particles[0], Scale, OutElements, true);
return Particles[0];
}
FPBDRigidParticleHandle* AppendClusteredParticleBox(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
auto Particles = SOAs.CreateClusteredParticles(1);
InitDynamicParticleBox2(Particles[0], Scale, 0.0f, OutElements);
return Particles[0];
}
void InitStaticParticleBox(FGeometryParticleHandle* Particle, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
Particle->SetX(FVec3(0, 0, 0));
Particle->SetR(FRotation3::FromIdentity());
FReal ScaleSq = Scale.X * Scale.X;
Particle->SetGeometry(MakeImplicitObjectPtr<TBox<FReal, 3>>(-Scale / 2.0, Scale / 2.0));
// This is needed for calculating contacts (Bounds are bigger than they need to be, even allowing for rotation)
Particle->SetLocalBounds(FAABB3(FVec3(-Scale[0]), FVec3(Scale[0])));
Particle->UpdateWorldSpaceState(FRigidTransform3::Identity, FVec3(0));
Particle->SetHasBounds(true);
if (OutElements != nullptr)
{
/*
//cw
OutElements->Add(TVec3<int32>(4, 1, 5)); // Front
OutElements->Add(TVec3<int32>(1, 4, 0));
OutElements->Add(TVec3<int32>(7, 2, 6)); // Back
OutElements->Add(TVec3<int32>(2, 7, 3));
OutElements->Add(TVec3<int32>(6, 0, 4)); // Right
OutElements->Add(TVec3<int32>(0, 6, 2));
OutElements->Add(TVec3<int32>(5, 3, 7)); // Left
OutElements->Add(TVec3<int32>(3, 5, 1));
OutElements->Add(TVec3<int32>(6, 5, 7)); // Top
OutElements->Add(TVec3<int32>(5, 6, 4));
OutElements->Add(TVec3<int32>(0, 2, 1)); // Front
OutElements->Add(TVec3<int32>(2, 0, 3));
*/
//ccw
OutElements->Add(TVec3<int32>(1, 4, 5)); // Front
OutElements->Add(TVec3<int32>(4, 1, 0));
OutElements->Add(TVec3<int32>(2, 7, 6)); // Back
OutElements->Add(TVec3<int32>(7, 2, 3));
OutElements->Add(TVec3<int32>(0, 6, 4)); // Right
OutElements->Add(TVec3<int32>(6, 0, 2));
OutElements->Add(TVec3<int32>(3, 5, 7)); // Left
OutElements->Add(TVec3<int32>(5, 3, 1));
OutElements->Add(TVec3<int32>(5, 6, 7)); // Top
OutElements->Add(TVec3<int32>(6, 5, 4));
OutElements->Add(TVec3<int32>(2, 0, 1)); // Front
OutElements->Add(TVec3<int32>(0, 2, 3));
}
::ChaosTest::SetParticleSimDataToCollide({ Particle });
}
FGeometryParticleHandle* AppendStaticParticleBox(FPBDRigidsSOAs& SOAs, const FVec3& Scale, TArray<TVec3<int32>>* OutElements)
{
TArray<FGeometryParticleHandle*> Particles = SOAs.CreateStaticParticles(1);
InitStaticParticleBox(Particles[0], Scale, OutElements);
return Particles[0];
}
int AppendStaticAnalyticFloor(FPBDRigidParticles& InParticles)
{
InParticles.AddParticles(1);
int32 RigidBodyIndex = InParticles.Size() - 1;
InParticles.SetX(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetV(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.SetR(RigidBodyIndex, FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
InParticles.SetW(RigidBodyIndex, FVec3(0, 0, 0));
InParticles.M(RigidBodyIndex) = 1.0;
InParticles.InvM(RigidBodyIndex) = 0.0;
InParticles.I(RigidBodyIndex) = TVec3<FRealSingle>(1);
InParticles.InvI(RigidBodyIndex) = TVec3<FRealSingle>(0);
InParticles.SetGeometry(RigidBodyIndex, MakeImplicitObjectPtr<TPlane<FReal, 3>>(FVec3(0, 0, 0), FVec3(0, 0, 1)));
InParticles.SetObjectState(RigidBodyIndex, EObjectStateType::Kinematic);
InParticles.SetP(RigidBodyIndex, InParticles.GetX(RigidBodyIndex));
InParticles.SetQ(RigidBodyIndex, InParticles.GetR(RigidBodyIndex));
return RigidBodyIndex;
}
FKinematicGeometryParticleHandle* AppendStaticAnalyticFloor(FPBDRigidsSOAs& SOAs)
{
TArray<FKinematicGeometryParticleHandle*> Particles = SOAs.CreateKinematicParticles(1);
FKinematicGeometryParticleHandle* Particle = Particles[0];
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
Particle->SetGeometry(MakeImplicitObjectPtr<TPlane<FReal, 3>>(FVec3(0, 0, 0), FVec3(0, 0, 1)));
::ChaosTest::SetParticleSimDataToCollide({ Particle });
return Particle;
}
FKinematicGeometryParticleHandle* AppendStaticConvexFloor(FPBDRigidsSOAs& SOAs)
{
TArray<FKinematicGeometryParticleHandle*> Particles = SOAs.CreateKinematicParticles(1);
FKinematicGeometryParticleHandle* Particle = Particles[0];
Particle->SetX(FVec3(0, 0, 0));
Particle->SetV(FVec3(0, 0, 0));
Particle->SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
Particle->SetW(FVec3(0, 0, 0));
TArray<Chaos::FConvex::FVec3Type> Cube;
Cube.SetNum(9);
Cube[0] = { -1000, -1000, -20 };
Cube[1] = { -1000, -1000, 0 };
Cube[2] = { -1000, 1000, -20 };
Cube[3] = { -1000, 1000, 0 };
Cube[4] = { 1000, -1000, -20 };
Cube[5] = { 1000, -1000, 0 };
Cube[6] = { 1000, 1000, -20 };
Cube[7] = { 1000, 1000, 0 };
Cube[8] = { 0, 0, 0 };
Particle->SetGeometry(MakeImplicitObjectPtr<FConvex>(Cube, 0.0f));
::ChaosTest::SetParticleSimDataToCollide({ Particle });
return Particle;
}
/**/
FLevelSet ConstructLevelset(FParticles & SurfaceParticles, TArray<TVec3<int32>> & Elements)
{
// build Particles and bounds
Chaos::FAABB3 BoundingBox(FVec3(0), FVec3(0));
for (int32 CollisionParticleIndex = 0; CollisionParticleIndex < (int32)SurfaceParticles.Size(); CollisionParticleIndex++)
{
BoundingBox.GrowToInclude(SurfaceParticles.GetX(CollisionParticleIndex));
}
// build cell domain
int32 MaxAxisSize = 10;
int MaxAxis = BoundingBox.LargestAxis();
FVec3 Extents = BoundingBox.Extents();
Chaos::TVec3<int32> Counts(MaxAxisSize * Extents[0] / Extents[MaxAxis], MaxAxisSize * Extents[1] / Extents[MaxAxis], MaxAxisSize * Extents[2] / Extents[MaxAxis]);
Counts[0] = Counts[0] < 1 ? 1 : Counts[0];
Counts[1] = Counts[1] < 1 ? 1 : Counts[1];
Counts[2] = Counts[2] < 1 ? 1 : Counts[2];
TUniformGrid<FReal, 3> Grid(BoundingBox.Min(), BoundingBox.Max(), Counts, 1);
FTriangleMesh CollisionMesh(MoveTemp(Elements));
FErrorReporter ErrorReporter;
return FLevelSet(ErrorReporter, Grid, SurfaceParticles, CollisionMesh);
}
/**/
void AppendDynamicParticleConvexBox(FPBDRigidParticleHandle& InParticles, const FVec3& Scale, FReal Margin)
{
FConvex::FVec3Type ScaleFloat{ FRealSingle(Scale.X), FRealSingle(Scale.Y), FRealSingle(Scale.Z) };
TArray<FConvex::FVec3Type> Cube;
Cube.SetNum(9);
Cube[0] = FConvex::FVec3Type{ -1, -1, -1 } * ScaleFloat;
Cube[1] = FConvex::FVec3Type{ -1, -1, 1 } * ScaleFloat;
Cube[2] = FConvex::FVec3Type{ -1, 1, -1 } * ScaleFloat;
Cube[3] = FConvex::FVec3Type{ -1, 1, 1 } * ScaleFloat;
Cube[4] = FConvex::FVec3Type{ 1, -1, -1 } * ScaleFloat;
Cube[5] = FConvex::FVec3Type{ 1, -1, 1 } * ScaleFloat;
Cube[6] = FConvex::FVec3Type{ 1, 1, -1 } * ScaleFloat;
Cube[7] = FConvex::FVec3Type{ 1, 1, 1 } * ScaleFloat;
Cube[8] = FConvex::FVec3Type{ 0, 0, 0 };
InParticles.SetX(FVec3(0, 0, 0));
InParticles.SetV(FVec3(0, 0, 0));
InParticles.SetR(FRotation3::MakeFromEuler(FVec3(0, 0, 0)).GetNormalized());
InParticles.SetW(FVec3(0, 0, 0));
InParticles.SetP(InParticles.GetX());
InParticles.SetQ(InParticles.GetR());
// TODO: Change this error prone API to set bounds more automatically. This is easy to forget
InParticles.SetLocalBounds(FAABB3(Cube[0], Cube[7]));
InParticles.UpdateWorldSpaceState(FRigidTransform3::Identity, FVec3(0));
InParticles.SetHasBounds(true);
InParticles.M() = 1.0;
InParticles.InvM() = 1.0;
InParticles.I() = TVec3<FRealSingle>(1);
InParticles.InvI() = TVec3<FRealSingle>(1);
InParticles.SetGeometry(MakeImplicitObjectPtr<FConvex>(Cube, Margin));
InParticles.SetObjectStateLowLevel(EObjectStateType::Dynamic);
::ChaosTest::SetParticleSimDataToCollide({ &InParticles });
//for (const TUniquePtr<Chaos::FPerShapeData>& Shape : InParticles.ShapesArray())
//{
// Shape->SimData.Word3 = 1;
// Shape->SimData.Word1 = 1;
//}
}
FPBDRigidParticleHandle* AppendDynamicParticleConvexBox(FPBDRigidsSOAs& SOAs, const FVec3& Scale)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
AppendDynamicParticleConvexBox(*Particles[0], Scale, 0.0f);
return Particles[0];
}
FPBDRigidParticleHandle* AppendDynamicParticleConvexBoxMargin(FPBDRigidsSOAs& SOAs, const FVec3& Scale, FReal Margin)
{
TArray<FPBDRigidParticleHandle*> Particles = SOAs.CreateDynamicParticles(1);
AppendDynamicParticleConvexBox(*Particles[0], Scale, Margin);
return Particles[0];
}
/**/
FVec3 ObjectSpacePoint(FPBDRigidParticles& InParticles, const int32 Index, const FVec3& WorldSpacePoint)
{
FRigidTransform3 LocalToWorld(InParticles.GetX(Index), InParticles.GetR(Index));
return LocalToWorld.InverseTransformPosition(WorldSpacePoint);
}
FVec3 ObjectSpacePoint(FGeometryParticleHandle& Particle, const FVec3& WorldSpacePoint)
{
FRigidTransform3 LocalToWorld(Particle.GetX(), Particle.GetR());
return LocalToWorld.InverseTransformPosition(WorldSpacePoint);
}
/**/
FReal PhiWithNormal(FPBDRigidParticles& InParticles, const int32 Index, const FVec3& WorldSpacePoint, FVec3& Normal)
{
FRigidTransform3(InParticles.GetX(Index), InParticles.GetR(Index));
FVec3 BodySpacePoint = ObjectSpacePoint(InParticles, Index, WorldSpacePoint);
FReal LocalPhi = InParticles.GetGeometry(Index)->PhiWithNormal(BodySpacePoint, Normal);
Normal = FRigidTransform3(InParticles.GetX(Index), InParticles.GetR(Index)).TransformVector(Normal);
return LocalPhi;
}
/**/
FReal SignedDistance(FPBDRigidParticles& InParticles, const int32 Index, const FVec3& WorldSpacePoint)
{
FVec3 Normal;
return PhiWithNormal(InParticles, Index, WorldSpacePoint, Normal);
}
/**/
FReal PhiWithNormal(FGeometryParticleHandle& Particle, const FVec3& WorldSpacePoint, FVec3& Normal)
{
FRigidTransform3(Particle.GetX(), Particle.GetR());
FVec3 BodySpacePoint = ObjectSpacePoint(Particle, WorldSpacePoint);
FReal LocalPhi = Particle.GetGeometry()->PhiWithNormal(BodySpacePoint, Normal);
Normal = FRigidTransform3(Particle.GetX(), Particle.GetR()).TransformVector(Normal);
return LocalPhi;
}
/**/
FReal SignedDistance(FGeometryParticleHandle& Particle, const FVec3& WorldSpacePoint)
{
FVec3 Normal;
return PhiWithNormal(Particle, WorldSpacePoint, Normal);
}
/**/
FVec3 RandAxis()
{
for (int32 It = 0; It < 1000; ++It)
{
FVec3 Point = FVec3(FMath::RandRange(-1.0f, 1.0f), FMath::RandRange(-1.0f, 1.0f), FMath::RandRange(-1.0f, 1.0f));
if (Point.Size() > KINDA_SMALL_NUMBER)
{
return FVec3(Point.GetSafeNormal());
}
}
return FVec3(FVector::UpVector);
}
void SetParticleSimDataToCollide(TArray< Chaos::FGeometryParticle* > ParticleArray)
{
for (Chaos::FGeometryParticle* Particle : ParticleArray)
{
for (const TUniquePtr<Chaos::FPerShapeData>& Shape :Particle->ShapesArray())
{
Shape->ModifySimData([](auto& SimData)
{
SimData.Word3 = 1;
SimData.Word1 = 1;
});
}
}
}
void SetParticleSimDataToCollide(TArray< Chaos::FGeometryParticleHandle* > ParticleArray)
{
for (Chaos::FGeometryParticleHandle* Particle : ParticleArray)
{
for (const TUniquePtr<Chaos::FPerShapeData>& Shape : Particle->ShapesArray())
{
Shape->ModifySimData([](auto& SimData)
{
SimData.Word3 = 1;
SimData.Word1 = 1;
});
}
}
}
TArray<FConvex::FVec3Type> MakeBoxVerts(const FConvex::FVec3Type& Center, const FConvex::FVec3Type& HalfSize)
{
return
{
Center + FConvex::FVec3Type(-HalfSize.X, -HalfSize.Y, -HalfSize.Z),
Center + FConvex::FVec3Type(-HalfSize.X, HalfSize.Y, -HalfSize.Z),
Center + FConvex::FVec3Type(HalfSize.X, HalfSize.Y, -HalfSize.Z),
Center + FConvex::FVec3Type(HalfSize.X, -HalfSize.Y, -HalfSize.Z),
Center + FConvex::FVec3Type(-HalfSize.X, -HalfSize.Y, HalfSize.Z),
Center + FConvex::FVec3Type(-HalfSize.X, HalfSize.Y, HalfSize.Z),
Center + FConvex::FVec3Type(HalfSize.X, HalfSize.Y, HalfSize.Z),
Center + FConvex::FVec3Type(HalfSize.X, -HalfSize.Y, HalfSize.Z),
};
}
FImplicitConvex3 CreateConvexBox(const FConvex::FVec3Type& BoxSize, const FReal Margin)
{
const FConvex::FVec3Type HalfSize = 0.5f * BoxSize;
return FImplicitConvex3(MakeBoxVerts(FConvex::FVec3Type(0), HalfSize), Margin);
}
FImplicitConvex3 CreateConvexBox(const FConvex::FVec3Type& BoxMin, const FConvex::FVec3Type& BoxMax, const FReal Margin)
{
const FConvex::FVec3Type Center = 0.5f * (BoxMin + BoxMax);
const FConvex::FVec3Type HalfSize = 0.5f * (BoxMax - BoxMin);
return FImplicitConvex3(MakeBoxVerts(Center, HalfSize), Margin);
}
TImplicitObjectInstanced<FImplicitConvex3> CreateInstancedConvexBox(const FConvex::FVec3Type& BoxSize, const FReal Margin)
{
const FConvex::FVec3Type HalfSize = 0.5f * BoxSize;
FConvexPtr BoxConvex( new FImplicitConvex3(MakeBoxVerts(FConvex::FVec3Type(0), HalfSize), 0.0f));
return TImplicitObjectInstanced<FImplicitConvex3>(BoxConvex, Margin);
}
TImplicitObjectScaled<FImplicitConvex3> CreateScaledConvexBox(const FConvex::FVec3Type& BoxSize, const FVec3 BoxScale, const FReal Margin)
{
const FConvex::FVec3Type HalfSize = 0.5f * BoxSize;
FConvexPtr BoxConvex( new FImplicitConvex3(MakeBoxVerts(FConvex::FVec3Type(0), HalfSize), 0.0f));
return TImplicitObjectScaled<FImplicitConvex3>(BoxConvex, BoxScale, Margin);
}
}