From 73692b710e5814bd519b4e30fec10f14b97d32e8 Mon Sep 17 00:00:00 2001 From: reo Date: Mon, 15 Dec 2025 15:53:54 +0300 Subject: [PATCH] Add debug wireframe rendering support --- app/src/prelude.rs | 3 + assets/shaders/debug_wireframe.frag | 9 +++ assets/shaders/debug_wireframe.vert | 14 ++++ game/src/main.rs | 4 +- game/src/systems/mod.rs | 2 + game/src/systems/physics_debug.rs | 35 +++++++++ glium_platform/src/platform.rs | 14 +++- glium_platform/src/render/debug_wireframe.rs | 80 ++++++++++++++++++++ glium_platform/src/render/mod.rs | 2 + glium_platform/src/system.rs | 2 + platform/src/context.rs | 61 ++++++++++++++- platform/src/lib.rs | 2 +- 12 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 assets/shaders/debug_wireframe.frag create mode 100644 assets/shaders/debug_wireframe.vert create mode 100644 game/src/systems/physics_debug.rs create mode 100644 glium_platform/src/render/debug_wireframe.rs diff --git a/app/src/prelude.rs b/app/src/prelude.rs index 1171d0f..fc9cdc1 100644 --- a/app/src/prelude.rs +++ b/app/src/prelude.rs @@ -11,6 +11,9 @@ pub use raidillon_platform::{ Camera, PlatformContext, TimeContext, + DebugWireframes, + DebugWireframesRef, + DebugWireframeVertex, settings::{Settings, WindowMode}, }; diff --git a/assets/shaders/debug_wireframe.frag b/assets/shaders/debug_wireframe.frag new file mode 100644 index 0000000..96d53b9 --- /dev/null +++ b/assets/shaders/debug_wireframe.frag @@ -0,0 +1,9 @@ +#version 330 core + +in vec4 v_color; + +out vec4 frag_color; + +void main() { + frag_color = v_color; +} diff --git a/assets/shaders/debug_wireframe.vert b/assets/shaders/debug_wireframe.vert new file mode 100644 index 0000000..7a53438 --- /dev/null +++ b/assets/shaders/debug_wireframe.vert @@ -0,0 +1,14 @@ +#version 330 core + +in vec3 position; +in vec4 color; + +uniform mat4 view; +uniform mat4 projection; + +out vec4 v_color; + +void main() { + v_color = color; + gl_Position = projection * view * vec4(position, 1.0); +} diff --git a/game/src/main.rs b/game/src/main.rs index 0f84666..3e1f318 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -8,7 +8,8 @@ use winit::event::{Event, WindowEvent}; use systems::debug_camera::FPSDebugCameraSystem; use crate::systems::common::should_draw_menu; use crate::systems::{ - DisplaySettings, KeybindsSystem, KinematicCharacterController, MenuSystem, PhysicsSystem + DisplaySettings, KeybindsSystem, KinematicCharacterController, MenuSystem, PhysicsSystem, + PhysicsDebugSystem, }; const TEST_GLTF: &str = "sphere.glb"; @@ -125,6 +126,7 @@ impl System for MainSystem { fn main() { raidillon_app::App::new() .add_system::() + .add_system::() .add_system::() .add_system::() .add_system::() diff --git a/game/src/systems/mod.rs b/game/src/systems/mod.rs index bcf77ca..08c54ab 100644 --- a/game/src/systems/mod.rs +++ b/game/src/systems/mod.rs @@ -1,4 +1,5 @@ mod physics; +mod physics_debug; mod kinematic_character_controller; mod keybinds; mod menu; @@ -7,6 +8,7 @@ pub mod common; mod display_settings; pub use physics::PhysicsSystem; +pub use physics_debug::PhysicsDebugSystem; pub use kinematic_character_controller::KinematicCharacterController; pub use keybinds::KeybindsSystem; pub use menu::MenuSystem; diff --git a/game/src/systems/physics_debug.rs b/game/src/systems/physics_debug.rs new file mode 100644 index 0000000..33364d2 --- /dev/null +++ b/game/src/systems/physics_debug.rs @@ -0,0 +1,35 @@ +use raidillon_app::prelude::*; + +/// renders aabb wireframes for all physics colliders +#[derive(Default)] +pub struct PhysicsDebugSystem; + +impl System for PhysicsDebugSystem { + fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) { + let pctx = res.get::().expect("PlatformContext missing").clone(); + + let mut debug_wireframes = pctx.debug_wireframes.borrow_mut(); + if !debug_wireframes.enabled { + return; + } + + let physics = match scene.resources.get::() { + Some(p) => p, + None => return, + }; + + let color = [1.0, 0.0, 0.0, 1.0]; + + for (_, collider) in physics.collider_set.iter() { + let aabb = collider.compute_aabb(); + let min = aabb.mins; + let max = aabb.maxs; + + debug_wireframes.add_box( + [min.x, min.y, min.z], + [max.x, max.y, max.z], + color, + ); + } + } +} diff --git a/glium_platform/src/platform.rs b/glium_platform/src/platform.rs index b67ef77..8f00d92 100644 --- a/glium_platform/src/platform.rs +++ b/glium_platform/src/platform.rs @@ -1,7 +1,7 @@ use std::cell::{RefCell, Cell}; use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; -use raidillon_platform::{Platform, PlatformContext, TimeContext}; +use raidillon_platform::{Platform, PlatformContext, TimeContext, DebugWireframes, DebugWireframesRef}; use glium::backend::glutin::Display; use glium::backend::glutin::SimpleWindowBuilder; use glium::glutin::surface::WindowSurface; @@ -14,7 +14,7 @@ use raidillon_assets::ModelManagerRef; use raidillon_core::engine::EngineTrait; use raidillon_core::time; use raidillon_core::time::Time; -use crate::render::{BasicMeshRenderingSystem, EguiRenderer, SkyboxRenderingSystem}; +use crate::render::{BasicMeshRenderingSystem, DebugWireframeRenderingSystem, EguiRenderer, SkyboxRenderingSystem}; use crate::GliumAssetManager; use glam::Vec3; use winit::event::DeviceEvent::MouseMotion; @@ -31,7 +31,7 @@ pub struct GliumPlatform> { time: time::Time, egui_queue: Rc>, settings: Arc>, - /// Used for [`raidillon_platform::context::PlatformContext::should_egui_receive_input_events`] + debug_wireframes: DebugWireframesRef, should_egui_receive_input_events: Rc>, } @@ -57,6 +57,7 @@ impl> Platform for GliumPlatfor // Install rendering systems in order rendering_system_manager.add::(&display, window.clone(), &event_loop); rendering_system_manager.add::(&display, window.clone(), &event_loop); + rendering_system_manager.add::(&display, window.clone(), &event_loop); rendering_system_manager.add::(&display, window.clone(), &event_loop); let egui_queue = Rc::new(RefCell::new(EguiQueue::new())); @@ -66,6 +67,7 @@ impl> Platform for GliumPlatfor Settings::load_or_default(default_config_path()).unwrap() ) ); + let debug_wireframes = Rc::new(RefCell::new(DebugWireframes::new())); let should_egui_receive_input_events = Rc::new(Cell::new(false)); Self { @@ -78,6 +80,7 @@ impl> Platform for GliumPlatfor time, egui_queue, settings, + debug_wireframes, should_egui_receive_input_events, } } @@ -96,6 +99,7 @@ impl> Platform for GliumPlatfor window: self.window.clone(), egui_queue: self.egui_queue.clone(), settings: self.settings.clone(), + debug_wireframes: self.debug_wireframes.clone(), should_egui_receive_input_events: self.should_egui_receive_input_events.clone(), }; self.engine.initialize(ctx.clone()); @@ -140,6 +144,7 @@ impl> Platform for GliumPlatfor asset_manager: self.asset_manager.clone(), window: self.window.clone(), egui_queue: self.egui_queue.clone(), + debug_wireframes: self.debug_wireframes.clone(), env_light_dir: Vec3::new(0.0, -1.0, 0.0), should_egui_receive_input_events: self.should_egui_receive_input_events.clone(), }; @@ -149,6 +154,9 @@ impl> Platform for GliumPlatfor .values_mut() .for_each(|system| system.render(&mut context)); + // clear debug wireframes after rendering + self.debug_wireframes.borrow_mut().clear(); + target.finish().unwrap(); } _ => {}, diff --git a/glium_platform/src/render/debug_wireframe.rs b/glium_platform/src/render/debug_wireframe.rs new file mode 100644 index 0000000..2f259e9 --- /dev/null +++ b/glium_platform/src/render/debug_wireframe.rs @@ -0,0 +1,80 @@ +use std::sync::{Arc, Mutex}; +use glium::{Display, Program, Surface, VertexBuffer, implement_vertex}; +use glium::glutin::surface::WindowSurface; +use glium::index::PrimitiveType; +use glium::uniform; +use winit::event_loop::EventLoop; +use raidillon_assets::include_shader; +use crate::system::RenderingContext; +use crate::RenderingSystem; +pub use raidillon_platform::Camera; + +#[derive(Copy, Clone)] +struct DebugVertex { + position: [f32; 3], + color: [f32; 4], +} + +implement_vertex!(DebugVertex, position, color); + +/// renders debug wireframes from the shared buffer +pub struct DebugWireframeRenderingSystem { + program: Program, + params: glium::DrawParameters<'static>, +} + +impl RenderingSystem for DebugWireframeRenderingSystem { + fn initialize(display: &Display, _window: Arc>, _event_loop: &EventLoop<()>) -> Self { + const VERT_SRC: &str = include_shader!("debug_wireframe.vert"); + const FRAG_SRC: &str = include_shader!("debug_wireframe.frag"); + + let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap(); + + let params = glium::DrawParameters { + depth: glium::Depth { + test: glium::draw_parameters::DepthTest::IfLess, + write: false, + ..Default::default() + }, + line_width: Some(1.0), + ..Default::default() + }; + + Self { program, params } + } + + fn render(&mut self, ctx: &mut RenderingContext) { + let debug_wireframes = ctx.debug_wireframes.borrow(); + + if !debug_wireframes.enabled || debug_wireframes.vertices.is_empty() { + return; + } + + let cam = match ctx.scene.world.query::<&Camera>().iter().next() { + Some((_, cam)) => *cam, + None => return, + }; + + let vertices: Vec = debug_wireframes.vertices.iter() + .map(|v| DebugVertex { position: v.position, color: v.color }) + .collect(); + + let vbuf = match VertexBuffer::new(ctx.display, &vertices) { + Ok(vb) => vb, + Err(_) => return, + }; + + let uniforms = uniform! { + view: cam.view().to_cols_array_2d(), + projection: cam.projection().to_cols_array_2d(), + }; + + ctx.target.draw( + &vbuf, + glium::index::NoIndices(PrimitiveType::LinesList), + &self.program, + &uniforms, + &self.params, + ).ok(); + } +} diff --git a/glium_platform/src/render/mod.rs b/glium_platform/src/render/mod.rs index fdfa1ea..22aa047 100644 --- a/glium_platform/src/render/mod.rs +++ b/glium_platform/src/render/mod.rs @@ -1,7 +1,9 @@ mod basic; mod skybox; mod egui; +mod debug_wireframe; pub use basic::BasicMeshRenderingSystem; pub use skybox::SkyboxRenderingSystem; pub use egui::EguiRenderer; +pub use debug_wireframe::DebugWireframeRenderingSystem; diff --git a/glium_platform/src/system.rs b/glium_platform/src/system.rs index 92fd553..cf5f945 100644 --- a/glium_platform/src/system.rs +++ b/glium_platform/src/system.rs @@ -8,6 +8,7 @@ use glium::glutin::surface::WindowSurface; use raidillon_assets::ModelManagerRef; use raidillon_core::{define_typemap, EguiQueue}; use raidillon_core::scene::Scene; +use raidillon_platform::DebugWireframesRef; use glam::Vec3; use winit::event_loop::EventLoop; use std::cell::Cell; @@ -19,6 +20,7 @@ pub struct RenderingContext<'a> { pub display: &'a Display, pub asset_manager: ModelManagerRef, pub egui_queue: Rc>, + pub debug_wireframes: DebugWireframesRef, pub env_light_dir: Vec3, pub should_egui_receive_input_events: Rc> } diff --git a/platform/src/context.rs b/platform/src/context.rs index 0990527..31bd6cb 100644 --- a/platform/src/context.rs +++ b/platform/src/context.rs @@ -6,6 +6,62 @@ use raidillon_assets::ModelManagerRef; use raidillon_core::EguiQueue; use crate::settings::Settings; +/// a single debug wireframe vertex with position and color +#[derive(Clone, Copy)] +pub struct DebugWireframeVertex { + pub position: [f32; 3], + pub color: [f32; 4], +} + +/// shared buffer for debug wireframe rendering +#[derive(Clone, Default)] +pub struct DebugWireframes { + pub vertices: Vec, + pub enabled: bool, +} + +impl DebugWireframes { + pub fn new() -> Self { + Self { vertices: Vec::new(), enabled: true } + } + + pub fn clear(&mut self) { + self.vertices.clear(); + } + + /// add a single line segment + pub fn add_line(&mut self, start: [f32; 3], end: [f32; 3], color: [f32; 4]) { + self.vertices.push(DebugWireframeVertex { position: start, color }); + self.vertices.push(DebugWireframeVertex { position: end, color }); + } + + /// add a wireframe box from min/max corners + pub fn add_box(&mut self, min: [f32; 3], max: [f32; 3], color: [f32; 4]) { + let [x0, y0, z0] = min; + let [x1, y1, z1] = max; + + // bottom face edges + self.add_line([x0, y0, z0], [x1, y0, z0], color); + self.add_line([x1, y0, z0], [x1, y0, z1], color); + self.add_line([x1, y0, z1], [x0, y0, z1], color); + self.add_line([x0, y0, z1], [x0, y0, z0], color); + + // top face edges + self.add_line([x0, y1, z0], [x1, y1, z0], color); + self.add_line([x1, y1, z0], [x1, y1, z1], color); + self.add_line([x1, y1, z1], [x0, y1, z1], color); + self.add_line([x0, y1, z1], [x0, y1, z0], color); + + // vertical edges + self.add_line([x0, y0, z0], [x0, y1, z0], color); + self.add_line([x1, y0, z0], [x1, y1, z0], color); + self.add_line([x1, y0, z1], [x1, y1, z1], color); + self.add_line([x0, y0, z1], [x0, y1, z1], color); + } +} + +pub type DebugWireframesRef = Rc>; + #[derive(Clone)] pub struct PlatformContext { pub current_event: Event<()>, @@ -16,8 +72,9 @@ pub struct PlatformContext { pub window: Arc>, pub egui_queue: Rc>, pub settings: Arc>, - /// Sets whether or not egui will receive input events. - /// Added to prevent the mouse state conflict between the engine and egui. + /// shared debug wireframe buffer + pub debug_wireframes: DebugWireframesRef, + /// sets whether or not egui will receive input events pub should_egui_receive_input_events: Rc>, } diff --git a/platform/src/lib.rs b/platform/src/lib.rs index 94c467b..ef88e08 100644 --- a/platform/src/lib.rs +++ b/platform/src/lib.rs @@ -6,4 +6,4 @@ pub mod settings; pub use platform::Platform; pub use camera::Camera; -pub use context::{PlatformContext, TimeContext}; +pub use context::{PlatformContext, TimeContext, DebugWireframes, DebugWireframesRef, DebugWireframeVertex};