@babylonjs/core 9.2.0 → 9.2.2

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 (200) hide show
  1. package/Animations/animation.d.ts +9 -0
  2. package/Animations/animation.js +9 -0
  3. package/Animations/animation.js.map +1 -1
  4. package/Animations/runtimeAnimation.js +28 -0
  5. package/Animations/runtimeAnimation.js.map +1 -1
  6. package/Cameras/Inputs/geospatialCameraPointersInput.js +10 -8
  7. package/Cameras/Inputs/geospatialCameraPointersInput.js.map +1 -1
  8. package/Cameras/geospatialCameraMovement.js +21 -21
  9. package/Cameras/geospatialCameraMovement.js.map +1 -1
  10. package/Debug/physicsViewer.js +2 -12
  11. package/Debug/physicsViewer.js.map +1 -1
  12. package/Engines/abstractEngine.js +2 -2
  13. package/Engines/abstractEngine.js.map +1 -1
  14. package/Engines/webgpuEngine.js +2 -0
  15. package/Engines/webgpuEngine.js.map +1 -1
  16. package/FlowGraph/Blocks/flowGraphBlockFactory.js +14 -1
  17. package/FlowGraph/Blocks/flowGraphBlockFactory.js.map +1 -1
  18. package/FlowGraph/flowGraph.js +6 -0
  19. package/FlowGraph/flowGraph.js.map +1 -1
  20. package/FlowGraph/flowGraphEventBlock.d.ts +10 -0
  21. package/FlowGraph/flowGraphEventBlock.js +24 -0
  22. package/FlowGraph/flowGraphEventBlock.js.map +1 -1
  23. package/FlowGraph/flowGraphParser.js +23 -4
  24. package/FlowGraph/flowGraphParser.js.map +1 -1
  25. package/FlowGraph/serialization.js +36 -14
  26. package/FlowGraph/serialization.js.map +1 -1
  27. package/FrameGraph/Node/Blocks/Rendering/iblShadowsRendererBlock.d.ts +105 -0
  28. package/FrameGraph/Node/Blocks/Rendering/iblShadowsRendererBlock.js +318 -0
  29. package/FrameGraph/Node/Blocks/Rendering/iblShadowsRendererBlock.js.map +1 -0
  30. package/FrameGraph/Node/Blocks/index.d.ts +1 -0
  31. package/FrameGraph/Node/Blocks/index.js +1 -0
  32. package/FrameGraph/Node/Blocks/index.js.map +1 -1
  33. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.d.ts +34 -0
  34. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.js +144 -0
  35. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsAccumulationTask.js.map +1 -0
  36. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsSpatialBlurTask.d.ts +26 -0
  37. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsSpatialBlurTask.js +82 -0
  38. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsSpatialBlurTask.js.map +1 -0
  39. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsTracingTask.d.ts +61 -0
  40. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsTracingTask.js +207 -0
  41. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsTracingTask.js.map +1 -0
  42. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsVoxelizationTask.d.ts +104 -0
  43. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsVoxelizationTask.js +218 -0
  44. package/FrameGraph/Tasks/Rendering/iblShadows/iblShadowsVoxelizationTask.js.map +1 -0
  45. package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.d.ts +217 -0
  46. package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.js +640 -0
  47. package/FrameGraph/Tasks/Rendering/iblShadowsRendererTask.js.map +1 -0
  48. package/FrameGraph/frameGraph.js +1 -0
  49. package/FrameGraph/frameGraph.js.map +1 -1
  50. package/FrameGraph/index.d.ts +1 -0
  51. package/FrameGraph/index.js +1 -0
  52. package/FrameGraph/index.js.map +1 -1
  53. package/Gizmos/boundingBoxGizmo.js +4 -0
  54. package/Gizmos/boundingBoxGizmo.js.map +1 -1
  55. package/Layers/thinEffectLayer.js +8 -1
  56. package/Layers/thinEffectLayer.js.map +1 -1
  57. package/Lights/Clustered/clusteredLightContainer.js +8 -5
  58. package/Lights/Clustered/clusteredLightContainer.js.map +1 -1
  59. package/Loading/Plugins/babylonFileLoader.js +26 -0
  60. package/Loading/Plugins/babylonFileLoader.js.map +1 -1
  61. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js +15 -2
  62. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js.map +1 -1
  63. package/Materials/Node/Blocks/Fragment/fragmentOutputBlock.js +3 -1
  64. package/Materials/Node/Blocks/Fragment/fragmentOutputBlock.js.map +1 -1
  65. package/Materials/PBR/openpbrMaterial.d.ts +13 -2
  66. package/Materials/PBR/openpbrMaterial.js +47 -16
  67. package/Materials/PBR/openpbrMaterial.js.map +1 -1
  68. package/Materials/PBR/pbrBRDFConfiguration.js +1 -1
  69. package/Materials/PBR/pbrBRDFConfiguration.js.map +1 -1
  70. package/Materials/Textures/Filtering/hdrFiltering.js +6 -0
  71. package/Materials/Textures/Filtering/hdrFiltering.js.map +1 -1
  72. package/Materials/Textures/envCubeTexture.js +13 -13
  73. package/Materials/Textures/envCubeTexture.js.map +1 -1
  74. package/Materials/materialHelper.functions.js +1 -1
  75. package/Materials/materialHelper.functions.js.map +1 -1
  76. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.d.ts +18 -4
  77. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.js +29 -4
  78. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.js.map +1 -1
  79. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +48 -8
  80. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +373 -84
  81. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  82. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.d.ts +39 -4
  83. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js +152 -47
  84. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js.map +1 -1
  85. package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.d.ts +61 -7
  86. package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.js +94 -11
  87. package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.js.map +1 -1
  88. package/Meshes/mesh.d.ts +15 -0
  89. package/Meshes/mesh.js +40 -1
  90. package/Meshes/mesh.js.map +1 -1
  91. package/Meshes/transformNode.js +2 -2
  92. package/Meshes/transformNode.js.map +1 -1
  93. package/Misc/sceneSerializer.js +2 -1
  94. package/Misc/sceneSerializer.js.map +1 -1
  95. package/Misc/textureTools.d.ts +3 -1
  96. package/Misc/textureTools.js +74 -13
  97. package/Misc/textureTools.js.map +1 -1
  98. package/Misc/tools.js +1 -1
  99. package/Misc/tools.js.map +1 -1
  100. package/Particles/baseParticleSystem.d.ts +47 -1
  101. package/Particles/baseParticleSystem.js +88 -0
  102. package/Particles/baseParticleSystem.js.map +1 -1
  103. package/Particles/computeShaderParticleSystem.js +12 -0
  104. package/Particles/computeShaderParticleSystem.js.map +1 -1
  105. package/Particles/gpuParticleSystem.d.ts +61 -25
  106. package/Particles/gpuParticleSystem.js +249 -75
  107. package/Particles/gpuParticleSystem.js.map +1 -1
  108. package/Particles/particleSystem.d.ts +0 -6
  109. package/Particles/particleSystem.js +3 -14
  110. package/Particles/particleSystem.js.map +1 -1
  111. package/Particles/thinParticleSystem.d.ts +1 -17
  112. package/Particles/thinParticleSystem.js +1 -50
  113. package/Particles/thinParticleSystem.js.map +1 -1
  114. package/Particles/webgl2ParticleSystem.d.ts +1 -0
  115. package/Particles/webgl2ParticleSystem.js +18 -2
  116. package/Particles/webgl2ParticleSystem.js.map +1 -1
  117. package/Rendering/IBLShadows/iblShadowsAccumulationPass.js +1 -1
  118. package/Rendering/IBLShadows/iblShadowsAccumulationPass.js.map +1 -1
  119. package/Rendering/IBLShadows/iblShadowsPluginMaterial.d.ts +3 -1
  120. package/Rendering/IBLShadows/iblShadowsPluginMaterial.js +11 -1
  121. package/Rendering/IBLShadows/iblShadowsPluginMaterial.js.map +1 -1
  122. package/Rendering/IBLShadows/iblShadowsRenderPipeline.d.ts +0 -19
  123. package/Rendering/IBLShadows/iblShadowsRenderPipeline.js +21 -65
  124. package/Rendering/IBLShadows/iblShadowsRenderPipeline.js.map +1 -1
  125. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.d.ts +15 -52
  126. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.js +129 -220
  127. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.js.map +1 -1
  128. package/Rendering/IBLShadows/iblShadowsVoxelTracingPass.js +3 -0
  129. package/Rendering/IBLShadows/iblShadowsVoxelTracingPass.js.map +1 -1
  130. package/Rendering/depthRenderer.js +6 -0
  131. package/Rendering/depthRenderer.js.map +1 -1
  132. package/Rendering/geometryBufferRenderer.d.ts +14 -5
  133. package/Rendering/geometryBufferRenderer.js +6 -2
  134. package/Rendering/geometryBufferRenderer.js.map +1 -1
  135. package/Rendering/geometryBufferRendererSceneComponent.d.ts +4 -6
  136. package/Rendering/geometryBufferRendererSceneComponent.js.map +1 -1
  137. package/Rendering/iblCdfGenerator.d.ts +10 -0
  138. package/Rendering/iblCdfGenerator.js +52 -17
  139. package/Rendering/iblCdfGenerator.js.map +1 -1
  140. package/Rendering/index.d.ts +0 -6
  141. package/Rendering/index.js +0 -6
  142. package/Rendering/index.js.map +1 -1
  143. package/Shaders/ShadersInclude/gaussianSplatting.js +25 -4
  144. package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
  145. package/Shaders/ShadersInclude/openpbrDirectLighting.js +6 -1
  146. package/Shaders/ShadersInclude/openpbrDirectLighting.js.map +1 -1
  147. package/Shaders/ShadersInclude/openpbrEnvironmentLighting.js +1 -1
  148. package/Shaders/ShadersInclude/openpbrEnvironmentLighting.js.map +1 -1
  149. package/Shaders/gaussianSplatting.vertex.js +3 -0
  150. package/Shaders/gaussianSplatting.vertex.js.map +1 -1
  151. package/Shaders/gpuRenderParticles.vertex.js +14 -2
  152. package/Shaders/gpuRenderParticles.vertex.js.map +1 -1
  153. package/Shaders/gpuUpdateParticles.vertex.js +24 -6
  154. package/Shaders/gpuUpdateParticles.vertex.js.map +1 -1
  155. package/Shaders/iblShadowVoxelTracing.fragment.js +5 -1
  156. package/Shaders/iblShadowVoxelTracing.fragment.js.map +1 -1
  157. package/Shaders/iblVoxelGrid.fragment.d.ts +1 -0
  158. package/Shaders/iblVoxelGrid.fragment.js +33 -5
  159. package/Shaders/iblVoxelGrid.fragment.js.map +1 -1
  160. package/Shaders/{iblVoxelSlabDebug.fragment.d.ts → lod3D.fragment.d.ts} +1 -1
  161. package/Shaders/lod3D.fragment.js +13 -0
  162. package/Shaders/lod3D.fragment.js.map +1 -0
  163. package/Shaders/openpbr.fragment.js +5 -0
  164. package/Shaders/openpbr.fragment.js.map +1 -1
  165. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +37 -5
  166. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
  167. package/ShadersWGSL/ShadersInclude/openpbrDirectLighting.js +6 -1
  168. package/ShadersWGSL/ShadersInclude/openpbrDirectLighting.js.map +1 -1
  169. package/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.js +1 -1
  170. package/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.js.map +1 -1
  171. package/ShadersWGSL/gaussianSplatting.vertex.js +3 -0
  172. package/ShadersWGSL/gaussianSplatting.vertex.js.map +1 -1
  173. package/ShadersWGSL/gpuUpdateParticles.compute.js +29 -8
  174. package/ShadersWGSL/gpuUpdateParticles.compute.js.map +1 -1
  175. package/ShadersWGSL/iblShadowVoxelTracing.fragment.js +5 -1
  176. package/ShadersWGSL/iblShadowVoxelTracing.fragment.js.map +1 -1
  177. package/ShadersWGSL/iblVoxelGrid.fragment.js +1 -1
  178. package/ShadersWGSL/iblVoxelGrid.fragment.js.map +1 -1
  179. package/{Shaders/iblVoxelSlabDebug.vertex.d.ts → ShadersWGSL/lod3D.fragment.d.ts} +1 -1
  180. package/ShadersWGSL/lod3D.fragment.js +13 -0
  181. package/ShadersWGSL/lod3D.fragment.js.map +1 -0
  182. package/ShadersWGSL/openpbr.fragment.js +5 -0
  183. package/ShadersWGSL/openpbr.fragment.js.map +1 -1
  184. package/package.json +1 -1
  185. package/Shaders/iblVoxelGrid3dDebug.fragment.d.ts +0 -5
  186. package/Shaders/iblVoxelGrid3dDebug.fragment.js +0 -24
  187. package/Shaders/iblVoxelGrid3dDebug.fragment.js.map +0 -1
  188. package/Shaders/iblVoxelSlabDebug.fragment.js +0 -13
  189. package/Shaders/iblVoxelSlabDebug.fragment.js.map +0 -1
  190. package/Shaders/iblVoxelSlabDebug.vertex.js +0 -11
  191. package/Shaders/iblVoxelSlabDebug.vertex.js.map +0 -1
  192. package/ShadersWGSL/iblVoxelGrid3dDebug.fragment.d.ts +0 -5
  193. package/ShadersWGSL/iblVoxelGrid3dDebug.fragment.js +0 -23
  194. package/ShadersWGSL/iblVoxelGrid3dDebug.fragment.js.map +0 -1
  195. package/ShadersWGSL/iblVoxelSlabDebug.fragment.d.ts +0 -5
  196. package/ShadersWGSL/iblVoxelSlabDebug.fragment.js +0 -14
  197. package/ShadersWGSL/iblVoxelSlabDebug.fragment.js.map +0 -1
  198. package/ShadersWGSL/iblVoxelSlabDebug.vertex.d.ts +0 -5
  199. package/ShadersWGSL/iblVoxelSlabDebug.vertex.js +0 -12
  200. package/ShadersWGSL/iblVoxelSlabDebug.vertex.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { FactorGradient, ColorGradient, GradientHelper } from "../Misc/gradients.js";
