Skip to main content

ply_engine/
shaders.rs

1use std::borrow::Cow;
2
3/// Represents a shader asset that can be loaded from a file path, embedded as source,
4/// or looked up from the runtime shader storage in [`MaterialManager`].
5///
6/// `Path` is loaded from the filesystem at runtime (useful for development/hot-reloading).
7/// `Source` embeds the shader in the binary (via `include_str!`).
8/// `Stored` references a named entry in the shader storage, enabling runtime-updateable shader code.
9#[derive(Debug, Clone)]
10pub enum ShaderAsset {
11    /// Path to a compiled .glsl file, loaded at runtime
12    Path(&'static str),
13    /// Embedded GLSL ES 3.00 fragment shader source
14    Source {
15        /// Cache key for MaterialManager
16        file_name: &'static str,
17        /// GLSL ES 3.00 fragment shader source
18        fragment: &'static str,
19    },
20    /// References a named entry in the runtime shader storage.
21    Stored(&'static str),
22}
23
24impl ShaderAsset {
25    /// Returns the fragment shader source.
26    /// For `Path` variant, reads the file synchronously.
27    /// For `Source` variant, returns a borrowed reference (zero-copy).
28    /// For `Stored` variant, looks up the source from the global shader storage.
29    pub fn fragment_source(&self) -> Cow<'static, str> {
30        match self {
31            ShaderAsset::Path(path) => {
32                Cow::Owned(std::fs::read_to_string(path)
33                    .unwrap_or_else(|e| panic!("Failed to read shader file '{}': {}", path, e)))
34            }
35            ShaderAsset::Source { fragment, .. } => Cow::Borrowed(fragment),
36            ShaderAsset::Stored(name) => {
37                let mgr = crate::renderer::MATERIAL_MANAGER.lock().unwrap();
38                match mgr.get_source(name) {
39                    Some(src) => Cow::Owned(src.to_string()),
40                    None => {
41                        eprintln!("Shader storage '{}' not found, using fallback", name);
42                        Cow::Borrowed(crate::renderer::DEFAULT_FRAGMENT_SHADER)
43                    }
44                }
45            }
46        }
47    }
48
49    /// Returns the cache key used by MaterialManager.
50    pub fn cache_key(&self) -> &str {
51        match self {
52            ShaderAsset::Path(path) => path,
53            ShaderAsset::Source { file_name, .. } => file_name,
54            ShaderAsset::Stored(name) => name,
55        }
56    }
57}
58
59/// Configuration for a shader effect, stored in render commands.
60/// Contains the fragment shader source and uniform values.
61#[derive(Debug, Clone)]
62pub struct ShaderConfig {
63    /// The GLSL ES 3.00 fragment shader source (resolved from ShaderAsset).
64    pub fragment: Cow<'static, str>,
65    /// The uniform values to set on the shader.
66    pub uniforms: Vec<ShaderUniform>,
67    /// Debug-friendly name derived from the shader asset.
68    pub name: String,
69}
70
71/// A single shader uniform with a name and typed value.
72#[derive(Debug, Clone)]
73pub struct ShaderUniform {
74    /// The uniform variable name in the shader.
75    pub name: String,
76    /// The value to set for this uniform.
77    pub value: ShaderUniformValue,
78}
79
80/// Typed values for shader uniforms.
81#[derive(Debug, Clone)]
82pub enum ShaderUniformValue {
83    /// A single float value.
84    Float(f32),
85    /// A 2-component float vector.
86    Vec2([f32; 2]),
87    /// A 3-component float vector.
88    Vec3([f32; 3]),
89    /// A 4-component float vector.
90    Vec4([f32; 4]),
91    /// A single integer value.
92    Int(i32),
93    /// A 4x4 matrix.
94    Mat4([[f32; 4]; 4]),
95}
96
97impl From<f32> for ShaderUniformValue {
98    fn from(v: f32) -> Self {
99        ShaderUniformValue::Float(v)
100    }
101}
102
103impl From<[f32; 2]> for ShaderUniformValue {
104    fn from(v: [f32; 2]) -> Self {
105        ShaderUniformValue::Vec2(v)
106    }
107}
108
109impl From<[f32; 3]> for ShaderUniformValue {
110    fn from(v: [f32; 3]) -> Self {
111        ShaderUniformValue::Vec3(v)
112    }
113}
114
115impl From<[f32; 4]> for ShaderUniformValue {
116    fn from(v: [f32; 4]) -> Self {
117        ShaderUniformValue::Vec4(v)
118    }
119}
120
121impl From<i32> for ShaderUniformValue {
122    fn from(v: i32) -> Self {
123        ShaderUniformValue::Int(v)
124    }
125}
126
127impl From<[[f32; 4]; 4]> for ShaderUniformValue {
128    fn from(v: [[f32; 4]; 4]) -> Self {
129        ShaderUniformValue::Mat4(v)
130    }
131}
132
133/// Builder for configuring shader uniforms.
134/// Used in the closure passed to `.effect()` and `.shader()` on `ElementBuilder`.
135pub struct ShaderBuilder<'a> {
136    source: &'a ShaderAsset,
137    uniforms: Vec<ShaderUniform>,
138}
139
140impl<'a> ShaderBuilder<'a> {
141    /// Creates a new ShaderBuilder for the given shader asset.
142    pub(crate) fn new(source: &'a ShaderAsset) -> Self {
143        Self {
144            source,
145            uniforms: Vec::new(),
146        }
147    }
148
149    /// Sets a uniform value on the shader.
150    ///
151    /// Supports `f32`, `[f32; 2]`, `[f32; 3]`, `[f32; 4]`, `i32`, and `[[f32; 4]; 4]`.
152    pub fn uniform(&mut self, name: &str, value: impl Into<ShaderUniformValue>) -> &mut Self {
153        self.uniforms.push(ShaderUniform {
154            name: name.to_string(),
155            value: value.into(),
156        });
157        self
158    }
159
160    /// Builds the ShaderConfig from this builder.
161    pub(crate) fn into_config(&mut self) -> ShaderConfig {
162        ShaderConfig {
163            fragment: self.source.fragment_source(),
164            uniforms: std::mem::take(&mut self.uniforms),
165            name: self.source.cache_key().to_string(),
166        }
167    }
168}