diff --git a/Cargo.lock b/Cargo.lock index fb2c1f5..0e55cd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,12 +290,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - [[package]] name = "drm" version = "0.11.1" @@ -521,15 +515,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - [[package]] name = "ndk" version = "0.8.0" @@ -685,15 +670,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.35" @@ -749,12 +725,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "serde" version = "1.0.196" @@ -784,12 +754,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - [[package]] name = "smol_str" version = "0.2.1" @@ -815,15 +779,12 @@ dependencies = [ "foreign-types", "js-sys", "log", - "memmap2", "objc", "raw-window-handle", "redox_syscall 0.4.1", "rustix", "tiny-xlib", "wasm-bindgen", - "wayland-backend", - "wayland-client", "wayland-sys", "web-sys", "windows-sys 0.52.0", @@ -994,43 +955,6 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" -[[package]] -name = "wayland-backend" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" -dependencies = [ - "cc", - "downcast-rs", - "rustix", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" -dependencies = [ - "bitflags 2.4.2", - "rustix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" -dependencies = [ - "proc-macro2", - "quick-xml", - "quote", -] - [[package]] name = "wayland-sys" version = "0.31.1" @@ -1039,7 +963,6 @@ checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", "log", - "once_cell", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 99e0880..caa999b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -softbuffer = { version = "0.4.1" } +softbuffer = { version = "0.4.1", default-features = false, features = [ "kms", "x11", "x11-dlopen"] } winit = { version = "0.29.10", default-features = false, features = [ "x11", "rwh_06" ] } diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 0000000..4870f41 --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,349 @@ +const GAME_SIZE: (u32, u32) = (500, 800); +use std::{ + collections::HashMap, + num::NonZeroU32, + process, + rc::Rc, + time::{Duration, Instant}, +}; + +use softbuffer::Buffer; +use winit::{ + dpi::PhysicalSize, + event::{Event, KeyEvent, StartCause, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, NamedKey}, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, + window::WindowBuilder, +}; + +use self::objs::{MovingObject, World}; + +type RenderFn = fn(&mut render::RenderCtx<'_, '_>, &mut World, &GameTimer); +// core game engine struct +pub struct Engine { + event_loop: EventLoop<()>, + render_fn: RenderFn, + world: World, +} + +impl Engine { + pub fn new() -> Self { + let event_loop = EventLoop::new().unwrap(); + + Self { + event_loop, + render_fn: |_, _, _| {}, + world: World::new(), + } + } + + pub fn insert_into_world(&mut self, obj: Rc) -> usize { + self.world.insert(obj) + } + + // sets the render function for the game + pub fn set_render_fn(mut self, f: RenderFn) -> Self { + self.render_fn = f; + self + } + + // runs the game and consumes self, this will finish the process + pub fn run(self) -> ! { + let Self { + event_loop, + mut world, + render_fn, + } = self; + + // set up window + let window = WindowBuilder::new() + .with_inner_size(winit::dpi::Size::Physical(PhysicalSize::new(1200, 700))) + .build(&event_loop) + .unwrap(); + + // set up softbuffer + let context = softbuffer::Context::new(window.display_handle().unwrap()).unwrap(); + let mut surface = + softbuffer::Surface::new(&context, window.window_handle().unwrap()).unwrap(); + + let mut timer = GameTimer::new(); + + event_loop + .run(|event, elwt| { + // shoddy vsync + elwt.set_control_flow(ControlFlow::WaitUntil( + Instant::now() + Duration::from_millis(1000 / 60), + )); + + match event { + // redraw + Event::NewEvents(StartCause::ResumeTimeReached { .. }) => { + window.request_redraw(); + } + Event::WindowEvent { + window_id, + event: WindowEvent::RedrawRequested, + } => { + if window_id == window.id() { + if let (Some(width), Some(height)) = { + let size = window.inner_size(); + (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) + } { + surface.resize(width, height).unwrap(); + let mut buffer = surface.buffer_mut().unwrap(); + + let mut ctx = + render::RenderCtx::new(buffer, (width, height), GAME_SIZE); + + // render + render_fn(&mut ctx, &mut world, &timer); + ctx.force_present(); + timer.frame_update(); + } + } + } + // crash game to exit lol + Event::WindowEvent { + window_id: _window_id, + event: + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Named(NamedKey::Escape), + .. + }, + .. + }, + } => todo!(), + // potential future keyboard handling + Event::WindowEvent { + window_id: _window_id, + event: + WindowEvent::KeyboardInput { + device_id: _device_id, + event: _event, + is_synthetic: _is_synthetic, + }, + } => {} + _ => {} + } + }) + .unwrap(); + process::exit(0); + } +} + +pub struct GameTimer { + game_start: Instant, + last_frame: Instant, + stopwatches: HashMap, +} + +impl GameTimer { + pub fn new() -> Self { + Self { + game_start: Instant::now(), + last_frame: Instant::now(), + stopwatches: HashMap::new(), + } + } + + pub fn frame_update(&mut self) { + self.last_frame = Instant::now() + } + + pub fn delta_t(&self) -> f32 { + Instant::now().duration_since(self.last_frame).as_secs_f32() + } + + pub fn game_time_passed(&self) -> f32 { + Instant::now().duration_since(self.game_start).as_secs_f32() + } + + pub fn start_stopwatch(&mut self, name: impl ToString) { + let _ = self.stopwatches.insert(name.to_string(), Instant::now()); + } + + pub fn get_stopwatch_time(&self, name: &str) -> Option { + self.stopwatches + .get(name) + .map(|start_time| Instant::now().duration_since(*start_time).as_secs_f32()) + } + + pub fn delete_stopwatch(&mut self, name: &str) { + let _ = self.stopwatches.remove(name); + } +} + +mod render; + +pub mod objs { + use std::rc::Rc; + + use super::render::RenderCtx; + + pub struct World { + objects: Vec>, + } + + impl World { + pub fn new() -> Self { + Self { + objects: Vec::new(), + } + } + + pub fn insert(&mut self, obj: Rc) -> usize { + self.objects.push(obj); + self.objects.len() - 1 + } + + pub fn get(&self, i: usize) -> Rc { + self.objects[i].clone() + } + + pub fn get_mut(&mut self, i: usize) -> &mut Rc { + &mut self.objects[i] + } + + pub fn draw_all(&self, ctx: &mut RenderCtx<'_, '_>) { + self.objects.iter().for_each(|obj| obj.display(ctx)) + } + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct Rect { + pos: Position, + size: Size, + } + + impl Rect { + pub fn new(x: u32, y: u32, height: u32, width: u32) -> Self { + Self { + pos: Position::new(x, y), + size: Size::new(width, height), + } + } + + pub fn square(x: u32, y: u32, size: u32) -> Self { + Self { + pos: Position::new(x, y), + size: Size::new(size, size), + } + } + } + + impl Object for Rect { + fn position(&self) -> Position { + self.pos + } + + fn size(&self) -> Size { + self.size + } + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct MovingRect { + prev_pos: Position, + current: Rect, + } + + impl MovingRect { + pub fn new(x: u32, y: u32, height: u32, width: u32) -> Self { + Self { + prev_pos: Position::new(x, y), + current: Rect::new(x, y, height, width), + } + } + + pub fn square(x: u32, y: u32, size: u32) -> Self { + Self { + prev_pos: Position::new(x, y), + current: Rect::square(x, y, size), + } + } + } + + impl Object for MovingRect { + fn position(&self) -> Position { + self.current.position() + } + + fn size(&self) -> Size { + self.current.size() + } + } + + impl MovingObject for MovingRect { + fn previous_pos(&self) -> Position { + self.prev_pos + } + + fn update_pos(&mut self, x: u32, y: u32) { + self.prev_pos = self.current.pos; + self.current.pos = Position::new(x, y); + } + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct Position { + x: u32, + y: u32, + } + + impl Position { + pub fn new(x: u32, y: u32) -> Self { + Self { x, y } + } + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct Size { + width: u32, + height: u32, + } + + impl Size { + pub fn new(width: u32, height: u32) -> Self { + Self { width, height } + } + } + + pub trait Object { + fn position(&self) -> Position; + fn size(&self) -> Size; + fn display(&self, ctx: &mut RenderCtx<'_, '_>) { + let Position { x, y } = self.position(); + let Size { width, height } = self.size(); + ctx.rect(x, y, width, height, 0xffffff); + } + } + + pub trait MovingObject: Object { + fn previous_pos(&self) -> Position; + fn update_pos(&mut self, x: u32, y: u32); + fn move_obj(&mut self, x: i32, y: i32) { + let Position { x: cur_x, y: cur_y } = self.position(); + + self.update_pos( + cur_x.saturating_add_signed(x), + cur_y.saturating_add_signed(y), + ) + } + + // TODO: damage + fn draw_move(&self, ctx: &mut RenderCtx<'_, '_>) { + let Position { + x: prev_x, + y: prev_y, + } = self.previous_pos(); + let Size { width, height } = self.size(); + + ctx.rect(prev_x, prev_y, width, height, 0x000000); + self.display(ctx); + } + } +} diff --git a/src/engine/render.rs b/src/engine/render.rs new file mode 100644 index 0000000..1e5ec7d --- /dev/null +++ b/src/engine/render.rs @@ -0,0 +1,62 @@ +use std::num::NonZeroU32; + +use winit::raw_window_handle::WindowHandle; + +use winit::raw_window_handle::DisplayHandle; + +use softbuffer::Buffer; + +use super::objs::Object; + +// render context +pub struct RenderCtx<'buf, 'win> { + pub(crate) buffer: Buffer<'buf, DisplayHandle<'win>, WindowHandle<'win>>, + pub(crate) win_size: (u32, u32), + pub(crate) context_size: (u32, u32), + pub(crate) context_pos: (u32, u32), +} + +impl<'buf, 'win> RenderCtx<'buf, 'win> { + // create new render context + pub fn new( + buffer: Buffer<'buf, DisplayHandle<'win>, WindowHandle<'win>>, + win_size: (NonZeroU32, NonZeroU32), + context_size: (u32, u32), + ) -> Self { + Self { + buffer, + win_size: (win_size.0.get(), win_size.1.get()), + context_size, + context_pos: ( + (win_size.0.get() / 2).saturating_sub(context_size.0 / 2), + (win_size.1.get() / 2).saturating_sub(context_size.1 / 2), + ), + } + } + + pub fn clear(&mut self, color: u32) { + self.rect(0, 0, self.win_size.0, self.win_size.1, color) + } + + // draw a rectangle in the context + // + // coordinates are relative to the context + pub fn rect(&mut self, x: u32, y: u32, width: u32, height: u32, color: u32) { + // position in buffer coordinates and not relative coordinates + let x_buf_pos = self.context_pos.0 + x; + let y_buf_pos = self.context_pos.1 + y; + + for y in y_buf_pos..(y_buf_pos + height) { + for x in x_buf_pos..(x_buf_pos + width) { + let index = y as usize * self.win_size.0 as usize + x as usize; + if let Some(px) = self.buffer.get_mut(index) { + *px = color + } + } + } + } + + pub fn force_present(self) { + self.buffer.present().unwrap() + } +} diff --git a/src/main.rs b/src/main.rs index a884bfe..816ae69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,181 +1,25 @@ -use engine::Engine; +use std::rc::Rc; + +use engine::{ + objs::{MovingRect, Object, Rect}, + Engine, +}; fn main() { - Engine::new() - .set_render_fn(|mut ctx| { - // draw three colored rectagles(tm) - ctx.rect_unchecked(0, 0, 100, 100, 0xff0000); - ctx.rect_unchecked(100, 0, 100, 100, 0x00ff00); - ctx.rect_unchecked(0, 100, 100, 100, 0x0000ff); + let mut engine = Engine::new(); + let rect_id = engine.insert_into_world(Rc::new(MovingRect::square(0, 0, 1))); + engine + .set_render_fn(|ctx, world, timer| { + println!("t: {}", timer.game_time_passed()); + let obj = Rc::get_mut(world.get_mut(0)).unwrap(); + obj.update_pos( + ((timer.game_time_passed().sin() + 1.0) * 256.) as u32, + ((timer.game_time_passed().cos() + 1.0) * 256.) as u32, + ); + obj.display(ctx) + // Rect::square((timer.game_time_passed() * 20.) as u32, 0, 200).display(ctx); }) .run(); } -mod engine { - const GAME_SIZE: (u32, u32) = (500, 800); - use std::{ - num::NonZeroU32, - process, - time::{Duration, Instant}, - }; - - use softbuffer::Buffer; - use winit::{ - dpi::PhysicalSize, - event::{Event, KeyEvent, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - keyboard::{Key, NamedKey}, - raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle, WindowHandle}, - window::WindowBuilder, - }; - - type RenderFn = fn(&mut RenderCtx<'_, '_>); - // core game engine struct - pub struct Engine { - event_loop: EventLoop<()>, - render_fn: RenderFn, - } - - impl Engine { - pub fn new() -> Self { - let event_loop = EventLoop::new().unwrap(); - - Self { - event_loop, - render_fn: |_| {}, - } - } - - // sets the render function for the game - pub fn set_render_fn(mut self, f: RenderFn) -> Self { - self.render_fn = f; - self - } - - // runs the game and consumes self, this will finish the process - pub fn run(self) -> ! { - let Self { - event_loop, - render_fn, - } = self; - - // set up window - let window = WindowBuilder::new() - .with_inner_size(winit::dpi::Size::Physical(PhysicalSize::new(1200, 700))) - .build(&event_loop) - .unwrap(); - - // set up softbuffer - let context = softbuffer::Context::new(window.display_handle().unwrap()).unwrap(); - let mut surface = - softbuffer::Surface::new(&context, window.window_handle().unwrap()).unwrap(); - - event_loop - .run(|event, elwt| { - // shoddy vsync - elwt.set_control_flow(ControlFlow::WaitUntil( - Instant::now() + Duration::from_millis(1000 / 60), - )); - - match event { - // redraw - Event::NewEvents(StartCause::ResumeTimeReached { - start: _start, - requested_resume: _requested_resume, - }) => { - if let (Some(width), Some(height)) = { - let size = window.inner_size(); - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - } { - surface.resize(width, height).unwrap(); - let mut buffer = surface.buffer_mut().unwrap(); - buffer.fill(0); - - let mut ctx = RenderCtx::new(buffer, (width, height), GAME_SIZE); - - // render - render_fn(&mut ctx); - ctx.force_present(); - } - } - // crash game to exit lol - Event::WindowEvent { - window_id: _window_id, - event: - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - event: - KeyEvent { - logical_key: Key::Named(NamedKey::Escape), - .. - }, - .. - }, - } => todo!(), - // potential future keyboard handling - Event::WindowEvent { - window_id: _window_id, - event: - WindowEvent::KeyboardInput { - device_id: _device_id, - event: _event, - is_synthetic: _is_synthetic, - }, - } => {} - _ => {} - } - }) - .unwrap(); - process::exit(0); - } - } - - // render context - pub struct RenderCtx<'buf, 'win> { - buffer: Buffer<'buf, DisplayHandle<'win>, WindowHandle<'win>>, - win_size: (u32, u32), - context_size: (u32, u32), - context_pos: (u32, u32), - } - - impl<'buf, 'win> RenderCtx<'buf, 'win> { - // create new render context - pub fn new( - buffer: Buffer<'buf, DisplayHandle<'win>, WindowHandle<'win>>, - win_size: (NonZeroU32, NonZeroU32), - context_size: (u32, u32), - ) -> Self { - Self { - buffer, - win_size: (win_size.0.get(), win_size.1.get()), - context_size, - context_pos: ( - (win_size.0.get() / 2).saturating_sub(context_size.0 / 2), - (win_size.1.get() / 2).saturating_sub(context_size.1 / 2), - ), - } - } - - // draw a rectangle in the context - // - // coordinates are relative to the context - pub fn rect_unchecked(&mut self, x: u32, y: u32, width: u32, height: u32, color: u32) { - // position in buffer coordinates and not relative coordinates - let x_buf_pos = self.context_pos.0 + x; - let y_buf_pos = self.context_pos.1 + y; - - for y in y_buf_pos..(y_buf_pos + height) { - for x in x_buf_pos..(x_buf_pos + width) { - let index = y as usize * self.win_size.0 as usize + x as usize; - if let Some(px) = self.buffer.get_mut(index) { - *px = color - } - } - } - } - - pub fn force_present(self) { - self.buffer.present().unwrap() - } - } -} +mod engine;