Compare commits
14 Commits
bevy-rapie
...
main
Author | SHA1 | Date |
---|---|---|
Zynh0722 | e259bf6019 | |
Zynh0722 | 4c36162aa5 | |
Zynh0722 | 8cde3ba399 | |
Zynh0722 | 1e90b2c514 | |
Zynh0722 | 5bba4f019e | |
Zynh0722 | b7a5277a11 | |
Zynh0722 | be7c6763f0 | |
Zynh0722 | 9d5ced0539 | |
Zynh0722 | 8d822448f8 | |
Zynh0722 | ab92ac9430 | |
Zynh0722 | 76758fa2a8 | |
Zynh0722 | d10985909b | |
Zynh0722 | a663efd150 | |
Zynh0722 | c8ae692c33 |
|
@ -1,4 +1,5 @@
|
|||
use nannou::prelude::*;
|
||||
use rapier2d::geometry::{Collider, ColliderSet, TypedShape};
|
||||
|
||||
pub(crate) trait Drawable {
|
||||
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>
|
||||
where
|
||||
T: Drawable,
|
||||
|
|
|
@ -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),
|
||||
&(),
|
||||
&(),
|
||||
)
|
||||
}
|
||||
}
|
136
src/main.rs
136
src/main.rs
|
@ -1,29 +1,57 @@
|
|||
mod drawable;
|
||||
mod particle;
|
||||
mod engine;
|
||||
|
||||
use drawable::DrawShape;
|
||||
use nannou::prelude::*;
|
||||
use particle::Particle;
|
||||
use engine::{PhysicsEngine, PhysicsState};
|
||||
|
||||
use nalgebra::vector;
|
||||
use nannou::prelude::*;
|
||||
use rapier2d::{
|
||||
dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodySet},
|
||||
geometry::{ColliderBuilder, ColliderSet},
|
||||
};
|
||||
|
||||
const WINDOW_WIDTH: u32 = 512;
|
||||
const WINDOW_HEIGHT: u32 = WINDOW_WIDTH;
|
||||
const PARTICLE_COUNT: u32 = 200;
|
||||
const PARTICLE_SIZE: f32 = 10.0;
|
||||
const PARTICLE_SIZE: f32 = 5.;
|
||||
|
||||
struct Model {
|
||||
// Store the window ID so we can refer to this specific window later if needed.
|
||||
_window: WindowId,
|
||||
particles: Vec<Particle>,
|
||||
window: WindowId,
|
||||
// particles: Vec<Particle>,
|
||||
engine: PhysicsEngine,
|
||||
}
|
||||
|
||||
fn fill_particles(app: &App, particles: &mut Vec<Particle>) {
|
||||
let boundary = app.window_rect();
|
||||
fn random_vec_in_rect(boundary: &Rect) -> nalgebra::Vector2<f32> {
|
||||
let x = random_range(-100., 100.);
|
||||
let y = random_range(-150., boundary.top());
|
||||
|
||||
vector![x, y]
|
||||
}
|
||||
|
||||
fn fill_particles(boundary: &Rect, colliders: &mut ColliderSet, bodies: &mut RigidBodySet) {
|
||||
// Keeping track of already placed balls to avoid overlap
|
||||
// Need to look into a way to do this with rapier directly
|
||||
let mut positions: Vec<nalgebra::Vector2<f32>> = Vec::new();
|
||||
|
||||
for _ in 0..PARTICLE_COUNT {
|
||||
let x = random_range(boundary.left(), boundary.right());
|
||||
let y = random_range(boundary.top(), boundary.bottom());
|
||||
let mut xy = random_vec_in_rect(boundary);
|
||||
while !positions
|
||||
.iter()
|
||||
.all(|pos| pos.metric_distance(&xy) > PARTICLE_SIZE * 2.)
|
||||
{
|
||||
xy = random_vec_in_rect(boundary)
|
||||
}
|
||||
positions.push(xy);
|
||||
|
||||
let particle = Particle::new(Vec2::new(x, y));
|
||||
|
||||
particles.push(particle);
|
||||
/* Create the bouncing ball. */
|
||||
let rigid_body = RigidBodyBuilder::dynamic().translation(xy).build();
|
||||
let collider = ColliderBuilder::ball(PARTICLE_SIZE)
|
||||
.restitution(0.1)
|
||||
.build();
|
||||
let ball_body_handle = bodies.insert(rigid_body);
|
||||
colliders.insert_with_parent(collider, ball_body_handle, bodies);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,20 +61,54 @@ fn model(app: &App) -> Model {
|
|||
// per app
|
||||
|
||||
// Create a new window! Store the ID so we can refer to it later.
|
||||
let _window = app
|
||||
let window = app
|
||||
.new_window()
|
||||
.size(512, 512)
|
||||
.size(WINDOW_WIDTH, WINDOW_HEIGHT)
|
||||
.title("nannou")
|
||||
.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.
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut particles = Vec::new();
|
||||
let mut engine = PhysicsEngine {
|
||||
state: PhysicsState {
|
||||
gravity: vector![0., -9.81 * 10.],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
fill_particles(app, &mut particles);
|
||||
let boundary = app.window(window).unwrap().rect();
|
||||
|
||||
Model { _window, particles }
|
||||
/* Create the ground. */
|
||||
let collider = ColliderBuilder::cuboid(boundary.w(), 4.)
|
||||
.translation(vector![0., boundary.bottom()])
|
||||
.build();
|
||||
engine.state.colliders.insert(collider);
|
||||
|
||||
/* Create the walls. */
|
||||
let collider = ColliderBuilder::cuboid(4., boundary.h())
|
||||
.translation(vector![boundary.left(), 0.])
|
||||
.build();
|
||||
engine.state.colliders.insert(collider);
|
||||
let collider = ColliderBuilder::cuboid(4., boundary.h())
|
||||
.translation(vector![boundary.right(), 0.])
|
||||
.build();
|
||||
engine.state.colliders.insert(collider);
|
||||
|
||||
/* Create the ceiling. */
|
||||
let collider = ColliderBuilder::cuboid(boundary.w(), 4.)
|
||||
.translation(vector![0., boundary.top()])
|
||||
.build();
|
||||
engine.state.colliders.insert(collider);
|
||||
|
||||
fill_particles(
|
||||
&boundary,
|
||||
&mut engine.state.colliders,
|
||||
&mut engine.state.bodies,
|
||||
);
|
||||
|
||||
Model { window, engine }
|
||||
}
|
||||
|
||||
// Handle events related to the window and update the model if necessary
|
||||
|
@ -56,20 +118,38 @@ fn event(app: &App, model: &mut Model, event: WindowEvent) {
|
|||
}
|
||||
|
||||
if let Resized(_) = event {
|
||||
model.particles.clear();
|
||||
// Rust borrowing rules means I need to first gather a list of handles,
|
||||
// then delete them all
|
||||
let handles: Vec<RigidBodyHandle> = model
|
||||
.engine
|
||||
.state
|
||||
.bodies
|
||||
.iter()
|
||||
.map(|(handle, _)| handle)
|
||||
.collect();
|
||||
|
||||
fill_particles(app, &mut model.particles);
|
||||
for handle in handles {
|
||||
model.engine.state.bodies.remove(
|
||||
handle,
|
||||
&mut model.engine.state.islands,
|
||||
&mut model.engine.state.colliders,
|
||||
&mut model.engine.state.joints,
|
||||
&mut model.engine.state.multibody_joints,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fill_particles(
|
||||
&app.window(model.window).unwrap().rect(),
|
||||
&mut model.engine.state.colliders,
|
||||
&mut model.engine.state.bodies,
|
||||
);
|
||||
}
|
||||
|
||||
println!("{:?}", event);
|
||||
}
|
||||
|
||||
// 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
|
||||
});
|
||||
fn update(_app: &App, model: &mut Model, _update: Update) {
|
||||
model.engine.step();
|
||||
}
|
||||
|
||||
// Draw the state of your `Model` into the given `Frame` here.
|
||||
|
@ -77,7 +157,7 @@ fn view(app: &App, model: &Model, frame: Frame) {
|
|||
let canvas = app.draw();
|
||||
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?
|
||||
canvas.to_frame(app, &frame).unwrap();
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
use crate::drawable::Drawable;
|
||||
use crate::PARTICLE_SIZE;
|
||||
|
||||
use nannou::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Particle {
|
||||
pub pos: Vec2,
|
||||
pub start_pos: Vec2,
|
||||
pub radius: f32,
|
||||
pub time_offset: f32,
|
||||
}
|
||||
|
||||
impl Particle {
|
||||
pub fn new(pos: Vec2) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
start_pos: pos,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Particle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pos: Default::default(),
|
||||
radius: PARTICLE_SIZE,
|
||||
start_pos: Default::default(),
|
||||
time_offset: random_range(-PI, PI),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drawable for Particle {
|
||||
fn draw(&self, draw: &Draw) {
|
||||
draw.ellipse()
|
||||
.color(RED)
|
||||
.stroke_weight(1.0)
|
||||
.radius(self.radius)
|
||||
.xy(self.pos);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue