@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 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.5.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.5.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.136.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,4 @@
1
- import * as THREE from "three/build/three.module.js";
1
+ import * as THREE from "three";
2
2
 
3
3
  import { getCurveFunction } from "./three-particles-curves.js";
4
4
 
@@ -1,4 +1,6 @@
1
- import * as THREE from "three/build/three.module.js";
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/build/three.module.js";
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 "@newkrok/three-particles/src/js/effects/three-particles/three-particles-bezier";
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
- const tempPosX = particleSystem.position.x;
649
- const tempPosY = particleSystem.position.y;
650
- const tempPosZ = particleSystem.position.z;
651
- gravityVelocity.set(0, gravity, 0);
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
- activateParticle({ particleIndex, activationTime: now });
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
  }