@newkrok/three-particles 0.0.1 → 0.1.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 +15 -0
- package/package.json +1 -1
- package/src/js/effects/three-particles-utils.js +121 -0
- package/src/js/effects/three-particles.js +108 -91
package/README.md
CHANGED
|
@@ -6,3 +6,18 @@ You can create your own particle effects with it's editor https://github.com/New
|
|
|
6
6
|
|
|
7
7
|
# Live demo
|
|
8
8
|
https://newkrok.com/three-particles-editor/index.html
|
|
9
|
+
|
|
10
|
+
# Install
|
|
11
|
+
npm package https://www.npmjs.com/package/@newkrok/three-particles
|
|
12
|
+
|
|
13
|
+
Install with npm
|
|
14
|
+
`npm i @newkrok/three-particles`
|
|
15
|
+
|
|
16
|
+
Add as a package.json dependency
|
|
17
|
+
`
|
|
18
|
+
"dependencies": {
|
|
19
|
+
...
|
|
20
|
+
"@newkrok/three-particles": "0.0.1"
|
|
21
|
+
...
|
|
22
|
+
},
|
|
23
|
+
`
|
package/package.json
CHANGED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as THREE from "three/build/three.module.js";
|
|
2
|
+
|
|
3
|
+
export const calculateRandomPositionAndVelocityOnSphere = (
|
|
4
|
+
position,
|
|
5
|
+
velocity,
|
|
6
|
+
startSpeed,
|
|
7
|
+
{ radius, radiusThickness, arc }
|
|
8
|
+
) => {
|
|
9
|
+
const u = Math.random() * (arc / 360);
|
|
10
|
+
const v = Math.random();
|
|
11
|
+
const randomizedDistanceRatio = Math.random();
|
|
12
|
+
const theta = 2 * Math.PI * u;
|
|
13
|
+
const phi = Math.acos(2 * v - 1);
|
|
14
|
+
const sinPhi = Math.sin(phi);
|
|
15
|
+
|
|
16
|
+
const xDirection = sinPhi * Math.cos(theta);
|
|
17
|
+
const yDirection = sinPhi * Math.sin(theta);
|
|
18
|
+
const zDirection = Math.cos(phi);
|
|
19
|
+
const normalizedThickness = 1 - radiusThickness;
|
|
20
|
+
|
|
21
|
+
position.x =
|
|
22
|
+
radius * normalizedThickness * xDirection +
|
|
23
|
+
radius * radiusThickness * randomizedDistanceRatio * xDirection;
|
|
24
|
+
position.y =
|
|
25
|
+
radius * normalizedThickness * yDirection +
|
|
26
|
+
radius * radiusThickness * randomizedDistanceRatio * yDirection;
|
|
27
|
+
position.z =
|
|
28
|
+
radius * normalizedThickness * zDirection +
|
|
29
|
+
radius * radiusThickness * randomizedDistanceRatio * zDirection;
|
|
30
|
+
|
|
31
|
+
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
32
|
+
startSpeed.min,
|
|
33
|
+
startSpeed.max
|
|
34
|
+
);
|
|
35
|
+
const speedMultiplierByPosition = 1 / position.length();
|
|
36
|
+
velocity.set(
|
|
37
|
+
position.x * speedMultiplierByPosition * randomizedSpeed,
|
|
38
|
+
position.y * speedMultiplierByPosition * randomizedSpeed,
|
|
39
|
+
position.z * speedMultiplierByPosition * randomizedSpeed
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const calculateRandomPositionAndVelocityOnCone = (
|
|
44
|
+
position,
|
|
45
|
+
velocity,
|
|
46
|
+
startSpeed,
|
|
47
|
+
{ radius, radiusThickness, arc, angle = 90 }
|
|
48
|
+
) => {
|
|
49
|
+
const theta = 2 * Math.PI * Math.random() * (arc / 360);
|
|
50
|
+
const randomizedDistanceRatio = Math.random();
|
|
51
|
+
|
|
52
|
+
const xDirection = Math.cos(theta);
|
|
53
|
+
const yDirection = Math.sin(theta);
|
|
54
|
+
const normalizedThickness = 1 - radiusThickness;
|
|
55
|
+
|
|
56
|
+
position.x =
|
|
57
|
+
radius * normalizedThickness * xDirection +
|
|
58
|
+
radius * radiusThickness * randomizedDistanceRatio * xDirection;
|
|
59
|
+
position.y =
|
|
60
|
+
radius * normalizedThickness * yDirection +
|
|
61
|
+
radius * radiusThickness * randomizedDistanceRatio * yDirection;
|
|
62
|
+
position.z = 0;
|
|
63
|
+
|
|
64
|
+
const positionLength = position.length();
|
|
65
|
+
const normalizedAngle = Math.abs(
|
|
66
|
+
(positionLength / radius) * THREE.Math.degToRad(angle)
|
|
67
|
+
);
|
|
68
|
+
const sinNormalizedAngle = Math.sin(normalizedAngle);
|
|
69
|
+
|
|
70
|
+
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
71
|
+
startSpeed.min,
|
|
72
|
+
startSpeed.max
|
|
73
|
+
);
|
|
74
|
+
const speedMultiplierByPosition = 1 / positionLength;
|
|
75
|
+
velocity.set(
|
|
76
|
+
position.x *
|
|
77
|
+
sinNormalizedAngle *
|
|
78
|
+
speedMultiplierByPosition *
|
|
79
|
+
randomizedSpeed,
|
|
80
|
+
position.y *
|
|
81
|
+
sinNormalizedAngle *
|
|
82
|
+
speedMultiplierByPosition *
|
|
83
|
+
randomizedSpeed,
|
|
84
|
+
-Math.cos(normalizedAngle) * randomizedSpeed
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const calculateRandomPositionAndVelocityOnCircle = (
|
|
89
|
+
position,
|
|
90
|
+
velocity,
|
|
91
|
+
startSpeed,
|
|
92
|
+
{ radius, radiusThickness, arc }
|
|
93
|
+
) => {
|
|
94
|
+
const theta = 2 * Math.PI * Math.random() * (arc / 360);
|
|
95
|
+
const randomizedDistanceRatio = Math.random();
|
|
96
|
+
|
|
97
|
+
const xDirection = Math.cos(theta);
|
|
98
|
+
const yDirection = Math.sin(theta);
|
|
99
|
+
const normalizedThickness = 1 - radiusThickness;
|
|
100
|
+
|
|
101
|
+
position.x =
|
|
102
|
+
radius * normalizedThickness * xDirection +
|
|
103
|
+
radius * radiusThickness * randomizedDistanceRatio * xDirection;
|
|
104
|
+
position.y =
|
|
105
|
+
radius * normalizedThickness * yDirection +
|
|
106
|
+
radius * radiusThickness * randomizedDistanceRatio * yDirection;
|
|
107
|
+
position.z = 0;
|
|
108
|
+
|
|
109
|
+
const positionLength = position.length();
|
|
110
|
+
|
|
111
|
+
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
112
|
+
startSpeed.min,
|
|
113
|
+
startSpeed.max
|
|
114
|
+
);
|
|
115
|
+
const speedMultiplierByPosition = 1 / positionLength;
|
|
116
|
+
velocity.set(
|
|
117
|
+
position.x * speedMultiplierByPosition * randomizedSpeed,
|
|
118
|
+
position.y * speedMultiplierByPosition * randomizedSpeed,
|
|
119
|
+
0
|
|
120
|
+
);
|
|
121
|
+
};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as THREE from "three/build/three.module.js";
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
calculateRandomPositionAndVelocityOnCircle,
|
|
5
|
+
calculateRandomPositionAndVelocityOnCone,
|
|
6
|
+
calculateRandomPositionAndVelocityOnSphere,
|
|
7
|
+
} from "./three-particles-utils.js";
|
|
8
|
+
|
|
3
9
|
import ParticleSystemFragmentShader from "./shaders/particle-system-fragment-shader.glsl.js";
|
|
4
10
|
import ParticleSystemVertexShader from "./shaders/particle-system-vertex-shader.glsl.js";
|
|
5
11
|
|
|
@@ -46,34 +52,40 @@ const createFloat32Attributes = ({
|
|
|
46
52
|
);
|
|
47
53
|
};
|
|
48
54
|
|
|
49
|
-
const
|
|
50
|
-
|
|
55
|
+
const calculatePositionAndVelocity = (
|
|
56
|
+
{ shape, sphere, cone, circle },
|
|
57
|
+
startSpeed,
|
|
58
|
+
position,
|
|
59
|
+
velocity
|
|
60
|
+
) => {
|
|
51
61
|
switch (shape) {
|
|
52
|
-
case Shape.SPHERE:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
radius * radiusThickness * randomizedDistanceRatio * yDirection;
|
|
69
|
-
position.z =
|
|
70
|
-
radius * normalizedThickness * zDirection +
|
|
71
|
-
radius * radiusThickness * randomizedDistanceRatio * zDirection;
|
|
62
|
+
case Shape.SPHERE:
|
|
63
|
+
calculateRandomPositionAndVelocityOnSphere(
|
|
64
|
+
position,
|
|
65
|
+
velocity,
|
|
66
|
+
startSpeed,
|
|
67
|
+
sphere
|
|
68
|
+
);
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case Shape.CONE:
|
|
72
|
+
calculateRandomPositionAndVelocityOnCone(
|
|
73
|
+
position,
|
|
74
|
+
velocity,
|
|
75
|
+
startSpeed,
|
|
76
|
+
cone
|
|
77
|
+
);
|
|
72
78
|
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
case Shape.CIRCLE:
|
|
81
|
+
calculateRandomPositionAndVelocityOnCircle(
|
|
82
|
+
position,
|
|
83
|
+
velocity,
|
|
84
|
+
startSpeed,
|
|
85
|
+
circle
|
|
86
|
+
);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
77
89
|
};
|
|
78
90
|
|
|
79
91
|
export const createParticleSystem = ({
|
|
@@ -96,12 +108,7 @@ export const createParticleSystem = ({
|
|
|
96
108
|
rateOverTime: 10.0,
|
|
97
109
|
rateOverDistance: 0.0,
|
|
98
110
|
},
|
|
99
|
-
shape
|
|
100
|
-
shape: Shape.SPHERE,
|
|
101
|
-
radius: 1.0,
|
|
102
|
-
radiusThickness: 1.0,
|
|
103
|
-
arc: 360.0,
|
|
104
|
-
},
|
|
111
|
+
shape,
|
|
105
112
|
map,
|
|
106
113
|
onUpdate = null,
|
|
107
114
|
onComplete = null,
|
|
@@ -129,12 +136,37 @@ export const createParticleSystem = ({
|
|
|
129
136
|
};
|
|
130
137
|
const normalizedShape = {
|
|
131
138
|
shape: Shape.SPHERE,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
sphere: {
|
|
140
|
+
radius: 1.0,
|
|
141
|
+
radiusThickness: 1.0,
|
|
142
|
+
arc: 360.0,
|
|
143
|
+
...shape.sphere,
|
|
144
|
+
},
|
|
145
|
+
cone: {
|
|
146
|
+
angle: 25,
|
|
147
|
+
radius: 1.0,
|
|
148
|
+
radiusThickness: 1.0,
|
|
149
|
+
arc: 360.0,
|
|
150
|
+
...shape.cone,
|
|
151
|
+
},
|
|
152
|
+
circle: {
|
|
153
|
+
radius: 1.0,
|
|
154
|
+
radiusThickness: 1.0,
|
|
155
|
+
arc: 360.0,
|
|
156
|
+
...shape.circle,
|
|
157
|
+
},
|
|
135
158
|
...shape,
|
|
136
159
|
};
|
|
137
160
|
|
|
161
|
+
const startPositions = Array.from(
|
|
162
|
+
{ length: maxParticles },
|
|
163
|
+
() => new THREE.Vector3()
|
|
164
|
+
);
|
|
165
|
+
const velocities = Array.from(
|
|
166
|
+
{ length: maxParticles },
|
|
167
|
+
() => new THREE.Vector3()
|
|
168
|
+
);
|
|
169
|
+
|
|
138
170
|
const rawUniforms = {
|
|
139
171
|
...defaultTextureSheetAnimation,
|
|
140
172
|
...textureSheetAnimation,
|
|
@@ -171,9 +203,18 @@ export const createParticleSystem = ({
|
|
|
171
203
|
|
|
172
204
|
const geometry = new THREE.BufferGeometry();
|
|
173
205
|
|
|
174
|
-
|
|
206
|
+
for (let i = 0; i < maxParticles; i++)
|
|
207
|
+
calculatePositionAndVelocity(
|
|
208
|
+
normalizedShape,
|
|
209
|
+
normalizedStartSpeed,
|
|
210
|
+
startPositions[i],
|
|
211
|
+
velocities[i]
|
|
212
|
+
);
|
|
213
|
+
|
|
175
214
|
geometry.setFromPoints(
|
|
176
|
-
Array.from({ length: maxParticles }, () => ({
|
|
215
|
+
Array.from({ length: maxParticles }, (_, index) => ({
|
|
216
|
+
...startPositions[index],
|
|
217
|
+
}))
|
|
177
218
|
);
|
|
178
219
|
|
|
179
220
|
const createFloat32AttributesRequest = (propertyName, factory) => {
|
|
@@ -194,23 +235,6 @@ export const createParticleSystem = ({
|
|
|
194
235
|
normalizedStartLifeTime.max
|
|
195
236
|
)
|
|
196
237
|
);
|
|
197
|
-
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
198
|
-
normalizedStartSpeed.min,
|
|
199
|
-
normalizedStartSpeed.max
|
|
200
|
-
);
|
|
201
|
-
const speedMultiplierByPosition = startPosition.length() / 1;
|
|
202
|
-
createFloat32AttributesRequest(
|
|
203
|
-
"velocityX",
|
|
204
|
-
() => startPosition.x * speedMultiplierByPosition * randomizedSpeed
|
|
205
|
-
);
|
|
206
|
-
createFloat32AttributesRequest(
|
|
207
|
-
"velocityY",
|
|
208
|
-
() => startPosition.y * speedMultiplierByPosition * randomizedSpeed
|
|
209
|
-
);
|
|
210
|
-
createFloat32AttributesRequest(
|
|
211
|
-
"velocityZ",
|
|
212
|
-
() => startPosition.z * speedMultiplierByPosition * randomizedSpeed
|
|
213
|
-
);
|
|
214
238
|
|
|
215
239
|
createFloat32AttributesRequest("opacity", 0);
|
|
216
240
|
|
|
@@ -325,26 +349,20 @@ export const createParticleSystem = ({
|
|
|
325
349
|
);
|
|
326
350
|
geometry.attributes.rotation.needsUpdate = true;
|
|
327
351
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
geometry.attributes.position.array[Math.floor(particleIndex * 3) + 2] =
|
|
334
|
-
startPosition.z;
|
|
335
|
-
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
336
|
-
|
|
337
|
-
const randomizedSpeed = THREE.MathUtils.randFloat(
|
|
338
|
-
normalizedStartSpeed.min,
|
|
339
|
-
normalizedStartSpeed.max
|
|
352
|
+
calculatePositionAndVelocity(
|
|
353
|
+
normalizedShape,
|
|
354
|
+
normalizedStartSpeed,
|
|
355
|
+
startPositions[particleIndex],
|
|
356
|
+
velocities[particleIndex]
|
|
340
357
|
);
|
|
341
|
-
const
|
|
342
|
-
geometry.attributes.
|
|
343
|
-
|
|
344
|
-
geometry.attributes.
|
|
345
|
-
|
|
346
|
-
geometry.attributes.
|
|
347
|
-
|
|
358
|
+
const positionIndex = Math.floor(particleIndex * 3);
|
|
359
|
+
geometry.attributes.position.array[positionIndex] =
|
|
360
|
+
startPositions[particleIndex].x;
|
|
361
|
+
geometry.attributes.position.array[positionIndex + 1] =
|
|
362
|
+
startPositions[particleIndex].y;
|
|
363
|
+
geometry.attributes.position.array[positionIndex + 2] =
|
|
364
|
+
startPositions[particleIndex].z;
|
|
365
|
+
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
348
366
|
|
|
349
367
|
geometry.attributes.lifeTime.array[particleIndex] = 0;
|
|
350
368
|
geometry.attributes.lifeTime.needsUpdate = true;
|
|
@@ -376,6 +394,7 @@ export const createParticleSystem = ({
|
|
|
376
394
|
gravity,
|
|
377
395
|
emission: normalizedEmission,
|
|
378
396
|
iterationCount: 0,
|
|
397
|
+
velocities,
|
|
379
398
|
deactivateParticle,
|
|
380
399
|
activateParticle,
|
|
381
400
|
});
|
|
@@ -409,6 +428,7 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
409
428
|
looping,
|
|
410
429
|
emission,
|
|
411
430
|
iterationCount,
|
|
431
|
+
velocities,
|
|
412
432
|
deactivateParticle,
|
|
413
433
|
activateParticle,
|
|
414
434
|
simulationSpace,
|
|
@@ -440,29 +460,26 @@ export const updateParticleSystems = ({ delta, elapsed }) => {
|
|
|
440
460
|
)
|
|
441
461
|
deactivateParticle(index);
|
|
442
462
|
else {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
particleSystem.geometry.attributes.velocityZ.array[index];
|
|
454
|
-
|
|
455
|
-
if (gravity !== 0 || accelerationX !== 0 || accelerationY !== 0) {
|
|
463
|
+
const velocity = velocities[index];
|
|
464
|
+
velocity.y -= gravity;
|
|
465
|
+
|
|
466
|
+
if (
|
|
467
|
+
gravity !== 0 ||
|
|
468
|
+
velocity.x !== 0 ||
|
|
469
|
+
velocity.y !== 0 ||
|
|
470
|
+
velocity.z !== 0
|
|
471
|
+
) {
|
|
472
|
+
const positionIndex = index * 3;
|
|
456
473
|
const positionArr =
|
|
457
474
|
particleSystem.geometry.attributes.position.array;
|
|
458
475
|
if (simulationSpace === SimulationSpace.WORLD) {
|
|
459
|
-
positionArr[
|
|
460
|
-
positionArr[
|
|
461
|
-
positionArr[
|
|
476
|
+
positionArr[positionIndex] -= worldPositionChange.x;
|
|
477
|
+
positionArr[positionIndex + 1] -= worldPositionChange.y;
|
|
478
|
+
positionArr[positionIndex + 2] -= worldPositionChange.z;
|
|
462
479
|
}
|
|
463
|
-
positionArr[
|
|
464
|
-
positionArr[
|
|
465
|
-
positionArr[
|
|
480
|
+
positionArr[positionIndex] += velocity.x * delta;
|
|
481
|
+
positionArr[positionIndex + 1] += velocity.y * delta;
|
|
482
|
+
positionArr[positionIndex + 2] += velocity.z * delta;
|
|
466
483
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
467
484
|
}
|
|
468
485
|
|