Browse Source

Undo 2bd2cfec11396cf903046c9f32156ea8a381907a (#982)

Jorrit Rouwe 1 year ago
parent
commit
e60de1aa54

+ 0 - 1
Docs/APIChanges.md

@@ -6,7 +6,6 @@ Changes that make some state saved through SaveBinaryState from a prior version
 
 
 ## Changes between v4.0.2 and latest
 ## Changes between v4.0.2 and latest
 
 
-* 20240313 - *SBS* - Soft bodies switched from XPBD to PBD. As a result mCompliance is now mStiffness for the edge and volume constraints. This makes it compatible with Godot. (2bd2cfec11396cf903046c9f32156ea8a381907a)
 * 20240310 - *SBS* - Soft body skinned constraints now use a sphere as backstop instead of an infinite plane. This also breaks the serialization format for SoftBodySharedSettings. (17db6d3f245d2198319c3787f62498fe5935b7c8)
 * 20240310 - *SBS* - Soft body skinned constraints now use a sphere as backstop instead of an infinite plane. This also breaks the serialization format for SoftBodySharedSettings. (17db6d3f245d2198319c3787f62498fe5935b7c8)
 * 20240225 - *SBS* - Changes were made to SoftBodySharedSettings that break the binary serialization format of that class. (277b818ffefed4f15477ff1e6d0cc07065899903)
 * 20240225 - *SBS* - Changes were made to SoftBodySharedSettings that break the binary serialization format of that class. (277b818ffefed4f15477ff1e6d0cc07065899903)
 * 20240223 - Added ConvexShape::ESupportMode::Default. If you have custom convex shapes you need to handle this in ConvexShape::GetSupportFunction. (0f67cc2915c5e34a4a38480580dad73888a1952e)
 * 20240223 - Added ConvexShape::ESupportMode::Default. If you have custom convex shapes you need to handle this in ConvexShape::GetSupportFunction. (0f67cc2915c5e34a4a38480580dad73888a1952e)

+ 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 PBD (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 XPBD (also partially distributing a single soft body on multiple threads).
 
 
 ## Soft Body Finalize {#soft-body-finalize}
 ## Soft Body Finalize {#soft-body-finalize}
 
 

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

@@ -246,10 +246,12 @@ void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &i
 		}
 		}
 }
 }
 
 
-void SoftBodyMotionProperties::ApplyVolumeConstraints()
+void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext)
 {
 {
 	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)
 	{
 	{
@@ -282,7 +284,7 @@ void SoftBodyMotionProperties::ApplyVolumeConstraints()
 		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 = -v.mStiffness * c / (w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq());
+		float lambda = -c / (w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v.mCompliance * inv_dt_sq);
 		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;
@@ -344,10 +346,12 @@ void SoftBodyMotionProperties::ApplySkinConstraints([[maybe_unused]] const SoftB
 	}
 	}
 }
 }
 
 
-void SoftBodyMotionProperties::ApplyEdgeConstraints(uint inStartIndex, uint inEndIndex)
+void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, 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)
@@ -363,7 +367,7 @@ void SoftBodyMotionProperties::ApplyEdgeConstraints(uint inStartIndex, uint inEn
 		if (length > 0.0f)
 		if (length > 0.0f)
 		{
 		{
 			// Apply correction
 			// Apply correction
-			Vec3 correction = e.mStiffness * delta * (length - e.mRestLength) / (length * (v0.mInvMass + v1.mInvMass));
+			Vec3 correction = delta * (length - e.mRestLength) / (length * (v0.mInvMass + v1.mInvMass + e.mCompliance * inv_dt_sq));
 			v0.mPosition += v0.mInvMass * correction;
 			v0.mPosition += v0.mInvMass * correction;
 			v1.mPosition -= v1.mInvMass * correction;
 			v1.mPosition -= v1.mInvMass * correction;
 		}
 		}
@@ -403,7 +407,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;
 
 
-			// Verlet velocity update
+			// XPBD velocity update
 			v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
 			v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
 
 
 			// Satisfy collision constraint
 			// Satisfy collision constraint
@@ -630,7 +634,7 @@ void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &i
 
 
 	IntegratePositions(ioContext);
 	IntegratePositions(ioContext);
 
 
-	ApplyVolumeConstraints();
+	ApplyVolumeConstraints(ioContext);
 }
 }
 
 
 SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
 SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
@@ -686,7 +690,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(edge_start_idx, edge_start_idx + num_edges_to_process);
+					ApplyEdgeConstraints(ioContext, 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;

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

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

+ 7 - 7
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, mStiffness)
+	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance)
 }
 }
 
 
 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, mStiffness)
+	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance)
 }
 }
 
 
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
 JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
@@ -83,7 +83,7 @@ JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
 }
 }
 
 
