use crate::plugin::RapierContext;
use crate::render::lines::DebugLinesConfig;
use bevy::prelude::*;
use lines::DebugLines;
use rapier::math::{Point, Real};
use rapier::pipeline::{DebugRenderBackend, DebugRenderObject, DebugRenderPipeline};
pub use rapier::pipeline::{DebugRenderMode, DebugRenderStyle};
use std::fmt::Debug;
mod lines;
#[derive(Copy, Clone, Component, PartialEq, Debug)]
pub struct ColliderDebugColor(pub Color);
pub struct RapierDebugRenderPlugin {
pub always_on_top: bool,
pub enabled: bool,
pub style: DebugRenderStyle,
pub mode: DebugRenderMode,
}
#[allow(clippy::derivable_impls)] impl Default for RapierDebugRenderPlugin {
#[cfg(feature = "dim2")]
fn default() -> Self {
Self {
enabled: true,
always_on_top: true,
style: DebugRenderStyle {
rigid_body_axes_length: 20.0,
..Default::default()
},
mode: DebugRenderMode::default(),
}
}
#[cfg(feature = "dim3")]
fn default() -> Self {
Self {
enabled: true,
always_on_top: false,
style: DebugRenderStyle::default(),
mode: DebugRenderMode::default(),
}
}
}
impl RapierDebugRenderPlugin {
pub fn always_on_top(mut self) -> Self {
self.always_on_top = true;
self
}
pub fn disabled(mut self) -> Self {
self.enabled = false;
self
}
}
#[derive(Resource)]
pub struct DebugRenderContext {
pub enabled: bool,
pub pipeline: DebugRenderPipeline,
pub always_on_top: bool,
}
impl Default for DebugRenderContext {
fn default() -> Self {
Self {
enabled: true,
pipeline: DebugRenderPipeline::default(),
always_on_top: true,
}
}
}
impl Plugin for RapierDebugRenderPlugin {
fn build(&self, app: &mut App) {
app.add_plugin(lines::DebugLinesPlugin::always_on_top(self.always_on_top))
.insert_resource(DebugRenderContext {
enabled: self.enabled,
pipeline: DebugRenderPipeline::new(self.style, self.mode),
always_on_top: self.always_on_top,
})
.add_system_to_stage(
CoreStage::PostUpdate,
debug_render_scene.before("draw_lines"),
);
}
}
struct BevyLinesRenderBackend<'world, 'state, 'a, 'b, 'c> {
physics_scale: f32,
custom_colors: Query<'world, 'state, &'a ColliderDebugColor>,
context: &'b RapierContext,
lines: &'c mut DebugLines,
}
impl<'world, 'state, 'a, 'b, 'c> BevyLinesRenderBackend<'world, 'state, 'a, 'b, 'c> {
fn object_color(&self, object: DebugRenderObject, default: [f32; 4]) -> [f32; 4] {
let color = match object {
DebugRenderObject::Collider(h, ..) => self.context.colliders.get(h).and_then(|co| {
self.custom_colors
.get(Entity::from_bits(co.user_data as u64))
.map(|co| co.0)
.ok()
}),
_ => None,
};
color.map(|co| co.as_hsla_f32()).unwrap_or(default)
}
}
impl<'world, 'state, 'a, 'b, 'c> DebugRenderBackend
for BevyLinesRenderBackend<'world, 'state, 'a, 'b, 'c>
{
#[cfg(feature = "dim2")]
fn draw_line(
&mut self,
object: DebugRenderObject,
a: Point<Real>,
b: Point<Real>,
color: [f32; 4],
) {
let scale = self.physics_scale;
let color = self.object_color(object, color);
self.lines.line_colored(
[a.x * scale, a.y * scale, 0.0].into(),
[b.x * scale, b.y * scale, 0.0].into(),
0.0,
Color::hsla(color[0], color[1], color[2], color[3]),
)
}
#[cfg(feature = "dim3")]
fn draw_line(
&mut self,
object: DebugRenderObject,
a: Point<Real>,
b: Point<Real>,
color: [f32; 4],
) {
let scale = self.physics_scale;
let color = self.object_color(object, color);
self.lines.line_colored(
[a.x * scale, a.y * scale, a.z * scale].into(),
[b.x * scale, b.y * scale, b.z * scale].into(),
0.0,
Color::hsla(color[0], color[1], color[2], color[3]),
)
}
}
fn debug_render_scene(
rapier_context: Res<RapierContext>,
mut render_context: ResMut<DebugRenderContext>,
lines_config: ResMut<DebugLinesConfig>,
mut lines: ResMut<DebugLines>,
custom_colors: Query<&ColliderDebugColor>,
) {
if !render_context.enabled {
return;
}
*lines_config.always_on_top.write().unwrap() = render_context.always_on_top;
let mut backend = BevyLinesRenderBackend {
physics_scale: rapier_context.physics_scale,
custom_colors,
context: &rapier_context,
lines: &mut lines,
};
let unscaled_style = render_context.pipeline.style;
render_context.pipeline.style.rigid_body_axes_length /= rapier_context.physics_scale;
render_context.pipeline.render(
&mut backend,
&rapier_context.bodies,
&rapier_context.colliders,
&rapier_context.impulse_joints,
&rapier_context.multibody_joints,
&rapier_context.narrow_phase,
);
render_context.pipeline.style = unscaled_style;
}