@newkrok/three-particles 0.1.0 → 0.2.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
@@ -1,23 +1,21 @@
1
1
  # THREE Particles
2
+
2
3
  Particle sytem for ThreeJS
3
4
 
4
5
  # THREE Particles Editor
6
+
5
7
  You can create your own particle effects with it's editor https://github.com/NewKrok/three-particles-editor
6
8
 
7
9
  # Live demo
10
+
8
11
  https://newkrok.com/three-particles-editor/index.html
9
12
 
10
13
  # Install
14
+
11
15
  npm package https://www.npmjs.com/package/@newkrok/three-particles
12
16
 
13
17
  Install with npm
14
18
  `npm i @newkrok/three-particles`
15
19
 
16
20
  Add as a package.json dependency
17
- `
18
- "dependencies": {
19
- ...
20
- "@newkrok/three-particles": "0.0.1"
21
- ...
22
- },
23
- `
21
+ `"dependencies": { ... "@newkrok/three-particles": "0.2.0" ... }, `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newkrok/three-particles",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Particle system for ThreeJS",
5
5
  "main": "src/js/three-particles.js",
6
6
  "bin": {
@@ -22,6 +22,9 @@
22
22
  "url": "https://github.com/NewKrok/three-particles/issues"
23
23
  },
24
24
  "homepage": "https://github.com/NewKrok/three-particles#readme",
25
+ "dependencies": {
26
+ "three": "^0.135.0"
27
+ },
25
28
  "scripts": {
26
29
  "test": "echo \"Error: no test specified\" && exit 1"
27
30
  }
@@ -1,5 +1,18 @@
1
1
  import * as THREE from "three/build/three.module.js";
2
2
 
