1use std::collections::HashMap;
2use ugli::Ugli;
3
4pub struct Library {
5 ugli: Ugli,
6 files: HashMap<String, String>,
7 prefix: Option<(String, String)>, }
9
10impl Library {
11 pub fn empty(ugli: &Ugli) -> Self {
12 Self {
13 ugli: ugli.clone(),
14 files: HashMap::new(),
15 prefix: None,
16 }
17 }
18 pub fn new(ugli: &Ugli, antialias: bool, prefix: Option<(String, String)>) -> Self {
19 let mut library = Self::empty(ugli);
20 let mut prelude = include_str!("prelude.glsl").to_owned();
21 if antialias {
22 prelude = "#define GENG_ANTIALIAS\n".to_owned() + &prelude;
23 }
24 fn default_prefix() -> (String, String) {
25 let common_glsl = "precision highp int;\nprecision highp float;\n";
26 if cfg!(any(target_arch = "wasm32", target_os = "android")) {
27 (
28 format!("{common_glsl}#define VERTEX_SHADER\n"),
29 format!("#extension GL_OES_standard_derivatives : enable\n{common_glsl}#define FRAGMENT_SHADER\n"),
30 )
31 } else {
32 (
33 format!("#version 150\n{common_glsl}#define VERTEX_SHADER\n"),
34 format!("#version 150\n{common_glsl}#define FRAGMENT_SHADER\n"),
35 )
36 }
37 }
38 library.prefix = Some(prefix.unwrap_or_else(default_prefix));
39 library.add("prelude", &prelude);
40 library.add("noise", include_str!("noise.glsl"));
41 library
42 }
43
44 pub fn add(&mut self, file_name: &str, source: &str) {
45 self.files.insert(file_name.to_owned(), source.to_owned());
46 }
47
48 fn preprocess(&self, source: &str) -> Result<String, anyhow::Error> {
49 let mut result = String::new();
50 for line in source.lines() {
51 if line.starts_with("#include") {
52 let mut iter = line.split_whitespace();
53 iter.next();
54 let file = iter.next().expect("Expected path to include");
55 assert!(iter.next().is_none(), "Unexpected token");
56 assert!(
57 file.starts_with('<') && file.ends_with('>'),
58 "include path should be enclosed in angular brackets"
59 );
60 let file = file.trim_start_matches('<').trim_end_matches('>');
61 if let Some(file) = self.files.get(file) {
62 result.push_str(&self.preprocess(file)?);
63 } else {
64 anyhow::bail!("{:?} not found in shader library", file);
65 }
66 } else {
67 result.push_str(line);
68 result.push('\n');
69 }
70 }
71 Ok(result)
72 }
73 pub fn process(
74 &self,
75 shader_type: ugli::ShaderType,
76 source: &str,
77 ) -> Result<String, anyhow::Error> {
78 let mut result = String::new();
79 if let Some((vertex_prefix, fragment_prefix)) = &self.prefix {
80 result.push_str(match shader_type {
81 ugli::ShaderType::Vertex => vertex_prefix,
82 ugli::ShaderType::Fragment => fragment_prefix,
83 });
84 result.push('\n');
85 }
86 result.push_str(match shader_type {
87 ugli::ShaderType::Vertex => "#define VERTEX_SHADER\n",
88 ugli::ShaderType::Fragment => "#define FRAGMENT_SHADER\n",
89 });
90 result.push_str(&self.preprocess("#include <prelude>")?);
91 result.push_str(&self.preprocess(source)?);
92 Ok(result)
93 }
94 pub fn compile(&self, source: &str) -> Result<ugli::Program, anyhow::Error> {
95 let shader = |shader_type| -> anyhow::Result<ugli::Shader> {
96 Ok(ugli::Shader::new(
97 &self.ugli,
98 shader_type,
99 &self.process(shader_type, source)?,
100 )?)
101 };
102 Ok(ugli::Program::new(
103 &self.ugli,
104 [
105 &shader(ugli::ShaderType::Vertex)?,
106 &shader(ugli::ShaderType::Fragment)?,
107 ],
108 )?)
109 }
110}