@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.
- package/LICENSE +203 -0
- package/README.md +73 -0
- package/dist/field.wgsl +225 -0
- package/dist/fractal-prepass.wgsl +290 -0
- package/dist/index.cjs +1942 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +447 -0
- package/dist/index.d.ts +447 -0
- package/dist/index.js +1848 -0
- package/dist/index.js.map +1 -0
- package/dist/terrain.wgsl +451 -0
- package/docs/adrs/adr-0001-package-scope.md +18 -0
- package/docs/adrs/adr-0002-world-tiling-lod-stitching.md +28 -0
- package/docs/adrs/adr-0003-terrain-generation-style-mixing.md +21 -0
- package/docs/adrs/index.md +5 -0
- package/docs/biomes.md +206 -0
- package/docs/lod-zoning.md +22 -0
- package/docs/plan.md +107 -0
- package/docs/procedural-surface.md +73 -0
- package/docs/resources.md +55 -0
- package/package.json +53 -0
- package/src/biomes/temperate.ts +387 -0
- package/src/field.wgsl +225 -0
- package/src/fields.ts +321 -0
- package/src/fractal-prepass.ts +237 -0
- package/src/fractal-prepass.wgsl +290 -0
- package/src/generator.ts +106 -0
- package/src/hex.ts +54 -0
- package/src/index.ts +11 -0
- package/src/mesh.ts +285 -0
- package/src/perf-monitor.ts +133 -0
- package/src/shaders/demo-terrain.wgsl +193 -0
- package/src/shaders/demo-trees.wgsl +172 -0
- package/src/shaders/demo-water-sim.wgsl +361 -0
- package/src/shaders/library/common.wgsl +38 -0
- package/src/shaders/library/materials.wgsl +175 -0
- package/src/terrain.wgsl +451 -0
- package/src/tile-cache.ts +274 -0
- package/src/tiles.ts +417 -0
- package/src/types.ts +220 -0
- package/src/wgsl/field.job.wgsl +225 -0
- package/src/wgsl/terrain.job.wgsl +451 -0
- 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
|
+
}
|