Galactica/src/sprite.rs

142 lines
3.3 KiB
Rust
Raw Normal View History

2023-12-20 19:05:12 -08:00
use image::io::Reader as ImageReader;
use sdl2::{
2023-12-20 20:31:09 -08:00
rect::{Point, Rect},
render::{Canvas, Texture, TextureCreator},
video::{Window, WindowContext},
2023-12-20 19:05:12 -08:00
};
use std::collections::HashMap;
2023-12-21 11:26:44 -08:00
use crate::physics::Cartesian;
2023-12-20 20:31:09 -08:00
2023-12-20 19:05:12 -08:00
/// A handle for a sprite inside a SpriteAtlas
pub struct Sprite<'a> {
2023-12-20 20:31:09 -08:00
texture: &'a Texture<'a>,
rect: Rect,
scale: f64,
}
impl<'a> Sprite<'a> {
/// Draw this sprite on the screen.
///
2023-12-20 20:53:23 -08:00
/// Position represents the center of the sprite
/// on-screen position, NOT in the world.
2023-12-20 20:31:09 -08:00
pub fn draw(
&self,
canvas: &mut Canvas<Window>,
2023-12-21 11:26:44 -08:00
position: Cartesian,
2023-12-20 20:31:09 -08:00
angle: f64,
2023-12-20 20:36:18 -08:00
scale: f64,
2023-12-20 20:31:09 -08:00
) -> Result<(), String> {
2023-12-21 11:26:44 -08:00
let win_size = Cartesian::from(canvas.window().size());
2023-12-20 20:36:18 -08:00
let scale = scale * self.scale;
2023-12-20 20:31:09 -08:00
2023-12-20 20:53:23 -08:00
// Post-scale dimensions on the screen
let width = self.rect.width() as f64 * scale;
let height = self.rect.height() as f64 * scale;
2023-12-20 20:31:09 -08:00
// 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(());
}
2023-12-20 20:53:23 -08:00
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));
2023-12-20 20:31:09 -08:00
// copy the frame to the canvas
canvas.copy_ex(
&self.texture,
Some(self.rect),
Some(dest),
2023-12-20 20:53:23 -08:00
angle, // angle
Point::new((width / 2.0) as i32, (height / 2.0) as i32), // Rotation center
2023-12-20 20:31:09 -08:00
false,
false,
)?;
2023-12-21 11:26:44 -08:00
/*
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));
*/
2023-12-20 20:31:09 -08:00
return Ok(());
}
2023-12-20 19:05:12 -08:00
}
/// 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> {
2023-12-20 20:31:09 -08:00
data: HashMap<String, (Texture<'a>, Rect, f64)>,
2023-12-20 19:05:12 -08:00
}
impl<'a> SpriteAtlas<'a> {
pub fn new(texture_creator: &'a TextureCreator<WindowContext>) -> Result<Self, String> {
let mut b = Self {
data: HashMap::new(),
};
2023-12-20 20:36:18 -08:00
b.load_one(texture_creator, "gypsum.png", 0.75)?;
2023-12-20 20:31:09 -08:00
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)?;
2023-12-20 19:05:12 -08:00
return Ok(b);
}
pub fn get(&'a self, name: &str) -> Sprite<'a> {
2023-12-20 20:31:09 -08:00
let (texture, rect, scale) = self.data.get(name).unwrap();
2023-12-20 19:05:12 -08:00
return Sprite {
texture,
2023-12-20 20:31:09 -08:00
scale: scale.clone(),
2023-12-20 19:05:12 -08:00
rect: rect.clone(),
};
}
fn load_one(
&mut self,
texture_creator: &'a TextureCreator<WindowContext>,
s: &str,
2023-12-20 20:31:09 -08:00
scale: f64,
2023-12-20 19:05:12 -08:00
) -> 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())?;
2023-12-20 20:31:09 -08:00
self.data.insert(
s.to_owned(),
(texture, Rect::new(0, 0, width, height), scale),
);
2023-12-20 19:05:12 -08:00
return Ok(());
}
}