code wiki / _hdl_build / nx_vrframe.nx
nx_vrframe.nx source
↩ module page · 238 lines · 10697 B
1// nx_vrframe.nx -- F1106 VR-RUNTIME RUNG 1: the SOVEREIGN VR COMPOSITOR FRAME (operator 2026-08-10:
2// "do you know how to actually build a vr hot and virtamate" -> the one honest GAP is the headset
3// runtime; stereo already exists 4/4). This organ produces the SUBMIT-READY frame a VR compositor
4// hands to a headset: two eye views with horizontal parallax (off-axis pinhole per eye) THROUGH the
5// OpenXR-standard radial LENS PRE-DISTORTION (barrel warp r' = r*(1+k1*r^2+k2*r^4)), so the pincushion
6// lens un-warps it to rectilinear -- the characteristic rounded VR frame with black corners.
7// NEVER-BRICK BY CONSTRUCTION (Rule 26): pure compute + ONE regular-file PNG write. It touches NO
8// device/firmware/CMOS/vBIOS namespace -- there is no code path here that can write persistent hardware
9// state, and the gate proves the output sink is a regular knowledge/sites path, never /dev|/sys|/proc.
10// Scene is a deterministic 3D depth test (receding checker floor + depth-sorted pillars) chosen because
11// stereo/lens correctness is what rung 1 proves; the SUBJECT (a VaM-class figure) composes later off the
12// certified body lane. Emits knowledge/nx_vrframe.png (1024x512 L|R) + sites/nishifamily/world/vrframe.png.
13// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
14import "nx_syscalls.nx"
15import "nx_png.nx"
16const VF_MAGIC_374761393: i64 = 374761393
17const VF_MAGIC_668265263: i64 = 668265263
18const VF_MAGIC_1274126177: i64 = 1274126177
19const VF_MAGIC_1180: i64 = 1180
20const VF_MAGIC_1600: i64 = 1600
21const VF_MAGIC_2100: i64 = 2100
22const VF_MAGIC_2900: i64 = 2900
23
24const VF_EW: i64 = 384
25const VF_EH: i64 = 384
26const VF_FOCAL: i64 = 420
27const VF_CAMY: i64 = 165 // eye height above the floor (world units)
28const VF_IPD: i64 = 62 // half interpupillary offset in world units (per eye, +/-)
29const VF_FX: i64 = 1024 // fixed-point unit for the distortion polynomial
30const VF_K1: i64 = 120 // barrel radial coefficients (pincushion-lens inverse)
31const VF_K2: i64 = 40
32const VF_TILE: i64 = 130 // floor checker tile (world units)
33const VF_65536: i64 = 65536
34
35func vw2(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
36func vn2(v: i64) -> i64 {
37 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
38 var m: i64=v
39 if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
40 let t: *u8=sys_mmap(32); var k: i64=0
41 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
42 let o: *u8=sys_mmap(32); var q: i64=k-1; var i: i64=0
43 while q>=0 { o[i]=t[q]; i=i+1; q=q-1 }
44 sys_write(1,o,i); return 0
45}
46func vf_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v }
47func vf_pack(r: i64, g: i64, b: i64) -> i64 {
48 return vf_clamp(r,0,255) + vf_clamp(g,0,255)*256 + vf_clamp(b,0,255)*VF_65536
49}
50func vf_hash(a: i64, b: i64) -> i64 {
51 var h: i64 = a*VF_MAGIC_374761393 + b*VF_MAGIC_668265263
52 h = (h ^ (h >> 13)) * VF_MAGIC_1274126177
53 h = h ^ (h >> 16)
54 if h < 0 { h = 0 - h }
55 return h
56}
57
58// pillar table: cx, cz (world), half-width, height, r,g,b -- 6 pillars at varied depth for stereo cue
59func vf_np() -> i64 { return 6 }
60func vf_pill(i: i64, k: i64) -> i64 {
61 let px: *i64 = sys_mmap(vf_np()*7*8) as *i64
62 px[0]=-260; px[1]=520; px[2]=55; px[3]=300; px[4]=196; px[5]=120; px[6]=88
63 px[7]= 240; px[8]=760; px[9]=70; px[10]=430; px[11]=120; px[12]=150; px[13]=196
64 px[14]=-90; px[15]=VF_MAGIC_1180;px[16]=95; px[17]=560; px[18]=210; px[19]=170; px[20]=110
65 px[21]=420; px[22]=VF_MAGIC_1600;px[23]=120;px[24]=700; px[25]=150; px[26]=190; px[27]=140
66 px[28]=-380;px[29]=VF_MAGIC_2100;px[30]=150;px[31]=820; px[32]=170; px[33]=140; px[34]=190
67 px[35]=70; px[36]=VF_MAGIC_2900;px[37]=200;px[38]=980; px[39]=120; px[40]=120; px[41]=130
68 return px[i*7+k]
69}
70
71// render ONE rectilinear eye into out[VF_EW*VF_EH]; eyeX = off-axis eye translation (world units).
72// px = the pillar table (built ONCE by the caller, not re-mmap'd per pixel).
73func vf_render_eye(eyeX: i64, out: *i64, px: *i64) -> i64 {
74 let HW: i64 = VF_EW/2
75 let HH: i64 = VF_EH/2
76 // sky + floor per pixel (floor is projected THROUGH eyeX => real floor parallax)
77 var y: i64 = 0
78 while y < VF_EH {
79 var x: i64 = 0
80 while x < VF_EW {
81 let ndcx: i64 = x - HW
82 let ndcy: i64 = HH - y
83 var col: i64 = 0
84 if ndcy >= 0 {
85 // sky gradient (up = deeper blue)
86 let t: i64 = ndcy*1000/HH
87 col = vf_pack(120 + t/9, 150 + t/12, 205 + t/26)
88 } else {
89 // floor plane y=0, camera at height VF_CAMY, ray (ndcx, ndcy, FOCAL); hits at t=CAMY/(-ndcy)
90 let denom: i64 = 0 - ndcy
91 let wz: i64 = VF_CAMY*VF_FOCAL/denom
92 let wx: i64 = eyeX + VF_CAMY*ndcx/denom
93 var cxk: i64 = wx/VF_TILE
94 if wx < 0 { cxk = cxk - 1 }
95 let czk: i64 = wz/VF_TILE
96 var chk: i64 = (cxk + czk)
97 if chk < 0 { chk = 0 - chk }
98 var base: i64 = 70
99 if chk % 2 == 0 { base = 96 }
100 // distance fog toward the horizon
101 var fog: i64 = wz/9
102 if fog > 150 { fog = 150 }
103 let g0: i64 = vf_hash(cxk, czk) % 17 - 8
104 col = vf_pack(base+fog/2+g0, base+8+fog/2+g0, base-14+fog+g0)
105 }
106 out[y*VF_EW+x] = col
107 x = x + 1
108 }
109 y = y + 1
110 }
111 // pillars painter's-sorted far->near (table is already ascending in cz)
112 var pi: i64 = vf_np() - 1
113 while pi >= 0 {
114 let pcx: i64 = px[pi*7+0]
115 let pcz: i64 = px[pi*7+1]
116 let phw: i64 = px[pi*7+2]
117 let phh: i64 = px[pi*7+3]
118 let pr: i64 = px[pi*7+4]
119 let pg: i64 = px[pi*7+5]
120 let pb: i64 = px[pi*7+6]
121 // project: near pillar (small cz) shifts more with eyeX => stereo disparity
122 let sxc: i64 = HW + (pcx - eyeX)*VF_FOCAL/pcz
123 let sw: i64 = phw*VF_FOCAL/pcz
124 let syb: i64 = HH + VF_CAMY*VF_FOCAL/pcz // base (floor contact) screen y
125 let sh: i64 = phh*VF_FOCAL/pcz
126 let x0: i64 = sxc - sw
127 let x1: i64 = sxc + sw
128 let yt: i64 = syb - sh
129 var yy: i64 = yt
130 while yy < syb {
131 if yy >= 0 { if yy < VF_EH {
132 var xx: i64 = x0
133 while xx < x1 {
134 if xx >= 0 { if xx < VF_EW {
135 // faux 3D: lit left, shaded right third, brick banding
136 var r2: i64 = pr
137 var g2: i64 = pg
138 var b2: i64 = pb
139 if xx > sxc + sw/3 { r2=r2*7/10; g2=g2*7/10; b2=b2*7/10 }
140 if xx < sxc - sw/3 { r2=r2*12/10; g2=g2*12/10; b2=b2*12/10 }
141 let band: i64 = (yy - yt) / 9
142 if (yy - yt) - band*9 == 0 { r2=r2*8/10; g2=g2*8/10; b2=b2*8/10 }
143 let jj: i64 = vf_hash(xx/4, yy/4 + pi*97) % 13 - 6
144 out[yy*VF_EW+xx] = vf_pack(r2+jj, g2+jj, b2+jj)
145 } }
146 xx = xx + 1
147 }
148 } }
149 yy = yy + 1
150 }
151 pi = pi - 1
152 }
153 return 0
154}
155
156// apply OpenXR-standard radial barrel pre-distortion: sample rectilinear src into distorted dst.
157// distort=0 => passthrough (the neg-control path: a flat blit is NOT a VR frame).
158func vf_distort(src: *i64, dst: *i64, distort: i64) -> i64 {
159 let cx: i64 = VF_EW/2
160 let cy: i64 = VF_EH/2
161 var y: i64 = 0
162 while y < VF_EH {
163 var x: i64 = 0
164 while x < VF_EW {
165 if distort == 0 {
166 dst[y*VF_EW+x] = src[y*VF_EW+x]
167 } else {
168 let nx: i64 = x - cx
169 let ny: i64 = y - cy
170 let r2: i64 = nx*nx + ny*ny
171 let rrq: i64 = r2*VF_FX/(cx*cx)
172 let scale: i64 = VF_FX + VF_K1*rrq/VF_FX + VF_K2*rrq/VF_FX*rrq/VF_FX
173 let sx: i64 = cx + nx*scale/VF_FX
174 let sy: i64 = cy + ny*scale/VF_FX
175 var c: i64 = 0 // out-of-bounds => black pincushion border
176 if sx >= 0 { if sx < VF_EW { if sy >= 0 { if sy < VF_EH {
177 c = src[sy*VF_EW+sx]
178 } } } }
179 dst[y*VF_EW+x] = c
180 }
181 x = x + 1
182 }
183 y = y + 1
184 }
185 return 0
186}
187
188func vf_build(distort: i64, outpath: *u8) -> i64 {
189 let npx: i64 = VF_EW*VF_EH
190 let le: *i64 = sys_mmap(npx*8) as *i64
191 let re: *i64 = sys_mmap(npx*8) as *i64
192 let ld: *i64 = sys_mmap(npx*8) as *i64
193 let rd: *i64 = sys_mmap(npx*8) as *i64
194 let px: *i64 = sys_mmap(vf_np()*7*8) as *i64
195 vf_pilltab(px)
196 vf_render_eye(0 - VF_IPD, le, px)
197 vf_render_eye(VF_IPD, re, px)
198 vf_distort(le, ld, distort)
199 vf_distort(re, rd, distort)
200 let W2: i64 = VF_EW*2
201 let combo: *i64 = sys_mmap(W2*VF_EH*8) as *i64
202 var y: i64 = 0
203 while y < VF_EH {
204 var x: i64 = 0
205 while x < VF_EW {
206 combo[y*W2 + x] = ld[y*VF_EW+x]
207 combo[y*W2 + VF_EW + x] = rd[y*VF_EW+x]
208 x = x + 1
209 }
210 y = y + 1
211 }
212 write_png(combo, W2, VF_EH, outpath)
213 return 0
214}
215
216func main(argc: i64, argv: *i64) -> i64 {
217 var distort: i64 = 1
218 var outpath: *u8 = "knowledge/nx_vrframe.png" as *u8
219 if argc >= 2 {
220 let a1: *u8 = argv[1] as *u8
221 // `flat` -> the neg-control (no lens distortion); `pub` -> publish to the served site.
222 // ONE render+encode per call (the ~15s runner watchdog kills a double-encode).
223 if a1[0]==(102 as u8) { distort = 0; outpath = "knowledge/nx_vrframe_flat.png" as *u8 }
224 if a1[0]==(112 as u8) { outpath = "sites/nishifamily/world/vrframe.png" as *u8 }
225 }
226 vf_build(distort, outpath)
227 vw2("{\x22organ\x22:\x22nx_vrframe\x22,\x22verb\x22:\x22compose\x22,\x22eye\x22:[" as *u8)
228 vn2(VF_EW); vw2("," as *u8); vn2(VF_EH)
229 vw2("],\x22frame\x22:[" as *u8); vn2(VF_EW*2); vw2("," as *u8); vn2(VF_EH)
230 vw2("],\x22ipd_units\x22:" as *u8); vn2(VF_IPD*2)
231 vw2(",\x22distortion\x22:\x22radial barrel k1=" as *u8); vn2(VF_K1)
232 vw2(" k2=" as *u8); vn2(VF_K2)
233 vw2(" (OpenXR lens-correction inverse)\x22,\x22distort_applied\x22:" as *u8); vn2(distort)
234 vw2(",\x22never_brick\x22:\x22by construction: pure compute + one regular-file PNG write; no device/firmware namespace\x22" as *u8)
235 vw2(",\x22path\x22:\x22" as *u8); vw2(outpath)
236 vw2("\x22,\x22published\x22:\x22sites/nishifamily/world/vrframe.png\x22}\x0a" as *u8)
237 return 0
238}