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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
//! Resources to be handled by the engine like textures, sounds and fonts.

use super::Labelifier;
use crate::data::Data;
use crate::window::Window;
use crate::{error::textures::*, utils::u16tou8vec};
use image::{load_from_memory_with_format, DynamicImage, ImageFormat};
use parking_lot::Mutex;
use std::sync::Arc;
use vulkano::buffer::BufferContents;
use vulkano::descriptor_set::persistent::PersistentDescriptorSet;
use vulkano::descriptor_set::WriteDescriptorSet;
use vulkano::pipeline::cache::PipelineCache;

mod loader;
pub mod vulkan;
pub(crate) use loader::Loader;
use vulkan::Vulkan;

pub mod textures;
use textures::*;

pub mod data;
pub mod materials;
pub mod model;
pub use model::Model;
mod macros;
pub use macros::*;

const NOT_INITIALIZED_MSG: &str = "Resources are not initialized to a game.";

/// All the resources kept in the game engine like textures, fonts, sounds and models.
#[derive(Clone)]
pub struct Resources {
    pub(crate) vulkan: Option<Vulkan>,
    pub(crate) loader: Option<Arc<Mutex<Loader>>>,
    pub(crate) labelifier: Option<Arc<Mutex<Labelifier>>>,
}

impl Resources {
    pub fn new() -> Self {
        Self {
            vulkan: None,
            loader: None,
            labelifier: None,
        }
    }

    /// Initialisation
    pub(crate) fn init(&mut self, vulkan: Vulkan) {
        let mut loader = Loader::init(&vulkan);
        let labelifier = Some(Arc::new(Mutex::new(Labelifier::new(&vulkan, &mut loader))));
        *self = Self {
            vulkan: Some(vulkan),
            loader: Some(Arc::new(Mutex::new(loader))),
            labelifier,
        }
    }

    pub(crate) fn vulkan(&self) -> &Vulkan {
        self.vulkan.as_ref().expect(NOT_INITIALIZED_MSG)
    }
    pub(crate) fn loader(&self) -> &Arc<Mutex<Loader>> {
        self.loader.as_ref().expect(NOT_INITIALIZED_MSG)
    }
    pub(crate) fn labelifier(&self) -> &Arc<Mutex<Labelifier>> {
        self.labelifier.as_ref().expect(NOT_INITIALIZED_MSG)
    }
    //redraw
    pub(crate) fn update(&self) {
        // swap with layers
        let mut loader = self.loader.as_ref().unwrap().lock();
        let mut labelifier = self.labelifier.as_ref().unwrap().lock();
        labelifier.update(self.vulkan(), &mut loader);
    }

    /// Merges a pipeline cache into the resources potentially making the creation of materials faster.
    ///
    /// # Safety
    ///
    /// Unsafe because vulkan blindly trusts that this data comes from the `get_pipeline_binary` function.
    /// The program will crash if the data provided is not right.
    ///
    /// The binary given to the function must be made with the same hardware and vulkan driver version.
    pub unsafe fn load_pipeline_cache(&self, data: &[u8]) {
        let cache = PipelineCache::with_data(self.vulkan().device.clone(), data).unwrap();
        self.loader()
            .lock()
            .pipeline_cache
            .merge([&cache].iter())
            .unwrap();
    }

    /// Returns the binary of the pipeline cache.
    ///
    /// Allows this binary to be loaded with the `load_pipeline_cache` function to make loading materials potentially faster.
    pub fn get_pipeline_binary(&self) -> Vec<u8> {
        self.loader().lock().pipeline_cache.get_data().unwrap()
    }

    pub fn load_model(&self, data: Data) -> Model {
        let mut loader = self.loader().lock();
        Model::new(data, &mut loader)
    }

    /// Loads a font into the game resources.
    pub fn load_font(&self, data: &[u8]) -> Font {
        let mut labelifier = self.labelifier().lock();
        labelifier.load_font(data)
    }

    /// Loads a texture to the GPU using a raw image.
    pub fn load_texture_from_raw(
        &self,
        data: &[u8],
        dimensions: (u32, u32),
        format: Format,
        layers: u32,
        settings: TextureSettings,
    ) -> Texture {
        let loader = self.loader().lock();
        Texture {
            data: Arc::from(data.to_vec().into_boxed_slice()),
            dimensions,
            layers,
            set: loader.load_texture(self.vulkan(), data, dimensions, layers, format, settings),
        }
    }

