1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::math::Vec2;

// TODO: Determine how we want to manage bitmaps in the long run. Right now, it would be possible
// to load a bitmap in OpenGL, make copies of the `Bitmap` struct, and then destroy it in OpenGL.
// Because the `Bitmap` struct is Copy + Clone, we can't track ownership. As such, we can have
// copies of the now-deleted `Bitmap` floating around and getting submitted to the render pipeline.

#[derive(Clone, Copy, Debug)]
pub struct Sprite {
    pub(crate) texture_id: u32,
    pub(crate) uv: [Vec2; 4],
    pub(crate) dim: Vec2,
}

impl Sprite {
    /// Flips the sprite's UV-coordinates so that the image appears flipped about the y-axis.
    pub fn flip_uv_x(mut self) -> Self {
        self.uv = [self.uv[1], self.uv[0], self.uv[3], self.uv[2]];

        self
    }

    /// Flips the sprite's UV-coordinates so that the image appears flipped about the x-axis.
    pub fn flip_uv_y(mut self) -> Self {
        self.uv = [self.uv[3], self.uv[2], self.uv[1], self.uv[0]];

        self
    }
}

#[derive(Clone, Copy, Debug)]
pub struct Bitmap {
    texture_id: u32,
    pub width: f32,
    pub height: f32,
}

impl Bitmap {
    pub(crate) fn new(texture_id: u32, width: f32, height: f32) -> Self {
        Self {
            texture_id,
            width,
            height,
        }
    }

    pub const fn dim(&self) -> Vec2 {
        Vec2::new(self.width, self.height)
    }

    pub fn get_sprite(
        &self,
        Vec2 { x, y }: Vec2,
        Vec2 {
            x: width,
            y: height,
        }: Vec2,
    ) -> Sprite {
        let left = (x - width * 0.5) / self.width;
        let right = (x + width * 0.5) / self.width;
        let top = (y + height * 0.5) / self.height;
        let bottom = (y - height * 0.5) / self.height;

        let uv = [
            Vec2::new(left, bottom),
            Vec2::new(right, bottom),
            Vec2::new(right, top),
            Vec2::new(left, top),
        ];

        Sprite {
            texture_id: self.texture_id,
            uv,
            dim: Vec2::new(width, height),
        }
    }

    pub fn as_tiled(self, tiles_per_x: u32, tiles_per_y: u32) -> TiledBitmap {
        TiledBitmap::new(self, tiles_per_x, tiles_per_y)
    }
}

#[derive(Clone, Copy)]
pub struct TiledBitmap {
    bitmap: Bitmap,
    tile_dim: Vec2,
}

impl TiledBitmap {
    pub fn new(bitmap: Bitmap, tiles_per_x: u32, tiles_per_y: u32) -> Self {
        let tile_dim = Vec2::new(
            bitmap.width / tiles_per_x as f32,
            bitmap.height / tiles_per_y as f32,
        );

        Self { bitmap, tile_dim }
    }

    /// Calculates a sprite's location in a spritesheet like accessing a in a 2-dimensional 0-based
    /// array.
    pub fn get_sprite(&self, x: u32, y: u32) -> Sprite {
        let (x, y) = (x as f32, y as f32);
        let Vec2 {
            x: tile_width,
            y: tile_height,
        } = self.tile_dim;

        let pos = Vec2::new(
            x * tile_width + (tile_width * 0.5),
            y * tile_height + (tile_height * 0.5),
        );

        self.bitmap.get_sprite(pos, self.tile_dim * 0.95)
    }

    pub fn bitmap(&self) -> &Bitmap {
        &self.bitmap
    }
}