@plasius/gpu-world-generator 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +73 -0
  3. package/dist/field.wgsl +225 -0
  4. package/dist/fractal-prepass.wgsl +290 -0
  5. package/dist/index.cjs +1942 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.cts +447 -0
  8. package/dist/index.d.ts +447 -0
  9. package/dist/index.js +1848 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/terrain.wgsl +451 -0
  12. package/docs/adrs/adr-0001-package-scope.md +18 -0
  13. package/docs/adrs/adr-0002-world-tiling-lod-stitching.md +28 -0
  14. package/docs/adrs/adr-0003-terrain-generation-style-mixing.md +21 -0
  15. package/docs/adrs/index.md +5 -0
  16. package/docs/biomes.md +206 -0
  17. package/docs/lod-zoning.md +22 -0
  18. package/docs/plan.md +107 -0
  19. package/docs/procedural-surface.md +73 -0
  20. package/docs/resources.md +55 -0
  21. package/package.json +53 -0
  22. package/src/biomes/temperate.ts +387 -0
  23. package/src/field.wgsl +225 -0
  24. package/src/fields.ts +321 -0
  25. package/src/fractal-prepass.ts +237 -0
  26. package/src/fractal-prepass.wgsl +290 -0
  27. package/src/generator.ts +106 -0
  28. package/src/hex.ts +54 -0
  29. package/src/index.ts +11 -0
  30. package/src/mesh.ts +285 -0
  31. package/src/perf-monitor.ts +133 -0
  32. package/src/shaders/demo-terrain.wgsl +193 -0
  33. package/src/shaders/demo-trees.wgsl +172 -0
  34. package/src/shaders/demo-water-sim.wgsl +361 -0
  35. package/src/shaders/library/common.wgsl +38 -0
  36. package/src/shaders/library/materials.wgsl +175 -0
  37. package/src/terrain.wgsl +451 -0
  38. package/src/tile-cache.ts +274 -0
  39. package/src/tiles.ts +417 -0
  40. package/src/types.ts +220 -0
  41. package/src/wgsl/field.job.wgsl +225 -0
  42. package/src/wgsl/terrain.job.wgsl +451 -0
  43. package/src/wgsl.ts +77 -0
