doukutsu-rs/src/texture_set.rs

255 lines
8.1 KiB
Rust
Raw Normal View History

2020-08-18 16:46:07 +00:00
use std::collections::HashMap;
2020-08-29 06:59:46 +00:00
use std::io::{BufReader, Read, Seek, SeekFrom};
2020-08-18 16:46:07 +00:00
use ggez;
use ggez::{Context, GameError, GameResult, graphics};
use ggez::filesystem;
use ggez::graphics::{Color, Drawable, DrawMode, DrawParam, FilterMode, Image, Mesh, Rect, mint};
use ggez::graphics::spritebatch::SpriteBatch;
use ggez::nalgebra::{Point2, Vector2};
2020-08-29 06:59:46 +00:00
use image::RgbaImage;
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-29 06:59:46 +00:00
use crate::common::FILE_TYPES;
2020-08-19 00:55:21 +00:00
use crate::engine_constants::EngineConstants;
2020-11-02 01:38:39 +00:00
use crate::shared_game_state::{Season, Settings};
use crate::str;
2020-08-18 16:46:07 +00:00
pub static mut g_mag: f32 = 1.0;
2020-08-18 16:46:07 +00:00
pub struct SizedBatch {
pub batch: SpriteBatch,
width: usize,
height: usize,
real_width: usize,
real_height: usize,
scale_x: f32,
scale_y: f32,
}
impl SizedBatch {
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn width(&self) -> usize {
self.width
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn height(&self) -> usize {
self.height
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn dimensions(&self) -> (usize, usize) {
(self.width, self.height)
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn real_dimensions(&self) -> (usize, usize) {
(self.real_width, self.real_height)
}
#[inline(always)]
pub fn scale(&self) -> (f32, f32) {
(self.scale_x, self.scale_y)
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn to_rect(&self) -> common::Rect<usize> {
common::Rect::<usize>::new(0, 0, self.width, self.height)
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
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);
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
pub fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<u16>) {
2020-08-29 06:59:46 +00:00
self.add_rect_scaled(x, y, self.scale_x, self.scale_y, rect)
}
pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
2020-08-20 18:31:47 +00:00
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
return;
}
unsafe {
x = (x * g_mag).floor() / g_mag;
y = (y * g_mag).floor() / g_mag;
}
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))
2020-09-10 11:44:59 +00:00
.dest(mint::Point2 { x, y })
2020-08-29 06:59:46 +00:00
.scale(Vector2::new(scale_x, scale_y));
2020-08-18 16:46:07 +00:00
self.batch.add(param);
}
pub fn add_rect_scaled_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8), scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
return;
}
let param = DrawParam::new()
.color(color.into())
.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(mint::Point2 { x, y })
.scale(Vector2::new(scale_x, scale_y));
self.batch.add(param);
}
2020-09-25 19:25:40 +00:00
#[inline(always)]
2020-08-18 16:46:07 +00:00
pub fn draw(&mut self, ctx: &mut Context) -> GameResult {
self.draw_filtered(FilterMode::Nearest, ctx)
2020-09-25 19:25:40 +00:00
}
pub fn draw_filtered(&mut self, filter: FilterMode, ctx: &mut Context) -> GameResult {
self.batch.set_filter(filter);
2020-08-18 16:46:07 +00:00
self.batch.draw(ctx, DrawParam::new())?;
self.batch.clear();
Ok(())
}
}
pub struct TextureSet {
pub tex_map: HashMap<String, SizedBatch>,
2020-11-02 01:38:39 +00:00
pub paths: Vec<String>,
2020-08-18 16:46:07 +00:00
}
impl TextureSet {
pub fn new(base_path: &str) -> TextureSet {
TextureSet {
tex_map: HashMap::new(),
2020-11-02 01:38:39 +00:00
paths: vec![base_path.to_string(), "".to_string()],
}
}
pub fn apply_seasonal_content(&mut self, season: Season, settings: &Settings) {
if settings.original_textures {
self.paths.insert(0, "/base/ogph/".to_string())
} else if settings.seasonal_textures {
match season {
Season::Halloween => self.paths.insert(0, "/Halloween/season/".to_string()),
Season::Christmas => self.paths.insert(0, "/Christmas/season/".to_string()),
_ => {}
}
2020-08-18 16:46:07 +00:00
}
}
fn make_transparent(rgba: &mut RgbaImage) {
for (r, g, b, a) in rgba.iter_mut().tuples() {
if *r == 0 && *g == 0 && *b == 0 {
*a = 0;
}
}
}
fn load_image(&self, ctx: &mut Context, path: &str) -> GameResult<Image> {
2020-08-19 00:55:21 +00:00
let img = {
2020-08-28 03:44:33 +00:00
let mut buf = [0u8; 8];
2020-08-19 00:55:21 +00:00
let mut reader = filesystem::open(ctx, path)?;
2020-08-28 03:44:33 +00:00
reader.read_exact(&mut buf)?;
reader.seek(SeekFrom::Start(0))?;
let image = image::load(BufReader::new(reader), image::guess_format(&buf)?)?;
let mut rgba = image.to_rgba();
if image.color().channel_count() != 4 {
TextureSet::make_transparent(&mut rgba);
2020-08-19 00:55:21 +00:00
}
rgba
};
let (width, height) = img.dimensions();
2020-08-19 00:55:21 +00:00
Image::from_rgba8(ctx, width as u16, height as u16, img.as_ref())
}
fn load_image_from_buf(&self, ctx: &mut Context, buf: &[u8]) -> GameResult<Image> {
let img = {
let image = image::load_from_memory(buf)?;
let mut rgba = image.to_rgba();
if image.color().channel_count() != 4 {
TextureSet::make_transparent(&mut rgba);
}
2020-08-19 00:55:21 +00:00
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-11-02 01:38:39 +00:00
let path = self.paths.iter().find_map(|s| FILE_TYPES
2020-08-18 16:46:07 +00:00
.iter()
2020-11-02 01:38:39 +00:00
.map(|ext| [s, name, ext].join(""))
.find(|path| {
println!("{}", path);
filesystem::exists(ctx, path)
})
).ok_or_else(|| GameError::ResourceLoadError(format!("Texture {} does not exist.", name)))?;
2020-08-18 16:46:07 +00:00
info!("Loading texture: {}", path);
let image = self.load_image(ctx, &path)?;
2020-08-18 16:46:07 +00:00
let size = image.dimensions();
2020-08-27 02:43:21 +00:00
assert_ne!(size.w as isize, 0, "size.w == 0");
assert_ne!(size.h as isize, 0, "size.h == 0");
2020-08-18 16:46:07 +00:00
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.0 as f32 / size.w;
2020-08-19 00:55:21 +00:00
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) {
2020-08-23 02:16:31 +00:00
let batch = self.load_texture(ctx, constants, name)?;
2020-08-19 13:11:34 +00:00
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
}
2020-09-10 11:44:59 +00:00
pub fn draw_rect(&self, rect: common::Rect, color: [f32; 4], ctx: &mut Context) -> GameResult {
let rect = Mesh::new_rectangle(ctx, DrawMode::fill(), rect.into(), color.into())?;
graphics::draw(ctx, &rect, DrawParam::new())?;
Ok(())
}
pub fn draw_outline_rect(&self, rect: common::Rect, width: f32, color: [f32; 4], ctx: &mut Context) -> GameResult {
let rect = Mesh::new_rectangle(ctx, DrawMode::stroke(width), rect.into(), color.into())?;
graphics::draw(ctx, &rect, DrawParam::new())?;
Ok(())
}
2020-08-18 16:46:07 +00:00
}