diff --git a/game/Cargo.lock b/game/Cargo.lock index 0d7efc4..3a06627 100644 --- a/game/Cargo.lock +++ b/game/Cargo.lock @@ -104,6 +104,7 @@ dependencies = [ "lazy_static", "libc", "sdl3-sys", + "sdl3-ttf-sys", ] [[package]] @@ -112,6 +113,15 @@ version = "0.6.1+SDL-3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d73fc820b0b6b9bd0557de7b799ec9da1ca4f35fcd1cb42f7ceb85e640d1477" +[[package]] +name = "sdl3-ttf-sys" +version = "0.6.0+SDL-ttf-3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c30da3dcd7e062f19350b47a532f7629c42801b801687535fd31e8ba2adfd71d" +dependencies = [ + "sdl3-sys", +] + [[package]] name = "skate-slope" version = "0.1.0" diff --git a/game/Cargo.toml b/game/Cargo.toml index ef3aa1c..2b0ca34 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] -sdl3 = "0.17.3" +sdl3 = { version = "0.17.3", features = ["ttf"] } rand = "0.8" diff --git a/game/assets/BitcountGridDouble/BitcountGridDouble.ttf b/game/assets/BitcountGridDouble/BitcountGridDouble.ttf new file mode 100644 index 0000000..78fd014 Binary files /dev/null and b/game/assets/BitcountGridDouble/BitcountGridDouble.ttf differ diff --git a/game/assets/BitcountGridDouble/OFL.txt b/game/assets/BitcountGridDouble/OFL.txt new file mode 100644 index 0000000..656ede7 --- /dev/null +++ b/game/assets/BitcountGridDouble/OFL.txt @@ -0,0 +1,93 @@ +Copyright 1980 The Bitcount Project Authors (https://github.com/petrvanblokland/TYPETR-Bitcount) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/game/src/engine/game.rs b/game/src/engine/game.rs index 2960304..efa815f 100644 --- a/game/src/engine/game.rs +++ b/game/src/engine/game.rs @@ -11,6 +11,9 @@ pub trait Io> { } pub trait Renderer { + fn load_text(&mut self, text: &str, size: f64, color: Color) -> u32; + fn draw_texture(&mut self, id: u32, pos: V2); + fn query_texture(&mut self, id: u32) -> V2; fn draw_rect(&mut self, pos: V2, size: V2, color: Color); fn draw_point(&mut self, pos: V2, color: Color); fn draw_line(&mut self, from: V2, to: V2, color: Color); diff --git a/game/src/engine/sdl_io.rs b/game/src/engine/sdl_io.rs index 89e91a3..09f5256 100644 --- a/game/src/engine/sdl_io.rs +++ b/game/src/engine/sdl_io.rs @@ -1,12 +1,16 @@ -use std::time::{Duration, Instant}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; use sdl3::{ event::Event, keyboard::Keycode, pixels::{Color as SdlColor, FColor, PixelFormat}, rect::Point, - render::{Canvas, FPoint, FRect, Vertex, VertexIndices}, - video::Window, + render::{Canvas, FPoint, FRect, Texture, TextureCreator, Vertex, VertexIndices}, + ttf::{Font, Sdl3TtfContext}, + video::{Window, WindowContext}, Sdl, VideoSubsystem, }; @@ -20,16 +24,67 @@ use crate::engine::{ pub static WIDTH: f64 = 1280.0; pub static HEIGHT: f64 = 720.0; -pub struct SdlIo { +pub struct SdlIo<'me> { sdl_context: Sdl, video_subsystem: VideoSubsystem, + ttf_context: Sdl3TtfContext, canvas: Canvas, + texture_handler: TextureHandler<'me>, } -impl SdlIo { +mod textures { + use std::collections::HashMap; + + use sdl3::{ + render::{Texture, TextureCreator}, + ttf::Font, + video::WindowContext, + }; + + use crate::engine::Color; + + pub struct TextureHandler<'me> { + texture_creator: TextureCreator, + textures: HashMap>, + id_counter: u32, + } + + impl<'me> TextureHandler<'me> { + pub fn new(texture_creator: TextureCreator) -> Self { + Self { + texture_creator, + textures: Default::default(), + id_counter: 0, + } + } + pub fn render_font(&mut self, font: Font, text: &str, color: Color) -> u32 { + let surface = font.render(text).blended(color).unwrap(); + let texture = unsafe { + let static_texture_creator = &self.texture_creator as *const _; + let texture: Texture<'me> = surface.as_texture(&*static_texture_creator).unwrap(); + texture + }; + let id = self.id_counter; + self.textures.insert(id, texture); + self.id_counter += 1; + id + } + pub fn get(&self, id: u32) -> &Texture<'me> { + &self.textures[&id] + } + pub fn remove(&mut self, id: u32) { + self.textures.remove(&id); + } + } +} + +use textures::*; + +impl SdlIo<'_> { pub fn new() -> Result { let sdl_context = sdl3::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); + let ttf_context = sdl3::ttf::init().map_err(|e| e.to_string())?; let window = video_subsystem .window("Game", WIDTH as u32, HEIGHT as u32) .position_centered() @@ -41,10 +96,14 @@ impl SdlIo { canvas.clear(); canvas.present(); + let texture_handler = TextureHandler::new(canvas.texture_creator()); + Ok(Self { sdl_context, video_subsystem, + ttf_context, canvas, + texture_handler, }) } @@ -119,7 +178,7 @@ impl SdlIo { } } -impl Renderer for SdlIo { +impl Renderer for SdlIo<'_> { fn draw_rect(&mut self, pos: V2, size: V2, color: Color) { let pos = self.point_w2s(pos); let size = self.scale_w2s(size); @@ -181,6 +240,35 @@ impl Renderer for SdlIo { .render_geometry(&vertices, None, VertexIndices::Sequential) .unwrap(); } + + fn draw_texture(&mut self, id: u32, pos: V2) { + let texture = self.texture_handler.get(id); + let target = FRect::new( + pos.0 as f32, + pos.1 as f32, + texture.width() as f32, + texture.height() as f32, + ); + self.canvas.copy(&texture, None, Some(target)).unwrap(); + self.texture_handler.remove(id); + } + + fn load_text(&mut self, text: &str, size: f64, color: Color) -> u32 { + let font = self + .ttf_context + .load_font( + "assets/BitcountGridDouble/BitcountGridDouble.ttf", + size as f32, + ) + .unwrap(); + let id = self.texture_handler.render_font(font, text, color); + id + } + + fn query_texture(&mut self, id: u32) -> V2 { + let texture = self.texture_handler.get(id); + V2(texture.width() as f64, texture.height() as f64) + } } impl From for FPoint { diff --git a/game/src/main.rs b/game/src/main.rs index c422249..2aef94f 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -6,7 +6,7 @@ use rand::Rng; use core::panic; use std::{ - collections::HashSet, + collections::{HashSet, VecDeque}, f64::consts::PI, sync::{Arc, Mutex}, thread, @@ -14,7 +14,7 @@ use std::{ }; use crate::{ - engine::{Color, Key, Renderer, Scene, Shape, V2, V3}, + engine::{Color, Key, Renderer, Scene, Shape, V2, V3, WIDTH}, server::Server, }; @@ -26,11 +26,12 @@ struct Skateboard { rot: V3, nyoom_factor: f64, pivot_deg: f64, + pivot_deg_target: f64, } impl Skateboard { fn update(&mut self, delta_time: Duration) { - self.vel.0 = self.pivot_deg * delta_time.as_secs_f64(); + self.vel.0 = self.pivot_deg * 2.0 * delta_time.as_secs_f64(); self.pos += self.vel * delta_time.as_secs_f64(); self.nyoom_factor += 16.0 * delta_time.as_secs_f64(); } @@ -156,11 +157,12 @@ struct Game { segments: Vec, camera_pos: V3, keys_pressed: HashSet, + event_queue: Arc>>, } impl Game { fn new() -> Self { - let start_pos = V3(0.0, -0.15, -0.4); + let start_pos = V3(0.0, -0.15, 0.0); Self { skateboard: Skateboard { start_pos, @@ -170,8 +172,10 @@ impl Game { rot: V3(0.0, PI * 0.5, 0.0), nyoom_factor: 0.0, pivot_deg: 0.0, + pivot_deg_target: 0.0, }, - camera_pos: V3(0.0, 0.0, -1.0), + event_queue: Arc::new(Mutex::new(VecDeque::new())), + camera_pos: V3(0.0, 0.0, -0.6), segments: Vec::new(), keys_pressed: HashSet::new(), } @@ -200,23 +204,32 @@ impl engine::Game for Game { self.skateboard.pos.2 - 0.4, ); - if self.keys_pressed.contains(&Key::Left) == self.keys_pressed.contains(&Key::Right) { - let decay_rate = 1.0 - (4.0 * delta_time.as_secs_f64()); - self.skateboard.pivot_deg *= decay_rate; - } + // if self.keys_pressed.contains(&Key::Left) == self.keys_pressed.contains(&Key::Right) { + // let decay_rate = 1.0 - (4.0 * delta_time.as_secs_f64()); + // self.skateboard.pivot_deg *= decay_rate; + // } if self.keys_pressed.contains(&Key::Left) { self.skateboard.pivot_deg -= 36.0 * delta_time.as_secs_f64(); - if self.skateboard.pivot_deg < -12.5 { - self.skateboard.pivot_deg = -12.5; - } } if self.keys_pressed.contains(&Key::Right) { self.skateboard.pivot_deg += 36.0 * delta_time.as_secs_f64(); - if self.skateboard.pivot_deg > 12.5 { - self.skateboard.pivot_deg = 12.5; + } + + { + let mut angles = self.event_queue.lock().unwrap(); + + for angle in angles.drain(..) { + self.skateboard.pivot_deg_target = angle / 2.0; } } + self.skateboard.vel.2 += 0.02 * delta_time.as_secs_f64(); + + self.skateboard.pivot_deg += (self.skateboard.pivot_deg_target - self.skateboard.pivot_deg) + * 4.0 + * delta_time.as_secs_f64(); + self.skateboard.pivot_deg = self.skateboard.pivot_deg.max(-12.5).min(12.5); + let ids = self .segments .iter() @@ -256,6 +269,13 @@ impl engine::Game for Game { object.render(&mut scene); } scene.render(r, self.camera_pos); + let id = r.load_text( + &format!("{}", (self.skateboard.pos.2 * 100.0) as u32), + 48.0, + Color::Green, + ); + let V2(width, _height) = r.query_texture(id); + r.draw_texture(id, V2(WIDTH / 2.0 - width / 2.0, 50.0)); } fn event(&mut self, event: engine::Event) { @@ -400,18 +420,20 @@ impl ShapeGroup { } fn main() -> Result<(), Box> { - let t = thread::spawn(|| { + let mut sdl_io = engine::SdlIo::new()?; + + let mut game = Game::new(); + let event_queue = game.event_queue.clone(); + let _ = thread::spawn(move || { let mut server = Server::new("10.133.51.127:8888").unwrap(); server .subscribe(|measurement| { + event_queue.lock().unwrap().push_back(measurement.angle); println!("angle = {}", measurement.angle); }) .unwrap(); }); - let mut sdl_io = engine::SdlIo::new()?; - let mut game = Game::new(); - let first_segment: Segment = Segment::new( 0, vec![Obstacle {