Skip to content

[Enhancement] Add animation playback helpers for synchronized gameplay - #1316

Merged
untoldengine merged 3 commits into
developfrom
enhancement/1314
Oct 7, 2026
Merged

untoldengine merged 3 commits into
developfrom
enhancement/1314

Conversation

@untoldengine

Copy link
Copy Markdown
Owner

Summary

  • Adds restartAnimation, getAnimationPlaybackTime, and sampleAnimationJointPosition so callers can replay a one-shot clip from time zero, read its current playback time, and sample a named joint in world space at an arbitrary time without changing the displayed pose.
  • Fixes ClipSampler.sample() unconditionally wrapping time via fmod, even for clips whose channels are flagged non-repeating — this defeated the existing per-channel hold-last-pose logic and caused a completed one-shot clip (e.g. a kick) to jump back to its initial pose. Non-repeating clips now clamp to [0, duration]; repeating clips still wrap as before.
  • restartAnimation preserves warmed compiled-pose caches unless the clip's looping configuration actually changes.

Fixes #1314.

Test plan

  • swift build
  • swift test --filter AnimationCompiledSamplerTests — 18/18 pass, including the 4 new tests (final-pose holding, restart reset/rearm, contact-joint world-space sampling without disturbing playback, compiled-cache preservation/invalidation)
  • swift test --filter "AnimationSystemTest|FootIK|MotionMatching|PoseLayer|RootMotion|MLDeformer" — 70/70 pass
  • swift test --filter UntoldEngineTests (full unit target) — 1628/1628 pass (2 pre-existing skips)
  • Headset validation of synchronized repeated kicks in ImmersiveGol (prior validation reported in the issue; not rerun as part of this PR)

Adds restartAnimation, getAnimationPlaybackTime, and
sampleAnimationJointPosition so callers can replay a one-shot clip
from time zero, read its current playback time, and sample a named
joint in world space at an arbitrary time without changing the
displayed pose.

ClipSampler.sample() also unconditionally wrapped time via fmod, even
for clips whose channels were flagged non-repeating, defeating the
per-channel hold-last-pose logic and causing a completed one-shot
clip to jump back to its initial pose. It now clamps to [0, duration]
when no animated channel repeats, and only wraps for repeating clips.

restartAnimation preserves warmed compiled-pose caches unless the
clip's looping configuration actually changes.

Fixes #1314.
applyRootMotion computed its own channelTime via fmod(currentTime,
clipDuration), unconditionally, regardless of whether the clip's
channels repeat. Once currentTime passed a non-repeating clip's
duration, this wrapped back near zero, which wrappedChannelTime then
misread as a fresh loop and injected a full loop's root displacement
at that instant -- a visible snap right as a one-shot animation (e.g.
a kick) finished, even though ClipSampler already holds the displayed
pose correctly past that point.

Match ClipSampler: clamp to [0, duration] when no animated channel
repeats, only wrap when at least one does.
@untoldengine
untoldengine merged commit ec141a0 into develop Oct 7, 2026
4 checks passed
@untoldengine
untoldengine deleted the enhancement/1314 branch October 7, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement]Add animation playback helpers for synchronized gameplay and hold non-looping final poses

1 participant