enigma_3d/
material.rs

1use glium::uniforms::UniformBuffer;
2use glium::Display;
3use glium::glutin::surface::WindowSurface;
4use glium::texture::RawImage2d;
5use glium::uniforms::SamplerWrapFunction;
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8use crate::{resources, shader, texture};
9use crate::camera::Camera;
10use crate::geometry::BoneTransforms;
11use crate::light::{Light, LightBlock};
12
13#[derive(Serialize, Deserialize, Clone)]
14pub struct MaterialSerializer {
15    name: String,
16    color: [f32; 3],
17    albedo: Option<texture::TextureSerializer>,
18    transparency: f32,
19    normal: Option<texture::TextureSerializer>,
20    normal_strength: f32,
21    roughness: Option<texture::TextureSerializer>,
22    roughness_strength: f32,
23    metallic: Option<texture::TextureSerializer>,
24    metallic_strength: f32,
25    emissive: Option<texture::TextureSerializer>,
26    emissive_strength: f32,
27    shader: shader::ShaderSerializer,
28    matrix: [[f32; 4]; 4],
29    render_transparent: bool,
30    uuid: String,
31}
32
33pub struct Material {
34    pub name: String,
35    pub color: [f32; 3],
36    pub albedo: Option<texture::Texture>,
37    pub transparency: f32,
38    pub normal: Option<texture::Texture>,
39    pub normal_strength: f32,
40    pub roughness: Option<texture::Texture>,
41    pub roughness_strength: f32,
42    pub metallic: Option<texture::Texture>,
43    pub metallic_strength: f32,
44    pub emissive: Option<texture::Texture>,
45    pub emissive_strength: f32,
46    pub shader: shader::Shader,
47    _tex_white: glium::texture::SrgbTexture2d,
48    _tex_black: glium::texture::SrgbTexture2d,
49    _tex_gray: glium::texture::SrgbTexture2d,
50    _tex_normal: glium::texture::SrgbTexture2d,
51    //this should be a raw image
52    pub display: glium::Display<WindowSurface>,
53    pub program: glium::Program,
54    pub time: f32,
55    pub matrix: [[f32; 4]; 4],
56    pub render_transparent: bool,
57    pub uuid: Uuid,
58}
59
60pub enum TextureType {
61    Albedo,
62    Normal,
63    Roughness,
64    Metallic,
65    Emissive,
66}
67
68impl Clone for Material {
69    fn clone(&self) -> Self {
70        let mut material = Material::default(self.shader.clone(), &self.display);
71        let _tex_white = {
72            let raw = Material::tex_raw_from_array([1.0, 1.0, 1.0, 1.0]);
73            glium::texture::SrgbTexture2d::new(&self.display, raw).unwrap()
74        };
75        let _tex_black = {
76            let raw = Material::tex_raw_from_array([0.0, 0.0, 0.0, 1.0]);
77            glium::texture::SrgbTexture2d::new(&self.display, raw).unwrap()
78        };
79        let _tex_gray = {
80            let raw = Material::tex_raw_from_array([0.5, 0.5, 0.5, 1.0]);
81            glium::texture::SrgbTexture2d::new(&self.display, raw).unwrap()
82        };
83        let _tex_normal = {
84            let raw = Material::tex_raw_from_array([0.5, 0.5, 1.0, 1.0]);
85            glium::texture::SrgbTexture2d::new(&self.display, raw).unwrap()
86        };
87        material.name = self.name.clone();
88        material.color = self.color.clone();
89        material.albedo = match &self.albedo {
90            Some(tex) => Some(tex.get_texture_clone(&self.display)),
91            None => None
92        };
93        material.transparency = self.transparency.clone();
94        material.normal = match &self.normal {
95            Some(tex) => Some(tex.get_texture_clone(&self.display)),
96            None => None
97        };
98        material.normal_strength = self.normal_strength.clone();
99        material.roughness = match &self.roughness {
100            Some(tex) => Some(tex.get_texture_clone(&self.display)),
101            None => None
102        };
103        material.roughness_strength = self.roughness_strength.clone();
104        material.metallic = match &self.metallic {
105            Some(tex) => Some(tex.get_texture_clone(&self.display)),
106            None => None
107        };
108        material.metallic_strength = self.metallic_strength.clone();
109        material.emissive = match &self.emissive {
110            Some(tex) => Some(tex.get_texture_clone(&self.display)),
111            None => None
112        };
113        material.emissive_strength = self.emissive_strength.clone();
114        material.matrix = self.matrix.clone();
115        material.time = self.time.clone();
116        material.render_transparent = self.render_transparent.clone();
117        material.uuid = self.uuid.clone();
118        material
119    }
120}
121
122impl Material {
123    pub fn default(shader: shader::Shader, display: &glium::Display<WindowSurface>) -> Self {
124        Material::new(shader, display.clone(), None, None, None, None, None, None, None, None, None, None)
125    }
126
127    pub fn from_serializer(serializer: MaterialSerializer, display: &glium::Display<WindowSurface>) -> Self {
128        let shader = shader::Shader::from_serializer(serializer.shader);
129        let albedo = match serializer.albedo {
130            Some(albedo) => Some(texture::Texture::from_serializer(albedo, &display)),
131            None => None,
132        };
133        let normal = match serializer.normal {
134            Some(normal) => Some(texture::Texture::from_serializer(normal, &display)),
135            None => None,
136        };
137        let roughness = match serializer.roughness {
138            Some(roughness) => Some(texture::Texture::from_serializer(roughness, &display)),
139            None => None,
140        };
141        let metallic = match serializer.metallic {
142            Some(metallic) => Some(texture::Texture::from_serializer(metallic, &display)),
143            None => None,
144        };
145        let emissive = match serializer.emissive {
146            Some(emissive) => Some(texture::Texture::from_serializer(emissive, &display)),
147            None => None,
148        };
149
150        let mut mat = Material::new(shader, display.clone(), Some(serializer.color), albedo, normal, Some(serializer.normal_strength), roughness, Some(serializer.roughness_strength), metallic, Some(serializer.metallic_strength), emissive, Some(serializer.emissive_strength));
151        mat.name = serializer.name;
152        mat.matrix = serializer.matrix;
153        mat.set_transparency_strength(serializer.transparency);
154        mat.set_transparency(serializer.render_transparent);
155        mat.uuid = Uuid::parse_str(serializer.uuid.as_str()).expect("Failed parsing Uuid");
156        mat
157    }
158
159    pub fn to_serializer(&self) -> MaterialSerializer {
160        MaterialSerializer {
161            name: self.name.clone(),
162            color: self.color,
163            albedo: match &self.albedo {
164                Some(albedo) => Some(albedo.to_serializer()),
165                None => None,
166            },
167            transparency: self.transparency,
168            normal: match &self.normal {
169                Some(normal) => Some(normal.to_serializer()),
170                None => None,
171            },
172            normal_strength: self.normal_strength,
173            roughness: match &self.roughness {
174                Some(roughness) => Some(roughness.to_serializer()),
175                None => None,
176            },
177            roughness_strength: self.roughness_strength,
178            metallic: match &self.metallic {
179                Some(metallic) => Some(metallic.to_serializer()),
180                None => None,
181            },
182            metallic_strength: self.metallic_strength,
183            emissive: match &self.emissive {
184                Some(emissive) => Some(emissive.to_serializer()),
185                None => None,
186            },
187            emissive_strength: self.emissive_strength,
188            shader: self.shader.to_serializer(),
189            matrix: self.matrix,
190            render_transparent: self.render_transparent,
191            uuid: self.uuid.to_string(),
192        }
193    }
194
195    pub fn new(
196        shader: shader::Shader,
197        display: glium::Display<WindowSurface>,
198        color: Option<[f32; 3]>,
199        albedo: Option<texture::Texture>,
200        normal: Option<texture::Texture>,
201        normal_strength: Option<f32>,
202        roughness: Option<texture::Texture>,
203        roughness_strength: Option<f32>,
204        metallic: Option<texture::Texture>,
205        metallic_strength: Option<f32>,
206        emissive: Option<texture::Texture>,
207        emissive_strength: Option<f32>,
208    ) -> Self {
209        let geometry_shader = match &shader.geometry_shader {
210            Some(shader) => Some(shader.as_str()),
211            None => Some(resources::geometry_shader())
212        };
213
214        let _program = glium::Program::from_source(&display, &shader.get_vertex_shader(), &shader.get_fragment_shader(), geometry_shader).expect("Failed to compile shader program");
215        let _tex_white = {
216            let raw = Material::tex_raw_from_array([1.0, 1.0, 1.0, 1.0]);
217            glium::texture::SrgbTexture2d::new(&display, raw).unwrap()
218        };
219        let _tex_black = {
220            let raw = Material::tex_raw_from_array([0.0, 0.0, 0.0, 1.0]);
221            glium::texture::SrgbTexture2d::new(&display, raw).unwrap()
222        };
223        let _tex_gray = {
224            let raw = Material::tex_raw_from_array([0.5, 0.5, 0.5, 1.0]);
225            glium::texture::SrgbTexture2d::new(&display, raw).unwrap()
226        };
227        let _tex_normal = {
228            let raw = Material::tex_raw_from_array([0.5, 0.5, 1.0, 1.0]);
229            glium::texture::SrgbTexture2d::new(&display, raw).unwrap()
230        };
231
232        Self {
233            name: "New Material".to_string(),
234            shader,
235            display,
236            color: color.unwrap_or_else(|| [1.0, 1.0, 1.0]),
237            albedo: match albedo {
238                Some(albedo) => Some(albedo),
239                None => None,
240            },
241            transparency: 1.0,
242            normal: match normal {
243                Some(normal) => Some(normal),
244                None => None,
245            },
246            normal_strength: normal_strength.unwrap_or_else(|| 1.0),
247            roughness: match roughness {
248                Some(roughness) => Some(roughness),
249                None => None,
250            },
251            roughness_strength: roughness_strength.unwrap_or_else(|| 1.0),
252            metallic: match metallic {
253                Some(metallic) => Some(metallic),
254                None => None,
255            },
256            metallic_strength: metallic_strength.unwrap_or_else(|| 1.0),
257            emissive: match emissive {
258                Some(emissive) => Some(emissive),
259                None => None,
260            },
261            emissive_strength: emissive_strength.unwrap_or_else(|| 1.0),
262            _tex_white,
263            _tex_black,
264            _tex_gray,
265            _tex_normal,
266            program: _program,
267            time: 0.0,
268            matrix: [
269                [1.0, 0.0, 0.0, 0.0],
270                [0.0, 1.0, 0.0, 0.0],
271                [0.0, 0.0, 1.0, 0.0],
272                [0.0, 0.0, 0.0, 1.0f32],
273            ],
274            render_transparent: false,
275            uuid: Uuid::new_v4(),
276        }
277    }
278
279    pub fn set_name(&mut self, name: &str) {
280        self.name = name.to_string()
281    }
282
283    pub fn set_transparency(&mut self, transparent: bool) {
284        self.render_transparent = transparent;
285    }
286
287    pub fn set_emissive(&mut self, emissive: texture::Texture) {
288        self.emissive = Some(emissive);
289    }
290
291    pub fn set_emissive_strength(&mut self, emissive_strength: f32) {
292        self.emissive_strength = emissive_strength;
293    }
294
295    pub fn set_color(&mut self, color: [f32; 3]) {
296        self.color = color;
297    }
298
299    pub fn set_shader(&mut self, shader: shader::Shader) {
300        self.shader = shader.clone();
301        self.program = glium::Program::from_source(&self.display, &shader.get_vertex_shader(), &shader.get_fragment_shader(), shader.get_geometry_shader().as_deref()).expect("Failed to compile shader program");
302    }
303
304    pub fn set_albedo(&mut self, albedo: texture::Texture) {
305        self.albedo = Some(albedo);
306    }
307
308    pub fn set_normal(&mut self, normal: texture::Texture) {
309        self.normal = Some(normal);
310    }
311
312    pub fn set_transparency_strength(&mut self, transparency: f32) {
313        self.transparency = transparency;
314    }
315
316    pub fn set_normal_strength(&mut self, normal_strength: f32) {
317        self.normal_strength = normal_strength;
318    }
319
320    pub fn set_roughness(&mut self, roughness: texture::Texture) {
321        self.roughness = Some(roughness);
322    }
323
324    pub fn set_roughness_strength(&mut self, roughness_strength: f32) {
325        self.roughness_strength = roughness_strength;
326    }
327
328    pub fn set_metallic(&mut self, metallic: texture::Texture) {
329        self.metallic = Some(metallic);
330    }
331
332    pub fn set_metallic_strength(&mut self, metallic_strength: f32) {
333        self.metallic_strength = metallic_strength;
334    }
335
336    pub fn set_texture_from_file(&mut self, path: &str, texture_type: TextureType) {
337        match texture_type {
338            TextureType::Albedo => self.albedo = Some(texture::Texture::new(&self.display, path)),
339            TextureType::Normal => self.normal = Some(texture::Texture::new(&self.display, path)),
340            TextureType::Roughness => self.roughness = Some(texture::Texture::new(&self.display, path)),
341            TextureType::Metallic => self.metallic = Some(texture::Texture::new(&self.display, path)),
342            TextureType::Emissive => self.emissive = Some(texture::Texture::new(&self.display, path)),
343        }
344    }
345
346    pub fn set_texture_from_resource(&mut self, data: &[u8], texture_type: TextureType) {
347        match texture_type {
348            TextureType::Albedo => self.albedo = Some(texture::Texture::from_resource(&self.display, data)),
349            TextureType::Normal => self.normal = Some(texture::Texture::from_resource(&self.display, data)),
350            TextureType::Roughness => self.roughness = Some(texture::Texture::from_resource(&self.display, data)),
351            TextureType::Metallic => self.metallic = Some(texture::Texture::from_resource(&self.display, data)),
352            TextureType::Emissive => self.emissive = Some(texture::Texture::from_resource(&self.display, data)),
353        }
354    }
355
356    pub fn set_texture(&mut self, texture: texture::Texture, texture_type: TextureType) {
357        match texture_type {
358            TextureType::Albedo => self.albedo = Some(texture),
359            TextureType::Normal => self.normal = Some(texture),
360            TextureType::Roughness => self.roughness = Some(texture),
361            TextureType::Metallic => self.metallic = Some(texture),
362            TextureType::Emissive => self.emissive = Some(texture),
363        }
364    }
365
366    pub fn lit_pbr(display: Display<WindowSurface>, transparency: bool) -> Self {
367        let mut mat = Material::default(shader::Shader::from_strings(resources::vertex_shader(), resources::fragment_shader(), None), &display);
368        mat.set_transparency(transparency);
369        mat
370    }
371
372    pub fn unlit(display: Display<WindowSurface>, transparency: bool) -> Self {
373        let mut mat = Material::default(shader::Shader::from_strings(resources::vertex_shader(), resources::fragment_unlit_shader(), None), &display);
374        mat.set_transparency(transparency);
375        mat
376    }
377
378    fn light_block_from_vec(lights: &Vec<Light>, ambient_light: Option<Light>) -> LightBlock {
379        let mut light_amount = lights.len() as i32;
380        if light_amount > 4 {
381            light_amount = 4;
382        }
383
384        let mut light_position: [[f32; 4]; 4] = [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]];
385        let mut light_color: [[f32; 4]; 4] = [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]];
386        let mut light_intensity: [f32; 4] = [0.0, 0.0, 0.0, 0.0];
387        let mut light_direction: [[f32; 4]; 4] = [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]];
388        let mut cast_shadow: [i32; 4] = [0, 0, 0, 0];
389
390        for i in 0..5 {
391            if i < light_amount as usize {
392                light_position[i] = [lights[i].position[0], lights[i].position[1], lights[i].position[2], 0.0];
393                light_color[i] = [lights[i].color[0], lights[i].color[1], lights[i].color[2], 0.0];
394                light_intensity[i] = lights[i].intensity;
395                light_direction[i] = {
396                    let direction = lights[i].direction;
397                    if direction == [0.0, 0.0, 0.0] {
398                        [0.0, 0.0, 0.0, 0.0]
399                    } else {
400                        [lights[i].direction[0], lights[i].direction[1], lights[i].direction[2], 1.0]
401                    }
402                };
403                cast_shadow[i] = if lights[i].cast_shadow { 1 } else { 0 };
404            }
405        }
406
407        LightBlock {
408            position: light_position,
409            directions: light_direction,
410            cast_shadow,
411            color: light_color,
412            intensity: light_intensity,
413            amount: light_amount,
414            ambient_color: match ambient_light {
415                Some(ambient_light) => ambient_light.color,
416                None => [0.0, 0.0, 0.0],
417            },
418            ambient_intensity: match ambient_light {
419                Some(ambient_light) => ambient_light.intensity,
420                None => 0.0,
421            },
422        }
423    }
424
425    pub fn get_uniforms<'a>(&'a self, lights: &Vec<Light>, ambient_light: Option<Light>, camera: Option<Camera>, _bone_transforms: &'a UniformBuffer<BoneTransforms>, _has_skeleton: bool, skybox: &'a texture::Texture) -> impl glium::uniforms::Uniforms + 'a {
426        let light_block = Material::light_block_from_vec(lights, ambient_light);
427
428        glium::uniform! {
429            time: self.time,
430            matrix: self.matrix,
431            camera_position: match camera {
432                Some(camera) => {
433                    let pos = camera.transform.get_position();
434                    [pos.x, pos.y, pos.z]
435                }
436                None => [0.0,0.0,0.0],
437            },
438            projection_matrix: match camera {
439                Some(camera) => camera.get_projection_matrix(),
440                None => Camera::new(None, None, None, None, None, None).get_projection_matrix(),
441            },
442            view_matrix: match camera {
443                Some(camera) => camera.get_view_matrix(),
444                None => Camera::new(None, None, None, None, None, None).get_view_matrix(),
445            },
446            mat_color: self.color,
447            mat_albedo: match &self.albedo {
448                Some(albedo) => {
449                    if albedo.tileable{
450                        albedo.texture.sampled().wrap_function(SamplerWrapFunction::Repeat)
451                    } else{
452                        albedo.texture.sampled()
453                    }
454                },
455                None => self._tex_white.sampled()
456            },
457            mat_normal: match &self.normal {
458                Some(normal) => {
459                    if normal.tileable {
460                        normal.texture.sampled().wrap_function(SamplerWrapFunction::Repeat)
461                    } else {
462                        normal.texture.sampled()
463                    }
464
465                },
466                None => self._tex_normal.sampled(),
467            },
468            mat_normal_strength: self.normal_strength,
469            mat_roughness: match &self.roughness {
470                Some(roughness) => {
471                    if roughness.tileable {
472                        roughness.texture.sampled().wrap_function(SamplerWrapFunction::Repeat)
473                    } else {
474                        roughness.texture.sampled()
475                    }
476                },
477                None => self._tex_gray.sampled()
478            },
479            mat_roughness_strength: self.roughness_strength,
480            mat_metallic: match &self.metallic {
481                Some(metallic) => {
482                    if metallic.tileable{
483                        metallic.texture.sampled().wrap_function(SamplerWrapFunction::Repeat)
484                    } else {
485                        metallic.texture.sampled()
486                    }
487                }
488                None => self._tex_black.sampled()
489            },
490            mat_metallic_strength: self.metallic_strength,
491            mat_emissive: match &self.emissive {
492                Some(emissive) => {
493                    if emissive.tileable{
494                        emissive.texture.sampled().wrap_function(SamplerWrapFunction::Repeat)
495                    } else {
496                        emissive.texture.sampled()
497                    }
498                },
499                None => self._tex_black.sampled()
500            },
501            mat_emissive_strength: self.emissive_strength,
502            mat_transparency_strength: self.transparency,
503            light_position: light_block.position,
504            light_direction: light_block.directions,
505            light_color: light_block.color,
506            light_intensity: light_block.intensity,
507            light_amount: light_block.amount,
508            ambient_light_color: light_block.ambient_color,
509            ambient_light_intensity: light_block.ambient_intensity,
510            skybox: &skybox.texture,
511            //BoneTransforms: bone_transforms,
512            has_skeleton: false //has_skeleton
513        }
514    }
515    fn tex_raw_from_array(color: [f32; 4]) -> RawImage2d<'static, u8> {
516        let byte_color: [u8; 4] = [
517            (color[0] * 255.0) as u8,
518            (color[1] * 255.0) as u8,
519            (color[2] * 255.0) as u8,
520            (color[3] * 255.0) as u8,
521        ];
522
523        RawImage2d::from_raw_rgba_reversed(&byte_color, (1, 1))
524    }
525
526    pub fn update(&mut self) {
527        self.time += 0.001;
528    }
529}