Editor/HumanoidMocap/MocapPlayback.cs
using System;
using Editor;
using Sandbox;
namespace HumanoidMocap.Editor;
public sealed partial class RetargetWindow
{
double _playhead;
float _previewFps=30;
bool _playing=true;
IconButton _playButton;
(double Start,double Last,double End) PlaybackRange
{
get
{
if(_editedMotion is { Frames.Count: > 0 } motion)
return(motion.Frames[0].Time,motion.Frames[^1].Time,motion.Frames[^1].Time+1/motion.SourceFps);
var duration=Math.Max(0,_video?.Duration??0);
return(0,duration,duration);
}
}
internal double PlaybackTime=>_playhead;
internal double? SourcePlaybackTime=>_video?.FrameTime;
internal bool SourcePlaybackPaused=>!_playing;
internal string PlayButtonIcon=>_playButton.Icon;
internal bool PlayButtonIsIconButton=>_playButton is IconButton;
internal byte[] SourceFramePng()=>_video?.FramePng();
internal void SeekPlaybackFraction(float fraction)
{
var range=PlaybackRange;_playing=false;
_playhead=range.Start+Math.Clamp(fraction,0,1)*(range.Last-range.Start);
_video?.Request(_playhead);UpdateTransport();SynchronizePreview();
}
internal void TogglePlayback()
{
_playing=!_playing;
if(_playing&&_playhead>=PlaybackRange.Last)_playhead=PlaybackRange.Start;
UpdateTransport();
}
void ResetPlayback()
{
_playing=true;_playhead=PlaybackRange.Start;
_video?.Request(_playhead);UpdateTransport();
}
void TickPlayback()
{
var range=PlaybackRange;
if(_playing&&range.End>range.Start)
{
_playhead+=RealTime.Delta;
if(_playhead>=range.End)_playhead=range.Start+(_playhead-range.Start)%(range.End-range.Start);
}
_video?.Request(Math.Clamp(_playhead,range.Start,range.Last));
UpdateTransport();SynchronizePreview();
}
void UpdateTransport()
{
var range=PlaybackRange;
_clock.Text=$"{_playhead:F2} s";
_timeline.Value=range.Last>range.Start?(float)Math.Clamp((_playhead-range.Start)/(range.Last-range.Start),0,1):0;
_contactTimeline?.SetPlayhead(_playhead);
_playButton.Icon=_playing?"pause":"play_arrow";_playButton.Update();
}
void SynchronizePreview()
{
if(!_mocapPreview.IsValid()||_editedMotion is null)return;
// Follow the frame actually on screen when decoding is slower than playback.
var time=_video?.FrameTime??_playhead;
_mocapPreview.Scrub((int)Math.Round((time-_editedMotion.Frames[0].Time)*_previewFps));
_mocapPreview.ApplyCurrentFrame();
UpdateTrackingStatus();
}
}