Compare commits

...

5 Commits

Author SHA1 Message Date
Zynh0722 ab92ac9430 many balls 2024-02-16 23:34:02 -08:00
Zynh0722 76758fa2a8 removing an inaccurate underscore 2024-02-16 23:14:44 -08:00
Zynh0722 d10985909b deleting an old unused comment 2024-02-16 23:14:21 -08:00
Zynh0722 a663efd150 we have drawing physics 2024-02-16 23:13:40 -08:00
Zynh0722 c8ae692c33 rapier engine testing (don't like) 2024-02-16 19:18:24 -08:00
3 changed files with 133 additions and 28 deletions

View File

@ -1,4 +1,5 @@
use nannou::prelude::*; use nannou::prelude::*;
use rapier2d::geometry::{Collider, ColliderSet, TypedShape};
pub(crate) trait Drawable { pub(crate) trait Drawable {
fn draw(&self, draw: &Draw); fn draw(&self, draw: &Draw);
@ -13,6 +14,31 @@ where
} }
} }
impl Drawable for Collider {
fn draw(&self, draw: &Draw) {
match self.shape().as_typed_shape() {
TypedShape::Ball(ball) => {
draw.ellipse()
.radius(ball.radius)
.xy((*self.translation()).into());
}
TypedShape::Cuboid(cube) => {
draw.rect()
.w(cube.half_extents.x * 2.0)
.h(cube.half_extents.y * 2.0)
.xy((*self.translation()).into());
}
_ => eprintln!("Attempted to draw a shape with no draw impl"),
}
}
}
impl Drawable for ColliderSet {
fn draw(&self, draw: &Draw) {
self.iter().for_each(|(_, collider)| draw.draw(collider))
}
}
pub(crate) trait DrawShape<T> pub(crate) trait DrawShape<T>
where where
T: Drawable, T: Drawable,

50
src/engine.rs Normal file
View File

@ -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<f32>,
}
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),
&(),
&(),
)
}
}

View File

@ -1,29 +1,42 @@
mod drawable; mod drawable;
mod engine;
mod particle; mod particle;
use drawable::DrawShape; use drawable::DrawShape;
use nannou::prelude::*; use engine::{PhysicsEngine, PhysicsState};
use particle::Particle;
use nalgebra::vector;
use nannou::prelude::*;
use rapier2d::{
dynamics::{RigidBodyBuilder, RigidBodySet},
geometry::{ColliderBuilder, ColliderSet},
};
const WINDOW_WIDTH: u32 = 512;
const WINDOW_HEIGHT: u32 = WINDOW_WIDTH;
const PARTICLE_COUNT: u32 = 200; const PARTICLE_COUNT: u32 = 200;
const PARTICLE_SIZE: f32 = 10.0; const PARTICLE_SIZE: f32 = 10.0;
struct Model { struct Model {
// Store the window ID so we can refer to this specific window later if needed. // Store the window ID so we can refer to this specific window later if needed.
_window: WindowId, _window: WindowId,
particles: Vec<Particle>, // particles: Vec<Particle>,
engine: PhysicsEngine,
} }
fn fill_particles(app: &App, particles: &mut Vec<Particle>) { fn fill_particles(_app: &App, colliders: &mut ColliderSet, bodies: &mut RigidBodySet) {
let boundary = app.window_rect(); let boundary = _app.window_rect();
for _ in 0..PARTICLE_COUNT { for _ in 0..PARTICLE_COUNT {
let x = random_range(boundary.left(), boundary.right()); let x = random_range(-100., 100.);
let y = random_range(boundary.top(), boundary.bottom()); let y = random_range(-150., boundary.top());
let particle = Particle::new(Vec2::new(x, y)); /* Create the bouncing ball. */
let rigid_body = RigidBodyBuilder::dynamic()
particles.push(particle); .translation(vector![x, y])
.build();
let collider = ColliderBuilder::ball(5.).restitution(0.1).build();
let ball_body_handle = bodies.insert(rigid_body);
colliders.insert_with_parent(collider, ball_body_handle, bodies);
} }
} }
@ -35,41 +48,57 @@ fn model(app: &App) -> Model {
// Create a new window! Store the ID so we can refer to it later. // Create a new window! Store the ID so we can refer to it later.
let _window = app let _window = app
.new_window() .new_window()
.size(512, 512) .size(WINDOW_WIDTH, WINDOW_HEIGHT)
.title("nannou") .title("nannou")
.view(view) // The function that will be called for presenting graphics to a frame. .view(view) // The function that will be called for presenting graphics to a frame.
.event(event) // The function that will be called when the window receives events. .event(event) // The function that will be called when the window receives events.
.build() .build()
.unwrap(); .unwrap();
let mut particles = Vec::new(); let mut engine = PhysicsEngine {
state: PhysicsState {
gravity: vector![0.0, -9.81 * 10.0],
..Default::default()
},
..Default::default()
};
fill_particles(app, &mut particles); /* Create the ground. */
let collider = ColliderBuilder::cuboid(100.0, 10.0)
.translation(vector![0.0, -200.0])
.build();
engine.state.colliders.insert(collider);
Model { _window, particles } // let mut particles = Vec::new();
// fill_particles(app, &mut particles);
fill_particles(app, &mut engine.state.colliders, &mut engine.state.bodies);
Model {
_window,
// particles,
engine,
}
} }
// Handle events related to the window and update the model if necessary // Handle events related to the window and update the model if necessary
fn event(app: &App, model: &mut Model, event: WindowEvent) { fn event(app: &App, _model: &mut Model, event: WindowEvent) {
if let KeyReleased(Key::Escape) = event { if let KeyReleased(Key::Escape) = event {
app.quit() app.quit()
} }
if let Resized(_) = event { // if let Resized(_) = event {
model.particles.clear(); // model.particles.clear();
//
// fill_particles(app, &mut model.particles);
// }
fill_particles(app, &mut model.particles); // println!("{:?}", event);
}
println!("{:?}", event);
} }
// Update the state of your application here. By default, this gets called right before `view`. // Update the state of your application here. By default, this gets called right before `view`.
fn update(_app: &App, _model: &mut Model, _update: Update) { fn update(_app: &App, model: &mut Model, _update: Update) {
_model.particles.iter_mut().for_each(|particle| { model.engine.step();
let offset = (_app.time + particle.time_offset).sin() * 100.0;
particle.pos.x = particle.start_pos.x + offset
});
} }
// Draw the state of your `Model` into the given `Frame` here. // Draw the state of your `Model` into the given `Frame` here.
@ -77,7 +106,7 @@ fn view(app: &App, model: &Model, frame: Frame) {
let canvas = app.draw(); let canvas = app.draw();
canvas.background().color(CORNFLOWERBLUE); canvas.background().color(CORNFLOWERBLUE);
canvas.draw(&model.particles); canvas.draw(&model.engine.state.colliders);
// I don't think there is even a fail condition in this function, but it returns a result? // I don't think there is even a fail condition in this function, but it returns a result?
canvas.to_frame(app, &frame).unwrap(); canvas.to_frame(app, &frame).unwrap();