nx_bvh_test.nx source
↩ module page · 82 lines · 2887 B
1// nx_bvh_test.nx -- exercise BVH build on tetrahedron mesh in both
2// single-leaf and forced-multi-node configurations.
3//
4// Closed-form invariants:
5// (a) Default-threshold build on tetrahedron (4 tris, threshold=4)
6// yields exactly 1 node (a single leaf with count=4).
7// (b) Root AABB matches mesh bbox: (0,0,0) to (EDGE,EDGE,EDGE).
8// (c) Sum of leaf counts == n_tris.
9// (d) leaf_threshold=2 forces the tetrahedron to split, producing
10// >= 3 nodes (1 internal + 2 leaves minimum) and a tree where
11// sum of leaf counts is still 4.
12// (e) Internal node's right child index == left + 1 (depth-first
13// sibling-adjacency invariant).
14// (f) Empty mesh degenerate: n_tris=0 returns BVH with n_tris=0.
15//
16// expect_exit: 0
17// license_tier: ORIGINAL
18
19import "nx_syscalls.nx"
20import "nx_mesh.nx"
21import "nx_mesh_print_check.nx"
22import "nx_bvh.nx"
23
24const EDGE_Q14: i64 = 16384
25
26func main() -> i64 {
27 let m: *NxMesh = nx_mesh_make_tetrahedron(EDGE_Q14, 0)
28 if (m as i64) == 0 { return 5 }
29
30 // --- (a) Default-threshold build ---
31 let b: *NxBvh = nx_bvh_build(m)
32 if (b as i64) == 0 { return 10 }
33 if b.n_tris != 4 { return 11 }
34 if b.n_nodes != 1 { return 12 }
35
36 let root: *NxBvhNode = nx_bvh_node_at(b, 0)
37 if root.count != 4 { return 13 }
38
39 // --- (b) Root AABB matches mesh bbox ---
40 if root.mnx != 0 { return 20 }
41 if root.mny != 0 { return 21 }
42 if root.mnz != 0 { return 22 }
43 if root.mxx != EDGE_Q14 { return 23 }
44 if root.mxy != EDGE_Q14 { return 24 }
45 if root.mxz != EDGE_Q14 { return 25 }
46
47 // --- (c) Sum of leaf counts ---
48 if nx_bvh_total_leaf_count(b) != 4 { return 30 }
49 if nx_bvh_leaf_node_count(b) != 1 { return 31 }
50
51 // --- (d) Forced multi-leaf with threshold=2 ---
52 let b2: *NxBvh = nx_bvh_build_with_threshold(m, 2)
53 if (b2 as i64) == 0 { return 40 }
54 if b2.n_nodes < 3 { return 41 }
55 if nx_bvh_total_leaf_count(b2) != 4 { return 42 }
56 let root2: *NxBvhNode = nx_bvh_node_at(b2, 0)
57 if root2.count != 0 { return 43 } // root is internal
58
59 // --- (e) Sibling-adjacency invariant ---
60 // Walk every internal node and verify right child = left + 1.
61 var ni: i64 = 0
62 while ni < b2.n_nodes {
63 let n: *NxBvhNode = nx_bvh_node_at(b2, ni)
64 if n.count == 0 {
65 // Internal node. Its left = n.left_or_first; right
66 // should be left + 1 (always valid in our build).
67 let left: i64 = n.left_or_first
68 if left + 1 >= b2.n_nodes { return 50 + ni }
69 }
70 ni = ni + 1
71 }
72
73 // --- (f) Empty mesh ---
74 let m_empty: *NxMesh = nx_mesh_alloc(1, 1, 0)
75 m_empty.n_tris = 0
76 let b_empty: *NxBvh = nx_bvh_build(m_empty)
77 if (b_empty as i64) == 0 { return 60 }
78 if b_empty.n_tris != 0 { return 61 }
79 if b_empty.n_nodes != 0 { return 62 }
80
81 return 0
82}