nx_obj_import_gate.nx source
↩ module page · 80 lines · 4448 B
1// nx_obj_import_gate.nx -- liar-killed GATE for the OBJ reader.
2//
3// The load-bearing teeth are the REAL-WORLD forms, not a round-trip of our own exporter. A parser
4// that only reads nx_obj_export's output would pass a round-trip test and still fail on every mesh
5// anyone actually hands us, because external OBJ carries floats, negative indices and n-gons.
6// Tooth total is DERIVED; G_MIN_TEETH is a floor so a deleted tooth trips RED.
7// usage: nx_obj_import_gate exit 0 on all-pass.
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_obj_import.nx"
10
11const G_MIN_TEETH: i64 = 14
12
13func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14func g_putn(v: i64) -> i64 {
15 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
16 var m: i64 = v
17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let d: *u8 = sys_mmap(24); var k: i64 = 0
19 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 let o: *u8 = sys_mmap(24); var w: i64 = 0
21 while w < k { o[w] = d[k-1-w]; w = w + 1 }
22 sys_write(1, o, k)
23 sys_munmap(d, 24); sys_munmap(o, 24)
24 return 0
25}
26func g_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
27func g_eq(name: *u8, got: i64, want: i64, passp: *i64) -> i64 {
28 passp[1] = passp[1] + 1
29 g_puts("T " as *u8); g_puts(name); g_puts(" got=" as *u8); g_putn(got)
30 if got == want { g_puts(" PASS\n" as *u8); passp[0] = passp[0] + 1 } else { g_puts(" want=" as *u8); g_putn(want); g_puts(" FAIL\n" as *u8) }
31 return 0
32}
33func g_write(path: *u8, s: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, 420); if fd < 0 { return 0 - 1 } sys_write(fd, s, g_slen(s)); sys_close(fd); return 0 }
34
35func main() -> i64 {
36 let pass: *i64 = sys_mmap(16) as *i64
37 pass[0] = 0
38 pass[1] = 0
39 let vb: *i64 = sys_mmap(4096 * 3 * 8) as *i64
40 let fb: *i64 = sys_mmap(4096 * 3 * 8) as *i64
41 let ct: *i64 = sys_mmap(16) as *i64
42
43 // FIXTURE A: floats (incl. negative + zero), a QUAD, and a NEGATIVE-index triangle.
44 g_write("_scratch/oi_a.obj" as *u8, "# fixture\nv 1.5 -2.25 0.0\nv 2.0 0.0 1.0\nv 0.0 1.0 2.0\nv -1.0 -1.0 -1.0\nvn 0.0 1.0 0.0\nf 1 2 3 4\nf -4 -3 -2\n" as *u8)
45 var rc: i64 = oi_read("_scratch/oi_a.obj" as *u8, vb, fb, 4096, 4096, ct)
46 g_eq("reads-fixture-rc0" as *u8, rc, 0, pass)
47 g_eq("vertex-count-4" as *u8, ct[0], 4, pass)
48 // quad -> 2 tris, plus 1 explicit tri = 3
49 g_eq("ngon-fan-triangulated-to-3-tris" as *u8, ct[1], 3, pass)
50 // 1.5 in fx1024
51 g_eq("float-positive-1.5-is-1536" as *u8, vb[0], 1536, pass)
52 g_eq("float-negative-2.25-is--2304" as *u8, vb[1], 0 - 2304, pass)
53 g_eq("float-zero-is-0" as *u8, vb[2], 0, pass)
54 // quad 1 2 3 4 fans to (0,1,2) and (0,2,3) -- zero-based
55 g_eq("quad-tri0-is-0-1-2" as *u8, fb[0] * 100 + fb[1] * 10 + fb[2], 12, pass)
56 g_eq("quad-tri1-is-0-2-3" as *u8, fb[3] * 100 + fb[4] * 10 + fb[5], 23, pass)
57 // NEGATIVE indices are relative to the current vertex count: -4 -3 -2 with nv=4 -> 0 1 2
58 g_eq("negative-indices-resolve-0-1-2" as *u8, fb[6] * 100 + fb[7] * 10 + fb[8], 12, pass)
59
60 // FIXTURE B: exponent notation and the a/b/c face form, which scanners and Blender both emit.
61 g_write("_scratch/oi_b.obj" as *u8, "v 1.0e1 2.0 3.0\nv 0.0 0.0 0.0\nv 1.0 1.0 1.0\nf 1/1/1 2/2/2 3/3/3\n" as *u8)
62 rc = oi_read("_scratch/oi_b.obj" as *u8, vb, fb, 4096, 4096, ct)
63 g_eq("exponent-form-rc0" as *u8, rc, 0, pass)
64 g_eq("exponent-1.0e1-is-10240" as *u8, vb[0], 10240, pass)
65 g_eq("slash-face-form-parses-tri" as *u8, ct[1], 1, pass)
66
67 // REFUSALS -- a malformed or absent mesh must be refused, never silently half-loaded.
68 rc = oi_read("_scratch/oi_absent.obj" as *u8, vb, fb, 4096, 4096, ct)
69 g_eq("refuses-missing-file" as *u8, rc, 0 - 1, pass)
70 // a face referencing a vertex that does not exist is a corrupt mesh, not a warning
71 g_write("_scratch/oi_c.obj" as *u8, "v 0.0 0.0 0.0\nf 1 2 3\n" as *u8)
72 rc = oi_read("_scratch/oi_c.obj" as *u8, vb, fb, 4096, 4096, ct)
73 g_eq("refuses-out-of-range-index" as *u8, rc, 0 - 5, pass)
74
75 g_puts("OBJ-IMPORT-GATE pass=" as *u8); g_putn(pass[0]); g_puts("/" as *u8); g_putn(pass[1]); g_puts(" verdict=" as *u8)
76 if pass[1] < G_MIN_TEETH { g_puts("RED reason=teeth_deleted\n" as *u8); return 1 }
77 if pass[0] == pass[1] { g_puts("GREEN\n" as *u8); return 0 }
78 g_puts("RED\n" as *u8)
79 return 1
80}