@newkrok/three-particles 0.7.0 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newkrok/three-particles",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Particle system for ThreeJS",
5
5
  "main": "src/js/three-particles.js",
6
6
  "bin": {
@@ -27,8 +27,9 @@
27
27
  "homepage": "https://github.com/NewKrok/three-particles#readme",
28
28
  "dependencies": {
29
29
  "easing-functions": "1.0.1",
30
- "three": "0.137.0",
31
- "three-noise": "^1.1.1"
30
+ "three": "0.140.0",
31
+ "three-noise": "1.1.2",
32
+ "@newkrok/three-utils": "0.1.0"
32
33
  },
33
34
  "scripts": {
34
35
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -2,7 +2,9 @@ import * as THREE from "three";
2
2
 
3
3
  import { getCurveFunction } from "./three-particles-curves.js";
4
4
 
5
+ const ROTATION_CONVERTER = THREE.MathUtils.radToDeg(1);
5
6
  const noiseInput = new THREE.Vector3(0, 0, 0);
7
+ const orbitalEuler = new THREE.Euler();
6
8
 
7
9
  const curveModifiers = [
8
10
  // {key:"colorOverLifetime", attributeKeys:["colorR", "colorG", "colorB"]},
@@ -23,12 +25,38 @@ export const applyModifiers = ({
23
25
  noise,
24
26
  startValues,
25
27
  lifetimeValues,
28
+ hasOrbitalVelocity,
29
+ orbitalVelocityData,
26
30
  normalizedConfig,
27
31
  attributes,
32
+ particleLifetime,
28
33
  particleLifetimePercentage,
29
34
  particleIndex,
30
35
  forceUpdate = false,
31
36
  }) => {
37
+ if (hasOrbitalVelocity) {
38
+ const positionIndex = particleIndex * 3;
39
+ const positionArr = attributes.position.array;
40
+ const { speed, positionOffset } = orbitalVelocityData[particleIndex];
41
+
42
+ positionArr[positionIndex] -= positionOffset.x;
43
+ positionArr[positionIndex + 1] -= positionOffset.y;
44
+ positionArr[positionIndex + 2] -= positionOffset.z;
45
+
46
+ orbitalEuler.set(
47
+ speed.x * ROTATION_CONVERTER * delta,
48
+ speed.z * ROTATION_CONVERTER * delta,
49
+ speed.y * ROTATION_CONVERTER * delta
50
+ );
51
+ positionOffset.applyEuler(orbitalEuler);
52
+
53
+ positionArr[positionIndex] += positionOffset.x;
54
+ positionArr[positionIndex + 1] += positionOffset.y;
55
+ positionArr[positionIndex + 2] += positionOffset.z;
56
+
57
+ attributes.position.needsUpdate = true;
58
+ }
59
+
32
60
  curveModifiers.forEach(({ key, attributeKeys, startValueKeys }) => {
33
61
  const curveModifier = normalizedConfig[key];
34
62
  if (curveModifier.isActive) {
@@ -2,37 +2,9 @@ import * as THREE from "three";
2
2
 
3
3
  import { EmitFrom } from "../three-particles.js";
4
4
 
5
- export const patchObject = (
6
- objectA,
7
- objectB,
8
- config = { skippedProperties: [], applyToFirstObject: false }
9
- ) => {
10
- const result = {};
11
- Object.keys(objectA).forEach((key) => {
12
- if (!config.skippedProperties || !config.skippedProperties.includes(key)) {
13
- if (
14
- typeof objectA[key] === "object" &&
15
- objectA[key] &&
16
- objectB[key] &&
17
- !Array.isArray(objectA[key])
18
- ) {
19
- result[key] = patchObject(objectA[key], objectB[key], config);
20
- } else {
21
- result[key] =
22
- objectB[key] === 0
23
- ? 0
24
- : objectB[key] === false
25
- ? false
26
- : objectB[key] || objectA[key];
27
- if (config.applyToFirstObject) objectA[key] = result[key];
28
- }
29
- }
30
- });
31
- return result;
32
- };
33
-
34
5
  export const calculateRandomPositionAndVelocityOnSphere = (
35
6
  position,
7
+ quaternion,
36
8
  velocity,
37
9
  startSpeed,
38
10
  { radius, radiusThickness, arc }
@@ -59,6 +31,8 @@ export const calculateRandomPositionAndVelocityOnSphere = (
59
31
  radius * normalizedThickness * zDirection +
60
32
  radius * radiusThickness * randomizedDistanceRatio * zDirection;
61
33
 
34
+ position.applyQuaternion(quaternion);
35
+
62
36
  const randomizedSpeed = THREE.MathUtils.randFloat(
63
37
  startSpeed.min,
64
38
  startSpeed.max
@@ -69,10 +43,12 @@ export const calculateRandomPositionAndVelocityOnSphere = (
69
43
  position.y * speedMultiplierByPosition * randomizedSpeed,
70
44
  position.z * speedMultiplierByPosition * randomizedSpeed
71
45
  );
46
+ velocity.applyQuaternion(quaternion);
72
47
  };
73
48
 
74
49
  export const calculateRandomPositionAndVelocityOnCone = (
75
50
  position,
51
+ quaternion,
76
52
  velocity,
77
53
  startSpeed,
78
54
  { radius, radiusThickness, arc, angle = 90 }
@@ -92,6 +68,8 @@ export const calculateRandomPositionAndVelocityOnCone = (
92
68
  radius * radiusThickness * randomizedDistanceRatio * yDirection;
93
69
  position.z = 0;
94
70
 
71
+ position.applyQuaternion(quaternion);
72
+
95
73
  const positionLength = position.length();
96
74
  const normalizedAngle = Math.abs(
97
75
  (positionLength / radius) * THREE.Math.degToRad(angle)
@@ -114,10 +92,12 @@ export const calculateRandomPositionAndVelocityOnCone = (
114
92
  randomizedSpeed,
115
93
  Math.cos(normalizedAngle) * randomizedSpeed
116
94
  );
95
+ velocity.applyQuaternion(quaternion);
117
96
  };
118
97
 
119
98
  export const calculateRandomPositionAndVelocityOnBox = (
120
99
  position,
100
+ quaternion,
121
101
  velocity,
122
102
  startSpeed,
123
103
  { scale, emitFrom }
@@ -157,15 +137,19 @@ export const calculateRandomPositionAndVelocityOnBox = (
157
137
  break;
158
138
  }
159
139
 
140
+ position.applyQuaternion(quaternion);
141
+
160
142
  const randomizedSpeed = THREE.MathUtils.randFloat(
161
143
  startSpeed.min,
162
144
  startSpeed.max
163
145
  );
164
146
  velocity.set(0, 0, randomizedSpeed);
147
+ velocity.applyQuaternion(quaternion);
165
148
  };
166
149
 
167
150
  export const calculateRandomPositionAndVelocityOnCircle = (
168
151
  position,
152
+ quaternion,
169
153
  velocity,
170
154
  startSpeed,
171
155
  { radius, radiusThickness, arc }
@@ -185,6 +169,8 @@ export const calculateRandomPositionAndVelocityOnCircle = (
185
169
  radius * radiusThickness * randomizedDistanceRatio * yDirection;
186
170
  position.z = 0;
187
171
 
172
+ position.applyQuaternion(quaternion);
173
+
188
174
  const randomizedSpeed = THREE.MathUtils.randFloat(
189
175
  startSpeed.min,
190
176
  startSpeed.max
@@ -197,10 +183,12 @@ export const calculateRandomPositionAndVelocityOnCircle = (
197
183
  position.y * speedMultiplierByPosition * randomizedSpeed,
198
184
  0
199
185
  );
186
+ velocity.applyQuaternion(quaternion);
200
187
  };
201
188
 
202
189
  export const calculateRandomPositionAndVelocityOnRectangle = (
203
190
  position,
191
+ quaternion,
204
192
  velocity,
205
193
  startSpeed,
206
194
  { rotation, scale }
@@ -213,9 +201,12 @@ export const calculateRandomPositionAndVelocityOnRectangle = (
213
201
  position.y = yOffset * Math.cos(rotationX);
214
202
  position.z = xOffset * Math.sin(rotationY) - yOffset * Math.sin(rotationX);
215
203
 
204
+ position.applyQuaternion(quaternion);
205
+
216
206
  const randomizedSpeed = THREE.MathUtils.randFloat(
217
207
  startSpeed.min,
218
208
  startSpeed.max
219
209
  );
220
210
  velocity.set(0, 0, randomizedSpeed);
211
+ velocity.applyQuaternion(quaternion);
221
212
  };
@@ -6,15 +6,16 @@ import {
6
6
  calculateRandomPositionAndVelocityOnCone,
7
7
  calculateRandomPositionAndVelocityOnRectangle,
8
8
  calculateRandomPositionAndVelocityOnSphere,
9
- patchObject,
10
9
  } from "./three-particles/three-particles-utils.js";
11
10
 
12
11
  import { CurveFunction } from "./three-particles/three-particles-curves.js";
13
12
  import { FBM } from "three-noise/build/three-noise.module.js";
13
+ import { Gyroscope } from "three/examples/jsm/misc/Gyroscope.js";
14
14
  import ParticleSystemFragmentShader from "./three-particles/shaders/particle-system-fragment-shader.glsl.js";
15
15
  import ParticleSystemVertexShader from "./three-particles/shaders/particle-system-vertex-shader.glsl.js";
16
16
  import { applyModifiers } from "./three-particles/three-particles-modifiers.js";
17
17
  import { createBezierCurveFunction } from "./three-particles/three-particles-bezier";
18
+ import { patchObject } from "@newkrok/three-utils/src/js/newkrok/three-utils/object-utils.js";
18
19
 
19
20
  let createdParticleSystems = [];
20
21
 
@@ -195,6 +196,7 @@ const calculatePositionAndVelocity = (
195
196
  { shape, sphere, cone, circle, rectangle, box },
196
197
  startSpeed,
197
198
  position,
199
+ quaternion,
198
200
  velocity,
199
201
  velocityOverLifetime
200
202
  ) => {
@@ -202,6 +204,7 @@ const calculatePositionAndVelocity = (
202
204
  case Shape.SPHERE:
203
205
  calculateRandomPositionAndVelocityOnSphere(
204
206
  position,
207
+ quaternion,
205
208
  velocity,
206
209
  startSpeed,
207
210
  sphere
@@ -211,6 +214,7 @@ const calculatePositionAndVelocity = (
211
214
  case Shape.CONE:
212
215
  calculateRandomPositionAndVelocityOnCone(
213
216
  position,
217
+ quaternion,
214
218
  velocity,
215
219
  startSpeed,
216
220
  cone
@@ -220,6 +224,7 @@ const calculatePositionAndVelocity = (
220
224
  case Shape.CIRCLE:
221
225
  calculateRandomPositionAndVelocityOnCircle(
222
226
  position,
227
+ quaternion,
223
228
  velocity,
224
229
  startSpeed,
225
230
  circle
@@ -229,6 +234,7 @@ const calculatePositionAndVelocity = (
229
234
  case Shape.RECTANGLE:
230
235
  calculateRandomPositionAndVelocityOnRectangle(
231
236
  position,
237
+ quaternion,
232
238
  velocity,
233
239
  startSpeed,
234
240
  rectangle
@@ -238,6 +244,7 @@ const calculatePositionAndVelocity = (
238
244
  case Shape.BOX:
239
245
  calculateRandomPositionAndVelocityOnBox(
240
246
  position,
247
+ quaternion,
241
248
  velocity,
242
249
  startSpeed,
243
250
  box
@@ -246,18 +253,33 @@ const calculatePositionAndVelocity = (
246
253
  }
247
254
 
248
255
  if (velocityOverLifetime.isActive) {
249
- velocity.x += THREE.MathUtils.randFloat(
250
- velocityOverLifetime.linear.x.min,
251
- velocityOverLifetime.linear.x.max
252
- );
253
- velocity.y += THREE.MathUtils.randFloat(
254
- velocityOverLifetime.linear.y.min,
255
- velocityOverLifetime.linear.y.max
256
- );
257
- velocity.z += THREE.MathUtils.randFloat(
258
- velocityOverLifetime.linear.z.min,
259
- velocityOverLifetime.linear.z.max
260
- );
256
+ if (
257
+ velocityOverLifetime.linear.x.min !== 0 ||
258
+ velocityOverLifetime.linear.x.max !== 0
259
+ ) {
260
+ velocity.x += THREE.MathUtils.randFloat(
261
+ velocityOverLifetime.linear.x.min,
262
+ velocityOverLifetime.linear.x.max
263
+ );
264
+ }
265
+ if (
266
+ velocityOverLifetime.linear.y.min !== 0 ||
267
+ velocityOverLifetime.linear.y.max !== 0
268
+ ) {
269
+ velocity.y += THREE.MathUtils.randFloat(
270
+ velocityOverLifetime.linear.y.min,
271
+ velocityOverLifetime.linear.y.max
272
+ );
273
+ }
274
+ if (
275
+ velocityOverLifetime.linear.z.min !== 0 ||
276
+ velocityOverLifetime.linear.z.max !== 0
277
+ ) {
278
+ velocity.z += THREE.MathUtils.randFloat(
279
+ velocityOverLifetime.linear.z.min,
280
+ velocityOverLifetime.linear.z.max
281
+ );
282
+ }
261
283
  }
262
284
  };
263
285
 
@@ -271,10 +293,13 @@ export const createParticleSystem = (
271
293
  currentWorldPosition: new THREE.Vector3(-99999),
272
294
  worldPositionChange: new THREE.Vector3(),
273
295
  worldQuaternion: new THREE.Quaternion(),
296
+ wrapperQuaternion: new THREE.Quaternion(),
274
297
  lastWorldQuaternion: new THREE.Quaternion(-99999),
275
298
  worldEuler: new THREE.Euler(),
276
299
  gravityVelocity: new THREE.Vector3(0, 0, 0),
277
300
  startValues: {},
301
+ hasOrbitalVelocity: false,
302
+ orbitalVelocityData: [],
278
303
  lifetimeValues: {},
279
304
  creationTimes: [],
280
305
  noise: null,
@@ -335,6 +360,24 @@ export const createParticleSystem = (
335
360
  );
336
361
 
337
362
  generalData.creationTimes = Array.from({ length: maxParticles }, () => 0);
363
+ generalData.hasOrbitalVelocity =
364
+ normalizedConfig.velocityOverLifetime.isActive &&
365
+ (normalizedConfig.velocityOverLifetime.orbital.x.min !== 0 ||
366
+ normalizedConfig.velocityOverLifetime.orbital.x.max !== 0 ||
367
+ normalizedConfig.velocityOverLifetime.orbital.y.min !== 0 ||
368
+ normalizedConfig.velocityOverLifetime.orbital.y.max !== 0 ||
369
+ normalizedConfig.velocityOverLifetime.orbital.z.min !== 0 ||
370
+ normalizedConfig.velocityOverLifetime.orbital.z.max !== 0);
371
+
372
+ if (generalData.hasOrbitalVelocity) {
373
+ generalData.orbitalVelocityData = Array.from(
374
+ { length: maxParticles },
375
+ () => ({
376
+ speed: new THREE.Vector3(),
377
+ positionOffset: new THREE.Vector3(),
378
+ })
379
+ );
380
+ }
338
381
 
339
382
  const startValueKeys = ["startSize", "startOpacity"];
340
383
  startValueKeys.forEach((key) => {
@@ -419,6 +462,7 @@ export const createParticleSystem = (
419
462
  shape,
420
463
  startSpeed,
421
464
  startPositions[i],
465
+ generalData.wrapperQuaternion,
422
466
  velocities[i],
423
467
  velocityOverLifetime
424
468
  );
@@ -550,6 +594,7 @@ export const createParticleSystem = (
550
594
  shape,
551
595
  startSpeed,
552
596
  startPositions[particleIndex],
597
+ generalData.wrapperQuaternion,
553
598
  velocities[particleIndex],
554
599
  velocityOverLifetime
555
600
  );
@@ -562,6 +607,31 @@ export const createParticleSystem = (
562
607
  (position ? position.z : 0) + startPositions[particleIndex].z;
563
608
  geometry.attributes.position.needsUpdate = true;
564
609
 
610
+ if (generalData.hasOrbitalVelocity) {
611
+ generalData.orbitalVelocityData[particleIndex].speed.set(
612
+ THREE.MathUtils.randFloat(
613
+ normalizedConfig.velocityOverLifetime.orbital.x.min,
614
+ normalizedConfig.velocityOverLifetime.orbital.x.max
615
+ ) *
616
+ (Math.PI / 180),
617
+ THREE.MathUtils.randFloat(
618
+ normalizedConfig.velocityOverLifetime.orbital.y.min,
619
+ normalizedConfig.velocityOverLifetime.orbital.y.max
620
+ ) *
621
+ (Math.PI / 180),
622
+ THREE.MathUtils.randFloat(
623
+ normalizedConfig.velocityOverLifetime.orbital.z.min,
624
+ normalizedConfig.velocityOverLifetime.orbital.z.max
625
+ ) *
626
+ (Math.PI / 180)
627
+ );
628
+ generalData.orbitalVelocityData[particleIndex].positionOffset.set(
629
+ startPositions[particleIndex].x,
630
+ startPositions[particleIndex].y,
631
+ startPositions[particleIndex].z
632
+ );
633
+ }
634
+
565
635
  geometry.attributes.lifetime.array[particleIndex] = 0;
566
636
  geometry.attributes.lifetime.needsUpdate = true;
567
637
 
@@ -571,15 +641,18 @@ export const createParticleSystem = (
571
641
  noise: generalData.noise,
572
642
  startValues: generalData.startValues,
573
643
  lifetimeValues: generalData.lifetimeValues,
644
+ hasOrbitalVelocity: generalData.hasOrbitalVelocity,
645
+ orbitalVelocityData: generalData.orbitalVelocityData,
574
646
  normalizedConfig,
575
647
  attributes: particleSystem.geometry.attributes,
648
+ particleLifetime: 0,
576
649
  particleLifetimePercentage: 0,
577
650
  particleIndex,
578
651
  forceUpdate: true,
579
652
  });
580
653
  };
581
654
 
582
- const particleSystem = new THREE.Points(geometry, material);
655
+ let particleSystem = new THREE.Points(geometry, material);
583
656
  particleSystem.sortParticles = true;
584
657
 
585
658
  particleSystem.position.copy(transform.position);
@@ -591,8 +664,15 @@ export const createParticleSystem = (
591
664
  const calculatedCreationTime =
592
665
  now + THREE.MathUtils.randFloat(startDelay.min, startDelay.max) * 1000;
593
666
 
667
+ let wrapper;
668
+ if (normalizedConfig.simulationSpace === SimulationSpace.WORLD) {
669
+ wrapper = new Gyroscope();
670
+ wrapper.add(particleSystem);
671
+ }
672
+
594
673
  createdParticleSystems.push({
595
674
  particleSystem,
675
+ wrapper,
596
676
  generalData,
597
677
  onUpdate,
598
678
  onComplete,
@@ -609,18 +689,26 @@ export const createParticleSystem = (
609
689
  deactivateParticle,
610
690
  activateParticle,
611
691
  });
612
- return particleSystem;
692
+
693
+ return wrapper || particleSystem;
613
694
  };
614
695
 
615
696
  export const destroyParticleSystem = (particleSystem) => {
616
697
  createdParticleSystems = createdParticleSystems.filter(
617
- ({ particleSystem: savedParticleSystem }) =>
618
- savedParticleSystem !== particleSystem
619
- );
698
+ ({ particleSystem: savedParticleSystem, wrapper }) => {
699
+ if (
700
+ savedParticleSystem !== particleSystem &&
701
+ wrapper !== particleSystem
702
+ ) {
703
+ return true;
704
+ }
620
705
 
621
- particleSystem.geometry.dispose();
622
- particleSystem.material.dispose();
623
- particleSystem.parent.remove(particleSystem);
706
+ savedParticleSystem.geometry.dispose();
707
+ savedParticleSystem.material.dispose();
708
+ savedParticleSystem.parent.remove(savedParticleSystem);
709
+ return false;
710
+ }
711
+ );
624
712
  };
625
713
 
626
714
  export const updateParticleSystems = ({ now, delta, elapsed }) => {
@@ -630,6 +718,7 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
630
718
  generalData,
631
719
  onComplete,
632
720
  particleSystem,
721
+ wrapper,
633
722
  creationTime,
634
723
  lastEmissionTime,
635
724
  duration,
@@ -652,8 +741,11 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
652
741
  worldQuaternion,
653
742
  worldEuler,
654
743
  gravityVelocity,
744
+ hasOrbitalVelocity,
655
745
  } = generalData;
656
746
 
747
+ if (wrapper) generalData.wrapperQuaternion.copy(wrapper.parent.quaternion);
748
+
657
749
  const lastWorldPositionSnapshot = { ...lastWorldPosition };
658
750
 
659
751
  const lifetime = now - creationTime;
@@ -666,7 +758,6 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
666
758
  currentWorldPosition.y - lastWorldPosition.y,
667
759
  currentWorldPosition.z - lastWorldPosition.z
668
760
  );
669
- worldPositionChange.applyQuaternion(worldQuaternion.invert());
670
761
  }
671
762
  generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
672
763
  particleSystem.getWorldPosition(lastWorldPosition);
@@ -697,9 +788,9 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
697
788
  deactivateParticle(index);
698
789
  else {
699
790
  const velocity = velocities[index];
700
- velocity.x -= gravityVelocity.x;
701
- velocity.y -= gravityVelocity.y;
702
- velocity.z -= gravityVelocity.z;
791
+ velocity.x -= gravityVelocity.x * delta;
792
+ velocity.y -= gravityVelocity.y * delta;
793
+ velocity.z -= gravityVelocity.z * delta;
703
794
 
704
795
  if (
705
796
  gravity !== 0 ||
@@ -719,6 +810,7 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
719
810
  positionArr[positionIndex + 1] -= worldPositionChange.y;
720
811
  positionArr[positionIndex + 2] -= worldPositionChange.z;
721
812
  }
813
+
722
814
  positionArr[positionIndex] += velocity.x * delta;
723
815
  positionArr[positionIndex + 1] += velocity.y * delta;
724
816
  positionArr[positionIndex + 2] += velocity.z * delta;
@@ -738,8 +830,11 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
738
830
  noise: generalData.noise,
739
831
  startValues: generalData.startValues,
740
832
  lifetimeValues: generalData.lifetimeValues,
833
+ hasOrbitalVelocity: generalData.hasOrbitalVelocity,
834
+ orbitalVelocityData: generalData.orbitalVelocityData,
741
835
  normalizedConfig,
742
836
  attributes: particleSystem.geometry.attributes,
837
+ particleLifetime,
743
838
  particleLifetimePercentage,
744
839
  particleIndex: index,
745
840
  });