    /// Loads a texture to the GPU using the given image format.
    pub fn load_texture(
        &self,
        data: &[u8],
        image_format: ImageFormat,
        layers: u32,
        settings: TextureSettings,
    ) -> Result<Texture, InvalidFormatError> {
        let image = match load_from_memory_with_format(data, image_format) {
            Err(_) => return Err(InvalidFormatError),
            Ok(v) => v,
        };

        let mut dimensions: (u32, u32);

        let mut format = Format::RGBA8;
        let image: Vec<u8> = match image {
            DynamicImage::ImageLuma8(image) => {
                format = Format::R8;
                dimensions = image.dimensions();
                image.into_vec()
            }
            DynamicImage::ImageLumaA8(_) => {
                let image = image.to_rgba8();
                dimensions = image.dimensions();
                image.into_vec()
            }
            DynamicImage::ImageLuma16(_) => {
                let image = image.to_luma8();
                dimensions = image.dimensions();
                format = Format::R8;
                image.into_vec()
            }
            DynamicImage::ImageLumaA16(_) => {
                let image = image.to_rgba16();
                dimensions = image.dimensions();
                format = Format::RGBA16;
                u16tou8vec(image.into_vec())
            }
            DynamicImage::ImageRgb8(_) => {
                let image = image.to_rgba8();
                dimensions = image.dimensions();
                image.into_vec()
            }
            DynamicImage::ImageRgba8(image) => {
                dimensions = image.dimensions();
                image.into_vec()
            }
            DynamicImage::ImageRgb16(_) => {
                let image = image.to_rgba16();
                dimensions = image.dimensions();
                format = Format::RGBA16;
                u16tou8vec(image.into_vec())
            }
            DynamicImage::ImageRgba16(image) => {
                format = Format::RGBA16;
                dimensions = image.dimensions();
                u16tou8vec(image.into_vec())
            }
            DynamicImage::ImageRgb32F(_) => {
                let image = image.to_rgba16();
                dimensions = image.dimensions();
                format = Format::RGBA16;
                u16tou8vec(image.into_vec())
            }
            DynamicImage::ImageRgba32F(_) => {
                let image = image.to_rgba16();
                dimensions = image.dimensions();
                format = Format::RGBA16;
                u16tou8vec(image.into_vec())
            }
            _ => {
                let image = image.to_rgba8();
                dimensions = image.dimensions();
                image.into_vec()
            }
        };

        dimensions.1 /= layers;

        Ok(Self::load_texture_from_raw(
            self, &image, dimensions, format, layers, settings,
        ))
    }
    //shaders
    /// Loads a shader from spirv bytes.
    ///
    /// # Safety
    ///
    /// Just crashes the program if the bytes given are not right.
    pub unsafe fn new_shader_from_raw(
        // loading things all temporary. Will get sepparated to their own things soon.
        &self,
        vertex_data: &[u8],
        fragment_data: &[u8],
    ) -> materials::Shaders {
        unsafe { materials::Shaders::from_bytes(vertex_data, fragment_data, self.vulkan()) }
    }

    // fn new_shader ..requires the vulkano_shaders library function load() device

    /// Loads a new write operation for a shader.
    pub fn new_descriptor_write<T: BufferContents>(&self, buf: T, set: u32) -> WriteDescriptorSet {
        let loader = self.loader().lock();
        loader.write_descriptor(buf, set)
    }

    /// Creates a new material using the given shaders, settings and write operations.
    pub fn new_material_with_shaders(
        &self,
        settings: materials::MaterialSettings,
        shaders: &materials::Shaders,
        descriptor_bindings: Vec<WriteDescriptorSet>,
    ) -> materials::Material {
        let loader = self.loader().lock();
        loader.load_material(self.vulkan(), shaders, settings, descriptor_bindings)
    }
    pub fn new_material(&self, settings: materials::MaterialSettings) -> materials::Material {
        let loader = self.loader().lock();
        let shaders = self.vulkan().default_shaders.clone();
        loader.load_material(self.vulkan(), &shaders, settings, vec![])
    }

    /// Simplification of making a texture and putting it into a material.
    pub fn new_material_from_texture(
        &self,
        texture: &[u8],
        format: ImageFormat,
        layers: u32,
        settings: TextureSettings,
    ) -> Result<materials::Material, InvalidFormatError> {
        let texture = Self::load_texture(self, texture, format, layers, settings)?;

        Ok(Self::default_textured_material(self, &texture))
    }

    /// Simplification of making a texture from raw and putting it into a material.
    pub fn new_material_from_raw_texture(
        &self,
        texture: &[u8],
        format: Format,
        dimensions: (u32, u32),
        layers: u32,
        settings: TextureSettings,
    ) -> materials::Material {
        let texture =
            Self::load_texture_from_raw(self, texture, dimensions, format, layers, settings);
        Self::default_textured_material(self, &texture)
    }

    /// Creates a simple material made just for showing a texture.
    pub fn default_textured_material(&self, texture: &Texture) -> materials::Material {
        let default = if texture.layers == 1 {
            self.vulkan().textured_material.clone()
        } else {
            self.vulkan().texture_array_material.clone()
        };
        materials::Material {
            texture: Some(texture.clone()),
            ..default
        }
    }

    /// Returns the window instance from resources.
    pub fn get_window(&self) -> Window {
        self.vulkan().window.clone()
    }
}

impl Default for Resources {
    fn default() -> Self {
        Self::new()
    }
}

/// A texture to be used with materials.
#[derive(Clone)]
pub struct Texture {
    pub data: Arc<[u8]>,
    pub dimensions: (u32, u32),
    pub layers: u32,
    pub set: Arc<PersistentDescriptorSet>,
}

/// A font to be used with the default label system.
#[derive(Clone)]
pub struct Font {
    pub font: Arc<rusttype::Font<'static>>,
    pub fontid: usize,
}

pub struct Sound {
    pub data: Arc<[u8]>,
}

/// Not done.
#[allow(dead_code)]
pub fn load_sound(sound: &[u8]) -> Sound {
    Sound {
        data: Arc::from(sound.to_vec().into_boxed_slice()),
    }
}

impl PartialEq for Texture {
    fn eq(&self, other: &Self) -> bool {
        self.data == other.data
            && self.dimensions == other.dimensions
            && Arc::ptr_eq(&self.set, &other.set)
    }
}

impl std::fmt::Debug for Texture {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Texture")
            .field("size", &self.data.len())
            .field("dimensions", &self.dimensions)
            .field("frames", &self.layers)
            .finish()
    }
}