Search Unity

Inconsistent Mesh Collider Results (Fixed)

Discussion in 'DOTS Physics' started by ssellvf, Jul 7, 2020.

  1. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    Unity Version: 2019.4.2f1
    Unity Physics Version: preview.5 - 0.4.0
    Entities Version: preview.4 - 0.11.1

    Summary:

    Collisions between a Capsule Collider and Mesh Colliders is inconsistent.

    Details:

    I have a custom terrain system which generates its own terrain meshes, in segments of 64x64 units. The terrain uses Mesh Colliders to add physics. These colliders work for some segments, but do not work for others.

    I have tried, and failed, to discern what may be causing this inconsistent behaviour. It occurs even when the terrain is flat, and even when the same mesh is used to build the collider for all of the segments. Some will generate collisions, others will not and the player falls through. Interestingly, sometimes direction entering/traversing the terrain matters. Going from segment A to B may fail, but C to B works.

    Some details about these meshes:
    • Approximately 7,000 to 28,000 vertices with a third that many triangles, depending on segment complexity.
    • Have tried both a mesh of only the terrain surface as well as a "complete" mesh (all sides).
    • All terrain entities have the same components, including: PhysicsMass, RenderBounds, PhysicsCollider, Rotation, Translation, etc.
    The only components that vary in value between the segments are: WorldRenderBounds, LocalToWorld, Translation, and ChunkWorldRenderBounds. As would be expected.

    Raycasts also fail to return any hits for these problem segments as well, in addition to the player falling through.

    I have a bunch of code surrounding my implementation, so it is hard to give a good, simple code snippet. However the most relevant section is below.

    Code (CSharp):
    1.  
    2. protected override void OnUpdate()
    3. {
    4.     Entities.ForEach((
    5.         Entity entity,
    6.         ref TerrainColliderProxyComponent proxy,
    7.         ref Translation position,
    8.         ref RenderBounds bounds) =>
    9.     {
    10.         if (proxy.RequiresColliderUpdate)
    11.         {
    12.             Material material = GetMaterial(entity);
    13.             CollisionFilter filter = GetCollisionFilter(entity);
    14.             TerrainSegment segment = ...;
    15.  
    16.             UpdatePhysicsMeshCollider(entity, segment.TerrainMesh, material, filter);
    17.  
    18.             proxy.RequiresColliderUpdate = false;
    19.         }
    20.     });
    21. }
    22.  
    23. private void UpdatePhysicsMeshCollider(Entity entity, CustomMesh mesh, Material material, CollisionFilter filter)
    24. {
    25.     mesh.FetchPhysicsData(out NativeArray<float3> vertices, out NativeArray<int3> triangles);
    26.  
    27.     var collider = MeshCollider.Create(vertices, triangles, filter, material);
    28.     var component = new PhysicsCollider() { Value = collider };
    29.  
    30.     if (!EntityManager.HasComponent<PhysicsCollider>(entity))
    31.     {
    32.         PostUpdateCommands.AddComponent(entity, component);
    33.     }
    34.     else
    35.     {
    36.         EntityManager.SetComponentData(entity, component);
    37.     }
    38.  
    39.     triangles.Dispose();
    40.     vertices.Dispose();
    41. }
    42.  
    43. private Material GetMaterial(Entity entity)
    44. {
    45.     if (EntityManager.HasComponent<PhysicsMaterialProxyComponent>(entity))
    46.     {
    47.         return EntityManager.GetComponentData<PhysicsMaterialProxyComponent>(entity).ToMaterial();
    48.     }
    49.  
    50.     return Material.Default;
    51. }
    52.  
    53. private CollisionFilter GetCollisionFilter(Entity entity)
    54. {
    55.     if (EntityManager.HasComponent<CollisionFilterProxyComponent>(entity))
    56.     {
    57.         return EntityManager.GetComponentData<CollisionFilterProxyComponent>(entity).ToCollisionFilter();
    58.     }
    59.  
    60.     return CollisionFilter.Default;
    61. }
    62.  
    And finally, a GIF. It shows where four segments meet, and the top two segments are OK while the bottom two the collisions fail.



    Questions:

    1. Any ideas why the same mesh which works as the source of the Mesh Collider for Entity A but it fails for Entity B?
    2. Are there any limitations to the mesh data supplied to the MeshCollider.Create method? For example, Unity Meshes have a limit of 65536 vertices. Though I do not think this is the issue since the identical meshes work for some entities, but fails for others.
    3. The documentation states that the Mesh Collider is a "triangle soup." Can the mesh have holes? Can it be a single plane (ie terrain surface)?
    4. Are there any limitations/concerns with when a mesh collider is made/timing of creating multiple mesh colliders? I assume it is safe to do so within a system?
    5. Does winding order matter for mesh colliders? From my experience it does not seem so.
    Thank you for your time.
     
    Last edited: Jul 7, 2020
  2. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,311
    Have you used the PhysicsDebugDisplay component to see what the actual colliders look like?

    Also for terrain you should use the TerrainCollider. Takes heights as input it's far more optimized for this use case.
     
  3. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    First thing that comes to my mind (other than what @snacktime suggested) is that your system doesn't have UpdateBefore or UpdateAfter so it could run at the random time... I would make it run before BuildPhysicsWorld, because that's the only way to guarantee that physics world will pick up all these colliders. Now that might not be the reason you are seeing this, but it's a start.
     
  4. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    Thank you for your responses.

    Using the PhysicsDebugDisplay unfortunately doesn't provide much more insight. See the GIF below (sorry for the poor performance, the gizmos really bogged me down).

    You can see that I can move/jump in the bottom right, but then I fall through once I move up.



    As for the TerrainCollider, I used to use it but now I use a custom mesh with vertical walls, sort of like voxels. From my understanding the TerrainCollider would try to create slopes based off of the heightmap and lose the verticals.

    And the snippet didn't show it, but the system (UpdateTerrainSystem) is set in a group which is before BuildPhysicsWorld.

     
  5. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,311
    Strange. Maybe there is a clue in the closest geometry that it does find. Like the closest hit on a distance query might be useful?

    If it's a dynamic body maybe crank up solver iterations maybe it hit something it can't solve completely?
     
  6. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    I tried your suggestion, snacktime, and used CollisionWorld.CalculateDistance to get the nearest point of contact.

    Unsurprisingly when I fall through the mesh the closest point is the nearest segment that is working. Again, see the GIF below (magenta line is line to nearest hit).



    I also tried mucking around with the various settings in the Physics Step script, including:

    • Trying Havok Physics instead of Unity
    • Increase Solver Iteration Count from 4 to 8 to 32
    • Enable "Synchronize Collision World"
    • Enable "Contact Solver Stabilization Heuristic"
    There must be something obvious I am missing.
     
  7. polemical

    polemical

    Joined:
    Jun 17, 2019
    Posts:
    1,096
    I've been handling mesh colliders for custom meshes without issue by setting .sharedMesh of the MeshCollider to .sharedMesh of the MeshFilter. This assumes each mesh is unique - I haven't tried it where more than one MeshFilter uses the same mesh.
    Code (CSharp):
    1. MeshFilter mf = GetComponent<MeshFilter>();
    2. MeshCollider mc = GetComponent<MeshCollider>();
    3. mc.sharedMesh = mf.sharedMesh;
     
  8. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,311
    I don't know how much surface normals matter in cast hits if at all. But I'm curious if an aabb query returns what the distance query won't. That might narrow this down some. Other then that I'm out of ideas.
     
  9. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    @polemical Unfortunately this is about the Unity Physics package (aka DOTS Physics) and not UnityEngine.PhysicsModule.

    Thanks @snacktime, I am running out of ideas too. Actually, I ran out right before make this post! About half of my terrain is usable so might chock this up to Unity.Physics being an alpha preview package and hopefully this just fixes itself while I focus on other things.

    Wonder if I can detect when a terrain has an invalid collider (no ray hit) and try recreating the collider. What was the definition of insanity again? Its doing the same thing over and over again because its bound to work eventually?
     
  10. Sima_Havok

    Sima_Havok

    Joined:
    Dec 16, 2019
    Posts:
    47
    I am curious what the RayTraceCamera will show. RayTraceCamera does what it name tries to explain, it just shoots bunch of rays and for every hit it displays the pixel in the target canvas as on the pic below.
    Raytracer.png

    To enable this do the following:
    • copy RayTraceCamera from any sample demo (e.g. Hello World). It's child of the MainCamera.
    • copy RayTraceUI from the same demo
    • In your scene drag MainCamera to Render Camera field of the RayTraceUI
    • In your scene drag RayTraceDrawer (child of RayTraceUI) into RayTraceCamera's Display Target field
    • Make sure that RayTraceCamera is enabled
    • Play the demo and go to game view
     
    florianhanke and Rory_Havok like this.
  11. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    florianhanke likes this.
  12. Sima_Havok

    Sima_Havok

    Joined:
    Dec 16, 2019
    Posts:
    47
    I am totally puzzled around cases where rays hit the mesh where character is but the character goes true, as well as cases where casts stop hitting the mesh (although that may be because mesh is too close to camera).

    The raytrace camera gives just 2 colors although based on the hit normal it should color hits on the river slopes differently. That is very strange.

    Do you use scaling?
     
  13. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    @Sima_Havok here is another capture where the camera never goes below the ground.

    In this one you can clearly see segments which were fine one second then vanish the next.



    (https://vertexfragment.s3.amazonaws.com/public/collisionissuesraycamera3.gif)

    And I don't use scaling. The meshes are generated at the appropriate 1:1 world scale and then their RenderBounds are set to match the AABB returned by the MeshCollider.

    Below are all of the components attached to one of these segments. This one is segment (0, 0) at the world origin, which I was falling through at the time.



    There used to be a PhysicsMass also attached, set as Kinematic, but removed the component to check if that was the cause for some reason (when first using Unity.Physics I didn't have kinematic enabled so I was actually bouncing my terrain away).

    If this was a render issue instead of a physics one, I would say it looks like incorrect bounds. But the bounds are correct.
     
  14. Sima_Havok

    Sima_Havok

    Joined:
    Dec 16, 2019
    Posts:
    47
    Stupid question, since you have your own terrain system the creates tiles of meshes and based on gifs I would say that whole tile is appearing and disappearing. That let me to an experiment with the crappy code below. The component was added to letter H.

    Code (CSharp):
    1. using System.ComponentModel;
    2. using Unity.Entities;
    3. using Unity.Physics;
    4. using Unity.Physics.Systems;
    5.  
    6. [GenerateAuthoringComponent]
    7. public struct AddRemovePhysicsExclude : IComponentData
    8. {
    9.     [DefaultValue(2)]
    10.     public int counter;
    11. }
    12.  
    13. [UpdateBefore(typeof(BuildPhysicsWorld))]
    14. public class AddPemovePhysicsExcludeSystem : SystemBase
    15. {
    16.     private EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;
    17.  
    18.     protected override void OnCreate()
    19.     {
    20.         m_EntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    21.     }
    22.  
    23.     protected override void OnUpdate()
    24.     {
    25.         var commandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer();
    26.  
    27.         Entities.WithoutBurst().ForEach((Entity e, ref AddRemovePhysicsExclude exclude) =>
    28.         {
    29.             if (exclude.counter == 0)
    30.             {
    31.                 commandBuffer.AddComponent(e, new PhysicsExclude());
    32.                 exclude.counter = -5;
    33.             }
    34.             else if (exclude.counter == -1)
    35.             {
    36.                 commandBuffer.RemoveComponent(e, typeof(PhysicsExclude));
    37.                 exclude.counter = 5;
    38.             }
    39.             else if (exclude.counter > 0)
    40.             {
    41.                 exclude.counter--;
    42.             }
    43.             else
    44.             {
    45.                 exclude.counter++;
    46.             }
    47.  
    48.         }).Run();
    49.     }
    50. }
    51.  
    Resulting behavior is
    PhysicsExclude.gif

    So do you enable and disable tiles that character controller is around during the simulation?
     
  15. teasully

    teasully

    Joined:
    Nov 26, 2016
    Posts:
    13
    Hello,

    I seem to be having a similar problem in my Unity project using MeshColliders and the new Unity Physics package.

    Use case: I am generating a grid of entities with MeshColliders which verts/tris are generated at runtime to make 2D voxel "terrain". I am going to use it in a similar way that Terraria does for its terrain; I also split the voxels into chunks for performant reasons. Everything seems to work great until I edit the voxels a certain amount. In order to produce 2D physics, I constrain the cube physics entities using linear and angular constraints on a PhysicsJoint component (locked z linear and xy angular) connected to a Entity.Null via a PhysicsConstrainedBodyPair entity.

    I read the thread and I do not have my physics colliders for the voxels updating using UpdateBefore or UpdateAfter. However, I have a video that, unless I am not understanding how the physics works, suggests the issue an invalid pointer or memory allocation in the meshcollider backend regarding... no idea. Vertex / tri memory data maybe? Just a wild guess.

    Video:


    I edited the video to speed it up at points because when I incorporated the RayCastCamera from the demo project, it was eating up around 100ms per second in the entity debugger.

    Background info for the video: In the video, the pink area in the left is the grid of voxel RenderMesh/MeshCollider entites (a few thousand verts/tris). If I click, I spawn 20 cubes entities in that location. If I hover my mouse over a voxel, it finds a voxel (via mouse pos and magic array magic) and updates the chunks mesh and collider. All of the entity data for the cubes and voxels are hard-coded (not prefab). However, I have four uniformly-scaled cubes surrounding the "play area", one left, right, above, and below. These four cubes are gameobjects in the scene and are converted to entities and physics objects using the Unity "ConvertToEntity"/physics scripts. These are the only GameObject physics entities like this in my project. I never interact with the four cubes in the code that would have them exhibit the behavior in the video (such as using code like in the post above).

    What happens: Up until 30 seconds in the video, everything works as intended: I spawn a bunch of cubes that interact with the runtime generated voxel MeshColliders along with being able to hover my mouse over some voxels and remove them (which in turn regenerates the mesh and colliders). Around the 30 second mark, I am still editing voxels and updating the collider; look at the raytracing camera near the top right of the grid of voxels- they start to phase in and out like the post above. Keep watching and magically the giant cube on the right disappears. Around 1:40ish, I zoom in the raytracing camera; the one chunk of voxels is phasing in and out. Could this be caused by not using UpdateBefore or UpdateAfter? Why does the chunk not phase out in the beginning, or why do the four surrounding cubes begin to phase out if I never mess with their colliders? Throughout the rest of the video, the physics system seems to get less and less stable the more I update the MeshCollider (see 2:30 in video).

    I have tried spawning just cubes with no voxels. No bug. I try editing just the voxels without cubes. No bug. But the cubes interacting with the updating (?) colliders with the voxels seems to cause this issue. Furthermore, I originally opted to use the Havok backend for this project. But when whatever happens happens, the Havok physics engine does not freak out like above, it simply crashes. The crash happens in the editor or in the build. I have included the crash files.

    Sorry if this is a lot of information or is disorganized, first post.
     

    Attached Files:

  16. teasully

    teasully

    Joined:
    Nov 26, 2016
    Posts:
    13
  17. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Let me ask a few simple questions:

    1. Can you try going to BuildPhysicsWorld.cs and locating a haveStaticBodiesChanged[0] = 0; line, converting that to haveStaticBodiesChanged[0] = 1; and commenting out a block below that tries to set this value. There is a logic that decides to skip building the static layer if there are no changes and I just want to make sure it doesn't have issues that are causing your problems. This applies to both @ssellvf and @teasully.
    2. @teasully It seems like your dynamic boxes are also disappearing from time to time. What is your number of bodies? Since every body has a joint, you might be hitting a maximum number of joints, as explained in Scheduler.cs (the big comment at the top of the file). I mean it doesn't look like you have that much, but still, worth asking. Same question (although slightly less likely for @ssellvf ).
    3. @teasully These voxels, are they all combined in a single mesh collider? Or they are all individually mesh colliders?

    Also, please reply to @Sima_Havok's question regarding PhysicsExclude tag. Again, applies to both @teasully and @ssellvf.

    Let's start with that and work our way through.
     
  18. teasully

    teasully

    Joined:
    Nov 26, 2016
    Posts:
    13
    1. In BuildPhysicsWorld.cs, I edited line 137 to say "haveStaticBodiesChanged[0] = 1;". Then I commented out lines 138-170. This did not have any changes; the bug still appears.
    2. I'm not sure how to tell how many physics bodies there are, however, I would like to safely say that I have less than 16,777,216 rigid bodies and 32,767 joints. Using a simple counter on cube spawn, I replicated the issue with around 1,600 bodies each with a joint of its own, so 1,600 joints.
    3. I distribute the voxels into chunks, so each chunk holds 8x8 voxels (64). Each chunk has its own MeshCollider components. In the video, there is a 10x10 grid of chunks (100 chunks = 100 MeshColliders). Each MeshCollider is updated as needed, they are not updated every frame.
    As for Sima_Havok's question, I do not use the PhysicsExclude tag.

    Also, I stumbled upon this thread (https://forum.unity.com/threads/havok-crashing-on-terrain-collider.922952/) which is about a bug concerning the TerrainCollider and an issue using triangle collision. The solution was to change the Collision Method from triangles to vertexes; is there a similar setting for MeshColliders and is that issue related to this one?
     
  19. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    @petarmHavok I do not currently actively cull any of the segments. So no use of PhysicsExclude component or the generic Disabled component.

    I also tried your suggestion of modifying BuildPhysicsWorld.cs

    Code (CSharp):
    1. haveStaticBodiesChanged[0] = 1;
    2. /*{
    3.     if (PhysicsWorld.NumStaticBodies != previousStaticBodyCount)
    4.     {
    5.         haveStaticBodiesChanged[0] = 1;
    6.     }
    7.     else
    8.     {
    9.         // Make a job to test for changes
    10.         int numChunks;
    11.         using (NativeArray<ArchetypeChunk> chunks = StaticEntityGroup.CreateArchetypeChunkArray(Allocator.TempJob))
    12.         {
    13.             numChunks = chunks.Length;
    14.         }
    15.         var chunksHaveChanges = new NativeArray<int>(numChunks, Allocator.TempJob);
    16.  
    17.         staticBodiesCheckHandle = new Jobs.CheckStaticBodyChangesJob
    18.         {
    19.             LocalToWorldType = localToWorldType,
    20.             ParentType = parentType,
    21.             PositionType = positionType,
    22.             RotationType = rotationType,
    23.             PhysicsColliderType = physicsColliderType,
    24.             ChunkHasChangesOutput = chunksHaveChanges,
    25.             m_LastSystemVersion = LastSystemVersion
    26.         }.Schedule(StaticEntityGroup, Dependency);
    27.  
    28.         staticBodiesCheckHandle = new Jobs.CheckStaticBodyChangesReduceJob
    29.         {
    30.             ChunkHasChangesOutput = chunksHaveChanges,
    31.             Result = haveStaticBodiesChanged
    32.         }.Schedule(staticBodiesCheckHandle);
    33.     }
    34. }*/
    35.  
    Which unfortunately had no effect.

    I just want to add that I greatly appreciate the time that various people have spent in this thread to help me.
     
  20. Sima_Havok

    Sima_Havok

    Joined:
    Dec 16, 2019
    Posts:
    47
    The terrain only has 2 different collision options. Mesh is only about triangles.
     
  21. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Can I ask @teasully and @ssellvf to try and make a really small repro project that shows this issue? You can strip out all sensitive data, we just need a demo that shows a collider disappearing and appearing randomly. Your 2 issues seem quite related, so even one repro could do the trick for us. We really want to get to the bottom of this but I'm out of ideas to ask about, need to see the code at this point. :) And thanks for answering all my questions, I know some of them were a long shot.
     
  22. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    I was going to offer to do something similar. Is there an email address I could send it to, or just PM it to you over the Unity Forums when it is ready?

    Edit: I sent you a direct message.
     
    Last edited: Jul 14, 2020
    petarmHavok likes this.
  23. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Thanks, will look at this first thing tomorrow!
     
  24. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    So after taking a look at @ssellvf's repro, we have some progress, so please @teasully also try to apply these and let us know:

    There are 2 issues:

    1. Locate CheckStaticBodyChangesJob and in the Execute method add a condition
    || chunk.DidOrderChange(m_LastSystemVersion)
    This one is not that big but it causes stuff to disappear once in a while in the repro scene. We were already aware of this and it will probably be in the next release.

    2. Locate public Aabb CalculateAabb(RigidTransform transform) method in MeshCollider and add the following block at the beginning of that method:
    if (!m_Aabb.IsValid)
    {
    return m_Aabb;
    }
    Now this is an interesting one. Empty AABBs can appear if some colliders in the scene are not valid for whatever reason. They will not collide with anything, and that's fine. But without the fix above, they will mess up the entire bounding volume hierarchy since it is a spatial tree and it combines AABBs, and the method above ends up with NaNs without the fix. We will include the fix in the next release, but it's best to take a look at your colliders and see which one has an empty AABB, since it will cost performance for no reason since it can't affect the simulation.

    I hope this fixes it for both of you. Let us know!
     
    GliderGuy, Sima_Havok and ssellvf like this.
  25. ssellvf

    ssellvf

    Joined:
    Aug 3, 2018
    Posts:
    15
    @petarmHavok Those changes fixed it!

    A big thank you to you and everyone else who contributed in this thread, I really appreciate it.
     
    GliderGuy and petarmHavok like this.
  26. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Glad to hear it helped! We'll include those fixes in the next release.

    @teasully Please let us know the state of your project after these fixes!
     
  27. teasully

    teasully

    Joined:
    Nov 26, 2016
    Posts:
    13
    I did not implement your fix but instead added a check when updating my PhysicsColliders. If the PhysicsCollider would be empty (0 verts), I remove the PhysicsCollider; this seems to do the job!
     
    petarmHavok likes this.
unityunity