@newkrok/three-particles 0.4.0 → 0.6.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 +5 -2
- package/src/js/effects/three-particles/three-particles-bezier.js +36 -0
- package/src/js/effects/three-particles/three-particles-curves.js +1 -0
- package/src/js/effects/three-particles/three-particles-modifiers.js +1 -2
- package/src/js/effects/three-particles/three-particles-utils.js +57 -2
- package/src/js/effects/three-particles.js +64 -17
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newkrok/three-particles",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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": {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const nCr = (n, k) => {
|
|
2
|
+
let z = 1;
|
|
3
|
+
for (let i = 1; i <= k; i++) z *= (n + 1 - i) / i;
|
|
4
|
+
return z;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const createBezierCurveFunction = (bezierPoints) => (percentage) => {
|
|
8
|
+
if (percentage < 0) return bezierPoints[0];
|
|
9
|
+
if (percentage > 1) return bezierPoints[bezierPoints.length - 1];
|
|
10
|
+
|
|
11
|
+
let start = 0;
|
|
12
|
+
let stop = bezierPoints.length - 1;
|
|
13
|
+
|
|
14
|
+
bezierPoints.find((point, index) => {
|
|
15
|
+
const result = percentage < point.percentage;
|
|
16
|
+
if (result) stop = index;
|
|
17
|
+
else if (point.percentage !== undefined) start = index;
|
|
18
|
+
return result;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const n = stop - start;
|
|
22
|
+
const calculatedPercentage =
|
|
23
|
+
(percentage - bezierPoints[start].percentage) /
|
|
24
|
+
(bezierPoints[stop].percentage - bezierPoints[start].percentage);
|
|
25
|
+
|
|
26
|
+
let value = 0;
|
|
27
|
+
for (let i = 0; i <= n; i++) {
|
|
28
|
+
const p = bezierPoints[start + i];
|
|
29
|
+
const c =
|
|
30
|
+
nCr(n, i) *
|
|
31
|
+
Math.pow(1 - calculatedPercentage, n - i) *
|
|
32
|
+
Math.pow(calculatedPercentage, i);
|
|
33
|
+
value += c * p.y;
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
@@ -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,
|
|
@@ -8,7 +10,12 @@ export const patchObject = (
|
|
|
8
10
|
const result = {};
|
|
9
11
|
Object.keys(objectA).forEach((key) => {
|
|
10
12
|
if (!config.skippedProperties || !config.skippedProperties.includes(key)) {
|
|
11
|
-
if (
|
|
13
|
+
if (
|
|
14
|
+
typeof objectA[key] === "object" &&
|
|
15
|
+
objectA[key] &&
|
|
16
|
+
objectB[key] &&
|
|
17
|
+
!Array.isArray(objectA[key])
|
|
18
|
+
) {
|
|
12
19
|
result[key] = patchObject(objectA[key], objectB[key], config);
|
|
13
20
|
} else {
|
|
14
21
|
result[key] =
|
|
@@ -109,6 +116,54 @@ export const calculateRandomPositionAndVelocityOnCone = (
|
|
|
109
116
|
);
|
|
110
117
|
};
|
|
111
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
|
+
|
|
112
167
|
export const calculateRandomPositionAndVelocityOnCircle = (
|
|
113
168
|
position,
|
|
114
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,6 +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";
|
|
17
|
+
import { createBezierCurveFunction } from "./three-particles/three-particles-bezier";
|
|
16
18
|
|
|
17
19
|
let createdParticleSystems = [];
|
|
18
20
|
|
|
@@ -29,6 +31,12 @@ export const Shape = {
|
|
|
29
31
|
RECTANGLE: "RECTANGLE",
|
|
30
32
|
};
|
|
31
33
|
|
|
34
|
+
export const EmitFrom = {
|
|
35
|
+
VOLUME: "VOLUME",
|
|
36
|
+
SHELL: "SHELL",
|
|
37
|
+
EDGE: "EDGE",
|
|
38
|
+
};
|
|
39
|
+
|
|
32
40
|
export const TimeMode = {
|
|
33
41
|
LIFETIME: "LIFETIME",
|
|
34
42
|
FPS: "FPS",
|
|
@@ -92,10 +100,14 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
92
100
|
rotation: { x: 0.0, y: 0.0 }, // TODO: add z rotation
|
|
93
101
|
scale: { x: 1.0, y: 1.0 },
|
|
94
102
|
},
|
|
103
|
+
box: {
|
|
104
|
+
scale: { x: 1.0, y: 1.0, z: 1.0 },
|
|
105
|
+
emitFrom: EmitFrom.VOLUME,
|
|
106
|
+
},
|
|
95
107
|
},
|
|
96
108
|
map: null,
|
|
97
109
|
renderer: {
|
|
98
|
-
blending: THREE.
|
|
110
|
+
blending: THREE.NormalBlending,
|
|
99
111
|
transparent: true,
|
|
100
112
|
depthTest: true,
|
|
101
113
|
depthWrite: false,
|
|
@@ -115,7 +127,11 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
115
127
|
},
|
|
116
128
|
sizeOverLifetime: {
|
|
117
129
|
isActive: false,
|
|
118
|
-
curveFunction: CurveFunction.
|
|
130
|
+
curveFunction: CurveFunction.BEZIER,
|
|
131
|
+
bezierPoints: [
|
|
132
|
+
{ x: 0, y: 0, percentage: 0 },
|
|
133
|
+
{ x: 1, y: 1, percentage: 1 },
|
|
134
|
+
],
|
|
119
135
|
},
|
|
120
136
|
/* colorOverLifetime: {
|
|
121
137
|
isActive: false,
|
|
@@ -123,7 +139,11 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
|
|
|
123
139
|
}, */
|
|
124
140
|
opacityOverLifetime: {
|
|
125
141
|
isActive: false,
|
|
126
|
-
curveFunction: CurveFunction.
|
|
142
|
+
curveFunction: CurveFunction.BEZIER,
|
|
143
|
+
bezierPoints: [
|
|
144
|
+
{ x: 0, y: 0, percentage: 0 },
|
|
145
|
+
{ x: 1, y: 1, percentage: 1 },
|
|
146
|
+
],
|
|
127
147
|
},
|
|
128
148
|
rotationOverLifetime: {
|
|
129
149
|
isActive: false,
|
|
@@ -169,7 +189,7 @@ const createFloat32Attributes = ({
|
|
|
169
189
|
};
|
|
170
190
|
|
|
171
191
|
const calculatePositionAndVelocity = (
|
|
172
|
-
{ shape, sphere, cone, circle, rectangle },
|
|
192
|
+
{ shape, sphere, cone, circle, rectangle, box },
|
|
173
193
|
startSpeed,
|
|
174
194
|
position,
|
|
175
195
|
velocity,
|
|
@@ -211,6 +231,15 @@ const calculatePositionAndVelocity = (
|
|
|
211
231
|
rectangle
|
|
212
232
|
);
|
|
213
233
|
break;
|
|
234
|
+
|
|
235
|
+
case Shape.BOX:
|
|
236
|
+
calculateRandomPositionAndVelocityOnBox(
|
|
237
|
+
position,
|
|
238
|
+
velocity,
|
|
239
|
+
startSpeed,
|
|
240
|
+
box
|
|
241
|
+
);
|
|
242
|
+
break;
|
|
214
243
|
}
|
|
215
244
|
|
|
216
245
|
if (velocityOverLifetime.isActive) {
|
|
@@ -249,6 +278,22 @@ export const createParticleSystem = (
|
|
|
249
278
|
};
|
|
250
279
|
|
|
251
280
|
const normalizedConfig = patchObject(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
|
|
281
|
+
|
|
282
|
+
const bezierCompatibleProperties = [
|
|
283
|
+
"sizeOverLifetime",
|
|
284
|
+
"opacityOverLifetime",
|
|
285
|
+
];
|
|
286
|
+
bezierCompatibleProperties.forEach((key) => {
|
|
287
|
+
if (
|
|
288
|
+
normalizedConfig[key].isActive &&
|
|
289
|
+
normalizedConfig[key].curveFunction === CurveFunction.BEZIER &&
|
|
290
|
+
normalizedConfig[key].bezierPoints
|
|
291
|
+
)
|
|
292
|
+
normalizedConfig[key].curveFunction = createBezierCurveFunction(
|
|
293
|
+
normalizedConfig[key].bezierPoints
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
252
297
|
const {
|
|
253
298
|
transform,
|
|
254
299
|
duration,
|
|
@@ -503,6 +548,7 @@ export const createParticleSystem = (
|
|
|
503
548
|
startPositions[particleIndex].y;
|
|
504
549
|
geometry.attributes.position.array[positionIndex + 2] =
|
|
505
550
|
startPositions[particleIndex].z;
|
|
551
|
+
geometry.attributes.position.needsUpdate = true;
|
|
506
552
|
|
|
507
553
|
geometry.attributes.lifetime.array[particleIndex] = 0;
|
|
508
554
|
geometry.attributes.lifetime.needsUpdate = true;
|
|
@@ -600,15 +646,16 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
600
646
|
particleSystem.material.uniforms.elapsed.value = elapsed;
|
|
601
647
|
|
|
602
648
|
particleSystem.getWorldPosition(currentWorldPosition);
|
|
603
|
-
if (lastWorldPosition.x !== -99999)
|
|
649
|
+
if (lastWorldPosition.x !== -99999) {
|
|
604
650
|
worldPositionChange.set(
|
|
605
651
|
currentWorldPosition.x - lastWorldPosition.x,
|
|
606
652
|
currentWorldPosition.y - lastWorldPosition.y,
|
|
607
653
|
currentWorldPosition.z - lastWorldPosition.z
|
|
608
654
|
);
|
|
655
|
+
worldPositionChange.applyQuaternion(worldQuaternion.invert())
|
|
656
|
+
}
|
|
609
657
|
generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
|
|
610
658
|
particleSystem.getWorldPosition(lastWorldPosition);
|
|
611
|
-
|
|
612
659
|
particleSystem.getWorldQuaternion(worldQuaternion);
|
|
613
660
|
if (
|
|
614
661
|
lastWorldQuaternion.x === -99999 ||
|
|
@@ -618,16 +665,12 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
618
665
|
) {
|
|
619
666
|
worldEuler.setFromQuaternion(worldQuaternion);
|
|
620
667
|
lastWorldQuaternion.copy(worldQuaternion);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
particleSystem.position.set(0, 0, 0);
|
|
627
|
-
particleSystem.updateMatrixWorld();
|
|
668
|
+
gravityVelocity.set(
|
|
669
|
+
lastWorldPosition.x,
|
|
670
|
+
lastWorldPosition.y + gravity,
|
|
671
|
+
lastWorldPosition.z
|
|
672
|
+
);
|
|
628
673
|
particleSystem.worldToLocal(gravityVelocity);
|
|
629
|
-
particleSystem.position.set(tempPosX, tempPosY, tempPosZ);
|
|
630
|
-
particleSystem.updateMatrixWorld();
|
|
631
674
|
}
|
|
632
675
|
|
|
633
676
|
generalData.creationTimes.forEach((entry, index) => {
|
|
@@ -648,11 +691,15 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
|
|
|
648
691
|
gravity !== 0 ||
|
|
649
692
|
velocity.x !== 0 ||
|
|
650
693
|
velocity.y !== 0 ||
|
|
651
|
-
velocity.z !== 0
|
|
694
|
+
velocity.z !== 0 ||
|
|
695
|
+
worldPositionChange.x !== 0 ||
|
|
696
|
+
worldPositionChange.y !== 0 ||
|
|
697
|
+
worldPositionChange.z !== 0
|
|
652
698
|
) {
|
|
653
699
|
const positionIndex = index * 3;
|
|
654
700
|
const positionArr =
|
|
655
701
|
particleSystem.geometry.attributes.position.array;
|
|
702
|
+
|
|
656
703
|
if (simulationSpace === SimulationSpace.WORLD) {
|
|
657
704
|
positionArr[positionIndex] -= worldPositionChange.x;
|
|
658
705
|
positionArr[positionIndex + 1] -= worldPositionChange.y;
|