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 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 has_skeleton: false }
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}