use bytemuck::{Pod, Zeroable};
use super::{Color3, OpenPbrSurface};
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Material {
pub base_color_weight: [f32; 4],
pub specular_color_weight: [f32; 4],
pub transmission_color_depth: [f32; 4],
pub transmission_scatter_aniso: [f32; 4],
pub subsurface_color_weight: [f32; 4],
pub subsurface_radius_scale_radius: [f32; 4],
pub coat_color_weight: [f32; 4],
pub fuzz_color_weight: [f32; 4],
pub emission_color_luminance: [f32; 4],
pub base_diffuse_roughness: f32,
pub base_metalness: f32,
pub specular_roughness: f32,
pub specular_anisotropy: f32,
pub specular_ior: f32,
pub transmission_weight: f32,
pub transmission_dispersion_abbe_number: f32,
pub transmission_dispersion_scale: f32,
pub subsurface_anisotropy: f32,
pub coat_roughness: f32,
pub coat_anisotropy: f32,
pub coat_ior: f32,
pub coat_darkening: f32,
pub fuzz_roughness: f32,
pub thin_film_weight: f32,
pub thin_film_thickness: f32,
pub thin_film_ior: f32,
pub geometry_opacity: f32,
pub geometry_thin_walled: u32,
pub normal_map_y_along_v: u32,
pub base_color_layer: f32,
pub roughness_layer: f32,
pub metalness_layer: f32,
pub normal_layer: f32,
}
fn c4(c: Color3, w: f32) -> [f32; 4] {
[c.r, c.g, c.b, w]
}
pub fn material_of(surface: &OpenPbrSurface) -> Material {
material_of_scaled(surface, 1.0)
}
pub fn material_of_scaled(surface: &OpenPbrSurface, length_scale: f32) -> Material {
let s = surface;
Material {
base_color_weight: c4(s.base_color, s.base_weight),
specular_color_weight: c4(s.specular_color, s.specular_weight),
transmission_color_depth: c4(s.transmission_color, s.transmission_depth * length_scale),
transmission_scatter_aniso: c4(s.transmission_scatter, s.transmission_scatter_anisotropy),
subsurface_color_weight: c4(s.subsurface_color, s.subsurface_weight),
subsurface_radius_scale_radius: c4(
s.subsurface_radius_scale,
s.subsurface_radius * length_scale,
),
coat_color_weight: c4(s.coat_color, s.coat_weight),
fuzz_color_weight: c4(s.fuzz_color, s.fuzz_weight),
emission_color_luminance: c4(s.emission_color, s.emission_luminance),
base_diffuse_roughness: s.base_diffuse_roughness,
base_metalness: s.base_metalness,
specular_roughness: s.specular_roughness,
specular_anisotropy: s.specular_roughness_anisotropy,
specular_ior: s.specular_ior,
transmission_weight: s.transmission_weight,
transmission_dispersion_abbe_number: s.transmission_dispersion_abbe_number,
transmission_dispersion_scale: s.transmission_dispersion_scale,
subsurface_anisotropy: s.subsurface_scatter_anisotropy,
coat_roughness: s.coat_roughness,
coat_anisotropy: s.coat_roughness_anisotropy,
coat_ior: s.coat_ior,
coat_darkening: s.coat_darkening,
fuzz_roughness: s.fuzz_roughness,
thin_film_weight: s.thin_film_weight,
thin_film_thickness: s.thin_film_thickness * 1000.0,
thin_film_ior: s.thin_film_ior,
geometry_opacity: s.geometry_opacity,
geometry_thin_walled: s.geometry_thin_walled as u32,
normal_map_y_along_v: 0,
base_color_layer: -1.0,
roughness_layer: -1.0,
metalness_layer: -1.0,
normal_layer: -1.0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_colour_and_its_weight_share_a_row() {
let mut surface = OpenPbrSurface::default();
surface.base_color = Color3::new(0.1, 0.2, 0.3);
surface.base_weight = 0.5;
let m = material_of(&surface);
assert_eq!(m.base_color_weight, [0.1, 0.2, 0.3, 0.5]);
}
#[test]
fn thin_film_crosses_into_nanometres() {
let mut surface = OpenPbrSurface::default();
surface.thin_film_thickness = 0.5;
assert_eq!(material_of(&surface).thin_film_thickness, 500.0);
}
#[test]
fn only_the_lengths_answer_to_scale() {
let mut surface = OpenPbrSurface::default();
surface.transmission_depth = 2.0;
surface.subsurface_radius = 3.0;
surface.specular_roughness = 0.4;
let m = material_of_scaled(&surface, 10.0);
assert_eq!(m.transmission_color_depth[3], 20.0);
assert_eq!(m.subsurface_radius_scale_radius[3], 30.0);
assert_eq!(m.specular_roughness, 0.4);
}
#[test]
fn a_material_with_no_maps_says_so() {
let m = material_of(&OpenPbrSurface::default());
for layer in [
m.base_color_layer,
m.roughness_layer,
m.metalness_layer,
m.normal_layer,
] {
assert!(
layer < 0.0,
"a missing map is a negative layer, got {layer}"
);
}
}
#[test]
fn the_block_is_the_size_the_shader_reads() {
assert_eq!(std::mem::size_of::<Material>(), 9 * 16 + 24 * 4);
}
}