bones_framework/render/
sprite.rs

1//! Sprite rendering components.
2
3use crate::prelude::*;
4
5/// Sprite session plugin.
6pub fn sprite_plugin(_session: &mut SessionBuilder) {
7    Sprite::register_schema();
8    AtlasSprite::register_schema();
9}
10
11/// Image component.
12#[derive(Clone, HasSchema, Debug)]
13#[schema(opaque, no_default)]
14#[type_data(asset_loader(["png", "jpg", "jpeg"], ImageAssetLoader))]
15pub enum Image {
16    /// Loaded image data
17    Data(image::DynamicImage),
18    /// A reference to image data stored in the external bones renderer.
19    External(u32),
20}
21
22/// Implements [`AssetLoader`] which attempts to return a [`SchemaBox`]
23/// containing [`Image::Data`] schema data.
24pub struct ImageAssetLoader;
25impl AssetLoader for ImageAssetLoader {
26    fn load(&self, _ctx: AssetLoadCtx, bytes: &[u8]) -> BoxedFuture<anyhow::Result<SchemaBox>> {
27        let bytes = bytes.to_vec();
28        Box::pin(async move {
29            Ok(SchemaBox::new(Image::Data(image::load_from_memory(
30                &bytes,
31            )?)))
32        })
33    }
34}
35
36/// Atlas image component.
37#[derive(Clone, HasSchema, Debug, Default)]
38#[repr(C)]
39#[type_data(metadata_asset("atlas"))]
40pub struct Atlas {
41    /// The image for the atlas.
42    pub image: Handle<Image>,
43    /// The size of each tile in the atlas.
44    pub tile_size: Vec2,
45    /// The number of rows in the atlas.
46    pub rows: u32,
47    /// The number of columns in the atlas.
48    pub columns: u32,
49    /// The amount of padding between tiles.
50    pub padding: Vec2,
51    /// The offset of the first tile from the top-left of the image.
52    pub offset: Vec2,
53
54    /// Map tile indices to extra collision metadata. This is optional and
55    /// may not be specified for all tiles, or at all.
56    pub tile_collision: SMap<String, AtlasCollisionTile>,
57}
58
59impl Atlas {
60    /// Get the position in pixels of the top-left corner of the atlas tile with the given index.
61    pub fn tile_pos(&self, idx: u32) -> Vec2 {
62        let row = idx / self.columns;
63        let col = idx % self.columns;
64        uvec2(col, row).as_vec2() * self.tile_size
65    }
66
67    /// Get the size in pixels of the entire atlas image.
68    pub fn size(&self) -> Vec2 {
69        uvec2(self.columns, self.rows).as_vec2() * self.tile_size
70    }
71}
72
73/// Metadata to define collider an atlas's tile.
74#[derive(Copy, Clone, HasSchema, Debug, Default)]
75#[repr(C)]
76pub struct AtlasCollisionTile {
77    /// min point on AABB
78    pub min: Vec2,
79    /// max point on AABB
80    pub max: Vec2,
81}
82
83impl AtlasCollisionTile {
84    /// Clamp values between range (0,0) and (1,1).
85    ///
86    /// How collision metadata is used is implementation specific,
87    /// but if using normalized values to scale with tile size,
88    /// this is useful for enforcing metadata is valid.
89    pub fn clamped_values(&self) -> AtlasCollisionTile {
90        let zero = Vec2::ZERO;
91        let one = Vec2::new(1.0, 1.0);
92        AtlasCollisionTile {
93            min: self.min.clamp(zero, one),
94            max: self.max.clamp(zero, one),
95        }
96    }
97
98    /// Return true if both components of max are greater than min.
99    /// false if equal on either axis
100    pub fn has_area(&self) -> bool {
101        let extent = self.max - self.min;
102        extent.x > 0.0 && extent.y > 0.0
103    }
104}
105
106/// A 2D sprite component
107#[derive(Clone, HasSchema, Debug, Default)]
108#[repr(C)]
109pub struct Sprite {
110    /// The sprite's color tint
111    pub color: Color,
112    /// The sprite image handle.
113    pub image: Handle<Image>,
114    /// Whether or not the flip the sprite horizontally.
115    pub flip_x: bool,
116    /// Whether or not the flip the sprite vertically.
117    pub flip_y: bool,
118}
119
120/// An animated sprite component.
121///
122/// Represents one or more [`Atlas`]s stacked on top of each other, and possibly animated through a
123/// range of frames out of the atlas.
124#[derive(Debug, Default, Clone, HasSchema)]
125#[repr(C)]
126pub struct AtlasSprite {
127    /// The sprite's color tint
128    pub color: Color,
129    /// This is the current index in the animation, with an `idx` of `0` meaning that the index in
130    /// the sprite sheet will be `start`.
131    ///
132    /// If the idx is greater than `end - start`, then the animation will loop around.
133    pub index: u32,
134    /// The atlas handle.
135    pub atlas: Handle<Atlas>,
136    /// Whether or not the flip the sprite horizontally.
137    pub flip_x: bool,
138    /// Whether or not the flip the sprite vertically.
139    pub flip_y: bool,
140}
141
142impl AtlasSprite {
143    /// Create a new [`AtlasSprite`] from the given atlas handle.
144    pub fn new(atlas: Handle<Atlas>) -> Self {
145        Self {
146            atlas,
147            color: Color::WHITE,
148            ..default()
149        }
150    }
151}