Skip to content

Commit

Permalink
Example bounding sphere using macroquad (dimforge#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vrixyz authored Sep 20, 2024
1 parent 5d550d8 commit e09f966
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 59 deletions.
154 changes: 124 additions & 30 deletions crates/parry2d/examples/bounding_sphere2d.rs
Original file line number Diff line number Diff line change
@@ -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);
}
135 changes: 106 additions & 29 deletions crates/parry3d/examples/bounding_sphere3d.rs
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit e09f966

Please sign in to comment.