use crate::shared::{Vertex, QUAD};
use crate::sprites::texture_storage::TextureToken;
use crate::sprites::{
AtlasSegments, AtlasToken, EmoticonSprite, GameSprite, ParticleSprite, SpriteVertex, TeeSprite,
};
use vek::{Rgba, Vec2};
#[derive(Debug, Default, Clone)]
pub struct SpriteCache {
limit: Option<usize>,
sprites: Vec<[SpriteVertex; 4]>,
textures: Vec<TextureToken>,
cache: Vec<([SpriteVertex; 4], TextureToken)>,
}
impl SpriteCache {
pub fn len(&self) -> usize {
self.sprites.len()
}
pub fn is_empty(&self) -> bool {
self.sprites.is_empty()
}
pub fn set_limit(&mut self, sprite_limit: usize) {
self.limit = Some(sprite_limit);
}
pub fn limit_reached(&self) -> bool {
self.limit
.map_or(false, |limit| self.sprites.len() >= limit)
}
pub fn sprites(&self) -> &[[SpriteVertex; 4]] {
&self.sprites
}
pub fn textures(&self) -> &[TextureToken] {
&self.textures
}
pub fn add_sprite(&mut self, sprite: &[SpriteVertex; 4], texture: TextureToken) -> bool {
if self.limit_reached() {
self.cache.push((*sprite, texture));
false
} else {
self.sprites.push(*sprite);
self.textures.push(texture);
true
}
}
pub fn clear(&mut self) {
self.sprites.clear();
self.textures.clear();
}
pub fn drain_sprites<T: SpriteGenerator>(&mut self, generator: &mut T) -> bool {
if !self.cache.is_empty() {
let remaining_space = self
.limit
.map_or(usize::MAX, |limit| limit.saturating_sub(self.len()));
for (sprite, texture) in self.cache.drain(0..remaining_space.min(self.cache.len())) {
self.sprites.push(sprite);
self.textures.push(texture);
}
if self.limit_reached() {
return false;
}
}
let finished = generator.generate(|sprite, texture| self.add_sprite(sprite, texture));
finished && self.cache.is_empty()
}
}
pub trait SpriteGenerator {
fn generate<F>(&mut self, f: F) -> bool
where
F: FnMut(&[SpriteVertex; 4], TextureToken) -> bool;
}
pub trait Size {
fn size(self) -> Vec2<f32>;
}
pub trait AtlasSpriteBuilding: AtlasSegments + Size {
fn sprite(self, position: Vec2<f32>, token: AtlasToken<Self>) -> SpriteBuilder {
SpriteBuilder::new(position, self.size(), token.select(self))
}
fn transformed_sprite(
self,
position: Vec2<f32>,
transformation: Transformation,
token: AtlasToken<Self>,
) -> SpriteBuilder {
self.sprite(position + transformation.offset, token)
.scale(transformation.scale)
.rotate(transformation.rotation)
}
}
impl<T: AtlasSegments + Size> AtlasSpriteBuilding for T {}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct SpriteBuilder {
vertices: [SpriteVertex; 4],
center: Vec2<f32>,
texture: TextureToken,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Transformation {
pub offset: Vec2<f32>,
pub rotation: f32,
pub scale: f32,
}
impl Default for Transformation {
fn default() -> Self {
Transformation::DEFAULT
}
}
impl Transformation {
pub const DEFAULT: Self = Self {
offset: Vec2::new(0., 0.),
rotation: 0.0,
scale: 1.0,
};
}
impl SpriteBuilder {
pub fn new(position: Vec2<f32>, size: Vec2<f32>, texture: TextureToken) -> Self {
Self {
vertices: QUAD.map(|corner| SpriteVertex {
vertex: Vertex {
position: position + corner.direction() * size / 2.,
uv: corner.uv(),
},
color: Rgba::white(),
}),
center: position,
texture,
}
}
pub fn mirror_x(mut self, mirror: bool) -> Self {
if mirror {
let tmp = self.vertices[0].vertex.uv;
self.vertices[0].vertex.uv = self.vertices[1].vertex.uv;
self.vertices[1].vertex.uv = tmp;
let tmp = self.vertices[2].vertex.uv;
self.vertices[2].vertex.uv = self.vertices[3].vertex.uv;
self.vertices[3].vertex.uv = tmp;
}
self
}
pub fn mirror_y(mut self, mirror: bool) -> Self {
if mirror {
let tmp = self.vertices[0].vertex.uv;
self.vertices[0].vertex.uv = self.vertices[2].vertex.uv;
self.vertices[2].vertex.uv = tmp;
let tmp = self.vertices[1].vertex.uv;
self.vertices[1].vertex.uv = self.vertices[3].vertex.uv;
self.vertices[3].vertex.uv = tmp;
}
self
}
pub fn offset(mut self, offset: Vec2<f32>) -> Self {
for v in self.vertices.iter_mut() {
v.vertex.position += offset;
}
self
}
pub fn rotate(mut self, radians: f32) -> Self {
for v in self.vertices.iter_mut() {
let offset = v.vertex.position - self.center;
v.vertex.position = self.center + offset.rotated_z(radians);
}
self
}
pub fn scale(mut self, factor: f32) -> Self {
for v in self.vertices.iter_mut() {
let offset = v.vertex.position - self.center;
v.vertex.position = self.center + offset * factor;
}
self
}
pub fn scale2(mut self, factor: Vec2<f32>) -> Self {
for v in self.vertices.iter_mut() {
let offset = v.vertex.position - self.center;
v.vertex.position = self.center + offset * factor;
}
self
}
pub fn transform(self, t: Transformation) -> Self {
self.offset(t.offset).scale(t.scale).rotate(t.rotation)
}
pub fn multiply_color(mut self, color: Rgba<u8>) -> Self {
for v in self.vertices.iter_mut() {
v.color = (v.color.az::<u16>() * color.az::<u16>() / 255).az::<u8>();
}
self
}
pub fn finish(&self) -> ([SpriteVertex; 4], TextureToken) {
(self.vertices, self.texture)
}
pub fn pass_to<F>(&self, mut sprite_fn: F)
where
F: FnMut(&[SpriteVertex; 4], TextureToken),
{
sprite_fn(&self.vertices, self.texture)
}
pub fn line(from: Vec2<f32>, to: Vec2<f32>, width: f32, texture: TextureToken) -> Self {
let diff = to - from;
let distance = from.distance(to);
let direction = diff / distance;
let perpendicular = Vec2::new(direction.y, -direction.x);
Self {
vertices: QUAD.map(|corner| SpriteVertex {
vertex: Vertex {
position: from
+ corner.is_top() as i32 as f32 * diff
+ perpendicular * corner.direction().x * width / 2.,
uv: corner.uv(),
},
color: Rgba::white(),
}),
center: from + diff / 2.,
texture,
}
}
}
impl Size for TeeSprite {
fn size(self) -> Vec2<f32> {
use TeeSprite::*;
let blocks_to_world_scale = (match self {
Body | BodyOutline => Vec2::new(1., 1.),
EyeNormal | EyeAngry | EyePain | EyeHappy | EyeDead | EyeSurprise => {
Vec2::new(1.2, 1.2)
}
Foot | FootOutline => Vec2::new(1.5, 1.5),
Hand | HandOutline => Vec2::broadcast(0.9375),
}) * 2.
/ 3.;
self.blocks_size().az::<f32>() * blocks_to_world_scale
}
}
fn sprite_scale<T: AtlasSegments>(segment: T) -> Vec2<f32> {
sprite_scale_impl(segment.blocks_size().az::<f32>())
}
fn sprite_scale_impl(size: Vec2<f32>) -> Vec2<f32> {
size / size.magnitude()
}
impl Size for GameSprite {
fn size(self) -> Vec2<f32> {
use GameSprite::*;
if matches!(self, PistolMuzzle1 | PistolMuzzle2 | PistolMuzzle3) {
return sprite_scale_impl(Vec2::new(96., 64.)) * 2. * Vec2::new(4. / 3., 1.);
}
if matches!(self, ShotgunMuzzle1 | ShotgunMuzzle2 | ShotgunMuzzle3) {
return sprite_scale_impl(Vec2::new(96., 64.)) * 3. * Vec2::new(4. / 3., 1.);
}
let factor = match self {
Hook | HookTip => return Vec2::new(0.75, 0.5),
RedFlag | BlueFlag => return Vec2::new(42., 84.) / 32.,
Hammer => 3.,
Pistol => 2.,
Shotgun | Grenade => 3.,
Ninja => 4., Laser => 2.875,
PistolProjectile | ShotgunProjectile | GrenadeProjectile => return Vec2::new(1., 1.),
NinjaDash1 | NinjaDash2 | NinjaDash3 => 5.,
_ => 2.,
};
factor * sprite_scale(self)
}
}
impl Size for ParticleSprite {
fn size(self) -> Vec2<f32> {
Vec2::new(24., 24.) / 32.
}
}
impl Size for EmoticonSprite {
fn size(self) -> Vec2<f32> {
Vec2::new(2., 2.)
}
}