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 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}