use std::ops::Range;
use wgpu::{util::BufferInitDescriptor, PrimitiveTopology};
use crate::{
core::components::{
material::Material, maths::coordinates::Coordinates, tiles::tileset::Tileset,
},
rendering::{gl_representations::TexturedGlVertex, Renderable2D},
};
use crate::core::components::maths::Pivot;
use crate::utils::maths::Vector;
const INDICES: &[u16] = &[0, 1, 3, 3, 1, 2];
#[derive(Debug)]
pub struct Sprite {
tile_number: usize,
contents: Option<[TexturedGlVertex; 4]>,
dirty: bool,
pivot: Pivot,
}
impl Sprite {
pub fn new(tile_number: usize) -> Self {
Self { tile_number, contents: None, dirty: false, pivot: Pivot::TopLeft }
}
pub fn pivot(self, pivot: Pivot) -> Self {
Self { tile_number: self.tile_number, contents: None, dirty: false, pivot }
}
pub fn set_tile_nb(&mut self, new_tile_nb: usize) {
self.tile_number = new_tile_nb;
self.dirty = true;
}
pub fn get_tile_nb(&self) -> usize {
self.tile_number
}
fn compute_pivot_offset(pivot: &Pivot, width: usize, height: usize) -> Vector {
match pivot {
Pivot::TopLeft => Vector::new(0., 0.),
Pivot::Center => Vector::new(width as f32 / 2., height as f32 / 2.),
Pivot::Custom(x,y) => Vector::new(*x, *y)
}
}
fn uv_refs(&self, tileset: &Tileset) -> [Coordinates; 4] {
let line = (self.tile_number / tileset.width) as f32;
let column = (self.tile_number % tileset.width) as f32;
let unit_line = 1.0 / tileset.height as f32;
let unit_column = 1.0 / tileset.width as f32;
let a = Coordinates::new(column * unit_column, line * unit_line);
let b = Coordinates::new(a.x(), a.y() + unit_line);
let c = Coordinates::new(a.x() + unit_column, a.y() + unit_line);
let d = Coordinates::new(a.x() + unit_column, a.y());
[a, b, c, d]
}
pub(crate) fn compute_content(&self, material: Option<&Material>) -> [TexturedGlVertex; 4] {
if (self.dirty || self.contents.is_none()) && material.is_some() {
if let Material::Tileset(tileset) = material.unwrap() {
let offset = Self::compute_pivot_offset(&self.pivot, tileset.tile_width, tileset.tile_height);
let a = Coordinates::new(0. - offset.x, 0. - offset.y);
let b = Coordinates::new(a.x, a.y + tileset.tile_height as f32);
let c = Coordinates::new(a.x + tileset.tile_width as f32, a.y + tileset.tile_height as f32);
let d = Coordinates::new(a.x + tileset.tile_width as f32, a.y);
let uvs_ref = self.uv_refs(tileset);
return [
TexturedGlVertex::from((&a, &uvs_ref[0])),
TexturedGlVertex::from((&b, &uvs_ref[1])),
TexturedGlVertex::from((&c, &uvs_ref[2])),
TexturedGlVertex::from((&d, &uvs_ref[3])),
];
}
}
*self.contents.as_ref().expect("A computed content is missing in Sprite component")
}
pub(crate) fn indices() -> Vec<u16> {
INDICES.to_vec()
}
pub(crate) fn set_content(&mut self, content: [TexturedGlVertex; 4]) {
self.contents = Some(content);
}
}
impl Renderable2D for Sprite {
fn vertex_buffer_descriptor(&mut self, material: Option<&Material>) -> BufferInitDescriptor {
let content = self.compute_content(material);
self.contents = Some(content);
BufferInitDescriptor {
label: Some("Sprite Vertex Buffer"),
contents: bytemuck::cast_slice(
self.contents.as_ref().expect("A computed content is missing in Sprite component"),
),
usage: wgpu::BufferUsages::VERTEX,
}
}
fn indexes_buffer_descriptor(&self) -> BufferInitDescriptor {
BufferInitDescriptor {
label: Some("Sprite Index Buffer"),
contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsages::INDEX,
}
}
fn range(&self) -> Range<u32> {
0..INDICES.len() as u32
}
fn topology() -> PrimitiveTopology {
wgpu::PrimitiveTopology::TriangleList
}
fn dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self, is_dirty: bool) {
self.dirty = is_dirty;
}
fn get_pivot_offset(&self, material: Option<&Material>) -> Vector {
if material.is_none() {
Vector::default()
} else if let Material::Tileset(tileset) = material.unwrap() {
Self::compute_pivot_offset(&self.pivot, tileset.tile_width, tileset.tile_height)
} else { Vector::default() }
}
fn get_pivot(&self) -> Pivot {
self.pivot
}
}