diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 0000000..35924c5 --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,50 @@ +use rapier2d::{ + dynamics::{ + CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, + RigidBodySet, + }, + geometry::{BroadPhase, ColliderSet, NarrowPhase}, + math::Vector, + pipeline::{PhysicsPipeline, QueryPipeline}, +}; + +#[derive(Default)] +pub struct PhysicsEngine { + pub state: PhysicsState, + pub pipeline: PhysicsPipeline, +} + +#[derive(Default)] +pub struct PhysicsState { + pub islands: IslandManager, + pub broad_phase: BroadPhase, + pub narrow_phase: NarrowPhase, + pub bodies: RigidBodySet, + pub colliders: ColliderSet, + pub joints: ImpulseJointSet, + pub multibody_joints: MultibodyJointSet, + pub ccd_solver: CCDSolver, + pub query_pipeline: QueryPipeline, + pub integration_parameters: IntegrationParameters, + pub gravity: Vector, +} + +impl PhysicsEngine { + pub fn step(&mut self) { + self.pipeline.step( + &self.state.gravity, + &self.state.integration_parameters, + &mut self.state.islands, + &mut self.state.broad_phase, + &mut self.state.narrow_phase, + &mut self.state.bodies, + &mut self.state.colliders, + &mut self.state.joints, + &mut self.state.multibody_joints, + &mut self.state.ccd_solver, + Some(&mut self.state.query_pipeline), + &(), + &(), + ) + } +} diff --git a/src/main.rs b/src/main.rs index 0cb98c3..e74987c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,17 @@ mod drawable; +mod engine; mod particle; use drawable::DrawShape; +use engine::{PhysicsEngine, PhysicsState}; + +use nalgebra::vector; use nannou::prelude::*; use particle::Particle; +use rapier2d::{ + dynamics::{RigidBodyBuilder, RigidBodyHandle}, + geometry::ColliderBuilder, +}; const PARTICLE_COUNT: u32 = 200; const PARTICLE_SIZE: f32 = 10.0; @@ -12,6 +20,8 @@ struct Model { // Store the window ID so we can refer to this specific window later if needed. _window: WindowId, particles: Vec, + engine: PhysicsEngine, + handles: Vec, } fn fill_particles(app: &App, particles: &mut Vec) { @@ -42,11 +52,45 @@ fn model(app: &App) -> Model { .build() .unwrap(); + let mut engine = PhysicsEngine { + state: PhysicsState { + gravity: vector![0.0, -9.81 * 10.0], + ..Default::default() + }, + ..Default::default() + }; + + let mut handles = Vec::new(); + + /* Create the ground. */ + let collider = ColliderBuilder::cuboid(100.0, 0.1) + .translation(vector![0.0, -200.0]) + .build(); + engine.state.colliders.insert(collider); + + /* Create the bouncing ball. */ + let rigid_body = RigidBodyBuilder::dynamic() + .translation(vector![0.0, 10.0]) + .build(); + let collider = ColliderBuilder::ball(10.0).restitution(0.7).build(); + let ball_body_handle = engine.state.bodies.insert(rigid_body); + engine + .state + .colliders + .insert_with_parent(collider, ball_body_handle, &mut engine.state.bodies); + + handles.push(ball_body_handle); + let mut particles = Vec::new(); fill_particles(app, &mut particles); - Model { _window, particles } + Model { + _window, + particles, + engine, + handles, + } } // Handle events related to the window and update the model if necessary @@ -66,10 +110,12 @@ fn event(app: &App, model: &mut Model, event: WindowEvent) { // Update the state of your application here. By default, this gets called right before `view`. fn update(_app: &App, _model: &mut Model, _update: Update) { - _model.particles.iter_mut().for_each(|particle| { - let offset = (_app.time + particle.time_offset).sin() * 100.0; - particle.pos.x = particle.start_pos.x + offset - }); + // _model.particles.iter_mut().for_each(|particle| { + // let offset = (_app.time + particle.time_offset).sin() * 100.0; + // particle.pos.x = particle.start_pos.x + offset + // }); + + _model.engine.step(); } // Draw the state of your `Model` into the given `Frame` here. @@ -77,7 +123,17 @@ fn view(app: &App, model: &Model, frame: Frame) { let canvas = app.draw(); canvas.background().color(CORNFLOWERBLUE); - canvas.draw(&model.particles); + // canvas.draw(&model.particles); + model + .handles + .iter() + .map(|handle| model.engine.state.bodies.get(*handle).unwrap()) + .for_each(|body| { + canvas + .ellipse() + .radius(10.0) + .xy(std::convert::Into::::into(*body.translation())); + }); // I don't think there is even a fail condition in this function, but it returns a result? canvas.to_frame(app, &frame).unwrap();