private static bool CollDetectBoxStaticMeshOverlap(Box oldBox, Box newBox, TriangleMesh mesh, ref CollDetectInfo info, float collTolerance, CollisionFunctor collisionFunctor) { float boxRadius = newBox.GetBoundingRadiusAroundCentre(); #region REFERENCE: Vector3 boxCentre = newBox.GetCentre(); Vector3 boxCentre; newBox.GetCentre(out boxCentre); // Deano need to trasnform the box center into mesh space Matrix invTransformMatrix = mesh.InverseTransformMatrix; Vector3.TransformCoordinate(ref boxCentre, ref invTransformMatrix, out boxCentre); #endregion BoundingBox bb = BoundingBoxHelper.InitialBox; BoundingBoxHelper.AddBox(newBox, ref bb); unsafe { bool collision = false; #if USE_STACKALLOC int* potentialTriangles = stackalloc int[MaxLocalStackTris]; { #else int[] potTriArray = IntStackAlloc(); fixed( int* potentialTriangles = potTriArray) { #endif // aabox is done in mesh space and handles the mesh transform correctly int numTriangles = mesh.GetTrianglesIntersectingtAABox(potentialTriangles, MaxLocalStackTris, ref bb); for (int iTriangle = 0; iTriangle < numTriangles; ++iTriangle) { IndexedTriangle meshTriangle = mesh.GetTriangle(potentialTriangles[iTriangle]); // quick early test is done in mesh space float dist = SlimDX.Plane.DotCoordinate(meshTriangle.Plane, boxCentre); if (dist > boxRadius || dist < 0.0f) continue; if (DoOverlapBoxTriangleTest( oldBox, newBox, ref meshTriangle, mesh, ref info, collTolerance, collisionFunctor)) { collision = true; } } #if USE_STACKALLOC } #else } FreeStackAlloc(potTriArray); #endif return collision; } }
/// <summary> /// GetBoxTriangleIntersectionPoints /// Pushes intersection points onto the back of pts. Returns the /// number of points found. /// Points that are close together (compared to /// combinationDistance) get combined /// </summary> /// <param name="pts"></param> /// <param name="box"></param> /// <param name="triangle"></param> /// <param name="combinationDistance"></param> /// <returns></returns> private static int GetBoxTriangleIntersectionPoints(List<Vector3> pts, Box box, Triangle triangle, float combinationDistance) { // first intersect each edge of the box with the triangle Box.Edge[] edges; box.GetEdges(out edges); Vector3[] boxPts; box.GetCornerPoints(out boxPts); float tS; float tv1, tv2; int iEdge; for (iEdge = 0; iEdge < 12; ++iEdge) { Box.Edge edge = edges[iEdge]; Segment seg = new Segment(boxPts[(int)edge.Ind0], boxPts[(int)edge.Ind1] - boxPts[(int)edge.Ind0]); if (Intersection.SegmentTriangleIntersection(out tS, out tv1, out tv2, seg, triangle)) { AddPoint(pts, seg.GetPoint(tS), combinationDistance * combinationDistance); } } Vector3 pos, n; // now each edge of the triangle with the box for (iEdge = 0; iEdge < 3; ++iEdge) { Vector3 pt0 = triangle.GetPoint(iEdge); Vector3 pt1 = triangle.GetPoint((iEdge + 1) % 3); Segment s1 = new Segment(pt0, pt1 - pt0); Segment s2 = new Segment(pt1, pt0 - pt1); if (box.SegmentIntersect(out tS, out pos, out n, s1)) AddPoint(pts, pos, combinationDistance * combinationDistance); if (box.SegmentIntersect(out tS, out pos, out n, s2)) AddPoint(pts, pos, combinationDistance * combinationDistance); } return pts.Count; }
private void CollDetectSweep(ref CollDetectInfo info, float collTolerance, CollisionFunctor collisionFunctor) { // todo - proper swept test // note - mesh is static and its triangles are in world space TriangleMesh mesh = info.Skin1.GetPrimitiveNewWorld(info.IndexPrim1) as TriangleMesh; Box oldBox = info.Skin0.GetPrimitiveOldWorld(info.IndexPrim0) as Box; Box newBox = info.Skin0.GetPrimitiveNewWorld(info.IndexPrim0) as Box; Vector3 oldCentre; oldBox.GetCentre(out oldCentre); Vector3 newCentre; newBox.GetCentre(out newCentre); Vector3 delta; Vector3.Subtract(ref newCentre, ref oldCentre, out delta); float boxMinLen = 0.5f * System.Math.Min(newBox.SideLengths.X, System.Math.Min(newBox.SideLengths.Y, newBox.SideLengths.Z)); int nPositions = 1 + (int)(delta.Length() / boxMinLen); // limit the max positions... if (nPositions > 50) { System.Diagnostics.Debug.WriteLine("Warning - clamping max positions in swept box test"); nPositions = 50; } if (nPositions == 1) { CollDetectBoxStaticMeshOverlap(oldBox, newBox, mesh,ref info, collTolerance, collisionFunctor); } else { BoundingBox bb = BoundingBoxHelper.InitialBox; BoundingBoxHelper.AddBox(oldBox, ref bb); BoundingBoxHelper.AddBox(newBox, ref bb); unsafe { #if USE_STACKALLOC int* potentialTriangles = stackalloc int[MaxLocalStackTris]; { #else int[] potTriArray = IntStackAlloc(); fixed( int* potentialTriangles = potTriArray) { #endif int numTriangles = mesh.GetTrianglesIntersectingtAABox(potentialTriangles, MaxLocalStackTris, ref bb); if (numTriangles > 0) { for (int i = 0; i <= nPositions; ++i) { float frac = ((float)i) / nPositions; Vector3 centre; Vector3.Multiply(ref delta, frac, out centre); Vector3.Add(ref centre, ref oldCentre, out centre); Matrix orient = Matrix.Add(Matrix.Multiply(oldBox.Orientation, 1.0f - frac), Matrix.Multiply(newBox.Orientation, frac)); Box box = new Box(centre - 0.5f * Vector3.TransformCoordinate(newBox.SideLengths, orient), orient, newBox.SideLengths); // ideally we'd break if we get one collision... but that stops us getting multiple collisions // when we enter a corner (two walls meeting) - can let us pass through CollDetectBoxStaticMeshOverlap(oldBox, box, mesh, ref info, collTolerance, collisionFunctor); } } #if USE_STACKALLOC } #else } FreeStackAlloc(potTriArray); #endif } } }
/// <summary> /// Disjoint Returns true if disjoint. Returns false if intersecting, /// and sets the overlap depth, d scaled by the axis length /// </summary> /// <param name="d"></param> /// <param name="axis"></param> /// <param name="box"></param> /// <param name="triangle"></param> /// <param name="collTolerance"></param> /// <returns></returns> private static bool Disjoint(out float d, Vector3 axis, Box box, Triangle triangle, float collTolerance) { float min0, max0, min1, max1; box.GetSpan(out min0, out max0, axis); triangle.GetSpan(out min1, out max1, axis); if (min0 > (max1 + collTolerance + JiggleMath.Epsilon) || min1 > (max0 + collTolerance + JiggleMath.Epsilon)) { d = 0.0f; return true; } if ((max0 > max1) && (min1 > min0)) { // triangle is inside - choose the min dist to move it out d = System.Math.Min(max0 - min1, max1 - min0); } else if ((max1 > max0) && (min0 > min1)) { // box is inside - choose the min dist to move it out d = System.Math.Min(max1 - min0, max0 - min1); } else { // objects overlap d = (max0 < max1) ? max0 : max1; d -= (min0 > min1) ? min0 : min1; } return false; }
private static bool DoOverlapBoxTriangleTest(Box oldBox, Box newBox, ref IndexedTriangle triangle, TriangleMesh mesh, ref CollDetectInfo info, float collTolerance, CollisionFunctor collisionFunctor) { Matrix dirs0 = newBox.Orientation; #region REFERENCE: Triangle tri = new Triangle(mesh.GetVertex(triangle.GetVertexIndex(0)),mesh.GetVertex(triangle.GetVertexIndex(1)),mesh.GetVertex(triangle.GetVertexIndex(2))); Vector3 triVec0; Vector3 triVec1; Vector3 triVec2; mesh.GetVertex(triangle.GetVertexIndex(0), out triVec0); mesh.GetVertex(triangle.GetVertexIndex(1), out triVec1); mesh.GetVertex(triangle.GetVertexIndex(2), out triVec2); // Deano move tri into world space Matrix transformMatrix = mesh.TransformMatrix; Vector3.TransformCoordinate(ref triVec0, ref transformMatrix, out triVec0); Vector3.TransformCoordinate(ref triVec1, ref transformMatrix, out triVec1); Vector3.TransformCoordinate(ref triVec2, ref transformMatrix, out triVec2); Triangle tri = new Triangle(ref triVec0,ref triVec1,ref triVec2); #endregion #region REFERENCE Vector3 triEdge0 = (tri.GetPoint(1) - tri.GetPoint(0)); Vector3 pt0; Vector3 pt1; tri.GetPoint(0, out pt0); tri.GetPoint(1, out pt1); Vector3 triEdge0; Vector3.Subtract(ref pt1, ref pt0, out triEdge0); #endregion #region REFERENCE Vector3 triEdge1 = (tri.GetPoint(2) - tri.GetPoint(1)); Vector3 pt2; tri.GetPoint(2, out pt2); Vector3 triEdge1; Vector3.Subtract(ref pt2, ref pt1, out triEdge1); #endregion #region REFERENCE Vector3 triEdge2 = (tri.GetPoint(0) - tri.GetPoint(2)); Vector3 triEdge2; Vector3.Subtract(ref pt0, ref pt2, out triEdge2); #endregion triEdge0.Normalize(); triEdge1.Normalize(); triEdge2.Normalize(); Vector3 triNormal = triangle.Plane.Normal; // the 15 potential separating axes const int numAxes = 13; Vector3[] axes = new Vector3[numAxes]; axes[0] = triNormal; MatrixHelper.GetRight(ref dirs0, out axes[1]); MatrixHelper.GetUp(ref dirs0, out axes[2]); MatrixHelper.GetBackward(ref dirs0, out axes[3]); Vector3.Cross(ref axes[1], ref triEdge0, out axes[4]); Vector3.Cross(ref axes[1], ref triEdge1, out axes[5]); Vector3.Cross(ref axes[1], ref triEdge2, out axes[6]); Vector3.Cross(ref axes[2], ref triEdge0, out axes[7]); Vector3.Cross(ref axes[2], ref triEdge1, out axes[8]); Vector3.Cross(ref axes[2], ref triEdge2, out axes[9]); Vector3.Cross(ref axes[3], ref triEdge0, out axes[10]); Vector3.Cross(ref axes[3], ref triEdge1, out axes[11]); Vector3.Cross(ref axes[3], ref triEdge2, out axes[12]); // the overlap depths along each axis float[] overlapDepths = new float[numAxes]; // see if the boxes are separate along any axis, and if not keep a // record of the depths along each axis int i; for (i = 0; i < numAxes; ++i) { overlapDepths[i] = 1.0f; if (Disjoint(out overlapDepths[i], axes[i], newBox, tri, collTolerance)) return false; } // The box overlap, find the separation depth closest to 0. float minDepth = float.MaxValue; int minAxis = -1; for (i = 0; i < numAxes; ++i) { // If we can't normalise the axis, skip it float l2 = axes[i].LengthSquared(); if (l2 < JiggleMath.Epsilon) continue; // Normalise the separation axis and the depth float invl = 1.0f / (float)System.Math.Sqrt(l2); axes[i] *= invl; overlapDepths[i] *= invl; // If this axis is the minimum, select it if (overlapDepths[i] < minDepth) { minDepth = overlapDepths[i]; minAxis = i; } } if (minAxis == -1) return false; // Make sure the axis is facing towards the 0th box. // if not, invert it Vector3 D = newBox.GetCentre() - tri.Centre; Vector3 N = axes[minAxis]; float depth = overlapDepths[minAxis]; if (Vector3.Dot(D, N) < 0.0f) N *= -1; Vector3 boxOldPos = (info.Skin0.Owner != null) ? info.Skin0.Owner.OldPosition : Vector3.Zero; Vector3 boxNewPos = (info.Skin0.Owner != null) ? info.Skin0.Owner.Position : Vector3.Zero; Vector3 meshPos = (info.Skin1.Owner != null) ? info.Skin1.Owner.OldPosition : Vector3.Zero; List<Vector3> pts = new List<Vector3>(); //pts.Clear(); const float combinationDist = 0.05f; GetBoxTriangleIntersectionPoints(pts, newBox, tri, depth + combinationDist); // adjust the depth #region REFERENCE: Vector3 delta = boxNewPos - boxOldPos; Vector3 delta; Vector3.Subtract(ref boxNewPos, ref boxOldPos, out delta); #endregion #region REFERENCE: float oldDepth = depth + Vector3.Dot(delta, N); float oldDepth = Vector3.Dot(delta, N); oldDepth += depth; #endregion unsafe { // report collisions int numPts = pts.Count; #if USE_STACKALLOC SmallCollPointInfo* collPts = stackalloc SmallCollPointInfo[MaxLocalStackSCPI]; #else SmallCollPointInfo[] collPtArray = SCPIStackAlloc(); fixed (SmallCollPointInfo* collPts = collPtArray) #endif { if (numPts > 0) { if (numPts >= MaxLocalStackSCPI) { numPts = MaxLocalStackSCPI - 1; } // adjust positions for (i = 0; i < numPts; ++i) { collPts[i] = new SmallCollPointInfo(pts[i] - boxNewPos, pts[i] - meshPos, oldDepth); } collisionFunctor.CollisionNotify(ref info, ref N, collPts, numPts); #if !USE_STACKALLOC FreeStackAlloc(collPtArray); #endif return true; } else { #if !USE_STACKALLOC FreeStackAlloc(collPtArray); #endif return false; } } } }
private static void GetSupportPoint(out Vector3 p, Box box,Vector3 axis) { #region INLINE: Vector3 orient0 = box.Orientation.Right; Vector3 orient0 = new Vector3( box.transform.Orientation.M11, box.transform.Orientation.M12, box.transform.Orientation.M13); #endregion #region INLINE: Vector3 orient1 = box.Orientation.Up; Vector3 orient1 = new Vector3( box.transform.Orientation.M21, box.transform.Orientation.M22, box.transform.Orientation.M23); #endregion #region INLINE: Vector3 orient2 = box.Orientation.Backward; Vector3 orient2 = new Vector3( box.transform.Orientation.M31, box.transform.Orientation.M32, box.transform.Orientation.M33); #endregion #region INLINE: float ass = Vector3.Dot(axis,orient0); float ass = axis.X * orient0.X + axis.Y * orient0.Y + axis.Z * orient0.Z; #endregion #region INLINE: float au = Vector3.Dot(axis,orient1); float au = axis.X * orient1.X + axis.Y * orient1.Y + axis.Z * orient1.Z; #endregion #region INLINE: float ad = Vector3.Dot(axis,orient2); float ad = axis.X * orient2.X + axis.Y * orient2.Y + axis.Z * orient2.Z; #endregion float threshold = JiggleMath.Epsilon; box.GetCentre(out p); if (ass < -threshold) { #region INLINE: p += orient0 * (0.5 * box.SideLength.X); p.X += orient0.X * (0.5f * box.sideLengths.X); p.Y += orient0.Y * (0.5f * box.sideLengths.X); p.Z += orient0.Z * (0.5f * box.sideLengths.X); #endregion } else if (ass >= threshold) { #region INLINE: p -= orient0 * (0.5 * box.SideLength.X); p.X -= orient0.X * (0.5f * box.sideLengths.X); p.Y -= orient0.Y * (0.5f * box.sideLengths.X); p.Z -= orient0.Z * (0.5f * box.sideLengths.X); #endregion } if (au < -threshold) { #region INLINE: p += orient1 * (0.5 * box.SideLength.Y); p.X += orient1.X * (0.5f * box.sideLengths.Y); p.Y += orient1.Y * (0.5f * box.sideLengths.Y); p.Z += orient1.Z * (0.5f * box.sideLengths.Y); #endregion } else if (au >= threshold) { #region INLINE: p -= orient1 * (0.5 * box.SideLength.Y); p.X -= orient1.X * (0.5f * box.sideLengths.Y); p.Y -= orient1.Y * (0.5f * box.sideLengths.Y); p.Z -= orient1.Z * (0.5f * box.sideLengths.Y); #endregion } if (ad < -threshold) { #region INLINE: p += orient2 * (0.5 * box.SideLength.Z); p.X += orient2.X * (0.5f * box.sideLengths.Z); p.Y += orient2.Y * (0.5f * box.sideLengths.Z); p.Z += orient2.Z * (0.5f * box.sideLengths.Z); #endregion } else if (ad >= threshold) { #region INLINE: p -= orient2 * (0.5 * box.SideLength.Z); p.X -= orient2.X * (0.5f * box.sideLengths.Z); p.Y -= orient2.Y * (0.5f * box.sideLengths.Z); p.Z -= orient2.Z * (0.5f * box.sideLengths.Z); #endregion } }
/// <summary> /// Pushes intersection points onto the back of pts. Returns the /// number of points found. /// Points that are close together (compared to /// combinationDistance) get combined /// dirToBody0 is the collision normal towards box0 /// </summary> private static int GetBoxBoxIntersectionPoints(List<ContactPoint> pts, Box box0, Box box1, float combinationDistance, float collTolerance) { // first transform box1 into box0 space - there box0 has a corner // at the origin and faces parallel to the world planes. Then intersect // each of box1's edges with box0 faces, transforming each point back into // world space. Finally combine points float tolVal = 0.5f * collTolerance; Vector3 tol = new Vector3(tolVal); combinationDistance += collTolerance * 2.0f * (float)System.Math.Sqrt(3.0d); for (int ibox = 0; ibox < 2; ++ibox) { Box boxA = (ibox != 0) ? box1 : box0; Box boxB = (ibox != 0) ? box0 : box1; #region REFERENCE: Matrix boxAInvOrient = Matrix.Transpose(boxA.Orientation); Matrix boxAInvOrient; Matrix.Transpose(ref boxA.transform.Orientation, out boxAInvOrient); #endregion #region REFERENCE: Vector3 pos = Vector3.TransformCoordinate(boxB.Position - boxA.Position,boxAInvOrient) Vector3 pos; Vector3.Subtract(ref boxB.transform.Position, ref boxA.transform.Position, out pos); Vector3.TransformCoordinate(ref pos, ref boxAInvOrient, out pos); #endregion #region REFERENCE: Matrix boxOrient = boxB.Orientation * boxAInvOrient; Matrix boxOrient; Matrix.Multiply(ref boxB.transform.Orientation, ref boxAInvOrient, out boxOrient); #endregion Box box = tempBox; box.Position = pos; box.Orientation = boxOrient; box.SideLengths = boxB.SideLengths; // if we get more than a certain number of points back from this call, // and iBox == 0, could probably skip the other test... Vector3 sL = boxA.SideLengths; GetAABox2BoxEdgesIntersectionPoints(pts, ref sL, box, ref boxA.transform.Orientation, ref boxA.transform.Position, combinationDistance * combinationDistance); } return pts.Count; }
/// <summary> /// The AABox has a corner at the origin and size sides. /// </summary> private static int GetAABox2EdgeIntersectionPoints(List<ContactPoint> pts, ref Vector3 sides, Box box, ref Vector3 edgePt0, ref Vector3 edgePt1, ref Matrix origBoxOrient, ref Vector3 origBoxPos, float combinationDistanceSq) { // The AABox faces are aligned with the world directions. Loop // over the 3 directions and do the two tests. We know that the // AABox has a corner at the origin #region REFERENCE: Vector3 edgeDir = JiggleMath.NormalizeSafe(edgePt1 - edgePt0); Vector3 edgeDir; Vector3.Subtract(ref edgePt1, ref edgePt0, out edgeDir); JiggleMath.NormalizeSafe(ref edgeDir); #endregion int num = 0; for (int idir = 3; idir-- != 0; ) { // skip edge/face tests if nearly parallel if (System.Math.Abs(JiggleUnsafe.Get(ref edgeDir, idir)) < 0.1f) continue; int jdir = (idir + 1) % 3; int kdir = (idir + 2) % 3; for (int iface = 2; iface-- != 0; ) { float offset = 0.0f; if (iface == 1) { offset = JiggleUnsafe.Get(ref sides, idir); } float dist0 = JiggleUnsafe.Get(ref edgePt0, idir) - offset; float dist1 = JiggleUnsafe.Get(ref edgePt1, idir) - offset; float frac = -1.0f; if (dist0 * dist1 < -JiggleMath.Epsilon) frac = -dist0 / (dist1 - dist0); else if (System.Math.Abs(dist0) < JiggleMath.Epsilon) frac = 0.0f; else if (System.Math.Abs(dist1) < JiggleMath.Epsilon) frac = 1.0f; if (frac >= 0.0f) { #region REFERENCE: Vector3 pt = (1.0f - frac) * edgePt0 + frac * edgePt1 Vector3 tmp; Vector3 pt; Vector3.Multiply(ref edgePt1, frac, out tmp); Vector3.Multiply(ref edgePt0, 1.0f - frac, out pt); Vector3.Add(ref pt, ref tmp, out pt); #endregion // check the point is within the face rectangle float ptJdir = JiggleUnsafe.Get(ref pt, jdir); float ptKdir = JiggleUnsafe.Get(ref pt, kdir); if ((ptJdir > -JiggleMath.Epsilon) && (ptJdir < JiggleUnsafe.Get(ref sides, jdir) + JiggleMath.Epsilon) && (ptKdir > -JiggleMath.Epsilon) && (ptKdir < JiggleUnsafe.Get(ref sides, kdir) + JiggleMath.Epsilon)) { // woohoo got a point #region REFERENCE: Vector3 pos = origBoxPos + Vector3.TransformCoordinate(pt, origBoxOrient); Vector3 pos; Vector3.TransformCoordinate(ref pt, ref origBoxOrient, out pos); Vector3.Add(ref origBoxPos, ref pos, out pos); #endregion AddPoint(pts, ref pos, combinationDistanceSq); if (++num == 2) return num; } } } } return num; }
/// <summary> /// Pushes intersection points (in world space) onto the back of pts. /// Intersection is between an AABox faces and an orientated box's /// edges. orient and pos are used to transform the points from the /// AABox frame back into the original frame. /// </summary> private static int GetAABox2BoxEdgesIntersectionPoints(List<ContactPoint> pts, ref Vector3 sides, Box box, ref Matrix origBoxOrient, ref Vector3 origBoxPos, float combinationDistanceSq) { int num = 0; Vector3[] boxPts; box.GetCornerPoints(out boxPts); Box.Edge[] edges; box.GetEdges(out edges); for (int iedge = 0; iedge < 12; ++iedge) { Vector3 edgePt0 = boxPts[(int)edges[iedge].Ind0]; Vector3 edgePt1 = boxPts[(int)edges[iedge].Ind1]; num += GetAABox2EdgeIntersectionPoints(pts, ref sides, box, ref edgePt0, ref edgePt1, ref origBoxOrient, ref origBoxPos, combinationDistanceSq); // Don't think we can get more than 8... and anyway if we get too many // then the penetration must be so bad who cares about the details? if (num >= 8) return num; } return num; }
/// <summary> /// Disjoint Returns true if disjoint. Returns false if intersecting, /// and sets the overlap depth, d scaled by the axis length. /// </summary> private static bool Disjoint(out float d, ref Vector3 axis, Box box0, Box box1, float collTolerance) { float min0, max0, min1, max1; box0.GetSpan(out min0, out max0, axis); box1.GetSpan(out min1, out max1, axis); if (min0 > (max1 + collTolerance + JiggleMath.Epsilon) || min1 > (max0 + collTolerance + JiggleMath.Epsilon)) { d = 0.0f; return true; } if ((max0 > max1) && (min1 > min0)) { // box1 is inside - choose the min dist to move it out d = MathHelper.Min(max0 - min1, max1 - min0); } else if ((max1 > max0) && (min0 > min1)) { // box0 is inside - choose the min dist to move it out d = MathHelper.Min(max1 - min0, max0 - min1); } else { // boxes overlap d = (max0 < max1) ? max0 : max1; d -= (min0 > min1) ? min0 : min1; } return false; }