135 lines
4.8 KiB
Rust
135 lines
4.8 KiB
Rust
//! Kernel-exactness tests against the validated CPU oracle fixtures
|
|
//! (local/faithc_ref/fixtures/*.bin, generated by make_fixtures.py from the
|
|
//! polyfill that itself passed fp64 reference validation).
|
|
//!
|
|
//! These pin the two known risk areas: SAT epsilon discipline (1e-6 actually
|
|
//! used; 1e-4 narrowing) and the clip/centroid/area conventions — all
|
|
//! BIT-EXACT, including the 9-gon clips and degenerate/contact cases.
|
|
|
|
mod common;
|
|
|
|
use common::{faithc_ref_dir, FcDump};
|
|
use makepad_remesh::sat::{clip_tri_box, tri_box_sat};
|
|
use makepad_remesh::spatial::{
|
|
closest_tri, ray_tri_intersect, segment_nearest_hit, tri_barycentric, tri_closest_point,
|
|
BinGrid,
|
|
};
|
|
use makepad_remesh::math::*;
|
|
|
|
fn fixture(name: &str) -> Option<FcDump> {
|
|
let dir = faithc_ref_dir()?;
|
|
let path = dir.join("fixtures").join(name);
|
|
if !path.is_file() {
|
|
eprintln!("SKIP: fixture {name} not found");
|
|
return None;
|
|
}
|
|
Some(FcDump::load(&path).unwrap())
|
|
}
|
|
|
|
#[test]
|
|
fn sat_and_clip_bit_exact() {
|
|
let Some(fx) = fixture("satclip.bin") else { return };
|
|
let tris = fx.tris("tris");
|
|
let bmin = fx.v3s("bmin");
|
|
let bmax = fx.v3s("bmax");
|
|
let (_, sat6) = fx.i8("sat6");
|
|
let (_, sat4) = fx.i8("sat4");
|
|
let (_, clip_hit) = fx.i8("clip_hit");
|
|
let cents = fx.v3s("clip_centroid");
|
|
let (_, areas) = fx.f32("clip_area");
|
|
|
|
let n = tris.len();
|
|
assert!(n > 4000);
|
|
let mut hits6 = 0;
|
|
for i in 0..n {
|
|
let s6 = tri_box_sat(&tris[i], bmin[i], bmax[i], 1e-6);
|
|
let s4 = tri_box_sat(&tris[i], bmin[i], bmax[i], 1e-4);
|
|
assert_eq!(s6 as i8, sat6[i], "case {i}: SAT(1e-6) mismatch");
|
|
assert_eq!(s4 as i8, sat4[i], "case {i}: SAT(1e-4) mismatch");
|
|
// oracle clip ran on ALL pairs (not gated on SAT)
|
|
let c = clip_tri_box(&tris[i], bmin[i], bmax[i]);
|
|
assert_eq!(c.hit as i8, clip_hit[i], "case {i}: clip hit mismatch");
|
|
if c.hit {
|
|
hits6 += 1;
|
|
for d in 0..3 {
|
|
assert_eq!(
|
|
c.centroid[d].to_bits(),
|
|
cents[i][d].to_bits(),
|
|
"case {i}: centroid[{d}] {} vs {}",
|
|
c.centroid[d],
|
|
cents[i][d]
|
|
);
|
|
}
|
|
assert_eq!(
|
|
c.area.to_bits(),
|
|
areas[i].to_bits(),
|
|
"case {i}: area {} vs {}",
|
|
c.area,
|
|
areas[i]
|
|
);
|
|
}
|
|
}
|
|
assert!(hits6 > 1000, "only {hits6} clip hits exercised");
|
|
}
|
|
|
|
#[test]
|
|
fn segment_nearest_and_udf_bit_exact() {
|
|
let Some(fx) = fixture("rayudf.bin") else { return };
|
|
let scene = fx.tris("scene_tris");
|
|
let grid = BinGrid::build(&scene);
|
|
|
|
// ---- segments ----
|
|
let s0 = fx.v3s("seg_start");
|
|
let s1 = fx.v3s("seg_end");
|
|
let (_, seg_hit) = fx.i8("seg_hit");
|
|
let (_, seg_face) = fx.i64("seg_face");
|
|
let (_, seg_t) = fx.f32("seg_t");
|
|
let (_, max_t_arr) = fx.f32("seg_max_t");
|
|
|
|
// torch preamble replicated in f32
|
|
let lens: Vec<f32> = s0
|
|
.iter()
|
|
.zip(&s1)
|
|
.map(|(a, b)| norm3(sub3(*b, *a)))
|
|
.collect();
|
|
let max_t = lens.iter().fold(f32::NEG_INFINITY, |m, &l| m.max(l));
|
|
assert_eq!(max_t.to_bits(), max_t_arr[0].to_bits());
|
|
|
|
let mut hits = 0;
|
|
for i in 0..s0.len() {
|
|
let d = sub3(s1[i], s0[i]);
|
|
let inv = lens[i] + 1e-8;
|
|
let rd = [d[0] / inv, d[1] / inv, d[2] / inv];
|
|
let (t, face) = segment_nearest_hit(&grid, &scene, s0[i], s1[i], s0[i], rd, max_t);
|
|
let hit = face >= 0 && t <= lens[i];
|
|
assert_eq!(hit as i8, seg_hit[i], "seg {i}: hit mismatch");
|
|
assert_eq!(face, seg_face[i], "seg {i}: face mismatch");
|
|
assert_eq!(t.to_bits(), seg_t[i].to_bits(), "seg {i}: t {} vs {}", t, seg_t[i]);
|
|
if hit {
|
|
hits += 1;
|
|
}
|
|
}
|
|
assert!(hits > 1000, "only {hits} segment hits exercised");
|
|
|
|
// basic MT sanity: a miss returns 1e10
|
|
assert_eq!(ray_tri_intersect([5.0, 5.0, 5.0], [1.0, 0.0, 0.0], &scene[0]), 1e10);
|
|
|
|
// ---- UDF ----
|
|
let pts = fx.v3s("udf_points");
|
|
let (_, udf_face) = fx.i64("udf_face");
|
|
let (_, udf_dist) = fx.f32("udf_dist");
|
|
let closest = fx.v3s("udf_closest");
|
|
let uvw = fx.v3s("udf_uvw");
|
|
for i in 0..pts.len() {
|
|
let (dsq, tid) = closest_tri(&grid, &scene, pts[i]);
|
|
assert_eq!(tid, udf_face[i], "udf {i}: face mismatch");
|
|
let dist = dsq.sqrt();
|
|
assert_eq!(dist.to_bits(), udf_dist[i].to_bits(), "udf {i}: dist");
|
|
let cp = tri_closest_point(pts[i], &scene[tid as usize]);
|
|
let bc = tri_barycentric(cp, &scene[tid as usize]);
|
|
for d in 0..3 {
|
|
assert_eq!(cp[d].to_bits(), closest[i][d].to_bits(), "udf {i}: closest[{d}]");
|
|
assert_eq!(bc[d].to_bits(), uvw[i][d].to_bits(), "udf {i}: uvw[{d}]");
|
|
}
|
|
}
|
|
}
|