p.Chaos.Collision.CCD.ConstraintMaxProcessCount

p.Chaos.Collision.CCD.ConstraintMaxProcessCount

#Overview

name: p.Chaos.Collision.CCD.ConstraintMaxProcessCount

This variable is created as a Console Variable (cvar).

It is referenced in 9 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of p.Chaos.Collision.CCD.ConstraintMaxProcessCount is to control the maximum number of times each Continuous Collision Detection (CCD) constraint can be resolved during the physics simulation in Unreal Engine’s Chaos physics system.

This setting variable is primarily used in the Chaos physics system, which is part of Unreal Engine’s experimental physics framework. It’s specifically related to the collision detection and resolution subsystem within Chaos.

The value of this variable is set through a console variable (CVar) system, which allows it to be modified at runtime. It’s initialized with a default value of 1 in the source code.

This variable interacts closely with other CCD-related variables, such as bChaosCollisionCCDAllowClipping and bChaosCollisionCCDUseTightBoundingBox. These variables work together to control the behavior of the CCD system.

Developers must be aware that:

  1. Increasing this value will result in more accurate CCD resolution but at the cost of performance.
  2. Setting this value too low might result in missed collisions or visual velocity glitches.
  3. When this value is greater than 1, it can affect the bounding box calculation for CCD, potentially causing missed secondary collisions.

Best practices when using this variable include:

  1. Keep the value low (1 or 2) for performance-critical scenarios.
  2. Increase the value for scenarios where precise collision detection is crucial.
  3. Always test thoroughly after adjusting this value to ensure a good balance between accuracy and performance.

Regarding the associated variable ChaosCollisionCCDConstraintMaxProcessCount:

This is the internal C++ variable that actually stores the value set by the console variable. It’s used directly in the code to control the CCD constraint processing loop. The purpose and considerations are the same as for the console variable. It’s important to note that modifying this variable directly in code won’t have the same runtime flexibility as using the console variable system.

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:27

Scope (from outer to inner):

file
namespace    Chaos
namespace    CVars

Source code excerpt:

		// This will result in a visual velocity glitch when it happens, but usually this doesn't matter since the impact is very high energy anyway
		int32 ChaosCollisionCCDConstraintMaxProcessCount = 1;
		FAutoConsoleVariableRef CVarChaosCollisionCCDConstraintMaxProcessCount(TEXT("p.Chaos.Collision.CCD.ConstraintMaxProcessCount"), ChaosCollisionCCDConstraintMaxProcessCount, TEXT("The max number of times each constraint can be resolved when applying CCD constraints. Default is 2. The larger this number is, the more fully CCD constraints are resolved."));

		// Determines when CCD is switched on
		FRealSingle CCDEnableThresholdBoundsScale = 0.4f;
		FAutoConsoleVariableRef  CVarCCDEnableThresholdBoundsScale(TEXT("p.Chaos.CCD.EnableThresholdBoundsScale"), CCDEnableThresholdBoundsScale , TEXT("CCD is used when object position is changing > smallest bound's extent * BoundsScale. 0 will always Use CCD. Values < 0 disables CCD."));

		// Determines how much penetration CCD leaves after rewinding to TOI. Must be less than CCDEnableThresholdBoundsScale

#Associated Variable and Callsites

This variable is associated with another variable named ChaosCollisionCCDConstraintMaxProcessCount. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:20

Scope (from outer to inner):

file
namespace    Chaos
namespace    CVars

