From 868413839a41e217bb7973fb9d9c0433e6a76d63 Mon Sep 17 00:00:00 2001 From: Federico Andres Date: Tue, 11 Aug 2026 16:13:19 -0300 Subject: [PATCH] Fix glTF pose sampled at the end of an animation returning a pose from the start (#6048) GetPoseAtTimeGLTF searches for the interval containing the requested time with (tstart <= time) && (time < tend). A time equal to the last keyframe satisfies no interval, so the loop ends without a match and keyframe stays at its 0 default, making the function return a pose from the beginning of the animation. LoadModelAnimationsGLTF samples at t = j/60 for j in [0, keyframeCount), with keyframeCount = (int)(duration*60) + 1, so the last sample lands exactly on the end of the animation whenever duration*60 is a whole number. The last pose of those clips is wrong, which looks like a jerk right before the animation ends. Measured on a glTF with a 0.85 s clip: the delta between the last two poses was 23x the median delta between consecutive poses; with this change it is 0.3x, in line with every other step. --- src/rmodels.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index fb975f7b7..4dbfb4beb 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6390,6 +6390,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ float tstart = 0.0f; float tend = 0.0f; int keyframe = 0; // Defaults to first pose + bool found = false; for (int i = 0; i < (int)input->count - 1; i++) { @@ -6402,10 +6403,26 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ if ((tstart <= time) && (time < tend)) { keyframe = i; + found = true; break; } } + // No interval contains a time at (or past) the last keyframe, because the + // search above requires time < tend: clamp to the edge interval instead of + // falling back to keyframe 0, which returns a pose from the start + if (!found && ((int)input->count >= 2)) + { + keyframe = (int)input->count - 2; + + float tfirst = 0.0f; + if (!cgltf_accessor_read_float(input, 0, &tfirst, 1)) return false; + if (time < tfirst) keyframe = 0; + + if (!cgltf_accessor_read_float(input, keyframe, &tstart, 1)) return false; + if (!cgltf_accessor_read_float(input, keyframe + 1, &tend, 1)) return false; + } + // Constant animation, no need to interpolate if (FloatEquals(tend, tstart)) {