-void SoftBodySharedSettings::CreateEdges(float inStiffness, float inAngleTolerance)
+void SoftBodySharedSettings::CreateEdges(float inCompliance, float inAngleTolerance)
 {
 {
 	struct EdgeHelper
 	struct EdgeHelper
 	{
 	{
@@ -111,13 +111,13 @@ void SoftBodySharedSettings::CreateEdges(float inStiffness, float inAngleToleran
 	QuickSort(edges.begin(), edges.end(), [](const EdgeHelper &inLHS, const EdgeHelper &inRHS) { return inLHS.mVertex[0] < inRHS.mVertex[0] || (inLHS.mVertex[0] == inRHS.mVertex[0] && inLHS.mVertex[1] < inRHS.mVertex[1]); });
 	QuickSort(edges.begin(), edges.end(), [](const EdgeHelper &inLHS, const EdgeHelper &inRHS) { return inLHS.mVertex[0] < inRHS.mVertex[0] || (inLHS.mVertex[0] == inRHS.mVertex[0] && inLHS.mVertex[1] < inRHS.mVertex[1]); });
 
 
 	// Only add edges if one of the vertices is movable
 	// Only add edges if one of the vertices is movable
-	auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inStiffnessParam) {
+	auto add_edge = [this, inCompliance](uint32 inVtx1, uint32 inVtx2) {
 		if (mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
 		if (mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
 		{
 		{
 			Edge temp_edge;
 			Edge temp_edge;
 			temp_edge.mVertex[0] = inVtx1;
 			temp_edge.mVertex[0] = inVtx1;
 			temp_edge.mVertex[1] = inVtx2;
 			temp_edge.mVertex[1] = inVtx2;
-			temp_edge.mStiffness = inStiffnessParam;
+			temp_edge.mCompliance = inCompliance;
 			temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
 			temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
 			JPH_ASSERT(temp_edge.mRestLength > 0.0f);
 			JPH_ASSERT(temp_edge.mRestLength > 0.0f);
 			mEdgeConstraints.push_back(temp_edge);
 			mEdgeConstraints.push_back(temp_edge);
@@ -134,7 +134,7 @@ void SoftBodySharedSettings::CreateEdges(float inStiffness, float inAngleToleran
 		const EdgeHelper &e0 = edges[i];
 		const EdgeHelper &e0 = edges[i];
 
 
 		// Create a regular edge constraint
 		// Create a regular edge constraint
-		add_edge(e0.mVertex[0], e0.mVertex[1], inStiffness);
+		add_edge(e0.mVertex[0], e0.mVertex[1]);
 
 
 		// Test if there are any shared edges
 		// Test if there are any shared edges
 		for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
 		for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
@@ -159,7 +159,7 @@ void SoftBodySharedSettings::CreateEdges(float inStiffness, float inAngleToleran
 					if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
 					if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
 					{
 					{
 						// Shear constraint
 						// Shear constraint
-						add_edge(min(v0, v1), max(v0, v1), inStiffness);
+						add_edge(min(v0, v1), max(v0, v1));
 					}
 					}
 				}
 				}
 			}
 			}

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

@@ -18,9 +18,9 @@ public:
 	JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings)
 	JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings)
 
 
 	/// Automatically create all edges based on the faces
 	/// Automatically create all edges based on the faces
-	/// @param inStiffness The stiffness of the edges
+	/// @param inCompliance The compliance of the edges
 	/// @param inAngleTolerance Shear edges are created when two connected triangles form a quad (are roughly in the same plane and form a square with roughly 90 degree angles). This defines the tolerance (in radians).
 	/// @param inAngleTolerance Shear edges are created when two connected triangles form a quad (are roughly in the same plane and form a square with roughly 90 degree angles). This defines the tolerance (in radians).
-	void				CreateEdges(float inStiffness = 0.9f, float inAngleTolerance = DegreesToRadians(8.0f));
+	void				CreateEdges(float inCompliance, float inAngleTolerance = DegreesToRadians(8.0f));
 
 
 	/// Calculate the initial lengths of all springs of the edges of this soft body
 	/// Calculate the initial lengths of all springs of the edges of this soft body
 	void				CalculateEdgeLengths();
 	void				CalculateEdgeLengths();
@@ -106,11 +106,11 @@ public:
 
 
 		/// Constructor
 		/// Constructor
 						Edge() = default;
 						Edge() = default;
-						Edge(uint32 inVertex1, uint32 inVertex2, float inStiffness = 0.9f) : mVertex { inVertex1, inVertex2 }, mStiffness(inStiffness) { }
+						Edge(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { }
 
 
 		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			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.
+		float			mCompliance = 0.0f;							///< Inverse of the stiffness of the spring
 	};
 	};
 
 
 	/// Volume constraint, keeps the volume of a tetrahedron constant
 	/// Volume constraint, keeps the volume of a tetrahedron constant
@@ -120,11 +120,11 @@ public:
 
 
 		/// Constructor
 		/// Constructor
 						Volume() = default;
 						Volume() = default;
-						Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inStiffness = 0.9f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mStiffness(inStiffness) { }
+						Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { }
 
 
-		uint32			mVertex[4];									///< Indices of the vertices that form the tetrahedron
+		uint32			mVertex[4];									///< Indices of the vertices that form the tetrhedron
 		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			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.
+		float			mCompliance = 0.0f;							///< Inverse of the stiffness of the constraint
 	};
 	};
 
 
 	/// 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.mStiffness = 0.1f; // Soften the edges a bit so that the effect of the LRA constraints is more visible
+		e.mCompliance = 1.0e-3f; // 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.mStiffness = 0.1f;
+		e.mCompliance = 1.0e-3f;
 
 
 	// 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);

+ 2 - 2
Samples/Utils/SoftBodyCreator.cpp

@@ -46,7 +46,7 @@ Ref<SoftBodySharedSettings> CreateCloth(uint inGridSizeX, uint inGridSizeZ, floa
 		}
 		}
 
 
 	// Create edges
 	// Create edges
-	settings->CreateEdges();
+	settings->CreateEdges(0.00001f);
 
 
 	// Optimize the settings
 	// Optimize the settings
 	settings->Optimize();
 	settings->Optimize();
@@ -267,7 +267,7 @@ Ref<SoftBodySharedSettings> CreateSphere(float inRadius, uint inNumTheta, uint i
 	}
 	}
 
 
 	// Create edges
 	// Create edges
-	settings->CreateEdges(0.1f);
+	settings->CreateEdges(0.0001f);
 
 
 	// Optimize the settings
 	// Optimize the settings
 	settings->Optimize();
 	settings->Optimize();