use core::mem::offset_of;
use crate::optics::raytracer::{DEFAULT_HEAD_SHADOW_COSINES, TentParams};
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GpuTransportParams {
pub num_pixels: u32,
pub max_bounces: u32,
pub sample_offset: u32,
pub env_mode: u32,
pub l0: f32,
pub studio_temp_k: f32,
pub studio_spot_mult: f32,
pub studio_exposure: f32,
pub studio_light_yaw: f32,
pub studio_light_pitch: f32,
pub pixel_offset: u32,
pub write_debug_buffers: u32,
pub white_balance: [f32; 3],
pub studio_use_d65: u32,
pub studio_model: u32,
pub backdrop: f32,
pub surface_glare: f32,
pub head_shadow_outer_cos: f32,
pub head_shadow_inner_cos: f32,
pub tent_flat: f32,
_pad_head_shadow1: u32,
_pad_head_shadow2: u32,
pub tent_walls: f32,
pub tent_cards: f32,
pub tent_spark: f32,
pub tent_ground: f32,
}
pub mod studio_model {
pub const STUDIO: u32 = 0;
pub const ISO_HEMISPHERE: u32 = 1;
pub const LIGHT_TENT: u32 = 2;
pub const DAYLIGHT_DOME: u32 = 3;
pub const ASET: u32 = 4;
pub const DAYLIGHT_SUN: u32 = 5;
}
pub mod transport_env_mode {
pub const UNIFORM_FURNACE: u32 = 0;
pub const STUDIO_RIG: u32 = 1;
pub const HDR_MAP: u32 = 2;
}
impl GpuTransportParams {
#[must_use]
#[expect(
clippy::too_many_arguments,
reason = "one parameter per GpuTransportParams field, in field order, because \
that field order is itself the #[repr(C)] layout spectral_transport.wgsl's \
uniform buffer binds against; bundling them into a context struct here \
would just re-introduce this exact struct one level removed"
)]
pub const fn new(
num_pixels: u32,
max_bounces: u32,
sample_offset: u32,
env_mode: u32,
l0: f32,
studio_temp_k: f32,
studio_spot_mult: f32,
studio_exposure: f32,
studio_light_yaw: f32,
studio_light_pitch: f32,
white_balance: [f32; 3],
) -> Self {
Self {
num_pixels,
max_bounces,
sample_offset,
env_mode,
l0,
studio_temp_k,
studio_spot_mult,
studio_exposure,
studio_light_yaw,
studio_light_pitch,
pixel_offset: 0,
write_debug_buffers: 1,
white_balance,
studio_use_d65: 0,
studio_model: 0,
backdrop: 0.0,
surface_glare: 1.0,
head_shadow_outer_cos: DEFAULT_HEAD_SHADOW_COSINES[0],
head_shadow_inner_cos: DEFAULT_HEAD_SHADOW_COSINES[1],
tent_flat: TentParams::DEFAULT.flat,
_pad_head_shadow1: 0,
_pad_head_shadow2: 0,
tent_walls: TentParams::DEFAULT.walls,
tent_cards: TentParams::DEFAULT.cards,
tent_spark: TentParams::DEFAULT.spark,
tent_ground: TentParams::DEFAULT.ground,
}
}
#[must_use]
pub const fn with_tent(mut self, tent: TentParams) -> Self {
self.tent_walls = tent.walls;
self.tent_cards = tent.cards;
self.tent_spark = tent.spark;
self.tent_ground = tent.ground;
self.tent_flat = tent.flat;
self
}
#[must_use]
pub const fn with_tent_of(
self,
environment: crate::optics::raytracer::EnvironmentSource<'_>,
) -> Self {
match environment.lighting_preset() {
Some(preset) => self.with_tent(preset.params().tent),
None => self.with_tent(TentParams::DEFAULT),
}
}
#[must_use]
pub const fn with_pixel_offset(mut self, pixel_offset: u32) -> Self {
self.pixel_offset = pixel_offset;
self
}
#[must_use]
pub const fn with_debug_buffers_disabled(mut self) -> Self {
self.write_debug_buffers = 0;
self
}
#[must_use]
pub const fn with_studio_use_d65(mut self, use_d65: bool) -> Self {
self.studio_use_d65 = if use_d65 { 1 } else { 0 };
self
}
#[must_use]
pub const fn with_studio_model(mut self, model: u32) -> Self {
self.studio_model = model;
self
}
#[must_use]
pub const fn with_backdrop(mut self, backdrop: f32) -> Self {
self.backdrop = backdrop;
self
}
#[must_use]
pub const fn with_surface_glare(mut self, surface_glare: f32) -> Self {
self.surface_glare = if surface_glare >= 1.0 || surface_glare.is_nan() {
1.0
} else if surface_glare > 0.0 {
surface_glare
} else {
0.0
};
self
}
#[must_use]
pub const fn with_head_shadow(mut self, cosines: [f32; 2]) -> Self {
self.head_shadow_outer_cos = cosines[0];
self.head_shadow_inner_cos = cosines[1];
self
}
}
const _: () = {
assert!(offset_of!(GpuTransportParams, num_pixels) == 0);
assert!(offset_of!(GpuTransportParams, max_bounces) == 4);
assert!(offset_of!(GpuTransportParams, sample_offset) == 8);
assert!(offset_of!(GpuTransportParams, env_mode) == 12);
assert!(offset_of!(GpuTransportParams, l0) == 16);
assert!(offset_of!(GpuTransportParams, studio_temp_k) == 20);
assert!(offset_of!(GpuTransportParams, studio_spot_mult) == 24);
assert!(offset_of!(GpuTransportParams, studio_exposure) == 28);
assert!(offset_of!(GpuTransportParams, studio_light_yaw) == 32);
assert!(offset_of!(GpuTransportParams, studio_light_pitch) == 36);
assert!(offset_of!(GpuTransportParams, white_balance) == 48);
assert!(offset_of!(GpuTransportParams, studio_use_d65) == 60);
assert!(offset_of!(GpuTransportParams, studio_model) == 64);
assert!(offset_of!(GpuTransportParams, backdrop) == 68);
assert!(offset_of!(GpuTransportParams, surface_glare) == 72);
assert!(offset_of!(GpuTransportParams, head_shadow_outer_cos) == 76);
assert!(offset_of!(GpuTransportParams, head_shadow_inner_cos) == 80);
assert!(offset_of!(GpuTransportParams, tent_flat) == 84);
assert!(offset_of!(GpuTransportParams, _pad_head_shadow1) == 88);
assert!(offset_of!(GpuTransportParams, _pad_head_shadow2) == 92);
assert!(offset_of!(GpuTransportParams, tent_walls) == 96);
assert!(offset_of!(GpuTransportParams, tent_cards) == 100);
assert!(offset_of!(GpuTransportParams, tent_spark) == 104);
assert!(offset_of!(GpuTransportParams, tent_ground) == 108);
assert!(size_of::<GpuTransportParams>() == 112);
};