@@ -557,7 +557,7 @@ public ConversionEnum<ManagedConverter, OverlapInfoStack, OverlapInfo> OverlapIn
557557 /// <summary>
558558 /// Called by the BroadPhase.GetOverlaps to collect all encountered collidables.
559559 /// </summary>
560- struct BroadPhaseOverlapEnumerator : IBreakableForEach < CollidableReference >
560+ private struct BroadPhaseOverlapEnumerator : IBreakableForEach < CollidableReference >
561561 {
562562 public QuickList < CollidableReference > References ;
563563 //The enumerator never gets stored into unmanaged memory, so it's safe to include a reference type instance.
@@ -574,7 +574,7 @@ public bool LoopBody(CollidableReference reference)
574574 /// <summary>
575575 /// Provides callbacks for filtering and data collection to the CollisionBatcher we'll be using to test query shapes against the detected environment.
576576 /// </summary>
577- struct BatcherCallbacks < T > : ICollisionCallbacks where T : IOverlapCollector
577+ private struct BatcherCallbacks < T > : ICollisionCallbacks where T : IOverlapCollector
578578 {
579579 public required CollisionMask CollisionMask ;
580580 public required QuickList < CollidableReference > References ;
@@ -605,7 +605,7 @@ public void OnPairCompleted<TManifold>(int pairId, ref TManifold manifold) where
605605 }
606606 }
607607
608- unsafe void OverlapInner < TShape , TCollector > ( in TShape shape , in SRigidPose pose , CollisionMask collisionMask , ref TCollector collector ) where TShape : unmanaged, IConvexShape where TCollector : IOverlapCollector
608+ private unsafe void OverlapInner < TShape , TCollector > ( in TShape shape , in SRigidPose pose , CollisionMask collisionMask , ref TCollector collector ) where TShape : unmanaged, IConvexShape where TCollector : IOverlapCollector
609609 {
610610 fixed ( TShape * queryShapeData = & shape )
611611 {
@@ -749,49 +749,14 @@ internal void Update(TimeSpan elapsed)
749749
750750 private void SyncActiveTransformsWithPhysics ( )
751751 {
752+ var job = new SyncTransformsJob ( Simulation . Bodies , this ) ;
752753 if ( ParallelUpdate )
753754 {
754- Dispatcher . For ( 0 , Simulation . Bodies . ActiveSet . Count , ( i ) => SyncTransformsWithPhysics ( Simulation . Bodies . GetBodyReference ( Simulation . Bodies . ActiveSet . IndexToHandle [ i ] ) , this ) ) ;
755+ Dispatcher . ForBatched ( Simulation . Bodies . ActiveSet . Count , job ) ;
755756 }
756757 else
757758 {
758- for ( int i = 0 ; i < Simulation . Bodies . ActiveSet . Count ; i ++ )
759- {
760- SyncTransformsWithPhysics ( Simulation . Bodies . GetBodyReference ( Simulation . Bodies . ActiveSet . IndexToHandle [ i ] ) , this ) ;
761- }
762- }
763-
764- static void SyncTransformsWithPhysics ( in BodyReference body , BepuSimulation bepuSim )
765- {
766- var collidable = bepuSim . GetComponent ( body . Handle ) ;
767-
768- for ( var item = collidable . Parent ; item != null ; item = item . Parent )
769- {
770- if ( item . BodyReference is { } bRef )
771- {
772- // Have to go through our parents to make sure they're up to date since we're reading from the parent's world matrix
773- // This means that we're potentially updating bodies that are not part of the active set but checking that may be more costly than just doing the thing
774- SyncTransformsWithPhysics ( bRef , bepuSim ) ;
775- // This can be slower than expected when we have multiple collidables as parents recursively since we would recompute the topmost collidable n times, the second topmost n-1 etc.
776- // It's not that likely but should still be documented as suboptimal somewhere
777- item . Entity . Transform . Parent . UpdateWorldMatrix ( ) ;
778- }
779- }
780-
781- var localPosition = body . Pose . Position . ToStride ( ) ;
782- var localRotation = body . Pose . Orientation . ToStride ( ) ;
783-
784- var entityTransform = collidable . Entity . Transform ;
785- if ( entityTransform . Parent is { } parent )
786- {
787- parent . WorldMatrix . Decompose ( out Vector3 _ , out Quaternion parentEntityRotation , out Vector3 parentEntityPosition ) ;
788- var iRotation = Quaternion . Invert ( parentEntityRotation ) ;
789- localPosition = Vector3 . Transform ( localPosition - parentEntityPosition , iRotation ) ;
790- localRotation = localRotation * iRotation ;
791- }
792-
793- entityTransform . Rotation = localRotation ;
794- entityTransform . Position = localPosition - Vector3 . Transform ( collidable . CenterOfMass , localRotation ) ;
759+ job . Process ( 0 , Simulation . Bodies . ActiveSet . Count ) ;
795760 }
796761 }
797762
@@ -801,30 +766,63 @@ private void InterpolateTransforms()
801766 // a value of 0.5 means that we're halfway to the next physics update, just have to wait for the same amount of time.
802767 var interpolationFactor = ( float ) ( _remainingUpdateTime . TotalSeconds / FixedTimeStep . TotalSeconds ) ;
803768 interpolationFactor = MathF . Min ( interpolationFactor , 1f ) ;
769+ var job = new InterpolateTransformsJob ( interpolationFactor , _interpolatedBodies ) ;
804770 if ( ParallelUpdate )
805771 {
806- Dispatcher . For ( 0 , _interpolatedBodies . Count , i => InterpolateBody ( _interpolatedBodies [ i ] , interpolationFactor ) ) ;
772+ Dispatcher . ForBatched ( _interpolatedBodies . Count , job ) ;
807773 }
808774 else
809775 {
810- foreach ( var body in _interpolatedBodies )
776+ job . Process ( 0 , _interpolatedBodies . Count ) ;
777+ }
778+ }
779+
780+ internal void Register ( ISimulationUpdate simulationUpdateComponent )
781+ {
782+ Elider . AddToHandlers ( simulationUpdateComponent , _simulationUpdateComponents ) ;
783+ }
784+
785+ internal bool Unregister ( ISimulationUpdate simulationUpdateComponent )
786+ {
787+ return Elider . RemoveFromHandlers ( simulationUpdateComponent , _simulationUpdateComponents ) ;
788+ }
789+
790+ internal void RegisterInterpolated ( BodyComponent body )
791+ {
792+ _interpolatedBodies . Add ( body ) ;
793+
794+ Debug . Assert ( body . BodyReference . HasValue ) ;
795+
796+ body . PreviousPose = body . CurrentPose = body . BodyReference . Value . Pose ;
797+ }
798+
799+ internal void UnregisterInterpolated ( BodyComponent body )
800+ {
801+ _interpolatedBodies . Remove ( body ) ;
802+ }
803+
804+ private readonly struct InterpolateTransformsJob ( float interpolationFactor , List < BodyComponent > bodies ) : Dispatcher . IBatchJob
805+ {
806+ public void Process ( int start , int endExclusive )
807+ {
808+ for ( int i = start ; i < endExclusive ; i ++ )
811809 {
812- InterpolateBody ( body , interpolationFactor ) ;
810+ InterpolateBody ( bodies [ i ] , interpolationFactor ) ;
813811 }
814812 }
815813
816- static void InterpolateBody ( BodyComponent body , float interpolationFactor )
814+ private static void InterpolateBody ( BodyComponent body , float interpolationFactor )
817815 {
818816 // Have to go through our parents to make sure they're up-to-date since we're reading from the parent's world matrix
819817 // This means that we're potentially updating bodies that are not part of the active set but checking that may be more costly than just doing the thing
820- for ( var item = body . Parent ; item != null ; item = item . Parent )
818+ for ( var parent = body . Parent ; parent != null ; parent = parent . Parent )
821819 {
822- if ( item is BodyComponent parentBody && parentBody . InterpolationMode != InterpolationMode . None )
820+ if ( parent . InterpolationMode != InterpolationMode . None )
823821 {
824- InterpolateBody ( parentBody , interpolationFactor ) ; // This one will take care of his parents too.
822+ InterpolateBody ( parent , interpolationFactor ) ; // This one will take care of his parents too.
825823 // This can be slower than expected when we have multiple collidables as parents recursively since we would recompute the topmost collidable n times, the second topmost n-1 etc.
826824 // It's not that likely but should still be documented as suboptimal somewhere
827- parentBody . Entity . Transform . Parent . UpdateWorldMatrix ( ) ;
825+ parent . Entity . Transform . UpdateWorldMatrix ( ) ;
828826 break ;
829827 }
830828 }
@@ -842,28 +840,45 @@ static void InterpolateBody(BodyComponent body, float interpolationFactor)
842840 }
843841 }
844842
845- internal void Register ( ISimulationUpdate simulationUpdateComponent )
843+ private readonly struct SyncTransformsJob ( Bodies bodies , BepuSimulation bepuSimulation ) : Dispatcher . IBatchJob
846844 {
847- Elider . AddToHandlers ( simulationUpdateComponent , _simulationUpdateComponents ) ;
848- }
849-
850- internal bool Unregister ( ISimulationUpdate simulationUpdateComponent )
851- {
852- return Elider . RemoveFromHandlers ( simulationUpdateComponent , _simulationUpdateComponents ) ;
853- }
845+ public void Process ( int start , int endExclusive )
846+ {
847+ for ( int i = start ; i < endExclusive ; i ++ )
848+ {
849+ var bepuBody = bodies . GetBodyReference ( bodies . ActiveSet . IndexToHandle [ i ] ) ;
850+ var strideBody = bepuSimulation . GetComponent ( bepuBody ) ;
851+ SyncTransformsWithPhysics ( bepuBody , strideBody ) ;
852+ }
853+ }
854854
855- internal void RegisterInterpolated ( BodyComponent body )
856- {
857- _interpolatedBodies . Add ( body ) ;
855+ private static void SyncTransformsWithPhysics ( in BodyReference body , BodyComponent component )
856+ {
857+ if ( component . Parent ? . BodyReference is { } bRef )
858+ {
859+ // Have to go through our parents to make sure they're up to date since we're reading from the parent's world matrix
860+ // This means that we're potentially updating bodies that are not part of the active set but checking that may be more costly than just doing the thing
861+ SyncTransformsWithPhysics ( bRef , component . Parent ) ;
862+ // This can be slower than expected when we have multiple collidables as parents recursively since we would recompute the topmost collidable n times, the second topmost n-1 etc.
863+ // It's not that likely but should still be documented as suboptimal somewhere
864+ component . Parent . Entity . Transform . UpdateWorldMatrix ( ) ;
865+ }
858866
859- Debug . Assert ( body . BodyReference . HasValue ) ;
867+ var localPosition = body . Pose . Position . ToStride ( ) ;
868+ var localRotation = body . Pose . Orientation . ToStride ( ) ;
860869
861- body . PreviousPose = body . CurrentPose = body . BodyReference . Value . Pose ;
862- }
870+ var entityTransform = component . Entity . Transform ;
871+ if ( entityTransform . Parent is { } parent )
872+ {
873+ parent . WorldMatrix . Decompose ( out Vector3 _ , out Quaternion parentEntityRotation , out Vector3 parentEntityPosition ) ;
874+ var iRotation = Quaternion . Invert ( parentEntityRotation ) ;
875+ localPosition = Vector3 . Transform ( localPosition - parentEntityPosition , iRotation ) ;
876+ localRotation = localRotation * iRotation ;
877+ }
863878
864- internal void UnregisterInterpolated ( BodyComponent body )
865- {
866- _interpolatedBodies . Remove ( body ) ;
879+ entityTransform . Rotation = localRotation ;
880+ entityTransform . Position = localPosition - Vector3 . Transform ( component . CenterOfMass , localRotation ) ;
881+ }
867882 }
868883
869884 /// <summary>
0 commit comments