nx_voxel_native.nx source
↩ module page · 362 lines · 13750 B
1// nx_voxel_native.nx -- PLAYABLE first-person RAYCAST voxel explorer (the 3rd game mode:
2// the operator's "minecraft stuff"). A procgen world (driven by the REAL nx_world_seed
3// terrain procgen) rendered first-person via per-column raymarch with truecolor depth
4// shading, walk + turn. Native (nx_cc->nxasm_x86, no gcc), terminal-rendered, hostable.
5//
6// HONEST LADDER PLACEMENT (operator: weak-voxel < our-minecraftclone < Minecraft < Unreal
7// procgen): this is a SOVEREIGN first-person voxel walker -- well above the static 1bpp
8// substrate demo, but far below Minecraft (no block place/break/craft/infinite-chunks/
9// lighting/save) and far below Unreal-class procgen. The procgen here = nx_world_seed
10// heightmap (a real Nishi organ); biome/feature procgen (nx_procgen_biome/features) +
11// chunked infinite world + block editing are NAMED next rungs, not claimed here.
12//
13// LAWS: <=6 args/func, flat ifs, no &&/||, integer-only. Reuses engine-lib terminal helpers.
14// license_tier: ORIGINAL
15import "nx_tier.nx"
16import "nx_syscalls.nx"
17import "nx_palette.nx"
18import "nx_tissue.nx"
19import "nx_world_seed.nx"
20import "nx_game_engine_lib.nx"
21const K_MAGIC_1092: i64 = 1092
22const K_MAGIC_1189: i64 = 1189
23const K_MAGIC_1285: i64 = 1285
24const K_MAGIC_1380: i64 = 1380
25const K_MAGIC_1474: i64 = 1474
26const K_MAGIC_1567: i64 = 1567
27const K_MAGIC_1660: i64 = 1660
28const K_MAGIC_1751: i64 = 1751
29const K_MAGIC_1842: i64 = 1842
30const K_MAGIC_1931: i64 = 1931
31const K_MAGIC_2019: i64 = 2019
32const K_MAGIC_2106: i64 = 2106
33const K_MAGIC_2191: i64 = 2191
34const K_MAGIC_2276: i64 = 2276
35const K_MAGIC_2359: i64 = 2359
36const K_MAGIC_2440: i64 = 2440
37const K_MAGIC_2520: i64 = 2520
38const K_MAGIC_2598: i64 = 2598
39const K_MAGIC_2675: i64 = 2675
40const K_MAGIC_2751: i64 = 2751
41const K_MAGIC_2824: i64 = 2824
42const K_MAGIC_2896: i64 = 2896
43const K_MAGIC_2967: i64 = 2967
44const K_MAGIC_3035: i64 = 3035
45const K_MAGIC_3102: i64 = 3102
46const K_MAGIC_3166: i64 = 3166
47const K_MAGIC_3229: i64 = 3229
48const K_MAGIC_3290: i64 = 3290
49const K_MAGIC_3349: i64 = 3349
50const K_MAGIC_3406: i64 = 3406
51const K_MAGIC_3461: i64 = 3461
52const K_MAGIC_3513: i64 = 3513
53const K_MAGIC_3564: i64 = 3564
54const K_MAGIC_3612: i64 = 3612
55const K_MAGIC_3659: i64 = 3659
56const K_MAGIC_3703: i64 = 3703
57const K_MAGIC_3745: i64 = 3745
58const K_MAGIC_3784: i64 = 3784
59const K_MAGIC_3822: i64 = 3822
60const K_MAGIC_3857: i64 = 3857
61const K_MAGIC_3889: i64 = 3889
62const K_MAGIC_3920: i64 = 3920
63const K_MAGIC_3948: i64 = 3948
64const K_MAGIC_3973: i64 = 3973
65const K_MAGIC_3996: i64 = 3996
66const K_MAGIC_4017: i64 = 4017
67const K_MAGIC_4036: i64 = 4036
68const K_MAGIC_4052: i64 = 4052
69const K_MAGIC_4065: i64 = 4065
70const K_MAGIC_4076: i64 = 4076
71const K_MAGIC_4085: i64 = 4085
72const K_MAGIC_4091: i64 = 4091
73const K_MAGIC_4095: i64 = 4095
74const K_MAGIC_4096: i64 = 4096
75const K_MAGIC_2048: i64 = 2048
76const K_MAGIC_12345: i64 = 12345
77const K_MAGIC_8192: i64 = 8192
78const K_MAGIC_131072: i64 = 131072
79const K_MAGIC_1024: i64 = 1024
80
81// state st[]: 0=px(q8) 1=py(q8) 2=ang(0..255) 3=rng ; map 24x24 u8 at st+2048 ; qtab 65 i64 at st+4096
82
83func vx_qfill(q: *i64) -> i64 {
84 q[0]=0; q[1]=101; q[2]=201; q[3]=301; q[4]=401; q[5]=501; q[6]=601; q[7]=700; q[8]=799; q[9]=897; q[10]=995; q[11]=K_MAGIC_1092; q[12]=K_MAGIC_1189
85 q[13]=K_MAGIC_1285; q[14]=K_MAGIC_1380; q[15]=K_MAGIC_1474; q[16]=K_MAGIC_1567; q[17]=K_MAGIC_1660; q[18]=K_MAGIC_1751; q[19]=K_MAGIC_1842; q[20]=K_MAGIC_1931; q[21]=K_MAGIC_2019; q[22]=K_MAGIC_2106; q[23]=K_MAGIC_2191; q[24]=K_MAGIC_2276; q[25]=K_MAGIC_2359
86 q[26]=K_MAGIC_2440; q[27]=K_MAGIC_2520; q[28]=K_MAGIC_2598; q[29]=K_MAGIC_2675; q[30]=K_MAGIC_2751; q[31]=K_MAGIC_2824; q[32]=K_MAGIC_2896; q[33]=K_MAGIC_2967; q[34]=K_MAGIC_3035; q[35]=K_MAGIC_3102; q[36]=K_MAGIC_3166; q[37]=K_MAGIC_3229; q[38]=K_MAGIC_3290
87 q[39]=K_MAGIC_3349; q[40]=K_MAGIC_3406; q[41]=K_MAGIC_3461; q[42]=K_MAGIC_3513; q[43]=K_MAGIC_3564; q[44]=K_MAGIC_3612; q[45]=K_MAGIC_3659; q[46]=K_MAGIC_3703; q[47]=K_MAGIC_3745; q[48]=K_MAGIC_3784; q[49]=K_MAGIC_3822; q[50]=K_MAGIC_3857; q[51]=K_MAGIC_3889
88 q[52]=K_MAGIC_3920; q[53]=K_MAGIC_3948; q[54]=K_MAGIC_3973; q[55]=K_MAGIC_3996; q[56]=K_MAGIC_4017; q[57]=K_MAGIC_4036; q[58]=K_MAGIC_4052; q[59]=K_MAGIC_4065; q[60]=K_MAGIC_4076; q[61]=K_MAGIC_4085; q[62]=K_MAGIC_4091; q[63]=K_MAGIC_4095; q[64]=K_MAGIC_4096
89 return 0
90}
91func vx_qtab(st: *i64) -> *i64 { return ((st as i64) + K_MAGIC_4096) as *i64 }
92func vx_sin(q: *i64, a0: i64) -> i64 {
93 let a: i64 = a0 & 255
94 let quad: i64 = a >> 6
95 let idx: i64 = a & 63
96 if quad == 0 { return q[idx] }
97 if quad == 1 { return q[64 - idx] }
98 if quad == 2 { return 0 - q[idx] }
99 return 0 - q[64 - idx]
100}
101func vx_cos(q: *i64, a: i64) -> i64 { return vx_sin(q, a + 64) }
102
103func vx_cell(st: *i64, x: i64, y: i64) -> i64 {
104 if x < 0 { return 1 }
105 if y < 0 { return 1 }
106 if x >= 24 { return 1 }
107 if y >= 24 { return 1 }
108 let m: *u8 = ((st as i64) + K_MAGIC_2048) as *u8
109 return m[y * 24 + x] as i64
110}
111func vx_set(st: *i64, x: i64, y: i64, v: i64) -> i64 {
112 let m: *u8 = ((st as i64) + K_MAGIC_2048) as *u8
113 m[y * 24 + x] = v as u8
114 return 0
115}
116
117func vx_build(st: *i64, seed: i64) -> i64 {
118 st[3] = seed
119 let world: *NxTissue = nx_tissue_new(24, 24, 8, NX_BPP_3)
120 nx_world_seed_generate(world, seed)
121 var y: i64 = 0
122 while y < 24 {
123 var x: i64 = 0
124 while x < 24 {
125 var top: i64 = 0 - 1
126 var surf: i64 = 1
127 var z: i64 = 7
128 while z >= 0 {
129 if top < 0 {
130 let b: i64 = nx_tissue_get(world, x, y, z)
131 if b != 0 { top = z; surf = b }
132 }
133 z = z - 1
134 }
135 var border: i64 = 0
136 if x == 0 { border = 1 }
137 if y == 0 { border = 1 }
138 if x == 23 { border = 1 }
139 if y == 23 { border = 1 }
140 var solid: i64 = 0
141 if border == 1 { solid = 1; surf = 1 }
142 if border == 0 { if top >= 5 { solid = 1 } }
143 if solid == 1 { vx_set(st, x, y, surf) } else { vx_set(st, x, y, 0) }
144 x = x + 1
145 }
146 y = y + 1
147 }
148 // clear a spawn pocket at center
149 vx_set(st, 12, 12, 0); vx_set(st, 12, 11, 0); vx_set(st, 11, 12, 0)
150 vx_set(st, 13, 12, 0); vx_set(st, 12, 13, 0); vx_set(st, 11, 11, 0); vx_set(st, 13, 13, 0)
151 st[0] = 12 * 256 + 128
152 st[1] = 12 * 256 + 128
153 st[2] = 0
154 return 0
155}
156func vx_init(st: *i64, seed: i64) -> i64 {
157 vx_qfill(vx_qtab(st))
158 vx_build(st, seed)
159 st[5] = 3
160 return 0
161}
162
163// look: march the CENTER ray; record the first solid cell hit (st[6],st[7]) and the last
164// air cell before it (st[8],st[9]) for placing. Short reach (~3 cells) like Minecraft.
165// st[10] = 1 if something is in reach.
166func vx_look(st: *i64, q: *i64) -> i64 {
167 let dx: i64 = vx_cos(q, st[2])
168 let dy: i64 = vx_sin(q, st[2])
169 var pcx: i64 = st[0] >> 8
170 var pcy: i64 = st[1] >> 8
171 st[10] = 0
172 var s: i64 = 2
173 while s < 36 {
174 let d: i64 = s * 24
175 let cx: i64 = (st[0] + ((dx * d) >> 12)) >> 8
176 let cy: i64 = (st[1] + ((dy * d) >> 12)) >> 8
177 let c: i64 = vx_cell(st, cx, cy)
178 if c != 0 { st[6] = cx; st[7] = cy; st[8] = pcx; st[9] = pcy; st[10] = 1; s = 900 }
179 if c == 0 { pcx = cx; pcy = cy }
180 s = s + 1
181 }
182 return 0
183}
184func vx_break(st: *i64) -> i64 {
185 if st[10] == 1 { vx_set(st, st[6], st[7], 0) }
186 return 0
187}
188func vx_place(st: *i64) -> i64 {
189 if st[10] == 1 {
190 var ok: i64 = 1
191 if st[8] == (st[0] >> 8) { if st[9] == (st[1] >> 8) { ok = 0 } }
192 if vx_cell(st, st[8], st[9]) != 0 { ok = 0 }
193 if ok == 1 { vx_set(st, st[8], st[9], st[5]) }
194 }
195 return 0
196}
197func vx_blockname(st: *i64, buf: *u8, off: *i64) -> i64 {
198 if st[5] == 1 { bput(buf, off, "STONE" as *u8) }
199 if st[5] == 2 { bput(buf, off, "DIRT" as *u8) }
200 if st[5] == 3 { bput(buf, off, "GRASS" as *u8) }
201 if st[5] == 4 { bput(buf, off, "WATER" as *u8) }
202 return 0
203}
204
205func vx_move(st: *i64, q: *i64, fwd: i64) -> i64 {
206 let dx: i64 = vx_cos(q, st[2])
207 let dy: i64 = vx_sin(q, st[2])
208 let nx: i64 = st[0] + ((dx * 56 * fwd) >> 12)
209 let ny: i64 = st[1] + ((dy * 56 * fwd) >> 12)
210 if vx_cell(st, nx >> 8, st[1] >> 8) == 0 { st[0] = nx }
211 if vx_cell(st, st[0] >> 8, ny >> 8) == 0 { st[1] = ny }
212 return 0
213}
214func vx_turn(st: *i64, d: i64) -> i64 { st[2] = (st[2] + d) & 255; return 0 }
215
216func vx_basecol(cell: i64) -> i64 {
217 if cell == 1 { return (130 << 16) | (130 << 8) | 145 }
218 if cell == 2 { return (150 << 16) | (104 << 8) | 60 }
219 if cell == 3 { return (70 << 16) | (180 << 8) | 80 }
220 if cell == 4 { return (60 << 16) | (110 << 8) | 200 }
221 return (120 << 16) | (120 << 8) | 120
222}
223func vx_shade(rgb: i64, f: i64) -> i64 {
224 let r: i64 = (((rgb >> 16) & 255) * f) / 255
225 let g: i64 = (((rgb >> 8) & 255) * f) / 255
226 let b: i64 = ((rgb & 255) * f) / 255
227 return (r << 16) | (g << 8) | b
228}
229
230// cast one column: returns packed (top<<20)|(bot<<12)| nothing; we instead fill via out arrays.
231func vx_cast_col(st: *i64, q: *i64, c: i64, ctop: *i64, cbot: *i64, ccol: *i64) -> i64 {
232 let off_ang: i64 = c - 32
233 let rayang: i64 = st[2] + off_ang
234 let dx: i64 = vx_cos(q, rayang)
235 let dy: i64 = vx_sin(q, rayang)
236 var s: i64 = 1
237 var hit: i64 = 0
238 var cell: i64 = 0
239 var dq8: i64 = 0
240 while s < 170 {
241 dq8 = s * 24
242 let wx: i64 = st[0] + ((dx * dq8) >> 12)
243 let wy: i64 = st[1] + ((dy * dq8) >> 12)
244 cell = vx_cell(st, wx >> 8, wy >> 8)
245 if cell != 0 { hit = 1; s = 999 }
246 if cell == 0 { s = s + 1 }
247 }
248 let perp: i64 = (dq8 * vx_cos(q, off_ang)) >> 12
249 var dcells: i64 = perp >> 8
250 if dcells < 1 { dcells = 1 }
251 var h: i64 = (28 * 7) / dcells
252 if h > 28 { h = 28 }
253 if hit == 0 { h = 0 }
254 let top: i64 = (28 - h) / 2
255 ctop[c] = top
256 cbot[c] = top + h
257 var f: i64 = 255 - dcells * 11
258 if f < 45 { f = 45 }
259 ccol[c] = vx_shade(vx_basecol(cell), f)
260 return 0
261}
262
263func vx_render(buf: *u8, st: *i64, ctop: *i64, cbot: *i64, ccol: *i64) -> i64 {
264 let q: *i64 = vx_qtab(st)
265 let off: *i64 = sys_mmap(16) as *i64
266 off[0] = 0
267 bput_clear(buf, off)
268 bput(buf, off, " NISHI VOXEL first-person raycast over nx_world_seed procgen facing " as *u8)
269 bputn(buf, off, st[2]); bputc(buf, off, 10); bputc(buf, off, 10)
270 var c: i64 = 0
271 while c < 64 { vx_cast_col(st, q, c, ctop, cbot, ccol); c = c + 1 }
272 var r: i64 = 0
273 while r < 28 {
274 var last: i64 = 0 - 1
275 bput(buf, off, " " as *u8)
276 var x: i64 = 0
277 while x < 64 {
278 var rgb: i64 = 0
279 if r < ctop[x] { rgb = (50 << 16) | (72 << 8) | 110 }
280 if r >= ctop[x] { if r < cbot[x] { rgb = ccol[x] } }
281 if r >= cbot[x] { rgb = (34 << 16) | (30 << 8) | 28 }
282 if rgb != last {
283 bput_bg(buf, off, (rgb >> 16) & 255, (rgb >> 8) & 255, rgb & 255)
284 last = rgb
285 }
286 var ch: i64 = 32
287 if r == 14 { if x == 32 { ch = 43 } }
288 if ch == 43 { bput_fg(buf, off, 255, 255, 255); bputc(buf, off, 43); last = 0 - 1 } else { bputc(buf, off, 32) }
289 x = x + 1
290 }
291 bput_reset(buf, off); bputc(buf, off, 10)
292 r = r + 1
293 }
294 bputc(buf, off, 10)
295 bput(buf, off, " w/s walk a/d turn f BREAK g PLACE block at crosshair 1-4 select r regen q quit" as *u8); bputc(buf, off, 10)
296 bput(buf, off, " selected block: " as *u8); vx_blockname(st, buf, off)
297 bput(buf, off, " (+ = aim; break/place a block, then walk through the hole you dug)" as *u8); bputc(buf, off, 10)
298 sys_write(1, buf, off[0])
299 return 0
300}
301
302func vx_input(st: *i64, key: i64) -> i64 {
303 let q: *i64 = vx_qtab(st)
304 if key == 0 { vx_move(st, q, 1) }
305 if key == 1 { vx_move(st, q, 0 - 1) }
306 if key == 2 { vx_turn(st, 0 - 6) }
307 if key == 3 { vx_turn(st, 6) }
308 if key == 7 { vx_look(st, q); vx_break(st) }
309 if key == 8 { vx_look(st, q); vx_place(st) }
310 if key == 10 { st[5] = 1 }
311 if key == 11 { st[5] = 2 }
312 if key == 12 { st[5] = 3 }
313 if key == 13 { st[5] = 4 }
314 if key == 6 { vx_build(st, st[3] + K_MAGIC_12345); st[5] = 3 }
315 return 0
316}
317func vx_key(c: i64) -> i64 {
318 if c == 119 { return 0 }
319 if c == 115 { return 1 }
320 if c == 97 { return 2 }
321 if c == 100 { return 3 }
322 if c == 102 { return 7 }
323 if c == 103 { return 8 }
324 if c == 49 { return 10 }
325 if c == 50 { return 11 }
326 if c == 51 { return 12 }
327 if c == 52 { return 13 }
328 if c == 114 { return 6 }
329 return 9
330}
331
332func main() -> i64 {
333 let st: *i64 = sys_mmap(K_MAGIC_8192) as *i64
334 let orig: *u8 = sys_mmap(128)
335 let raw: *u8 = sys_mmap(128)
336 let is_tty: i64 = term_raw_on(orig, raw)
337 vx_init(st, sys_now_ms())
338 let buf: *u8 = sys_mmap(K_MAGIC_131072)
339 let ib: *u8 = sys_mmap(8)
340 let ctop: *i64 = sys_mmap(K_MAGIC_1024) as *i64
341 let cbot: *i64 = sys_mmap(K_MAGIC_1024) as *i64
342 let ccol: *i64 = sys_mmap(K_MAGIC_1024) as *i64
343 var running: i64 = 1
344 while running == 1 {
345 vx_render(buf, st, ctop, cbot, ccol)
346 let n: i64 = sys_read(0, ib, 1)
347 if n <= 0 { running = 0 }
348 if n > 0 {
349 let c: i64 = ib[0] as i64
350 if c == 113 { running = 0 }
351 if c == 81 { running = 0 }
352 if c != 113 { if c != 81 { vx_input(st, vx_key(c)) } }
353 }
354 }
355 if is_tty == 1 { term_raw_off(orig) }
356 let off: *i64 = sys_mmap(16) as *i64
357 off[0] = 0
358 bput_clear(buf, off); bput(buf, off, "thanks for exploring -- NISHI VOXEL\n" as *u8)
359 sys_write(1, buf, off[0])
360 sys_exit(0)
361 return 0
362}