Replace Kiss3d by Bevy for the testbed renderer.

This commit is contained in:
Crozet Sébastien
2021-05-16 17:49:20 +02:00
parent f350ac35d9
commit 1a84bf2af3
88 changed files with 2327 additions and 3940 deletions

View File

@@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ "build/rapier2d", "build/rapier2d-f64", "build/rapier_testbed2d", "examples2d", "benchmarks2d", members = [ "build/rapier2d", "build/rapier2d-f64", "build/rapier_testbed2d", "examples2d", "benchmarks2d",
"build/rapier3d", "build/rapier3d-f64", "build/rapier_testbed3d", "examples3d", "benchmarks3d" ] "build/rapier3d", "build/rapier3d-f64", "build/rapier_testbed3d", "examples3d", "benchmarks3d" ]
resolver = "2"
[patch.crates-io] [patch.crates-io]
#wrapped2d = { git = "https://github.com/Bastacyclop/rust_box2d.git" } #wrapped2d = { git = "https://github.com/Bastacyclop/rust_box2d.git" }

BIN
assets/FiraSans-Bold.ttf Normal file

Binary file not shown.

View File

@@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*;
use inflector::Inflector; use inflector::Inflector;
use rapier_testbed3d::Testbed; use rapier_testbed3d::{Testbed, TestbedApp};
use std::cmp::Ordering; use std::cmp::Ordering;
mod balls3; mod balls3;
@@ -80,12 +80,12 @@ pub fn main() {
.iter() .iter()
.position(|builder| builder.0.to_camel_case().as_str() == demo.as_str()) .position(|builder| builder.0.to_camel_case().as_str() == demo.as_str())
{ {
Testbed::from_builders(0, vec![builders[i]]).run() TestbedApp::from_builders(0, vec![builders[i]]).run()
} else { } else {
eprintln!("Invalid example to run provided: '{}'", demo); eprintln!("Invalid example to run provided: '{}'", demo);
} }
} }
Command::RunAll => Testbed::from_builders(0, builders).run(), Command::RunAll => TestbedApp::from_builders(0, builders).run(),
Command::List => { Command::List => {
for builder in &builders { for builder in &builders {
println!("{}", builder.0.to_camel_case()) println!("{}", builder.0.to_camel_case())

View File

@@ -51,8 +51,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Balls", init_world)]);
testbed.run()
}

View File

@@ -61,8 +61,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -62,8 +62,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -78,8 +78,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -69,8 +69,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -76,8 +76,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -75,8 +75,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -63,8 +63,3 @@ pub fn init_world(testbed: &mut Testbed) {
Point3::new(54.0, -38.0, 29.0), Point3::new(54.0, -38.0, 29.0),
); );
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -85,8 +85,3 @@ pub fn init_world(testbed: &mut Testbed) {
Point3::new(46.0, 12.0, 23.0), Point3::new(46.0, 12.0, 23.0),
); );
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -74,8 +74,3 @@ pub fn init_world(testbed: &mut Testbed) {
Point3::new(101.0, 4.0, -3.0), Point3::new(101.0, 4.0, -3.0),
); );
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -82,8 +82,3 @@ pub fn init_world(testbed: &mut Testbed) {
Point3::new(134.0, 83.0, -116.0), Point3::new(134.0, 83.0, -116.0),
); );
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -51,7 +51,7 @@ pub fn build_block(
let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build(); let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build();
colliders.insert(collider, handle, bodies); colliders.insert(collider, handle, bodies);
testbed.set_body_color(handle, color0); testbed.set_initial_body_color(handle, color0);
std::mem::swap(&mut color0, &mut color1); std::mem::swap(&mut color0, &mut color1);
} }
} }
@@ -73,7 +73,7 @@ pub fn build_block(
let handle = bodies.insert(rigid_body); let handle = bodies.insert(rigid_body);
let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build(); let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build();
colliders.insert(collider, handle, bodies); colliders.insert(collider, handle, bodies);
testbed.set_body_color(handle, color0); testbed.set_initial_body_color(handle, color0);
std::mem::swap(&mut color0, &mut color1); std::mem::swap(&mut color0, &mut color1);
} }
} }
@@ -137,8 +137,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -76,8 +76,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -184,8 +184,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -80,8 +80,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -27,7 +27,6 @@ other-backends = [ "wrapped2d", "nphysics2d" ]
[dependencies] [dependencies]
nalgebra = { version = "0.26", features = [ "rand" ] } nalgebra = { version = "0.26", features = [ "rand" ] }
kiss3d = { version = "0.31", features = [ "conrod" ] }
rand = "0.8" rand = "0.8"
rand_pcg = "0.3" rand_pcg = "0.3"
instant = { version = "0.1", features = [ "web-sys", "now" ]} instant = { version = "0.1", features = [ "web-sys", "now" ]}
@@ -42,6 +41,18 @@ bincode = "1"
Inflector = "0.11" Inflector = "0.11"
md5 = "0.7" md5 = "0.7"
bevy_egui = "0.4"
bevy-orbit-controls = "2"
# Dependencies for native only.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
bevy = {version = "0.5", default-features = false, features = ["bevy_wgpu", "bevy_winit", "render", "x11"]}
# Dependencies for WASM only.
[target.'cfg(target_arch = "wasm32")'.dependencies]
bevy = {version = "0.5", default-features = false, features = ["bevy_winit", "render"]}
bevy_webgl2 = "0.5"
[dependencies.rapier2d] [dependencies.rapier2d]
path = "../rapier2d" path = "../rapier2d"
version = "0.8" version = "0.8"

View File

@@ -26,7 +26,6 @@ other-backends = [ "physx", "physx-sys", "glam", "nphysics3d" ]
[dependencies] [dependencies]
nalgebra = { version = "0.26", features = [ "rand" ] } nalgebra = { version = "0.26", features = [ "rand" ] }
kiss3d = { version = "0.31", features = [ "conrod" ] }
rand = "0.8" rand = "0.8"
rand_pcg = "0.3" rand_pcg = "0.3"
instant = { version = "0.1", features = [ "web-sys", "now" ]} instant = { version = "0.1", features = [ "web-sys", "now" ]}
@@ -44,6 +43,18 @@ md5 = "0.7"
Inflector = "0.11" Inflector = "0.11"
serde = { version = "1", features = [ "derive" ] } serde = { version = "1", features = [ "derive" ] }
bevy_egui = "0.4"
bevy-orbit-controls = "2"
# Dependencies for native only.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
bevy = {version = "0.5", default-features = false, features = ["bevy_wgpu", "bevy_winit", "render", "x11"]}
# Dependencies for WASM only.
[target.'cfg(target_arch = "wasm32")'.dependencies]
bevy = {version = "0.5", default-features = false, features = ["bevy_winit", "render"]}
bevy_webgl2 = "0.5"
[dependencies.rapier3d] [dependencies.rapier3d]
path = "../rapier3d" path = "../rapier3d"
version = "0.8" version = "0.8"

View File

@@ -10,7 +10,7 @@ pub fn init_world(testbed: &mut Testbed) {
let rad = 0.5; let rad = 0.5;
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |mut window, mut graphics, physics, _, _| { testbed.add_callback(move |mut graphics, physics, _, _| {
let rigid_body = RigidBodyBuilder::new_dynamic() let rigid_body = RigidBodyBuilder::new_dynamic()
.translation(0.0, 10.0) .translation(0.0, 10.0)
.build(); .build();
@@ -20,8 +20,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(collider, handle, &mut physics.bodies); .insert(collider, handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.add(*window, handle, &physics.bodies, &physics.colliders); graphics.add_body(handle, &physics.bodies, &physics.colliders);
} }
let to_remove: Vec<_> = physics let to_remove: Vec<_> = physics
@@ -38,8 +38,8 @@ pub fn init_world(testbed: &mut Testbed) {
&mut physics.joints, &mut physics.joints,
); );
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.remove_body_nodes(*window, handle); graphics.remove_body(handle);
} }
} }
}); });
@@ -50,8 +50,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 0.0), 20.0); testbed.look_at(Point2::new(0.0, 0.0), 20.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Add-remove", init_world)]);
testbed.run()
}

View File

@@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*;
use inflector::Inflector; use inflector::Inflector;
use rapier_testbed2d::Testbed; use rapier_testbed2d::{Testbed, TestbedApp};
use std::cmp::Ordering; use std::cmp::Ordering;
mod add_remove2; mod add_remove2;
@@ -89,7 +89,7 @@ pub fn main() {
.iter() .iter()
.position(|builder| builder.0.to_camel_case().as_str() == demo.as_str()) .position(|builder| builder.0.to_camel_case().as_str() == demo.as_str())
.unwrap_or(0); .unwrap_or(0);
let testbed = Testbed::from_builders(i, builders); let testbed = TestbedApp::from_builders(i, builders);
testbed.run() testbed.run()
} }

View File

@@ -90,7 +90,7 @@ pub fn init_world(testbed: &mut Testbed) {
} }
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |_, mut graphics, physics, events, _| { testbed.add_callback(move |mut graphics, physics, events, _| {
while let Ok(prox) = events.intersection_events.try_recv() { while let Ok(prox) = events.intersection_events.try_recv() {
let color = if prox.intersecting { let color = if prox.intersecting {
Point3::new(1.0, 1.0, 0.0) Point3::new(1.0, 1.0, 0.0)
@@ -117,8 +117,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 2.5), 20.0); testbed.look_at(Point2::new(0.0, 2.5), 20.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Balls", init_world)]);
testbed.run()
}

View File

@@ -39,7 +39,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
let green_collider_handle = colliders.insert(green_floor, floor_handle, &mut bodies); let green_collider_handle = colliders.insert(green_floor, floor_handle, &mut bodies);
testbed.set_collider_initial_color(green_collider_handle, Point3::new(0.0, 1.0, 0.0)); testbed.set_initial_collider_color(green_collider_handle, Point3::new(0.0, 1.0, 0.0));
/* /*
* A blue floor that will collide with the BLUE group only. * A blue floor that will collide with the BLUE group only.
@@ -50,7 +50,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
let blue_collider_handle = colliders.insert(blue_floor, floor_handle, &mut bodies); let blue_collider_handle = colliders.insert(blue_floor, floor_handle, &mut bodies);
testbed.set_collider_initial_color(blue_collider_handle, Point3::new(0.0, 0.0, 1.0)); testbed.set_initial_collider_color(blue_collider_handle, Point3::new(0.0, 0.0, 1.0));
/* /*
* Create the cubes * Create the cubes
@@ -81,7 +81,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_body_color(handle, color); testbed.set_initial_body_color(handle, color);
} }
} }
@@ -91,8 +91,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 1.0), 100.0); testbed.look_at(Point2::new(0.0, 1.0), 100.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -79,8 +79,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 50.0), 10.0); testbed.look_at(Point2::new(0.0, 50.0), 10.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Balls", init_world)]);
testbed.run()
}

View File

@@ -1,3 +1,4 @@
use na::Point2;
use rapier2d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet}; use rapier2d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
use rapier2d::geometry::{ColliderBuilder, ColliderSet}; use rapier2d::geometry::{ColliderBuilder, ColliderSet};
use rapier_testbed2d::Testbed; use rapier_testbed2d::Testbed;
@@ -35,10 +36,5 @@ pub fn init_world(testbed: &mut Testbed) {
* Set up the testbed. * Set up the testbed.
*/ */
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
// testbed.look_at(Point2::new(10.0, 10.0, 10.0), Point2::origin()); testbed.look_at(Point2::new(0.0, 0.0), 50.0);
}
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
} }

View File

@@ -65,8 +65,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 0.0), 10.0); testbed.look_at(Point2::new(0.0, 0.0), 10.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Heightfield", init_world)]);
testbed.run()
}

View File

