1use std::mem;
4
5use glam::{Mat3, Mat3A, Vec3, Vec4};
6use rend3::types::{Material, TextureHandle};
7
8bitflags::bitflags! {
9 pub struct MaterialFlags : u32 {
11 const ALBEDO_ACTIVE = 0b0000_0000_0000_0001;
12 const ALBEDO_BLEND = 0b0000_0000_0000_0010;
13 const ALBEDO_VERTEX_SRGB = 0b0000_0000_0000_0100;
14 const BICOMPONENT_NORMAL = 0b0000_0000_0000_1000;
15 const SWIZZLED_NORMAL = 0b0000_0000_0001_0000;
16 const AOMR_COMBINED = 0b0000_0000_0010_0000;
17 const AOMR_SWIZZLED_SPLIT = 0b0000_0000_0100_0000;
18 const AOMR_SPLIT = 0b0000_0000_1000_0000;
19 const AOMR_BW_SPLIT = 0b0000_0001_0000_0000;
20 const CC_GLTF_COMBINED = 0b0000_0010_0000_0000;
21 const CC_GLTF_SPLIT = 0b0000_0100_0000_0000;
22 const CC_BW_SPLIT = 0b0000_1000_0000_0000;
23 const UNLIT = 0b0001_0000_0000_0000;
24 const NEAREST = 0b0010_0000_0000_0000;
25 }
26}
27
28#[derive(Debug, Clone)]
30pub enum AlbedoComponent {
31 None,
33 Vertex {
35 srgb: bool,
37 },
38 Value(Vec4),
40 ValueVertex {
42 value: Vec4,
43 srgb: bool,
45 },
46 Texture(TextureHandle),
48 TextureVertex {
51 texture: TextureHandle,
52 srgb: bool,
54 },
55 TextureValue { texture: TextureHandle, value: Vec4 },
58 TextureVertexValue {
61 texture: TextureHandle,
62 srgb: bool,
64 value: Vec4,
65 },
66}
67
68impl Default for AlbedoComponent {
69 fn default() -> Self {
70 Self::None
71 }
72}
73
74impl AlbedoComponent {
75 pub fn to_value(&self) -> Vec4 {
76 match *self {
77 Self::Value(value) => value,
78 Self::ValueVertex { value, .. } => value,
79 Self::TextureValue { value, .. } => value,
80 _ => Vec4::splat(1.0),
81 }
82 }
83
84 pub fn to_flags(&self) -> MaterialFlags {
85 match *self {
86 Self::None => MaterialFlags::empty(),
87 Self::Value(_) | Self::Texture(_) | Self::TextureValue { .. } => MaterialFlags::ALBEDO_ACTIVE,
88 Self::Vertex { srgb: false }
89 | Self::ValueVertex { srgb: false, .. }
90 | Self::TextureVertex { srgb: false, .. }
91 | Self::TextureVertexValue { srgb: false, .. } => {
92 MaterialFlags::ALBEDO_ACTIVE | MaterialFlags::ALBEDO_BLEND
93 }
94 Self::Vertex { srgb: true }
95 | Self::ValueVertex { srgb: true, .. }
96 | Self::TextureVertex { srgb: true, .. }
97 | Self::TextureVertexValue { srgb: true, .. } => {
98 MaterialFlags::ALBEDO_ACTIVE | MaterialFlags::ALBEDO_BLEND | MaterialFlags::ALBEDO_VERTEX_SRGB
99 }
100 }
101 }
102
103 pub fn is_texture(&self) -> bool {
104 matches!(
105 *self,
106 Self::Texture(..)
107 | Self::TextureVertex { .. }
108 | Self::TextureValue { .. }
109 | Self::TextureVertexValue { .. }
110 )
111 }
112
113 pub fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
114 where
115 Func: FnOnce(&TextureHandle) -> Out,
116 {
117 match *self {
118 Self::None | Self::Vertex { .. } | Self::Value(_) | Self::ValueVertex { .. } => None,
119 Self::Texture(ref texture)
120 | Self::TextureVertex { ref texture, .. }
121 | Self::TextureValue { ref texture, .. }
122 | Self::TextureVertexValue { ref texture, .. } => Some(func(texture)),
123 }
124 }
125}
126
127#[derive(Debug, Clone)]
129pub enum MaterialComponent<T> {
130 None,
131 Value(T),
132 Texture(TextureHandle),
133 TextureValue { texture: TextureHandle, value: T },
134}
135
136impl<T> Default for MaterialComponent<T> {
137 fn default() -> Self {
138 Self::None
139 }
140}
141
142impl<T: Copy> MaterialComponent<T> {
143 pub fn to_value(&self, default: T) -> T {
144 match *self {
145 Self::Value(value) | Self::TextureValue { value, .. } => value,
146 Self::None | Self::Texture(_) => default,
147 }
148 }
149
150 pub fn is_texture(&self) -> bool {
151 matches!(*self, Self::Texture(..) | Self::TextureValue { .. })
152 }
153
154 pub fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
155 where
156 Func: FnOnce(&TextureHandle) -> Out,
157 {
158 match *self {
159 Self::None | Self::Value(_) => None,
160 Self::Texture(ref texture) | Self::TextureValue { ref texture, .. } => Some(func(texture)),
161 }
162 }
163}
164
165#[derive(Debug, Clone)]
167pub enum NormalTexture {
168 None,
170 Tricomponent(TextureHandle),
172 Bicomponent(TextureHandle),
174 BicomponentSwizzled(TextureHandle),
177}
178impl Default for NormalTexture {
179 fn default() -> Self {
180 Self::None
181 }
182}
183
184impl NormalTexture {
185 pub fn to_texture<Func, Out>(&self, func: Func) -> Option<Out>
186 where
187 Func: FnOnce(&TextureHandle) -> Out,
188 {
189 match *self {
190 Self::None => None,
191 Self::Tricomponent(ref texture)
192 | Self::Bicomponent(ref texture)
193 | Self::BicomponentSwizzled(ref texture) => Some(func(texture)),
194 }
195 }
196
197 pub fn to_flags(&self) -> MaterialFlags {
198 match self {
199 Self::None => MaterialFlags::empty(),
200 Self::Tricomponent(..) => MaterialFlags::empty(),
201 Self::Bicomponent(..) => MaterialFlags::BICOMPONENT_NORMAL,
202 Self::BicomponentSwizzled(..) => MaterialFlags::BICOMPONENT_NORMAL | MaterialFlags::SWIZZLED_NORMAL,
203 }
204 }
205}
206
207#[derive(Debug, Clone)]
209pub enum AoMRTextures {
210 None,
211 Combined {
212 texture: Option<TextureHandle>,
214 },
215 SwizzledSplit {
216 ao_texture: Option<TextureHandle>,
218 mr_texture: Option<TextureHandle>,
220 },
221 Split {
222 ao_texture: Option<TextureHandle>,
224 mr_texture: Option<TextureHandle>,
226 },
227 BWSplit {
228 ao_texture: Option<TextureHandle>,
230 m_texture: Option<TextureHandle>,
232 r_texture: Option<TextureHandle>,
234 },
235}
236
237impl AoMRTextures {
238 pub fn to_roughness_texture<Func, Out>(&self, func: Func) -> Option<Out>
239 where
240 Func: FnOnce(&TextureHandle) -> Out,
241 {
242 match *self {
243 Self::Combined {
244 texture: Some(ref texture),
245 } => Some(func(texture)),
246 Self::SwizzledSplit {
247 mr_texture: Some(ref texture),
248 ..
249 } => Some(func(texture)),
250 Self::Split {
251 mr_texture: Some(ref texture),
252 ..
253 } => Some(func(texture)),
254 Self::BWSplit {
255 r_texture: Some(ref texture),
256 ..
257 } => Some(func(texture)),
258 _ => None,
259 }
260 }
261
262 pub fn to_metallic_texture<Func, Out>(&self, func: Func) -> Option<Out>
263 where
264 Func: FnOnce(&TextureHandle) -> Out,
265 {
266 match *self {
267 Self::Combined { .. } => None,
268 Self::SwizzledSplit { .. } => None,
269 Self::Split { .. } => None,
270 Self::BWSplit {
271 m_texture: Some(ref texture),
272 ..
273 } => Some(func(texture)),
274 _ => None,
275 }
276 }
277
278 pub fn to_ao_texture<Func, Out>(&self, func: Func) -> Option<Out>
279 where
280 Func: FnOnce(&TextureHandle) -> Out,
281 {
282 match *self {
283 Self::Combined { .. } => None,
284 Self::SwizzledSplit {
285 ao_texture: Some(ref texture),
286 ..
287 } => Some(func(texture)),
288 Self::Split {
289 ao_texture: Some(ref texture),
290 ..
291 } => Some(func(texture)),
292 Self::BWSplit {
293 ao_texture: Some(ref texture),
294 ..
295 } => Some(func(texture)),
296 _ => None,
297 }
298 }
299
300 pub fn to_flags(&self) -> MaterialFlags {
301 match self {
302 Self::Combined { .. } => MaterialFlags::AOMR_COMBINED,
303 Self::SwizzledSplit { .. } => MaterialFlags::AOMR_SWIZZLED_SPLIT,
304 Self::Split { .. } => MaterialFlags::AOMR_SPLIT,
305 Self::BWSplit { .. } => MaterialFlags::AOMR_BW_SPLIT,
306 Self::None => MaterialFlags::AOMR_COMBINED,
308 }
309 }
310}
311impl Default for AoMRTextures {
312 fn default() -> Self {
313 Self::None
314 }
315}
316
317#[derive(Debug, Clone)]
319pub enum ClearcoatTextures {
320 GltfCombined {
321 texture: Option<TextureHandle>,
323 },
324 GltfSplit {
325 clearcoat_texture: Option<TextureHandle>,
327 clearcoat_roughness_texture: Option<TextureHandle>,
329 },
330 BWSplit {
331 clearcoat_texture: Option<TextureHandle>,
333 clearcoat_roughness_texture: Option<TextureHandle>,
335 },
336 None,
337}
338
339impl ClearcoatTextures {
340 pub fn to_clearcoat_texture<Func, Out>(&self, func: Func) -> Option<Out>
341 where
342 Func: FnOnce(&TextureHandle) -> Out,
343 {
344 match *self {
345 Self::GltfCombined {
346 texture: Some(ref texture),
347 } => Some(func(texture)),
348 Self::GltfSplit {
349 clearcoat_texture: Some(ref texture),
350 ..
351 } => Some(func(texture)),
352 Self::BWSplit {
353 clearcoat_texture: Some(ref texture),
354 ..
355 } => Some(func(texture)),
356 _ => None,
357 }
358 }
359
360 pub fn to_clearcoat_roughness_texture<Func, Out>(&self, func: Func) -> Option<Out>
361 where
362 Func: FnOnce(&TextureHandle) -> Out,
363 {
364 match *self {
365 Self::GltfCombined { .. } => None,
366 Self::GltfSplit {
367 clearcoat_roughness_texture: Some(ref texture),
368 ..
369 } => Some(func(texture)),
370 Self::BWSplit {
371 clearcoat_roughness_texture: Some(ref texture),
372 ..
373 } => Some(func(texture)),
374 _ => None,
375 }
376 }
377
378 pub fn to_flags(&self) -> MaterialFlags {
379 match self {
380 Self::GltfCombined { .. } => MaterialFlags::CC_GLTF_COMBINED,
381 Self::GltfSplit { .. } => MaterialFlags::CC_GLTF_SPLIT,
382 Self::BWSplit { .. } => MaterialFlags::CC_BW_SPLIT,
383 Self::None => MaterialFlags::CC_GLTF_COMBINED,
385 }
386 }
387}
388impl Default for ClearcoatTextures {
389 fn default() -> Self {
390 Self::None
391 }
392}
393
394#[derive(Debug, Copy, Clone, PartialEq, Eq)]
396pub enum SampleType {
397 Nearest,
398 Linear,
399}
400impl Default for SampleType {
401 fn default() -> Self {
402 Self::Linear
403 }
404}
405
406#[repr(u8)]
408#[derive(Debug, Copy, Clone, PartialEq)]
409pub enum TransparencyType {
410 Opaque,
412 Cutout,
414 Blend,
416}
417impl From<Transparency> for TransparencyType {
418 fn from(t: Transparency) -> Self {
419 match t {
420 Transparency::Opaque => Self::Opaque,
421 Transparency::Cutout { .. } => Self::Cutout,
422 Transparency::Blend => Self::Blend,
423 }
424 }
425}
426impl TransparencyType {
427 pub fn to_debug_str(self) -> &'static str {
428 match self {
429 TransparencyType::Opaque => "opaque",
430 TransparencyType::Cutout => "cutout",
431 TransparencyType::Blend => "blend",
432 }
433 }
434}
435
436#[allow(clippy::cmp_owned)] impl PartialEq<Transparency> for TransparencyType {
438 fn eq(&self, other: &Transparency) -> bool {
439 *self == Self::from(*other)
440 }
441}
442
443#[allow(clippy::cmp_owned)]
444impl PartialEq<TransparencyType> for Transparency {
445 fn eq(&self, other: &TransparencyType) -> bool {
446 TransparencyType::from(*self) == *other
447 }
448}
449
450#[derive(Debug, Copy, Clone, PartialEq)]
452pub enum Transparency {
453 Opaque,
455 Cutout { cutout: f32 },
457 Blend,
459}
460impl Default for Transparency {
461 fn default() -> Self {
462 Self::Opaque
463 }
464}
465
466#[derive(Default)]
471pub struct PbrMaterial {
472 pub albedo: AlbedoComponent,
473 pub transparency: Transparency,
474 pub normal: NormalTexture,
475 pub aomr_textures: AoMRTextures,
476 pub ao_factor: Option<f32>,
477 pub metallic_factor: Option<f32>,
478 pub roughness_factor: Option<f32>,
479 pub clearcoat_textures: ClearcoatTextures,
480 pub clearcoat_factor: Option<f32>,
481 pub clearcoat_roughness_factor: Option<f32>,
482 pub emissive: MaterialComponent<Vec3>,
483 pub reflectance: MaterialComponent<f32>,
484 pub anisotropy: MaterialComponent<f32>,
485 pub uv_transform0: Mat3,
486 pub uv_transform1: Mat3,
487 pub unlit: bool,
489 pub sample_type: SampleType,
490}
491
492impl Material for PbrMaterial {
493 const TEXTURE_COUNT: u32 = 10;
494 const DATA_SIZE: u32 = mem::size_of::<ShaderMaterial>() as _;
495
496 fn object_key(&self) -> u64 {
497 TransparencyType::from(self.transparency) as u64
498 }
499
500 fn to_textures(
501 &self,
502 slice: &mut [Option<std::num::NonZeroU32>],
503 translation_fn: &mut (dyn FnMut(&TextureHandle) -> std::num::NonZeroU32 + '_),
504 ) {
505 slice[0] = self.albedo.to_texture(&mut *translation_fn);
506 slice[1] = self.normal.to_texture(&mut *translation_fn);
507 slice[2] = self.aomr_textures.to_roughness_texture(&mut *translation_fn);
508 slice[3] = self.aomr_textures.to_metallic_texture(&mut *translation_fn);
509 slice[4] = self.reflectance.to_texture(&mut *translation_fn);
510 slice[5] = self.clearcoat_textures.to_clearcoat_texture(&mut *translation_fn);
511 slice[6] = self
512 .clearcoat_textures
513 .to_clearcoat_roughness_texture(&mut *translation_fn);
514 slice[7] = self.emissive.to_texture(&mut *translation_fn);
515 slice[8] = self.anisotropy.to_texture(&mut *translation_fn);
516 slice[9] = self.aomr_textures.to_ao_texture(&mut *translation_fn);
517 }
518
519 fn to_data(&self, slice: &mut [u8]) {
520 slice.copy_from_slice(bytemuck::bytes_of(&ShaderMaterial::from_material(self)));
521 }
522}
523
524#[repr(C)]
525#[derive(Debug, Copy, Clone)]
526struct ShaderMaterial {
527 uv_transform0: Mat3A,
528 uv_transform1: Mat3A,
529
530 albedo: Vec4,
531 emissive: Vec3,
532 roughness: f32,
533 metallic: f32,
534 reflectance: f32,
535 clear_coat: f32,
536 clear_coat_roughness: f32,
537 anisotropy: f32,
538 ambient_occlusion: f32,
539 alpha_cutout: f32,
540
541 material_flags: MaterialFlags,
542}
543
544unsafe impl bytemuck::Zeroable for ShaderMaterial {}
545unsafe impl bytemuck::Pod for ShaderMaterial {}
546
547impl ShaderMaterial {
548 fn from_material(material: &PbrMaterial) -> Self {
549 Self {
550 uv_transform0: material.uv_transform0.into(),
551 uv_transform1: material.uv_transform1.into(),
552 albedo: material.albedo.to_value(),
553 roughness: material.roughness_factor.unwrap_or(0.0),
554 metallic: material.metallic_factor.unwrap_or(0.0),
555 reflectance: material.reflectance.to_value(0.5),
556 clear_coat: material.clearcoat_factor.unwrap_or(0.0),
557 clear_coat_roughness: material.clearcoat_roughness_factor.unwrap_or(0.0),
558 emissive: material.emissive.to_value(Vec3::ZERO),
559 anisotropy: material.anisotropy.to_value(0.0),
560 ambient_occlusion: material.ao_factor.unwrap_or(1.0),
561 alpha_cutout: match material.transparency {
562 Transparency::Cutout { cutout } => cutout,
563 _ => 0.0,
564 },
565 material_flags: {
566 let mut flags = material.albedo.to_flags();
567 flags |= material.normal.to_flags();
568 flags |= material.aomr_textures.to_flags();
569 flags |= material.clearcoat_textures.to_flags();
570 flags.set(MaterialFlags::UNLIT, material.unlit);
571 flags.set(
572 MaterialFlags::NEAREST,
573 match material.sample_type {
574 SampleType::Nearest => true,
575 SampleType::Linear => false,
576 },
577 );
578 flags
579 },
580 }
581 }
582}