|
|
@ -0,0 +1,122 @@ |
|
|
|
extern crate kiss3d;
|
|
|
|
extern crate nalgebra as na;
|
|
|
|
|
|
|
|
use na::{Vector3, UnitQuaternion, Translation3};
|
|
|
|
use kiss3d::window::Window;
|
|
|
|
use kiss3d::light::Light;
|
|
|
|
use kiss3d::scene::SceneNode;
|
|
|
|
use rand::prelude::*;
|
|
|
|
use std::time::Instant;
|
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct RigidPoint {
|
|
|
|
position: Vector3<f32>,
|
|
|
|
velocity: Vector3<f32>,
|
|
|
|
mass: f64,
|
|
|
|
index: usize |
|
|
|
}
|
|
|
|
|
|
|
|
impl RigidPoint {
|
|
|
|
fn new(position: Vector3<f32>, velocity: Vector3<f32>, mass: f64, index: usize) -> Self {
|
|
|
|
RigidPoint {
|
|
|
|
position: position,
|
|
|
|
velocity: velocity,
|
|
|
|
mass: mass,
|
|
|
|
index: index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_node(&self, node: &mut SceneNode) {
|
|
|
|
node.set_local_translation(Translation3::from(self.position));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct Universe {
|
|
|
|
particles: Vec<RigidPoint>,
|
|
|
|
nodes: Vec<SceneNode>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Universe {
|
|
|
|
fn new() -> Self {
|
|
|
|
Universe { particles: Vec::new(), nodes: Vec::new() }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push(&mut self, particle: RigidPoint, node: SceneNode) {
|
|
|
|
self.particles.push(particle);
|
|
|
|
self.nodes.push(node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
println!("{}", std::mem::size_of::<RigidPoint>());
|
|
|
|
let mut window = Window::new("Galaxy simulator");
|
|
|
|
|
|
|
|
window.set_light(Light::StickToCamera);
|
|
|
|
|
|
|
|
let mut particles = generate_random_points(&mut window);
|
|
|
|
|
|
|
|
let mut start_time = Instant::now();
|
|
|
|
while window.render() {
|
|
|
|
calc_velocities(&mut particles, start_time.elapsed().as_secs_f64());
|
|
|
|
tick(&mut particles);
|
|
|
|
start_time = Instant::now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_random_points(window: &mut Window) -> Universe {
|
|
|
|
let mut ret: Universe = Universe::new();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
|
|
|
for i in 0..3000 {
|
|
|
|
let x: f32 = rng.gen_range(-50.0, 50.0);
|
|
|
|
let y: f32 = rng.gen_range(-50.0, 50.0);
|
|
|
|
let z: f32 = rng.gen_range(-5.0, 5.0); // Flat galaxy
|
|
|
|
let position: Vector3<f32> = Vector3::new(x, y, z);
|
|
|
|
let velocity: Vector3<f32> = Vector3::new(-z, x, y);
|
|
|
|
let node = window.add_sphere(0.1);
|
|
|
|
ret.push(RigidPoint::new(position, velocity / 1000.0, 1.0, i), node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
const G: f32 = 0.00667408;
|
|
|
|
|
|
|
|
fn calc_velocities(u: &mut Universe, delta: f64) {
|
|
|
|
let mut p = u.particles.clone();
|
|
|
|
u.particles.par_iter_mut().for_each(|mut b| {
|
|
|
|
p.iter().for_each(|a| {
|
|
|
|
calc_gravity(&mut b, &a, delta);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_gravity(body: &mut RigidPoint, other: &RigidPoint, delta: f64) {
|
|
|
|
let dx = body.position.x - other.position.x;
|
|
|
|
let dy = body.position.y - other.position.y;
|
|
|
|
let dz = body.position.z - other.position.z;
|
|
|
|
|
|
|
|
let dist_squared = dx * dx + dy * dy + dz * dz;
|
|
|
|
|
|
|
|
if dist_squared < 0.01 {
|
|
|
|
// don't calculate gravity for stars that are really close
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let v = -G * (other.mass as f32) / dist_squared * delta as f32;
|
|
|
|
let hyp = dist_squared.sqrt();
|
|
|
|
body.velocity += Vector3::new(v * dx / hyp, v * dy / hyp, v * dz / hyp);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tick(u: &mut Universe) {
|
|
|
|
u.particles.par_iter_mut().for_each(|a| {
|
|
|
|
a.position += a.velocity;
|
|
|
|
});
|
|
|
|
|
|
|
|
u.particles.clone().iter().for_each(|a| {
|
|
|
|
u.nodes[a.index].set_local_translation(Translation3::from(a.position));
|
|
|
|
});
|
|
|
|
println!("tick");
|
|
|
|
}
|