Ver Fonte

Switch from using XPBD to classic PBD with substepping (#979)

Jorrit Rouwe há 1 ano atrás
pai
commit
2bd2cfec11

+ 1 - 1
Docs/Architecture.md

@@ -738,7 +738,7 @@ These jobs will do broadphase checks for all of the soft bodies. A thread picks
 
 
 ## Soft Body Simulate {#soft-body-simulate}
 ## Soft Body Simulate {#soft-body-simulate}
 
 
-These jobs will do the actual simulation of the soft bodies. They first collide batches of soft body vertices with the rigid bodies found during the Collide job (multiple threads can work on a single soft body) and then perform the simulation using XPBD (also partially distributing a single soft body on multiple threads).
+These jobs will do the actual simulation of the soft bodies. They first collide batches of soft body vertices with the rigid bodies found during the Collide job (multiple threads can work on a single soft body) and then perform the simulation using PBD (also partially distributing a single soft body on multiple threads).
 
 
 ## Soft Body Finalize {#soft-body-finalize}
 ## Soft Body Finalize {#soft-body-finalize}
 
 

+ 7 - 11
Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp

@@ -246,12 +246,10 @@ void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &i
 		}
 		}
 }
 }
 
 
-void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext)
+void SoftBodyMotionProperties::ApplyVolumeConstraints()
 {
 {
 	JPH_PROFILE_FUNCTION();
 	JPH_PROFILE_FUNCTION();
 
 
-	float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
-
 	// Satisfy volume constraints
 	// Satisfy volume constraints
 	for (const Volume &v : mSettings->mVolumeConstraints)
 	for (const Volume &v : mSettings->mVolumeConstraints)
 	{
 	{
@@ -284,7 +282,7 @@ void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContex
 		JPH_ASSERT(w1 > 0.0f || w2 > 0.0f || w3 > 0.0f || w4 > 0.0f);
 		JPH_ASSERT(w1 > 0.0f || w2 > 0.0f || w3 > 0.0f || w4 > 0.0f);
 
 
 		// Apply correction
 		// Apply correction
-		float lambda = -c / (w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v.mCompliance * inv_dt_sq);
+		float lambda = -v.mStiffness * c / (w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq());
 		v1.mPosition += lambda * w1 * d1c;
 		v1.mPosition += lambda * w1 * d1c;
 		v2.mPosition += lambda * w2 * d2c;
 		v2.mPosition += lambda * w2 * d2c;
 		v3.mPosition += lambda * w3 * d3c;
 		v3.mPosition += lambda * w3 * d3c;
@@ -346,12 +344,10 @@ void SoftBodyMotionProperties::ApplySkinConstraints([[maybe_unused]] const SoftB
 	}
 	}
 }
 }
 
 
-void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
+void SoftBodyMotionProperties::ApplyEdgeConstraints(uint inStartIndex, uint inEndIndex)
 {
 {
 	JPH_PROFILE_FUNCTION();
 	JPH_PROFILE_FUNCTION();
 
 
-	float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
-
 	// Satisfy edge constraints
 	// Satisfy edge constraints
 	const Array<Edge> &edge_constraints = mSettings->mEdgeConstraints;
 	const Array<Edge> &edge_constraints = mSettings->mEdgeConstraints;
 	for (uint i = inStartIndex; i < inEndIndex; ++i)
 	for (uint i = inStartIndex; i < inEndIndex; ++i)
@@ -367,7 +363,7 @@ void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext
 		if (length > 0.0f)
 		if (length > 0.0f)
 		{
 		{
 			// Apply correction
 			// Apply correction
-			Vec3 correction = delta * (length - e.mRestLength) / (length * (v0.mInvMass + v1.mInvMass + e.mCompliance * inv_dt_sq));
+			Vec3 correction = e.mStiffness * delta * (length - e.mRestLength) / (length * (v0.mInvMass + v1.mInvMass));
 			v0.mPosition += v0.mInvMass * correction;
 			v0.mPosition += v0.mInvMass * correction;
 			v1.mPosition -= v1.mInvMass * correction;
 			v1.mPosition -= v1.mInvMass * correction;
 		}
 		}
@@ -407,7 +403,7 @@ void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(cons
 			// Remember previous velocity for restitution calculations
 			// Remember previous velocity for restitution calculations
 			Vec3 prev_v = v.mVelocity;
 			Vec3 prev_v = v.mVelocity;
 
 
-			// XPBD velocity update
+			// Verlet velocity update
 			v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
 			v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
 
 
 			// Satisfy collision constraint
 			// Satisfy collision constraint
@@ -634,7 +630,7 @@ void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &i
 
 
 	IntegratePositions(ioContext);
 	IntegratePositions(ioContext);
 
 
-	ApplyVolumeConstraints(ioContext);
+	ApplyVolumeConstraints();
 }
 }
 
 
 SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
 SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
@@ -690,7 +686,7 @@ SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyEdgeCon
 					uint num_edges_to_process = non_parallel_group? edge_group_size : min(SoftBodyUpdateContext::cEdgeConstraintBatch, edge_group_size - edge_start_idx);
 					uint num_edges_to_process = non_parallel_group? edge_group_size : min(SoftBodyUpdateContext::cEdgeConstraintBatch, edge_group_size - edge_start_idx);
 					if (edge_group > 0)
 					if (edge_group > 0)
 						edge_start_idx += mSettings->mEdgeGroupEndIndices[edge_group - 1];
 						edge_start_idx += mSettings->mEdgeGroupEndIndices[edge_group - 1];
-					ApplyEdgeConstraints(ioContext, edge_start_idx, edge_start_idx + num_edges_to_process);
+					ApplyEdgeConstraints(edge_start_idx, edge_start_idx + num_edges_to_process);
 
 
 					// Test if we're at the end of this group
 					// Test if we're at the end of this group
 					uint edge_constraints_processed = ioContext.mNumEdgeConstraintsProcessed.fetch_add(num_edges_to_process, memory_order_relaxed) + num_edges_to_process;
 					uint edge_constraints_processed = ioContext.mNumEdgeConstraintsProcessed.fetch_add(num_edges_to_process, memory_order_relaxed) + num_edges_to_process;

+ 4 - 5
Jolt/Physics/SoftBody/SoftBodyMotionProperties.h

@@ -27,8 +27,7 @@ class DebugRenderer;
 
 
 /// This class contains the runtime information of a soft body.
 /// This class contains the runtime information of a soft body.
 //
 //
-// Based on: XPBD, Extended Position Based Dynamics, Matthias Muller, Ten Minute Physics
-// See: https://matthias-research.github.io/pages/tenMinutePhysics/09-xpbd.pdf
+// Using a classic PBD simulation with sub stepping instead of running multiple Gauss-Seidel iterations before stepping.
 class JPH_EXPORT SoftBodyMotionProperties : public MotionProperties
 class JPH_EXPORT SoftBodyMotionProperties : public MotionProperties
 {
 {
 public:
 public:
@@ -194,18 +193,18 @@ private:
 	void								IntegratePositions(const SoftBodyUpdateContext &inContext);
 	void								IntegratePositions(const SoftBodyUpdateContext &inContext);
 
 
 	/// Enforce all volume constraints
 	/// Enforce all volume constraints
-	void								ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext);
+	void								ApplyVolumeConstraints();
 
 
 	/// Enforce all skin constraints
 	/// Enforce all skin constraints
 	void								ApplySkinConstraints(const SoftBodyUpdateContext &inContext);
 	void								ApplySkinConstraints(const SoftBodyUpdateContext &inContext);
 
 
 	/// Enforce all edge constraints
 	/// Enforce all edge constraints
-	void								ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
+	void								ApplyEdgeConstraints(uint inStartIndex, uint inEndIndex);
 
 
 	/// Enforce all LRA constraints
 	/// Enforce all LRA constraints
 	void								ApplyLRAConstraints();
 	void								ApplyLRAConstraints();
 
 
-	/// Enforce all collision constraints & update all velocities according the XPBD algorithm
+	/// Enforce all collision constraints & update all velocities
 	void								ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext);
 	void								ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext);
 
 
 	/// Update the state of the soft body (position, velocity, bounds)
 	/// Update the state of the soft body (position, velocity, bounds)

+ 2 - 2
Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp

@@ -32,14 +32,14 @@ JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Edge)
 {
 {
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength)
-	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance)
+	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mStiffness)
 }
 }
 
 
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume)
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume)
 {
 {
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume)
-	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance)
+	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mStiffness)
 }
 }
 
 
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)

