1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::*;

pub struct GameLoop<G, T: TimeTrait, W> {
    pub game: G,
    pub updates_per_second: u32,
    pub max_frame_time: f64,
    pub exit_next_iteration: bool,
    pub window: W,
    pub window_occluded: bool,

    fixed_time_step: f64,
    number_of_updates: u64,
    number_of_renders: u64,
    last_frame_time: f64,
    running_time: f64,
    accumulated_time: f64,
    blending_factor: f64,
    previous_instant: T,
    current_instant: T,
}

impl<G, T: TimeTrait, W> GameLoop<G, T, W> {
    pub fn new(game: G, updates_per_second: u32, max_frame_time: f64, window: W) -> Self {
        Self {
            game,
            updates_per_second,
            max_frame_time,
            window,
            window_occluded: false,
            exit_next_iteration: false,

            fixed_time_step: 1.0 / updates_per_second as f64,
            number_of_updates: 0,
            number_of_renders: 0,
            running_time: 0.0,
            accumulated_time: 0.0,
            blending_factor: 0.0,
            previous_instant: T::now(),
            current_instant: T::now(),
            last_frame_time: 0.0,
        }
    }

    pub fn next_frame<U, R>(&mut self, mut update: U, mut render: R) -> bool
        where U: FnMut(&mut GameLoop<G, T, W>),
              R: FnMut(&mut GameLoop<G, T, W>),
    {
        let g = self;

        if g.exit_next_iteration { return false; }

        g.current_instant = T::now();

        let mut elapsed = g.current_instant.sub(&g.previous_instant);
        if elapsed > g.max_frame_time { elapsed = g.max_frame_time; }

        g.last_frame_time = elapsed;
        g.running_time += elapsed;
        g.accumulated_time += elapsed;

        while g.accumulated_time >= g.fixed_time_step {
            update(g);

            g.accumulated_time -= g.fixed_time_step;
            g.number_of_updates += 1;
        }

        g.blending_factor = g.accumulated_time / g.fixed_time_step;

        if g.window_occluded && T::supports_sleep() {
            T::sleep(g.fixed_time_step);
        } else {
            render(g);
            g.number_of_renders += 1;
        }

        g.previous_instant = g.current_instant;

        true
    }

    pub fn re_accumulate(&mut self) {
        let g = self;

        g.current_instant = T::now();

        let prev_elapsed = g.last_frame_time;
        let new_elapsed = g.current_instant.sub(&g.previous_instant);

        let delta = new_elapsed - prev_elapsed;

        // We don't update g.last_frame_time since this additional time in the
        // render function is considered part of the current frame.

        g.running_time += delta;
        g.accumulated_time += delta;

        g.blending_factor = g.accumulated_time / g.fixed_time_step;
    }

    pub fn exit(&mut self) {
        self.exit_next_iteration = true;
    }

    pub fn set_updates_per_second(&mut self, new_updates_per_second: u32) {
        self.updates_per_second = new_updates_per_second;
        self.fixed_time_step = 1.0 / new_updates_per_second as f64;
    }

    pub fn fixed_time_step(&self) -> f64 {
        self.fixed_time_step
    }

    pub fn number_of_updates(&self) -> u64 {
        self.number_of_updates
    }

    pub fn number_of_renders(&self) -> u64 {
        self.number_of_renders
    }

    pub fn last_frame_time(&self) -> f64 {
        self.last_frame_time
    }

    pub fn running_time(&self) -> f64 {
        self.running_time
    }

    pub fn accumulated_time(&self) -> f64 {
        self.accumulated_time
    }

    pub fn blending_factor(&self) -> f64 {
        self.blending_factor
    }

    pub fn previous_instant(&self) -> T {
        self.previous_instant
    }

    pub fn current_instant(&self) -> T {
        self.current_instant
    }
}