1
+ import { ColorGradient, GradientHelper } from "../Misc/gradients.js";
2
2
  import { Observable } from "../Misc/observable.js";
3
3
  import { Vector3, Matrix, TmpVectors } from "../Maths/math.vector.js";
4
4
  import { Color4, TmpColors } from "../Maths/math.color.js";
@@ -6,6 +6,8 @@ import { Lerp } from "../Maths/math.scalar.functions.js";
6
6
  import { VertexBuffer, Buffer } from "../Buffers/buffer.js";
7
7
  import { BaseParticleSystem } from "./baseParticleSystem.js";
8
8
  import { ParticleSystem } from "./particleSystem.js";
9
+ import { Attractor } from "./attractor.js";
10
+ import { Logger } from "../Misc/logger.js";
9
11
  import { BoxParticleEmitter } from "../Particles/EmitterTypes/boxParticleEmitter.js";
10
12
  import { Scene } from "../scene.js";
11
13
  import { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration.js";
@@ -28,6 +30,15 @@ import { CreateConeEmitter, CreateCylinderEmitter, CreateDirectedCylinderEmitter
28
30
  * @see https://www.babylonjs-playground.com/#PU4WYI#4
29
31
  */
30
32
  export class GPUParticleSystem extends BaseParticleSystem {
33
+ /**
34
+ * Whether the particle buffer needs to store the initial emission direction.
35
+ * True when particles are not billboarded (they orient by direction) or when
36
+ * using stretched-local billboard mode (stretches along initial direction).
37
+ * @internal
38
+ */
39
+ get _needsInitialDirection() {
40
+ return !this._isBillboardBased || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL;
41
+ }
31
42
  /**
32
43
  * Gets a boolean indicating if the GPU particles can be rendered on current browser
33
44
  */
@@ -79,6 +90,17 @@ export class GPUParticleSystem extends BaseParticleSystem {
79
90
  set activeParticleCount(value) {
80
91
  this.maxActiveParticleCount = value;
81
92
  }
93
+ /**
94
+ * Add an attractor to the particle system. Attractors are used to change the direction of the particles in the system.
95
+ * @param attractor - The attractor to add to the particle system
96
+ */
97
+ addAttractor(attractor) {
98
+ if (this._attractors.length >= this.maxAttractors) {
99
+ Logger.Warn(`GPU particle system supports a maximum of ${this.maxAttractors} attractors. Ignoring additional attractor.`);
100
+ return;
101
+ }
102
+ super.addAttractor(attractor);
103
+ }
82
104
  /**
83
105
  * Creates a Point Emitter for the particle system (emits directly from the emitter position)
84
106
  * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
@@ -273,6 +295,28 @@ export class GPUParticleSystem extends BaseParticleSystem {
273
295
  this._stopped = false;
274
296
  this._actualFrame = 0;
275
297
  this._preWarmDone = false;
298
+ // Reset emit gradient so it acts the same on every start
299
+ if (this._emitRateGradients) {
300
+ if (this._emitRateGradients.length > 0) {
301
+ this._currentEmitRateGradient = this._emitRateGradients[0];
302
+ this._currentEmitRate1 = this._currentEmitRateGradient.getFactor();
303
+ this._currentEmitRate2 = this._currentEmitRate1;
304
+ }
305
+ if (this._emitRateGradients.length > 1) {
306
+ this._currentEmitRate2 = this._emitRateGradients[1].getFactor();
307
+ }
308
+ }
309
+ // Reset start size gradient so it acts the same on every start
310
+ if (this._startSizeGradients) {
311
+ if (this._startSizeGradients.length > 0) {
312
+ this._currentStartSizeGradient = this._startSizeGradients[0];
313
+ this._currentStartSize1 = this._currentStartSizeGradient.getFactor();
314
+ this._currentStartSize2 = this._currentStartSize1;
315
+ }
316
+ if (this._startSizeGradients.length > 1) {
317
+ this._currentStartSize2 = this._startSizeGradients[1].getFactor();
318
+ }
319
+ }
276
320
  // Animations
277
321
  if (this.beginAnimationOnStart && this.animations && this.animations.length > 0 && this._scene) {
278
322
  this._scene.beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
@@ -430,23 +474,19 @@ export class GPUParticleSystem extends BaseParticleSystem {
430
474
  drawWrapper.drawContext?.reset();
431
475
  }
432
476
  }
433
- _addFactorGradient(factorGradients, gradient, factor) {
434
- const valueGradient = new FactorGradient(gradient, factor);
435
- factorGradients.push(valueGradient);
436
- this._releaseBuffers();
437
- }
438
477
  /**
439
478
  * Adds a new size gradient
440
479
  * @param gradient defines the gradient to use (between 0 and 1)
441
480
  * @param factor defines the size factor to affect to the specified gradient
481
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
442
482
  * @returns the current particle system
443
483
  */
444
- addSizeGradient(gradient, factor) {
484
+ addSizeGradient(gradient, factor, factor2) {
445
485
  if (!this._sizeGradients) {
446
486
  this._sizeGradients = [];
447
487
  }
448
- this._addFactorGradient(this._sizeGradients, gradient, factor);
449
- this._refreshFactorGradient(this._sizeGradients, "_sizeGradientsTexture", true);
488
+ this._addFactorGradient(this._sizeGradients, gradient, factor, factor2);
489
+ this._refreshFactorGradient(this._sizeGradients, "_sizeGradientsTexture");
450
490
  this._releaseBuffers();
451
491
  return this;
452
492
  }
@@ -460,21 +500,10 @@ export class GPUParticleSystem extends BaseParticleSystem {
460
500
  this._sizeGradientsTexture = null;
461
501
  return this;
462
502
  }
463
- _refreshFactorGradient(factorGradients, textureName, reorder = false) {
503
+ _refreshFactorGradient(factorGradients, textureName) {
464
504
  if (!factorGradients) {
465
505
  return;
466
506
  }
467
- if (reorder) {
468
- factorGradients.sort((a, b) => {
469
- if (a.gradient < b.gradient) {
470
- return -1;
471
- }
472
- else if (a.gradient > b.gradient) {
473
- return 1;
474
- }
475
- return 0;
476
- });
477
- }
478
507
  const that = this;
479
508
  if (that[textureName]) {
480
509
  that[textureName].dispose();
@@ -485,14 +514,15 @@ export class GPUParticleSystem extends BaseParticleSystem {
485
514
  * Adds a new angular speed gradient
486
515
  * @param gradient defines the gradient to use (between 0 and 1)
487
516
  * @param factor defines the angular speed to affect to the specified gradient
517
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
488
518
  * @returns the current particle system
489
519
  */
490
- addAngularSpeedGradient(gradient, factor) {
520
+ addAngularSpeedGradient(gradient, factor, factor2) {
491
521
  if (!this._angularSpeedGradients) {
492
522
  this._angularSpeedGradients = [];
493
523
  }
494
- this._addFactorGradient(this._angularSpeedGradients, gradient, factor);
495
- this._refreshFactorGradient(this._angularSpeedGradients, "_angularSpeedGradientsTexture", true);
524
+ this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
525
+ this._refreshFactorGradient(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
496
526
  this._releaseBuffers();
497
527
  return this;
498
528
  }
@@ -510,14 +540,15 @@ export class GPUParticleSystem extends BaseParticleSystem {
510
540
  * Adds a new velocity gradient
511
541
  * @param gradient defines the gradient to use (between 0 and 1)
512
542
  * @param factor defines the velocity to affect to the specified gradient
543
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
513
544
  * @returns the current particle system
514
545
  */
515
- addVelocityGradient(gradient, factor) {
546
+ addVelocityGradient(gradient, factor, factor2) {
516
547
  if (!this._velocityGradients) {
517
548
  this._velocityGradients = [];
518
549
  }
519
- this._addFactorGradient(this._velocityGradients, gradient, factor);
520
- this._refreshFactorGradient(this._velocityGradients, "_velocityGradientsTexture", true);
550
+ this._addFactorGradient(this._velocityGradients, gradient, factor, factor2);
551
+ this._refreshFactorGradient(this._velocityGradients, "_velocityGradientsTexture");
521
552
  this._releaseBuffers();
522
553
  return this;
523
554
  }
@@ -535,14 +566,15 @@ export class GPUParticleSystem extends BaseParticleSystem {
535
566
  * Adds a new limit velocity gradient
536
567
  * @param gradient defines the gradient to use (between 0 and 1)
537
568
  * @param factor defines the limit velocity value to affect to the specified gradient
569
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
538
570
  * @returns the current particle system
539
571
  */
540
- addLimitVelocityGradient(gradient, factor) {
572
+ addLimitVelocityGradient(gradient, factor, factor2) {
541
573
  if (!this._limitVelocityGradients) {
542
574
  this._limitVelocityGradients = [];
543
575
  }
544
- this._addFactorGradient(this._limitVelocityGradients, gradient, factor);
545
- this._refreshFactorGradient(this._limitVelocityGradients, "_limitVelocityGradientsTexture", true);
576
+ this._addFactorGradient(this._limitVelocityGradients, gradient, factor, factor2);
577
+ this._refreshFactorGradient(this._limitVelocityGradients, "_limitVelocityGradientsTexture");
546
578
  this._releaseBuffers();
547
579
  return this;
548
580
  }
@@ -560,14 +592,15 @@ export class GPUParticleSystem extends BaseParticleSystem {
560
592
  * Adds a new drag gradient
561
593
  * @param gradient defines the gradient to use (between 0 and 1)
562
594
  * @param factor defines the drag value to affect to the specified gradient
595
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
563
596
  * @returns the current particle system
564
597
  */
565
- addDragGradient(gradient, factor) {
598
+ addDragGradient(gradient, factor, factor2) {
566
599
  if (!this._dragGradients) {
567
600
  this._dragGradients = [];
568
601
  }
569
- this._addFactorGradient(this._dragGradients, gradient, factor);
570
- this._refreshFactorGradient(this._dragGradients, "_dragGradientsTexture", true);
602
+ this._addFactorGradient(this._dragGradients, gradient, factor, factor2);
603
+ this._refreshFactorGradient(this._dragGradients, "_dragGradientsTexture");
571
604
  this._releaseBuffers();
572
605
  return this;
573
606
  }
@@ -582,35 +615,34 @@ export class GPUParticleSystem extends BaseParticleSystem {
582
615
  return this;
583
616
  }
584
617
  /**
585
- * Not supported by GPUParticleSystem
586
- * @returns the current particle system
587
- */
588
- addEmitRateGradient() {
589
- // Do nothing as emit rate is not supported by GPUParticleSystem
590
- return this;
591
- }
592
- /**
593
- * Not supported by GPUParticleSystem
594
- * @returns the current particle system
595
- */
596
- removeEmitRateGradient() {
597
- // Do nothing as emit rate is not supported by GPUParticleSystem
598
- return this;
599
- }
600
- /**
601
- * Not supported by GPUParticleSystem
618
+ * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
619
+ * @param gradient defines the gradient to use (between 0 and 1)
620
+ * @param factor defines the start size factor to affect to the specified gradient
621
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
602
622
  * @returns the current particle system
603
623
  */
604
- addStartSizeGradient() {
605
- // Do nothing as start size is not supported by GPUParticleSystem
624
+ addStartSizeGradient(gradient, factor, factor2) {
625
+ if (!this._startSizeGradients) {
626
+ this._startSizeGradients = [];
627
+ }
628
+ const hadGradients = this._startSizeGradients.length > 0;
629
+ this._addFactorGradient(this._startSizeGradients, gradient, factor, factor2);
630
+ if (!hadGradients) {
631
+ this._resetEffect();
632
+ }
606
633
  return this;
607
634
  }
608
635
  /**
609
- * Not supported by GPUParticleSystem
636
+ * Remove a specific start size gradient
637
+ * @param gradient defines the gradient to remove
610
638
  * @returns the current particle system
611
639
  */
612
- removeStartSizeGradient() {
613
- // Do nothing as start size is not supported by GPUParticleSystem
640
+ removeStartSizeGradient(gradient) {
641
+ const hadGradients = this._startSizeGradients && this._startSizeGradients.length > 0;
642
+ this._removeFactorGradient(this._startSizeGradients, gradient);
643
+ if (hadGradients && (!this._startSizeGradients || this._startSizeGradients.length === 0)) {
644
+ this._resetEffect();
645
+ }
614
646
  return this;
615
647
  }
616
648
  /**
@@ -681,19 +713,34 @@ export class GPUParticleSystem extends BaseParticleSystem {
681
713
  //Not supported by GPUParticleSystem
682
714
  }
683
715
  /**
684
- * Not supported by GPUParticleSystem
716
+ * Adds a new life time gradient (please note that this will only work if you set the targetStopDuration property)
717
+ * @param gradient defines the gradient to use (between 0 and 1)
718
+ * @param factor defines the life time factor to affect to the specified gradient
719
+ * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
685
720
  * @returns the current particle system
686
721
  */
687
- addLifeTimeGradient() {
688
- //Not supported by GPUParticleSystem
722
+ addLifeTimeGradient(gradient, factor, factor2) {
723
+ if (!this._lifeTimeGradients) {
724
+ this._lifeTimeGradients = [];
725
+ }
726
+ const hadGradients = this._lifeTimeGradients.length > 0;
727
+ this._addFactorGradient(this._lifeTimeGradients, gradient, factor, factor2);
728
+ if (!hadGradients) {
729
+ this._resetEffect();
730
+ }
689
731
  return this;
690
732
  }
691
733
  /**
692
- * Not supported by GPUParticleSystem
734
+ * Remove a specific life time gradient
735
+ * @param gradient defines the gradient to remove
693
736
  * @returns the current particle system
694
737
  */
695
- removeLifeTimeGradient() {
696
- //Not supported by GPUParticleSystem
738
+ removeLifeTimeGradient(gradient) {
739
+ const hadGradients = this._lifeTimeGradients && this._lifeTimeGradients.length > 0;
740
+ this._removeFactorGradient(this._lifeTimeGradients, gradient);
741
+ if (hadGradients && (!this._lifeTimeGradients || this._lifeTimeGradients.length === 0)) {
742
+ this._resetEffect();
743
+ }
697
744
  return this;
698
745
  }
699
746
  /**
@@ -727,6 +774,18 @@ export class GPUParticleSystem extends BaseParticleSystem {
727
774
  this._actualFrame = 0;
728
775
  this._rawTextureWidth = 256;
729
776
  this._rebuildingAfterContextLost = false;
777
+ // Emit rate gradient caching (mirrors ThinParticleSystem)
778
+ this._currentEmitRateGradient = null;
779
+ this._currentEmitRate1 = 0;
780
+ this._currentEmitRate2 = 0;
781
+ // Start size gradient caching (mirrors ThinParticleSystem)
782
+ this._currentStartSizeGradient = null;
783
+ this._currentStartSize1 = 0;
784
+ this._currentStartSize2 = 0;
785
+ this._startSizeGradientFactor = 1.0;
786
+ // Life time gradient factor range for per-particle randomization in shader
787
+ this._lifeTimeGradientMin = 1.0;
788
+ this._lifeTimeGradientMax = 1.0;
730
789
  /**
731
790
  * Specifies if the particle system should be serialized
732
791
  */
@@ -817,6 +876,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
817
876
  this._currentActiveCount = 0;
818
877
  this._isAnimationSheetEnabled = isAnimationSheetEnabled;
819
878
  this._emitRateControl = !!options.emitRateControl;
879
+ this.maxAttractors = options.maxAttractors ?? 8;
820
880
  this.particleEmitterType = new BoxParticleEmitter();
821
881
  // Random data
822
882
  const maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
@@ -858,7 +918,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
858
918
  renderVertexBuffers["life"] = renderBuffer.createVertexBuffer("life", offset, 1, this._attributesStrideSize, true);
859
919
  offset += 1;
860
920
  offset += 4; // seed
861
- if (this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED) {
921
+ if (this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL) {
862
922
  renderVertexBuffers["direction"] = renderBuffer.createVertexBuffer("direction", offset, 3, this._attributesStrideSize, true);
863
923
  }
864
924
  offset += 3; // direction
@@ -875,7 +935,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
875
935
  renderVertexBuffers["color"] = renderBuffer.createVertexBuffer("color", offset, 4, this._attributesStrideSize, true);
876
936
  offset += 4;
877
937
  }
878
- if (!this._isBillboardBased) {
938
+ if (this._needsInitialDirection) {
879
939
  renderVertexBuffers["initialDirection"] = renderBuffer.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
880
940
  offset += 3;
881
941
  if (this._platform.alignDataInBuffer) {
@@ -931,7 +991,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
931
991
  this._attributesStrideSize += 1;
932
992
  }
933
993
  }
934
- if (!this.isBillboardBased) {
994
+ if (this._needsInitialDirection) {
935
995
  this._attributesStrideSize += 3;
936
996
  if (this._platform.alignDataInBuffer) {
937
997
  this._attributesStrideSize += 1;
@@ -1013,7 +1073,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1013
1073
  data.push(0.0);
1014
1074
  offset += 4;
1015
1075
  }
1016
- if (!this.isBillboardBased) {
1076
+ if (this._needsInitialDirection) {
1017
1077
  // initialDirection
1018
1078
  data.push(0.0);
1019
1079
  data.push(0.0);
@@ -1078,6 +1138,12 @@ export class GPUParticleSystem extends BaseParticleSystem {
1078
1138
  this._sourceBuffer = this._buffer0;
1079
1139
  this._targetBuffer = this._buffer1;
1080
1140
  }
1141
+ /**
1142
+ * Forces the update effect to be recreated on the next render.
1143
+ */
1144
+ _resetEffect() {
1145
+ this._cachedUpdateDefines = "";
1146
+ }
1081
1147
  /** @internal */
1082
1148
  _recreateUpdateEffect() {
1083
1149
  this._createColorGradientTexture();
@@ -1088,7 +1154,11 @@ export class GPUParticleSystem extends BaseParticleSystem {
1088
1154
  this._createDragGradientTexture();
1089
1155
  let defines = this.particleEmitterType ? this.particleEmitterType.getEffectDefines() : "";
1090
1156
  if (this._isBillboardBased) {
1091
- defines += "\n#define BILLBOARD";
1157
+ // Stretched local needs initialDirection in the buffer, which requires !BILLBOARD in the update shader.
1158
+ // The render shader still uses BILLBOARD — that's handled separately in fillDefines().
1159
+ if (this.billboardMode !== ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL) {
1160
+ defines += "\n#define BILLBOARD";
1161
+ }
1092
1162
  }
1093
1163
  if (this._colorGradientsTexture) {
1094
1164
  defines += "\n#define COLORGRADIENTS";
@@ -1123,9 +1193,19 @@ export class GPUParticleSystem extends BaseParticleSystem {
1123
1193
  if (this.isLocal) {
1124
1194
  defines += "\n#define LOCAL";
1125
1195
  }
1196
+ if (this._attractors.length > 0) {
1197
+ defines += "\n#define ATTRACTORS";
1198
+ defines += "\n#define MAX_ATTRACTORS " + this.maxAttractors;
1199
+ }
1126
1200
  if (this._emitRateControl) {
1127
1201
  defines += "\n#define EMITRATECTRL";
1128
1202
  }
1203
+ if (this._startSizeGradients && this._startSizeGradients.length > 0) {
1204
+ defines += "\n#define STARTSIZEGRADIENTS";
1205
+ }
1206
+ if (this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
1207
+ defines += "\n#define LIFETIMEGRADIENTS";
1208
+ }
1129
1209
  if (this._platform.isUpdateBufferCreated() && this._cachedUpdateDefines === defines) {
1130
1210
  return this._platform.isUpdateBufferReady();
1131
1211
  }
@@ -1165,7 +1245,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1165
1245
  /**
1166
1246
  * @internal
1167
1247
  */
1168
- static _GetAttributeNamesOrOptions(hasColorGradients = false, isAnimationSheetEnabled = false, isBillboardBased = false, isBillboardStretched = false) {
1248
+ static _GetAttributeNamesOrOptions(hasColorGradients = false, isAnimationSheetEnabled = false, isBillboardBased = false, isBillboardStretched = false, isBillboardStretchedLocal = false) {
1169
1249
  const attributeNamesOrOptions = [VertexBuffer.PositionKind, "age", "life", "size", "angle"];
1170
1250
  if (!hasColorGradients) {
1171
1251
  attributeNamesOrOptions.push(VertexBuffer.ColorKind);
@@ -1173,7 +1253,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1173
1253
  if (isAnimationSheetEnabled) {
1174
1254
  attributeNamesOrOptions.push("cellIndex");
1175
1255
  }
1176
- if (!isBillboardBased) {
1256
+ if (!isBillboardBased || isBillboardStretchedLocal) {
1177
1257
  attributeNamesOrOptions.push("initialDirection");
1178
1258
  }
1179
1259
  if (isBillboardStretched) {
@@ -1231,6 +1311,10 @@ export class GPUParticleSystem extends BaseParticleSystem {
1231
1311
  case ParticleSystem.BILLBOARDMODE_STRETCHED:
1232
1312
  defines.push("#define BILLBOARDSTRETCHED");
1233
1313
  break;
1314
+ case ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL:
1315
+ defines.push("#define BILLBOARDSTRETCHED");
1316
+ defines.push("#define BILLBOARDSTRETCHED_LOCAL");
1317
+ break;
1234
1318
  case ParticleSystem.BILLBOARDMODE_ALL:
1235
1319
  defines.push("#define BILLBOARDMODE_ALL");
1236
1320
  break;
@@ -1259,7 +1343,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1259
1343
  * @param samplers Samplers array to fill
1260
1344
  */
1261
1345
  fillUniformsAttributesAndSamplerNames(uniforms, attributes, samplers) {
1262
- attributes.push(...GPUParticleSystem._GetAttributeNamesOrOptions(!!this._colorGradientsTexture, this._isAnimationSheetEnabled, this._isBillboardBased, this._isBillboardBased && this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED));
1346
+ attributes.push(...GPUParticleSystem._GetAttributeNamesOrOptions(!!this._colorGradientsTexture, this._isAnimationSheetEnabled, this._isBillboardBased, this._isBillboardBased && (this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL), this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL));
1263
1347
  uniforms.push(...GPUParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled, this.useLogarithmicDepth, this.applyFog));
1264
1348
  samplers.push("diffuseSampler", "colorGradientSampler");
1265
1349
  if (this._imageProcessingConfiguration) {
@@ -1288,14 +1372,17 @@ export class GPUParticleSystem extends BaseParticleSystem {
1288
1372
  if (!factorGradients || !factorGradients.length || texture) {
1289
1373
  return;
1290
1374
  }
1291
- const data = new Float32Array(this._rawTextureWidth);
1375
+ const data = new Float32Array(this._rawTextureWidth * 2);
1292
1376
  for (let x = 0; x < this._rawTextureWidth; x++) {
1293
1377
  const ratio = x / this._rawTextureWidth;
1294
1378
  GradientHelper.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
1295
- data[x] = Lerp(currentGradient.factor1, nextGradient.factor1, scale);
1379
+ const cg = currentGradient;
1380
+ const ng = nextGradient;
1381
+ data[x * 2] = Lerp(cg.factor1, ng.factor1, scale);
1382
+ data[x * 2 + 1] = Lerp(cg.factor2 ?? cg.factor1, ng.factor2 ?? ng.factor1, scale);
1296
1383
  });
1297
1384
  }
1298
- this[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene || this._engine, false, false, 1);
1385
+ this[textureName] = new RawTexture(data, this._rawTextureWidth, 1, 7, this._scene || this._engine, false, false, 1, 1);
1299
1386
  this[textureName].name = textureName.substring(1);
1300
1387
  }
1301
1388
  _createSizeGradientTexture() {
@@ -1460,6 +1547,33 @@ export class GPUParticleSystem extends BaseParticleSystem {
1460
1547
  if (!this.isLocal) {
1461
1548
  this._updateBuffer.setMatrix("emitterWM", emitterWM);
1462
1549
  }
1550
+ if (this._attractors.length > 0) {
1551
+ this._updateBuffer.setInt("attractorCount", this._attractors.length);
1552
+ // Compute inverse world matrix once for all attractors when in local space
1553
+ let invWorld;
1554
+ if (this.isLocal) {
1555
+ invWorld = TmpVectors.Matrix[1];
1556
+ emitterWM.invertToRef(invWorld);
1557
+ }
1558
+ for (let i = 0; i < this._attractors.length; i++) {
1559
+ const attractor = this._attractors[i];
1560
+ const name = "attractorPositionAndStrength[" + i + "]";
1561
+ if (invWorld) {
1562
+ const localPos = TmpVectors.Vector3[0];
1563
+ Vector3.TransformCoordinatesToRef(attractor.position, invWorld, localPos);
1564
+ this._updateBuffer.setFloat4(name, localPos.x, localPos.y, localPos.z, attractor.strength);
1565
+ }
1566
+ else {
1567
+ this._updateBuffer.setFloat4(name, attractor.position.x, attractor.position.y, attractor.position.z, attractor.strength);
1568
+ }
1569
+ }
1570
+ }
1571
+ if (this._startSizeGradients && this._startSizeGradients.length > 0) {
1572
+ this._updateBuffer.setFloat("startSizeGradientFactor", this._startSizeGradientFactor);
1573
+ }
1574
+ if (this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
1575
+ this._updateBuffer.setFloat2("lifeTimeGradientRange", this._lifeTimeGradientMin, this._lifeTimeGradientMax);
1576
+ }
1463
1577
  this._platform.updateParticleBuffer(this._targetIndex, this._targetBuffer, this._currentActiveCount);
1464
1578
  // Switch VAOs
1465
1579
  this._targetIndex++;
@@ -1504,6 +1618,48 @@ export class GPUParticleSystem extends BaseParticleSystem {
1504
1618
  }
1505
1619
  // Get everything ready to render
1506
1620
  this._initialize();
1621
+ // Compute effective emit rate, applying emit rate gradients if set
1622
+ let effectiveEmitRate = this.emitRate;
1623
+ if (this._emitRateGradients && this._emitRateGradients.length > 0 && this.targetStopDuration) {
1624
+ const ratio = this._actualFrame / this.targetStopDuration;
1625
+ GradientHelper.GetCurrentGradient(ratio, this._emitRateGradients, (currentGradient, nextGradient, scale) => {
1626
+ if (currentGradient !== this._currentEmitRateGradient) {
1627
+ this._currentEmitRate1 = this._currentEmitRate2;
1628
+ this._currentEmitRate2 = nextGradient.getFactor();
1629
+ this._currentEmitRateGradient = currentGradient;
1630
+ }
1631
+ effectiveEmitRate = Lerp(this._currentEmitRate1, this._currentEmitRate2, scale);
1632
+ });
1633
+ }
1634
+ // Compute start size and life time gradient factors for shader uniforms
1635
+ if (this.targetStopDuration) {
1636
+ const ratio = this._actualFrame / this.targetStopDuration;
1637
+ if (this._startSizeGradients && this._startSizeGradients.length > 0) {
1638
+ GradientHelper.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
1639
+ if (currentGradient !== this._currentStartSizeGradient) {
1640
+ this._currentStartSize1 = this._currentStartSize2;
1641
+ this._currentStartSize2 = nextGradient.getFactor();
1642
+ this._currentStartSizeGradient = currentGradient;
1643
+ }
1644
+ this._startSizeGradientFactor = Lerp(this._currentStartSize1, this._currentStartSize2, scale);
1645
+ });
1646
+ }
1647
+ else {
1648
+ this._startSizeGradientFactor = 1.0;
1649
+ }
1650
+ if (this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
1651
+ GradientHelper.GetCurrentGradient(ratio, this._lifeTimeGradients, (currentGradient, nextGradient, scale) => {
1652
+ const current = currentGradient;
1653
+ const next = nextGradient;
1654
+ this._lifeTimeGradientMin = Lerp(current.factor1, next.factor1, scale);
1655
+ this._lifeTimeGradientMax = Lerp(current.factor2 ?? current.factor1, next.factor2 ?? next.factor1, scale);
1656
+ });
1657
+ }
1658
+ else {
1659
+ this._lifeTimeGradientMin = 1.0;
1660
+ this._lifeTimeGradientMax = 1.0;
1661
+ }
1662
+ }
1507
1663
  if (this._emitRateControl) {
1508
1664
  // Emit-rate-controlled mode: limits active particles to ~emitRate * maxLifeTime,
1509
1665
  // matching CPU particle behavior with circular buffer recycling.
@@ -1513,7 +1669,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1513
1669
  this.manualEmitCount = 0;
1514
1670
  }
1515
1671
  else if (!this._stopped) {
1516
- this._accumulatedCount += this.emitRate * this._timeDelta;
1672
+ this._accumulatedCount += effectiveEmitRate * this._timeDelta;
1517
1673
  }
1518
1674
  // Convert accumulated fractional count into whole particles to emit this frame.
1519
1675
  // The fractional remainder carries over to the next frame.
@@ -1528,7 +1684,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1528
1684
  // When emitRate is 0 but manualEmitCount is used, the rate-based steady state
1529
1685
  // would be 0, blocking buffer growth. Use newParticles as a floor so manual
1530
1686
  // emissions can always allocate slots.
1531
- const steadyStateCount = Math.min(Math.max(Math.ceil(this.emitRate * this.maxLifeTime), newParticles), this._maxActiveParticleCount);
1687
+ const steadyStateCount = Math.min(Math.max(Math.ceil(effectiveEmitRate * this.maxLifeTime), newParticles), this._maxActiveParticleCount);
1532
1688
  // During ramp-up, grow the active buffer size by adding new slots.
1533
1689
  // Once _currentActiveCount reaches steadyStateCount, no new slots are added —
1534
1690
  // existing slots are recycled instead (handled by _emitIndex below).
@@ -1557,7 +1713,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1557
1713
  this.manualEmitCount = 0;
1558
1714
  }
1559
1715
  else {
1560
- this._accumulatedCount += this.emitRate * this._timeDelta;
1716
+ this._accumulatedCount += effectiveEmitRate * this._timeDelta;
1561
1717
  }
1562
1718
  if (this._accumulatedCount >= 1) {
1563
1719
  const intPart = this._accumulatedCount | 0;
@@ -1749,12 +1905,20 @@ export class GPUParticleSystem extends BaseParticleSystem {
1749
1905
  serializationObject.activeParticleCount = this.activeParticleCount;
1750
1906
  serializationObject.randomTextureSize = this._randomTextureSize;
1751
1907
  serializationObject.emitRateControl = this._emitRateControl;
1908
+ serializationObject.maxAttractors = this.maxAttractors;
1752
1909
  serializationObject.customShader = this.customShader;
1753
1910
  serializationObject.preventAutoStart = this.preventAutoStart;
1754
1911
  serializationObject.worldOffset = this.worldOffset.asArray();
1755
1912
  if (this.metadata) {
1756
1913
  serializationObject.metadata = this.metadata;
1757
1914
  }
1915
+ // Attractors
1916
+ if (this._attractors.length > 0) {
1917
+ serializationObject.attractors = [];
1918
+ for (const attractor of this._attractors) {
1919
+ serializationObject.attractors.push(attractor.serialize());
1920
+ }
1921
+ }
1758
1922
  return serializationObject;
1759
1923
  }
1760
1924
  /**
@@ -1781,6 +1945,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1781
1945
  capacity: capacity || parsedParticleSystem.capacity,
1782
1946
  randomTextureSize: parsedParticleSystem.randomTextureSize,
1783
1947
  emitRateControl: parsedParticleSystem.emitRateControl,
1948
+ maxAttractors: parsedParticleSystem.maxAttractors,
1784
1949
  }, sceneOrEngine, null, parsedParticleSystem.isAnimationSheetEnabled);
1785
1950
  particleSystem._rootUrl = rootUrl;
1786
1951
  if (parsedParticleSystem.customShader && engine.createEffectForParticles) {
@@ -1807,6 +1972,15 @@ export class GPUParticleSystem extends BaseParticleSystem {
1807
1972
  if (parsedParticleSystem.metadata) {
1808
1973
  particleSystem.metadata = parsedParticleSystem.metadata;
1809
1974
  }
1975
+ // Attractors
1976
+ if (parsedParticleSystem.attractors) {
1977
+ for (const attractorData of parsedParticleSystem.attractors) {
1978
+ const attractor = new Attractor();
1979
+ attractor.position = Vector3.FromArray(attractorData.position);
1980
+ attractor.strength = attractorData.strength;
1981
+ particleSystem.addAttractor(attractor);
1982
+ }
1983
+ }
1810
1984
  if (!doNotStart && !particleSystem.preventAutoStart) {
1811
1985
  particleSystem.start();
1812
1986
  }