diff --git a/Cargo.lock b/Cargo.lock index bc40061..0a091ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,8 +1743,6 @@ version = "0.1.0" dependencies = [ "anyhow", "glam", - "glium", - "glutin", "hecs", "raidillon_core", "raidillon_ecs", @@ -1774,6 +1772,7 @@ dependencies = [ "hecs", "image", "raidillon_ecs", + "winit", ] [[package]] @@ -1785,6 +1784,7 @@ dependencies = [ "imgui", "imgui-glium-renderer", "imgui-winit-support", + "raidillon_render", "winit", ] diff --git a/raidillon_game/Cargo.toml b/raidillon_game/Cargo.toml index a5aad2f..c127172 100644 --- a/raidillon_game/Cargo.toml +++ b/raidillon_game/Cargo.toml @@ -6,8 +6,6 @@ edition = "2021" [dependencies] anyhow = "1.0.98" glam = "0.30.4" -glium = { version = "0.35.0", features = ["glutin_backend", "simple_window_builder"] } -glutin = { version = "0.32.3", default-features = false } winit = "0.30" raidillon_render = { path = "../raidillon_render" } raidillon_ecs = { path = "../raidillon_ecs" } diff --git a/raidillon_game/src/main.rs b/raidillon_game/src/main.rs index 17d665c..825f1c2 100644 --- a/raidillon_game/src/main.rs +++ b/raidillon_game/src/main.rs @@ -1,13 +1,13 @@ use anyhow::Result; use glam::{Quat, Vec3, EulerRot}; -use glium::backend::glutin::SimpleWindowBuilder; use raidillon_core::Time; use raidillon_ecs::Transform; -use raidillon_render::{Camera, GliumRenderer, gltf_loader, ECSRenderer}; +use raidillon_render::{Camera, ECSRenderer, init_render_window, DisplayHandle}; use raidillon_ui::Gui; use raidillon_input::{Input, FPSCameraController}; use winit::keyboard::KeyCode; use winit::window::CursorGrabMode; +use winit::event::MouseButton; #[derive(Copy, Clone, Eq, PartialEq, Hash)] enum Action { @@ -18,24 +18,17 @@ enum Action { } fn main() -> Result<()> { - let event_loop = glium::winit::event_loop::EventLoop::builder() + let event_loop = winit::event_loop::EventLoop::builder() .build() .expect("create event-loop"); - let (window, display) = SimpleWindowBuilder::new() - .with_title("raidillon") - .with_inner_size(1280, 720) - .build(&event_loop); + let (window, _display): (winit::window::Window, DisplayHandle) = init_render_window(&event_loop, "raidillon", (1280, 720))?; // Create ECS renderer which internally owns both the world and the renderer - let mut ecsr = { - let world = hecs::World::new(); - let renderer = GliumRenderer::new(display.clone())?; - ECSRenderer::new(renderer, world) - }; + let mut ecsr = ECSRenderer::from_display_handle(&_display)?; // Dear ImGui integration - let mut gui = Gui::new(&display, &window)?; + let mut gui = Gui::new(&_display, &window)?; let mut input = Input::::new(); input.map_key(KeyCode::KeyW, Action::MoveForward); @@ -45,28 +38,21 @@ fn main() -> Result<()> { let mut camera_controller = FPSCameraController::new(Vec3::new(0.0, 0.0, 2.0)); - let mut cursor_grabbed = false; - let mut attempted_initial_grab = false; + let mut right_mouse_held = false; let mut time = Time::new(); - let object_ent = { - let model_3d = gltf_loader::load_gltf("resources/models/tree.gltf", &display)?; - ecsr.spawn_mesh(model_3d, Transform { - translation: Vec3::new(0.0, -2.5, -5.0), - rotation: Quat::IDENTITY, - scale: Vec3::new(0.01, 0.01, 0.01), - }) - }; + let object_ent = ecsr.load_mesh_from_gltf("resources/models/tree.gltf", Transform { + translation: Vec3::new(0.0, -2.5, -5.0), + rotation: Quat::IDENTITY, + scale: Vec3::new(0.01, 0.01, 0.01), + })?; - let ground_ent = { - let model_3d = gltf_loader::load_gltf("resources/models/plane.gltf", &display)?; - ecsr.spawn_mesh(model_3d, Transform { - translation: Vec3::new(0.0, -1.5, 0.0), - rotation: Quat::IDENTITY, - scale: Vec3::new(1.0, 1.0, 1.0), - }) - }; + let ground_ent = ecsr.load_mesh_from_gltf("resources/models/plane.gltf", Transform { + translation: Vec3::new(0.0, -1.5, 0.0), + rotation: Quat::IDENTITY, + scale: Vec3::new(1.0, 1.0, 1.0), + })?; let camera_ent = { @@ -84,7 +70,7 @@ fn main() -> Result<()> { event_loop .run(move |event, el| { - use glium::winit::event::{Event, WindowEvent}; + use winit::event::{Event, WindowEvent}; gui.handle_event(&window, &event); @@ -98,13 +84,33 @@ fn main() -> Result<()> { cam.aspect = sz.width as f32 / sz.height as f32; }); } + WindowEvent::MouseInput { state, button, .. } => { + if button == MouseButton::Right { + match state { + winit::event::ElementState::Pressed => { + if window + .set_cursor_grab(CursorGrabMode::Confined) + .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)) + .is_ok() + { + window.set_cursor_visible(false); + right_mouse_held = true; + } + } + winit::event::ElementState::Released => { + let _ = window.set_cursor_grab(CursorGrabMode::None); + window.set_cursor_visible(true); + right_mouse_held = false; + } + } + } + } WindowEvent::RedrawRequested => { - let mut target = display.draw(); - - ecsr.render_into(&mut target); - - gui.render_with(&mut target, &window, |ui| { + gui.render_world(&mut ecsr, &window, |ui, ecsr| { if let Ok(mut tr) = ecsr.world.query_one_mut::<&mut Transform>(object_ent) { + ui.text("Hold right click to control the camera"); + ui.text("WASD to move"); + // Translation controls let mut translation = [tr.translation.x, tr.translation.y, tr.translation.z]; if ui.input_float3("Translation", &mut translation).build() { @@ -128,49 +134,21 @@ fn main() -> Result<()> { } } }); - target.finish().unwrap(); } _ => {} }, Event::AboutToWait => { time.tick(); - if !attempted_initial_grab { - attempted_initial_grab = true; - if window - .set_cursor_grab(CursorGrabMode::Confined) - .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)) - .is_ok() - { - window.set_cursor_visible(false); - cursor_grabbed = true; - } - } - { let dt = time.delta_seconds(); camera_controller.update( &input, dt, - cursor_grabbed, + right_mouse_held, (Action::MoveForward, Action::MoveBackward, Action::MoveLeft, Action::MoveRight), ); - if input.key_pressed(KeyCode::Escape) { - if cursor_grabbed { - let _ = window.set_cursor_grab(CursorGrabMode::None); - window.set_cursor_visible(true); - cursor_grabbed = false; - } else if window - .set_cursor_grab(CursorGrabMode::Confined) - .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)) - .is_ok() - { - window.set_cursor_visible(false); - cursor_grabbed = true; - } - } - if let Ok(mut cam) = ecsr.world.query_one_mut::<&mut Camera>(camera_ent) { cam.eye = camera_controller.position; cam.center = camera_controller.position + camera_controller.front(); diff --git a/raidillon_render/Cargo.toml b/raidillon_render/Cargo.toml index 1f5247c..ae6c94a 100644 --- a/raidillon_render/Cargo.toml +++ b/raidillon_render/Cargo.toml @@ -12,3 +12,4 @@ glutin = { version = "0.32.3", default-features = false } hecs = "0.10.5" image = "0.25.6" raidillon_ecs = { path = "../raidillon_ecs" } +winit = "0.30" diff --git a/raidillon_render/src/ecs_renderer.rs b/raidillon_render/src/ecs_renderer.rs index edac676..e21b036 100644 --- a/raidillon_render/src/ecs_renderer.rs +++ b/raidillon_render/src/ecs_renderer.rs @@ -12,6 +12,11 @@ pub struct ECSRenderer { } impl ECSRenderer { + pub fn from_display_handle(handle: &crate::window::DisplayHandle) -> anyhow::Result { + let world = World::new(); + let renderer = crate::render::GliumRenderer::new(handle.as_inner().clone())?; + Ok(Self { renderer, world }) + } pub fn new(renderer: GliumRenderer, world: World) -> Self { Self { renderer, world } } @@ -45,4 +50,13 @@ impl ECSRenderer { pub fn render_into(&mut self, target: &mut S) { self.renderer.render_into(&self.world, target); } + + pub fn load_mesh_from_gltf + std::fmt::Debug>( + &mut self, + path: P, + transform: Transform, + ) -> anyhow::Result { + let model = crate::gltf_loader::load_gltf(path, self.renderer.display())?; + Ok(self.spawn_mesh(model, transform)) + } } diff --git a/raidillon_render/src/lib.rs b/raidillon_render/src/lib.rs index 40e3033..f883c91 100644 --- a/raidillon_render/src/lib.rs +++ b/raidillon_render/src/lib.rs @@ -3,7 +3,9 @@ pub mod model; pub mod gltf_loader; pub mod render; pub mod ecs_renderer; +pub mod window; pub use camera::Camera; pub use render::GliumRenderer; pub use ecs_renderer::ECSRenderer; +pub use window::{DisplayHandle, init_window as init_render_window}; diff --git a/raidillon_render/src/render.rs b/raidillon_render/src/render.rs index 38f4175..1d7554d 100644 --- a/raidillon_render/src/render.rs +++ b/raidillon_render/src/render.rs @@ -160,4 +160,8 @@ impl GliumRenderer { self.draw_scene(world, &mut frame); frame.finish().unwrap(); } + + pub fn display(&self) -> &glium::Display { + &self.display + } } diff --git a/raidillon_render/src/window.rs b/raidillon_render/src/window.rs new file mode 100644 index 0000000..db0897c --- /dev/null +++ b/raidillon_render/src/window.rs @@ -0,0 +1,28 @@ +use glium::backend::glutin::SimpleWindowBuilder; +use glium::glutin::surface::WindowSurface; +use glium::Display; +use anyhow::Result; +use winit::event_loop::EventLoop; +use winit::window::Window; + +#[derive(Clone)] +pub struct DisplayHandle(Display); + +impl DisplayHandle { + pub fn as_inner(&self) -> &Display { + &self.0 + } +} + +pub fn init_window( + event_loop: &EventLoop, + title: &str, + size: (u32, u32), +) -> Result<(Window, DisplayHandle)> { + let (window, display) = SimpleWindowBuilder::new() + .with_title(title) + .with_inner_size(size.0, size.1) + .build(event_loop); + + Ok((window, DisplayHandle(display))) +} diff --git a/raidillon_ui/Cargo.toml b/raidillon_ui/Cargo.toml index d26d063..2f4b606 100644 --- a/raidillon_ui/Cargo.toml +++ b/raidillon_ui/Cargo.toml @@ -10,3 +10,4 @@ imgui = "0.12" imgui-winit-support = "0.13" imgui-glium-renderer = "0.13" winit = "0.30" +raidillon_render = { path = "../raidillon_render" } diff --git a/raidillon_ui/src/ui.rs b/raidillon_ui/src/ui.rs index 8ac7b0d..224ee1a 100644 --- a/raidillon_ui/src/ui.rs +++ b/raidillon_ui/src/ui.rs @@ -5,8 +5,8 @@ use imgui::{Context as ImguiContext, Ui}; use imgui_winit_support::{HiDpiMode, WinitPlatform}; use imgui_glium_renderer::Renderer as ImguiGliumRenderer; use winit::window::Window; -use glium::{Frame}; -use glium::glutin::surface::WindowSurface; +use glium::Frame; +use raidillon_render::{DisplayHandle, ECSRenderer}; /// Convenience wrapper that owns all ImGui state required for integration with /// winit + glium. @@ -18,13 +18,13 @@ pub struct Gui { } impl Gui { - pub fn new(display: &glium::Display, window: &Window) -> Result { + pub fn new(display: &DisplayHandle, window: &Window) -> Result { let mut imgui = ImguiContext::create(); imgui.set_ini_filename(None); let mut platform = WinitPlatform::new(&mut imgui); 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)?; + let renderer = ImguiGliumRenderer::new(&mut imgui, display.as_inner())?; Ok(Self { imgui, @@ -74,6 +74,21 @@ impl Gui { .expect("imgui rendering failed"); } + pub fn render_world(&mut self, ecsr: &mut ECSRenderer, window: &Window, build_ui: F) + where + F: FnOnce(&Ui, &mut ECSRenderer), + { + let mut target = ecsr.renderer.display().draw(); + + ecsr.render_into(&mut target); + + self.render_with(&mut target, window, |ui| { + build_ui(ui, ecsr); + }); + + target.finish().expect("Failed to swap buffers"); + } + pub fn ui(&mut self, build: F) where F: FnOnce(&Ui),