~starkingdoms/starkingdoms

ref: 93b350fc44934524b95c9f80884d921c53fe7981 starkingdoms/crates/client/src/wasm/assets.rs -rw-r--r-- 4.5 KiB
93b350fc — ghostly_zsh chassis change merge 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
use std::{
    collections::HashMap,
    fmt::Display,
    sync::{Arc, Mutex},
};

use crate::rendering::assets::{AssetLoader, ImgData};
use bevy_ecs::system::Resource;
use image::EncodableLayout;
use poll_promise::Promise;
use resvg::{tiny_skia, usvg};

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

impl AssetLoader for Assets {
    fn new() -> Self {
        Assets {
            textures: Arc::new(Mutex::new(HashMap::new())),
            texture_promises: Arc::new(Mutex::new(HashMap::new())),
        }
    }
    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/textures/{}",
                    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()
        }
    }
}