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}