@newkrok/three-particles 0.2.0 → 0.3.1
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 +2 -2
- package/package.json +3 -2
- package/src/js/effects/{shaders → three-particles/shaders}/particle-system-fragment-shader.glsl.js +13 -3
- package/src/js/effects/{shaders → three-particles/shaders}/particle-system-vertex-shader.glsl.js +13 -7
- package/src/js/effects/three-particles/three-particles-curves.js +74 -0
- package/src/js/effects/three-particles/three-particles-modifiers.js +44 -0
- package/src/js/effects/{three-particles-utils.js → three-particles/three-particles-utils.js} +15 -9
- package/src/js/effects/three-particles.js +182 -84
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# THREE Particles
|
|
2
2
|
|
|
3
|
-
Particle
|
|
3
|
+
Particle system for ThreeJS
|
|
4
4
|
|
|
5
5
|
# THREE Particles Editor
|
|
6
6
|
|
|
@@ -18,4 +18,4 @@ Install with npm
|
|
|
18
18
|
`npm i @newkrok/three-particles`
|
|
19
19
|
|
|
20
20
|
Add as a package.json dependency
|
|
21
|
-
`"dependencies": { ... "@newkrok/three-particles": "0.2.
|
|
21
|
+
`"dependencies": { ... "@newkrok/three-particles": "0.2.2" ... }, `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newkrok/three-particles",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Particle system for ThreeJS",
|
|
5
5
|
"main": "src/js/three-particles.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/NewKrok/three-particles#readme",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"three": "
|
|
26
|
+
"three": "0.135.0",
|
|
27
|
+
"easing-functions": "1.0.1"
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/src/js/effects/{shaders → three-particles/shaders}/particle-system-fragment-shader.glsl.js
RENAMED
|
@@ -2,20 +2,30 @@ const ParticleSystemFragmentShader = `
|
|
|
2
2
|
uniform sampler2D map;
|
|
3
3
|
uniform float elapsed;
|
|
4
4
|
uniform float fps;
|
|
5
|
+
uniform bool useFPSForFrameIndex;
|
|
5
6
|
uniform vec2 tiles;
|
|
6
7
|
|
|
7
8
|
varying vec4 vColor;
|
|
8
|
-
varying float
|
|
9
|
+
varying float vLifetime;
|
|
10
|
+
varying float vStartLifetime;
|
|
9
11
|
varying float vRotation;
|
|
12
|
+
varying float vStartFrame;
|
|
10
13
|
|
|
11
14
|
void main()
|
|
12
15
|
{
|
|
13
16
|
gl_FragColor = vColor;
|
|
14
17
|
float mid = 0.5;
|
|
15
18
|
|
|
16
|
-
float frameIndex =
|
|
19
|
+
float frameIndex = round(vStartFrame) + (
|
|
20
|
+
useFPSForFrameIndex == true
|
|
21
|
+
? fps == 0.0
|
|
22
|
+
? 0.0
|
|
23
|
+
: max((vLifetime / 1000.0) * fps, 0.0)
|
|
24
|
+
: max(min(floor(min(vLifetime / vStartLifetime, 1.0) * (tiles.x * tiles.y)), tiles.x * tiles.y - 1.0), 0.0)
|
|
25
|
+
);
|
|
26
|
+
|
|
17
27
|
float spriteXIndex = floor(mod(frameIndex, tiles.x));
|
|
18
|
-
float spriteYIndex = floor(mod(frameIndex / tiles.
|
|
28
|
+
float spriteYIndex = floor(mod(frameIndex / tiles.x, tiles.y));
|
|
19
29
|
|
|
20
30
|
vec2 frameUV = vec2(
|
|
21
31
|
gl_PointCoord.x / tiles.x + spriteXIndex / tiles.x,
|
package/src/js/effects/{shaders → three-particles/shaders}/particle-system-vertex-shader.glsl.js
RENAMED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
const ParticleSystemVertexShader = `
|
|
2
|
-
attribute float
|
|
2
|
+
attribute float size;
|
|
3
3
|
attribute float colorR;
|
|
4
4
|
attribute float colorG;
|
|
5
5
|
attribute float colorB;
|
|
6
6
|
attribute float colorA;
|
|
7
|
-
attribute float
|
|
7
|
+
attribute float lifetime;
|
|
8
|
+
attribute float startLifetime;
|
|
8
9
|
attribute float rotation;
|
|
10
|
+
attribute float startFrame;
|
|
9
11
|
|
|
10
12
|
varying mat4 vPosition;
|
|
11
13
|
varying vec4 vColor;
|
|
12
|
-
varying float
|
|
14
|
+
varying float vLifetime;
|
|
15
|
+
varying float vStartLifetime;
|
|
13
16
|
varying float vRotation;
|
|
17
|
+
varying float vStartFrame;
|
|
14
18
|
|
|
15
19
|
void main()
|
|
16
20
|
{
|
|
17
21
|
vColor = vec4(colorR, colorG, colorB, colorA);
|
|
18
|
-
|
|
22
|
+
vLifetime = lifetime;
|
|
23
|
+
vStartLifetime = startLifetime;
|
|
19
24
|
vRotation = rotation;
|
|
25
|
+
vStartFrame = startFrame;
|
|
20
26
|
|
|
21
27
|
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
22
|
-
gl_PointSize =
|
|
28
|
+
gl_PointSize = size * (100.0 / length(mvPosition.xyz));
|
|
23
29
|
gl_Position = projectionMatrix * mvPosition;
|
|
24
30
|
}
|
|
25
|
-
|
|
31
|
+
`;
|
|
26
32
|
|
|
27
|
-
export default ParticleSystemVertexShader
|
|
33
|
+
export default ParticleSystemVertexShader;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import Easing from "easing-functions";
|
|
2
|
+
|
|
3
|
+
export const CurveFunction = {
|
|
4
|
+
LINEAR: "LINEAR",
|
|
5
|
+
QUADRATIC_IN: "QUADRATIC_IN",
|
|
6
|
+
QUADRATIC_OUT: "QUADRATIC_OUT",
|
|
7
|
+
QUADRATIC_IN_OUT: "QUADRATIC_IN_OUT",
|
|
8
|
+
CUBIC_IN: "CUBIC_IN",
|
|
9
|
+
CUBIC_OUT: "CUBIC_OUT",
|
|
10
|
+
CUBIC_IN_OUT: "CUBIC_IN_OUT",
|
|
11
|
+
QUARTIC_IN: "QUARTIC_IN",
|
|
12
|
+
QUARTIC_OUT: "QUARTIC_OUT",
|
|
13
|
+
QUARTIC_IN_OUT: "QUARTIC_IN_OUT",
|
|
14
|
+
QUINTIC_IN: "QUINTIC_IN",
|
|
15
|
+
QUINTIC_OUT: "QUINTIC_OUT",
|
|
16
|
+
QUINTIC_IN_OUT: "QUINTIC_IN_OUT",
|
|
17
|
+
SINUSOIDAL_IN: "SINUSOIDAL_IN",
|
|
18
|
+
SINUSOIDAL_OUT: "SINUSOIDAL_OUT",
|
|
19
|
+
SINUSOIDAL_IN_OUT: "SINUSOIDAL_IN_OUT",
|
|
20
|
+
EXPONENTIAL_IN: "EXPONENTIAL_IN",
|
|
21
|
+
EXPONENTIAL_OUT: "EXPONENTIAL_OUT",
|
|
22
|
+
EXPONENTIAL_IN_OUT: "EXPONENTIAL_IN_OUT",
|
|
23
|
+
CIRCULAR_IN: "CIRCULAR_IN",
|
|
24
|
+
CIRCULAR_OUT: "CIRCULAR_OUT",
|
|
25
|
+
CIRCULAR_IN_OUT: "CIRCULAR_IN_OUT",
|
|
26
|
+
ELASTIC_IN: "ELASTIC_IN",
|
|
27
|
+
ELASTIC_OUT: "ELASTIC_OUT",
|
|
28
|
+
ELASTIC_IN_OUT: "ELASTIC_IN_OUT",
|
|
29
|
+
BACK_IN: "BACK_IN",
|
|
30
|
+
BACK_OUT: "BACK_OUT",
|
|
31
|
+
BACK_IN_OUT: "BACK_IN_OUT",
|
|
32
|
+
BOUNCE_IN: "BOUNCE_IN",
|
|
33
|
+
BOUNCE_OUT: "BOUNCE_OUT",
|
|
34
|
+
BOUNCE_IN_OUT: "BOUNCE_IN_OUT",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const CurveFunctionMap = {
|
|
38
|
+
[CurveFunction.LINEAR]: Easing.Linear.None,
|
|
39
|
+
[CurveFunction.QUADRATIC_IN]: Easing.Quadratic.In,
|
|
40
|
+
[CurveFunction.QUADRATIC_OUT]: Easing.Quadratic.Out,
|
|
41
|
+
[CurveFunction.QUADRATIC_IN_OUT]: Easing.Quadratic.InOut,
|
|
42
|
+
[CurveFunction.CUBIC_IN]: Easing.Cubic.In,
|
|
43
|
+
[CurveFunction.CUBIC_OUT]: Easing.Cubic.Out,
|
|
44
|
+
[CurveFunction.CUBIC_IN_OUT]: Easing.Cubic.InOut,
|
|
45
|
+
[CurveFunction.QUARTIC_IN]: Easing.Quartic.In,
|
|
46
|
+
[CurveFunction.QUARTIC_OUT]: Easing.Quartic.Out,
|
|
47
|
+
[CurveFunction.QUARTIC_IN_OUT]: Easing.Quartic.InOut,
|
|
48
|
+
[CurveFunction.QUINTIC_IN]: Easing.Quintic.In,
|
|
49
|
+
[CurveFunction.QUINTIC_OUT]: Easing.Quintic.Out,
|
|
50
|
+
[CurveFunction.QUINTIC_IN_OUT]: Easing.Quintic.InOut,
|
|
51
|
+
[CurveFunction.SINUSOIDAL_IN]: Easing.Sinusoidal.In,
|
|
52
|
+
[CurveFunction.SINUSOIDAL_OUT]: Easing.Sinusoidal.Out,
|
|
53
|
+
[CurveFunction.SINUSOIDAL_IN_OUT]: Easing.Sinusoidal.InOut,
|
|
54
|
+
[CurveFunction.EXPONENTIAL_IN]: Easing.Exponential.In,
|
|
55
|
+
[CurveFunction.EXPONENTIAL_OUT]: Easing.Exponential.Out,
|
|
56
|
+
[CurveFunction.EXPONENTIAL_IN_OUT]: Easing.Exponential.InOut,
|
|
57
|
+
[CurveFunction.CIRCULAR_IN]: Easing.Circular.In,
|
|
58
|
+
[CurveFunction.CIRCULAR_OUT]: Easing.Circular.Out,
|
|
59
|
+
[CurveFunction.CIRCULAR_IN_OUT]: Easing.Circular.InOut,
|
|
60
|
+
[CurveFunction.ELASTIC_IN]: Easing.Elastic.In,
|
|
61
|
+
[CurveFunction.ELASTIC_OUT]: Easing.Elastic.Out,
|
|
62
|
+
[CurveFunction.ELASTIC_IN_OUT]: Easing.Elastic.InOut,
|
|
63
|
+
[CurveFunction.BACK_IN]: Easing.Back.In,
|
|
64
|
+
[CurveFunction.BACK_OUT]: Easing.Back.Out,
|
|
65
|
+
[CurveFunction.BACK_IN_OUT]: Easing.Back.InOut,
|
|
66
|
+
[CurveFunction.BOUNCE_IN]: Easing.Bounce.In,
|
|
67
|
+
[CurveFunction.BOUNCE_OUT]: Easing.Bounce.Out,
|
|
68
|
+
[CurveFunction.BOUNCE_IN_OUT]: Easing.Bounce.InOut,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const getCurveFunction = (curveFunction) =>
|
|
72
|
+
typeof curveFunction === "function"
|
|
73
|
+
? curveFunction
|
|
74
|
+
: CurveFunctionMap[curveFunction];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getCurveFunction } from "./three-particles-curves.js";
|
|
2
|
+
|
|
3
|
+
const modifiers = [
|
|
4
|
+
// {key:"colorOverLifetime", attributeKeys:["colorR", "colorG", "colorB"]},
|
|
5
|
+
{
|
|
6
|
+
key: "opacityOverLifetime",
|
|
7
|
+
attributeKeys: ["colorA"],
|
|
8
|
+
startValueKeys: ["startOpacity"],
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: "sizeOverLifetime",
|
|
12
|
+
attributeKeys: ["size"],
|
|
13
|
+
startValueKeys: ["startSize"],
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export const applyModifiers = ({
|
|
18
|
+
startValues,
|
|
19
|
+
normalizedConfig,
|
|
20
|
+
attributes,
|
|
21
|
+
particleLifetimePercentage,
|
|
22
|
+
particleIndex,
|
|
23
|
+
forceUpdate = false,
|
|
24
|
+
}) => {
|
|
25
|
+
modifiers.forEach(({ key, attributeKeys, startValueKeys }) => {
|
|
26
|
+
const modifier = normalizedConfig[key];
|
|
27
|
+
if (modifier.isActive) {
|
|
28
|
+
const multiplier = getCurveFunction(modifier.curveFunction)(
|
|
29
|
+
particleLifetimePercentage
|
|
30
|
+
);
|
|
31
|
+
attributeKeys.forEach((attributeKey, index) => {
|
|
32
|
+
attributes[attributeKey].array[particleIndex] =
|
|
33
|
+
startValues[startValueKeys[index]][particleIndex] * multiplier;
|
|
34
|
+
attributes[attributeKey].needsUpdate = true;
|
|
35
|
+
});
|
|
36
|
+
} else if (forceUpdate) {
|
|
37
|
+
attributeKeys.forEach((attributeKey, index) => {
|
|
38
|
+
attributes[attributeKey].array[particleIndex] =
|
|
39
|
+
startValues[startValueKeys[index]][particleIndex];
|
|
40
|
+
attributes[attributeKey].needsUpdate = true;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
package/src/js/effects/{three-particles-utils.js → three-particles/three-particles-utils.js}
RENAMED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import * as THREE from "three/build/three.module.js";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const patchObject = (
|
|
4
|
+
objectA,
|
|
5
|
+
objectB,
|
|
6
|
+
config = { skippedProperties: [], applyToFirstObject: false }
|
|
7
|
+
) => {
|
|
4
8
|
const result = {};
|
|
5
9
|
Object.keys(objectA).forEach((key) => {
|
|
6
|
-
|
|
7
|
-
typeof objectA[key] === "object" && objectA[key] && objectB[key]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
? 0
|
|
11
|
-
|
|
10
|
+
if (!config.skippedProperties || !config.skippedProperties.includes(key)) {
|
|
11
|
+
if (typeof objectA[key] === "object" && objectA[key] && objectB[key]) {
|
|
12
|
+
result[key] = patchObject(objectA[key], objectB[key], config);
|
|
13
|
+
} else {
|
|
14
|
+
result[key] = objectB[key] === 0 ? 0 : objectB[key] || objectA[key];
|
|
15
|
+
if (config.applyToFirstObject) objectA[key] = result[key];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
12
18
|
});
|
|
13
19
|
return result;
|
|
14
20
|
};
|
|
@@ -94,7 +100,7 @@ export const calculateRandomPositionAndVelocityOnCone = (
|
|
|
94
100
|
sinNormalizedAngle *
|
|
95
101
|
speedMultiplierByPosition *
|
|
96
102
|
randomizedSpeed,
|
|
97
|
-
|
|
103
|
+
Math.cos(normalizedAngle) * randomizedSpeed
|
|
98
104
|
);
|
|
99
105
|
};
|
|
100
106
|
|
|
@@ -151,5 +157,5 @@ export const calculateRandomPositionAndVelocityOnRectangle = (
|
|
|
151
157
|
startSpeed.min,
|
|
152
158
|
startSpeed.max
|
|
153
159
|
);
|
|
154
|
-
velocity.set(0, 0,
|
|
160
|
+
velocity.set(0, 0, randomizedSpeed);
|
|
155
161
|
};
|
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
calculateRandomPositionAndVelocityOnCone,
|
|
6
6
|
calculateRandomPositionAndVelocityOnRectangle,
|
|
7
7
|
calculateRandomPositionAndVelocityOnSphere,
|
|
8
|
-
|
|
9
|
-
} from "./three-particles-utils.js";
|
|
8
|
+
patchObject,
|
|
9
|
+
} from "./three-particles/three-particles-utils.js";
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import { CurveFunction } from "./three-particles/three-particles-curves.js";
|
|
12
|
+
import ParticleSystemFragmentShader from "./three-particles/shaders/particle-system-fragment-shader.glsl.js";
|
|
13
|
+
import ParticleSystemVertexShader from "./three-particles/shaders/particle-system-vertex-shader.glsl.js";
|
|
14
|
+
import { applyModifiers } from "./three-particles/three-particles-modifiers.js";
|
|
13
15
|
|
|
14
16
|
// Float32Array is not enough accurate when we are storing timestamp in it so we just remove unnecessary time
|
|
15
17
|
const float32Helper = 1638200000000;
|
|
@@ -29,14 +31,24 @@ export const Shape = {
|
|
|
29
31
|
RECTANGLE: "RECTANGLE",
|
|
30
32
|
};
|
|
31
33
|
|
|
34
|
+
export const TimeMode = {
|
|
35
|
+
LIFETIME: "LIFETIME",
|
|
36
|
+
FPS: "FPS",
|
|
37
|
+
};
|
|
38
|
+
|
|
32
39
|
export const getDefaultParticleSystemConfig = () =>
|
|
33
40
|
JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
|
|
34
41
|
|
|
35
42
|
const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
43
|
+
transform: {
|
|
44
|
+
position: { x: 0, y: 0, z: 0 },
|
|
45
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
46
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
47
|
+
},
|
|
36
48
|
duration: 5.0,
|
|
37
49
|
looping: true,
|
|
38
50
|
startDelay: { min: 0.0, max: 0.0 },
|
|
39
|
-
|
|
51
|
+
startLifetime: { min: 2.0, max: 2.0 },
|
|
40
52
|
startSpeed: { min: 1.0, max: 1.0 },
|
|
41
53
|
startSize: { min: 1.0, max: 1.0 },
|
|
42
54
|
startRotation: { min: 0.0, max: 0.0 },
|
|
@@ -45,7 +57,7 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
45
57
|
max: { r: 1.0, g: 1.0, b: 1.0 },
|
|
46
58
|
},
|
|
47
59
|
startOpacity: { min: 1.0, max: 1.0 },
|
|
48
|
-
gravity: 0,
|
|
60
|
+
gravity: 0.0,
|
|
49
61
|
simulationSpace: SimulationSpace.LOCAL,
|
|
50
62
|
maxParticles: 100.0,
|
|
51
63
|
emission: {
|
|
@@ -76,7 +88,24 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
76
88
|
},
|
|
77
89
|
},
|
|
78
90
|
map: null,
|
|
79
|
-
|
|
91
|
+
sizeOverLifetime: {
|
|
92
|
+
isActive: false,
|
|
93
|
+
curveFunction: CurveFunction.LINEAR,
|
|
94
|
+
},
|
|
95
|
+
/* colorOverLifetime: {
|
|
96
|
+
isActive: false,
|
|
97
|
+
curveFunction: CurveFunction.LINEAR,
|
|
98
|
+
}, */
|
|
99
|
+
opacityOverLifetime: {
|
|
100
|
+
isActive: false,
|
|
101
|
+
curveFunction: CurveFunction.LINEAR,
|
|
102
|
+
},
|
|
103
|
+
textureSheetAnimation: {
|
|
104
|
+
tiles: new THREE.Vector2(1.0, 1.0),
|
|
105
|
+
timeMode: TimeMode.LIFETIME,
|
|
106
|
+
fps: 10.0,
|
|
107
|
+
startFrame: { min: 0.0, max: 0.0 },
|
|
108
|
+
},
|
|
80
109
|
};
|
|
81
110
|
|
|
82
111
|
const createFloat32Attributes = ({
|
|
@@ -148,15 +177,25 @@ export const createParticleSystem = (
|
|
|
148
177
|
config = DEFAULT_PARTICLE_SYSTEM_CONFIG
|
|
149
178
|
) => {
|
|
150
179
|
const now = Date.now();
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
const generalData = {
|
|
181
|
+
distanceFromLastEmitByDistance: 0,
|
|
182
|
+
lastWorldPosition: new THREE.Vector3(-99999),
|
|
183
|
+
currentWorldPosition: new THREE.Vector3(-99999),
|
|
184
|
+
worldPositionChange: new THREE.Vector3(),
|
|
185
|
+
worldQuaternion: new THREE.Quaternion(),
|
|
186
|
+
lastWorldQuaternion: new THREE.Quaternion(-99999),
|
|
187
|
+
worldEuler: new THREE.Euler(),
|
|
188
|
+
gravityVelocity: new THREE.Vector3(0, 0, 0),
|
|
189
|
+
startValues: {},
|
|
190
|
+
};
|
|
154
191
|
|
|
192
|
+
const normalizedConfig = patchObject(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
|
|
155
193
|
const {
|
|
194
|
+
transform,
|
|
156
195
|
duration,
|
|
157
196
|
looping,
|
|
158
197
|
startDelay,
|
|
159
|
-
|
|
198
|
+
startLifetime,
|
|
160
199
|
startSpeed,
|
|
161
200
|
startSize,
|
|
162
201
|
startRotation,
|
|
@@ -171,7 +210,7 @@ export const createParticleSystem = (
|
|
|
171
210
|
onUpdate,
|
|
172
211
|
onComplete,
|
|
173
212
|
textureSheetAnimation,
|
|
174
|
-
} =
|
|
213
|
+
} = normalizedConfig;
|
|
175
214
|
|
|
176
215
|
const startPositions = Array.from(
|
|
177
216
|
{ length: maxParticles },
|
|
@@ -182,13 +221,15 @@ export const createParticleSystem = (
|
|
|
182
221
|
() => new THREE.Vector3()
|
|
183
222
|
);
|
|
184
223
|
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
224
|
+
const startValueKeys = ["startSize", "startOpacity"];
|
|
225
|
+
startValueKeys.forEach((key) => {
|
|
226
|
+
generalData.startValues[key] = Array.from({ length: maxParticles }, () =>
|
|
227
|
+
THREE.MathUtils.randFloat(
|
|
228
|
+
normalizedConfig[key].min,
|
|
229
|
+
normalizedConfig[key].max
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
});
|
|
192
233
|
|
|
193
234
|
const material = new THREE.ShaderMaterial({
|
|
194
235
|
uniforms: {
|
|
@@ -198,7 +239,15 @@ export const createParticleSystem = (
|
|
|
198
239
|
map: {
|
|
199
240
|
value: map,
|
|
200
241
|
},
|
|
201
|
-
|
|
242
|
+
tiles: {
|
|
243
|
+
value: textureSheetAnimation.tiles,
|
|
244
|
+
},
|
|
245
|
+
fps: {
|
|
246
|
+
value: textureSheetAnimation.fps,
|
|
247
|
+
},
|
|
248
|
+
useFPSForFrameIndex: {
|
|
249
|
+
value: textureSheetAnimation.timeMode === TimeMode.FPS,
|
|
250
|
+
},
|
|
202
251
|
},
|
|
203
252
|
vertexShader: ParticleSystemVertexShader,
|
|
204
253
|
fragmentShader: ParticleSystemFragmentShader,
|
|
@@ -235,29 +284,29 @@ export const createParticleSystem = (
|
|
|
235
284
|
|
|
236
285
|
createFloat32AttributesRequest("isActive", false);
|
|
237
286
|
createFloat32AttributesRequest("creationTime", 0);
|
|
238
|
-
createFloat32AttributesRequest("
|
|
239
|
-
createFloat32AttributesRequest("
|
|
240
|
-
THREE.MathUtils.randFloat(
|
|
287
|
+
createFloat32AttributesRequest("lifetime", 0);
|
|
288
|
+
createFloat32AttributesRequest("startLifetime", () =>
|
|
289
|
+
THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max)
|
|
290
|
+
);
|
|
291
|
+
createFloat32AttributesRequest("startFrame", () =>
|
|
292
|
+
THREE.MathUtils.randInt(
|
|
293
|
+
textureSheetAnimation.startFrame.min,
|
|
294
|
+
textureSheetAnimation.startFrame.max
|
|
295
|
+
)
|
|
241
296
|
);
|
|
242
297
|
|
|
243
298
|
createFloat32AttributesRequest("opacity", 0);
|
|
244
299
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
THREE.Math.degToRad(
|
|
251
|
-
THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
|
|
252
|
-
),
|
|
253
|
-
});
|
|
300
|
+
createFloat32AttributesRequest("rotation", () =>
|
|
301
|
+
THREE.Math.degToRad(
|
|
302
|
+
THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
|
|
303
|
+
)
|
|
304
|
+
);
|
|
254
305
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
factory: () => THREE.MathUtils.randFloat(startSize.min, startSize.max),
|
|
260
|
-
});
|
|
306
|
+
createFloat32AttributesRequest(
|
|
307
|
+
"size",
|
|
308
|
+
(_, index) => generalData.startValues.startSize[index]
|
|
309
|
+
);
|
|
261
310
|
|
|
262
311
|
createFloat32AttributesRequest("rotation", 0);
|
|
263
312
|
|
|
@@ -284,8 +333,6 @@ export const createParticleSystem = (
|
|
|
284
333
|
|
|
285
334
|
const deactivateParticle = (particleIndex) => {
|
|
286
335
|
geometry.attributes.isActive.array[particleIndex] = false;
|
|
287
|
-
geometry.attributes.lifeTime.array[particleIndex] = 0;
|
|
288
|
-
geometry.attributes.lifeTime.needsUpdate = true;
|
|
289
336
|
geometry.attributes.colorA.array[particleIndex] = 0;
|
|
290
337
|
geometry.attributes.colorA.needsUpdate = true;
|
|
291
338
|
};
|
|
@@ -312,24 +359,28 @@ export const createParticleSystem = (
|
|
|
312
359
|
colorRandomRatio * (startColor.max.b - startColor.min.b);
|
|
313
360
|
geometry.attributes.colorB.needsUpdate = true;
|
|
314
361
|
|
|
315
|
-
geometry.attributes.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
362
|
+
geometry.attributes.startFrame.array[particleIndex] =
|
|
363
|
+
THREE.MathUtils.randInt(
|
|
364
|
+
textureSheetAnimation.startFrame.min,
|
|
365
|
+
textureSheetAnimation.startFrame.max
|
|
366
|
+
);
|
|
367
|
+
geometry.attributes.startFrame.needsUpdate = true;
|
|
320
368
|
|
|
321
|
-
geometry.attributes.
|
|
322
|
-
THREE.MathUtils.randFloat(
|
|
323
|
-
geometry.attributes.
|
|
369
|
+
geometry.attributes.startLifetime.array[particleIndex] =
|
|
370
|
+
THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max) * 1000;
|
|
371
|
+
geometry.attributes.startLifetime.needsUpdate = true;
|
|
324
372
|
|
|
325
|
-
|
|
373
|
+
generalData.startValues.startSize[particleIndex] =
|
|
326
374
|
THREE.MathUtils.randFloat(startSize.min, startSize.max);
|
|
327
|
-
|
|
375
|
+
generalData.startValues.startOpacity[particleIndex] =
|
|
376
|
+
THREE.MathUtils.randFloat(startOpacity.min, startOpacity.max);
|
|
328
377
|
|
|
329
378
|
geometry.attributes.rotation.array[particleIndex] = THREE.Math.degToRad(
|
|
330
379
|
THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
|
|
331
380
|
);
|
|
381
|
+
|
|
332
382
|
geometry.attributes.rotation.needsUpdate = true;
|
|
383
|
+
geometry.attributes.colorB.needsUpdate = true;
|
|
333
384
|
|
|
334
385
|
calculatePositionAndVelocity(
|
|
335
386
|
shape,
|
|
@@ -346,21 +397,34 @@ export const createParticleSystem = (
|
|
|
346
397
|
startPositions[particleIndex].z;
|
|
347
398
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
348
399
|
|
|
349
|
-
geometry.attributes.
|
|
350
|
-
geometry.attributes.
|
|
400
|
+
geometry.attributes.lifetime.array[particleIndex] = 0;
|
|
401
|
+
geometry.attributes.lifetime.needsUpdate = true;
|
|
402
|
+
|
|
403
|
+
applyModifiers({
|
|
404
|
+
startValues: generalData.startValues,
|
|
405
|
+
normalizedConfig,
|
|
406
|
+
attributes: particleSystem.geometry.attributes,
|
|
407
|
+
particleLifetimePercentage: 0,
|
|
408
|
+
particleIndex,
|
|
409
|
+
forceUpdate: true,
|
|
410
|
+
});
|
|
351
411
|
};
|
|
352
412
|
|
|
353
413
|
const particleSystem = new THREE.Points(geometry, material);
|
|
354
414
|
particleSystem.sortParticles = true;
|
|
355
415
|
|
|
416
|
+
particleSystem.position.copy(transform.position);
|
|
417
|
+
particleSystem.rotation.x = THREE.Math.degToRad(transform.rotation.x);
|
|
418
|
+
particleSystem.rotation.y = THREE.Math.degToRad(transform.rotation.y);
|
|
419
|
+
particleSystem.rotation.z = THREE.Math.degToRad(transform.rotation.z);
|
|
420
|
+
particleSystem.scale.copy(transform.scale);
|
|
421
|
+
|
|
356
422
|
const calculatedCreationTime =
|
|
357
423
|
now + THREE.MathUtils.randFloat(startDelay.min, startDelay.max) * 1000;
|
|
358
424
|
|
|
359
425
|
createdParticleSystems.push({
|
|
360
426
|
particleSystem,
|
|
361
427
|
generalData,
|
|
362
|
-
lastWorldPosition,
|
|
363
|
-
worldPositionChange,
|
|
364
428
|
onUpdate,
|
|
365
429
|
onComplete,
|
|
366
430
|
creationTime: calculatedCreationTime,
|
|
@@ -370,6 +434,7 @@ export const createParticleSystem = (
|
|
|
370
434
|
simulationSpace,
|
|
371
435
|
gravity,
|
|
372
436
|
emission,
|
|
437
|
+
normalizedConfig,
|
|
373
438
|
iterationCount: 0,
|
|
374
439
|
velocities,
|
|
375
440
|
deactivateParticle,
|
|
@@ -389,14 +454,11 @@ export const destroyParticleSystem = (particleSystem) => {
|
|
|
389
454
|
particleSystem.parent.remove(particleSystem);
|
|
390
455
|
};
|
|
391
456
|
|
|
392
|
-
export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
393
|
-
const now = Date.now();
|
|
457
|
+
export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
394
458
|
createdParticleSystems.forEach((props) => {
|
|
395
459
|
const {
|
|
396
460
|
onUpdate,
|
|
397
461
|
generalData,
|
|
398
|
-
lastWorldPosition,
|
|
399
|
-
worldPositionChange,
|
|
400
462
|
onComplete,
|
|
401
463
|
particleSystem,
|
|
402
464
|
creationTime,
|
|
@@ -404,6 +466,7 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
404
466
|
duration,
|
|
405
467
|
looping,
|
|
406
468
|
emission,
|
|
469
|
+
normalizedConfig,
|
|
407
470
|
iterationCount,
|
|
408
471
|
velocities,
|
|
409
472
|
deactivateParticle,
|
|
@@ -411,34 +474,65 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
411
474
|
simulationSpace,
|
|
412
475
|
gravity,
|
|
413
476
|
} = props;
|
|
414
|
-
|
|
477
|
+
|
|
478
|
+
const {
|
|
479
|
+
lastWorldPosition,
|
|
480
|
+
currentWorldPosition,
|
|
481
|
+
worldPositionChange,
|
|
482
|
+
lastWorldQuaternion,
|
|
483
|
+
worldQuaternion,
|
|
484
|
+
worldEuler,
|
|
485
|
+
gravityVelocity,
|
|
486
|
+
} = generalData;
|
|
487
|
+
|
|
488
|
+
const lifetime = now - creationTime;
|
|
415
489
|
particleSystem.material.uniforms.elapsed.value = elapsed;
|
|
416
490
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
lastWorldPosition.y !== -99999 &&
|
|
420
|
-
lastWorldPosition.z !== -99999
|
|
421
|
-
)
|
|
491
|
+
particleSystem.getWorldPosition(currentWorldPosition);
|
|
492
|
+
if (lastWorldPosition.x !== -99999)
|
|
422
493
|
worldPositionChange.set(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
494
|
+
currentWorldPosition.x - lastWorldPosition.x,
|
|
495
|
+
currentWorldPosition.y - lastWorldPosition.y,
|
|
496
|
+
currentWorldPosition.z - lastWorldPosition.z
|
|
426
497
|
);
|
|
427
498
|
generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
|
|
428
|
-
|
|
499
|
+
particleSystem.getWorldPosition(lastWorldPosition);
|
|
500
|
+
|
|
501
|
+
particleSystem.getWorldQuaternion(worldQuaternion);
|
|
502
|
+
if (
|
|
503
|
+
lastWorldQuaternion.x === -99999 ||
|
|
504
|
+
lastWorldQuaternion.x != worldQuaternion.x ||
|
|
505
|
+
lastWorldQuaternion.y != worldQuaternion.y ||
|
|
506
|
+
lastWorldQuaternion.z != worldQuaternion.z
|
|
507
|
+
) {
|
|
508
|
+
worldEuler.setFromQuaternion(worldQuaternion);
|
|
509
|
+
lastWorldQuaternion.copy(worldQuaternion);
|
|
510
|
+
|
|
511
|
+
const tempPosX = particleSystem.position.x;
|
|
512
|
+
const tempPosY = particleSystem.position.y;
|
|
513
|
+
const tempPosZ = particleSystem.position.z;
|
|
514
|
+
gravityVelocity.set(0, gravity, 0);
|
|
515
|
+
particleSystem.position.set(0, 0, 0);
|
|
516
|
+
particleSystem.updateMatrixWorld();
|
|
517
|
+
particleSystem.worldToLocal(gravityVelocity);
|
|
518
|
+
particleSystem.position.set(tempPosX, tempPosY, tempPosZ);
|
|
519
|
+
particleSystem.updateMatrixWorld();
|
|
520
|
+
}
|
|
429
521
|
|
|
430
522
|
particleSystem.geometry.attributes.creationTime.array.forEach(
|
|
431
523
|
(entry, index) => {
|
|
432
524
|
if (particleSystem.geometry.attributes.isActive.array[index]) {
|
|
433
|
-
const
|
|
525
|
+
const particleLifetime = now - float32Helper - entry;
|
|
434
526
|
if (
|
|
435
|
-
|
|
436
|
-
particleSystem.geometry.attributes.
|
|
527
|
+
particleLifetime >
|
|
528
|
+
particleSystem.geometry.attributes.startLifetime.array[index]
|
|
437
529
|
)
|
|
438
530
|
deactivateParticle(index);
|
|
439
531
|
else {
|
|
440
532
|
const velocity = velocities[index];
|
|
441
|
-
velocity.
|
|
533
|
+
velocity.x -= gravityVelocity.x;
|
|
534
|
+
velocity.y -= gravityVelocity.y;
|
|
535
|
+
velocity.z -= gravityVelocity.z;
|
|
442
536
|
|
|
443
537
|
if (
|
|
444
538
|
gravity !== 0 ||
|
|
@@ -460,22 +554,26 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
460
554
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
461
555
|
}
|
|
462
556
|
|
|
463
|
-
particleSystem.geometry.attributes.
|
|
464
|
-
|
|
465
|
-
particleSystem.geometry.attributes.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
557
|
+
particleSystem.geometry.attributes.lifetime.array[index] =
|
|
558
|
+
particleLifetime;
|
|
559
|
+
particleSystem.geometry.attributes.lifetime.needsUpdate = true;
|
|
560
|
+
|
|
561
|
+
const particleLifetimePercentage =
|
|
562
|
+
particleLifetime /
|
|
563
|
+
particleSystem.geometry.attributes.startLifetime.array[index];
|
|
564
|
+
applyModifiers({
|
|
565
|
+
startValues: generalData.startValues,
|
|
566
|
+
normalizedConfig,
|
|
567
|
+
attributes: particleSystem.geometry.attributes,
|
|
568
|
+
particleLifetimePercentage,
|
|
569
|
+
particleIndex: index,
|
|
570
|
+
});
|
|
473
571
|
}
|
|
474
572
|
}
|
|
475
573
|
}
|
|
476
574
|
);
|
|
477
575
|
|
|
478
|
-
if (looping ||
|
|
576
|
+
if (looping || lifetime < duration * 1000) {
|
|
479
577
|
const emissionDelta = now - lastEmissionTime;
|
|
480
578
|
const neededParticlesByTime = Math.floor(
|
|
481
579
|
emission.rateOverTime * (emissionDelta / 1000)
|
|
@@ -523,7 +621,7 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
523
621
|
particleSystem,
|
|
524
622
|
delta,
|
|
525
623
|
elapsed,
|
|
526
|
-
|
|
624
|
+
lifetime,
|
|
527
625
|
iterationCount: iterationCount + 1,
|
|
528
626
|
});
|
|
529
627
|
} else if (onComplete)
|