fix(animation): harden presentation phase handoffs

Own every borrowed PartArray frame across callbacks, version presentation state across appearance rebinding, reject recursive phase consumption, and preserve static MotionDone when only a stale visual pose is discarded.
This commit is contained in:
Erik 2026-07-21 10:01:25 +02:00
parent a2fd61684a
commit 833520253a
10 changed files with 657 additions and 140 deletions

View file

@ -139,13 +139,18 @@ internal sealed class LiveEntityAnimationScheduler
projectile,
objectClockEpoch))
{
IReadOnlyList<PartTransform>? ownedFrames = schedule.SequenceFrames is { } produced
? animation.CaptureScheduleFrames(produced)
: null;
_schedules[entity.Id] = schedule with
{
SequenceFrames = ownedFrames,
Record = record,
Entity = entity,
Animation = animation,
ObjectClockEpoch = objectClockEpoch,
ProjectionMutationVersion = record.ProjectionMutationVersion,
PresentationRevision = animation.PresentationRevision,
};
}
}
@ -217,7 +222,9 @@ internal sealed class LiveEntityAnimationScheduler
}
return new LiveEntityAnimationSchedule(
prepared ?? (composeHidden ? sequencer?.SampleCurrentPose() : null),
prepared ?? (composeHidden && sequencer is not null
? animation?.CaptureSequenceFrames(sequencer.SampleCurrentPose())
: null),
0f,
ComposeParts: advanced || composeHidden);
}
@ -311,7 +318,9 @@ internal sealed class LiveEntityAnimationScheduler
rootFrame.Origin = Vector3.Zero;
rootFrame.Orientation = Quaternion.Identity;
if (sequencer is not null)
frames = sequencer.Advance(quantum, rootFrame);
frames = animation?.CaptureSequenceFrames(
sequencer.Advance(quantum, rootFrame))
?? sequencer.Advance(quantum, rootFrame);
else if (animation is not null)
legacyElapsed += quantum;
@ -441,7 +450,9 @@ internal sealed class LiveEntityAnimationScheduler
if (hidden)
{
return new LiveEntityAnimationSchedule(
sequencer?.SampleCurrentPose(),
sequencer is not null && animation is not null
? animation.CaptureSequenceFrames(sequencer.SampleCurrentPose())
: null,
0f,
ComposeParts: animation is not null);
}
@ -531,4 +542,5 @@ internal readonly record struct LiveEntityAnimationSchedule(
WorldEntity? Entity = null,
LiveEntityAnimationState? Animation = null,
ulong ObjectClockEpoch = 0UL,
ulong ProjectionMutationVersion = 0UL);
ulong ProjectionMutationVersion = 0UL,
ulong PresentationRevision = 0UL);