geng_core/shader_lib/
mod.rs

1use super::*;
2
3pub struct ShaderLib {
4    ugli: Rc<Ugli>,
5    files: RefCell<HashMap<String, String>>,
6}
7
8impl ShaderLib {
9    pub fn new(ugli: &Rc<Ugli>) -> Self {
10        let lib = Self {
11            ugli: ugli.clone(),
12            files: RefCell::new(HashMap::new()),
13        };
14        lib.add("prelude", include_str!("include/prelude.glsl"));
15        lib
16    }
17
18    pub fn add(&self, file_name: &str, source: &str) {
19        self.files
20            .borrow_mut()
21            .insert(file_name.to_owned(), source.to_owned());
22    }
23
24    fn preprocess(&self, source: &str) -> Result<String, Error> {
25        let mut result = String::new();
26        for line in source.lines() {
27            if line.starts_with("#include") {
28                let mut iter = line.trim().split_whitespace();
29                iter.next();
30                let file = iter.next().expect("Expected path to include");
31                assert!(iter.next().is_none(), "Unexpected token");
32                assert!(
33                    file.starts_with('<') && file.ends_with('>'),
34                    "include path should be enclosed in angular brackets"
35                );
36                let file = file.trim_start_matches('<').trim_end_matches('>');
37                if let Some(file) = self.files.borrow().get(file) {
38                    result.push_str(&self.preprocess(file)?);
39                } else {
40                    bail!("{:?} not found in shader library", file);
41                }
42            } else {
43                result.push_str(line);
44                result.push_str("\n");
45            }
46        }
47        Ok(result)
48    }
49    pub fn process(&self, shader_type: ugli::ShaderType, source: &str) -> Result<String, Error> {
50        let mut result = String::new();
51        #[cfg(not(target_arch = "wasm32"))]
52        result.push_str("#version 100\n");
53        result.push_str("precision highp int;\nprecision highp float;\n");
54        result.push_str(match shader_type {
55            ugli::ShaderType::Vertex => "#define VERTEX_SHADER\n",
56            ugli::ShaderType::Fragment => "#define FRAGMENT_SHADER\n",
57        });
58        result.push_str(&self.preprocess("#include <prelude>")?);
59        result.push_str(&self.preprocess(source)?);
60        Ok(result)
61    }
62    pub fn compile(&self, source: &str) -> Result<ugli::Program, Error> {
63        Ok(ugli::Program::new(
64            &self.ugli,
65            &[
66                &ugli::Shader::new(
67                    &self.ugli,
68                    ugli::ShaderType::Vertex,
69                    &self.process(ugli::ShaderType::Vertex, source)?,
70                )?,
71                &ugli::Shader::new(
72                    &self.ugli,
73                    ugli::ShaderType::Fragment,
74                    &self.process(ugli::ShaderType::Fragment, source)?,
75                )?,
76            ],
77        )?)
78    }
79}