From e09f96693dd6dbd09a6bbd7fc313c0e75bf7cdcc Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Fri, 20 Sep 2024 15:10:14 +0200 Subject: [PATCH] Example bounding sphere using macroquad (#259) --- crates/parry2d/examples/bounding_sphere2d.rs | 154 +++++++++++++++---- crates/parry3d/examples/bounding_sphere3d.rs | 135 ++++++++++++---- 2 files changed, 230 insertions(+), 59 deletions(-) diff --git a/crates/parry2d/examples/bounding_sphere2d.rs b/crates/parry2d/examples/bounding_sphere2d.rs index a5551dad..b5a495f1 100644 --- a/crates/parry2d/examples/bounding_sphere2d.rs +++ b/crates/parry2d/examples/bounding_sphere2d.rs @@ -1,36 +1,130 @@ +mod common_macroquad2d; + extern crate nalgebra as na; +use common_macroquad2d::{draw_polyline, lissajous_2d, mquad_from_na, na_from_mquad}; +use macroquad::prelude::*; use na::{Isometry2, Vector2}; -use parry2d::bounding_volume::BoundingVolume; +use parry2d::bounding_volume::{Aabb, BoundingVolume}; use parry2d::shape::Cuboid; -fn main() { - /* - * Initialize the shapes. - */ - let cube1 = Cuboid::new(Vector2::repeat(0.5)); - let cube2 = Cuboid::new(Vector2::new(1.0, 0.5)); - - let cube1_pos = Isometry2::translation(0.0, 1.0); - let cube2_pos = Isometry2::identity(); - - /* - * Compute their bounding spheres. - */ - let bounding_sphere_cube1 = cube1.bounding_sphere(&cube1_pos); - let bounding_sphere_cube2 = cube2.bounding_sphere(&cube2_pos); - - // Merge the two spheres. - let bounding_bounding_sphere = bounding_sphere_cube1.merged(&bounding_sphere_cube2); - - // Enlarge the cube2 bounding sphere. - let loose_bounding_sphere_cube2 = bounding_sphere_cube2.loosened(1.0); - - // Intersection and inclusion tests. - assert!(bounding_sphere_cube1.intersects(&bounding_sphere_cube2)); - assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube1)); - assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube2)); - assert!(!bounding_sphere_cube2.contains(&bounding_bounding_sphere)); - assert!(!bounding_sphere_cube1.contains(&bounding_bounding_sphere)); - assert!(loose_bounding_sphere_cube2.contains(&bounding_sphere_cube2)); +const RENDER_SCALE: f32 = 30.0; + +#[macroquad::main("parry2d::utils::point_in_poly2d")] +async fn main() { + let render_pos = Vec2::new(300.0, 300.0); + + loop { + let elapsed_time = get_time() as f32 * 0.7; + clear_background(BLACK); + + /* + * Initialize the shapes. + */ + let cube1: Cuboid = Cuboid::new(Vector2::repeat(0.5)); + let cube2 = Cuboid::new(Vector2::new(1., 0.5)); + + let cube1_pos = na_from_mquad(lissajous_2d(elapsed_time)) * 5f32; + let cube1_pos = Isometry2::from(cube1_pos); + let cube2_pos = Isometry2::identity(); + + /* + * Compute their bounding spheres. + */ + let bounding_sphere_cube1 = cube1.bounding_sphere(&cube1_pos); + let bounding_sphere_cube2 = cube2.bounding_sphere(&cube2_pos); + + // Merge the two spheres. + let bounding_bounding_sphere = bounding_sphere_cube1.merged(&bounding_sphere_cube2); + + // Enlarge the cube2 bounding sphere. + let loose_bounding_sphere_cube2 = bounding_sphere_cube2.loosened(3.0); + + // Intersection test + let color = if bounding_sphere_cube1.intersects(&bounding_sphere_cube2) { + RED + } else { + GREEN + }; + + // Due to float imprecisions, it's dangerous to assume that both shapes will be + // contained in the merged. + // You can leverage `BoundingVolume::loosened` with an epsilon for expected results. + // + // These might fail: + // assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube1)); + // assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube2)); + + assert!(loose_bounding_sphere_cube2.contains(&bounding_sphere_cube1)); + assert!(loose_bounding_sphere_cube2.contains(&bounding_sphere_cube2)); + + let cube1_translation = + mquad_from_na(cube1_pos.translation.vector.into()) * RENDER_SCALE + render_pos; + draw_cuboid(cube1, cube1_translation, color); + + let cube2_translation = + mquad_from_na(cube2_pos.translation.vector.into()) * RENDER_SCALE + render_pos; + draw_cuboid(cube2, cube2_translation, color); + draw_circle_lines( + bounding_sphere_cube1.center.x * RENDER_SCALE + render_pos.x, + bounding_sphere_cube1.center.y * RENDER_SCALE + render_pos.y, + bounding_sphere_cube1.radius * RENDER_SCALE, + 2f32, + color, + ); + draw_circle_lines( + bounding_sphere_cube2.center.x * RENDER_SCALE + render_pos.x, + bounding_sphere_cube2.center.y * RENDER_SCALE + render_pos.y, + bounding_sphere_cube2.radius * RENDER_SCALE, + 2f32, + color, + ); + draw_circle_lines( + bounding_bounding_sphere.center.x * RENDER_SCALE + render_pos.x, + bounding_bounding_sphere.center.y * RENDER_SCALE + render_pos.y, + bounding_bounding_sphere.radius * RENDER_SCALE, + 2f32, + YELLOW, + ); + + // Inclusion test + let color_included: Color = if loose_bounding_sphere_cube2.contains(&bounding_sphere_cube1) + { + BLUE + } else { + MAGENTA + }; + draw_circle_lines( + loose_bounding_sphere_cube2.center.x * RENDER_SCALE + render_pos.x, + loose_bounding_sphere_cube2.center.y * RENDER_SCALE + render_pos.y, + loose_bounding_sphere_cube2.radius * RENDER_SCALE, + 2f32, + color_included, + ); + next_frame().await + } +} + +fn draw_cuboid(cuboid: Cuboid, pos: Vec2, color: Color) { + let aabb = cuboid.local_aabb(); + draw_aabb(aabb, pos, color) +} + +fn draw_aabb(aabb: Aabb, offset: Vec2, color: Color) { + let mins = mquad_from_na(aabb.mins) * RENDER_SCALE + offset; + let maxs = mquad_from_na(aabb.maxs) * RENDER_SCALE + offset; + + let line = vec![ + Vec2::new(mins.x, mins.y), + Vec2::new(mins.x, maxs.y), + Vec2::new(maxs.x, maxs.y), + Vec2::new(maxs.x, mins.y), + Vec2::new(mins.x, mins.y), + ]; + let drawable_line = line + .iter() + .zip(line.iter().cycle().skip(1).take(line.len())) + .map(|item| (item.0.clone(), item.1.clone())) + .collect(); + draw_polyline(drawable_line, color); } diff --git a/crates/parry3d/examples/bounding_sphere3d.rs b/crates/parry3d/examples/bounding_sphere3d.rs index 0cbf01f9..889085ea 100644 --- a/crates/parry3d/examples/bounding_sphere3d.rs +++ b/crates/parry3d/examples/bounding_sphere3d.rs @@ -1,36 +1,113 @@ +mod common_macroquad3d; + extern crate nalgebra as na; +use std::ops::Rem; + +use common_macroquad3d::{lissajous_3d, mquad_from_na, na_from_mquad}; +use macroquad::prelude::*; use na::{Isometry3, Vector3}; use parry3d::bounding_volume::BoundingVolume; use parry3d::shape::Cuboid; -fn main() { - /* - * Initialize the shapes. - */ - let cube1 = Cuboid::new(Vector3::repeat(0.5)); - let cube2 = Cuboid::new(Vector3::new(0.5, 1.0, 0.5)); - - let cube1_pos = Isometry3::translation(0.0, 0.0, 1.0); // 1.0 along the `z` axis. - let cube2_pos = Isometry3::identity(); // Identity matrix. - - /* - * Compute their bounding spheres. - */ - let bounding_sphere_cube1 = cube1.bounding_sphere(&cube1_pos); - let bounding_sphere_cube2 = cube2.bounding_sphere(&cube2_pos); - - // Merge the two spheres. - let bounding_bounding_sphere = bounding_sphere_cube1.merged(&bounding_sphere_cube2); - - // Enlarge the cube2 bounding sphere. - let loose_bounding_sphere_cube2 = bounding_sphere_cube2.loosened(1.0); - - // Intersection and inclusion tests. - assert!(bounding_sphere_cube1.intersects(&bounding_sphere_cube2)); - assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube1)); - assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube2)); - assert!(!bounding_sphere_cube2.contains(&bounding_bounding_sphere)); - assert!(!bounding_sphere_cube1.contains(&bounding_bounding_sphere)); - assert!(loose_bounding_sphere_cube2.contains(&bounding_sphere_cube2)); +#[macroquad::main("parry2d::utils::point_in_poly2d")] +async fn main() { + let camera_pos = Vec3::new(8f32, 8f32, 12f32); + + loop { + let elapsed_time = get_time() as f32 * 0.7; + clear_background(BLACK); + // Initialize 3D camera. + set_camera(&Camera3D { + position: camera_pos, + up: Vec3::new(0f32, 1f32, 0f32), + target: Vec3::new(0.5f32, 0f32, 0.5f32), + ..Default::default() + }); + + /* + * Initialize the shapes. + */ + let cube1 = Cuboid::new(Vector3::repeat(0.5)); + let cube2 = Cuboid::new(Vector3::new(0.5, 1.0, 0.5)); + + let cube1_pos = na_from_mquad(lissajous_3d(elapsed_time)) * 4f32; + let cube1_pos = Isometry3::from(cube1_pos); + let cube2_pos = Isometry3::identity(); // Identity matrix. + + /* + * Compute their bounding spheres. + */ + let bounding_sphere_cube1 = cube1.bounding_sphere(&cube1_pos); + let bounding_sphere_cube2 = cube2.bounding_sphere(&cube2_pos); + + // Merge the two spheres. + let bounding_bounding_sphere = bounding_sphere_cube1.merged(&bounding_sphere_cube2); + + // Enlarge the cube2 bounding sphere. + let loose_bounding_sphere_cube2 = bounding_sphere_cube2.loosened(3.0); + + // Intersection and inclusion tests. + let mut color = if bounding_sphere_cube1.intersects(&bounding_sphere_cube2) { + RED + } else { + GREEN + }; + color.a = 1f32 * (elapsed_time.rem(1f32) - 0.5).abs() * 2f32; + + // Due to float imprecisions, it's dangerous to assume that both shapes will be + // contained in the merged. + // You can leverage `BoundingVolume::loosened` with an epsilon for expected results. + // + // These might fail: + //assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube1)); + //assert!(bounding_bounding_sphere.contains(&bounding_sphere_cube2)); + assert!(loose_bounding_sphere_cube2.contains(&bounding_sphere_cube2)); + + let cube1_translation = mquad_from_na(cube1_pos.translation.vector.into()); + draw_cube_wires( + cube1_translation, + mquad_from_na(cube1.half_extents.into()) * 2f32, + WHITE, + ); + let cube2_translation = mquad_from_na(cube2_pos.translation.vector.into()); + draw_cube_wires( + cube2_translation, + mquad_from_na(cube2.half_extents.into()) * 2f32, + WHITE, + ); + + draw_sphere_wires( + mquad_from_na(bounding_sphere_cube1.center), + bounding_sphere_cube1.radius, + None, + color, + ); + draw_sphere_wires( + mquad_from_na(bounding_sphere_cube2.center), + bounding_sphere_cube2.radius, + None, + color, + ); + draw_sphere_wires( + mquad_from_na(bounding_bounding_sphere.center), + bounding_bounding_sphere.radius, + None, + YELLOW, + ); + + let color_included: Color = if loose_bounding_sphere_cube2.contains(&bounding_sphere_cube1) + { + BLUE + } else { + MAGENTA + }; + draw_sphere_wires( + mquad_from_na(loose_bounding_sphere_cube2.center), + loose_bounding_sphere_cube2.radius, + None, + color_included, + ); + next_frame().await + } }