Merge branch '2025-11-16-egui'

This commit is contained in:
reo 2025-11-16 18:41:41 +03:00
commit 1f03afdab5
22 changed files with 816 additions and 323 deletions

802
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -8,4 +8,4 @@ hecs = "0.10.5"
indexmap = "2.10.0"
raidillon_assets = { path = "../asset" }
winit = "0.30.12"
imgui = "0.12.0"
egui = "0.33.2"

View file

@ -1,50 +0,0 @@
pub enum UICommand {
Text(String),
Separator,
SliderF32 { label: String, min: f32, max: f32, value: std::rc::Rc<std::cell::RefCell<f32>> },
}
pub struct DebugUIBuffer {
cmds: Vec<UICommand>,
}
impl DebugUIBuffer {
pub fn new() -> DebugUIBuffer {
DebugUIBuffer { cmds: vec![] }
}
// Commands
pub fn text(&mut self, text: String) {
self.cmds.push(UICommand::Text(text));
}
pub fn separator(&mut self) {
self.cmds.push(UICommand::Separator);
}
pub fn slider_f32<T: Into<String>>(&mut self, label: T, min: f32, max: f32, value: std::rc::Rc<std::cell::RefCell<f32>>) {
self.cmds.push(UICommand::SliderF32 { label: label.into(), min, max, value });
}
// End of commands
pub fn write_buffer(&self, ui: &imgui::Ui) {
for cmd in &self.cmds {
match cmd {
UICommand::Text(s) => {
ui.text(s);
}
UICommand::Separator => {
ui.separator();
}
UICommand::SliderF32 { label, min, max, value } => {
let mut v = value.borrow_mut();
ui.slider_config(label.as_str(), *min, *max).build(&mut *v);
}
}
}
}
pub fn reset_buffer(&mut self) {
self.cmds = vec![];
}
}

22
core/src/egui_queue.rs Normal file
View file

@ -0,0 +1,22 @@
pub struct EguiQueue {
pub queue_vec: Vec<Box<dyn FnOnce(&egui::Context) + Send>>,
}
impl EguiQueue {
pub fn new() -> Self {
Self { queue_vec: Vec::new() }
}
pub fn queue(&mut self, func: impl FnOnce(&egui::Context) + Send + 'static) {
self.queue_vec.push(Box::new(func));
}
pub fn clear(&mut self) {
self.queue_vec.clear()
}
pub fn run(&mut self, ctx: &egui::Context) {
self.queue_vec.drain(..).for_each(|func| func(ctx));
self.clear();
}
}

View file

