From 57bd2aa47dc0f186b9a6d193fa64995b3c731c6f Mon Sep 17 00:00:00 2001 From: Zicklag Date: Thu, 11 Aug 2022 19:19:34 -0500 Subject: [PATCH 1/2] Toggle Inspector & Collision With Function Keys It's a little annoying to have to show the debug window and then click the checkbox, so this makes it easier. --- src/ui/debug_tools.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ui/debug_tools.rs b/src/ui/debug_tools.rs index 88fa9d87..84466de7 100644 --- a/src/ui/debug_tools.rs +++ b/src/ui/debug_tools.rs @@ -17,11 +17,20 @@ pub fn debug_tools_window( ) { let ctx = egui_context.ctx_mut(); - // Toggle window visibility + // Toggle debug window visibility if input.just_pressed(KeyCode::F12) { *visible = !*visible; } + // Shortcut to toggle collision shapes without having to use the menu + if input.just_pressed(KeyCode::F10) { + rapier_debug.enabled = !rapier_debug.enabled; + } + // Shortcut to toggle the inspector without having to use the menu + if input.just_pressed(KeyCode::F9) { + inspector.enabled = !inspector.enabled; + } + // Display debug tool window egui::Window::new(localization.get("debug-tools")) // ID is needed because title comes from localizaition which can change From 152ff635a4b1f7762846dde8fa3db423b89eb0e1 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Thu, 11 Aug 2022 19:24:45 -0500 Subject: [PATCH 2/2] Implement Custom Rapier Debug Renderer Implements custom debug renderer for Rapier using Egui. This is to avoid an issue we have using Rapier's built-in debug renderer, which requires entities to be kept alive that we can't filter based on components. Fixes #132 --- src/main.rs | 23 ++++---- src/ui.rs | 3 +- src/ui/debug_tools.rs | 119 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 252b382b..f9fb5b05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,7 +112,7 @@ fn main() { .add_plugin(PlatformPlugin) .add_plugin(LocalizationPlugin) .add_plugin(LoadingPlugin) - .add_plugin(RapierPhysicsPlugin::::pixels_per_meter(100.0)) + .add_plugin(RapierPhysicsPlugin::::default()) .add_plugin(InputManagerPlugin::::default()) .add_plugin(InputManagerPlugin::::default()) .add_plugin(AttackPlugin) @@ -139,17 +139,16 @@ fn main() { // Add debug plugins if enabled if engine_config.debug_tools { - app.add_plugin(RapierDebugRenderPlugin::default()) - .insert_resource(DebugRenderContext { - enabled: false, - ..default() - }) - .add_plugin(InspectableRapierPlugin) - .insert_resource(WorldInspectorParams { - enabled: false, - ..default() - }) - .add_plugin(WorldInspectorPlugin::new()); + app.insert_resource(DebugRenderContext { + enabled: false, + ..default() + }) + .add_plugin(InspectableRapierPlugin) + .insert_resource(WorldInspectorParams { + enabled: false, + ..default() + }) + .add_plugin(WorldInspectorPlugin::new()); } // Register assets and loaders diff --git a/src/ui.rs b/src/ui.rs index a77740b1..7dcd8c14 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -53,7 +53,8 @@ impl Plugin for UIPlugin { ); if ENGINE_CONFIG.debug_tools { - app.add_system(debug_tools::debug_tools_window); + app.add_system(debug_tools::debug_tools_window) + .add_system_to_stage(CoreStage::Last, debug_tools::rapier_debug_render); } } } diff --git a/src/ui/debug_tools.rs b/src/ui/debug_tools.rs index 84466de7..7c125cd6 100644 --- a/src/ui/debug_tools.rs +++ b/src/ui/debug_tools.rs @@ -2,7 +2,14 @@ use bevy::prelude::*; use bevy_egui::*; use bevy_fluent::Localization; use bevy_inspector_egui::WorldInspectorParams; -use bevy_rapier2d::prelude::DebugRenderContext; +use bevy_rapier2d::{ + plugin::RapierContext, + prelude::{ColliderDebugColor, DebugRenderContext}, + rapier::{ + math::{Point, Real}, + prelude::{DebugRenderBackend, DebugRenderObject}, + }, +}; use crate::localization::LocalizationExt; @@ -40,13 +47,119 @@ pub fn debug_tools_window( // Show collision shapes ui.checkbox( &mut rapier_debug.enabled, - localization.get("show-collision-shapes"), + format!("{} ( F10 )", localization.get("show-collision-shapes")), ); // Show world inspector ui.checkbox( &mut inspector.enabled, - localization.get("show-world-inspector"), + format!("{} ( F9 )", localization.get("show-world-inspector")), ); }); } + +/// Renders the rapier debug display +pub fn rapier_debug_render( + rapier_context: Res, + mut egui_context: ResMut, + mut rapier_debug: ResMut, + camera: Query<(&Camera, &GlobalTransform)>, + custom_colors: Query<&ColliderDebugColor>, +) { + if !rapier_debug.enabled { + return; + } + let (camera, camera_transform) = camera.single(); + + // Create a frameless panel to allow us to render over anywhere on the screen + egui::CentralPanel::default() + .frame(egui::Frame::none()) + .show(egui_context.ctx_mut(), |ui| { + let painter = ui.painter(); + + let mut backend = RapierEguiRenderBackend { + egui_size: ui.available_size(), + camera, + camera_transform, + custom_colors, + context: &rapier_context, + painter, + }; + + rapier_debug.pipeline.render( + &mut backend, + &rapier_context.bodies, + &rapier_context.colliders, + &rapier_context.impulse_joints, + &rapier_context.multibody_joints, + &rapier_context.narrow_phase, + ); + }); +} + +/// Rapier debug rendering backend that uses Egui to draw the lines +struct RapierEguiRenderBackend<'world, 'state, 'a, 'b, 'c> { + egui_size: egui::Vec2, + custom_colors: Query<'world, 'state, &'a ColliderDebugColor>, + context: &'b RapierContext, + camera: &'c Camera, + camera_transform: &'c GlobalTransform, + painter: &'c egui::Painter, +} + +impl<'world, 'state, 'a, 'b, 'c> RapierEguiRenderBackend<'world, 'state, 'a, 'b, 'c> { + /// Helper to grab the objects custom collider color if it exists + fn object_color(&self, object: DebugRenderObject, default: [f32; 4]) -> egui::Color32 { + 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, + }; + + let color = color.map(|co| co.as_hsla_f32()).unwrap_or(default); + + egui::Rgba::from_rgba_premultiplied(color[0], color[1], color[2], color[3]).into() + } +} + +impl<'world, 'state, 'a, 'b, 'c> DebugRenderBackend + for RapierEguiRenderBackend<'world, 'state, 'a, 'b, 'c> +{ + /// Draw a debug line + fn draw_line( + &mut self, + object: DebugRenderObject, + a: Point, + b: Point, + color: [f32; 4], + ) { + // Convert world coordinates to normalized device coordinates + let a = self + .camera + .world_to_ndc(self.camera_transform, Vec3::new(a[0], a[1], 0.0)); + let b = self + .camera + .world_to_ndc(self.camera_transform, Vec3::new(b[0], b[1], 0.0)); + + if let (Some(a), Some(b)) = (a, b) { + // Invert y and convert to egui vec2 + let a = egui::Vec2::new(a.x, -a.y); + let b = egui::Vec2::new(b.x, -b.y); + + // Map NDC coordinates to egui points + let half_size = self.egui_size / 2.0; + let a = a * half_size + half_size; + let b = b * half_size + half_size; + + // Paint the line + self.painter.line_segment( + [a.to_pos2(), b.to_pos2()], + (1.0, self.object_color(object, color)), + ) + } + } +}