3
+ export const deepMerge = (objectA, objectB) => {
4
+ const result = {};
5
+ Object.keys(objectA).forEach((key) => {
6
+ result[key] =
7
+ typeof objectA[key] === "object" && objectA[key] && objectB[key]
8
+ ? deepMerge(objectA[key], objectB[key])
9
+ : objectB[key] === 0
10
+ ? 0
11
+ : objectB[key] || objectA[key];
12
+ });
13
+ return result;
14
+ };
15
+
3
16
  export const calculateRandomPositionAndVelocityOnSphere = (
4
17
  position,
5
18
  velocity,
@@ -106,12 +119,12 @@ export const calculateRandomPositionAndVelocityOnCircle = (
106
119
  radius * radiusThickness * randomizedDistanceRatio * yDirection;
107
120
  position.z = 0;
108
121
 
109
- const positionLength = position.length();
110
-
111
122
  const randomizedSpeed = THREE.MathUtils.randFloat(
112
123
  startSpeed.min,
113
124
  startSpeed.max
114
125
  );
126
+
127
+ const positionLength = position.length();
115
128
  const speedMultiplierByPosition = 1 / positionLength;
116
129
  velocity.set(
117
130
  position.x * speedMultiplierByPosition * randomizedSpeed,
@@ -119,3 +132,24 @@ export const calculateRandomPositionAndVelocityOnCircle = (
119
132
  0
120
133
  );
121
134
  };
135
+
136
+ export const calculateRandomPositionAndVelocityOnRectangle = (
137
+ position,
138
+ velocity,
139
+ startSpeed,
140
+ { rotation, scale }
141
+ ) => {
142
+ const xOffset = Math.random() * scale.x - scale.x / 2;
143
+ const yOffset = Math.random() * scale.y - scale.y / 2;
144
+ const rotationX = THREE.Math.degToRad(rotation.x);
145
+ const rotationY = THREE.Math.degToRad(rotation.y);
146
+ position.x = xOffset * Math.cos(rotationY);
147
+ position.y = yOffset * Math.cos(rotationX);
148
+ position.z = xOffset * Math.sin(rotationY) - yOffset * Math.sin(rotationX);
149
+
150
+ const randomizedSpeed = THREE.MathUtils.randFloat(
151
+ startSpeed.min,
152
+ startSpeed.max
153
+ );
154
+ velocity.set(0, 0, -randomizedSpeed);
155
+ };
@@ -3,7 +3,9 @@ import * as THREE from "three/build/three.module.js";
3
3
  import {
4
4
  calculateRandomPositionAndVelocityOnCircle,
5
5
  calculateRandomPositionAndVelocityOnCone,
6
+ calculateRandomPositionAndVelocityOnRectangle,
6
7
  calculateRandomPositionAndVelocityOnSphere,
8
+ deepMerge,
7
9
  } from "./three-particles-utils.js";
8
10
 
9
11
  import ParticleSystemFragmentShader from "./shaders/particle-system-fragment-shader.glsl.js";
@@ -27,9 +29,54 @@ export const Shape = {
27
29
  RECTANGLE: "RECTANGLE",
28
30
  };
29
31
 
30
- const defaultTextureSheetAnimation = {
31
- tiles: new THREE.Vector2(1.0, 1.0),
32
- fps: 30.0,
32
+ export const getDefaultParticleSystemConfig = () =>
33
+ JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
34
+
35
+ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
36
+ duration: 5.0,
37
+ looping: true,
38
+ startDelay: { min: 0.0, max: 0.0 },
39
+ startLifeTime: { min: 2.0, max: 2.0 },
40
+ startSpeed: { min: 1.0, max: 1.0 },
41
+ startSize: { min: 1.0, max: 1.0 },
42
+ startRotation: { min: 0.0, max: 0.0 },
43
+ startColor: {
44
+ min: { r: 1.0, g: 1.0, b: 1.0 },
45
+ max: { r: 1.0, g: 1.0, b: 1.0 },
46
+ },
47
+ startOpacity: { min: 1.0, max: 1.0 },
48
+ gravity: 0,
49
+ simulationSpace: SimulationSpace.LOCAL,
50
+ maxParticles: 100.0,
51
+ emission: {
52
+ rateOverTime: 10.0,
53
+ rateOverDistance: 0.0,
54
+ },
55
+ shape: {
56
+ shape: Shape.SPHERE,
57
+ sphere: {
58
+ radius: 1.0,
59
+ radiusThickness: 1.0,
60
+ arc: 360.0,
61
+ },
62
+ cone: {
63
+ angle: 25.0,
64
+ radius: 1.0,
65
+ radiusThickness: 1.0,
66
+ arc: 360.0,
67
+ },
68
+ circle: {
69
+ radius: 1.0,
70
+ radiusThickness: 1.0,
71
+ arc: 360.0,
72
+ },
73
+ rectangle: {
74
+ rotation: { x: 0.0, y: 0.0 }, // TODO: add z rotation
75
+ scale: { x: 1.0, y: 1.0 },
76
+ },
77
+ },
78
+ map: null,
79
+ textureSheetAnimation: { tiles: new THREE.Vector2(1.0, 1.0), fps: 30.0 },
33
80
  };
34
81
 
35
82
  const createFloat32Attributes = ({
@@ -53,7 +100,7 @@ const createFloat32Attributes = ({
53
100
  };
54
101
 
55
102
  const calculatePositionAndVelocity = (
56
- { shape, sphere, cone, circle },
103
+ { shape, sphere, cone, circle, rectangle },
57
104
  startSpeed,
58
105
  position,
59
106
  velocity
@@ -85,78 +132,46 @@ const calculatePositionAndVelocity = (
85
132
  circle
86
133
  );
87
134
  break;
135
+
136
+ case Shape.RECTANGLE:
137
+ calculateRandomPositionAndVelocityOnRectangle(
138
+ position,
139
+ velocity,
140
+ startSpeed,
141
+ rectangle
142
+ );
143
+ break;
88
144
  }
89
145
  };
90
146
 
91
- export const createParticleSystem = ({
92
- duration = 5.0,
93
- looping = true,
94
- startDelay = { min: 0.0, max: 0.0 },
95
- startLifeTime = { min: 5.0, max: 5.0 },
96
- startSpeed = { min: 5.0, max: 5.0 },
97
- startSize = { min: 1.0, max: 1.0 },
98
- startRotation = { min: 0.0, max: 0.0 },
99
- startColor = {
100
- min: { r: 1.0, g: 1.0, b: 1.0 },
101
- max: { r: 1.0, g: 1.0, b: 1.0 },
102
- },
103
- startOpacity = { min: 1.0, max: 1.0 },
104
- gravity = 0.0,
105
- simulationSpace = SimulationSpace.LOCAL,
106
- maxParticles = 100,
107
- emission = {
108
- rateOverTime: 10.0,
109
- rateOverDistance: 0.0,
110
- },
111
- shape,
112
- map,
113
- onUpdate = null,
114
- onComplete = null,
115
- textureSheetAnimation = defaultTextureSheetAnimation,
116
- }) => {
147
+ export const createParticleSystem = (
148
+ config = DEFAULT_PARTICLE_SYSTEM_CONFIG
149
+ ) => {
117
150
  const now = Date.now();
118
151
  const lastWorldPosition = new THREE.Vector3(-99999, -99999, -99999);
119
152
  const worldPositionChange = new THREE.Vector3();
120
153
  const generalData = { distanceFromLastEmitByDistance: 0 };
121
154
 
122
- const normalizedStartDelay = { min: 0.0, max: 0.0, ...startDelay };
123
- const normalizedStartLifeTime = { min: 5.0, max: 5.0, ...startLifeTime };
124
- const normalizedStartSpeed = { min: 5.0, max: 5.0, ...startSpeed };
125
- const normalizedStartSize = { min: 1.0, max: 1.0, ...startSize };
126
- const normalizedStartRotation = { min: 0.0, max: 0.0, ...startRotation };
127
- const normalizedStartColor = {
128
- min: { r: 1.0, g: 1.0, b: 1.0, ...startColor.min },
129
- max: { r: 1.0, g: 1.0, b: 1.0, ...startColor.max },
130
- };
131
- const normalizedStartOpacity = { min: 0.0, max: 0.0, ...startOpacity };
132
- const normalizedEmission = {
133
- rateOverTime: 10.0,
134
- rateOverDistance: 0.0,
135
- ...emission,
136
- };
137
- const normalizedShape = {
138
- shape: Shape.SPHERE,
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
- },
158
- ...shape,
159
- };
155
+ const {
156
+ duration,
157
+ looping,
158
+ startDelay,
159
+ startLifeTime,
160
+ startSpeed,
161
+ startSize,
162
+ startRotation,
163
+ startColor,
164
+ startOpacity,
165
+ gravity,
166
+ simulationSpace,
167
+ maxParticles,
168
+ emission,
169
+ shape,
170
+ map,
171
+ onUpdate,
172
+ onComplete,
173
+ textureSheetAnimation,
174
+ } = deepMerge(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
160
175
 
161
176
  const startPositions = Array.from(
162
177
  { length: maxParticles },
@@ -167,18 +182,10 @@ export const createParticleSystem = ({
167
182
  () => new THREE.Vector3()
168
183
  );
169
184
 
170
- const rawUniforms = {
171
- ...defaultTextureSheetAnimation,
172
- ...textureSheetAnimation,
173
- };
174
- rawUniforms.tiles = rawUniforms.tiles
175
- ? rawUniforms.tiles
176
- : defaultTextureSheetAnimation.tiles;
177
-
178
- const uniforms = Object.keys(rawUniforms).reduce(
185
+ const uniforms = Object.keys(textureSheetAnimation).reduce(
179
186
  (prev, key) => ({
180
187
  ...prev,
181
- [key]: { value: rawUniforms[key] },
188
+ [key]: { value: textureSheetAnimation[key] },
182
189
  }),
183
190
  {}
184
191
  );
@@ -205,8 +212,8 @@ export const createParticleSystem = ({
205
212
 
206
213
  for (let i = 0; i < maxParticles; i++)
207
214
  calculatePositionAndVelocity(
208
- normalizedShape,
209
- normalizedStartSpeed,
215
+ shape,
216
+ startSpeed,
210
217
  startPositions[i],
211
218
  velocities[i]
212
219
  );
@@ -230,10 +237,7 @@ export const createParticleSystem = ({
230
237
  createFloat32AttributesRequest("creationTime", 0);
231
238
  createFloat32AttributesRequest("lifeTime", 0);
232
239
  createFloat32AttributesRequest("startLifeTime", () =>
233
- THREE.MathUtils.randFloat(
234
- normalizedStartLifeTime.min,
235
- normalizedStartLifeTime.max
236
- )
240
+ THREE.MathUtils.randFloat(startLifeTime.min, startLifeTime.max)
237
241
  );
238
242
 
239
243
  createFloat32AttributesRequest("opacity", 0);
@@ -244,10 +248,7 @@ export const createParticleSystem = ({
244
248
  maxParticles,
245
249
  factory: () =>
246
250
  THREE.Math.degToRad(
247
- THREE.MathUtils.randFloat(
248
- normalizedStartRotation.min,
249
- normalizedStartRotation.max
250
- )
251
+ THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
251
252
  ),
252
253
  });
253
254
 
@@ -255,11 +256,7 @@ export const createParticleSystem = ({
255
256
  geometry,
256
257
  propertyName: "startSize",
257
258
  maxParticles,
258
- factory: () =>
259
- THREE.MathUtils.randFloat(
260
- normalizedStartSize.min,
261
- normalizedStartSize.max
262
- ),
259
+ factory: () => THREE.MathUtils.randFloat(startSize.min, startSize.max),
263
260
  });
264
261
 
265
262
  createFloat32AttributesRequest("rotation", 0);
@@ -268,23 +265,20 @@ export const createParticleSystem = ({
268
265
  createFloat32AttributesRequest(
269
266
  "colorR",
270
267
  () =>
271
- normalizedStartColor.min.r +
272
- colorRandomRatio *
273
- (normalizedStartColor.max.r - normalizedStartColor.min.r)
268
+ startColor.min.r +
269
+ colorRandomRatio * (startColor.max.r - startColor.min.r)
274
270
  );
275
271
  createFloat32AttributesRequest(
276
272
  "colorG",
277
273
  () =>
278
- normalizedStartColor.min.g +
279
- colorRandomRatio *
280
- (normalizedStartColor.max.g - normalizedStartColor.min.g)
274
+ startColor.min.g +
275
+ colorRandomRatio * (startColor.max.g - startColor.min.g)
281
276
  );
282
277
  createFloat32AttributesRequest(
283
278
  "colorB",
284
279
  () =>
285
- normalizedStartColor.min.b +
286
- colorRandomRatio *
287
- (normalizedStartColor.max.b - normalizedStartColor.min.b)
280
+ startColor.min.b +
281
+ colorRandomRatio * (startColor.max.b - startColor.min.b)
288
282
  );
289
283
  createFloat32AttributesRequest("colorA", 0);
290
284
 
@@ -304,54 +298,42 @@ export const createParticleSystem = ({
304
298
  const colorRandomRatio = Math.random();
305
299
 
306
300
  geometry.attributes.colorR.array[particleIndex] =
307
- normalizedStartColor.min.r +
308
- colorRandomRatio *
309
- (normalizedStartColor.max.r - normalizedStartColor.min.r);
301
+ startColor.min.r +
302
+ colorRandomRatio * (startColor.max.r - startColor.min.r);
310
303
  geometry.attributes.colorR.needsUpdate = true;
311
304
 
312
305
  geometry.attributes.colorG.array[particleIndex] =
313
- normalizedStartColor.min.g +
314
- colorRandomRatio *
315
- (normalizedStartColor.max.g - normalizedStartColor.min.g);
306
+ startColor.min.g +
307
+ colorRandomRatio * (startColor.max.g - startColor.min.g);
316
308
  geometry.attributes.colorG.needsUpdate = true;
317
309
 
318
310
  geometry.attributes.colorB.array[particleIndex] =
319
- normalizedStartColor.min.b +
320
- colorRandomRatio *
321
- (normalizedStartColor.max.b - normalizedStartColor.min.b);
311
+ startColor.min.b +
312
+ colorRandomRatio * (startColor.max.b - startColor.min.b);
322
313
  geometry.attributes.colorB.needsUpdate = true;
323
314
 
324
315
  geometry.attributes.colorA.array[particleIndex] = THREE.MathUtils.randFloat(
325
- normalizedStartOpacity.min,
326
- normalizedStartOpacity.max
316
+ startOpacity.min,
317
+ startOpacity.max
327
318
  );
328
319
  geometry.attributes.colorA.needsUpdate = true;
329
320
 
330
321
  geometry.attributes.startLifeTime.array[particleIndex] =
331
- THREE.MathUtils.randFloat(
332
- normalizedStartLifeTime.min,
333
- normalizedStartLifeTime.max
334
- ) * 1000;
322
+ THREE.MathUtils.randFloat(startLifeTime.min, startLifeTime.max) * 1000;
335
323
  geometry.attributes.startLifeTime.needsUpdate = true;
336
324
 
337
325
  geometry.attributes.startSize.array[particleIndex] =
338
- THREE.MathUtils.randFloat(
339
- normalizedStartSize.min,
340
- normalizedStartSize.max
341
- );
326
+ THREE.MathUtils.randFloat(startSize.min, startSize.max);
342
327
  geometry.attributes.startSize.needsUpdate = true;
343
328
 
344
329
  geometry.attributes.rotation.array[particleIndex] = THREE.Math.degToRad(
345
- THREE.MathUtils.randFloat(
346
- normalizedStartRotation.min,
347
- normalizedStartRotation.max
348
- )
330
+ THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
349
331
  );
350
332
  geometry.attributes.rotation.needsUpdate = true;
351
333
 
352
334
  calculatePositionAndVelocity(
353
- normalizedShape,
354
- normalizedStartSpeed,
335
+ shape,
336
+ startSpeed,
355
337
  startPositions[particleIndex],
356
338
  velocities[particleIndex]
357
339
  );
@@ -372,12 +354,7 @@ export const createParticleSystem = ({
372
354
  particleSystem.sortParticles = true;
373
355
 
374
356
  const calculatedCreationTime =
375
- now +
376
- THREE.MathUtils.randFloat(
377
- normalizedStartDelay.min,
378
- normalizedStartDelay.max
379
- ) *
380
- 1000;
357
+ now + THREE.MathUtils.randFloat(startDelay.min, startDelay.max) * 1000;
381
358
 
382
359
  createdParticleSystems.push({
383
360
  particleSystem,
@@ -392,7 +369,7 @@ export const createParticleSystem = ({
392
369
  looping,
393
370
  simulationSpace,
394
371
  gravity,
395
- emission: normalizedEmission,
372
+ emission,
396
373
  iterationCount: 0,
397
374
  velocities,
398
375
  deactivateParticle,