Parcourir la source

Added function to automatically create edges for soft body (#980)

Jorrit Rouwe il y a 1 an
Parent
commit
1b82aa8ae1

+ 91 - 0
Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp

@@ -83,6 +83,97 @@ JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings)
 	JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
 }
 
+void SoftBodySharedSettings::CreateEdges(float inStiffness, float inAngleTolerance)
+{
+	struct EdgeHelper
+	{
+		uint32	mVertex[2];
+		uint32	mEdgeIdx;
+	};
+
+	// Create list of all edges
+	Array<EdgeHelper> edges;
+	edges.reserve(mFaces.size() * 3);
+	for (const Face &f : mFaces)
+		for (int i = 0; i < 3; ++i)
+		{
+			uint32 v0 = f.mVertex[i];
+			uint32 v1 = f.mVertex[(i + 1) % 3];
+
+			EdgeHelper e;
+			e.mVertex[0] = min(v0, v1);
+			e.mVertex[1] = max(v0, v1);
+			e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i;
+			edges.push_back(e);
+		}
+
+	// Sort the edges
+	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
+	auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inStiffnessParam) {
+		if (mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
+		{
+			Edge temp_edge;
+			temp_edge.mVertex[0] = inVtx1;
+			temp_edge.mVertex[1] = inVtx2;
+			temp_edge.mStiffness = inStiffnessParam;
+			temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
+			JPH_ASSERT(temp_edge.mRestLength > 0.0f);
+			mEdgeConstraints.push_back(temp_edge);
+		}
+	};
+
+	// Create the constraints
+	float sq_sin_tolerance = Square(Sin(inAngleTolerance));
+	float sq_cos_tolerance = Square(Cos(inAngleTolerance));
+	mEdgeConstraints.clear();
+	mEdgeConstraints.reserve(edges.size());
+	for (Array<EdgeHelper>::size_type i = 0; i < edges.size(); ++i)
+	{
+		const EdgeHelper &e0 = edges[i];
+
+		// Create a regular edge constraint
+		add_edge(e0.mVertex[0], e0.mVertex[1], inStiffness);
+
+		// Test if there are any shared edges
+		for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
+		{
+			const EdgeHelper &e1 = edges[j];
+			if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1])
+			{
+				// Faces should be roughly in a plane
+				const Face &f0 = mFaces[e0.mEdgeIdx / 3];
+				const Face &f1 = mFaces[e1.mEdgeIdx / 3];
+				Vec3 n0 = (Vec3(mVertices[f0.mVertex[2]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f0.mVertex[1]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition));
+				Vec3 n1 = (Vec3(mVertices[f1.mVertex[2]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f1.mVertex[1]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition));
+				if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq())
+				{
+					// Get opposing vertices
+					uint32 v0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3];
+					uint32 v1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3];
+
+					// Faces should approximately form a quad
+					Vec3 e0_dir = Vec3(mVertices[v0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
+					Vec3 e1_dir = Vec3(mVertices[v1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
+					if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
+					{
+						// Shear constraint
+						add_edge(min(v0, v1), max(v0, v1), inStiffness);
+					}
+				}
+			}
+			else
+			{
+				// Start iterating from the first non-shared edge
+				i = j - 1;
+				break;
+			}
+		}
+	}
+	mEdgeConstraints.shrink_to_fit();
+}
+
 void SoftBodySharedSettings::CalculateEdgeLengths()
 {
 	for (Edge &e : mEdgeConstraints)

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

@@ -17,6 +17,11 @@ class JPH_EXPORT SoftBodySharedSettings : public RefTarget<SoftBodySharedSetting
 public:
 	JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings)
 
+	/// Automatically create all edges based on the faces
+	/// @param inStiffness The stiffness 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).
+	void				CreateEdges(float inStiffness = 0.9f, float inAngleTolerance = DegreesToRadians(8.0f));
+
 	/// Calculate the initial lengths of all springs of the edges of this soft body
 	void				CalculateEdgeLengths();
 

+ 6 - 58
Samples/Utils/SoftBodyCreator.cpp

@@ -30,40 +30,6 @@ Ref<SoftBodySharedSettings> CreateCloth(uint inGridSizeX, uint inGridSizeZ, floa
 		return inX + inY * inGridSizeX;
 	};
 
-	// Only add edges if one of the vertices is moveable
-	auto add_edge = [settings](const SoftBodySharedSettings::Edge &inEdge) {
-		if (settings->mVertices[inEdge.mVertex[0]].mInvMass > 0.0f || settings->mVertices[inEdge.mVertex[1]].mInvMass > 0.0f)
-			settings->mEdgeConstraints.push_back(inEdge);
-	};
-
-	// Create edges
-	for (uint z = 0; z < inGridSizeZ; ++z)
-		for (uint x = 0; x < inGridSizeX; ++x)
-		{
-			SoftBodySharedSettings::Edge e;
-			e.mVertex[0] = vertex_index(x, z);
-			if (x < inGridSizeX - 1)
-			{
-				e.mVertex[1] = vertex_index(x + 1, z);
-				add_edge(e);
-			}
-			if (z < inGridSizeZ - 1)
-			{
-				e.mVertex[1] = vertex_index(x, z + 1);
-				add_edge(e);
-			}
-			if (x < inGridSizeX - 1 && z < inGridSizeZ - 1)
-			{
-				e.mVertex[1] = vertex_index(x + 1, z + 1);
-				add_edge(e);
-
-				e.mVertex[0] = vertex_index(x + 1, z);
-				e.mVertex[1] = vertex_index(x, z + 1);
-				add_edge(e);
-			}
-		}
-	settings->CalculateEdgeLengths();
-
 	// Create faces
 	for (uint z = 0; z < inGridSizeZ - 1; ++z)
 		for (uint x = 0; x < inGridSizeX - 1; ++x)
@@ -79,6 +45,9 @@ Ref<SoftBodySharedSettings> CreateCloth(uint inGridSizeX, uint inGridSizeZ, floa
 			settings->AddFace(f);
 		}
 
+	// Create edges
+	settings->CreateEdges();
+
 	// Optimize the settings
 	settings->Optimize();
 
@@ -272,30 +241,6 @@ Ref<SoftBodySharedSettings> CreateSphere(float inRadius, uint inNumTheta, uint i
 			return 2 + (inTheta - 1) * inNumPhi + inPhi % inNumPhi;
 	};
 
-	// Create edge constraints
-	for (uint phi = 0; phi < inNumPhi; ++phi)
-	{
-		for (uint theta = 0; theta < inNumTheta - 1; ++theta)
-		{
-			SoftBodySharedSettings::Edge e;
-			e.mVertex[0] = vertex_index(theta, phi);
-
-			e.mVertex[1] = vertex_index(theta + 1, phi);
-			settings->mEdgeConstraints.push_back(e);
-
-			e.mVertex[1] = vertex_index(theta + 1, phi + 1);
-			settings->mEdgeConstraints.push_back(e);
-
-			if (theta > 0)
-			{
-				e.mVertex[1] =  vertex_index(theta, phi + 1);
-				settings->mEdgeConstraints.push_back(e);
-			}
-		}
-	}
-
-	settings->CalculateEdgeLengths();
-
 	// Create faces
 	SoftBodySharedSettings::Face f;
 	for (uint phi = 0; phi < inNumPhi; ++phi)
@@ -321,6 +266,9 @@ Ref<SoftBodySharedSettings> CreateSphere(float inRadius, uint inNumTheta, uint i
 		settings->AddFace(f);
 	}
 
+	// Create edges
+	settings->CreateEdges(0.1f);
+
 	// Optimize the settings
 	settings->Optimize();