#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct TransparentView {
pub vp: [[f32; 4]; 4],
pub inv_vp: [[f32; 4]; 4],
pub camera_pos: [f32; 4],
pub viewport: [f32; 2],
pub time: f32,
pub prefilter_mip_count: f32,
pub sky_rot: [[f32; 4]; 3],
pub sun_dir: [f32; 4],
pub sun_color: [f32; 4],
}
#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct GlassParams {
pub centre: [f32; 4],
pub normal: [f32; 4],
pub tint: [f32; 4],
pub opacity: f32,
pub refraction_strength: f32,
pub fresnel_power: f32,
pub planar: f32,
}
#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct GlassMeshParams {
pub model: [[f32; 4]; 4],
pub tint: [f32; 4],
pub opacity: f32,
pub refraction_strength: f32,
pub fresnel_power: f32,
pub prefilter_mip_count: f32,
}
pub const WATER_MAX_WAVES: usize = 4;
#[derive(Copy, Clone, Default, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(C)]
pub struct WaterWaveGpu {
pub dir_amp_wave: [f32; 4],
pub speed_steep_pad: [f32; 4],
}
#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct WaterParams {
pub centre: [f32; 4],
pub deep_colour: [f32; 4],
pub shallow_colour: [f32; 4],
pub depth_falloff: f32,
pub foam_width: f32,
pub foam_intensity: f32,
pub fresnel_power: f32,
pub roughness: f32,
pub refraction_strength: f32,
pub wave_count: u32,
pub _pad: f32,
pub waves: [WaterWaveGpu; WATER_MAX_WAVES],
pub planar: [f32; 4],
}
const PLANAR_DISTORTION_PER_ROUGHNESS: f32 = 0.6;
const PLANAR_DISTORTION_MAX: f32 = 0.06;
impl WaterParams {
pub fn planar_lane(roughness: f32, mirrored: bool) -> [f32; 4] {
if !mirrored {
return [0.0; 4];
}
let distortion =
(roughness.max(0.0) * PLANAR_DISTORTION_PER_ROUGHNESS).min(PLANAR_DISTORTION_MAX);
[1.0, distortion, 0.0, 0.0]
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::{offset_of, size_of};
#[test]
fn glass_mesh_params_layout_matches_shader() {
assert_eq!(size_of::<GlassMeshParams>(), 96);
assert_eq!(offset_of!(GlassMeshParams, model), 0);
assert_eq!(offset_of!(GlassMeshParams, tint), 64);
assert_eq!(offset_of!(GlassMeshParams, opacity), 80);
assert_eq!(offset_of!(GlassMeshParams, refraction_strength), 84);
assert_eq!(offset_of!(GlassMeshParams, fresnel_power), 88);
assert_eq!(offset_of!(GlassMeshParams, prefilter_mip_count), 92);
assert_eq!(size_of::<GlassMeshParams>() % 16, 0);
}
#[test]
fn transparent_view_layout_matches_shader() {
assert_eq!(size_of::<TransparentView>(), 240);
assert_eq!(offset_of!(TransparentView, vp), 0);
assert_eq!(offset_of!(TransparentView, inv_vp), 64);
assert_eq!(offset_of!(TransparentView, camera_pos), 128);
assert_eq!(offset_of!(TransparentView, viewport), 144);
assert_eq!(offset_of!(TransparentView, time), 152);
assert_eq!(offset_of!(TransparentView, prefilter_mip_count), 156);
assert_eq!(offset_of!(TransparentView, sky_rot), 160);
assert_eq!(offset_of!(TransparentView, sun_dir), 208);
assert_eq!(offset_of!(TransparentView, sun_color), 224);
assert_eq!(size_of::<TransparentView>() % 16, 0);
}
#[test]
fn the_planar_lane_scales_the_ripple_offset_by_roughness() {
assert_eq!(WaterParams::planar_lane(0.0, true), [1.0, 0.0, 0.0, 0.0]);
let default_roughness = WaterParams::planar_lane(0.05, true);
assert!(
(default_roughness[1] - 0.03).abs() < 1e-6,
"{default_roughness:?}"
);
let mirror = WaterParams::planar_lane(0.01, true)[1];
let rough = WaterParams::planar_lane(0.2, true)[1];
assert!(mirror < default_roughness[1] && default_roughness[1] < rough);
assert_eq!(
WaterParams::planar_lane(1.0, true)[1],
PLANAR_DISTORTION_MAX
);
assert_eq!(WaterParams::planar_lane(-1.0, true)[1], 0.0);
assert_eq!(WaterParams::planar_lane(0.5, false), [0.0; 4]);
}
#[test]
fn glass_params_layout_matches_shader() {
assert_eq!(size_of::<GlassParams>(), 64);
assert_eq!(offset_of!(GlassParams, centre), 0);
assert_eq!(offset_of!(GlassParams, normal), 16);
assert_eq!(offset_of!(GlassParams, tint), 32);
assert_eq!(offset_of!(GlassParams, opacity), 48);
assert_eq!(offset_of!(GlassParams, refraction_strength), 52);
assert_eq!(offset_of!(GlassParams, fresnel_power), 56);
assert_eq!(offset_of!(GlassParams, planar), 60);
}
#[test]
fn water_params_layout_matches_shader() {
assert_eq!(size_of::<WaterParams>(), 224);
assert_eq!(offset_of!(WaterParams, centre), 0);
assert_eq!(offset_of!(WaterParams, deep_colour), 16);
assert_eq!(offset_of!(WaterParams, shallow_colour), 32);
assert_eq!(offset_of!(WaterParams, depth_falloff), 48);
assert_eq!(offset_of!(WaterParams, foam_width), 52);
assert_eq!(offset_of!(WaterParams, foam_intensity), 56);
assert_eq!(offset_of!(WaterParams, fresnel_power), 60);
assert_eq!(offset_of!(WaterParams, roughness), 64);
assert_eq!(offset_of!(WaterParams, refraction_strength), 68);
assert_eq!(offset_of!(WaterParams, wave_count), 72);
assert_eq!(offset_of!(WaterParams, _pad), 76);
assert_eq!(offset_of!(WaterParams, waves), 80);
assert_eq!(offset_of!(WaterParams, planar), 208);
assert_eq!(size_of::<WaterWaveGpu>(), 32);
}
}