@needle-tools/engine 2.41.0-pre → 2.43.0-pre

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/needle-engine.d.ts +226 -76
  3. package/dist/needle-engine.js +3847 -433
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +51 -51
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine_element.js +1 -1
  8. package/lib/engine/engine_element.js.map +1 -1
  9. package/lib/engine/engine_element_loading.js +24 -3
  10. package/lib/engine/engine_element_loading.js.map +1 -1
  11. package/lib/engine/engine_gizmos.d.ts +8 -22
  12. package/lib/engine/engine_gizmos.js +37 -3
  13. package/lib/engine/engine_gizmos.js.map +1 -1
  14. package/lib/engine/engine_setup.d.ts +3 -0
  15. package/lib/engine/engine_setup.js +15 -0
  16. package/lib/engine/engine_setup.js.map +1 -1
  17. package/lib/engine/engine_three_utils.d.ts +17 -1
  18. package/lib/engine/engine_three_utils.js +104 -54
  19. package/lib/engine/engine_three_utils.js.map +1 -1
  20. package/lib/engine/engine_time.js +2 -0
  21. package/lib/engine/engine_time.js.map +1 -1
  22. package/lib/engine/engine_types.d.ts +6 -0
  23. package/lib/engine/engine_types.js.map +1 -1
  24. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +42 -0
  25. package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
  26. package/lib/engine-components/Animation.js +9 -1
  27. package/lib/engine-components/Animation.js.map +1 -1
  28. package/lib/engine-components/AnimationCurve.js +2 -2
  29. package/lib/engine-components/AnimationCurve.js.map +1 -1
  30. package/lib/engine-components/AnimatorController.js +4 -1
  31. package/lib/engine-components/AnimatorController.js.map +1 -1
  32. package/lib/engine-components/BoxHelperComponent.js +9 -10
  33. package/lib/engine-components/BoxHelperComponent.js.map +1 -1
  34. package/lib/engine-components/Camera.d.ts +3 -0
  35. package/lib/engine-components/Camera.js +17 -9
  36. package/lib/engine-components/Camera.js.map +1 -1
  37. package/lib/engine-components/ParticleSystem.d.ts +41 -13
  38. package/lib/engine-components/ParticleSystem.js +587 -219
  39. package/lib/engine-components/ParticleSystem.js.map +1 -1
  40. package/lib/engine-components/ParticleSystemBehaviours.d.ts +0 -0
  41. package/lib/engine-components/ParticleSystemBehaviours.js +2 -0
  42. package/lib/engine-components/ParticleSystemBehaviours.js.map +1 -0
  43. package/lib/engine-components/ParticleSystemModules.d.ts +123 -22
  44. package/lib/engine-components/ParticleSystemModules.js +377 -60
  45. package/lib/engine-components/ParticleSystemModules.js.map +1 -1
  46. package/lib/engine-components/ReflectionProbe.js +29 -11
  47. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  48. package/lib/engine-components/Renderer.d.ts +1 -0
  49. package/lib/engine-components/Renderer.js +4 -2
  50. package/lib/engine-components/Renderer.js.map +1 -1
  51. package/lib/engine-components/codegen/components.d.ts +5 -0
  52. package/lib/engine-components/codegen/components.js +5 -0
  53. package/lib/engine-components/codegen/components.js.map +1 -1
  54. package/lib/engine-components/js-extensions/RGBAColor.d.ts +1 -0
  55. package/lib/engine-components/js-extensions/RGBAColor.js +7 -0
  56. package/lib/engine-components/js-extensions/RGBAColor.js.map +1 -1
  57. package/package.json +3 -2
  58. package/src/engine/codegen/register_types.js +20 -0
  59. package/src/engine/dist/engine_three_utils.js +279 -0
  60. package/src/engine/engine_element.ts +1 -1
  61. package/src/engine/engine_element_loading.ts +23 -2
  62. package/src/engine/engine_gizmos.ts +45 -8
  63. package/src/engine/engine_setup.ts +25 -2
  64. package/src/engine/engine_three_utils.ts +113 -63
  65. package/src/engine/engine_time.ts +1 -0
  66. package/src/engine/engine_types.ts +8 -1
  67. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +43 -1
  68. package/src/engine-components/Animation.ts +7 -1
  69. package/src/engine-components/AnimationCurve.ts +2 -2
  70. package/src/engine-components/AnimatorController.ts +5 -1
  71. package/src/engine-components/BoxHelperComponent.ts +12 -15
  72. package/src/engine-components/Camera.ts +17 -10
  73. package/src/engine-components/ParticleSystem.ts +660 -258
  74. package/src/engine-components/ParticleSystemBehaviours.ts +0 -0
  75. package/src/engine-components/ParticleSystemModules.ts +379 -64
  76. package/src/engine-components/ReflectionProbe.ts +37 -13
  77. package/src/engine-components/Renderer.ts +4 -2
  78. package/src/engine-components/codegen/components.ts +5 -0
  79. package/src/engine-components/js-extensions/RGBAColor.ts +7 -0
