@nayan-ui/engine 1.0.0-beta.4 → 1.0.0-beta.6

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 (77) hide show
  1. package/README.md +225 -116
  2. package/lib/module/assets/ProceduralCache.js +5 -0
  3. package/lib/module/assets/loft.js +15 -63
  4. package/lib/module/assets/procedural.js +500 -0
  5. package/lib/module/assets/shape.js +147 -245
  6. package/lib/module/assets/texture.js +65 -0
  7. package/lib/module/drawing/DrawingDocument.js +1 -1
  8. package/lib/module/index.js +4 -1
  9. package/lib/module/media/audio-worklet.js +27 -0
  10. package/lib/module/media/audio.js +20 -0
  11. package/lib/module/media/input.web.js +107 -0
  12. package/lib/module/media/microphone.js +89 -0
  13. package/lib/module/media/speech.js +80 -0
  14. package/lib/module/media/speechPlatform.js +12 -0
  15. package/lib/module/media/speechPlatform.web.js +16 -0
  16. package/lib/module/native/BrowserEngine.js +122 -0
  17. package/lib/module/native/NativeNayanEngine.web.js +2 -0
  18. package/lib/module/render/GameView.js +45 -13
  19. package/lib/module/render/quality.js +151 -17
  20. package/lib/module/world/World.js +63 -0
  21. package/lib/typescript/src/assets/ProceduralCache.d.ts +2 -0
  22. package/lib/typescript/src/assets/loft.d.ts +3 -1
  23. package/lib/typescript/src/assets/procedural.d.ts +394 -0
  24. package/lib/typescript/src/assets/shape.d.ts +13 -7
  25. package/lib/typescript/src/assets/texture.d.ts +63 -0
  26. package/lib/typescript/src/index.d.ts +8 -2
  27. package/lib/typescript/src/media/audio.d.ts +13 -0
  28. package/lib/typescript/src/media/input.web.d.ts +4 -0
  29. package/lib/typescript/src/media/microphone.d.ts +51 -0
  30. package/lib/typescript/src/media/speech.d.ts +38 -0
  31. package/lib/typescript/src/media/speechPlatform.d.ts +5 -0
  32. package/lib/typescript/src/media/speechPlatform.web.d.ts +5 -0
  33. package/lib/typescript/src/native/EngineBridge.d.ts +7 -1
  34. package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +72 -0
  35. package/lib/typescript/src/render/GameView.d.ts +30 -3
  36. package/lib/typescript/src/render/quality.d.ts +54 -2
  37. package/lib/typescript/src/types.d.ts +9 -2
  38. package/lib/typescript/src/world/World.d.ts +16 -0
  39. package/lib/typescript/src/world/types.d.ts +8 -0
  40. package/lib/web/audio-worklet.js +27 -0
  41. package/lib/web/engine_core_bg.wasm +0 -0
  42. package/lib/web/index.js +1181 -422
  43. package/lib/web/types/src/assets/ProceduralCache.d.ts +2 -0
  44. package/lib/web/types/src/assets/loft.d.ts +3 -1
  45. package/lib/web/types/src/assets/procedural.d.ts +393 -0
  46. package/lib/web/types/src/assets/shape.d.ts +13 -7
  47. package/lib/web/types/src/assets/texture.d.ts +63 -0
  48. package/lib/web/types/src/index.d.ts +8 -2
  49. package/lib/web/types/src/media/audio.d.ts +13 -0
  50. package/lib/web/types/src/media/input.d.ts +3 -0
  51. package/lib/web/types/src/media/microphone.d.ts +50 -0
  52. package/lib/web/types/src/media/speech.d.ts +37 -0
  53. package/lib/web/types/src/media/speechPlatform.d.ts +4 -0
  54. package/lib/web/types/src/native/EngineBridge.d.ts +7 -1
  55. package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +72 -0
  56. package/lib/web/types/src/render/GameView.d.ts +30 -3
  57. package/lib/web/types/src/render/quality.d.ts +54 -2
  58. package/lib/web/types/src/types.d.ts +9 -2
  59. package/lib/web/types/src/world/World.d.ts +16 -0
  60. package/lib/web/types/src/world/types.d.ts +8 -0
  61. package/native/android/src/main/AndroidManifest.xml +10 -0
  62. package/native/android/src/main/java/com/nayanui/engine/EngineMicrophone.java +60 -0
  63. package/native/android/src/main/java/com/nayanui/engine/EngineSpeech.java +28 -0
  64. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +1 -0
  65. package/native/android/src/main/java/com/nayanui/engine/NayanEnginePackage.java +2 -0
  66. package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
  67. package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
  68. package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
  69. package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
  70. package/native/core/build/android/x86/libengine_core.a +0 -0
  71. package/native/core/build/android/x86_64/libengine_core.a +0 -0
  72. package/native/core/include/engine_core.h +63 -1
  73. package/native/cpp/NayanEngineModule.cpp +296 -0
  74. package/native/cpp/NayanEngineModule.h +40 -0
  75. package/native/cpp/generated/NayanEngineSpecJSI.h +397 -0
  76. package/package.json +1 -1
  77. package/src/native/spec/NativeNayanEngine.ts +77 -0
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+
3
+ // Data-driven procedural worlds and meshes, built by the native core. A game describes its fields
4
+ // (terrain height, material contours, walkable clearance, placement rules, model surfaces) with a
5
+ // `FieldGraph`, puts them in a recipe, and the core generates terrain, scenery, reachable ground and
6
+ // picked spots, and answers walker queries, without any of that work running in JavaScript.
7
+
8
+ import NativeEngine from '../native/NativeNayanEngine';
9
+ /** A node of a field graph. Plain numbers are constants wherever a field is expected. */
10
+ export class FieldRef {
11
+ constructor(id) {
12
+ this.id = id;
13
+ }
14
+ }
15
+
16
+ /** A 2D primitive in a field's (x, z) plane; `offset` is subtracted from its distance, `width` is its fade length. */
17
+
18
+ /**
19
+ * Builds a field graph: generic math and geometry nodes evaluated by the native core. Shared nodes
20
+ * are computed once per sample point and `select` evaluates only the branch it takes.
21
+ *
22
+ * ```ts
23
+ * const f = new FieldGraph();
24
+ * const bank = f.sub(f.abs(f.sub(f.x, f.sines(f.x, f.z, 10, [[0, 0.035, 0, 11]]))), 5);
25
+ * f.output('bank', bank);
26
+ * ```
27
+ */
28
+ export class FieldGraph {
29
+ nodes = [];
30
+ outputs = {};
31
+ constants = new Map();
32
+ inputs = new Map();
33
+ node(node) {
34
+ this.nodes.push(node);
35
+ return new FieldRef(this.nodes.length - 1);
36
+ }
37
+ ref(value) {
38
+ return (value instanceof FieldRef ? value : this.constant(value)).id;
39
+ }
40
+ refs(values) {
41
+ return values.map(v => this.ref(v));
42
+ }
43
+ constant(value) {
44
+ if (!Number.isFinite(value)) throw new Error('FieldGraph: constants must be finite');
45
+ let ref = this.constants.get(value);
46
+ if (!ref || Object.is(value, -0) !== Object.is(this.nodes[ref.id]?.value, -0)) {
47
+ ref = this.node({
48
+ op: 'const',
49
+ value
50
+ });
51
+ this.constants.set(value, ref);
52
+ }
53
+ return ref;
54
+ }
55
+ /** A named input: `x`/`z` (local frame) for terrain fields; u, v, row, col, i, count, r0… elsewhere. */
56
+ input(name) {
57
+ let ref = this.inputs.get(name);
58
+ if (!ref) {
59
+ ref = this.node({
60
+ op: 'var',
61
+ name
62
+ });
63
+ this.inputs.set(name, ref);
64
+ }
65
+ return ref;
66
+ }
67
+ get x() {
68
+ return this.input('x');
69
+ }
70
+ get z() {
71
+ return this.input('z');
72
+ }
73
+ add(a, b) {
74
+ return this.node({
75
+ op: 'add',
76
+ a: this.ref(a),
77
+ b: this.ref(b)
78
+ });
79
+ }
80
+ sub(a, b) {
81
+ return this.node({
82
+ op: 'sub',
83
+ a: this.ref(a),
84
+ b: this.ref(b)
85
+ });
86
+ }
87
+ mul(a, b) {
88
+ return this.node({
89
+ op: 'mul',
90
+ a: this.ref(a),
91
+ b: this.ref(b)
92
+ });
93
+ }
94
+ div(a, b) {
95
+ return this.node({
96
+ op: 'div',
97
+ a: this.ref(a),
98
+ b: this.ref(b)
99
+ });
100
+ }
101
+ /** a + b + c… left to right. */
102
+ sum(...values) {
103
+ if (!values.length) return this.constant(0);
104
+ let total = values[0] instanceof FieldRef ? values[0] : this.constant(values[0]);
105
+ for (const value of values.slice(1)) total = this.add(total, value);
106
+ return total;
107
+ }
108
+ /** a · b · c… left to right. */
109
+ product(...values) {
110
+ if (!values.length) return this.constant(1);
111
+ let total = values[0] instanceof FieldRef ? values[0] : this.constant(values[0]);
112
+ for (const value of values.slice(1)) total = this.mul(total, value);
113
+ return total;
114
+ }
115
+ min(...args) {
116
+ return this.node({
117
+ op: 'min',
118
+ args: this.refs(args)
119
+ });
120
+ }
121
+ max(...args) {
122
+ return this.node({
123
+ op: 'max',
124
+ args: this.refs(args)
125
+ });
126
+ }
127
+ neg(a) {
128
+ return this.node({
129
+ op: 'neg',
130
+ a: this.ref(a)
131
+ });
132
+ }
133
+ abs(a) {
134
+ return this.node({
135
+ op: 'abs',
136
+ a: this.ref(a)
137
+ });
138
+ }
139
+ sqrt(a) {
140
+ return this.node({
141
+ op: 'sqrt',
142
+ a: this.ref(a)
143
+ });
144
+ }
145
+ floor(a) {
146
+ return this.node({
147
+ op: 'floor',
148
+ a: this.ref(a)
149
+ });
150
+ }
151
+ square(a) {
152
+ return this.node({
153
+ op: 'square',
154
+ a: this.ref(a)
155
+ });
156
+ }
157
+ sin(a) {
158
+ return this.node({
159
+ op: 'sin',
160
+ a: this.ref(a)
161
+ });
162
+ }
163
+ cos(a) {
164
+ return this.node({
165
+ op: 'cos',
166
+ a: this.ref(a)
167
+ });
168
+ }
169
+ exp(a) {
170
+ return this.node({
171
+ op: 'exp',
172
+ a: this.ref(a)
173
+ });
174
+ }
175
+ /** `a` to the power `b`. */
176
+ pow(a, b) {
177
+ return this.node({
178
+ op: 'pow',
179
+ a: this.ref(a),
180
+ b: this.ref(b)
181
+ });
182
+ }
183
+ /** `a − floor(a)`. */
184
+ fract(a) {
185
+ return this.node({
186
+ op: 'fract',
187
+ a: this.ref(a)
188
+ });
189
+ }
190
+ /**
191
+ * Value noise in 0..1 on a unit lattice: the common sine-hash shader noise, smoothstep-blended.
192
+ * With `period`, it repeats every `period` cells in x (to wrap around a lathe without a seam).
193
+ */
194
+ noise(x, z, period = 0) {
195
+ return this.node({
196
+ op: 'noise',
197
+ x: this.ref(x),
198
+ z: this.ref(z),
199
+ period
200
+ });
201
+ }
202
+ /** a + (b − a) · t, componentwise on numbers or fields. */
203
+ mix(a, b, t) {
204
+ return this.add(a, this.mul(this.sub(b, a), t));
205
+ }
206
+ hypot(...args) {
207
+ return this.node({
208
+ op: 'hypot',
209
+ args: this.refs(args)
210
+ });
211
+ }
212
+ /** Hermite smoothstep of `a` clamped to 0..1. */
213
+ smooth(a) {
214
+ return this.node({
215
+ op: 'smooth',
216
+ a: this.ref(a)
217
+ });
218
+ }
219
+ clamp(a, lo, hi) {
220
+ return this.node({
221
+ op: 'clamp',
222
+ a: this.ref(a),
223
+ lo: this.ref(lo),
224
+ hi: this.ref(hi)
225
+ });
226
+ }
227
+ /** `le` where `test` <= 0, otherwise `gt` (only the chosen branch is evaluated). */
228
+ select(test, le, gt) {
229
+ return this.node({
230
+ op: 'select',
231
+ test: this.ref(test),
232
+ le: this.ref(le),
233
+ gt: this.ref(gt)
234
+ });
235
+ }
236
+ /** base + Σ amp · sin(fx·x + fz·z + phase), terms as [fx, fz, phase, amp]. */
237
+ sines(x, z, base, terms) {
238
+ return this.node({
239
+ op: 'sines',
240
+ x: this.ref(x),
241
+ z: this.ref(z),
242
+ base,
243
+ terms
244
+ });
245
+ }
246
+ /** Σ height · max(0, 1 − u² − v²)^power over oriented ellipses [x, z, height, radiusU, radiusV, yaw]. */
247
+ bumps(x, z, items, power = 2) {
248
+ return this.node({
249
+ op: 'bumps',
250
+ x: this.ref(x),
251
+ z: this.ref(z),
252
+ items,
253
+ power
254
+ });
255
+ }
256
+ /** The smallest signed distance to the shapes (minus each one's offset); +∞ with none. */
257
+ distance(x, z, shapes) {
258
+ return this.node({
259
+ op: 'distance',
260
+ x: this.ref(x),
261
+ z: this.ref(z),
262
+ shapes
263
+ });
264
+ }
265
+ /** Π smoothstep((distance − offset) / width): 0 at the shapes, 1 beyond their fade. */
266
+ falloff(x, z, shapes) {
267
+ return this.node({
268
+ op: 'falloff',
269
+ x: this.ref(x),
270
+ z: this.ref(z),
271
+ shapes
272
+ });
273
+ }
274
+ /** The largest depth inside any of the boxes (min of side clearances, minus offset); −∞ with none. */
275
+ depth(x, z, shapes) {
276
+ return this.node({
277
+ op: 'depth',
278
+ x: this.ref(x),
279
+ z: this.ref(z),
280
+ shapes
281
+ });
282
+ }
283
+
284
+ /** Names a field so recipes can refer to it. Returns the name. */
285
+ output(name, value) {
286
+ this.outputs[name] = this.ref(value);
287
+ return name;
288
+ }
289
+ toJSON() {
290
+ return {
291
+ nodes: this.nodes,
292
+ outputs: this.outputs
293
+ };
294
+ }
295
+ }
296
+
297
+ // Field functions: per-point model functions (colors, displacement) evaluated by the native core.
298
+
299
+ /**
300
+ * A function of named inputs written as a field graph, for model options that would otherwise call
301
+ * a JS function per vertex: `lathe`/`tube`/`loft` colors and `lathe` displacement, `recolor`. Build
302
+ * one with `surfaceColor`, `faceColor`, `sectionColor` or `surfaceDisplacement`.
303
+ */
304
+ export class FieldFunction {
305
+ constructor(fields, outputs) {
306
+ this.fields = fields;
307
+ this.outputs = outputs;
308
+ }
309
+ toJSON() {
310
+ return {
311
+ fields: this.fields,
312
+ out: Array.from({
313
+ length: this.outputs
314
+ }, (_, i) => `out${i}`)
315
+ };
316
+ }
317
+ }
318
+ function fieldFunction(names, build) {
319
+ const f = new FieldGraph();
320
+ const inputs = Object.fromEntries(names.map(name => [name, f.input(name)]));
321
+ const result = build(f, inputs);
322
+ const out = Array.isArray(result) ? result : [result];
323
+ out.forEach((value, i) => f.output(`out${i}`, value));
324
+ return new FieldFunction(f, out.length);
325
+ }
326
+
327
+ /**
328
+ * A color of each surface point: `u`, `v` (texture coordinates; around and along a lathe or tube),
329
+ * position `x`, `y`, `z` and normal `nx`, `ny`, `nz`. Returns [r, g, b].
330
+ */
331
+ export const surfaceColor = build => fieldFunction(['u', 'v', 'x', 'y', 'z', 'nx', 'ny', 'nz'], build);
332
+ /** A color across a round end face: `r` from its center (0) to its rim (1), `angle` around it (radians). */
333
+ export const faceColor = build => fieldFunction(['r', 'angle'], build);
334
+ /** A color across a lathe's cut face: `r` from the axis (0) to the skin (1) at that height, and the height `y`. */
335
+ export const sectionColor = build => fieldFunction(['r', 'y'], build);
336
+ /** A lathe's displacement along its normal at (`u` around, `v` down the profile). */
337
+ export const surfaceDisplacement = build => fieldFunction(['u', 'v'], build);
338
+
339
+ // Recipes (mirrors of the native core's schema).
340
+
341
+ const engine = () => {
342
+ const native = NativeEngine;
343
+ if (!native) throw new Error('procedural worlds: the native core is not linked into this build');
344
+ if (typeof native.worldgenBuild !== 'function') throw new Error("procedural worlds: this app's native core predates them: rebuild it");
345
+ return native;
346
+ };
347
+ const recipeText = recipe => JSON.stringify(recipe, (_key, value) => value instanceof FieldGraph ? value.toJSON() : value);
348
+ const geometry = buffers => ({
349
+ vertices: new Float32Array(buffers.vertices),
350
+ indices: new Uint32Array(buffers.indices)
351
+ });
352
+ const spots = points => points.map(([x, z]) => ({
353
+ x,
354
+ z
355
+ }));
356
+ const bodyBuffers = new Map();
357
+
358
+ /** A generated world held by the native core; queries take world coordinates. */
359
+ export class ProceduralWorld {
360
+ /** True when it was restored from storage rather than generated. */
361
+
362
+ fields = new Map();
363
+ disposed = false;
364
+ constructor(handle) {
365
+ this.handle = handle;
366
+ const info = JSON.parse(engine().worldgenInfo(handle));
367
+ this.spawn = {
368
+ x: info.spawn[0],
369
+ z: info.spawn[1]
370
+ };
371
+ this.restored = info.restored;
372
+ this.layers = info.layers;
373
+ this.picks = Object.fromEntries(Object.entries(info.picks).map(([name, points]) => [name, spots(points)]));
374
+ }
375
+ get id() {
376
+ if (this.disposed) throw new Error('ProceduralWorld: used after dispose');
377
+ return this.handle;
378
+ }
379
+
380
+ /** A scatter layer's items: [x, y, z, ...attributes] per item (see `layers[name].stride`). */
381
+ layer(name) {
382
+ return new Float64Array(engine().worldgenLayer(this.id, name));
383
+ }
384
+ /** Reachable cells as [x, z] pairs. */
385
+ reachable() {
386
+ return new Float64Array(engine().worldgenReachable(this.id));
387
+ }
388
+ /** The overview step's surface codes, row by row (0 outside the walk boundary). */
389
+ overview() {
390
+ return new Uint8Array(engine().worldgenOverview(this.id));
391
+ }
392
+ /** Re-picks the spots for run `run` (0 = as generated); false keeps the previous ones. */
393
+ pick(run) {
394
+ const native = engine();
395
+ if (!native.worldgenPick(this.id, run)) return false;
396
+ const info = JSON.parse(native.worldgenInfo(this.id));
397
+ this.picks = Object.fromEntries(Object.entries(info.picks).map(([name, points]) => [name, spots(points)]));
398
+ return true;
399
+ }
400
+ /** The terrain mesh of `material` (built once; each material is handed over once). */
401
+ terrain(material) {
402
+ return geometry(engine().worldgenTerrain(this.id, material));
403
+ }
404
+ ground(x, z) {
405
+ return engine().worldgenGround(this.id, x, z);
406
+ }
407
+ surface(x, z) {
408
+ return engine().worldgenSurface(this.id, x, z);
409
+ }
410
+ /** The platform (recipe index) under a point, or -1. */
411
+ platformAt(x, z) {
412
+ return engine().worldgenPlatform(this.id, x, z);
413
+ }
414
+ walkable(x, z, radius) {
415
+ return engine().worldgenWalkable(this.id, x, z, radius);
416
+ }
417
+ /** How far a walker of `radius` is from anything blocking it (negative when blocked). */
418
+ clearance(x, z, radius) {
419
+ return engine().worldgenClearance(this.id, x, z, radius);
420
+ }
421
+ solidClearance(x, z, radius, bottom, top) {
422
+ return engine().worldgenSolidClearance(this.id, x, z, radius, bottom, top);
423
+ }
424
+ /** A named field of the recipe at a world point. */
425
+ field(name, x, z) {
426
+ let index = this.fields.get(name);
427
+ if (index === undefined) {
428
+ index = engine().worldgenFieldIndex(this.id, name);
429
+ if (index < 0) throw new Error(`ProceduralWorld: no field named ${name}`);
430
+ this.fields.set(name, index);
431
+ }
432
+ return engine().worldgenField(this.id, index, x, z);
433
+ }
434
+ /** The ground's tilt as a quaternion [x, y, z, w], in a frame turned by `yaw`. */
435
+ tilt(x, z, yaw = 0) {
436
+ return engine().worldgenTilt(this.id, x, z, yaw);
437
+ }
438
+ /** Whether a walker of `radius` could walk straight from a to b, ends at most `rise` apart in height. */
439
+ canSee(a, b, {
440
+ radius = 0.25,
441
+ step = 0.4,
442
+ rise = Infinity
443
+ } = {}) {
444
+ return engine().worldgenSight(this.id, a.x, a.z, b.x, b.z, radius, step, rise === Infinity ? Number.MAX_VALUE : rise);
445
+ }
446
+ /** Sweeps a walker by (dx, dz), sliding along obstacles and other `bodies`; updates `point`. */
447
+ move(point, dx, dz, radius, bodies = []) {
448
+ let buffer = bodyBuffers.get(bodies.length);
449
+ if (!buffer) bodyBuffers.set(bodies.length, buffer = new Float64Array(bodies.length * 3));
450
+ for (let i = 0; i < bodies.length; i++) {
451
+ const b = bodies[i];
452
+ buffer[i * 3] = b.x;
453
+ buffer[i * 3 + 1] = b.z;
454
+ buffer[i * 3 + 2] = b.radius;
455
+ }
456
+ const [x, z] = engine().worldgenMove(this.id, point.x, point.z, dx, dz, radius, buffer.buffer);
457
+ point.x = x;
458
+ point.z = z;
459
+ }
460
+ dispose() {
461
+ if (this.disposed) return;
462
+ this.disposed = true;
463
+ engine().worldgenFree(this.handle);
464
+ }
465
+ }
466
+ const opened = handle => {
467
+ if (!handle) throw new Error(`procedural world: ${engine().worldgenError() || 'generation failed'}`);
468
+ return new ProceduralWorld(handle);
469
+ };
470
+
471
+ /** Generates (or restores, see `WorldOptions.key`) a world on this thread. */
472
+ export function generateWorld(recipe, options = {}) {
473
+ return opened(engine().worldgenBuild(recipeText(recipe), options.key ?? '', options.prepareTerrain ?? false));
474
+ }
475
+
476
+ /** `generateWorld` on a background native thread (the web core builds at once). */
477
+ export async function generateWorldAsync(recipe, options = {}) {
478
+ return opened(await engine().worldgenBuildAsync(recipeText(recipe), options.key ?? '', options.prepareTerrain ?? false));
479
+ }
480
+
481
+ /** Whether a world was saved under `key` (cheap: doesn't read the world). */
482
+ export function isWorldKept(key) {
483
+ return engine().worldgenKept(key);
484
+ }
485
+
486
+ /** Builds a procedural mesh (parametric surfaces and seeded cards) in the native core. */
487
+ export function generateMesh(recipe) {
488
+ return geometry(engine().worldgenMesh(recipeText(recipe)));
489
+ }
490
+
491
+ /** Recolors geometry in place (and returns it) by a `surfaceColor`, in the native core. */
492
+ export function paintGeometry(geometry, color) {
493
+ const v = geometry.vertices;
494
+ const whole = v.byteOffset === 0 && v.byteLength === v.buffer.byteLength;
495
+ const buffer = whole ? v.buffer : v.slice().buffer;
496
+ if (!engine().worldgenPaint(buffer, JSON.stringify(color))) throw new Error(`recolor: ${engine().worldgenError()}`);
497
+ if (!whole) v.set(new Float32Array(buffer));
498
+ return geometry;
499
+ }
500
+ //# sourceMappingURL=procedural.js.map