Add debug wireframe rendering support

This commit is contained in:
reo 2025-12-15 15:53:54 +03:00
parent 8041c7e01d
commit 73692b710e
12 changed files with 221 additions and 7 deletions

View file

@ -11,6 +11,9 @@ pub use raidillon_platform::{
Camera,
PlatformContext,
TimeContext,
DebugWireframes,
DebugWireframesRef,
DebugWireframeVertex,
settings::{Settings, WindowMode},
};

View file

@ -0,0 +1,9 @@
#version 330 core
in vec4 v_color;
out vec4 frag_color;
void main() {
frag_color = v_color;
}

View file

@ -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);
}

View file

@ -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::<PhysicsSystem>()
.add_system::<PhysicsDebugSystem>()
.add_system::<KeybindsSystem>()
.add_system::<KinematicCharacterController>()
.add_system::<FPSDebugCameraSystem>()

View file

@ -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;

View file

@ -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::<PlatformContext>().expect("PlatformContext missing").clone();
let mut debug_wireframes = pctx.debug_wireframes.borrow_mut();
if !debug_wireframes.enabled {
return;
}
let physics = match scene.resources.get::<Physics>() {
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,
);
}
}
}

View file

@ -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<E: EngineTrait<PlatformCtx = PlatformContext>> {
time: time::Time,
egui_queue: Rc<RefCell<EguiQueue>>,
settings: Arc<RwLock<Settings>>,
/// Used for [`raidillon_platform::context::PlatformContext::should_egui_receive_input_events`]
debug_wireframes: DebugWireframesRef,
should_egui_receive_input_events: Rc<Cell<bool>>,
}
@ -57,6 +57,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
// Install rendering systems in order
rendering_system_manager.add::<SkyboxRenderingSystem>(&display, window.clone(), &event_loop);
rendering_system_manager.add::<BasicMeshRenderingSystem>(&display, window.clone(), &event_loop);
rendering_system_manager.add::<DebugWireframeRenderingSystem>(&display, window.clone(), &event_loop);
rendering_system_manager.add::<EguiRenderer>(&display, window.clone(), &event_loop);
let egui_queue = Rc::new(RefCell::new(EguiQueue::new()));
@ -66,6 +67,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> 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<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
time,
egui_queue,
settings,
debug_wireframes,
should_egui_receive_input_events,
}
}
@ -96,6 +99,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> 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<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> 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<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> 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();
}
_ => {},

View file

@ -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<WindowSurface>, _window: Arc<Mutex<glium::winit::window::Window>>, _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<DebugVertex> = 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();
}
}

View file

@ -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;

View file

@ -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<WindowSurface>,
pub asset_manager: ModelManagerRef,
pub egui_queue: Rc<RefCell<EguiQueue>>,
pub debug_wireframes: DebugWireframesRef,
pub env_light_dir: Vec3,
pub should_egui_receive_input_events: Rc<Cell<bool>>
}

View file

@ -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<DebugWireframeVertex>,
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<RefCell<DebugWireframes>>;
#[derive(Clone)]
pub struct PlatformContext {
pub current_event: Event<()>,
@ -16,8 +72,9 @@ pub struct PlatformContext {
pub window: Arc<Mutex<winit::window::Window>>,
pub egui_queue: Rc<RefCell<EguiQueue>>,
pub settings: Arc<RwLock<Settings>>,
/// 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<Cell<bool>>,
}

View file

@ -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};