142 lines
3.3 KiB
Rust
142 lines
3.3 KiB
Rust
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<Window>,
|
|
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<String, (Texture<'a>, Rect, f64)>,
|
|
}
|
|
|
|
impl<'a> SpriteAtlas<'a> {
|
|
pub fn new(texture_creator: &'a TextureCreator<WindowContext>) -> Result<Self, String> {
|
|
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<WindowContext>,
|
|
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(());
|
|
}
|
|
}
|