micro_games_kit/assets/
texture.rs

1use crate::{assets::name_from_path, context::GameContext, game::GameSubsystem};
2use anput::world::World;
3use image::{GenericImage, GenericImageView, RgbaImage};
4use keket::{
5    database::{handle::AssetHandle, path::AssetPathStatic},
6    protocol::AssetProtocol,
7};
8use spitfire_glow::renderer::GlowTextureFormat;
9use std::error::Error;
10
11pub struct TextureAsset {
12    pub image: RgbaImage,
13    pub cols: u32,
14    pub rows: u32,
15}
16
17pub struct TextureAssetSubsystem;
18
19impl GameSubsystem for TextureAssetSubsystem {
20    fn run(&mut self, context: GameContext, _: f32) {
21        for entity in context.assets.storage.added().iter_of::<TextureAsset>() {
22            if let Some((path, asset)) = context
23                .assets
24                .storage
25                .lookup_one::<true, (&AssetPathStatic, &TextureAsset)>(entity)
26            {
27                let pages = asset.cols * asset.rows;
28                context.draw.textures.insert(
29                    name_from_path(&path).to_owned().into(),
30                    context
31                        .graphics
32                        .texture(
33                            asset.image.width(),
34                            asset.image.height() / pages,
35                            pages,
36                            GlowTextureFormat::Rgba,
37                            Some(asset.image.as_raw()),
38                        )
39                        .unwrap(),
40                );
41            }
42        }
43        for entity in context.assets.storage.removed().iter_of::<TextureAsset>() {
44            if let Some(path) = context
45                .assets
46                .storage
47                .lookup_one::<true, &AssetPathStatic>(entity)
48            {
49                context.draw.textures.remove(name_from_path(&path));
50            }
51        }
52    }
53}
54
55pub struct TextureAssetProtocol;
56
57impl AssetProtocol for TextureAssetProtocol {
58    fn name(&self) -> &str {
59        "texture"
60    }
61
62    fn process_bytes(
63        &mut self,
64        handle: AssetHandle,
65        storage: &mut World,
66        bytes: Vec<u8>,
67    ) -> Result<(), Box<dyn Error>> {
68        let path = storage.component::<true, AssetPathStatic>(handle.entity())?;
69        let mut cols = 1;
70        let mut rows = 1;
71        for (key, value) in path.meta_items() {
72            if key == "cols" || key == "c" {
73                cols = value.parse().unwrap_or(1);
74            } else if key == "rows" || key == "r" {
75                rows = value.parse().unwrap_or(1);
76            }
77        }
78        let mut image = image::load_from_memory(&bytes)
79            .map_err(|_| format!("Failed to load texture: {:?}", path.path()))?
80            .into_rgba8();
81        drop(path);
82        let pages = cols * rows;
83        image = if cols > 1 || rows > 1 {
84            let width = image.width() / cols;
85            let height = image.height() / rows;
86            let mut result = RgbaImage::new(width, height * pages);
87            for row in 0..rows {
88                for col in 0..cols {
89                    let view = image.view(col * width, row * height, width, height);
90                    result
91                        .copy_from(&*view, 0, (row * cols + col) * height)
92                        .unwrap();
93                }
94            }
95            result
96        } else {
97            image
98        };
99
100        storage.insert(handle.entity(), (TextureAsset { image, cols, rows },))?;
101
102        Ok(())
103    }
104}