public void Timestep(Simulation simulation, float dt, IThreadDispatcher threadDispatcher = null)
        {
            simulation.Sleep(threadDispatcher);
            Slept?.Invoke(dt, threadDispatcher);

            simulation.IntegrateVelocitiesBoundsAndInertias(dt, threadDispatcher);
            BeforeCollisionDetection?.Invoke(dt, threadDispatcher);

            simulation.CollisionDetection(dt, threadDispatcher);
            CollisionsDetected?.Invoke(dt, threadDispatcher);

            simulation.Solve(dt, threadDispatcher);
            ConstraintsSolved?.Invoke(dt, threadDispatcher);

            simulation.IntegratePoses(dt, threadDispatcher);
            PosesIntegrated?.Invoke(dt, threadDispatcher);

            simulation.IncrementallyOptimizeDataStructures(threadDispatcher);
        }
        public void Timestep(Simulation simulation, float dt, IThreadDispatcher threadDispatcher = null)
        {
            simulation.Sleep(threadDispatcher);
            Slept?.Invoke(dt, threadDispatcher);

            simulation.PredictBoundingBoxes(dt, threadDispatcher);
            BeforeCollisionDetection?.Invoke(dt, threadDispatcher);

            simulation.CollisionDetection(dt, threadDispatcher);
            CollisionsDetected?.Invoke(dt, threadDispatcher);

            Debug.Assert(SubstepCount >= 0, "Substep count should be positive.");
            var substepDt = dt / SubstepCount;

            for (int substepIndex = 0; substepIndex < SubstepCount; ++substepIndex)
            {
                SubstepStarted?.Invoke(substepIndex, dt, threadDispatcher);
                if (substepIndex > 0)
                {
                    //This takes the place of collision detection for the substeps. It uses the current velocity to update penetration depths.
                    //It's definitely an approximation, but it's important for avoiding some obviously weird behavior.
                    //Note that we do not run this on the first iteration- the actual collision detection above takes care of it.
                    simulation.IncrementallyUpdateContactConstraints(substepDt, threadDispatcher);
                    ContactConstraintsUpdatedForSubstep?.Invoke(substepIndex, dt, threadDispatcher);
                }
                simulation.IntegrateVelocitiesAndUpdateInertias(substepDt, threadDispatcher);
                VelocitiesIntegrated?.Invoke(substepIndex, dt, threadDispatcher);

                simulation.Solve(substepDt, threadDispatcher);
                ConstraintsSolved?.Invoke(substepIndex, dt, threadDispatcher);

                simulation.IntegratePoses(substepDt, threadDispatcher);
                PosesIntegrated?.Invoke(substepIndex, dt, threadDispatcher);
                SubstepEnded?.Invoke(substepIndex, dt, threadDispatcher);
            }
            SubstepsComplete?.Invoke(dt, threadDispatcher);

            simulation.IncrementallyOptimizeDataStructures(threadDispatcher);
        }