1use super::*;
2
3pub fn pack_finite(value: f64, non_finite: &str, out_of_range: &str) -> Result<f32, String> {
19 if !value.is_finite() {
20 return Err(non_finite.to_string());
21 }
22 let narrowed: f32 = value as f32;
23 if !narrowed.is_finite() {
24 return Err(out_of_range.to_string());
25 }
26 Ok(narrowed)
27}
28
29pub(crate) fn next_down(value: f32) -> f32 {
45 if value.is_nan() || value == f32::NEG_INFINITY {
46 return value;
47 }
48 if value > 0.0 {
49 return f32::from_bits(value.to_bits() - 1);
50 }
51 if value == 0.0 {
52 return -f32::from_bits(1);
53 }
54 f32::from_bits(value.to_bits() + 1)
55}
56
57pub(crate) fn next_up(value: f32) -> f32 {
69 if value.is_nan() || value == f32::INFINITY {
70 return value;
71 }
72 if value >= 0.0 {
73 return f32::from_bits(value.to_bits() + 1);
74 }
75 if value == 0.0 {
76 return f32::from_bits(1);
77 }
78 f32::from_bits(value.to_bits() - 1)
79}
80
81pub(crate) fn pack_lower_bound(value: f64) -> Result<f32, String> {
100 let narrowed: f32 = pack_finite(value, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?;
101 if (narrowed as f64) > value {
102 return Ok(next_down(narrowed));
104 }
105 Ok(narrowed)
106}
107
108pub(crate) fn pack_upper_bound(value: f64) -> Result<f32, String> {
119 let narrowed: f32 = pack_finite(value, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?;
120 if (narrowed as f64) < value {
121 return Ok(next_up(narrowed));
123 }
124 Ok(narrowed)
125}
126
127pub fn pack_vector(
139 vector: Vector3D,
140 non_finite: &str,
141 out_of_range: &str,
142) -> Result<[f32; 3], String> {
143 let x: f32 = pack_finite(vector.get_x(), non_finite, out_of_range)?;
144 let y: f32 = pack_finite(vector.get_y(), non_finite, out_of_range)?;
145 let z: f32 = pack_finite(vector.get_z(), non_finite, out_of_range)?;
146 Ok([x, y, z])
147}
148
149pub fn pack_vector_lower(
162 vector: Vector3D,
163 non_finite: &str,
164 out_of_range: &str,
165) -> Result<[f32; 3], String> {
166 if !vector.get_x().is_finite() || !vector.get_y().is_finite() || !vector.get_z().is_finite() {
167 return Err(non_finite.to_string());
168 }
169 let magnitude: f64 = vector
170 .get_x()
171 .abs()
172 .max(vector.get_y().abs())
173 .max(vector.get_z().abs());
174 if magnitude > RAYTRACE_MAX_F32_MAGNITUDE {
175 return Err(out_of_range.to_string());
176 }
177 Ok([
178 pack_lower_bound(vector.get_x())?,
179 pack_lower_bound(vector.get_y())?,
180 pack_lower_bound(vector.get_z())?,
181 ])
182}
183
184pub fn pack_vector_upper(
197 vector: Vector3D,
198 non_finite: &str,
199 out_of_range: &str,
200) -> Result<[f32; 3], String> {
201 if !vector.get_x().is_finite() || !vector.get_y().is_finite() || !vector.get_z().is_finite() {
202 return Err(non_finite.to_string());
203 }
204 let magnitude: f64 = vector
205 .get_x()
206 .abs()
207 .max(vector.get_y().abs())
208 .max(vector.get_z().abs());
209 if magnitude > RAYTRACE_MAX_F32_MAGNITUDE {
210 return Err(out_of_range.to_string());
211 }
212 Ok([
213 pack_upper_bound(vector.get_x())?,
214 pack_upper_bound(vector.get_y())?,
215 pack_upper_bound(vector.get_z())?,
216 ])
217}
218
219pub fn pack_index(index: usize) -> Result<f32, String> {
235 if index > RAYTRACE_MAX_EXACT_F32_INDEX {
236 return Err(RAYTRACE_ERROR_INDEX_RANGE.to_string());
237 }
238 Ok(index as f32)
239}
240
241pub fn packed_shape_code(kind: OccluderKind) -> f32 {
253 match kind {
254 OccluderKind::Sphere => 0.0,
255 OccluderKind::Aabb => 1.0,
256 OccluderKind::Triangle => 2.0,
257 }
258}
259
260pub fn packed_material_code(kind: MaterialKind) -> f32 {
272 match kind {
273 MaterialKind::Lambert => 0.0,
274 MaterialKind::Phong => 1.0,
275 MaterialKind::Pbr => 2.0,
276 }
277}
278
279pub fn packed_light_code(kind: LightType) -> f32 {
291 match kind {
292 LightType::Directional => 0.0,
293 LightType::Point => 1.0,
294 LightType::Spot => 2.0,
295 }
296}
297
298pub(crate) fn half_ulp(probe: f32) -> f64 {
312 let up: f64 = next_up(probe) as f64;
313 let down: f64 = next_down(probe) as f64;
314 let span: f64 = (up - down).abs() * 0.5;
315 if span.is_finite() && span > 0.0 {
316 return span;
317 }
318 f32::from_bits(1) as f64
319}
320
321pub fn pack_sphere_radius(center: Vector3D, radius: f64) -> Result<f32, String> {
338 let probe: f32 = pack_finite(
339 center.get_x(),
340 RAYTRACE_ERROR_NON_FINITE,
341 RAYTRACE_ERROR_F32_RANGE,
342 )?;
343 let widened: f64 = radius + half_ulp(probe) * 3.0_f64.sqrt();
344 pack_upper_bound(widened)
345}
346
347pub(crate) fn pack_primitive(
380 out: &mut Vec<f32>,
381 occluder: &Occluder,
382 original_index: usize,
383) -> Result<(), String> {
384 let bound_index: f32 = pack_index(original_index)?;
385 let kind: OccluderKind = occluder.get_kind();
386 let (center, extent): (Vector3D, Vector3D) = match kind {
387 OccluderKind::Aabb => {
388 let lo: Vector3D = occluder.get_center();
389 let hi: Vector3D = occluder.get_extent();
390 (
391 Vector3D::new(
392 lo.get_x().min(hi.get_x()),
393 lo.get_y().min(hi.get_y()),
394 lo.get_z().min(hi.get_z()),
395 ),
396 Vector3D::new(
397 lo.get_x().max(hi.get_x()),
398 lo.get_y().max(hi.get_y()),
399 lo.get_z().max(hi.get_z()),
400 ),
401 )
402 }
403 _ => (occluder.get_center(), occluder.get_extent()),
404 };
405 let material: &Material = occluder.get_material();
406 let center_packed: [f32; 3] = match kind {
407 OccluderKind::Sphere => {
408 pack_vector_lower(center, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?
409 }
410 _ => pack_vector(center, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?,
411 };
412 let extent_packed: [f32; 3] = match kind {
413 OccluderKind::Sphere => {
414 let radius: f32 = pack_sphere_radius(center, extent.get_x())?;
415 [radius, 0.0, 0.0]
416 }
417 OccluderKind::Aabb => {
418 pack_vector_upper(extent, RAYTRACE_ERROR_NON_FINITE, RAYTRACE_ERROR_F32_RANGE)?
419 }
420 OccluderKind::Triangle => [0.0, 0.0, 0.0],
421 };
422 let albedo: [f32; 3] = pack_vector(
423 material.get_albedo(),
424 RAYTRACE_ERROR_NON_FINITE,
425 RAYTRACE_ERROR_F32_RANGE,
426 )?;
427 let emissive: [f32; 3] = pack_vector(
428 material.get_emissive(),
429 RAYTRACE_ERROR_NON_FINITE,
430 RAYTRACE_ERROR_F32_RANGE,
431 )?;
432 let specular: f32 = pack_finite(
433 material.get_specular(),
434 RAYTRACE_ERROR_NON_FINITE,
435 RAYTRACE_ERROR_F32_RANGE,
436 )?;
437 let shininess: f32 = pack_finite(
438 material.get_shininess(),
439 RAYTRACE_ERROR_NON_FINITE,
440 RAYTRACE_ERROR_F32_RANGE,
441 )?;
442 let vertices: [Vector3D; 3] = occluder.get_vertices();
443 out.extend_from_slice(&[
444 center_packed[0],
445 center_packed[1],
446 center_packed[2],
447 packed_shape_code(kind),
448 ]);
449 out.extend_from_slice(&[
450 extent_packed[0],
451 extent_packed[1],
452 extent_packed[2],
453 packed_material_code(material.get_kind()),
454 ]);
455 let vertex0: [f32; 3] = pack_vector(
456 vertices[0],
457 RAYTRACE_ERROR_NON_FINITE,
458 RAYTRACE_ERROR_F32_RANGE,
459 )?;
460 out.extend_from_slice(&[vertex0[0], vertex0[1], vertex0[2], bound_index]);
461 let vertex1: [f32; 3] = pack_vector(
462 vertices[1],
463 RAYTRACE_ERROR_NON_FINITE,
464 RAYTRACE_ERROR_F32_RANGE,
465 )?;
466 out.extend_from_slice(&[vertex1[0], vertex1[1], vertex1[2], 0.0]);
467 let vertex2: [f32; 3] = pack_vector(
468 vertices[2],
469 RAYTRACE_ERROR_NON_FINITE,
470 RAYTRACE_ERROR_F32_RANGE,
471 )?;
472 out.extend_from_slice(&[vertex2[0], vertex2[1], vertex2[2], 0.0]);
473 out.extend_from_slice(&[albedo[0], albedo[1], albedo[2], specular]);
474 out.extend_from_slice(&[emissive[0], emissive[1], emissive[2], shininess]);
475 out.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
476 Ok(())
477}
478
479pub(crate) fn pack_node(out: &mut Vec<f32>, node: &RayTraceNode) -> Result<(), String> {
506 let lower: [f32; 3] = pack_vector_lower(
507 node.get_lower(),
508 RAYTRACE_ERROR_NON_FINITE,
509 RAYTRACE_ERROR_F32_RANGE,
510 )?;
511 let upper: [f32; 3] = pack_vector_upper(
512 node.get_upper(),
513 RAYTRACE_ERROR_NON_FINITE,
514 RAYTRACE_ERROR_F32_RANGE,
515 )?;
516 let start: f32 = pack_index(node.get_start())?;
517 let count: f32 = pack_index(node.get_count())?;
518 let escape: f32 = pack_index(node.get_escape())?;
519 out.extend_from_slice(&[lower[0], lower[1], lower[2], start]);
520 out.extend_from_slice(&[upper[0], upper[1], upper[2], count]);
521 out.extend_from_slice(&[escape, 0.0, 0.0, 0.0]);
522 Ok(())
523}
524
525pub(crate) fn frame_basis(
549 camera: &Camera3D,
550 width: u32,
551 height: u32,
552) -> Result<FrameBasis, String> {
553 if width == 0 || height == 0 {
554 return Err(RAYTRACE_ERROR_ZERO_DIMENSION.to_string());
555 }
556 let pixel_count: usize = match (width as usize).checked_mul(height as usize) {
557 Some(product) => product,
558 None => return Err(RAYTRACE_ERROR_DIMENSION_OVERFLOW.to_string()),
559 };
560 if pixel_count > RAYTRACE_MAX_PACKED_FLOATS {
561 return Err(RAYTRACE_ERROR_DIMENSION_OVERFLOW.to_string());
562 }
563 let eye: Vector3D = camera.get_position();
564 if !eye.get_x().is_finite() || !eye.get_y().is_finite() || !eye.get_z().is_finite() {
565 return Err(RAYTRACE_ERROR_CAMERA_NON_FINITE.to_string());
566 }
567 let fov: f64 = camera.get_fov();
568 let fov_usable: bool = fov.is_finite() && fov > 0.0 && fov < PI;
569 if !fov_usable {
570 return Err(RAYTRACE_ERROR_CAMERA_FOV.to_string());
571 }
572 let tan_half_fov: f64 = (fov * 0.5).tan();
573 let tan_usable: bool = tan_half_fov.is_finite() && tan_half_fov > 0.0;
574 if !tan_usable {
575 return Err(RAYTRACE_ERROR_CAMERA_FOV.to_string());
576 }
577 let forward: Vector3D = camera.forward();
578 if forward == Vector3D::zero() {
579 return Err(RAYTRACE_ERROR_CAMERA_BASIS.to_string());
580 }
581 let right: Vector3D = forward.cross(camera.get_up()).normalized();
582 let up: Vector3D = right.cross(forward);
583 if right == Vector3D::zero() || up == Vector3D::zero() {
584 return Err(RAYTRACE_ERROR_CAMERA_BASIS.to_string());
585 }
586 let near_t: f64 = camera.get_near();
587 let far_t: f64 = camera.get_far();
588 let range_ordered: bool =
589 near_t.is_finite() && far_t.is_finite() && near_t >= 0.0 && far_t > near_t;
590 if !range_ordered {
591 return Err(RAYTRACE_ERROR_CAMERA_RANGE.to_string());
592 }
593 let near_packed: f32 = pack_finite(
594 near_t,
595 RAYTRACE_ERROR_CAMERA_NON_FINITE,
596 RAYTRACE_ERROR_CAMERA_F32_RANGE,
597 )?;
598 let far_packed: f32 = pack_finite(
599 far_t,
600 RAYTRACE_ERROR_CAMERA_NON_FINITE,
601 RAYTRACE_ERROR_CAMERA_F32_RANGE,
602 )?;
603 let packed_range_ordered: bool = far_packed > near_packed;
604 if !packed_range_ordered {
605 return Err(RAYTRACE_ERROR_CAMERA_RANGE.to_string());
606 }
607 Ok(FrameBasis {
608 eye,
609 forward,
610 right,
611 up,
612 tan_half_fov,
613 aspect: width as f64 / height as f64,
614 width,
615 height,
616 near_t,
617 far_t,
618 })
619}
620
621pub fn pack_raytrace_frame(out: &mut Vec<f32>, frame: &RayTraceFrame<'_>) -> Result<(), String> {
684 let camera: &Camera3D = frame.get_camera();
685 let lights: &LightingUniforms = frame.get_lights();
686 let settings: &RayTraceSettings = frame.get_settings();
687 settings.validate()?;
688 let basis: FrameBasis = frame_basis(camera, frame.get_width(), frame.get_height())?;
689 let light_count: usize = lights.get_lights().len();
690 let light_slots: usize = light_count
691 .checked_mul(RAYTRACE_PACKED_LIGHT_VEC4S)
692 .ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
693 let total_slots: usize = RAYTRACE_PACKED_FRAME_HEADER_VEC4S
694 .checked_add(light_slots)
695 .ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
696 let total_floats: usize = total_slots
697 .checked_mul(4)
698 .ok_or_else(|| RAYTRACE_ERROR_FRAME_OVERFLOW.to_string())?;
699 if total_floats > RAYTRACE_MAX_PACKED_FLOATS {
700 return Err(RAYTRACE_ERROR_FRAME_OVERFLOW.to_string());
701 }
702 let eye: [f32; 3] = pack_vector(
703 camera.get_position(),
704 RAYTRACE_ERROR_CAMERA_NON_FINITE,
705 RAYTRACE_ERROR_CAMERA_NON_FINITE,
706 )?;
707 let forward_packed: [f32; 3] = pack_vector(
708 basis.get_forward(),
709 RAYTRACE_ERROR_CAMERA_NON_FINITE,
710 RAYTRACE_ERROR_CAMERA_NON_FINITE,
711 )?;
712 let right_packed: [f32; 3] = pack_vector(
713 basis.get_right(),
714 RAYTRACE_ERROR_CAMERA_NON_FINITE,
715 RAYTRACE_ERROR_CAMERA_NON_FINITE,
716 )?;
717 let up_packed: [f32; 3] = pack_vector(
718 basis.get_up(),
719 RAYTRACE_ERROR_CAMERA_NON_FINITE,
720 RAYTRACE_ERROR_CAMERA_NON_FINITE,
721 )?;
722 let tan_half_fov: f32 = pack_finite(
723 basis.get_tan_half_fov(),
724 RAYTRACE_ERROR_CAMERA_NON_FINITE,
725 RAYTRACE_ERROR_CAMERA_NON_FINITE,
726 )?;
727 let ambient: [f32; 3] = pack_vector(
728 lights.get_ambient(),
729 RAYTRACE_ERROR_LIGHT_NON_FINITE,
730 RAYTRACE_ERROR_LIGHT_F32_RANGE,
731 )?;
732 let lighting_eye: [f32; 3] = pack_vector(
733 lights.get_eye(),
734 RAYTRACE_ERROR_LIGHT_NON_FINITE,
735 RAYTRACE_ERROR_LIGHT_F32_RANGE,
736 )?;
737 out.clear();
738 out.extend_from_slice(&[eye[0], eye[1], eye[2], tan_half_fov]);
739 out.extend_from_slice(&[
740 forward_packed[0],
741 forward_packed[1],
742 forward_packed[2],
743 basis.get_aspect() as f32,
744 ]);
745 out.extend_from_slice(&[
746 right_packed[0],
747 right_packed[1],
748 right_packed[2],
749 basis.get_width() as f32,
750 ]);
751 out.extend_from_slice(&[
752 up_packed[0],
753 up_packed[1],
754 up_packed[2],
755 basis.get_height() as f32,
756 ]);
757 out.extend_from_slice(&[ambient[0], ambient[1], ambient[2], light_count as f32]);
758 out.extend_from_slice(&[
759 lighting_eye[0],
760 lighting_eye[1],
761 lighting_eye[2],
762 settings.get_max_bounces() as f32,
763 ]);
764 out.extend_from_slice(&[
765 basis.get_near_t() as f32,
766 basis.get_far_t() as f32,
767 settings.get_samples_per_axis() as f32,
768 0.0,
769 ]);
770 out.extend_from_slice(&[0.0, 0.0, 0.0, 0.0]);
771 for light in lights.get_lights().iter() {
772 let position: [f32; 3] = pack_vector(
773 light.get_position(),
774 RAYTRACE_ERROR_LIGHT_NON_FINITE,
775 RAYTRACE_ERROR_LIGHT_F32_RANGE,
776 )?;
777 let direction: [f32; 3] = pack_vector(
778 light.get_direction(),
779 RAYTRACE_ERROR_LIGHT_NON_FINITE,
780 RAYTRACE_ERROR_LIGHT_F32_RANGE,
781 )?;
782 let color: [f32; 3] = pack_vector(
783 light.get_color(),
784 RAYTRACE_ERROR_LIGHT_NON_FINITE,
785 RAYTRACE_ERROR_LIGHT_F32_RANGE,
786 )?;
787 let intensity: f32 = pack_finite(
788 light.get_intensity(),
789 RAYTRACE_ERROR_LIGHT_NON_FINITE,
790 RAYTRACE_ERROR_LIGHT_F32_RANGE,
791 )?;
792 let falloff: f32 = pack_finite(
793 light.get_falloff(),
794 RAYTRACE_ERROR_LIGHT_NON_FINITE,
795 RAYTRACE_ERROR_LIGHT_F32_RANGE,
796 )?;
797 let spot_cos: f32 = pack_finite(
798 light.get_spot_cos(),
799 RAYTRACE_ERROR_LIGHT_NON_FINITE,
800 RAYTRACE_ERROR_LIGHT_F32_RANGE,
801 )?;
802 out.extend_from_slice(&[
803 position[0],
804 position[1],
805 position[2],
806 packed_light_code(light.get_kind()),
807 ]);
808 out.extend_from_slice(&[direction[0], direction[1], direction[2], intensity]);
809 out.extend_from_slice(&[color[0], color[1], color[2], falloff]);
810 out.extend_from_slice(&[spot_cos, 0.0, 0.0, 0.0]);
811 }
812 Ok(())
813}