This repository has been archived on 2022-04-04. You can view files and clone it, but cannot push or open issues or pull requests.
2022-03-29 14:13:18 -04:00

158 lines
5.0 KiB
Rust

//! This module handles the code for rendering framerate-locked animations from textures
use nalgebra::Vector2;
use raylib::{
color::Color,
math::Rectangle,
prelude::{RaylibDraw, RaylibDrawHandle},
texture::Texture2D,
RaylibHandle, RaylibThread,
};
use serde::Deserialize;
use crate::asset_manager::{
load_json_structure, load_known_sprite_types, load_texture_from_internal_data,
InternalJsonLoadError,
};
/// Possible errors to be thrown during the animation texture loading process
#[derive(Debug, thiserror::Error)]
pub enum AnimatedTextureLoadError {
#[error(transparent)]
MetadataLoadError(#[from] InternalJsonLoadError),
#[error(transparent)]
KnownSpriteTypesLoadError(#[from] serde_json::Error),
#[error("Invalid Sprite Type: {0}")]
InvalidSpriteType(String),
#[error(transparent)]
TextureLoadError(#[from] crate::asset_manager::ResourceLoadError),
}
/// Definition for the structure describing a frame's size and position in a texture
#[derive(Debug, Clone, Deserialize)]
struct FrameTextureDescriptor {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Into<Rectangle> for FrameTextureDescriptor {
fn into(self) -> Rectangle {
Rectangle::new(self.x, self.y, self.width, self.height)
}
}
/// Definition for the metadata structure attached to each spritesheet
#[derive(Debug, Clone, Deserialize)]
struct AnimatedTextureMetadata {
pub sheet_height: u64,
pub sheet_width: u64,
pub fps: f32,
pub frames: Vec<FrameTextureDescriptor>,
}
#[derive(Debug)]
pub struct AnimatedTexture {
texture: Texture2D,
texture_metadata: AnimatedTextureMetadata,
// a list of source rects to reduce memory allocation needs during render time
texture_source_rects: Vec<Rectangle>,
}
impl AnimatedTexture {
/// Construct a new `AnimatedTexture`
#[profiling::function]
pub fn new(
raylib_handle: &mut RaylibHandle,
thread: &RaylibThread,
sprite_type: &str,
sprite_name: &str,
) -> Result<Self, AnimatedTextureLoadError> {
// Try to convert the sprite type string to a real type
let known_sprite_types = load_known_sprite_types()?;
let sprite_type_obj = known_sprite_types.iter().find(|known_sprite_type| {
known_sprite_type.short_name == sprite_type
|| known_sprite_type.friendly_name == sprite_type
});
if let None = sprite_type_obj {
error!("Invalid sprite type supplied: {}", sprite_type);
return Err(AnimatedTextureLoadError::InvalidSpriteType(
sprite_type.to_string(),
));
}
let sprite_type_obj = sprite_type_obj.unwrap();
// Now, we can construct the paths to the texture and metadata
let parent_dir_path = format!(
"assets/anm/{}/{}_{}",
sprite_type_obj.short_name, sprite_type_obj.short_name, sprite_name
);
let metadata_file_path = format!(
"{}/{}_{}.anim_meta.json",
parent_dir_path, sprite_type_obj.short_name, sprite_name
);
let texture_file_path = format!(
"{}/{}_{}.png",
parent_dir_path, sprite_type_obj.short_name, sprite_name
);
// Attempt to load the metadata
let texture_metadata: AnimatedTextureMetadata = load_json_structure(&metadata_file_path)?;
let source_rects = texture_metadata
.frames
.iter()
.map(|frame_descriptor| frame_descriptor.clone().into())
.collect();
// Attempt to load the texture itself
let texture = load_texture_from_internal_data(raylib_handle, thread, &texture_file_path)?;
Ok(Self {
texture,
texture_metadata,
texture_source_rects: source_rects,
})
}
#[profiling::function]
pub fn render_frame_by_index(
&self,
draw_handle: &mut RaylibDrawHandle,
index: usize,
position: Vector2<f32>,
percent_scale: Option<Vector2<f32>>,
origin: Option<Vector2<f32>>,
rotation: Option<f32>,
tint: Option<Color>,
) {
// Get the frame-specific metadata
let metadata = &self.texture_metadata.frames[index];
// Build a source rectangle
let source = self.texture_source_rects[index];
// Build a destination rectangle
let scaler = percent_scale.unwrap_or(Vector2::new(1.0, 1.0));
let destination = Rectangle::new(
position.x,
position.y,
metadata.width * scaler.x,
metadata.height * scaler.y,
);
let origin: raylib::core::math::Vector2 =
origin.unwrap_or_else(|| Vector2::<f32>::zeros()).into();
debug!("{:?} -> {:?}", source, destination);
// Render the frame
draw_handle.draw_texture_pro(
&self.texture,
source,
destination,
origin,
rotation.unwrap_or(0.0),
tint.unwrap_or(Color::WHITE),
);
}
}