now supporting sim-time
This commit is contained in:
parent
65829c0d7c
commit
d56a673e70
188
src/main.rs
188
src/main.rs
@ -1,72 +1,18 @@
|
|||||||
use core::time;
|
use core::time;
|
||||||
use std::{ops::Bound, time::{Duration, Instant}};
|
use std::time::{Duration, Instant};
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
// loop constants
|
// sim constants
|
||||||
|
// update interval is the minimum delay (in milliseconds) between update ticks
|
||||||
const UPDATE_INTERVAL: u32 = 40;
|
const UPDATE_INTERVAL: u32 = 40;
|
||||||
|
// timescale is the rate of the simulation proportional to real-time
|
||||||
|
const TIMESCALE: f32 = 1.0;
|
||||||
|
|
||||||
// debug constants
|
// debug constants
|
||||||
const DEBUG_LOOP: bool = false;
|
const DEBUG_LOOP: bool = false;
|
||||||
const DEBUG_TIME: bool = false;
|
const DEBUG_TIME: bool = true;
|
||||||
const DEBUG_PARTICLES: bool = true;
|
|
||||||
|
|
||||||
// scene constants
|
|
||||||
const TIMESCALE: f32 = 1.0;
|
|
||||||
const SIM_BOUNDS: BoundingBox = BoundingBox {
|
|
||||||
size: [10.0, 10.0, 10.0],
|
|
||||||
offset: [-5.0, -5.0, 0.0]
|
|
||||||
};
|
|
||||||
const GRAVITY: f32 = -9.8;
|
|
||||||
const PARTICLE_COUNT: i32 = 1;
|
|
||||||
|
|
||||||
// a struct made for defining a bounding box
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct BoundingBox {
|
|
||||||
size: [f32; 3],
|
|
||||||
offset: [f32; 3]
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoundingBox {
|
|
||||||
// return the lower bounds
|
|
||||||
fn lower(&self) -> [f32; 3] {
|
|
||||||
self.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the upper bounds
|
|
||||||
fn upper(&self) -> [f32; 3] {
|
|
||||||
let mut bounds: [f32; 3] = [0.0; 3];
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
bounds[i] = self.size[i] + self.offset[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
bounds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct PlaneCollider {
|
|
||||||
location: [f32; 3],
|
|
||||||
normal: [f32; 3]
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlaneCollider {
|
|
||||||
// returns if a location is outside the plane's space (past the plane in the direction of the plane's normal)
|
|
||||||
fn is_outside(&self, point: [f32; 3]) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a struct made for physics particles
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Particle {
|
|
||||||
location: [f32; 3],
|
|
||||||
velocity: [f32; 3],
|
|
||||||
acceleration: [f32; 3],
|
|
||||||
gravity_enabled: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// allow the simulation to be stopped from within the loop (by setting simulate to false)
|
||||||
let mut simulate: bool = true;
|
let mut simulate: bool = true;
|
||||||
|
|
||||||
// start the clock to keep track of real time
|
// start the clock to keep track of real time
|
||||||
@ -74,122 +20,58 @@ fn main() {
|
|||||||
|
|
||||||
// keep track of the last tick time
|
// keep track of the last tick time
|
||||||
let mut last_tick = Instant::now();
|
let mut last_tick = Instant::now();
|
||||||
|
|
||||||
// a vector of particles
|
|
||||||
let mut particles: Vec<Particle> = vec![];
|
|
||||||
for i in 0..PARTICLE_COUNT {
|
|
||||||
// each particle spawned at a random location within the sim bounds
|
|
||||||
let new_particle = Particle {
|
|
||||||
location: [0.0, 0.0, 5.0],
|
|
||||||
velocity: [0.0; 3],
|
|
||||||
acceleration: random_vector3([100.0; 3], [-50.0; 3]),
|
|
||||||
gravity_enabled: false
|
|
||||||
};
|
|
||||||
|
|
||||||
particles.push(new_particle)
|
// real-time and sim-time clocks
|
||||||
}
|
let mut real_time = Duration::new(0, 0);
|
||||||
|
let mut sim_time = Duration::new(0, 0);
|
||||||
|
|
||||||
while simulate {
|
while simulate {
|
||||||
// update
|
// TODO - support frameskips
|
||||||
if delta_time(last_tick) >= UPDATE_INTERVAL {
|
if delta_time(last_tick) >= UPDATE_INTERVAL {
|
||||||
|
// update clocks
|
||||||
|
let elapsed_time = Instant::now().duration_since(last_tick);
|
||||||
|
real_time += elapsed_time;
|
||||||
|
sim_time += elapsed_time.mul_f32(TIMESCALE);
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
if DEBUG_TIME {
|
if DEBUG_TIME {
|
||||||
println!("Real time: {}ms | Delta time: {}ms", delta_time(clock_start), delta_time(last_tick));
|
println!("Real time: {}ms | Sim time: {}ms | Delta time: {}ms", real_time.as_millis(), sim_time.as_millis(), delta_time(last_tick));
|
||||||
}
|
|
||||||
if DEBUG_PARTICLES {
|
|
||||||
for i in 0..particles.len() {
|
|
||||||
println!("Time: {}ms | Delta time: {} | Particle {} Location: {:?}, Velocity: {:?}, Acceleration: {:?}", delta_time(clock_start), delta_time(last_tick), i, particles[i].location, particles[i].velocity, particles[i].acceleration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(delta_time(last_tick), &mut particles);
|
// update
|
||||||
|
update(delta_time(last_tick), TIMESCALE);
|
||||||
|
|
||||||
// record last tick time
|
// record last tick time
|
||||||
last_tick = Instant::now();
|
last_tick = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
//display(delta_time, &test_particle);
|
// display
|
||||||
|
display(delta_time(last_tick), TIMESCALE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update function
|
// update function
|
||||||
// TODO - I think I'm resetting delta time in the wrong place or just using it incorrectly; frameskips aren't happening at all
|
// this is where all your per-tick logic should go
|
||||||
fn update(delta_time: u32, particles: &mut Vec<Particle>) {
|
fn update(delta_time: u32, timescale: f32) {
|
||||||
if DEBUG_LOOP {
|
|
||||||
println!("Updating");
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the exact timestep (fractional time in seconds) from delta time
|
|
||||||
let timestep: f32 = delta_time as f32 / 1000.0;
|
|
||||||
|
|
||||||
for particle in particles.iter_mut() {
|
|
||||||
// add gravitational constant to instantaneous acceleration
|
|
||||||
if particle.gravity_enabled {
|
|
||||||
// acceleration += constant
|
|
||||||
particle.acceleration[2] += GRAVITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update velocity
|
|
||||||
for i in 0..3 {
|
|
||||||
// velocity += timestep * acceleration
|
|
||||||
particle.velocity[i] = timestep.mul_add(particle.acceleration[i], particle.velocity[i]);
|
|
||||||
|
|
||||||
// kill instantaneous acceleration
|
|
||||||
particle.acceleration[i] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update location
|
|
||||||
for i in 0..3 {
|
|
||||||
// location += timestep * velocity
|
|
||||||
particle.location[i] = timestep.mul_add(particle.velocity[i], particle.location[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent the particle from exiting the sim bounds by clamping its position and killing its velocity upon "hitting" a wall
|
|
||||||
let upper_bounds = SIM_BOUNDS.upper();
|
|
||||||
let lower_bounds = SIM_BOUNDS.lower();
|
|
||||||
for i in 0..3 {
|
|
||||||
if particle.location[i] >= upper_bounds[i] {
|
|
||||||
particle.location[i] = upper_bounds[i];
|
|
||||||
particle.velocity[i] = 0.0;
|
|
||||||
} else if particle.location[i] <= lower_bounds[i] {
|
|
||||||
particle.location[i] = lower_bounds[i];
|
|
||||||
particle.velocity[i] = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render function
|
|
||||||
fn display(delta_time: u32, particle: &Particle) {
|
|
||||||
// calculate interpolation via delta time
|
|
||||||
let interpolation: f32 = delta_time as f32 / UPDATE_INTERVAL as f32;
|
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
if DEBUG_LOOP {
|
if DEBUG_LOOP {
|
||||||
println!("Displaying | Delta Time: {}ms | Relative Time: {}", delta_time, interpolation);
|
println!("Updating...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use timestep to scale per-tick calculations appropriately
|
||||||
|
let timestep: f32 = delta_time as f32 / 1000.0 * timescale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// display function
|
||||||
|
// this is where you should call a render function
|
||||||
|
fn display(delta_time: u32, timescale: f32) {
|
||||||
// DEBUG
|
// DEBUG
|
||||||
// println!("test_particle | Location: {:?} | Velocity: {:?}", render_particle.location, render_particle.velocity);
|
if DEBUG_LOOP {
|
||||||
}
|
println!("Displaying...");
|
||||||
|
|
||||||
// 1D linear interpolation
|
|
||||||
fn lerp_1d(x: f64, y: f64, a: f64) -> f64 {
|
|
||||||
x + ((y - x) * a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a vector3 with random components
|
|
||||||
fn random_vector3(scale: [f32; 3], offset: [f32; 3]) -> [f32; 3] {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
|
|
||||||
let mut vector3: [f32; 3] = [0.0; 3];
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
let component = rng.gen::<f32>();
|
|
||||||
vector3[i] = component.mul_add(scale[i], offset[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector3
|
// use interpolation to smooth display values between ticks
|
||||||
|
let interpolation: f32 = delta_time as f32 / UPDATE_INTERVAL as f32 * timescale;
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the time in milliseconds that's elapsed since the earlier Instant
|
// gets the time in milliseconds that's elapsed since the earlier Instant
|
||||||
|
Loading…
Reference in New Issue
Block a user