@@ -1,16 +1,38 @@
1
1
  import { init } from "@dimforge/rapier3d-compat";
2
2
  import * as THREE from "three";
3
- import { Color, Matrix4, Object3D, Vector3 } from "three";
3
+ import { Color, Matrix4, Object3D, PointLightShadow, Quaternion, Vector3 } from "three";
4
4
  import { Mathf } from "../engine/engine_math";
5
5
  import { serializeable } from "../engine/engine_serialization";
6
6
  import { RGBAColor } from "./js-extensions/RGBAColor";
7
7
  import { AnimationCurve } from "./AnimationCurve";
8
8
  import { Vec3 } from "../engine/engine_types";
9
+ import { EmitterShape, FrameOverLife, Particle, ShapeJSON } from "three.quarks";
9
10
 
10
11
  declare type Color4 = { r: number, g: number, b: number, a: number };
11
12
  declare type ColorKey = { time: number, color: Color4 };
12
13
  declare type AlphaKey = { time: number, alpha: number };
13
14
 
15
+ export interface IParticleSystem {
16
+ get currentParticles(): number;
17
+ get maxParticles(): number;
18
+ get time(): number;
19
+ get duration(): number;
20
+ readonly main: MainModule;
21
+ get container(): Object3D;
22
+ get worldQuaternion(): Quaternion;
23
+ get worldQuaternionInverted(): Quaternion;
24
+ }
25
+
26
+ export enum ParticleSystemRenderMode {
27
+ Billboard = 0,
28
+ // Stretch = 1,
29
+ // HorizontalBillboard = 2,
30
+ // VerticalBillboard = 3,
31
+ Mesh = 4,
32
+ // None = 5,
33
+ }
34
+
35
+
14
36
  export class Gradient {
15
37
  @serializeable()
16
38
  alphaKeys!: Array<AlphaKey>;
@@ -147,14 +169,14 @@ export class MinMaxCurve {
147
169
  case ParticleSystemCurveMode.Constant:
148
170
  return this.constant;
149
171
  case ParticleSystemCurveMode.Curve:
150
- t01 %= this.curve!.duration;
151
- return this.curve!.evaluate(t01);
172
+ t01 = Mathf.clamp01(t01);
173
+ return this.curve!.evaluate(t01) * this.curveMultiplier!;
152
174
  case ParticleSystemCurveMode.TwoCurves:
153
175
  const t1 = t01 * this.curveMin!.duration;
154
176
  const t2 = t01 * this.curveMax!.duration;
155
- return Mathf.lerp(this.curveMin!.evaluate(t1), this.curveMax!.evaluate(t2), t % 1);
177
+ return Mathf.lerp(this.curveMin!.evaluate(t1), this.curveMax!.evaluate(t2), t % 1) * this.curveMultiplier!;
156
178
  case ParticleSystemCurveMode.TwoConstants:
157
- return Mathf.lerp(this.constantMin, this.constantMax, t % 1);
179
+ return Mathf.lerp(this.constantMin, this.constantMax, t % 1)
158
180
  default:
159
181
  this.curveMax!.evaluate(t01) * this.curveMultiplier!;
160
182
  break;
@@ -178,7 +200,8 @@ export class MinMaxGradient {
178
200
  @serializeable(Gradient)
179
201
  gradientMax!: Gradient;
180
202
 
181
- private _temp: RGBAColor = new RGBAColor(0, 0, 0, 1);
203
+ private static _temp: RGBAColor = new RGBAColor(0, 0, 0, 1);
204
+ private static _temp2: RGBAColor = new RGBAColor(0, 0, 0, 1);
182
205
 
183
206
  evaluate(t01: number, lerpFactor?: number): RGBAColor {
184
207
  const t = lerpFactor === undefined ? Math.random() : lerpFactor;
@@ -186,15 +209,22 @@ export class MinMaxGradient {
186
209
  case ParticleSystemGradientMode.Color:
187
210
  return this.color;
188
211
  case ParticleSystemGradientMode.Gradient:
189
- this.gradient.evaluate(t01, this._temp);
190
- return this._temp
212
+ this.gradient.evaluate(t01, MinMaxGradient._temp);
213
+ return MinMaxGradient._temp
191
214
  case ParticleSystemGradientMode.TwoColors:
192
- return this._temp.lerpColors(this.colorMin, this.colorMax, t);
215
+ const col1 = MinMaxGradient._temp.lerpColors(this.colorMin, this.colorMax, t);
216
+ return col1;
217
+ case ParticleSystemGradientMode.TwoGradients:
218
+ const t2 = Math.random();
219
+ this.gradientMin.evaluate(t2, MinMaxGradient._temp);
220
+ this.gradientMax.evaluate(t2, MinMaxGradient._temp2);
221
+ return MinMaxGradient._temp.lerp(MinMaxGradient._temp2, t);
222
+
193
223
  }
194
224
  // console.warn("Not implemented", ParticleSystemGradientMode[this.mode]);
195
- this._temp.set(0xff00ff)
196
- this._temp.alpha = 1;
197
- return this._temp;
225
+ MinMaxGradient._temp.set(0xff00ff)
226
+ MinMaxGradient._temp.alpha = 1;
227
+ return MinMaxGradient._temp;
198
228
  }
199
229
  }
200
230
 
@@ -262,44 +292,106 @@ export class MainModule {
262
292
  useUnscaledTime!: boolean;
263
293
  }
264
294
 
295
+
296
+ export class ParticleBurst {
297
+ cycleCount!: number;
298
+ maxCount!: number;
299
+ minCount!: number;
300
+ probability!: number;
301
+ repeatInterval!: number;
302
+ time!: number;
303
+ count!: {
304
+ constant: number;
305
+ constantMax: number;
306
+ constantMin: number;
307
+ curve?: AnimationCurve;
308
+ curveMax?: AnimationCurve;
309
+ curveMin?: AnimationCurve;
310
+ curveMultiplier?: number;
311
+ mode: ParticleSystemCurveMode;
312
+ }
313
+
314
+
315
+ private _performed: number = 0;
316
+
317
+
318
+ reset() {
319
+ this._performed = 0;
320
+ }
321
+ run(time: number): number {
322
+ if (time <= this.time) {
323
+ this.reset();
324
+ return 0;
325
+ }
326
+ let amount = 0;
327
+ if (this.cycleCount === 0 || this._performed < this.cycleCount) {
328
+ const nextTime = this.time + this.repeatInterval * this._performed;
329
+ if (time >= nextTime) {
330
+ this._performed += 1;
331
+ if (Math.random() < this.probability) {
332
+ switch (this.count.mode) {
333
+ case ParticleSystemCurveMode.Constant:
334
+ amount = this.count.constant;
335
+ break;
336
+ case ParticleSystemCurveMode.TwoConstants:
337
+ amount = Mathf.lerp(this.count.constantMin, this.count.constantMax, Math.random());
338
+ break;
339
+ case ParticleSystemCurveMode.Curve:
340
+ amount = this.count.curve!.evaluate(Math.random());
341
+ break;
342
+ case ParticleSystemCurveMode.TwoCurves:
343
+ const t = Math.random();
344
+ amount = Mathf.lerp(this.count.curveMin!.evaluate(t), this.count.curveMax!.evaluate(t), Math.random());
345
+ break;
346
+ }
347
+ }
348
+ }
349
+ }
350
+ return amount;
351
+ }
352
+ }
353
+
265
354
  export class EmissionModule {
266
- burstCount!: number;
355
+
356
+ @serializeable()
267
357
  enabled!: boolean;
268
358
 
359
+
360
+ get burstCount() {
361
+ return this.bursts?.length ?? 0;
362
+ }
363
+
364
+ @serializeable()
365
+ bursts!: ParticleBurst[];
366
+
269
367
  @serializeable(MinMaxCurve)
270
368
  rateOverTime!: MinMaxCurve;
369
+ @serializeable()
271
370
  rateOverTimeMultiplier!: number;
272
371
 
273
372
  @serializeable(MinMaxCurve)
274
373
  rateOverDistance!: MinMaxCurve;
374
+ @serializeable()
275
375
  rateOverDistanceMultiplier!: number;
276
376
 
277
377
 
278
- currentParticles: number = 0;
279
- maxParticles: number = Infinity;
280
-
281
- private _time: number = 0;
282
- private _summed: number = 0;
283
- // private _didEmit: boolean = false;
378
+ /** set from system */
379
+ system!: IParticleSystem;
380
+ get time() { return this.system.time; }
284
381
 
285
- /** called by nebula */
286
- init() {
287
- }
288
382
 
289
- /** called by nebula */
290
- getValue(deltaTime: number) {
291
- if (this.currentParticles >= this.maxParticles)
292
- return 0;
293
- // if (this._didEmit) return 0;
294
- // this._didEmit = true;
295
- // return 1;
296
-
297
- if (!this.enabled) return 0;
298
- const time = this._time += deltaTime;
299
- let count = this.rateOverTime.evaluate(time, Math.random());
300
- this._summed += count * deltaTime;
301
- const amount = Math.floor(this._summed);
302
- this._summed -= amount;
383
+ getBurst() {
384
+ let amount = 0;
385
+ if (this.burstCount > 0) {
386
+ for (let i = 0; i < this.burstCount; i++) {
387
+ const burst = this.bursts[i];
388
+ if (burst.time >= this.time) {
389
+ burst.reset();
390
+ continue;
391
+ }
392
+ amount += Math.round(burst.run(this.time));
393
+ }
394
+ }
303
395
  return amount;
304
396
  }
305
397
  }
@@ -317,18 +409,26 @@ export class SizeOverLifetimeModule {
317
409
  size!: MinMaxCurve;
318
410
  sizeMultiplier!: number;
319
411
  @serializeable(MinMaxCurve)
320
- sizeX!: MinMaxCurve;
321
- sizeXMultiplier!: number;
412
+ x!: MinMaxCurve;
413
+ xMultiplier!: number;
322
414
  @serializeable(MinMaxCurve)
323
- sizeY!: MinMaxCurve;
324
- sizeYMultiplier!: number;
415
+ y!: MinMaxCurve;
416
+ yMultiplier!: number;
325
417
  @serializeable(MinMaxCurve)
326
- sizeZ!: MinMaxCurve;
327
- sizeZMultiplier!: number;
418
+ z!: MinMaxCurve;
419
+ zMultiplier!: number;
328
420
 
329
421
  private _time: number = 0;
422
+ private _temp = new Vector3();
423
+
424
+ evaluate(t01: number, target?: Vec3) {
425
+ if (!target) target = this._temp;
426
+
427
+ if (!this.enabled) {
428
+ target.x = target.y = target.z = 1;
429
+ return target;
430
+ }
330
431
 
331
- evaluate(t01: number, target: Vec3) {
332
432
  if (!this.separateAxes) {
333
433
  const scale = this.size.evaluate(t01) * this.sizeMultiplier;
334
434
  target.x = scale;
@@ -336,15 +436,31 @@ export class SizeOverLifetimeModule {
336
436
  // target.z = scale;
337
437
  }
338
438
  else {
339
- target.x = this.sizeX.evaluate(this._time) * this.sizeXMultiplier;
340
- // target.y = this.sizeY.evaluate(this._time) * this.sizeYMultiplier;
341
- // target.z = this.sizeZ.evaluate(this._time) * this.sizeZMultiplier;
439
+ target.x = this.x.evaluate(t01) * this.xMultiplier;
440
+ target.y = this.y.evaluate(t01) * this.yMultiplier;
441
+ target.z = this.z.evaluate(t01) * this.zMultiplier;
342
442
  }
343
443
  return target;
344
444
  }
345
445
  }
346
446
 
347
- export class ShapeModule {
447
+ export class ShapeModule implements EmitterShape {
448
+
449
+ get type(): string {
450
+ return ParticleSystemShapeType[this.shapeType];
451
+ }
452
+
453
+ initialize(particle: Particle): void {
454
+ this.getPosition();
455
+ particle.position.copy(this._vector);
456
+ }
457
+ toJSON(): ShapeJSON {
458
+ return this;
459
+ }
460
+ clone(): EmitterShape {
461
+ return new ShapeModule();
462
+ }
463
+
348
464
  @serializeable()
349
465
  shapeType: ParticleSystemShapeType = ParticleSystemShapeType.Box;
350
466
  @serializeable()
@@ -399,7 +515,6 @@ export class ShapeModule {
399
515
  get vector() {
400
516
  return this._vector;
401
517
  }
402
- /** called by nebula */
403
518
  getPosition(): void {
404
519
  switch (this.shapeType) {
405
520
  case ParticleSystemShapeType.Box:
@@ -410,11 +525,14 @@ export class ShapeModule {
410
525
  case ParticleSystemShapeType.Sphere:
411
526
  randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, this.arc, this._vector);
412
527
  break;
528
+ default:
529
+ this._vector.set(0, 0, 0);
530
+ break;
413
531
  // case ParticleSystemShapeType.Hemisphere:
414
532
  // randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
415
533
  // break;
416
534
  }
417
- if(this._space === ParticleSystemSimulationSpace.World) {
535
+ if (this._space === ParticleSystemSimulationSpace.World) {
418
536
  this._vector.applyMatrix4(this._worldSpacePosition);
419
537
  }
420
538
  }
@@ -422,7 +540,7 @@ export class ShapeModule {
422
540
 
423
541
 
424
542
  private _dir: Vector3 = new Vector3();
425
- getDirection(position): Vec3 {
543
+ getDirection(position): Vector3 {
426
544
  switch (this.shapeType) {
427
545
  case ParticleSystemShapeType.Box:
428
546
  return this._dir.set(0, 0, 1);
@@ -456,6 +574,8 @@ function randomSpherePoint(x0: number, y0: number, z0: number, radius: number, t
456
574
 
457
575
  import { createNoise4D, NoiseFunction4D } from 'simplex-noise';
458
576
  import { Context } from "../engine/engine_setup";
577
+ import { getWorldQuaternion } from "../engine/engine_three_utils";
578
+ import { Gizmos } from "../engine/engine_gizmos";
459
579
 
460
580
  export class NoiseModule {
461
581
  @serializeable()
@@ -523,34 +643,229 @@ export class NoiseModule {
523
643
 
524
644
  /** nebula implementations: */
525
645
  private _temp: Vector3 = new Vector3();
526
- applyNoise(index: number, pos: Vec3, vel: Vec3, deltaTime: number, age: number, life: number) {
646
+ apply(_index: number, pos: Vec3, vel: Vec3, _deltaTime: number, age: number, life: number) {
527
647
  if (!this.enabled) return;
528
648
  if (!this._noise) {
529
649
  this._noise = createNoise4D(() => 0);
530
650
  }
531
- const t = age / life;
532
- const dt = Context.Current.time.deltaTime;
533
651
  const temp = this._temp.set(pos.x, pos.y, pos.z).multiplyScalar(this.frequency);
534
652
  const nx = this._noise(temp.x, temp.y, temp.z, this._time);
535
- const ny = this._noise(temp.x, temp.y, temp.z, this._time + .2);
536
- const nz = this._noise(temp.x, temp.y, temp.z, this._time + .5);
537
- this._temp.set(nx, ny, nz).normalize();
653
+ const ny = this._noise(temp.x, temp.y, temp.z, this._time + .3);
654
+ const nz = this._noise(temp.x, temp.y, temp.z, this._time + 1);
655
+ this._temp.set(nx, ny, nz).normalize()
538
656
 
657
+ const t = age / life;
539
658
  let strengthFactor = this.positionAmount.evaluate(t);
540
659
  if (!this.separateAxes) {
541
- if (this.strengthX)
542
- strengthFactor *= this.strengthX.evaluate(t, index * 1.1);
543
- strengthFactor *= this.strengthMultiplier;
544
- strengthFactor *= .1;
660
+ if (this.strengthX) {
661
+ strengthFactor *= this.strengthX.evaluate(t) * Math.PI;
662
+ }
663
+ // strengthFactor *= this.strengthMultiplier;
664
+ // strengthFactor *= deltaTime;
545
665
  this._temp.multiplyScalar(strengthFactor);
546
666
  }
547
- if (this.separateAxes) {
548
- this._temp.x *= strengthFactor * deltaTime * this.strengthXMultiplier
549
- this._temp.y *= strengthFactor * deltaTime * this.strengthYMultiplier;
550
- this._temp.z *= strengthFactor * deltaTime * this.strengthZMultiplier;
667
+ else {
668
+ this._temp.x *= strengthFactor * this.strengthXMultiplier
669
+ this._temp.y *= strengthFactor * this.strengthYMultiplier;
670
+ this._temp.z *= strengthFactor * this.strengthZMultiplier;
551
671
  }
672
+ // this._temp.setLength(strengthFactor * deltaTime);
552
673
  vel.x += this._temp.x;
553
674
  vel.y += this._temp.y;
554
675
  vel.z += this._temp.z;
555
676
  }
677
+ }
678
+
679
+ export class TrailModule {
680
+
681
+ @serializeable()
682
+ enabled!: boolean;
683
+ }
684
+
685
+ export class VelocityOverLifetimeModule {
686
+ @serializeable()
687
+ enabled!: boolean;
688
+
689
+ /* orbital settings */
690
+
691
+
692
+ @serializeable()
693
+ space: ParticleSystemSimulationSpace = ParticleSystemSimulationSpace.Local;
694
+
695
+ @serializeable(MinMaxCurve)
696
+ speedModifier!: MinMaxCurve;
697
+ @serializeable()
698
+ speedModifierMultiplier!: number;
699
+ @serializeable(MinMaxCurve)
700
+ x!: MinMaxCurve;
701
+ @serializeable()
702
+ xMultiplier!: number;
703
+ @serializeable(MinMaxCurve)
704
+ y!: MinMaxCurve;
705
+ @serializeable()
706
+ yMultiplier!: number;
707
+ @serializeable(MinMaxCurve)
708
+ z!: MinMaxCurve;
709
+ @serializeable()
710
+ zMultiplier!: number;
711
+
712
+ private _system?: IParticleSystem;
713
+ // private _worldRotation: Quaternion = new Quaternion();
714
+
715
+ update(system: IParticleSystem) {
716
+ this._system = system;
717
+ }
718
+
719
+ private _temp: Vector3 = new Vector3();
720
+
721
+ apply(_index: number, _pos: Vec3, vel: Vec3, _dt: number, age: number, life: number) {
722
+ if (!this.enabled) return;
723
+ const t = age / life;
724
+
725
+ const speed = this.speedModifier.evaluate(t) * this.speedModifierMultiplier;
726
+ const x = this.x.evaluate(t) * speed;
727
+ const y = this.y.evaluate(t) * speed;
728
+ const z = this.z.evaluate(t) * speed;
729
+ this._temp.set(-x, y, z);
730
+ if (this._system) {
731
+ if (this.space === ParticleSystemSimulationSpace.World) {
732
+ this._temp.applyQuaternion(this._system.worldQuaternionInverted);
733
+ }
734
+ if (this._system.main.simulationSpace === ParticleSystemSimulationSpace.World) {
735
+ this._temp.applyQuaternion(this._system.worldQuaternion);
736
+ }
737
+ }
738
+ vel.x += this._temp.x;
739
+ vel.y += this._temp.y;
740
+ vel.z += this._temp.z;
741
+ }
742
+ }
743
+
744
+
745
+
746
+ enum ParticleSystemAnimationTimeMode {
747
+ Lifetime,
748
+ Speed,
749
+ FPS,
750
+ }
751
+
752
+ enum ParticleSystemAnimationMode {
753
+ Grid,
754
+ Sprites,
755
+ }
756
+
757
+ enum ParticleSystemAnimationRowMode {
758
+ Custom,
759
+ Random,
760
+ MeshIndex,
761
+ }
762
+
763
+ enum ParticleSystemAnimationType {
764
+ WholeSheet,
765
+ SingleRow,
766
+ }
767
+
768
+ export class TextureSheetAnimationModule {
769
+
770
+ @serializeable()
771
+ animation!: ParticleSystemAnimationType;
772
+
773
+ @serializeable()
774
+ enabled!: boolean;
775
+
776
+ @serializeable()
777
+ cycleCount!: number;
778
+
779
+ @serializeable(MinMaxCurve)
780
+ frameOverTime!: MinMaxCurve;
781
+ @serializeable()
782
+ frameOverTimeMultiplier!: number;
783
+
784
+ @serializeable()
785
+ numTilesX!: number;
786
+ @serializeable()
787
+ numTilesY!: number;
788
+
789
+ @serializeable(MinMaxCurve)
790
+ startFrame!: MinMaxCurve;
791
+ @serializeable()
792
+ startFrameMultiplier!: number;
793
+
794
+ @serializeable()
795
+ rowMode!: ParticleSystemAnimationRowMode;
796
+ @serializeable()
797
+ rowIndex!: number;
798
+
799
+ @serializeable()
800
+ spriteCount!: number;
801
+
802
+ @serializeable()
803
+ timeMode!: ParticleSystemAnimationTimeMode;
804
+
805
+ private sampleOnceAtStart(): boolean {
806
+ if (this.timeMode === ParticleSystemAnimationTimeMode.Lifetime) {
807
+ switch (this.frameOverTime.mode) {
808
+ case ParticleSystemCurveMode.Constant:
809
+ case ParticleSystemCurveMode.TwoConstants:
810
+ return true;
811
+ }
812
+ }
813
+ return false;
814
+ }
815
+
816
+ getStartIndex(): number {
817
+ if (this.sampleOnceAtStart()) {
818
+ return this.frameOverTime.evaluate(Math.random())
819
+ }
820
+ return 0;
821
+ }
822
+
823
+ evaluate(t01: number): number | undefined {
824
+ if (this.sampleOnceAtStart()) {
825
+ return;
826
+ }
827
+ return this.getIndex(t01);
828
+ }
829
+
830
+ private getIndex(t01: number): number {
831
+ const tiles = this.numTilesX * this.numTilesY;
832
+ // let pos = t01 * this.cycleCount;
833
+ let index = this.frameOverTime.evaluate(t01 % 1);
834
+ index *= this.frameOverTimeMultiplier;
835
+ index *= tiles;
836
+ index = index % tiles;
837
+ index = Math.floor(index);
838
+ // console.log(index);
839
+ return index;
840
+ }
841
+ }
842
+
843
+
844
+ export class RotationOverLifetimeModule {
845
+ @serializeable()
846
+ enabled!: boolean;
847
+
848
+ @serializeable()
849
+ separateAxes!: boolean;
850
+
851
+ @serializeable(MinMaxCurve)
852
+ x!: MinMaxCurve;
853
+ @serializeable()
854
+ xMultiplier!: number;
855
+ @serializeable(MinMaxCurve)
856
+ y!: MinMaxCurve;
857
+ @serializeable()
858
+ yMultiplier!: number;
859
+ @serializeable(MinMaxCurve)
860
+ z!: MinMaxCurve;
861
+ @serializeable()
862
+ zMultiplier!: number;
863
+
864
+ evaluate(t01: number): number {
865
+ if (!this.enabled) return 0;
866
+ if (!this.separateAxes) {
867
+ return this.z.evaluate(t01) * -1;
868
+ }
869
+ return 0;
870
+ }
556
871
  }
@@ -10,11 +10,14 @@ import { getParam } from "../engine/engine_utils";
10
10
  export const debug = getParam("debugreflectionprobe");
11
11
  const disable = getParam("noreflectionprobe");
12
12
 
13
+ const $reflectionProbeKey = Symbol("reflectionProbeKey");
14
+ const $originalMaterial = Symbol("original material");
15
+
13
16
  export class ReflectionProbe extends Behaviour {
14
17
 
15
18
  private static _probes: Map<Context, ReflectionProbe[]> = new Map();
16
19
 
17
- public static get(object: Object3D | null | undefined, context: Context, isAnchor:boolean): ReflectionProbe | null {
20
+ public static get(object: Object3D | null | undefined, context: Context, isAnchor: boolean): ReflectionProbe | null {
18
21
  if (!object || object.isObject3D !== true) return null;
19
22
  if (disable) return null;
20
23
  const probes = ReflectionProbe._probes.get(context);
@@ -91,7 +94,7 @@ export class ReflectionProbe extends Behaviour {
91
94
  // and some need reflection probe and some don't
92
95
  // we need to make sure we don't override the material but use a copy
93
96
 
94
- private static _rendererMaterialsCache: Map<IRenderer, Array<{ orig: Material, copy: Material }>> = new Map();
97
+ private static _rendererMaterialsCache: Map<IRenderer, Array<{ material: Material, copy: Material }>> = new Map();
95
98
 
96
99
  onSet(_rend: IRenderer) {
97
100
  if (disable) return;
@@ -109,22 +112,43 @@ export class ReflectionProbe extends Behaviour {
109
112
  // need to make sure materials are not shared when using reflection probes
110
113
  // otherwise some renderers outside of the probe will be affected or vice versa
111
114
  for (let i = 0; i < _rend.sharedMaterials.length; i++) {
112
- const orig = _rend.sharedMaterials[i];
113
- if (!orig) continue;
114
- if (orig["envMap"] === undefined) continue;
115
+ const material = _rend.sharedMaterials[i];
116
+ if (!material) continue;
117
+ if (material["envMap"] === undefined) continue;
118
+ if (material["envMap"] === this.texture) {
119
+ continue;
120
+ }
115
121
  let cached = rendererCache[i];
116
- let copy = cached?.copy;
117
- if (!cached) {
118
- const clone = orig.clone();
119
- copy = clone;
120
- rendererCache.push({ orig, copy });
121
122
 
122
- copy["__reflection_probe"] = this;
123
- copy["envMap"] = this.texture;
123
+ // make sure we have the currently assigned material cached (and an up to date clone of that)
124
+ if (!cached || cached.material !== material || cached.material.version !== material.version) {
125
+ const clone = material.clone();
126
+
127
+ if (cached) {
128
+ cached.copy = clone;
129
+ cached.material = material;
130
+ }
131
+ else {
132
+ cached = {
133
+ material: material,
134
+ copy: clone
135
+ };
136
+ rendererCache.push(cached);
137
+ }
138
+
139
+ clone[$reflectionProbeKey] = this;
140
+ clone[$originalMaterial] = material;
141
+
142
+ // make sure the reflection probe is assigned
143
+ clone["envMap"] = this.texture;
124
144
 
125
145
  if (debug)
126
146
  console.log("Set reflection", _rend.name, _rend.guid);
127
147
  }
148
+
149
+ /** this is the material that we copied and that has the reflection probe */
150
+ const copy = cached?.copy;
151
+
128
152
  _rend.sharedMaterials[i] = copy;
129
153
  }
130
154
  }
@@ -134,7 +158,7 @@ export class ReflectionProbe extends Behaviour {
134
158
  if (rendererCache) {
135
159
  for (let i = 0; i < rendererCache.length; i++) {
136
160
  const cached = rendererCache[i];
137
- _rend.sharedMaterials[i] = cached.orig;
161
+ _rend.sharedMaterials[i] = cached.material;
138
162
  }
139
163
  }
140
164
  }
@@ -219,6 +219,8 @@ export class Renderer extends Behaviour implements IRenderer {
219
219
  return lm !== null && lm !== undefined;
220
220
  }
221
221
 
222
+ allowProgressiveLoading : boolean = true;
223
+
222
224
  awake() {
223
225
  this.clearInstancingState();
224
226
 
@@ -457,7 +459,7 @@ export class Renderer extends Behaviour implements IRenderer {
457
459
  onBeforeRenderThree(_renderer, _scene, _camera, _geometry, material, _group) {
458
460
 
459
461
  // progressive load before rendering so we only load textures for visible materials
460
- if (!suppressProgressiveLoading && material._didRequestTextureLOD === undefined) {
462
+ if (!suppressProgressiveLoading && material._didRequestTextureLOD === undefined && this.allowProgressiveLoading) {
461
463
  material._didRequestTextureLOD = 0;
462
464
  if (debugProgressiveLoading) {
463
465
  console.log("Load material LOD (with delay)", material.name);
@@ -542,7 +544,7 @@ export class Renderer extends Behaviour implements IRenderer {
542
544
  // handle reflection probe
543
545
  this._reflectionProbe = null;
544
546
  if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off) {
545
- // if(this.gameObject.name.includes("Tank_Case_Jewlery") === false) return;
547
+ if(!this.probeAnchor) return;
546
548
  const obj = this.probeAnchor || this.gameObject;
547
549
  const isAnchor = this.probeAnchor ? true : false;
548
550
  this._reflectionProbe = ReflectionProbe.get(obj, this.context, isAnchor);