+ 5 - 5
Jolt/Physics/SoftBody/SoftBodySharedSettings.h

@@ -101,11 +101,11 @@ public:
 
 
 		/// Constructor
 		/// Constructor
 						Edge() = default;
 						Edge() = default;
-						Edge(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { }
+						Edge(uint32 inVertex1, uint32 inVertex2, float inStiffness = 0.9f) : mVertex { inVertex1, inVertex2 }, mStiffness(inStiffness) { }
 
 
 		uint32			mVertex[2];									///< Indices of the vertices that form the edge
 		uint32			mVertex[2];									///< Indices of the vertices that form the edge
 		float			mRestLength = 1.0f;							///< Rest length of the spring
 		float			mRestLength = 1.0f;							///< Rest length of the spring
-		float			mCompliance = 0.0f;							///< Inverse of the stiffness of the spring
+		float			mStiffness = 0.9f;							///< The stiffness of the spring. This value is normalized between 0 and 1. 1 means that the spring is completely stiff. Note that stiffness depends on the time step and the number of iterations, the smaller the time step or the higher the number of iterations, the stiffer the edge.
 	};
 	};
 
 
 	/// Volume constraint, keeps the volume of a tetrahedron constant
 	/// Volume constraint, keeps the volume of a tetrahedron constant
@@ -115,11 +115,11 @@ public:
 
 
 		/// Constructor
 		/// Constructor
 						Volume() = default;
 						Volume() = default;
