code wiki / _hdl_build / nx_meshview_page.nx
nx_meshview_page.nx source
↩ module page · 103 lines · 8751 B
1// nx_meshview_page.nx -- R3b: emit a self-contained, openable web page that renders our mesh IN THE BROWSER via
2// OUR sovereign WASM software rasterizer -- NO WebGL, NO NVIDIA, NO Emscripten, NO 3rd-party JS lib. Fork-compiles
3// nx_meshview_wasm.nx -> wasm (sovereign nx_compile_wat -> nx_wat_compiler), base64-embeds it (sovereign nx_base64),
4// and authors web_assets/meshview.html: a <canvas> that instantiates the wasm, calls mv_render(angle), reads the
5// i64 framebuffer straight from linear memory, and blits it (the browser's ONLY job is putImageData + the call;
6// ALL transform/projection/z-buffer rasterization is sovereign Nishi). Orbits on drag + auto-rotates. The scene
7// is the z-test pair (RED nearer stays in front of BLUE as you orbit = the z-buffer, live). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_g_puts_lib.nx"
10import "nx_base64.nx"
11
12const CWAT_ELF: *u8 = "/tmp/nx_compile_wat.sov.elf"
13const WATC_ELF: *u8 = "/tmp/nx_wat_compiler.sov.elf"
14const SRC_NX: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/runtime/nx_meshview_wasm.nx"
15const WAT_TMP: *u8 = "/tmp/mv.wat"
16const WASM_OUT: *u8 = "web_assets/mv.wasm"
17const HTML_OUT: *u8 = "web_assets/meshview.html"
18const CAP: i64 = 262144
19
20func run_elf(elf: *u8, a1: *u8, a2: *u8) -> i64 {
21 let pid: i64 = sys_fork()
22 if pid == 0 {
23 let argv: *i64 = sys_mmap(64) as *i64
24 argv[0] = elf as i64
25 var ai: i64 = 1
26 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 }
27 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 }
28 argv[ai] = 0
29 let envp: *i64 = sys_mmap(16) as *i64
30 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
31 envp[1] = 0
32 sys_execve(elf, argv, envp)
33 sys_exit(127)
34 }
35 let st: *i64 = sys_mmap(16) as *i64
36 sys_wait4(pid, st, 0)
37 return (st[0] >> 8) & 0xff
38}
39func cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=0 as u8 { dst[o]=s[i]; o=o+1; i=i+1 } return o }
40func main() -> i64 {
41 let rc1: i64 = run_elf(CWAT_ELF, SRC_NX, WAT_TMP)
42 let rc2: i64 = run_elf(WATC_ELF, WAT_TMP, WASM_OUT)
43 if rc1 != 0 { g_puts("FATAL: compile_wat failed\n" as *u8); sys_exit(2); return 2 }
44 if rc2 != 0 { g_puts("FATAL: wat_compiler failed\n" as *u8); sys_exit(2); return 2 }
45 let box: *i64 = sys_mmap(16) as *i64
46 let wbytes: *u8 = sys_read_file(WASM_OUT, box)
47 if (wbytes as i64) == 0 { g_puts("FATAL: no mv.wasm\n" as *u8); sys_exit(2); return 2 }
48 let wlen: i64 = box[0]
49 let b64: *u8 = sys_mmap(wlen * 2 + 64)
50 let b64len: i64 = b64_encode(wbytes, wlen, b64)
51
52 let h: *u8 = sys_mmap(CAP)
53 var w: i64 = 0
54 w = cat(h, w, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'>\n" as *u8)
55 w = cat(h, w, "<title>Nishi mesh viewer — sovereign WASM renderer</title>\n" as *u8)
56 w = cat(h, w, "<style>:root{--bg:#0b0f14;--ink:#e6edf3;--mut:#8aa0b4;--acc:#4cc2ff;--line:#243140}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,Segoe UI,Roboto,sans-serif}\n" as *u8)
57 w = cat(h, w, "header{max-width:760px;margin:0 auto;padding:28px 20px 6px}h1{margin:0;font-size:26px}.sub{color:var(--mut);margin:.5em 0 0;max-width:66ch}main{max-width:760px;margin:0 auto;padding:0 20px 50px}\n" as *u8)
58 w = cat(h, w, "canvas{image-rendering:pixelated;width:512px;max-width:100%;background:#0b0f14;border:1px solid var(--line);border-radius:8px;margin:14px 0;display:block;cursor:grab}\n" as *u8)
59 w = cat(h, w, "code{color:var(--acc)}footer{max-width:760px;margin:0 auto;padding:8px 20px 40px;color:var(--mut);font-size:12.5px}</style></head><body>\n" as *u8)
60 w = cat(h, w, "<header><h1>Nishi mesh viewer</h1><p class='sub'>A 3D mesh LOADED into the browser — its geometry (vertices, normals, faces) is baked into a plain array and written into the WASM's linear memory by a few lines of JS, then drawn by our generic mesh renderer; this is the EXACT path a real CAD STL takes (parse → buffer → render). <b>DRAG to orbit it, SCROLL to zoom</b> — an interactive 3D viewport, Gouraud-lit with a glossy highlight, rendered entirely by the Nishi software rasterizer (perspective + z-buffer + per-vertex shading), compiled <code>.nx→wasm</code> by the sovereign toolchain — <b>no WebGL, no GPU driver, no Emscripten, no 3rd-party library</b>. The browser's entire job: instantiate the wasm, <code>putImageData</code>, <code>requestAnimationFrame</code> — nothing else. The light orbits, so the glossy glint sweeps across the ball — per-vertex shading + z-buffer, live.</p></header>\n" as *u8)
61 w = cat(h, w, "<main><canvas id='c'></canvas></main>\n" as *u8)
62 w = cat(h, w, "<footer>Sovereign Nishi stack · renderer <code>nx_render_core</code>+<code>nx_meshview_wasm</code> · <code>.nx→wasm</code> via <code>nx_compile_wat</code>+<code>nx_wat_compiler</code> · verified in <code>nx_wasm_vm</code></footer>\n" as *u8)
63 w = cat(h, w, "<script>\n" as *u8)
64 // EXTERNAL geometry (an octahedron) baked here as plain arrays -- the page writes it into the WASM's linear
65 // memory below (the exact path a parsed STL takes). verts (Q14), per-vertex unit normals, face indices.
66 w = cat(h, w, "var GV=[8192,0,0,-8192,0,0,0,8192,0,0,-8192,0,0,0,8192,0,0,-8192];\n" as *u8)
67 w = cat(h, w, "var GN=[16384,0,0,-16384,0,0,0,16384,0,0,-16384,0,0,0,16384,0,0,-16384];\n" as *u8)
68 w = cat(h, w, "var GI=[0,2,4,0,4,3,0,3,5,0,5,2,1,4,2,1,3,4,1,5,3,1,2,5];\n" as *u8)
69 w = cat(h, w, "var NV=GV.length/3,NT=GI.length/3;\n" as *u8)
70 w = cat(h, w, "var B='" as *u8)
71 var k: i64 = 0
72 while k < b64len { h[w] = b64[k]; w = w + 1; k = k + 1 }
73 w = cat(h, w, "';\n" as *u8)
74 w = cat(h, w, "var bytes=Uint8Array.from(atob(B),function(c){return c.charCodeAt(0);});\n" as *u8)
75 w = cat(h, w, "WebAssembly.instantiate(bytes).then(function(r){var ex=r.instance.exports;\n" as *u8)
76 // LOAD the external geometry into the WASM's linear memory at the offsets the module exports, then render it.
77 w = cat(h, w, "var g=new BigInt64Array(ex.memory.buffer);var ov=Number(ex.mv_meshv_off())>>3,on=Number(ex.mv_meshn_off())>>3,oi=Number(ex.mv_meshi_off())>>3;var q;\n" as *u8)
78 w = cat(h, w, "for(q=0;q<GV.length;q++)g[ov+q]=BigInt(GV[q]);for(q=0;q<GN.length;q++)g[on+q]=BigInt(GN[q]);for(q=0;q<GI.length;q++)g[oi+q]=BigInt(GI[q]);\n" as *u8)
79 // MINIMAL browser surface: a W*H canvas (CSS scales it 'pixelated'), one 2D context, putImageData, RAF. No
80 // offscreen canvas, no drawImage, no event listeners -- the browser does the least possible; all render is ours.
81 w = cat(h, w, "var W=Number(ex.mv_w()),H=Number(ex.mv_h()),OFF=Number(ex.mv_fb_off());\n" as *u8)
82 w = cat(h, w, "var cv=document.getElementById('c');cv.width=W;cv.height=H;var ctx=cv.getContext('2d');var id=ctx.createImageData(W,H);\n" as *u8)
83 // INTERACTIVE ORBIT VIEWPORT: drag = orbit (yaw), scroll = zoom (dist), idle = slow auto-spin. The only added
84 // browser surface is the input events -- still no WebGL/drawImage; all transform + raster stays in our wasm.
85 w = cat(h, w, "var yaw=0,dist=65536,drag=0,lx=0,spin=1;\n" as *u8)
86 w = cat(h, w, "cv.addEventListener('mousedown',function(e){drag=1;lx=e.clientX;spin=0;});\n" as *u8)
87 w = cat(h, w, "window.addEventListener('mouseup',function(){drag=0;});\n" as *u8)
88 w = cat(h, w, "cv.addEventListener('mousemove',function(e){if(drag){yaw+=(e.clientX-lx)*1.5;lx=e.clientX;}});\n" as *u8)
89 w = cat(h, w, "cv.addEventListener('wheel',function(e){dist+=e.deltaY*120;if(dist<24000)dist=24000;if(dist>240000)dist=240000;e.preventDefault();});\n" as *u8)
90 w = cat(h, w, "function frame(){if(spin&&!drag)yaw+=0.6;ex.mv_render_loaded_cam(BigInt(Math.round(((yaw%360)+360)%360)),BigInt(Math.round(dist)),BigInt(NV),BigInt(NT));var mem=new Uint8Array(ex.memory.buffer);var i,p,n=W*H;\n" as *u8)
91 w = cat(h, w, "for(i=0;i<n;i++){p=OFF+i*8;id.data[i*4]=mem[p];id.data[i*4+1]=mem[p+1];id.data[i*4+2]=mem[p+2];id.data[i*4+3]=255;}\n" as *u8)
92 w = cat(h, w, "ctx.putImageData(id,0,0);requestAnimationFrame(frame);}\n" as *u8)
93 w = cat(h, w, "frame();}).catch(function(e){document.body.innerHTML+='<pre style=color:#f99>'+e+'</pre>';});\n" as *u8)
94 w = cat(h, w, "</script></body></html>\n" as *u8)
95
96 let fd: i64 = sys_openat_wr(HTML_OUT, 0x1a4)
97 if fd < 0 { g_puts("FATAL: cannot open meshview.html\n" as *u8); sys_exit(3); return 3 }
98 sys_write(fd, h, w)
99 sys_close(fd)
100 g_puts("[nx_meshview_page] wrote web_assets/meshview.html + web_assets/mv.wasm (sovereign, no 3rd-party)\n" as *u8)
101 sys_exit(0)
102 return 0
103}