sparcoon 0.9.1 → 0.9.3
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/README.md +176 -76
- package/dist/artifact/FXArtifact.d.ts +51 -3
- package/dist/behaviorTransformFeedbackLayout.d.ts +24 -0
- package/dist/behaviorTransformFeedbackLayout.js +24 -0
- package/dist/coreLayout.d.ts +9 -5
- package/dist/coreLayout.js +17 -4
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +1 -1
- package/dist/effect/FXEffect.d.ts +4 -0
- package/dist/effect/FXEffect.js +47 -14
- package/dist/effect/FXEffectSpec.d.ts +21 -4
- package/dist/emitter/FXApplyRenderArtifactOptions.d.ts +15 -0
- package/dist/emitter/FXApplyRenderArtifactOptions.js +1 -0
- package/dist/emitter/FXEmitter.d.ts +44 -14
- package/dist/emitter/FXEmitter.js +132 -156
- package/dist/emitter/FXEmitterDriver.Internal.d.ts +26 -0
- package/dist/emitter/FXEmitterDriver.Internal.js +70 -0
- package/dist/emitter/FXFromArtifactsOptions.d.ts +12 -1
- package/dist/emitter/FXGPUEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXGPUEmitterDriver.Internal.js +137 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.js +133 -0
- package/dist/emitter/FXSimulationHolder.js +7 -5
- package/dist/emitter/FXTransformFeedbackSimulationHolder.d.ts +1 -0
- package/dist/emitter/FXTransformFeedbackSimulationHolder.js +249 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/instancedParticle/FXInstancedParticle.js +28 -1
- package/dist/instancedParticle/FXTransformFeedbackParticle.d.ts +1 -0
- package/dist/instancedParticle/FXTransformFeedbackParticle.js +169 -0
- package/dist/miscellaneous/fxMath.d.ts +18 -11
- package/dist/miscellaneous/fxMath.js +89 -209
- package/dist/render/FXArtifactMaterial.Internal.js +66 -40
- package/dist/render/FXArtifactMaterial.js +1 -1
- package/dist/render/FXMesh.js +2 -4
- package/package.json +11 -3
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { GLBufferAttribute, InstancedBufferGeometry, Mesh } from "three";
|
|
2
|
+
/**
|
|
3
|
+
* @internal A `GLBufferAttribute` additionally declared instanced (divisor 1, one value per drawn
|
|
4
|
+
* particle instead of per vertex). Confirmed by reading `WebGLBindingStates.js:432-444`: the
|
|
5
|
+
* per-instance vertex-attribute divisor is driven by `isInstancedBufferAttribute`/
|
|
6
|
+
* `meshPerAttribute` on the attribute object itself - a plain `GLBufferAttribute` never sets
|
|
7
|
+
* either, so without this every particle's state would be read as per-VERTEX (one value shared
|
|
8
|
+
* by the whole base primitive) instead of per-INSTANCE.
|
|
9
|
+
*/
|
|
10
|
+
class FXGLInstancedBufferAttribute extends GLBufferAttribute {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.isInstancedBufferAttribute = true;
|
|
14
|
+
this.meshPerAttribute = 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* `InstancedBufferGeometry.setAttribute` is typed only for `BufferAttribute`/
|
|
19
|
+
* `InterleavedBufferAttribute` - its `Attributes` generic defaults to `NormalBufferAttributes`,
|
|
20
|
+
* and `InstancedBufferGeometry` itself is declared non-generic, so the wider
|
|
21
|
+
* `NormalOrGLBufferAttributes` union (which DOES include `GLBufferAttribute`) is unreachable
|
|
22
|
+
* through it at the type level, even though `GLBufferAttribute` is a real, three.js-documented
|
|
23
|
+
* attribute type for exactly this GPGPU use case. Narrow, isolated escape hatch - matches the
|
|
24
|
+
* existing three.js-typing-gap precedent in `FXInstancedParticle.invalidateInstanceCap`.
|
|
25
|
+
*/
|
|
26
|
+
function setInstancedGLAttribute(geometry, name, attribute) {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
geometry.setAttribute(name, attribute);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @internal GPU-resident sibling of `FXInstancedParticle`: same instanced-mesh shape (one base
|
|
32
|
+
* primitive drawn `capacity` times, one property buffer per declared varying), but every property
|
|
33
|
+
* buffer is a **ping-pong pair** of raw `WebGLBuffer`s the transform-feedback simulation holder
|
|
34
|
+
* writes directly - never a `Float32Array`-backed `InstancedBufferAttribute` three.js uploads
|
|
35
|
+
* itself (`FXInstancedParticle`'s own mechanism, untouched). Capacity is fixed at construction, no
|
|
36
|
+
* growth: this class is never resized, unlike `FXInstancedParticle.ensureCapacity` (a deferred
|
|
37
|
+
* future extension, not required for correctness today). Every
|
|
38
|
+
* particle slot is always drawn (`instanceCount` is always `capacity`) - the CPU never knows the
|
|
39
|
+
* true live count for a GPU-driven emitter, so dead slots are hidden by the render shader's own
|
|
40
|
+
* age/lifetime test, not by shrinking the draw range.
|
|
41
|
+
*/
|
|
42
|
+
export class FXTransformFeedbackParticle extends Mesh {
|
|
43
|
+
constructor(gl, varyings, capacity, material, baseGeometry) {
|
|
44
|
+
const instancedGeometry = new InstancedBufferGeometry();
|
|
45
|
+
// Copy the base geometry's position/uv/normal + index onto the instanced geometry, same as
|
|
46
|
+
// FXInstancedParticle - these stay real, CPU-readable BufferAttributes (the base primitive's
|
|
47
|
+
// own shape, not per-particle state), borrowed by reference, never disposed here.
|
|
48
|
+
const baseIndex = baseGeometry.getIndex();
|
|
49
|
+
if (baseIndex !== null) {
|
|
50
|
+
instancedGeometry.setIndex(baseIndex);
|
|
51
|
+
}
|
|
52
|
+
instancedGeometry.setAttribute("position", baseGeometry.getAttribute("position"));
|
|
53
|
+
instancedGeometry.setAttribute("uv", baseGeometry.getAttribute("uv"));
|
|
54
|
+
instancedGeometry.setAttribute("normal", baseGeometry.getAttribute("normal"));
|
|
55
|
+
super(instancedGeometry, material);
|
|
56
|
+
this.propertyBuffers = {};
|
|
57
|
+
// Per-buffer-name ping-pong pair; `currentIndex[name]` (0 or 1) is which half is the currently
|
|
58
|
+
// valid, renderable side - the other half is what the transform-feedback pass writes into this
|
|
59
|
+
// tick. Never read and written by name in the same pass (see `readBuffer`/`writeBuffer`).
|
|
60
|
+
this.rawBufferPairs = {};
|
|
61
|
+
this.currentIndex = {};
|
|
62
|
+
this.strides = {};
|
|
63
|
+
this.frustumCulled = false;
|
|
64
|
+
this.gl = gl;
|
|
65
|
+
this.instancedGeometry = instancedGeometry;
|
|
66
|
+
this.capacity = capacity;
|
|
67
|
+
this.instancedGeometry.instanceCount = capacity;
|
|
68
|
+
for (const name in varyings) {
|
|
69
|
+
const { bufferSize } = varyings[name];
|
|
70
|
+
this.strides[name] = bufferSize;
|
|
71
|
+
const bufferA = this.createSizedBuffer(bufferSize, capacity);
|
|
72
|
+
const bufferB = this.createSizedBuffer(bufferSize, capacity);
|
|
73
|
+
this.rawBufferPairs[name] = [bufferA, bufferB];
|
|
74
|
+
this.currentIndex[name] = 0;
|
|
75
|
+
const attribute = new FXGLInstancedBufferAttribute(bufferA, gl.FLOAT, bufferSize, 4, capacity);
|
|
76
|
+
setInstancedGLAttribute(this.instancedGeometry, `a_${name}`, attribute);
|
|
77
|
+
this.propertyBuffers[name] = attribute;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The buffer transform feedback should bind as its `in` (read) source this tick - last tick's
|
|
81
|
+
* result, or uninitialized garbage on the very first tick (safe: the assembled program's spawn
|
|
82
|
+
* branch always zero-defaults every buffer before the graph's own writes, so a freshly claimed
|
|
83
|
+
* slot never actually reads it before being overwritten). */
|
|
84
|
+
readBuffer(name) {
|
|
85
|
+
return this.rawBufferPairs[name][this.currentIndex[name]];
|
|
86
|
+
}
|
|
87
|
+
/** The buffer transform feedback should bind as its transform-feedback (write) target this tick
|
|
88
|
+
* - the stale half of the pair, about to become current once `swapBuffers` runs. */
|
|
89
|
+
writeBuffer(name) {
|
|
90
|
+
return this.rawBufferPairs[name][this.currentIndex[name] === 0 ? 1 : 0];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Call once per state buffer after a tick's transform-feedback pass has finished writing
|
|
94
|
+
* `writeBuffer(name)`: flips which half is current, and replaces the whole attribute object on
|
|
95
|
+
* the geometry - never just `.setBuffer()` + `.needsUpdate` on the existing one. Confirmed by
|
|
96
|
+
* reading `WebGLBindingStates.needsUpdate()` (`WebGLBindingStates.js:166-209`): it decides
|
|
97
|
+
* whether to rebind a cached VAO's vertex-attribute pointers purely by comparing attribute
|
|
98
|
+
* OBJECT identity, never `.version`/`.buffer` - reusing the same attribute instance across a
|
|
99
|
+
* swap would leave the render draw silently bound to the FIRST buffer forever (independently
|
|
100
|
+
* confirmed against three.js issue #22843, which describes the identical root cause). Mirrors
|
|
101
|
+
* `FXInstancedParticle.ensureCapacity`, which replaces the whole `InstancedBufferAttribute` for
|
|
102
|
+
* the same reason - there discovered as an array-reallocation side effect, here load-bearing on
|
|
103
|
+
* its own since nothing about capacity itself ever changes.
|
|
104
|
+
*/
|
|
105
|
+
swapBuffers(name) {
|
|
106
|
+
const nextIndex = this.currentIndex[name] === 0 ? 1 : 0;
|
|
107
|
+
this.currentIndex[name] = nextIndex;
|
|
108
|
+
const stride = this.strides[name];
|
|
109
|
+
const attribute = new FXGLInstancedBufferAttribute(this.rawBufferPairs[name][nextIndex], this.gl.FLOAT, stride, 4, this.capacity);
|
|
110
|
+
setInstancedGLAttribute(this.instancedGeometry, `a_${name}`, attribute);
|
|
111
|
+
this.propertyBuffers[name] = attribute;
|
|
112
|
+
}
|
|
113
|
+
drop() {
|
|
114
|
+
// No live-particle count to reset for a GPU-driven emitter (instanceCount is always
|
|
115
|
+
// `capacity`) - a reset instead means "everything currently alive should read as dead", which
|
|
116
|
+
// is the simulation holder's job (it owns dt/age, not this buffer-holding class).
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Swaps the mounted material and base geometry in place for a render-only structural edit -
|
|
120
|
+
* every ping-pong state buffer and `capacity` are left completely untouched, so playback never
|
|
121
|
+
* resets. Never called when the varyings shape itself changed (the driver's own
|
|
122
|
+
* `applyRenderArtifact` guards that) - only the constructor path handles a real buffer-layout
|
|
123
|
+
* change.
|
|
124
|
+
*/
|
|
125
|
+
replaceMaterialAndBaseGeometry(material, baseGeometry) {
|
|
126
|
+
// Detach the previous base attributes first, same as destroy()'s dance: they were borrowed by
|
|
127
|
+
// reference from a caller-owned geometry (possibly shared with other live consumers), so they
|
|
128
|
+
// must never be disposed here.
|
|
129
|
+
this.instancedGeometry.deleteAttribute("position");
|
|
130
|
+
this.instancedGeometry.deleteAttribute("uv");
|
|
131
|
+
this.instancedGeometry.deleteAttribute("normal");
|
|
132
|
+
this.instancedGeometry.setIndex(null);
|
|
133
|
+
const baseIndex = baseGeometry.getIndex();
|
|
134
|
+
if (baseIndex !== null) {
|
|
135
|
+
this.instancedGeometry.setIndex(baseIndex);
|
|
136
|
+
}
|
|
137
|
+
this.instancedGeometry.setAttribute("position", baseGeometry.getAttribute("position"));
|
|
138
|
+
this.instancedGeometry.setAttribute("uv", baseGeometry.getAttribute("uv"));
|
|
139
|
+
this.instancedGeometry.setAttribute("normal", baseGeometry.getAttribute("normal"));
|
|
140
|
+
// The mounted material IS disposable here (unlike the base geometry attributes above) - it was
|
|
141
|
+
// built exclusively for this mesh, never shared.
|
|
142
|
+
this.material?.dispose();
|
|
143
|
+
this.material = material;
|
|
144
|
+
}
|
|
145
|
+
// Disposes the instanced geometry, the currently mounted material, and every raw ping-pong
|
|
146
|
+
// buffer this instance owns - never the base geometry's borrowed position/uv/normal/index.
|
|
147
|
+
destroy() {
|
|
148
|
+
this.instancedGeometry.deleteAttribute("position");
|
|
149
|
+
this.instancedGeometry.deleteAttribute("uv");
|
|
150
|
+
this.instancedGeometry.deleteAttribute("normal");
|
|
151
|
+
this.instancedGeometry.setIndex(null);
|
|
152
|
+
this.instancedGeometry.dispose();
|
|
153
|
+
this.material?.dispose();
|
|
154
|
+
for (const name in this.rawBufferPairs) {
|
|
155
|
+
const [bufferA, bufferB] = this.rawBufferPairs[name];
|
|
156
|
+
this.gl.deleteBuffer(bufferA);
|
|
157
|
+
this.gl.deleteBuffer(bufferB);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
createSizedBuffer(itemSize, count) {
|
|
161
|
+
const buffer = this.gl.createBuffer();
|
|
162
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
|
|
163
|
+
// Sized but uninitialized (garbage) - the spawn branch's zero-default (see the constructor
|
|
164
|
+
// doc) makes this safe for every buffer, core and attribute alike.
|
|
165
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, itemSize * count * Float32Array.BYTES_PER_ELEMENT, this.gl.DYNAMIC_DRAW);
|
|
166
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
|
167
|
+
return buffer;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `
|
|
2
|
+
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `fxNoise3`, ...). A behavior
|
|
3
3
|
* kernel's authored `spawn`/`update` body calls these by name - the editor's compiler prints those
|
|
4
4
|
* exact calls - so every exported name here is a frozen contract with already-compiled kernels,
|
|
5
5
|
* like the `a_`/`p_` buffer prefixes. Never rename one without updating the compiler side in
|
|
@@ -9,18 +9,25 @@ export declare function fxFract(x: number): number;
|
|
|
9
9
|
export declare function fxMod(a: number, b: number): number;
|
|
10
10
|
export declare function fxMix(a: number, b: number, t: number): number;
|
|
11
11
|
export declare function fxSmoothstep(edge0: number, edge1: number, x: number): number;
|
|
12
|
-
|
|
13
|
-
/** Cheap deterministic 1D value-noise (smoothed hash interpolation), range [-1, 1]. */
|
|
14
|
-
export declare function fxValueNoise(x: number): number;
|
|
15
|
-
/** Fractal Brownian motion over {@link fxValueNoise} (gain 0.5, lacunarity 2). */
|
|
12
|
+
/** Fractal Brownian motion over {@link fxNoise1} (gain 0.5, lacunarity 2). */
|
|
16
13
|
export declare function fxFbm(x: number, octaves: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Fractal Brownian motion over the full {@link fxNoise3} domain (gain 0.5, lacunarity 2) - unlike
|
|
16
|
+
* {@link fxFbm}, which walks a single scalar axis, this samples all three input components
|
|
17
|
+
* together so the field varies with the whole position, not just one of its axes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fxFbm3(x: number, y: number, z: number, octaves: number): number;
|
|
17
20
|
/** Linearly samples a curve LUT at `t` in [0, 1] (clamped), interpolating between entries. */
|
|
18
21
|
export declare function fxSampleLut(lut: ArrayLike<number>, t: number): number;
|
|
19
22
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
+
* Cheap deterministic 1D value-noise (smoothed integer-hash interpolation), range [-1, 1]. The
|
|
24
|
+
* CPU twin of the compiler's GLSL `noise(float)` standard-tier form - see {@link fxIntHash1}.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fxNoise1(x: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* 2D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec2)` standard-tier overload op-for-op.
|
|
29
|
+
* Component arguments (rather than a vector) so it mirrors how the scalarized JS backend calls it.
|
|
23
30
|
*/
|
|
24
|
-
export declare function
|
|
25
|
-
/** 3D twin of {@link
|
|
26
|
-
export declare function
|
|
31
|
+
export declare function fxNoise2(vx: number, vy: number): number;
|
|
32
|
+
/** 3D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec3)` standard-tier overload op-for-op. */
|
|
33
|
+
export declare function fxNoise3(vx: number, vy: number, vz: number): number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `
|
|
2
|
+
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `fxNoise3`, ...). A behavior
|
|
3
3
|
* kernel's authored `spawn`/`update` body calls these by name - the editor's compiler prints those
|
|
4
4
|
* exact calls - so every exported name here is a frozen contract with already-compiled kernels,
|
|
5
5
|
* like the `a_`/`p_` buffer prefixes. Never rename one without updating the compiler side in
|
|
@@ -18,28 +18,34 @@ export function fxSmoothstep(edge0, edge1, x) {
|
|
|
18
18
|
const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0.0), 1.0);
|
|
19
19
|
return t * t * (3.0 - 2.0 * t);
|
|
20
20
|
}
|
|
21
|
-
export function fxHash(n) {
|
|
22
|
-
const s = Math.sin(n * 127.1) * 43758.5453123;
|
|
23
|
-
return s - Math.floor(s);
|
|
24
|
-
}
|
|
25
|
-
/** Cheap deterministic 1D value-noise (smoothed hash interpolation), range [-1, 1]. */
|
|
26
|
-
export function fxValueNoise(x) {
|
|
27
|
-
const i = Math.floor(x);
|
|
28
|
-
const fractional = x - i;
|
|
29
|
-
const u = fractional * fractional * (3 - 2 * fractional);
|
|
30
|
-
return (fxHash(i) * (1 - u) + fxHash(i + 1) * u) * 2 - 1;
|
|
31
|
-
}
|
|
32
21
|
// Mirrors FBM_MAX_OCTAVES in the editor engine: clamps a runaway octave count (a
|
|
33
22
|
// value parameter rebinds with no recompile/sanity gate) so it cannot blow up the update kernel.
|
|
34
23
|
const FBM_MAX_OCTAVES = 8;
|
|
35
|
-
/** Fractal Brownian motion over {@link
|
|
24
|
+
/** Fractal Brownian motion over {@link fxNoise1} (gain 0.5, lacunarity 2). */
|
|
36
25
|
export function fxFbm(x, octaves) {
|
|
37
26
|
const octaveCount = Math.min(Math.max(Math.floor(octaves), 0), FBM_MAX_OCTAVES);
|
|
38
27
|
let sum = 0;
|
|
39
28
|
let amplitude = 1;
|
|
40
29
|
let frequency = 1;
|
|
41
30
|
for (let i = 0; i < octaveCount; i += 1) {
|
|
42
|
-
sum +=
|
|
31
|
+
sum += fxNoise1(x * frequency) * amplitude;
|
|
32
|
+
amplitude *= 0.5;
|
|
33
|
+
frequency *= 2;
|
|
34
|
+
}
|
|
35
|
+
return sum;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fractal Brownian motion over the full {@link fxNoise3} domain (gain 0.5, lacunarity 2) - unlike
|
|
39
|
+
* {@link fxFbm}, which walks a single scalar axis, this samples all three input components
|
|
40
|
+
* together so the field varies with the whole position, not just one of its axes.
|
|
41
|
+
*/
|
|
42
|
+
export function fxFbm3(x, y, z, octaves) {
|
|
43
|
+
const octaveCount = Math.min(Math.max(Math.floor(octaves), 0), FBM_MAX_OCTAVES);
|
|
44
|
+
let sum = 0;
|
|
45
|
+
let amplitude = 1;
|
|
46
|
+
let frequency = 1;
|
|
47
|
+
for (let i = 0; i < octaveCount; i += 1) {
|
|
48
|
+
sum += fxNoise3(x * frequency, y * frequency, z * frequency) * amplitude;
|
|
43
49
|
amplitude *= 0.5;
|
|
44
50
|
frequency *= 2;
|
|
45
51
|
}
|
|
@@ -57,205 +63,79 @@ export function fxSampleLut(lut, t) {
|
|
|
57
63
|
const upperValue = lut[i + 1 < length ? i + 1 : last];
|
|
58
64
|
return lowerValue + (upperValue - lowerValue) * fraction;
|
|
59
65
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Integer-hash sub-primitive, private to `fxNoise1/2/3` - Ken Perlin's classic `IntNoise` bit
|
|
68
|
+
* scramble. `Math.imul` plus `| 0` truncation at each step forces genuine 32-bit wraparound so
|
|
69
|
+
* this tracks the compiler's GLSL `int` version bit-for-bit (see the `noise` case in the editor's
|
|
70
|
+
* `FXFunctions.Internal.ts` standard-tier helper).
|
|
71
|
+
*/
|
|
72
|
+
function fxIntHash1(n) {
|
|
73
|
+
const shifted = (n << 13) | 0;
|
|
74
|
+
const mixed = (shifted ^ n) | 0;
|
|
75
|
+
const squared = Math.imul(mixed, mixed);
|
|
76
|
+
const step1 = (Math.imul(squared, 15731) + Math.imul(mixed, 789221)) | 0;
|
|
77
|
+
const step2 = (Math.imul(step1, step1) + 1376312589) | 0;
|
|
78
|
+
return (step2 & 0x00ffffff) / 16777216.0;
|
|
63
79
|
}
|
|
64
|
-
function
|
|
65
|
-
return
|
|
80
|
+
function fxIntHash2(ix, iy) {
|
|
81
|
+
return fxIntHash1((ix + Math.imul(iy, 57)) | 0);
|
|
66
82
|
}
|
|
67
|
-
function
|
|
68
|
-
return
|
|
83
|
+
function fxIntHash3(ix, iy, iz) {
|
|
84
|
+
return fxIntHash1((ix + Math.imul(iy, 57) + Math.imul(iz, 113)) | 0);
|
|
69
85
|
}
|
|
70
86
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* it; the arithmetic tracks the GLSL version op-for-op. Output is roughly `[-1, 1]`.
|
|
87
|
+
* Cheap deterministic 1D value-noise (smoothed integer-hash interpolation), range [-1, 1]. The
|
|
88
|
+
* CPU twin of the compiler's GLSL `noise(float)` standard-tier form - see {@link fxIntHash1}.
|
|
74
89
|
*/
|
|
75
|
-
export function
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
const s = (vx + vy) * cy;
|
|
81
|
-
let ix = Math.floor(vx + s);
|
|
82
|
-
let iy = Math.floor(vy + s);
|
|
83
|
-
const t = (ix + iy) * cx;
|
|
84
|
-
const x0x = vx - ix + t;
|
|
85
|
-
const x0y = vy - iy + t;
|
|
86
|
-
let i1x;
|
|
87
|
-
let i1y;
|
|
88
|
-
if (x0x > x0y) {
|
|
89
|
-
i1x = 1.0;
|
|
90
|
-
i1y = 0.0;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
i1x = 0.0;
|
|
94
|
-
i1y = 1.0;
|
|
95
|
-
}
|
|
96
|
-
const x1x = x0x - i1x + cx;
|
|
97
|
-
const x1y = x0y - i1y + cx;
|
|
98
|
-
const x2x = x0x + cz;
|
|
99
|
-
const x2y = x0y + cz;
|
|
100
|
-
ix = fxsnMod289(ix);
|
|
101
|
-
iy = fxsnMod289(iy);
|
|
102
|
-
const p0 = fxsnPermute(fxsnPermute(iy + 0.0) + ix + 0.0);
|
|
103
|
-
const p1 = fxsnPermute(fxsnPermute(iy + i1y) + ix + i1x);
|
|
104
|
-
const p2 = fxsnPermute(fxsnPermute(iy + 1.0) + ix + 1.0);
|
|
105
|
-
let m0 = Math.max(0.5 - (x0x * x0x + x0y * x0y), 0.0);
|
|
106
|
-
let m1 = Math.max(0.5 - (x1x * x1x + x1y * x1y), 0.0);
|
|
107
|
-
let m2 = Math.max(0.5 - (x2x * x2x + x2y * x2y), 0.0);
|
|
108
|
-
m0 = m0 * m0;
|
|
109
|
-
m0 = m0 * m0;
|
|
110
|
-
m1 = m1 * m1;
|
|
111
|
-
m1 = m1 * m1;
|
|
112
|
-
m2 = m2 * m2;
|
|
113
|
-
m2 = m2 * m2;
|
|
114
|
-
const gx0 = 2.0 * fxsnFract(p0 * cw) - 1.0;
|
|
115
|
-
const gx1 = 2.0 * fxsnFract(p1 * cw) - 1.0;
|
|
116
|
-
const gx2 = 2.0 * fxsnFract(p2 * cw) - 1.0;
|
|
117
|
-
const h0 = Math.abs(gx0) - 0.5;
|
|
118
|
-
const h1 = Math.abs(gx1) - 0.5;
|
|
119
|
-
const h2 = Math.abs(gx2) - 0.5;
|
|
120
|
-
const ox0 = Math.floor(gx0 + 0.5);
|
|
121
|
-
const ox1 = Math.floor(gx1 + 0.5);
|
|
122
|
-
const ox2 = Math.floor(gx2 + 0.5);
|
|
123
|
-
const a0 = gx0 - ox0;
|
|
124
|
-
const a1 = gx1 - ox1;
|
|
125
|
-
const a2 = gx2 - ox2;
|
|
126
|
-
m0 *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h0 * h0);
|
|
127
|
-
m1 *= 1.79284291400159 - 0.85373472095314 * (a1 * a1 + h1 * h1);
|
|
128
|
-
m2 *= 1.79284291400159 - 0.85373472095314 * (a2 * a2 + h2 * h2);
|
|
129
|
-
const g0 = a0 * x0x + h0 * x0y;
|
|
130
|
-
const g1 = a1 * x1x + h1 * x1y;
|
|
131
|
-
const g2 = a2 * x2x + h2 * x2y;
|
|
132
|
-
return 130.0 * (m0 * g0 + m1 * g1 + m2 * g2);
|
|
90
|
+
export function fxNoise1(x) {
|
|
91
|
+
const i = Math.floor(x);
|
|
92
|
+
const f = x - i;
|
|
93
|
+
const u = f * f * (3 - 2 * f);
|
|
94
|
+
return (fxIntHash1(i) * (1 - u) + fxIntHash1(i + 1) * u) * 2 - 1;
|
|
133
95
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const nsy = oneOverSeven * 0.5 - 1.0;
|
|
180
|
-
const nsz = oneOverSeven;
|
|
181
|
-
const xx = [0.0, 0.0, 0.0, 0.0];
|
|
182
|
-
const yy = [0.0, 0.0, 0.0, 0.0];
|
|
183
|
-
const hh = [0.0, 0.0, 0.0, 0.0];
|
|
184
|
-
for (let k = 0; k < 4; k += 1) {
|
|
185
|
-
const j = p[k] - 49.0 * Math.floor(p[k] * nsz * nsz);
|
|
186
|
-
const xf = Math.floor(j * nsz);
|
|
187
|
-
const yf = Math.floor(j - 7.0 * xf);
|
|
188
|
-
xx[k] = xf * nsx + nsy;
|
|
189
|
-
yy[k] = yf * nsx + nsy;
|
|
190
|
-
hh[k] = 1.0 - Math.abs(xx[k]) - Math.abs(yy[k]);
|
|
191
|
-
}
|
|
192
|
-
const b0 = [xx[0], xx[1], yy[0], yy[1]];
|
|
193
|
-
const b1 = [xx[2], xx[3], yy[2], yy[3]];
|
|
194
|
-
const s0 = [
|
|
195
|
-
Math.floor(b0[0]) * 2.0 + 1.0,
|
|
196
|
-
Math.floor(b0[1]) * 2.0 + 1.0,
|
|
197
|
-
Math.floor(b0[2]) * 2.0 + 1.0,
|
|
198
|
-
Math.floor(b0[3]) * 2.0 + 1.0,
|
|
199
|
-
];
|
|
200
|
-
const s1 = [
|
|
201
|
-
Math.floor(b1[0]) * 2.0 + 1.0,
|
|
202
|
-
Math.floor(b1[1]) * 2.0 + 1.0,
|
|
203
|
-
Math.floor(b1[2]) * 2.0 + 1.0,
|
|
204
|
-
Math.floor(b1[3]) * 2.0 + 1.0,
|
|
205
|
-
];
|
|
206
|
-
const sh = [
|
|
207
|
-
-(hh[0] <= 0.0 ? 1.0 : 0.0),
|
|
208
|
-
-(hh[1] <= 0.0 ? 1.0 : 0.0),
|
|
209
|
-
-(hh[2] <= 0.0 ? 1.0 : 0.0),
|
|
210
|
-
-(hh[3] <= 0.0 ? 1.0 : 0.0),
|
|
211
|
-
];
|
|
212
|
-
const a00 = b0[0] + s0[0] * sh[0];
|
|
213
|
-
const a01 = b0[2] + s0[2] * sh[0];
|
|
214
|
-
const a02 = b0[1] + s0[1] * sh[1];
|
|
215
|
-
const a03 = b0[3] + s0[3] * sh[1];
|
|
216
|
-
const a10 = b1[0] + s1[0] * sh[2];
|
|
217
|
-
const a11 = b1[2] + s1[2] * sh[2];
|
|
218
|
-
const a12 = b1[1] + s1[1] * sh[3];
|
|
219
|
-
const a13 = b1[3] + s1[3] * sh[3];
|
|
220
|
-
let p0x = a00;
|
|
221
|
-
let p0y = a01;
|
|
222
|
-
let p0z = hh[0];
|
|
223
|
-
let p1x = a02;
|
|
224
|
-
let p1y = a03;
|
|
225
|
-
let p1z = hh[1];
|
|
226
|
-
let p2x = a10;
|
|
227
|
-
let p2y = a11;
|
|
228
|
-
let p2z = hh[2];
|
|
229
|
-
let p3x = a12;
|
|
230
|
-
let p3y = a13;
|
|
231
|
-
let p3z = hh[3];
|
|
232
|
-
const nrm0 = 1.79284291400159 - 0.85373472095314 * (p0x * p0x + p0y * p0y + p0z * p0z);
|
|
233
|
-
const nrm1 = 1.79284291400159 - 0.85373472095314 * (p1x * p1x + p1y * p1y + p1z * p1z);
|
|
234
|
-
const nrm2 = 1.79284291400159 - 0.85373472095314 * (p2x * p2x + p2y * p2y + p2z * p2z);
|
|
235
|
-
const nrm3 = 1.79284291400159 - 0.85373472095314 * (p3x * p3x + p3y * p3y + p3z * p3z);
|
|
236
|
-
p0x *= nrm0;
|
|
237
|
-
p0y *= nrm0;
|
|
238
|
-
p0z *= nrm0;
|
|
239
|
-
p1x *= nrm1;
|
|
240
|
-
p1y *= nrm1;
|
|
241
|
-
p1z *= nrm1;
|
|
242
|
-
p2x *= nrm2;
|
|
243
|
-
p2y *= nrm2;
|
|
244
|
-
p2z *= nrm2;
|
|
245
|
-
p3x *= nrm3;
|
|
246
|
-
p3y *= nrm3;
|
|
247
|
-
p3z *= nrm3;
|
|
248
|
-
let m0 = Math.max(0.6 - (x0x * x0x + x0y * x0y + x0z * x0z), 0.0);
|
|
249
|
-
let m1 = Math.max(0.6 - (x1x * x1x + x1y * x1y + x1z * x1z), 0.0);
|
|
250
|
-
let m2 = Math.max(0.6 - (x2x * x2x + x2y * x2y + x2z * x2z), 0.0);
|
|
251
|
-
let m3 = Math.max(0.6 - (x3x * x3x + x3y * x3y + x3z * x3z), 0.0);
|
|
252
|
-
m0 = m0 * m0;
|
|
253
|
-
m1 = m1 * m1;
|
|
254
|
-
m2 = m2 * m2;
|
|
255
|
-
m3 = m3 * m3;
|
|
256
|
-
const dp0 = p0x * x0x + p0y * x0y + p0z * x0z;
|
|
257
|
-
const dp1 = p1x * x1x + p1y * x1y + p1z * x1z;
|
|
258
|
-
const dp2 = p2x * x2x + p2y * x2y + p2z * x2z;
|
|
259
|
-
const dp3 = p3x * x3x + p3y * x3y + p3z * x3z;
|
|
260
|
-
return 42.0 * (m0 * m0 * dp0 + m1 * m1 * dp1 + m2 * m2 * dp2 + m3 * m3 * dp3);
|
|
96
|
+
/**
|
|
97
|
+
* 2D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec2)` standard-tier overload op-for-op.
|
|
98
|
+
* Component arguments (rather than a vector) so it mirrors how the scalarized JS backend calls it.
|
|
99
|
+
*/
|
|
100
|
+
export function fxNoise2(vx, vy) {
|
|
101
|
+
const ix = Math.floor(vx);
|
|
102
|
+
const iy = Math.floor(vy);
|
|
103
|
+
const fx = vx - ix;
|
|
104
|
+
const fy = vy - iy;
|
|
105
|
+
const ux = fx * fx * (3 - 2 * fx);
|
|
106
|
+
const uy = fy * fy * (3 - 2 * fy);
|
|
107
|
+
const a = fxIntHash2(ix, iy);
|
|
108
|
+
const b = fxIntHash2(ix + 1, iy);
|
|
109
|
+
const c = fxIntHash2(ix, iy + 1);
|
|
110
|
+
const d = fxIntHash2(ix + 1, iy + 1);
|
|
111
|
+
const ab = a + (b - a) * ux;
|
|
112
|
+
const cd = c + (d - c) * ux;
|
|
113
|
+
return (ab + (cd - ab) * uy) * 2 - 1;
|
|
114
|
+
}
|
|
115
|
+
/** 3D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec3)` standard-tier overload op-for-op. */
|
|
116
|
+
export function fxNoise3(vx, vy, vz) {
|
|
117
|
+
const ix = Math.floor(vx);
|
|
118
|
+
const iy = Math.floor(vy);
|
|
119
|
+
const iz = Math.floor(vz);
|
|
120
|
+
const fx = vx - ix;
|
|
121
|
+
const fy = vy - iy;
|
|
122
|
+
const fz = vz - iz;
|
|
123
|
+
const ux = fx * fx * (3 - 2 * fx);
|
|
124
|
+
const uy = fy * fy * (3 - 2 * fy);
|
|
125
|
+
const uz = fz * fz * (3 - 2 * fz);
|
|
126
|
+
const a = fxIntHash3(ix, iy, iz);
|
|
127
|
+
const b = fxIntHash3(ix + 1, iy, iz);
|
|
128
|
+
const c = fxIntHash3(ix, iy + 1, iz);
|
|
129
|
+
const d = fxIntHash3(ix + 1, iy + 1, iz);
|
|
130
|
+
const e = fxIntHash3(ix, iy, iz + 1);
|
|
131
|
+
const f = fxIntHash3(ix + 1, iy, iz + 1);
|
|
132
|
+
const g = fxIntHash3(ix, iy + 1, iz + 1);
|
|
133
|
+
const h = fxIntHash3(ix + 1, iy + 1, iz + 1);
|
|
134
|
+
const ab = a + (b - a) * ux;
|
|
135
|
+
const cd = c + (d - c) * ux;
|
|
136
|
+
const abcd = ab + (cd - ab) * uy;
|
|
137
|
+
const ef = e + (f - e) * ux;
|
|
138
|
+
const gh = g + (h - g) * ux;
|
|
139
|
+
const efgh = ef + (gh - ef) * uy;
|
|
140
|
+
return (abcd + (efgh - abcd) * uz) * 2 - 1;
|
|
261
141
|
}
|