// Schedule() implementation for IContactsJob when Havok Physics is available
        public static unsafe JobHandle Schedule <T>(this T jobData, ISimulation simulation, ref PhysicsWorld world, JobHandle inputDeps)
            where T : struct, IContactsJobBase
        {
            switch (simulation.Type)
            {
            case SimulationType.UnityPhysics:
                // Call the scheduling method for Unity.Physics
                return(IContactsJobExtensions.ScheduleUnityPhysicsContactsJob(jobData, simulation, ref world, inputDeps));

            case SimulationType.HavokPhysics:
            {
                var data = new ContactsJobData <T>
                {
                    UserJobData        = jobData,
                    ManifoldStream     = ((Havok.Physics.HavokSimulation)simulation).ManifoldStream,
                    PluginIndexToLocal = ((Havok.Physics.HavokSimulation)simulation).PluginIndexToLocal,
                    Bodies             = world.Bodies
                };
                var parameters = new JobsUtility.JobScheduleParameters(
                    UnsafeUtility.AddressOf(ref data),
                    ContactsJobProcess <T> .Initialize(), inputDeps, ScheduleMode.Batched);
                return(JobsUtility.Schedule(ref parameters));
            }

            default:
                return(inputDeps);
            }
        }
            public unsafe static void Execute(ref ContactsJobData <T> jobData, IntPtr additionalData,
                                              IntPtr bufferRangePatchData, ref JobRanges jobRanges, int jobIndex)
            {
                var  reader             = new Havok.Physics.HpBlockStreamReader(jobData.ManifoldStream);
                int *pluginIndexToLocal = jobData.PluginIndexToLocal->Data;

                while (reader.HasItems)
                {
                    var header       = (Havok.Physics.HpManifoldStreamHeader *)reader.ReadPtr <Havok.Physics.HpManifoldStreamHeader>();
                    int numManifolds = header->NumManifolds;

                    int bodyIndexA = pluginIndexToLocal[header->BodyIds.BodyIndexA & 0x00ffffff];
                    int bodyIndexB = pluginIndexToLocal[header->BodyIds.BodyIndexB & 0x00ffffff];

                    var userHeader = new ModifiableContactHeader();
                    userHeader.ContactHeader.BodyPair = new BodyIndexPair
                    {
                        BodyIndexA = bodyIndexA,
                        BodyIndexB = bodyIndexB
                    };
                    userHeader.EntityPair = new EntityPair
                    {
                        EntityA = jobData.Bodies[bodyIndexA].Entity,
                        EntityB = jobData.Bodies[bodyIndexB].Entity
                    };

                    while (numManifolds-- > 0)
                    {
                        var manifold = (Havok.Physics.HpManifold *)reader.ReadPtr <Havok.Physics.HpManifold>();

                        userHeader.ContactHeader.NumContacts = manifold->NumPoints;
                        userHeader.ContactHeader.Normal      = manifold->Normal.xyz;
                        var manifoldCache = manifold->m_CollisionCache;
                        userHeader.ContactHeader.CoefficientOfFriction           = manifoldCache->m_friction.Value;
                        userHeader.ContactHeader.CoefficientOfRestitution        = manifoldCache->m_restitution.Value;
                        userHeader.ContactHeader.ColliderKeys.ColliderKeyA.Value = manifold->m_ShapeKeyA;
                        userHeader.ContactHeader.ColliderKeys.ColliderKeyB.Value = manifold->m_ShapeKeyB;

                        Havok.Physics.HpPerManifoldProperty *cdp = manifoldCache->GetCustomPropertyStorage();
                        userHeader.ContactHeader.BodyCustomTags = cdp->m_bodyTagsPair;
                        userHeader.ContactHeader.JacobianFlags  = (JacobianFlags)cdp->m_jacobianFlags;

                        for (int p = 0; p < manifold->NumPoints; p++)
                        {
                            var userContact = new ModifiableContactPoint
                            {
                                Index        = p,
                                ContactPoint = new ContactPoint
                                {
                                    Position = new float3(manifold->Positions[p * 4 + 0], manifold->Positions[p * 4 + 1], manifold->Positions[p * 4 + 2]),
                                    Distance = manifold->Distances[p],
                                }
                            };

                            jobData.UserJobData.Execute(ref userHeader, ref userContact);

                            if (userContact.Modified)
                            {
                                manifold->Positions[p * 4 + 0] = userContact.ContactPoint.Position.x;
                                manifold->Positions[p * 4 + 1] = userContact.ContactPoint.Position.y;
                                manifold->Positions[p * 4 + 2] = userContact.ContactPoint.Position.z;
                                manifold->Distances[p]         = userContact.ContactPoint.Distance;
                            }

                            if (userHeader.Modified)
                            {
                                manifold->Normal.xyz               = userHeader.ContactHeader.Normal;
                                manifoldCache->m_friction.Value    = userHeader.ContactHeader.CoefficientOfFriction;
                                manifoldCache->m_restitution.Value = userHeader.ContactHeader.CoefficientOfRestitution;
                                cdp->m_jacobianFlags               = (byte)userHeader.ContactHeader.JacobianFlags;

                                if ((cdp->m_jacobianFlags & (byte)JacobianFlags.Disabled) != 0)
                                {
                                    manifold->m_ManifoldType        = 3;      // hknpManifoldType::DISABLED
                                    manifoldCache->m_collisionFlags = 1 << 9; // hknpCollisionFlags::DONT_BUILD_CONTACT_JACOBIANS
                                }
                                else
                                {
                                    // Not disabled, so check for other modifications.

                                    if ((cdp->m_jacobianFlags & (byte)JacobianFlags.EnableMassFactors) != 0)
                                    {
                                        manifold->m_DataFields |= 1 << 1; // hknpManifold::INERTIA_MODIFIED
                                        manifold->m_DataFields &= 0xfb;   // ~CONTAINS_TRIANGLE

                                        var mp = (MassFactors *)UnsafeUtility.AddressOf(ref manifold->m_Scratch[0]);
                                        mp->InverseInertiaFactorA = new float3(1);
                                        mp->InverseMassFactorA    = 1.0f;
                                        mp->InverseInertiaFactorB = new float3(1);
                                        mp->InverseMassFactorB    = 1.0f;
                                    }

                                    if ((cdp->m_jacobianFlags & (byte)JacobianFlags.IsTrigger) != 0)
                                    {
                                        manifold->m_ManifoldType = 1; // hknpManifoldType::TRIGGER
                                    }

                                    if ((cdp->m_jacobianFlags & (byte)JacobianFlags.EnableSurfaceVelocity) != 0)
                                    {
                                        manifoldCache->m_collisionFlags |= 1 << 25; // hknpCollisionFlags::ENABLE_SURFACE_VELOCITY
                                    }

                                    if (userHeader.ContactHeader.CoefficientOfRestitution != 0)
                                    {
                                        manifoldCache->m_collisionFlags |= 1 << 20; // hknpCollisionFlags::ENABLE_RESTITUTION
                                    }
                                }
                            }
                        }
                    }
                }
            }