fix(animation): restore retail transition sign gates
Match the v11.4186 CSequence transition assembly and ACE cross-reference, including direction-specific pose gates and the strict physics epsilon boundary. Add conformance tests and correct the stale research interpretation. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
c5ab99081c
commit
31a0889f08
4 changed files with 155 additions and 42 deletions
|
|
@ -424,14 +424,13 @@ public sealed class CSequence
|
|||
|
||||
/// <summary>
|
||||
/// <c>CSequence::advance_to_next_animation</c> (0x005252b0, §23): four
|
||||
/// pose operations per transition — un-apply the outgoing node's pose
|
||||
/// (subtract1 + residual physics), step (forward wraps to
|
||||
/// <c>first_cyclic</c>; REVERSE wraps to the LIST TAIL — asymmetric by
|
||||
/// design), reseed <see cref="FrameNumber"/> from the incoming node's
|
||||
/// direction-aware boundary, apply the incoming pose (combine +
|
||||
/// physics). Pose ops run in BOTH directions (ACE's framerate-sign
|
||||
/// gates are ACE-isms; the decomp is unconditional apart from the
|
||||
/// degenerate-framerate guard).
|
||||
/// sign-gated pose operations per transition. Positive elapsed time
|
||||
/// removes only a reverse-playing outgoing pose, steps next (wrapping to
|
||||
/// <c>first_cyclic</c>), and applies only a forward-playing incoming pose.
|
||||
/// Negative elapsed time mirrors that over previous/list-tail. The
|
||||
/// framerate sign gates are present in matching v11.4186 assembly and in
|
||||
/// ACE's line-for-line port; physics has a separate strict
|
||||
/// <c>abs(framerate) > F_EPSILON</c> gate.
|
||||
/// </summary>
|
||||
public void AdvanceToNextAnimation(double timeElapsed, Frame? frame)
|
||||
{
|
||||
|
|
@ -440,39 +439,57 @@ public sealed class CSequence
|
|||
|
||||
var outgoing = _currAnim.Value;
|
||||
|
||||
// (a) un-apply the outgoing node's pose at the CURRENT FrameNumber.
|
||||
if (frame is not null && Math.Abs(outgoing.Framerate) >= FrameOps.FEpsilon)
|
||||
{
|
||||
var pos = outgoing.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Subtract1(frame, pos);
|
||||
ApplyPhysics(frame, 1.0 / outgoing.Framerate, timeElapsed);
|
||||
}
|
||||
|
||||
// (b) step; (c) reseed FrameNumber from the incoming boundary.
|
||||
if (timeElapsed >= 0.0)
|
||||
{
|
||||
if (frame is not null && outgoing.Framerate < 0f)
|
||||
{
|
||||
var pos = outgoing.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Subtract1(frame, pos);
|
||||
if (Math.Abs(outgoing.Framerate) > FrameOps.FEpsilon)
|
||||
ApplyPhysics(frame, 1.0 / outgoing.Framerate, timeElapsed);
|
||||
}
|
||||
|
||||
_currAnim = _currAnim.Next ?? _firstCyclic;
|
||||
if (_currAnim is null)
|
||||
return;
|
||||
FrameNumber = _currAnim.Value.GetStartingFrame();
|
||||
|
||||
var incoming = _currAnim.Value;
|
||||
if (frame is not null && incoming.Framerate > 0f)
|
||||
{
|
||||
var pos = incoming.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Combine(frame, pos);
|
||||
if (Math.Abs(incoming.Framerate) > FrameOps.FEpsilon)
|
||||
ApplyPhysics(frame, 1.0 / incoming.Framerate, timeElapsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frame is not null && outgoing.Framerate >= 0f)
|
||||
{
|
||||
var pos = outgoing.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Subtract1(frame, pos);
|
||||
if (Math.Abs(outgoing.Framerate) > FrameOps.FEpsilon)
|
||||
ApplyPhysics(frame, 1.0 / outgoing.Framerate, timeElapsed);
|
||||
}
|
||||
|
||||
_currAnim = _currAnim.Previous ?? _animList.Last;
|
||||
if (_currAnim is null)
|
||||
return;
|
||||
FrameNumber = _currAnim.Value.GetEndingFrame();
|
||||
}
|
||||
|
||||
// (d) apply the incoming node's pose at the new FrameNumber.
|
||||
var incoming = _currAnim.Value;
|
||||
if (frame is not null && Math.Abs(incoming.Framerate) >= FrameOps.FEpsilon)
|
||||
{
|
||||
var pos = incoming.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Combine(frame, pos);
|
||||
ApplyPhysics(frame, 1.0 / incoming.Framerate, timeElapsed);
|
||||
var incoming = _currAnim.Value;
|
||||
if (frame is not null && incoming.Framerate < 0f)
|
||||
{
|
||||
var pos = incoming.GetPosFrame((int)FrameNumber);
|
||||
if (pos is not null)
|
||||
FrameOps.Combine(frame, pos);
|
||||
if (Math.Abs(incoming.Framerate) > FrameOps.FEpsilon)
|
||||
ApplyPhysics(frame, 1.0 / incoming.Framerate, timeElapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue