nx_world_seed.nx source
↩ module page · 132 lines · 4265 B
1// nx_world_seed.nx -- deterministic worldgen for voxel tissue.
2//
3// xorshift64 PRNG + procedural worldgen. Same seed = same world,
4// byte-for-byte. Foundation for Minecraft-equivalent procedural
5// terrain on T1/MCU — no float, no allocation, MCU-compatible.
6//
7// V1 generates a simple terrain:
8// z=0..2 : stone (palette idx 1)
9// z=3..h : dirt (idx 2) where h is per-column Q14 noise
10// z=h+1..z_max : air (idx 0)
11//
12// The "Q14 noise" is a cheap pseudo-Perlin: hash (x, y, seed) into
13// a height in [3, z_max). Not smooth, but deterministic + repeatable.
14//
15// Composes:
16// nx_tissue -- the world being filled
17// nx_palette -- voxel indices (0=air, 1=stone, 2=dirt, 3=grass)
18// nx_q14_math -- sin lookup for cheap noise
19
20import "nx_syscalls.nx"
21import "nx_tier.nx"
22import "nx_tissue.nx"
23import "nx_palette.nx"
24const NX_MAGIC_73856093: i64 = 73856093
25const NX_MAGIC_19349663: i64 = 19349663
26
27const NX_WS_PAL_AIR: nx_int = 0
28const NX_WS_PAL_STONE: nx_int = 1
29const NX_WS_PAL_DIRT: nx_int = 2
30const NX_WS_PAL_GRASS: nx_int = 3
31const NX_WS_PAL_WATER: nx_int = 4
32
33const NX_WS_OK: nx_int = 0
34const NX_WS_ERR_BAD_TISSUE: nx_int = 1
35
36// ===== Struct: NxWorldSeedState ===================================
37
38struct NxWorldSeedState {
39 seed: nx_size,
40 cursor: nx_size, // current PRNG state
41 cells_written: nx_int,
42}
43
44// ===== nx_xorshift64 ================================================
45//
46// xorshift64* PRNG. Pure function: state in, state out. Deterministic
47// across hosts. Period ~2^64.
48
49func nx_xorshift64(state: nx_size) -> nx_size {
50 var x: nx_size = state
51 if x == 0 { x = 1 } // 0 is a fixed point; avoid
52 x = x ^ (x << 13)
53 x = x & 0xFFFFFFFFFFFFFFFF
54 x = x ^ ((x >> 7) & 0x01FFFFFFFFFFFFFF)
55 x = x ^ (x << 17)
56 x = x & 0xFFFFFFFFFFFFFFFF
57 return x
58}
59
60func nx_world_seed_state_new(seed: nx_size) -> *NxWorldSeedState {
61 let s: *NxWorldSeedState = (sys_mmap(24)) as *NxWorldSeedState
62 s.seed = seed
63 s.cursor = seed
64 if s.cursor == 0 { s.cursor = 1 }
65 s.cells_written = 0
66 return s
67}
68
69func nx_world_seed_next(s: *NxWorldSeedState) -> nx_size {
70 s.cursor = nx_xorshift64(s.cursor)
71 return s.cursor
72}
73
74// ===== _height_at ==================================================
75//
76// Pseudo-noise height function. Hash (x, y, seed) into a height in
77// [min_h, max_h). Not smooth (no interpolation between cells) but
78// deterministic + repeatable.
79
80func _height_at(seed: nx_size, x: nx_size, y: nx_size,
81 min_h: nx_size, max_h: nx_size) -> nx_size {
82 let mixed: nx_size = nx_xorshift64(seed + x * NX_MAGIC_73856093 + y * NX_MAGIC_19349663)
83 let range: nx_size = max_h - min_h
84 return min_h + (mixed - (mixed / range) * range)
85}
86
87// ===== nx_world_seed_generate =====================================
88//
89// Fill the supplied tissue with terrain. Per-column height varies
90// via the noise function. Bottom NX_WS_PAL_STONE; column-top
91// NX_WS_PAL_GRASS; between NX_WS_PAL_DIRT; above air.
92
93func nx_world_seed_generate(world: *NxTissue, seed: nx_size) -> nx_int {
94 if (world as i64) == 0 { return NX_WS_ERR_BAD_TISSUE }
95 let min_h: nx_size = 3
96 var max_h: nx_size = world.dim_z - 2
97 if max_h < min_h + 1 { max_h = min_h + 1 }
98
99 var cells: nx_int = 0
100 var y: nx_size = 0
101 while y < world.dim_y {
102 var x: nx_size = 0
103 while x < world.dim_x {
104 let h: nx_size = _height_at(seed, x, y, min_h, max_h)
105 // Stone floor at z=0..2
106 var z: nx_size = 0
107 while z < 3 {
108 if z < world.dim_z {
109 nx_tissue_set(world, x, y, z, NX_WS_PAL_STONE)
110 cells = cells + 1
111 }
112 z = z + 1
113 }
114 // Dirt 3..h-1
115 while z < h {
116 if z < world.dim_z {
117 nx_tissue_set(world, x, y, z, NX_WS_PAL_DIRT)
118 cells = cells + 1
119 }
120 z = z + 1
121 }
122 // Grass top
123 if h < world.dim_z {
124 nx_tissue_set(world, x, y, h, NX_WS_PAL_GRASS)
125 cells = cells + 1
126 }
127 x = x + 1
128 }
129 y = y + 1
130 }
131 return cells
132}