~starkingdoms/starkingdoms

ref: d024fde6beb37c38cb2f0c7088a828aaa6b2a09d starkingdoms/crates/client/src/rendering/assets_wasm.rs -rw-r--r-- 4.9 KiB
d024fde6 — ghostly_zsh oh shut up cargo.lock 8 months ago
                                                                                
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
use std::{
    collections::HashMap,
    fmt::Display,
    sync::{Arc, Mutex},
};

use bevy_ecs::system::Resource;
use image::EncodableLayout;
use poll_promise::Promise;
use resvg::{tiny_skia, usvg};

#[derive(Debug, Clone)]
pub enum AssetError {
    AssetNotFound,
    ResponseNotOk(u16),
}
impl Display for AssetError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AssetError::AssetNotFound => write!(f, "Asset not found"),
            AssetError::ResponseNotOk(code) => write!(f, "Server response was not ok {}", code),
        }
    }
}
impl std::error::Error for AssetError {}

#[derive(Debug, Clone)]
pub struct ImgData {
    pub bytes: Vec<u8>,
    pub width: u32,
    pub height: u32,
}

#[derive(Resource)]
pub struct Assets {
    texture_promises: Arc<Mutex<HashMap<String, Promise<ImgData>>>>,
    textures: Arc<Mutex<HashMap<String, ImgData>>>,
}

impl Assets {
    pub fn new() -> Self {
        Assets {
            textures: Arc::new(Mutex::new(HashMap::new())),
            texture_promises: Arc::new(Mutex::new(HashMap::new())),
        }
    }
    pub fn get(&self, local_path: impl Into<String>) -> Option<ImgData> {
        let local_path = local_path.into();
        let contains_texture = { self.textures.lock().unwrap().contains_key(&local_path) };
        let contains_texture_promise = {
            self.texture_promises
                .lock()
                .unwrap()
                .contains_key(&local_path)
        };
        if !contains_texture && !contains_texture_promise {
            let local_path_clone = local_path.clone();
            let request_promise = poll_promise::Promise::spawn_local(async move {
                let window = web_sys::window().unwrap();
                let request = ehttp::Request::get(format!(
                    "{}/src/assets/{}",
                    window.location().origin().unwrap(),
                    local_path_clone
                ));
                let response = match ehttp::fetch_async(request).await {
                    Ok(resp) => resp,
                    Err(e) => {
                        panic!("{}", e);
                    }
                };
                if local_path_clone.ends_with(".svg") {
                    let opt = usvg::Options {
                        default_size: usvg::Size::from_wh(20.0, 20.0).unwrap(),
                        ..Default::default()
                    };
                    let tree = usvg::Tree::from_data(&response.bytes, &opt)
                        .expect(&format!("Couldn't parse svg {}", local_path_clone));
                    let tree_size = tree.size().to_int_size();
                    let size = usvg::Size::from_wh(512.0, 512.0).unwrap().to_int_size();
                    assert!(size.width() > 0 && size.height() > 0);
                    let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height())
                        .expect("Failed to construct pixmap");
                    resvg::render(
                        &tree,
                        tiny_skia::Transform::from_scale(
                            (size.width() as f32) / (tree_size.height() as f32),
                            (size.height() as f32) / (tree_size.height() as f32),
                        ),
                        &mut pixmap.as_mut(),
                    );
                    let data = ImgData {
                        bytes: pixmap.data().to_vec(),
                        width: size.width(),
                        height: size.height(),
                    };

                    data
                } else if local_path_clone.ends_with(".png") {
                    let img = image::load_from_memory(&response.bytes).unwrap();
                    let rgba = img.to_rgba8();
                    let data = ImgData {
                        bytes: rgba.as_bytes().to_vec(),
                        width: rgba.width(),
                        height: rgba.height(),
                    };
                    data
                } else {
                    panic!("Unsupported sprite type");
                }
            });
            {
                self.texture_promises
                    .lock()
                    .unwrap()
                    .insert(local_path.clone(), request_promise);
            }
            None
        } else if !contains_texture {
            let mut texture_promises = self.texture_promises.lock().unwrap();
            let promise = texture_promises.get_mut(&local_path).unwrap();
            let mut returned_value = None;
            if let Some(texture) = promise.ready() {
                self.textures
                    .lock()
                    .unwrap()
                    .insert(local_path.clone(), texture.clone());
                returned_value = Some(texture.clone());
                texture_promises.remove(&local_path);
            }
            return returned_value;
        } else {
            self.textures.lock().unwrap().get(&local_path).cloned()
        }
    }
}