Skip to content

Commit

Permalink
Add debug rendering for sleeping bodies (#185)
Browse files Browse the repository at this point in the history
# Objective

The debug renderer doesn't display whether bodies are sleeping.

## Solution

Add a "sleeping color multiplier" that multiplies the HSLA properties of colors used for colliders, AABBs and joints when the bodies are sleeping. By default, it just makes the colors darker for sleeping bodies.

The multiplier can be configured with `PhysicsDebugConfig` and `DebugRender`.
  • Loading branch information
Jondolf authored Oct 17, 2023
1 parent 5890b16 commit 09a8c6e
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 36 deletions.
24 changes: 24 additions & 0 deletions src/plugins/debug/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub struct PhysicsDebugConfig {
pub aabb_color: Option<Color>,
/// The color of the [collider](Collider) wireframes. If `None`, the colliders will not be rendered.
pub collider_color: Option<Color>,
/// The colors (in HSLA) for [sleeping](Sleeping) bodies will be multiplied by this array.
/// If `None`, sleeping will have no effect on the colors.
pub sleeping_color_multiplier: Option<[f32; 4]>,
/// The color of the contact points. If `None`, the contact points will not be rendered.
pub contact_color: Option<Color>,
/// The color of the lines drawn from the centers of bodies to their joint anchors.
Expand All @@ -36,6 +39,7 @@ impl Default for PhysicsDebugConfig {
axis_lengths: Some(Vector::new(0.5, 0.5, 0.5)),
aabb_color: None,
collider_color: Some(Color::ORANGE),
sleeping_color_multiplier: Some([1.0, 1.0, 0.4, 1.0]),
contact_color: None,
joint_anchor_color: Some(Color::PINK),
joint_separation_color: Some(Color::RED),
Expand All @@ -55,6 +59,7 @@ impl PhysicsDebugConfig {
axis_lengths: Some(Vector::new(0.5, 0.5, 0.5)),
aabb_color: Some(Color::rgb(0.8, 0.8, 0.8)),
collider_color: Some(Color::ORANGE),
sleeping_color_multiplier: Some([1.0, 1.0, 0.4, 1.0]),
contact_color: Some(Color::CYAN),
joint_anchor_color: Some(Color::PINK),
joint_separation_color: Some(Color::RED),
Expand All @@ -71,6 +76,7 @@ impl PhysicsDebugConfig {
axis_lengths: None,
aabb_color: None,
collider_color: None,
sleeping_color_multiplier: None,
contact_color: None,
joint_anchor_color: None,
joint_separation_color: None,
Expand Down Expand Up @@ -142,6 +148,12 @@ impl PhysicsDebugConfig {
self
}

/// Sets the multiplier used for the colors (in HSLA) of [sleeping](Sleeping) bodies.
pub fn with_sleeping_color_multiplier(mut self, color_multiplier: [f32; 4]) -> Self {
self.sleeping_color_multiplier = Some(color_multiplier);
self
}

/// Sets the contact color.
pub fn with_contact_color(mut self, color: Color) -> Self {
self.contact_color = Some(color);
Expand Down Expand Up @@ -198,6 +210,9 @@ pub struct DebugRender {
pub aabb_color: Option<Color>,
/// The color of the [collider](Collider) wireframe. If `None`, the collider will not be rendered.
pub collider_color: Option<Color>,
/// If the entity is [sleeping](Sleeping), its colors (in HSLA) will be multiplied by this array.
/// If `None`, sleeping will have no effect on the colors.
pub sleeping_color_multiplier: Option<[f32; 4]>,
/// Determines if the entity's visibility should be set to `Visibility::Hidden`, which will only show the debug render.
pub hide_mesh: bool,
}
Expand All @@ -211,6 +226,7 @@ impl Default for DebugRender {
axis_lengths: Some(Vector::new(0.5, 0.5, 0.5)),
aabb_color: None,
collider_color: Some(Color::ORANGE),
sleeping_color_multiplier: Some([1.0, 1.0, 0.4, 1.0]),
hide_mesh: false,
}
}
Expand All @@ -226,6 +242,7 @@ impl DebugRender {
axis_lengths: Some(Vector::new(0.5, 0.5, 0.5)),
aabb_color: Some(Color::rgb(0.8, 0.8, 0.8)),
collider_color: Some(Color::ORANGE),
sleeping_color_multiplier: Some([1.0, 1.0, 0.4, 1.0]),
hide_mesh: true,
}
}
Expand All @@ -236,6 +253,7 @@ impl DebugRender {
axis_lengths: None,
aabb_color: None,
collider_color: None,
sleeping_color_multiplier: None,
hide_mesh: false,
}
}
Expand Down Expand Up @@ -285,6 +303,12 @@ impl DebugRender {
self
}

/// Sets the multiplier used for the colors (in HSLA) of [sleeping](Sleeping) bodies.
pub fn with_sleeping_color_multiplier(mut self, color_multiplier: [f32; 4]) -> Self {
self.sleeping_color_multiplier = Some(color_multiplier);
self
}

/// Sets the visibility of the entity's visual mesh.
pub fn with_mesh_visibility(mut self, is_visible: bool) -> Self {
self.hide_mesh = !is_visible;
Expand Down
152 changes: 116 additions & 36 deletions src/plugins/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use configuration::*;
pub use renderer::*;

use crate::prelude::*;
use bevy::prelude::*;
use bevy::{ecs::query::Has, prelude::*};

/// Renders physics objects and properties for debugging purposes.
///
Expand All @@ -18,7 +18,8 @@ use bevy::prelude::*;
/// - Entity axes
/// - [AABBs](ColliderAabb)
/// - [Collider] wireframes
/// - [Contact] points
/// - Use different colors for [sleeping](Sleeping) bodies
/// - [Contacts]
/// - [Joints](joints)
/// - Changing the visibility of entities to only show debug rendering
///
Expand Down Expand Up @@ -79,51 +80,81 @@ impl Plugin for PhysicsDebugPlugin {
}
}

#[allow(clippy::type_complexity)]
fn debug_render_axes(
bodies: Query<(&Position, &Rotation, &CenterOfMass, Option<&DebugRender>)>,
bodies: Query<(
&Position,
&Rotation,
&CenterOfMass,
Has<Sleeping>,
Option<&DebugRender>,
)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for (pos, rot, local_com, render_config) in &bodies {
for (pos, rot, local_com, sleeping, render_config) in &bodies {
// If the body is sleeping, the colors will be multiplied by the sleeping color multiplier
if let Some(lengths) = render_config.map_or(config.axis_lengths, |c| c.axis_lengths) {
let mul = if sleeping {
render_config
.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
})
.unwrap_or([1.0; 4])
} else {
[1.0; 4]
};
let [x_color, y_color, _z_color, center_color] = [
Color::hsla(0.0, 1.0 * mul[1], 0.5 * mul[2], 1.0 * mul[3]),
Color::hsla(120.0 * mul[0], 1.0 * mul[1], 0.4 * mul[2], 1.0 * mul[3]),
Color::hsla(220.0 * mul[0], 1.0 * mul[1], 0.6 * mul[2], 1.0 * mul[3]),
Color::hsla(60.0 * mul[0], 1.0 * mul[1], 0.5 * mul[2], 1.0 * mul[3]),
];
let global_com = pos.0 + rot.rotate(local_com.0);

let x = rot.rotate(Vector::X * lengths.x);
debug_renderer.draw_line(global_com - x, global_com + x, Color::hsl(0.0, 1.0, 0.5));
debug_renderer.draw_line(global_com - x, global_com + x, x_color);

let y = rot.rotate(Vector::Y * lengths.y);
debug_renderer.draw_line(global_com - y, global_com + y, Color::hsl(120.0, 1.0, 0.4));
debug_renderer.draw_line(global_com - y, global_com + y, y_color);

#[cfg(feature = "3d")]
{
let z = rot.rotate(Vector::Z * lengths.z);
debug_renderer.draw_line(
global_com - z,
global_com + z,
Color::hsl(220.0, 1.0, 0.6),
);
debug_renderer.draw_line(global_com - z, global_com + z, _z_color);
}

// Draw dot at the center of mass
#[cfg(feature = "2d")]
debug_renderer
.gizmos
.circle_2d(global_com.as_f32(), 0.5, Color::YELLOW);
.circle_2d(global_com.as_f32(), 0.5, center_color);
#[cfg(feature = "3d")]
debug_renderer
.gizmos
.sphere(global_com.as_f32(), rot.as_f32(), 0.025, Color::YELLOW);
.sphere(global_com.as_f32(), rot.as_f32(), 0.025, center_color);
}
}
}

fn debug_render_aabbs(
aabbs: Query<(&ColliderAabb, Option<&DebugRender>)>,
aabbs: Query<(&ColliderAabb, Option<&DebugRender>, Has<Sleeping>)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
#[cfg(feature = "2d")]
for (aabb, render_config) in &aabbs {
if let Some(color) = render_config.map_or(config.aabb_color, |c| c.aabb_color) {
for (aabb, render_config, sleeping) in &aabbs {
if let Some(mut color) = render_config.map_or(config.aabb_color, |c| c.aabb_color) {
// If the body is sleeping, multiply the color by the sleeping color multiplier
if sleeping {
let [h, s, l, a] = color.as_hsla_f32();
if let Some(mul) = render_config.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
}) {
color = Color::hsla(h * mul[0], s * mul[1], l * mul[2], a * mul[3]);
}
}

debug_renderer.gizmos.cuboid(
Transform::from_scale(Vector::from(aabb.extents()).extend(0.0).as_f32())
.with_translation(Vector::from(aabb.center()).extend(0.0).as_f32()),
Expand All @@ -133,8 +164,18 @@ fn debug_render_aabbs(
}

#[cfg(feature = "3d")]
for (aabb, render_config) in &aabbs {
if let Some(color) = render_config.map_or(config.aabb_color, |c| c.aabb_color) {
for (aabb, render_config, sleeping) in &aabbs {
if let Some(mut color) = render_config.map_or(config.aabb_color, |c| c.aabb_color) {
// If the body is sleeping, multiply the color by the sleeping color multiplier
if sleeping {
let [h, s, l, a] = color.as_hsla_f32();
if let Some(mul) = render_config.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
}) {
color = Color::hsla(h * mul[0], s * mul[1], l * mul[2], a * mul[3]);
}
}

debug_renderer.gizmos.cuboid(
Transform::from_scale(Vector::from(aabb.extents()).as_f32())
.with_translation(Vector::from(aabb.center()).as_f32()),
Expand All @@ -144,6 +185,34 @@ fn debug_render_aabbs(
}
}

#[allow(clippy::type_complexity)]
fn debug_render_colliders(
mut colliders: Query<(
&Collider,
&Position,
&Rotation,
Option<&DebugRender>,
Has<Sleeping>,
)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for (collider, position, rotation, render_config, sleeping) in &mut colliders {
if let Some(mut color) = render_config.map_or(config.collider_color, |c| c.collider_color) {
// If the body is sleeping, multiply the color by the sleeping color multiplier
if sleeping {
let [h, s, l, a] = color.as_hsla_f32();
if let Some(mul) = render_config.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
}) {
color = Color::hsla(h * mul[0], s * mul[1], l * mul[2], a * mul[3]);
}
}
debug_renderer.draw_collider(collider, position, rotation, color);
}
}
}

fn debug_render_contacts(
colliders: Query<(&Position, &Rotation), With<Collider>>,
mut collisions: EventReader<Collision>,
Expand Down Expand Up @@ -184,27 +253,27 @@ fn debug_render_contacts(
}
}

fn debug_render_colliders(
mut colliders: Query<(&Collider, &Position, &Rotation, Option<&DebugRender>)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for (collider, position, rotation, render_config) in &mut colliders {
if let Some(color) = render_config.map_or(config.collider_color, |c| c.collider_color) {
debug_renderer.draw_collider(collider, position, rotation, color);
}
}
}

fn debug_render_joints<T: Joint>(
bodies: Query<(&Position, &Rotation)>,
joints: Query<&T>,
bodies: Query<(&Position, &Rotation, Has<Sleeping>)>,
joints: Query<(&T, Option<&DebugRender>)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for joint in &joints {
if let Ok([(pos1, rot1), (pos2, rot2)]) = bodies.get_many(joint.entities()) {
if let Some(anchor_color) = config.joint_anchor_color {
for (joint, render_config) in &joints {
if let Ok([(pos1, rot1, sleeping1), (pos2, rot2, sleeping2)]) =
bodies.get_many(joint.entities())
{
if let Some(mut anchor_color) = config.joint_anchor_color {
// If both bodies are sleeping, multiply the color by the sleeping color multiplier
if sleeping1 && sleeping2 {
let [h, s, l, a] = anchor_color.as_hsla_f32();
if let Some(mul) = render_config.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
}) {
anchor_color = Color::hsla(h * mul[0], s * mul[1], l * mul[2], a * mul[3]);
}
}

debug_renderer.draw_line(
pos1.0,
pos1.0 + rot1.rotate(joint.local_anchor_1()),
Expand All @@ -216,7 +285,18 @@ fn debug_render_joints<T: Joint>(
anchor_color,
);
}
if let Some(separation_color) = config.joint_separation_color {
if let Some(mut separation_color) = config.joint_separation_color {
// If both bodies are sleeping, multiply the color by the sleeping color multiplier
if sleeping1 && sleeping2 {
let [h, s, l, a] = separation_color.as_hsla_f32();
if let Some(mul) = render_config.map_or(config.sleeping_color_multiplier, |c| {
c.sleeping_color_multiplier
}) {
separation_color =
Color::hsla(h * mul[0], s * mul[1], l * mul[2], a * mul[3]);
}
}

debug_renderer.draw_line(
pos1.0 + rot1.rotate(joint.local_anchor_1()),
pos2.0 + rot2.rotate(joint.local_anchor_2()),
Expand Down

0 comments on commit 09a8c6e

Please sign in to comment.