use super::*;
pub fn pack_finite(value: f64, non_finite: &str, out_of_range: &str) -> Result<f32, String> {
if !value.is_finite() {
return Err(non_finite.to_string());
}
let narrowed: f32 = value as f32;
if !narrowed.is_finite() {
return Err(out_of_range.to_string());
}
Ok(narrowed)
}
pub(crate) fn next_down(value: f32) -> f32 {
if value.is_nan() || value == f32::NEG_INFINITY {
return value;
}
if value > 0.0 {
return f32::from_bits(value.to_bits() - 1);
}
if value == 0.0 {
return -f32::from_bits(1);
}
f32::from_bits(value.to_bits() + 1)
}
pub(crate) fn next_up(value: f32) -> f32 {
if value.is_nan() || value == f32::INFINITY {
return value;
}
if value >= 0.0 {
return f32::from_bits(value.to_bits() + 1);
}
if value == 0.0 {
return f32::from_bits(1);
}
f32::from_bits(value.to_bits() - 1)
}
pub(crate) fn pack_lower_bound(value: f64) -> Result<f32, String> {
let narrowed: f32 = pack_finite(value, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?;
if (narrowed as f64) > value {
return Ok(next_down(narrowed));
}
Ok(narrowed)
}
pub(crate) fn pack_upper_bound(value: f64) -> Result<f32, String> {
let narrowed: f32 = pack_finite(value, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?;
if (narrowed as f64) < value {
return Ok(next_up(narrowed));
}
Ok(narrowed)
}
pub fn pack_vector(
vector: Vector3D,
non_finite: &str,
out_of_range: &str,
) -> Result<[f32; 3], String> {
let x: f32 = pack_finite(vector.get_x(), non_finite, out_of_range)?;
let y: f32 = pack_finite(vector.get_y(), non_finite, out_of_range)?;
let z: f32 = pack_finite(vector.get_z(), non_finite, out_of_range)?;
Ok([x, y, z])
}
pub fn pack_vector_lower(
vector: Vector3D,
non_finite: &str,
out_of_range: &str,
) -> Result<[f32; 3], String> {
if !vector.get_x().is_finite() || !vector.get_y().is_finite() || !vector.get_z().is_finite() {
return Err(non_finite.to_string());
}
let magnitude: f64 = vector
.get_x()
.abs()
.max(vector.get_y().abs())
.max(vector.get_z().abs());
if magnitude > RAYTRACE_MAX_F32_MAGNITUDE {
return Err(out_of_range.to_string());
}
Ok([
pack_lower_bound(vector.get_x())?,
pack_lower_bound(vector.get_y())?,
pack_lower_bound(vector.get_z())?,
])
}
pub fn pack_vector_upper(
vector: Vector3D,
non_finite: &str,
out_of_range: &str,
) -> Result<[f32; 3], String> {
if !vector.get_x().is_finite() || !vector.get_y().is_finite() || !vector.get_z().is_finite() {
return Err(non_finite.to_string());
}
let magnitude: f64 = vector
.get_x()
.abs()
.max(vector.get_y().abs())
.max(vector.get_z().abs());
if magnitude > RAYTRACE_MAX_F32_MAGNITUDE {
return Err(out_of_range.to_string());
}
Ok([
pack_upper_bound(vector.get_x())?,
pack_upper_bound(vector.get_y())?,
pack_upper_bound(vector.get_z())?,
])
}
pub fn pack_index(index: usize) -> Result<f32, String> {
if index > RAYTRACE_MAX_EXACT_F32_INDEX {
return Err(RAYTRACE_ERROR_INDEX_RANGE.to_string());
}
Ok(index as f32)
}
pub fn packed_shape_code(kind: OccluderKind) -> f32 {
match kind {
OccluderKind::Sphere => 0.0,
OccluderKind::Aabb => 1.0,
OccluderKind::Triangle => 2.0,
}
}
pub fn packed_material_code(kind: MaterialKind) -> f32 {
match kind {
MaterialKind::Lambert => 0.0,
MaterialKind::Phong => 1.0,
MaterialKind::Pbr => 2.0,
}
}
pub fn packed_light_code(kind: LightType) -> f32 {
match kind {
LightType::Directional => 0.0,
LightType::Point => 1.0,
LightType::Spot => 2.0,
}
}
pub(crate) fn half_ulp(probe: f32) -> f64 {
let up: f64 = next_up(probe) as f64;
let down: f64 = next_down(probe) as f64;
let span: f64 = (up - down).abs() * 0.5;
if span.is_finite() && span > 0.0 {
return span;
}
f32::from_bits(1) as f64
}
pub fn pack_sphere_radius(center: Vector3D, radius: f64) -> Result<f32, String> {
let probe: f32 = pack_finite(
center.get_x(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let widened: f64 = radius + half_ulp(probe) * 3.0_f64.sqrt();
pack_upper_bound(widened)
}
pub(crate) fn pack_primitive(
out: &mut Vec<f32>,
occluder: &Occluder,
original_index: usize,
) -> Result<(), String> {
let bound_index: f32 = pack_index(original_index)?;
let kind: OccluderKind = occluder.get_kind();
let (center, extent): (Vector3D, Vector3D) = match kind {
OccluderKind::Aabb => {
let lo: Vector3D = occluder.get_center();
let hi: Vector3D = occluder.get_extent();
(
Vector3D::new(
lo.get_x().min(hi.get_x()),
lo.get_y().min(hi.get_y()),
lo.get_z().min(hi.get_z()),
),
Vector3D::new(
lo.get_x().max(hi.get_x()),
lo.get_y().max(hi.get_y()),
lo.get_z().max(hi.get_z()),
),
)
}
_ => (occluder.get_center(), occluder.get_extent()),
};
let material: &Material = occluder.get_material();
let center_packed: [f32; 3] = match kind {
OccluderKind::Sphere => {
pack_vector_lower(center, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?
}
_ => pack_vector(center, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?,
};
let extent_packed: [f32; 3] = match kind {
OccluderKind::Sphere => {
let radius: f32 = pack_sphere_radius(center, extent.get_x())?;
[radius, 0.0, 0.0]
}
OccluderKind::Aabb => {
pack_vector_upper(extent, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?
}
OccluderKind::Triangle => [0.0, 0.0, 0.0],
};
let albedo: [f32; 3] = pack_vector(
material.get_albedo(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let emissive: [f32; 3] = pack_vector(
material.get_emissive(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let specular: f32 = pack_finite(
material.get_specular(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let shininess: f32 = pack_finite(
material.get_shininess(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let vertices: [Vector3D; 3] = occluder.get_vertices();
out.extend_from_slice(&[
center_packed[0],
center_packed[1],
center_packed[2],
packed_shape_code(kind),
]);
out.extend_from_slice(&[
extent_packed[0],
extent_packed[1],
extent_packed[2],
packed_material_code(material.get_kind()),
]);
let vertex0: [f32; 3] = pack_vector(
vertices[0],
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
out.extend_from_slice(&[vertex0[0], vertex0[1], vertex0[2], bound_index]);
let vertex1: [f32; 3] = pack_vector(
vertices[1],
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
out.extend_from_slice(&[vertex1[0], vertex1[1], vertex1[2], 0.0]);
let vertex2: [f32; 3] = pack_vector(
vertices[2],
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
out.extend_from_slice(&[vertex2[0], vertex2[1], vertex2[2], 0.0]);
out.extend_from_slice(&[albedo[0], albedo[1], albedo[2], specular]);
out.extend_from_slice(&[emissive[0], emissive[1], emissive[2], shininess]);
out.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
Ok(())
}
pub(crate) fn pack_node(out: &mut Vec<f32>, node: &RayTraceNode) -> Result<(), String> {
let lower: [f32; 3] = pack_vector_lower(
node.get_lower(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let upper: [f32; 3] = pack_vector_upper(
node.get_upper(),
RAYTRACE_ERROR_NON_FINITE,
RAYTRACE_ERROR_F32_RANGE,
)?;
let start: f32 = pack_index(node.get_start())?;
let count: f32 = pack_index(node.get_count())?;
let escape: f32 = pack_index(node.get_escape())?;
out.extend_from_slice(&[lower[0], lower[1], lower[2], start]);
out.extend_from_slice(&[upper[0], upper[1], upper[2], count]);
out.extend_from_slice(&[escape, 0.0, 0.0, 0.0]);
Ok(())
}
pub(crate) fn frame_basis(
camera: &Camera3D,
width: u32,
height: u32,
) -> Result<FrameBasis, String> {
if width == 0 || height == 0 {
return Err(RAYTRACE_ERROR_ZERO_DIMENSION.to_string());
}
let pixel_count: usize = match (width as usize).checked_mul(height as usize) {
Some(product) => product,
None => return Err(RAYTRACE_ERROR_DIMENSION_OVERFLOW.to_string()),
};
if pixel_count > RAYTRACE_MAX_PACKED_FLOATS {
return Err(RAYTRACE_ERROR_DIMENSION_OVERFLOW.to_string());
}
let eye: Vector3D = camera.get_position();
if !eye.get_x().is_finite() || !eye.get_y().is_finite() || !eye.get_z().is_finite() {
return Err(RAYTRACE_ERROR_CAMERA_NON_FINITE.to_string());
}
let fov: f64 = camera.get_fov();
let fov_usable: bool = fov.is_finite() && fov > 0.0 && fov < PI;
if !fov_usable {
return Err(RAYTRACE_ERROR_CAMERA_FOV.to_string());
}
let tan_half_fov: f64 = (fov * 0.5).tan();
let tan_usable: bool = tan_half_fov.is_finite() && tan_half_fov > 0.0;
if !tan_usable {
return Err(RAYTRACE_ERROR_CAMERA_FOV.to_string());
}
let forward: Vector3D = camera.forward();
if forward == Vector3D::zero() {
return Err(RAYTRACE_ERROR_CAMERA_BASIS.to_string());
}
let right: Vector3D = forward.cross(camera.get_up()).normalized();
let up: Vector3D = right.cross(forward);
if right == Vector3D::zero() || up == Vector3D::zero() {
return Err(RAYTRACE_ERROR_CAMERA_BASIS.to_string());
}
let near_t: f64 = camera.get_near();
let far_t: f64 = camera.get_far();
let range_ordered: bool =
near_t.is_finite() && far_t.is_finite() && near_t >= 0.0 && far_t > near_t;
if !range_ordered {
return Err(RAYTRACE_ERROR_CAMERA_RANGE.to_string());
}
let near_packed: f32 = pack_finite(
near_t,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_F32_RANGE,
)?;
let far_packed: f32 = pack_finite(
far_t,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_F32_RANGE,
)?;
let packed_range_ordered: bool = far_packed > near_packed;
if !packed_range_ordered {
return Err(RAYTRACE_ERROR_CAMERA_RANGE.to_string());
}
Ok(FrameBasis {
eye,
forward,
right,
up,
tan_half_fov,
aspect: width as f64 / height as f64,
width,
height,
near_t,
far_t,
})
}
pub fn pack_raytrace_frame(out: &mut Vec<f32>, frame: &RayTraceFrame<'_>) -> Result<(), String> {
let camera: &Camera3D = frame.get_camera();
let lights: &LightingUniforms = frame.get_lights();
let settings: &RayTraceSettings = frame.get_settings();
settings.validate()?;
let basis: FrameBasis = frame_basis(camera, frame.get_width(), frame.get_height())?;
let light_count: usize = lights.get_lights().len();
let light_slots: usize = light_count
.checked_mul(RAYTRACE_PACKED_LIGHT_VEC4S)
.ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
let total_slots: usize = RAYTRACE_PACKED_FRAME_HEADER_VEC4S
.checked_add(light_slots)
.ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
let total_floats: usize = total_slots
.checked_mul(4)
.ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
if total_floats > RAYTRACE_MAX_PACKED_FLOATS {
return Err(RAYTRACE_ERROR_FRAME_OVERFLOW.to_string());
}
let eye: [f32; 3] = pack_vector(
camera.get_position(),
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
)?;
let forward_packed: [f32; 3] = pack_vector(
basis.get_forward(),
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
)?;
let right_packed: [f32; 3] = pack_vector(
basis.get_right(),
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
)?;
let up_packed: [f32; 3] = pack_vector(
basis.get_up(),
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
)?;
let tan_half_fov: f32 = pack_finite(
basis.get_tan_half_fov(),
RAYTRACE_ERROR_CAMERA_NON_FINITE,
RAYTRACE_ERROR_CAMERA_NON_FINITE,
)?;
let ambient: [f32; 3] = pack_vector(
lights.get_ambient(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let lighting_eye: [f32; 3] = pack_vector(
lights.get_eye(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
out.clear();
out.extend_from_slice(&[eye[0], eye[1], eye[2], tan_half_fov]);
out.extend_from_slice(&[
forward_packed[0],
forward_packed[1],
forward_packed[2],
basis.get_aspect() as f32,
]);
out.extend_from_slice(&[
right_packed[0],
right_packed[1],
right_packed[2],
basis.get_width() as f32,
]);
out.extend_from_slice(&[
up_packed[0],
up_packed[1],
up_packed[2],
basis.get_height() as f32,
]);
out.extend_from_slice(&[ambient[0], ambient[1], ambient[2], light_count as f32]);
out.extend_from_slice(&[
lighting_eye[0],
lighting_eye[1],
lighting_eye[2],
settings.get_max_bounces() as f32,
]);
out.extend_from_slice(&[
basis.get_near_t() as f32,
basis.get_far_t() as f32,
settings.get_samples_per_axis() as f32,
0.0,
]);
out.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
for light in lights.get_lights().iter() {
let position: [f32; 3] = pack_vector(
light.get_position(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let direction: [f32; 3] = pack_vector(
light.get_direction(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let color: [f32; 3] = pack_vector(
light.get_color(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let intensity: f32 = pack_finite(
light.get_intensity(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let falloff: f32 = pack_finite(
light.get_falloff(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
let spot_cos: f32 = pack_finite(
light.get_spot_cos(),
RAYTRACE_ERROR_LIGHT_NON_FINITE,
RAYTRACE_ERROR_LIGHT_F32_RANGE,
)?;
out.extend_from_slice(&[
position[0],
position[1],
position[2],
packed_light_code(light.get_kind()),
]);
out.extend_from_slice(&[direction[0], direction[1], direction[2], intensity]);
out.extend_from_slice(&[color[0], color[1], color[2], falloff]);
out.extend_from_slice(&[spot_cos, 0.0, 0.0, 0.0]);
}
Ok(())
}