Skip to main content

ursus_materials/
value.rs

1use glam::{Vec2, Vec3, Vec4};
2
3/// Stable, human-readable identifier for a material property.
4/// Backed by a string literal - no registry needed, no per-frame allocation
5/// (PropertyId is just a (ptr, len) pair, Copy).
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub struct PropertyId(pub &'static str);
8
9impl PropertyId {
10    pub const fn new(name: &'static str) -> Self {
11        Self(name)
12    }
13}
14
15impl std::fmt::Display for PropertyId {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{}", self.0)
18    }
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct TextureRef(pub u32);
23
24/// A single material property value. Strategies read these by PropertyId
25/// via `Material::get`.
26#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum MaterialValue {
28    Bool(bool),
29    Int(i32),
30    Float(f32),
31    Vec2(Vec2),
32    Vec3(Vec3),
33    Vec4(Vec4),
34    /// Linear-space RGBA color. Kept distinct from Vec4 so tooling/UI can
35    /// treat it differently (e.g. color picker) even though the payload
36    /// is the same shape.
37    Color(Vec4),
38    Texture(TextureRef),
39    UVec4([u32; 4]),
40}
41
42impl MaterialValue {
43    pub fn as_bool(&self) -> Option<bool> {
44        match self {
45            Self::Bool(v) => Some(*v),
46            _ => None,
47        }
48    }
49
50    pub fn as_float(&self) -> Option<f32> {
51        match self {
52            Self::Float(v) => Some(*v),
53            Self::Int(v) => Some(*v as f32),
54            _ => None,
55        }
56    }
57
58    pub fn as_vec4(&self) -> Option<Vec4> {
59        match self {
60            Self::Vec4(v) | Self::Color(v) => Some(*v),
61            Self::Vec3(v) => Some(v.extend(1.0)),
62            Self::Vec2(v) => Some(Vec4::new(v.x, v.y, 0.0, 1.0)),
63            _ => None,
64        }
65    }
66
67    pub fn as_vec3(&self) -> Option<Vec3> {
68        match self {
69            Self::Vec4(v) | Self::Color(v) => Some(v.truncate()),
70            Self::Vec3(v) => Some(*v),
71            _ => None,
72        }
73    }
74
75    pub fn as_texture(&self) -> Option<TextureRef> {
76        match self {
77            Self::Texture(v) => Some(*v),
78            _ => None,
79        }
80    }
81
82    pub fn as_uvec4(&self) -> Option<[u32; 4]> {
83        match self {
84            Self::UVec4(v) => Some(*v),
85            _ => None,
86        }
87    }
88
89    /// Human-readable type name, for error messages.
90    pub fn type_name(&self) -> &'static str {
91        match self {
92            Self::Bool(_) => "Bool",
93            Self::Int(_) => "Int",
94            Self::Float(_) => "Float",
95            Self::Vec2(_) => "Vec2",
96            Self::Vec3(_) => "Vec3",
97            Self::Vec4(_) => "Vec4",
98            Self::Color(_) => "Color",
99            Self::Texture(_) => "Texture",
100            Self::UVec4(_) => "UVec4",
101        }
102    }
103}