Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug Bug with CollisionWorld.CastRay using NativeList in DOTS 1.0.o-pre.65

Discussion in 'Physics for ECS' started by oreo_hbi, Mar 31, 2023.

  1. oreo_hbi

    oreo_hbi

    Joined:
    Apr 20, 2022
    Posts:
    4
    Update: Ran more tests as detailed in the posts below. Seems to be an issue when using large meshes. My test mesh reports 233145 vertices, which gives incorrect results with its index format set to 32bit, but work at 16bit(unity breaks up the mesh at 16bit though). In the mesh's import settings, I had "Normals" set to "Import", changing that to "Calculate" lowers my vertex count to 54082(though the number of indices stays the same), and everything starts working again. Issue does not happen when mesh has less than 65536 vertices, regardless of the selected index format.
    --

    Hi, I have a project which I started using DOTS 0.51, am upgrading it to 1.0 and I'm pretty sure that CastRay is broken(or I'm somehow doing something wrong, in which case I'm open to suggestions!). My project does a raycast through a mesh with a physics shape, and needs to determine every contact point. When calling CastRay with DOTS 0.51 using a native list, it acts as expected, a hit is detected wherever the ray enters and exits the mesh. But using the same function in DOTS 1.0, it reports many extra, incorrect collisions, and sometimes NONE of the collisions are correct.

    I set up test projects using Unity 2021.3.16f1(initial version I was using), 2022.2.9, and 2022.2.11. The only differences in what I'm doing is how the collision world is accessed

    This is my test script
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Mathematics;
    5. using Unity.Physics;
    6. using UnityEngine;
    7. using UnityEngine.InputSystem;
    8. using ECSWorld = Unity.Entities.World;
    9. using Material = UnityEngine.Material;
    10. using RaycastHit = Unity.Physics.RaycastHit;
    11.  
    12.  
    13.  
    14. public class raycast : MonoBehaviour {
    15.     private Mouse                  mouse;
    16.     private Camera                 _camera;
    17.     private List<List<GameObject>> dbgCubes;
    18.     public  Material               dbgMat;
    19.     public  float                  dbgLength = 20f;
    20.  
    21.  
    22.     // Start is called before the first frame update
    23.     void Start() {
    24.         dbgCubes = new List<List<GameObject>>();
    25.         mouse    = Mouse.current;
    26.         _camera  = Camera.main;
    27.     }
    28.  
    29.     // Update is called once per frame
    30.     void Update() {
    31.             GetWorldPos(mouse.position.value);
    32.     }
    33.  
    34.     NativeList<RaycastHit> cast(float3 RayFrom, float3 RayTo) {
    35.         EntityQueryBuilder builder        = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
    36.         EntityQuery        singletonQuery = ECSWorld.DefaultGameObjectInjectionWorld.EntityManager.CreateEntityQuery(builder);
    37.         var                collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
    38.         singletonQuery.Dispose();
    39.  
    40.         RaycastInput input = new RaycastInput() {
    41.                                                     Start = RayFrom,
    42.                                                     End   = RayTo,
    43.                                                     Filter = new CollisionFilter() {
    44.                                                                                        BelongsTo    = ~0u,
    45.                                                                                        CollidesWith = ~0u,
    46.                                                                                        GroupIndex   = 0
    47.                                                }};
    48.         var hitList = new NativeList<RaycastHit>(Allocator.Temp);
    49.         bool haveHit = collisionWorld.CastRay(input, ref hitList);
    50.  
    51.         Debug.DrawLine(RayFrom, RayTo, haveHit ? Color.green : Color.red, dbgLength);
    52.  
    53.  
    54.         if(haveHit) {
    55.             hitList.Sort(new HitSort());
    56.             var dbgs = new List<GameObject>();
    57.             var grp  = new GameObject("group "+dbgCubes.Count+": length "+hitList.Length);
    58.             Destroy(grp, dbgLength);
    59.             foreach (var raycastHit in hitList) {
    60.                 var nm  = "dbg " + dbgs.Count;
    61.                 var dbg = GameObject.CreatePrimitive(hitList.Length > 1 ? PrimitiveType.Sphere: PrimitiveType.Cube);
    62.                 dbg.transform.parent     = grp.transform;
    63.                 dbg.name                 = nm;
    64.                 dbg.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
    65.                 var mr = dbg.GetComponent<MeshRenderer>();
    66.                 mr.material = dbgMat ? dbgMat : Resources.Load<Material>("debug cube");
    67.                 dbgs.Add(dbg);
    68.  
    69.                 dbg.transform.position = raycastHit.Position;
    70.             }
    71.             dbgCubes.Add(dbgs);
    72.         }
    73.  
    74.         return hitList;
    75.     }
    76.  
    77.     public NativeList<RaycastHit> GetWorldPos(Vector2 mousePos2 = new Vector2(), bool dbg = false) {
    78.         Vector3 mousePos3 = mousePos2;
    79.         mousePos3.z = _camera.nearClipPlane;
    80.         var ray  = _camera.ScreenPointToRay(mousePos3);
    81.         var dest = ray.GetPoint(10000);
    82.         var     hits = cast(ray.origin, dest);
    83.         return hits;
    84.     }
    85.  
    86.     struct HitSort : IComparer<RaycastHit> {
    87.         public int Compare(RaycastHit a, RaycastHit b) {
    88.             if (a.Fraction > b.Fraction) return 1;
    89.             if (a.Fraction < b.Fraction) return -1;
    90.             return 0;
    91.         }
    92.     }
    93. }
    I use the exact same code for both 0.51 and 1.0, other than how the CollisionWorld is accessed

    0.51
    Code (CSharp):
    1.         var physicsWorldSystem = ECSWorld.DefaultGameObjectInjectionWorld
    2.                                     .GetExistingSystem<Unity.Physics.Systems.BuildPhysicsWorld>();
    3.         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
    4.  
    1.0
    Code (CSharp):
    1.         EntityQueryBuilder builder        = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>();
    2.         EntityQuery        singletonQuery = ECSWorld.DefaultGameObjectInjectionWorld.EntityManager.CreateEntityQuery(builder);
    3.         var                collisionWorld = singletonQuery.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
    4.         singletonQuery.Dispose();
    5.  
    I've included 2 packages for testing, can test using a unity project in one of the above mentioned editor versions, with the following packages installed
    - Entities
    - Entities Graphics/Hybrid Renderer
    - Unity Physics
    - Input System
    - HDRP

    OR you can download the full projects from here
    https://drive.google.com/drive/folders/11gcwa-ey8KDtFkMv3Cc6k6pyRXp0i0Eg?usp=sharing

    To use them, have the scene view and game view open at the same time, and run the project. A ray will cast from the camera to where the mouse is. As there are successful hits, GO's will be added to the hierarchy. You'll see groups with a number next to them representing the number of hits for a particular ray, and cubes(for single hits) or spheres(for multi-hits) as children of the group GO placed where the hit happened. The raycast script attached to the camera will let you change how long the debug rays and GO's stay in the scene.



    Looking forward to a response, kinda hoping I'm just screwing something up ^_^U


    Here's a screenshot showing expected results
    upload_2023-3-31_12-10-52.png

    And one with the results from DOTS 1.0
    upload_2023-3-31_12-16-19.png

    Note that in the second screenshot, both "hits" in group 2 are below the mesh, the hits in group 3 are litterally in the exact same position, and group 4 has both doubled up hits, and hits below the mesh.
     

    Attached Files:

    Last edited: Apr 3, 2023
  2. oreo_hbi

    oreo_hbi

    Joined:
    Apr 20, 2022
    Posts:
    4
    Doing more tests in my main project, I attempted to change from using the NativeArray version of CastRay to the single RaycastHit version, and I'm still getting incorrect results. So I added a cube to the scene, gave it a physics shape, and had no issues with the cube. I tried with both a box shape and a mesh shape for the cube, both worked with no issue. But as you might be able to see from the attached images, the hits for the terrain are still incorrect.
    upload_2023-3-31_13-6-4.png
    upload_2023-3-31_13-9-39.png
     
    daniel-holz likes this.
  3. oreo_hbi

    oreo_hbi

    Joined:
    Apr 20, 2022
    Posts:
    4
    Started testing with other meshes and wasn't able to immediately get the same problems, so I compared my terrain mesh to my test meshes. I found forcing the index format to 16bit on the terrain mesh makes raycasting work again, regardless of 1 or multiple hits, but changing the index format on the other meshes has no impact. Possibly the issue is the number of vertices this mesh has? Changing to 16bit also breaks the terrain mesh into parts, which is not ideal. But can maybe start to work around the problem now.