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

1299 lines
57 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "GeometryCollection/GeometryCollectionTestSimulationField.h"
#include "GeometryCollection/GeometryCollectionTestUtility.h"
#include "GeometryCollection/GeometryCollectionTestFramework.h"
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollection/GeometryCollectionUtility.h"
#include "GeometryCollection/GeometryCollectionAlgo.h"
#include "GeometryCollection/TransformCollection.h"
#include "UObject/Package.h"
#include "UObject/UObjectGlobals.h"
#include "Field/FieldSystem.h"
#include "Field/FieldSystemNodes.h"
#include "GeometryCollectionProxyData.h"
#include "GeometryCollection/GeometryCollectionSimulationTypes.h"
#include "PhysicsProxy/PhysicsProxies.h"
#include "Chaos/ErrorReporter.h"
#include "Chaos/PBDRigidClustering.h"
#include "PBDRigidsSolver.h"
#include "ChaosSolversModule.h"
#include "HeadlessChaosTestUtility.h"
//#include "GeometryCollection/GeometryCollectionAlgo.h"
#define SMALL_THRESHOLD 1e-4
// #TODO Lots of duplication in here, anyone making solver or object changes
// has to go and fix up so many callsites here and they're all pretty much
// Identical. The similar code should be pulled out
namespace GeometryCollectionTest
{
using namespace ChaosTest;
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_KinematicActivationOnProxyDuringInit)
{
const FVector Translation0(0, 0, 1);
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Kinematic;
Params.RootTransform.SetLocation(Translation0);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
Params.RootTransform.SetLocation(FVector(100, 0, 0));
FGeometryCollectionWrapper* CollectionOther = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
FFramework UnitTest;
UnitTest.AddSimulationObject(CollectionOther);
UnitTest.AddSimulationObject(Collection);
FRadialIntMask* RadialMaskTmp = new FRadialIntMask();
RadialMaskTmp->Position = FVector(0.0, 0.0, 0.0);
RadialMaskTmp->Radius = 100.0;
RadialMaskTmp->InteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMaskTmp->ExteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic;
RadialMaskTmp->SetMaskCondition = ESetMaskConditionType::Field_Set_IFF_NOT_Interior;
FName TargetNameTmp = GetFieldPhysicsName(EFieldPhysicsType::Field_DynamicState);
Collection->PhysObject->BufferFieldCommand_Internal(UnitTest.Solver, { TargetNameTmp, RadialMaskTmp });
UnitTest.Initialize();
UnitTest.Advance();
UnitTest.Solver->RegisterSimOneShotCallback([&]()
{
EXPECT_EQ(CollectionOther->DynamicCollection->DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic);
EXPECT_EQ(Collection->DynamicCollection->DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic);
});
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_KinematicActivationOnProxyDuringUpdate)
{
const FVector Translation0(0, 0, 1);
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Kinematic;
Params.RootTransform.SetLocation(Translation0);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
FFramework UnitTest; UnitTest.Dt = 1 / 24.0;
UnitTest.AddSimulationObject(Collection);
UnitTest.Initialize();
{
UnitTest.Advance();
}
TManagedArray<uint8>& DynamicState = Collection->DynamicCollection->DynamicState;
EXPECT_EQ(DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic);
// simulated
EXPECT_EQ(Collection->DynamicCollection->GetNumTransforms(), 1);
const FVector Translation1 = FVector(Collection->DynamicCollection->GetTransform(0).GetTranslation());
EXPECT_NEAR((Translation0 - Translation1).Size(), 0.f, KINDA_SMALL_NUMBER);
EXPECT_NEAR(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 1.f, KINDA_SMALL_NUMBER);
FRadialIntMask* RadialMask = new FRadialIntMask();
RadialMask->Position = FVector(0.0, 0.0, 0.0);
RadialMask->Radius = 100.0;
RadialMask->InteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMask->ExteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic;
RadialMask->SetMaskCondition = ESetMaskConditionType::Field_Set_IFF_NOT_Interior;
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_DynamicState);
Collection->PhysObject->BufferFieldCommand_Internal(UnitTest.Solver, { TargetName, RadialMask });
{
UnitTest.Advance();
}
EXPECT_EQ(DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic);
const FVector Translation2 = FVector(Collection->DynamicCollection->GetTransform(0).GetTranslation());
EXPECT_NE(Translation1, Translation2);
EXPECT_LE(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 0.f);
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_KinematicActivation)
{
const FVector Translation0(0, 0, 1);
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Kinematic;
Params.RootTransform.SetLocation(Translation0);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
FFramework UnitTest;
UnitTest.AddSimulationObject(Collection);
UnitTest.Initialize();
for (int i = 0; i < 100; i++)
{
UnitTest.Advance();
}
// simulated
EXPECT_EQ(Collection->DynamicCollection->GetNumTransforms(), 1);
const FVector Translation1 = FVector(Collection->DynamicCollection->GetTransform(0).GetTranslation());
EXPECT_NEAR((Translation0 - Translation1).Size(), 0.f, KINDA_SMALL_NUMBER);
EXPECT_NEAR(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 1.f, KINDA_SMALL_NUMBER);
FRadialIntMask* RadialMask = new FRadialIntMask();
RadialMask->Position = FVector(0.0, 0.0, 0.0);
RadialMask->Radius = 100.0;
RadialMask->InteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMask->ExteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic;
RadialMask->SetMaskCondition = ESetMaskConditionType::Field_Set_IFF_NOT_Interior;
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_DynamicState);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, RadialMask });
for (int i = 0; i < 100; i++)
{
UnitTest.Advance();
}
const FVector Translation2 = FVector(Collection->DynamicCollection->GetTransform(0).GetTranslation());
EXPECT_NE(Translation1, Translation2);
EXPECT_LE(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 0.f);
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_InitialLinearVelocity)
{
FFramework UnitTest;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Kinematic;
Params.RootTransform.SetLocation(FVector(0.0, 0.0, 0.0));
Params.InitialVelocityType = EInitialVelocityTypeEnum::Chaos_Initial_Velocity_User_Defined;
Params.InitialLinearVelocity = FVector3f(0.f, 100.f, 0.f);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FRadialIntMask* RadialMask = new FRadialIntMask();
RadialMask->Position = FVector(0.0, 0.0, 0.0);
RadialMask->Radius = 5.0f;
RadialMask->InteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMask->ExteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMask->SetMaskCondition = ESetMaskConditionType::Field_Set_Always;
UnitTest.Initialize();
TManagedArray<uint8>& DynamicState = Collection->DynamicCollection->DynamicState;
FReal PreviousY = 0.f;
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, 0);
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y, 0);
for (int Frame = 0; Frame < 10; Frame++)
{
UnitTest.Advance();
if (Frame == 1)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_DynamicState);
Collection->PhysObject->BufferFieldCommand_Internal(UnitTest.Solver, { TargetName, RadialMask });
}
if (Frame >= 2)
{
EXPECT_EQ(DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic);
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, 0);
EXPECT_GT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y, PreviousY);
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 0);
}
else
{
EXPECT_EQ(DynamicState[0], (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic);
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, 0);
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y, 0);
EXPECT_EQ(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 0);
}
PreviousY = Collection->DynamicCollection->GetTransform(0).GetTranslation().Y;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_StayDynamic)
{
FFramework UnitTest;
FReal PreviousHeight = 5.0;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Static;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, PreviousHeight));
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FRadialIntMask* RadialMask = new FRadialIntMask();
RadialMask->Position = FVector(0.0, 0.0, PreviousHeight);
RadialMask->Radius = 5.0;
RadialMask->InteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Dynamic;
RadialMask->ExteriorValue = (int32)EObjectStateTypeEnum::Chaos_Object_Kinematic;
RadialMask->SetMaskCondition = ESetMaskConditionType::Field_Set_IFF_NOT_Interior;
UnitTest.Initialize();
for (int Frame = 0; Frame < 10; Frame++)
{
// Set everything inside the r=5.0 sphere to dynamic
if (Frame == 5)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_DynamicState);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, RadialMask });
}
UnitTest.Advance();
if (Frame < 5)
{
// Before frame 5 nothing should have moved
EXPECT_LT(FMath::Abs(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z - 5.f), SMALL_THRESHOLD);
}
else
{
// Frame 5 and after should be falling
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, PreviousHeight);
}
// Track current height of the object
PreviousHeight = Collection->DynamicCollection->GetTransform(0).GetTranslation().Z;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_LinearForce)
{
FFramework UnitTest;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, 5.f));
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FUniformVector* UniformVector = new FUniformVector();
UniformVector->Direction = FVector(0.0, 1.0, 0.0);
UniformVector->Magnitude = 1000.0;
UnitTest.Initialize();
FReal PreviousY = 0.0;
for (int Frame = 0; Frame < 10; Frame++)
{
if (Frame >= 5)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, UniformVector->NewCopy() });
}
UnitTest.Advance();
if (Frame < 5)
{
EXPECT_LT(FMath::Abs(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y), SMALL_THRESHOLD);
}
else
{
EXPECT_GT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y, PreviousY);
}
PreviousY = Collection->DynamicCollection->GetTransform(0).GetTranslation().Y;
}
delete UniformVector;
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_Torque)
{
FFramework UnitTest;
// Physics Object Setup
FVector Scale = FVector(10.0);
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, 5.f));
Params.GeomTransform.SetScale3D(Scale);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FUniformVector* UniformVector = new FUniformVector();
UniformVector->Direction = FVector(0.0, 1.0, 0.0);
UniformVector->Magnitude = 100.0;
UnitTest.Initialize();
FReal PreviousY = 0.0;
for (int Frame = 0; Frame < 10; Frame++)
{
if (Frame >= 5)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_AngularTorque);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, UniformVector->NewCopy() });
}
UnitTest.Advance();
auto& Particles = UnitTest.Solver->GetParticles().GetGeometryCollectionParticles();
if (Frame < 5)
{
EXPECT_LT(FMath::Abs(Collection->DynamicCollection->GetTransform(0).GetRotation().Euler().Y), SMALL_THRESHOLD);
}
else
{
EXPECT_NE(FMath::Abs(Collection->DynamicCollection->GetTransform(0).GetRotation().Euler().Y), SMALL_THRESHOLD);
EXPECT_GT(Particles.GetW(0).Y, PreviousY);
}
PreviousY = Particles.GetW(0).Y;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_Kill)
{
FFramework UnitTest;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, 20.f));
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FPlaneFalloff* FalloffField = new FPlaneFalloff();
FalloffField->Magnitude = 1.0;
FalloffField->Distance = 10.0f;
FalloffField->Position = FVector(0.0, 0.0, 5.0);
FalloffField->Normal = FVector(0.0, 0.0, 1.0);
FalloffField->Falloff = EFieldFalloffType::Field_Falloff_Linear;
UnitTest.Initialize();
TManagedArray<bool>& Active = Collection->DynamicCollection->Active;
auto& Particles = UnitTest.Solver->GetParticles().GetGeometryCollectionParticles();
for (int Frame = 0; Frame < 20; Frame++)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_Kill);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
UnitTest.Advance();
if (Particles.Disabled(0))
{
break;
}
}
EXPECT_EQ(Particles.Disabled(0), true);
// hasn't fallen any further than this due to being disabled
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, 5.f);
EXPECT_GT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, -5.0f);
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_LinearVelocity)
{
FFramework UnitTest;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, 20.f));
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Field setup
FUniformVector* VectorField = new FUniformVector();
VectorField->Magnitude = 100.0;
VectorField->Direction = FVector(1.0, 0.0, 0.0);
UnitTest.Initialize();
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_LinearVelocity);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, VectorField->NewCopy() });
UnitTest.Advance();
FReal PreviousX = 0.0;
for (int Frame = 1; Frame < 10; Frame++)
{
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearVelocity), VectorField->NewCopy() });
UnitTest.Advance();
EXPECT_GT(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, PreviousX);
PreviousX = Collection->DynamicCollection->GetTransform(0).GetTranslation().X;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_CollisionGroup)
{
/**
* Create a stack of boxes on the ground and verify that we we change their collision
* group, they drop through the ground.
*/
FFramework UnitTest; UnitTest.Dt = 1 / 24.0;
RigidBodyWrapper* Floor = TNewSimulationObject<GeometryType::RigidFloor>::Init()->template As<RigidBodyWrapper>();
UnitTest.AddSimulationObject(Floor);
// Generate Geometry - a stack of boxes.
// The bottom box is on the ground, and the others are dropped into it.
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
FVector Scale = FVector(100);
Params.GeomTransform.SetScale3D(Scale);
FGeometryCollectionWrapper* Collection[4];
for (int n = 0; n < 3; n++)
{
Params.RootTransform.SetLocation(FVector(0.f, 0.f, n * 200.0f + 100.0f));
Params.CollisionType = ECollisionTypeEnum::Chaos_Volumetric;
Collection[n + 1] = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection[n + 1]);
}
// Field setup
FRadialIntMask* RadialMask = new FRadialIntMask();
RadialMask->Position = FVector(0.0, 0.0, 0.0);
RadialMask->Radius = 0;
RadialMask->InteriorValue = -1;
RadialMask->ExteriorValue = -1;
RadialMask->SetMaskCondition = ESetMaskConditionType::Field_Set_Always;
UnitTest.Initialize();
for (int Frame = 0; Frame < 60; Frame++)
{
UnitTest.Advance();
auto& Particles = UnitTest.Solver->GetParticles().GetGeometryCollectionParticles();
if (Frame == 30)
{
// The boxes should have landed on each other and settled by now
EXPECT_NEAR(Collection[1]->DynamicCollection->GetTransform(0).GetTranslation().Z, (FReal)100, (FReal)20);
EXPECT_NEAR(Collection[2]->DynamicCollection->GetTransform(0).GetTranslation().Z, (FReal)300, (FReal)20);
EXPECT_NEAR(Collection[3]->DynamicCollection->GetTransform(0).GetTranslation().Z, (FReal)500, (FReal)20);
}
if (Frame == 31)
{
EFieldPhysicsType PhysicsType = GetGeometryCollectionPhysicsType(EGeometryCollectionPhysicsTypeEnum::Chaos_CollisionGroup);
Collection[1]->PhysObject->BufferFieldCommand_Internal(UnitTest.Solver, { PhysicsType, RadialMask });
}
}
// The bottom boxes should have fallen below the ground level, box 2 now on the ground with box 3 on top
auto& Particles = UnitTest.Solver->GetParticles().GetGeometryCollectionParticles();
EXPECT_LT(Collection[1]->DynamicCollection->GetTransform(0).GetTranslation().Z, 0);
EXPECT_TRUE(FMath::IsNearlyEqual((FReal)Collection[2]->DynamicCollection->GetTransform(0).GetTranslation().Z, (FReal)100, (FReal)20));
EXPECT_TRUE(FMath::IsNearlyEqual((FReal)Collection[3]->DynamicCollection->GetTransform(0).GetTranslation().Z, (FReal)300, (FReal)20));
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_ClusterBreak_StrainModel_Test1)
{
FFramework UnitTest;
TSharedPtr<FGeometryCollection> RestCollection = CreateClusteredBody_TwoByTwo_ThreeTransform(FVector(0));
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 1.0 };
Params.MaxClusterLevel = 1000;
Params.ClusterConnectionMethod = Chaos::FClusterCreationParameters::EConnectionMethod::DelaunayTriangulation;
Params.ClusterGroupIndex = 0;
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = 1.5;
FalloffField->Radius = 100.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
UnitTest.Advance();
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_ExternalClusterStrain);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
EXPECT_EQ(ClusterMap.Num(), 3);
EXPECT_EQ(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Num(), 2);
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Contains(Collection->PhysObject->GetParticle_Internal(0)));
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Contains(Collection->PhysObject->GetParticle_Internal(1)));
EXPECT_EQ(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Num(), 2);
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Contains(Collection->PhysObject->GetParticle_Internal(2)));
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Contains(Collection->PhysObject->GetParticle_Internal(3)));
EXPECT_EQ(ClusterMap[Collection->PhysObject->GetParticle_Internal(6)].Num(), 2);
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(6)].Contains(Collection->PhysObject->GetParticle_Internal(5)));
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(6)].Contains(Collection->PhysObject->GetParticle_Internal(4)));
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(0)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(1)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(2)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(3)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(4)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(5)->Disabled());
EXPECT_FALSE(Collection->PhysObject->GetParticle_Internal(6)->Disabled());
UnitTest.Advance();
// todo: indices here might seem odd, particles 4 & 5 are swapped
EXPECT_EQ(ClusterMap.Num(), 2);
EXPECT_EQ(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Num(), 2);
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Contains(Collection->PhysObject->GetParticle_Internal(0)));
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(4)].Contains(Collection->PhysObject->GetParticle_Internal(1)));
EXPECT_EQ(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Num(), 2);
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Contains(Collection->PhysObject->GetParticle_Internal(2)));
EXPECT_TRUE(ClusterMap[Collection->PhysObject->GetParticle_Internal(5)].Contains(Collection->PhysObject->GetParticle_Internal(3)));
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(0)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(1)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(2)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(3)->Disabled());
EXPECT_FALSE(Collection->PhysObject->GetParticle_Internal(4)->Disabled());
EXPECT_FALSE(Collection->PhysObject->GetParticle_Internal(5)->Disabled());
EXPECT_TRUE(Collection->PhysObject->GetParticle_Internal(6)->Disabled());
}
delete FalloffField;
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_ClusterBreak_StrainModel_Test2)
{
FFramework UnitTest;
TSharedPtr<FGeometryCollection> RestCollection = CreateClusteredBody_ThreeByTwo_ThreeTransform(FVector(0));
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 1.0 };
Params.MaxClusterLevel = 1000;
Params.ClusterGroupIndex = 0;
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = 1.5;
FalloffField->Radius = 200.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
UnitTest.Advance();
auto ParticleHandles = [&Collection](int32 Index) -> Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*
{
return Collection->PhysObject->GetParticle_Internal(Index);
};
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_ExternalClusterStrain);
FFieldSystemCommand Command(TargetName, FalloffField->NewCopy());
FFieldSystemMetaDataProcessingResolution* ResolutionData = new FFieldSystemMetaDataProcessingResolution(EFieldResolutionType::Field_Resolution_Maximum);
Command.MetaData.Add(FFieldSystemMetaData::EMetaType::ECommandData_ProcessingResolution, TUniquePtr< FFieldSystemMetaDataProcessingResolution >(ResolutionData));
UnitTest.Solver->GetPerSolverField().AddTransientCommand(Command);
EXPECT_EQ(ClusterMap.Num(), 3);
EXPECT_EQ(ClusterMap[ParticleHandles(6)].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(0)));
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(1)));
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(2)));
EXPECT_EQ(ClusterMap[ParticleHandles(7)].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(3)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(4)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(5)));
EXPECT_EQ(ClusterMap[ParticleHandles(8)].Num(), 2);
EXPECT_TRUE(ClusterMap[ParticleHandles(8)].Contains(ParticleHandles(7)));
EXPECT_TRUE(ClusterMap[ParticleHandles(8)].Contains(ParticleHandles(6)));
EXPECT_TRUE(ParticleHandles(0)->Disabled());
EXPECT_TRUE(ParticleHandles(1)->Disabled());
EXPECT_TRUE(ParticleHandles(2)->Disabled());
EXPECT_TRUE(ParticleHandles(3)->Disabled());
EXPECT_TRUE(ParticleHandles(4)->Disabled());
EXPECT_TRUE(ParticleHandles(5)->Disabled());
EXPECT_TRUE(ParticleHandles(6)->Disabled());
EXPECT_TRUE(ParticleHandles(7)->Disabled());
EXPECT_FALSE(ParticleHandles(8)->Disabled());
UnitTest.Advance();
UnitTest.Solver->GetPerSolverField().AddTransientCommand(Command);
UnitTest.Advance();
EXPECT_EQ(ClusterMap.Num(), 1);
EXPECT_EQ(ClusterMap[ParticleHandles(7)].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(3)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(4)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(5)));
EXPECT_FALSE(ParticleHandles(0)->Disabled());
EXPECT_FALSE(ParticleHandles(1)->Disabled());
EXPECT_FALSE(ParticleHandles(2)->Disabled());
EXPECT_TRUE(ParticleHandles(3)->Disabled());
EXPECT_TRUE(ParticleHandles(4)->Disabled());
EXPECT_TRUE(ParticleHandles(5)->Disabled());
EXPECT_TRUE(ParticleHandles(6)->Disabled());
EXPECT_FALSE(ParticleHandles(7)->Disabled());
EXPECT_TRUE(ParticleHandles(8)->Disabled());
}
delete FalloffField;
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_ClusterBreak_StrainModel_Test3)
{
FFramework UnitTest;
TSharedPtr<FGeometryCollection> RestCollection = CreateClusteredBody_ThreeByTwo_ThreeTransform(FVector(0));
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 1.0 };
Params.MaxClusterLevel = 1000;
Params.ClusterGroupIndex = 0;
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = 1.1;
FalloffField->Radius = 200.0;
FalloffField->Position = FVector(350.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
UnitTest.Advance();
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
auto ParticleHandles = [&Collection](int32 Index) -> Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*
{
return Collection->PhysObject->GetParticle_Internal(Index);
};
UnitTest.Advance();
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_ExternalClusterStrain);
FFieldSystemCommand Command(TargetName, FalloffField->NewCopy());
FFieldSystemMetaDataProcessingResolution* ResolutionData = new FFieldSystemMetaDataProcessingResolution(EFieldResolutionType::Field_Resolution_Maximum);
Command.MetaData.Add(FFieldSystemMetaData::EMetaType::ECommandData_ProcessingResolution, TUniquePtr< FFieldSystemMetaDataProcessingResolution >(ResolutionData));
UnitTest.Solver->GetPerSolverField().AddTransientCommand(Command);
EXPECT_TRUE(ParticleHandles(0)->Disabled());
EXPECT_TRUE(ParticleHandles(1)->Disabled());
EXPECT_TRUE(ParticleHandles(2)->Disabled());
EXPECT_TRUE(ParticleHandles(3)->Disabled());
EXPECT_TRUE(ParticleHandles(4)->Disabled());
EXPECT_TRUE(ParticleHandles(5)->Disabled());
EXPECT_TRUE(ParticleHandles(6)->Disabled());
EXPECT_TRUE(ParticleHandles(7)->Disabled());
EXPECT_FALSE(ParticleHandles(8)->Disabled());
UnitTest.Advance();
// todo: indices here might be off but the test crashes before this so we can't validate yet
EXPECT_EQ(ClusterMap.Num(), 2);
EXPECT_EQ(ClusterMap[ParticleHandles(7)].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(3)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(4)));
EXPECT_TRUE(ClusterMap[ParticleHandles(7)].Contains(ParticleHandles(5)));
EXPECT_EQ(ClusterMap[ParticleHandles(6)].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(0)));
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(1)));
EXPECT_TRUE(ClusterMap[ParticleHandles(6)].Contains(ParticleHandles(2)));
EXPECT_TRUE(ParticleHandles(0)->Disabled());
EXPECT_TRUE(ParticleHandles(1)->Disabled());
EXPECT_TRUE(ParticleHandles(2)->Disabled());
EXPECT_TRUE(ParticleHandles(3)->Disabled());
EXPECT_TRUE(ParticleHandles(4)->Disabled());
EXPECT_TRUE(ParticleHandles(5)->Disabled());
EXPECT_FALSE(ParticleHandles(6)->Disabled());
EXPECT_FALSE(ParticleHandles(7)->Disabled());
EXPECT_TRUE(ParticleHandles(8)->Disabled());
}
delete FalloffField;
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_ClusterBreak_StrainModel_Test4)
{
FFramework UnitTest;
TSharedPtr<FGeometryCollection> RestCollection = CreateClusteredBody_TwoByTwo_ThreeTransform(FVector(0));
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 1.0 };
Params.MaxClusterLevel = 1000;
Params.ClusterGroupIndex = 0;
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = 1.5;
FalloffField->Radius = 100.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
UnitTest.Advance();
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
Collection->PhysObject->GetParticle_Internal(1),
Collection->PhysObject->GetParticle_Internal(2),
Collection->PhysObject->GetParticle_Internal(3),
Collection->PhysObject->GetParticle_Internal(4),
Collection->PhysObject->GetParticle_Internal(5),
Collection->PhysObject->GetParticle_Internal(6),
};
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_ExternalClusterStrain);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
EXPECT_TRUE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_TRUE(ParticleHandles[4]->Disabled());
EXPECT_TRUE(ParticleHandles[5]->Disabled());
EXPECT_FALSE(ParticleHandles[6]->Disabled());
UnitTest.Advance();
EXPECT_EQ(ClusterMap.Num(), 2);
EXPECT_EQ(ClusterMap[ParticleHandles[4]].Num(), 2);
EXPECT_TRUE(ClusterMap[ParticleHandles[4]].Contains(ParticleHandles[0]));
EXPECT_TRUE(ClusterMap[ParticleHandles[4]].Contains(ParticleHandles[1]));
EXPECT_EQ(ClusterMap[ParticleHandles[5]].Num(), 2);
EXPECT_TRUE(ClusterMap[ParticleHandles[5]].Contains(ParticleHandles[2]));
EXPECT_TRUE(ClusterMap[ParticleHandles[5]].Contains(ParticleHandles[3]));
EXPECT_TRUE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_FALSE(ParticleHandles[4]->Disabled());
EXPECT_FALSE(ParticleHandles[5]->Disabled());
EXPECT_TRUE(ParticleHandles[6]->Disabled());
}
delete FalloffField;
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_ClusterBreak_StrainModel_TestResolutionMinimal)
{
FFramework UnitTest;
TSharedPtr<FGeometryCollection> RestCollection = CreateClusteredBody_ThreeByTwo_ThreeTransform(FVector(0));
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 101.0, 103.0};
Params.MaxClusterLevel = 10;
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff(); // field resolution is minimal by default
FalloffField->Magnitude = -100.0;
FalloffField->Radius = 10000.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
Collection->PhysObject->GetParticle_Internal(1),
Collection->PhysObject->GetParticle_Internal(2),
Collection->PhysObject->GetParticle_Internal(3),
Collection->PhysObject->GetParticle_Internal(4),
Collection->PhysObject->GetParticle_Internal(5),
Collection->PhysObject->GetParticle_Internal(6),
Collection->PhysObject->GetParticle_Internal(7),
Collection->PhysObject->GetParticle_Internal(8),
};
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ClusteredParticleHandles = {
Collection->PhysObject->GetSolverClusterHandle_Internal(0),
Collection->PhysObject->GetSolverClusterHandle_Internal(1),
Collection->PhysObject->GetSolverClusterHandle_Internal(2),
Collection->PhysObject->GetSolverClusterHandle_Internal(3),
Collection->PhysObject->GetSolverClusterHandle_Internal(4),
Collection->PhysObject->GetSolverClusterHandle_Internal(5),
Collection->PhysObject->GetSolverClusterHandle_Internal(6),
Collection->PhysObject->GetSolverClusterHandle_Internal(7),
Collection->PhysObject->GetSolverClusterHandle_Internal(8),
};
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
UnitTest.Solver->RegisterSimOneShotCallback([&]()
{
ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
Collection->PhysObject->GetParticle_Internal(1),
Collection->PhysObject->GetParticle_Internal(2),
Collection->PhysObject->GetParticle_Internal(3),
Collection->PhysObject->GetParticle_Internal(4),
Collection->PhysObject->GetParticle_Internal(5),
Collection->PhysObject->GetParticle_Internal(6),
Collection->PhysObject->GetParticle_Internal(7),
Collection->PhysObject->GetParticle_Internal(8),
};
ClusteredParticleHandles = {
Collection->PhysObject->GetSolverClusterHandle_Internal(0),
Collection->PhysObject->GetSolverClusterHandle_Internal(1),
Collection->PhysObject->GetSolverClusterHandle_Internal(2),
Collection->PhysObject->GetSolverClusterHandle_Internal(3),
Collection->PhysObject->GetSolverClusterHandle_Internal(4),
Collection->PhysObject->GetSolverClusterHandle_Internal(5),
Collection->PhysObject->GetSolverClusterHandle_Internal(6),
Collection->PhysObject->GetSolverClusterHandle_Internal(7),
Collection->PhysObject->GetSolverClusterHandle_Internal(8),
};
EXPECT_EQ(ClusterMap.Num(), 3);
EXPECT_EQ(ClusteredParticleHandles[1]->GetInternalStrains(), 101);
});
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_InternalClusterStrain);
FalloffField->Magnitude = 0.0;
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
UnitTest.Advance();
{
// { 101.0, 103.0 }
EXPECT_TRUE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_TRUE(ParticleHandles[4]->Disabled());
EXPECT_TRUE(ParticleHandles[5]->Disabled());
EXPECT_TRUE(ParticleHandles[6]->Disabled());
EXPECT_TRUE(ParticleHandles[7]->Disabled());
EXPECT_FALSE(ParticleHandles[8]->Disabled());
FalloffField->Magnitude = -100;
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
UnitTest.Advance();
// { 1.0, 3.0 }
EXPECT_TRUE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_TRUE(ParticleHandles[4]->Disabled());
EXPECT_TRUE(ParticleHandles[5]->Disabled());
EXPECT_TRUE(ParticleHandles[6]->Disabled());
EXPECT_TRUE(ParticleHandles[7]->Disabled());
EXPECT_FALSE(ParticleHandles[8]->Disabled()); // Parent still clustered w/ threshold > 0
EXPECT_EQ(ClusterMap.Num(), 3);
EXPECT_EQ(ClusterMap[ParticleHandles[6]].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles[6]].Contains(ParticleHandles[0]));
EXPECT_TRUE(ClusterMap[ParticleHandles[6]].Contains(ParticleHandles[1]));
EXPECT_TRUE(ClusterMap[ParticleHandles[6]].Contains(ParticleHandles[2]));
EXPECT_EQ(ClusterMap[ParticleHandles[7]].Num(), 3);
EXPECT_TRUE(ClusterMap[ParticleHandles[7]].Contains(ParticleHandles[3]));
EXPECT_TRUE(ClusterMap[ParticleHandles[7]].Contains(ParticleHandles[4]));
EXPECT_TRUE(ClusterMap[ParticleHandles[7]].Contains(ParticleHandles[5]));
EXPECT_EQ(ClusterMap[ParticleHandles[8]].Num(), 2);
EXPECT_TRUE(ClusterMap[ParticleHandles[8]].Contains(ParticleHandles[6]));
EXPECT_TRUE(ClusterMap[ParticleHandles[8]].Contains(ParticleHandles[7]));
FalloffField->Magnitude = -1.0;
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
UnitTest.Advance();
// { 0.0, 2.0 } broken
EXPECT_FALSE(ParticleHandles[6]->Disabled());
EXPECT_FALSE(ParticleHandles[7]->Disabled());
EXPECT_TRUE(ParticleHandles[8]->Disabled());
FalloffField->Magnitude = -103;
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, FalloffField->NewCopy() });
UnitTest.Advance();
// { 0.0, 0.0 }
EXPECT_FALSE(ParticleHandles[0]->Disabled());
EXPECT_FALSE(ParticleHandles[1]->Disabled());
EXPECT_FALSE(ParticleHandles[2]->Disabled());
EXPECT_FALSE(ParticleHandles[3]->Disabled());
EXPECT_FALSE(ParticleHandles[4]->Disabled());
EXPECT_FALSE(ParticleHandles[5]->Disabled());
EXPECT_TRUE(ParticleHandles[6]->Disabled());
EXPECT_TRUE(ParticleHandles[7]->Disabled());
EXPECT_TRUE(ParticleHandles[8]->Disabled());
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_Algebra1)
{
FFramework UnitTest;
FVector ExpectedLocation = FVector(0.0f, 0.0f, 0.0f);
FReal LastZ = ExpectedLocation.Z;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(ExpectedLocation);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Opposing Fields setup (velocity)
FUniformVector* VectorFieldRight = new FUniformVector();
VectorFieldRight->Magnitude = 100.0;
VectorFieldRight->Direction = FVector(1.0, 0.0, 0.0);
FUniformVector* VectorFieldLeft = new FUniformVector();
VectorFieldLeft->Magnitude = 100.0;
VectorFieldLeft->Direction = FVector(-1.0f, 0.0, 0.0);
// Opposing Fields setup (force)
FUniformVector* VectorFieldForward = new FUniformVector();
VectorFieldRight->Magnitude = 100.0;
VectorFieldRight->Direction = FVector(0.0, 100.0, 0.0);
FUniformVector* VectorFieldBackward = new FUniformVector();
VectorFieldLeft->Magnitude = 100.0;
VectorFieldLeft->Direction = FVector(0.0f, -100.0, 0.0);
UnitTest.Initialize();
FName TargetNameVel = GetFieldPhysicsName(EFieldPhysicsType::Field_LinearVelocity);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetNameVel, VectorFieldRight->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetNameVel, VectorFieldLeft->NewCopy() });
FName TargetNameForce = GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetNameForce, VectorFieldForward->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetNameForce, VectorFieldBackward->NewCopy() });
UnitTest.Advance();
for (int Frame = 1; Frame < 10; Frame++)
{
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearVelocity), VectorFieldRight->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearVelocity), VectorFieldLeft->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce), VectorFieldForward->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce), VectorFieldBackward->NewCopy() });
UnitTest.Advance();
EXPECT_NEAR(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, ExpectedLocation.X, KINDA_SMALL_NUMBER);
EXPECT_NEAR(Collection->DynamicCollection->GetTransform(0).GetTranslation().Y, ExpectedLocation.Y, KINDA_SMALL_NUMBER);
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, ExpectedLocation.Z);
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, LastZ);
LastZ = Collection->DynamicCollection->GetTransform(0).GetTranslation().Z;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_Algebra2)
{
FFramework UnitTest;
FVector ExpectedLocation = FVector(0.0f, 0.0f, 0.0f);
FVector LastLocation = ExpectedLocation;
// Physics Object Setup
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.InitialVelocityType = EInitialVelocityTypeEnum::Chaos_Initial_Velocity_User_Defined;
Params.InitialLinearVelocity = FVector3f(100.0, 0.0, 0.0);
Params.RootTransform.SetLocation(ExpectedLocation);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Opposing Fields setup (force)
FUniformVector* VectorFieldRightForce = new FUniformVector();
VectorFieldRightForce->Magnitude = 100.0;
VectorFieldRightForce->Direction = FVector(100.0, 0.0, 0.0);
FUniformVector* VectorFieldLeftForce = new FUniformVector();
VectorFieldLeftForce->Magnitude = 100.0;
VectorFieldLeftForce->Direction = FVector(-100.0f, 0.0, 0.0);
UnitTest.Initialize();
UnitTest.Advance();
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
};
Chaos::TVector<float, 3> CurrV = ParticleHandles[0]->GetV();
for (int Frame = 1; Frame < 10; Frame++)
{
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce), VectorFieldRightForce->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ GetFieldPhysicsName(EFieldPhysicsType::Field_LinearForce), VectorFieldLeftForce->NewCopy() });
UnitTest.Advance();
CurrV = ParticleHandles[0]->GetV();
EXPECT_NEAR(CurrV.X, Params.InitialLinearVelocity.X, KINDA_SMALL_NUMBER); // Velocity in +x
EXPECT_GT(Collection->DynamicCollection->GetTransform(0).GetTranslation().X, LastLocation.X); // Pos in +x
// Still falling?
EXPECT_LT(Collection->DynamicCollection->GetTransform(0).GetTranslation().Z, LastLocation.Z);
LastLocation = FVector(Collection->DynamicCollection->GetTransform(0).GetTranslation());
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_Algebra3)
{
FFramework UnitTest;
// Physics Object Setup
FVector Scale = FVector(10.0);
CreationParameters Params;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.RootTransform.SetLocation(FVector(0.f, 0.f, 5.f));
Params.GeomTransform.SetScale3D(Scale);
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSingleRigid>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
// Fields setup... when both our applied, we expect Y rotation to cancel, while X rotation > 0.
FUniformVector* UniformVector1 = new FUniformVector();
UniformVector1->Direction = FVector(2.0, 1.0, 0.0);
UniformVector1->Magnitude = 100.0;
FUniformVector* UniformVector2 = new FUniformVector();
UniformVector2->Direction = FVector(-1.0, -1.0, 0.0);
UniformVector2->Magnitude = 100.0;
UnitTest.Initialize();
FReal PreviousHeight = Collection->DynamicCollection->GetTransform(0).GetTranslation().Z;
FReal PreviousX = Collection->DynamicCollection->GetTransform(0).GetRotation().Euler().X;
for (int Frame = 0; Frame < 10; Frame++)
{
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_AngularTorque);
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, UniformVector1->NewCopy() });
UnitTest.Solver->GetPerSolverField().AddTransientCommand({ TargetName, UniformVector2->NewCopy() });
UnitTest.Advance();
Chaos::TPBDGeometryCollectionParticles<Chaos::FReal, 3>& Particles = UnitTest.Solver->GetParticles().GetGeometryCollectionParticles();
EXPECT_NE(FMath::Abs(Collection->DynamicCollection->GetTransform(0).GetRotation().Euler().Y), SMALL_THRESHOLD); // not rotating in Y?
EXPECT_GT(Particles.GetW(0).X, PreviousX); // rotating in X?
EXPECT_LT(Particles.GetX(0).Z, PreviousHeight); // still falling?
PreviousHeight = Particles.GetX(0).Z;
PreviousX = Particles.GetW(0).X;
}
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_FieldResolutionMinimal)
{
FFramework UnitTest;
// Construct Rest Collection...
TSharedPtr<FGeometryCollection> RestCollection = GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(0, 0, 0)), FVector(1.0));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(100, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(200, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(300, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(400, 0, 0)), FVector(1.0)));
RestCollection->Transform[0].SetTranslation(FVector3f(0.0f));
RestCollection->SimulationType[0] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[1] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[2] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[3] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[4] = FGeometryCollection::ESimulationTypes::FST_Rigid;
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 0, { 1 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 1, { 2 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 2, { 3 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 3, { 4 });
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 91.0f, 92.0f, 93.0f, 94.0f };
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = -100;
FalloffField->Radius = 1000.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
UnitTest.Advance();
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
Collection->PhysObject->GetParticle_Internal(1),
Collection->PhysObject->GetParticle_Internal(2),
Collection->PhysObject->GetParticle_Internal(3),
Collection->PhysObject->GetParticle_Internal(4),
};
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
Chaos::TArrayCollectionArray<Chaos::FRealSingle>& StrainArray = UnitTest.Solver->GetEvolution()->GetRigidClustering().GetStrainArray();
EXPECT_FALSE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_TRUE(ParticleHandles[4]->Disabled());
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_InternalClusterStrain);
FFieldSystemCommand Command(TargetName, FalloffField->NewCopy());
FFieldSystemMetaDataProcessingResolution* ResolutionData = new FFieldSystemMetaDataProcessingResolution(EFieldResolutionType::Field_Resolution_Minimal);
Command.MetaData.Add(FFieldSystemMetaData::EMetaType::ECommandData_ProcessingResolution, TUniquePtr< FFieldSystemMetaDataProcessingResolution >(ResolutionData));
UnitTest.Solver->GetPerSolverField().AddTransientCommand(Command);
UnitTest.Advance();
// We expect only active clusters and their children to be affected by the field with Field_Resolution_Minimal
EXPECT_GT(StrainArray[0], 0.0f);
EXPECT_GT(StrainArray[1], 0.0f);
EXPECT_GT(0.0f, StrainArray[2]);
EXPECT_GT(0.0f, StrainArray[3]);
}
GTEST_TEST(AllTraits, GeometryCollection_RigidBodies_Field_FieldResolutionMaximum)
{
FFramework UnitTest;
// Construct Rest Collection...
TSharedPtr<FGeometryCollection> RestCollection = GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(0, 0, 0)), FVector(1.0));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(100, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(200, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(300, 0, 0)), FVector(1.0)));
RestCollection->AppendGeometry(*GeometryCollection::MakeCubeElement(FTransform(FQuat::MakeFromEuler(FVector(0.f)), FVector(400, 0, 0)), FVector(1.0)));
RestCollection->Transform[0].SetTranslation(FVector3f(0.0f));
RestCollection->SimulationType[0] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[1] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[2] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[3] = FGeometryCollection::ESimulationTypes::FST_Clustered;
RestCollection->SimulationType[4] = FGeometryCollection::ESimulationTypes::FST_Rigid;
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 0, { 1 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 1, { 2 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 2, { 3 });
GeometryCollectionAlgo::ParentTransforms(RestCollection.Get(), 3, { 4 });
CreationParameters Params;
Params.RestCollection = RestCollection;
Params.DynamicState = EObjectStateTypeEnum::Chaos_Object_Dynamic;
Params.ImplicitType = EImplicitTypeEnum::Chaos_Implicit_Box;
Params.CollisionType = ECollisionTypeEnum::Chaos_Surface_Volumetric;
Params.Simulating = true;
Params.EnableClustering = true;
Params.DamageThreshold = { 91.0f, 92.0f, 93.0f, 94.0f };
FGeometryCollectionWrapper* Collection = TNewSimulationObject<GeometryType::GeometryCollectionWithSuppliedRestCollection>::Init(Params)->template As<FGeometryCollectionWrapper>();
UnitTest.AddSimulationObject(Collection);
FRadialFalloff* FalloffField = new FRadialFalloff();
FalloffField->Magnitude = -100;
FalloffField->Radius = 1000.0;
FalloffField->Position = FVector(0.0, 0.0, 0.0);
FalloffField->Falloff = EFieldFalloffType::Field_FallOff_None;
UnitTest.Initialize();
UnitTest.Advance();
TArray<Chaos::TPBDRigidClusteredParticleHandle<FReal, 3>*> ParticleHandles = {
Collection->PhysObject->GetParticle_Internal(0),
Collection->PhysObject->GetParticle_Internal(1),
Collection->PhysObject->GetParticle_Internal(2),
Collection->PhysObject->GetParticle_Internal(3),
Collection->PhysObject->GetParticle_Internal(4),
};
auto& Clustering = UnitTest.Solver->GetEvolution()->GetRigidClustering();
const auto& ClusterMap = Clustering.GetChildrenMap();
Chaos::TArrayCollectionArray<Chaos::FRealSingle>& StrainArray = UnitTest.Solver->GetEvolution()->GetRigidClustering().GetStrainArray();
EXPECT_FALSE(ParticleHandles[0]->Disabled());
EXPECT_TRUE(ParticleHandles[1]->Disabled());
EXPECT_TRUE(ParticleHandles[2]->Disabled());
EXPECT_TRUE(ParticleHandles[3]->Disabled());
EXPECT_TRUE(ParticleHandles[4]->Disabled());
FName TargetName = GetFieldPhysicsName(EFieldPhysicsType::Field_InternalClusterStrain);
FFieldSystemCommand Command(TargetName, FalloffField->NewCopy());
FFieldSystemMetaDataProcessingResolution* ResolutionData = new FFieldSystemMetaDataProcessingResolution(EFieldResolutionType::Field_Resolution_Maximum);
Command.MetaData.Add(FFieldSystemMetaData::EMetaType::ECommandData_ProcessingResolution, TUniquePtr< FFieldSystemMetaDataProcessingResolution >(ResolutionData));
UnitTest.Solver->GetPerSolverField().AddTransientCommand(Command);
UnitTest.Advance();
UnitTest.Advance();
// We expect all clusters to be affected by the field with Field_Resolution_Maximum
EXPECT_GT(0.0f, StrainArray[0]);
EXPECT_GT(0.0f, StrainArray[1]);
EXPECT_GT(0.0f, StrainArray[2]);
EXPECT_GT(0.0f, StrainArray[3]);
}
}