Skip to main content

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