@ -1,6 +1,3 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::DebugUIBuffer;
use crate::scene::Scene;
pub trait EngineTrait {
@ -12,7 +9,4 @@ pub trait EngineTrait {
fn handle_event(&mut self, platform_context: Self::PlatformCtx);
fn current_scene_mut(&mut self) -> &mut Scene;
fn current_scene(&self) -> &Scene;
fn get_debug_ui_buffer(&self) -> &DebugUIBuffer;
fn reset_debug_ui_buffer(&mut self);
// fn scene_and_debug_ui_buffer_mut(&mut self) -> (&mut Self::Scene, &DebugUIBuffer);
}

View file

@ -1,7 +1,7 @@
pub mod engine;
pub mod debug_ui;
pub mod time;
pub mod utils;
pub mod scene;
mod egui_queue;
pub use debug_ui::*;
pub use egui_queue::EguiQueue;

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::{define_typemap, DebugUIBuffer};
use crate::{define_typemap};
pub struct Scene {
pub title: String,
@ -23,10 +23,7 @@ impl Scene {
s
}
pub fn load_default_resources(&mut self) {
let dbg = DebugUIBuffer::new();
self.resources.insert(dbg);
}
pub fn load_default_resources(&mut self) {}
}
impl Scene {}

View file

@ -11,4 +11,5 @@ raidillon_ecs = { path = "../ecs" }
winit = "0.30.12"
hecs = "0.10.5"
indexmap = "2.10.0"
glam = "0.30.8"
glam = "0.30.8"
egui = "0.33.2"

View file

@ -3,7 +3,7 @@ use std::rc::Rc;
use raidillon_core::scene::{Scene, SceneManager};
use crate::system::{SystemContext, SystemManager};
use raidillon_platform::PlatformContext;
use raidillon_core::{define_typemap, DebugUIBuffer};
use raidillon_core::{define_typemap};
use raidillon_core::engine::EngineTrait;
use crate::input::InputState;
use crate::resources::EngineResources;
@ -50,7 +50,6 @@ impl EngineTrait for Engine {
/// Update the engine
fn frame_update(&mut self, platform_context: PlatformContext) {
self.scene_manager.current_mut().resources.get_mut::<DebugUIBuffer>().unwrap().reset_buffer();
self.resources.insert(platform_context);
for system in self.system_manager.systems.values_mut() {
@ -86,17 +85,4 @@ impl EngineTrait for Engine {
fn current_scene(&self) -> &Scene {
self.scene_manager.current()
}
fn get_debug_ui_buffer(&self) -> &DebugUIBuffer {
self.scene_manager.current().resources.get::<DebugUIBuffer>().unwrap()
}
fn reset_debug_ui_buffer(&mut self) {
self.scene_manager.current_mut().resources.get_mut::<DebugUIBuffer>().unwrap().reset_buffer();
}
// fn scene_and_debug_ui_buffer_mut(&mut self) -> (&mut Scene, &DebugUIBuffer) {
// let (sm, dub) = (&mut self.scene_manager, self.scene_manager.current().resources.get::<DebugUIBuffer>().unwrap());
// (sm.current_mut(), dub)
// }
}

View file

@ -1,6 +1,5 @@
use indexmap::IndexMap;
use raidillon_core::scene::Scene;
use raidillon_core::DebugUIBuffer;
use raidillon_platform::PlatformContext;
use std::any::TypeId;
use std::cell::RefCell;
@ -11,7 +10,6 @@ use crate::resources::EngineResources;
pub struct SystemContext<'a> {
pub scene: &'a mut Scene,
pub platform_context: PlatformContext,
pub debug_ui_buffer: Rc<RefCell<DebugUIBuffer>>,
pub input_state: Rc<RefCell<InputState>>,
}

View file

@ -19,3 +19,4 @@ glam = "0.30.5"
winit = "0.30.12"
rapier3d = "0.30.1"
hecs = "0.10.5"
egui = "0.33.2"

View file

@ -16,7 +16,7 @@ use raidillon_glium::GliumPlatform;
use winit::event::{ElementState, Event, WindowEvent};
use winit::event::DeviceEvent::MouseMotion;
use winit::keyboard::{KeyCode, PhysicalKey};
use raidillon_core::DebugUIBuffer;
use raidillon_core::{EguiQueue};
use systems::debug_camera::FPSDebugCameraSystem;
use raidillon_glium::RenderingSystem;
use raidillon_physics::Physics;
@ -102,18 +102,30 @@ impl System for RenderingTestSystem {
}
fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) {
let pctx = res.get::<PlatformContext>().unwrap();
let input = res.get::<InputState>().unwrap();
let dbg_ui = scene.resources.get_mut::<DebugUIBuffer>().unwrap();
dbg_ui.text("Hello World!".to_owned());
dbg_ui.text(format!("Frame Delta: {}", pctx.time_ctx.frame_dt));
dbg_ui.text(format!("Fixed Delta: {}", pctx.time_ctx.fixed_dt));
dbg_ui.text(format!("FPS: {}", 1.0 / pctx.time_ctx.frame_dt));
let (
pctx,
input,
) = res.get_many_mut::<(
PlatformContext,
InputState,
)>().unwrap();
let mut egui_queue = pctx.egui_queue.borrow_mut();
let time_ctx = pctx.time_ctx.clone();
let mut character_pos = Vec3::ZERO;
for (_ent, (tr, ch_component)) in scene.world.query::<(&Transform, &CharacterBodyComponent)>().iter() {
dbg_ui.text(format!("Character POS: {}", tr.translation));
character_pos = tr.translation;
}
egui_queue.queue(move |egui_ctx| {
egui::Window::new("Debug").show(egui_ctx, |ui| {
ui.label("Hello World!");
ui.label(format!("Frame Delta: {}", time_ctx.frame_dt));
ui.label(format!("Fixed Delta: {}", time_ctx.fixed_dt));
ui.label(format!("FPS: {}", 1.0 / time_ctx.frame_dt));
ui.label(format!("Character POS: {}", character_pos));
});
});
}
}

View file

@ -1,5 +1,4 @@
use winit::keyboard::KeyCode;
use raidillon_core::DebugUIBuffer;
use raidillon_core::scene::Scene;
use raidillon_ecs::components::CameraMode;
use raidillon_engine::{EngineResources, InputState};
@ -24,16 +23,21 @@ impl System for KeybindsSystem {
}
fn frame_update(&mut self, res: &mut EngineResources, scene: &mut Scene) {
let dbg_ui = scene.resources.get_mut::<DebugUIBuffer>().unwrap();
dbg_ui.text("F5 to switch camera".to_owned());
let pctx = res.get_mut::<PlatformContext>().unwrap();
let mut q = scene.world.query::<(&Camera, &CameraMode)>();
let (cam_ent, (cam, cam_mode)) = q
.iter()
.next()
.unwrap();
dbg_ui.text(format!("Camera Mode: {:?}", cam_mode));
let cam_mode_str = format!("Camera Mode: {:?}", cam_mode);
pctx.egui_queue.borrow_mut().queue(move |egui_ctx| {
egui::Window::new("Camera").show(egui_ctx, |ui| {
ui.label("F5 to switch camera");
ui.label(cam_mode_str);
});
});
}
}

