@newkrok/three-particles 0.5.0 → 0.7.0
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 +6 -1
- package/package.json +5 -2
- package/src/js/effects/three-particles/shaders/particle-system-fragment-shader.glsl.js +5 -0
- package/src/js/effects/three-particles/three-particles-modifiers.js +1 -1
- package/src/js/effects/three-particles/three-particles-utils.js +51 -1
- package/src/js/effects/three-particles.js +78 -19
package/README.md
CHANGED
|
@@ -6,6 +6,11 @@ Particle system for ThreeJS
|
|
|
6
6
|
|
|
7
7
|
You can create your own particle effects with it's editor https://github.com/NewKrok/three-particles-editor
|
|
8
8
|
|
|
9
|
+
# Video
|
|
10
|
+
|
|
11
|
+
- Projectiles: https://youtu.be/Q352JuxON04
|
|
12
|
+
- First preview: https://youtu.be/dtN_bndvoGU
|
|
13
|
+
|
|
9
14
|
# Live demo
|
|
10
15
|
|
|
11
16
|
https://newkrok.com/three-particles-editor/index.html
|
|
@@ -18,4 +23,4 @@ Install with npm
|
|
|
18
23
|
`npm i @newkrok/three-particles`
|
|
19
24
|
|
|
20
25
|
Add as a package.json dependency
|
|
21
|
-
`"dependencies": { ... "@newkrok/three-particles": "0.
|
|
26
|
+
`"dependencies": { ... "@newkrok/three-particles": "0.7.0" ... }, `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newkrok/three-particles",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Particle system for ThreeJS",
|
|
5
5
|
"main": "src/js/three-particles.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"threejs",
|
|
15
|
+
"particle",
|
|
15
16
|
"particles",
|
|
17
|
+
"particle engine",
|
|
18
|
+
"effects",
|
|
16
19
|
"3d",
|
|
17
20
|
"lib"
|
|
18
21
|
],
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
"homepage": "https://github.com/NewKrok/three-particles#readme",
|
|
25
28
|
"dependencies": {
|
|
26
29
|
"easing-functions": "1.0.1",
|
|
27
|
-
"three": "0.
|
|
30
|
+
"three": "0.137.0",
|
|
28
31
|
"three-noise": "^1.1.1"
|
|
29
32
|
},
|
|
30
33
|
"scripts": {
|
|
@@ -4,6 +4,9 @@ const ParticleSystemFragmentShader = `
|
|
|
4
4
|
uniform float fps;
|
|
5
5
|
uniform bool useFPSForFrameIndex;
|
|
6
6
|
uniform vec2 tiles;
|
|
7
|
+
uniform bool discardBackgroundColor;
|
|
8
|
+
uniform vec3 backgroundColor;
|
|
9
|
+
uniform float backgroundColorTolerance;
|
|
7
10
|
|
|
8
11
|
varying vec4 vColor;
|
|
9
12
|
varying float vLifetime;
|
|
@@ -56,6 +59,8 @@ const ParticleSystemFragmentShader = `
|
|
|
56
59
|
vec4 rotatedTexture = texture2D(map, uvPoint);
|
|
57
60
|
|
|
58
61
|
gl_FragColor = gl_FragColor * rotatedTexture;
|
|
62
|
+
|
|
63
|
+
if (discardBackgroundColor && abs(length(rotatedTexture.rgb - backgroundColor.rgb)) < backgroundColorTolerance ) discard;
|
|
59
64
|
}
|
|
60
65
|
`;
|
|
61
66
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import * as THREE from "three
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
|
|
3
|
+
import { EmitFrom } from "../three-particles.js";
|
|
2
4
|
|
|
3
5
|
export const patchObject = (
|
|
4
6
|
objectA,
|
|
@@ -114,6 +116,54 @@ export const calculateRandomPositionAndVelocityOnCone = (
|
|
|
114
116
|
);
|
|
115
117
|
};
|
|
116
118
|
|
|
119
|
+
export const calculateRandomPositionAndVelocityOnBox = (
|
|
120
|
+
position,
|
|
121
|
+
velocity,
|
|
122
|
+
startSpeed,
|
|
123
|
+
{ scale, emitFrom }
|
|
124
|
+
) => {
|
|
125
|
+
switch (emitFrom) {
|
|
126
|
+
case EmitFrom.VOLUME:
|
|
127
|
+
position.x = Math.random() * scale.x - scale.x / 2;
|
|
128
|
+
position.y = Math.random() * scale.y - scale.y / 2;
|
|
129
|
+
position.z = Math.random() * scale.z - scale.z / 2;
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case EmitFrom.SHELL:
|
|
133
|
+
const side = Math.floor(Math.random() * 6);
|
|
134
|
+
const perpendicularAxis = side % 3;
|
|
135
|
+
const shellResult = [];
|
|
136
|
+
shellResult[perpendicularAxis] = side > 2 ? 1 : 0;
|
|
137
|
+
shellResult[(perpendicularAxis + 1) % 3] = Math.random();
|
|
138
|
+
shellResult[(perpendicularAxis + 2) % 3] = Math.random();
|
|
139
|
+
position.x = shellResult[0] * scale.x - scale.x / 2;
|
|
140
|
+
position.y = shellResult[1] * scale.y - scale.y / 2;
|
|
141
|
+
position.z = shellResult[2] * scale.z - scale.z / 2;
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case EmitFrom.EDGE:
|
|
145
|
+
const side2 = Math.floor(Math.random() * 6);
|
|
146
|
+
const perpendicularAxis2 = side2 % 3;
|
|
147
|
+
const edge = Math.floor(Math.random() * 4);
|
|
148
|
+
const edgeResult = [];
|
|
149
|
+
edgeResult[perpendicularAxis2] = side2 > 2 ? 1 : 0;
|
|
150
|
+
edgeResult[(perpendicularAxis2 + 1) % 3] =
|
|
151
|
+
edge < 2 ? Math.random() : edge - 2;
|
|
152
|
+
edgeResult[(perpendicularAxis2 + 2) % 3] =
|
|
153
|
+
edge < 2 ? edge : Math.random();
|
|
154
|
+
position.x = edgeResult[0] * scale.x - scale.x / 2;
|
|
155
|
+
position.y = edgeResult[1] * scale.y - scale.y / 2;
|
|
156
|
+
position.z = edgeResult[2] * scale.z - scale.z / 2;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
161
|
+
startSpeed.min,
|
|
162
|
+
startSpeed.max
|
|
163
|
+
);
|
|
164
|
+
velocity.set(0, 0, randomizedSpeed);
|
|
165
|
+
};
|
|
166
|
+
|
|
117
167
|
export const calculateRandomPositionAndVelocityOnCircle = (
|
|
118
168
|
position,
|
|
119
169
|
velocity,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import * as THREE from "three
|
|
1
|
+
import * as THREE from "three";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
calculateRandomPositionAndVelocityOnBox,
|
|
4
5
|
calculateRandomPositionAndVelocityOnCircle,
|
|
5
6
|
calculateRandomPositionAndVelocityOnCone,
|
|
6
7
|
calculateRandomPositionAndVelocityOnRectangle,
|
|
@@ -13,7 +14,7 @@ import { FBM } from "three-noise/build/three-noise.module.js";
|
|
|
13
14
|
import ParticleSystemFragmentShader from "./three-particles/shaders/particle-system-fragment-shader.glsl.js";
|
|
14
15
|
import ParticleSystemVertexShader from "./three-particles/shaders/particle-system-vertex-shader.glsl.js";
|
|
15
16
|
import { applyModifiers } from "./three-particles/three-particles-modifiers.js";
|
|
16
|
-
import { createBezierCurveFunction } from "
|
|
17
|
+
import { createBezierCurveFunction } from "./three-particles/three-particles-bezier";
|
|
17
18
|
|
|
18
19
|
let createdParticleSystems = [];
|
|
19
20
|
|
|
@@ -30,6 +31,12 @@ export const Shape = {
|
|
|
30
31
|
RECTANGLE: "RECTANGLE",
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
export const EmitFrom = {
|
|
35
|
+
VOLUME: "VOLUME",
|
|
36
|
+
SHELL: "SHELL",
|
|
37
|
+
EDGE: "EDGE",
|
|
38
|
+
};
|
|
39
|
+
|
|
33
40
|
export const TimeMode = {
|
|
34
41
|
LIFETIME: "LIFETIME",
|
|
35
42
|
FPS: "FPS",
|
|
@@ -93,10 +100,17 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
93
100
|
rotation: { x: 0.0, y: 0.0 }, // TODO: add z rotation
|
|
94
101
|
scale: { x: 1.0, y: 1.0 },
|
|
95
102
|
},
|
|
103
|
+
box: {
|
|
104
|
+
scale: { x: 1.0, y: 1.0, z: 1.0 },
|
|
105
|
+
emitFrom: EmitFrom.VOLUME,
|
|
106
|
+
},
|
|
96
107
|
},
|
|
97
108
|
map: null,
|
|
98
109
|
renderer: {
|
|
99
110
|
blending: THREE.NormalBlending,
|
|
111
|
+
discardBackgroundColor: false,
|
|
112
|
+
backgroundColorTolerance: 1.0,
|
|
113
|
+
backgroundColor: { r: 1.0, g: 1.0, b: 1.0 },
|
|
100
114
|
transparent: true,
|
|
101
115
|
depthTest: true,
|
|
102
116
|
depthWrite: false,
|
|
@@ -178,7 +192,7 @@ const createFloat32Attributes = ({
|
|
|
178
192
|
};
|
|
179
193
|
|
|
180
194
|
const calculatePositionAndVelocity = (
|
|
181
|
-
{ shape, sphere, cone, circle, rectangle },
|
|
195
|
+
{ shape, sphere, cone, circle, rectangle, box },
|
|
182
196
|
startSpeed,
|
|
183
197
|
position,
|
|
184
198
|
velocity,
|
|
@@ -220,6 +234,15 @@ const calculatePositionAndVelocity = (
|
|
|
220
234
|
rectangle
|
|
221
235
|
);
|
|
222
236
|
break;
|
|
237
|
+
|
|
238
|
+
case Shape.BOX:
|
|
239
|
+
calculateRandomPositionAndVelocityOnBox(
|
|
240
|
+
position,
|
|
241
|
+
velocity,
|
|
242
|
+
startSpeed,
|
|
243
|
+
box
|
|
244
|
+
);
|
|
245
|
+
break;
|
|
223
246
|
}
|
|
224
247
|
|
|
225
248
|
if (velocityOverLifetime.isActive) {
|
|
@@ -371,6 +394,15 @@ export const createParticleSystem = (
|
|
|
371
394
|
useFPSForFrameIndex: {
|
|
372
395
|
value: textureSheetAnimation.timeMode === TimeMode.FPS,
|
|
373
396
|
},
|
|
397
|
+
backgroundColor: {
|
|
398
|
+
value: renderer.backgroundColor,
|
|
399
|
+
},
|
|
400
|
+
discardBackgroundColor: {
|
|
401
|
+
value: renderer.discardBackgroundColor,
|
|
402
|
+
},
|
|
403
|
+
backgroundColorTolerance: {
|
|
404
|
+
value: renderer.backgroundColorTolerance,
|
|
405
|
+
},
|
|
374
406
|
},
|
|
375
407
|
vertexShader: ParticleSystemVertexShader,
|
|
376
408
|
fragmentShader: ParticleSystemFragmentShader,
|
|
@@ -460,7 +492,7 @@ export const createParticleSystem = (
|
|
|
460
492
|
geometry.attributes.colorA.needsUpdate = true;
|
|
461
493
|
};
|
|
462
494
|
|
|
463
|
-
const activateParticle = ({ particleIndex, activationTime }) => {
|
|
495
|
+
const activateParticle = ({ particleIndex, activationTime, position }) => {
|
|
464
496
|
geometry.attributes.isActive.array[particleIndex] = true;
|
|
465
497
|
generalData.creationTimes[particleIndex] = activationTime;
|
|
466
498
|
|
|
@@ -523,11 +555,12 @@ export const createParticleSystem = (
|
|
|
523
555
|
);
|
|
524
556
|
const positionIndex = Math.floor(particleIndex * 3);
|
|
525
557
|
geometry.attributes.position.array[positionIndex] =
|
|
526
|
-
startPositions[particleIndex].x;
|
|
558
|
+
(position ? position.x : 0) + startPositions[particleIndex].x;
|
|
527
559
|
geometry.attributes.position.array[positionIndex + 1] =
|
|
528
|
-
startPositions[particleIndex].y;
|
|
560
|
+
(position ? position.y : 0) + startPositions[particleIndex].y;
|
|
529
561
|
geometry.attributes.position.array[positionIndex + 2] =
|
|
530
|
-
startPositions[particleIndex].z;
|
|
562
|
+
(position ? position.z : 0) + startPositions[particleIndex].z;
|
|
563
|
+
geometry.attributes.position.needsUpdate = true;
|
|
531
564
|
|
|
532
565
|
geometry.attributes.lifetime.array[particleIndex] = 0;
|
|
533
566
|
geometry.attributes.lifetime.needsUpdate = true;
|
|
@@ -621,6 +654,8 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
621
654
|
gravityVelocity,
|
|
622
655
|
} = generalData;
|
|
623
656
|
|
|
657
|
+
const lastWorldPositionSnapshot = { ...lastWorldPosition };
|
|
658
|
+
|
|
624
659
|
const lifetime = now - creationTime;
|
|
625
660
|
particleSystem.material.uniforms.elapsed.value = elapsed;
|
|
626
661
|
|
|
@@ -631,10 +666,10 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
631
666
|
currentWorldPosition.y - lastWorldPosition.y,
|
|
632
667
|
currentWorldPosition.z - lastWorldPosition.z
|
|
633
668
|
);
|
|
669
|
+
worldPositionChange.applyQuaternion(worldQuaternion.invert());
|
|
634
670
|
}
|
|
635
671
|
generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
|
|
636
672
|
particleSystem.getWorldPosition(lastWorldPosition);
|
|
637
|
-
|
|
638
673
|
particleSystem.getWorldQuaternion(worldQuaternion);
|
|
639
674
|
if (
|
|
640
675
|
lastWorldQuaternion.x === -99999 ||
|
|
@@ -644,16 +679,12 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
644
679
|
) {
|
|
645
680
|
worldEuler.setFromQuaternion(worldQuaternion);
|
|
646
681
|
lastWorldQuaternion.copy(worldQuaternion);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
particleSystem.position.set(0, 0, 0);
|
|
653
|
-
particleSystem.updateMatrixWorld();
|
|
682
|
+
gravityVelocity.set(
|
|
683
|
+
lastWorldPosition.x,
|
|
684
|
+
lastWorldPosition.y + gravity,
|
|
685
|
+
lastWorldPosition.z
|
|
686
|
+
);
|
|
654
687
|
particleSystem.worldToLocal(gravityVelocity);
|
|
655
|
-
particleSystem.position.set(tempPosX, tempPosY, tempPosZ);
|
|
656
|
-
particleSystem.updateMatrixWorld();
|
|
657
688
|
}
|
|
658
689
|
|
|
659
690
|
generalData.creationTimes.forEach((entry, index) => {
|
|
@@ -729,12 +760,28 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
729
760
|
(1 / emission.rateOverDistance)
|
|
730
761
|
)
|
|
731
762
|
: 0;
|
|
763
|
+
const distanceStep =
|
|
764
|
+
neededParticlesByDistance > 0
|
|
765
|
+
? {
|
|
766
|
+
x:
|
|
767
|
+
(currentWorldPosition.x - lastWorldPositionSnapshot.x) /
|
|
768
|
+
neededParticlesByDistance,
|
|
769
|
+
y:
|
|
770
|
+
(currentWorldPosition.y - lastWorldPositionSnapshot.y) /
|
|
771
|
+
neededParticlesByDistance,
|
|
772
|
+
z:
|
|
773
|
+
(currentWorldPosition.z - lastWorldPositionSnapshot.z) /
|
|
774
|
+
neededParticlesByDistance,
|
|
775
|
+
}
|
|
776
|
+
: null;
|
|
732
777
|
const neededParticles = neededParticlesByTime + neededParticlesByDistance;
|
|
733
778
|
|
|
734
|
-
if (emission.rateOverDistance > 0 && neededParticlesByDistance >= 1)
|
|
779
|
+
if (emission.rateOverDistance > 0 && neededParticlesByDistance >= 1) {
|
|
735
780
|
generalData.distanceFromLastEmitByDistance = 0;
|
|
781
|
+
}
|
|
736
782
|
|
|
737
783
|
if (neededParticles > 0) {
|
|
784
|
+
let generatedParticlesByDistanceNeeds = 0;
|
|
738
785
|
for (let i = 0; i < neededParticles; i++) {
|
|
739
786
|
let particleIndex = -1;
|
|
740
787
|
particleSystem.geometry.attributes.isActive.array.find(
|
|
@@ -753,7 +800,19 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
753
800
|
particleIndex <
|
|
754
801
|
particleSystem.geometry.attributes.isActive.array.length
|
|
755
802
|
) {
|
|
756
|
-
|
|
803
|
+
let position;
|
|
804
|
+
if (generatedParticlesByDistanceNeeds < neededParticlesByDistance) {
|
|
805
|
+
position =
|
|
806
|
+
generatedParticlesByDistanceNeeds < neededParticlesByDistance
|
|
807
|
+
? {
|
|
808
|
+
x: distanceStep.x * generatedParticlesByDistanceNeeds,
|
|
809
|
+
y: distanceStep.y * generatedParticlesByDistanceNeeds,
|
|
810
|
+
z: distanceStep.z * generatedParticlesByDistanceNeeds,
|
|
811
|
+
}
|
|
812
|
+
: null;
|
|
813
|
+
generatedParticlesByDistanceNeeds++;
|
|
814
|
+
}
|
|
815
|
+
activateParticle({ particleIndex, activationTime: now, position });
|
|
757
816
|
props.lastEmissionTime = now;
|
|
758
817
|
}
|
|
759
818
|
}
|