public unsafe void Execute(CollisionEvent collisionEvent)
            {
                CollisionEvent.Details details = collisionEvent.CalculateDetails(ref World);

                // Color code the impulse depending on the collision feature
                // vertex - blue
                // edge - cyan
                // face - magenta
                UnityEngine.Color color;
                switch (details.EstimatedContactPointPositions.Length)
                {
                case 1:
                    color = UnityEngine.Color.blue;
                    break;

                case 2:
                    color = UnityEngine.Color.cyan;
                    break;

                default:
                    color = UnityEngine.Color.magenta;
                    break;
                }

                var averageContactPosition = details.AverageContactPointPosition;

                OutputStreamContext->Point(averageContactPosition, 0.01f, color);
                OutputStreamContext->Arrow(averageContactPosition, collisionEvent.Normal * details.EstimatedImpulse, color);
            }
            private void ProcessForEntity(Entity entity,
                                          Entity otherEntity,
                                          float3 normal,
                                          bool hasDetails,
                                          CollisionEvent.Details collisionEventDetails)
            {
                DynamicBuffer <StatefulCollisionEvent> collisionEventBuffer =
                    collisionEventBufferFromEntity[entity];

                var foundMatch = false;

                for (var i = 0; i < collisionEventBuffer.Length; i++)
                {
                    StatefulCollisionEvent collisionEvent = collisionEventBuffer[i];

                    // If entity is already there, update to Stay
                    if (collisionEvent.Entity == otherEntity)
                    {
                        foundMatch            = true;
                        collisionEvent.Normal = normal;
                        collisionEvent.HasCollisionDetails         = hasDetails;
                        collisionEvent.AverageContactPointPosition =
                            collisionEventDetails.AverageContactPointPosition;
                        collisionEvent.EstimatedImpulse =
                            collisionEventDetails.EstimatedImpulse;
                        collisionEvent.State    = PhysicsEventState.Stay;
                        collisionEvent._isStale = false;
                        collisionEventBuffer[i] = collisionEvent;

                        break;
                    }
                }

                // If it's a new entity, add as Enter
                if (!foundMatch)
                {
                    collisionEventBuffer.Add(
                        new StatefulCollisionEvent
                    {
                        Entity = otherEntity,
                        Normal = normal,
                        HasCollisionDetails         = hasDetails,
                        AverageContactPointPosition =
                            collisionEventDetails.AverageContactPointPosition,
                        EstimatedImpulse = collisionEventDetails.EstimatedImpulse,
                        State            = PhysicsEventState.Enter,
                        _isStale         = false
                    }
                        );
                }
            }
            public void Execute(CollisionEvent collisionEvent)
            {
                CollisionEvent.Details collisionEventDetails = default;

                var aHasDetails = false;
                var bHasDetails = false;

                if (collisionEventsReceiverPropertiesFromEntity
                    .HasComponent(collisionEvent.EntityA))
                {
                    aHasDetails =
                        collisionEventsReceiverPropertiesFromEntity[collisionEvent.EntityA]
                        .UsesCollisionDetails;
                }

                if (collisionEventsReceiverPropertiesFromEntity
                    .HasComponent(collisionEvent.EntityB))
                {
                    bHasDetails =
                        collisionEventsReceiverPropertiesFromEntity[collisionEvent.EntityB]
                        .UsesCollisionDetails;
                }

                if (aHasDetails || bHasDetails)
                {
                    collisionEventDetails = collisionEvent.CalculateDetails(ref physicsWorld);
                }

                if (collisionEventBufferFromEntity.HasComponent(collisionEvent.EntityA))
                {
                    ProcessForEntity(
                        collisionEvent.EntityA,
                        collisionEvent.EntityB,
                        collisionEvent.Normal,
                        aHasDetails,
                        collisionEventDetails
                        );
                }

                if (collisionEventBufferFromEntity.HasComponent(collisionEvent.EntityB))
                {
                    ProcessForEntity(
                        collisionEvent.EntityB,
                        collisionEvent.EntityA,
                        collisionEvent.Normal,
                        bHasDetails,
                        collisionEventDetails
                        );
                }
            }
 public void Execute(CollisionEvent collisionEvent)
 {
     // Collision event is between a static and dynamic box.
     // Verify all data in the provided event struct.
     CollisionEvent.Details details = collisionEvent.CalculateDetails(ref World);
     Assert.IsTrue(details.EstimatedImpulse >= 0.0f);
     Assert.IsTrue(details.EstimatedContactPointPositions.Length == 4);
     Assert.AreNotEqual(collisionEvent.BodyIndexA, collisionEvent.BodyIndexB);
     Assert.AreEqual(collisionEvent.ColliderKeyA.Value, ColliderKey.Empty.Value);
     Assert.AreEqual(collisionEvent.ColliderKeyB.Value, ColliderKey.Empty.Value);
     Assert.AreEqual(collisionEvent.EntityA, Bodies[collisionEvent.BodyIndexA].Entity);
     Assert.AreEqual(collisionEvent.EntityB, Bodies[collisionEvent.BodyIndexB].Entity);
     Assert.AreApproximatelyEqual(collisionEvent.Normal.x, 0.0f, 0.01f);
     Assert.AreApproximatelyEqual(collisionEvent.Normal.y, 1.0f, 0.01f);
     Assert.AreApproximatelyEqual(collisionEvent.Normal.z, 0.0f, 0.01f);
 }
        // Calculate extra details about the collision, by re-integrating the leaf colliders to the time of collision
        internal unsafe CollisionEvent.Details CalculateDetails(
            ref PhysicsWorld physicsWorld, float timeStep, Velocity inputVelocityA, Velocity inputVelocityB, NativeArray <ContactPoint> narrowPhaseContactPoints)
        {
            int  bodyIndexA     = BodyIndices.BodyIndexA;
            int  bodyIndexB     = BodyIndices.BodyIndexB;
            bool bodyAIsDynamic = bodyIndexA < physicsWorld.MotionVelocities.Length;
            bool bodyBIsDynamic = bodyIndexB < physicsWorld.MotionVelocities.Length;

            MotionVelocity motionVelocityA = bodyAIsDynamic ? physicsWorld.MotionVelocities[bodyIndexA] : MotionVelocity.Zero;
            MotionVelocity motionVelocityB = bodyBIsDynamic ? physicsWorld.MotionVelocities[bodyIndexB] : MotionVelocity.Zero;
            MotionData     motionDataA     = bodyAIsDynamic ? physicsWorld.MotionDatas[bodyIndexA] : MotionData.Zero;
            MotionData     motionDataB     = bodyBIsDynamic ? physicsWorld.MotionDatas[bodyIndexB] : MotionData.Zero;

            float estimatedImpulse = SolverImpulse;

            // First calculate minimum time of impact and estimate the impulse
            float toi = timeStep;
            {
                float sumRemainingVelocities = 0.0f;
                float numRemainingVelocities = 0.0f;
                for (int i = 0; i < narrowPhaseContactPoints.Length; i++)
                {
                    var cp = narrowPhaseContactPoints[i];

                    // Collect data for impulse estimation
                    {
                        float3 pointVelA = GetPointVelocity(motionDataA.WorldFromMotion,
                                                            motionVelocityA.LinearVelocity, motionVelocityA.AngularVelocity, cp.Position + Normal * cp.Distance);
                        float3 pointVelB = GetPointVelocity(motionDataB.WorldFromMotion,
                                                            motionVelocityB.LinearVelocity, motionVelocityB.AngularVelocity, cp.Position);
                        float projRelVel = math.dot(pointVelB - pointVelA, Normal);
                        if (projRelVel > 0.0f)
                        {
                            sumRemainingVelocities += projRelVel;
                            numRemainingVelocities += 1.0f;
                        }
                    }

                    // Get minimum time of impact
                    {
                        float3 pointVelA = GetPointVelocity(motionDataA.WorldFromMotion,
                                                            inputVelocityA.Linear, inputVelocityA.Angular, cp.Position + Normal * cp.Distance);
                        float3 pointVelB = GetPointVelocity(motionDataB.WorldFromMotion,
                                                            inputVelocityB.Linear, inputVelocityB.Angular, cp.Position);
                        float projRelVel = math.dot(pointVelB - pointVelA, Normal);
                        if (projRelVel > 0.0f)
                        {
                            float newToi = math.max(0.0f, cp.Distance / projRelVel);
                            toi = math.min(toi, newToi);
                        }
                        else if (cp.Distance <= 0.0f)
                        {
                            // If in penetration, time of impact is 0 for sure
                            toi = 0.0f;
                        }
                    }
                }

                if (numRemainingVelocities > 0.0f)
                {
                    float sumInvMass = motionVelocityA.InverseMass + motionVelocityB.InverseMass;
                    estimatedImpulse += sumRemainingVelocities / (numRemainingVelocities * sumInvMass);
                }
            }

            // Then, sub-integrate for time of impact and keep contact points closer than hitDistanceThreshold
            int   closestContactIndex = -1;
            float minDistance         = float.MaxValue;
            {
                int estimatedContactPointCount = 0;
                for (int i = 0; i < narrowPhaseContactPoints.Length; i++)
                {
                    // Estimate new position
                    var cp = narrowPhaseContactPoints[i];
                    {
                        float3 pointVelA  = GetPointVelocity(motionDataA.WorldFromMotion, inputVelocityA.Linear, inputVelocityA.Angular, cp.Position + Normal * cp.Distance);
                        float3 pointVelB  = GetPointVelocity(motionDataB.WorldFromMotion, inputVelocityB.Linear, inputVelocityB.Angular, cp.Position);
                        float3 relVel     = pointVelB - pointVelA;
                        float  projRelVel = math.dot(relVel, Normal);

                        // Only sub integrate if approaching, otherwise leave it as is
                        // (it can happen that input velocity was separating but there
                        // still was a collision event - penetration recovery, or other
                        // body pushing in different direction).
                        if (projRelVel > 0.0f)
                        {
                            // Position the point on body A
                            cp.Position += Normal * cp.Distance;

                            // Sub integrate the point
                            cp.Position -= relVel * toi;

                            // Reduce the distance
                            cp.Distance -= projRelVel * toi;
                        }

                        // Filter out contacts that are still too far away
                        if (cp.Distance <= physicsWorld.CollisionWorld.CollisionTolerance)
                        {
                            narrowPhaseContactPoints[estimatedContactPointCount++] = cp;
                        }
                        else if (cp.Distance < minDistance)
                        {
                            minDistance         = cp.Distance;
                            closestContactIndex = i;
                        }
                    }
                }

                // If due to estimation of relative velocity no contact points will
                // get closer than the tolerance, we need to export the closest one
                // to make sure at least one contact point is reported.
                if (estimatedContactPointCount == 0)
                {
                    narrowPhaseContactPoints[estimatedContactPointCount++] = narrowPhaseContactPoints[closestContactIndex];
                }

                // Instantiate collision details and allocate memory
                var details = new CollisionEvent.Details
                {
                    EstimatedContactPointPositions = new NativeArray <float3>(estimatedContactPointCount, Allocator.Temp),
                    EstimatedImpulse = estimatedImpulse
                };

                // Fill the contact point positions array
                for (int i = 0; i < estimatedContactPointCount; i++)
                {
                    details.EstimatedContactPointPositions[i] = narrowPhaseContactPoints[i].Position;
                }

                return(details);
            }
        }