@newkrok/three-particles 0.2.0 → 0.2.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 +1 -1
- package/package.json +1 -1
- package/src/js/effects/shaders/particle-system-fragment-shader.glsl.js +13 -3
- package/src/js/effects/shaders/particle-system-vertex-shader.glsl.js +10 -4
- package/src/js/effects/three-particles-utils.js +13 -7
- package/src/js/effects/three-particles.js +58 -36
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -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,
|
|
@@ -4,24 +4,30 @@ const ParticleSystemVertexShader = `
|
|
|
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
28
|
gl_PointSize = startSize * (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;
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import * as THREE from "three/build/three.module.js";
|
|
2
2
|
|
|
3
|
-
export const deepMerge = (
|
|
3
|
+
export const deepMerge = (
|
|
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] = deepMerge(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
|
};
|
|
@@ -29,6 +29,11 @@ export const Shape = {
|
|
|
29
29
|
RECTANGLE: "RECTANGLE",
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
export const TimeMode = {
|
|
33
|
+
LIFETIME: "LIFETIME",
|
|
34
|
+
FPS: "FPS",
|
|
35
|
+
};
|
|
36
|
+
|
|
32
37
|
export const getDefaultParticleSystemConfig = () =>
|
|
33
38
|
JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
|
|
34
39
|
|
|
@@ -36,7 +41,7 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
36
41
|
duration: 5.0,
|
|
37
42
|
looping: true,
|
|
38
43
|
startDelay: { min: 0.0, max: 0.0 },
|
|
39
|
-
|
|
44
|
+
startLifetime: { min: 2.0, max: 2.0 },
|
|
40
45
|
startSpeed: { min: 1.0, max: 1.0 },
|
|
41
46
|
startSize: { min: 1.0, max: 1.0 },
|
|
42
47
|
startRotation: { min: 0.0, max: 0.0 },
|
|
@@ -45,7 +50,7 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
45
50
|
max: { r: 1.0, g: 1.0, b: 1.0 },
|
|
46
51
|
},
|
|
47
52
|
startOpacity: { min: 1.0, max: 1.0 },
|
|
48
|
-
gravity: 0,
|
|
53
|
+
gravity: 0.0,
|
|
49
54
|
simulationSpace: SimulationSpace.LOCAL,
|
|
50
55
|
maxParticles: 100.0,
|
|
51
56
|
emission: {
|
|
@@ -76,7 +81,12 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
76
81
|
},
|
|
77
82
|
},
|
|
78
83
|
map: null,
|
|
79
|
-
textureSheetAnimation: {
|
|
84
|
+
textureSheetAnimation: {
|
|
85
|
+
tiles: new THREE.Vector2(1.0, 1.0),
|
|
86
|
+
timeMode: TimeMode.LIFETIME,
|
|
87
|
+
fps: 10.0,
|
|
88
|
+
startFrame: { min: 0.0, max: 0.0 },
|
|
89
|
+
},
|
|
80
90
|
};
|
|
81
91
|
|
|
82
92
|
const createFloat32Attributes = ({
|
|
@@ -156,7 +166,7 @@ export const createParticleSystem = (
|
|
|
156
166
|
duration,
|
|
157
167
|
looping,
|
|
158
168
|
startDelay,
|
|
159
|
-
|
|
169
|
+
startLifetime,
|
|
160
170
|
startSpeed,
|
|
161
171
|
startSize,
|
|
162
172
|
startRotation,
|
|
@@ -182,14 +192,6 @@ export const createParticleSystem = (
|
|
|
182
192
|
() => new THREE.Vector3()
|
|
183
193
|
);
|
|
184
194
|
|
|
185
|
-
const uniforms = Object.keys(textureSheetAnimation).reduce(
|
|
186
|
-
(prev, key) => ({
|
|
187
|
-
...prev,
|
|
188
|
-
[key]: { value: textureSheetAnimation[key] },
|
|
189
|
-
}),
|
|
190
|
-
{}
|
|
191
|
-
);
|
|
192
|
-
|
|
193
195
|
const material = new THREE.ShaderMaterial({
|
|
194
196
|
uniforms: {
|
|
195
197
|
elapsed: {
|
|
@@ -198,7 +200,15 @@ export const createParticleSystem = (
|
|
|
198
200
|
map: {
|
|
199
201
|
value: map,
|
|
200
202
|
},
|
|
201
|
-
|
|
203
|
+
tiles: {
|
|
204
|
+
value: textureSheetAnimation.tiles,
|
|
205
|
+
},
|
|
206
|
+
fps: {
|
|
207
|
+
value: textureSheetAnimation.fps,
|
|
208
|
+
},
|
|
209
|
+
useFPSForFrameIndex: {
|
|
210
|
+
value: textureSheetAnimation.timeMode === TimeMode.FPS,
|
|
211
|
+
},
|
|
202
212
|
},
|
|
203
213
|
vertexShader: ParticleSystemVertexShader,
|
|
204
214
|
fragmentShader: ParticleSystemFragmentShader,
|
|
@@ -235,9 +245,15 @@ export const createParticleSystem = (
|
|
|
235
245
|
|
|
236
246
|
createFloat32AttributesRequest("isActive", false);
|
|
237
247
|
createFloat32AttributesRequest("creationTime", 0);
|
|
238
|
-
createFloat32AttributesRequest("
|
|
239
|
-
createFloat32AttributesRequest("
|
|
240
|
-
THREE.MathUtils.randFloat(
|
|
248
|
+
createFloat32AttributesRequest("lifetime", 0);
|
|
249
|
+
createFloat32AttributesRequest("startLifetime", () =>
|
|
250
|
+
THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max)
|
|
251
|
+
);
|
|
252
|
+
createFloat32AttributesRequest("startFrame", () =>
|
|
253
|
+
THREE.MathUtils.randInt(
|
|
254
|
+
textureSheetAnimation.startFrame.min,
|
|
255
|
+
textureSheetAnimation.startFrame.max
|
|
256
|
+
)
|
|
241
257
|
);
|
|
242
258
|
|
|
243
259
|
createFloat32AttributesRequest("opacity", 0);
|
|
@@ -284,8 +300,8 @@ export const createParticleSystem = (
|
|
|
284
300
|
|
|
285
301
|
const deactivateParticle = (particleIndex) => {
|
|
286
302
|
geometry.attributes.isActive.array[particleIndex] = false;
|
|
287
|
-
geometry.attributes.
|
|
288
|
-
geometry.attributes.
|
|
303
|
+
geometry.attributes.lifetime.array[particleIndex] = 0;
|
|
304
|
+
geometry.attributes.lifetime.needsUpdate = true;
|
|
289
305
|
geometry.attributes.colorA.array[particleIndex] = 0;
|
|
290
306
|
geometry.attributes.colorA.needsUpdate = true;
|
|
291
307
|
};
|
|
@@ -318,9 +334,16 @@ export const createParticleSystem = (
|
|
|
318
334
|
);
|
|
319
335
|
geometry.attributes.colorA.needsUpdate = true;
|
|
320
336
|
|
|
321
|
-
geometry.attributes.
|
|
322
|
-
THREE.MathUtils.
|
|
323
|
-
|
|
337
|
+
geometry.attributes.startFrame.array[particleIndex] =
|
|
338
|
+
THREE.MathUtils.randInt(
|
|
339
|
+
textureSheetAnimation.startFrame.min,
|
|
340
|
+
textureSheetAnimation.startFrame.max
|
|
341
|
+
);
|
|
342
|
+
geometry.attributes.startFrame.needsUpdate = true;
|
|
343
|
+
|
|
344
|
+
geometry.attributes.startLifetime.array[particleIndex] =
|
|
345
|
+
THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max) * 1000;
|
|
346
|
+
geometry.attributes.startLifetime.needsUpdate = true;
|
|
324
347
|
|
|
325
348
|
geometry.attributes.startSize.array[particleIndex] =
|
|
326
349
|
THREE.MathUtils.randFloat(startSize.min, startSize.max);
|
|
@@ -346,8 +369,8 @@ export const createParticleSystem = (
|
|
|
346
369
|
startPositions[particleIndex].z;
|
|
347
370
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
348
371
|
|
|
349
|
-
geometry.attributes.
|
|
350
|
-
geometry.attributes.
|
|
372
|
+
geometry.attributes.lifetime.array[particleIndex] = 0;
|
|
373
|
+
geometry.attributes.lifetime.needsUpdate = true;
|
|
351
374
|
};
|
|
352
375
|
|
|
353
376
|
const particleSystem = new THREE.Points(geometry, material);
|
|
@@ -389,8 +412,7 @@ export const destroyParticleSystem = (particleSystem) => {
|
|
|
389
412
|
particleSystem.parent.remove(particleSystem);
|
|
390
413
|
};
|
|
391
414
|
|
|
392
|
-
export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
393
|
-
const now = Date.now();
|
|
415
|
+
export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
394
416
|
createdParticleSystems.forEach((props) => {
|
|
395
417
|
const {
|
|
396
418
|
onUpdate,
|
|
@@ -411,7 +433,7 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
411
433
|
simulationSpace,
|
|
412
434
|
gravity,
|
|
413
435
|
} = props;
|
|
414
|
-
const
|
|
436
|
+
const lifetime = now - creationTime;
|
|
415
437
|
particleSystem.material.uniforms.elapsed.value = elapsed;
|
|
416
438
|
|
|
417
439
|
if (
|
|
@@ -430,10 +452,10 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
430
452
|
particleSystem.geometry.attributes.creationTime.array.forEach(
|
|
431
453
|
(entry, index) => {
|
|
432
454
|
if (particleSystem.geometry.attributes.isActive.array[index]) {
|
|
433
|
-
const
|
|
455
|
+
const particleLifetime = now - float32Helper - entry;
|
|
434
456
|
if (
|
|
435
|
-
|
|
436
|
-
particleSystem.geometry.attributes.
|
|
457
|
+
particleLifetime >
|
|
458
|
+
particleSystem.geometry.attributes.startLifetime.array[index]
|
|
437
459
|
)
|
|
438
460
|
deactivateParticle(index);
|
|
439
461
|
else {
|
|
@@ -460,22 +482,22 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
460
482
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
461
483
|
}
|
|
462
484
|
|
|
463
|
-
particleSystem.geometry.attributes.
|
|
464
|
-
|
|
465
|
-
particleSystem.geometry.attributes.
|
|
485
|
+
particleSystem.geometry.attributes.lifetime.array[index] =
|
|
486
|
+
particleLifetime;
|
|
487
|
+
particleSystem.geometry.attributes.lifetime.needsUpdate = true;
|
|
466
488
|
|
|
467
489
|
// TEMP
|
|
468
490
|
particleSystem.geometry.attributes.colorA.array[index] =
|
|
469
491
|
1 -
|
|
470
|
-
|
|
471
|
-
particleSystem.geometry.attributes.
|
|
492
|
+
particleLifetime /
|
|
493
|
+
particleSystem.geometry.attributes.startLifetime.array[index];
|
|
472
494
|
particleSystem.geometry.attributes.colorA.needsUpdate = true;
|
|
473
495
|
}
|
|
474
496
|
}
|
|
475
497
|
}
|
|
476
498
|
);
|
|
477
499
|
|
|
478
|
-
if (looping ||
|
|
500
|
+
if (looping || lifetime < duration * 1000) {
|
|
479
501
|
const emissionDelta = now - lastEmissionTime;
|
|
480
502
|
const neededParticlesByTime = Math.floor(
|
|
481
503
|
emission.rateOverTime * (emissionDelta / 1000)
|
|
@@ -523,7 +545,7 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
523
545
|
particleSystem,
|
|
524
546
|
delta,
|
|
525
547
|
elapsed,
|
|
526
|
-
|
|
548
|
+
lifetime,
|
|
527
549
|
iterationCount: iterationCount + 1,
|
|
528
550
|
});
|
|
529
551
|
} else if (onComplete)
|