Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

ECS Physics Collider Queries Return Wrong Outputs

Discussion in 'Physics Previews' started by maszkutronstudios, Apr 22, 2020.

  1. maszkutronstudios

    maszkutronstudios

    Joined:
    Oct 2, 2018
    Posts:
    1
    Hi,
    I have been testing Unity.Physics for a day and came upon very frustrating issue.
    When I make CollisionWorld Distance queries everything seems to work fine, not as performant as I'd expect but results are correct.
    The problems start when i try to make Collider-To-Collider or Collider-To-Point distance queries, they either return unexpected bodies or fail completely.

    My questions are:
    - why are collider-to-collider and collider-to-point distance queries in given code returning wrong outputs?
    - is there something fundamental about collider-to-collider / collider-to-point queries that I'm missing?
    - why world queries require to provide explicit RigidTransform for ColliderDistanceInput and c2c/c2p queries dont

    Here is some code that I'm using for testing, this is very simple system that should store number of bodies in given range around entity, there is no code to exclude entity itself but this is only for testing purposes.
    Full project: https://github.com/Maszkutron/ecs_physics_query_test

    Code (CSharp):
    1. using Unity.Jobs;
    2. using Unity.Entities;
    3. using Unity.Transforms;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6. using Unity.Collections;
    7. using Unity.Mathematics;
    8. using UnityEngine;
    9. using UnityEngine.Rendering;
    10.  
    11. [UpdateInGroup(typeof(SimulationSystemGroup))]
    12. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    13. public unsafe class EntitiesAroundCountSystem : JobComponentSystem
    14. {
    15.     private EndFramePhysicsSystem finalPhysicsSystem = null;
    16.     private BuildPhysicsWorld buildPhysicsSystem = null;
    17.     private StepPhysicsWorld stepPhysicsSystem = null;
    18.     private EndSimulationEntityCommandBufferSystem barrier = null;
    19.  
    20.     protected override void OnCreate()
    21.     {
    22.         base.OnCreate();
    23.         finalPhysicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<EndFramePhysicsSystem>();
    24.         buildPhysicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<BuildPhysicsWorld>();
    25.         stepPhysicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<StepPhysicsWorld>();
    26.         barrier = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    27.     }
    28.  
    29.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    30.     {
    31.         // overall:
    32.         //  - world queries always return expected values but are slower than expected
    33.         //  - collider-to-collider/collider-to-point either do not return expected values or always return false
    34.  
    35.         // broken, see method body for more info
    36.         return UpdateWithCollider2ColliderQuery(inputDeps);
    37.  
    38.         // works but is much less performant than I anticipated
    39.         //return UpdateWithWorldQuery(inputDeps);
    40.     }
    41.  
    42.     private JobHandle UpdateWithCollider2ColliderQuery(JobHandle inputDeps)
    43.     {
    44.         var physicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    45.         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
    46.  
    47.         // this is probably redundant but just for safety lets include all previous physics systems
    48.         inputDeps = JobHandle.CombineDependencies(inputDeps, finalPhysicsSystem.FinalJobHandle);
    49.         inputDeps = JobHandle.CombineDependencies(inputDeps, buildPhysicsSystem.FinalJobHandle);
    50.         inputDeps = JobHandle.CombineDependencies(inputDeps, stepPhysicsSystem.FinalJobHandle);
    51.  
    52.         inputDeps = Entities.ForEach((Entity entity, ref EntitiesAroundCountCmp around, in Translation translation, in PhysicsCollider collider) =>
    53.         {
    54.             around.count = 0;
    55.  
    56.             float3 offset = new float3(around.range, 1, around.range);
    57.             OverlapAabbInput input = new OverlapAabbInput()
    58.             {
    59.                 Aabb = new Aabb()
    60.                 {
    61.                     Min = translation.Value - offset,
    62.                     Max = translation.Value + offset,
    63.                 },
    64.                 Filter = CollisionFilter.Default
    65.             };
    66.  
    67.             NativeList<int> bodyIndices = new NativeList<int>(Allocator.Temp);
    68.  
    69.             // OverlapAabb is really nice and fast, all expected colliders are returned
    70.             if (collisionWorld.OverlapAabb(input, ref bodyIndices))
    71.             {
    72.                 for (int i = 0; i < bodyIndices.Length; ++i)
    73.                 {
    74.                     var body = collisionWorld.Bodies[bodyIndices[i]];
    75.  
    76.                     // why this returns true for colliders in AABB instead of actual distance?
    77.                     var colliderDistanceInput = new ColliderDistanceInput()
    78.                     {
    79.                         Collider = collider.ColliderPtr,
    80.                         Transform = RigidTransform.identity,
    81.                         MaxDistance = around.range
    82.                     };
    83.  
    84.                     // why this always returns false?
    85.                     var pointDistanceInput = new PointDistanceInput()
    86.                     {
    87.                         Filter = CollisionFilter.Default,
    88.                         MaxDistance = around.range,
    89.                         Position = translation.Value
    90.                     };
    91.  
    92.                     if (body.CalculateDistance(pointDistanceInput))
    93.                     {
    94.                         ++around.count;
    95.                     }
    96.                 }
    97.             }
    98.             bodyIndices.Dispose();
    99.         })
    100.         .WithBurst()
    101.         .Schedule(inputDeps);
    102.  
    103.         barrier.AddJobHandleForProducer(inputDeps);
    104.  
    105.         return inputDeps;
    106.     }
    107.  
    108.     private JobHandle UpdateWithWorldQuery(JobHandle inputDeps)
    109.     {
    110.         var physicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    111.         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
    112.  
    113.         // this is probably redundant but just for safety lets include all previous physics systems
    114.         inputDeps = JobHandle.CombineDependencies(inputDeps, finalPhysicsSystem.FinalJobHandle);
    115.         inputDeps = JobHandle.CombineDependencies(inputDeps, buildPhysicsSystem.FinalJobHandle);
    116.         inputDeps = JobHandle.CombineDependencies(inputDeps, stepPhysicsSystem.FinalJobHandle);
    117.  
    118.         inputDeps = Entities.ForEach((Entity entity, ref EntitiesAroundCountCmp around, in Translation translation, in PhysicsCollider collider) =>
    119.         {
    120.             NativeList<DistanceHit> distanceHits = new NativeList<DistanceHit>(Allocator.Temp);
    121.  
    122.             // why in CollisionWorld queries I need to explicitly create RigidTransform
    123.             // but then in ICollider queries I only identity works
    124.             var colliderDistanceInput = new ColliderDistanceInput()
    125.             {
    126.                 Collider = collider.ColliderPtr,
    127.                 MaxDistance = around.range,
    128.                 Transform = new RigidTransform(quaternion.identity, translation.Value)
    129.             };
    130.  
    131.             // point query for performance testing
    132.             var pointDistanceInput = new PointDistanceInput()
    133.             {
    134.                 Filter = CollisionFilter.Default,
    135.                 MaxDistance = around.range,
    136.                 Position = translation.Value
    137.             };
    138.  
    139.             if (collisionWorld.CalculateDistance(colliderDistanceInput, ref distanceHits))
    140.             {
    141.                 around.count = distanceHits.Length;
    142.             }
    143.             else around.count = 0;
    144.  
    145.             distanceHits.Dispose();
    146.         })
    147.         .WithBurst()
    148.         .Schedule(inputDeps);
    149.  
    150.         barrier.AddJobHandleForProducer(inputDeps);
    151.  
    152.         return inputDeps;
    153.     }
    154. }
     
  2. Rory_Havok

    Rory_Havok

    Joined:
    Jun 25, 2018
    Posts:
    70
    Hi,
    Apologies. There is currently a bug where body queries (like body.CalculateDistance()) are actually performed in collider space when they should be performed in world space, as you had expected. This explains your observations:

    "why this returns true for colliders in AABB instead of actual distance?"
    Because the 2 colliders overlap in their local space.

    "why this always returns false?"
    Because the Position in this case is not near the local origin of the target.

    "why in CollisionWorld queries I need to explicitly create RigidTransform"
    This is expected behavior.

    You can workaround the bug by transforming the spaces of the input and output yourself. If you step into the world query implementation you will see it doing the same thing for each body that it hits.