macroquad_ply/
material.rs1use crate::{get_context, quad_gl::GlPipeline, texture::Texture2D, tobytes::ToBytes, Error};
4use miniquad::{PipelineParams, UniformDesc};
5use std::sync::Arc;
6
7#[derive(PartialEq)]
8struct GlPipelineGuarded(GlPipeline);
9
10impl Drop for GlPipelineGuarded {
11 fn drop(&mut self) {
12 get_context().gl.delete_pipeline(self.0);
13 }
14}
15
16#[derive(Clone, PartialEq)]
18pub struct Material {
19 pipeline: Arc<GlPipelineGuarded>,
20}
21
22impl std::fmt::Debug for Material {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("Material").finish()
25 }
26}
27
28impl Material {
29 pub fn set_uniform<T>(&self, name: &str, uniform: T) {
33 get_context().gl.set_uniform(self.pipeline.0, name, uniform);
34 }
35
36 pub fn set_uniform_array<T: ToBytes>(&self, name: &str, uniform: &[T]) {
37 get_context()
38 .gl
39 .set_uniform_array(self.pipeline.0, name, uniform);
40 }
41
42 pub fn set_texture(&self, name: &str, texture: Texture2D) {
43 get_context().gl.set_texture(self.pipeline.0, name, texture);
44 }
45}
46
47#[derive(Default)]
51pub struct MaterialParams {
52 pub pipeline_params: PipelineParams,
55
56 pub uniforms: Vec<UniformDesc>,
58
59 pub textures: Vec<String>,
61}
62
63pub fn load_material(
123 shader: crate::ShaderSource,
124 params: MaterialParams,
125) -> Result<Material, Error> {
126 let context = &mut get_context();
127
128 let pipeline = context.gl.make_pipeline(
129 &mut *context.quad_context,
130 shader,
131 params.pipeline_params,
132 params.uniforms,
133 params.textures,
134 )?;
135
136 Ok(Material {
137 pipeline: Arc::new(GlPipelineGuarded(pipeline)),
138 })
139}
140
141pub fn gl_use_material(material: &Material) {
143 get_context().gl.pipeline(Some(material.pipeline.0));
144}
145
146pub fn gl_use_default_material() {
148 get_context().gl.pipeline(None);
149}
150
151#[doc(hidden)]
152pub mod shaders {
153 type IncludeFilename = String;
154 type IncludeContent = String;
155
156 #[derive(Debug, Clone)]
157 pub struct PreprocessorConfig {
158 pub includes: Vec<(IncludeFilename, IncludeContent)>,
159 }
160 impl Default for PreprocessorConfig {
161 fn default() -> PreprocessorConfig {
162 PreprocessorConfig { includes: vec![] }
163 }
164 }
165
166 impl PreprocessorConfig {}
167
168 pub fn preprocess_shader(source: &str, config: &PreprocessorConfig) -> String {
169 let mut res = source.chars().collect::<Vec<_>>();
170
171 fn find(data: &[char], n: &mut usize, target: &str) -> bool {
172 if *n >= data.len() {
173 return false;
174 }
175 let target = target.chars().collect::<Vec<_>>();
176
177 'outer: for i in *n..data.len() {
178 for j in 0..target.len() {
179 if data[i + j] != target[j] {
180 *n += 1;
181 continue 'outer;
182 }
183 }
184 return true;
185 }
186 false
187 }
188
189 fn skip_character(data: &[char], n: &mut usize, target: char) {
190 while *n < data.len() && data[*n] == target {
191 *n += 1;
192 }
193 }
194
195 let mut i = 0;
196 while find(&res, &mut i, "#include") {
197 let directive_start_ix = i;
198 i += "#include".len();
199 skip_character(&res, &mut i, ' ');
200 assert!(res[i] == '\"');
201 i += 1;
202 let filename_start_ix = i;
203 find(&res, &mut i, "\"");
204 let filename_end_ix = i;
205 let filename = res[filename_start_ix..filename_end_ix]
206 .iter()
207 .cloned()
208 .collect::<String>();
209
210 let include_content = config
211 .includes
212 .iter()
213 .find(|(name, _)| name == &filename)
214 .expect(&format!(
215 "Include file {filename} in not on \"includes\" list"
216 ));
217
218 let _ = res
219 .splice(
220 directive_start_ix..filename_end_ix + 1,
221 include_content.1.chars(),
222 )
223 .collect::<Vec<_>>();
224 }
225
226 res.into_iter().collect()
227 }
228
229 #[test]
230 fn preprocessor_test() {
231 let shader_string = r#"
232#version blah blah
233
234asd
235asd
236
237#include "hello.glsl"
238
239qwe
240"#;
241
242 let preprocessed = r#"
243#version blah blah
244
245asd
246asd
247
248iii
249jjj
250
251qwe
252"#;
253
254 let result = preprocess_shader(
255 shader_string,
256 &PreprocessorConfig {
257 includes: vec![("hello.glsl".to_string(), "iii\njjj".to_string())],
258 ..Default::default()
259 },
260 );
261
262 assert_eq!(result, preprocessed);
263 }
264}