public override void UpdateCollision(float dt) { WasContaining = Containing; WasTouching = Touching; RigidTransform transform = new RigidTransform { Orientation = Quaternion.Identity }; DetectorVolume.TriangleMesh.Tree.GetOverlaps(convex.boundingBox, overlaps); for (int i = 0; i < overlaps.Count; i++) { DetectorVolume.TriangleMesh.Data.GetTriangle(overlaps.Elements[i], out triangle.vA, out triangle.vB, out triangle.vC); Vector3.Add(ref triangle.vA, ref triangle.vB, out transform.Position); Vector3.Add(ref triangle.vC, ref transform.Position, out transform.Position); Vector3.Multiply(ref transform.Position, 1 / 3f, out transform.Position); Vector3.Subtract(ref triangle.vA, ref transform.Position, out triangle.vA); Vector3.Subtract(ref triangle.vB, ref transform.Position, out triangle.vB); Vector3.Subtract(ref triangle.vC, ref transform.Position, out triangle.vC); //If this triangle collides with the convex, we can stop immediately since we know we're touching and not containing.))) //[MPR is used here in lieu of GJK because the MPR implementation tends to finish quicker when objects are overlapping than GJK. The GJK implementation does better on separated objects.] if (MPRToolbox.AreShapesOverlapping(convex.Shape, triangle, ref convex.worldTransform, ref transform)) { Touching = true; //The convex can't be fully contained if it's still touching the surface. Containing = false; overlaps.Clear(); goto events; } } overlaps.Clear(); //If we get here, then there was no shell intersection. //If the convex's center point is contained by the mesh, then the convex is fully contained. //If this is a child pair, the CheckContainment flag may be set to false. This is because the parent has //already determined that it is not contained (another child performed the check and found that it was not contained) //and that it is already touching somehow (either by intersection or by containment). //so further containment tests are unnecessary. if (CheckContainment && DetectorVolume.IsPointContained(ref convex.worldTransform.Position, overlaps)) { Touching = true; Containing = true; goto events; } //If we get here, then there was no surface intersection and the convex's center is not contained- the volume and convex are separate! Touching = false; Containing = false; events: NotifyDetectorVolumeOfChanges(); }
public override void UpdateCollision(float dt) { WasContaining = Containing; WasTouching = Touching; mobileTriangle.collisionMargin = mesh.Shape.MeshCollisionMargin; //Scan the pairs in sequence, updating the state as we go. //Touching can be set to true by a single touching subpair. Touching = false; //Containing can be set to false by a single noncontaining or nontouching subpair. Containing = true; var meshData = mesh.Shape.TriangleMesh.Data; RigidTransform mobileTriangleTransform, detectorTriangleTransform; mobileTriangleTransform.Orientation = Quaternion.Identity; detectorTriangleTransform.Orientation = Quaternion.Identity; for (int i = 0; i < meshData.Indices.Length; i += 3) { //Grab a triangle associated with the mobile mesh. meshData.GetTriangle(i, out mobileTriangle.vA, out mobileTriangle.vB, out mobileTriangle.vC); RigidTransform.Transform(ref mobileTriangle.vA, ref mesh.worldTransform, out mobileTriangle.vA); RigidTransform.Transform(ref mobileTriangle.vB, ref mesh.worldTransform, out mobileTriangle.vB); RigidTransform.Transform(ref mobileTriangle.vC, ref mesh.worldTransform, out mobileTriangle.vC); Vector3f.Add(ref mobileTriangle.vA, ref mobileTriangle.vB, out mobileTriangleTransform.Position); Vector3f.Add(ref mobileTriangle.vC, ref mobileTriangleTransform.Position, out mobileTriangleTransform.Position); Vector3f.Multiply(ref mobileTriangleTransform.Position, 1 / 3f, out mobileTriangleTransform.Position); Vector3f.Subtract(ref mobileTriangle.vA, ref mobileTriangleTransform.Position, out mobileTriangle.vA); Vector3f.Subtract(ref mobileTriangle.vB, ref mobileTriangleTransform.Position, out mobileTriangle.vB); Vector3f.Subtract(ref mobileTriangle.vC, ref mobileTriangleTransform.Position, out mobileTriangle.vC); //Go through all the detector volume triangles which are near the mobile mesh triangle. bool triangleTouching, triangleContaining; BoundingBox mobileBoundingBox; mobileTriangle.GetBoundingBox(ref mobileTriangleTransform, out mobileBoundingBox); DetectorVolume.TriangleMesh.Tree.GetOverlaps(mobileBoundingBox, overlaps); for (int j = 0; j < overlaps.Count; j++) { DetectorVolume.TriangleMesh.Data.GetTriangle(overlaps.Elements[j], out detectorTriangle.vA, out detectorTriangle.vB, out detectorTriangle.vC); Vector3f.Add(ref detectorTriangle.vA, ref detectorTriangle.vB, out detectorTriangleTransform.Position); Vector3f.Add(ref detectorTriangle.vC, ref detectorTriangleTransform.Position, out detectorTriangleTransform.Position); Vector3f.Multiply(ref detectorTriangleTransform.Position, 1 / 3f, out detectorTriangleTransform.Position); Vector3f.Subtract(ref detectorTriangle.vA, ref detectorTriangleTransform.Position, out detectorTriangle.vA); Vector3f.Subtract(ref detectorTriangle.vB, ref detectorTriangleTransform.Position, out detectorTriangle.vB); Vector3f.Subtract(ref detectorTriangle.vC, ref detectorTriangleTransform.Position, out detectorTriangle.vC); //If this triangle collides with the convex, we can stop immediately since we know we're touching and not containing.))) //[MPR is used here in lieu of GJK because the MPR implementation tends to finish quicker than GJK when objects are overlapping. The GJK implementation does better on separated objects.] if (MPRToolbox.AreShapesOverlapping(detectorTriangle, mobileTriangle, ref detectorTriangleTransform, ref mobileTriangleTransform)) { triangleTouching = true; //The convex can't be fully contained if it's still touching the surface. triangleContaining = false; overlaps.Clear(); goto finishTriangleTest; } } overlaps.Clear(); //If we get here, then there was no shell intersection. //If the convex's center point is contained by the mesh, then the convex is fully contained. //This test is only needed if containment hasn't yet been outlawed or a touching state hasn't been established. if ((!Touching || Containing) && DetectorVolume.IsPointContained(ref mobileTriangleTransform.Position, overlaps)) { triangleTouching = true; triangleContaining = true; goto finishTriangleTest; } //If we get here, then there was no surface intersection and the convex's center is not contained- the volume and convex are separate! triangleTouching = false; triangleContaining = false; finishTriangleTest: //Analyze the results of the triangle test. if (triangleTouching) { Touching = true; //If one child is touching, then we are touching too. } else { Containing = false; //If one child isn't touching, then we aren't containing. } if (!triangleContaining) //If one child isn't containing, then we aren't containing. { Containing = false; } if (!Containing && Touching) { //If it's touching but not containing, no further pairs will change the state. //Containment has been invalidated by something that either didn't touch or wasn't contained. //Touching has been ensured by at least one object touching. break; } } //There is a possibility that the MobileMesh is solid and fully contains the DetectorVolume. //In this case, we should be Touching, but currently we are not. if (mesh.Shape.solidity == MobileMeshSolidity.Solid && !Containing && !Touching) { //To determine if the detector volume is fully contained, check if one of the detector mesh's vertices //are in the mobile mesh. //This *could* fail if the mobile mesh is actually multiple pieces, but that's not a common or really supported case for solids. Vector3f vertex; DetectorVolume.TriangleMesh.Data.GetVertexPosition(0, out vertex); Ray ray; ray.Direction = VectorHelper.Up; RayHit hit; RigidTransform.TransformByInverse(ref vertex, ref mesh.worldTransform, out ray.Position); if (mesh.Shape.IsLocalRayOriginInMesh(ref ray, out hit)) { Touching = true; } } NotifyDetectorVolumeOfChanges(); }
/// <summary> /// Constructs a new demo. /// </summary> /// <param name="game">Game owning this demo.</param> public BooleanConvexTestDemo(DemosGame game) : base(game) { var random = new Random(); int numberOfConfigurations = 1000; int numberOfTestsPerConfiguration = 10000; float size = 2; var aPositionBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size)); var bPositionBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size)); size = 1; var aShapeBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size)); var bShapeBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size)); int pointsInA = 10; int pointsInB = 10; RawList <Vector3> points = new RawList <Vector3>(); long accumulatedMPR = 0; long accumulatedGJK = 0; long accumulatedGJKSeparatingAxis = 0; for (int i = 0; i < numberOfConfigurations; i++) { //Create two convex hull shapes. for (int j = 0; j < pointsInA; j++) { Vector3 point; GetRandomPointInBoundingBox(random, ref aShapeBounds, out point); points.Add(point); } var a = new ConvexHullShape(points); points.Clear(); for (int j = 0; j < pointsInB; j++) { Vector3 point; GetRandomPointInBoundingBox(random, ref bShapeBounds, out point); points.Add(point); } var b = new ConvexHullShape(points); points.Clear(); //Generate some random tranforms for the shapes. RigidTransform aTransform; var axis = Vector3.Normalize(new Vector3((float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2))); var angle = (float)random.NextDouble() * MathHelper.TwoPi; Quaternion.CreateFromAxisAngle(ref axis, angle, out aTransform.Orientation); GetRandomPointInBoundingBox(random, ref aPositionBounds, out aTransform.Position); RigidTransform bTransform; axis = Vector3.Normalize(new Vector3((float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2))); angle = (float)random.NextDouble() * MathHelper.TwoPi; Quaternion.CreateFromAxisAngle(ref axis, angle, out bTransform.Orientation); GetRandomPointInBoundingBox(random, ref bPositionBounds, out bTransform.Position); //Perform MPR tests. //Warm up the cache a bit. MPRToolbox.AreShapesOverlapping(a, b, ref aTransform, ref bTransform); long start = Stopwatch.GetTimestamp(); for (int j = 0; j < numberOfTestsPerConfiguration; j++) { if (MPRToolbox.AreShapesOverlapping(a, b, ref aTransform, ref bTransform)) { overlapsMPR++; } } long end = Stopwatch.GetTimestamp(); accumulatedMPR += end - start; //Perform GJK tests. //Warm up the cache a bit. GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform); start = Stopwatch.GetTimestamp(); for (int j = 0; j < numberOfTestsPerConfiguration; j++) { if (GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform)) { overlapsGJK++; } } end = Stopwatch.GetTimestamp(); accumulatedGJK += end - start; //Perform GJK Separating Axis tests. //Warm up the cache a bit. Vector3 localSeparatingAxis = Vector3.Up; GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform, ref localSeparatingAxis); start = Stopwatch.GetTimestamp(); for (int j = 0; j < numberOfTestsPerConfiguration; j++) { if (GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform, ref localSeparatingAxis)) { overlapsGJKSeparatingAxis++; } } end = Stopwatch.GetTimestamp(); accumulatedGJKSeparatingAxis += end - start; } //Compute the actual time per test. long denominator = Stopwatch.Frequency * numberOfConfigurations * numberOfTestsPerConfiguration; timeMPR = (double)accumulatedMPR / denominator; timeGJK = (double)accumulatedGJK / denominator; timeGJKSeparatingAxis = (double)accumulatedGJKSeparatingAxis / denominator; }