Runtime-generated collider ignored by rigidbodies only if Havok is enabled

Hey, I’m having problems with my runtime-generated colliders being ignored by rigidbodies only when Havok is enabled - casts with PhysicsWorldSingleton are working though.

I was able to reproduce the issue with the following system. It only generates a simple plane mesh with MeshDataArray, creates the collider from that and assigns it to the PhysicsCollider.
With Havok and the PhysicsDebugDisplay enabled, add the RuntimeColliderAuthoring to an empty game object in a subscene and place an authored rigidbody above it.

However, if PhysicsCollider and PhysicsWorldIndex are added by a PhysicsShape authoring component and not by myself (via this baker or at runtime), everything works again even though the entity’s values are the same.

Also allowed to ping @daniel-holz here. Writing something simpler that could reproduce the issue was done faster than I anticipated. :laughing:

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;

public class RuntimeColliderAuthoring : MonoBehaviour
{
	class Baker : Baker<RuntimeColliderAuthoring>
	{
		public override void Bake(RuntimeColliderAuthoring authoring)
		{
			Entity entity = GetEntity(TransformUsageFlags.Dynamic);

			AddComponent<MeshGenTestTag>(entity);

			// I'm adding these components by hand here as in my actual project, I'm creating entities completely at runtime without using Physics authoring.
			AddSharedComponent(entity, new PhysicsWorldIndex());
			AddComponent(entity, new PhysicsCollider());
		}
	}
}

public struct MeshGenTestTag : IComponentData { }

partial struct MeshGenTestSystem : ISystem
{
	[BurstCompile]
	void ISystem.OnCreate(ref SystemState state)
	{
		state.RequireForUpdate<MeshGenTestTag>();
	}

	[BurstCompile]
	void ISystem.OnUpdate(ref SystemState state)
	{
		foreach (var (transform, collider) in SystemAPI.Query<RefRO<LocalTransform>, RefRW<PhysicsCollider>>().WithAll<MeshGenTestTag>())
		{
			Mesh.MeshDataArray meshDataArray = Mesh.AllocateWritableMeshData(1);
			Mesh.MeshData meshData = meshDataArray[0];

			NativeArray<VertexAttributeDescriptor> vertexAttributes = new NativeArray<VertexAttributeDescriptor>(4, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

			// Set vertex attributes
			vertexAttributes[0] = new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3, 0);
			vertexAttributes[1] = new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3, 1);
			vertexAttributes[2] = new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 3, 2);
			vertexAttributes[3] = new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2, 3);

			meshData.SetVertexBufferParams(4, vertexAttributes);
			meshData.SetIndexBufferParams(6, IndexFormat.UInt32);

			// Set vertex data
			NativeArray<float3> vertices = meshData.GetVertexData<float3>(0);
			NativeArray<float3> normals = meshData.GetVertexData<float3>(1);
			NativeArray<float3> tangents = meshData.GetVertexData<float3>(2);
			NativeArray<float2> uvs = meshData.GetVertexData<float2>(3);

			vertices[0] = float3.zero;
			vertices[1] = new float3(0, 0, 10);
			vertices[2] = new float3(10, 0, 10);
			vertices[3] = new float3(10, 0, 0);

			normals[0] = math.up();
			normals[1] = math.up();
			normals[2] = math.up();
			normals[3] = math.up();

			tangents[0] = math.right();
			tangents[1] = math.right();
			tangents[2] = math.right();
			tangents[3] = math.right();

			uvs[0] = float2.zero;
			uvs[1] = new float2(0, 1);
			uvs[2] = new float2(1, 1);
			uvs[3] = new float2(1, 0);

			// Set indices
			NativeArray<uint> indices = meshData.GetIndexData<uint>();
			indices[0] = 0;
			indices[1] = 1;
			indices[2] = 2;

			indices[3] = 0;
			indices[4] = 2;
			indices[5] = 3;

			meshData.subMeshCount = 1;
			meshData.SetSubMesh(0, new SubMeshDescriptor(0, 6, MeshTopology.Triangles));

			// Create and apply collider
			BlobAssetReference<Unity.Physics.Collider> colliderBlob = Unity.Physics.MeshCollider.Create(meshData, CollisionFilter.Default, Unity.Physics.Material.Default);
			collider.ValueRW.Value = colliderBlob;

			meshDataArray.Dispose();
		}

		state.Enabled = false; // Run this system just once for demonstration
	}
}


