pub use monotonic_clock::{
Clock, TimeStats, FrameRateCounter, FloatDuration, FixedStep, VariableStep, FloatInstant
};
use rin_events::PropertyLastValue;
use std::time::Duration;
#[cfg(feature="web")]
use std::time::Instant;
#[cfg(feature="web")]
use std::mem;
use rinecs::{EntitiesThreadLocal, ResourcesThreadLocal, SystemThreadLocal};
use crate::Bundle;
#[cfg(feature="gl")]
use color::consts::*;
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
use rin_window::{self as window, Window};
#[cfg(feature="gl")]
use rin_graphics::Mvp;
#[cfg(feature="gl")]
use rin_math::pnt2;
use rin_math::Pnt2;
use crate::DeferredScene;
#[cfg(gui)]
use rin_gui::{self as gui, ParameterGroup, EnumParam};
#[cfg(gui)]
use rin_events::{StreamExt, Property, RangedPropertyMut, RangedProperty, TryIter};
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
use rin_gl as gl;
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
use rinecs::system_thread_local;
#[cfg(gui)]
#[derive(EnumParam)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StepType{
Variable,
Fixed,
}
#[cfg(gui)]
#[derive(Clone, Copy, Debug)]
pub enum Step{
Variable,
Fixed(u32)
}
#[cfg(gui)]
#[derive(ParameterGroup)]
pub struct Time{
time_factor: gui::group_v::Tuple<(
gui::group_h::Tuple<(
Property<'static, ()>,
gui::SepH,
gui::Styled<RangedPropertyMut<'static, f64>>
)>,
gui::FontRelativeSep,
gui::group_h::Tuple<(
gui::Styled<String>,
gui::Styled<gui::AsControl<gui::TextBox<f64>, Property<'static, f64>>>,
)>
)>,
fps: RangedProperty<'static, f32>,
#[gui(not_parameter)]
fps_p: Property<'static, f32>,
time_last_frame: RangedProperty<'static, f32>,
time_last_frame_min: RangedProperty<'static, f32>,
time_last_frame_max: RangedProperty<'static, f32>,
#[gui(not_parameter)]
time_last_frame_p: Property<'static, f32>,
#[gui(not_parameter)]
time_last_frame_min_p: Property<'static, f32>,
#[gui(not_parameter)]
time_last_frame_max_p: Property<'static, f32>,
step_type: Property<'static, StepType>,
fixed_step: RangedPropertyMut<'static, u32>,
}
#[cfg(not(gui))]
pub struct Time{
}
impl Time {
#[cfg(gui)]
pub fn new() -> Time {
let reset_button = Property::new(());
let time_factor = reset_button.clone().stream().map(|_| 1.)
.to_property(1.);
let time_factor = RangedPropertyMut::from_property_log_scale(time_factor, 0. .. 10.);
let time_label = "time factor: ".to_string();
let fps_p = Property::new(60.);
let fps = RangedProperty::new(fps_p.clone(), 0. .. 120.);
let time_last_frame_p = Property::new(16.);
let time_last_frame = RangedProperty::new(time_last_frame_p.clone(), 0. .. 1000.);
let time_last_frame_min_p = Property::new(16.);
let time_last_frame_min = RangedProperty::new(time_last_frame_min_p.clone(), 0. .. 1000.);
let time_last_frame_max_p = Property::new(16.);
let time_last_frame_max = RangedProperty::new(time_last_frame_max_p.clone(), 0. .. 1000.);
let time_factor_p = time_factor.clone().into_property();
let time_factor_textbox = gui::styled(
gui::as_control!(gui::TextBox<f64>, time_factor_p),
serde_json::from_str(r#"{
"border": false
}"#).unwrap());
let time_factor = gui::styled(
time_factor,
serde_json::from_str(r#"{
"label_as_textbox": false
}"#).unwrap());
let time_label = gui::styled(
time_label,
serde_json::from_str(r#"{
"full_width": false
}"#).unwrap());
Time {
time_factor: gui::group_v!{
gui::group_h!(
reset_button,
gui::SepH(10.),
time_factor
),
gui::FontRelativeSep::VLineGap(0.5),
gui::group_h!(
time_label,
time_factor_textbox
)
},
fps,
fps_p,
time_last_frame,
time_last_frame_min,
time_last_frame_max,
time_last_frame_p,
time_last_frame_min_p,
time_last_frame_max_p,
step_type: Property::new(StepType::Variable),
fixed_step: RangedPropertyMut::new_log_scale(16, 0 .. 3000),
}
}
#[cfg(not(gui))]
pub fn new() -> Time{
Time{}
}
#[cfg(gui)]
fn parameters(&self) -> Parameters{
let tuple_h = &self.time_factor.parameters().0;
let mut time_factor = tuple_h.parameters().2.parameter()
.clone()
.into_property()
.into_property_last_value();
time_factor.last_value();
let fps = self.fps_p.clone();
let time_last_frame_min = self.time_last_frame_min_p.clone();
let time_last_frame_max = self.time_last_frame_max_p.clone();
let time_last_frame = self.time_last_frame_p.clone();
Parameters{
time_factor,
fps,
time_last_frame,
time_last_frame_min,
time_last_frame_max,
time_last_frame_min_current: None,
time_last_frame_max_current: None,
time_last_second: None,
step_type: self.step_type.clone(),
fixed_step: self.fixed_step.clone().into_property(),
step: self.step_type.clone().zip(self.fixed_step.clone().into_property())
.map(|(ty, fixed)|{
match ty{
StepType::Variable => Step::Variable,
StepType::Fixed => Step::Fixed(fixed)
}
})
}
}
}
#[cfg(gui)]
pub struct Parameters{
pub time_factor: PropertyLastValue<'static, f64>,
fps: Property<'static, f32>,
time_last_frame: Property<'static, f32>,
time_last_frame_min: Property<'static, f32>,
time_last_frame_max: Property<'static, f32>,
time_last_frame_min_current: Option<f32>,
time_last_frame_max_current: Option<f32>,
time_last_second: Option<FloatDuration>,
step: Property<'static, Step>,
pub step_type: Property<'static, StepType>,
pub fixed_step: Property<'static, u32>,
}
impl Bundle for Time{
type Parameters = Self;
fn parameters(&self) -> Option<&Self> {
Some(self)
}
fn name(&self) -> &str{
"time"
}
fn setup(self, world: &mut DeferredScene){
#[cfg(not(feature="web"))]
world.add_resource(Clock::new());
#[cfg(feature="web")]
world.add_resource(Clock::new_with_wall_time(FloatInstant::from_secs_f64(0.)));
world.add_resource(FrameRateCounter::new(Duration::from_secs(2)));
#[cfg(gui)]
world.add_resource_thread_local(self.parameters());
}
}
pub(crate) fn reset_clock(resources: ResourcesThreadLocal){
let mut clock = resources.get_mut::<Clock>().unwrap();
let mut step = monotonic_clock::FixedStep::new(FloatDuration::from_nanoseconds(0));
#[cfg(feature="web")]
let now = FloatInstant::from_secs_f64(0.);
#[cfg(gui)]
{
let mut parameters = resources.get_mut::<Parameters>().unwrap();
if let Some(factor) = parameters.time_factor.last_value() {
clock.set_multiplier(*factor);
}
#[cfg(not(feature="web"))]
clock.tick(&mut step);
#[cfg(feature="web")]
clock.tick_with_wall_time(&mut step, now);
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
let fps = resources.get::<FrameRateCounter>().unwrap().fps() as f32;
parameters.fps.set(fps);
parameters.time_last_second = Some(clock.wall_time());
let t_last_frame = clock.wall_time_delta().as_milliseconds() as f32;
parameters.time_last_frame.set(t_last_frame);
parameters.time_last_frame_min.set(t_last_frame);
parameters.time_last_frame_max.set(t_last_frame);
parameters.time_last_frame_min_current = Some(t_last_frame);
}
#[cfg(not(gui))]
{
#[cfg(not(feature="web"))]
clock.tick(&mut step);
#[cfg(feature="web")]
clock.tick_with_wall_time(&mut step, now);
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
}
}
pub(crate) fn update_clock(resources: ResourcesThreadLocal){
let mut clock = resources.get_mut::<Clock>().unwrap();
#[cfg(gui)]
{
let mut parameters = resources.get_mut::<Parameters>().unwrap();
if let Some(factor) = parameters.time_factor.last_value() {
clock.set_multiplier(*factor);
}
#[cfg(not(feature="web"))]
{
if let Step::Fixed(step) = *parameters.step.get() {
let step = FloatDuration::from_milliseconds(step as u64);
clock.tick(&mut monotonic_clock::FixedStep::new(step))
}else{
clock.tick(&mut monotonic_clock::VariableStep::new())
}
}
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
let t_to_second = clock.wall_time() - *parameters.time_last_second.get_or_insert(clock.wall_time());
let t_last_frame = clock.wall_time_delta().as_milliseconds() as f32;
if t_to_second >= FloatDuration::from_seconds(1) {
let t_last_frame_min = parameters.time_last_frame_min_current.unwrap();
parameters.time_last_frame_min.set(t_last_frame_min);
let t_last_frame_max = parameters.time_last_frame_max_current.unwrap();
parameters.time_last_frame_max.set(t_last_frame_max);
parameters.time_last_frame_min_current = Some(t_last_frame);
parameters.time_last_frame_max_current = Some(t_last_frame);
parameters.time_last_second = Some(clock.wall_time());
}
let t_last_frame_min = parameters.time_last_frame_min_current.get_or_insert(t_last_frame);
*t_last_frame_min = t_last_frame_min.min(t_last_frame);
let t_last_frame_max = parameters.time_last_frame_max_current.get_or_insert(t_last_frame);
*t_last_frame_max = t_last_frame_max.max(t_last_frame);
parameters.time_last_frame.set(t_last_frame);
parameters.fps.set(resources.get::<FrameRateCounter>().unwrap().fps() as f32);
}
#[cfg(not(any(gui, feature="web")))]
{
clock.tick(&mut monotonic_clock::VariableStep::new());
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
}
}
#[cfg(feature="web")]
pub(crate) fn update_clock_with_wall_time(resources: ResourcesThreadLocal, wall_time_secs: f64){
let mut clock = resources.get_mut::<Clock>().unwrap();
let now = FloatInstant::from_secs_f64(wall_time_secs);
#[cfg(gui)]
{
let mut parameters = resources.get_mut::<Parameters>().unwrap();
if let Some(factor) = parameters.time_factor.last_value() {
clock.set_multiplier(*factor);
}
if let Step::Fixed(step) = *parameters.step.get() {
let step = FloatDuration::from_milliseconds(step as u64);
clock.tick_with_wall_time(&mut monotonic_clock::FixedStep::new(step), now)
}else{
clock.tick_with_wall_time(&mut monotonic_clock::VariableStep::new(), now)
}
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
parameters.fps.set(resources.get::<FrameRateCounter>().unwrap().fps() as f32);
parameters.time_last_frame.set(clock.wall_time_delta().as_milliseconds() as f32);
}
#[cfg(not(gui))]
{
clock.tick_with_wall_time(&mut monotonic_clock::VariableStep::new(), now);
resources.get_mut::<FrameRateCounter>()
.unwrap()
.tick(clock.instant_at_frame_start());
}
}
pub enum Placement{
TopRight,
BottomRight,
TopLeft,
BottomLeft,
Custom(Pnt2),
}
pub struct FpsRenderer{
placement: Placement
}
impl FpsRenderer{
pub fn new(placement: Placement) -> FpsRenderer{
FpsRenderer{ placement }
}
}
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
#[system_thread_local(name = "draw fps")]
#[cfg_attr(gui, after(crate::gui::gui_renderer))]
#[needs(Window, FrameRateCounter)]
#[reads(gl::Renderer)]
impl SystemThreadLocal for FpsRenderer{
fn run(&mut self, _entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let window = resources.as_trait::<dyn window::WindowExt>().unwrap();
let viewport = window.viewport();
let glin = glin.with_mvp(Mvp::ortho_top_left(window.viewport()));
let pos = match self.placement {
Placement::TopRight => pnt2(viewport.width as f32 - 60., 20.),
Placement::BottomRight => pnt2(viewport.width as f32 - 60., viewport.height as f32 - 20.),
Placement::TopLeft => pnt2(20., 20.),
Placement::BottomLeft => pnt2(20., viewport.height as f32 - 20.),
Placement::Custom(pos) => pos
};
if let Some(fps) = resources.get::<FrameRateCounter>().map(|counter| counter.fps()) {
glin.draw_bitmap_string(&format!("{:#.02}", fps), &pos, &WHITE);
}
}
}