Browse Source

Fixed character not properly sliding on box mesh (#380)

The surface normal can oppose movement, so it's better to use contact normals for creating the vertical wall that prevents characters from moving up a steep slope.

Should fix #363
Jorrit Rouwe 2 years ago
parent
commit
7e4d3bdbf4

+ 6 - 5
Jolt/Physics/Character/CharacterVirtual.cpp

@@ -341,19 +341,20 @@ void CharacterVirtual::DetermineConstraints(TempContactList &inContacts, Constra
 		// Next check if the angle is too steep and if it is add an additional constraint that holds the character back
 		if (IsSlopeTooSteep(c.mSurfaceNormal))
 		{
-			// Only take planes that point up
-			float dot = c.mSurfaceNormal.Dot(mUp);
-			if (dot > 0.0f)
+			// Only take planes that point up. 
+			// Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement.
+			float dot = c.mContactNormal.Dot(mUp);
+			if (dot > 1.0e-3f) // Add a little slack, if the normal is perfectly horizontal we already have our vertical plane.
 			{
 				// Make horizontal normal
-				Vec3 normal = (c.mSurfaceNormal - dot * mUp).Normalized();
+				Vec3 normal = (c.mContactNormal - dot * mUp).Normalized();
 
 				// Create a secondary constraint that blocks horizontal movement
 				outConstraints.emplace_back();
 				Constraint &vertical_constraint = outConstraints.back();
 				vertical_constraint.mContact = &c;
 				vertical_constraint.mLinearVelocity = contact_velocity.Dot(normal) * normal; // Project the contact velocity on the new normal so that both planes push at an equal rate
-				vertical_constraint.mPlane = Plane(normal, c.mDistance / normal.Dot(c.mSurfaceNormal)); // Calculate the distance we have to travel horizontally to hit the contact plane
+				vertical_constraint.mPlane = Plane(normal, c.mDistance / normal.Dot(c.mContactNormal)); // Calculate the distance we have to travel horizontally to hit the contact plane
 			}
 		}
 	}

+ 35 - 0
Samples/Tests/Character/CharacterBaseTest.cpp

@@ -69,6 +69,7 @@ static const float cMeshWallStepStart = 0.5f;
 static const float cMeshWallStepEnd = 4.0f;
 static const int cMeshWallSegments = 25;
 static const RVec3 cHalfCylinderPosition(5.0f, 0, 8.0f);
+static const RVec3 cMeshBoxPosition(30.0f, 1.5f, 5.0f);
 
 void CharacterBaseTest::Initialize()
 {
@@ -406,6 +407,40 @@ void CharacterBaseTest::Initialize()
 			BodyCreationSettings mesh_cylinder(&mesh, cHalfCylinderPosition, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
 			mBodyInterface->CreateAndAddBody(mesh_cylinder, EActivation::DontActivate);
 		}
+
+		// Create a box made out of polygons (character should not get stuck behind back facing side)
+		{
+			VertexList vertices = {
+				Float3(-1,  1, -1),
+				Float3( 1,  1, -1),
+				Float3( 1,  1,  1),
+				Float3(-1,  1,  1),
+				Float3(-1, -1, -1),
+				Float3( 1, -1, -1),
+				Float3( 1, -1,  1),
+				Float3(-1, -1,  1)
+			};
+
+			IndexedTriangleList triangles = {
+				IndexedTriangle(0, 3, 2),
+				IndexedTriangle(0, 2, 1),
+				IndexedTriangle(4, 5, 6),
+				IndexedTriangle(4, 6, 7),
+				IndexedTriangle(0, 4, 3),
+				IndexedTriangle(3, 4, 7),
+				IndexedTriangle(2, 6, 5),
+				IndexedTriangle(2, 5, 1),
+				IndexedTriangle(3, 7, 6),
+				IndexedTriangle(3, 6, 2),
+				IndexedTriangle(0, 1, 5),
+				IndexedTriangle(0, 5, 4)
+			};
+
+			MeshShapeSettings mesh(vertices, triangles);
+			mesh.SetEmbedded();
+			BodyCreationSettings box(&mesh, cMeshBoxPosition, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
+			mBodyInterface->CreateAndAddBody(box, EActivation::DontActivate);
+		}
 	}
 	else
 	{