This might look a bit awkward in your code but in order for the change to the PhysicsCollider to be picked up correctly in the synchronization with Havok you need to assign it to the ValueRW.

So just do the following instead:

collider.ValueRW = new PhysicsCollider { Value = colliderBlob };

That should do it.

Hmm, unfortunately, that doesn’t fix the problem…which seems reasonable because the blob asset reference is the only field in the component.
I’m wondering whether the built-in bakers (no matter whether box collider, mesh collider, physics shape…) do something that I don’t.

The default bakers use baking systems to generate the colliders. You are just using an ordinary ISystem here.

I wonder if by you using an empty PhysicsCollider in your baker and swapping it out not in a bakingsystem but in a runtime system, the Havok integration receives the empty one initially and then sort of marks that collider as invalid and then refuses to accept any changes to it after.
So using a bakingsystem could work.
Or, make sure your mesh creation system is run before the physics systems by running it in the BeforePhysicsSystemGroup or in the InitializationSystemGroup.

Maybe also try to add the PhysicsCollider component later, at the point when you add the underlying mesh collider.

Or, when creating the PhysicsCollider the first time, give it a valid collider immediately in the baker. Say, a sphere. If then the collision works we can refine our hypothesis. If it collides like that initial sphere, there is definitely something broken with the data synchronization when swapping out collider blobs. If it collides like your final mesh, it’s something with the treatment of empty PhysicsColliders that is broken.

1 Like

Thanks for your detailed answer. :slight_smile:

Okay, so some interesting results here.

  1. When updating the system in InitializationSystemGroup, you need to update it after SceneSystemGroup. Otherwise, the system might run one frame after the subscene is loaded which is enough time for Havok to pick up the empty collider.
  2. Updating it in SimulationSystemGroup but before FixedStepSimulationSystemGroup works.
  3. Updating it in BeforePhysicsSystemGroup works.
  4. Setting an initial collider in a custom baker that adds a PhysicsCollider works.
  5. Adding the PhysicsCollider at the point when I can set the collider works. The PhysicsWorldIndex can be authored though.

So empty colliders really seem to be treated incorrectly, but I don’t understand why updating the system in InitializationSystemGroup behaves differently than the rest.

2 Likes

Ok! Great results!
So, as you say there seems to be definitely some tricky behavior when it comes to invalid colliders.
I’ll file something for that internally.
For the time being, this thread will hopefully help others which experience the same issue.

Regarding the InitializationSystemGroup, I am not sure why this wouldn’t work. Maybe there are some ECB systems used that playback their buffers at some moment after this group and that somehow conflicts with what you are doing.
Or, your system is actually not run when you expect it to, creating the old ordering issue.
You can use the profiler to check when your system is run exactly in all the tested cases, and make sure that it is as expected also in this case.

1 Like

Cool, that’s very helpful!
For now, I can work around the issue and hopefully others can, too.

Also, you’re right about the system order with InitializationSystemGroup. By default, the system updates one frame after the subscene is loaded, which is already too late.
Updating it after SceneSystemGroup solves that. I’ll put that in my comment above.

Thanks for taking the time to investigate. :slight_smile:

1 Like

Nice! Good job confirming the exact reason for the difference in behavior with the InitializationSystemGroup so quickly!

And you are very welcome. :slightly_smiling_face:

1 Like

This same thing is happening to me with a runtime swap out of physicscollider value, but without havok. Only this works fine in-editor and does not work at all in builds (PC standalone or console)