private static float transformToAxis(Box box, Vector3 axis) { return box.HalfSizes.X * TrickyMath.Abs(Vector3.Dot(axis, box.getAxis(0))) + box.HalfSizes.Y * TrickyMath.Abs(Vector3.Dot(axis, box.getAxis(1))) + box.HalfSizes.Z * TrickyMath.Abs(Vector3.Dot(axis, box.getAxis(2))); }
//TODO - write unit tests!!!!!! public static int boxAndPoint(Box box, Vector3 pointWorld, CollisionData data) { Vector3 pointLocal = Vector3.Transform(pointWorld, Matrix.Invert(box.Body.World * box.OffsetMatrix)); Vector3 halfSizes = box.HalfSizes; Vector3 absPoint = new Vector3( TrickyMath.Abs(pointLocal.X), TrickyMath.Abs(pointLocal.Y), TrickyMath.Abs(pointLocal.Z) ); Vector3 check = halfSizes - absPoint; if (check.X < 0 || check.Y < 0 || check.Z < 0) { return 0; } Vector3 depth = new Vector3( TrickyMath.Abs(absPoint.X - halfSizes.X), TrickyMath.Abs(absPoint.Y - halfSizes.Y), TrickyMath.Abs(absPoint.Z - halfSizes.Z) ); depth = new Vector3( depth.X != 0f ? depth.X : float.MaxValue, depth.Y != 0f ? depth.Y : float.MaxValue, depth.Z != 0f ? depth.Z : float.MaxValue ); if (depth.X <= depth.Y && depth.X <= depth.Z) { Contact contact = new Contact(); contact.DiscoveryHint = ContactDiscoveryHint.BoxOnBox_Point_Face; Vector3 boxAxis = box.getAxis(0); contact.Normal = boxAxis * (pointLocal.X > 0 ? -1f : 1f); contact.Point = pointWorld; contact.Penetration = TrickyMath.Abs(absPoint.X - halfSizes.X); contact.Bodies[0] = box.Body; contact.Bodies[1] = null; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); return 1; } else if (depth.Y <= depth.X && depth.Y <= depth.Z) { Contact contact = new Contact(); contact.DiscoveryHint = ContactDiscoveryHint.BoxOnBox_Point_Face; Vector3 boxAxis = box.getAxis(1); contact.Normal = boxAxis * (pointLocal.Y > 0 ? -1f : 1f); contact.Point = pointWorld; contact.Penetration = TrickyMath.Abs(absPoint.Y - halfSizes.Y); contact.Bodies[0] = box.Body; contact.Bodies[1] = null; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); return 1; } else if (depth.Z <= depth.X && depth.Z <= depth.Y) { Contact contact = new Contact(); contact.DiscoveryHint = ContactDiscoveryHint.BoxOnBox_Point_Face; Vector3 boxAxis = box.getAxis(2); contact.Normal = boxAxis * (pointLocal.Z > 0 ? -1f : 1f); contact.Point = pointWorld; contact.Penetration = TrickyMath.Abs(absPoint.Z - halfSizes.Z); contact.Bodies[0] = box.Body; contact.Bodies[1] = null; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); return 1; } else { return 0; } }
private static void fillPointFaceBoxBox(Box one, Box two, Vector3 toCenter, CollisionData data, int best, float penetration) { Contact contact = new Contact(); Vector3 normal = one.getAxis(best); if (Vector3.Dot(normal, toCenter) > 0) { normal *= -1f; } Vector3 vertex = two.HalfSizes; float oneDot = Vector3.Dot(two.getAxis(0), normal); if (oneDot < 0) { vertex *= new Vector3(-1f, 0f, 0f); } float twoDot = Vector3.Dot(two.getAxis(1), normal); if (twoDot < 0) { vertex *= new Vector3(0f, -1f, 0f); } float threeDot = Vector3.Dot(two.getAxis(2), normal); if (threeDot < 0) { vertex *= new Vector3(0f, 0f, -1f); } contact.DiscoveryHint = ContactDiscoveryHint.BoxOnBox_Point_Face; contact.Normal = normal; contact.Penetration = penetration; contact.Point = Vector3.Transform(vertex, two.Transform); contact.Bodies[0] = one.Body; contact.Bodies[1] = two.Body; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); }
public static int boxAndBox(Box one, Box two, CollisionData data) { Vector3 toCenter = two.Body.Position - one.Body.Position; float pen = float.MaxValue; int best = int.MaxValue; if (!tryAxis(one, two, one.getAxis(0), toCenter, 0, ref pen, ref best)) return 0; if (!tryAxis(one, two, one.getAxis(1), toCenter, 1, ref pen, ref best)) return 0; if (!tryAxis(one, two, one.getAxis(2), toCenter, 2, ref pen, ref best)) return 0; if (!tryAxis(one, two, one.getAxis(0), toCenter, 3, ref pen, ref best)) return 0; if (!tryAxis(one, two, one.getAxis(1), toCenter, 4, ref pen, ref best)) return 0; if (!tryAxis(one, two, one.getAxis(2), toCenter, 5, ref pen, ref best)) return 0; int bestSingleAxis = best; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(0), two.getAxis(0)), toCenter, 6, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(0), two.getAxis(1)), toCenter, 7, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(0), two.getAxis(2)), toCenter, 8, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(1), two.getAxis(0)), toCenter, 9, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(1), two.getAxis(1)), toCenter, 10, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(1), two.getAxis(2)), toCenter, 11, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(2), two.getAxis(0)), toCenter, 12, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(2), two.getAxis(1)), toCenter, 13, ref pen, ref best)) return 0; if (!tryAxis(one, two, TrickyMath.VectorProduct(one.getAxis(2), two.getAxis(2)), toCenter, 14, ref pen, ref best)) return 0; if (best == int.MaxValue) { throw new Exception("Found no good axis"); } if (best < 3) { fillPointFaceBoxBox(one, two, toCenter, data, best, pen); return 1; } else if (best < 6) { // We've got a vertex of box one on a face of box two. // We use the same algorithm as above, but swap around // one and two (and therefore also the vector between their // centres). fillPointFaceBoxBox(two, one, toCenter * -1.0f, data, best - 3, pen); return 1; } else { best -= 6; //move this back to talk about a cardinal axis int oneAxisIndex = best / 3; int twoAxisIndex = best % 3; Vector3 oneAxis = one.getAxis(oneAxisIndex); Vector3 twoAxis = two.getAxis(twoAxisIndex); Vector3 axis = TrickyMath.VectorProduct(oneAxis, twoAxis); axis.Normalize(); if (Vector3.Dot(axis, toCenter) > 0) { axis *= -1f; } float[] ptOnOneEdge = TrickyMath.Vector3ToFloatArray(one.HalfSizes); float[] ptOnTwoEdge = TrickyMath.Vector3ToFloatArray(two.HalfSizes); for (int i = 0; i < 3; i++) { if (i == oneAxisIndex) { ptOnOneEdge[i] = 0f; } else if (Vector3.Dot(one.getAxis(i), axis) > 0) { ptOnOneEdge[i] *= -1f; } if (i == twoAxisIndex) { ptOnTwoEdge[i] = 0; } else if (Vector3.Dot(two.getAxis(i), axis) < 0) { ptOnTwoEdge[i] *= -1f; } } Vector3 vertexOnOneEdge = Vector3.Transform( TrickyMath.FloatArrayToVector3(ptOnOneEdge), one.Transform ); Vector3 vertexOnTwoEdge = Vector3.Transform( TrickyMath.FloatArrayToVector3(ptOnTwoEdge), two.Transform ); float[] oneHalfSizes = TrickyMath.Vector3ToFloatArray(one.HalfSizes); float[] twoHalfSizes = TrickyMath.Vector3ToFloatArray(two.HalfSizes); Vector3 vertex = contactPoint( vertexOnOneEdge, oneAxis, oneHalfSizes[oneAxisIndex], vertexOnTwoEdge, twoAxis, twoHalfSizes[twoAxisIndex], bestSingleAxis > 2 ); Contact contact = new Contact(); contact.DiscoveryHint = ContactDiscoveryHint.BoxOnBox_Edge_Edge; contact.Penetration = pen; contact.Normal = axis; contact.Point = vertex; contact.Bodies[0] = one.Body; contact.Bodies[1] = two.Body; contact.Restitution = data.restitution; contact.Friction = data.friction; data.contacts.Add(contact); return 1; } return 0; }