1496 lines
62 KiB
C++
1496 lines
62 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ChaosSolversModule.h"
|
|
#include "HeadlessChaosTestUtility.h"
|
|
#include "PBDRigidsSolver.h"
|
|
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
|
|
#include "PhysicsProxy/JointConstraintProxy.h"
|
|
#include "Chaos/PBDJointConstraintData.h"
|
|
#include "HAL/ConsoleManager.h"
|
|
|
|
namespace ChaosTest
|
|
{
|
|
using namespace Chaos;
|
|
|
|
class SleepingTests : public ::testing::TestWithParam<bool>
|
|
{
|
|
void SetUp()
|
|
{
|
|
SetPartialIslandSleepUnitTestDefaults();
|
|
}
|
|
|
|
void TearDown()
|
|
{
|
|
RestorePartialIslandSleepEditorDefaults();
|
|
}
|
|
|
|
void SetPartialIslandSleepUnitTestDefaults()
|
|
{
|
|
CVarPostStepWake = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.PostStepWakeThreshold"), false);
|
|
check(CVarPostStepWake && CVarPostStepWake->IsVariableFloat());
|
|
PostStepWake_EditorDefault = CVarPostStepWake->GetFloat();
|
|
CVarPostStepWake->Set(PostStepWake_UnitTestDefault);
|
|
|
|
CVarMomentumPropagation = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.MomentumPropagation"), false);
|
|
check(CVarMomentumPropagation && CVarMomentumPropagation->IsVariableFloat());
|
|
MomentumPropagation_EditorDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(MomentumPropagation_UnitTestDefault);
|
|
|
|
CVarPartialSleepCollisionConstraintsOnly = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.CollisionConstraintsOnly"), false);
|
|
check(CVarPartialSleepCollisionConstraintsOnly && CVarPartialSleepCollisionConstraintsOnly->IsVariableBool());
|
|
bPartialSleepCollisionConstraintsOnly_EditorDefault = CVarPartialSleepCollisionConstraintsOnly->GetBool();
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnly_UnitTestDefault);
|
|
|
|
CVarPartialWakePreIntegration = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.PartialWakePreIntegration"), false);
|
|
check(CVarPartialWakePreIntegration && CVarPartialWakePreIntegration->IsVariableBool());
|
|
bPartialWakePreIntegration_EditorDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegration_UnitTestDefault);
|
|
|
|
CVarMinMotionlessRatio = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.MinimumMotionlessRatio"), false);
|
|
check(CVarMinMotionlessRatio && CVarMinMotionlessRatio->IsVariableFloat());
|
|
MinimumMotionlessThreshold_EditorDefault = CVarMinMotionlessRatio->GetFloat();
|
|
CVarMinMotionlessRatio->Set(MinimumMotionlessThreshold_UnitTestDefault);
|
|
|
|
CVarLinearWakeThresholdMultiplier = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.LinearWakeThresholdMultiplier"), false);
|
|
check(CVarLinearWakeThresholdMultiplier && CVarLinearWakeThresholdMultiplier->IsVariableFloat());
|
|
LinearWakeThresholdMultiplier_EditorDefault = CVarLinearWakeThresholdMultiplier->GetFloat();
|
|
CVarLinearWakeThresholdMultiplier->Set(LinearWakeThresholdMultiplier_UnitTestDefault);
|
|
|
|
CVarAngularWakeThresholdMultiplier = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.AngularWakeThresholdMultiplier"), false);
|
|
check(CVarAngularWakeThresholdMultiplier && CVarAngularWakeThresholdMultiplier->IsVariableFloat());
|
|
AngularWakeThresholdMultiplier_EditorDefault = CVarAngularWakeThresholdMultiplier->GetFloat();
|
|
CVarAngularWakeThresholdMultiplier->Set(AngularWakeThresholdMultiplier_UnitTestDefault);
|
|
|
|
CVarValidateConstraintSleepState = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep.ValidateConstraintSleepState"), false);
|
|
check(CVarValidateConstraintSleepState && CVarValidateConstraintSleepState->IsVariableBool());
|
|
bValidateConstraintSleepState_EditorDefault = CVarValidateConstraintSleepState->GetBool();
|
|
CVarValidateConstraintSleepState->Set(bValidateConstraintSleepState_UnitTestDefault);
|
|
}
|
|
|
|
void RestorePartialIslandSleepEditorDefaults()
|
|
{
|
|
CVarPostStepWake->Set(PostStepWake_EditorDefault);
|
|
CVarMomentumPropagation->Set(MomentumPropagation_EditorDefault);
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnly_EditorDefault);
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegration_EditorDefault);
|
|
CVarMinMotionlessRatio->Set(MinimumMotionlessThreshold_EditorDefault);
|
|
CVarLinearWakeThresholdMultiplier->Set(LinearWakeThresholdMultiplier_EditorDefault);
|
|
CVarAngularWakeThresholdMultiplier->Set(AngularWakeThresholdMultiplier_EditorDefault);
|
|
CVarValidateConstraintSleepState->Set(bValidateConstraintSleepState_EditorDefault);
|
|
}
|
|
|
|
// Default values for unit testing
|
|
FRealSingle PostStepWake_UnitTestDefault = 0.0f;
|
|
FRealSingle MomentumPropagation_UnitTestDefault = 0.0f;
|
|
bool bPartialSleepCollisionConstraintsOnly_UnitTestDefault = false;
|
|
bool bPartialWakePreIntegration_UnitTestDefault = false;
|
|
FRealSingle MinimumMotionlessThreshold_UnitTestDefault = 0.0f;
|
|
FRealSingle LinearWakeThresholdMultiplier_UnitTestDefault = 1.0f;
|
|
FRealSingle AngularWakeThresholdMultiplier_UnitTestDefault = 1.0f;
|
|
bool bValidateConstraintSleepState_UnitTestDefault = true;
|
|
|
|
// Storage for default values editor hard-coded in IslandManager.cpp
|
|
FRealSingle PostStepWake_EditorDefault;
|
|
FRealSingle MomentumPropagation_EditorDefault;
|
|
bool bPartialSleepCollisionConstraintsOnly_EditorDefault;
|
|
bool bPartialWakePreIntegration_EditorDefault;
|
|
FRealSingle MinimumMotionlessThreshold_EditorDefault;
|
|
FRealSingle LinearWakeThresholdMultiplier_EditorDefault;
|
|
FRealSingle AngularWakeThresholdMultiplier_EditorDefault;
|
|
bool bValidateConstraintSleepState_EditorDefault;
|
|
|
|
public:
|
|
// Storage of console variable pointer
|
|
IConsoleVariable* CVarPostStepWake;
|
|
IConsoleVariable* CVarMomentumPropagation;
|
|
IConsoleVariable* CVarPartialSleepCollisionConstraintsOnly;
|
|
IConsoleVariable* CVarPartialWakePreIntegration;
|
|
IConsoleVariable* CVarMinMotionlessRatio;
|
|
IConsoleVariable* CVarLinearWakeThresholdMultiplier;
|
|
IConsoleVariable* CVarAngularWakeThresholdMultiplier;
|
|
IConsoleVariable* CVarValidateConstraintSleepState;
|
|
};
|
|
|
|
class FSleepingTest
|
|
{
|
|
public:
|
|
FSleepingTest(const bool bPartialSleeping)
|
|
: MaterialData()
|
|
, FloorProxy()
|
|
, ParticleProxies()
|
|
, ParticleHandles()
|
|
, JointHandles()
|
|
, TickCount(0)
|
|
, CVarPartialSleeping()
|
|
{
|
|
// Create a new material before creating the solver
|
|
FMaterialHandle MaterialHandle = FPhysicalMaterialManager::Get().Create();
|
|
FPhysicsMaterial* PhysicsMaterial = MaterialHandle.Get();
|
|
PhysicsMaterial->Friction = 0.7;
|
|
PhysicsMaterial->StaticFriction = 0.0;
|
|
PhysicsMaterial->Restitution = 0.0;
|
|
PhysicsMaterial->Density = 1.0;
|
|
PhysicsMaterial->SleepingLinearThreshold = 1.0;
|
|
PhysicsMaterial->SleepingAngularThreshold = 0.05;
|
|
PhysicsMaterial->SleepCounterThreshold = 4;
|
|
MaterialData.Materials.Add(MaterialHandle);
|
|
|
|
Module = FChaosSolversModule::GetModule();
|
|
Solver = Module->CreateSolver(nullptr, /*AsyncDt=*/-1);
|
|
InitSolverSettings(Solver);
|
|
Solver->SetThreadingMode_External(EThreadingModeTemp::SingleThread);
|
|
|
|
CVarPartialSleeping = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.Solver.Sleep.PartialIslandSleep"), false);
|
|
check(CVarPartialSleeping && CVarPartialSleeping->IsVariableBool());
|
|
PartialSleepDefault = CVarPartialSleeping->GetBool();
|
|
CVarPartialSleeping->Set(bPartialSleeping);
|
|
}
|
|
|
|
~FSleepingTest()
|
|
{
|
|
CVarPartialSleeping->Set(PartialSleepDefault);
|
|
|
|
for (FSingleParticlePhysicsProxy* ParticleProxy : ParticleProxies)
|
|
{
|
|
Solver->UnregisterObject(ParticleProxy);
|
|
}
|
|
for (FJointConstraint* Joint : JointHandles)
|
|
{
|
|
if (Joint)
|
|
Solver->UnregisterObject(Joint);
|
|
}
|
|
if (FloorProxy)
|
|
Solver->UnregisterObject(FloorProxy);
|
|
Module->DestroySolver(Solver);
|
|
}
|
|
|
|
FRigidBodyHandle_External* MakeFloor(const FVec3& Position = FVec3(0))
|
|
{
|
|
FloorProxy = FSingleParticlePhysicsProxy::Create(Chaos::FGeometryParticle::CreateParticle());
|
|
FRigidBodyHandle_External* FloorParticle = &FloorProxy->GetGameThreadAPI();
|
|
FImplicitObjectPtr FloorGeom = Chaos::FImplicitObjectPtr(new TBox<FReal, 3>(FVec3(-5000, -5000, -100), FVec3(5000, 5000, 0)));
|
|
FloorParticle->SetGeometry(FloorGeom);
|
|
FloorParticle->SetObjectState(EObjectStateType::Static);
|
|
Solver->RegisterObject(FloorProxy);
|
|
FloorParticle->SetX(Position);
|
|
ChaosTest::SetParticleSimDataToCollide({ FloorProxy->GetParticle_LowLevel() });
|
|
for (const TUniquePtr<FPerShapeData>& Shape : FloorParticle->ShapesArray())
|
|
{
|
|
Shape->SetMaterialData(MaterialData);
|
|
}
|
|
return FloorParticle;
|
|
}
|
|
|
|
FRigidBodyHandle_External* MakeBox(const FVec3& Position, const FVec3& Dimensions = FVec3(200, 400, 100), const FReal Mass = 1)
|
|
{
|
|
FSingleParticlePhysicsProxy* BoxProxy = FSingleParticlePhysicsProxy::Create(Chaos::FPBDRigidParticle::CreateParticle());
|
|
FRigidBodyHandle_External* BoxParticle = &BoxProxy->GetGameThreadAPI();
|
|
const FVec3 HalfSize = Dimensions / 2.0f;
|
|
FImplicitObjectPtr CollidingCubeGeom = Chaos::FImplicitObjectPtr(new TBox<FReal, 3>(-HalfSize, HalfSize));
|
|
BoxParticle->SetGeometry(CollidingCubeGeom);
|
|
Solver->RegisterObject(BoxProxy);
|
|
BoxParticle->SetGravityEnabled(true);
|
|
BoxParticle->SetX(Position);
|
|
SetBoxInertiaTensor(*BoxParticle, /*Dimensions=*/Dimensions, /*Mass=*/Mass);
|
|
ChaosTest::SetParticleSimDataToCollide({ BoxProxy->GetParticle_LowLevel() });
|
|
for (const TUniquePtr<FPerShapeData>& Shape : BoxParticle->ShapesArray())
|
|
{
|
|
Shape->SetMaterialData(MaterialData);
|
|
}
|
|
|
|
ParticleProxies.Add(BoxProxy);
|
|
ParticleHandles.Add(BoxParticle);
|
|
|
|
return BoxParticle;
|
|
}
|
|
|
|
FRigidBodyHandle_External* MakeCube(const FVec3& Position, const FReal Size = 200, const FReal Mass = 1)
|
|
{
|
|
FSingleParticlePhysicsProxy* CubeProxy = FSingleParticlePhysicsProxy::Create(Chaos::FPBDRigidParticle::CreateParticle());
|
|
FRigidBodyHandle_External* CubeParticle = &CubeProxy->GetGameThreadAPI();
|
|
const FReal HalfSize = Size / 2.0f;
|
|
FImplicitObjectPtr CollidingCubeGeom = Chaos::FImplicitObjectPtr(new TBox<FReal, 3>(FVec3(-HalfSize), FVec3(HalfSize)));
|
|
CubeParticle->SetGeometry(CollidingCubeGeom);
|
|
Solver->RegisterObject(CubeProxy);
|
|
CubeParticle->SetGravityEnabled(true);
|
|
CubeParticle->SetX(Position);
|
|
SetCubeInertiaTensor(*CubeParticle, /*Dimension=*/Size, /*Mass=*/Mass);
|
|
ChaosTest::SetParticleSimDataToCollide({ CubeProxy->GetParticle_LowLevel() });
|
|
for (const TUniquePtr<FPerShapeData>& Shape : CubeParticle->ShapesArray())
|
|
{
|
|
Shape->SetMaterialData(MaterialData);
|
|
}
|
|
|
|
ParticleProxies.Add(CubeProxy);
|
|
ParticleHandles.Add(CubeParticle);
|
|
|
|
return CubeParticle;
|
|
}
|
|
|
|
FRigidBodyHandle_External* MakeSphere(const FVec3& Position, const FReal Radius = 100, const FReal Mass = 1)
|
|
{
|
|
FSingleParticlePhysicsProxy* SphereProxy = FSingleParticlePhysicsProxy::Create(Chaos::FPBDRigidParticle::CreateParticle());
|
|
FRigidBodyHandle_External* SphereParticle = &SphereProxy->GetGameThreadAPI();
|
|
FImplicitObjectPtr CollidingSphereGeom = Chaos::FImplicitObjectPtr(new TSphere<FReal, 3>(FVec3(0), Radius));
|
|
SphereParticle->SetGeometry(CollidingSphereGeom);
|
|
Solver->RegisterObject(SphereProxy);
|
|
SphereParticle->SetGravityEnabled(true);
|
|
SphereParticle->SetX(Position);
|
|
SetSphereInertiaTensor(*SphereParticle, /*Radius=*/Radius, /*Mass=*/Mass);
|
|
ChaosTest::SetParticleSimDataToCollide({ SphereProxy->GetParticle_LowLevel() });
|
|
for (const TUniquePtr<FPerShapeData>& Shape : SphereParticle->ShapesArray())
|
|
{
|
|
Shape->SetMaterialData(MaterialData);
|
|
}
|
|
|
|
ParticleProxies.Add(SphereProxy);
|
|
ParticleHandles.Add(SphereParticle);
|
|
|
|
return SphereParticle;
|
|
}
|
|
|
|
FJointConstraint* AddJoint(const FProxyBasePair& InConstrainedParticles, const FTransformPair& InTransform)
|
|
{
|
|
FJointConstraint* Joint = new FJointConstraint();
|
|
Joint->SetParticleProxies(InConstrainedParticles);
|
|
|
|
FPBDJointSettings SettingsTemp = Joint->GetJointSettings();
|
|
SettingsTemp.ConnectorTransforms = InTransform;
|
|
Joint->SetJointSettings(SettingsTemp);
|
|
|
|
Solver->RegisterObject(Joint);
|
|
|
|
JointHandles.Add(Joint);
|
|
return Joint;
|
|
}
|
|
|
|
void MakeStackOfCubes(const int32 Num, const FReal Size = 200, const FVec3 Pos = FVec3(0))
|
|
{
|
|
MakeFloor();
|
|
const FReal InitialHeight = Size / 2.0f;
|
|
for (int32 Id = 0; Id < Num; ++Id)
|
|
{
|
|
const FReal Z = InitialHeight + Size * Id;
|
|
MakeCube(Pos + FVec3(0, 0, Z), Size);
|
|
}
|
|
}
|
|
|
|
void MakeQueueOfCubes(const int32 Num, const FReal Size = 200)
|
|
{
|
|
MakeFloor();
|
|
const FReal InitialHeight = Size / 2.0f;
|
|
for (int32 Id = 0; Id < Num; ++Id)
|
|
{
|
|
const FReal Y = Size * Id;
|
|
const FReal Z = InitialHeight;
|
|
MakeCube(FVec3(0, Y, Z), Size);
|
|
}
|
|
}
|
|
|
|
void MakeStackOfSpheres(const int32 Num, const FReal Radius = 100)
|
|
{
|
|
MakeFloor();
|
|
const FReal InitialHeight = Radius;
|
|
for (int32 Id = 0; Id < Num; ++Id)
|
|
{
|
|
const FReal Z = InitialHeight + 2 * Radius * Id;
|
|
MakeSphere(FVec3(0, 0, Z), Radius);
|
|
}
|
|
}
|
|
|
|
void MakeChainOfSpheres(const int32 Num, const FReal Radius = 100)
|
|
{
|
|
const int32 DistanceMultiplier = 3;
|
|
for (int32 Id = 0; Id < Num; ++Id)
|
|
{
|
|
const FReal Z = -DistanceMultiplier * Radius * Id;
|
|
FRigidBodyHandle_External* SphereParticle = MakeSphere(FVec3(0, 0, Z), Radius);
|
|
}
|
|
for (int32 Id = 0; Id < Num - 1; ++Id)
|
|
{
|
|
FRigidTransform3 Transform1 = FRigidTransform3(FVec3(0, 0, -DistanceMultiplier * Radius * 0.5), FRotation3::FromIdentity());
|
|
FRigidTransform3 Transform2 = FRigidTransform3(FVec3(0, 0, DistanceMultiplier * Radius * 0.5), FRotation3::FromIdentity());
|
|
AddJoint({ ParticleProxies[Id], ParticleProxies[Id + 1] }, { Transform1 , Transform2 });
|
|
}
|
|
|
|
}
|
|
|
|
// Build a brick wall using the following pattern (NumY = 5, NumZ = 3)
|
|
//
|
|
// if (bShiftUnevenRows)
|
|
// P10 -P11 -P12 -P13 - P14
|
|
// \ / \ / \ / \ / \
|
|
// P5 - P6 - P7 - P8 - P9
|
|
// / \ / \ / \ / \ /
|
|
// P0 - P1 - P2 - P3 - P4
|
|
// | | | | |
|
|
// Floor
|
|
//
|
|
// if (!bShiftUnevenRows)
|
|
// P10 -P11 -P12 -P13 -P14
|
|
// | | | | |
|
|
// P5 - P6 - P7 - P8 - P9
|
|
// | | | | |
|
|
// P0 - P1 - P2 - P3 - P4
|
|
// | | | | |
|
|
// Floor
|
|
// NOTE: The COM of P0 is located at (0, 0, 0) and the top of the floor at (0, 0, -HalfBrickSize).
|
|
void MakeBrickWall(const int32 NumY, const int32 NumZ, const FVec3& BrickDimensions = FVec3(100, 200, 50), const FReal Mass = 2, const bool bShiftUnevenRows = true)
|
|
{
|
|
const FReal HalfWidth = BrickDimensions.Y * 0.5;
|
|
const FReal HalfHeight = BrickDimensions.Z * 0.5;
|
|
MakeFloor(FVec3(0, 0, -HalfHeight));
|
|
for (int32 Z = 0; Z < NumZ; ++Z)
|
|
{
|
|
for (int32 Y = 0; Y < NumY; ++Y)
|
|
{
|
|
const bool bIsUneven = Z % 2 == 1;
|
|
if (bShiftUnevenRows && bIsUneven) // uneven
|
|
{
|
|
//if (Y + 1 == NumY) continue; // skip the last brick
|
|
// Shift every uneven row by HalfWidth
|
|
MakeBox(FVec3(0, Y * BrickDimensions.Y + HalfWidth, Z * BrickDimensions.Z), BrickDimensions, Mass);
|
|
}
|
|
else // even
|
|
{
|
|
MakeBox(FVec3(0, Y * BrickDimensions.Y, Z * BrickDimensions.Z), BrickDimensions, Mass);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Advance()
|
|
{
|
|
Solver->AdvanceAndDispatch_External(1 / 60.0);
|
|
Solver->UpdateGameThreadStructures();
|
|
++TickCount;
|
|
}
|
|
|
|
void AdvanceUntilSleeping(const int32 MaxIterations = 200)
|
|
{
|
|
const int32 MaxTickCount = TickCount + MaxIterations;
|
|
bool bIsSleeping = false;
|
|
while (!bIsSleeping && (TickCount < MaxTickCount))
|
|
{
|
|
Advance();
|
|
|
|
bIsSleeping = true;
|
|
for (FRigidBodyHandle_External* ParticleHandle : ParticleHandles)
|
|
{
|
|
// Check that none of the particles is dynamic
|
|
if (ParticleHandle->ObjectState() == EObjectStateType::Dynamic)
|
|
{
|
|
bIsSleeping = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPECT_TRUE(bIsSleeping);
|
|
EXPECT_LT(TickCount, MaxTickCount);
|
|
}
|
|
|
|
FMaterialData MaterialData;
|
|
|
|
FSingleParticlePhysicsProxy* FloorProxy;
|
|
TArray<FSingleParticlePhysicsProxy*> ParticleProxies;
|
|
TArray<FRigidBodyHandle_External*> ParticleHandles;
|
|
TArray< FJointConstraint*> JointHandles;
|
|
|
|
FPBDRigidsSolver* Solver;
|
|
FChaosSolversModule* Module;
|
|
|
|
int32 TickCount;
|
|
|
|
IConsoleVariable* CVarPartialSleeping;
|
|
bool PartialSleepDefault;
|
|
};
|
|
|
|
// Dummy test to make sure a stack of cubes will fall asleep.
|
|
TEST_P(SleepingTests, SleepChainOfSpheres)
|
|
{
|
|
FSleepingTest Test(GetParam());
|
|
Test.MakeChainOfSpheres(5);
|
|
Test.ParticleHandles[0]->SetObjectState(EObjectStateType::Static);
|
|
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
for (int32 Id = 1; Id < Test.ParticleHandles.Num(); ++Id)
|
|
{
|
|
const FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
}
|
|
|
|
// Dummy test to make sure a stack of cubes will fall asleep.
|
|
TEST_P(SleepingTests, SleepStackOfCubes)
|
|
{
|
|
FSleepingTest Test(GetParam());
|
|
Test.MakeStackOfCubes(5);
|
|
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
}
|
|
|
|
// Dummy test to make sure a stack of perfectly aligned spheres will fall asleep.
|
|
TEST_P(SleepingTests, SleepStackOfSpheres)
|
|
{
|
|
FSleepingTest Test(GetParam());
|
|
Test.MakeStackOfSpheres(5);
|
|
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
}
|
|
|
|
TEST_P(SleepingTests, DropCubeOnStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Add another cube on top
|
|
// @todo (Chaos): This presently only triggers a wake-up event if the top cube is moving.
|
|
// This is not great and should be updated
|
|
const FReal PosZ = 1100; // Right above the stack
|
|
FRigidBodyHandle_External* TopCube = Test.MakeCube(FVec3(0, 0, PosZ), Dimension);
|
|
TopCube->SetV(FVec3(0, 0, -10));
|
|
|
|
Test.Advance();
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire stack will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only the top of the stack will wake up
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, CollideWithStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Make the stack collide with a particle
|
|
const FReal PosZ = 550; // slightly above the 3rd cube from below
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(200, 0, PosZ), Dimension);
|
|
CollidingCube->SetV(FVec3(-100, 0, 0), true);
|
|
|
|
Test.Advance();
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire stack will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only the top of the stack will wake up
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, PullCubeOutOfStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Pull one of the particles out of the stack by applying an impulse
|
|
Test.ParticleHandles[3]->SetLinearImpulse(FVec3(0, 100, 0), true);
|
|
|
|
Test.Advance();
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire stack will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The stack wakes up only above the level of where the impulse is applied.
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, UpdateMassOfCubeInStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off momentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Update the particle mass without waking the particle
|
|
Test.ParticleHandles[3]->SetM(10);
|
|
|
|
Test.Advance();
|
|
|
|
// Updating the particle mass only will not wake the stack.
|
|
// Neither in full nor in partial island sleep mode.
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Update the particle mass and wake up the particle
|
|
Test.ParticleHandles[3]->SetM(20);
|
|
Test.ParticleHandles[3]->SetObjectState(EObjectStateType::Dynamic);
|
|
|
|
Test.Advance();
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire stack wakes up.
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The stack wakes up only above the level of where the mass was changed.
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, DeleteBottomCubeInStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off momentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Delete the bottom cube
|
|
FSingleParticlePhysicsProxy* BottomBoxProxy = Test.ParticleProxies[0];
|
|
Test.ParticleHandles.RemoveAt(0);
|
|
Test.ParticleProxies.RemoveAt(0);
|
|
Test.Solver->UnregisterObject(BottomBoxProxy);
|
|
|
|
Test.Advance();
|
|
|
|
// The entire stack will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, TeleportCubeOutOfStack)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Teleport one of the cubes out of the stack
|
|
Test.ParticleHandles[2]->SetX(FVec3(0, 500, 0), true);
|
|
|
|
Test.Advance();
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire stack will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// With pre-integration partial waking, only one particle below the teleported one wakes up.
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, TeleportCubeFromOneStackToAnother)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const FRealSingle MomentumPropagation = GetParam() ? 0.5f : 0.0f;
|
|
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(MomentumPropagation);
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create two identical stacks of cubes which don't touch
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension, /*Pos=*/FVec3(0, 500, 0));
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Teleport one of the cubes out of the stack into the other stack
|
|
const FVec3 NewX = Test.ParticleHandles[4]->GetX() + FVec3(0, 500, 0);
|
|
Test.ParticleHandles[4]->SetX(NewX, true);
|
|
|
|
Test.Advance();
|
|
|
|
if (MomentumPropagation > 0.0f)
|
|
{
|
|
// Stack we teleport out of
|
|
// Everything wakes up if momentum transfer is enabled
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
|
|
// Stack we teleport into
|
|
// Everything wakes up if momentum transfer is enabled
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
else // MomentumPropagation == 0.0f
|
|
{
|
|
// Stack we teleport out of
|
|
// Only one particle above the one which is teleported will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
|
|
// Stack we teleport into
|
|
// Only one particle above the one which is teleported into the stack will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, TeleportCubeFromOneStackToAnotherWithPartialSleepDisallowed)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const bool bPartialIslandSleepAllowed = GetParam();
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create two identical stacks of cubes which don't touch
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension, /*Pos=*/FVec3(0, 500, 0));
|
|
|
|
// Limitation: This flag needs to be set at least a step before teleporting the particle
|
|
// since the island flag is only updated in CreateConstraintGraph and already needs to be read in UpdateExplicitSleep.
|
|
Test.ParticleHandles[4]->SetPartialIslandSleepAllowed(bPartialIslandSleepAllowed);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Teleport one of the cubes out of the stack into the other stack
|
|
const FVec3 NewX = Test.ParticleHandles[4]->GetX() + FVec3(0, 500, 0);
|
|
Test.ParticleHandles[4]->SetX(NewX, true);
|
|
|
|
Test.Advance();
|
|
|
|
if (bPartialIslandSleepAllowed)
|
|
{
|
|
// Stack we teleport out of
|
|
// Only one particle above the one which is teleported will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
|
|
// Stack we teleport into
|
|
// Only one particle above the one which is teleported into the stack will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
else
|
|
{
|
|
// Stack we teleport out of
|
|
// Everything wakes up since partial island sleeping is deactivated
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
|
|
// Stack we teleport into
|
|
// Everything wakes up since partial island sleeping is deactivated
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, TeleportCubeFromOneStackToAnotherWithKinematicObstacle)
|
|
{
|
|
const int32 NumOfStackedObjects = 6;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const FRealSingle MomentumPropagation = GetParam() ? 0.5f : 0.0f;
|
|
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(MomentumPropagation);
|
|
|
|
const bool bPartialWakePreIntegrationDefault = CVarPartialWakePreIntegration->GetBool();
|
|
CVarPartialWakePreIntegration->Set(true);
|
|
|
|
// Create two identical stacks of cubes which don't touch
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
Test.MakeStackOfCubes(NumOfStackedObjects, /*Size=*/Dimension, /*Pos=*/FVec3(0, 500, 0));
|
|
|
|
// Make the top of the static kinematic for levels not to correspond to horizontal height
|
|
Test.ParticleHandles[5]->SetObjectState(EObjectStateType::Static);
|
|
Test.ParticleHandles[11]->SetObjectState(EObjectStateType::Static);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
//// Check that everything is sleeping
|
|
//for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
//{
|
|
// EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
//}
|
|
|
|
// Teleport one of the cubes out of the stack into the other stack
|
|
const FVec3 NewX = Test.ParticleHandles[2]->GetX() + FVec3(0, 500, 400);
|
|
Test.ParticleHandles[2]->SetX(NewX, true);
|
|
|
|
Test.Advance();
|
|
|
|
if (MomentumPropagation > 0.0f)
|
|
{
|
|
// Stack we teleport out of
|
|
// Everything wakes up if momentum transfer is enabled
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Static);
|
|
|
|
// Stack we teleport into
|
|
// Everything wakes up if momentum transfer is enabled
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[10]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[11]->ObjectState() == EObjectStateType::Static);
|
|
}
|
|
else // MomentumPropagation == 0.0f
|
|
{
|
|
// Stack we teleport out of
|
|
// Only one particle above the one which is teleported will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Static);
|
|
|
|
// Stack we teleport into
|
|
// Only one particle above the one which is teleported into the stack will wake up (level-based waking).
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[10]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[11]->ObjectState() == EObjectStateType::Static);
|
|
}
|
|
|
|
CVarPartialWakePreIntegration->Set(bPartialWakePreIntegrationDefault);
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
// Uses a traditional brick wall where every 2nd row is shifted (see SleepingTests::MakeBrickWall)
|
|
TEST_P(SleepingTests, CollideWithBrickWall)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FVec3 Dimensions = FVec3(100, 200, 50);
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeBrickWall(/*NumY=*/ 5, /*NumZ=*/3, /*Dimensions=*/Dimensions);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping(/*MaxIterations=*/500);
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Make the stack collide with a particle located:
|
|
// - between the 2nd & 3rd row in height
|
|
// - between the 2nd & 3rd column in width
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(50, 300, 75), 50);
|
|
CollidingCube->SetV(FVec3(-200, 0, 0), true);
|
|
|
|
Test.Advance();
|
|
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire brick wall will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only the brick 6 colliding with the spawned cube and the bricks directly above wake up
|
|
// 1st row
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 2nd row
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 3rd row
|
|
EXPECT_TRUE(Test.ParticleHandles[10]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[11]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[12]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[13]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[14]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
// Uses a block of bricks without shifting (see SleepingTests::MakeBrickWall)
|
|
TEST_P(SleepingTests, CollideWithBlockOfBricks)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FVec3 Dimensions = FVec3(100, 200, 50);
|
|
const bool bPartialSleepEnabled = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeBrickWall(/*NumY=*/ 5, /*NumZ=*/3, /*Dimensions=*/Dimensions, /*Mass=*/2, /*bShiftUnevenRows*/false);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping(/*MaxIterations=*/500);
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Make the stack collide with a particle located:
|
|
// - between the 2nd & 3rd row in height
|
|
// - between the 2nd & 3rd column in width
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(50, 300, 75), 50);
|
|
CollidingCube->SetV(FVec3(-200, 0, 0), true);
|
|
|
|
Test.Advance();
|
|
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
|
|
if (!bPartialSleepEnabled)
|
|
{
|
|
// The entire brick wall will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only the bricks 6 & 7 colliding with the spawned cube and the bricks directly above wake up
|
|
// 1st row
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 2nd row
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 3rd row
|
|
EXPECT_TRUE(Test.ParticleHandles[10]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[11]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[12]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[13]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[14]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, CollideWithQueueOfCubes)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const bool bAllowPartialSleep = GetParam();
|
|
|
|
// Turn off mommentum propagation to test level-based waking only
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(0);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeQueueOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Create another particle which will collide with the queue
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(0, -200, 100), 200);
|
|
CollidingCube->SetV(FVec3(0, 200, 0), true);
|
|
CollidingCube->SetPartialIslandSleepAllowed(bAllowPartialSleep);
|
|
|
|
Test.Advance();
|
|
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
|
|
if (!bAllowPartialSleep)
|
|
{
|
|
// Check that everything is awake
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only the colliding cube and first particle in the queue wake up
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, PropagateMomentumThroughQueue)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const FRealSingle MomentumPropagation = GetParam() ? 0.5f : 0.1f;
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(MomentumPropagation);
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeQueueOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
// Create another particle which will collide with the queue
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(0, -200, 100), 200);
|
|
// Note: We use the VSmooth for momentum propagation (0.3 * v_old + 0.7 * v_new)
|
|
// Thus, only 30% of the velocity are actually transmitted.
|
|
CollidingCube->SetLinearImpulse(FVec3(0, 200, 0), true);
|
|
Test.Advance();
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
if (GetParam()) // 50% propagation
|
|
{
|
|
// Check that everything is awake
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else // 10% propagation
|
|
{
|
|
// Only the colliding cube and first two particles in the queue wake up
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
// Uses a block of bricks without shifting (see SleepingTests::MakeBrickWall)
|
|
TEST_P(SleepingTests, PropagateMomentumThroughBlockOfBricks)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FVec3 Dimensions = FVec3(100, 200, 50);
|
|
const bool bPartialSleepEnabled = true;
|
|
const FRealSingle MomentumPropagation = GetParam() ? 0.5f : 0.1f;
|
|
const FRealSingle MomentumPropagationDefault = CVarMomentumPropagation->GetFloat();
|
|
CVarMomentumPropagation->Set(MomentumPropagation);
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeBrickWall(/*NumY=*/ 5, /*NumZ=*/3, /*Dimensions=*/Dimensions, /*Mass=*/2, /*bShiftUnevenRows*/false);
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping(/*MaxIterations=*/500);
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
// Cube spawned about particles 11 & 12 (i.e. the 2nd and 3rd of the top row)
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(0, 300, 150), 50);
|
|
// Note: We use the VSmooth for momentum propagation (0.3 * v_old + 0.7 * v_new)
|
|
// Thus, only 30% of the velocity are actually transmitted.
|
|
CollidingCube->SetLinearImpulse(FVec3(0, 0, -200), true);
|
|
Test.Advance();
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
if (GetParam()) // 50% propagation
|
|
{
|
|
// The entire brick wall will wake up
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else // 10% propagation
|
|
{
|
|
// Colliding with particles 11 & 12
|
|
// Only particles with constraint distance <= 2 wake up.
|
|
// 1st row
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 2nd row
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[6]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[7]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[8]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[9]->ObjectState() == EObjectStateType::Sleeping);
|
|
// 3rd row
|
|
EXPECT_TRUE(Test.ParticleHandles[10]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[11]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[12]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[13]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[14]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
CVarMomentumPropagation->Set(MomentumPropagationDefault);
|
|
}
|
|
|
|
TEST_P(SleepingTests, PostStepWaking)
|
|
{
|
|
const int32 NumOfStackedObjects = 5;
|
|
const FReal Dimension = 200;
|
|
const bool bPartialSleepEnabled = true;
|
|
const FRealSingle PostStepWakeThreshold = GetParam() ? 10.0f : 0.0f;
|
|
|
|
const FRealSingle PostStepWakeThresholdDefault = CVarPostStepWake->GetFloat();
|
|
CVarPostStepWake->Set(PostStepWakeThreshold);
|
|
|
|
// Create a stack of cubes
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeQueueOfCubes(NumOfStackedObjects, /*Size=*/Dimension);
|
|
|
|
// Simulate until all particles are sleeping
|
|
Test.AdvanceUntilSleeping();
|
|
|
|
// Check that everything is sleeping
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
// Create another particle which will collide with the queue
|
|
FRigidBodyHandle_External* CollidingCube = Test.MakeCube(FVec3(0, -200, 100), 200);
|
|
|
|
for (int32 Step = 0; Step < 10; ++Step)
|
|
{
|
|
CollidingCube->SetLinearImpulse(FVec3(0, 1000, 0), true);
|
|
Test.Advance();
|
|
|
|
//// Debug print of all particle positions
|
|
//for (int32 Id = 0; Id < Test.ParticleHandles.Num(); ++Id)
|
|
//{
|
|
// FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
// printf("Particle Id: %d, State: %d, Position: %f, %f, %f\n", Id,
|
|
// ParticleHandle->ObjectState(), ParticleHandle->GetX().X, ParticleHandle->GetX().Y, ParticleHandle->GetX().Z);
|
|
//}
|
|
}
|
|
|
|
if (GetParam()) // post-step waking enabled
|
|
{
|
|
// Check that everything is awake
|
|
for (FRigidBodyHandle_External* ParticleHandle : Test.ParticleHandles)
|
|
{
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else // post-step waking disabled
|
|
{
|
|
// Only the colliding cube and first particle in the queue wake up
|
|
EXPECT_TRUE(Test.ParticleHandles[5]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
|
|
CVarPostStepWake->Set(PostStepWakeThresholdDefault);
|
|
}
|
|
|
|
// Sleep all particles except one which are connected by joint constraints.
|
|
// Since joint constraints don't support partial sleeping, the entire island wakes up.
|
|
TEST_P(SleepingTests, PartiallySleepingIslandWithJoints)
|
|
{
|
|
const bool bPartialSleepCollisionConstraintsOnly = GetParam();
|
|
const bool bPartialSleepCollisionConstraintsOnlyDefault = CVarPartialSleepCollisionConstraintsOnly->GetBool();
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnly);
|
|
|
|
const bool bPartialSleepEnabled = true;
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeChainOfSpheres(5);
|
|
|
|
Test.ParticleHandles[0]->SetObjectState(EObjectStateType::Static);
|
|
Test.ParticleHandles[1]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[2]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[3]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[4]->SetObjectState(EObjectStateType::Dynamic);
|
|
|
|
Test.Advance();
|
|
|
|
if (bPartialSleepCollisionConstraintsOnly)
|
|
{
|
|
// All particles wake up since the connecting constraints don't support partial sleeping
|
|
for (int32 Id = 1; Id < Test.ParticleHandles.Num(); ++Id)
|
|
{
|
|
const FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Partial sleep supported, no state change
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Static);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnlyDefault);
|
|
}
|
|
|
|
// TODO: Test with CVars::bChaosSolverPartialSleepCollisionConstraintsOnly == true
|
|
// Same as PartiallySleepingIslandWithJoints, just assign a different particle to be awake.
|
|
TEST_P(SleepingTests, PartiallySleepingIslandWithJoints_2)
|
|
{
|
|
const bool bPartialSleepCollisionConstraintsOnly = GetParam();
|
|
const bool bPartialSleepCollisionConstraintsOnlyDefault = CVarPartialSleepCollisionConstraintsOnly->GetBool();
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnly);
|
|
|
|
const bool bPartialSleepEnabled = true;
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeChainOfSpheres(5);
|
|
|
|
Test.ParticleHandles[0]->SetObjectState(EObjectStateType::Static);
|
|
Test.ParticleHandles[1]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[2]->SetObjectState(EObjectStateType::Dynamic);
|
|
Test.ParticleHandles[3]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[4]->SetObjectState(EObjectStateType::Sleeping);
|
|
|
|
Test.Advance();
|
|
|
|
if (bPartialSleepCollisionConstraintsOnly)
|
|
{
|
|
// All particles wake up since the connecting constraints don't support partial sleeping
|
|
for (int32 Id = 1; Id < Test.ParticleHandles.Num(); ++Id)
|
|
{
|
|
const FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Dynamic);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Partial sleep supported, no state change
|
|
EXPECT_TRUE(Test.ParticleHandles[0]->ObjectState() == EObjectStateType::Static);
|
|
EXPECT_TRUE(Test.ParticleHandles[1]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[2]->ObjectState() == EObjectStateType::Dynamic);
|
|
EXPECT_TRUE(Test.ParticleHandles[3]->ObjectState() == EObjectStateType::Sleeping);
|
|
EXPECT_TRUE(Test.ParticleHandles[4]->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnlyDefault);
|
|
}
|
|
|
|
// TODO: Test with CVars::bChaosSolverPartialSleepCollisionConstraintsOnly == true
|
|
// Sleep all particles except one which are connected by joint constraints.
|
|
// Since all constraints already sleep, the entire island remains asleep.
|
|
TEST_P(SleepingTests, FullySleepingIslandWithJoints)
|
|
{
|
|
const bool bPartialSleepCollisionConstraintsOnly = GetParam();
|
|
const bool bPartialSleepCollisionConstraintsOnlyDefault = CVarPartialSleepCollisionConstraintsOnly->GetBool();
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnly);
|
|
|
|
const bool bPartialSleepEnabled = true;
|
|
FSleepingTest Test(bPartialSleepEnabled);
|
|
Test.MakeChainOfSpheres(5);
|
|
|
|
Test.ParticleHandles[0]->SetObjectState(EObjectStateType::Static);
|
|
Test.ParticleHandles[1]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[2]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[3]->SetObjectState(EObjectStateType::Sleeping);
|
|
Test.ParticleHandles[4]->SetObjectState(EObjectStateType::Sleeping);
|
|
|
|
Test.Advance();
|
|
|
|
// All particles remain asleep since the entire island is asleep
|
|
for (int32 Id = 1; Id < Test.ParticleHandles.Num(); ++Id)
|
|
{
|
|
const FRigidBodyHandle_External* ParticleHandle = Test.ParticleHandles[Id];
|
|
EXPECT_TRUE(ParticleHandle->ObjectState() == EObjectStateType::Sleeping);
|
|
}
|
|
CVarPartialSleepCollisionConstraintsOnly->Set(bPartialSleepCollisionConstraintsOnlyDefault);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(, SleepingTests, testing::Bool());
|
|
} |