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}