Skip to content

Commit 12c8b25

Browse files
authored
fix: Bepu transformation for bodies children of bodies (#3013)
1 parent 1412215 commit 12c8b25

File tree

1 file changed

+81
-66
lines changed

1 file changed

+81
-66
lines changed

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs

Lines changed: 81 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)