View file

@ -6,7 +6,7 @@ edition = "2024"
[dependencies]
anyhow = "1.0.98"
glam = "0.30.5"
glium = { version = "0.35.0", features = ["glutin_backend", "simple_window_builder"] }
glium = { version = "0.36.0", features = ["glutin_backend", "simple_window_builder"] }
gltf = { version = "1.4.1", features = ["import", "utils", "KHR_texture_transform"] }
raidillon_platform = { path = "../platform" }
raidillon_core = { path = "../core" }
@ -15,8 +15,7 @@ raidillon_ecs = { path = "../ecs" }
raidillon_engine = { path = "../engine" }
winit = "0.30.12"
indexmap = "2.10.0"
imgui = "0.12.0"
imgui-winit-support = "0.13.0"
imgui-glium-renderer = "0.13.0"
exr = "1.73.0"
image = { version = "0.25.8", default-features = false, features = ["exr"] }
egui = "0.33.2"
egui_glium = { version = "0.31.1", git = "https://github.com/reo6/egui_glium.git" }

View file

@ -14,11 +14,11 @@ use raidillon_assets::ModelManagerRef;
use raidillon_core::engine::EngineTrait;
use raidillon_core::time;
use raidillon_core::time::Time;
use crate::render::debug_ui::ImguiBridge;
use crate::render::{BasicMeshRenderingSystem, SkyboxRenderingSystem};
use crate::render::{BasicMeshRenderingSystem, EguiRenderer, SkyboxRenderingSystem};
use crate::GliumAssetManager;
use glam::Vec3;
use winit::event::DeviceEvent::MouseMotion;
use raidillon_core::EguiQueue;
pub struct GliumPlatform<E: EngineTrait<PlatformCtx = PlatformContext>> {
event_loop: EventLoop<()>,
@ -28,6 +28,7 @@ pub struct GliumPlatform<E: EngineTrait<PlatformCtx = PlatformContext>> {
asset_manager: ModelManagerRef,
engine: E,
time: time::Time,
egui_queue: Rc<RefCell<EguiQueue>>,
}
impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatform<E> {
@ -50,9 +51,11 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
let window = Arc::new(Mutex::new(window));
// Install rendering systems in order
rendering_system_manager.add::<SkyboxRenderingSystem>(&display, window.clone());
rendering_system_manager.add::<BasicMeshRenderingSystem>(&display, window.clone());
rendering_system_manager.add::<ImguiBridge>(&display, window.clone());
rendering_system_manager.add::<SkyboxRenderingSystem>(&display, window.clone(), &event_loop);
rendering_system_manager.add::<BasicMeshRenderingSystem>(&display, window.clone(), &event_loop);
rendering_system_manager.add::<EguiRenderer>(&display, window.clone(), &event_loop);
let egui_queue = Rc::new(RefCell::new(EguiQueue::new()));
Self {
event_loop,
@ -62,6 +65,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
asset_manager,
engine,
time,
egui_queue,
}
}
@ -77,6 +81,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
frame_height: h as f32,
time_ctx: self.construct_time_ctx(),
window: self.window.clone(),
egui_queue: self.egui_queue.clone(),
};
self.engine.initialize(ctx.clone());
let _ = &self.event_loop.run(move |event, el| {
@ -98,16 +103,14 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
WindowEvent::RedrawRequested => {
let mut target = self.display.draw();
target.clear_color_and_depth((0.1, 0.1, 0.15, 1.0), 1.0);
let (scene, debug_ui_buffer) = (
self.engine.current_scene(),
self.engine.get_debug_ui_buffer(),
);
let scene = self.engine.current_scene();
let mut context = RenderingContext {
scene,
target: &mut target,
display: &self.display,
asset_manager: self.asset_manager.clone(),
window: self.window.clone(),
debug_ui_buffer,
egui_queue: self.egui_queue.clone(),
env_light_dir: Vec3::new(0.0, -1.0, 0.0),
};

View file

@ -9,6 +9,8 @@ use raidillon_assets::include_shader;
pub use raidillon_platform::Camera;
use glam::Vec3;
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
use winit::event::Event;
use winit::event_loop::EventLoop;
use raidillon_ecs::{Transform, ModelID};
use raidillon_ecs::components::ModelHandle;
use crate::model::Model;
@ -21,7 +23,7 @@ pub struct BasicMeshRenderingSystem {
}
impl RenderingSystem for BasicMeshRenderingSystem {
fn initialize(display: &Display<WindowSurface>, _window: Arc<Mutex<glium::winit::window::Window>>) -> Self {
fn initialize(display: &Display<WindowSurface>, _window: Arc<Mutex<glium::winit::window::Window>>, event_loop: &EventLoop<()>) -> Self {
const VERT_SRC: &str = include_shader!("gl_textured.vert");
const FRAG_SRC: &str = include_shader!("gl_textured.frag");

View file

@ -1,75 +0,0 @@
use std::sync::{Arc, Mutex};
use std::time::Instant;
use glium::Display;
use glium::glutin::surface::WindowSurface;
use imgui::{Context as ImguiContext};
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use imgui_glium_renderer::Renderer as ImguiGliumRenderer;
use winit::window::Window;
use winit::event::Event;
use glium::Frame;
use crate::RenderingSystem;
use crate::system::RenderingContext;
pub struct ImguiBridge {
imgui: ImguiContext,
platform: WinitPlatform,
renderer: ImguiGliumRenderer,
last_frame: Instant,
rendered_this_frame: bool,
}
impl RenderingSystem for ImguiBridge {
fn handle_event(&mut self, window: Arc<Mutex<Window>>, event: Event<()>) {
let window = window.lock().unwrap();
self.platform.handle_event(self.imgui.io_mut(), &*window, &event);
}
fn prepare_frame(&mut self, window: Arc<Mutex<Window>>) {
self.rendered_this_frame = false;
let now = Instant::now();
self.imgui.io_mut().update_delta_time(now - self.last_frame);
self.last_frame = now;
let window = window.lock().unwrap();
self.platform
.prepare_frame(self.imgui.io_mut(), &*window)
.expect("Failed to prepare frame");
}
fn render(&mut self, ctx: &mut RenderingContext) {
if self.rendered_this_frame { return; }
self.rendered_this_frame = true;
let ui = self.imgui.frame();
ctx.debug_ui_buffer.write_buffer(&ui);
{
let window = ctx.window.lock().unwrap();
self.platform.prepare_render(&ui, &*window);
}
let draw_data = self.imgui.render();
if draw_data.total_vtx_count == 0 && draw_data.total_idx_count == 0 {
return;
}
self.renderer.render(ctx.target, draw_data).expect("imgui rendering failed");
}
fn initialize(display: &Display<WindowSurface>, window: Arc<Mutex<Window>>) -> Self {
let mut imgui = ImguiContext::create();
imgui.set_ini_filename(None);
let mut platform = WinitPlatform::new(&mut imgui);
let window = window.lock().unwrap();
platform.attach_window(imgui.io_mut(), &*window, HiDpiMode::Default);
imgui.fonts().add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
let renderer = ImguiGliumRenderer::new(&mut imgui, display).unwrap();
Self {
imgui,
platform,
renderer,
last_frame: Instant::now(),
rendered_this_frame: false,
}
}
}

View file

@ -0,0 +1,46 @@
use std::sync::{Arc, Mutex};
use egui::ViewportId;
use glium::{Display, Frame};
use glium::glutin::surface::WindowSurface;
use winit::window::Window;
use crate::RenderingSystem;
use crate::system::RenderingContext;
use egui_glium::EguiGlium;
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
pub struct EguiRenderer {
egui_glium: EguiGlium,
}
impl RenderingSystem for EguiRenderer {
fn initialize(display: &Display<WindowSurface>, window: Arc<Mutex<Window>>, event_loop: &EventLoop<()>) -> Self
where
Self: Sized,
{
let window = window.lock().unwrap();
let egui_glium = EguiGlium::new(ViewportId::ROOT, &display, &window, &event_loop);
Self { egui_glium }
}
fn render(&mut self, ctx: &mut RenderingContext) {
let window = ctx.window.lock().unwrap();
self.egui_glium.run(&window, |egui_ctx| {
ctx.egui_queue.borrow_mut().run(egui_ctx);
});
self.egui_glium.paint(ctx.display, ctx.target);
}
fn handle_event(&mut self, window: Arc<Mutex<Window>>, event: Event<()>) {
let window = window.lock().unwrap();
match event {
Event::WindowEvent { event, .. } => {
let _ = self.egui_glium.on_event(&window, &event);
}
_ => {},
}
}
}

View file

@ -1,6 +1,7 @@
mod basic;
pub mod debug_ui;
mod skybox;
mod egui;
pub use basic::BasicMeshRenderingSystem;
pub use skybox::SkyboxRenderingSystem;
pub use egui::EguiRenderer;

View file

@ -8,6 +8,7 @@ use glium::index::PrimitiveType;
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2d};
use glium::uniform;
use glam::{Mat4, Vec2, Vec3};
use winit::event_loop::EventLoop;
use raidillon_assets::include_shader;
use crate::system::RenderingContext;
use crate::RenderingSystem;
@ -109,7 +110,7 @@ impl SkyboxRenderingSystem {
}
impl RenderingSystem for SkyboxRenderingSystem {
fn initialize(display: &Display<WindowSurface>, _window: Arc<Mutex<glium::winit::window::Window>>) -> Self {
fn initialize(display: &Display<WindowSurface>, _window: Arc<Mutex<glium::winit::window::Window>>, event_loop: &EventLoop<()>) -> Self {
const VERT_SRC: &str = include_shader!("skybox.vert");
const FRAG_SRC: &str = include_shader!("skybox.frag");
let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap();

View file

@ -6,16 +6,18 @@ use indexmap::IndexMap;
use glium::{Display, Frame};
use glium::glutin::surface::WindowSurface;
use raidillon_assets::ModelManagerRef;
use raidillon_core::{define_typemap, DebugUIBuffer};
use raidillon_core::{define_typemap, EguiQueue};
use raidillon_core::scene::Scene;
use glam::Vec3;
use winit::event_loop::EventLoop;
pub struct RenderingContext<'a> {
pub scene: &'a Scene,
pub target: &'a mut Frame,
pub window: Arc<Mutex<glium::winit::window::Window>>,
pub display: &'a Display<WindowSurface>,
pub asset_manager: ModelManagerRef,
pub debug_ui_buffer: &'a DebugUIBuffer,
pub egui_queue: Rc<RefCell<EguiQueue>>,
pub env_light_dir: Vec3,
}
@ -30,7 +32,7 @@ pub trait RenderingSystem {
}
fn prepare_frame(&mut self, _window: Arc<Mutex<glium::winit::window::Window>>) {}
fn render(&mut self, ctx: &mut RenderingContext);
fn initialize(display: &Display<WindowSurface>, window: Arc<Mutex<glium::winit::window::Window>>) -> Self
fn initialize(display: &Display<WindowSurface>, window: Arc<Mutex<glium::winit::window::Window>>, event_loop: &EventLoop<()>) -> Self
where
Self: Sized;
}
@ -48,11 +50,11 @@ impl RenderingSystemManager {
}
}
pub fn add<R>(&mut self, display: &Display<WindowSurface>, window: Arc<Mutex<glium::winit::window::Window>>)
pub fn add<R>(&mut self, display: &Display<WindowSurface>, window: Arc<Mutex<glium::winit::window::Window>>, event_loop: &EventLoop<()>)
where
R: RenderingSystem + 'static,
{
let system = R::initialize(display, window);
let system = R::initialize(display, window, event_loop);
self.systems.insert(TypeId::of::<R>(), Box::new(system));
}

View file

@ -1,6 +1,8 @@
use std::{cell::RefCell, rc::Rc};
use std::sync::{Arc, Mutex};
use winit::event::Event;
use raidillon_assets::ModelManagerRef;
use raidillon_core::EguiQueue;
#[derive(Clone)]
pub struct PlatformContext {
@ -10,6 +12,7 @@ pub struct PlatformContext {
pub frame_height: f32,
pub time_ctx: TimeContext,
pub window: Arc<Mutex<winit::window::Window>>,
pub egui_queue: Rc<RefCell<EguiQueue>>,
}
#[derive(Clone)]