public static unsafe Result BoxSphere(BoxCollider *boxA, SphereCollider *sphereB, MTransform aFromB) { MTransform aFromBoxA = new MTransform(boxA->Orientation, boxA->Center); float3 posBinA = Mul(aFromB, sphereB->Center); float3 posBinBoxA = Mul(Inverse(aFromBoxA), posBinA); float3 innerHalfExtents = boxA->Size * 0.5f - boxA->ConvexRadius; float3 normalInBoxA; float distance; { // from hkAabb::signedDistanceToPoint(), can optimize a lot float3 projection = math.min(posBinBoxA, innerHalfExtents); projection = math.max(projection, -innerHalfExtents); float3 difference = projection - posBinBoxA; float distanceSquared = math.lengthsq(difference); // Check if the sphere center is inside the box if (distanceSquared < 1e-6f) { float3 projectionLocal = projection; float3 absProjectionLocal = math.abs(projectionLocal); float3 del = absProjectionLocal - innerHalfExtents; int axis = IndexOfMaxComponent(new float4(del, -float.MaxValue)); switch (axis) { case 0: normalInBoxA = new float3(projectionLocal.x < 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); break; case 1: normalInBoxA = new float3(0.0f, projectionLocal.y < 0.0f ? 1.0f : -1.0f, 0.0f); break; case 2: normalInBoxA = new float3(0.0f, 0.0f, projectionLocal.z < 0.0f ? 1.0f : -1.0f); break; default: normalInBoxA = new float3(1, 0, 0); Assert.IsTrue(false); break; } distance = math.max(del.x, math.max(del.y, del.z)); } else { float invDistance = math.rsqrt(distanceSquared); normalInBoxA = difference * invDistance; distance = distanceSquared * invDistance; } } float3 normalInA = math.mul(aFromBoxA.Rotation, normalInBoxA); return(new Result { NormalInA = normalInA, PositionOnAinA = posBinA + normalInA * (distance - boxA->ConvexRadius), Distance = distance - (sphereB->Radius + boxA->ConvexRadius) }); }
// Create a contact point for a box and a sphere in world space. public static unsafe void BoxSphere( BoxCollider *boxA, SphereCollider *sphereB, MTransform worldFromA, MTransform aFromB, float maxDistance, out Manifold manifold) { DistanceQueries.Result convexDistance = DistanceQueries.BoxSphere(boxA, sphereB, aFromB); if (convexDistance.Distance < maxDistance) { manifold = new Manifold(convexDistance, worldFromA); } else { manifold = new Manifold(); } }
public unsafe void Execute(ref PhysicsCollider collider, ref LevelBorderComponent border, ref Translation translation) { // make sure we are dealing with boxes if (collider.ColliderPtr->Type != ColliderType.Box) { return; } translation.Value = float3.zero; // tweak the physical representation of the box // grab the box pointer BoxCollider *scPtr = (BoxCollider *)collider.ColliderPtr; var box = scPtr->Geometry; var minSize = 0.1f; switch (border.Position) { case BorderPosition.Left: box.Center = new float3(ScreenRect.xMin, ScreenRect.center.y, 0); box.Size = new float3(minSize, ScreenRect.height, minSize); break; case BorderPosition.Right: box.Center = new float3(ScreenRect.xMax, ScreenRect.center.y, 0); box.Size = new float3(minSize, ScreenRect.height, minSize); break; case BorderPosition.Top: box.Center = new float3(ScreenRect.center.x, ScreenRect.yMax, 0); box.Size = new float3(ScreenRect.width, minSize, minSize); break; case BorderPosition.Bottom: box.Center = new float3(ScreenRect.center.x, ScreenRect.yMin, 0); box.Size = new float3(ScreenRect.width, minSize, minSize); break; } // update the collider geometry box.Orientation = quaternion.identity; box.BevelRadius = 0; scPtr->Geometry = box; }
protected override void OnStartRunning() { base.OnStartRunning(); Entities .WithoutBurst() .ForEach((ref Translation topBoundaryTrans, ref PhysicsCollider collider, in TopBoundaryTag topBoundaryTag) => { // make sure we are dealing with spheres if (collider.Value.Value.Type != ColliderType.Box) { return; } unsafe { // grab the sphere pointer BoxCollider *scPtr = (BoxCollider *)collider.ColliderPtr; // update the collider geometry var BoxGeometry = scPtr->Geometry; BoxGeometry.Size = new float3(GameManager.Instance.ScreenSizeInWorldSpace.x, 0.1f, 0.1f);; scPtr->Geometry = BoxGeometry; } topBoundaryTrans.Value = new float3(0, GameManager.Instance.bounds.y, 0); }).Run(); Entities .WithoutBurst() .ForEach((ref Translation bottomBoundaryTrans, ref PhysicsCollider collider, in BottomBoundaryTag bottomBoundaryTag) => { // make sure we are dealing with spheres if (collider.Value.Value.Type != ColliderType.Box) { return; } unsafe { // grab the sphere pointer BoxCollider *scPtr = (BoxCollider *)collider.ColliderPtr; // update the collider geometry var BoxGeometry = scPtr->Geometry; BoxGeometry.Size = new float3(GameManager.Instance.ScreenSizeInWorldSpace.x, 0.1f, 0.1f);; scPtr->Geometry = BoxGeometry; } bottomBoundaryTrans.Value = new float3(0, -GameManager.Instance.bounds.y, 0); }).Run(); Entities .WithoutBurst() .ForEach((ref Translation leftBoundaryTrans, ref PhysicsCollider collider, in LeftBoundaryTag leftBoundaryTag) => { // make sure we are dealing with spheres if (collider.Value.Value.Type != ColliderType.Box) { return; } unsafe { // grab the sphere pointer BoxCollider *scPtr = (BoxCollider *)collider.ColliderPtr; // update the collider geometry var BoxGeometry = scPtr->Geometry; BoxGeometry.Size = new float3(0.1f, GameManager.Instance.ScreenSizeInWorldSpace.y, 0.1f);; scPtr->Geometry = BoxGeometry; } leftBoundaryTrans.Value = new float3(-GameManager.Instance.bounds.x, 0, 0); }).Run(); Entities .WithoutBurst() .ForEach((ref Translation rightBoundaryTrans, ref PhysicsCollider collider, in RightBoundaryTag rightBoundaryTag) => { // make sure we are dealing with spheres if (collider.Value.Value.Type != ColliderType.Box) { return; } unsafe { // grab the sphere pointer BoxCollider *scPtr = (BoxCollider *)collider.ColliderPtr; // update the collider geometry var BoxGeometry = scPtr->Geometry; BoxGeometry.Size = new float3(0.1f, GameManager.Instance.ScreenSizeInWorldSpace.y, 0.1f);; scPtr->Geometry = BoxGeometry; } rightBoundaryTrans.Value = new float3(GameManager.Instance.bounds.x, 0, 0); }).Run(); }
// Create contact points for a box and triangle in world space. public static unsafe void BoxTriangle( BoxCollider *boxA, PolygonCollider *triangleB, MTransform worldFromA, MTransform aFromB, float maxDistance, out Manifold manifold) { Assert.IsTrue(triangleB->Vertices.Length == 3); // Get triangle in box space MTransform aFromBoxA = new MTransform(boxA->Orientation, boxA->Center); MTransform boxAFromB = Mul(Inverse(aFromBoxA), aFromB); float3 t0 = Mul(boxAFromB, triangleB->ConvexHull.Vertices[0]); float3 t1 = Mul(boxAFromB, triangleB->ConvexHull.Vertices[1]); float3 t2 = Mul(boxAFromB, triangleB->ConvexHull.Vertices[2]); Plane triPlane = triangleB->ConvexHull.Planes[0]; float3 triangleNormal = math.mul(boxAFromB.Rotation, triPlane.Normal); FourTransposedPoints vertsB; FourTransposedPoints edgesB; FourTransposedPoints perpsB; CalcTrianglePlanes(t0, t1, t2, triangleNormal, out vertsB, out edgesB, out perpsB); float3 halfExtents = boxA->Size * 0.5f + maxDistance; // find the closest minkowski plane float4 plane; { // Box face vs triangle vertex float4 planeFaceVertex; { // get aabb of minkowski diff float3 tMin = math.min(math.min(t0, t1), t2) - halfExtents; float3 tMax = math.max(math.max(t0, t1), t2) + halfExtents; // find the aabb face closest to the origin float3 axis0 = new float3(1, 0, 0); float3 axis1 = axis0.zxy; // 010 float3 axis2 = axis0.yzx; // 001 float4 planeX = SelectMaxW(new float4(axis0, -tMax.x), new float4(-axis0, tMin.x)); float4 planeY = SelectMaxW(new float4(axis1, -tMax.y), new float4(-axis1, tMin.y)); float4 planeZ = SelectMaxW(new float4(axis2, -tMax.z), new float4(-axis2, tMin.z)); planeFaceVertex = SelectMaxW(planeX, planeY); planeFaceVertex = SelectMaxW(planeFaceVertex, planeZ); } // Box vertex vs triangle face float4 planeVertexFace; { // Calculate the triangle normal float triangleOffset = math.dot(triangleNormal, t0); float expansionOffset = math.dot(math.abs(triangleNormal), halfExtents); planeVertexFace = SelectMaxW( new float4(triangleNormal, -triangleOffset - expansionOffset), new float4(-triangleNormal, triangleOffset - expansionOffset)); } // Edge planes float4 planeEdgeEdge = new float4(0, 0, 0, -float.MaxValue); { // Test the planes from crossing axis i with each edge of the triangle, for example if i = 1 then n0 is from (0, 1, 0) x (t1 - t0). for (int i = 0, j = 1, k = 2; i < 3; j = k, k = i, i++) { // Normalize the cross product and flip it to point outward from the edge float4 lengthsSq = edgesB.GetComponent(j) * edgesB.GetComponent(j) + edgesB.GetComponent(k) * edgesB.GetComponent(k); float4 invLengths = math.rsqrt(lengthsSq); float4 dots = edgesB.GetComponent(j) * perpsB.GetComponent(k) - edgesB.GetComponent(k) * perpsB.GetComponent(j); float4 factors = invLengths * math.sign(dots); float4 nj = -edgesB.GetComponent(k) * factors; float4 nk = edgesB.GetComponent(j) * factors; float4 distances = -nj *vertsB.GetComponent(j) - nk * vertsB.GetComponent(k) - math.abs(nj) * halfExtents[j] - math.abs(nk) * halfExtents[k]; // If the box edge is parallel to the triangle face then skip it, the plane is redundant with a vertex-face plane bool4 valid = dots != float4.zero; distances = math.select(Constants.Min4F, distances, valid); float3 n0 = new float3(); n0[i] = 0.0f; n0[j] = nj[0]; n0[k] = nk[0]; float3 n1 = new float3(); n1[i] = 0.0f; n1[j] = nj[1]; n1[k] = nk[1]; float3 n2 = new float3(); n2[i] = 0.0f; n2[j] = nj[2]; n2[k] = nk[2]; float4 temp = SelectMaxW(SelectMaxW(new float4(n0, distances.x), new float4(n1, distances.y)), new float4(n2, distances.z)); planeEdgeEdge = SelectMaxW(planeEdgeEdge, temp); } } plane = SelectMaxW(SelectMaxW(planeFaceVertex, planeVertexFace), planeEdgeEdge); } manifold = new Manifold(); // Check for a separating plane TODO.ma could early out as soon as any plane with w>0 is found if (plane.w <= 0.0f) { // Get the normal and supporting faces float3 normalInA = math.mul(boxA->Orientation, plane.xyz); manifold.Normal = math.mul(worldFromA.Rotation, normalInA); int faceIndexA = boxA->ConvexHull.GetSupportingFace(-normalInA); int faceIndexB = triangleB->ConvexHull.GetSupportingFace(math.mul(math.transpose(aFromB.Rotation), normalInA)); // Build manifold if (!FaceFace(ref boxA->ConvexHull, ref triangleB->ConvexHull, faceIndexA, faceIndexB, worldFromA, aFromB, normalInA, float.MaxValue, ref manifold)) { // The closest points are vertices, we need GJK to find them ConvexConvex( ref ((ConvexCollider *)boxA)->ConvexHull, ref ((ConvexCollider *)triangleB)->ConvexHull, worldFromA, aFromB, maxDistance, out manifold); } } }
// Create contact points for a pair of boxes in world space. public static unsafe void BoxBox( BoxCollider *boxA, BoxCollider *boxB, MTransform worldFromA, MTransform aFromB, float maxDistance, out Manifold manifold) { manifold = new Manifold(); // Get transforms with box center at origin MTransform bFromBoxB = new MTransform(boxB->Orientation, boxB->Center); MTransform aFromBoxA = new MTransform(boxA->Orientation, boxA->Center); MTransform boxAFromBoxB = Mul(Inverse(aFromBoxA), Mul(aFromB, bFromBoxB)); MTransform boxBFromBoxA = Inverse(boxAFromBoxB); float3 halfExtentsA = boxA->Size * 0.5f; float3 halfExtentsB = boxB->Size * 0.5f; // Test planes of each box against the other's vertices float3 normal; // in BoxA-space float distance; { float3 normalA = new float3(1, 0, 0); float3 normalB = new float3(1, 0, 0); float distA = 0.0f; float distB = 0.0f; if (!PointPlanes(boxAFromBoxB, halfExtentsA, halfExtentsB, maxDistance, ref normalA, ref distA) || !PointPlanes(boxBFromBoxA, halfExtentsB, halfExtentsA, maxDistance, ref normalB, ref distB)) { return; } normalB = math.mul(boxAFromBoxB.Rotation, normalB); bool aGreater = distA > distB; normal = math.select(-normalB, normalA, (bool3)aGreater); distance = math.select(distB, distA, aGreater); } // Test edge pairs { float3 edgeA = new float3(1.0f, 0.0f, 0.0f); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { float3 edgeB; switch (j) { case 0: edgeB = boxAFromBoxB.Rotation.c0; break; case 1: edgeB = boxAFromBoxB.Rotation.c1; break; case 2: edgeB = boxAFromBoxB.Rotation.c2; break; default: edgeB = new float3(0.0f); break; } float3 dir = math.cross(edgeA, edgeB); // hack around parallel edges if (math.all(math.abs(dir) < new float3(1e-5f))) { continue; } float3 edgeNormal = math.normalize(dir); float3 supportA = math.select(halfExtentsA, -halfExtentsA, dir < new float3(0.0f)); float maxA = math.abs(math.dot(supportA, edgeNormal)); float minA = -maxA; float3 dirInB = math.mul(boxBFromBoxA.Rotation, dir); float3 supportBinB = math.select(halfExtentsB, -halfExtentsB, dirInB < new float3(0.0f)); float3 supportB = math.mul(boxAFromBoxB.Rotation, supportBinB); float offsetB = math.abs(math.dot(supportB, edgeNormal)); float centerB = math.dot(boxAFromBoxB.Translation, edgeNormal); float maxB = centerB + offsetB; float minB = centerB - offsetB; float2 diffs = new float2(minB - maxA, minA - maxB); // positive normal, negative normal if (math.all(diffs > new float2(maxDistance))) { return; } if (diffs.x > distance) { distance = diffs.x; normal = -edgeNormal; } if (diffs.y > distance) { distance = diffs.y; normal = edgeNormal; } } edgeA = edgeA.zxy; } } if (distance < maxDistance) { // Get the normal and supporting faces float3 normalInA = math.mul(boxA->Orientation, normal); manifold.Normal = math.mul(worldFromA.Rotation, normalInA); int faceIndexA = boxA->ConvexHull.GetSupportingFace(-normalInA); int faceIndexB = boxB->ConvexHull.GetSupportingFace(math.mul(math.transpose(aFromB.Rotation), normalInA)); // Build manifold if (!FaceFace(ref boxA->ConvexHull, ref boxB->ConvexHull, faceIndexA, faceIndexB, worldFromA, aFromB, normalInA, distance, ref manifold)) { // The closest points are vertices, we need GJK to find them ConvexConvex( ref ((ConvexCollider *)boxA)->ConvexHull, ref ((ConvexCollider *)boxB)->ConvexHull, worldFromA, aFromB, maxDistance, out manifold); } } }
// Dispatch any pair of convex colliders public static unsafe Result ConvexConvex(Collider *convexA, Collider *convexB, MTransform aFromB) { Result result; bool flip = false; switch (convexA->Type) { case ColliderType.Sphere: SphereCollider *sphereA = (SphereCollider *)convexA; switch (convexB->Type) { case ColliderType.Sphere: result = SphereSphere(sphereA, (SphereCollider *)convexB, aFromB); break; case ColliderType.Capsule: CapsuleCollider *capsuleB = (CapsuleCollider *)convexB; result = CapsuleSphere(capsuleB->Vertex0, capsuleB->Vertex1, capsuleB->Radius, sphereA->Center, sphereA->Radius, Inverse(aFromB)); flip = true; break; case ColliderType.Triangle: PolygonCollider *triangleB = (PolygonCollider *)convexB; result = TriangleSphere( triangleB->Vertices[0], triangleB->Vertices[1], triangleB->Vertices[2], triangleB->Planes[0].Normal, sphereA->Center, sphereA->Radius, Inverse(aFromB)); flip = true; break; case ColliderType.Quad: PolygonCollider *quadB = (PolygonCollider *)convexB; result = QuadSphere( quadB->Vertices[0], quadB->Vertices[1], quadB->Vertices[2], quadB->Vertices[3], quadB->Planes[0].Normal, sphereA->Center, sphereA->Radius, Inverse(aFromB)); flip = true; break; case ColliderType.Box: result = BoxSphere((BoxCollider *)convexB, sphereA, Inverse(aFromB)); flip = true; break; case ColliderType.Cylinder: case ColliderType.Convex: result = ConvexConvex(ref sphereA->ConvexHull, ref ((ConvexCollider *)convexB)->ConvexHull, aFromB); break; default: throw new NotImplementedException(); } break; case ColliderType.Capsule: CapsuleCollider *capsuleA = (CapsuleCollider *)convexA; switch (convexB->Type) { case ColliderType.Sphere: SphereCollider *sphereB = (SphereCollider *)convexB; result = CapsuleSphere(capsuleA->Vertex0, capsuleA->Vertex1, capsuleA->Radius, sphereB->Center, sphereB->Radius, aFromB); break; case ColliderType.Capsule: result = CapsuleCapsule(capsuleA, (CapsuleCollider *)convexB, aFromB); break; case ColliderType.Triangle: result = CapsuleTriangle(capsuleA, (PolygonCollider *)convexB, aFromB); break; case ColliderType.Box: case ColliderType.Quad: case ColliderType.Cylinder: case ColliderType.Convex: result = ConvexConvex(ref capsuleA->ConvexHull, ref ((ConvexCollider *)convexB)->ConvexHull, aFromB); break; default: throw new NotImplementedException(); } break; case ColliderType.Triangle: PolygonCollider *triangleA = (PolygonCollider *)convexA; switch (convexB->Type) { case ColliderType.Sphere: SphereCollider *sphereB = (SphereCollider *)convexB; result = TriangleSphere( triangleA->Vertices[0], triangleA->Vertices[1], triangleA->Vertices[2], triangleA->Planes[0].Normal, sphereB->Center, sphereB->Radius, aFromB); break; case ColliderType.Capsule: result = CapsuleTriangle((CapsuleCollider *)convexB, triangleA, Inverse(aFromB)); flip = true; break; case ColliderType.Box: case ColliderType.Triangle: case ColliderType.Quad: case ColliderType.Cylinder: case ColliderType.Convex: result = ConvexConvex(ref triangleA->ConvexHull, ref ((ConvexCollider *)convexB)->ConvexHull, aFromB); break; default: throw new NotImplementedException(); } break; case ColliderType.Box: BoxCollider *boxA = (BoxCollider *)convexA; switch (convexB->Type) { case ColliderType.Sphere: result = BoxSphere(boxA, (SphereCollider *)convexB, aFromB); break; case ColliderType.Capsule: case ColliderType.Box: case ColliderType.Triangle: case ColliderType.Quad: case ColliderType.Cylinder: case ColliderType.Convex: result = ConvexConvex(ref boxA->ConvexHull, ref ((ConvexCollider *)convexB)->ConvexHull, aFromB); break; default: throw new NotImplementedException(); } break; case ColliderType.Quad: case ColliderType.Cylinder: case ColliderType.Convex: result = ConvexConvex(ref ((ConvexCollider *)convexA)->ConvexHull, ref ((ConvexCollider *)convexB)->ConvexHull, aFromB); break; default: throw new NotImplementedException(); } if (flip) { result.PositionOnAinA = Mul(aFromB, result.PositionOnBinA); result.NormalInA = math.mul(aFromB.Rotation, -result.NormalInA); } return(result); }
public void CollideWithSolid(Entity solidAgentEntity, Entity solidEntity, ref RigidBody solidAgentRigidBody, ref RigidBody solidRigidBody) { Translation solidAgentTranslation = TranslationGroup[solidAgentEntity]; Rotation solidAgentRotation = RotationGroup[solidAgentEntity]; Translation solidTranslation = TranslationGroup[solidEntity]; Rotation solidRotation = RotationGroup[solidEntity]; //The solid agent is guaranteed to be a solid agent. //We assume anything the solid agent collides with is a solid. //Triggers, for example, will not be triggered here - only collisions happen here BoxCollider *solidBoxCollider = (BoxCollider *)solidRigidBody.Collider; //Flatten out the translation, since the rigid body caster seems bugged? //TODO check into this with later updates to UnityPhysics BoxCollider *updatedBoxCollider = (BoxCollider *)BoxCollider.Create( new BoxGeometry { BevelRadius = solidBoxCollider->BevelRadius, Center = solidBoxCollider->Center + solidTranslation.Value, Orientation = solidRotation.Value, Size = solidBoxCollider->Size }, solidBoxCollider->Filter, solidBoxCollider->Material).GetUnsafePtr(); RigidBody updatedSolidRigidBody = new RigidBody { CustomTags = solidRigidBody.CustomTags, Collider = (Collider *)updatedBoxCollider, Entity = solidRigidBody.Entity, WorldFromBody = new RigidTransform(quaternion.identity, float3.zero) }; //Collision Check ColliderCastInput colliderCastInput = new ColliderCastInput { Collider = solidAgentRigidBody.Collider, Start = solidAgentTranslation.Value, End = solidAgentTranslation.Value, Orientation = solidAgentRotation.Value }; ColliderCastHit colliderCastHit; bool isSolidAgentCollided = updatedSolidRigidBody.CastCollider(colliderCastInput, out colliderCastHit); if (isSolidAgentCollided) { bool isGroundCollided = false; bool isCeilingCollided = false; bool isLeftWallCollided = false; bool isRightWallCollided = false; if (colliderCastHit.SurfaceNormal.y >= SurfaceNormalEdgeThreshold) { isGroundCollided = true; } else if (colliderCastHit.SurfaceNormal.y <= -SurfaceNormalEdgeThreshold) { isCeilingCollided = true; } else if (colliderCastHit.SurfaceNormal.x >= SurfaceNormalEdgeThreshold) { isLeftWallCollided = true; } else if (colliderCastHit.SurfaceNormal.x <= -SurfaceNormalEdgeThreshold) { isRightWallCollided = true; } if (isGroundCollided || isCeilingCollided || isLeftWallCollided || isRightWallCollided) { SolidAgent solidAgent = SolidAgentGroup[solidAgentEntity]; if (isGroundCollided) { solidAgent.IsGroundCollided = true; solidAgent.GroundSurfaceNormal = colliderCastHit.SurfaceNormal.y; } if (isCeilingCollided) { solidAgent.IsCeilingCollided = true; solidAgent.CeilingSurfaceNormal = colliderCastHit.SurfaceNormal.y; } if (isLeftWallCollided) { solidAgent.IsLeftWallCollided = true; solidAgent.LeftWallSurfaceNormal = colliderCastHit.SurfaceNormal.x; } if (isRightWallCollided) { solidAgent.IsRightWallCollided = true; solidAgent.RightWallSurfaceNormal = colliderCastHit.SurfaceNormal.x; } SolidAgentGroup[solidAgentEntity] = solidAgent; } } }