use crate::graphics::game::{timeline, transform, construct};
use crate::graphics::utils::boundary::calculate_animation_bounds;
use crate::graphics::utils::periodicity::calculate_difference;
use crate::graphics::data::imgcut::SpriteSheet;
pub use crate::graphics::data::mamodel::Model;
pub use crate::graphics::data::maanim::Animation as Anim;
pub struct Unit {
pub model: Model,
pub sheet: SpriteSheet,
}
impl Unit {
#[inline(always)]
pub fn parse(
png: impl AsRef<[u8]>,
imgcut: impl AsRef<[u8]>,
mamodel: impl AsRef<[u8]>,
) -> Option<Self> {
Self::parse_inner(png.as_ref(), imgcut.as_ref(), mamodel.as_ref())
}
fn parse_inner(png: &[u8], imgcut: &[u8], mamodel: &[u8]) -> Option<Self> {
let sheet = SpriteSheet::parse(png, imgcut)?;
let model = Model::parse(mamodel)?;
Some(Self { model, sheet })
}
pub fn calculate_bounds(
&self,
animations: &[&Anim],
tolerance: f32
) -> Option<(f32, f32, f32, f32)> {
let tolerance = crate::graphics::utils::boundary::Tolerance::new(tolerance);
let bounds = calculate_animation_bounds(&self.model, &self.sheet, animations, tolerance)?;
Some((bounds.min_x, bounds.min_y, bounds.width(), bounds.height()))
}
pub fn calculate_cycle(
&self,
animation: &Anim,
tolerance: f32,
minimum_frame: Option<i32>,
maximum_frame: Option<i32>,
mut progress_callback: impl FnMut(usize) -> bool,
) -> Option<(i32, i32)> {
let mut frame_states: Vec<Vec<([f32; 9], f32)>> = Vec::new();
let mut state_buffer = self.model.parts.clone();
let mut current_frame = 0;
let minimum_loop_length = minimum_frame.unwrap_or(1);
loop {
if !progress_callback(current_frame) {
return None;
}
if let Some(maximum) = maximum_frame
&& current_frame > maximum as usize {
return None;
}
let frame_float = current_frame as f32;
let _ = timeline::animate(&self.model, animation, frame_float, &mut state_buffer);
let world_parts = transform::solve_hierarchy(&state_buffer, &self.model);
let mut current_state = Vec::with_capacity(world_parts.len());
for part in &world_parts {
current_state.push((part.matrix, part.opacity));
}
for (past_frame_index, past_state) in frame_states.iter().enumerate() {
let loop_length = current_frame as i32 - past_frame_index as i32;
if loop_length < minimum_loop_length {
continue;
}
let difference = calculate_difference(¤t_state, past_state);
if difference <= tolerance {
return Some((past_frame_index as i32, current_frame as i32));
}
}
frame_states.push(current_state);
current_frame += 1;
}
}
}
#[derive(Clone, Debug, Default)]
pub struct FrameData {
pub sprite_index: usize,
pub final_matrix: [f32; 9],
pub vertices: [f32; 12],
pub uvs: [f32; 12],
pub opacity: f32,
pub glow: i32,
}
pub fn resolve_frame(
unit: &Unit,
anim: Option<&Anim>,
frame: f32,
) -> Vec<FrameData> {
let parts = if let Some(animation) = anim {
let mut state_buffer = unit.model.parts.clone();
let _ = timeline::animate(&unit.model, animation, frame, &mut state_buffer);
state_buffer
} else {
unit.model.parts.clone()
};
let world_parts = transform::solve_hierarchy(&parts, &unit.model);
construct::build_geometry(&world_parts, &unit.sheet)
}