code wiki / (root) / nx_obj_import_gate.nx

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}