use image::io::Reader as ImageReader; use sdl2::{ rect::{Point, Rect}, render::{Canvas, Texture, TextureCreator}, video::{Window, WindowContext}, }; use std::collections::HashMap; use crate::physics::Cartesian; /// A handle for a sprite inside a SpriteAtlas pub struct Sprite<'a> { texture: &'a Texture<'a>, rect: Rect, scale: f64, } impl<'a> Sprite<'a> { /// Draw this sprite on the screen. /// /// Position represents the center of the sprite /// on-screen position, NOT in the world. pub fn draw( &self, canvas: &mut Canvas, position: Cartesian, angle: f64, scale: f64, ) -> Result<(), String> { let win_size = Cartesian::from(canvas.window().size()); let scale = scale * self.scale; // Post-scale dimensions on the screen let width = self.rect.width() as f64 * scale; let height = self.rect.height() as f64 * scale; // Don't draw if we're not on the screen. // An offset is included to ensure we're completely // off the screen. We add the whole width intentionally. if position.x < -1.0 * (width as f64) || position.x > win_size.x + width as f64 || position.y < -1.0 * (height as f64) || position.y > win_size.y + height as f64 { return Ok(()); } let mut dest = Rect::new(0, 0, width as u32, height as u32); dest.center_on(Point::new((position.x) as i32, (position.y) as i32)); // copy the frame to the canvas canvas.copy_ex( &self.texture, Some(self.rect), Some(dest), angle, // angle Point::new((width / 2.0) as i32, (height / 2.0) as i32), // Rotation center false, false, )?; /* canvas.set_draw_color(Color::RGB(255, 0, 0)); canvas.aa_circle( position.x as i16, position.y as i16, 5, Color::RGB(255, 0, 0), )?; canvas.set_draw_color(Color::RGB(0, 0, 0)); */ return Ok(()); } } /// A cache of textures we use when drawing the screen. /// /// This is implemented very carefully, since SDL2 textures have tricky lifetimes. pub struct SpriteAtlas<'a> { data: HashMap, Rect, f64)>, } impl<'a> SpriteAtlas<'a> { pub fn new(texture_creator: &'a TextureCreator) -> Result { let mut b = Self { data: HashMap::new(), }; b.load_one(texture_creator, "gypsum.png", 0.75)?; b.load_one(texture_creator, "a0.png", 1.0)?; b.load_one(texture_creator, "small.png", 1.0)?; b.load_one(texture_creator, "earth.png", 1.0)?; return Ok(b); } pub fn get(&'a self, name: &str) -> Sprite<'a> { let (texture, rect, scale) = self.data.get(name).unwrap(); return Sprite { texture, scale: scale.clone(), rect: rect.clone(), }; } fn load_one( &mut self, texture_creator: &'a TextureCreator, s: &str, scale: f64, ) -> Result<(), String> { let im = ImageReader::open(format!("assets/{s}")) .unwrap() .decode() .unwrap(); let width = im.width(); let height = im.height(); let mut im = im.as_bytes().to_vec(); let surface = sdl2::surface::Surface::from_data( &mut im, width, height, width * 4, sdl2::pixels::PixelFormatEnum::RGBA32, )?; let texture = texture_creator .create_texture_from_surface(&surface) .map_err(|e| e.to_string())?; self.data.insert( s.to_owned(), (texture, Rect::new(0, 0, width, height), scale), ); return Ok(()); } }