use std::collections::VecDeque;
use std::time::{Duration, Instant};
use crate::Context;
#[derive(Debug, Copy, Clone)]
#[cfg_attr(
feature = "serde_support",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Timestep {
Fixed(f64),
Variable,
}
struct FixedTimeStepState {
ticks_per_second: f64,
tick_rate: Duration,
accumulator: Duration,
}
pub(crate) struct TimeContext {
timestep: Option<FixedTimeStepState>,
fps_tracker: VecDeque<f64>,
last_time: Instant,
elapsed: Duration,
}
impl TimeContext {
pub(crate) fn new(timestep: Timestep) -> TimeContext {
let mut fps_tracker = VecDeque::with_capacity(200);
fps_tracker.resize(200, 1.0 / 60.0);
TimeContext {
timestep: create_timestep_state(timestep),
fps_tracker,
last_time: Instant::now(),
elapsed: Duration::from_secs(0),
}
}
}
pub(crate) fn reset(ctx: &mut Context) {
ctx.time.last_time = Instant::now();
if let Some(fixed) = &mut ctx.time.timestep {
fixed.accumulator = Duration::from_secs(0);
}
}
pub(crate) fn tick(ctx: &mut Context) {
let current_time = Instant::now();
ctx.time.elapsed = current_time - ctx.time.last_time;
ctx.time.last_time = current_time;
if let Some(fixed) = &mut ctx.time.timestep {
fixed.accumulator += ctx.time.elapsed;
}
ctx.time.fps_tracker.pop_front();
ctx.time
.fps_tracker
.push_back(ctx.time.elapsed.as_secs_f64());
}
pub(crate) fn is_fixed_update_ready(ctx: &mut Context) -> bool {
match &mut ctx.time.timestep {
Some(fixed) if fixed.accumulator >= fixed.tick_rate => {
fixed.accumulator -= fixed.tick_rate;
true
}
_ => false,
}
}
pub fn get_delta_time(ctx: &Context) -> Duration {
ctx.time.elapsed
}
pub fn get_accumulator(ctx: &Context) -> Duration {
match &ctx.time.timestep {
Some(fixed) => fixed.accumulator,
None => Duration::from_secs(0),
}
}
pub fn get_blend_factor(ctx: &Context) -> f32 {
match &ctx.time.timestep {
Some(fixed) => fixed.accumulator.as_secs_f32() / fixed.tick_rate.as_secs_f32(),
None => 0.0,
}
}
pub fn get_blend_factor_precise(ctx: &Context) -> f64 {
match &ctx.time.timestep {
Some(fixed) => fixed.accumulator.as_secs_f64() / fixed.tick_rate.as_secs_f64(),
None => 0.0,
}
}
pub fn get_timestep(ctx: &Context) -> Timestep {
match &ctx.time.timestep {
Some(fixed) => Timestep::Fixed(fixed.ticks_per_second),
None => Timestep::Variable,
}
}
pub fn set_timestep(ctx: &mut Context, timestep: Timestep) {
ctx.time.timestep = create_timestep_state(timestep);
}
fn create_timestep_state(timestep: Timestep) -> Option<FixedTimeStepState> {
match timestep {
Timestep::Fixed(ticks_per_second) => Some(FixedTimeStepState {
ticks_per_second,
tick_rate: Duration::from_secs_f64(1.0 / ticks_per_second),
accumulator: Duration::from_secs(0),
}),
Timestep::Variable => None,
}
}
pub fn get_fps(ctx: &Context) -> f64 {
1.0 / (ctx.time.fps_tracker.iter().sum::<f64>() / ctx.time.fps_tracker.len() as f64)
}