From ca7ae45518f8e51a70da0d3e5302e6337704894f Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 13 Apr 2026 12:31:32 +0200 Subject: [PATCH] fix(anim): handle reversed frame range in BuildBlendedFrame Backward-playback nodes (TurnLeft, SideStepLeft) have LowFrame > HighFrame after multiply_framerate swaps them. Math.Clamp(x, 19, 0) throws because min > max. Fix: compute rangeLo/rangeHi from Min/Max of Low/High, use those for clamping. Also step nextIdx in the playback direction (forward +1, backward -1) instead of always +1. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Physics/AnimationSequencer.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/AcDream.Core/Physics/AnimationSequencer.cs b/src/AcDream.Core/Physics/AnimationSequencer.cs index 4c2226f..12f1abf 100644 --- a/src/AcDream.Core/Physics/AnimationSequencer.cs +++ b/src/AcDream.Core/Physics/AnimationSequencer.cs @@ -487,11 +487,26 @@ public sealed class AnimationSequencer int numPartFrames = curr.Anim.PartFrames.Count; int frameIdx = (int)Math.Floor(_frameNum); - frameIdx = Math.Clamp(frameIdx, curr.LowFrame, Math.Min(curr.HighFrame, numPartFrames - 1)); + // For backward playback, LowFrame > HighFrame. Use actual min/max + // of the two to get a valid range for clamping. + int rangeLo = Math.Min(curr.LowFrame, curr.HighFrame); + int rangeHi = Math.Min(Math.Max(curr.LowFrame, curr.HighFrame), numPartFrames - 1); + frameIdx = Math.Clamp(frameIdx, rangeLo, rangeHi); - int nextIdx = frameIdx + 1; - if (nextIdx > curr.HighFrame || nextIdx >= numPartFrames) - nextIdx = curr.LowFrame; + // Next frame for interpolation: step in the playback direction. + int nextIdx; + if (curr.Framerate >= 0f) + { + nextIdx = frameIdx + 1; + if (nextIdx > rangeHi || nextIdx >= numPartFrames) + nextIdx = rangeLo; // wrap forward + } + else + { + nextIdx = frameIdx - 1; + if (nextIdx < rangeLo) + nextIdx = rangeHi; // wrap backward + } float t = _frameNum - (float)Math.Floor(_frameNum); if (t < 0f) t = 0f;