public void Contact_BoxSpinningOnYAxisFallsOnToXZPlane() { // X-Z plane NoBody planeBody = new NoBody(Vector3.Zero); planeBody.InverseMass = 0f; //infinite mass Forever.Physics.Collide.Plane plane = new Forever.Physics.Collide.Plane(planeBody, Vector3.Zero, Vector3.Up); planeBody.calculateDerivedData(); // Spinning box colliding with the plane NoBody boxBody = new NoBody(Vector3.Up * 0.95f); boxBody.Mass = 1f; boxBody.Rotation = new Vector3(0f, 0.01f, 0f); boxBody.Velocity = new Vector3(0f, -1f, 0f); boxBody.calculateDerivedData(); Box box = new Box(boxBody, Matrix.Identity, new Vector3(1f, 1f, 1f)); Contact contact = new Contact(); contact.Bodies[0] = planeBody; contact.Bodies[1] = boxBody; contact.Point = Vector3.Zero; contact.Penetration = 0.05f; contact.Restitution = 1f; contact.Friction = 0f; contact.Normal = Vector3.Up; contact.ReCalc(1f); Assert.AreEqual(Vector3.Zero, contact.CalcLocalVelocity(0, 1f)); Assert.AreEqual(Vector3.Left, contact.CalcLocalVelocity(1, 1f)); Assert.AreEqual(Vector3.Zero, contact.RelativeContactPositions[0]); Assert.AreEqual(Vector3.Down * 0.95f, contact.RelativeContactPositions[1]); Assert.AreEqual(Vector3.Right, contact.ContactVelocity); // Position Change Vector3[] linearChange = new Vector3[2]; Vector3[] angularChange = new Vector3[2]; contact.ApplyPositionChange( ref linearChange, ref angularChange, contact.Penetration); Assert.AreEqual(Vector3.Zero, angularChange[0], "Zero angular change for object 0"); Assert.AreEqual(Vector3.Zero, angularChange[1], "Zero angular change for object 1"); Assert.AreEqual(new Vector3(0.0f, 0f, 0f), linearChange[0], "Body 0 is not pushed at all because it has infinite mass"); Assert.AreEqual(new Vector3(0f, -0.05f, 0f), linearChange[1], "Body 1 is pushed up (forward in contact direction) by half penetration"); //Velocity Change Vector3[] velocityChange = new Vector3[2]; Vector3[] rotationChange = new Vector3[2]; contact.ApplyVelocityChange(ref velocityChange, ref rotationChange); Assert.AreEqual( Vector3.Zero, rotationChange[0], "Zero rotation change for object 0"); Assert.AreEqual( Vector3.Zero, rotationChange[1], "Zero rotation change for object 1"); Assert.AreEqual( Vector3.Zero, velocityChange[0], "Zero velocity applied to body 0"); Assert.AreEqual( Vector3.Up * 2f, velocityChange[1], "Counter velocity applied to body 1"); }
public static int sphereAndHalfSpace(Sphere sphere, Plane plane, CollisionData data) { Vector3 pos = sphere.Body.Position; //d = p * L - l float ballDist = Vector3.Dot(plane.Normal, pos) - (sphere.Radius - plane.Offset); if (ballDist > 0) return 0; Contact contact = new Contact(); contact.Normal = plane.Normal; contact.Penetration = -ballDist; contact.Point = pos - (plane.Normal * (ballDist + sphere.Radius)); contact.Bodies[0] = sphere.Body; contact.Bodies[1] = null; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); return 1; }
public void IntersectionTests_BoxVsPlane() { IRigidBody planeBody = new NoBody(); Box box; Forever.Physics.Collide.Plane plane; CollisionData data; Vector3 planeNormal = Vector3.Up; // Plane is x-z plane for all tests //////// unit box is sitting at {0,1,0} (surface touching) box = new Box(new NoBody( Matrix.CreateTranslation(new Vector3(0f, 1f, 0f))), Matrix.Identity, new Vector3(1f, 1f, 1f)); plane = new Forever.Physics.Collide.Plane(planeBody, Vector3.Zero, planeNormal); data = new CollisionData(); int contactsFound = IntersectionTests.boxAndPlane(box, plane, data); Assert.AreEqual(4, contactsFound); Contact contact1 = data.contacts[0]; Contact contact2 = data.contacts[1]; Contact contact3 = data.contacts[2]; Contact contact4 = data.contacts[3]; Assert.AreEqual(new Vector3(-1f, 0f, -1f), contact1.Point); Assert.AreEqual(planeNormal, contact1.Normal); Assert.AreEqual(0, contact1.Penetration); Assert.AreEqual(new Vector3(-1f, 0f, 1f), contact2.Point); Assert.AreEqual(planeNormal, contact2.Normal); Assert.AreEqual(0, contact2.Penetration); Assert.AreEqual(new Vector3(1f, 0f, -1f), contact3.Point); Assert.AreEqual(planeNormal, contact3.Normal); Assert.AreEqual(0, contact3.Penetration); Assert.AreEqual(new Vector3(1f, 0f, 1f), contact4.Point); Assert.AreEqual(planeNormal, contact4.Normal); Assert.AreEqual(0, contact4.Penetration); //////// unit box is centered at origin box = new Box(new NoBody(Matrix.CreateTranslation(new Vector3(0f, 0f, 0f))), Matrix.Identity, new Vector3(1f, 1f, 1f)); plane = new Forever.Physics.Collide.Plane(planeBody, Vector3.Zero, planeNormal); data = new CollisionData(); contactsFound = IntersectionTests.boxAndPlane(box, plane, data); Assert.AreEqual(4, contactsFound); contact1 = data.contacts[0]; contact2 = data.contacts[1]; contact3 = data.contacts[2]; contact4 = data.contacts[3]; Assert.AreEqual(new Vector3(-1f, 0f, -1f), contact1.Point); Assert.AreEqual(planeNormal, contact1.Normal); Assert.AreEqual(1f, contact1.Penetration); Assert.AreEqual(new Vector3(-1f, 0f, 1f), contact2.Point); Assert.AreEqual(planeNormal, contact2.Normal); Assert.AreEqual(1f, contact2.Penetration); Assert.AreEqual(new Vector3(1f, 0f, -1f), contact3.Point); Assert.AreEqual(planeNormal, contact3.Normal); Assert.AreEqual(1f, contact3.Penetration); Assert.AreEqual(new Vector3(1f, 0f, 1f), contact4.Point); Assert.AreEqual(planeNormal, contact4.Normal); Assert.AreEqual(1f, contact4.Penetration); //// unit box centered at y=1 and rotated 45 degrees on each axis float angle = (float)Math.PI * 0.25f; Matrix boxMatrix = Matrix.CreateFromYawPitchRoll(angle, angle, angle) * Matrix.CreateTranslation(new Vector3(0f, 1f, 0f)); box = new Box(new NoBody(boxMatrix), Matrix.Identity, new Vector3(1f, 1f, 1f)); plane = new Forever.Physics.Collide.Plane(planeBody, Vector3.Zero, planeNormal); data = new CollisionData(); contactsFound = IntersectionTests.boxAndPlane(box, plane, data); Assert.AreEqual(1, contactsFound); contact1 = data.contacts[0]; Assert.True( TrickyMath.AlmostEquals(0.7071067f, contact1.Penetration) ); Assert.True( TrickyMath.CloseEnoughToBeSame( new Vector3(-0.2071069f, 0f, -0.2071068f), contact1.Point)); ////unit box centered at y=sqrt(2) and rotated 45 degrees on each axis boxMatrix = Matrix.CreateFromYawPitchRoll(angle, angle, angle) * Matrix.CreateTranslation(new Vector3(0f, TrickyMath.Sqrt(2), 0f)); box = new Box(new NoBody(boxMatrix), Matrix.Identity, new Vector3(1f, 1f, 1f)); plane = new Forever.Physics.Collide.Plane(planeBody, Vector3.Zero, planeNormal); data = new CollisionData(); contactsFound = IntersectionTests.boxAndPlane(box, plane, data); Assert.AreEqual(1, contactsFound); contact1 = data.contacts[0]; Assert.True( TrickyMath.AlmostEquals(0.292893142f,contact1.Penetration) ); Assert.True( TrickyMath.CloseEnoughToBeSame( new Vector3(-0.2071069f, 0f, -0.2071068f), contact1.Point ) ); Assert.AreEqual(Vector3.UnitY, contact1.Normal); }
/// <summary> /// danger! This works for ContactDemo, but there is one hang up. The call to Math.Abs() should not /// be needed. It isn't in the orange book text. However, we only get negative numbers from that Dot. /// </summary> /// <param name="box"></param> /// <param name="plane"></param> /// <param name="data"></param> /// <returns></returns> public static int boxAndPlane(Box box, Plane plane, CollisionData data) { if (data.contactsLeft <= 0) return 0; int contacts_found = 0; // p dot L < l Vector3[] verts = box.WorldVerts(); foreach (Vector3 vert in verts) { //float vertexDist = (float)Math.Abs( (double)Vector3.Dot(plane.Normal, vert)); float vertexDist = Vector3.Dot(-plane.Normal, vert); if (vertexDist >= plane.Offset + data.tolerance) { // the contact point is halfway between the vertex and the plane // we multiply the direction by half the spearation distance // and add the vertex location Contact contact = new Contact(); contact.DiscoveryHint = ContactDiscoveryHint.BoxOnPlane_Corner; contact.Bodies[0] = box.Body; contact.Bodies[1] = null; contact.Point = plane.ClosestPoint(vert); contact.Normal = plane.Normal; contact.Penetration = vertexDist - plane.Offset;//TrickyMathHelper.Abs(plane.Offset - vertexDist); contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); contacts_found++; } } return contacts_found; }
public void IntersectionTests_SphereVsPlane() { Matrix sphereMatrix; float sphereRadius = 1f; Forever.Physics.Collide.Plane plane; Forever.Physics.Collide.Sphere sphere; CollisionData data; int contactsFound; Contact contact1; // plane is always the x-z plane for this test method Vector3 planeNormal = Vector3.Up; plane = new Forever.Physics.Collide.Plane(new NoBody(), Vector3.Zero, planeNormal); // unit sphere centered at y=1 (one point of surface touching) sphereMatrix = Matrix.CreateTranslation(Vector3.UnitY); sphere = new Sphere( new NoBody(sphereMatrix), Matrix.Identity, sphereRadius); data = new CollisionData(); contactsFound = IntersectionTests.sphereAndHalfSpace(sphere, plane, data); Assert.AreEqual(1, contactsFound); contact1 = data.contacts[0]; Assert.AreEqual(Vector3.Zero, contact1.Point); Assert.AreEqual(0f, contact1.Penetration); Assert.AreEqual(planeNormal, contact1.Normal); // unit sphere centered at y=0.9 (.1 penetration) sphereMatrix = Matrix.CreateTranslation(Vector3.UnitY * 0.9f); sphere = new Sphere(new NoBody(sphereMatrix), Matrix.Identity, sphereRadius); data = new CollisionData(); contactsFound = IntersectionTests.sphereAndHalfSpace(sphere, plane, data); Assert.AreEqual(1, contactsFound); contact1 = data.contacts[0]; Assert.AreEqual(Vector3.Zero, contact1.Point); Assert.True( TrickyMath.AlmostEquals(0.1f, contact1.Penetration) ); Assert.AreEqual(planeNormal, contact1.Normal); }