@@ -0,0 +1,290 @@
1
+ struct FractalParams {
2
+ grid: vec4<f32>, // grid_points, extent, step, seed
3
+ field0: vec4<f32>, // scale, warp_scale, warp_strength, power
4
+ field1: vec4<f32>, // detail_scale, detail_power, ridge_power, heat_bias
5
+ field2: vec4<f32>, // moisture_bias, mandel_scale, mandel_strength, mandel_rock_boost
6
+ field3: vec4<f32>, // iterations, detail_iterations, macro_scale, macro_warp_strength
7
+ field4: vec4<f32>, // style_mix_strength, terrace_steps, terrace_strength, crater_strength
8
+ field5: vec4<f32>, // crater_scale, height_min, height_max, pad
9
+ };
10
+
11
+ struct Sample {
12
+ data0: vec4<f32>, // height, heat, moisture, rockiness
13
+ data1: vec4<f32>, // water, ridge, base, detail
14
+ };
15
+
16
+ @group(0) @binding(0) var<uniform> params: FractalParams;
17
+ @group(0) @binding(1) var<storage, read_write> samples_out: array<Sample>;
18
+ @group(0) @binding(2) var<storage, read> samples_in: array<Sample>;
19
+
20
+ fn hash32(x: u32) -> u32 {
21
+ var v = x;
22
+ v ^= v >> 17u;
23
+ v *= 0xed5ad4bbu;
24
+ v ^= v >> 11u;
25
+ v *= 0xac4c1b51u;
26
+ v ^= v >> 15u;
27
+ v *= 0x31848babu;
28
+ v ^= v >> 14u;
29
+ return v;
30
+ }
31
+
32
+ fn hash01(x: u32) -> f32 {
33
+ let v = hash32(x) & 0x00ffffffu;
34
+ return f32(v) / 16777216.0;
35
+ }
36
+
37
+ fn smooth_mandelbrot(cx: f32, cy: f32, iterations: u32, power: f32) -> f32 {
38
+ var zx = 0.0;
39
+ var zy = 0.0;
40
+ var i: u32 = 0u;
41
+ loop {
42
+ if (i >= iterations) {
43
+ break;
44
+ }
45
+ let r2 = zx * zx + zy * zy;
46
+ if (r2 > 4.0) {
47
+ break;
48
+ }
49
+ let r = sqrt(r2);
50
+ let theta = atan2(zy, zx);
51
+ let rp = pow(r, power);
52
+ zx = rp * cos(theta * power) + cx;
53
+ zy = rp * sin(theta * power) + cy;
54
+ i = i + 1u;
55
+ }
56
+ if (i >= iterations) {
57
+ return 1.0;
58
+ }
59
+ let r = max(sqrt(zx * zx + zy * zy), 1e-6);
60
+ let nu = log2(log(r));
61
+ let smoothVal = (f32(i) + 1.0 - nu) / f32(iterations);
62
+ return clamp(smoothVal, 0.0, 1.0);
63
+ }
64
+
65
+ fn terrace_height(h: f32, steps: f32) -> f32 {
66
+ let count = max(1.0, steps);
67
+ let step = 1.0 / count;
68
+ let clamped = clamp(h, 0.0, 1.0);
69
+ let band = floor(clamped / step);
70
+ let t = fract(clamped / step);
71
+ let blend = t * t * (3.0 - 2.0 * t);
72
+ return (band + blend) * step;
73
+ }
74
+
75
+ fn crater_field(p: vec2<f32>, scale: f32, seed: u32) -> f32 {
76
+ let sp = p * scale;
77
+ let cell = floor(sp);
78
+ let local = sp - cell;
79
+ let cx = i32(cell.x);
80
+ let cz = i32(cell.y);
81
+ let hx = u32(bitcast<u32>(cx));
82
+ let hz = u32(bitcast<u32>(cz));
83
+ let h0 = hash01((hx * 374761393u) ^ (hz * 668265263u) ^ seed ^ 0x9e3779b9u);
84
+ let h1 = hash01((hx * 2246822519u) ^ (hz * 3266489917u) ^ seed ^ 0x85ebca6bu);
85
+ let h2 = hash01((hx * 1597334677u) ^ (hz * 3812015801u) ^ seed ^ 0xc2b2ae35u);
86
+ let center = vec2<f32>(h0, h1);
87
+ let radius = 0.22 + 0.25 * h2;
88
+ let dist = distance(local, center);
89
+ return smoothstep(radius, radius * 0.35, dist);
90
+ }
91
+
92
+ fn macro_map(p: vec2<f32>, iterations: u32, power: f32, scale: f32, warp_strength: f32, mix_strength: f32) -> f32 {
93
+ let macro_iter = max(12u, iterations / 3u);
94
+ let macroA = smooth_mandelbrot(p.x * scale, p.y * scale, macro_iter, power);
95
+ let macroB = smooth_mandelbrot((p.x + 13.7) * scale, (p.y - 9.2) * scale, macro_iter, power + 0.35);
96
+ let warp = vec2<f32>(macroA - 0.5, macroB - 0.5) * warp_strength;
97
+ let macroC = smooth_mandelbrot((p.x + warp.x) * scale, (p.y + warp.y) * scale, macro_iter, power);
98
+ return clamp((macroC - 0.5) * mix_strength + 0.5, 0.0, 1.0);
99
+ }
100
+
101
+ fn sample_field(p: vec2<f32>) -> Sample {
102
+ let seed = u32(params.grid.w);
103
+ let iterations = u32(params.field3.x);
104
+ let detail_iterations = u32(params.field3.y);
105
+ let macro_scale = params.field3.z;
106
+ let macro_warp_strength = params.field3.w;
107
+ let style_mix_strength = params.field4.x;
108
+ let terrace_steps = params.field4.y;
109
+ let terrace_strength = params.field4.z;
110
+ let crater_strength = params.field4.w;
111
+ let crater_scale = params.field5.x;
112
+ let height_min = params.field5.y;
113
+ let height_max = params.field5.z;
114
+ let scale = params.field0.x;
115
+ let warp_scale = params.field0.y;
116
+ let warp_strength = params.field0.z;
117
+ let power = params.field0.w;
118
+ let detail_scale = params.field1.x;
119
+ let detail_power = params.field1.y;
120
+ let ridge_power = params.field1.z;
121
+ let heat_bias = params.field1.w;
122
+ let moisture_bias = params.field2.x;
123
+
124
+ let offX = hash01(seed ^ 0x7f4a7c15u) * 4.0 - 2.0;
125
+ let offZ = hash01(seed ^ 0xa9d84d2bu) * 4.0 - 2.0;
126
+ let warpOffX = hash01(seed ^ 0x8c2f1d3bu) * 6.0 - 3.0;
127
+ let warpOffZ = hash01(seed ^ 0x5d2c79e9u) * 6.0 - 3.0;
128
+
129
+ let warpIter = max(16u, iterations * 6u / 10u);
130
+ let warpA = smooth_mandelbrot(
131
+ (p.x + warpOffX) * warp_scale,
132
+ (p.y + warpOffZ) * warp_scale,
133
+ warpIter,
134
+ power
135
+ );
136
+ let warpB = smooth_mandelbrot(
137
+ (p.x - warpOffZ) * warp_scale,
138
+ (p.y + warpOffX) * warp_scale,
139
+ warpIter,
140
+ power
141
+ );
142
+
143
+ let warped = vec2<f32>(
144
+ p.x + (warpA - 0.5) * warp_strength,
145
+ p.y + (warpB - 0.5) * warp_strength
146
+ );
147
+
148
+ let base = smooth_mandelbrot(
149
+ warped.x * scale + offX,
150
+ warped.y * scale + offZ,
151
+ iterations,
152
+ power
153
+ );
154
+ let mid = smooth_mandelbrot(
155
+ warped.x * scale * 2.15 + offX * 0.6,
156
+ warped.y * scale * 2.15 + offZ * 0.6,
157
+ max(18u, iterations * 7u / 10u),
158
+ power + 0.2
159
+ );
160
+ let detail = smooth_mandelbrot(
161
+ warped.x * scale * detail_scale + offX * 1.4,
162
+ warped.y * scale * detail_scale + offZ * 1.4,
163
+ detail_iterations,
164
+ detail_power
165
+ );
166
+
167
+ let ridge = 1.0 - abs(2.0 * mid - 1.0);
168
+ let baseHeight = pow(base, 0.9) * pow(mid, 1.05) * pow(detail, 1.1);
169
+
170
+ let styleMask = macro_map(warped, iterations, power, macro_scale, macro_warp_strength, style_mix_strength);
171
+ let terrace = terrace_height(baseHeight, terrace_steps);
172
+ let crater = crater_field(warped, crater_scale, seed);
173
+
174
+ let styleA = clamp(pow(baseHeight, 0.8) + pow(ridge, 1.4) * 0.2, 0.0, 1.0);
175
+ let styleB = clamp(
176
+ baseHeight * (1.0 - terrace_strength) +
177
+ terrace * terrace_strength -
178
+ crater * crater_strength +
179
+ pow(ridge, 1.6) * 0.12,
180
+ 0.0,
181
+ 1.0
182
+ );
183
+ let mixed = mix(styleA, styleB, styleMask);
184
+ let ridgeBoost = pow(ridge, 1.35) * 0.22;
185
+ let centered = (mixed - 0.5) * 2.0;
186
+ let shaped = sign(centered) * pow(abs(centered), 0.75);
187
+ let macroOffset = (styleMask - 0.5) * 0.25;
188
+ let rawHeight = clamp(0.5 + shaped * 0.8 + macroOffset + ridgeBoost, height_min, height_max);
189
+ let height01 = clamp(rawHeight, 0.0, 1.0);
190
+
191
+ let roughness = clamp(pow(ridge, ridge_power) * 0.7 + detail * 0.3, 0.0, 1.0);
192
+ let heat = clamp(0.55 * mid + 0.35 * (1.0 - height01) + heat_bias, 0.0, 1.0);
193
+ let moisture = clamp(
194
+ 0.55 * detail + 0.35 * (1.0 - height01) - (heat - 0.5) * 0.1 + moisture_bias,
195
+ 0.0,
196
+ 1.0
197
+ );
198
+ let rockiness = clamp(roughness * 0.6 + height01 * 0.4, 0.0, 1.0);
199
+ let water = clamp((0.32 - height01) * 3.0 + (moisture - 0.5) * 0.2, 0.0, 1.0);
200
+
201
+ return Sample(
202
+ vec4<f32>(rawHeight, heat, moisture, rockiness),
203
+ vec4<f32>(water, ridge, base, detail)
204
+ );
205
+ }
206
+
207
+ @compute @workgroup_size(8, 8)
208
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
209
+ let grid_points = u32(params.grid.x);
210
+ if (gid.x >= grid_points || gid.y >= grid_points) {
211
+ return;
212
+ }
213
+ let extent = params.grid.y;
214
+ let step = params.grid.z;
215
+ let x = -extent + f32(gid.x) * step;
216
+ let z = -extent + f32(gid.y) * step;
217
+
218
+ var sample = sample_field(vec2<f32>(x, z));
219
+
220
+ let mandel_scale = params.field2.y;
221
+ let mandel_strength = params.field2.z;
222
+ let mandel_rock = params.field2.w;
223
+ let height_min = params.field5.y;
224
+ let height_max = params.field5.z;
225
+ let mandel = smooth_mandelbrot(x * mandel_scale, z * mandel_scale, 24u, 2.0);
226
+ let mandel_bias = (mandel - 0.5) * mandel_strength;
227
+ let rawHeight = clamp(sample.data0.x + mandel_bias, height_min, height_max);
228
+ let height01 = clamp(rawHeight, 0.0, 1.0);
229
+ let rockiness = clamp(sample.data0.w + max(0.0, mandel - 0.55) * mandel_rock, 0.0, 1.0);
230
+ let water = clamp((0.32 - height01) * 3.0 + (sample.data0.z - 0.5) * 0.2, 0.0, 1.0);
231
+
232
+ sample.data0.x = rawHeight;
233
+ sample.data0.w = rockiness;
234
+ sample.data1.x = water;
235
+
236
+ let index = gid.y * grid_points + gid.x;
237
+ samples_out[index] = sample;
238
+ }
239
+
240
+ fn height_at(ix: i32, iy: i32, size: i32) -> f32 {
241
+ let x = clamp(ix, 0, size - 1);
242
+ let y = clamp(iy, 0, size - 1);
243
+ let idx = y * size + x;
244
+ return samples_in[idx].data0.x;
245
+ }
246
+
247
+ @compute @workgroup_size(8, 8)
248
+ fn accent_heights(@builtin(global_invocation_id) gid: vec3<u32>) {
249
+ let grid_points = i32(params.grid.x);
250
+ let ix = i32(gid.x);
251
+ let iy = i32(gid.y);
252
+ if (ix >= grid_points || iy >= grid_points) {
253
+ return;
254
+ }
255
+ let idx = iy * grid_points + ix;
256
+ let center = samples_in[idx];
257
+ var sum = 0.0;
258
+ var count = 0.0;
259
+ for (var dx = -1; dx <= 1; dx = dx + 1) {
260
+ for (var dy = -1; dy <= 1; dy = dy + 1) {
261
+ if (dx == 0 && dy == 0) {
262
+ continue;
263
+ }
264
+ sum = sum + height_at(ix + dx, iy + dy, grid_points);
265
+ count = count + 1.0;
266
+ }
267
+ }
268
+ let avg = select(center.data0.x, sum / count, count > 0.0);
269
+ let delta = center.data0.x - avg;
270
+ let signed = sign(delta) * pow(abs(delta), 0.8);
271
+ let ridge = max(0.0, delta);
272
+ let accentStrength = 1.6;
273
+ let ridgeStrength = 0.35;
274
+ let macroStrength = 0.25;
275
+ let minHeight = params.field5.y;
276
+ let maxHeight = params.field5.z;
277
+ let macroOffset = (avg - 0.5) * macroStrength;
278
+ let rawHeight = clamp(
279
+ avg + signed * accentStrength + ridge * ridgeStrength + macroOffset,
280
+ minHeight,
281
+ maxHeight
282
+ );
283
+ let clampedHeight = clamp(rawHeight, 0.0, 1.0);
284
+
285
+ var out = center;
286
+ out.data0.x = rawHeight;
287
+ out.data0.w = clamp(out.data0.w + max(0.0, clampedHeight - 0.65) * 0.5, 0.0, 1.0);
288
+ out.data1.x = clamp((0.32 - clampedHeight) * 3.0 + (out.data0.z - 0.5) * 0.2, 0.0, 1.0);
289
+ samples_out[idx] = out;
290
+ }