code wiki / (root) / nx_voxel_world.nx

nx_voxel_world.nx source

↩ module page · 365 lines · 14921 B

1// nx_voxel_world.nx -- PARAMETERIZED sovereign 3D-world renderer: a game-SPEC (DATA) -> a rendered world. 2// Style is a spec field, NOT a hardcode -- the engine is not locked to one look: SP_STYLE 0=realism (textured, 3// smooth light), 1=toon (banded light + silhouette outline = anime/cel), 2=flat (untextured facets = artsy 4// low-poly). Geometry here is voxel terrain, but the rasterizer (fill_ztex) draws arbitrary textured triangles, 5// so other geometry (meshes/characters) plugs into the same path. Composes nx_f32_hw. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_f32_hw.nx" 8const SP_MAGIC_65536: i64 = 65536 9 10const W: i64 = 320 11const H: i64 = 240 12const HW: i64 = 160 13const HH: i64 = 120 14const TN: i64 = 4 15const ZFAR: i64 = 1073741824 16const SP_SEED: i64 = 0 17const SP_N: i64 = 1 18const SP_WATER: i64 = 2 19const SP_MAXH: i64 = 3 20const SP_LAT: i64 = 4 21const SP_PSIN: i64 = 5 22const SP_PCOS: i64 = 6 23const SP_YSIN: i64 = 7 24const SP_YCOS: i64 = 8 25const SP_PUSH: i64 = 9 26const SP_FOCAL: i64 = 10 27const SP_STYLE: i64 = 11 28const SP_CAMX: i64 = 12 // first-person camera position (f32 bit-patterns) 29const SP_CAMY: i64 = 13 30const SP_CAMZ: i64 = 14 31const SP_FPS: i64 = 15 // 1 = first-person (subtract cam pos, no push); 0 = orbit (default) 32const STYLE_REAL: i64 = 0 33const STYLE_TOON: i64 = 1 34const STYLE_FLAT: i64 = 2 35 36func world_pixels() -> i64 { return W * H } 37func world_fb_bytes() -> i64 { return W * H * 8 } 38func world_sky() -> i64 { return 142 + 186 * 256 + 228 * SP_MAGIC_65536 } 39 40func imin(a: i64, b: i64) -> i64 { if a < b { return a } return b } 41func imax(a: i64, b: i64) -> i64 { if a > b { return a } return b } 42func iabs(a: i64) -> i64 { if a < 0 { return 0 - a } return a } 43func min3(a: i64, b: i64, c: i64) -> i64 { return imin(a, imin(b, c)) } 44func max3(a: i64, b: i64, c: i64) -> i64 { return imax(a, imax(b, c)) } 45func clamp255(v: i64) -> i64 { if v < 0 { return 0 } if v > 255 { return 255 } return v } 46func same_sign3(a: i64, b: i64, c: i64) -> i64 { 47 if a >= 0 { if b >= 0 { if c >= 0 { return 1 } } } 48 if a <= 0 { if b <= 0 { if c <= 0 { return 1 } } } 49 return 0 50} 51func fratio(num: i64, den: i64) -> i64 { return f32_div(f32_of(num), f32_of(den)) } 52 53func lat(ix: i64, iz: i64, seed: i64) -> i64 { 54 var n: i64 = (ix * 71 + iz * 191 + ix * iz * 7 + seed * 101) % 6 55 if n < 0 { n = n + 6 } 56 return n 57} 58func terrain_h(sp: *i64, gx: i64, gz: i64) -> i64 { 59 let L: i64 = sp[SP_LAT] 60 let sd: i64 = sp[SP_SEED] 61 let bx: i64 = gx + 200 62 let bz: i64 = gz + 200 63 let ix: i64 = bx / L 64 let iz: i64 = bz / L 65 let fx: i64 = (bx % L) * 256 / L 66 let fz: i64 = (bz % L) * 256 / L 67 let h00: i64 = lat(ix, iz, sd) 68 let h10: i64 = lat(ix + 1, iz, sd) 69 let h01: i64 = lat(ix, iz + 1, sd) 70 let h11: i64 = lat(ix + 1, iz + 1, sd) 71 let a: i64 = h00 * 256 + (h10 - h00) * fx 72 let b: i64 = h01 * 256 + (h11 - h01) * fx 73 var h: i64 = (a * 256 + (b - a) * fz) / SP_MAGIC_65536 + 1 74 if h < 1 { h = 1 } 75 if h > sp[SP_MAXH] { h = sp[SP_MAXH] } 76 return h 77} 78func hN(sp: *i64, gx: i64, gz: i64) -> i64 { 79 if gx < 0 { return 0 } 80 if gz < 0 { return 0 } 81 if gx >= sp[SP_N] { return 0 } 82 if gz >= sp[SP_N] { return 0 } 83 return terrain_h(sp, gx, gz) 84} 85func top_color(sp: *i64, h: i64) -> i64 { 86 if h <= sp[SP_WATER] { return 64 + 116 * 256 + 204 * SP_MAGIC_65536 } 87 if h >= sp[SP_MAXH] { return 236 + 238 * 256 + 245 * SP_MAGIC_65536 } 88 if h == sp[SP_MAXH] - 1 { return 122 + 118 * 256 + 112 * SP_MAGIC_65536 } 89 return 96 + 172 * 256 + 76 * SP_MAGIC_65536 90} 91func side_color(sp: *i64, h: i64) -> i64 { 92 if h <= sp[SP_WATER] { return 58 + 104 * 256 + 188 * SP_MAGIC_65536 } 93 if h >= sp[SP_MAXH] - 1 { return 110 + 106 * 256 + 100 * SP_MAGIC_65536 } 94 return 134 + 98 * 256 + 60 * SP_MAGIC_65536 95} 96// per-pixel colour: realism adds per-texel speckle; toon/flat use the flat band colour. 97func texel_col(style: i64, baseR: i64, baseG: i64, baseB: i64, u: i64, v: i64, bright: i64) -> i64 { 98 var n: i64 = 0 99 if style == STYLE_REAL { n = ((u * 7 + v * 13 + u * v * 5) % 7) - 3 } 100 let r: i64 = clamp255(baseR + n * 8) 101 let g: i64 = clamp255(baseG + n * 8) 102 let b: i64 = clamp255(baseB + n * 8) 103 return ((r * bright) / 255) + ((g * bright) / 255) * 256 + ((b * bright) / 255) * SP_MAGIC_65536 104} 105// toon: hard 2-tone (lit / shadow) cel shading. 106func toon_band(b: i64) -> i64 { 107 if b < 150 { return 105 } 108 return 235 109} 110func fill_ztex(fb: *i64, zb: *i64, t: *i64, br: i64, bg: i64, bb: i64, bright: i64, style: i64) -> i64 { 111 let x0: i64 = t[0]; let y0: i64 = t[1]; let d0: i64 = t[2]; let u0: i64 = t[3]; let v0: i64 = t[4] 112 let x1: i64 = t[5]; let y1: i64 = t[6]; let d1: i64 = t[7]; let u1: i64 = t[8]; let v1: i64 = t[9] 113 let x2: i64 = t[10]; let y2: i64 = t[11]; let d2: i64 = t[12]; let u2: i64 = t[13]; let v2: i64 = t[14] 114 let area: i64 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0) 115 if area == 0 { return 0 } 116 let minx: i64 = imax(0, min3(x0, x1, x2)) 117 let maxx: i64 = imin(W - 1, max3(x0, x1, x2)) 118 let miny: i64 = imax(0, min3(y0, y1, y2)) 119 let maxy: i64 = imin(H - 1, max3(y0, y1, y2)) 120 var py: i64 = miny 121 while py <= maxy { 122 var px: i64 = minx 123 while px <= maxx { 124 let e0: i64 = (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1) 125 let e1: i64 = (x0 - x2) * (py - y2) - (y0 - y2) * (px - x2) 126 let e2: i64 = (x1 - x0) * (py - y0) - (y1 - y0) * (px - x0) 127 if same_sign3(e0, e1, e2) == 1 { 128 let d: i64 = (e0 * d0 + e1 * d1 + e2 * d2) / area 129 let idx: i64 = py * W + px 130 if d < zb[idx] { 131 zb[idx] = d 132 let u: i64 = (e0 * u0 + e1 * u1 + e2 * u2) / area 133 let v: i64 = (e0 * v0 + e1 * v1 + e2 * v2) / area 134 fb[idx] = texel_col(style, br, bg, bb, u, v, bright) 135 } 136 } 137 px = px + 1 138 } 139 py = py + 1 140 } 141 return 0 142} 143func project(sp: *i64, R: *i64, iwx: i64, iwy: i64, iwz: i64, out: *i64) -> i64 { 144 let v: *i64 = sys_mmap(32) as *i64 145 let r: *i64 = sys_mmap(32) as *i64 146 if sp[SP_FPS] == 1 { 147 v[0] = f32_sub(f32_of(iwx), sp[SP_CAMX]) 148 v[1] = f32_sub(f32_of(iwy), sp[SP_CAMY]) 149 v[2] = f32_sub(f32_of(iwz), sp[SP_CAMZ]) 150 } else { 151 v[0] = f32_sub(f32_of(iwx), fratio(sp[SP_N], 2)) 152 v[1] = f32_sub(f32_of(iwy), f32_of(2)) 153 v[2] = f32_sub(f32_of(iwz), fratio(sp[SP_N], 2)) 154 } 155 v[3] = f32_of(0) 156 m4_vec4(R, v, r) 157 var pz: i64 = f32_add(r[2], f32_of(sp[SP_PUSH])) 158 if sp[SP_FPS] == 1 { pz = r[2] } 159 if (pz & 0x80000000) != 0 { out[3] = 0; return 0 } 160 let zi: i64 = f32_int(f32_mul(pz, f32_of(64))) 161 if zi <= 0 { out[3] = 0; return 0 } 162 out[0] = HW + f32_int(f32_div(f32_mul(r[0], f32_of(sp[SP_FOCAL])), pz)) 163 out[1] = HH - f32_int(f32_div(f32_mul(r[1], f32_of(sp[SP_FOCAL])), pz)) 164 out[2] = zi 165 out[3] = 1 166 return 0 167} 168func shade_local(lnx: i64, lny: i64, lnz: i64) -> i64 { 169 var d: i64 = lnx * 2 + lny * 3 + lnz * 1 170 if d < 0 { d = 0 } 171 return clamp255(110 + d * 38) 172} 173func draw_face(fb: *i64, zb: *i64, sx: *i64, sy: *i64, sd: *i64, sok: *i64, 174 a: i64, b: i64, c: i64, dd: i64, lnx: i64, lny: i64, lnz: i64, col: i64, style: i64) -> i64 { 175 if sok[a] == 0 { return 0 } 176 if sok[b] == 0 { return 0 } 177 if sok[c] == 0 { return 0 } 178 if sok[dd] == 0 { return 0 } 179 var bright: i64 = shade_local(lnx, lny, lnz) 180 if style == STYLE_TOON { bright = toon_band(bright) } 181 let br: i64 = col & 0xff 182 let bg: i64 = (col >> 8) & 0xff 183 let bb: i64 = (col >> 16) & 0xff 184 let t: *i64 = sys_mmap(15 * 8) as *i64 185 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = 0; t[4] = 0 186 t[5] = sx[b]; t[6] = sy[b]; t[7] = sd[b]; t[8] = TN; t[9] = 0 187 t[10] = sx[c]; t[11] = sy[c]; t[12] = sd[c]; t[13] = TN; t[14] = TN 188 fill_ztex(fb, zb, t, br, bg, bb, bright, style) 189 t[0] = sx[a]; t[1] = sy[a]; t[2] = sd[a]; t[3] = 0; t[4] = 0 190 t[5] = sx[c]; t[6] = sy[c]; t[7] = sd[c]; t[8] = TN; t[9] = TN 191 t[10] = sx[dd]; t[11] = sy[dd]; t[12] = sd[dd]; t[13] = 0; t[14] = TN 192 fill_ztex(fb, zb, t, br, bg, bb, bright, style) 193 return 0 194} 195func draw_col(sp: *i64, fb: *i64, zb: *i64, R: *i64, gx: i64, gz: i64) -> i64 { 196 let st: i64 = sp[SP_STYLE] 197 let h: i64 = terrain_h(sp, gx, gz) 198 let sx: *i64 = sys_mmap(8 * 8) as *i64 199 let sy: *i64 = sys_mmap(8 * 8) as *i64 200 let sd: *i64 = sys_mmap(8 * 8) as *i64 201 let sok: *i64 = sys_mmap(8 * 8) as *i64 202 var k: i64 = 0 203 while k < 8 { 204 let dx: i64 = k & 1 205 let dy: i64 = (k >> 1) & 1 206 let dz: i64 = (k >> 2) & 1 207 let o: *i64 = sys_mmap(32) as *i64 208 project(sp, R, gx + dx, dy * h, gz + dz, o) 209 sx[k] = o[0]; sy[k] = o[1]; sd[k] = o[2]; sok[k] = o[3] 210 k = k + 1 211 } 212 let tc: i64 = top_color(sp, h) 213 let sc: i64 = side_color(sp, h) 214 draw_face(fb, zb, sx, sy, sd, sok, 2, 3, 7, 6, 0, 1, 0, tc, st) 215 if hN(sp, gx + 1, gz) < h { draw_face(fb, zb, sx, sy, sd, sok, 1, 5, 7, 3, 1, 0, 0, sc, st) } 216 if hN(sp, gx - 1, gz) < h { draw_face(fb, zb, sx, sy, sd, sok, 0, 4, 6, 2, 0 - 1, 0, 0, sc, st) } 217 if hN(sp, gx, gz + 1) < h { draw_face(fb, zb, sx, sy, sd, sok, 4, 5, 7, 6, 0, 0, 1, sc, st) } 218 if hN(sp, gx, gz - 1) < h { draw_face(fb, zb, sx, sy, sd, sok, 0, 1, 3, 2, 0, 0, 0 - 1, sc, st) } 219 return 0 220} 221// draw an arbitrary world-space box (the entity/object primitive: trees, props, mobs, buildings). 222func draw_obox(sp: *i64, fb: *i64, zb: *i64, R: *i64, x0: i64, y0: i64, z0: i64, hx: i64, hy: i64, hz: i64, col: i64, style: i64) -> i64 { 223 let sx: *i64 = sys_mmap(8 * 8) as *i64 224 let sy: *i64 = sys_mmap(8 * 8) as *i64 225 let sd: *i64 = sys_mmap(8 * 8) as *i64 226 let sok: *i64 = sys_mmap(8 * 8) as *i64 227 var k: i64 = 0 228 while k < 8 { 229 let dx: i64 = (k & 1) * hx 230 let dy: i64 = ((k >> 1) & 1) * hy 231 let dz: i64 = ((k >> 2) & 1) * hz 232 let o: *i64 = sys_mmap(32) as *i64 233 project(sp, R, x0 + dx, y0 + dy, z0 + dz, o) 234 sx[k] = o[0]; sy[k] = o[1]; sd[k] = o[2]; sok[k] = o[3] 235 k = k + 1 236 } 237 draw_face(fb, zb, sx, sy, sd, sok, 2, 3, 7, 6, 0, 1, 0, col, style) 238 draw_face(fb, zb, sx, sy, sd, sok, 1, 5, 7, 3, 1, 0, 0, col, style) 239 draw_face(fb, zb, sx, sy, sd, sok, 0, 4, 6, 2, 0 - 1, 0, 0, col, style) 240 draw_face(fb, zb, sx, sy, sd, sok, 4, 5, 7, 6, 0, 0, 1, col, style) 241 draw_face(fb, zb, sx, sy, sd, sok, 0, 1, 3, 2, 0, 0, 0 - 1, col, style) 242 return 0 243} 244// sparse deterministic trees on grass columns (not water/rock/snow). 245func has_tree(sp: *i64, gx: i64, gz: i64) -> i64 { 246 let h: i64 = terrain_h(sp, gx, gz) 247 if h <= sp[SP_WATER] { return 0 } 248 if h >= sp[SP_MAXH] - 1 { return 0 } 249 var n: i64 = (gx * 131 + gz * 197 + gx * gz * 7 + 13) % 19 250 if n < 0 { n = n + 19 } 251 if n == 0 { return 1 } 252 return 0 253} 254func draw_tree(sp: *i64, fb: *i64, zb: *i64, R: *i64, gx: i64, gz: i64, style: i64) -> i64 { 255 let h: i64 = terrain_h(sp, gx, gz) 256 draw_obox(sp, fb, zb, R, gx, h, gz, 1, 3, 1, 110 + 74 * 256 + 44 * SP_MAGIC_65536, style) // trunk 257 draw_obox(sp, fb, zb, R, gx - 1, h + 3, gz - 1, 3, 2, 3, 56 + 132 * 256 + 58 * SP_MAGIC_65536, style) // canopy 258 return 0 259} 260// toon silhouette/edge outline: black where depth jumps (incl. against sky). 261func outline_pass(fb: *i64, zb: *i64) -> i64 { 262 var y: i64 = 1 263 while y < H - 1 { 264 var x: i64 = 1 265 while x < W - 1 { 266 let i: i64 = y * W + x 267 let d: i64 = zb[i] 268 if d < ZFAR { 269 var edge: i64 = 0 270 if zb[i - 1] == ZFAR { edge = 1 } 271 if zb[i + 1] == ZFAR { edge = 1 } 272 if zb[i - W] == ZFAR { edge = 1 } 273 if zb[i + W] == ZFAR { edge = 1 } 274 if iabs(d - zb[i - 1]) > 14 { edge = 1 } 275 if iabs(d - zb[i + 1]) > 14 { edge = 1 } 276 if iabs(d - zb[i - W]) > 14 { edge = 1 } 277 if iabs(d - zb[i + W]) > 14 { edge = 1 } 278 if edge == 1 { fb[i] = 0 } 279 } 280 x = x + 1 281 } 282 y = y + 1 283 } 284 return 0 285} 286func put_u32le(b: *u8, o: i64, v: i64) -> i64 { 287 b[o] = (v & 0xff) as u8 288 b[o + 1] = ((v >> 8) & 0xff) as u8 289 b[o + 2] = ((v >> 16) & 0xff) as u8 290 b[o + 3] = ((v >> 24) & 0xff) as u8 291 return 0 292} 293func write_bmp(fb: *i64, path: *u8) -> i64 { 294 let pix: i64 = W * H * 3 295 let total: i64 = 54 + pix 296 let b: *u8 = sys_mmap(total) as *u8 297 b[0] = 66 as u8 298 b[1] = 77 as u8 299 put_u32le(b, 2, total) 300 put_u32le(b, 10, 54) 301 put_u32le(b, 14, 40) 302 put_u32le(b, 18, W) 303 put_u32le(b, 22, H) 304 b[26] = 1 as u8 305 b[28] = 24 as u8 306 put_u32le(b, 34, pix) 307 var o: i64 = 54 308 var ry: i64 = 0 309 while ry < H { 310 let syrow: i64 = H - 1 - ry 311 var x: i64 = 0 312 while x < W { 313 let c: i64 = fb[syrow * W + x] 314 b[o] = ((c >> 16) & 0xff) as u8 315 b[o + 1] = ((c >> 8) & 0xff) as u8 316 b[o + 2] = (c & 0xff) as u8 317 o = o + 3 318 x = x + 1 319 } 320 ry = ry + 1 321 } 322 let fd: i64 = sys_openat_wr(path, 0x1a4) 323 if fd < 0 { return 0 - 1 } 324 sys_write(fd, b, total) 325 sys_close(fd) 326 return 0 327} 328// build the camera matrix from the spec (yaw/pitch). Shared so entities use the same camera as the terrain. 329func build_cam(sp: *i64, R: *i64) -> i64 { 330 let Ry: *i64 = sys_mmap(128) as *i64 331 let Rx: *i64 = sys_mmap(128) as *i64 332 m4_roty(fratio(sp[SP_YCOS], 1000), fratio(sp[SP_YSIN], 1000), Ry) 333 m4_rotx(fratio(sp[SP_PCOS], 1000), f32_neg(fratio(sp[SP_PSIN], 1000)), Rx) 334 m4_mul(Rx, Ry, R) 335 return 0 336} 337// expose the projector + terrain height so a caller can place entities on the terrain in the same camera. 338func world_project(sp: *i64, R: *i64, iwx: i64, iwy: i64, iwz: i64, out: *i64) -> i64 { return project(sp, R, iwx, iwy, iwz, out) } 339func world_height(sp: *i64, gx: i64, gz: i64) -> i64 { return terrain_h(sp, gx, gz) } 340func world_obox(sp: *i64, fb: *i64, zb: *i64, R: *i64, x0: i64, y0: i64, z0: i64, hx: i64, hy: i64, hz: i64, col: i64, style: i64) -> i64 { 341 return draw_obox(sp, fb, zb, R, x0, y0, z0, hx, hy, hz, col, style) 342} 343func render_world(sp: *i64, fb: *i64, zb: *i64) -> i64 { 344 let sky: i64 = world_sky() 345 var i: i64 = 0 346 while i < W * H { 347 fb[i] = sky 348 zb[i] = ZFAR 349 i = i + 1 350 } 351 let R: *i64 = sys_mmap(128) as *i64 352 build_cam(sp, R) 353 var gz: i64 = 0 354 while gz < sp[SP_N] { 355 var gx: i64 = 0 356 while gx < sp[SP_N] { 357 draw_col(sp, fb, zb, R, gx, gz) 358 if has_tree(sp, gx, gz) == 1 { draw_tree(sp, fb, zb, R, gx, gz, sp[SP_STYLE]) } 359 gx = gx + 1 360 } 361 gz = gz + 1 362 } 363 if sp[SP_STYLE] == STYLE_TOON { outline_pass(fb, zb) } 364 return 0 365}