Improve abstraction of the engine
- Improved camera controls - Introduce new convenience function ecsr.load_mesh_from_gltf - Abstract out the glium stuff to allow for more backends in the future. We're still tied to winit though as it can be used with any of the major graphics libraries in the Rust ecosystem.
This commit is contained in:
parent
97195fbd05
commit
a3d3f641cd
10 changed files with 115 additions and 74 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -1743,8 +1743,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glam",
|
"glam",
|
||||||
"glium",
|
|
||||||
"glutin",
|
|
||||||
"hecs",
|
"hecs",
|
||||||
"raidillon_core",
|
"raidillon_core",
|
||||||
"raidillon_ecs",
|
"raidillon_ecs",
|
||||||
|
|
@ -1774,6 +1772,7 @@ dependencies = [
|
||||||
"hecs",
|
"hecs",
|
||||||
"image",
|
"image",
|
||||||
"raidillon_ecs",
|
"raidillon_ecs",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1785,6 +1784,7 @@ dependencies = [
|
||||||
"imgui",
|
"imgui",
|
||||||
"imgui-glium-renderer",
|
"imgui-glium-renderer",
|
||||||
"imgui-winit-support",
|
"imgui-winit-support",
|
||||||
|
"raidillon_render",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
glam = "0.30.4"
|
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"
|
winit = "0.30"
|
||||||
raidillon_render = { path = "../raidillon_render" }
|
raidillon_render = { path = "../raidillon_render" }
|
||||||
raidillon_ecs = { path = "../raidillon_ecs" }
|
raidillon_ecs = { path = "../raidillon_ecs" }
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use glam::{Quat, Vec3, EulerRot};
|
use glam::{Quat, Vec3, EulerRot};
|
||||||
use glium::backend::glutin::SimpleWindowBuilder;
|
|
||||||
use raidillon_core::Time;
|
use raidillon_core::Time;
|
||||||
use raidillon_ecs::Transform;
|
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_ui::Gui;
|
||||||
use raidillon_input::{Input, FPSCameraController};
|
use raidillon_input::{Input, FPSCameraController};
|
||||||
use winit::keyboard::KeyCode;
|
use winit::keyboard::KeyCode;
|
||||||
use winit::window::CursorGrabMode;
|
use winit::window::CursorGrabMode;
|
||||||
|
use winit::event::MouseButton;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
enum Action {
|
enum Action {
|
||||||
|
|
@ -18,24 +18,17 @@ enum Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
let event_loop = winit::event_loop::EventLoop::builder()
|
||||||
.build()
|
.build()
|
||||||
.expect("create event-loop");
|
.expect("create event-loop");
|
||||||
|
|
||||||
let (window, display) = SimpleWindowBuilder::new()
|
let (window, _display): (winit::window::Window, DisplayHandle) = init_render_window(&event_loop, "raidillon", (1280, 720))?;
|
||||||
.with_title("raidillon")
|
|
||||||
.with_inner_size(1280, 720)
|
|
||||||
.build(&event_loop);
|
|
||||||
|
|
||||||
// Create ECS renderer which internally owns both the world and the renderer
|
// Create ECS renderer which internally owns both the world and the renderer
|
||||||
let mut ecsr = {
|
let mut ecsr = ECSRenderer::from_display_handle(&_display)?;
|
||||||
let world = hecs::World::new();
|
|
||||||
let renderer = GliumRenderer::new(display.clone())?;
|
|
||||||
ECSRenderer::new(renderer, world)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Dear ImGui integration
|
// Dear ImGui integration
|
||||||
let mut gui = Gui::new(&display, &window)?;
|
let mut gui = Gui::new(&_display, &window)?;
|
||||||
|
|
||||||
let mut input = Input::<Action>::new();
|
let mut input = Input::<Action>::new();
|
||||||
input.map_key(KeyCode::KeyW, Action::MoveForward);
|
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 camera_controller = FPSCameraController::new(Vec3::new(0.0, 0.0, 2.0));
|
||||||
|
|
||||||
let mut cursor_grabbed = false;
|
let mut right_mouse_held = false;
|
||||||
let mut attempted_initial_grab = false;
|
|
||||||
|
|
||||||
let mut time = Time::new();
|
let mut time = Time::new();
|
||||||
|
|
||||||
let object_ent = {
|
let object_ent = ecsr.load_mesh_from_gltf("resources/models/tree.gltf", Transform {
|
||||||
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),
|
translation: Vec3::new(0.0, -2.5, -5.0),
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::new(0.01, 0.01, 0.01),
|
scale: Vec3::new(0.01, 0.01, 0.01),
|
||||||
})
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
let ground_ent = {
|
let ground_ent = ecsr.load_mesh_from_gltf("resources/models/plane.gltf", Transform {
|
||||||
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),
|
translation: Vec3::new(0.0, -1.5, 0.0),
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::new(1.0, 1.0, 1.0),
|
scale: Vec3::new(1.0, 1.0, 1.0),
|
||||||
})
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let camera_ent = {
|
let camera_ent = {
|
||||||
|
|
@ -84,7 +70,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
event_loop
|
event_loop
|
||||||
.run(move |event, el| {
|
.run(move |event, el| {
|
||||||
use glium::winit::event::{Event, WindowEvent};
|
use winit::event::{Event, WindowEvent};
|
||||||
|
|
||||||
gui.handle_event(&window, &event);
|
gui.handle_event(&window, &event);
|
||||||
|
|
||||||
|
|
@ -98,13 +84,33 @@ fn main() -> Result<()> {
|
||||||
cam.aspect = sz.width as f32 / sz.height as f32;
|
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 => {
|
WindowEvent::RedrawRequested => {
|
||||||
let mut target = display.draw();
|
gui.render_world(&mut ecsr, &window, |ui, ecsr| {
|
||||||
|
|
||||||
ecsr.render_into(&mut target);
|
|
||||||
|
|
||||||
gui.render_with(&mut target, &window, |ui| {
|
|
||||||
if let Ok(mut tr) = ecsr.world.query_one_mut::<&mut Transform>(object_ent) {
|
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
|
// Translation controls
|
||||||
let mut translation = [tr.translation.x, tr.translation.y, tr.translation.z];
|
let mut translation = [tr.translation.x, tr.translation.y, tr.translation.z];
|
||||||
if ui.input_float3("Translation", &mut translation).build() {
|
if ui.input_float3("Translation", &mut translation).build() {
|
||||||
|
|
@ -128,49 +134,21 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
target.finish().unwrap();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Event::AboutToWait => {
|
Event::AboutToWait => {
|
||||||
time.tick();
|
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();
|
let dt = time.delta_seconds();
|
||||||
camera_controller.update(
|
camera_controller.update(
|
||||||
&input,
|
&input,
|
||||||
dt,
|
dt,
|
||||||
cursor_grabbed,
|
right_mouse_held,
|
||||||
(Action::MoveForward, Action::MoveBackward, Action::MoveLeft, Action::MoveRight),
|
(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) {
|
if let Ok(mut cam) = ecsr.world.query_one_mut::<&mut Camera>(camera_ent) {
|
||||||
cam.eye = camera_controller.position;
|
cam.eye = camera_controller.position;
|
||||||
cam.center = camera_controller.position + camera_controller.front();
|
cam.center = camera_controller.position + camera_controller.front();
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@ glutin = { version = "0.32.3", default-features = false }
|
||||||
hecs = "0.10.5"
|
hecs = "0.10.5"
|
||||||
image = "0.25.6"
|
image = "0.25.6"
|
||||||
raidillon_ecs = { path = "../raidillon_ecs" }
|
raidillon_ecs = { path = "../raidillon_ecs" }
|
||||||
|
winit = "0.30"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,11 @@ pub struct ECSRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ECSRenderer {
|
impl ECSRenderer {
|
||||||
|
pub fn from_display_handle(handle: &crate::window::DisplayHandle) -> anyhow::Result<Self> {
|
||||||
|
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 {
|
pub fn new(renderer: GliumRenderer, world: World) -> Self {
|
||||||
Self { renderer, world }
|
Self { renderer, world }
|
||||||
}
|
}
|
||||||
|
|
@ -45,4 +50,13 @@ impl ECSRenderer {
|
||||||
pub fn render_into<S: glium::Surface>(&mut self, target: &mut S) {
|
pub fn render_into<S: glium::Surface>(&mut self, target: &mut S) {
|
||||||
self.renderer.render_into(&self.world, target);
|
self.renderer.render_into(&self.world, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_mesh_from_gltf<P: AsRef<std::path::Path> + std::fmt::Debug>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
transform: Transform,
|
||||||
|
) -> anyhow::Result<Entity> {
|
||||||
|
let model = crate::gltf_loader::load_gltf(path, self.renderer.display())?;
|
||||||
|
Ok(self.spawn_mesh(model, transform))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ pub mod model;
|
||||||
pub mod gltf_loader;
|
pub mod gltf_loader;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod ecs_renderer;
|
pub mod ecs_renderer;
|
||||||
|
pub mod window;
|
||||||
|
|
||||||
pub use camera::Camera;
|
pub use camera::Camera;
|
||||||
pub use render::GliumRenderer;
|
pub use render::GliumRenderer;
|
||||||
pub use ecs_renderer::ECSRenderer;
|
pub use ecs_renderer::ECSRenderer;
|
||||||
|
pub use window::{DisplayHandle, init_window as init_render_window};
|
||||||
|
|
|
||||||
|
|
@ -160,4 +160,8 @@ impl GliumRenderer {
|
||||||
self.draw_scene(world, &mut frame);
|
self.draw_scene(world, &mut frame);
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display(&self) -> &glium::Display<WindowSurface> {
|
||||||
|
&self.display
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
raidillon_render/src/window.rs
Normal file
28
raidillon_render/src/window.rs
Normal file
|
|
@ -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<WindowSurface>);
|
||||||
|
|
||||||
|
impl DisplayHandle {
|
||||||
|
pub fn as_inner(&self) -> &Display<WindowSurface> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_window<T>(
|
||||||
|
event_loop: &EventLoop<T>,
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
|
@ -10,3 +10,4 @@ imgui = "0.12"
|
||||||
imgui-winit-support = "0.13"
|
imgui-winit-support = "0.13"
|
||||||
imgui-glium-renderer = "0.13"
|
imgui-glium-renderer = "0.13"
|
||||||
winit = "0.30"
|
winit = "0.30"
|
||||||
|
raidillon_render = { path = "../raidillon_render" }
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ use imgui::{Context as ImguiContext, Ui};
|
||||||
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||||
use imgui_glium_renderer::Renderer as ImguiGliumRenderer;
|
use imgui_glium_renderer::Renderer as ImguiGliumRenderer;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
use glium::{Frame};
|
use glium::Frame;
|
||||||
use glium::glutin::surface::WindowSurface;
|
use raidillon_render::{DisplayHandle, ECSRenderer};
|
||||||
|
|
||||||
/// Convenience wrapper that owns all ImGui state required for integration with
|
/// Convenience wrapper that owns all ImGui state required for integration with
|
||||||
/// winit + glium.
|
/// winit + glium.
|
||||||
|
|
@ -18,13 +18,13 @@ pub struct Gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gui {
|
impl Gui {
|
||||||
pub fn new(display: &glium::Display<WindowSurface>, window: &Window) -> Result<Self> {
|
pub fn new(display: &DisplayHandle, window: &Window) -> Result<Self> {
|
||||||
let mut imgui = ImguiContext::create();
|
let mut imgui = ImguiContext::create();
|
||||||
imgui.set_ini_filename(None);
|
imgui.set_ini_filename(None);
|
||||||
let mut platform = WinitPlatform::new(&mut imgui);
|
let mut platform = WinitPlatform::new(&mut imgui);
|
||||||
platform.attach_window(imgui.io_mut(), window, HiDpiMode::Default);
|
platform.attach_window(imgui.io_mut(), window, HiDpiMode::Default);
|
||||||
imgui.fonts().add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
|
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 {
|
Ok(Self {
|
||||||
imgui,
|
imgui,
|
||||||
|
|
@ -74,6 +74,21 @@ impl Gui {
|
||||||
.expect("imgui rendering failed");
|
.expect("imgui rendering failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_world<F>(&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<F>(&mut self, build: F)
|
pub fn ui<F>(&mut self, build: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(&Ui),
|
F: FnOnce(&Ui),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue