code wiki / _hdl_build / nx_sphere_gate.nx
nx_sphere_gate.nx source
↩ module page · 244 lines · 10018 B
1// nx_sphere_gate.nx -- realism/anime on an OBJECT (not terrain): a smooth Gouraud-lit sphere on the sovereign
2// f32 raster path. Geometry = spherified cube (no trig; normalize a cube grid via nx_f32_sqrt, same bit layout
3// as hw f32). Per-vertex lambert brightness (normal=unit position) interpolated PER PIXEL = smooth shading.
4// Rendered twice: style 0 realism (smooth), style 1 toon (2-tone + black outline). 2 BMPs. exit 0 = rendered.
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_f32_hw.nx"
8import "nx_f32.nx"
9
10const W: i64 = 320
11const H: i64 = 240
12const HW: i64 = 160
13const HH: i64 = 120
14const G: i64 = 10 // cells per cube-face edge (verts/edge = G+1)
15const VPF: i64 = 121 // (G+1)*(G+1)
16const NV: i64 = 726 // 6*VPF
17const FOCAL: i64 = 560
18const PUSH: i64 = 4
19const ZFAR: i64 = 1073741824
20
21func imin(a: i64, b: i64) -> i64 { if a < b { return a } return b }
22func imax(a: i64, b: i64) -> i64 { if a > b { return a } return b }
23func iabs(a: i64) -> i64 { if a < 0 { return 0 - a } return a }
24func min3(a: i64, b: i64, c: i64) -> i64 { return imin(a, imin(b, c)) }
25func max3(a: i64, b: i64, c: i64) -> i64 { return imax(a, imax(b, c)) }
26func clamp255(v: i64) -> i64 { if v < 0 { return 0 } if v > 255 { return 255 } return v }
27func same_sign3(a: i64, b: i64, c: i64) -> i64 {
28 if a >= 0 { if b >= 0 { if c >= 0 { return 1 } } }
29 if a <= 0 { if b <= 0 { if c <= 0 { return 1 } } }
30 return 0
31}
32func fratio(num: i64, den: i64) -> i64 { return f32_div(f32_of(num), f32_of(den)) }
33
34func cube_pt(f: i64, u: i64, v: i64, out: *i64) -> i64 {
35 let one: i64 = f32_of(1)
36 let none: i64 = f32_neg(one)
37 if f == 0 { out[0] = one; out[1] = u; out[2] = v }
38 if f == 1 { out[0] = none; out[1] = u; out[2] = v }
39 if f == 2 { out[0] = u; out[1] = one; out[2] = v }
40 if f == 3 { out[0] = u; out[1] = none; out[2] = v }
41 if f == 4 { out[0] = u; out[1] = v; out[2] = one }
42 if f == 5 { out[0] = u; out[1] = v; out[2] = none }
43 return 0
44}
45// gouraud (per-vertex brightness) z-buffered triangle. t = [x0,y0,d0,b0, x1,y1,d1,b1, x2,y2,d2,b2].
46func fill_gour(fb: *i64, zb: *i64, t: *i64, baseR: i64, baseG: i64, baseB: i64, style: i64) -> i64 {
47 let x0: i64 = t[0]; let y0: i64 = t[1]; let d0: i64 = t[2]; let b0: i64 = t[3]
48 let x1: i64 = t[4]; let y1: i64 = t[5]; let d1: i64 = t[6]; let b1: i64 = t[7]
49 let x2: i64 = t[8]; let y2: i64 = t[9]; let d2: i64 = t[10]; let b2: i64 = t[11]
50 let area: i64 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)
51 if area == 0 { return 0 }
52 let minx: i64 = imax(0, min3(x0, x1, x2))
53 let maxx: i64 = imin(W - 1, max3(x0, x1, x2))
54 let miny: i64 = imax(0, min3(y0, y1, y2))
55 let maxy: i64 = imin(H - 1, max3(y0, y1, y2))
56 var py: i64 = miny
57 while py <= maxy {
58 var px: i64 = minx
59 while px <= maxx {
60 let e0: i64 = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)
61 let e1: i64 = (x0 - x2) * (py - y2) - (y0 - y2) * (px - x2)
62 let e2: i64 = (x1 - x0) * (py - y0) - (y1 - y0) * (px - x0)
63 if same_sign3(e0, e1, e2) == 1 {
64 let d: i64 = (e0 * d0 + e1 * d1 + e2 * d2) / area
65 let idx: i64 = py * W + px
66 if d < zb[idx] {
67 zb[idx] = d
68 var bri: i64 = (e0 * b0 + e1 * b1 + e2 * b2) / area
69 if style == 1 { if bri < 145 { bri = 90 } else { bri = 235 } }
70 fb[idx] = ((baseR * bri) / 255) + ((baseG * bri) / 255) * 256 + ((baseB * bri) / 255) * 65536
71 }
72 }
73 px = px + 1
74 }
75 py = py + 1
76 }
77 return 0
78}
79func outline_pass(fb: *i64, zb: *i64) -> i64 {
80 var y: i64 = 1
81 while y < H - 1 {
82 var x: i64 = 1
83 while x < W - 1 {
84 let i: i64 = y * W + x
85 let d: i64 = zb[i]
86 if d < ZFAR {
87 var edge: i64 = 0
88 if zb[i - 1] == ZFAR { edge = 1 }
89 if zb[i + 1] == ZFAR { edge = 1 }
90 if zb[i - W] == ZFAR { edge = 1 }
91 if zb[i + W] == ZFAR { edge = 1 }
92 if edge == 1 { fb[i] = 0 }
93 }
94 x = x + 1
95 }
96 y = y + 1
97 }
98 return 0
99}
100func put_u32le(b: *u8, o: i64, v: i64) -> i64 {
101 b[o] = (v & 0xff) as u8
102 b[o + 1] = ((v >> 8) & 0xff) as u8
103 b[o + 2] = ((v >> 16) & 0xff) as u8
104 b[o + 3] = ((v >> 24) & 0xff) as u8
105 return 0
106}
107func write_bmp(fb: *i64, path: *u8) -> i64 {
108 let pix: i64 = W * H * 3
109 let total: i64 = 54 + pix
110 let b: *u8 = sys_mmap(total) as *u8
111 b[0] = 66 as u8
112 b[1] = 77 as u8
113 put_u32le(b, 2, total)
114 put_u32le(b, 10, 54)
115 put_u32le(b, 14, 40)
116 put_u32le(b, 18, W)
117 put_u32le(b, 22, H)
118 b[26] = 1 as u8
119 b[28] = 24 as u8
120 put_u32le(b, 34, pix)
121 var o: i64 = 54
122 var ry: i64 = 0
123 while ry < H {
124 let syrow: i64 = H - 1 - ry
125 var x: i64 = 0
126 while x < W {
127 let c: i64 = fb[syrow * W + x]
128 b[o] = ((c >> 16) & 0xff) as u8
129 b[o + 1] = ((c >> 8) & 0xff) as u8
130 b[o + 2] = (c & 0xff) as u8
131 o = o + 3
132 x = x + 1
133 }
134 ry = ry + 1
135 }
136 let fd: i64 = sys_openat_wr(path, 0x1a4)
137 if fd < 0 { return 0 - 1 }
138 sys_write(fd, b, total)
139 sys_close(fd)
140 return 0
141}
142// render the sphere: build verts (pos+brightness via spherified-cube + lambert), project, raster.
143func render_sphere(fb: *i64, zb: *i64, R: *i64, style: i64, baseR: i64, baseG: i64, baseB: i64,
144 sx: *i64, sy: *i64, sd: *i64, sok: *i64, sb: *i64) -> i64 {
145 let sky: i64 = 150 + 170 * 256 + 210 * 65536
146 var p: i64 = 0
147 while p < W * H { fb[p] = sky; zb[p] = ZFAR; p = p + 1 }
148 // light toward upper-front-camera (-z toward viewer).
149 let lx: i64 = fratio(3, 10)
150 let ly: i64 = fratio(5, 10)
151 let lz: i64 = f32_neg(fratio(8, 10))
152 var f: i64 = 0
153 while f < 6 {
154 var j: i64 = 0
155 while j <= G {
156 var ii: i64 = 0
157 while ii <= G {
158 let u: i64 = fratio(2 * ii - G, G)
159 let v: i64 = fratio(2 * j - G, G)
160 let cp: *i64 = sys_mmap(32) as *i64
161 cube_pt(f, u, v, cp)
162 let l2: i64 = f32_add(f32_add(f32_mul(cp[0], cp[0]), f32_mul(cp[1], cp[1])), f32_mul(cp[2], cp[2]))
163 let len: i64 = nx_f32_sqrt(l2)
164 let nv: *i64 = sys_mmap(32) as *i64
165 nv[0] = f32_div(cp[0], len)
166 nv[1] = f32_div(cp[1], len)
167 nv[2] = f32_div(cp[2], len)
168 nv[3] = f32_of(0)
169 let rp: *i64 = sys_mmap(32) as *i64
170 m4_vec4(R, nv, rp) // rotated unit normal = rotated unit position
171 let g: i64 = f * VPF + j * (G + 1) + ii
172 // lambert: dot(rotated normal, light)
173 var bd: i64 = v3_dot(rp[0], rp[1], rp[2], lx, ly, lz)
174 if (bd & 0x80000000) != 0 { bd = f32_of(0) }
175 sb[g] = clamp255(45 + f32_int(f32_mul(bd, f32_of(210))))
176 // project (sphere radius 1 at depth PUSH)
177 let pz: i64 = f32_add(rp[2], f32_of(PUSH))
178 if (pz & 0x80000000) != 0 { sok[g] = 0 } else {
179 sx[g] = HW + f32_int(f32_div(f32_mul(rp[0], f32_of(FOCAL)), pz))
180 sy[g] = HH - f32_int(f32_div(f32_mul(rp[1], f32_of(FOCAL)), pz))
181 sd[g] = f32_int(f32_mul(pz, f32_of(256)))
182 sok[g] = 1
183 }
184 ii = ii + 1
185 }
186 j = j + 1
187 }
188 f = f + 1
189 }
190 // triangulate each face grid.
191 var ff: i64 = 0
192 while ff < 6 {
193 var cj: i64 = 0
194 while cj < G {
195 var ci: i64 = 0
196 while ci < G {
197 let a: i64 = ff * VPF + cj * (G + 1) + ci
198 let b: i64 = ff * VPF + cj * (G + 1) + ci + 1
199 let c: i64 = ff * VPF + (cj + 1) * (G + 1) + ci + 1
200 let dd: i64 = ff * VPF + (cj + 1) * (G + 1) + ci
201 if sok[a] == 1 { if sok[b] == 1 { if sok[c] == 1 { if sok[dd] == 1 {
202 let t: *i64 = sys_mmap(12 * 8) as *i64
203 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = sb[a]
204 t[4] = sx[b]; t[5] = sy[b]; t[6] = sd[b]; t[7] = sb[b]
205 t[8] = sx[c]; t[9] = sy[c]; t[10] = sd[c]; t[11] = sb[c]
206 fill_gour(fb, zb, t, baseR, baseG, baseB, style)
207 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = sb[a]
208 t[4] = sx[c]; t[5] = sy[c]; t[6] = sd[c]; t[7] = sb[c]
209 t[8] = sx[dd]; t[9] = sy[dd]; t[10] = sd[dd]; t[11] = sb[dd]
210 fill_gour(fb, zb, t, baseR, baseG, baseB, style)
211 } } } }
212 ci = ci + 1
213 }
214 cj = cj + 1
215 }
216 ff = ff + 1
217 }
218 if style == 1 { outline_pass(fb, zb) }
219 return 0
220}
221
222func main() -> i64 {
223 let fb: *i64 = sys_mmap(W * H * 8) as *i64
224 let zb: *i64 = sys_mmap(W * H * 8) as *i64
225 let sx: *i64 = sys_mmap(NV * 8) as *i64
226 let sy: *i64 = sys_mmap(NV * 8) as *i64
227 let sd: *i64 = sys_mmap(NV * 8) as *i64
228 let sok: *i64 = sys_mmap(NV * 8) as *i64
229 let sb: *i64 = sys_mmap(NV * 8) as *i64
230 let R: *i64 = sys_mmap(128) as *i64
231 let Rx: *i64 = sys_mmap(128) as *i64
232 let Ry: *i64 = sys_mmap(128) as *i64
233 m4_roty(fratio(940, 1000), fratio(342, 1000), Ry)
234 m4_rotx(fratio(966, 1000), fratio(259, 1000), Rx)
235 m4_mul(Rx, Ry, R)
236
237 render_sphere(fb, zb, R, 0, 226, 142, 70, sx, sy, sd, sok, sb) // realism, warm orange
238 write_bmp(fb, "web_assets/_game_build/sphere_real.bmp" as *u8)
239 render_sphere(fb, zb, R, 1, 226, 142, 70, sx, sy, sd, sok, sb) // toon
240 write_bmp(fb, "web_assets/_game_build/sphere_toon.bmp" as *u8)
241
242 if fb[(H / 2) * W + (W / 2)] == 0 { return 1 }
243 return 0
244}