-						Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { }
+						Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inStiffness = 0.9f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mStiffness(inStiffness) { }
 
 
-		uint32			mVertex[4];									///< Indices of the vertices that form the tetrhedron
+		uint32			mVertex[4];									///< Indices of the vertices that form the tetrahedron
 		float			mSixRestVolume = 1.0f;						///< 6 times the rest volume of the tetrahedron
 		float			mSixRestVolume = 1.0f;						///< 6 times the rest volume of the tetrahedron
-		float			mCompliance = 0.0f;							///< Inverse of the stiffness of the constraint
+		float			mStiffness = 0.9f;							///< The stiffness of the constraint. This value is normalized between 0 and 1. 1 means that the volume is completely stiff. Note that stiffness depends on the time step and the number of iterations, the smaller the time step or the higher the number of iterations, the stiffer the volume.
 	};
 	};
 
 
 	/// An inverse bind matrix take a skinned vertex from its bind pose into joint local space
 	/// An inverse bind matrix take a skinned vertex from its bind pose into joint local space

+ 1 - 1
Samples/Tests/SoftBody/SoftBodyLRAConstraintTest.cpp

@@ -22,7 +22,7 @@ void SoftBodyLRAConstraintTest::Initialize()
 	auto inv_mass = [](uint, uint inZ) { return inZ == 0? 0.0f : 1.0f; };
 	auto inv_mass = [](uint, uint inZ) { return inZ == 0? 0.0f : 1.0f; };
 	Ref<SoftBodySharedSettings> cloth_settings = SoftBodyCreator::CreateCloth(cNumVerticesX, cNumVerticesZ, cVertexSpacing, inv_mass);
 	Ref<SoftBodySharedSettings> cloth_settings = SoftBodyCreator::CreateCloth(cNumVerticesX, cNumVerticesZ, cVertexSpacing, inv_mass);
 	for (SoftBodySharedSettings::Edge &e : cloth_settings->mEdgeConstraints)
 	for (SoftBodySharedSettings::Edge &e : cloth_settings->mEdgeConstraints)
-		e.mCompliance = 1.0e-3f; // Soften the edges a bit so that the effect of the LRA constraints is more visible
+		e.mStiffness = 0.1f; // Soften the edges a bit so that the effect of the LRA constraints is more visible
 	SoftBodyCreationSettings cloth(cloth_settings, RVec3(-10.0f, 25.0f, 0), Quat::sIdentity(), Layers::MOVING);
 	SoftBodyCreationSettings cloth(cloth_settings, RVec3(-10.0f, 25.0f, 0), Quat::sIdentity(), Layers::MOVING);
 	mBodyInterface->CreateAndAddSoftBody(cloth, EActivation::Activate);
 	mBodyInterface->CreateAndAddSoftBody(cloth, EActivation::Activate);
 
 

+ 1 - 1
Samples/Tests/SoftBody/SoftBodySkinnedConstraintTest.cpp

@@ -65,7 +65,7 @@ void SoftBodySkinnedConstraintTest::Initialize()
 
 
 	// Make edges soft
 	// Make edges soft
 	for (SoftBodySharedSettings::Edge &e : settings->mEdgeConstraints)
 	for (SoftBodySharedSettings::Edge &e : settings->mEdgeConstraints)
-		e.mCompliance = 1.0e-3f;
+		e.mStiffness = 0.1f;
 
 
 	// Create inverse bind matrices by moving the bind pose to the center of mass space for the body
 	// Create inverse bind matrices by moving the bind pose to the center of mass space for the body
 	Array<Mat44> bind_pose = GetWorldSpacePose(0.0f);
 	Array<Mat44> bind_pose = GetWorldSpacePose(0.0f);

+ 0 - 2
Samples/Utils/SoftBodyCreator.cpp

@@ -41,7 +41,6 @@ Ref<SoftBodySharedSettings> CreateCloth(uint inGridSizeX, uint inGridSizeZ, floa
 		for (uint x = 0; x < inGridSizeX; ++x)
 		for (uint x = 0; x < inGridSizeX; ++x)
 		{
 		{
 			SoftBodySharedSettings::Edge e;
 			SoftBodySharedSettings::Edge e;
-			e.mCompliance = 0.00001f;
 			e.mVertex[0] = vertex_index(x, z);
 			e.mVertex[0] = vertex_index(x, z);
 			if (x < inGridSizeX - 1)
 			if (x < inGridSizeX - 1)
 			{
 			{
@@ -279,7 +278,6 @@ Ref<SoftBodySharedSettings> CreateSphere(float inRadius, uint inNumTheta, uint i
 		for (uint theta = 0; theta < inNumTheta - 1; ++theta)
 		for (uint theta = 0; theta < inNumTheta - 1; ++theta)
 		{
 		{
 			SoftBodySharedSettings::Edge e;
 			SoftBodySharedSettings::Edge e;
-			e.mCompliance = 0.0001f;
 			e.mVertex[0] = vertex_index(theta, phi);
 			e.mVertex[0] = vertex_index(theta, phi);
 
 
 			e.mVertex[1] = vertex_index(theta + 1, phi);
 			e.mVertex[1] = vertex_index(theta + 1, phi);