code wiki / _hdl_build / nx_gx4_walk_gate.nx

nx_gx4_walk_gate.nx source

↩ module page · 196 lines · 12322 B

1// nx_gx4_walk_gate.nx -- Gx-4 FIRST PLAYABLE: an INPUT-DRIVEN player walks through a 3D heightfield world, 2// rendered per-tick through the first-class <canvas> substrate (Gx-1c) in the LIVE Nishi-Browser. The loop is 3// the real game shape: input event -> player state (position + heading) -> build the player-relative world 4// mesh -> the REAL nx_swgpu pipeline (3 altitude-band albedo passes over ONE shared zbuf via the new 5// sg_render_pass) -> blit into the page's canvas box -> full browser repaint. FLOOR LAW: lit, color-banded 6// 3D terrain with sky + depth (no terminal rects). DETERMINISM: the same input script replayed gives 7// byte-identical frames (the sovereign exceed). Honest: keyboard events are SIMULATED as a script (no 8// interactive event loop in a gate); terrain is double-sided (no backface ambiguity while winding conventions 9// settle). World geometry now comes from the shared nx_game_world lib; near-plane clipping and the tonemap 10// are live (seq249/seq260). license_tier: ORIGINAL expect_exit: 0 11import "nx_browser_render.nx" 12import "nx_game_world.nx" // terrain + entity geometry (extracted here on the second consumer) 13 14const WG_STEP: i64 = 8192 // forward step = 2 units 15const WG_TURN: i64 = 3217 // ~45 degrees in rad*4096 16const WG_TICKS: i64 = 6 17 18func wg_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 19func wg_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 20func wg_now_ms() -> i64 { let ts: *i64=sys_mmap(16) as *i64; sys_clock_gettime_mono(ts); return ts[0]*1000 + ts[1]/1000000 } 21 22// one tick's render: 3 altitude-band albedo passes over ONE shared zbuf -> RGB into dst 23func wg_render_tick(base: i64, ppx: i64, ppz: i64, yaw: i64, eyey: i64, dst: *u8) -> i64 { 24 gw_build_terrain(base, 0, ppx, ppz, eyey) 25 sg_project_clip(base, yaw, 0) 26 sg_render(base, 74, 156, 62) // grass (clears fb+zbuf) 27 gw_build_terrain(base, 1, ppx, ppz, eyey) 28 sg_project_clip(base, yaw, 0) 29 sg_render_pass(base, 128, 112, 100) // rock (shared zbuf) 30 gw_build_terrain(base, 2, ppx, ppz, eyey) 31 sg_project_clip(base, yaw, 0) 32 sg_render_pass(base, 232, 234, 240) // snow (shared zbuf) 33 let fb: *i64 = sg_fb(base) 34 var p: i64 = 0 35 while p < ww()*hh() { 36 let v: i64 = fb[p] 37 dst[p*3] = (v % 256) as u8 38 dst[p*3+1] = ((v/256) % 256) as u8 39 dst[p*3+2] = ((v/65536) % 256) as u8 40 p = p + 1 41 } 42 return 0 43} 44 45// run the WHOLE input script deterministically; frames -> fbuf (6 frames back to back); returns checksum 46func wg_run_script(base: i64, fbuf: *u8) -> i64 { 47 // input script: FWD FWD TURN-LEFT FWD TURN-RIGHT FWD (1=fwd 2=left 3=right) 48 let scr: *i64 = sys_mmap(8*8) as *i64 49 scr[0]=1; scr[1]=1; scr[2]=2; scr[3]=1; scr[4]=3; scr[5]=1 50 var ppx: i64 = 14*GW_CELL 51 var ppz: i64 = 6*GW_CELL 52 var yaw: i64 = 0 53 var ck: i64 = 0 54 var tck: i64 = 0 55 while tck < WG_TICKS { 56 // 1. INPUT -> STATE 57 if scr[tck] == 1 { ppx = ppx + it_sin4096(yaw)*WG_STEP/4096; ppz = ppz + it_cos4096(yaw)*WG_STEP/4096 } 58 if scr[tck] == 2 { yaw = yaw + WG_TURN } 59 if scr[tck] == 3 { yaw = yaw - WG_TURN } 60 // eye rides the local ground (nearest grid vertex) + 1.6 units 61 var gi: i64 = ppx / GW_CELL; if gi < 0 { gi = 0 } if gi > GW_N { gi = GW_N } 62 var gj: i64 = ppz / GW_CELL; if gj < 0 { gj = 0 } if gj > GW_N { gj = GW_N } 63 let eyey: i64 = gw_h(gi,gj) + 6600 64 // 2. RENDER through the real pipeline 65 let dst: *u8 = ((fbuf as i64) + tck*ww()*hh()*3) as *u8 66 wg_render_tick(base, ppx, ppz, yaw, eyey, dst) 67 // 3. fold this frame into the run checksum (sampled) 68 var p: i64 = 0 69 while p < ww()*hh()*3 { ck = ck + (dst[p] as i64)*(1 + p % 7); p = p + 97 } 70 tck = tck + 1 71 } 72 wg_puts(" final player state: x="); wg_pn(ppx); wg_puts(" z="); wg_pn(ppz); wg_puts(" yaw="); wg_pn(yaw); wg_puts("\n" as *u8) 73 return ck 74} 75 76func main() -> i64 { 77 wg_puts("=== nx_gx4_walk_gate -- first playable: input-driven walk through a sovereign 3D world in the live browser ===\n" as *u8) 78 var fails: i64=0 79 80 // ---- the page: the world lives in a fetched page's canvas ---- 81 let html: *u8 = "<html><head><style>canvas{display:block;width:512px;height:384px;margin:18px 0}</style></head><body><h1>Sovereign Walk</h1><p>An input script drives a player over a procedurally generated heightfield: forward, forward, turn left, forward, turn right, forward. Every tick the sovereign software GPU renders the world from the player camera and the browser repaints the canvas.</p><canvas></canvas><p>Deterministic by construction: the same inputs replay to byte-identical frames. Zero third-party.</p></body></html>\x00" as *u8 82 let page: *Page = sys_mmap(NX_PAGE_BYTES) as *Page 83 page.raw = html; page.raw_len = br_slen(html); page.dark_mode = 0 84 br_layout(page, 1000) 85 var t1: i64=0 86 if page.ok==1 { if page.tree.count>5 { t1=1 } } 87 if t1==1 { wg_puts("T1 PASS page laid out by the live engine\n" as *u8) } else { fails=fails+1; wg_puts("T1 FAIL layout\n" as *u8) } 88 let cv: i64 = br_find_canvas(page, 0) 89 if cv>=0 { wg_puts("T2 PASS canvas surface box idx="); wg_pn(cv); wg_puts("\n" as *u8) } else { fails=fails+1; wg_puts("T2 FAIL no canvas\n" as *u8) } 90 91 // ---- run the input script TWICE (determinism) ---- 92 let base: i64 = sys_mmap(swgpu_bytes()) as i64 93 let runA: *u8 = sys_mmap(WG_TICKS*ww()*hh()*3+16) 94 let runB: *u8 = sys_mmap(WG_TICKS*ww()*hh()*3+16) 95 let t_start: i64 = wg_now_ms() 96 let ckA: i64 = wg_run_script(base, runA) 97 let ms_run: i64 = wg_now_ms() - t_start 98 let ckB: i64 = wg_run_script(base, runB) 99 let tpf: i64 = ms_run / WG_TICKS 100 wg_puts(" MEASURED: 6 world ticks in "); wg_pn(ms_run); wg_puts(" ms = "); wg_pn(tpf) 101 wg_puts(" ms/tick (envelope: 841-vert 3-pass banded terrain at 512x384; browser repaint + PNG measured separately below)\n" as *u8) 102 var t3: i64=0 103 if ckA==ckB { if ckA>0 { t3=1 } } 104 if t3==1 { wg_puts("T3 PASS DETERMINISTIC: identical input script -> identical frames (checksum "); wg_pn(ckA); wg_puts(" both runs)\n" as *u8) } else { fails=fails+1; wg_puts("T3 FAIL ckA="); wg_pn(ckA); wg_puts(" ckB="); wg_pn(ckB); wg_puts("\n" as *u8) } 105 106 // ---- T3b GOLDEN-CK RATCHET. Provenance, because a ratchet that moves silently is worthless: 107 // 19387962 = original, sg_project (whole-triangle cull), no tonemap. 108 // 19387962 = UNCHANGED after switching to sg_project_clip -- that bit-identity is what PROVED 109 // near-plane clipping is a no-op on dense meshes (the seq249 self-correction). 110 // 16691393 = current, after the seq260 TONEMAP landed. This one moved for a KNOWN, intended reason: 111 // shading now rolls highlights off instead of clamping, so pixel values legitimately 112 // changed (blown-to-white went 99090 -> 0 on this very frame). Re-baselined deliberately. 113 // Any drift from here without a named cause means something regressed. 114 let WG_GOLDEN_CK: i64 = 16691393 115 var t3b: i64=0 116 if ckA==WG_GOLDEN_CK { t3b=1 } 117 if t3b==1 { wg_puts("T3b PASS golden ck held (post-tonemap baseline; clip proved a no-op here at the pre-tonemap baseline)\n" as *u8) } 118 else { fails=fails+1; wg_puts("T3b FAIL golden ck "); wg_pn(WG_GOLDEN_CK); wg_puts(" != "); wg_pn(ckA); wg_puts("\n" as *u8) } 119 120 // ---- diagnostic: BLOWN-OUT pixels (clamped to pure 255,255,255). High-albedo snow plus rim plus 121 // specular saturates with no tonemap headroom -- this, NOT a hole, is the white region on the walk frames. 122 var blown: i64=0 123 var bp: i64=0 124 while bp < ww()*hh() { 125 let f5: i64 = 5*ww()*hh()*3 126 if (runA[f5+bp*3] as i64)==255 { if (runA[f5+bp*3+1] as i64)==255 { if (runA[f5+bp*3+2] as i64)==255 { blown=blown+1 } } } 127 bp = bp + 1 128 } 129 wg_puts(" DIAG tick5 blown-to-pure-white px = "); wg_pn(blown); wg_puts(" of "); wg_pn(ww()*hh()) 130 wg_puts(" (was 99090 = 50.4 pct before the seq260 tonemap; highlights now roll off)\n" as *u8) 131 132 // ---- T4 the world is FLOOR-QUALITY: all 3 altitude bands + sky visible in frame 0 ---- 133 var grass: i64=0 134 var rock: i64=0 135 var snow: i64=0 136 var sky: i64=0 137 var p4: i64=0 138 while p4 < ww()*hh() { 139 let r: i64 = runA[p4*3] as i64 140 let g: i64 = runA[p4*3+1] as i64 141 let b: i64 = runA[p4*3+2] as i64 142 if g > r+25 { if g > b+25 { grass=grass+1 } } 143 if r > 180 { if g > 180 { if b > 180 { snow=snow+1 } } } 144 if r > 60 { if r < 180 { var dg: i64=r-g; if dg<0 {dg=0-dg} var db: i64=g-b; if db<0 {db=0-db} if dg<26 { if db<30 { if b<r+12 { rock=rock+1 } } } } } 145 if b > r+10 { if b > g+8 { if p4 < ww()*hh()/3 { sky=sky+1 } } } 146 p4 = p4 + 1 147 } 148 wg_puts(" frame0 pixel classes: grass="); wg_pn(grass); wg_puts(" rock="); wg_pn(rock); wg_puts(" snow="); wg_pn(snow); wg_puts(" sky(top-third)="); wg_pn(sky); wg_puts("\n" as *u8) 149 var t4: i64=0 150 if grass>2000 { if rock>800 { if snow>300 { if sky>5000 { t4=1 } } } } 151 if t4==1 { wg_puts("T4 PASS floor-quality world: grass + rock + snow bands under a sky, lit + z-buffered\n" as *u8) } else { fails=fails+1; wg_puts("T4 FAIL bands\n" as *u8) } 152 153 // ---- T5 INPUT MOVES THE WORLD: consecutive frames differ (fwd, turn each visible) ---- 154 var d01: i64=0 155 var d23: i64=0 156 var p5: i64=0 157 while p5 < ww()*hh() { 158 var e1: i64 = (runA[p5*3] as i64) - (runA[(ww()*hh()*3)+p5*3] as i64); if e1<0 {e1=0-e1} 159 var e2: i64 = (runA[(2*ww()*hh()*3)+p5*3] as i64) - (runA[(3*ww()*hh()*3)+p5*3] as i64); if e2<0 {e2=0-e2} 160 if e1 > 25 { d01=d01+1 } 161 if e2 > 25 { d23=d23+1 } 162 p5 = p5 + 7 163 } 164 wg_puts(" changed px: tick0->1 (fwd)="); wg_pn(d01); wg_puts(" tick2->3 (turn-then-fwd)="); wg_pn(d23); wg_puts(" (sampled/7)\n" as *u8) 165 var t5: i64=0 166 if d01>400 { if d23>400 { t5=1 } } 167 if t5==1 { wg_puts("T5 PASS input drives the camera: walking and turning both repaint the world\n" as *u8) } else { fails=fails+1; wg_puts("T5 FAIL d01="); wg_pn(d01); wg_puts(" d23="); wg_pn(d23); wg_puts("\n" as *u8) } 168 169 // ---- browser-paint 3 ticks of the walk (t0, t3, t5) through the canvas ---- 170 let bp_start: i64 = wg_now_ms() 171 if cv>=0 { 172 page.bimg_w[cv] = ww(); page.bimg_h[cv] = hh() 173 page.bimg[cv] = (runA as i64) 174 br_shot_png(page, "https://nishifamily.com/world/walk\x00" as *u8, 34, "knowledge/nx_gx4_walk_t0.png\x00" as *u8) 175 page.bimg[cv] = (runA as i64) + 3*ww()*hh()*3 176 br_shot_png(page, "https://nishifamily.com/world/walk\x00" as *u8, 34, "knowledge/nx_gx4_walk_t3.png\x00" as *u8) 177 page.bimg[cv] = (runA as i64) + 5*ww()*hh()*3 178 br_shot_png(page, "https://nishifamily.com/world/walk\x00" as *u8, 34, "knowledge/nx_gx4_walk_t5.png\x00" as *u8) 179 } 180 let ms_paint: i64 = (wg_now_ms() - bp_start) / 3 181 wg_puts(" MEASURED: browser full-repaint + PNG encode = "); wg_pn(ms_paint); wg_puts(" ms/frame on top of the world tick\n" as *u8) 182 let szp: *i64 = sys_mmap(16) as *i64 183 var okpng: i64=0 184 let r0: *u8 = sys_read_file("knowledge/nx_gx4_walk_t0.png\x00" as *u8, szp) 185 if (r0 as i64)!=0 { if szp[0]>1000 { okpng=okpng+1 } } 186 let r3: *u8 = sys_read_file("knowledge/nx_gx4_walk_t3.png\x00" as *u8, szp) 187 if (r3 as i64)!=0 { if szp[0]>1000 { okpng=okpng+1 } } 188 let r5: *u8 = sys_read_file("knowledge/nx_gx4_walk_t5.png\x00" as *u8, szp) 189 if (r5 as i64)!=0 { if szp[0]>1000 { okpng=okpng+1 } } 190 if okpng==3 { wg_puts("T6 PASS 3 walk frames browser-painted to disk (t0/t3/t5)\n" as *u8) } else { fails=fails+1; wg_puts("T6 FAIL pngs="); wg_pn(okpng); wg_puts("\n" as *u8) } 191 192 if fails==0 { wg_puts("GX4-WALK GREEN -- an input script walks a player through a lit, banded, z-buffered sovereign world inside the live browser, deterministically (zero 3rd-party)\n" as *u8); sys_exit(0); return 0 } 193 wg_puts("GX4-WALK RED fails="); wg_pn(fails); wg_puts("\n" as *u8) 194 sys_exit(1) 195 return 1 196}