1use crate::ecs::asset_id::{MaterialId, TextureId};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, enum2schema::Schema)]
10pub struct MaterialRef {
11 pub name: String,
13 #[serde(skip)]
15 #[schema(skip)]
16 pub id: Option<MaterialId>,
17}
18
19impl MaterialRef {
20 pub fn new(name: impl Into<String>) -> Self {
22 Self {
23 name: name.into(),
24 id: None,
25 }
26 }
27
28 pub fn with_id(name: impl Into<String>, id: MaterialId) -> Self {
30 Self {
31 name: name.into(),
32 id: Some(id),
33 }
34 }
35}
36
37impl Default for MaterialRef {
38 fn default() -> Self {
39 Self {
40 name: "Default".to_string(),
41 id: None,
42 }
43 }
44}
45
46impl From<String> for MaterialRef {
47 fn from(name: String) -> Self {
48 Self { name, id: None }
49 }
50}
51
52#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
60pub struct MaterialVariants {
61 pub default_material_name: String,
62 pub mappings: Vec<MaterialVariantMapping>,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66pub struct MaterialVariantMapping {
67 pub variant_names: Vec<String>,
68 pub material_name: String,
69}
70
71impl MaterialVariants {
72 pub fn material_for_variant(&self, variant_name: &str) -> &str {
75 for mapping in &self.mappings {
76 if mapping.variant_names.iter().any(|n| n == variant_name) {
77 return &mapping.material_name;
78 }
79 }
80 &self.default_material_name
81 }
82}
83
84impl From<&str> for MaterialRef {
85 fn from(name: &str) -> Self {
86 Self {
87 name: name.to_string(),
88 id: None,
89 }
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
99pub struct TextureTransform {
100 pub offset: [f32; 2],
102 pub rotation: f32,
104 pub scale: [f32; 2],
106 pub uv_set: u32,
108}
109
110impl Default for TextureTransform {
111 fn default() -> Self {
112 Self {
113 offset: [0.0, 0.0],
114 rotation: 0.0,
115 scale: [1.0, 1.0],
116 uv_set: 0,
117 }
118 }
119}
120
121impl TextureTransform {
122 pub const IDENTITY: Self = Self {
123 offset: [0.0, 0.0],
124 rotation: 0.0,
125 scale: [1.0, 1.0],
126 uv_set: 0,
127 };
128
129 pub fn to_packed(&self) -> ([f32; 3], [f32; 3]) {
142 let cos_r = self.rotation.cos();
143 let sin_r = self.rotation.sin();
144 let m00 = cos_r * self.scale[0];
145 let m01 = sin_r * self.scale[1];
146 let m02 = self.offset[0];
147 let m10 = -sin_r * self.scale[0];
148 let m11 = cos_r * self.scale[1];
149 let m12 = self.offset[1];
150 ([m00, m01, m02], [m10, m11, m12])
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
156pub enum AlphaMode {
157 #[default]
159 Opaque,
160 Mask,
162 Blend,
164}
165
166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct Material {
173 pub base_color: [f32; 4],
175 pub emissive_factor: [f32; 3],
177 pub alpha_mode: AlphaMode,
179 pub alpha_cutoff: f32,
181 #[serde(default = "default_blend_opaque_alpha_threshold")]
190 pub blend_opaque_alpha_threshold: f32,
191 pub base_texture: Option<String>,
193 #[serde(default)]
194 pub base_texture_transform: TextureTransform,
195 pub emissive_texture: Option<String>,
197 #[serde(default)]
198 pub emissive_texture_transform: TextureTransform,
199 pub normal_texture: Option<String>,
201 #[serde(default)]
202 pub normal_texture_transform: TextureTransform,
203 #[serde(default = "default_normal_scale")]
205 pub normal_scale: f32,
206 #[serde(default)]
208 pub normal_map_flip_y: bool,
209 #[serde(default)]
211 pub normal_map_two_component: bool,
212 pub metallic_roughness_texture: Option<String>,
214 #[serde(default)]
215 pub metallic_roughness_texture_transform: TextureTransform,
216 pub occlusion_texture: Option<String>,
218 #[serde(default)]
219 pub occlusion_texture_transform: TextureTransform,
220 #[serde(default = "default_occlusion_strength")]
222 pub occlusion_strength: f32,
223 pub roughness: f32,
225 pub metallic: f32,
227 pub unlit: bool,
229 #[serde(default)]
231 pub double_sided: bool,
232 #[serde(default)]
234 pub transmission_factor: f32,
235 #[serde(default)]
236 pub transmission_texture: Option<String>,
237 #[serde(default)]
238 pub transmission_texture_transform: TextureTransform,
239 #[serde(default)]
241 pub thickness: f32,
242 #[serde(default)]
243 pub thickness_texture: Option<String>,
244 #[serde(default)]
245 pub thickness_texture_transform: TextureTransform,
246 #[serde(default = "default_attenuation_color")]
248 pub attenuation_color: [f32; 3],
249 #[serde(default)]
251 pub attenuation_distance: f32,
252 #[serde(default = "default_ior")]
254 pub ior: f32,
255 #[serde(default = "default_specular_factor")]
257 pub specular_factor: f32,
258 #[serde(default = "default_specular_color_factor")]
260 pub specular_color_factor: [f32; 3],
261 #[serde(default)]
262 pub specular_texture: Option<String>,
263 #[serde(default)]
264 pub specular_texture_transform: TextureTransform,
265 #[serde(default)]
266 pub specular_color_texture: Option<String>,
267 #[serde(default)]
268 pub specular_color_texture_transform: TextureTransform,
269 #[serde(default = "default_emissive_strength")]
271 pub emissive_strength: f32,
272 #[serde(default)]
275 pub diffuse_transmission_factor: f32,
276 #[serde(default)]
277 pub diffuse_transmission_texture: Option<String>,
278 #[serde(default)]
279 pub diffuse_transmission_texture_transform: TextureTransform,
280 #[serde(default = "default_diffuse_transmission_color")]
282 pub diffuse_transmission_color_factor: [f32; 3],
283 #[serde(default)]
284 pub diffuse_transmission_color_texture: Option<String>,
285 #[serde(default)]
286 pub diffuse_transmission_color_texture_transform: TextureTransform,
287 #[serde(default)]
290 pub dispersion: f32,
291 #[serde(default)]
293 pub anisotropy_strength: f32,
294 #[serde(default)]
296 pub anisotropy_rotation: f32,
297 #[serde(default)]
298 pub anisotropy_texture: Option<String>,
299 #[serde(default)]
300 pub anisotropy_texture_transform: TextureTransform,
301 #[serde(default)]
303 pub iridescence_factor: f32,
304 #[serde(default)]
305 pub iridescence_texture: Option<String>,
306 #[serde(default)]
307 pub iridescence_texture_transform: TextureTransform,
308 #[serde(default = "default_iridescence_ior")]
310 pub iridescence_ior: f32,
311 #[serde(default = "default_iridescence_thickness_min")]
313 pub iridescence_thickness_minimum: f32,
314 #[serde(default = "default_iridescence_thickness_max")]
316 pub iridescence_thickness_maximum: f32,
317 #[serde(default)]
318 pub iridescence_thickness_texture: Option<String>,
319 #[serde(default)]
320 pub iridescence_thickness_texture_transform: TextureTransform,
321 #[serde(default)]
323 pub sheen_color_factor: [f32; 3],
324 #[serde(default)]
325 pub sheen_color_texture: Option<String>,
326 #[serde(default)]
327 pub sheen_color_texture_transform: TextureTransform,
328 #[serde(default)]
330 pub sheen_roughness_factor: f32,
331 #[serde(default)]
332 pub sheen_roughness_texture: Option<String>,
333 #[serde(default)]
334 pub sheen_roughness_texture_transform: TextureTransform,
335 #[serde(default)]
337 pub clearcoat_factor: f32,
338 #[serde(default)]
339 pub clearcoat_texture: Option<String>,
340 #[serde(default)]
341 pub clearcoat_texture_transform: TextureTransform,
342 #[serde(default)]
344 pub clearcoat_roughness_factor: f32,
345 #[serde(default)]
346 pub clearcoat_roughness_texture: Option<String>,
347 #[serde(default)]
348 pub clearcoat_roughness_texture_transform: TextureTransform,
349 #[serde(default)]
351 pub clearcoat_normal_texture: Option<String>,
352 #[serde(default)]
353 pub clearcoat_normal_texture_transform: TextureTransform,
354 #[serde(default = "default_normal_scale")]
355 pub clearcoat_normal_scale: f32,
356}
357
358#[derive(Clone, Copy, Debug, Default)]
366pub struct MaterialTextureIds {
367 pub base: Option<TextureId>,
368 pub emissive: Option<TextureId>,
369 pub normal: Option<TextureId>,
370 pub metallic_roughness: Option<TextureId>,
371 pub occlusion: Option<TextureId>,
372 pub transmission: Option<TextureId>,
373 pub thickness: Option<TextureId>,
374 pub specular: Option<TextureId>,
375 pub specular_color: Option<TextureId>,
376 pub diffuse_transmission: Option<TextureId>,
377 pub diffuse_transmission_color: Option<TextureId>,
378 pub anisotropy: Option<TextureId>,
379 pub iridescence: Option<TextureId>,
380 pub iridescence_thickness: Option<TextureId>,
381 pub sheen_color: Option<TextureId>,
382 pub sheen_roughness: Option<TextureId>,
383 pub clearcoat: Option<TextureId>,
384 pub clearcoat_roughness: Option<TextureId>,
385 pub clearcoat_normal: Option<TextureId>,
386}
387
388fn default_blend_opaque_alpha_threshold() -> f32 {
389 0.99
390}
391
392fn default_normal_scale() -> f32 {
393 1.0
394}
395
396fn default_occlusion_strength() -> f32 {
397 1.0
398}
399
400fn default_attenuation_color() -> [f32; 3] {
401 [1.0, 1.0, 1.0]
402}
403
404fn default_ior() -> f32 {
405 1.5
406}
407
408fn default_specular_factor() -> f32 {
409 1.0
410}
411
412fn default_specular_color_factor() -> [f32; 3] {
413 [1.0, 1.0, 1.0]
414}
415
416fn default_emissive_strength() -> f32 {
417 1.0
418}
419
420fn default_diffuse_transmission_color() -> [f32; 3] {
421 [1.0, 1.0, 1.0]
422}
423
424fn default_iridescence_ior() -> f32 {
425 1.3
426}
427
428fn default_iridescence_thickness_min() -> f32 {
429 100.0
430}
431
432fn default_iridescence_thickness_max() -> f32 {
433 400.0
434}
435
436impl Material {
437 pub fn is_transparent(&self) -> bool {
439 matches!(self.alpha_mode, AlphaMode::Mask | AlphaMode::Blend)
440 }
441
442 pub fn texture_names(&self) -> impl Iterator<Item = &str> {
447 [
448 self.base_texture.as_deref(),
449 self.emissive_texture.as_deref(),
450 self.normal_texture.as_deref(),
451 self.metallic_roughness_texture.as_deref(),
452 self.occlusion_texture.as_deref(),
453 self.transmission_texture.as_deref(),
454 self.thickness_texture.as_deref(),
455 self.specular_texture.as_deref(),
456 self.specular_color_texture.as_deref(),
457 self.diffuse_transmission_texture.as_deref(),
458 self.diffuse_transmission_color_texture.as_deref(),
459 self.anisotropy_texture.as_deref(),
460 self.iridescence_texture.as_deref(),
461 self.iridescence_thickness_texture.as_deref(),
462 self.sheen_color_texture.as_deref(),
463 self.sheen_roughness_texture.as_deref(),
464 self.clearcoat_texture.as_deref(),
465 self.clearcoat_roughness_texture.as_deref(),
466 self.clearcoat_normal_texture.as_deref(),
467 ]
468 .into_iter()
469 .flatten()
470 }
471}
472
473impl Default for Material {
474 fn default() -> Self {
475 Self {
476 base_color: [0.7, 0.7, 0.7, 1.0],
477 emissive_factor: [0.0, 0.0, 0.0],
478 alpha_mode: AlphaMode::Opaque,
479 alpha_cutoff: 0.5,
480 blend_opaque_alpha_threshold: 0.99,
481 base_texture: None,
482 base_texture_transform: TextureTransform::IDENTITY,
483 emissive_texture: None,
484 emissive_texture_transform: TextureTransform::IDENTITY,
485 normal_texture: None,
486 normal_texture_transform: TextureTransform::IDENTITY,
487 normal_scale: 1.0,
488 normal_map_flip_y: false,
489 normal_map_two_component: false,
490 metallic_roughness_texture: None,
491 metallic_roughness_texture_transform: TextureTransform::IDENTITY,
492 occlusion_texture: None,
493 occlusion_texture_transform: TextureTransform::IDENTITY,
494 occlusion_strength: 1.0,
495 roughness: 0.5,
496 metallic: 0.0,
497 unlit: false,
498 double_sided: false,
499 transmission_factor: 0.0,
500 transmission_texture: None,
501 transmission_texture_transform: TextureTransform::IDENTITY,
502 thickness: 0.0,
503 thickness_texture: None,
504 thickness_texture_transform: TextureTransform::IDENTITY,
505 attenuation_color: [1.0, 1.0, 1.0],
506 attenuation_distance: 0.0,
507 ior: 1.5,
508 specular_factor: 1.0,
509 specular_color_factor: [1.0, 1.0, 1.0],
510 specular_texture: None,
511 specular_texture_transform: TextureTransform::IDENTITY,
512 specular_color_texture: None,
513 specular_color_texture_transform: TextureTransform::IDENTITY,
514 emissive_strength: 1.0,
515 diffuse_transmission_factor: 0.0,
516 diffuse_transmission_texture: None,
517 diffuse_transmission_texture_transform: TextureTransform::IDENTITY,
518 diffuse_transmission_color_factor: [1.0, 1.0, 1.0],
519 diffuse_transmission_color_texture: None,
520 diffuse_transmission_color_texture_transform: TextureTransform::IDENTITY,
521 dispersion: 0.0,
522 anisotropy_strength: 0.0,
523 anisotropy_rotation: 0.0,
524 anisotropy_texture: None,
525 anisotropy_texture_transform: TextureTransform::IDENTITY,
526 iridescence_factor: 0.0,
527 iridescence_texture: None,
528 iridescence_texture_transform: TextureTransform::IDENTITY,
529 iridescence_ior: 1.3,
530 iridescence_thickness_minimum: 100.0,
531 iridescence_thickness_maximum: 400.0,
532 iridescence_thickness_texture: None,
533 iridescence_thickness_texture_transform: TextureTransform::IDENTITY,
534 sheen_color_factor: [0.0, 0.0, 0.0],
535 sheen_color_texture: None,
536 sheen_color_texture_transform: TextureTransform::IDENTITY,
537 sheen_roughness_factor: 0.0,
538 sheen_roughness_texture: None,
539 sheen_roughness_texture_transform: TextureTransform::IDENTITY,
540 clearcoat_factor: 0.0,
541 clearcoat_texture: None,
542 clearcoat_texture_transform: TextureTransform::IDENTITY,
543 clearcoat_roughness_factor: 0.0,
544 clearcoat_roughness_texture: None,
545 clearcoat_roughness_texture_transform: TextureTransform::IDENTITY,
546 clearcoat_normal_texture: None,
547 clearcoat_normal_texture_transform: TextureTransform::IDENTITY,
548 clearcoat_normal_scale: 1.0,
549 }
550 }
551}