Skip to content

Commit

Permalink
Debug render spatial queries (#186)
Browse files Browse the repository at this point in the history
# Objective

It can often be useful to visualize spatial queries like raycasts and shapecasts.

## Solution

Add debug rendering for `RayCaster` and `ShapeCaster`. `SpatialQuery` methods aren't debug rendered to avoid requiring mutable access to `Gizmos` in all of the methods, but `PhysicsDebugRenderer` now has utilities for rendering e.g. raycasts for those who need it.

---

## Todo

- [x] Debug render raycasts
- [x] Debug render shapecasts

## Other changes

- Renamed "ray cast" and "shape cast" in docs to "raycast" and "shapecast"
- Fixed issue with multiple hits in shapecasts
- Fixed documentation for `ShapeHitData`
  • Loading branch information
Jondolf authored Oct 22, 2023
1 parent 4284070 commit b9bad8d
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 31 deletions.
7 changes: 7 additions & 0 deletions src/components/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ impl From<Rotation> for Scalar {
}
}

#[cfg(feature = "2d")]
impl From<Scalar> for Rotation {
fn from(radians: Scalar) -> Self {
Self::from_radians(radians)
}
}

#[cfg(feature = "2d")]
impl From<Rotation> for Quaternion {
fn from(rot: Rotation) -> Self {
Expand Down
95 changes: 92 additions & 3 deletions src/plugins/debug/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ pub struct PhysicsDebugConfig {
pub joint_anchor_color: Option<Color>,
/// The color of the lines drawn between joint anchors, indicating the separation.
pub joint_separation_color: Option<Color>,
/// The color used for the rays in [raycasts](spatial_query#ray-casting).
pub raycast_color: Option<Color>,
/// The color used for the hit points in [raycasts](spatial_query#ray-casting).
pub raycast_point_color: Option<Color>,
/// The color used for the hit normals in [raycasts](spatial_query#ray-casting).
pub raycast_normal_color: Option<Color>,
/// The color used for the ray in [shapecasts](spatial_query#shape-casting).
pub shapecast_color: Option<Color>,
/// The color used for the shape in [shapecasts](spatial_query#shape-casting).
pub shapecast_shape_color: Option<Color>,
/// The color used for the hit points in [shapecasts](spatial_query#shape-casting).
pub shapecast_point_color: Option<Color>,
/// The color used for the hit normals in [shapecasts](spatial_query#shape-casting).
pub shapecast_normal_color: Option<Color>,
/// Determines if the visibility of entities with [colliders](Collider) should be set to `Visibility::Hidden`,
/// which will only show the debug renders.
pub hide_meshes: bool,
Expand All @@ -43,6 +57,13 @@ impl Default for PhysicsDebugConfig {
contact_color: None,
joint_anchor_color: Some(Color::PINK),
joint_separation_color: Some(Color::RED),
raycast_color: Some(Color::RED),
raycast_point_color: Some(Color::YELLOW),
raycast_normal_color: Some(Color::PINK),
shapecast_color: Some(Color::RED),
shapecast_shape_color: Some(Color::rgb(0.4, 0.6, 1.0)),
shapecast_point_color: Some(Color::YELLOW),
shapecast_normal_color: Some(Color::PINK),
hide_meshes: false,
}
}
Expand All @@ -63,6 +84,13 @@ impl PhysicsDebugConfig {
contact_color: Some(Color::CYAN),
joint_anchor_color: Some(Color::PINK),
joint_separation_color: Some(Color::RED),
raycast_color: Some(Color::RED),
raycast_point_color: Some(Color::YELLOW),
raycast_normal_color: Some(Color::PINK),
shapecast_color: Some(Color::RED),
shapecast_shape_color: Some(Color::rgb(0.4, 0.6, 1.0)),
shapecast_point_color: Some(Color::YELLOW),
shapecast_normal_color: Some(Color::PINK),
hide_meshes: true,
}
}
Expand All @@ -80,6 +108,13 @@ impl PhysicsDebugConfig {
contact_color: None,
joint_anchor_color: None,
joint_separation_color: None,
raycast_color: None,
raycast_point_color: None,
raycast_normal_color: None,
shapecast_color: None,
shapecast_shape_color: None,
shapecast_point_color: None,
shapecast_normal_color: None,
hide_meshes: false,
}
}
Expand Down Expand Up @@ -122,10 +157,10 @@ impl PhysicsDebugConfig {

/// Creates a [`PhysicsDebugConfig`] configuration with given colors for
/// joint anchors and separation distances. Other debug rendering options will be disabled.
pub fn joints(anchor_color: Color, separation_color: Color) -> Self {
pub fn joints(anchor_color: Option<Color>, separation_color: Option<Color>) -> Self {
Self {
joint_anchor_color: Some(anchor_color),
joint_separation_color: Some(separation_color),
joint_anchor_color: anchor_color,
joint_separation_color: separation_color,
..Self::none()
}
}
Expand Down Expand Up @@ -160,6 +195,43 @@ impl PhysicsDebugConfig {
self
}

/// Sets the colors used for debug rendering joints.
pub fn with_joint_colors(anchor_color: Option<Color>, separation_color: Option<Color>) -> Self {
Self {
joint_anchor_color: anchor_color,
joint_separation_color: separation_color,
..Self::none()
}
}

/// Sets the colors used for debug rendering raycasts.
pub fn with_raycast_colors(
mut self,
ray: Option<Color>,
hit_point: Option<Color>,
hit_normal: Option<Color>,
) -> Self {
self.raycast_color = ray;
self.raycast_point_color = hit_point;
self.raycast_normal_color = hit_normal;
self
}

/// Sets the colors used for debug rendering shapecasts.
pub fn with_shapecast_colors(
mut self,
ray: Option<Color>,
shape: Option<Color>,
hit_point: Option<Color>,
hit_normal: Option<Color>,
) -> Self {
self.shapecast_color = ray;
self.shapecast_shape_color = shape;
self.shapecast_point_color = hit_point;
self.shapecast_normal_color = hit_normal;
self
}

/// Sets the visibility of the entity's visual mesh.
pub fn with_mesh_visibility(mut self, is_visible: bool) -> Self {
self.hide_meshes = !is_visible;
Expand Down Expand Up @@ -196,6 +268,23 @@ impl PhysicsDebugConfig {
self.joint_separation_color = None;
self
}

/// Disables raycast debug rendering.
pub fn without_raycasts(mut self) -> Self {
self.raycast_color = None;
self.raycast_point_color = None;
self.raycast_normal_color = None;
self
}

/// Disables shapecast debug rendering.
pub fn without_shapecasts(mut self) -> Self {
self.shapecast_color = None;
self.shapecast_shape_color = None;
self.shapecast_point_color = None;
self.shapecast_normal_color = None;
self
}
}

/// A component for the debug render configuration of an entity.
Expand Down
68 changes: 68 additions & 0 deletions src/plugins/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use bevy::{ecs::query::Has, prelude::*};
/// - Use different colors for [sleeping](Sleeping) bodies
/// - [Contacts]
/// - [Joints](joints)
/// - [`RayCaster`]
/// - [`ShapeCaster`]
/// - Changing the visibility of entities to only show debug rendering
///
/// By default, only axes, colliders and joints are debug rendered. You can use the [`PhysicsDebugConfig`]
Expand Down Expand Up @@ -72,6 +74,8 @@ impl Plugin for PhysicsDebugPlugin {
debug_render_joints::<DistanceJoint>,
debug_render_joints::<RevoluteJoint>,
debug_render_joints::<SphericalJoint>,
debug_render_raycasts,
debug_render_shapecasts,
change_mesh_visibility,
)
.after(PhysicsSet::StepSimulation)
Expand Down Expand Up @@ -307,6 +311,70 @@ fn debug_render_joints<T: Joint>(
}
}

fn debug_render_raycasts(
query: Query<(&RayCaster, &RayHits)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for (ray, hits) in &query {
let ray_color = config
.raycast_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));
let point_color = config
.raycast_point_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));
let normal_color = config
.raycast_normal_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));

