JobHandle ProduceMeshColliders(
            NativeHashMap <Hash128, MeshInput> inputs,
            NativeList <float3> vertices,
            NativeList <int3> indices,
            out NativeHashMap <Hash128, BlobAssetReference <Collider> > meshColliders,
            JobHandle inputDeps = default
            )
        {
            // create collider blob assets
            var meshCollidersArray =
                new NativeArray <KeyValuePair <Hash128, BlobAssetReference <Collider> > >(inputs.Count(), Allocator.TempJob);
            const int arrayLength = 5;
            var       jobHandle   = new ProduceMeshCollidersJob
            {
                InputKeys   = inputs.GetKeyArray(Allocator.TempJob),
                InputValues = inputs.GetValueArray(Allocator.TempJob),
                AllVertices = vertices,
                AllIndices  = indices,
                Output      = meshCollidersArray
            }.Schedule(inputs.Count(), arrayLength, inputDeps);

            // put blob assets into hash map
            meshColliders = new NativeHashMap <Hash128, BlobAssetReference <Collider> >(inputs.Count(), Allocator.TempJob);
            jobHandle     = new ConvertToHashMapJob <Hash128, BlobAssetReference <Collider> >
            {
                Input  = meshCollidersArray,
                Output = meshColliders
            }.Schedule(jobHandle);

            return(jobHandle);
        }
        protected override void OnUpdate()
        {
            var shapeQuery = EntityManager.CreateEntityQuery(ComponentType.ReadOnly <T>());
            var shapeCount = shapeQuery.CalculateEntityCount();

            // A map from entities to arrays of colliders
            m_ExtraColliders = new NativeMultiHashMap <ComparableEntity, LeafShapeData>(shapeCount, Allocator.Temp);

            // Lists to store input data for deferred convex and mesh jobs
            const int defaultPointsPerShape = 1024;

            m_ConvexColliderJobs   = new NativeList <ConvertConvexColliderInput>(shapeCount, Allocator.TempJob);
            m_ConvexColliderPoints = new NativeList <float3>(shapeCount * defaultPointsPerShape, Allocator.TempJob);

            m_MeshColliderJobs     = new NativeList <ConvertMeshColliderInput>(shapeCount, Allocator.TempJob);
            m_MeshColliderVertices = new NativeList <float3>(shapeCount * defaultPointsPerShape, Allocator.TempJob);
            m_MeshColliderIndices  = new NativeList <int>(shapeCount * defaultPointsPerShape / 2, Allocator.TempJob);

            // First pass.
            // Convert all shape authoring components into colliders, and collect them for each primary body
            Entities.ForEach <T>(ConvertShape);

            // Second pass.
            // Produce all convex and mesh collider blobs in parallel
            const int arrayLength     = 5;
            var       convexColliders =
                new NativeArray <KeyValuePair <Entity, LeafShapeData> >(m_ConvexColliderJobs.Length, Allocator.TempJob);
            var convexJob = new ProduceConvexCollidersJob
            {
                InputParameters = m_ConvexColliderJobs,
                AllPoints       = m_ConvexColliderPoints,
                Output          = convexColliders
            }.Schedule(m_ConvexColliderJobs.Length, arrayLength);

            var meshColliders =
                new NativeArray <KeyValuePair <Entity, LeafShapeData> >(m_MeshColliderJobs.Length, Allocator.TempJob);
            var meshJob = new ProduceMeshCollidersJob
            {
                InputParameters = m_MeshColliderJobs,
                AllVertices     = m_MeshColliderVertices,
                AllIndices      = m_MeshColliderIndices,
                Output          = meshColliders
            }.Schedule(m_MeshColliderJobs.Length, arrayLength);

            JobHandle.CombineDependencies(convexJob, meshJob).Complete();

            AppendLeafShapeDataToShapeMap(convexColliders, m_ExtraColliders, m_ConvexShapes);
            convexColliders.Dispose();

            AppendLeafShapeDataToShapeMap(meshColliders, m_ExtraColliders, m_MeshShapes);
            meshColliders.Dispose();

            // Final pass.
            // Assign PhysicsCollider components to rigid bodies, merging multiples into compounds as needed
            var keys = m_ExtraColliders.GetUniqueKeyArray(Allocator.Temp);

            using (keys.Item1)
            {
                for (var k = 0; k < keys.Item2; ++k)
                {
                    ComparableEntity body = keys.Item1[k];
                    var collider          = DstEntityManager.HasComponent <PhysicsCollider>(body.Entity)
                        ? DstEntityManager.GetComponentData <PhysicsCollider>(body.Entity)
                        : new PhysicsCollider();
                    var children = new NativeList <CompoundCollider.ColliderBlobInstance>(16, Allocator.Temp);

                    // collect any existing valid shapes
                    if (collider.IsValid)
                    {
                        ColliderType colliderType;
                        unsafe { colliderType = collider.ColliderPtr->Type; }

                        // if there is already a compound, add its leaves to the list of children
                        if (colliderType == ColliderType.Compound)
                        {
                            unsafe
                            {
                                var existingChildren = ((CompoundCollider *)collider.ColliderPtr)->Children;
                                for (int i = 0, count = existingChildren.Length; i < count; ++i)
                                {
                                    children.Add(new CompoundCollider.ColliderBlobInstance
                                    {
                                        Collider = BlobAssetReference <Collider> .Create(
                                            existingChildren[i].Collider,
                                            existingChildren[i].Collider->MemorySize
                                            ),
                                        CompoundFromChild = existingChildren[i].CompoundFromChild
                                    });
                                }
                            }
                        }
                        // otherwise add the single collider to the list of children
                        else
                        {
                            children.Add(
                                new CompoundCollider.ColliderBlobInstance
                            {
                                Collider          = collider.Value,
                                CompoundFromChild = RigidTransform.identity
                            }
                                );
                        }
                    }

                    // if collider is already valid, a shape already existed from another system
                    var isSingleShapeOnPrimaryBody = !collider.IsValid;
                    // collect all children found by this system
                    if (m_ExtraColliders.TryGetFirstValue(body, out var child, out var iterator))
                    {
                        do
                        {
                            children.Add(child.ColliderBlobInstance);
                            isSingleShapeOnPrimaryBody &= child.LeafEntity.Equals(body.Entity);
                        } while (m_ExtraColliders.TryGetNextValue(out child, ref iterator));
                    }

                    // if there is a single shape on the primary body, use it as-is, otherwise create a compound
                    // (assume a single leaf should still be a compound so that local offset values in authoring representation are retained)
                    collider.Value = isSingleShapeOnPrimaryBody
                        ? children[0].Collider
                        : CompoundCollider.Create(children);

                    children.Dispose();

                    DstEntityManager.AddOrSetComponent(body.Entity, collider);
                }
            }

            m_ConvexShapes.Clear();
            m_ConvexColliderJobs.Dispose();
            m_ConvexColliderPoints.Dispose();

            m_MeshShapes.Clear();
            m_MeshColliderJobs.Dispose();
            m_MeshColliderVertices.Dispose();
            m_MeshColliderIndices.Dispose();
        }