Skip to main content

goud_engine/assets/loaders/material/
loader.rs

1//! [`MaterialLoader`] -- parses material descriptor JSON into [`MaterialAsset`].
2
3use std::collections::HashMap;
4
5use serde::Deserialize;
6
7use crate::assets::{AssetLoadError, AssetLoader, LoadContext};
8
9use super::{asset::MaterialAsset, uniform::UniformValue};
10
11/// Intermediate deserialization target for material JSON files.
12#[derive(Deserialize)]
13struct MaterialDescriptor {
14    name: String,
15    shader_path: String,
16    #[serde(default)]
17    uniforms: HashMap<String, UniformValue>,
18    #[serde(default)]
19    texture_slots: HashMap<String, String>,
20}
21
22/// Asset loader for material descriptor files (`.mat.json`).
23///
24/// Parses a JSON descriptor into a [`MaterialAsset`] and declares
25/// shader and texture dependencies via [`LoadContext`].
26///
27/// # JSON Format
28///
29/// ```json
30/// {
31///   "name": "brick_wall",
32///   "shader_path": "shaders/pbr.glsl",
33///   "uniforms": {
34///     "roughness": { "type": "Float", "value": 0.7 },
35///     "base_color": { "type": "Vec4", "value": [0.8, 0.3, 0.1, 1.0] }
36///   },
37///   "texture_slots": {
38///     "albedo": "textures/brick_albedo.png",
39///     "normal": "textures/brick_normal.png"
40///   }
41/// }
42/// ```
43///
44/// # Example
45///
46/// ```no_run
47/// use goud_engine::assets::{AssetServer, loaders::material::{MaterialLoader, MaterialAsset}};
48///
49/// let mut server = AssetServer::new();
50/// server.register_loader(MaterialLoader::default());
51///
52/// let handle = server.load::<MaterialAsset>("materials/brick.mat.json");
53/// ```
54#[derive(Debug, Clone, Default)]
55pub struct MaterialLoader;
56
57impl MaterialLoader {
58    /// Creates a new material loader.
59    pub fn new() -> Self {
60        Self
61    }
62}
63
64impl AssetLoader for MaterialLoader {
65    type Asset = MaterialAsset;
66    type Settings = ();
67
68    fn extensions(&self) -> &[&str] {
69        &["mat.json"]
70    }
71
72    fn load<'a>(
73        &'a self,
74        bytes: &'a [u8],
75        _settings: &'a Self::Settings,
76        context: &'a mut LoadContext,
77    ) -> Result<Self::Asset, AssetLoadError> {
78        let descriptor: MaterialDescriptor = serde_json::from_slice(bytes).map_err(|e| {
79            AssetLoadError::decode_failed(format!("Material JSON parse error: {e}"))
80        })?;
81
82        // Declare shader dependency.
83        context.add_dependency(&descriptor.shader_path);
84
85        // Declare texture dependencies.
86        for texture_path in descriptor.texture_slots.values() {
87            context.add_dependency(texture_path);
88        }
89
90        Ok(MaterialAsset::new(
91            descriptor.name,
92            descriptor.shader_path,
93            descriptor.uniforms,
94            descriptor.texture_slots,
95        ))
96    }
97}