From 552a36a14603451b1b0a5dc9344321fa28ee6b3b Mon Sep 17 00:00:00 2001 From: LunarAkai Date: Sun, 27 Jul 2025 10:25:31 +0200 Subject: [PATCH] refactoring --- Cargo.lock | 1 + moonhare_engine/src/game.rs | 129 ++++++++++++++++++++++++++++ moonhare_engine/src/game_plugin.rs | 8 ++ moonhare_engine/src/lib.rs | 132 ++--------------------------- moonhare_engine/src/vertex.rs | 26 +++++- playground/Cargo.toml | 1 + playground/src/main.rs | 115 ++++++++++++++++++++++++- 7 files changed, 282 insertions(+), 130 deletions(-) create mode 100644 moonhare_engine/src/game.rs create mode 100644 moonhare_engine/src/game_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index c43e450..c20498d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1028,6 +1028,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" name = "playground" version = "0.1.0" dependencies = [ + "glium", "moonhare_engine", ] diff --git a/moonhare_engine/src/game.rs b/moonhare_engine/src/game.rs new file mode 100644 index 0000000..430382c --- /dev/null +++ b/moonhare_engine/src/game.rs @@ -0,0 +1,129 @@ +use std::sync::{Arc, Mutex}; + +use glium::{glutin::surface::WindowSurface, winit::{application::ApplicationHandler, event::{Event, WindowEvent}, event_loop::{self, EventLoop}, window::{self, Window}}, Display}; + +use crate::game_plugin::GamePlugin; + +pub struct Game { + running: bool, + game_plugin: Option>, + window: Option, + display: Arc>>>, + event_loop: EventLoop<()> +} + +impl Game { + pub fn new() -> Self { + Self { + running: true, + game_plugin: None, + window: None, + display: Arc::new(Mutex::new(None)), + event_loop: init_event_loop(), + } + } + + pub fn register_plugin(&mut self, plugin: Box) { + self.game_plugin = Some(plugin); + } + + pub fn init(&mut self) { + if let Some(ref mut game_plugin) = self.game_plugin { + game_plugin.init(); + } else { + todo!("Default Impl init") + } + + let (_window, _display) = return_window(&self.event_loop); + self.window = Some(_window); + self.display = Arc::new(Mutex::new(Some(_display))); + } + + pub fn update(&mut self) { + if let Some(ref mut game_plugin) = self.game_plugin { + game_plugin.update(); + } else { + todo!("Default Impl update") + } + } + + pub fn render(&mut self) { + if let Some(ref display) = self.display.into() { + let mut target = display.draw(); + + if let Some(ref mut game_plugin) = self.game_plugin { + game_plugin.render(&mut target); + } else { + todo!("Default Impl render") + } + + target.finish().unwrap(); + } + } + + pub fn cleanup(&mut self) { + if let Some(ref mut game_plugin) = self.game_plugin { + game_plugin.cleanup(); + } else { + todo!("Default Impl cleanup") + } + } + + pub fn run(&mut self) { + self.init(); + + while self.running { + self.update(); + self.render(); + } + + self.cleanup(); + } +} + +impl ApplicationHandler for Game { + fn resumed(&mut self, event_loop: &event_loop::ActiveEventLoop) { + self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap()) + } + + fn window_event( + &mut self, + event_loop: &event_loop::ActiveEventLoop, + window_id: window::WindowId, + event: WindowEvent, + ) { + + match event { + + WindowEvent::CloseRequested => { + event_loop.exit(); + }, + WindowEvent::Resized(window_size) => { + self.display.as_ref().unwrap().resize(window_size.into()); + }, + + WindowEvent::RedrawRequested => { + self.window.as_ref().unwrap().request_redraw(); + }, + _ => (), + } + + } + +} + +fn init_event_loop() -> EventLoop<()> { + let event_loop = glium::winit::event_loop::EventLoop::builder() + .build() + .expect("event loop building"); + + return event_loop; +} + +fn return_window(event_loop: &EventLoop<()>) -> (Window, Display) { + let (_window, display) = glium::backend::glutin::SimpleWindowBuilder::new() + .with_title(crate::ENGINE_NAME) + .build(event_loop); + + return (_window, display); +} diff --git a/moonhare_engine/src/game_plugin.rs b/moonhare_engine/src/game_plugin.rs new file mode 100644 index 0000000..17fcb4f --- /dev/null +++ b/moonhare_engine/src/game_plugin.rs @@ -0,0 +1,8 @@ +use glium::Frame; + +pub trait GamePlugin { + fn init(&mut self); + fn update(&mut self); + fn render(&mut self, target: &mut Frame); + fn cleanup(&mut self); +} \ No newline at end of file diff --git a/moonhare_engine/src/lib.rs b/moonhare_engine/src/lib.rs index 9d8f477..4fa0028 100644 --- a/moonhare_engine/src/lib.rs +++ b/moonhare_engine/src/lib.rs @@ -1,135 +1,17 @@ -mod vertex; +pub mod vertex; +pub mod game; +pub mod game_plugin; -use glium::{uniform, uniforms, Surface}; +use glium::{glutin::surface::WindowSurface, uniform, uniforms, winit::{event::{self, StartCause}, event_loop::{self, EventLoop, EventLoopBuilder}, window::{self, Window}}, Display, Surface}; -use vertex::Vertex; + + +const ENGINE_NAME: &str = "Moonhare Engine"; // rescaling: position *= factor; // rotating: new_position = vec2(pos.x * cos(angle) - pos.y * sin(angle), pos.x * sin(single) + pos.y * cos(angle)); // skewing: position.x += position.y * factor; -fn main() { - let event_loop = glium::winit::event_loop::EventLoop::builder() - .build() - .expect("event loop building"); - - let (_window, display) = glium::backend::glutin::SimpleWindowBuilder::new() - .with_title("Moonhare Engine") - .build(&event_loop); - - let shape = vec![ - Vertex { position: [-0.5, -0.5], color: [1.0, 0.0, 0.0] }, - Vertex { position: [ 0.0, 0.5], color: [0.0, 1.0, 0.0] }, - Vertex { position: [ 0.5, -0.25], color: [0.0, 0.0, 1.0] } - ]; - - // "Upload" shape to the memory of the GPU (Vertex Buffer) - // Isn't strictly necessary but, makes tge drawing operation faster - let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap(); - - // Complex shapes consist of hundreds/thousands of vertices -> need to have a list of vertices and tell OpenGL how to link these - // vertices together to obtain triangles. - // For only one triangle -> pass dummy marker to glium - // This line tells OpenGl that we don't use indices and instand want to draw a certain number of seperate triangles - let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList); - - // (Simplified) Render Pipeline: - // Vertex Shader -> Fragment (Pixel) Shader - // uniform: - // value is set when we draw by passing its value to the draw function - // (easiest way is uniform! macro) - // Important to write matrix * vertex -> Matrix operations produce different results depending on the order - // out: defines a variable that is going to be passed along to the fragment shader - let vertex_shader_src = r#" - #version 140 - - in vec2 position; - in vec3 color; - out vec3 vertex_color; - - uniform mat4 matrix; - - void main() { - vertex_color = color; - gl_Position = matrix * vec4(position, 0.0, 1.0); - } - "#; - - let fragment_shader_src = r#" - #version 140 - - in vec3 vertex_color; - out vec4 color; - - void main() { - color = vec4(vertex_color, 1.0); - } - "#; - - // send shader source code to glium - let program = glium::Program::from_source( - &display, - vertex_shader_src, - fragment_shader_src, - None - ).unwrap(); - - let mut t: f32 = 0.0; - let _ = event_loop.run(move |event, window_target| { - match event { - glium::winit::event::Event::WindowEvent { event, .. } => match event { - glium::winit::event::WindowEvent::CloseRequested => window_target.exit(), - glium::winit::event::WindowEvent::Resized(window_size) => { - display.resize(window_size.into()); - } - glium::winit::event::WindowEvent::RedrawRequested => { - t += 0.02; - // use 't' as an offset -> smooth animation - let x_offset = t.sin() * 0.5; - - let mut target = display.draw(); - - target.clear_color( - 0.0, - 0.0, - 1.0, - 1.0); - - // Note: In OpenGL matrices are column-major - // Standard mathematical notation is row major: - // 1.0 0.0 0.0 x_offset - // 0.0 1.0 0.0 0.0 - // 0.0 0.0 1.0 0.0 - // 0.0 0.0 0.0 1.0 - let uniforms = uniform! { - matrix: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [x_offset, 0.0, 0.0, 1.0f32], - ] - }; - - target.draw( - &vertex_buffer, - &indices, - &program, - &uniforms, - &Default::default() - ).unwrap(); - - target.finish().unwrap(); - }, - _ => (), - }, - // request a redraw ourselves once we've finished rendering - glium::winit::event::Event::AboutToWait => { - _window.request_redraw(); - }, - _ => (), - }; - }); -} diff --git a/moonhare_engine/src/vertex.rs b/moonhare_engine/src/vertex.rs index 2fa5be2..d087b63 100644 --- a/moonhare_engine/src/vertex.rs +++ b/moonhare_engine/src/vertex.rs @@ -1,8 +1,26 @@ use glium::implement_vertex; #[derive(Copy, Clone)] -pub(crate) struct Vertex { - pub(crate) position: [f32; 2], - pub(crate) color: [f32; 3], +pub struct Vertex { + pub position: [f32; 2], + pub color: [f32; 3], } -implement_vertex!(Vertex, position, color); \ No newline at end of file +implement_vertex!(Vertex, position, color); + +impl Vertex { + pub fn new() -> Self { + Self { + position: [0.0, 0.0], + color: [0.0, 0.0, 0.0], + } + } + + pub fn define_shape(v1: Vertex, v2: Vertex, v3: Vertex) -> Vec { + let shape = vec![ + v1, + v2, + v3 + ]; + return shape; + } +} \ No newline at end of file diff --git a/playground/Cargo.toml b/playground/Cargo.toml index f62b1ed..6e08b67 100644 --- a/playground/Cargo.toml +++ b/playground/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] moonhare_engine = { path = "../moonhare_engine" } +glium = "0.36.0" diff --git a/playground/src/main.rs b/playground/src/main.rs index 9bc5e5f..9bdcc40 100644 --- a/playground/src/main.rs +++ b/playground/src/main.rs @@ -1,3 +1,116 @@ +use std::fs::read_to_string; + +use glium::{index::NoIndices, Frame, Program, VertexBuffer}; +use glium::{program, uniform, Display, Surface}; +use moonhare_engine::{game::Game, game_plugin::GamePlugin, vertex::Vertex}; + +struct PlaygroundGame { + t: f32, + shape: Vec, + vertex_buffer: VertexBuffer, + indices: NoIndices, + program: Program, +} + +impl GamePlugin for PlaygroundGame { + fn init(&mut self) { + self.t = 0.0; + } + fn update(&mut self) { + self.t += 0.02; + // use 't' as an offset -> smooth animation + + } + fn render(&mut self, target: &mut Frame) { + + target.clear_color( + 0.0, + 0.0, + 1.0, + 1.0 + ); + let x_offset = self.t.sin() * 0.5; + + // Note: In OpenGL matrices are column-major + // Standard mathematical notation is row major: + // 1.0 0.0 0.0 x_offset + // 0.0 1.0 0.0 0.0 + // 0.0 0.0 1.0 0.0 + // 0.0 0.0 0.0 1.0 + let uniforms = uniform! { + matrix: [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [x_offset, 0.0, 0.0, 1.0f32], + ] + }; + + target.draw( + &self.vertex_buffer, + &self.indices, + &self.program, + &uniforms, + &Default::default() + ).unwrap(); + + target.finish().unwrap(); + } + fn cleanup(&mut self) { + + } +} + fn main() { - println!("Hello World"); + + let mut game: Game = Game::new(); + + let shape = Vertex::define_shape( + Vertex { position: [-0.5, -0.5], color: [1.0, 0.0, 0.0] }, + Vertex { position: [ 0.0, 0.5], color: [0.0, 1.0, 0.0] }, + Vertex { position: [ 0.5, -0.25], color: [0.0, 0.0, 1.0] } + ); + + // "Upload" shape to the memory of the GPU (Vertex Buffer) + // Isn't strictly necessary but, makes tge drawing operation faster + let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap(); + + + // Complex shapes consist of hundreds/thousands of vertices -> need to have a list of vertices and tell OpenGL how to link these + // vertices together to obtain triangles. + // For only one triangle -> pass dummy marker to glium + // This line tells OpenGl that we don't use indices and instand want to draw a certain number of seperate triangles + let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList); + + // (Simplified) Render Pipeline: + // Vertex Shader -> Fragment (Pixel) Shader + // uniform: + // value is set when we draw by passing its value to the draw function + // (easiest way is uniform! macro) + // Important to write matrix * vertex -> Matrix operations produce different results depending on the order + // out: defines a variable that is going to be passed along to the fragment shader + + let vertex_shader_src = read_to_string("./shaders/vertex_shader.glsl").unwrap(); + let fragment_shader_src = read_to_string("./shaders/fragment_shader.glsl").unwrap(); + + // send shader source code to glium + let program = glium::Program::from_source( + glium::Display::new(context, surface), + &vertex_shader_src, + &fragment_shader_src, + None + ).unwrap(); + + + let mut pg_game = PlaygroundGame { + t: 0.0, + shape: shape, + vertex_buffer: vertex_buffer, + indices: indices, + program: program, + }; + + game.register_plugin(Box::new(pg_game)); + + game.run(); } \ No newline at end of file