2 Commits

Author SHA1 Message Date
d46a59367f [msbuild] MSVC x64 build compatibility support (#6052) (#6053)
Change EnableEnhancedInstructionSet to NotSet for compatibility old hardware.
2026-08-11 21:14:47 +02:00
868413839a 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.
2026-08-11 21:13:19 +02:00
2 changed files with 18 additions and 1 deletions

View File

@ -357,7 +357,7 @@
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\src\external\glfw\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\src\external\glfw\include</AdditionalIncludeDirectories>
<CompileAs>CompileAsC</CompileAs> <CompileAs>CompileAsC</CompileAs>
<DebugInformationFormat /> <DebugInformationFormat />
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet> <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>

View File

@ -6390,6 +6390,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_
float tstart = 0.0f; float tstart = 0.0f;
float tend = 0.0f; float tend = 0.0f;
int keyframe = 0; // Defaults to first pose int keyframe = 0; // Defaults to first pose
bool found = false;
for (int i = 0; i < (int)input->count - 1; i++) 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)) if ((tstart <= time) && (time < tend))
{ {
keyframe = i; keyframe = i;
found = true;
break; 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 // Constant animation, no need to interpolate
if (FloatEquals(tend, tstart)) if (FloatEquals(tend, tstart))
{ {