twgpu 0.4.1

Render Teeworlds and DDNet maps
Documentation
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 {
    /// Return `false`, if this got interrupted and should be called again.
    fn generate<F>(&mut self, f: F) -> bool
    where
        F: FnMut(&[SpriteVertex; 4], TextureToken) -> bool;
}

/// Trait for structs and enums that want to be rendered as sprites.
/// Size is in tiles, so 1.0 = one tile.
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., // Equipped ninja is smaller (3.)
            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.)
    }
}