public void UpdateWorldOneDynamicBoxTest()
 {
     Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld(0, 1);
     BroadPhaseTests.addDynamicBoxToWorld(world, 0, Vector3.zero, quaternion.identity, new Vector3(10, .1f, 10));
     world.CollisionWorld.UpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
示例#2
0
        static unsafe void CheckColliderCastHit(ref Physics.PhysicsWorld world, ColliderCastInput input, ColliderCastHit hit, string failureMessage)
        {
            // Fetch the leaf collider and convert the shape cast result into a distance result at the hit transform
            ChildCollider leaf;
            MTransform    queryFromWorld = Math.Inverse(new MTransform(input.Orientation, input.Position + input.Direction * hit.Fraction));

            GetHitLeaf(ref world, hit.RigidBodyIndex, hit.ColliderKey, queryFromWorld, out leaf, out MTransform queryFromTarget);
            DistanceQueries.Result result = new DistanceQueries.Result
            {
                PositionOnAinA = Math.Mul(queryFromWorld, hit.Position),
                NormalInA      = math.mul(queryFromWorld.Rotation, hit.SurfaceNormal),
                Distance       = 0.0f
            };

            // If the fraction is zero then the shapes should penetrate, otherwise they should have zero distance
            if (hit.Fraction == 0.0f)
            {
                // Do a distance query to verify initial penetration
                result.Distance = DistanceQueries.ConvexConvex(input.Collider, leaf.Collider, queryFromTarget).Distance;
                Assert.Less(result.Distance, tolerance, failureMessage + ": zero fraction with positive distance");
            }

            // Verify the distance at the hit transform
            ValidateDistanceResult(result, ref ((ConvexCollider *)input.Collider)->ConvexHull, ref ((ConvexCollider *)leaf.Collider)->ConvexHull, queryFromTarget, result.Distance, failureMessage);
        }
        public void WorldTest()
        {
            var world = new Physics.PhysicsWorld(10, 5, 10);

            Assert.IsTrue((world.NumDynamicBodies == 5) && (world.NumStaticBodies == 10) && (world.NumBodies == 15) && (world.NumJoints == 10));

            world.Reset(0, 0, 0);
            Assert.IsTrue((world.NumDynamicBodies == 0) && (world.NumStaticBodies == 0) && (world.NumBodies == 0) && (world.NumJoints == 0));

            world.Reset(5, 1, 7);
            Assert.IsTrue((world.NumDynamicBodies == 1) && (world.NumStaticBodies == 5) && (world.NumBodies == 6) && (world.NumJoints == 7));

            // clone world
            var worldClone = new Physics.PhysicsWorld(0, 0, 0);

            Assert.IsTrue((worldClone.NumDynamicBodies == 0) && (worldClone.NumStaticBodies == 0) && (worldClone.NumBodies == 0) && (worldClone.NumJoints == 0));

            worldClone.Dispose();
            worldClone = (Physics.PhysicsWorld)world.Clone();
            Assert.IsTrue((worldClone.NumDynamicBodies == 1) && (worldClone.NumStaticBodies == 5) && (worldClone.NumBodies == 6) && (worldClone.NumJoints == 7));

            // dispose cloned world
            worldClone.Dispose();
            world.Dispose();
        }
示例#4
0
 public void BuildBPEmptyWorldTest()
 {
     Physics.PhysicsWorld world = createTestWorld();
     world.CollisionWorld.Broadphase.Build(world.StaticBodies, world.DynamicBodies, world.MotionVelocities,
                                           world.CollisionWorld.CollisionTolerance, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
示例#5
0
        static unsafe void CheckRaycastHit(ref Physics.PhysicsWorld world, RaycastInput input, RaycastHit hit, string failureMessage)
        {
            // Fetch the leaf collider
            ChildCollider leaf;
            {
                Physics.RigidBody body = world.Bodies[hit.RigidBodyIndex];
                Collider.GetLeafCollider(body.Collider, body.WorldFromBody, hit.ColliderKey, out leaf);
            }

            // Check that the hit position matches the fraction
            float3 hitPosition = input.Ray.Origin + input.Ray.Direction * hit.Fraction;

            Assert.Less(math.length(hitPosition - hit.Position), tolerance, failureMessage + ": inconsistent fraction and position");

            // Query the hit position and check that it's on the surface of the shape
            PointDistanceInput pointInput = new PointDistanceInput
            {
                Position    = math.transform(math.inverse(leaf.TransformFromChild), hit.Position),
                MaxDistance = float.MaxValue
            };
            DistanceHit distanceHit;

            leaf.Collider->CalculateDistance(pointInput, out distanceHit);
            if (((ConvexCollider *)leaf.Collider)->ConvexHull.ConvexRadius > 0.0f)
            {
                // Convex raycast approximates radius, so it's possible that the hit position is not exactly on the shape, but must at least be outside
                Assert.Greater(distanceHit.Distance, -tolerance, failureMessage);
            }
            else
            {
                Assert.AreEqual(distanceHit.Distance, 0.0f, tolerance, failureMessage);
            }
        }
示例#6
0
        public void load(Physics.PhysicsWorld physics_world)
        {
            _player.controlAndWatch(_character_1, _camera_1);
            _player.getPhysical(physics_world);

            _scene.load(physics_world, _config.near_far.X);
            _scene.toggleFlashlight(_player.enable_flashlight);
        }
示例#7
0
 public void BuildBPOneDynamicBoxTest()
 {
     Physics.PhysicsWorld world = createTestWorld(0, 1);
     addDynamicBoxToWorld(world, 0, new Vector3(0, 0, 0), Quaternion.identity, new Vector3(10, 10, 10));
     world.CollisionWorld.Broadphase.Build(world.StaticBodies, world.DynamicBodies, world.MotionVelocities,
                                           world.CollisionWorld.CollisionTolerance, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
示例#8
0
        static unsafe void GetHitLeaf(ref Physics.PhysicsWorld world, int rigidBodyIndex, ColliderKey colliderKey, MTransform queryFromWorld, out ChildCollider leaf, out MTransform queryFromTarget)
        {
            Physics.RigidBody body = world.Bodies[rigidBodyIndex];
            Collider.GetLeafCollider(body.Collider, body.WorldFromBody, colliderKey, out leaf);
            MTransform worldFromLeaf = new MTransform(leaf.TransformFromChild);

            queryFromTarget = Math.Mul(queryFromWorld, worldFromLeaf);
        }
示例#9
0
 public void ScheduleBuildJobsOneStaticBoxTest()
 {
     Physics.PhysicsWorld world = createTestWorld(1);
     addStaticBoxToWorld(world, 0, new Vector3(0, 0, 0), Quaternion.identity, new Vector3(10, 0.1f, 10));
     Unity.Jobs.JobHandle handle = new Unity.Jobs.JobHandle();
     Unity.Jobs.JobHandle result = world.CollisionWorld.Broadphase.ScheduleBuildJobs(ref world, 1 / 60, 1, true, handle);
     result.Complete();
     Assert.IsTrue(result.IsCompleted);
     world.Dispose();
 }
示例#10
0
        public void WorldTest()
        {
            var world = new Physics.PhysicsWorld(1, 1, 0);

            Assert.IsTrue((world.NumStaticBodies == 1) && (world.NumDynamicBodies == 1) && (world.NumBodies == 2));

            // TODO: add test for dynamics

            world.Dispose();
        }
示例#11
0
        //Adds a dynamic box to the world
        static public unsafe void addDynamicBoxToWorld(Physics.PhysicsWorld world, int index, Vector3 pos, Quaternion orientation, Vector3 size)
        {
            Assert.IsTrue(index < world.NumDynamicBodies, "Dynamic body index is out of range in addDynamicBoxToWorld");
            Unity.Collections.NativeSlice <Physics.RigidBody> dynamicBodies = world.DynamicBodies;
            Physics.RigidBody rb = dynamicBodies[index];
            BlobAssetReference <Physics.Collider> collider = Unity.Physics.BoxCollider.Create(pos, orientation, size, .01f);

            rb.Collider          = (Collider *)collider.GetUnsafePtr();
            dynamicBodies[index] = rb;
        }
 public void UpdateWorldOneHundredDynamicBoxesTest()
 {
     Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld(0, 100);
     for (int i = 0; i < 100; ++i)
     {
         BroadPhaseTests.addDynamicBoxToWorld(world, i, new Vector3(11 * i, 0, 0), quaternion.identity, new Vector3(10, .1f, 10));
     }
     world.CollisionWorld.UpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
示例#13
0
 public void BuildBPOneHundredDynamicBoxesTest()
 {
     Physics.PhysicsWorld world = createTestWorld(0, 100);
     for (int i = 0; i < 100; ++i)
     {
         addDynamicBoxToWorld(world, i, new Vector3(i * 11, 0, 0), Quaternion.identity, new Vector3(10, 10, 10));
     }
     world.CollisionWorld.Broadphase.Build(world.StaticBodies, world.DynamicBodies, world.MotionVelocities,
                                           world.CollisionWorld.CollisionTolerance, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
 public void SheduleUpdateJobsEmptyWorldTest()
 {
     for (int numThreads = 0; numThreads <= 8; numThreads++)
     {
         Physics.PhysicsWorld world          = BroadPhaseTests.createTestWorld();
         Unity.Jobs.JobHandle handle         = new Unity.Jobs.JobHandle();
         Unity.Jobs.JobHandle worldJobHandle = world.CollisionWorld.ScheduleUpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up(), handle, numThreads);
         worldJobHandle.Complete();
         Assert.IsTrue(worldJobHandle.IsCompleted);
         world.Dispose();
     }
 }
示例#15
0
        // Does distance queries and checks some properties of the results:
        // - Closest hit returned from the all hits query has the same fraction as the hit returned from the closest hit query
        // - Any hit and closest hit queries return a hit if and only if the all hits query does
        // - Hit distance is the same as the support distance in the hit normal direction
        // - Fetching the shapes from any world query hit and querying them directly gives a matching result
        static unsafe void WorldCalculateDistanceTest(ref Physics.PhysicsWorld world, ColliderDistanceInput input, ref NativeList <DistanceHit> hits, string failureMessage)
        {
            // Do an all-hits query
            hits.Clear();
            world.CalculateDistance(input, ref hits);

            // Check each hit and find the closest
            float      closestDistance = float.MaxValue;
            MTransform queryFromWorld  = Math.Inverse(new MTransform(input.Transform));

            for (int iHit = 0; iHit < hits.Length; iHit++)
            {
                DistanceHit hit = hits[iHit];
                closestDistance = math.min(closestDistance, hit.Distance);

                // Fetch the leaf collider and query it directly
                ChildCollider leaf;
                MTransform    queryFromTarget;
                GetHitLeaf(ref world, hit.RigidBodyIndex, hit.ColliderKey, queryFromWorld, out leaf, out queryFromTarget);
                float referenceDistance = DistanceQueries.ConvexConvex(input.Collider, leaf.Collider, queryFromTarget).Distance;

                // Compare to the world query result
                DistanceQueries.Result result = DistanceResultFromDistanceHit(hit, queryFromWorld);
                ValidateDistanceResult(result, ref ((ConvexCollider *)input.Collider)->ConvexHull, ref ((ConvexCollider *)leaf.Collider)->ConvexHull, queryFromTarget, referenceDistance,
                                       failureMessage + ", hits[" + iHit + "]");
            }

            // Do a closest-hit query and check that the distance matches
            DistanceHit closestHit;
            bool        hasClosestHit = world.CalculateDistance(input, out closestHit);

            if (hits.Length == 0)
            {
                Assert.IsFalse(hasClosestHit, failureMessage + ", closestHit: no matching result in hits");
            }
            else
            {
                ChildCollider leaf;
                MTransform    queryFromTarget;
                GetHitLeaf(ref world, closestHit.RigidBodyIndex, closestHit.ColliderKey, queryFromWorld, out leaf, out queryFromTarget);

                DistanceQueries.Result result = DistanceResultFromDistanceHit(closestHit, queryFromWorld);
                ValidateDistanceResult(result, ref ((ConvexCollider *)input.Collider)->ConvexHull, ref ((ConvexCollider *)leaf.Collider)->ConvexHull, queryFromTarget, closestDistance,
                                       failureMessage + ", closestHit");
            }

            // Do an any-hit query and check that it is consistent with the others
            bool hasAnyHit = world.CalculateDistance(input);

            Assert.AreEqual(hasAnyHit, hasClosestHit, failureMessage + ": any hit result inconsistent with the others");

            // TODO - this test can't catch false misses.  We could do brute-force broadphase / midphase search to cover those.
        }
 public void SheduleUpdateJobsOneHundredStaticBoxesTest()
 {
     Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld(100);
     for (int i = 0; i < 100; ++i)
     {
         BroadPhaseTests.addStaticBoxToWorld(world, i, new Vector3(11 * i, 0, 0), quaternion.identity, new Vector3(10, .1f, 10));
     }
     Unity.Jobs.JobHandle handle         = new Unity.Jobs.JobHandle();
     Unity.Jobs.JobHandle worldJobHandle = world.CollisionWorld.ScheduleUpdateDynamicLayer(ref world, 1 / 60, 1, handle);
     worldJobHandle.Complete();
     Assert.IsTrue(worldJobHandle.IsCompleted);
     world.Dispose();
 }
 public void SheduleUpdateJobsOneDynamicBoxTest()
 {
     for (int numThreads = 0; numThreads <= 8; numThreads++)
     {
         Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld(0, 1);
         BroadPhaseTests.addDynamicBoxToWorld(world, 0, Vector3.zero, quaternion.identity, new Vector3(10, .1f, 10));
         Unity.Jobs.JobHandle handle         = new Unity.Jobs.JobHandle();
         Unity.Jobs.JobHandle worldJobHandle = world.CollisionWorld.ScheduleUpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up(), handle, numThreads);
         worldJobHandle.Complete();
         Assert.IsTrue(worldJobHandle.IsCompleted);
         world.Dispose();
     }
 }
示例#18
0
 public void ScheduleBuildJobsEmptyWorldTest()
 {
     for (int numThreads = 0; numThreads <= 8; numThreads++)
     {
         Physics.PhysicsWorld world = createTestWorld();
         var handle          = new JobHandle();
         var buildStaticTree = new NativeArray <int>(1, Allocator.TempJob);
         buildStaticTree[0] = 1;
         JobHandle result = world.CollisionWorld.Broadphase.ScheduleBuildJobs(ref world, 1 / 60, -9.81f * math.up(), buildStaticTree, handle, numThreads);
         result.Complete();
         world.Dispose();
         buildStaticTree.Dispose();
     }
 }
示例#19
0
        internal World(bool makeCurrent = false, bool hasPhysics = true)
        {
            entities  = new HashSet <Entity>();
            NativePtr = CreateWorld_Native(makeCurrent, hasPhysics);

            if (hasPhysics)
            {
                PhysicsWorld           = new Physics.PhysicsWorld();
                PhysicsWorld.NativePtr = GetPhysicsWorld_Native(NativePtr);
            }
            else
            {
                PhysicsWorld = null;
            }
        }
示例#20
0
 public void SheduleUpdateJobsTenStaticBoxesTest()
 {
     for (int numThreads = 0; numThreads <= 1; numThreads++)
     {
         Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld(10);
         for (int i = 0; i < 10; ++i)
         {
             BroadPhaseTests.addStaticBoxToWorld(world, i, new Vector3(11 * i, 0, 0), quaternion.identity, new Vector3(10, .1f, 10));
         }
         Unity.Jobs.JobHandle handle         = new Unity.Jobs.JobHandle();
         Unity.Jobs.JobHandle worldJobHandle = world.CollisionWorld.ScheduleUpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up(), handle, numThreads == 1);
         worldJobHandle.Complete();
         Assert.IsTrue(worldJobHandle.IsCompleted);
         world.Dispose();
     }
 }
        //Adds a static box to the world
        static public unsafe void addStaticBoxToWorld(Physics.PhysicsWorld world, int index, Vector3 pos, Quaternion orientation, Vector3 size)
        {
            Assert.IsTrue(index < world.NumStaticBodies, "Static body index is out of range in addStaticBoxToWorld");
            Unity.Collections.NativeSlice <Physics.RigidBody> staticBodies = world.StaticBodies;
            Physics.RigidBody rb = staticBodies[index];
            BlobAssetReference <Physics.Collider> collider = Unity.Physics.BoxCollider.Create(new BoxGeometry
            {
                Center      = pos,
                Orientation = orientation,
                Size        = size,
                BevelRadius = 0.01f
            });

            rb.Collider         = (Collider *)collider.GetUnsafePtr();
            staticBodies[index] = rb;
        }
示例#22
0
 public void ScheduleBuildJobsOneStaticBoxTest()
 {
     for (int numThreads = 0; numThreads <= 8; numThreads++)
     {
         Physics.PhysicsWorld world = createTestWorld(1);
         addStaticBoxToWorld(world, 0, new Vector3(0, 0, 0), Quaternion.identity, new Vector3(10, 0.1f, 10));
         var handle          = new JobHandle();
         var buildStaticTree = new NativeArray <int>(1, Allocator.TempJob);
         buildStaticTree[0] = 1;
         JobHandle result = world.CollisionWorld.Broadphase.ScheduleBuildJobs(ref world, 1 / 60, -9.81f * math.up(), buildStaticTree, handle, numThreads);
         result.Complete();
         Assert.IsTrue(result.IsCompleted);
         world.Dispose();
         buildStaticTree.Dispose();
     }
 }
        public void ScheduleBuildJobsOneStaticBoxTest()
        {
            Physics.PhysicsWorld world = createTestWorld(1);
            addStaticBoxToWorld(world, 0, new Vector3(0, 0, 0), Quaternion.identity, new Vector3(10, 0.1f, 10));
            JobHandle             handle = new JobHandle();
            StaticLayerChangeInfo staticLayerChangeInfo = new StaticLayerChangeInfo();

            staticLayerChangeInfo.Init(Allocator.TempJob);
            staticLayerChangeInfo.NumStaticBodies         = 1;
            staticLayerChangeInfo.HaveStaticBodiesChanged = 1;
            JobHandle result = world.CollisionWorld.Broadphase.ScheduleBuildJobs(ref world, 1 / 60, -9.81f * math.up(), 1, ref staticLayerChangeInfo, handle);

            result.Complete();
            Assert.IsTrue(result.IsCompleted);
            world.Dispose();
            staticLayerChangeInfo.Deallocate();
        }
示例#24
0
        //Adds a dynamic box to the world
        static public unsafe void addDynamicBoxToWorld(Physics.PhysicsWorld world, int index, Vector3 pos, Quaternion orientation, Vector3 size)
        {
            Assert.IsTrue(index < world.NumDynamicBodies, "Dynamic body index is out of range in addDynamicBoxToWorld");
            NativeArray <Physics.RigidBody> dynamicBodies = world.DynamicBodies;

            Physics.RigidBody             rb       = dynamicBodies[index];
            BlobAssetReference <Collider> collider = BoxCollider.Create(new BoxGeometry
            {
                Center      = pos,
                Orientation = orientation,
                Size        = size,
                BevelRadius = 0.01f
            });

            rb.Collider          = collider;
            dynamicBodies[index] = rb;
        }
示例#25
0
        // Does collider casts and checks some properties of the results:
        // - Closest hit returned from the all hits query has the same fraction as the hit returned from the closest hit query
        // - Any hit and closest hit queries return a hit if and only if the all hits query does
        // - Distance between the shapes at the hit fraction is zero
        static unsafe void WorldColliderCastTest(ref Physics.PhysicsWorld world, ColliderCastInput input, ref NativeList <ColliderCastHit> hits, string failureMessage)
        {
            // Do an all-hits query
            hits.Clear();
            world.CastCollider(input, ref hits);

            // Check each hit and find the earliest
            float          minFraction    = float.MaxValue;
            RigidTransform worldFromQuery = new RigidTransform(input.Orientation, input.Position);

            for (int iHit = 0; iHit < hits.Length; iHit++)
            {
                ColliderCastHit hit = hits[iHit];
                minFraction = math.min(minFraction, hit.Fraction);
                CheckColliderCastHit(ref world, input, hit, failureMessage + ", hits[" + iHit + "]");
            }

            // Do a closest-hit query and check that the fraction matches
            ColliderCastHit closestHit;
            bool            hasClosestHit = world.CastCollider(input, out closestHit);

            if (hits.Length == 0)
            {
                Assert.IsFalse(hasClosestHit, failureMessage + ", closestHit: no matching result in hits");
            }
            else
            {
                Assert.AreEqual(closestHit.Fraction, minFraction, tolerance * math.length(input.Direction), failureMessage + ", closestHit: fraction does not match");
                CheckColliderCastHit(ref world, input, closestHit, failureMessage + ", closestHit");
            }

            // Do an any-hit query and check that it is consistent with the others
            bool hasAnyHit = world.CastCollider(input);

            Assert.AreEqual(hasAnyHit, hasClosestHit, failureMessage + ": any hit result inconsistent with the others");

            // TODO - this test can't catch false misses.  We could do brute-force broadphase / midphase search to cover those.
        }
 public void UpdateEmptyWorldTest()
 {
     Physics.PhysicsWorld world = BroadPhaseTests.createTestWorld();
     world.CollisionWorld.UpdateDynamicTree(ref world, 1 / 60, -9.81f * math.up());
     world.Dispose();
 }
示例#27
0
        public unsafe void WorldQueryTest()
        {
            const uint seed     = 0x12345678;
            uint       dbgWorld = 0; // set dbgWorld, dbgTest to the seed reported from a failure message to repeat the failing case alone
            uint       dbgTest  = 0;

            int numWorlds = 200;
            int numTests  = 5000;

            if (dbgWorld > 0)
            {
                numWorlds = 1;
                numTests  = 1;
            }

            Random rnd = new Random(seed);
            NativeList <DistanceHit>     distanceHits     = new NativeList <DistanceHit>(Allocator.Temp);
            NativeList <ColliderCastHit> colliderCastHits = new NativeList <ColliderCastHit>(Allocator.Temp);
            NativeList <RaycastHit>      raycastHits      = new NativeList <RaycastHit>(Allocator.Temp);

            for (int iWorld = 0; iWorld < numWorlds; iWorld++)
            {
                // Save state to repro this query without doing everything that came before it
                if (dbgWorld > 0)
                {
                    rnd.state = dbgWorld;
                }
                uint worldState            = rnd.state;
                Physics.PhysicsWorld world = TestUtils.GenerateRandomWorld(ref rnd, rnd.NextInt(1, 20), 10.0f);

                for (int iTest = 0; iTest < (numTests / numWorlds); iTest++)
                {
                    if (dbgTest > 0)
                    {
                        rnd.state = dbgTest;
                    }
                    uint   testState      = rnd.state;
                    string failureMessage = iWorld + ", " + iTest + " (" + worldState.ToString() + ", " + testState.ToString() + ")";

                    // Generate common random query inputs
                    Collider *     collider  = (Collider *)TestUtils.GenerateRandomConvex(ref rnd).GetUnsafePtr();
                    RigidTransform transform = new RigidTransform
                    {
                        pos = rnd.NextFloat3(-10.0f, 10.0f),
                        rot = (rnd.NextInt(10) > 0) ? rnd.NextQuaternionRotation() : quaternion.identity,
                    };
                    Ray ray = new Ray(transform.pos, rnd.NextFloat3(-5.0f, 5.0f));

                    // Distance test
                    {
                        ColliderDistanceInput input = new ColliderDistanceInput
                        {
                            Collider    = collider,
                            Transform   = transform,
                            MaxDistance = (rnd.NextInt(4) > 0) ? rnd.NextFloat(5.0f) : 0.0f
                        };
                        WorldCalculateDistanceTest(ref world, input, ref distanceHits, "WorldQueryTest failed CalculateDistance " + failureMessage);
                    }

                    // Collider cast test
                    {
                        ColliderCastInput input = new ColliderCastInput
                        {
                            Collider    = collider,
                            Position    = transform.pos,
                            Orientation = transform.rot,
                            Direction   = ray.Direction
                        };
                        WorldColliderCastTest(ref world, input, ref colliderCastHits, "WorldQueryTest failed ColliderCast " + failureMessage);
                    }

                    // Ray cast test
                    {
                        RaycastInput input = new RaycastInput
                        {
                            Ray    = ray,
                            Filter = CollisionFilter.Default
                        };
                        WorldRaycastTest(ref world, input, ref raycastHits, "WorldQueryTest failed Raycast " + failureMessage);
                    }
                }

                world.Dispose(); // TODO leaking memory if the test fails
            }

            distanceHits.Dispose(); // TODO leaking memory if the test fails
            colliderCastHits.Dispose();
            raycastHits.Dispose();
        }
示例#28
0
        public unsafe void ManifoldQueryTest()
        {
            const uint seed      = 0x98765432;
            Random     rnd       = new Random(seed);
            int        numWorlds = 1000;

            uint dbgWorld = 0;

            if (dbgWorld > 0)
            {
                numWorlds = 1;
            }

            for (int iWorld = 0; iWorld < numWorlds; iWorld++)
            {
                // Save state to repro this query without doing everything that came before it
                if (dbgWorld > 0)
                {
                    rnd.state = dbgWorld;
                }
                uint worldState            = rnd.state;
                Physics.PhysicsWorld world = TestUtils.GenerateRandomWorld(ref rnd, rnd.NextInt(1, 20), 3.0f);

                // Manifold test
                // TODO would be nice if we could change the world collision tolerance
                for (int iBodyA = 0; iBodyA < world.NumBodies; iBodyA++)
                {
                    for (int iBodyB = iBodyA + 1; iBodyB < world.NumBodies; iBodyB++)
                    {
                        Physics.RigidBody bodyA = world.Bodies[iBodyA];
                        Physics.RigidBody bodyB = world.Bodies[iBodyB];
                        if (bodyA.Collider->Type == ColliderType.Mesh && bodyB.Collider->Type == ColliderType.Mesh)
                        {
                            continue; // TODO - no mesh-mesh manifold support yet
                        }

                        // Build manifolds
                        BlockStream        contacts      = new BlockStream(1, 0, Allocator.Temp);
                        BlockStream.Writer contactWriter = contacts;
                        contactWriter.BeginForEachIndex(0);
                        ManifoldQueries.BodyBody(ref world, new BodyIndexPair {
                            BodyAIndex = iBodyA, BodyBIndex = iBodyB
                        }, 1.0f, ref contactWriter);
                        contactWriter.EndForEachIndex();

                        // Read each manifold
                        BlockStream.Reader contactReader = contacts;
                        contactReader.BeginForEachIndex(0);
                        int manifoldIndex = 0;
                        while (contactReader.RemainingItemCount > 0)
                        {
                            string failureMessage = iWorld + " (" + worldState + ") " + iBodyA + " vs " + iBodyB + " #" + manifoldIndex;
                            manifoldIndex++;

                            // Read the manifold header
                            ContactHeader header = contactReader.Read <ContactHeader>();
                            ConvexConvexManifoldQueries.Manifold manifold = new ConvexConvexManifoldQueries.Manifold();
                            manifold.NumContacts = header.NumContacts;
                            manifold.Normal      = header.Normal;

                            // Get the leaf shapes
                            ChildCollider leafA, leafB;
                            {
                                Collider.GetLeafCollider(bodyA.Collider, bodyA.WorldFromBody, header.ColliderKeys.ColliderKeyA, out leafA);
                                Collider.GetLeafCollider(bodyB.Collider, bodyB.WorldFromBody, header.ColliderKeys.ColliderKeyB, out leafB);
                            }

                            // Read each contact point
                            int minIndex = 0;
                            for (int iContact = 0; iContact < header.NumContacts; iContact++)
                            {
                                // Read the contact and find the closest
                                ContactPoint contact = contactReader.Read <ContactPoint>();
                                manifold[iContact] = contact;
                                if (contact.Distance < manifold[minIndex].Distance)
                                {
                                    minIndex = iContact;
                                }

                                // Check that the contact point is on or inside the shape
                                CheckPointOnSurface(ref leafA, contact.Position + manifold.Normal * contact.Distance, failureMessage + " contact " + iContact + " leaf A");
                                CheckPointOnSurface(ref leafB, contact.Position, failureMessage + " contact " + iContact + " leaf B");
                            }

                            // Check the closest point
                            {
                                ContactPoint           closestPoint = manifold[minIndex];
                                RigidTransform         aFromWorld   = math.inverse(leafA.TransformFromChild);
                                DistanceQueries.Result result       = new DistanceQueries.Result
                                {
                                    PositionOnAinA = math.transform(aFromWorld, closestPoint.Position + manifold.Normal * closestPoint.Distance),
                                    NormalInA      = math.mul(aFromWorld.rot, manifold.Normal),
                                    Distance       = closestPoint.Distance
                                };

                                MTransform aFromB            = new MTransform(math.mul(aFromWorld, leafB.TransformFromChild));
                                float      referenceDistance = DistanceQueries.ConvexConvex(leafA.Collider, leafB.Collider, aFromB).Distance;
                                ValidateDistanceResult(result, ref ((ConvexCollider *)leafA.Collider)->ConvexHull, ref ((ConvexCollider *)leafB.Collider)->ConvexHull, aFromB, referenceDistance, failureMessage + " closest point");
                            }

                            // Check that the manifold is flat
                            CheckManifoldFlat(ref manifold, manifold.Normal, failureMessage + ": non-flat A");
                            CheckManifoldFlat(ref manifold, float3.zero, failureMessage + ": non-flat B");
                        }

                        contacts.Dispose();
                    }
                }

                world.Dispose(); // TODO leaking memory if the test fails
            }
        }
        public void CalculateDetailsTest()
        {
            // Simple collision event with straight normal and 3 contact points
            LowLevel.CollisionEvent lowLevelEvent = new LowLevel.CollisionEvent();
            lowLevelEvent.BodyIndices.BodyAIndex    = 0;
            lowLevelEvent.BodyIndices.BodyBIndex    = 1;
            lowLevelEvent.ColliderKeys.ColliderKeyA = ColliderKey.Empty;
            lowLevelEvent.ColliderKeys.ColliderKeyB = ColliderKey.Empty;
            lowLevelEvent.Normal = new float3(0.0f, -1.00000f, 0.0f);
            lowLevelEvent.NumNarrowPhaseContactPoints = 3;
            lowLevelEvent.SolverImpulse = 1.0f;

            // Wrapping collision event
            CollisionEvent collisionEvent = new CollisionEvent();

            collisionEvent.EventData = lowLevelEvent;
            collisionEvent.TimeStep  = 1.0f / 60.0f;

            // Input velocity is obviously separating, but high angular velocity still caused an event
            collisionEvent.InputVelocityA = new Velocity
            {
                Angular = new float3(-0.00064f, 11.17604f, 0.02133f),
                Linear  = new float3(-3.81205f, -0.56607f, 9.14945f)
            };
            collisionEvent.InputVelocityB = new Velocity
            {
                Angular = new float3(0.00000f, 0.00000f, 0.00000f),
                Linear  = new float3(0.00000f, 0.00000f, 0.00000f)
            };

            // Initialize 3 contact points
            collisionEvent.NarrowPhaseContactPoints = new NativeArray <ContactPoint>(3, Allocator.Temp)
            {
                [0] = new ContactPoint
                {
                    Distance = 0.177905f,
                    Position = new float3(-22.744950f, 2.585318f, -50.108990f)
                },
                [1] = new ContactPoint
                {
                    Distance = 0.276652f,
                    Position = new float3(-20.731140f, 2.486506f, -50.322240f)
                },
                [2] = new ContactPoint
                {
                    Distance = 0.278534f,
                    Position = new float3(-20.766140f, 2.484623f, -50.652630f)
                }
            };

            // Allocate a simple world of 1 dynamic and 1 static body
            var simpleWorld      = new Physics.PhysicsWorld(1, 1, 0);
            var motionVelocities = simpleWorld.MotionVelocities;
            var motionDatas      = simpleWorld.MotionDatas;

            motionDatas[0] = new MotionData
            {
                LinearDamping   = 0.0f,
                AngularDamping  = 0.0f,
                GravityFactor   = 1.0f,
                BodyFromMotion  = new RigidTransform(new quaternion(0.0f, 0.0f, 0.0f, 1.0f), new float3(0.0f, 0.0f, 0.0f)),
                WorldFromMotion = new RigidTransform(new quaternion(0.09212853f, 0.1400256f, -0.006776567f, -0.9858292f), new float3(-22.17587f, 0.5172966f, -52.24425f))
            };
            motionVelocities[0] = new MotionVelocity
            {
                LinearVelocity         = new float3(-3.81221f, -1.37538f, -15.41893f),
                AngularVelocity        = new float3(-7.30913f, -4.78899f, 1.14168f),
                InverseInertiaAndMass  = new float4(0.00045f, 0.00045f, 0.00045f, 0.00018f),
                AngularExpansionFactor = 2.05061f
            };

            // Calculate the collision event details and make sure 1 contact point is returned
            var details = collisionEvent.CalculateDetails(ref simpleWorld);

            Assert.AreEqual(details.EstimatedContactPointPositions.Length, 1);

            // Dispose the world data
            simpleWorld.Dispose();
        }