diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72c9f745..3c8dd416 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,11 +46,13 @@ jobs: - run: cargo test --no-default-features --features 2d if: matrix.crate == 'rapier' || matrix.crate == 'debug' || matrix.crate == '.' - run: cargo test --no-default-features --features 3d - if: matrix.crate == 'rapier' || matrix.crate == '.' + if: matrix.crate == 'rapier' || matrix.crate == 'debug' || matrix.crate == '.' - run: cargo test --no-default-features --features enhanced-determinism if: matrix.crate == 'rapier' || matrix.crate == '.' - run: cargo test --no-default-features --features debug-2d if: matrix.crate == '.' + - run: cargo test --no-default-features --features debug-3d + if: matrix.crate == '.' - run: cargo test --all-features -- --ignored code-style: diff --git a/Cargo.toml b/Cargo.toml index 230b9df8..046d5364 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ collision-from-mesh = ["heron_core/collision-from-mesh"] 2d = ["heron_rapier/2d"] 3d = ["heron_rapier/3d", "heron_core/3d"] debug-2d = ["2d", "heron_debug/2d"] +debug-3d = ["3d", "heron_debug/3d"] enhanced-determinism = ["heron_rapier/enhanced-determinism"] [dependencies] @@ -46,6 +47,10 @@ required-features = ["2d"] name = "debug_2d" required-features = ["debug-2d"] +[[example]] +name = "debug_3d" +required-features = ["debug-3d"] + [[example]] name = "quickstart" required-features = ["2d"] diff --git a/README.md b/README.md index 22448d39..71cb52bb 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ One must choose to use either `2d` or `3d`. If neither of the two features is en * `3d` Enable simulation on the 3 axes `x`, `y`, and `z`. * `2d` Enable simulation only on the first 2 axes `x` and `y`. * `debug-2d` Render 2d collision shapes. +* `debug-3d` Render 3d collision shapes. * `enhanced-determinism` Enable rapier's [enhanced-determinism](https://rapier.rs/docs/user_guides/rust/determinism) diff --git a/debug/Cargo.toml b/debug/Cargo.toml index 0df80350..f182c5de 100644 --- a/debug/Cargo.toml +++ b/debug/Cargo.toml @@ -8,12 +8,10 @@ license = "MIT" description = "Rendering of Heron's collision shapes for debugging purposes" repository = "https://github.com/jcornaz/heron/" -[package.metadata.docs.rs] -all-features = true - [features] default = [] 2d = ["heron_rapier/2d", "bevy_prototype_lyon", "lyon_path"] +3d = ["heron_rapier/3d", "bevy_prototype_debug_lines/3d"] [dependencies] heron_core = { version = "2.1.0", path = "../core" } @@ -22,3 +20,10 @@ bevy = { version = "0.6.0", default-features = false, features = ["render"] } bevy_prototype_lyon = { version = "0.4.0", optional = true } lyon_path = { version = "0.17.4", optional = true } fnv = "1.0" +bevy_prototype_debug_lines = { version = "0.6.1", default-features = false, optional = true } + +[package.metadata.docs.rs] +all-features = true + +[package.metadata.cargo-udeps.ignore] +normal = ["lyon_path", "bevy_prototype_lyon"] # False positive, they are used but only when 2d is enabled and 3d disabled diff --git a/debug/src/lib.rs b/debug/src/lib.rs index d8a10543..24694fef 100644 --- a/debug/src/lib.rs +++ b/debug/src/lib.rs @@ -54,7 +54,7 @@ struct Indexed; impl Plugin for DebugPlugin { fn build(&self, app: &mut App) { #[cfg(feature = "3d")] - app.add_plugin(bevy_prototype_debug_lines::DebugLinesPlugin) + app.add_plugin(bevy_prototype_debug_lines::DebugLinesPlugin::default()) .add_system_set_to_stage(CoreStage::PostUpdate, dim3::systems()); #[cfg(all(feature = "2d", not(feature = "3d")))] diff --git a/examples/debug_3d.rs b/examples/debug_3d.rs new file mode 100644 index 00000000..8816b9fe --- /dev/null +++ b/examples/debug_3d.rs @@ -0,0 +1,199 @@ +use bevy::prelude::*; + +use heron::prelude::*; + +fn main() { + App::new() + .insert_resource(Msaa { samples: 4 }) + .insert_resource(Gravity::from(Vec3::new(0., -9.81, 0.))) + .add_plugins(DefaultPlugins) + .add_plugin(PhysicsPlugin::default()) + .add_startup_system(setup.system()) + .run(); +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // HeightField + commands + .spawn_bundle((Transform::identity(), GlobalTransform::identity())) + .insert(RigidBody::Static) + .insert(CollisionShape::HeightField { + size: Vec2::new(20., 20.), + heights: vec![ + vec![1.5, 0.8, 0., 0., 3.0], + vec![0.8, 0.2, 0., 0., 3.0], + vec![0., 0.5, 0., 0., 3.0], + vec![0., 0., 0.6, 0., 3.0], + vec![3., 3., 3., 3., 3.0], + ], + }); + + commands + .spawn_bundle(PerspectiveCameraBundle::new_3d()) + .insert( + Transform { + translation: Vec3::new(3.0, 7., -19.0), + ..Default::default() + } + .looking_at(Vec3::new(1., 4., 0.), Vec3::Y), + ); + + // Cube (with radius) + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::BLUE.into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(7.0, 15., 7.0), + ..Default::default() + }) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Cuboid { + half_extends: Vec3::new(0.3, 0.3, 0.3), + border_radius: Some(0.3), + }); + + // Cube (no radius) + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..Default::default() + }) + .insert(GlobalTransform::identity()) + .insert(Transform { + translation: Vec3::new(0.0, 3.0, 0.0), + ..Default::default() + }) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Cuboid { + half_extends: Vec3::new(0.5, 0.5, 0.5), + border_radius: None, + }); + + // ConvexHull (no radius) + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 2.0 })), + material: materials.add(Color::RED.into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(-2.0, 15., 8.0), + ..Default::default() + }) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::ConvexHull { + points: vec![ + Vec3::new(-1.0, -1.0, -1.0), + Vec3::new(-1.0, -1.0, 1.0), + Vec3::new(-1.0, 1.0, -1.0), + Vec3::new(1.0, -1.0, -1.0), + Vec3::new(-1.0, 1.0, 1.0), + Vec3::new(1.0, -1.0, 1.0), + Vec3::new(1.0, 1.0, 1.0), + Vec3::new(1.0, 1.0, -1.0), + Vec3::new(0.0, 1.4, 0.0), + Vec3::new(0.0, -1.4, 0.0), + Vec3::new(0.0, 0.0, -1.6), + Vec3::new(0.0, 0.0, 1.6), + ], + border_radius: None, + }); + + // Sphere + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Icosphere { + radius: 1.0, + ..Default::default() + })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(3.0, 15., 3.0), + ..Default::default() + }) + .insert(GlobalTransform::identity()) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Sphere { radius: 1.0 }); + + // Cylinder + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Capsule { + radius: 0.5, + depth: 2.0, + ..Default::default() + })), + material: materials.add(Color::GREEN.into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(3., 15., -7.), + ..Default::default() + }) + .insert(GlobalTransform::identity()) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Cylinder { + half_height: 1.0, + radius: 0.5, + }); + + // Cone + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Capsule { + radius: 0.5, + depth: 2.0, + ..Default::default() + })), + material: materials.add(Color::RED.into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(5., 15., -7.), + ..Default::default() + }) + .insert(GlobalTransform::identity()) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Cone { + half_height: 2.0, + radius: 1.0, + }); + + // Capsule + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Capsule { + radius: 0.5, + depth: 2.0, + ..Default::default() + })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..Default::default() + }) + .insert(Transform { + translation: Vec3::new(0., 15., 0.), + ..Default::default() + }) + .insert(GlobalTransform::identity()) + .insert(RigidBody::Dynamic) + .insert(CollisionShape::Capsule { + radius: 0.5, + half_segment: 1.0, + }); + + // light + commands.spawn_bundle(PointLightBundle { + transform: Transform::from_xyz(-4.0, 9.0, -4.0), + ..Default::default() + }); +} diff --git a/src/lib.rs b/src/lib.rs index b20a5708..f6c06782 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,8 @@ //! //! * `3d` Enable simulation on the 3 axes `x`, `y`, and `z`. Incompatible with the feature `2d`. //! * `2d` Enable simulation only on the first 2 axes `x` and `y`. Incompatible with the feature `3d`, therefore require to disable the default features. -//! * `debug-2d` Render 2d collision shapes. Works only in 2d, support for 3d may be added later. +//! * `debug-2d` Render 2d collision shapes +//! * `debug-3d` Render 3d collision shapes //! * `enhanced-determinism` Enable rapier's [enhanced-determinism](https://rapier.rs/docs/user_guides/rust/determinism) //! //!