@@ -68,8 +68,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(numk as f32 * rad, numi as f32 * -rad), 20.0); testbed.look_at(Point2::new(numk as f32 * rad, numi as f32 * -rad), 20.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -56,8 +56,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 0.0), 40.0); testbed.look_at(Point2::new(0.0, 0.0), 40.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -100,7 +100,7 @@ pub fn init_world(testbed: &mut Testbed) {
* Spawn cubes at regular intervals and apply a custom gravity * Spawn cubes at regular intervals and apply a custom gravity
* depending on their position. * depending on their position.
*/ */
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| { testbed.add_callback(move |graphics, physics, _, run_state| {
if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 { if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 {
// Spawn a new cube. // Spawn a new cube.
let collider = ColliderBuilder::cuboid(1.5, 2.0).build(); let collider = ColliderBuilder::cuboid(1.5, 2.0).build();
@@ -112,8 +112,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(collider, handle, &mut physics.bodies); .insert(collider, handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = graphics {
graphics.add(window, handle, &physics.bodies, &physics.colliders); graphics.add_body(handle, &physics.bodies, &physics.colliders);
} }
} }
@@ -139,8 +139,3 @@ pub fn init_world(testbed: &mut Testbed) {
); );
testbed.look_at(Point2::new(0.0, 0.0), 20.0); testbed.look_at(Point2::new(0.0, 0.0), 20.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -60,7 +60,7 @@ pub fn init_world(testbed: &mut Testbed) {
/* /*
* Setup a callback to control the platform. * Setup a callback to control the platform.
*/ */
testbed.add_callback(move |_, _, physics, _, run_state| { testbed.add_callback(move |_, physics, _, run_state| {
let platform = physics.bodies.get_mut(platform_handle).unwrap(); let platform = physics.bodies.get_mut(platform_handle).unwrap();
let mut next_pos = *platform.position(); let mut next_pos = *platform.position();
@@ -84,8 +84,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 1.0), 40.0); testbed.look_at(Point2::new(0.0, 1.0), 40.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Kinematic body", init_world)]);
testbed.run()
}

View File

@@ -67,8 +67,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 0.0), 10.0); testbed.look_at(Point2::new(0.0, 0.0), 10.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Heightfield", init_world)]);
testbed.run()
}

View File

@@ -53,8 +53,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 2.5), 20.0); testbed.look_at(Point2::new(0.0, 2.5), 20.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Balls", init_world)]);
testbed.run()
}

View File

@@ -49,8 +49,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 1.0), 25.0); testbed.look_at(Point2::new(0.0, 1.0), 25.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -43,7 +43,7 @@ pub fn init_world(testbed: &mut Testbed) {
let collider = ColliderBuilder::cuboid(rad, rad).build(); let collider = ColliderBuilder::cuboid(rad, rad).build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_body_color(handle, Point3::new(0.5, 0.5, 1.0)); testbed.set_initial_body_color(handle, Point3::new(0.5, 0.5, 1.0));
} }
/* /*
@@ -66,10 +66,10 @@ pub fn init_world(testbed: &mut Testbed) {
let sensor_collider = ColliderBuilder::ball(rad * 5.0).sensor(true).build(); let sensor_collider = ColliderBuilder::ball(rad * 5.0).sensor(true).build();
colliders.insert(sensor_collider, sensor_handle, &mut bodies); colliders.insert(sensor_collider, sensor_handle, &mut bodies);
testbed.set_body_color(sensor_handle, Point3::new(0.5, 1.0, 1.0)); testbed.set_initial_body_color(sensor_handle, Point3::new(0.5, 1.0, 1.0));
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |_, mut graphics, physics, events, _| { testbed.add_callback(move |mut graphics, physics, events, _| {
while let Ok(prox) = events.intersection_events.try_recv() { while let Ok(prox) = events.intersection_events.try_recv() {
let color = if prox.intersecting { let color = if prox.intersecting {
Point3::new(1.0, 1.0, 0.0) Point3::new(1.0, 1.0, 0.0)
@@ -96,8 +96,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point2::new(0.0, 1.0), 100.0); testbed.look_at(Point2::new(0.0, 1.0), 100.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Sensor", init_world)]);
testbed.run()
}

View File

@@ -109,11 +109,6 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.look_at(Point2::new(0.0, 20.0), 17.0); testbed.look_at(Point2::new(0.0, 20.0), 17.0);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Balls", init_world)]);
testbed.run()
}
const RAPIER_SVG_STR: &'static str = r#" const RAPIER_SVG_STR: &'static str = r#"
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 527 131" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"> <svg width="100%" height="100%" viewBox="0 0 527 131" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">

View File

@@ -17,7 +17,8 @@ rand = "0.8"
getrandom = { version = "0.2", features = [ "js" ] } getrandom = { version = "0.2", features = [ "js" ] }
Inflector = "0.11" Inflector = "0.11"
nalgebra = "0.26" nalgebra = "0.26"
kiss3d = "0.31" wasm-bindgen = "0.2"
obj-rs = { version = "0.6", default-features = false }
[dependencies.rapier_testbed3d] [dependencies.rapier_testbed3d]
path = "../build/rapier_testbed3d" path = "../build/rapier_testbed3d"
@@ -32,3 +33,8 @@ path = "./all_examples3.rs"
[[bin]] [[bin]]
name = "harness_capsules3" name = "harness_capsules3"
path = "./harness_capsules3.rs" path = "./harness_capsules3.rs"
#[lib]
#crate-type = ["cdylib", "rlib"]
#path = "./all_examples3_wasm.rs"

View File

@@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*;
use inflector::Inflector; use inflector::Inflector;
use rapier_testbed3d::Testbed; use rapier_testbed3d::{Testbed, TestbedApp};
use std::cmp::Ordering; use std::cmp::Ordering;
mod ccd3; mod ccd3;
@@ -131,7 +131,6 @@ pub fn main() {
.position(|builder| builder.0.to_camel_case().as_str() == demo.as_str()) .position(|builder| builder.0.to_camel_case().as_str() == demo.as_str())
.unwrap_or(0); .unwrap_or(0);
let testbed = Testbed::from_builders(i, builders); let testbed = TestbedApp::from_builders(i, builders);
testbed.run() testbed.run()
} }

View File

@@ -0,0 +1,136 @@
#![allow(dead_code)]
extern crate nalgebra as na;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
use inflector::Inflector;
use rapier_testbed3d::{Testbed, TestbedApp};
use std::cmp::Ordering;
mod ccd3;
mod collision_groups3;
mod compound3;
mod convex_decomposition3;
mod convex_polyhedron3;
mod damping3;
mod debug_add_remove_collider3;
mod debug_big_colliders3;
mod debug_boxes3;
mod debug_cylinder3;
mod debug_dynamic_collider_add3;
mod debug_friction3;
mod debug_infinite_fall3;
mod debug_prismatic3;
mod debug_rollback3;
mod debug_shape_modification3;
mod debug_triangle3;
mod debug_trimesh3;
mod domino3;
mod fountain3;
mod heightfield3;
mod joints3;
mod keva3;
mod locked_rotations3;
mod one_way_platforms3;
mod platform3;
mod primitives3;
mod restitution3;
mod sensor3;
mod trimesh3;
fn demo_name_from_command_line() -> Option<String> {
let mut args = std::env::args();
while let Some(arg) = args.next() {
if &arg[..] == "--example" {
return args.next();
}
}
None
}
#[cfg(any(target_arch = "wasm32", target_arch = "asmjs"))]
fn demo_name_from_url() -> Option<String> {
None
// let window = stdweb::web::window();
// let hash = window.location()?.search().ok()?;
// if hash.len() > 0 {
// Some(hash[1..].to_string())
// } else {
// None
// }
}
#[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))]
fn demo_name_from_url() -> Option<String> {
None
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
pub fn main() {
let demo = demo_name_from_command_line()
.or_else(|| demo_name_from_url())
.unwrap_or(String::new())
.to_camel_case();
let mut builders: Vec<(_, fn(&mut Testbed))> = vec![
("Fountain", fountain3::init_world),
("Primitives", primitives3::init_world),
("CCD", ccd3::init_world),
("Collision groups", collision_groups3::init_world),
("Compound", compound3::init_world),
("Convex decomposition", convex_decomposition3::init_world),
("Convex polyhedron", convex_polyhedron3::init_world),
("Damping", damping3::init_world),
("Domino", domino3::init_world),
("Heightfield", heightfield3::init_world),
("Joints", joints3::init_world),
("Locked rotations", locked_rotations3::init_world),
("One-way platforms", one_way_platforms3::init_world),
("Platform", platform3::init_world),
("Restitution", restitution3::init_world),
("Sensor", sensor3::init_world),
("TriMesh", trimesh3::init_world),
("Keva tower", keva3::init_world),
(
"(Debug) add/rm collider",
debug_add_remove_collider3::init_world,
),
("(Debug) big colliders", debug_big_colliders3::init_world),
("(Debug) boxes", debug_boxes3::init_world),
(
"(Debug) dyn. coll. add",
debug_dynamic_collider_add3::init_world,
),
("(Debug) friction", debug_friction3::init_world),
("(Debug) triangle", debug_triangle3::init_world),
("(Debug) trimesh", debug_trimesh3::init_world),
("(Debug) cylinder", debug_cylinder3::init_world),
("(Debug) infinite fall", debug_infinite_fall3::init_world),
("(Debug) prismatic", debug_prismatic3::init_world),
("(Debug) rollback", debug_rollback3::init_world),
(
"(Debug) shape modification",
debug_shape_modification3::init_world,
),
];
// Lexicographic sort, with stress tests moved at the end of the list.
builders.sort_by(|a, b| match (a.0.starts_with("("), b.0.starts_with("(")) {
(true, true) | (false, false) => a.0.cmp(b.0),
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
});
let i = builders
.iter()
.position(|builder| builder.0.to_camel_case().as_str() == demo.as_str())
.unwrap_or(0);
let testbed = TestbedApp::from_builders(i, builders);
testbed.run()
}

View File

@@ -30,9 +30,15 @@ fn create_wall(
colliders.insert(collider, handle, bodies); colliders.insert(collider, handle, bodies);
k += 1; k += 1;
if k % 2 == 0 { if k % 2 == 0 {
testbed.set_body_color(handle, Point3::new(255. / 255., 131. / 255., 244.0 / 255.)); testbed.set_initial_body_color(
handle,
Point3::new(255. / 255., 131. / 255., 244.0 / 255.),
);
} else { } else {
testbed.set_body_color(handle, Point3::new(131. / 255., 255. / 255., 244.0 / 255.)); testbed.set_initial_body_color(
handle,
Point3::new(131. / 255., 255. / 255., 244.0 / 255.),
);
} }
} }
} }
@@ -114,10 +120,10 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
let handle = bodies.insert(rigid_body); let handle = bodies.insert(rigid_body);
colliders.insert(collider.clone(), handle, &mut bodies); colliders.insert(collider.clone(), handle, &mut bodies);
testbed.set_body_color(handle, Point3::new(0.2, 0.2, 1.0)); testbed.set_initial_body_color(handle, Point3::new(0.2, 0.2, 1.0));
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |_, mut graphics, physics, events, _| { testbed.add_callback(move |mut graphics, physics, events, _| {
while let Ok(prox) = events.intersection_events.try_recv() { while let Ok(prox) = events.intersection_events.try_recv() {
let color = if prox.intersecting { let color = if prox.intersecting {
Point3::new(1.0, 1.0, 0.0) Point3::new(1.0, 1.0, 0.0)
@@ -145,8 +151,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -39,7 +39,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
let green_collider_handle = colliders.insert(green_floor, floor_handle, &mut bodies); let green_collider_handle = colliders.insert(green_floor, floor_handle, &mut bodies);
testbed.set_collider_initial_color(green_collider_handle, Point3::new(0.0, 1.0, 0.0)); testbed.set_initial_collider_color(green_collider_handle, Point3::new(0.0, 1.0, 0.0));
/* /*
* A blue floor that will collide with the BLUE group only. * A blue floor that will collide with the BLUE group only.
@@ -50,7 +50,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
let blue_collider_handle = colliders.insert(blue_floor, floor_handle, &mut bodies); let blue_collider_handle = colliders.insert(blue_floor, floor_handle, &mut bodies);
testbed.set_collider_initial_color(blue_collider_handle, Point3::new(0.0, 0.0, 1.0)); testbed.set_initial_collider_color(blue_collider_handle, Point3::new(0.0, 0.0, 1.0));
/* /*
* Create the cubes * Create the cubes
@@ -84,7 +84,7 @@ pub fn init_world(testbed: &mut Testbed) {
.build(); .build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_body_color(handle, color); testbed.set_initial_body_color(handle, color);
} }
} }
} }
@@ -95,8 +95,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -93,8 +93,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -1,10 +1,11 @@
use kiss3d::loader::obj; use na::Point3;
use na::{Point3, Translation3}; use obj::raw::object::Polygon;
use rapier3d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet}; use rapier3d::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
use rapier3d::geometry::{ColliderBuilder, ColliderSet, SharedShape}; use rapier3d::geometry::{ColliderBuilder, ColliderSet, SharedShape};
use rapier3d::parry::bounding_volume::{self, BoundingVolume}; use rapier3d::parry::bounding_volume;
use rapier_testbed3d::Testbed; use rapier_testbed3d::Testbed;
use std::path::Path; use std::fs::File;
use std::io::BufReader;
/* /*
* NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type. * NOTE: The `r` macro is only here to convert from f64 to the `N` scalar type.
@@ -42,47 +43,45 @@ pub fn init_world(testbed: &mut Testbed) {
for (igeom, obj_path) in geoms.into_iter().enumerate() { for (igeom, obj_path) in geoms.into_iter().enumerate() {
let deltas = na::one(); let deltas = na::one();
let mtl_path = Path::new("");
let mut shapes = Vec::new(); let mut shapes = Vec::new();
println!("Parsing and decomposing: {}", obj_path); println!("Parsing and decomposing: {}", obj_path);
let obj = obj::parse_file(&Path::new(&obj_path), &mtl_path, ""); let input = BufReader::new(File::open(obj_path).unwrap());
if let Ok(model) = obj { if let Ok(model) = obj::raw::parse_obj(input) {
let meshes: Vec<_> = model let mut vertices: Vec<_> = model
.positions
.iter()
.map(|v| Point3::new(v.0, v.1, v.2))
.collect();
use std::iter::FromIterator;
let indices: Vec<_> = model
.polygons
.into_iter() .into_iter()
.map(|mesh| mesh.1.to_trimesh().unwrap()) .flat_map(|p| match p {
Polygon::P(idx) => idx.into_iter(),
Polygon::PT(idx) => Vec::from_iter(idx.into_iter().map(|i| i.0)).into_iter(),
Polygon::PN(idx) => Vec::from_iter(idx.into_iter().map(|i| i.0)).into_iter(),
Polygon::PTN(idx) => Vec::from_iter(idx.into_iter().map(|i| i.0)).into_iter(),
})
.collect(); .collect();
// Compute the size of the model, to scale it and have similar size for everything. // Compute the size of the model, to scale it and have similar size for everything.
let mut aabb = let aabb = bounding_volume::details::point_cloud_aabb(&deltas, &vertices);
bounding_volume::details::point_cloud_aabb(&deltas, &meshes[0].coords[..]); let center = aabb.center();
for mesh in meshes[1..].iter() {
aabb.merge(&bounding_volume::details::point_cloud_aabb(
&deltas,
&mesh.coords[..],
));
}
let center = aabb.center().coords;
let diag = (aabb.maxs - aabb.mins).norm(); let diag = (aabb.maxs - aabb.mins).norm();
for mut trimesh in meshes.into_iter() { vertices
trimesh.translate_by(&Translation3::from(-center)); .iter_mut()
trimesh.scale_by_scalar(6.0 / diag); .for_each(|p| *p = (*p - center.coords) * 6.0 / diag);
let vertices = trimesh.coords; let indices: Vec<_> = indices
let indices: Vec<_> = trimesh .chunks(3)
.indices .map(|idx| [idx[0] as u32, idx[1] as u32, idx[2] as u32])
.unwrap_unified()
.into_iter()
.map(|idx| [idx.x, idx.y, idx.z])
.collect(); .collect();
let decomposed_shape = SharedShape::convex_decomposition(&vertices, &indices); let decomposed_shape = SharedShape::convex_decomposition(&vertices, &indices);
shapes.push(decomposed_shape); shapes.push(decomposed_shape);
}
// let compound = SharedShape::compound(compound_parts); // let compound = SharedShape::compound(compound_parts);
@@ -122,7 +121,7 @@ fn models() -> Vec<String> {
"media/models/hornbug.obj".to_string(), "media/models/hornbug.obj".to_string(),
"media/models/octopus_decimated.obj".to_string(), "media/models/octopus_decimated.obj".to_string(),
"media/models/rabbit_decimated.obj".to_string(), "media/models/rabbit_decimated.obj".to_string(),
"media/models/rust_logo.obj".to_string(), // "media/models/rust_logo.obj".to_string(),
"media/models/rust_logo_simplified.obj".to_string(), "media/models/rust_logo_simplified.obj".to_string(),
"media/models/screwdriver_decimated.obj".to_string(), "media/models/screwdriver_decimated.obj".to_string(),
"media/models/table.obj".to_string(), "media/models/table.obj".to_string(),

View File

@@ -71,8 +71,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(30.0, 30.0, 30.0), Point3::origin()); testbed.look_at(Point3::new(30.0, 30.0, 30.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -34,12 +34,13 @@ pub fn init_world(testbed: &mut Testbed) {
let collider = ColliderBuilder::ball(ball_rad).density(100.0).build(); let collider = ColliderBuilder::ball(ball_rad).density(100.0).build();
colliders.insert(collider, ball_handle, &mut bodies); colliders.insert(collider, ball_handle, &mut bodies);
testbed.add_callback(move |mut window, mut graphics, physics, _, _| { testbed.add_callback(move |_, physics, _, _| {
// Remove then re-add the ground collider. // Remove then re-add the ground collider.
let removed_collider_handle = ground_collider_handle;
let coll = physics let coll = physics
.colliders .colliders
.remove( .remove(
ground_collider_handle, removed_collider_handle,
&mut physics.islands, &mut physics.islands,
&mut physics.bodies, &mut physics.bodies,
true, true,
@@ -48,10 +49,6 @@ pub fn init_world(testbed: &mut Testbed) {
ground_collider_handle = physics ground_collider_handle = physics
.colliders .colliders
.insert(coll, ground_handle, &mut physics.bodies); .insert(coll, ground_handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) {
graphics.add_collider(window, ground_collider_handle, &physics.colliders);
}
}); });
/* /*
@@ -60,8 +57,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -43,8 +43,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -44,8 +44,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -58,8 +58,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -45,7 +45,7 @@ pub fn init_world(testbed: &mut Testbed) {
let mut extra_colliders = Vec::new(); let mut extra_colliders = Vec::new();
let snapped_frame = 51; let snapped_frame = 51;
testbed.add_callback(move |mut window, mut graphics, physics, _, _| { testbed.add_callback(move |mut graphics, physics, _, _| {
step += 1; step += 1;
// Add a bigger ball collider // Add a bigger ball collider
@@ -57,8 +57,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(collider, ball_handle, &mut physics.bodies); .insert(collider, ball_handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.add_collider(window, new_ball_collider_handle, &physics.colliders); graphics.add_collider(new_ball_collider_handle, &physics.colliders);
} }
extra_colliders.push(new_ball_collider_handle); extra_colliders.push(new_ball_collider_handle);
@@ -79,6 +79,10 @@ pub fn init_world(testbed: &mut Testbed) {
step = snapped_frame; step = snapped_frame;
for handle in &extra_colliders { for handle in &extra_colliders {
if let Some(graphics) = &mut graphics {
graphics.remove_collider(*handle, &physics.colliders);
}
physics physics
.colliders .colliders
.remove(*handle, &mut physics.islands, &mut physics.bodies, true); .remove(*handle, &mut physics.islands, &mut physics.bodies, true);
@@ -102,8 +106,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(coll, ground_handle, &mut physics.bodies); .insert(coll, ground_handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.add_collider(window, new_ground_collider_handle, &physics.colliders); graphics.add_collider(new_ground_collider_handle, &physics.colliders);
} }
extra_colliders.push(new_ground_collider_handle); extra_colliders.push(new_ground_collider_handle);
@@ -115,8 +119,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -41,10 +41,5 @@ pub fn init_world(testbed: &mut Testbed) {
* Set up the testbed. * Set up the testbed.
*/ */
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
}
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
} }

View File

