Files
UnrealEngineUWP/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestClustering.cpp
Benn Gallagher a44f6f0156 Chaos: Fix headless tests compile after SOA changes in 17869846
#rb none
#jira none
#robomerge 5.0

[CL 17871732 by Benn Gallagher in ue5-main branch]
2021-10-20 08:27:20 -04:00

302 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeadlessChaosTestClustering.h"
#include "HeadlessChaos.h"
#include "Modules/ModuleManager.h"
#include "Chaos/PBDRigidsEvolution.h"
#include "Chaos/PBDRigidParticles.h"
#include "Chaos/Box.h"
#include "Chaos/Plane.h"
#include "Chaos/Sphere.h"
#include "Chaos/ImplicitObject.h"
#include "Chaos/ImplicitObjectTransformed.h"
#include "Chaos/ImplicitObjectUnion.h"
#include "Chaos/Levelset.h"
#include "Chaos/UniformGrid.h"
#include "Chaos/Utilities.h"
#include "Chaos/PBDRigidsEvolutionGBF.h"
namespace ChaosTest {
using namespace Chaos;
void ImplicitCluster()
{
FParticleUniqueIndicesMultithreaded UniqueIndices;
FPBDRigidsSOAs Particles(UniqueIndices);
THandleArray<FChaosPhysicsMaterial> PhysicalMaterials;
FPBDRigidsEvolution Evolution(Particles, PhysicalMaterials);
FPBDRigidClusteredParticles& ClusteredParticles = Particles.GetClusteredParticles();
uint32 FirstId = ClusteredParticles.Size();
FPBDRigidParticleHandle* Box1 = AppendClusteredParticleBox(Particles, FVec3((FReal)100, (FReal)100, (FReal)100));
uint32 BoxId = FirstId++;
FPBDRigidParticleHandle* Box2 = AppendClusteredParticleBox(Particles, FVec3((FReal)100, (FReal)100, (FReal)100));
uint32 Box2Id = FirstId++;
Box2->X() = FVec3((FReal)100, (FReal)0, (FReal)0);
Box2->P() = Box2->X();
Evolution.AdvanceOneTimeStep(0); //hack to initialize islands
//Evolution.InitializeAccelerationStructures(); //make sure islands are created
FClusterCreationParameters ClusterParams;
TArray<Chaos::FPBDRigidParticleHandle*> ClusterChildren;
ClusterChildren.Add(Box1);
ClusterChildren.Add(Box2);
Evolution.GetRigidClustering().CreateClusterParticle(0, MoveTemp(ClusterChildren), ClusterParams);
EXPECT_EQ(ClusteredParticles.Size(), 3);
FVec3 ClusterX = ClusteredParticles.X(2);
FRotation3 ClusterRot = ClusteredParticles.R(2);
EXPECT_TRUE(ClusterX.Equals(FVec3 {(FReal)50, 0, 0}));
EXPECT_TRUE(ClusterRot.Equals(FRotation3::Identity));
EXPECT_TRUE(ClusterX.Equals(ClusteredParticles.P(2)));
EXPECT_TRUE(ClusterRot.Equals(ClusteredParticles.Q(2)));
FRigidTransform3 ClusterTM(ClusterX, ClusterRot);
FVec3 LocalPos = ClusterTM.InverseTransformPositionNoScale(FVec3 {(FReal)200, (FReal)0, (FReal)0});
FVec3 Normal;
FReal Phi = ClusteredParticles.Geometry(2)->PhiWithNormal(LocalPos, Normal);
EXPECT_TRUE(FMath::IsNearlyEqual(Phi, (FReal)50));
EXPECT_TRUE(Normal.Equals(FVec3{(FReal)1, (FReal)0, (FReal)0}));
//EXPECT_TRUE(Evolution.GetParticles().Geometry(2)->IsConvex()); we don't actually guarantee this
}
void FractureCluster()
{
FParticleUniqueIndicesMultithreaded UniqueIndices;
FPBDRigidsSOAs Particles(UniqueIndices);
THandleArray<FChaosPhysicsMaterial> PhysicalMaterials;
FPBDRigidsEvolution Evolution(Particles, PhysicalMaterials);
auto& ClusteredParticles = Particles.GetClusteredParticles();
//create a long row of boxes - the depth 0 cluster is the entire row, the depth 1 clusters 4 boxes each, the depth 2 clusters are 1 box each
constexpr int32 NumBoxes = 32;
TArray<FPBDRigidParticleHandle*> Boxes;
TArray<uint32> BoxIDs;
for (int i = 0; i < NumBoxes; ++i)
{
BoxIDs.Add(ClusteredParticles.Size());
FPBDRigidParticleHandle* Box = AppendClusteredParticleBox(Particles, FVec3((FReal)100, (FReal)100, (FReal)100));
Box->X() = FVec3((FReal)i * (FReal)100, (FReal)0, (FReal)0);
Box->P() = Box->X();
Boxes.Add(Box);
}
Evolution.AdvanceOneTimeStep(0); //hack to generate islands
TArray<Chaos::FPBDRigidParticleHandle* > ClusterHandles;
for (int i = 0; i < NumBoxes / 4; ++i)
{
FClusterCreationParameters ClusterParams;
TArray<Chaos::FPBDRigidParticleHandle*> ClusterChildren;
ClusterChildren.Add(Boxes[i * 4]);
ClusterChildren.Add(Boxes[i * 4+1]);
ClusterChildren.Add(Boxes[i * 4+2]);
ClusterChildren.Add(Boxes[i * 4+3]);
ClusterHandles.Add(Evolution.GetRigidClustering().CreateClusterParticle(0, MoveTemp(ClusterChildren), ClusterParams));
}
FClusterCreationParameters ClusterParams;
Chaos::FPBDRigidParticleHandle* RootClusterHandle = Evolution.GetRigidClustering().CreateClusterParticle(0, MoveTemp(ClusterHandles), ClusterParams);
FVec3 InitialVelocity((FReal)50, (FReal)20, (FReal)100);
RootClusterHandle->V() = InitialVelocity;
constexpr int NumParticles = NumBoxes + NumBoxes / 4 + 1;
EXPECT_EQ(ClusteredParticles.Size(), NumParticles);
for (int i = 0; i < NumParticles-1; ++i)
{
EXPECT_TRUE(ClusteredParticles.Disabled(i));
}
EXPECT_TRUE(RootClusterHandle->Disabled() == false);
EXPECT_EQ(Particles.GetNonDisabledView().Num(), 1);
EXPECT_EQ(Particles.GetNonDisabledView().Begin()->Handle(), RootClusterHandle);
const FReal Dt = 0; //don't want to integrate gravity, just want to test fracture
Evolution.AdvanceOneTimeStep(Dt);
EXPECT_TRUE(RootClusterHandle->Disabled()); //not a cluster anymore, so disabled
for (auto& Particle : Particles.GetNonDisabledView())
{
EXPECT_NE(Particle.Handle(), RootClusterHandle); //view no longer contains root
}
EXPECT_EQ(Particles.GetNonDisabledView().Num(), NumBoxes / 4);
//children are still in a cluster, so disabled
for (uint32 BoxID : BoxIDs)
{
EXPECT_TRUE(ClusteredParticles.Disabled(BoxID));
for (auto& Particle : Particles.GetNonDisabledView())
{
EXPECT_NE(Particle.Handle(), ClusteredParticles.Handle(BoxID)); //make sure boxes are not in non disabled array
}
for (int32 Island = 0; Island < Evolution.NumIslands(); ++Island)
{
EXPECT_TRUE(Evolution.GetIslandParticles(Island).Contains(ClusteredParticles.Handle(BoxID)) == false);
}
}
for (Chaos::FPBDRigidParticleHandle* ClusterHandle : ClusterHandles)
{
EXPECT_TRUE(ClusterHandle->Disabled() == false); //not a cluster anymore, so disabled
bool bFoundInNonDisabled = false;
for (auto& Particle : Particles.GetNonDisabledView())
{
bFoundInNonDisabled |= Particle.Handle() == ClusterHandle;
}
EXPECT_TRUE(bFoundInNonDisabled); //clusters are enabled and in non disabled array
EXPECT_TRUE(ClusterHandle->V().Equals(InitialVelocity));
}
Evolution.AdvanceOneTimeStep(Dt);
//second fracture, all clusters are now disabled
for (Chaos::FPBDRigidParticleHandle* ClusterHandle : ClusterHandles)
{
EXPECT_TRUE(ClusterHandle->Disabled() == true); //not a cluster anymore, so disabled
for (auto& Particle : Particles.GetNonDisabledView())
{
EXPECT_NE(Particle.Handle(), ClusterHandle); //make sure boxes are not in non disabled array
}
for (int32 Island = 0; Island < Evolution.NumIslands(); ++Island)
{
EXPECT_TRUE(Evolution.GetIslandParticles(Island).Contains(ClusterHandle) == false);
}
}
EXPECT_EQ(Particles.GetNonDisabledView().Num(), NumBoxes);
for (FPBDRigidParticleHandle* BoxHandle : Boxes)
{
EXPECT_TRUE(BoxHandle->Disabled() == false);
bool bFoundInNonDisabled = false;
for (auto& Particle : Particles.GetNonDisabledView())
{
bFoundInNonDisabled |= Particle.Handle() == BoxHandle;
}
EXPECT_TRUE(bFoundInNonDisabled);
EXPECT_TRUE(BoxHandle->V().Equals(InitialVelocity));
}
}
void PartialFractureCluster()
{
FParticleUniqueIndicesMultithreaded UniqueIndices;
FPBDRigidsSOAs Particles(UniqueIndices);
THandleArray<FChaosPhysicsMaterial> PhysicalMaterials;
FPBDRigidsEvolution Evolution(Particles, PhysicalMaterials);
auto& ClusteredParticles = Particles.GetClusteredParticles();
//create a long row of boxes - the depth 0 cluster is the entire row, the depth 1 clusters 4 boxes each, the depth 2 clusters are 1 box each
constexpr int32 NumBoxes = 32;
TArray<FPBDRigidParticleHandle*> Boxes;
TArray<uint32> BoxIDs;
for (int i = 0; i < NumBoxes; ++i)
{
BoxIDs.Add(ClusteredParticles.Size());
FPBDRigidParticleHandle* Box = AppendClusteredParticleBox(Particles, FVec3((FReal)100, (FReal)100, (FReal)100));
Box->X() = FVec3((FReal)i * (FReal)100, (FReal)0, (FReal)0);
Box->P() = Box->X();
Boxes.Add(Box);
}
Evolution.AdvanceOneTimeStep(0); //hack to generate islands
TArray<Chaos::FPBDRigidParticleHandle* > ClusterHandles;
for (int i = 0; i < NumBoxes / 4; ++i)
{
FClusterCreationParameters ClusterParams;
TArray<Chaos::FPBDRigidParticleHandle*> ClusterChildren;
ClusterChildren.Add(Boxes[i * 4]);
ClusterChildren.Add(Boxes[i * 4 + 1]);
ClusterChildren.Add(Boxes[i * 4 + 2]);
ClusterChildren.Add(Boxes[i * 4 + 3]);
ClusterHandles.Add(Evolution.GetRigidClustering().CreateClusterParticle(0, MoveTemp(ClusterChildren), ClusterParams));
}
TArray<Chaos::FPBDRigidParticleHandle* > ClusterHandlesDup = ClusterHandles;
FClusterCreationParameters ClusterParams;
Chaos::FPBDRigidParticleHandle* RootClusterHandle = Evolution.GetRigidClustering().CreateClusterParticle(0, MoveTemp(ClusterHandles), ClusterParams);
FVec3 InitialVelocity((FReal)50, (FReal)20, (FReal)100);
RootClusterHandle->V() = InitialVelocity;
TUniquePtr<FChaosPhysicsMaterial> PhysicalMaterial = MakeUnique<FChaosPhysicsMaterial>();
PhysicalMaterial->Friction = 0;
PhysicalMaterial->Restitution = 0;
PhysicalMaterial->SleepingLinearThreshold = 0;
PhysicalMaterial->SleepingAngularThreshold = 0;
PhysicalMaterial->DisabledLinearThreshold = 0;
PhysicalMaterial->DisabledAngularThreshold = 0;
Chaos::TArrayCollectionArray<FReal>& SolverStrainArray = Evolution.GetRigidClustering().GetStrainArray();
for (int i = 0; i < NumBoxes + NumBoxes / 4 + 1; ++i)
{
SolverStrainArray[i] = (FReal)1;
Evolution.SetPhysicsMaterial(ClusteredParticles.Handle(i), MakeSerializable(PhysicalMaterial));
}
Evolution.AdvanceOneTimeStep((FReal)1 / (FReal)60);
EXPECT_TRUE(RootClusterHandle->Disabled() == false); //strain > 0 so no fracture yet
// todo: is this the correct replacement for strain?
static_cast<Chaos::FPBDRigidClusteredParticleHandle*>(ClusterHandlesDup[2])->SetStrain((FReal)0); //fracture the third cluster, this should leave us with three pieces (0, 1), (2), (3,4,5,6,7)
Evolution.AdvanceOneTimeStep((FReal)1 / (FReal)60);
//EXPECT_TRUE(Evolution.GetParticles().Disabled(RootClusterHandle) == false); //one of the connected pieces should re-use this
EXPECT_TRUE(ClusterHandlesDup[2]->Disabled() == false); //this cluster is on its own and should be enabled
EXPECT_EQ(Evolution.GetRigidClustering().GetTopLevelClusterParents().Num(), 3); //there should only be 3 pieces
for (uint32 BoxID : BoxIDs)
{
EXPECT_TRUE(ClusteredParticles.Disabled(BoxID)); //no boxes should be active yet
EXPECT_TRUE(Evolution.GetRigidClustering().GetTopLevelClusterParents().Contains(ClusteredParticles.Handle(BoxID)) == false);
for (int32 Island = 0; Island < Evolution.NumIslands(); ++Island)
{
EXPECT_TRUE(Evolution.GetIslandParticles(Island).Contains(ClusteredParticles.Handle(BoxID)) == false);
}
}
SolverStrainArray[NumBoxes + NumBoxes / 4 + 1] = (FReal)1;
Evolution.SetPhysicsMaterial(ClusteredParticles.Handle(NumBoxes + NumBoxes / 4 + 1), MakeSerializable(PhysicalMaterial));
SolverStrainArray[NumBoxes + NumBoxes / 4 + 2] = (FReal)1;
Evolution.SetPhysicsMaterial(ClusteredParticles.Handle(NumBoxes + NumBoxes / 4 + 2), MakeSerializable(PhysicalMaterial));
Evolution.AdvanceOneTimeStep((FReal)1 / (FReal)60); //next frame nothing should fracture
//EXPECT_TRUE(Evolution.GetParticles().Disabled(RootClusterHandle) == false); //one of the connected pieces should re-use this
EXPECT_TRUE(ClusterHandlesDup[2]->Disabled() == false); //this cluster is on its own and should be enabled
EXPECT_EQ(Evolution.GetRigidClustering().GetTopLevelClusterParents().Num(), 3); //there should only be 3 pieces
for (uint32 BoxID : BoxIDs)
{
EXPECT_TRUE(ClusteredParticles.Disabled(BoxID)); //no boxes should be active yet
EXPECT_TRUE(Evolution.GetRigidClustering().GetTopLevelClusterParents().Contains(ClusteredParticles.Handle(BoxID)) == false);
for (int32 Island = 0; Island < Evolution.NumIslands(); ++Island)
{
EXPECT_TRUE(Evolution.GetIslandParticles(Island).Contains(ClusteredParticles.Handle(BoxID)) == false);
}
}
}
}