use sdl2::gfx::primitives::DrawRenderer; use sdl2::pixels::Color; use sdl2::event::Event; use sdl2::rect::{Rect, Point}; use sdl2::keyboard::Keycode; use sdl2::render::Texture; use std::time::Duration; use std::collections::HashMap; struct Camera { x: f32, y: f32, scale: f32, offset: Pos, rotation: Rot, } #[allow(dead_code)] impl Camera { pub fn new() -> Camera { Camera { x: 0.0, y: 0.0, scale: 1.0, offset: Pos(0.0, 0.0), rotation: Rot::new() } } fn translate(&mut self, x: f32, y: f32) { self.x += x; self.y += y; } fn set_pos(&mut self, x: f32, y: f32) { self.x = x; self.y = y; } fn get_pos(&self) -> Pos { Pos(self.x.into(), self.y.into()) } fn get_scale(&self) -> f32 { self.scale } fn set_scale(&mut self, d: f32) { if d.is_finite() { self.scale = d; } } fn modify_scale(&mut self, d: f32) { let d = self.scale + d; if d < 0.1 { self.scale = 0.1 } else { self.scale = d } } fn set_rotation(&mut self, deg: f32) { self.rotation.set(deg); } fn modify_rotation(&mut self, deg: f32) { self.rotation.modify(deg); } fn rotate_point(&self, p_origin: Pos, p: Pos, deg: Rot) -> Pos { let deg = deg.get_deg(); let p = Pos(p.0 - p_origin.0, p.1 - p_origin.1); let p = Pos( p.0 * deg.cos() - p.1 * deg.sin(), p.0 * deg.sin() + p.1 * deg.cos() ); p } fn transform(&self, mut p: Pos, window_size: (u32, u32)) -> Pos { p.0 = self.x + p.0 * self.scale; p.1 = self.y - p.1 * self.scale; let window_size = Pos(window_size.0 as f32, window_size.1 as f32) / 2.0; let mut p = self.rotate_point(Pos(window_size.0, window_size.1), p, self.rotation); p.0 += window_size.0; p.1 += window_size.1; p } fn translate_offset(&mut self, p: Pos) { self.offset += p * (self.get_scale().powi(-1) + 0.3) } fn reset(&mut self) { self.offset = Pos(0.0, 0.0); self.rotation = Rot::new(); self.scale = 1.0; } fn center_on(&mut self, pos: &Pos, window_size: (u32, u32)) { self.x = (window_size.0 / 2) as f32 - (pos.0 * self.scale) - (self.offset.0 * self.scale); self.y = (window_size.1 / 2) as f32 + (pos.1 * self.scale) + (self.offset.1 * self.scale); } } #[derive(Copy, Clone, Debug)] struct Pos(f32, f32); impl std::ops::Add for Pos { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0, self.1 + rhs.1) } } impl std::ops::Sub for Pos { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { Self(self.0 - rhs.0, self.1 - rhs.1) } } impl std::ops::Div for Pos { type Output = Self; fn div(self, rhs: f32) -> Self::Output { Pos(self.0 / rhs, self.1 / rhs) } } impl std::ops::Mul for Pos { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { Pos(self.0 * rhs, self.1 * rhs) } } impl std::ops::AddAssign for Pos { fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; self.1 += rhs.1; } } impl From for Point { fn from(value: Pos) -> Self { (value.0 as i32, value.1 as i32).into() } } impl From for Pos { fn from(value: Point) -> Self { Pos(value.x as f32, value.y as f32) } } const PI: f32 = 3.14159; #[derive(Copy, Clone, Debug)] struct Rot(f32); impl Rot { fn new() -> Rot { Rot(0.0) } fn modify(&mut self, deg: f32) { self.0 = Rot::sanitize(self.0 + deg); } fn set(&mut self, deg: f32) { self.0 = Rot::sanitize(deg); } #[allow(dead_code)] fn get(&self) -> f32 { self.0 } fn sanitize(deg: f32) -> f32 { if deg > 360.0 { -360.0 + deg.fract() } else if deg < -360.0 { Rot::sanitize(deg * -1.0) * -1.0 } else { deg } } fn get_deg(&self) -> f32 { ((self.0 / 360.0) * PI) * -1.0 } } #[derive(Copy, Clone)] enum Object { Circle(Pos, Rot, f32), Rect(Rot, Rect), } impl Object { fn get_pos(&self) -> Pos { match self { Self::Circle(p, _, _) => *p, Self::Rect(_, rect) => {rect.center().into()}, } } fn translate(&mut self, pos: Pos) { match self { Self::Circle(p, _, _) => { let new = *p + pos; p.0 = new.0; p.1 = new.1; }, Self::Rect(_, rect) => { rect.offset(pos.0 as i32, pos.1 as i32) } } } fn modify_rotation(&mut self, deg: f32) { match self { Self::Circle(_, r, _) => { r.modify(deg) }, Self::Rect(r, _) => { r.modify(deg) } } } fn draw(&mut self, canvas: &mut sdl2::render::Canvas, cam: &Camera, color: Color, t: Option<&Texture>) { let x = match self { Self::Circle(p, _, rad) => { let p = cam.transform(*p, canvas.output_size().unwrap()); canvas.aa_circle(p.0 as i16, p.1 as i16, (*rad * cam.scale) as i16, color) }, Self::Rect(r, rect) => { let r = *r; let window_size = canvas.output_size().unwrap(); let center = rect.center().into(); let tl = rect.top_left().into(); let tr = rect.top_right().into(); let bl = rect.bottom_left().into(); let br = rect.bottom_right().into(); let tl = cam.rotate_point(center, tl, r) + center; let tr = cam.rotate_point(center, tr, r) + center; let bl = cam.rotate_point(center, bl, r) + center; let br = cam.rotate_point(center, br, r) + center; let tl = cam.transform(tl, window_size); let tr = cam.transform(tr, window_size); let bl = cam.transform(bl, window_size); let br = cam.transform(br, window_size); let pos1 = Point::from(tl); // Top Left let pos2 = Point::from(tr); // Top Right let pos3 = Point::from(br); // Bottom Right let pos4 = Point::from(bl); // Bottom Left match t { Some(n) => { canvas.copy_ex(n, *rect, None, r.get_deg() as f64, rect.center(), false, false) }, None => { canvas.aa_polygon(&[pos1.x as i16, pos2.x as i16, pos3.x as i16, pos4.x as i16], &[pos1.y as i16, pos2.y as i16, pos3.y as i16, pos4.y as i16], color) } } } }; match x { Ok(_) => (), Err(n) => println!("Draw Error: {}", n), } } fn new_rect(p: Pos, p2: Pos) -> Object { let w = p2.0 - p.0; let h = p2.1 - p.1; let center = Pos(p.0 - (w / 2.0), p.1 - (h / 2.0)); Self::Rect(Rot::new(), Rect::new(center.0 as i32, center.1 as i32, w as u32, h as u32)) } } struct KeyState(HashMap); impl KeyState { fn new() -> KeyState { KeyState ( HashMap::new() ) } #[allow(dead_code)] fn get(&self, k: Keycode) -> bool { *self.0.get(&k).unwrap_or(&false) } fn set(&mut self, k: Keycode, v: bool) { self.0.insert(k, v); } fn pairs(&self) -> Vec<(Keycode, bool)> { self.0.clone().into_iter().collect() } } fn main() { println!("--Program start--"); let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem.window("rust-sdl2 demo", 800, 600) .position_centered() .build() .unwrap(); let mut canvas = window.into_canvas().build().unwrap(); // let texture_creator = canvas.texture_creator(); let mut event_pump = sdl_context.event_pump().unwrap(); let mut cam = Camera::new(); let mut keymap = KeyState::new(); // let mut character = Object::Circle(Pos(0.0, 0.0), Rot::new(), 10.0); let mut character = Object::new_rect(Pos(0.0, 0.0), Pos(100.0, 100.0)); let mut degree = Rot::new(); 'main: loop { degree.modify(0.8 * PI); // Clear the frame canvas.set_draw_color(Color::RGB(128, 128, 128)); canvas.clear(); let window_size = canvas.output_size().unwrap(); // Process events for event in event_pump.poll_iter() { match event { Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { // Quit program without saving break 'main }, Event::KeyDown { keycode: Some(n), ..} => { keymap.set(n, true); } Event::KeyUp { keycode: Some(n), .. } => { keymap.set(n, false) } _ => () } } // Process keymap for x in keymap.pairs() { match x { (n, true) => { use Keycode::*; match n { // Character translation W => character.translate(Pos(0.0, 5.0)), A => character.translate(Pos(-5.0, 0.0)), S => character.translate(Pos(0.0, -5.0)), D => character.translate(Pos(5.0, 0.0)), // Character rotation Q => character.modify_rotation(-2.0), E => character.modify_rotation(2.0), // Camera translation (local axis) Up => cam.translate_offset(Pos(0.0, 5.0)), Left => cam.translate_offset(Pos(-5.0, 0.0)), Down => cam.translate_offset(Pos(0.0, -5.0)), Right => cam.translate_offset(Pos(5.0, 0.0)), // Camera translation (global axis) I => cam.translate_offset(cam.rotate_point(Pos(0.0, 0.0), Pos(0.0, 5.0), cam.rotation)), J => cam.translate_offset(cam.rotate_point(Pos(0.0, 0.0), Pos(-5.0, 0.0), cam.rotation)), K => cam.translate_offset(cam.rotate_point(Pos(0.0, 0.0), Pos(0.0, -5.0), cam.rotation)), L => cam.translate_offset(cam.rotate_point(Pos(0.0, 0.0), Pos(5.0, 0.0), cam.rotation)), // Camera rotation U => cam.modify_rotation(-2.0), O => cam.modify_rotation(2.0), // Camera scale N => cam.modify_scale(0.1), M => cam.modify_scale(-0.1), // Reset Camera Space => cam.reset(), _ => (), } }, _ => () } } // Center on the character cam.center_on(&character.get_pos(), canvas.output_size().unwrap()); //Draw stuff let mut wall = Object::Circle(Pos(-15.0, -100.0), Rot::new(), 5.0); wall.draw(&mut canvas, &cam, Color::RGB(255, 255, 255), None); // let rect_pos = cam.transform(Pos(0.0, 0.0)); // let rect = Rect::new(rect_pos.0 as i32, rect_pos.1 as i32, 5, 5); // canvas.draw_rect(rect).unwrap(); // let mut circle = Object::Circle(Pos(50.0, 50.0), 100.0); // circle.draw(&canvas, &cam, Color::RGB(255,255,255)); canvas.set_draw_color(Color::RGB(50, 255, 50)); let start = cam.transform(Pos(0.0, -300.0), window_size); let end = cam.transform(Pos(0.0, 300.0), window_size); canvas.draw_line(start, end); let start = cam.transform(Pos(-300.0, 0.0), window_size); let end = cam.transform(Pos(300.0, 0.0), window_size); canvas.draw_line(start, end); let trigon_center = Pos(30.0, -30.0); let trigon_1 = cam.transform(cam.rotate_point(trigon_center, Pos(0.0, 0.0), degree), window_size); let trigon_2 = cam.transform(cam.rotate_point(trigon_center, Pos(-20.0, -60.0), degree), window_size); let trigon_3 = cam.transform(cam.rotate_point(trigon_center, Pos(20.0, -60.0), degree), window_size); canvas.trigon(trigon_1.0 as i16, trigon_1.1 as i16, trigon_2.0 as i16, trigon_2.1 as i16, trigon_3.0 as i16, trigon_3.1 as i16, Color::RGB(255, 255, 255)) .unwrap(); // Draw player character.draw(&mut canvas, &cam, Color::RGB(255, 0, 255), None); // Draw text // let s_pos = cam.transform(Pos(50.0, 50.0), window_size); // canvas.string(s_pos.0 as i16, s_pos.1 as i16, "Hello", Color::RGB(255, 255, 255)); // Present the screen and sleep for 1/60th a second canvas.present(); ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); } }