@@ -48,8 +48,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.look_at(Point3::new(100.0, -10.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, -10.0, 100.0), Point3::origin());
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -103,8 +103,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -44,7 +44,7 @@ pub fn init_world(testbed: &mut Testbed) {
let mut step = 0; let mut step = 0;
let snapped_frame = 51; let snapped_frame = 51;
testbed.add_callback(move |_, _, physics, _, _| { testbed.add_callback(move |_, physics, _, _| {
step += 1; step += 1;
// Snap the ball velocity or restore it. // Snap the ball velocity or restore it.
@@ -70,8 +70,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -44,7 +44,7 @@ pub fn init_world(testbed: &mut Testbed) {
let mut step = 0; let mut step = 0;
let snapped_frame = 51; let snapped_frame = 51;
testbed.add_callback(move |_, _, physics, _, _| { testbed.add_callback(move |_, physics, _, _| {
step += 1; step += 1;
// Snap the ball velocity or restore it. // Snap the ball velocity or restore it.
@@ -116,8 +116,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -41,8 +41,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -24,16 +24,16 @@ pub fn init_world(testbed: &mut Testbed) {
Point3::new(-width, -width, width), Point3::new(-width, -width, width),
]; ];
let idx = vec![ let idx = vec![
[0, 1, 2], [0, 2, 1],
[0, 2, 3], [0, 3, 2],
[4, 5, 6], [4, 5, 6],
[4, 6, 7], [4, 6, 7],
[0, 4, 7], [0, 4, 7],
[0, 7, 3], [0, 7, 3],
[1, 5, 6], [1, 6, 5],
[1, 6, 2], [1, 2, 6],
[3, 2, 7], [3, 7, 2],
[2, 6, 7], [2, 7, 6],
[0, 1, 5], [0, 1, 5],
[0, 5, 4], [0, 5, 4],
]; ];
@@ -54,6 +54,7 @@ pub fn init_world(testbed: &mut Testbed) {
let handle = bodies.insert(rigid_body); let handle = bodies.insert(rigid_body);
let collider = ColliderBuilder::trimesh(vtx, idx).build(); let collider = ColliderBuilder::trimesh(vtx, idx).build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_initial_body_color(handle, Point3::new(0.3, 0.3, 0.3));
/* /*
* Set up the testbed. * Set up the testbed.
@@ -61,8 +62,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin()); testbed.look_at(Point3::new(10.0, 10.0, 10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -60,7 +60,7 @@ pub fn init_world(testbed: &mut Testbed) {
let handle = bodies.insert(rigid_body); let handle = bodies.insert(rigid_body);
let collider = ColliderBuilder::cuboid(thickness, width * 2.0, width).build(); let collider = ColliderBuilder::cuboid(thickness, width * 2.0, width).build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_body_color(handle, colors[i % 2]); testbed.set_initial_body_color(handle, colors[i % 2]);
} else { } else {
skip -= 1; skip -= 1;
} }
@@ -78,8 +78,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -25,7 +25,7 @@ pub fn init_world(testbed: &mut Testbed) {
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| { testbed.add_callback(move |mut graphics, physics, _, run_state| {
let rigid_body = RigidBodyBuilder::new_dynamic() let rigid_body = RigidBodyBuilder::new_dynamic()
.translation(0.0, 10.0, 0.0) .translation(0.0, 10.0, 0.0)
.build(); .build();
@@ -40,8 +40,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(collider, handle, &mut physics.bodies); .insert(collider, handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.add(window, handle, &physics.bodies, &physics.colliders); graphics.add_body(handle, &physics.bodies, &physics.colliders);
} }
if physics.bodies.len() > MAX_NUMBER_OF_BODIES { if physics.bodies.len() > MAX_NUMBER_OF_BODIES {
@@ -68,8 +68,8 @@ pub fn init_world(testbed: &mut Testbed) {
&mut physics.joints, &mut physics.joints,
); );
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = &mut graphics {
graphics.remove_body_nodes(window, *handle); graphics.remove_body(*handle);
} }
} }
} }
@@ -85,8 +85,3 @@ pub fn init_world(testbed: &mut Testbed) {
// .velocity_based_erp = 0.2; // .velocity_based_erp = 0.2;
testbed.look_at(Point3::new(-30.0, 4.0, -30.0), Point3::new(0.0, 1.0, 0.0)); testbed.look_at(Point3::new(-30.0, 4.0, -30.0), Point3::new(0.0, 1.0, 0.0));
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Add-remove", init_world)]);
testbed.run()
}

View File

@@ -97,8 +97,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -472,8 +472,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(15.0, 5.0, 42.0), Point3::new(13.0, 1.0, 1.0)); testbed.look_at(Point3::new(15.0, 5.0, 42.0), Point3::new(13.0, 1.0, 1.0));
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Joints", init_world)]);
testbed.run()
}

View File

@@ -51,7 +51,7 @@ pub fn build_block(
let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build(); let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build();
colliders.insert(collider, handle, bodies); colliders.insert(collider, handle, bodies);
testbed.set_body_color(handle, color0); testbed.set_initial_body_color(handle, color0);
std::mem::swap(&mut color0, &mut color1); std::mem::swap(&mut color0, &mut color1);
} }
} }
@@ -73,7 +73,7 @@ pub fn build_block(
let handle = bodies.insert(rigid_body); let handle = bodies.insert(rigid_body);
let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build(); let collider = ColliderBuilder::cuboid(dim.x, dim.y, dim.z).build();
colliders.insert(collider, handle, bodies); colliders.insert(collider, handle, bodies);
testbed.set_body_color(handle, color0); testbed.set_initial_body_color(handle, color0);
std::mem::swap(&mut color0, &mut color1); std::mem::swap(&mut color0, &mut color1);
} }
} }
@@ -137,8 +137,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -59,8 +59,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(10.0, 3.0, 0.0), Point3::new(0.0, 3.0, 0.0)); testbed.look_at(Point3::new(10.0, 3.0, 0.0), Point3::new(0.0, 3.0, 0.0));
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -100,7 +100,7 @@ pub fn init_world(testbed: &mut Testbed) {
* Spawn cubes at regular intervals and apply a custom gravity * Spawn cubes at regular intervals and apply a custom gravity
* depending on their position. * depending on their position.
*/ */
testbed.add_callback(move |mut window, mut graphics, physics, _, run_state| { testbed.add_callback(move |graphics, physics, _, run_state| {
if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 { if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 {
// Spawn a new cube. // Spawn a new cube.
let collider = ColliderBuilder::cuboid(1.0, 2.0, 1.5).build(); let collider = ColliderBuilder::cuboid(1.0, 2.0, 1.5).build();
@@ -112,8 +112,8 @@ pub fn init_world(testbed: &mut Testbed) {
.colliders .colliders
.insert(collider, handle, &mut physics.bodies); .insert(collider, handle, &mut physics.bodies);
if let (Some(graphics), Some(window)) = (&mut graphics, &mut window) { if let Some(graphics) = graphics {
graphics.add(window, handle, &physics.bodies, &physics.colliders); graphics.add_body(handle, &physics.bodies, &physics.colliders);
} }
} }
@@ -139,8 +139,3 @@ pub fn init_world(testbed: &mut Testbed) {
); );
testbed.look_at(Point3::new(-100.0, 0.0, 0.0), Point3::origin()); testbed.look_at(Point3::new(-100.0, 0.0, 0.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -65,7 +65,7 @@ pub fn init_world(testbed: &mut Testbed) {
* Setup a callback to control the platform. * Setup a callback to control the platform.
*/ */
let mut count = 0; let mut count = 0;
testbed.add_callback(move |_, _, physics, _, run_state| { testbed.add_callback(move |_, physics, _, run_state| {
count += 1; count += 1;
if count % 100 > 50 { if count % 100 > 50 {
return; return;
@@ -95,8 +95,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(-10.0, 5.0, -10.0), Point3::origin()); testbed.look_at(Point3::new(-10.0, 5.0, -10.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Kinematic body", init_world)]);
testbed.run()
}

View File

@@ -73,8 +73,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -49,8 +49,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(0.0, 3.0, 30.0), Point3::new(0.0, 3.0, 0.0)); testbed.look_at(Point3::new(0.0, 3.0, 30.0), Point3::new(0.0, 3.0, 0.0));
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

View File

@@ -46,7 +46,7 @@ pub fn init_world(testbed: &mut Testbed) {
let collider = ColliderBuilder::cuboid(rad, rad, rad).build(); let collider = ColliderBuilder::cuboid(rad, rad, rad).build();
colliders.insert(collider, handle, &mut bodies); colliders.insert(collider, handle, &mut bodies);
testbed.set_body_color(handle, Point3::new(0.5, 0.5, 1.0)); testbed.set_initial_body_color(handle, Point3::new(0.5, 0.5, 1.0));
} }
} }
@@ -70,10 +70,10 @@ pub fn init_world(testbed: &mut Testbed) {
let sensor_collider = ColliderBuilder::ball(rad * 5.0).sensor(true).build(); let sensor_collider = ColliderBuilder::ball(rad * 5.0).sensor(true).build();
colliders.insert(sensor_collider, sensor_handle, &mut bodies); colliders.insert(sensor_collider, sensor_handle, &mut bodies);
testbed.set_body_color(sensor_handle, Point3::new(0.5, 1.0, 1.0)); testbed.set_initial_body_color(sensor_handle, Point3::new(0.5, 1.0, 1.0));
// Callback that will be executed on the main loop to handle proximities. // Callback that will be executed on the main loop to handle proximities.
testbed.add_callback(move |_, mut graphics, physics, events, _| { testbed.add_callback(move |mut graphics, physics, events, _| {
while let Ok(prox) = events.intersection_events.try_recv() { while let Ok(prox) = events.intersection_events.try_recv() {
let color = if prox.intersecting { let color = if prox.intersecting {
Point3::new(1.0, 1.0, 0.0) Point3::new(1.0, 1.0, 0.0)
@@ -101,8 +101,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(-6.0, 4.0, -6.0), Point3::new(0.0, 1.0, 0.0)); testbed.look_at(Point3::new(-6.0, 4.0, -6.0), Point3::new(0.0, 1.0, 0.0));
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Sensor", init_world)]);
testbed.run()
}

View File

@@ -102,8 +102,3 @@ pub fn init_world(testbed: &mut Testbed) {
testbed.set_world(bodies, colliders, joints); testbed.set_world(bodies, colliders, joints);
testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin()); testbed.look_at(Point3::new(100.0, 100.0, 100.0), Point3::origin());
} }
fn main() {
let testbed = Testbed::from_builders(0, vec![("Boxes", init_world)]);
testbed.run()
}

94
src_testbed/camera.rs Normal file
View File

@@ -0,0 +1,94 @@
// NOTE: this is inspired from the `bevy-orbit-controls` projects but
// with some modifications like Panning, and 2D support.
use bevy::input::mouse::MouseMotion;
use bevy::input::mouse::MouseScrollUnit::{Line, Pixel};
use bevy::input::mouse::MouseWheel;
use bevy::prelude::*;
use bevy::render::camera::Camera;
const LINE_TO_PIXEL_RATIO: f32 = 0.1;
pub struct OrbitCamera {
pub zoom: f32,
pub center: Vec3,
pub pan_sensitivity: f32,
pub zoom_sensitivity: f32,
pub pan_button: MouseButton,
pub enabled: bool,
}
impl Default for OrbitCamera {
fn default() -> Self {
OrbitCamera {
zoom: 100.0,
center: Vec3::ZERO,
pan_sensitivity: 1.0,
zoom_sensitivity: 0.8,
pan_button: MouseButton::Right,
enabled: true,
}
}
}
// Adapted from the 3D orbit camera from bevy-orbit-controls
pub struct OrbitCameraPlugin;
impl OrbitCameraPlugin {
fn update_transform_system(
mut query: Query<(&OrbitCamera, &mut Transform), (Changed<OrbitCamera>, With<Camera>)>,
) {
for (camera, mut transform) in query.iter_mut() {
if camera.enabled {
transform.translation = camera.center;
transform.scale = Vec3::new(1.0 / camera.zoom, 1.0 / camera.zoom, 1.0);
}
}
}
fn mouse_motion_system(
_time: Res<Time>,
mut mouse_motion_events: EventReader<MouseMotion>,
mouse_button_input: Res<Input<MouseButton>>,
mut query: Query<(&mut OrbitCamera, &mut Transform, &mut Camera)>,
) {
let mut delta = Vec2::ZERO;
for event in mouse_motion_events.iter() {
delta += event.delta;
}
for (mut camera, _, _) in query.iter_mut() {
if !camera.enabled {
continue;
}
if mouse_button_input.pressed(camera.pan_button) {
let delta = delta * camera.pan_sensitivity;
camera.center += Vec3::new(-delta.x, delta.y, 0.0);
}
}
}
fn zoom_system(
mut mouse_wheel_events: EventReader<MouseWheel>,
mut query: Query<&mut OrbitCamera, With<Camera>>,
) {
let mut total = 0.0;
for event in mouse_wheel_events.iter() {
total -= event.y
* match event.unit {
Line => 1.0,
Pixel => LINE_TO_PIXEL_RATIO,
};
}
for mut camera in query.iter_mut() {
if camera.enabled {
camera.zoom *= camera.zoom_sensitivity.powf(total);
}
}
}
}
impl Plugin for OrbitCameraPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_system(Self::mouse_motion_system.system())
.add_system(Self::zoom_system.system())
.add_system(Self::update_transform_system.system());
}
}

View File

@@ -1,712 +0,0 @@
#[cfg(feature = "dim3")]
use kiss3d::camera::ArcBall as Camera;
#[cfg(feature = "dim2")]
use kiss3d::planar_camera::Sidescroll as Camera;
use kiss3d::window::Window;
use na::Point3;
use crate::math::{Isometry, Point};
use crate::objects::ball::Ball;
use crate::objects::box_node::Box as BoxNode;
use crate::objects::heightfield::HeightField;
use crate::objects::node::{GraphicsNode, Node};
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
use rapier::geometry::{ColliderHandle, ColliderSet, Shape};
//use crate::objects::capsule::Capsule;
use crate::objects::convex::Convex;
//#[cfg(feature = "dim3")]
//use crate::objects::mesh::Mesh;
//use crate::objects::plane::Plane;
//#[cfg(feature = "dim2")]
//use crate::objects::polyline::Polyline;
use crate::objects::capsule::Capsule;
#[cfg(feature = "dim3")]
use crate::objects::cone::Cone;
#[cfg(feature = "dim3")]
use crate::objects::cylinder::Cylinder;
use crate::objects::mesh::Mesh;
use crate::objects::polyline::Polyline;
use rand::{Rng, SeedableRng};
use rand_pcg::Pcg32;
use std::collections::HashMap;
pub trait GraphicsWindow {
fn remove_graphics_node(&mut self, node: &mut GraphicsNode);
fn draw_graphics_line(&mut self, p1: &Point<f32>, p2: &Point<f32>, color: &Point3<f32>);
}
impl GraphicsWindow for Window {
fn remove_graphics_node(&mut self, node: &mut GraphicsNode) {
#[cfg(feature = "dim2")]
self.remove_planar_node(node);
#[cfg(feature = "dim3")]
self.remove_node(node);
}
fn draw_graphics_line(&mut self, p1: &Point<f32>, p2: &Point<f32>, color: &Point3<f32>) {
#[cfg(feature = "dim2")]
self.draw_planar_line(p1, p2, color);
#[cfg(feature = "dim3")]
self.draw_line(p1, p2, color);
}
}
pub struct GraphicsManager {
rand: Pcg32,
b2sn: HashMap<RigidBodyHandle, Vec<Node>>,
b2color: HashMap<RigidBodyHandle, Point3<f32>>,
c2color: HashMap<ColliderHandle, Point3<f32>>,
b2wireframe: HashMap<RigidBodyHandle, bool>,
ground_color: Point3<f32>,
camera: Camera,
}
impl GraphicsManager {
pub fn new() -> GraphicsManager {
let mut camera;
#[cfg(feature = "dim3")]
{
camera = Camera::new(Point3::new(10.0, 10.0, 10.0), Point3::new(0.0, 0.0, 0.0));
camera.set_rotate_modifiers(Some(kiss3d::event::Modifiers::Control));
}
#[cfg(feature = "dim2")]
{
camera = Camera::new();
camera.set_zoom(50.0);
}
GraphicsManager {
camera,
rand: Pcg32::seed_from_u64(0),
b2sn: HashMap::new(),
b2color: HashMap::new(),
c2color: HashMap::new(),
ground_color: Point3::new(0.5, 0.5, 0.5),
b2wireframe: HashMap::new(),
}
}
pub fn clear(&mut self, window: &mut Window) {
for sns in self.b2sn.values_mut() {
for sn in sns.iter_mut() {
if let Some(node) = sn.scene_node_mut() {
window.remove_graphics_node(node);
}
}
}
self.b2sn.clear();
self.c2color.clear();
self.b2color.clear();
self.b2wireframe.clear();
self.rand = Pcg32::seed_from_u64(0);
}
pub fn remove_body_nodes(&mut self, window: &mut Window, body: RigidBodyHandle) {
if let Some(sns) = self.b2sn.get_mut(&body) {
for sn in sns.iter_mut() {
if let Some(node) = sn.scene_node_mut() {
window.remove_graphics_node(node);
}
}
}
self.b2sn.remove(&body);
}
pub fn set_body_color(&mut self, b: RigidBodyHandle, color: Point3<f32>) {
self.b2color.insert(b, color);
if let Some(ns) = self.b2sn.get_mut(&b) {
for n in ns.iter_mut() {
n.set_color(color)
}
}
}
pub fn set_collider_initial_color(&mut self, c: ColliderHandle, color: Point3<f32>) {
self.c2color.insert(c, color);
}
pub fn set_body_wireframe(&mut self, b: RigidBodyHandle, enabled: bool) {
self.b2wireframe.insert(b, enabled);
if let Some(ns) = self.b2sn.get_mut(&b) {
for n in ns.iter_mut().filter_map(|n| n.scene_node_mut()) {
if enabled {
n.set_surface_rendering_activation(true);
n.set_lines_width(1.0);
} else {
n.set_surface_rendering_activation(false);
n.set_lines_width(1.0);
}
}
}
}
pub fn toggle_wireframe_mode(&mut self, colliders: &ColliderSet, enabled: bool) {
for n in self.b2sn.values_mut().flat_map(|val| val.iter_mut()) {
let force_wireframe = if let Some(collider) = colliders.get(n.collider()) {
collider.is_sensor()
|| self
.b2wireframe
.get(&collider.parent())
.cloned()
.unwrap_or(false)
} else {
false
};
if let Some(node) = n.scene_node_mut() {
if force_wireframe || enabled {
node.set_lines_width(1.0);
node.set_surface_rendering_activation(false);
} else {
node.set_lines_width(0.0);
node.set_surface_rendering_activation(true);
}
}
}
}
pub fn next_color(&mut self) -> Point3<f32> {
Self::gen_color(&mut self.rand)
}
fn gen_color(rng: &mut Pcg32) -> Point3<f32> {
let mut color: Point3<f32> = rng.gen();
color *= 1.5;
color.x = color.x.min(1.0);
color.y = color.y.min(1.0);
color.z = color.z.min(1.0);
color
}
fn alloc_color(&mut self, handle: RigidBodyHandle, is_static: bool) -> Point3<f32> {
let mut color = self.ground_color;
if !is_static {
match self.b2color.get(&handle).cloned() {
Some(c) => color = c,
None => color = Self::gen_color(&mut self.rand),
}
}
self.set_body_color(handle, color);
color
}
pub fn add(
&mut self,
window: &mut Window,
handle: RigidBodyHandle,
bodies: &RigidBodySet,
colliders: &ColliderSet,
) {
let body = bodies.get(handle).unwrap();
let color = self
.b2color
.get(&handle)
.cloned()
.unwrap_or_else(|| self.alloc_color(handle, !body.is_dynamic()));
self.add_with_color(window, handle, bodies, colliders, color)
}
pub fn add_with_color(
&mut self,
window: &mut Window,
handle: RigidBodyHandle,
bodies: &RigidBodySet,
colliders: &ColliderSet,
color: Point3<f32>,
) {
// let body = bodies.get(handle).unwrap();
let mut new_nodes = Vec::new();
for collider_handle in bodies[handle].colliders() {
let color = self.c2color.get(collider_handle).copied().unwrap_or(color);
let collider = &colliders[*collider_handle];
self.do_add_shape(
window,
*collider_handle,
collider.shape(),
&Isometry::identity(),
color,
&mut new_nodes,
);
}
new_nodes.iter_mut().for_each(|n| n.update(colliders));
for node in new_nodes.iter_mut().filter_map(|n| n.scene_node_mut()) {
if self.b2wireframe.get(&handle).cloned() == Some(true) {
node.set_lines_width(1.0);
node.set_surface_rendering_activation(false);
} else {
node.set_lines_width(0.0);
node.set_surface_rendering_activation(true);
}
}
let nodes = self.b2sn.entry(handle).or_insert_with(Vec::new);
nodes.append(&mut new_nodes);
}
pub fn add_collider(
&mut self,
window: &mut Window,
handle: ColliderHandle,
colliders: &ColliderSet,
) {
let collider = &colliders[handle];
let color = *self.b2color.get(&collider.parent()).unwrap();
let color = self.c2color.get(&handle).copied().unwrap_or(color);
let mut nodes =
std::mem::replace(self.b2sn.get_mut(&collider.parent()).unwrap(), Vec::new());
self.do_add_shape(
window,
handle,
collider.shape(),
&Isometry::identity(),
color,
&mut nodes,
);
self.b2sn.insert(collider.parent(), nodes);
}
fn do_add_shape(
&mut self,
window: &mut Window,
handle: ColliderHandle,
shape: &dyn Shape,
delta: &Isometry<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
if let Some(compound) = shape.as_compound() {
for (shape_pos, shape) in compound.shapes() {
self.do_add_shape(window, handle, &**shape, shape_pos, color, out)
}
}
if let Some(ball) = shape.as_ball() {
out.push(Node::Ball(Ball::new(
handle,
*delta,
ball.radius,
color,
window,
)))
}
if let Some(cuboid) = shape
.as_cuboid()
.or(shape.as_round_cuboid().map(|r| &r.base_shape))
{
out.push(Node::Box(BoxNode::new(
handle,
*delta,
cuboid.half_extents,
color,
window,
)))
}
if let Some(capsule) = shape.as_capsule() {
out.push(Node::Capsule(Capsule::new(
handle, *delta, capsule, color, window,
)))
}
if let Some(triangle) = shape
.as_triangle()
.or(shape.as_round_triangle().map(|r| &r.base_shape))
{
out.push(Node::Mesh(Mesh::new(
handle,
vec![triangle.a, triangle.b, triangle.c],
vec![[0, 1, 2]],
color,
window,
)))
}
if let Some(trimesh) = shape.as_trimesh() {
out.push(Node::Mesh(Mesh::new(
handle,
trimesh.vertices().to_vec(),
trimesh.indices().to_vec(),
color,
window,
)))
}
if let Some(polyline) = shape.as_polyline() {
out.push(Node::Polyline(Polyline::new(
handle,
polyline.vertices().to_vec(),
polyline.indices().to_vec(),
color,
)))
}
if let Some(heightfield) = shape.as_heightfield() {
out.push(Node::HeightField(HeightField::new(
handle,
heightfield,
color,
window,
)))
}
#[cfg(feature = "dim2")]
if let Some(convex_polygon) = shape
.as_convex_polygon()
.or(shape.as_round_convex_polygon().map(|r| &r.base_shape))
{
let vertices = convex_polygon.points().to_vec();
out.push(Node::Convex(Convex::new(
handle, *delta, vertices, color, window,
)))
}
#[cfg(feature = "dim3")]
if let Some(convex_polyhedron) = shape
.as_convex_polyhedron()
.or(shape.as_round_convex_polyhedron().map(|r| &r.base_shape))
{
let (vertices, indices) = convex_polyhedron.to_trimesh();
out.push(Node::Convex(Convex::new(
handle, *delta, vertices, indices, color, window,
)))
}
#[cfg(feature = "dim3")]
if let Some(cylinder) = shape
.as_cylinder()
.or(shape.as_round_cylinder().map(|r| &r.base_shape))
{
out.push(Node::Cylinder(Cylinder::new(
handle,
*delta,
cylinder.half_height,
cylinder.radius,
color,
window,
)))
}
#[cfg(feature = "dim3")]
if let Some(cone) = shape
.as_cone()
.or(shape.as_round_cone().map(|r| &r.base_shape))
{
out.push(Node::Cone(Cone::new(
handle,
*delta,
cone.half_height,
cone.radius,
color,
window,
)))
}
}
/*
fn add_plane(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
shape: &shape::Plane<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let pos = colliders.get(object).unwrap().position();
let position = Point::from(pos.translation.vector);
let normal = pos * shape.normal();
out.push(Node::Plane(Plane::new(
object, colliders, &position, &normal, color, window,
)))
}
#[cfg(feature = "dim2")]
fn add_polyline(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &shape::Polyline<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let vertices = shape.points().to_vec();
let indices = shape.edges().iter().map(|e| e.indices).collect();
out.push(Node::Polyline(Polyline::new(
object, colliders, delta, vertices, indices, color, window,
)))
}
#[cfg(feature = "dim3")]
fn add_mesh(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &TriMesh<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let points = shape.points();
let faces = shape.faces();
let is = faces
.iter()
.map(|f| Point3::new(f.indices.x as u32, f.indices.y as u32, f.indices.z as u32))
.collect();
out.push(Node::Mesh(Mesh::new(
object,
colliders,
delta,
points.to_vec(),
is,
color,
window,
)))
}
fn add_heightfield(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
heightfield: &shape::HeightField<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
out.push(Node::HeightField(HeightField::new(
object,
colliders,
delta,
heightfield,
color,
window,
)))
}
fn add_capsule(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &shape::Capsule<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let margin = colliders.get(object).unwrap().margin();
out.push(Node::Capsule(Capsule::new(
object,
colliders,
delta,
shape.radius() + margin,
shape.height(),
color,
window,
)))
}
fn add_ball(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &shape::Ball<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let margin = colliders.get(object).unwrap().margin();
out.push(Node::Ball(Ball::new(
object,
colliders,
delta,
shape.radius() + margin,
color,
window,
)))
}
fn add_box(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &Cuboid,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let margin = colliders.get(object).unwrap().margin();
out.push(Node::Box(Box::new(
object,
colliders,
delta,
shape.half_extents() + Vector::repeat(margin),
color,
window,
)))
}
#[cfg(feature = "dim2")]
fn add_convex(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &ConvexPolygon<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let points = shape.points();
out.push(Node::Convex(Convex::new(
object,
colliders,
delta,
points.to_vec(),
color,
window,
)))
}
#[cfg(feature = "dim3")]
fn add_convex(
&mut self,
window: &mut Window,
object: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
delta: Isometry<f32>,
shape: &ConvexHull<f32>,
color: Point3<f32>,
out: &mut Vec<Node>,
) {
let mut chull = transformation::convex_hull(shape.points());
chull.replicate_vertices();
chull.recompute_normals();
out.push(Node::Convex(Convex::new(
object, colliders, delta, &chull, color, window,
)))
}
*/
pub fn draw(&mut self, _bodies: &RigidBodySet, colliders: &ColliderSet, window: &mut Window) {
// use kiss3d::camera::Camera;
// println!(
// "camera eye {:?}, at: {:?}",
// self.camera.eye(),
// self.camera.at()
// );
for (_, ns) in self.b2sn.iter_mut() {
for n in ns.iter_mut() {
// if let Some(co) = colliders.get(n.collider()) {
// let bo = &_bodies[co.parent()];
//
// if bo.is_dynamic() {
// if bo.is_ccd_active() {
// n.set_color(Point3::new(1.0, 0.0, 0.0));
// } else {
// n.set_color(Point3::new(0.0, 1.0, 0.0));
// }
// }
// }
n.update(colliders);
n.draw(window);
}
}
}
// pub fn draw_positions(&mut self, window: &mut Window, rbs: &RigidBodies<f32>) {
// for (_, ns) in self.b2sn.iter_mut() {
// for n in ns.iter_mut() {
// let object = n.object();
// let rb = rbs.get(object).expect("Rigid body not found.");
// // if let WorldObjectBorrowed::RigidBody(rb) = object {
// let t = rb.position();
// let center = rb.center_of_mass();
// let rotmat = t.rotation.to_rotation_matrix().unwrap();
// let x = rotmat.column(0) * 0.25f32;
// let y = rotmat.column(1) * 0.25f32;
// let z = rotmat.column(2) * 0.25f32;
// window.draw_line(center, &(*center + x), &Point3::new(1.0, 0.0, 0.0));
// window.draw_line(center, &(*center + y), &Point3::new(0.0, 1.0, 0.0));
// window.draw_line(center, &(*center + z), &Point3::new(0.0, 0.0, 1.0));
// // }
// }
// }
// }
pub fn camera(&self) -> &Camera {
&self.camera
}
pub fn camera_mut(&mut self) -> &mut Camera {
&mut self.camera
}
#[cfg(feature = "dim3")]
pub fn look_at(&mut self, eye: Point<f32>, at: Point<f32>) {
self.camera.look_at(eye, at);
}
#[cfg(feature = "dim2")]
pub fn look_at(&mut self, at: Point<f32>, zoom: f32) {
self.camera.look_at(at, zoom);
}
pub fn body_nodes(&self, handle: RigidBodyHandle) -> Option<&Vec<Node>> {
self.b2sn.get(&handle)
}
pub fn body_nodes_mut(&mut self, handle: RigidBodyHandle) -> Option<&mut Vec<Node>> {
self.b2sn.get_mut(&handle)
}
pub fn nodes(&self) -> impl Iterator<Item = &Node> {
self.b2sn.values().flat_map(|val| val.iter())
}
pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut Node> {
self.b2sn.values_mut().flat_map(|val| val.iter_mut())
}
#[cfg(feature = "dim3")]
pub fn set_up_axis(&mut self, up_axis: na::Vector3<f32>) {
self.camera.set_up_axis(up_axis);
}
}
impl Default for GraphicsManager {
fn default() -> Self {
Self::new()
}
}

400
src_testbed/graphics.rs Normal file
View File

@@ -0,0 +1,400 @@
use bevy::prelude::*;
use na::Point3;
use crate::math::Isometry;
use crate::objects::node::EntityWithGraphics;
use rapier::dynamics::{RigidBodyHandle, RigidBodySet};
use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType};
//use crate::objects::capsule::Capsule;
//#[cfg(feature = "dim3")]
//use crate::objects::mesh::Mesh;
//use crate::objects::plane::Plane;
//#[cfg(feature = "dim2")]
//use crate::objects::polyline::Polyline;
// use crate::objects::mesh::Mesh;
use rand::{Rng, SeedableRng};
use rand_pcg::Pcg32;
use std::collections::HashMap;
pub struct GraphicsManager {
rand: Pcg32,
b2sn: HashMap<RigidBodyHandle, Vec<EntityWithGraphics>>,
b2color: HashMap<RigidBodyHandle, Point3<f32>>,
c2color: HashMap<ColliderHandle, Point3<f32>>,
b2wireframe: HashMap<RigidBodyHandle, bool>,
ground_color: Point3<f32>,
prefab_meshes: HashMap<ShapeType, Handle<Mesh>>,
}
impl GraphicsManager {
pub fn new() -> GraphicsManager {
GraphicsManager {
rand: Pcg32::seed_from_u64(0),
b2sn: HashMap::new(),
b2color: HashMap::new(),
c2color: HashMap::new(),
ground_color: Point3::new(0.5, 0.5, 0.5),
b2wireframe: HashMap::new(),
prefab_meshes: HashMap::new(),
}
}
pub fn clear(&mut self, commands: &mut Commands) {
for sns in self.b2sn.values_mut() {
for sn in sns.iter_mut() {
commands.entity(sn.entity).despawn()
}
}
self.b2sn.clear();
self.c2color.clear();
self.b2color.clear();
self.b2wireframe.clear();
self.rand = Pcg32::seed_from_u64(0);
}
pub fn remove_collider_nodes(
&mut self,
commands: &mut Commands,
body: RigidBodyHandle,
collider: ColliderHandle,
) {
if let Some(sns) = self.b2sn.get_mut(&body) {
for sn in sns.iter_mut() {
if sn.collider == collider {
commands.entity(sn.entity).despawn();
}
}
}
}
pub fn remove_body_nodes(&mut self, commands: &mut Commands, body: RigidBodyHandle) {
if let Some(sns) = self.b2sn.get_mut(&body) {
for sn in sns.iter_mut() {
commands.entity(sn.entity).despawn();
}
}
self.b2sn.remove(&body);
}
pub fn set_body_color(
&mut self,
materials: &mut Assets<StandardMaterial>,
b: RigidBodyHandle,
color: Point3<f32>,
) {
self.b2color.insert(b, color);
if let Some(ns) = self.b2sn.get_mut(&b) {
for n in ns.iter_mut() {
n.set_color(materials, color)
}
}
}
pub fn set_initial_body_color(&mut self, b: RigidBodyHandle, color: Point3<f32>) {
self.b2color.insert(b, color);
}
pub fn set_initial_collider_color(&mut self, c: ColliderHandle, color: Point3<f32>) {
self.c2color.insert(c, color);
}
pub fn set_body_wireframe(&mut self, b: RigidBodyHandle, enabled: bool) {
self.b2wireframe.insert(b, enabled);
if let Some(_ns) = self.b2sn.get_mut(&b) {
// for n in ns.iter_mut().filter_map(|n| n.scene_node_mut()) {
// if enabled {
// n.set_surface_rendering_activation(true);
// n.set_lines_width(1.0);
// } else {
// n.set_surface_rendering_activation(false);
// n.set_lines_width(1.0);
// }
// }
}
}
pub fn toggle_wireframe_mode(&mut self, colliders: &ColliderSet, _enabled: bool) {
for n in self.b2sn.values_mut().flat_map(|val| val.iter_mut()) {
let _force_wireframe = if let Some(collider) = colliders.get(n.collider) {
collider.is_sensor()
|| self
.b2wireframe
.get(&collider.parent())
.cloned()
.unwrap_or(false)
} else {
false
};
// if let Some(node) = n.scene_node_mut() {
// if force_wireframe || enabled {
// node.set_lines_width(1.0);
// node.set_surface_rendering_activation(false);
// } else {
// node.set_lines_width(0.0);
// node.set_surface_rendering_activation(true);
// }
// }
}
}
pub fn next_color(&mut self) -> Point3<f32> {
Self::gen_color(&mut self.rand)
}
fn gen_color(rng: &mut Pcg32) -> Point3<f32> {
let mut color: Point3<f32> = rng.gen();
color *= 1.5;
color.x = color.x.min(1.0);
color.y = color.y.min(1.0);
color.z = color.z.min(1.0);
color
}
fn alloc_color(
&mut self,
materials: &mut Assets<StandardMaterial>,
handle: RigidBodyHandle,
is_static: bool,
) -> Point3<f32> {
let mut color = self.ground_color;
if !is_static {
match self.b2color.get(&handle).cloned() {
Some(c) => color = c,
None => color = Self::gen_color(&mut self.rand),
}
}
self.set_body_color(materials, handle, color);
color
}
pub fn add(
&mut self,
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
components: &mut Query<(&mut Transform,)>,
handle: RigidBodyHandle,
bodies: &RigidBodySet,
colliders: &ColliderSet,
) {
let body = bodies.get(handle).unwrap();
let color = self
.b2color
.get(&handle)
.cloned()
.unwrap_or_else(|| self.alloc_color(materials, handle, !body.is_dynamic()));
self.add_with_color(
commands, meshes, materials, components, handle, bodies, colliders, color,
)
}
pub fn add_with_color(
&mut self,
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
components: &mut Query<(&mut Transform,)>,
handle: RigidBodyHandle,
bodies: &RigidBodySet,
colliders: &ColliderSet,
color: Point3<f32>,
) {
// let body = bodies.get(handle).unwrap();
let mut new_nodes = Vec::new();
for collider_handle in bodies[handle].colliders() {
let color = self.c2color.get(collider_handle).copied().unwrap_or(color);
let collider = &colliders[*collider_handle];
self.do_add_shape(
commands,
meshes,
materials,
*collider_handle,
collider.shape(),
collider.is_sensor(),
collider.position(),
&Isometry::identity(),
color,
&mut new_nodes,
);
}
new_nodes
.iter_mut()
.for_each(|n| n.update(colliders, components));
// for node in new_nodes.iter_mut().filter_map(|n| n.scene_node_mut()) {
// if self.b2wireframe.get(&handle).cloned() == Some(true) {
// node.set_lines_width(1.0);
// node.set_surface_rendering_activation(false);
// } else {
// node.set_lines_width(0.0);
// node.set_surface_rendering_activation(true);
// }
// }
let nodes = self.b2sn.entry(handle).or_insert_with(Vec::new);
nodes.append(&mut new_nodes);
}
pub fn add_collider(
&mut self,
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
handle: ColliderHandle,
colliders: &ColliderSet,
) {
let collider = &colliders[handle];
let color = *self.b2color.get(&collider.parent()).unwrap();
let color = self.c2color.get(&handle).copied().unwrap_or(color);
let mut nodes =
std::mem::replace(self.b2sn.get_mut(&collider.parent()).unwrap(), Vec::new());
self.do_add_shape(
commands,
meshes,
materials,
handle,
collider.shape(),
collider.is_sensor(),
collider.position(),
&Isometry::identity(),
color,
&mut nodes,
);
self.b2sn.insert(collider.parent(), nodes);
}
fn do_add_shape(
&mut self,
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
handle: ColliderHandle,
shape: &dyn Shape,
sensor: bool,
pos: &Isometry<f32>,
delta: &Isometry<f32>,
color: Point3<f32>,
out: &mut Vec<EntityWithGraphics>,
) {
if let Some(compound) = shape.as_compound() {
for (shape_pos, shape) in compound.shapes() {
self.do_add_shape(
commands,
meshes,
materials,
handle,
&**shape,
sensor,
pos,
&(shape_pos * delta),
color,
out,
)
}
} else {
if self.prefab_meshes.is_empty() {
EntityWithGraphics::gen_prefab_meshes(&mut self.prefab_meshes, meshes);
}
let node = EntityWithGraphics::spawn(
commands,
meshes,
materials,
&self.prefab_meshes,
shape,
handle,
*pos,
*delta,
color,
sensor,
);
out.push(node);
}
}
pub fn draw(
&mut self,
_bodies: &RigidBodySet,
colliders: &ColliderSet,
components: &mut Query<(&mut Transform,)>,
) {
for (_, ns) in self.b2sn.iter_mut() {
for n in ns.iter_mut() {
// if let Some(co) = colliders.get(n.collider()) {
// let bo = &_bodies[co.parent()];
//
// if bo.is_dynamic() {
// if bo.is_ccd_active() {
// n.set_color(Point3::new(1.0, 0.0, 0.0));
// } else {
// n.set_color(Point3::new(0.0, 1.0, 0.0));
// }
// }
// }
n.update(colliders, components);
}
}
}
// pub fn draw_positions(&mut self, window: &mut Window, rbs: &RigidBodies<f32>) {
// for (_, ns) in self.b2sn.iter_mut() {
// for n in ns.iter_mut() {
// let object = n.object();
// let rb = rbs.get(object).expect("Rigid body not found.");
// // if let WorldObjectBorrowed::RigidBody(rb) = object {
// let t = rb.position();
// let center = rb.center_of_mass();
// let rotmat = t.rotation.to_rotation_matrix().unwrap();
// let x = rotmat.column(0) * 0.25f32;
// let y = rotmat.column(1) * 0.25f32;
// let z = rotmat.column(2) * 0.25f32;
// window.draw_line(center, &(*center + x), &Point3::new(1.0, 0.0, 0.0));
// window.draw_line(center, &(*center + y), &Point3::new(0.0, 1.0, 0.0));
// window.draw_line(center, &(*center + z), &Point3::new(0.0, 0.0, 1.0));
// // }
// }
// }
// }
pub fn body_nodes(&self, handle: RigidBodyHandle) -> Option<&Vec<EntityWithGraphics>> {
self.b2sn.get(&handle)
}
pub fn body_nodes_mut(
&mut self,
handle: RigidBodyHandle,
) -> Option<&mut Vec<EntityWithGraphics>> {
self.b2sn.get_mut(&handle)
}
pub fn nodes(&self) -> impl Iterator<Item = &EntityWithGraphics> {
self.b2sn.values().flat_map(|val| val.iter())
}
pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut EntityWithGraphics> {
self.b2sn.values_mut().flat_map(|val| val.iter_mut())
}
}
impl Default for GraphicsManager {
fn default() -> Self {
Self::new()
}
}

View File

@@ -1,8 +1,7 @@
use crate::{ use crate::{
physics::{PhysicsEvents, PhysicsState}, physics::{PhysicsEvents, PhysicsState},
GraphicsManager, TestbedGraphics,
}; };
use kiss3d::window::Window;
use plugin::HarnessPlugin; use plugin::HarnessPlugin;
use rapier::dynamics::{CCDSolver, IntegrationParameters, IslandManager, JointSet, RigidBodySet}; use rapier::dynamics::{CCDSolver, IntegrationParameters, IslandManager, JointSet, RigidBodySet};
use rapier::geometry::{BroadPhase, ColliderSet, NarrowPhase}; use rapier::geometry::{BroadPhase, ColliderSet, NarrowPhase};
@@ -52,17 +51,8 @@ pub struct Harness {
pub state: RunState, pub state: RunState,
} }
type Callbacks = Vec< type Callbacks =
Box< Vec<Box<dyn FnMut(Option<&mut TestbedGraphics>, &mut PhysicsState, &PhysicsEvents, &RunState)>>;
dyn FnMut(
Option<&mut Window>,
Option<&mut GraphicsManager>,
&mut PhysicsState,
&PhysicsEvents,
&RunState,
),
>,
>;
#[allow(dead_code)] #[allow(dead_code)]
impl Harness { impl Harness {
@@ -145,13 +135,7 @@ impl Harness {
} }
pub fn add_callback< pub fn add_callback<
F: FnMut( F: FnMut(Option<&mut TestbedGraphics>, &mut PhysicsState, &PhysicsEvents, &RunState) + 'static,
Option<&mut Window>,
Option<&mut GraphicsManager>,
&mut PhysicsState,
&PhysicsEvents,
&RunState,
) + 'static,
>( >(
&mut self, &mut self,
callback: F, callback: F,
@@ -160,14 +144,10 @@ impl Harness {
} }
pub fn step(&mut self) { pub fn step(&mut self) {
self.step_with_graphics(None, None); self.step_with_graphics(None);
} }
pub fn step_with_graphics( pub fn step_with_graphics(&mut self, mut graphics: Option<&mut TestbedGraphics>) {
&mut self,
window: Option<&mut Window>,
graphics: Option<&mut GraphicsManager>,
) {
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
{ {
let physics = &mut self.physics; let physics = &mut self.physics;
@@ -214,27 +194,14 @@ impl Harness {
plugin.step(&mut self.physics, &self.state) plugin.step(&mut self.physics, &self.state)
} }
// FIXME: This assumes either window & graphics are Some, or they are all None
// this is required as we cannot pass Option<&mut Window> & Option<&mut GraphicsManager directly in a loop
// there must be a better way of doing this?
match (window, graphics) {
(Some(window), Some(graphics)) => {
for f in &mut self.callbacks { for f in &mut self.callbacks {
f( f(
Some(window), graphics.as_deref_mut(),
Some(graphics),
&mut self.physics, &mut self.physics,
&self.events, &self.events,
&self.state, &self.state,
); );
} }
}
_ => {
for f in &mut self.callbacks {
f(None, None, &mut self.physics, &self.events, &self.state);
}
}
}
for plugin in &mut self.plugins { for plugin in &mut self.plugins {
plugin.run_callbacks(&mut self.physics, &self.events, &self.state) plugin.run_callbacks(&mut self.physics, &self.events, &self.state)

View File

@@ -1,5 +1,3 @@
#[macro_use]
extern crate kiss3d;
extern crate nalgebra as na; extern crate nalgebra as na;
#[cfg(feature = "dim2")] #[cfg(feature = "dim2")]
extern crate ncollide2d as ncollide; extern crate ncollide2d as ncollide;
@@ -25,15 +23,16 @@ extern crate bitflags;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
pub use crate::engine::GraphicsManager; pub use crate::graphics::GraphicsManager;
pub use crate::harness::plugin::HarnessPlugin; pub use crate::harness::plugin::HarnessPlugin;
pub use crate::physics::PhysicsState; pub use crate::physics::PhysicsState;
pub use crate::plugin::TestbedPlugin; pub use crate::plugin::TestbedPlugin;
pub use crate::testbed::Testbed; pub use crate::testbed::{Testbed, TestbedApp, TestbedGraphics};
#[cfg(all(feature = "dim2", feature = "other-backends"))] #[cfg(all(feature = "dim2", feature = "other-backends"))]
mod box2d_backend; mod box2d_backend;
mod engine; mod camera;
mod graphics;
pub mod harness; pub mod harness;
#[cfg(feature = "other-backends")] #[cfg(feature = "other-backends")]
mod nphysics_backend; mod nphysics_backend;

View File

@@ -1,76 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window::Window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::Isometry;
pub struct Ball {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
delta: Isometry<f32>,
}
impl Ball {
pub fn new(
collider: ColliderHandle,
delta: Isometry<f32>,
radius: f32,
color: Point3<f32>,
window: &mut Window,
) -> Ball {
#[cfg(feature = "dim2")]
let node = window.add_circle(radius);
#[cfg(feature = "dim3")]
let node = window.add_sphere(radius);
let mut res = Ball {
color,
base_color: color,
gfx: node,
collider,
delta,
};
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&self.delta,
);
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,76 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::{Isometry, Vector};
pub struct Box {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
delta: Isometry<f32>,
}
impl Box {
pub fn new(
collider: ColliderHandle,
delta: Isometry<f32>,
half_extents: Vector<f32>,
color: Point3<f32>,
window: &mut window::Window,
) -> Box {
let extents = half_extents * 2.0;
#[cfg(feature = "dim2")]
let node = window.add_rectangle(extents.x, extents.y);
#[cfg(feature = "dim3")]
let node = window.add_cube(extents.x, extents.y, extents.z);
let mut res = Box {
color,
base_color: color,
gfx: node,
collider,
delta,
};
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&self.delta,
);
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,77 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window;
use na::Point3;
use rapier::geometry::{self, ColliderHandle, ColliderSet};
use rapier::math::Isometry;
pub struct Capsule {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
delta: Isometry<f32>,
}
impl Capsule {
pub fn new(
collider: ColliderHandle,
delta: Isometry<f32>,
capsule: &geometry::Capsule,
color: Point3<f32>,
window: &mut window::Window,
) -> Capsule {
let r = capsule.radius;
let h = capsule.half_height() * 2.0;
#[cfg(feature = "dim2")]
let node = window.add_planar_capsule(r, h);
#[cfg(feature = "dim3")]
let node = window.add_capsule(r, h);
let mut res = Capsule {
color,
base_color: color,
gfx: node,
collider,
delta: delta * capsule.transform_wrt_y(),
};
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&self.delta,
);
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,77 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window::Window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::Isometry;
pub struct Cone {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
delta: Isometry<f32>,
}
impl Cone {
pub fn new(
collider: ColliderHandle,
delta: Isometry<f32>,
half_height: f32,
radius: f32,
color: Point3<f32>,
window: &mut Window,
) -> Cone {
#[cfg(feature = "dim2")]
let node = window.add_rectangle(radius, half_height);
#[cfg(feature = "dim3")]
let node = window.add_cone(radius, half_height * 2.0);
let mut res = Cone {
color,
base_color: color,
gfx: node,
collider,
delta,
};
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&self.delta,
);
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,96 +0,0 @@
#![allow(warnings)] // TODO: remove this.
#[cfg(feature = "dim2")]
use crate::math::Vector;
use crate::math::{Isometry, Point};
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window::Window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
pub struct Convex {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
body: ColliderHandle,
delta: Isometry<f32>,
}
impl Convex {
pub fn new(
body: ColliderHandle,
delta: Isometry<f32>,
vertices: Vec<Point<f32>>,
#[cfg(feature = "dim3")] indices: Vec<[u32; 3]>,
color: Point3<f32>,
window: &mut Window,
) -> Convex {
#[cfg(feature = "dim2")]
let node = window.add_convex_polygon(vertices, Vector::from_element(1.0));
#[cfg(feature = "dim3")]
let node = {
use std::cell::RefCell;
use std::rc::Rc;
let mut mesh_vertices = Vec::new();
let mut mesh_indices = Vec::new();
for idx in indices {
let i = mesh_vertices.len() as u16;
mesh_vertices.push(vertices[idx[0] as usize]);
mesh_vertices.push(vertices[idx[1] as usize]);
mesh_vertices.push(vertices[idx[2] as usize]);
mesh_indices.push(Point3::new(i, i + 1, i + 2));
}
let mesh = kiss3d::resource::Mesh::new(mesh_vertices, mesh_indices, None, None, false);
window.add_mesh(Rc::new(RefCell::new(mesh)), na::Vector3::from_element(1.0))
};
let mut res = Convex {
color,
base_color: color,
gfx: node,
body,
delta,
};
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.body,
&self.color,
&self.delta,
);
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.body
}
}

View File

@@ -1,77 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window::Window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::Isometry;
pub struct Cylinder {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
delta: Isometry<f32>,
}
impl Cylinder {
pub fn new(
collider: ColliderHandle,
delta: Isometry<f32>,
half_height: f32,
radius: f32,
color: Point3<f32>,
window: &mut Window,
) -> Cylinder {
#[cfg(feature = "dim2")]
let node = window.add_rectangle(radius, half_height);
#[cfg(feature = "dim3")]
let node = window.add_cylinder(radius, half_height * 2.0);
let mut res = Cylinder {
color,
base_color: color,
gfx: node,
collider,
delta,
};
// res.gfx.set_texture_from_file(&Path::new("media/kitten.png"), "kitten");
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&self.delta,
);
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,128 +0,0 @@
use kiss3d::window::Window;
use na::{self, Point3};
use parry::shape;
use rapier::geometry::{ColliderHandle, ColliderSet};
#[cfg(feature = "dim2")]
use rapier::math::Point;
#[cfg(feature = "dim3")]
use {
crate::objects::node::{self, GraphicsNode},
kiss3d::resource::Mesh,
rapier::math::Vector,
std::cell::RefCell,
};
pub struct HeightField {
color: Point3<f32>,
base_color: Point3<f32>,
#[cfg(feature = "dim2")]
vertices: Vec<Point<f32>>,
#[cfg(feature = "dim3")]
gfx: GraphicsNode,
collider: ColliderHandle,
}
impl HeightField {
#[cfg(feature = "dim2")]
pub fn new(
collider: ColliderHandle,
heightfield: &shape::HeightField,
color: Point3<f32>,
_: &mut Window,
) -> HeightField {
let mut vertices = Vec::new();
for seg in heightfield.segments() {
vertices.push(seg.a);
vertices.push(seg.b);
}
HeightField {
color,
base_color: color,
vertices,
collider,
}
}
#[cfg(feature = "dim3")]
pub fn new(
collider: ColliderHandle,
heightfield: &shape::HeightField,
color: Point3<f32>,
window: &mut Window,
) -> HeightField {
use std::rc::Rc;
let (vertices, indices) = heightfield.to_trimesh();
let indices = indices
.into_iter()
.map(|idx| Point3::new(idx[0] as u16, idx[1] as u16, idx[2] as u16))
.collect();
let mesh = Mesh::new(vertices, indices, None, None, false);
let mut res = HeightField {
color,
base_color: color,
gfx: window.add_mesh(Rc::new(RefCell::new(mesh)), Vector::repeat(1.0)),
collider: collider,
};
res.gfx.enable_backface_culling(false);
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
#[cfg(feature = "dim3")]
{
self.gfx.set_color(color.x, color.y, color.z);
}
self.color = color;
self.base_color = color;
}
#[cfg(feature = "dim3")]
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&na::Isometry::identity(),
);
}
#[cfg(feature = "dim2")]
pub fn update(&mut self, _colliders: &ColliderSet) {}
#[cfg(feature = "dim3")]
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
#[cfg(feature = "dim3")]
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
#[cfg(feature = "dim2")]
pub fn draw(&mut self, window: &mut Window) {
for vtx in self.vertices.chunks(2) {
window.draw_planar_line(&vtx[0], &vtx[1], &self.color)
}
}
}

View File

@@ -1,111 +0,0 @@
use crate::objects::node::{self, GraphicsNode};
use kiss3d::window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::{Isometry, Point};
use std::cell::RefCell;
use std::rc::Rc;
pub struct Mesh {
color: Point3<f32>,
base_color: Point3<f32>,
gfx: GraphicsNode,
collider: ColliderHandle,
}
impl Mesh {
pub fn new(
collider: ColliderHandle,
vertices: Vec<Point<f32>>,
indices: Vec<[u32; 3]>,
color: Point3<f32>,
window: &mut window::Window,
) -> Mesh {
let vs = vertices;
let is = indices
.into_iter()
.map(|idx| Point3::new(idx[0] as u16, idx[1] as u16, idx[2] as u16))
.collect();
let mesh;
let gfx;
#[cfg(feature = "dim2")]
{
mesh = kiss3d::resource::PlanarMesh::new(vs, is, None, false);
gfx = window.add_planar_mesh(
Rc::new(RefCell::new(mesh)),
crate::math::Vector::from_element(1.0),
);
}
#[cfg(feature = "dim3")]
{
mesh = kiss3d::resource::Mesh::new(vs, is, None, None, false);
gfx = window.add_mesh(Rc::new(RefCell::new(mesh)), na::Vector3::from_element(1.0));
}
let mut res = Mesh {
color,
base_color: color,
gfx,
collider,
};
res.gfx.enable_backface_culling(false);
res.gfx.set_color(color.x, color.y, color.z);
res
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
node::update_scene_node(
&mut self.gfx,
colliders,
self.collider,
&self.color,
&Isometry::identity(),
);
// // Update if some deformation occurred.
// // FIXME: don't update if it did not move.
// if let Some(c) = colliders.get(self.collider) {
// if let ColliderAnchor::OnDeformableBody { .. } = c.anchor() {
// let shape = c.shape().as_shape::<TriMesh<f32>>().unwrap();
// let vtx = shape.points();
//
// self.gfx.modify_vertices(&mut |vertices| {
// for (v, new_v) in vertices.iter_mut().zip(vtx.iter()) {
// *v = *new_v
// }
// });
// self.gfx.recompute_normals();
// }
// }
}
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
}

View File

@@ -1,13 +1 @@
pub mod ball;
pub mod box_node;
pub mod capsule;
pub mod cone;
pub mod convex;
pub mod cylinder;
pub mod heightfield;
pub mod mesh;
pub mod node; pub mod node;
pub mod polyline;
//pub mod plane;
//#[cfg(feature = "dim2")]
//pub mod polyline;

View File

@@ -1,177 +1,432 @@
use crate::objects::ball::Ball; use bevy::prelude::*;
use crate::objects::box_node::Box; use bevy::render::mesh::{Indices, VertexAttributeValues};
use crate::objects::capsule::Capsule;
use crate::objects::convex::Convex;
use crate::objects::heightfield::HeightField;
use crate::objects::mesh::Mesh;
//use crate::objects::plane::Plane; //use crate::objects::plane::Plane;
use crate::objects::polyline::Polyline; use na::{Point3, Vector3};
use kiss3d::window::Window; use std::collections::HashMap;
use na::Point3;
use crate::objects::cone::Cone; use bevy::render::pipeline::PrimitiveTopology;
use crate::objects::cylinder::Cylinder; use bevy::render::wireframe::Wireframe;
use rapier::geometry::{ColliderHandle, ColliderSet}; use rapier::geometry::{ColliderHandle, ColliderSet, Shape, ShapeType};
#[cfg(feature = "dim3")]
use rapier::geometry::{Cone, Cylinder};
use rapier::math::Isometry; use rapier::math::Isometry;
#[cfg(feature = "dim2")] #[cfg(feature = "dim2")]
pub type GraphicsNode = kiss3d::scene::PlanarSceneNode; use {
na::{Point2, Vector2},
rapier::geometry::{Ball, Cuboid},
};
pub struct EntityWithGraphics {
pub entity: Entity,
pub color: Point3<f32>,
pub base_color: Point3<f32>,
pub collider: ColliderHandle,
pub delta: Isometry<f32>,
material: Handle<StandardMaterial>,
}
impl EntityWithGraphics {
pub fn spawn(
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
prefab_meshs: &HashMap<ShapeType, Handle<Mesh>>,
shape: &dyn Shape,
collider: ColliderHandle,
collider_pos: Isometry<f32>,
delta: Isometry<f32>,
color: Point3<f32>,
sensor: bool,
) -> Self {
let entity = commands.spawn().id();
let scale = collider_mesh_scale(shape);
let mesh = prefab_meshs
.get(&shape.shape_type())
.cloned()
.or_else(|| generate_collider_mesh(shape).map(|m| meshes.add(m)))
.expect("Could not build the collider's render mesh");
let bevy_color = Color::rgb(color.x, color.y, color.z);
let shape_pos = collider_pos * delta;
let mut transform = Transform::from_scale(scale);
transform.translation.x = shape_pos.translation.vector.x;
transform.translation.y = shape_pos.translation.vector.y;
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]
pub type GraphicsNode = kiss3d::scene::SceneNode; {
transform.translation.z = shape_pos.translation.vector.z;
pub enum Node { transform.rotation = Quat::from_xyzw(
// Plane(Plane), shape_pos.rotation.i,
Ball(Ball), shape_pos.rotation.j,
Box(Box), shape_pos.rotation.k,
HeightField(HeightField), shape_pos.rotation.w,
Capsule(Capsule), );
Polyline(Polyline), }
Mesh(Mesh), #[cfg(feature = "dim2")]
Convex(Convex), {
Cylinder(Cylinder), if sensor {
Cone(Cone), transform.translation.z = -10.0;
}
transform.rotation = Quat::from_rotation_z(shape_pos.rotation.angle());
} }
impl Node { let material = StandardMaterial {
pub fn select(&mut self) { metallic: 0.5,
match *self { roughness: 0.5,
// Node::Plane(ref mut n) => n.select(), double_sided: true, // TODO: this doesn't do anything?
Node::Ball(ref mut n) => n.select(), ..StandardMaterial::from(bevy_color)
Node::Box(ref mut n) => n.select(), };
Node::Capsule(ref mut n) => n.select(), let material_handle = materials.add(material);
Node::HeightField(ref mut n) => n.select(), let material_weak_handle = material_handle.clone_weak();
Node::Polyline(ref mut n) => n.select(), let pbr = PbrBundle {
Node::Mesh(ref mut n) => n.select(), mesh,
Node::Convex(ref mut n) => n.select(), material: material_handle,
Node::Cylinder(ref mut n) => n.select(), transform,
Node::Cone(ref mut n) => n.select(), ..Default::default()
};
let mut entity_commands = commands.entity(entity);
entity_commands.insert_bundle(pbr);
if sensor {
entity_commands.insert(Wireframe);
}
EntityWithGraphics {
entity,
color,
base_color: color,
collider,
delta,
material: material_weak_handle,
} }
} }
pub fn unselect(&mut self) { pub fn select(&mut self, materials: &mut Assets<StandardMaterial>) {
match *self { // NOTE: we don't just call `self.set_color` because that would
// Node::Plane(ref mut n) => n.unselect(), // overwrite self.base_color too.
Node::Ball(ref mut n) => n.unselect(), self.color = Point3::new(1.0, 0.0, 0.0);
Node::Box(ref mut n) => n.unselect(), if let Some(material) = materials.get_mut(&self.material) {
Node::Capsule(ref mut n) => n.unselect(), material.base_color = Color::rgb(self.color.x, self.color.y, self.color.z);
Node::HeightField(ref mut n) => n.unselect(),
Node::Polyline(ref mut n) => n.unselect(),
Node::Mesh(ref mut n) => n.unselect(),
Node::Convex(ref mut n) => n.unselect(),
Node::Cylinder(ref mut n) => n.unselect(),
Node::Cone(ref mut n) => n.unselect(),
} }
} }
pub fn update(&mut self, colliders: &ColliderSet) { pub fn unselect(&mut self, materials: &mut Assets<StandardMaterial>) {
match *self { self.set_color(materials, self.base_color);
// Node::Plane(ref mut n) => n.update(colliders),
Node::Ball(ref mut n) => n.update(colliders),
Node::Box(ref mut n) => n.update(colliders),
Node::Capsule(ref mut n) => n.update(colliders),
Node::HeightField(ref mut n) => n.update(colliders),
Node::Polyline(ref mut n) => n.update(colliders),
Node::Mesh(ref mut n) => n.update(colliders),
Node::Convex(ref mut n) => n.update(colliders),
Node::Cylinder(ref mut n) => n.update(colliders),
Node::Cone(ref mut n) => n.update(colliders),
} }
pub fn set_color(&mut self, materials: &mut Assets<StandardMaterial>, color: Point3<f32>) {
if let Some(material) = materials.get_mut(&self.material) {
material.base_color = Color::rgb(color.x, color.y, color.z);
}
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet, components: &mut Query<(&mut Transform,)>) {
if let Some(co) = colliders.get(self.collider) {
if let Ok(mut pos) = components.get_component_mut::<Transform>(self.entity) {
let co_pos = co.position() * self.delta;
pos.translation.x = co_pos.translation.vector.x;
pos.translation.y = co_pos.translation.vector.y;
#[cfg(feature = "dim3")]
{
pos.translation.z = co_pos.translation.vector.z;
pos.rotation = Quat::from_xyzw(
co_pos.rotation.i,
co_pos.rotation.j,
co_pos.rotation.k,
co_pos.rotation.w,
);
}
#[cfg(feature = "dim2")]
{
pos.rotation = Quat::from_rotation_z(co_pos.rotation.angle());
}
}
}
}
pub fn object(&self) -> ColliderHandle {
self.collider
} }
#[cfg(feature = "dim2")] #[cfg(feature = "dim2")]
pub fn draw(&mut self, window: &mut Window) { pub fn gen_prefab_meshes(
match *self { out: &mut HashMap<ShapeType, Handle<Mesh>>,
Node::Polyline(ref mut n) => n.draw(window), meshes: &mut Assets<Mesh>,
Node::HeightField(ref mut n) => n.draw(window),
// Node::Plane(ref mut n) => n.draw(_window),
_ => {}
}
}
#[cfg(feature = "dim3")]
pub fn draw(&mut self, _: &mut Window) {}
pub fn scene_node(&self) -> Option<&GraphicsNode> {
match *self {
// #[cfg(feature = "dim3")]
// Node::Plane(ref n) => Some(n.scene_node()),
Node::Ball(ref n) => Some(n.scene_node()),
Node::Box(ref n) => Some(n.scene_node()),
Node::Capsule(ref n) => Some(n.scene_node()),
#[cfg(feature = "dim3")]
Node::HeightField(ref n) => Some(n.scene_node()),
Node::Mesh(ref n) => Some(n.scene_node()),
Node::Convex(ref n) => Some(n.scene_node()),
Node::Cylinder(ref n) => Some(n.scene_node()),
Node::Cone(ref n) => Some(n.scene_node()),
Node::Polyline(_) => None,
#[cfg(feature = "dim2")]
Node::HeightField(_) => None,
}
}
pub fn scene_node_mut(&mut self) -> Option<&mut GraphicsNode> {
match *self {
// #[cfg(feature = "dim3")]
// Node::Plane(ref mut n) => Some(n.scene_node_mut()),
Node::Ball(ref mut n) => Some(n.scene_node_mut()),
Node::Box(ref mut n) => Some(n.scene_node_mut()),
Node::Capsule(ref mut n) => Some(n.scene_node_mut()),
#[cfg(feature = "dim3")]
Node::HeightField(ref mut n) => Some(n.scene_node_mut()),
Node::Mesh(ref mut n) => Some(n.scene_node_mut()),
Node::Convex(ref mut n) => Some(n.scene_node_mut()),
Node::Cylinder(ref mut n) => Some(n.scene_node_mut()),
Node::Cone(ref mut n) => Some(n.scene_node_mut()),
Node::Polyline(_) => None,
#[cfg(feature = "dim2")]
Node::HeightField(_) => None,
}
}
pub fn collider(&self) -> ColliderHandle {
match *self {
// Node::Plane(ref n) => n.object(),
Node::Ball(ref n) => n.object(),
Node::Box(ref n) => n.object(),
Node::Capsule(ref n) => n.object(),
Node::HeightField(ref n) => n.object(),
Node::Polyline(ref n) => n.object(),
Node::Mesh(ref n) => n.object(),
Node::Convex(ref n) => n.object(),
Node::Cylinder(ref n) => n.object(),
Node::Cone(ref n) => n.object(),
}
}
pub fn set_color(&mut self, color: Point3<f32>) {
match *self {
// Node::Plane(ref mut n) => n.set_color(color),
Node::Ball(ref mut n) => n.set_color(color),
Node::Box(ref mut n) => n.set_color(color),
Node::Capsule(ref mut n) => n.set_color(color),
Node::HeightField(ref mut n) => n.set_color(color),
Node::Polyline(ref mut n) => n.set_color(color),
Node::Mesh(ref mut n) => n.set_color(color),
Node::Convex(ref mut n) => n.set_color(color),
Node::Cylinder(ref mut n) => n.set_color(color),
Node::Cone(ref mut n) => n.set_color(color),
}
}
}
pub fn update_scene_node(
node: &mut GraphicsNode,
colliders: &ColliderSet,
handle: ColliderHandle,
color: &Point3<f32>,
delta: &Isometry<f32>,
) { ) {
if let Some(co) = colliders.get(handle) { //
node.set_local_transformation(co.position() * delta); // Cuboid mesh
node.set_color(color.x, color.y, color.z); //
let cuboid = bevy_mesh_from_polyline(Cuboid::new(Vector2::new(1.0, 1.0)).to_polyline());
out.insert(ShapeType::Cuboid, meshes.add(cuboid));
//
// Ball mesh
//
let ball = bevy_mesh_from_polyline(Ball::new(1.0).to_polyline(30));
out.insert(ShapeType::Ball, meshes.add(ball));
}
#[cfg(feature = "dim3")]
pub fn gen_prefab_meshes(
out: &mut HashMap<ShapeType, Handle<Mesh>>,
meshes: &mut Assets<Mesh>,
) {
//
// Cuboid mesh
//
let cuboid = Mesh::from(shape::Cube { size: 2.0 });
out.insert(ShapeType::Cuboid, meshes.add(cuboid));
//
// Ball mesh
//
let ball = Mesh::from(shape::Icosphere {
subdivisions: 2,
radius: 1.0,
});
out.insert(ShapeType::Ball, meshes.add(ball));
//
// Cylinder mesh
//
let cylinder = Cylinder::new(1.0, 1.0);
let mesh = bevy_mesh(cylinder.to_trimesh(20));
out.insert(ShapeType::Cylinder, meshes.add(mesh.clone()));
out.insert(ShapeType::RoundCylinder, meshes.add(mesh));
//
// Cone mesh
//
let cone = Cone::new(1.0, 1.0);
let mesh = bevy_mesh(cone.to_trimesh(10));
out.insert(ShapeType::Cone, meshes.add(mesh.clone()));
out.insert(ShapeType::RoundCone, meshes.add(mesh));
//
// Halfspace
//
let vertices = vec![
Point3::new(-1000.0, 0.0, -1000.0),
Point3::new(1000.0, 0.0, -1000.0),
Point3::new(1000.0, 0.0, 1000.0),
Point3::new(-1000.0, 0.0, 1000.0),
];
let indices = vec![[0, 1, 2], [0, 2, 3]];
let mesh = bevy_mesh((vertices, indices));
out.insert(ShapeType::HalfSpace, meshes.add(mesh));
}
}
#[cfg(feature = "dim2")]
fn bevy_mesh_from_polyline(vertices: Vec<Point2<f32>>) -> Mesh {
let n = vertices.len();
let idx = (1..n as u32 - 1).map(|i| [0, i, i + 1]).collect();
let vtx = vertices
.into_iter()
.map(|v| Point3::new(v.x, v.y, 0.0))
.collect();
bevy_mesh((vtx, idx))
}
#[cfg(feature = "dim2")]
fn bevy_polyline(buffers: (Vec<Point2<f32>>, Option<Vec<[u32; 2]>>)) -> Mesh {
let (vtx, idx) = buffers;
// let mut normals: Vec<[f32; 3]> = vec![];
let mut vertices: Vec<[f32; 3]> = vec![];
if let Some(idx) = idx {
for idx in idx {
let a = vtx[idx[0] as usize];
let b = vtx[idx[1] as usize];
vertices.push([a.x, a.y, 0.0]);
vertices.push([b.x, b.y, 0.0]);
}
} else { } else {
node.set_visible(false); vertices = vtx.iter().map(|v| [v.x, v.y, 0.0]).collect();
node.unlink(); }
let indices: Vec<_> = (0..vertices.len() as u32).collect();
let uvs: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0]).collect();
let normals: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0, 1.0]).collect();
// Generate the mesh
let mut mesh = Mesh::new(PrimitiveTopology::LineStrip);
mesh.set_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::from(vertices),
);
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, VertexAttributeValues::from(normals));
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::from(uvs));
mesh.set_indices(Some(Indices::U32(indices)));
mesh
}
fn bevy_mesh(buffers: (Vec<Point3<f32>>, Vec<[u32; 3]>)) -> Mesh {
let (vtx, idx) = buffers;
let mut normals: Vec<[f32; 3]> = vec![];
let mut vertices: Vec<[f32; 3]> = vec![];
for idx in idx {
let a = vtx[idx[0] as usize];
let b = vtx[idx[1] as usize];
let c = vtx[idx[2] as usize];
vertices.push(a.into());
vertices.push(b.into());
vertices.push(c.into());
}
for vtx in vertices.chunks(3) {
let a = Point3::from(vtx[0]);
let b = Point3::from(vtx[1]);
let c = Point3::from(vtx[2]);
let n = (b - a).cross(&(c - a)).normalize();
normals.push(n.into());
normals.push(n.into());
normals.push(n.into());
}
normals
.iter_mut()
.for_each(|n| *n = Vector3::from(*n).normalize().into());
let indices: Vec<_> = (0..vertices.len() as u32).collect();
let uvs: Vec<_> = (0..vertices.len()).map(|_| [0.0, 0.0]).collect();
// Generate the mesh
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
mesh.set_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::from(vertices),
);
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, VertexAttributeValues::from(normals));
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::from(uvs));
mesh.set_indices(Some(Indices::U32(indices)));
mesh
}
fn collider_mesh_scale(co_shape: &dyn Shape) -> Vec3 {
match co_shape.shape_type() {
#[cfg(feature = "dim2")]
ShapeType::Cuboid => {
let c = co_shape.as_cuboid().unwrap();
Vec3::new(c.half_extents.x, c.half_extents.y, 1.0)
}
ShapeType::Ball => {
let b = co_shape.as_ball().unwrap();
Vec3::new(b.radius, b.radius, b.radius)
}
#[cfg(feature = "dim3")]
ShapeType::Cuboid => {
let c = co_shape.as_cuboid().unwrap();
Vec3::from_slice_unaligned(c.half_extents.as_slice())
}
#[cfg(feature = "dim3")]
ShapeType::Cylinder => {
let c = co_shape.as_cylinder().unwrap();
Vec3::new(c.radius, c.half_height, c.radius)
}
#[cfg(feature = "dim3")]
ShapeType::RoundCylinder => {
let c = &co_shape.as_round_cylinder().unwrap().base_shape;
Vec3::new(c.radius, c.half_height, c.radius)
}
#[cfg(feature = "dim3")]
ShapeType::Cone => {
let c = co_shape.as_cone().unwrap();
Vec3::new(c.radius, c.half_height, c.radius)
}
#[cfg(feature = "dim3")]
ShapeType::RoundCone => {
let c = &co_shape.as_round_cone().unwrap().base_shape;
Vec3::new(c.radius, c.half_height, c.radius)
}
_ => Vec3::ONE,
} }
} }
#[cfg(feature = "dim2")]
fn generate_collider_mesh(co_shape: &dyn Shape) -> Option<Mesh> {
let mesh = match co_shape.shape_type() {
ShapeType::Capsule => {
let capsule = co_shape.as_capsule().unwrap();
bevy_mesh_from_polyline(capsule.to_polyline(10))
}
ShapeType::Triangle => {
let tri = co_shape.as_triangle().unwrap();
bevy_mesh_from_polyline(vec![tri.a, tri.b, tri.c])
}
ShapeType::TriMesh => {
let trimesh = co_shape.as_trimesh().unwrap();
let vertices = trimesh
.vertices()
.iter()
.map(|p| Point3::new(p.x, p.y, 0.0))
.collect();
bevy_mesh((vertices, trimesh.indices().to_vec()))
}
ShapeType::Polyline => {
let polyline = co_shape.as_polyline().unwrap();
bevy_polyline((
polyline.vertices().to_vec(),
Some(polyline.indices().to_vec()),
))
}
ShapeType::HeightField => {
let heightfield = co_shape.as_heightfield().unwrap();
let vertices: Vec<_> = heightfield
.segments()
.flat_map(|s| vec![s.a, s.b])
.collect();
bevy_polyline((vertices, None))
}
ShapeType::ConvexPolygon => {
let poly = co_shape.as_convex_polygon().unwrap();
bevy_mesh_from_polyline(poly.points().to_vec())
}
ShapeType::RoundConvexPolygon => {
let poly = co_shape.as_round_convex_polygon().unwrap();
bevy_mesh_from_polyline(poly.base_shape.points().to_vec())
}
_ => return None,
};
Some(mesh)
}
#[cfg(feature = "dim3")]
fn generate_collider_mesh(co_shape: &dyn Shape) -> Option<Mesh> {
let mesh = match co_shape.shape_type() {
ShapeType::Capsule => {
let capsule = co_shape.as_capsule().unwrap();
bevy_mesh(capsule.to_trimesh(20, 10))
}
ShapeType::Triangle => {
let tri = co_shape.as_triangle().unwrap();
bevy_mesh((vec![tri.a, tri.b, tri.c], vec![[0, 1, 2], [0, 2, 1]]))
}
ShapeType::TriMesh => {
let trimesh = co_shape.as_trimesh().unwrap();
bevy_mesh((trimesh.vertices().to_vec(), trimesh.indices().to_vec()))
}
ShapeType::HeightField => {
let heightfield = co_shape.as_heightfield().unwrap();
bevy_mesh(heightfield.to_trimesh())
}
ShapeType::ConvexPolyhedron => {
let poly = co_shape.as_convex_polyhedron().unwrap();
bevy_mesh(poly.to_trimesh())
}
ShapeType::RoundConvexPolyhedron => {
let poly = co_shape.as_round_convex_polyhedron().unwrap();
bevy_mesh(poly.base_shape.to_trimesh())
}
_ => return None,
};
Some(mesh)
}

View File

@@ -1,132 +0,0 @@
#[cfg(feature = "dim3")]
use crate::objects::node::GraphicsNode;
use kiss3d::window::Window;
use na::Point3;
#[cfg(feature = "dim3")]
use na::Vector3;
#[cfg(feature = "dim2")]
use nphysics::math::{Point, Vector};
use nphysics::object::{DefaultColliderHandle, DefaultColliderSet};
#[cfg(feature = "dim3")]
use num::Zero;
#[cfg(feature = "dim3")]
pub struct Plane {
gfx: GraphicsNode,
collider: DefaultColliderHandle,
}
#[cfg(feature = "dim2")]
pub struct Plane {
color: Point3<f32>,
base_color: Point3<f32>,
position: Point<f32>,
normal: na::Unit<Vector<f32>>,
collider: DefaultColliderHandle,
}
impl Plane {
#[cfg(feature = "dim2")]
pub fn new(
collider: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
position: &Point<f32>,
normal: &Vector<f32>,
color: Point3<f32>,
_: &mut Window,
) -> Plane {
let mut res = Plane {
color,
base_color: color,
position: *position,
normal: na::Unit::new_normalize(*normal),
collider,
};
res.update(colliders);
res
}
#[cfg(feature = "dim3")]
pub fn new(
collider: DefaultColliderHandle,
colliders: &DefaultColliderSet<f32>,
world_pos: &Point3<f32>,
world_normal: &Vector3<f32>,
color: Point3<f32>,
window: &mut Window,
) -> Plane {
let mut res = Plane {
gfx: window.add_quad(100.0, 100.0, 10, 10),
collider,
};
if colliders
.get(collider)
.unwrap()
.query_type()
.is_proximity_query()
{
res.gfx.set_surface_rendering_activation(false);
res.gfx.set_lines_width(1.0);
}
res.gfx.set_color(color.x, color.y, color.z);
let up = if world_normal.z.is_zero() && world_normal.y.is_zero() {
Vector3::z()
} else {
Vector3::x()
};
res.gfx
.reorient(world_pos, &(*world_pos + *world_normal), &up);
res.update(colliders);
res
}
pub fn select(&mut self) {}
pub fn unselect(&mut self) {}
pub fn update(&mut self, _: &DefaultColliderSet<f32>) {
// FIXME: atm we assume the plane does not move
}
#[cfg(feature = "dim3")]
pub fn set_color(&mut self, color: Point3<f32>) {
self.gfx.set_color(color.x, color.y, color.z);
}
#[cfg(feature = "dim2")]
pub fn set_color(&mut self, color: Point3<f32>) {
self.color = color;
self.base_color = color;
}
#[cfg(feature = "dim3")]
pub fn scene_node(&self) -> &GraphicsNode {
&self.gfx
}
#[cfg(feature = "dim3")]
pub fn scene_node_mut(&mut self) -> &mut GraphicsNode {
&mut self.gfx
}
pub fn object(&self) -> DefaultColliderHandle {
self.collider
}
#[cfg(feature = "dim2")]
pub fn draw(&mut self, window: &mut Window) {
let orth = Vector::new(-self.normal.y, self.normal.x);
window.draw_planar_line(
&(self.position - orth * 50.0),
&(self.position + orth * 50.0),
&self.color,
);
}
}

View File

@@ -1,67 +0,0 @@
use kiss3d::window::Window;
use na::Point3;
use rapier::geometry::{ColliderHandle, ColliderSet};
use rapier::math::{Isometry, Point};
pub struct Polyline {
color: Point3<f32>,
base_color: Point3<f32>,
vertices: Vec<Point<f32>>,
indices: Vec<[u32; 2]>,
collider: ColliderHandle,
pos: Isometry<f32>,
}
impl Polyline {
pub fn new(
collider: ColliderHandle,
vertices: Vec<Point<f32>>,
indices: Vec<[u32; 2]>,
color: Point3<f32>,
) -> Polyline {
Polyline {
color,
pos: Isometry::identity(),
base_color: color,
vertices,
indices,
collider,
}
}
pub fn select(&mut self) {
self.color = Point3::new(1.0, 0.0, 0.0);
}
pub fn unselect(&mut self) {
self.color = self.base_color;
}
pub fn set_color(&mut self, color: Point3<f32>) {
self.color = color;
self.base_color = color;
}
pub fn update(&mut self, colliders: &ColliderSet) {
self.pos = colliders
.get(self.collider)
.map(|c| *c.position())
.unwrap_or(Isometry::identity());
}
pub fn object(&self) -> ColliderHandle {
self.collider
}
pub fn draw(&mut self, window: &mut Window) {
for idx in &self.indices {
let p1 = self.pos * self.vertices[idx[0] as usize];
let p2 = self.pos * self.vertices[idx[1] as usize];
#[cfg(feature = "dim2")]
window.draw_planar_line(&p1, &p2, &self.color);
#[cfg(feature = "dim3")]
window.draw_line(&p1, &p2, &self.color);
}
}
}

View File

@@ -1,17 +1,11 @@
use crate::harness::RunState; use crate::harness::RunState;
use crate::physics::PhysicsState; use crate::physics::PhysicsState;
use kiss3d::window::Window;
use na::Point3; use na::Point3;
pub trait TestbedPlugin { pub trait TestbedPlugin {
fn init_graphics(&mut self, window: &mut Window, gen_color: &mut dyn FnMut() -> Point3<f32>); fn init_graphics(&mut self, gen_color: &mut dyn FnMut() -> Point3<f32>);
fn clear_graphics(&mut self, window: &mut Window); fn clear_graphics(&mut self);
fn run_callbacks( fn run_callbacks(&mut self, physics: &mut PhysicsState, run_state: &RunState);
&mut self,
window: &mut Window,
physics: &mut PhysicsState,
run_state: &RunState,
);
fn step(&mut self, physics: &mut PhysicsState); fn step(&mut self, physics: &mut PhysicsState);
fn draw(&mut self); fn draw(&mut self);
fn profiling_string(&self) -> String; fn profiling_string(&self) -> String;

File diff suppressed because it is too large Load Diff

View File

@@ -1,142 +1,35 @@
use kiss3d::conrod::{self, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget}; use rapier::counters::Counters;
use kiss3d::window::Window;
use rapier::dynamics::IntegrationParameters;
use crate::harness::RunState; use crate::harness::Harness;
use crate::testbed::{RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags}; use crate::testbed::{RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags};
const SIDEBAR_W: f64 = 200.0; use crate::PhysicsState;
const ELEMENT_W: f64 = SIDEBAR_W - 20.0; use bevy_egui::egui::Slider;
const ELEMENT_H: f64 = 20.0; use bevy_egui::{egui, EguiContext};
const VSPACE: f64 = 4.0;
const TITLE_VSPACE: f64 = 4.0;
const LEFT_MARGIN: f64 = 10.0;
const ALPHA: f32 = 0.9;
widget_ids! { pub fn update_ui(ui_context: &EguiContext, state: &mut TestbedState, harness: &mut Harness) {
pub struct ConrodIds { egui::Window::new("Parameters").show(ui_context.ctx(), |ui| {
canvas,
title_backends_list,
title_demos_list,
title_slider_vel_iter,
title_slider_pos_iter,
title_slider_num_threads,
title_slider_ccd_substeps,
title_slider_min_island_size,
title_warmstart_coeff,
title_frequency,
backends_list,
demos_list,
button_pause,
button_single_step,
button_restart,
button_quit,
button_prev_example,
button_next_example,
button_take_snapshot,
button_restore_snapshot,
slider_vel_iter,
slider_pos_iter,
slider_num_threads,
slider_ccd_substeps,
slider_min_island_size,
slider_warmstart_coeff,
slider_frequency,
toggle_sleep,
toggle_warm_starting,
toggle_sub_stepping,
toggle_shapes,
toggle_joints,
toggle_aabbs,
toggle_contact_points,
toggle_contact_normals,
toggle_center_of_masses,
toggle_statistics,
toggle_profile,
toggle_debug,
toggle_wireframe,
separator0,
separator1,
separator2,
}
}
pub struct TestbedUi {
ids: ConrodIds,
}
impl TestbedUi {
pub fn new(window: &mut Window) -> Self {
use conrod::position::{Align, Direction, Padding, Position, Relative};
let mut ui = window.conrod_ui_mut();
ui.theme = conrod::Theme {
name: "Testbed theme".to_string(),
padding: Padding::none(),
x_position: Position::Relative(Relative::Align(Align::Start), None),
y_position: Position::Relative(Relative::Direction(Direction::Backwards, 20.0), None),
background_color: conrod::color::DARK_CHARCOAL.alpha(ALPHA),
shape_color: conrod::color::LIGHT_CHARCOAL.alpha(ALPHA),
border_color: conrod::color::BLACK.alpha(ALPHA),
border_width: 0.0,
label_color: conrod::color::WHITE.alpha(ALPHA),
font_id: None,
font_size_large: 15,
font_size_medium: 11,
font_size_small: 8,
widget_styling: conrod::theme::StyleMap::default(),
mouse_drag_threshold: 0.0,
double_click_threshold: std::time::Duration::from_millis(500),
};
Self {
ids: ConrodIds::new(ui.widget_id_generator()),
}
}
pub fn update(
&mut self,
window: &mut Window,
integration_parameters: &mut IntegrationParameters,
state: &mut TestbedState,
_run_state: &mut RunState,
) {
let ui_root = window.conrod_ui().window;
let mut ui = window.conrod_ui_mut().set_widgets();
conrod::widget::Canvas::new()
// .title_bar("Demos")
// .title_bar_color(conrod::color::Color::Rgba(1.0, 0.0, 0.0, 1.0))
// .pad(100.0)
// .pad_left(MARGIN)
// .pad_right(MARGIN)
.scroll_kids_vertically()
.mid_right_with_margin(10.0)
.w(SIDEBAR_W)
.padded_h_of(ui_root, 10.0)
.set(self.ids.canvas, &mut ui);
// NOTE: If examples_names is empty, we can't change the backend because
// we have no way to properly restart the simulation.
if state.backend_names.len() > 1 && !state.example_names.is_empty() { if state.backend_names.len() > 1 && !state.example_names.is_empty() {
/* #[cfg(feature = "dim3")]
* Backend drop-down. let prev_selected_backend = state.selected_backend;
*/ let mut changed = false;
conrod::widget::Text::new("Select backend:") egui::ComboBox::from_label("backend")
.top_left_with_margins_on(self.ids.canvas, VSPACE, LEFT_MARGIN) .width(150.0)
.set(self.ids.title_backends_list, &mut ui); .selected_text(state.backend_names[state.selected_backend])
.show_ui(ui, |ui| {
for (id, name) in state.backend_names.iter().enumerate() {
changed = ui
.selectable_value(&mut state.selected_backend, id, *name)
.changed()
|| changed;
}
});
if changed {
state
.action_flags
.set(TestbedActionFlags::BACKEND_CHANGED, true);
for selected in conrod::widget::DropDownList::new(
&state.backend_names,
Some(state.selected_backend),
)
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_backends_list, TITLE_VSPACE)
.left_justify_label()
.w_h(ELEMENT_W, ELEMENT_H)
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
.set(self.ids.backends_list, &mut ui)
{
if selected != state.selected_backend {
#[cfg(all(feature = "dim3", feature = "other-backends"))] #[cfg(all(feature = "dim3", feature = "other-backends"))]
fn is_physx(id: usize) -> bool { fn is_physx(id: usize) -> bool {
id == crate::testbed::PHYSX_BACKEND_PATCH_FRICTION id == crate::testbed::PHYSX_BACKEND_PATCH_FRICTION
@@ -144,82 +37,29 @@ impl TestbedUi {
} }
#[cfg(all(feature = "dim3", feature = "other-backends"))] #[cfg(all(feature = "dim3", feature = "other-backends"))]
if (is_physx(state.selected_backend) && !is_physx(selected)) if (is_physx(state.selected_backend) && !is_physx(prev_selected_backend))
|| (!is_physx(state.selected_backend) && is_physx(selected)) || (!is_physx(state.selected_backend) && is_physx(prev_selected_backend))
{ {
// PhysX defaults (4 position iterations, 1 velocity) are the // PhysX defaults (4 position iterations, 1 velocity) are the
// opposite of rapier's (4 velocity iterations, 1 position). // opposite of rapier's (4 velocity iterations, 1 position).
std::mem::swap( std::mem::swap(
&mut integration_parameters.max_position_iterations, &mut harness
&mut integration_parameters.max_velocity_iterations, .physics
.integration_parameters
.max_position_iterations,
&mut harness
.physics
.integration_parameters
.max_velocity_iterations,
); );
} }
state.selected_backend = selected;
state
.action_flags
.set(TestbedActionFlags::BACKEND_CHANGED, true)
}
} }
separator( ui.separator();
self.ids.canvas,
self.ids.backends_list,
self.ids.separator0,
&mut ui,
);
} else {
conrod::widget::Text::new("")
.top_left_with_margins_on(self.ids.canvas, 0.0, LEFT_MARGIN)
.set(self.ids.separator0, &mut ui);
} }
let display_ticks = state.example_names.len() > 1; ui.horizontal(|ui| {
let _select_example_title = if display_ticks { if ui.button("<").clicked() {
"Select example:"
} else {
"Current example:"
};
let tick_width = if display_ticks { 20.0 } else { 0.0 };
/*
* Examples drop-down.
*/
conrod::widget::Text::new("Select example:")
.down_from(self.ids.separator0, VSPACE)
// .top_left_with_margins_on(self.ids.canvas, VSPACE, LEFT_MARGIN)
// .w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.title_demos_list, &mut ui);
for selected in
conrod::widget::DropDownList::new(&state.example_names, Some(state.selected_example))
// .mid_top_with_margin_on(self.ids.canvas, 20.0)
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_demos_list, TITLE_VSPACE)
// .right_from(self.ids.button_prev_example, 0.0)
.left_justify_label()
.w_h(ELEMENT_W - tick_width, ELEMENT_H)
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
.set(self.ids.demos_list, &mut ui)
{
if selected != state.selected_example {
state.selected_example = selected;
state
.action_flags
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
}
}
if display_ticks {
for _click in conrod::widget::Button::new()
.label("<")
.align_middle_x_of(self.ids.canvas)
.left_from(self.ids.demos_list, 0.0)
.w_h(10.0, ELEMENT_H)
.enabled(state.selected_example > 0)
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
.set(self.ids.button_prev_example, &mut ui)
{
if state.selected_example > 0 { if state.selected_example > 0 {
state.selected_example -= 1; state.selected_example -= 1;
state state
@@ -228,15 +68,7 @@ impl TestbedUi {
} }
} }
for _click in conrod::widget::Button::new() if ui.button(">").clicked() {
.label(">")
.align_middle_x_of(self.ids.canvas)
.right_from(self.ids.demos_list, 0.0)
.w_h(10.0, ELEMENT_H)
.enabled(state.selected_example + 1 < state.example_names.len())
.color(conrod::color::LIGHT_CHARCOAL) // No alpha.
.set(self.ids.button_next_example, &mut ui)
{
if state.selected_example + 1 < state.example_names.len() { if state.selected_example + 1 < state.example_names.len() {
state.selected_example += 1; state.selected_example += 1;
state state
@@ -244,199 +76,94 @@ impl TestbedUi {
.set(TestbedActionFlags::EXAMPLE_CHANGED, true) .set(TestbedActionFlags::EXAMPLE_CHANGED, true)
} }
} }
}
separator( let mut changed = false;
self.ids.canvas, egui::ComboBox::from_label("example")
self.ids.demos_list, .width(150.0)
self.ids.separator1, .selected_text(state.example_names[state.selected_example])
&mut ui, .show_ui(ui, |ui| {
for (id, name) in state.example_names.iter().enumerate() {
changed = ui
.selectable_value(&mut state.selected_example, id, *name)
.changed()
|| changed;
}
});
if changed {
state
.action_flags
.set(TestbedActionFlags::EXAMPLE_CHANGED, true);
}
});
ui.separator();
ui.collapsing("Profile infos", |ui| {
ui.horizontal_wrapped(|ui| {
ui.label(profiling_string(&harness.physics.pipeline.counters))
});
});
ui.collapsing("Serialization infos", |ui| {
ui.horizontal_wrapped(|ui| {
ui.label(serialization_string(
harness.state.timestep_id,
&harness.physics,
))
});
});
let integration_parameters = &mut harness.physics.integration_parameters;
ui.add(
Slider::new(&mut integration_parameters.max_velocity_iterations, 0..=200)
.text("vels. iters."),
);
ui.add(
Slider::new(&mut integration_parameters.max_position_iterations, 0..=200)
.text("pos. iters."),
); );
let curr_vel_iters = integration_parameters.max_velocity_iterations;
let curr_pos_iters = integration_parameters.max_position_iterations;
#[cfg(feature = "parallel")]
let curr_num_threads = _run_state.num_threads;
let curr_max_ccd_substeps = integration_parameters.max_ccd_substeps;
let curr_min_island_size = integration_parameters.min_island_size;
let curr_warmstart_coeff = integration_parameters.warmstart_coeff;
let curr_frequency = integration_parameters.inv_dt().round() as usize;
conrod::widget::Text::new("Vel. Iters.:")
.down_from(self.ids.separator1, VSPACE)
.set(self.ids.title_slider_vel_iter, &mut ui);
for val in conrod::widget::Slider::new(curr_vel_iters as f32, 0.0, 200.0)
.label(&curr_vel_iters.to_string())
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_slider_vel_iter, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_vel_iter, &mut ui)
{
integration_parameters.max_velocity_iterations = val as usize;
}
conrod::widget::Text::new("Pos. Iters.:")
.down_from(self.ids.slider_vel_iter, VSPACE)
.set(self.ids.title_slider_pos_iter, &mut ui);
for val in conrod::widget::Slider::new(curr_pos_iters as f32, 0.0, 200.0)
.label(&curr_pos_iters.to_string())
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_slider_pos_iter, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_pos_iter, &mut ui)
{
integration_parameters.max_position_iterations = val as usize;
}
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
{ {
conrod::widget::Text::new("Num. Threads.:") ui.add(
.down_from(self.ids.slider_pos_iter, VSPACE) Slider::new(&mut harness.state.num_threads, 1..num_cpus::get_physical())
.set(self.ids.title_slider_num_threads, &mut ui); .text("num. threads"),
for val in conrod::widget::Slider::new(
curr_num_threads as f32,
1.0,
num_cpus::get_physical() as f32,
)
.label(&curr_num_threads.to_string())
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_slider_num_threads, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_num_threads, &mut ui)
{
if _run_state.num_threads != val as usize {
_run_state.num_threads = val as usize;
_run_state.thread_pool = rapier::rayon::ThreadPoolBuilder::new()
.num_threads(_run_state.num_threads)
.build()
.unwrap();
}
}
}
conrod::widget::Text::new("CCD substeps:")
.down_from(
if cfg!(feature = "parallel") {
self.ids.slider_num_threads
} else {
self.ids.slider_pos_iter
},
VSPACE,
)
.set(self.ids.title_slider_ccd_substeps, &mut ui);
for val in conrod::widget::Slider::new(curr_max_ccd_substeps as f32, 0.0, 10.0)
.label(&curr_max_ccd_substeps.to_string())
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_slider_ccd_substeps, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_ccd_substeps, &mut ui)
{
integration_parameters.max_ccd_substeps = val as usize;
}
conrod::widget::Text::new("Min island size:")
.down_from(self.ids.slider_ccd_substeps, VSPACE)
.set(self.ids.title_slider_min_island_size, &mut ui);
for val in conrod::widget::Slider::new(curr_min_island_size as f32, 1.0, 10000.0)
.label(&curr_min_island_size.to_string())
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_slider_min_island_size, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_min_island_size, &mut ui)
{
integration_parameters.min_island_size = val as usize;
}
conrod::widget::Text::new("Warm-start coeff.:")
.down_from(self.ids.slider_min_island_size, VSPACE)
.set(self.ids.title_warmstart_coeff, &mut ui);
for val in conrod::widget::Slider::new(curr_warmstart_coeff as f32, 0.0, 1.0)
.label(&format!("{:.2}", curr_warmstart_coeff))
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_warmstart_coeff, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_warmstart_coeff, &mut ui)
{
integration_parameters.warmstart_coeff = val;
}
conrod::widget::Text::new("Frequency:")
.down_from(self.ids.slider_warmstart_coeff, VSPACE)
.set(self.ids.title_frequency, &mut ui);
for val in conrod::widget::Slider::new(curr_frequency as f32, 0.0, 240.0)
.label(&format!("{:.2}Hz", curr_frequency))
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.title_frequency, TITLE_VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.slider_frequency, &mut ui)
{
integration_parameters.set_inv_dt(val.round());
}
let toggle_list = [
("Sleep", self.ids.toggle_sleep, TestbedStateFlags::SLEEP),
// ("Warm Starting", self.ids.toggle_warm_starting, TestbedStateFlags::WARM_STARTING),
(
"Sub-Stepping",
self.ids.toggle_sub_stepping,
TestbedStateFlags::SUB_STEPPING,
),
("", self.ids.separator2, TestbedStateFlags::NONE),
// ("Shapes", self.ids.toggle_shapes, TestbedStateFlags::SHAPES),
// ("Joints", self.ids.toggle_joints, TestbedStateFlags::JOINTS),
("AABBs", self.ids.toggle_aabbs, TestbedStateFlags::AABBS),
(
"Contacts",
self.ids.toggle_contact_points,
TestbedStateFlags::CONTACT_POINTS,
),
// ("ContactManifold Normals", self.ids.toggle_contact_normals, TestbedStateFlags::CONTACT_NORMALS),
(
"Wireframe",
self.ids.toggle_wireframe,
TestbedStateFlags::WIREFRAME,
),
// ("Center of Masses", self.ids.toggle_center_of_masses, TestbedStateFlags::CENTER_OF_MASSES),
// ("Statistics", self.ids.toggle_statistics, TestbedStateFlags::STATISTICS),
(
"Profile",
self.ids.toggle_profile,
TestbedStateFlags::PROFILE,
),
(
"Debug infos",
self.ids.toggle_debug,
TestbedStateFlags::DEBUG,
),
];
toggles(
&toggle_list,
self.ids.canvas,
self.ids.slider_frequency,
&mut ui,
&mut state.flags,
); );
}
ui.add(
Slider::new(&mut integration_parameters.max_ccd_substeps, 0..=10).text("CCD substeps"),
);
ui.add(
Slider::new(&mut integration_parameters.min_island_size, 1..=10_000)
.text("min island size"),
);
ui.add(
Slider::new(&mut integration_parameters.warmstart_coeff, 0.0..=1.0)
.text("warmstart coeff"),
);
let mut frequency = integration_parameters.inv_dt().round() as u32;
ui.add(Slider::new(&mut frequency, 0..=240).text("frequency (Hz)"));
integration_parameters.set_inv_dt(frequency as f32);
let mut sleep = state.flags.contains(TestbedStateFlags::SLEEP);
// let mut contact_points = state.flags.contains(TestbedStateFlags::CONTACT_POINTS);
// let mut wireframe = state.flags.contains(TestbedStateFlags::WIREFRAME);
ui.checkbox(&mut sleep, "sleep enabled");
// ui.checkbox(&mut contact_points, "draw contacts");
// ui.checkbox(&mut wireframe, "draw wireframes");
state.flags.set(TestbedStateFlags::SLEEP, sleep);
// state
// .flags
// .set(TestbedStateFlags::CONTACT_POINTS, contact_points);
// state.flags.set(TestbedStateFlags::WIREFRAME, wireframe);
ui.separator();
let label = if state.running == RunMode::Stop { let label = if state.running == RunMode::Stop {
"Start (T)" "Start (T)"
} else { } else {
"Pause (T)" "Pause (T)"
}; };
for _press in conrod::widget::Button::new()
.label(label) if ui.button(label).clicked() {
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.toggle_debug, VSPACE)
.w_h(ELEMENT_W, ELEMENT_H)
.set(self.ids.button_pause, &mut ui)
{
if state.running == RunMode::Stop { if state.running == RunMode::Stop {
state.running = RunMode::Running state.running = RunMode::Running
} else { } else {
@@ -444,127 +171,109 @@ impl TestbedUi {
} }
} }
for _press in conrod::widget::Button::new() if ui.button("Single Step (S)").clicked() {
.label("Single Step (S)") state.running = RunMode::Step;
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.button_pause, VSPACE)
.set(self.ids.button_single_step, &mut ui)
{
state.running = RunMode::Step
} }
for _press in conrod::widget::Button::new() if ui.button("Take snapshot").clicked() {
.label("Take snapshot")
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.button_single_step, VSPACE)
.set(self.ids.button_take_snapshot, &mut ui)
{
state state
.action_flags .action_flags
.set(TestbedActionFlags::TAKE_SNAPSHOT, true); .set(TestbedActionFlags::TAKE_SNAPSHOT, true);
} }
for _press in conrod::widget::Button::new() if ui.button("Restore snapshot").clicked() {
.label("Restore snapshot")
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.button_take_snapshot, VSPACE)
.set(self.ids.button_restore_snapshot, &mut ui)
{
state state
.action_flags .action_flags
.set(TestbedActionFlags::RESTORE_SNAPSHOT, true); .set(TestbedActionFlags::RESTORE_SNAPSHOT, true);
} }
let before_quit_button_id = if !state.example_names.is_empty() { if ui.button("Restart (R)").clicked() {
for _press in conrod::widget::Button::new()
.label("Restart (R)")
.align_middle_x_of(self.ids.canvas)
.down_from(self.ids.button_restore_snapshot, VSPACE)
.set(self.ids.button_restart, &mut ui)
{
state.action_flags.set(TestbedActionFlags::RESTART, true); state.action_flags.set(TestbedActionFlags::RESTART, true);
} }
});
self.ids.button_restart
} else {
self.ids.button_restore_snapshot
};
#[cfg(not(target_arch = "wasm32"))]
for _press in conrod::widget::Button::new()
.label("Quit (Esc)")
.align_middle_x_of(self.ids.canvas)
.down_from(before_quit_button_id, VSPACE)
.set(self.ids.button_quit, &mut ui)
{
state.running = RunMode::Quit
}
}
} }
fn toggles( fn profiling_string(counters: &Counters) -> String {
toggles: &[(&str, conrod::widget::Id, TestbedStateFlags)], format!(
canvas: conrod::widget::Id, r#"Total: {:.2}ms
prev: conrod::widget::Id, Collision detection: {:.2}ms
ui: &mut conrod::UiCell, |_ Broad-phase: {:.2}ms
flags: &mut TestbedStateFlags, Narrow-phase: {:.2}ms
) { Island computation: {:.2}ms
toggle( Solver: {:.2}ms
toggles[0].0, |_ Velocity assembly: {:.2}ms
toggles[0].2, Velocity resolution: {:.2}ms
canvas, Velocity integration: {:.2}ms
prev, Position assembly: {:.2}ms
toggles[0].1, Position resolution: {:.2}ms
ui, CCD: {:.2}ms
flags, |_ # of substeps: {}
); TOI computation: {:.2}ms
Broad-phase: {:.2}ms
for win in toggles.windows(2) { Narrow-phase: {:.2}ms
toggle(win[1].0, win[1].2, canvas, win[0].1, win[1].1, ui, flags) Solver: {:.2}ms"#,
} counters.step_time(),
counters.collision_detection_time(),
counters.broad_phase_time(),
counters.narrow_phase_time(),
counters.island_construction_time(),
counters.solver_time(),
counters.solver.velocity_assembly_time.time(),
counters.velocity_resolution_time(),
counters.solver.velocity_update_time.time(),
counters.solver.position_assembly_time.time(),
counters.position_resolution_time(),
counters.ccd_time(),
counters.ccd.num_substeps,
counters.ccd.toi_computation_time.time(),
counters.ccd.broad_phase_time.time(),
counters.ccd.narrow_phase_time.time(),
counters.ccd.solver_time.time(),
)
} }
fn toggle( fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
title: &str, let t = instant::now();
flag: TestbedStateFlags, // let t = instant::now();
canvas: conrod::widget::Id, let bf = bincode::serialize(&physics.broad_phase).unwrap();
prev: conrod::widget::Id, // println!("bf: {}", instant::now() - t);
curr: conrod::widget::Id, // let t = instant::now();
ui: &mut conrod::UiCell, let nf = bincode::serialize(&physics.narrow_phase).unwrap();
flags: &mut TestbedStateFlags, // println!("nf: {}", instant::now() - t);
) { // let t = instant::now();
if title == "" { let bs = bincode::serialize(&physics.bodies).unwrap();
// This is a separator. // println!("bs: {}", instant::now() - t);
separator(canvas, prev, curr, ui) // let t = instant::now();
} else { let cs = bincode::serialize(&physics.colliders).unwrap();
for _pressed in conrod::widget::Toggle::new(flags.contains(flag)) // println!("cs: {}", instant::now() - t);
.mid_left_with_margin_on(canvas, LEFT_MARGIN) // let t = instant::now();
.down_from(prev, VSPACE) let js = bincode::serialize(&physics.joints).unwrap();
.w_h(20.0 /*ELEMENT_W*/, ELEMENT_H) // println!("js: {}", instant::now() - t);
.label(title) let serialization_time = instant::now() - t;
.label_color(kiss3d::conrod::color::WHITE) let hash_bf = md5::compute(&bf);
.label_x(conrod::position::Relative::Direction( let hash_nf = md5::compute(&nf);
conrod::position::Direction::Forwards, let hash_bodies = md5::compute(&bs);
5.0, let hash_colliders = md5::compute(&cs);
)) let hash_joints = md5::compute(&js);
.border(2.0) format!(
// .border_color(kiss3d::conrod::color::WHITE) r#"Serialization time: {:.2}ms
.set(curr, ui) Hashes at frame: {}
{ |_ Broad phase [{:.1}KB]: {}
flags.toggle(flag) |_ Narrow phase [{:.1}KB]: {}
} |_ Bodies [{:.1}KB]: {}
} |_ Colliders [{:.1}KB]: {}
} |_ Joints [{:.1}KB]: {}"#,
serialization_time,
fn separator( timestep_id,
canvas: conrod::widget::Id, bf.len() as f32 / 1000.0,
prev: conrod::widget::Id, format!("{:?}", hash_bf).split_at(10).0,
curr: conrod::widget::Id, nf.len() as f32 / 1000.0,
ui: &mut conrod::UiCell, format!("{:?}", hash_nf).split_at(10).0,
) { bs.len() as f32 / 1000.0,
conrod::widget::Line::centred([-ELEMENT_W / 2.0, 0.0], [ELEMENT_W / 2.0, 0.0]) format!("{:?}", hash_bodies).split_at(10).0,
.align_middle_x_of(canvas) cs.len() as f32 / 1000.0,
.down_from(prev, VSPACE) format!("{:?}", hash_colliders).split_at(10).0,
.w(ELEMENT_W) js.len() as f32 / 1000.0,
.set(curr, ui); format!("{:?}", hash_joints).split_at(10).0,
)
} }