sparcoon 0.0.5 → 0.0.7
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/dist/behaviorModules/FXBehaviorDirectionalGravity.js +11 -7
- package/dist/behaviorModules/FXBehaviorPointGravity.js +26 -20
- package/dist/behaviorModules/FXBehaviorScaleOverLife.js +5 -5
- package/dist/behaviorModules/FXBehaviorTorqueDamping.js +4 -4
- package/dist/behaviorModules/FXBehaviorTorqueNoise.js +8 -8
- package/dist/behaviorModules/FXBehaviorVelocityDamping.js +6 -5
- package/dist/behaviorModules/FXBehaviorVelocityNoise.js +8 -8
- package/dist/color/FXColor.js +30 -30
- package/dist/elements/FXEmitter.js +7 -5
- package/dist/instancedParticle/FXInstancedParticle.Internal.js +1 -1
- package/dist/instancedParticle/shared.d.ts +1 -7
- package/dist/instancedParticle/shared.js +1 -1
- package/dist/miscellaneous/miscellaneous.d.ts +1 -1
- package/dist/miscellaneous/miscellaneous.js +2 -2
- package/dist/miscellaneous/texture/FXTextureView.js +11 -11
- package/dist/renderingModules/FXRenderingColorOverLife.js +1 -1
- package/dist/renderingModules/FXRenderingColorOverVelocity.js +2 -2
- package/dist/shaders/FXRenderingColorOverVelocity.glsl.js +1 -1
- package/dist/spawnModules/FXSpawnBox.d.ts +7 -7
- package/dist/spawnModules/FXSpawnBox.js +24 -24
- package/dist/spawnModules/FXSpawnOffset.js +9 -9
- package/dist/spawnModules/FXSpawnRandomLifetime.js +4 -4
- package/dist/spawnModules/FXSpawnRandomRotation.js +4 -4
- package/dist/spawnModules/FXSpawnRandomScale.js +6 -6
- package/dist/spawnModules/FXSpawnRandomTorque.js +4 -4
- package/package.json +6 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertValidNumber } from '../miscellaneous/asserts.js';
|
|
2
|
-
import {
|
|
2
|
+
import { resolveFXVector3Config, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z } from '../miscellaneous/miscellaneous.js';
|
|
3
3
|
import { FXBehaviorModule } from './FXBehaviorModule.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,9 +15,10 @@ class FXBehaviorDirectionalGravity extends FXBehaviorModule {
|
|
|
15
15
|
super();
|
|
16
16
|
/** @internal */
|
|
17
17
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
18
|
-
this._private_directionInternal =
|
|
19
|
-
assertValidNumber(this._private_directionInternal.x, "
|
|
20
|
-
assertValidNumber(this._private_directionInternal.y, "
|
|
18
|
+
this._private_directionInternal = resolveFXVector3Config(direction);
|
|
19
|
+
assertValidNumber(this._private_directionInternal.x, "FXBehaviorDirectionalGravity.constructor.direction.x");
|
|
20
|
+
assertValidNumber(this._private_directionInternal.y, "FXBehaviorDirectionalGravity.constructor.direction.y");
|
|
21
|
+
assertValidNumber(this._private_directionInternal.z, "FXBehaviorDirectionalGravity.constructor.direction.z");
|
|
21
22
|
}
|
|
22
23
|
/** Acceleration vector in units/second² */
|
|
23
24
|
get direction() {
|
|
@@ -25,9 +26,10 @@ class FXBehaviorDirectionalGravity extends FXBehaviorModule {
|
|
|
25
26
|
}
|
|
26
27
|
/** Acceleration vector in units/second² */
|
|
27
28
|
set direction(value) {
|
|
28
|
-
this._private_directionInternal =
|
|
29
|
-
assertValidNumber(this._private_directionInternal.x, "
|
|
30
|
-
assertValidNumber(this._private_directionInternal.y, "
|
|
29
|
+
this._private_directionInternal = resolveFXVector3Config(value);
|
|
30
|
+
assertValidNumber(this._private_directionInternal.x, "FXBehaviorDirectionalGravity.direction.x");
|
|
31
|
+
assertValidNumber(this._private_directionInternal.y, "FXBehaviorDirectionalGravity.direction.y");
|
|
32
|
+
assertValidNumber(this._private_directionInternal.z, "FXBehaviorDirectionalGravity.direction.z");
|
|
31
33
|
}
|
|
32
34
|
/** @internal */
|
|
33
35
|
update(properties, instanceCount, deltaTime) {
|
|
@@ -35,10 +37,12 @@ class FXBehaviorDirectionalGravity extends FXBehaviorModule {
|
|
|
35
37
|
const { array, itemSize } = builtin;
|
|
36
38
|
const offsetX = this._private_directionInternal.x * deltaTime;
|
|
37
39
|
const offsetY = this._private_directionInternal.y * deltaTime;
|
|
40
|
+
const offsetZ = this._private_directionInternal.z * deltaTime;
|
|
38
41
|
for (let i = 0; i < instanceCount; i++) {
|
|
39
42
|
const itemOffset = i * itemSize;
|
|
40
43
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] += offsetX;
|
|
41
44
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] += offsetY;
|
|
45
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] += offsetZ;
|
|
42
46
|
}
|
|
43
47
|
builtin.needsUpdate = true;
|
|
44
48
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MathUtils } from 'three';
|
|
2
2
|
import { assertValidNumber } from '../miscellaneous/asserts.js';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveFXVector3Config, resolveFXRangeConfig, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z } from '../miscellaneous/miscellaneous.js';
|
|
4
4
|
import { FXBehaviorModule } from './FXBehaviorModule.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -19,18 +19,19 @@ class FXBehaviorPointGravity extends FXBehaviorModule {
|
|
|
19
19
|
super();
|
|
20
20
|
/** @internal */
|
|
21
21
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
22
|
-
this._private_centerInternal =
|
|
23
|
-
assertValidNumber(this._private_centerInternal.x, "
|
|
24
|
-
assertValidNumber(this._private_centerInternal.y, "
|
|
22
|
+
this._private_centerInternal = resolveFXVector3Config(center);
|
|
23
|
+
assertValidNumber(this._private_centerInternal.x, "FXBehaviorPointGravity.constructor.center.x");
|
|
24
|
+
assertValidNumber(this._private_centerInternal.y, "FXBehaviorPointGravity.constructor.center.y");
|
|
25
|
+
assertValidNumber(this._private_centerInternal.z, "FXBehaviorPointGravity.constructor.center.z");
|
|
25
26
|
this._private_strengthInternal = resolveFXRangeConfig(strength);
|
|
26
|
-
assertValidNumber(this._private_strengthInternal.min, "
|
|
27
|
-
assertValidNumber(this._private_strengthInternal.max, "
|
|
27
|
+
assertValidNumber(this._private_strengthInternal.min, "FXBehaviorPointGravity.constructor.strength.min");
|
|
28
|
+
assertValidNumber(this._private_strengthInternal.max, "FXBehaviorPointGravity.constructor.strength.max");
|
|
28
29
|
this._private_exponentInternal = resolveFXRangeConfig(exponent);
|
|
29
|
-
assertValidNumber(this._private_exponentInternal.min, "
|
|
30
|
-
assertValidNumber(this._private_exponentInternal.max, "
|
|
30
|
+
assertValidNumber(this._private_exponentInternal.min, "FXBehaviorPointGravity.constructor.exponent.min");
|
|
31
|
+
assertValidNumber(this._private_exponentInternal.max, "FXBehaviorPointGravity.constructor.exponent.max");
|
|
31
32
|
this._private_thresholdInternal = resolveFXRangeConfig(threshold);
|
|
32
|
-
assertValidNumber(this._private_thresholdInternal.min, "
|
|
33
|
-
assertValidNumber(this._private_thresholdInternal.max, "
|
|
33
|
+
assertValidNumber(this._private_thresholdInternal.min, "FXBehaviorPointGravity.constructor.threshold.min");
|
|
34
|
+
assertValidNumber(this._private_thresholdInternal.max, "FXBehaviorPointGravity.constructor.threshold.max");
|
|
34
35
|
}
|
|
35
36
|
/** Gravity center position */
|
|
36
37
|
get center() {
|
|
@@ -50,27 +51,28 @@ class FXBehaviorPointGravity extends FXBehaviorModule {
|
|
|
50
51
|
}
|
|
51
52
|
/** Gravity center position */
|
|
52
53
|
set center(value) {
|
|
53
|
-
this._private_centerInternal =
|
|
54
|
-
assertValidNumber(this._private_centerInternal.x, "
|
|
55
|
-
assertValidNumber(this._private_centerInternal.y, "
|
|
54
|
+
this._private_centerInternal = resolveFXVector3Config(value);
|
|
55
|
+
assertValidNumber(this._private_centerInternal.x, "FXBehaviorPointGravity.center.x");
|
|
56
|
+
assertValidNumber(this._private_centerInternal.y, "FXBehaviorPointGravity.center.y");
|
|
57
|
+
assertValidNumber(this._private_centerInternal.z, "FXBehaviorPointGravity.center.z");
|
|
56
58
|
}
|
|
57
59
|
/** Force multiplier range */
|
|
58
60
|
set strength(value) {
|
|
59
61
|
this._private_strengthInternal = resolveFXRangeConfig(value);
|
|
60
|
-
assertValidNumber(this._private_strengthInternal.min, "
|
|
61
|
-
assertValidNumber(this._private_strengthInternal.max, "
|
|
62
|
+
assertValidNumber(this._private_strengthInternal.min, "FXBehaviorPointGravity.strength.min");
|
|
63
|
+
assertValidNumber(this._private_strengthInternal.max, "FXBehaviorPointGravity.strength.max");
|
|
62
64
|
}
|
|
63
65
|
/** Distance power exponent range */
|
|
64
66
|
set exponent(value) {
|
|
65
67
|
this._private_exponentInternal = resolveFXRangeConfig(value);
|
|
66
|
-
assertValidNumber(this._private_exponentInternal.min, "
|
|
67
|
-
assertValidNumber(this._private_exponentInternal.max, "
|
|
68
|
+
assertValidNumber(this._private_exponentInternal.min, "FXBehaviorPointGravity.exponent.min");
|
|
69
|
+
assertValidNumber(this._private_exponentInternal.max, "FXBehaviorPointGravity.exponent.max");
|
|
68
70
|
}
|
|
69
71
|
/** Minimum distance range */
|
|
70
72
|
set threshold(value) {
|
|
71
73
|
this._private_thresholdInternal = resolveFXRangeConfig(value);
|
|
72
|
-
assertValidNumber(this._private_thresholdInternal.min, "
|
|
73
|
-
assertValidNumber(this._private_thresholdInternal.max, "
|
|
74
|
+
assertValidNumber(this._private_thresholdInternal.min, "FXBehaviorPointGravity.threshold.min");
|
|
75
|
+
assertValidNumber(this._private_thresholdInternal.max, "FXBehaviorPointGravity.threshold.max");
|
|
74
76
|
}
|
|
75
77
|
/** @internal */
|
|
76
78
|
update(properties, instanceCount, deltaTime) {
|
|
@@ -80,11 +82,12 @@ class FXBehaviorPointGravity extends FXBehaviorModule {
|
|
|
80
82
|
const itemOffset = i * itemSize;
|
|
81
83
|
const dx = this._private_centerInternal.x - array[itemOffset + BUILTIN_OFFSET_POSITION_X];
|
|
82
84
|
const dy = this._private_centerInternal.y - array[itemOffset + BUILTIN_OFFSET_POSITION_Y];
|
|
85
|
+
const dz = this._private_centerInternal.z - array[itemOffset + BUILTIN_OFFSET_POSITION_Z];
|
|
83
86
|
// Constant over the life of a particle but different for each particle
|
|
84
87
|
const thresholdT = array[itemOffset + BUILTIN_OFFSET_RANDOM_A];
|
|
85
88
|
const threshold = MathUtils.lerp(this.threshold.min, this.threshold.max, thresholdT);
|
|
86
89
|
const thresholdSquared = threshold * threshold;
|
|
87
|
-
const distanceSquared = dx * dx + dy * dy;
|
|
90
|
+
const distanceSquared = dx * dx + dy * dy + dz * dz;
|
|
88
91
|
if (distanceSquared < thresholdSquared) {
|
|
89
92
|
continue;
|
|
90
93
|
}
|
|
@@ -98,10 +101,13 @@ class FXBehaviorPointGravity extends FXBehaviorModule {
|
|
|
98
101
|
const forceMagnitude = strength / Math.pow(distance, exponent);
|
|
99
102
|
const directionX = dx / distance;
|
|
100
103
|
const directionY = dy / distance;
|
|
104
|
+
const directionZ = dz / distance;
|
|
101
105
|
const accelerationX = directionX * forceMagnitude;
|
|
102
106
|
const accelerationY = directionY * forceMagnitude;
|
|
107
|
+
const accelerationZ = directionZ * forceMagnitude;
|
|
103
108
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] += accelerationX * deltaTime;
|
|
104
109
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] += accelerationY * deltaTime;
|
|
110
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] += accelerationZ * deltaTime;
|
|
105
111
|
}
|
|
106
112
|
builtin.needsUpdate = true;
|
|
107
113
|
}
|
|
@@ -19,16 +19,16 @@ class FXBehaviorScaleOverLife extends FXBehaviorModule {
|
|
|
19
19
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
20
20
|
this._private_scales = [];
|
|
21
21
|
if (scales.length < 2) {
|
|
22
|
-
throw new Error("
|
|
22
|
+
throw new Error("FXBehaviorScaleOverLife.scales: the number of scale anchors must be more than two");
|
|
23
23
|
}
|
|
24
24
|
for (let i = 0; i < scales.length; i++) {
|
|
25
25
|
const scale = resolveFXRangeConfig(scales[i]);
|
|
26
|
-
assertValidNonNegativeNumber(scale.min, `
|
|
27
|
-
assertValidNonNegativeNumber(scale.max, `
|
|
26
|
+
assertValidNonNegativeNumber(scale.min, `FXBehaviorScaleOverLife.constructor.scales[${i}].min`);
|
|
27
|
+
assertValidNonNegativeNumber(scale.max, `FXBehaviorScaleOverLife.constructor.scales[${i}].max`);
|
|
28
28
|
this._private_scales.push(scale);
|
|
29
29
|
}
|
|
30
30
|
this._private_aspectInternal = resolveAspect(aspect);
|
|
31
|
-
assertValidPositiveNumber(this._private_aspectInternal, "
|
|
31
|
+
assertValidPositiveNumber(this._private_aspectInternal, "FXBehaviorScaleOverLife.constructor.aspect");
|
|
32
32
|
}
|
|
33
33
|
/** Width/height aspect ratio */
|
|
34
34
|
get aspect() {
|
|
@@ -37,7 +37,7 @@ class FXBehaviorScaleOverLife extends FXBehaviorModule {
|
|
|
37
37
|
/** Width/height aspect ratio */
|
|
38
38
|
set aspect(value) {
|
|
39
39
|
this._private_aspectInternal = resolveAspect(value);
|
|
40
|
-
assertValidPositiveNumber(this._private_aspectInternal, "
|
|
40
|
+
assertValidPositiveNumber(this._private_aspectInternal, "FXBehaviorScaleOverLife.aspect");
|
|
41
41
|
}
|
|
42
42
|
/** @internal */
|
|
43
43
|
update(properties, instanceCount) {
|
|
@@ -17,8 +17,8 @@ class FXBehaviorTorqueDamping extends FXBehaviorModule {
|
|
|
17
17
|
/** @internal */
|
|
18
18
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
19
|
this._private_dampingInternal = resolveFXRangeConfig(damping);
|
|
20
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.min, `
|
|
21
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.max, `
|
|
20
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.min, `FXBehaviorTorqueDamping.constructor.damping.min`);
|
|
21
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.max, `FXBehaviorTorqueDamping.constructor.damping.max`);
|
|
22
22
|
}
|
|
23
23
|
/** Damping coefficient range (0-1) */
|
|
24
24
|
get damping() {
|
|
@@ -27,8 +27,8 @@ class FXBehaviorTorqueDamping extends FXBehaviorModule {
|
|
|
27
27
|
/** Damping coefficient range (0-1) */
|
|
28
28
|
set damping(value) {
|
|
29
29
|
this._private_dampingInternal = resolveFXRangeConfig(value);
|
|
30
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.min, `
|
|
31
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.max, `
|
|
30
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.min, `FXBehaviorTorqueDamping.damping.min`);
|
|
31
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.max, `FXBehaviorTorqueDamping.damping.max`);
|
|
32
32
|
}
|
|
33
33
|
/** @internal */
|
|
34
34
|
update(properties, instanceCount, deltaTime) {
|
|
@@ -18,11 +18,11 @@ class FXBehaviorTorqueNoise extends FXBehaviorModule {
|
|
|
18
18
|
/** @internal */
|
|
19
19
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
20
20
|
this._private_scaleInternal = resolveFXRangeConfig(scale);
|
|
21
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, `
|
|
22
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, `
|
|
21
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, `FXBehaviorTorqueNoise.constructor.scale.min`);
|
|
22
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, `FXBehaviorTorqueNoise.constructor.scale.max`);
|
|
23
23
|
this._private_strengthInternal = resolveFXRangeConfig(strength);
|
|
24
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.min, `
|
|
25
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.max, `
|
|
24
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.min, `FXBehaviorTorqueNoise.constructor.strength.min`);
|
|
25
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.max, `FXBehaviorTorqueNoise.constructor.strength.max`);
|
|
26
26
|
}
|
|
27
27
|
/** Noise frequency range */
|
|
28
28
|
get scale() {
|
|
@@ -35,14 +35,14 @@ class FXBehaviorTorqueNoise extends FXBehaviorModule {
|
|
|
35
35
|
/** Noise frequency range */
|
|
36
36
|
set scale(value) {
|
|
37
37
|
this._private_scaleInternal = resolveFXRangeConfig(value);
|
|
38
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, `
|
|
39
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, `
|
|
38
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, `FXBehaviorTorqueNoise.scale.min`);
|
|
39
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, `FXBehaviorTorqueNoise.scale.max`);
|
|
40
40
|
}
|
|
41
41
|
/** Force multiplier range */
|
|
42
42
|
set strength(value) {
|
|
43
43
|
this._private_strengthInternal = resolveFXRangeConfig(value);
|
|
44
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.min, `
|
|
45
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.max, `
|
|
44
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.min, `FXBehaviorTorqueNoise.strength.min`);
|
|
45
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.max, `FXBehaviorTorqueNoise.strength.max`);
|
|
46
46
|
}
|
|
47
47
|
/** @internal */
|
|
48
48
|
update(properties, instanceCount, deltaTime) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MathUtils } from 'three';
|
|
2
2
|
import { assertValidNonNegativeNumber } from '../miscellaneous/asserts.js';
|
|
3
|
-
import { resolveFXRangeConfig, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y } from '../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { resolveFXRangeConfig, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z } from '../miscellaneous/miscellaneous.js';
|
|
4
4
|
import { FXBehaviorModule } from './FXBehaviorModule.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -17,8 +17,8 @@ class FXBehaviorVelocityDamping extends FXBehaviorModule {
|
|
|
17
17
|
/** @internal */
|
|
18
18
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
19
|
this._private_dampingInternal = resolveFXRangeConfig(damping);
|
|
20
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.min, `
|
|
21
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.max, `
|
|
20
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.min, `FXBehaviorVelocityDamping.constructor.damping.min`);
|
|
21
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.max, `FXBehaviorVelocityDamping.constructor.damping.max`);
|
|
22
22
|
}
|
|
23
23
|
/** Damping coefficient range (0-1) */
|
|
24
24
|
get damping() {
|
|
@@ -27,8 +27,8 @@ class FXBehaviorVelocityDamping extends FXBehaviorModule {
|
|
|
27
27
|
/** Damping coefficient range (0-1) */
|
|
28
28
|
set damping(value) {
|
|
29
29
|
this._private_dampingInternal = resolveFXRangeConfig(value);
|
|
30
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.min, `
|
|
31
|
-
assertValidNonNegativeNumber(this._private_dampingInternal.max, `
|
|
30
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.min, `FXBehaviorVelocityDamping.damping.min`);
|
|
31
|
+
assertValidNonNegativeNumber(this._private_dampingInternal.max, `FXBehaviorVelocityDamping.damping.max`);
|
|
32
32
|
}
|
|
33
33
|
/** @internal */
|
|
34
34
|
update(properties, instanceCount, deltaTime) {
|
|
@@ -41,6 +41,7 @@ class FXBehaviorVelocityDamping extends FXBehaviorModule {
|
|
|
41
41
|
const dampingFactor = Math.pow(1 - MathUtils.lerp(this._private_dampingInternal.min, this._private_dampingInternal.max, dampingT), deltaTime);
|
|
42
42
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] *= dampingFactor;
|
|
43
43
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] *= dampingFactor;
|
|
44
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] *= dampingFactor;
|
|
44
45
|
}
|
|
45
46
|
builtin.needsUpdate = true;
|
|
46
47
|
}
|
|
@@ -18,11 +18,11 @@ class FXBehaviorVelocityNoise extends FXBehaviorModule {
|
|
|
18
18
|
/** @internal */
|
|
19
19
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
20
20
|
this._private_scaleInternal = resolveFXRangeConfig(scale);
|
|
21
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, `
|
|
22
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, `
|
|
21
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, `FXBehaviorVelocityNoise.constructor.scale.min`);
|
|
22
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, `FXBehaviorVelocityNoise.constructor.scale.max`);
|
|
23
23
|
this._private_strengthInternal = resolveFXRangeConfig(strength);
|
|
24
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.min, `
|
|
25
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.max, `
|
|
24
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.min, `FXBehaviorVelocityNoise.constructor.strength.min`);
|
|
25
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.max, `FXBehaviorVelocityNoise.constructor.strength.max`);
|
|
26
26
|
}
|
|
27
27
|
/** Noise frequency range */
|
|
28
28
|
get scale() {
|
|
@@ -35,14 +35,14 @@ class FXBehaviorVelocityNoise extends FXBehaviorModule {
|
|
|
35
35
|
/** Noise frequency range */
|
|
36
36
|
set scale(value) {
|
|
37
37
|
this._private_scaleInternal = resolveFXRangeConfig(value);
|
|
38
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, `
|
|
39
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, `
|
|
38
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, `FXBehaviorVelocityNoise.scale.min`);
|
|
39
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, `FXBehaviorVelocityNoise.scale.max`);
|
|
40
40
|
}
|
|
41
41
|
/** Force multiplier range */
|
|
42
42
|
set strength(value) {
|
|
43
43
|
this._private_strengthInternal = resolveFXRangeConfig(value);
|
|
44
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.min, `
|
|
45
|
-
assertValidNonNegativeNumber(this._private_strengthInternal.max, `
|
|
44
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.min, `FXBehaviorVelocityNoise.strength.min`);
|
|
45
|
+
assertValidNonNegativeNumber(this._private_strengthInternal.max, `FXBehaviorVelocityNoise.strength.max`);
|
|
46
46
|
}
|
|
47
47
|
/** @internal */
|
|
48
48
|
update(properties, instanceCount, deltaTime) {
|
package/dist/color/FXColor.js
CHANGED
|
@@ -212,7 +212,7 @@ class FXColor {
|
|
|
212
212
|
}
|
|
213
213
|
/** Red component (0 to 1) */
|
|
214
214
|
set r(value) {
|
|
215
|
-
assertValidNumber(value, "
|
|
215
|
+
assertValidNumber(value, "FXColor.r");
|
|
216
216
|
value = MathUtils.clamp(value, 0, 1);
|
|
217
217
|
this._private_ensureRGBUpdatedFromHSL();
|
|
218
218
|
if (value !== this._private_rInternal) {
|
|
@@ -223,7 +223,7 @@ class FXColor {
|
|
|
223
223
|
}
|
|
224
224
|
/** Green component (0 to 1) */
|
|
225
225
|
set g(value) {
|
|
226
|
-
assertValidNumber(value, "
|
|
226
|
+
assertValidNumber(value, "FXColor.g");
|
|
227
227
|
value = MathUtils.clamp(value, 0, 1);
|
|
228
228
|
this._private_ensureRGBUpdatedFromHSL();
|
|
229
229
|
if (value !== this._private_gInternal) {
|
|
@@ -234,7 +234,7 @@ class FXColor {
|
|
|
234
234
|
}
|
|
235
235
|
/** Blue component (0 to 1) */
|
|
236
236
|
set b(value) {
|
|
237
|
-
assertValidNumber(value, "
|
|
237
|
+
assertValidNumber(value, "FXColor.b");
|
|
238
238
|
value = MathUtils.clamp(value, 0, 1);
|
|
239
239
|
this._private_ensureRGBUpdatedFromHSL();
|
|
240
240
|
if (value !== this._private_bInternal) {
|
|
@@ -245,7 +245,7 @@ class FXColor {
|
|
|
245
245
|
}
|
|
246
246
|
/** Alpha component (0 to 1) */
|
|
247
247
|
set a(value) {
|
|
248
|
-
assertValidNumber(value, "
|
|
248
|
+
assertValidNumber(value, "FXColor.a");
|
|
249
249
|
value = MathUtils.clamp(value, 0, 1);
|
|
250
250
|
if (value !== this._private_aInternal) {
|
|
251
251
|
this._private_aInternal = value;
|
|
@@ -254,7 +254,7 @@ class FXColor {
|
|
|
254
254
|
}
|
|
255
255
|
/** Hue component (0 to 360) */
|
|
256
256
|
set hue(value) {
|
|
257
|
-
assertValidNumber(value, "
|
|
257
|
+
assertValidNumber(value, "FXColor.hue");
|
|
258
258
|
value = MathUtils.clamp(value, 0, 360);
|
|
259
259
|
this._private_ensureHSLUpdatedFromRGB();
|
|
260
260
|
if (value !== this._private_hInternal) {
|
|
@@ -265,7 +265,7 @@ class FXColor {
|
|
|
265
265
|
}
|
|
266
266
|
/** Saturation component (0 to 1) */
|
|
267
267
|
set saturation(value) {
|
|
268
|
-
assertValidNumber(value, "
|
|
268
|
+
assertValidNumber(value, "FXColor.saturation");
|
|
269
269
|
value = MathUtils.clamp(value, 0, 1);
|
|
270
270
|
this._private_ensureHSLUpdatedFromRGB();
|
|
271
271
|
if (value !== this._private_sInternal) {
|
|
@@ -276,7 +276,7 @@ class FXColor {
|
|
|
276
276
|
}
|
|
277
277
|
/** Lightness component (0 to 1) */
|
|
278
278
|
set lightness(value) {
|
|
279
|
-
assertValidNumber(value, "
|
|
279
|
+
assertValidNumber(value, "FXColor.lightness");
|
|
280
280
|
value = MathUtils.clamp(value, 0, 1);
|
|
281
281
|
this._private_ensureHSLUpdatedFromRGB();
|
|
282
282
|
if (value !== this._private_lInternal) {
|
|
@@ -312,14 +312,14 @@ class FXColor {
|
|
|
312
312
|
if (typeof firstArgument === "number") {
|
|
313
313
|
return this.setHexRGB(firstArgument, a);
|
|
314
314
|
}
|
|
315
|
-
throw new Error("
|
|
315
|
+
throw new Error("FXColor.set: cannot create color from provided arguments");
|
|
316
316
|
}
|
|
317
317
|
/** Sets RGBA components directly */
|
|
318
318
|
setRGBA(r, g, b, a = this._private_aInternal) {
|
|
319
|
-
assertValidNumber(r, "
|
|
320
|
-
assertValidNumber(g, "
|
|
321
|
-
assertValidNumber(b, "
|
|
322
|
-
assertValidNumber(a, "
|
|
319
|
+
assertValidNumber(r, "FXColor.setRGBA.r");
|
|
320
|
+
assertValidNumber(g, "FXColor.setRGBA.g");
|
|
321
|
+
assertValidNumber(b, "FXColor.setRGBA.b");
|
|
322
|
+
assertValidNumber(a, "FXColor.setRGBA.a");
|
|
323
323
|
r = MathUtils.clamp(r, 0, 1);
|
|
324
324
|
g = MathUtils.clamp(g, 0, 1);
|
|
325
325
|
b = MathUtils.clamp(b, 0, 1);
|
|
@@ -365,7 +365,7 @@ class FXColor {
|
|
|
365
365
|
}
|
|
366
366
|
/** Sets from 32-bit hex RGBA (RRGGBBAA) */
|
|
367
367
|
setHexRGBA(hex) {
|
|
368
|
-
assertValidNumber(hex, "
|
|
368
|
+
assertValidNumber(hex, "FXColor.setHexRGBA.hex");
|
|
369
369
|
this._private_ensureRGBUpdatedFromHSL();
|
|
370
370
|
const r = ((hex >> 16) & 0xff) / 255;
|
|
371
371
|
const g = ((hex >> 8) & 0xff) / 255;
|
|
@@ -386,8 +386,8 @@ class FXColor {
|
|
|
386
386
|
}
|
|
387
387
|
/** Sets from 24-bit hex RGB (RRGGBB) */
|
|
388
388
|
setHexRGB(hex, a = this._private_aInternal) {
|
|
389
|
-
assertValidNumber(hex, "
|
|
390
|
-
assertValidNumber(a, "
|
|
389
|
+
assertValidNumber(hex, "FXColor.setHexRGB.hex");
|
|
390
|
+
assertValidNumber(a, "FXColor.setHexRGB.a");
|
|
391
391
|
a = MathUtils.clamp(a, 0, 1);
|
|
392
392
|
this._private_ensureRGBUpdatedFromHSL();
|
|
393
393
|
const r = ((hex >> 16) & 0xff) / 255;
|
|
@@ -414,33 +414,33 @@ class FXColor {
|
|
|
414
414
|
*/
|
|
415
415
|
setHexString(hexString) {
|
|
416
416
|
if (!hexString.startsWith("#")) {
|
|
417
|
-
throw new Error(`
|
|
417
|
+
throw new Error(`FXColor.setHexString.hexString: invalid hex string format. expected format: "#ffffff" or "#ffffffff"`);
|
|
418
418
|
}
|
|
419
419
|
const hex = hexString.slice(1);
|
|
420
420
|
if (hex.length === 6) {
|
|
421
421
|
const hexNumber = parseInt(hex, 16);
|
|
422
422
|
if (isNaN(hexNumber)) {
|
|
423
|
-
throw new Error(`
|
|
423
|
+
throw new Error(`FXColor.setHexString.hexString: invalid hex string format. expected format: "#ffffff" or "#ffffffff"`);
|
|
424
424
|
}
|
|
425
425
|
return this.setHexRGB(hexNumber);
|
|
426
426
|
}
|
|
427
427
|
else if (hex.length === 8) {
|
|
428
428
|
const hexNumber = parseInt(hex, 16);
|
|
429
429
|
if (isNaN(hexNumber)) {
|
|
430
|
-
throw new Error(`
|
|
430
|
+
throw new Error(`FXColor.setHexString.hexString: invalid hex string format. expected format: "#ffffff" or "#ffffffff"`);
|
|
431
431
|
}
|
|
432
432
|
return this.setHexRGBA(hexNumber);
|
|
433
433
|
}
|
|
434
434
|
else {
|
|
435
|
-
throw new Error(`
|
|
435
|
+
throw new Error(`FXColor.setHexString.hexString: invalid hex string format. expected format: "#ffffff" or "#ffffffff"`);
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
/** Sets from HSL values */
|
|
439
439
|
setHSL(h, s, l, a = this._private_aInternal) {
|
|
440
|
-
assertValidNumber(h, "
|
|
441
|
-
assertValidNumber(s, "
|
|
442
|
-
assertValidNumber(l, "
|
|
443
|
-
assertValidNumber(a, "
|
|
440
|
+
assertValidNumber(h, "FXColor.setHSL.h");
|
|
441
|
+
assertValidNumber(s, "FXColor.setHSL.s");
|
|
442
|
+
assertValidNumber(l, "FXColor.setHSL.l");
|
|
443
|
+
assertValidNumber(a, "FXColor.setHSL.a");
|
|
444
444
|
h = MathUtils.clamp(h, 0, 360);
|
|
445
445
|
s = MathUtils.clamp(s, 0, 1);
|
|
446
446
|
l = MathUtils.clamp(l, 0, 1);
|
|
@@ -465,21 +465,21 @@ class FXColor {
|
|
|
465
465
|
* @throws If color name unknown
|
|
466
466
|
*/
|
|
467
467
|
setColorName(colorName, a = this._private_aInternal) {
|
|
468
|
-
assertValidNumber(a, "
|
|
468
|
+
assertValidNumber(a, "FXColor.setColorName.a");
|
|
469
469
|
a = MathUtils.clamp(a, 0, 1);
|
|
470
470
|
const normalizedName = colorName.toLowerCase().trim();
|
|
471
471
|
const hex = COLORS[normalizedName];
|
|
472
472
|
if (hex === undefined) {
|
|
473
|
-
throw new Error(`
|
|
473
|
+
throw new Error(`FXColor.setColorName.colorName: unknown color name`);
|
|
474
474
|
}
|
|
475
475
|
return this.setHexRGB(hex, a);
|
|
476
476
|
}
|
|
477
477
|
/** Sets from linear (GLSL) color space */
|
|
478
478
|
setGLSLColor(r, g, b, a = this._private_aInternal) {
|
|
479
|
-
assertValidNumber(r, "
|
|
480
|
-
assertValidNumber(g, "
|
|
481
|
-
assertValidNumber(b, "
|
|
482
|
-
assertValidNumber(a, "
|
|
479
|
+
assertValidNumber(r, "FXColor.setGLSLColor.r");
|
|
480
|
+
assertValidNumber(g, "FXColor.setGLSLColor.g");
|
|
481
|
+
assertValidNumber(b, "FXColor.setGLSLColor.b");
|
|
482
|
+
assertValidNumber(a, "FXColor.setGLSLColor.a");
|
|
483
483
|
a = MathUtils.clamp(a, 0, 1);
|
|
484
484
|
this._private_ensureRGBUpdatedFromHSL();
|
|
485
485
|
const sRGBR = linearToSRGB(r);
|
|
@@ -500,7 +500,7 @@ class FXColor {
|
|
|
500
500
|
}
|
|
501
501
|
/** Sets from Three.js Color */
|
|
502
502
|
setThreeColor(threeColor, a = this._private_aInternal) {
|
|
503
|
-
assertValidNumber(a, "
|
|
503
|
+
assertValidNumber(a, "FXColor.setThreeColor.a");
|
|
504
504
|
a = MathUtils.clamp(a, 0, 1);
|
|
505
505
|
this._private_ensureRGBUpdatedFromHSL();
|
|
506
506
|
if (threeColor.r !== this._private_rInternal ||
|
|
@@ -2,7 +2,7 @@ import { Object3D, NormalBlending } from 'three';
|
|
|
2
2
|
import { FXInstancedParticle } from '../instancedParticle/FXInstancedParticle.js';
|
|
3
3
|
import { collectProperties, collectUniforms } from '../instancedParticle/FXInstancedParticle.Internal.js';
|
|
4
4
|
import { resolveGLSLTypeInfo } from '../instancedParticle/shared.js';
|
|
5
|
-
import { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C } from '../miscellaneous/miscellaneous.js';
|
|
5
|
+
import { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_VELOCITY_Z, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C } from '../miscellaneous/miscellaneous.js';
|
|
6
6
|
import { EMITTER_DEFAULT_EXPECTED_CAPACITY, EMITTER_DEFAULT_CAPACITY_STEP, EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES } from './FXEmitter.Internal.js';
|
|
7
7
|
|
|
8
8
|
const INSTANCES = new Array();
|
|
@@ -58,6 +58,8 @@ class FXEmitter extends Object3D {
|
|
|
58
58
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] * deltaTime;
|
|
59
59
|
array[itemOffset + BUILTIN_OFFSET_POSITION_Y] +=
|
|
60
60
|
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] * deltaTime;
|
|
61
|
+
array[itemOffset + BUILTIN_OFFSET_POSITION_Z] +=
|
|
62
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] * deltaTime;
|
|
61
63
|
}
|
|
62
64
|
builtin.needsUpdate = true;
|
|
63
65
|
}
|
|
@@ -73,11 +75,11 @@ class FXEmitter extends Object3D {
|
|
|
73
75
|
const collectedProperties = {
|
|
74
76
|
builtin: resolveGLSLTypeInfo("Matrix4"),
|
|
75
77
|
};
|
|
76
|
-
collectProperties(collectedProperties, _private_spawnSequence, "
|
|
77
|
-
collectProperties(collectedProperties, _private_behaviorSequence, "
|
|
78
|
-
collectProperties(collectedProperties, _private_renderingSequence, "
|
|
78
|
+
collectProperties(collectedProperties, _private_spawnSequence, "FXEmitter.constructor.spawnSequence:");
|
|
79
|
+
collectProperties(collectedProperties, _private_behaviorSequence, "FXEmitter.constructor.behaviorSequence");
|
|
80
|
+
collectProperties(collectedProperties, _private_renderingSequence, "FXEmitter.constructor.renderingSequence");
|
|
79
81
|
const collectedUniforms = {};
|
|
80
|
-
collectUniforms(collectedUniforms, _private_renderingSequence, "
|
|
82
|
+
collectUniforms(collectedUniforms, _private_renderingSequence, "FXEmitter.constructor.renderingSequence");
|
|
81
83
|
this._private_mesh = new FXInstancedParticle(this._private_renderingSequence.map((module) => module.source), collectedUniforms, collectedProperties, options.expectedCapacity ?? EMITTER_DEFAULT_EXPECTED_CAPACITY, options.capacityStep ?? EMITTER_DEFAULT_CAPACITY_STEP, options.blending ?? NormalBlending, options.useAlphaHashing ?? false);
|
|
82
84
|
this.automaticallyDestroyModules =
|
|
83
85
|
options.automaticallyDestroyModules ?? EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES;
|
|
@@ -177,7 +177,7 @@ function buildParticleFragmentShader(uniformDeclarations, varyingDeclarations, s
|
|
|
177
177
|
discard;
|
|
178
178
|
}
|
|
179
179
|
#ifdef USE_ALPHAHASH
|
|
180
|
-
if (diffuseColor.a < getAlphaHashThreshold(
|
|
180
|
+
if (diffuseColor.a < getAlphaHashThreshold(vec3(p_builtin[0][0], p_builtin[0][1], p_builtin[0][2]))) {
|
|
181
181
|
discard;
|
|
182
182
|
}
|
|
183
183
|
#endif
|
|
@@ -2,7 +2,7 @@ import type { IUniform, ShaderMaterial } from "three";
|
|
|
2
2
|
import { Matrix3, Matrix4, Texture, Vector2, Vector3, Vector4 } from "three";
|
|
3
3
|
import { FXColor } from "../color/FXColor";
|
|
4
4
|
export type FXProperty = Texture | FXColor | Vector2 | Vector3 | Vector4 | Matrix3 | Matrix4 | number;
|
|
5
|
-
export type FXPropertyName = "Texture" | "
|
|
5
|
+
export type FXPropertyName = "Texture" | "FXColor" | "Vector2" | "Vector3" | "Vector4" | "Matrix3" | "Matrix4" | "number";
|
|
6
6
|
export type FXPropertyConstructor = typeof Texture | typeof FXColor | typeof Vector2 | typeof Vector3 | typeof Vector4 | typeof Matrix3 | typeof Matrix4 | NumberConstructor;
|
|
7
7
|
export type FXPropertyCopyTo = FXColor | Vector2 | Vector3 | Vector4 | Matrix3 | Matrix4;
|
|
8
8
|
export type FXPropertyCopyFrom = FXColor & Vector2 & Vector3 & Vector4 & Matrix3 & Matrix4;
|
|
@@ -15,11 +15,5 @@ export interface GLProperty {
|
|
|
15
15
|
value: FXProperty;
|
|
16
16
|
glslTypeInfo: GLTypeInfo;
|
|
17
17
|
}
|
|
18
|
-
export interface PlaneData {
|
|
19
|
-
source: string;
|
|
20
|
-
properties: Record<string, GLProperty>;
|
|
21
|
-
transform: Matrix4;
|
|
22
|
-
visibility: boolean;
|
|
23
|
-
}
|
|
24
18
|
export declare function resolveGLSLTypeInfo(value: FXProperty | FXPropertyName | FXPropertyConstructor): GLTypeInfo;
|
|
25
19
|
export declare function resolvePropertyUniform(name: string, material: ShaderMaterial): IUniform<FXProperty>;
|
|
@@ -11,7 +11,7 @@ export type FXRangeConfig = FXRange | [number, number, number] | number;
|
|
|
11
11
|
export type FXVector3Config = Vector3Like | Vector3Tuple | number;
|
|
12
12
|
export type FXAspectConfig = number | FXTextureConfig;
|
|
13
13
|
export declare function resolveFXRangeConfig(config: FXRangeConfig): FXRange;
|
|
14
|
-
export declare function
|
|
14
|
+
export declare function resolveFXVector3Config(config: FXVector3Config): Vector3Like;
|
|
15
15
|
export declare function resolveAspect(config: number | FXTextureConfig): number;
|
|
16
16
|
export declare function generateNoise3D(x: number, y: number, z: number): number;
|
|
17
17
|
export declare function buildGradientTexture(colors: FXColor[]): DataTexture;
|
|
@@ -9,7 +9,7 @@ function resolveFXRangeConfig(config) {
|
|
|
9
9
|
}
|
|
10
10
|
return config;
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function resolveFXVector3Config(config) {
|
|
13
13
|
if (typeof config === "number") {
|
|
14
14
|
return { x: config, y: config, z: config };
|
|
15
15
|
}
|
|
@@ -63,4 +63,4 @@ const BUILTIN_OFFSET_RANDOM_A = 13;
|
|
|
63
63
|
const BUILTIN_OFFSET_RANDOM_B = 14;
|
|
64
64
|
const BUILTIN_OFFSET_RANDOM_C = 15;
|
|
65
65
|
|
|
66
|
-
export { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_LIFETIME, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_SCALE_X, BUILTIN_OFFSET_SCALE_Y, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z, buildGradientTexture, generateNoise3D, resolveAspect, resolveFXRangeConfig,
|
|
66
|
+
export { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_LIFETIME, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_SCALE_X, BUILTIN_OFFSET_SCALE_Y, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z, buildGradientTexture, generateNoise3D, resolveAspect, resolveFXRangeConfig, resolveFXVector3Config };
|
|
@@ -178,18 +178,18 @@ class FXTextureView {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
_private_setFromAtlasConfig(config) {
|
|
181
|
-
assertValidPositiveNumber(config.sourceSize.w, "
|
|
182
|
-
assertValidPositiveNumber(config.sourceSize.h, "
|
|
183
|
-
assertValidNonNegativeNumber(config.frame.x, "
|
|
184
|
-
assertValidNonNegativeNumber(config.frame.y, "
|
|
185
|
-
assertValidPositiveNumber(config.frame.w, "
|
|
186
|
-
assertValidPositiveNumber(config.frame.h, "
|
|
187
|
-
assertValidNonNegativeNumber(config.spriteSourceSize.x, "
|
|
188
|
-
assertValidNonNegativeNumber(config.spriteSourceSize.y, "
|
|
189
|
-
assertValidPositiveNumber(config.spriteSourceSize.w, "
|
|
190
|
-
assertValidPositiveNumber(config.spriteSourceSize.h, "
|
|
181
|
+
assertValidPositiveNumber(config.sourceSize.w, "FXTextureView.setFromAtlasConfig.config.sourceSize.w");
|
|
182
|
+
assertValidPositiveNumber(config.sourceSize.h, "FXTextureView.setFromAtlasConfig.config.sourceSize.h");
|
|
183
|
+
assertValidNonNegativeNumber(config.frame.x, "FXTextureView.setFromAtlasConfig.config.frame.x");
|
|
184
|
+
assertValidNonNegativeNumber(config.frame.y, "FXTextureView.setFromAtlasConfig.config.frame.y");
|
|
185
|
+
assertValidPositiveNumber(config.frame.w, "FXTextureView.setFromAtlasConfig.config.frame.w");
|
|
186
|
+
assertValidPositiveNumber(config.frame.h, "FXTextureView.setFromAtlasConfig.config.frame.h");
|
|
187
|
+
assertValidNonNegativeNumber(config.spriteSourceSize.x, "FXTextureView.setFromAtlasConfig.config.spriteSourceSize.x");
|
|
188
|
+
assertValidNonNegativeNumber(config.spriteSourceSize.y, "FXTextureView.setFromAtlasConfig.config.spriteSourceSize.y");
|
|
189
|
+
assertValidPositiveNumber(config.spriteSourceSize.w, "FXTextureView.setFromAtlasConfig.config.spriteSourceSize.w");
|
|
190
|
+
assertValidPositiveNumber(config.spriteSourceSize.h, "FXTextureView.setFromAtlasConfig.config.spriteSourceSize.h");
|
|
191
191
|
if (config.scale !== undefined) {
|
|
192
|
-
assertValidPositiveNumber(config.scale, "
|
|
192
|
+
assertValidPositiveNumber(config.scale, "FXTextureView.setFromAtlasConfig.config.scale");
|
|
193
193
|
}
|
|
194
194
|
this._private_sourceWidth = config.sourceSize.w;
|
|
195
195
|
this._private_sourceHeight = config.sourceSize.h;
|
|
@@ -20,7 +20,7 @@ class FXRenderingColorOverLife extends FXRenderingModule {
|
|
|
20
20
|
/** @internal */
|
|
21
21
|
this.source = source;
|
|
22
22
|
if (colors.length === 0) {
|
|
23
|
-
throw new Error("
|
|
23
|
+
throw new Error("FXRenderingColorOverLife: colors array cannot be empty");
|
|
24
24
|
}
|
|
25
25
|
this.requiredUniforms = { colorOverLifeTexture: buildGradientTexture(colors) };
|
|
26
26
|
}
|
|
@@ -21,10 +21,10 @@ class FXRenderingColorOverVelocity extends FXRenderingModule {
|
|
|
21
21
|
/** @internal */
|
|
22
22
|
this.source = source;
|
|
23
23
|
if (colors.length === 0) {
|
|
24
|
-
throw new Error("
|
|
24
|
+
throw new Error("FXRenderingColorOverVelocity: colors array cannot be empty");
|
|
25
25
|
}
|
|
26
26
|
if (maxVelocity <= 0) {
|
|
27
|
-
throw new Error("
|
|
27
|
+
throw new Error("FXRenderingColorOverVelocity: maxVelocity must be greater than 0");
|
|
28
28
|
}
|
|
29
29
|
this.source = source.replace("COLOR_OVER_VELOCITY_MAX", `COLOR_OVER_VELOCITY_MAX ${maxVelocity.toFixed(2)}`);
|
|
30
30
|
this.requiredUniforms = { colorOverVelocityTexture: buildGradientTexture(colors) };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var source = "#define COLOR_OVER_VELOCITY_MAX vec4 draw(){float t=clamp(length(
|
|
1
|
+
var source = "#define COLOR_OVER_VELOCITY_MAX vec4 draw(){float t=clamp(length(vec3(PARTICLE_VELOCITY_X,PARTICLE_VELOCITY_Y,PARTICLE_VELOCITY_Z))/COLOR_OVER_VELOCITY_MAX,0.0,1.0);return srgbTexture2D(p_colorOverVelocityTexture,vec2(t,0.5));}";
|
|
2
2
|
|
|
3
3
|
export { source as default };
|
|
@@ -2,7 +2,7 @@ import type { Vector3Like } from "../miscellaneous/math";
|
|
|
2
2
|
import { type FXVector3Config } from "../miscellaneous/miscellaneous";
|
|
3
3
|
import { FXSpawnModule } from "./FXSpawnModule";
|
|
4
4
|
/**
|
|
5
|
-
* Spawns particles at random positions within
|
|
5
|
+
* Spawns particles at random positions within an axis-aligned bounding box.
|
|
6
6
|
*
|
|
7
7
|
* Position is chosen uniformly within the bounds defined by min and max.
|
|
8
8
|
*/
|
|
@@ -12,16 +12,16 @@ export declare class FXSpawnBox extends FXSpawnModule<{
|
|
|
12
12
|
private minInternal;
|
|
13
13
|
private maxInternal;
|
|
14
14
|
/**
|
|
15
|
-
* @param min -
|
|
16
|
-
* @param max -
|
|
15
|
+
* @param min - Minimum corner of the spawn box
|
|
16
|
+
* @param max - Maximum corner of the spawn box
|
|
17
17
|
*/
|
|
18
18
|
constructor(min?: FXVector3Config, max?: FXVector3Config);
|
|
19
|
-
/**
|
|
19
|
+
/** Minimum corner of the spawn box */
|
|
20
20
|
get min(): Vector3Like;
|
|
21
|
-
/**
|
|
21
|
+
/** Maximum corner of the spawn box */
|
|
22
22
|
get max(): Vector3Like;
|
|
23
|
-
/**
|
|
23
|
+
/** Minimum corner of the spawn box */
|
|
24
24
|
set min(value: FXVector3Config);
|
|
25
|
-
/**
|
|
25
|
+
/** Maximum corner of the spawn box */
|
|
26
26
|
set max(value: FXVector3Config);
|
|
27
27
|
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
import { MathUtils } from 'three';
|
|
2
2
|
import { assertValidNumber } from '../miscellaneous/asserts.js';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveFXVector3Config, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z } from '../miscellaneous/miscellaneous.js';
|
|
4
4
|
import { FXSpawnModule } from './FXSpawnModule.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Spawns particles at random positions within
|
|
7
|
+
* Spawns particles at random positions within an axis-aligned bounding box.
|
|
8
8
|
*
|
|
9
9
|
* Position is chosen uniformly within the bounds defined by min and max.
|
|
10
10
|
*/
|
|
11
11
|
class FXSpawnBox extends FXSpawnModule {
|
|
12
12
|
/**
|
|
13
|
-
* @param min -
|
|
14
|
-
* @param max -
|
|
13
|
+
* @param min - Minimum corner of the spawn box
|
|
14
|
+
* @param max - Maximum corner of the spawn box
|
|
15
15
|
*/
|
|
16
16
|
constructor(min = { x: -1, y: -1, z: -1 }, max = { x: 1, y: 1, z: 1 }) {
|
|
17
17
|
super();
|
|
18
18
|
/** @internal */
|
|
19
19
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
20
|
-
this._private_minInternal =
|
|
21
|
-
this._private_maxInternal =
|
|
22
|
-
assertValidNumber(this._private_minInternal.x, "
|
|
23
|
-
assertValidNumber(this._private_minInternal.y, "
|
|
24
|
-
assertValidNumber(this._private_minInternal.z, "
|
|
25
|
-
assertValidNumber(this._private_maxInternal.x, "
|
|
26
|
-
assertValidNumber(this._private_maxInternal.y, "
|
|
27
|
-
assertValidNumber(this._private_maxInternal.z, "
|
|
20
|
+
this._private_minInternal = resolveFXVector3Config(min);
|
|
21
|
+
this._private_maxInternal = resolveFXVector3Config(max);
|
|
22
|
+
assertValidNumber(this._private_minInternal.x, "FXSpawnBox.constructor.min.x");
|
|
23
|
+
assertValidNumber(this._private_minInternal.y, "FXSpawnBox.constructor.min.y");
|
|
24
|
+
assertValidNumber(this._private_minInternal.z, "FXSpawnBox.constructor.min.z");
|
|
25
|
+
assertValidNumber(this._private_maxInternal.x, "FXSpawnBox.constructor.max.x");
|
|
26
|
+
assertValidNumber(this._private_maxInternal.y, "FXSpawnBox.constructor.max.y");
|
|
27
|
+
assertValidNumber(this._private_maxInternal.z, "FXSpawnBox.constructor.max.z");
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
29
|
+
/** Minimum corner of the spawn box */
|
|
30
30
|
get min() {
|
|
31
31
|
return this._private_minInternal;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
33
|
+
/** Maximum corner of the spawn box */
|
|
34
34
|
get max() {
|
|
35
35
|
return this._private_maxInternal;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
37
|
+
/** Minimum corner of the spawn box */
|
|
38
38
|
set min(value) {
|
|
39
|
-
this._private_minInternal =
|
|
40
|
-
assertValidNumber(this._private_minInternal.x, "
|
|
41
|
-
assertValidNumber(this._private_minInternal.y, "
|
|
42
|
-
assertValidNumber(this._private_minInternal.z, "
|
|
39
|
+
this._private_minInternal = resolveFXVector3Config(value);
|
|
40
|
+
assertValidNumber(this._private_minInternal.x, "FXSpawnBox.min.x");
|
|
41
|
+
assertValidNumber(this._private_minInternal.y, "FXSpawnBox.min.y");
|
|
42
|
+
assertValidNumber(this._private_minInternal.z, "FXSpawnBox.min.z");
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
44
|
+
/** Maximum corner of the spawn box */
|
|
45
45
|
set max(value) {
|
|
46
|
-
this._private_maxInternal =
|
|
47
|
-
assertValidNumber(this._private_maxInternal.x, "
|
|
48
|
-
assertValidNumber(this._private_maxInternal.y, "
|
|
49
|
-
assertValidNumber(this._private_maxInternal.z, "
|
|
46
|
+
this._private_maxInternal = resolveFXVector3Config(value);
|
|
47
|
+
assertValidNumber(this._private_maxInternal.x, "FXSpawnBox.max.x");
|
|
48
|
+
assertValidNumber(this._private_maxInternal.y, "FXSpawnBox.max.y");
|
|
49
|
+
assertValidNumber(this._private_maxInternal.z, "FXSpawnBox.max.z");
|
|
50
50
|
}
|
|
51
51
|
/** @internal */
|
|
52
52
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertValidNumber } from '../miscellaneous/asserts.js';
|
|
2
|
-
import {
|
|
2
|
+
import { resolveFXVector3Config, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z } from '../miscellaneous/miscellaneous.js';
|
|
3
3
|
import { FXSpawnModule } from './FXSpawnModule.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,10 +15,10 @@ class FXSpawnOffset extends FXSpawnModule {
|
|
|
15
15
|
super();
|
|
16
16
|
/** @internal */
|
|
17
17
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
18
|
-
this._private_offsetInternal =
|
|
19
|
-
assertValidNumber(this._private_offsetInternal.x, "
|
|
20
|
-
assertValidNumber(this._private_offsetInternal.y, "
|
|
21
|
-
assertValidNumber(this._private_offsetInternal.z, "
|
|
18
|
+
this._private_offsetInternal = resolveFXVector3Config(offset);
|
|
19
|
+
assertValidNumber(this._private_offsetInternal.x, "FXSpawnOffset.constructor.offset.x");
|
|
20
|
+
assertValidNumber(this._private_offsetInternal.y, "FXSpawnOffset.constructor.offset.y");
|
|
21
|
+
assertValidNumber(this._private_offsetInternal.z, "FXSpawnOffset.constructor.offset.z");
|
|
22
22
|
}
|
|
23
23
|
/** Particle position offset */
|
|
24
24
|
get offset() {
|
|
@@ -26,10 +26,10 @@ class FXSpawnOffset extends FXSpawnModule {
|
|
|
26
26
|
}
|
|
27
27
|
/** Particle position offset */
|
|
28
28
|
set offset(value) {
|
|
29
|
-
this._private_offsetInternal =
|
|
30
|
-
assertValidNumber(this._private_offsetInternal.x, "
|
|
31
|
-
assertValidNumber(this._private_offsetInternal.y, "
|
|
32
|
-
assertValidNumber(this._private_offsetInternal.z, "
|
|
29
|
+
this._private_offsetInternal = resolveFXVector3Config(value);
|
|
30
|
+
assertValidNumber(this._private_offsetInternal.x, "FXSpawnOffset.offset.x");
|
|
31
|
+
assertValidNumber(this._private_offsetInternal.y, "FXSpawnOffset.offset.y");
|
|
32
|
+
assertValidNumber(this._private_offsetInternal.z, "FXSpawnOffset.offset.z");
|
|
33
33
|
}
|
|
34
34
|
/** @internal */
|
|
35
35
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
@@ -17,8 +17,8 @@ class FXSpawnRandomLifetime extends FXSpawnModule {
|
|
|
17
17
|
/** @internal */
|
|
18
18
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
19
|
this._private_lifetimeInternal = resolveFXRangeConfig(lifetime);
|
|
20
|
-
assertValidPositiveNumber(this._private_lifetimeInternal.min, "
|
|
21
|
-
assertValidPositiveNumber(this._private_lifetimeInternal.max, "
|
|
20
|
+
assertValidPositiveNumber(this._private_lifetimeInternal.min, "FXSpawnRandomLifetime.constructor.lifetime.min");
|
|
21
|
+
assertValidPositiveNumber(this._private_lifetimeInternal.max, "FXSpawnRandomLifetime.constructor.lifetime.max");
|
|
22
22
|
}
|
|
23
23
|
/** Lifetime range in seconds */
|
|
24
24
|
get lifetime() {
|
|
@@ -27,8 +27,8 @@ class FXSpawnRandomLifetime extends FXSpawnModule {
|
|
|
27
27
|
/** Lifetime range in seconds */
|
|
28
28
|
set lifetime(value) {
|
|
29
29
|
this._private_lifetimeInternal = resolveFXRangeConfig(value);
|
|
30
|
-
assertValidPositiveNumber(this._private_lifetimeInternal.min, "
|
|
31
|
-
assertValidPositiveNumber(this._private_lifetimeInternal.max, "
|
|
30
|
+
assertValidPositiveNumber(this._private_lifetimeInternal.min, "FXSpawnRandomLifetime.lifetime.min");
|
|
31
|
+
assertValidPositiveNumber(this._private_lifetimeInternal.max, "FXSpawnRandomLifetime.lifetime.max");
|
|
32
32
|
}
|
|
33
33
|
/** @internal */
|
|
34
34
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
@@ -17,8 +17,8 @@ class FXSpawnRandomRotation extends FXSpawnModule {
|
|
|
17
17
|
/** @internal */
|
|
18
18
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
19
|
this._private_rotationInternal = resolveFXRangeConfig(rotation);
|
|
20
|
-
assertValidNumber(this._private_rotationInternal.min, "
|
|
21
|
-
assertValidNumber(this._private_rotationInternal.max, "
|
|
20
|
+
assertValidNumber(this._private_rotationInternal.min, "FXSpawnRandomRotation.constructor.rotation.min");
|
|
21
|
+
assertValidNumber(this._private_rotationInternal.max, "FXSpawnRandomRotation.constructor.rotation.max");
|
|
22
22
|
}
|
|
23
23
|
/** Rotation range in radians */
|
|
24
24
|
get rotation() {
|
|
@@ -27,8 +27,8 @@ class FXSpawnRandomRotation extends FXSpawnModule {
|
|
|
27
27
|
/** Rotation range in radians */
|
|
28
28
|
set rotation(value) {
|
|
29
29
|
this._private_rotationInternal = resolveFXRangeConfig(value);
|
|
30
|
-
assertValidNumber(this._private_rotationInternal.min, "
|
|
31
|
-
assertValidNumber(this._private_rotationInternal.max, "
|
|
30
|
+
assertValidNumber(this._private_rotationInternal.min, "FXSpawnRandomRotation.rotation.min");
|
|
31
|
+
assertValidNumber(this._private_rotationInternal.max, "FXSpawnRandomRotation.rotation.max");
|
|
32
32
|
}
|
|
33
33
|
/** @internal */
|
|
34
34
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
@@ -19,9 +19,9 @@ class FXSpawnRandomScale extends FXSpawnModule {
|
|
|
19
19
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
20
20
|
this._private_scaleInternal = resolveFXRangeConfig(scale);
|
|
21
21
|
this._private_aspectInternal = resolveAspect(aspect);
|
|
22
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, "
|
|
23
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, "
|
|
24
|
-
assertValidPositiveNumber(this._private_aspectInternal, "
|
|
22
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, "FXSpawnRandomScale.constructor.scale.min");
|
|
23
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, "FXSpawnRandomScale.constructor.scale.max");
|
|
24
|
+
assertValidPositiveNumber(this._private_aspectInternal, "FXSpawnRandomScale.constructor.aspect");
|
|
25
25
|
}
|
|
26
26
|
/** Base scale range */
|
|
27
27
|
get scale() {
|
|
@@ -34,13 +34,13 @@ class FXSpawnRandomScale extends FXSpawnModule {
|
|
|
34
34
|
/** Base scale range */
|
|
35
35
|
set scale(value) {
|
|
36
36
|
this._private_scaleInternal = resolveFXRangeConfig(value);
|
|
37
|
-
assertValidPositiveNumber(this._private_scaleInternal.min, "
|
|
38
|
-
assertValidPositiveNumber(this._private_scaleInternal.max, "
|
|
37
|
+
assertValidPositiveNumber(this._private_scaleInternal.min, "FXSpawnRandomScale.scale.min");
|
|
38
|
+
assertValidPositiveNumber(this._private_scaleInternal.max, "FXSpawnRandomScale.scale.max");
|
|
39
39
|
}
|
|
40
40
|
/** Width/height aspect ratio */
|
|
41
41
|
set aspect(value) {
|
|
42
42
|
this._private_aspectInternal = resolveAspect(value);
|
|
43
|
-
assertValidPositiveNumber(this._private_aspectInternal, "
|
|
43
|
+
assertValidPositiveNumber(this._private_aspectInternal, "FXSpawnRandomScale.aspect");
|
|
44
44
|
}
|
|
45
45
|
/** @internal */
|
|
46
46
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
@@ -17,8 +17,8 @@ class FXSpawnRandomTorque extends FXSpawnModule {
|
|
|
17
17
|
/** @internal */
|
|
18
18
|
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
19
|
this._private_torqueInternal = resolveFXRangeConfig(torque);
|
|
20
|
-
assertValidNumber(this._private_torqueInternal.min, "
|
|
21
|
-
assertValidNumber(this._private_torqueInternal.max, "
|
|
20
|
+
assertValidNumber(this._private_torqueInternal.min, "FXSpawnRandomTorque.constructor.torque.min");
|
|
21
|
+
assertValidNumber(this._private_torqueInternal.max, "FXSpawnRandomTorque.constructor.torque.max");
|
|
22
22
|
}
|
|
23
23
|
/** Angular velocity range in radians/second */
|
|
24
24
|
get torque() {
|
|
@@ -27,8 +27,8 @@ class FXSpawnRandomTorque extends FXSpawnModule {
|
|
|
27
27
|
/** Angular velocity range in radians/second */
|
|
28
28
|
set torque(value) {
|
|
29
29
|
this._private_torqueInternal = resolveFXRangeConfig(value);
|
|
30
|
-
assertValidNumber(this._private_torqueInternal.min, "
|
|
31
|
-
assertValidNumber(this._private_torqueInternal.max, "
|
|
30
|
+
assertValidNumber(this._private_torqueInternal.min, "FXSpawnRandomTorque.torque.min");
|
|
31
|
+
assertValidNumber(this._private_torqueInternal.max, "FXSpawnRandomTorque.torque.max");
|
|
32
32
|
}
|
|
33
33
|
/** @internal */
|
|
34
34
|
spawn(properties, instanceBegin, instanceEnd) {
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sparcoon",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"particles",
|
|
7
|
+
"particle-system",
|
|
7
8
|
"three.js",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
9
|
+
"3d",
|
|
10
|
+
"webgl",
|
|
11
|
+
"instanced",
|
|
12
|
+
"billboard",
|
|
10
13
|
"typescript"
|
|
11
14
|
],
|
|
12
15
|
"author": "jango",
|