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
// Material-palette schema: a named set of Material entries with short aliases.
use alloc::string::String;
use alloc::vec::Vec;
/// A named set of [Material](#material) entries with short aliases.
///
/// Expands into [Material](#material) assets named `<palette_name>_<alias>`.
/// [Prop](#prop)s reference the expanded names.
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct MaterialPalette {
/// Name of a built-in or file-backed preset (e.g. "pal_stone_dungeon").
/// When set, `entries` is ignored.
pub preset: String,
/// Inline material entries. Ignored when `preset` is set.
pub entries: Vec<PaletteEntry>,
}
/// One entry in a [MaterialPalette]. Each carries an `alias` (the suffix of the
/// expanded [Material](#material) name) plus the Material fields the expansion
/// fills in. Names in `albedo` / `normal_map` are unresolved [Texture](#texture)
/// references, resolved on the expanded Material.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct PaletteEntry {
/// Alias suffix; the expanded material is named `<palette>_<alias>`.
pub alias: String,
/// [Texture](#texture) name for the material's albedo.
#[serde(skip_serializing_if = "String::is_empty")]
pub albedo: String,
/// [Texture](#texture) name for the material's normal map.
#[serde(skip_serializing_if = "String::is_empty")]
pub normal_map: String,
/// Surface roughness in [0, 1].
pub roughness: f32,
/// Metallic factor in [0, 1].
pub metallic: f32,
/// Linear-space RGB tint multiplier.
pub tint: [f32; 3],
/// Linear-space RGB emissive factor.
pub emissive_factor: [f32; 3],
}
impl Default for PaletteEntry {
fn default() -> Self {
Self {
alias: String::from("surface"),
albedo: String::new(),
normal_map: String::new(),
roughness: 0.8,
metallic: 0.0,
tint: [1.0, 1.0, 1.0],
emissive_factor: [0.0, 0.0, 0.0],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entry_deserialises_with_material_defaults() {
let e: PaletteEntry =
serde_json::from_str(r#"{"alias":"floor","albedo":"tex_stone","roughness":0.9}"#)
.unwrap();
assert_eq!(e.alias, "floor");
assert_eq!(e.albedo, "tex_stone");
assert_eq!(e.roughness, 0.9);
// Omitted fields fall back to the material defaults the expansion uses.
assert_eq!(e.metallic, 0.0);
assert_eq!(e.tint, [1.0, 1.0, 1.0]);
assert_eq!(e.emissive_factor, [0.0, 0.0, 0.0]);
}
#[test]
fn preset_palette_leaves_entries_empty() {
let p: MaterialPalette = serde_json::from_str(r#"{"preset":"pal_stone_dungeon"}"#).unwrap();
assert_eq!(p.preset, "pal_stone_dungeon");
assert!(p.entries.is_empty());
}
}