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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Sprite rendering components.

use crate::prelude::*;

/// Sprite session plugin.
pub fn sprite_plugin(_session: &mut Session) {
    Sprite::register_schema();
    AtlasSprite::register_schema();
}

/// Image component.
#[derive(Clone, HasSchema, Debug)]
#[schema(opaque, no_default)]
#[type_data(asset_loader(["png", "jpg", "jpeg"], ImageAssetLoader))]
pub enum Image {
    /// Loaded image data
    Data(image::DynamicImage),
    /// A reference to image data stored in the external bones renderer.
    External(u32),
}

struct ImageAssetLoader;
impl AssetLoader for ImageAssetLoader {
    fn load(&self, _ctx: AssetLoadCtx, bytes: &[u8]) -> BoxedFuture<anyhow::Result<SchemaBox>> {
        let bytes = bytes.to_vec();
        Box::pin(async move {
            Ok(SchemaBox::new(Image::Data(image::load_from_memory(
                &bytes,
            )?)))
        })
    }
}

/// Atlas image component.
#[derive(Clone, HasSchema, Debug, Default)]
#[repr(C)]
#[type_data(metadata_asset("atlas"))]
pub struct Atlas {
    /// The image for the atlas.
    pub image: Handle<Image>,
    /// The size of each tile in the atlas.
    pub tile_size: Vec2,
    /// The number of rows in the atlas.
    pub rows: u32,
    /// The number of columns in the atlas.
    pub columns: u32,
    /// The amount of padding between tiles.
    pub padding: Vec2,
    /// The offset of the first tile from the top-left of the image.
    pub offset: Vec2,

    /// Map tile indices to extra collision metadata. This is optional and
    /// may not be specified for all tiles, or at all.
    pub tile_collision: SMap<String, AtlasCollisionTile>,
}

impl Atlas {
    /// Get the position in pixels of the top-left corner of the atlas tile with the given index.
    pub fn tile_pos(&self, idx: u32) -> Vec2 {
        let row = idx / self.columns;
        let col = idx % self.columns;
        uvec2(col, row).as_vec2() * self.tile_size
    }

    /// Get the size in pixels of the entire atlas image.
    pub fn size(&self) -> Vec2 {
        uvec2(self.columns, self.rows).as_vec2() * self.tile_size
    }
}

/// Metadata to define collider an atlas's tile.
#[derive(Copy, Clone, HasSchema, Debug, Default)]
#[repr(C)]
pub struct AtlasCollisionTile {
    /// min point on AABB
    pub min: Vec2,
    /// max point on AABB
    pub max: Vec2,
}

impl AtlasCollisionTile {
    /// Clamp values between range (0,0) and (1,1).
    ///
    /// How collision metadata is used is implementation specific,
    /// but if using normalized values to scale with tile size,
    /// this is useful for enforcing metadata is valid.
    pub fn clamped_values(&self) -> AtlasCollisionTile {
        let zero = Vec2::ZERO;
        let one = Vec2::new(1.0, 1.0);
        AtlasCollisionTile {
            min: self.min.clamp(zero, one),
            max: self.max.clamp(zero, one),
        }
    }

    /// Return true if both components of max are greater than min.
    /// false if equal on either axis
    pub fn has_area(&self) -> bool {
        let extent = self.max - self.min;
        extent.x > 0.0 && extent.y > 0.0
    }
}

/// A 2D sprite component
#[derive(Clone, HasSchema, Debug, Default)]
#[repr(C)]
pub struct Sprite {
    /// The sprite's color tint
    pub color: Color,
    /// The sprite image handle.
    pub image: Handle<Image>,
    /// Whether or not the flip the sprite horizontally.
    pub flip_x: bool,
    /// Whether or not the flip the sprite vertically.
    pub flip_y: bool,
}

/// An animated sprite component.
///
/// Represents one or more [`Atlas`]s stacked on top of each other, and possibly animated through a
/// range of frames out of the atlas.
#[derive(Debug, Default, Clone, HasSchema)]
#[repr(C)]
pub struct AtlasSprite {
    /// The sprite's color tint
    pub color: Color,
    /// This is the current index in the animation, with an `idx` of `0` meaning that the index in
    /// the sprite sheet will be `start`.
    ///
    /// If the idx is greater than `end - start`, then the animation will loop around.
    pub index: u32,
    /// The atlas handle.
    pub atlas: Handle<Atlas>,
    /// Whether or not the flip the sprite horizontally.
    pub flip_x: bool,
    /// Whether or not the flip the sprite vertically.
    pub flip_y: bool,
}

impl AtlasSprite {
    /// Create a new [`AtlasSprite`] from the given atlas handle.
    pub fn new(atlas: Handle<Atlas>) -> Self {
        Self {
            atlas,
            color: Color::WHITE,
            ..default()
        }
    }
}