Source code excerpt:

		FAutoConsoleVariableRef CVarChaosCollisionCCDEnableResweep(TEXT("p.Chaos.Collision.CCD.EnableResweep"), bChaosCollisionCCDEnableResweep, TEXT("Enable resweep for CCD. Resweeping allows CCD to catch more secondary collisions but also is more costly. Default is true."));

		// NOTE: With this disabled, secondary collisions can be missed. When enabled, velocity will not be visually consistent after CCD collisions (if ChaosCollisionCCDConstraintMaxProcessCount is too low)
		bool bChaosCollisionCCDAllowClipping = true;
		FAutoConsoleVariableRef CVarChaosCollisionCCDAllowClipping(TEXT("p.Chaos.Collision.CCD.AllowClipping"), bChaosCollisionCCDAllowClipping, TEXT("This will clip the CCD object at colliding positions when computation budgets run out. Default is true. Turning this option off might cause tunneling."));

		// By default, we stop processing CCD contacts after a single CCD interaction
		// This will result in a visual velocity glitch when it happens, but usually this doesn't matter since the impact is very high energy anyway
		int32 ChaosCollisionCCDConstraintMaxProcessCount = 1;
		FAutoConsoleVariableRef CVarChaosCollisionCCDConstraintMaxProcessCount(TEXT("p.Chaos.Collision.CCD.ConstraintMaxProcessCount"), ChaosCollisionCCDConstraintMaxProcessCount, TEXT("The max number of times each constraint can be resolved when applying CCD constraints. Default is 2. The larger this number is, the more fully CCD constraints are resolved."));

		// Determines when CCD is switched on
		FRealSingle CCDEnableThresholdBoundsScale = 0.4f;
		FAutoConsoleVariableRef  CVarCCDEnableThresholdBoundsScale(TEXT("p.Chaos.CCD.EnableThresholdBoundsScale"), CCDEnableThresholdBoundsScale , TEXT("CCD is used when object position is changing > smallest bound's extent * BoundsScale. 0 will always Use CCD. Values < 0 disables CCD."));

		// Determines how much penetration CCD leaves after rewinding to TOI. Must be less than CCDEnableThresholdBoundsScale

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:114

Scope (from outer to inner):

file
namespace    Chaos
function     void FCCDManager::ApplySweptConstraints

