shady/
template.rs

1//! Module to generate templates which can be modified and used for [Shady].
2use std::fmt;
3
4use crate::{resources::Resources, BIND_GROUP_INDEX, FRAGMENT_ENTRYPOINT};
5
6pub const DEFAULT_TEMPLATE_WGSL_BODY: &str = "
7    let uv = pos.xy/iResolution.xy;
8    let col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3<f32>(0.0, 2.0, 4.0));
9
10    return vec4<f32>(col, 1.0);
11";
12
13pub const DEFAULT_TEMPLATE_GLSL_BODY: &str = "
14    // Normalized pixel coordinates (from 0 to 1)
15    vec2 uv = gl_FragCoord.xy/iResolution.xy;
16
17    // Time varying pixel color
18    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
19
20    // Output to screen
21    fragColor = vec4(col,1.0);      
22";
23
24/// The shader languages where the templates can be generated for.
25///
26/// # Example
27/// ```
28/// use shady::TemplateLang;
29///
30/// // Create a template in wgsl
31/// let template = TemplateLang::Wgsl
32///     .generate_to_string(None) // You can also provide your own code which should be placed within the main function
33///     .unwrap();
34/// ```
35#[derive(Debug, Clone, Copy, Hash)]
36pub enum TemplateLang {
37    /// A template in the wgsl shader language.
38    Wgsl,
39
40    /// The glsl shader language.
41    Glsl,
42}
43
44pub(crate) trait TemplateGenerator {
45    fn write_wgsl_template(
46        writer: &mut dyn fmt::Write,
47        bind_group_index: u32,
48    ) -> Result<(), fmt::Error>;
49
50    fn write_glsl_template(writer: &mut dyn fmt::Write) -> Result<(), fmt::Error>;
51}
52
53impl TemplateLang {
54    /// Create the template and return them as a String.
55    ///
56    /// # Arguments
57    /// - `body`: Setting it `None` will create
58    ///
59    /// # Example
60    /// ```
61    /// use shady::TemplateLang;
62    ///
63    /// // Create a template in wgsl
64    /// let template = TemplateLang::Wgsl
65    ///     .generate_to_string(None)
66    ///     .unwrap();
67    /// ```
68    pub fn generate_to_string(self, body: Option<&str>) -> Result<String, fmt::Error> {
69        let mut string = String::new();
70        self.generate(&mut string, body)?;
71        Ok(string)
72    }
73
74    /// Create the template and write it to the given `writer`.
75    ///
76    /// # Arguments
77    /// - `writer`: Where to write the template into.
78    /// - `body`: Optional shadercode which should be pasted into the main function of the fragment.
79    ///
80    /// # Example
81    /// ```
82    /// use shady::TemplateLang;
83    ///
84    /// let mut template = String::new();
85    ///
86    /// // Generate the template and store it into `template`.
87    /// TemplateLang::Wgsl
88    ///     .generate(&mut template, None)
89    ///     .unwrap();
90    /// ```
91    pub fn generate(
92        self,
93        writer: &mut dyn std::fmt::Write,
94        body: Option<&str>,
95    ) -> Result<(), fmt::Error> {
96        match self {
97            TemplateLang::Wgsl => {
98                Resources::write_wgsl_template(writer, BIND_GROUP_INDEX)?;
99
100                writer.write_fmt(format_args!(
101                    "
102@fragment
103fn {}(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {{
104{}
105}}
106",
107                    FRAGMENT_ENTRYPOINT,
108                    body.unwrap_or(DEFAULT_TEMPLATE_WGSL_BODY)
109                ))?;
110            }
111
112            TemplateLang::Glsl => {
113                Resources::write_glsl_template(writer)?;
114
115                writer.write_fmt(format_args!(
116                    "
117// the color which the pixel should have
118layout(location = 0) out vec4 fragColor;
119
120void {}() {{
121{}
122}}
123",
124                    FRAGMENT_ENTRYPOINT,
125                    body.unwrap_or(DEFAULT_TEMPLATE_GLSL_BODY)
126                ))?;
127            }
128        };
129
130        Ok(())
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use wgpu::naga::{front::glsl::Options, ShaderStage};
137
138    use super::*;
139
140    /// Check if the generate default template is valid
141    #[test]
142    fn valid_wgsl_template() {
143        let template = TemplateLang::Wgsl.generate_to_string(None).unwrap();
144
145        if let Err(err) = wgpu::naga::front::wgsl::parse_str(&template) {
146            let msg = err.emit_to_string(&template);
147            panic!("{}", msg);
148        }
149    }
150
151    /// Check if the generate default template is valid
152    #[test]
153    fn valid_glsl_template() {
154        let template = TemplateLang::Glsl.generate_to_string(None).unwrap();
155
156        let mut parser = wgpu::naga::front::glsl::Frontend::default();
157        if let Err(err) = parser.parse(&Options::from(ShaderStage::Fragment), &template) {
158            let msg = err.emit_to_string(&template);
159            panic!("{}", msg);
160        }
161    }
162}