debug_renderer.draw_raycast(
ray.global_origin(),
ray.global_direction(),
// f32::MAX renders nothing, but this number seems to be fine :P
ray.max_time_of_impact.min(1_000_000_000_000_000_000.0),
hits.as_slice(),
ray_color,
point_color,
normal_color,
);
}
}

fn debug_render_shapecasts(
query: Query<(&ShapeCaster, &ShapeHits)>,
mut debug_renderer: PhysicsDebugRenderer,
config: Res<PhysicsDebugConfig>,
) {
for (shape_caster, hits) in &query {
let ray_color = config
.shapecast_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));
let shape_color = config
.shapecast_shape_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));
let point_color = config
.shapecast_point_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));
let normal_color = config
.shapecast_normal_color
.unwrap_or(Color::rgba(0.0, 0.0, 0.0, 0.0));

debug_renderer.draw_shapecast(
&shape_caster.shape,
shape_caster.global_origin(),
shape_caster.global_shape_rotation(),
shape_caster.global_direction(),
// f32::MAX renders nothing, but this number seems to be fine :P
shape_caster.max_time_of_impact.min(1_000_000_000_000_000.0),
hits.as_slice(),
ray_color,
shape_color,
point_color,
normal_color,
);
}
}

type MeshVisibilityQueryFilter = (
Or<(With<RigidBody>, With<Collider>)>,
Or<(Changed<DebugRender>, Without<DebugRender>)>,
Expand Down
Loading

0 comments on commit b9bad8d

Please sign in to comment.