use super::*;
use crate::*;
const UV_CENTER: f32 = 0.5;
fn coverage(cascade: Cascade) -> f32 {
cascade.texel_width * RESOLUTION as f32
}
fn world_to_uv(world: Vec2, cascade: Cascade) -> Vec2 {
(world - cascade.center) / coverage(cascade) + Vec2::splat(UV_CENTER)
}
fn uv_to_world(uv: Vec2, cascade: Cascade) -> Vec2 {
coverage(cascade) * (uv - Vec2::splat(UV_CENTER)) + cascade.center
}
#[test]
fn body_params_abi_is_six_full_vec4s() {
assert_eq!(std::mem::size_of::<BodyParams>(), 96);
let optics = crate::cascade::BodyOptics {
extinction: Vec3::new(0.28, 0.16, 0.12),
scatter_scale: 0.18,
sun_roughness: 0.1,
};
let mut bytes = Vec::new();
bevy::render::render_resource::encase::UniformBuffer::new(&mut bytes)
.write(&BodyParams::bounded(
Vec2::new(-40.0, 20.0),
25.0,
Vec2::new(-70.0, -5.0),
Vec2::new(60.0, 50.0),
true,
Some(optics),
))
.expect("body params write");
assert_eq!(bytes.len(), 96);
let words: [f32; 24] = std::array::from_fn(|index| {
f32::from_le_bytes(bytes[index * 4..index * 4 + 4].try_into().unwrap())
});
assert_eq!(
words,
[
1.0, 1.0, 0.0, 0.0, -40.0, 20.0, 0.0, 25.0, -70.0, -5.0, 0.0, 0.0, 60.0, 50.0, 0.0, 0.0, 0.28, 0.16, 0.12, 1.0, 0.18, 0.1, 1.0, 0.0,
]
);
}
#[test]
fn gpu_cascade_sentinel_and_bed_echo() {
assert_eq!(std::mem::size_of::<GpuCascade>(), 32);
let mut gpu = GpuLayout::new(&layout(Vec2::ZERO), Vec2::ZERO, 1.25);
assert_eq!(gpu.center.z, 1.25);
let mut expected = gpu.cascades[LOD_COUNT - 1];
expected.weight = 0.0;
assert_eq!(gpu.cascades[LOD_COUNT], expected);
gpu.set_bed(None, 2.5);
assert_eq!(gpu.bed_range.y, crate::bed::NO_BED_SPAN);
assert_eq!(gpu.bed_range.z, 2.5);
let mut images = bevy::asset::Assets::<bevy::image::Image>::default();
let map = crate::bed::BedHeightMap::from_height_fn(
&mut images,
|x, _z| x,
4,
Vec2::new(-1.5, -3.0),
1.0,
);
gpu.set_bed(Some(&map), -0.5);
assert_eq!(gpu.bed_transform.xy(), Vec2::new(-1.5, -3.0));
assert_eq!(gpu.bed_transform.zw(), Vec2::splat(1.0 / 3.0));
assert_eq!(gpu.bed_range.x, map.height_range[0]);
assert_eq!(gpu.bed_range.y, map.height_range[1] - map.height_range[0]);
assert_eq!(gpu.bed_range.z, -0.5);
}
#[test]
fn scale_and_coverage_double_per_lod() {
let cascades = layout(Vec2::new(17.0, -9.0));
for pair in cascades.windows(2) {
assert_eq!(pair[1].scale, pair[0].scale * 2.0);
assert_eq!(coverage(pair[1]), coverage(pair[0]) * 2.0);
assert_eq!(pair[1].texel_width, pair[0].texel_width * 2.0);
}
}
#[test]
fn centres_snap_down_for_negative_positions() {
for cascade in layout(Vec2::new(-0.01, -31.7)) {
let texels = cascade.center / cascade.texel_width;
assert_eq!(texels, texels.round());
assert!(cascade.center.x <= -0.01);
assert!(cascade.center.y <= -31.7);
}
}
#[test]
fn first_cascade_corners_map_to_unit_uv() {
let cascade = layout(Vec2::ZERO)[0];
let half_coverage = Vec2::splat(48.0);
assert_eq!(coverage(cascade), 96.0);
assert_eq!(world_to_uv(-half_coverage, cascade), Vec2::ZERO);
assert_eq!(world_to_uv(half_coverage, cascade), Vec2::ONE);
assert_eq!(uv_to_world(Vec2::ZERO, cascade), -half_coverage);
assert_eq!(uv_to_world(Vec2::ONE, cascade), half_coverage);
}
#[test]
fn depth_gated_transmission_limits_residual_to_two_to_the_minus_ten() {
const MAXIMUM_RESIDUAL: f32 = 1.0 / 1024.0;
for (_, optics) in WaterOptics::PRESETS {
let mut surface = SurfaceParams::default();
surface.apply_optics(&optics);
let density = surface.fog_density.truncate();
let minimum_extinction = density.min_element();
let cutoff = 1024.0_f32.ln() / minimum_extinction;
assert!(minimum_extinction.is_finite() && minimum_extinction > 0.0);
assert!(
(-density * cutoff)
.exp()
.cmple(Vec3::splat(MAXIMUM_RESIDUAL * (1.0 + 1e-5)))
.all()
);
}
let crest_cutoff = 1024.0_f32.ln() / 0.3;
assert!((crest_cutoff - 23.104_906).abs() < 0.000_01);
}
#[test]
fn detail_mips_preserve_filtered_slope_variance() {
let mut source = Vec::new();
for slope in [Vec2::X, -Vec2::X, Vec2::X, -Vec2::X] {
source.extend_from_slice(&encode_detail_normal(slope, slope.length_squared()));
}
let filtered = downsample_detail_normals(&source, 2);
let mean = Vec2::new(
filtered[0] as f32 / 127.5 - 1.0,
filtered[1] as f32 / 127.5 - 1.0,
);
let second_moment = 2.0 * filtered[2] as f32 / 255.0;
assert!(mean.length() < 0.01);
assert!((second_moment - 1.0).abs() < 0.01);
assert!((second_moment - mean.length_squared() - 1.0).abs() < 0.01);
}
#[test]
fn detail_mips_leave_constant_slopes_without_variance() {
let slope = Vec2::new(0.25, -0.5);
let pixel = encode_detail_normal(slope, slope.length_squared());
let filtered = downsample_detail_normals(&pixel.repeat(4), 2);
let mean = Vec2::new(
filtered[0] as f32 / 127.5 - 1.0,
filtered[1] as f32 / 127.5 - 1.0,
);
let second_moment = 2.0 * filtered[2] as f32 / 255.0;
assert!((second_moment - mean.length_squared()).abs() < 0.015);
}