Source code excerpt:


		// Use simpler processing loop if we have only 1 CCD iteration
		const bool bUseSimpleCCD = (CVars::ChaosCollisionCCDConstraintMaxProcessCount == 1);

		AssignParticleIslandsAndGroupParticles();
		AssignConstraintIslandsAndRecordConstraintNum();
		GroupConstraintsWithIslands();
		PhysicsParallelFor(IslandNum, [&](const int32 Island)
		{

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:537

Scope (from outer to inner):

file
namespace    Chaos
function     void FCCDManager::ApplyIslandSweptConstraints

Source code excerpt:

			}
			
			ensure(CCDConstraint->ProcessedCount < CVars::ChaosCollisionCCDConstraintMaxProcessCount);

			// In UpdateConstraintSwept, InitManifoldPoint requires P, Q to be at TOI=1., but the input of 
			// UpdateConstraintSwept requires transforms at current TOI. So instead of rewinding P, Q, we 
			// advance X, R to current TOI and keep P, Q at TOI=1.
			if (CCDParticle0 && !CCDParticle0->Done)
			{

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:561

Scope (from outer to inner):

file
namespace    Chaos
function     void FCCDManager::ApplyIslandSweptConstraints

Source code excerpt:

			bool bMovedParticle0 = false;
			bool bMovedParticle1 = false;
			if (CCDConstraint->ProcessedCount >= CVars::ChaosCollisionCCDConstraintMaxProcessCount)
			{
				/* Here is how clipping works:
				* Assuming collision detection gives us all the possible collision pairs in the current frame.
				* Because we sort and apply constraints based on their TOIs, at current IslandTOI, the two particles cannot tunnel through other particles in the island. 
				* Now, we run out of the computational budget for this constraint, then we freeze the two particles in place. The current two particles cannot tunnel through each other this frame.
				* The two particles are then treated as static. When resweeping, we update TOIs of other constraints to make sure other particles in the island cannot tunnel through this two particles.
				* Therefore, by clipping, we can avoid tunneling but this is at the cost of reduced momentum.
				* For kinematic particles, we cannot freeze them in place. In this case, we simply offset the particle with the kinematic motion from [IslandTOI, 1] along the collision normal and freeze it there.
				* If collision detection is not perfect and does not give us all the secondary collision pairs, setting ChaosCollisionCCDConstraintMaxProcessCount to 1 will always prevent tunneling.
				*/ 
				if (CVars::bChaosCollisionCCDAllowClipping)
				{
					if (CCDParticle0)
					{
						if (CCDConstraint->FastMovingKinematicIndex != INDEX_NONE)
						{
							// @todo(chaos): can this just get the particle from the CCD constraint?
							const FConstGenericParticleHandle KinematicParticle = FGenericParticleHandle(CCDConstraint->SweptConstraint->GetParticle(CCDConstraint->FastMovingKinematicIndex));
							const FVec3 Normal = CCDConstraint->SweptConstraint->CalculateWorldContactNormal();
							const FVec3 Offset = FVec3::DotProduct(KinematicParticle->V() * ((1.f - IslandTOI) * Dt), Normal) * Normal;
							ClipParticleP(CCDParticle0, Offset);
						}
						else

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/CCDUtilities.cpp:675

Scope (from outer to inner):

file
namespace    Chaos
function     bool FCCDManager::UpdateParticleSweptConstraints

Source code excerpt:

			{
				FCCDConstraint* AttachedCCDConstraint = CCDParticle->AttachedCCDConstraints[AttachedCCDConstraintIndex];
				if (AttachedCCDConstraint->ProcessedCount >= CVars::ChaosCollisionCCDConstraintMaxProcessCount)
				{
					continue;
				}

				const bool bParticle0Done = ((AttachedCCDConstraint->Particle[0] == nullptr) || AttachedCCDConstraint->Particle[0]->Done);
				const bool bParticle1Done = ((AttachedCCDConstraint->Particle[1] == nullptr) || AttachedCCDConstraint->Particle[1]->Done);

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDRigidsEvolutionGBF.cpp:62

Scope (from outer to inner):

file
namespace    Chaos
namespace    CVars

Source code excerpt:

		FAutoConsoleVariableRef CVarDisableParticleUpdateVelocityParallelFor(TEXT("p.DisableParticleUpdateVelocityParallelFor"), DisableParticleUpdateVelocityParallelFor, TEXT("Disable Particle Update Velocity ParallelFor and run the update on a single thread"));

		// NOTE: If we have mrope than 1 CCD iteration (ChaosCollisionCCDConstraintMaxProcessCount), the tight bounding box will cause us to miss secondary CCD collisions if the first one(s) result in a change in direction
		bool bChaosCollisionCCDUseTightBoundingBox = true;
		FAutoConsoleVariableRef  CVarChaosCollisionCCDUseTightBoundingBox(TEXT("p.Chaos.Collision.CCD.UseTightBoundingBox"), bChaosCollisionCCDUseTightBoundingBox , TEXT(""));

		// Collision Solver Type (see ECollisionSolverType)
		int32 ChaosSolverCollisionSolverType = -1;
		FAutoConsoleVariableRef CVarChaosSolverCollisionSolverType(TEXT("p.Chaos.Solver.Collision.SolverType"), ChaosSolverCollisionSolverType, TEXT("-1: Use default (Gauss Seidel); 0: Gauss Seidel; 1: Gauss Seidel SOA 2: Partial Jacobi"));

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDRigidsEvolutionGBF.cpp:873

Scope (from outer to inner):

file
function     void FPBDRigidsEvolutionGBF::Integrate
lambda-function

Source code excerpt:

						const FVec3 VDt = V * Dt;
						FReal CCDBoundsExpansion = BoundsThickness;
						if (!CVars::bChaosCollisionCCDUseTightBoundingBox && (CVars::ChaosCollisionCCDConstraintMaxProcessCount > 1))
						{
							CCDBoundsExpansion += VDt.GetAbsMax();
						}
						Particle.UpdateWorldSpaceStateSwept(FRigidTransform3(Particle.GetP(), Particle.GetQ()), FVec3(CCDBoundsExpansion), -VDt);
					}
					else

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Public/Chaos/PBDRigidsEvolutionGBF.h:33

Scope (from outer to inner):

file
namespace    Chaos
namespace    CVars

Source code excerpt:

		CHAOS_API extern FRealSingle SmoothedPositionLerpRate;
		CHAOS_API extern bool bChaosCollisionCCDUseTightBoundingBox;
		CHAOS_API extern int32 ChaosCollisionCCDConstraintMaxProcessCount;
		CHAOS_API extern int32 ChaosSolverDrawCCDThresholds;
	}

	using FPBDRigidsEvolutionCallback = TFunction<void(FReal Dt)>;

	using FPBDRigidsEvolutionIslandCallback = TFunction<void(int32 Island)>;