2020-08-18 16:46:07 +00:00
|
|
|
use std::collections::HashMap;
|
2020-08-19 00:55:21 +00:00
|
|
|
use std::io::Read;
|
2020-08-18 16:46:07 +00:00
|
|
|
|
2020-08-19 19:11:32 +00:00
|
|
|
use crate::ggez::{Context, GameError, GameResult};
|
|
|
|
use crate::ggez::filesystem;
|
|
|
|
use crate::ggez::graphics::{Drawable, DrawParam, FilterMode, Image, Rect};
|
|
|
|
use crate::ggez::graphics::spritebatch::SpriteBatch;
|
|
|
|
use crate::ggez::nalgebra::{Point2, Vector2};
|
2020-08-19 00:55:21 +00:00
|
|
|
use itertools::Itertools;
|
2020-08-19 11:21:40 +00:00
|
|
|
use log::info;
|
2020-08-18 16:46:07 +00:00
|
|
|
|
|
|
|
use crate::common;
|
2020-08-19 00:55:21 +00:00
|
|
|
use crate::engine_constants::EngineConstants;
|
2020-08-18 16:46:07 +00:00
|
|
|
use crate::str;
|
|
|
|
|
|
|
|
pub struct SizedBatch {
|
|
|
|
pub batch: SpriteBatch,
|
|
|
|
width: usize,
|
|
|
|
height: usize,
|
|
|
|
real_width: usize,
|
|
|
|
real_height: usize,
|
|
|
|
scale_x: f32,
|
|
|
|
scale_y: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SizedBatch {
|
|
|
|
pub fn width(&self) -> usize {
|
|
|
|
self.width
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> usize {
|
|
|
|
self.height
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dimensions(&self) -> (usize, usize) {
|
|
|
|
(self.width, self.height)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn real_dimensions(&self) -> (usize, usize) {
|
|
|
|
(self.real_width, self.real_height)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_rect(&self) -> common::Rect<usize> {
|
|
|
|
common::Rect::<usize>::new(0, 0, self.width, self.height)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.batch.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add(&mut self, x: f32, y: f32) {
|
|
|
|
let param = DrawParam::new()
|
|
|
|
.dest(Point2::new(x, y))
|
|
|
|
.scale(Vector2::new(self.scale_x, self.scale_y));
|
|
|
|
|
|
|
|
self.batch.add(param);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<usize>) {
|
2020-08-20 18:31:47 +00:00
|
|
|
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-18 16:46:07 +00:00
|
|
|
let param = DrawParam::new()
|
|
|
|
.src(Rect::new(rect.left as f32 / self.width as f32,
|
|
|
|
rect.top as f32 / self.height as f32,
|
|
|
|
(rect.right - rect.left) as f32 / self.width as f32,
|
|
|
|
(rect.bottom - rect.top) as f32 / self.height as f32))
|
|
|
|
.dest(Point2::new(x, y))
|
|
|
|
.scale(Vector2::new(self.scale_x, self.scale_y));
|
|
|
|
|
|
|
|
self.batch.add(param);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
|
|
|
self.batch.set_filter(FilterMode::Nearest);
|
|
|
|
self.batch.draw(ctx, DrawParam::new())?;
|
|
|
|
self.batch.clear();
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TextureSet {
|
|
|
|
pub tex_map: HashMap<String, SizedBatch>,
|
|
|
|
base_path: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
static FILE_TYPES: [&str; 3] = [".png", ".bmp", ".pbm"];
|
|
|
|
|
|
|
|
impl TextureSet {
|
|
|
|
pub fn new(base_path: &str) -> TextureSet {
|
|
|
|
TextureSet {
|
|
|
|
tex_map: HashMap::new(),
|
|
|
|
base_path: str!(base_path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 01:59:16 +00:00
|
|
|
fn load_image(&self, ctx: &mut Context, path: &str) -> GameResult<Image> {
|
2020-08-19 00:55:21 +00:00
|
|
|
let img = {
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
let mut reader = filesystem::open(ctx, path)?;
|
|
|
|
let _ = reader.read_to_end(&mut buf)?;
|
2020-08-19 01:59:16 +00:00
|
|
|
let image = image::load_from_memory(&buf)?;
|
|
|
|
let mut rgba = image.to_rgba();
|
2020-08-19 00:55:21 +00:00
|
|
|
|
2020-08-19 01:59:16 +00:00
|
|
|
if image.color().channel_count() != 4 {
|
2020-08-19 00:55:21 +00:00
|
|
|
for (r, g, b, a) in rgba.iter_mut().tuples() {
|
|
|
|
if *r == 0 && *g == 0 && *b == 0 {
|
|
|
|
*a = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rgba
|
|
|
|
};
|
|
|
|
let (width, height) = img.dimensions();
|
|
|
|
|
|
|
|
Image::from_rgba8(ctx, width as u16, height as u16, img.as_ref())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_texture(&self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<SizedBatch> {
|
2020-08-18 16:46:07 +00:00
|
|
|
let path = FILE_TYPES
|
|
|
|
.iter()
|
2020-08-19 00:55:21 +00:00
|
|
|
.map(|ext| [&self.base_path, name, ext].join(""))
|
2020-08-18 16:46:07 +00:00
|
|
|
.find(|path| filesystem::exists(ctx, path))
|
2020-08-19 00:55:21 +00:00
|
|
|
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {:?} does not exist.", name)))?;
|
2020-08-18 16:46:07 +00:00
|
|
|
|
|
|
|
info!("Loading texture: {}", path);
|
|
|
|
|
2020-08-19 01:59:16 +00:00
|
|
|
let image = self.load_image(ctx, &path)?;
|
2020-08-18 16:46:07 +00:00
|
|
|
let size = image.dimensions();
|
|
|
|
|
|
|
|
assert_ne!(size.w, 0.0, "size.w == 0");
|
|
|
|
assert_ne!(size.h, 0.0, "size.h == 0");
|
|
|
|
|
2020-08-19 00:55:21 +00:00
|
|
|
let dim = (size.w as usize, size.h as usize);
|
|
|
|
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &dim);
|
|
|
|
let scale_x = orig_dimensions.0 as f32 / size.w;
|
|
|
|
let scale_y = orig_dimensions.1 as f32 / size.h;
|
|
|
|
let width = (size.w * scale_x) as usize;
|
|
|
|
let height = (size.h * scale_y) as usize;
|
2020-08-18 16:46:07 +00:00
|
|
|
|
|
|
|
Ok(SizedBatch {
|
|
|
|
batch: SpriteBatch::new(image),
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
scale_x,
|
|
|
|
scale_y,
|
|
|
|
real_width: size.w as usize,
|
|
|
|
real_height: size.h as usize,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:11:34 +00:00
|
|
|
pub fn get_or_load_batch(&mut self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<&mut SizedBatch> {
|
|
|
|
if !self.tex_map.contains_key(name) {
|
|
|
|
let mut batch = self.load_texture(ctx, constants, name)?;
|
|
|
|
self.tex_map.insert(str!(name), batch);
|
2020-08-18 16:46:07 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:11:34 +00:00
|
|
|
Ok(self.tex_map.get_mut(name).unwrap())
|
2020-08-18 16:46:07 +00:00
|
|
|
}
|
|
|
|
}
|