Sprite encoding

unlisted ⁨1⁩ ⁨file⁩ 2018-12-30 14:40:37 UTC

sprite.rs

Raw
use crate::pass::encoded_2d::Flat2DData;
use amethyst::{
    assets::{Asset, AssetStorage, Handle, ProcessingState, Result as AssetsResult},
    core::{
        nalgebra::{Point3, Vector4},
        GlobalTransform,
    },
    ecs::{Join, Read, ReadStorage, System, VecStorage, Write},
    renderer::{
        ActiveCamera, Camera, Flipped, Hidden, HiddenPropagate, Sprite as SpriteFrame, Texture,
        Transparent,
    },
};

#[derive(Debug, PartialEq)]
pub struct Sprite {
    pub texture: Handle<Texture>,
    pub sprite_data: SpriteFrame,
}

impl Asset for Sprite {
    const NAME: &'static str = "Sprite";
    type Data = Self;
    type HandleStorage = VecStorage<Handle<Self>>;
}

impl From<Sprite> for AssetsResult<ProcessingState<Sprite>> {
    fn from(sprite: Sprite) -> AssetsResult<ProcessingState<Sprite>> {
        Ok(ProcessingState::Loaded(sprite))
    }
}

pub struct SpriteFlat2DAssetEncoder;
impl<'a> System<'a> for SpriteFlat2DAssetEncoder {
    type SystemData = (
        Write<'a, Vec<Flat2DData>>,
        ReadStorage<'a, Handle<Sprite>>,
        Read<'a, AssetStorage<Sprite>>,
        ReadStorage<'a, GlobalTransform>,
        ReadStorage<'a, Flipped>,
        ReadStorage<'a, Transparent>,
        ReadStorage<'a, Hidden>,
        ReadStorage<'a, HiddenPropagate>,
    );
    fn run(
        &mut self,
        (mut buffer, handles, storage, transforms, flips, transparent, hidden, hidden_prop): Self::SystemData,
    ) {
        for (handle, transform, flip, transparent, _, _) in (
            &handles,
            &transforms,
            flips.maybe(),
            transparent.maybe(),
            !&hidden,
            !&hidden_prop,
        )
            .join()
        {
            if let Some(sprite) = storage.get(handle) {
                encode_sprite(
                    &mut buffer,
                    &sprite,
                    &transform,
                    flip,
                    transparent.is_some(),
                );
            }
        }
    }
}

pub struct ImageFlat2DAssetEncoder;
impl<'a> System<'a> for ImageFlat2DAssetEncoder {
    type SystemData = (
        Write<'a, Vec<Flat2DData>>,
        ReadStorage<'a, Handle<Texture>>,
        Read<'a, AssetStorage<Texture>>,
        ReadStorage<'a, GlobalTransform>,
        ReadStorage<'a, Flipped>,
        ReadStorage<'a, Transparent>,
        ReadStorage<'a, Hidden>,
        ReadStorage<'a, HiddenPropagate>,
    );
    fn run(
        &mut self,
        (mut buffer, handles, storage, transforms, flips, transparent, hidden, hidden_prop): Self::SystemData,
    ) {
        for (handle, transform, flip, transparent, _, _) in (
            &handles,
            &transforms,
            flips.maybe(),
            transparent.maybe(),
            !&hidden,
            !&hidden_prop,
        )
            .join()
        {
            if let Some(tex) = storage.get(handle) {
                encode_image(
                    &mut buffer,
                    tex,
                    handle.clone(),
                    transform,
                    flip,
                    transparent.is_some(),
                );
            }
        }
    }
}

fn encode_sprite(
    buffer: &mut Vec<Flat2DData>,
    sprite: &Sprite,
    transform: &GlobalTransform,
    flip: Option<&Flipped>,
    transparent: bool,
) {
    let sprite_data = &sprite.sprite_data;

    let (flip_horizontal, flip_vertical) = match flip {
        Some(Flipped::Horizontal) => (true, false),
        Some(Flipped::Vertical) => (false, true),
        Some(Flipped::Both) => (true, true),
        _ => (false, false),
    };

    let tex_coords = &sprite_data.tex_coords;
    let (uv_left, uv_right) = if flip_horizontal {
        (tex_coords.right, tex_coords.left)
    } else {
        (tex_coords.left, tex_coords.right)
    };
    let (uv_bottom, uv_top) = if flip_vertical {
        (tex_coords.top, tex_coords.bottom)
    } else {
        (tex_coords.bottom, tex_coords.top)
    };

    let dir_x = transform.0.column(0) * sprite_data.width;
    let dir_y = transform.0.column(1) * sprite_data.height;

    // The offsets are negated to shift the sprite left and down relative to the entity, in
    // regards to pivot points. This is the convention adopted in:
    //
    // * libgdx: <https://gamedev.stackexchange.com/q/22553>
    // * godot: <https://godotengine.org/qa/9784>
    let pos =
        transform.0 * Vector4::new(-sprite_data.offsets[0], -sprite_data.offsets[1], 0.0, 1.0);

    buffer.push(Flat2DData {
        texture: sprite.texture.clone(),
        dir_x,
        dir_y,
        pos,
        uv_left,
        uv_right,
        uv_top,
        uv_bottom,
        transparent,
    });
}

fn encode_image(
    buffer: &mut Vec<Flat2DData>,
    texture: &Texture,
    texture_handle: Handle<Texture>,
    transform: &GlobalTransform,
    flip: Option<&Flipped>,
    transparent: bool,
) {
    let (width, height) = texture.size();

    let (uv_left, uv_right, uv_bottom, uv_top) = match flip {
        Some(Flipped::Horizontal) => (1.0, 0.0, 0.0, 1.0),
        Some(Flipped::Vertical) => (0.0, 1.0, 1.0, 0.0),
        Some(Flipped::Both) => (1.0, 0.0, 1.0, 0.0),
        _ => (0.0, 1.0, 0.0, 1.0),
    };

    let dir_x = transform.0.column(0) * (width as f32);
    let dir_y = transform.0.column(1) * (height as f32);
    let pos = transform.0 * Vector4::new(1.0, 1.0, 0.0, 1.0);

    buffer.push(Flat2DData {
        texture: texture_handle,
        dir_x,
        dir_y,
        pos,
        uv_left,
        uv_right,
        uv_top,
        uv_bottom,
        transparent,
    });
}