@babylonjs/core 9.3.1 → 9.3.3

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 (84) hide show
  1. package/Engines/AbstractEngine/abstractEngine.textureSelector.d.ts +45 -0
  2. package/Engines/AbstractEngine/abstractEngine.textureSelector.js +69 -0
  3. package/Engines/AbstractEngine/abstractEngine.textureSelector.js.map +1 -0
  4. package/Engines/AbstractEngine/index.d.ts +2 -0
  5. package/Engines/AbstractEngine/index.js +4 -0
  6. package/Engines/AbstractEngine/index.js.map +1 -1
  7. package/Engines/Extensions/engine.textureSelector.d.ts +2 -45
  8. package/Engines/Extensions/engine.textureSelector.js +8 -68
  9. package/Engines/Extensions/engine.textureSelector.js.map +1 -1
  10. package/Engines/abstractEngine.js +2 -2
  11. package/Engines/abstractEngine.js.map +1 -1
  12. package/FlowGraph/flowGraph.d.ts +22 -0
  13. package/FlowGraph/flowGraph.js +11 -0
  14. package/FlowGraph/flowGraph.js.map +1 -1
  15. package/FlowGraph/flowGraphCoordinator.d.ts +2 -1
  16. package/FlowGraph/flowGraphCoordinator.js +4 -2
  17. package/FlowGraph/flowGraphCoordinator.js.map +1 -1
  18. package/FlowGraph/flowGraphParser.js +7 -0
  19. package/FlowGraph/flowGraphParser.js.map +1 -1
  20. package/FlowGraph/typeDefinitions.d.ts +8 -0
  21. package/FlowGraph/typeDefinitions.js.map +1 -1
  22. package/Lights/Clustered/clusteredLightContainer.d.ts +1 -0
  23. package/Lights/Clustered/clusteredLightContainer.js +19 -0
  24. package/Lights/Clustered/clusteredLightContainer.js.map +1 -1
  25. package/Lights/light.d.ts +6 -0
  26. package/Lights/light.js +8 -0
  27. package/Lights/light.js.map +1 -1
  28. package/Lights/spotLight.d.ts +2 -0
  29. package/Lights/spotLight.js +10 -0
  30. package/Lights/spotLight.js.map +1 -1
  31. package/Materials/Background/backgroundMaterial.js +4 -1
  32. package/Materials/Background/backgroundMaterial.js.map +1 -1
  33. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js +6 -2
  34. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js.map +1 -1
  35. package/Materials/Node/Blocks/Dual/lightBlock.d.ts +8 -0
  36. package/Materials/Node/Blocks/Dual/lightBlock.js +16 -0
  37. package/Materials/Node/Blocks/Dual/lightBlock.js.map +1 -1
  38. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js +3 -0
  39. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js.map +1 -1
  40. package/Materials/Node/nodeMaterial.js +4 -1
  41. package/Materials/Node/nodeMaterial.js.map +1 -1
  42. package/Materials/PBR/openpbrMaterial.js +4 -1
  43. package/Materials/PBR/openpbrMaterial.js.map +1 -1
  44. package/Materials/PBR/pbrBaseMaterial.js +4 -1
  45. package/Materials/PBR/pbrBaseMaterial.js.map +1 -1
  46. package/Materials/materialHelper.functions.d.ts +12 -0
  47. package/Materials/materialHelper.functions.js +24 -0
  48. package/Materials/materialHelper.functions.js.map +1 -1
  49. package/Materials/standardMaterial.js +4 -1
  50. package/Materials/standardMaterial.js.map +1 -1
  51. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +13 -0
  52. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +26 -0
  53. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  54. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.d.ts +3 -0
  55. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js +113 -10
  56. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js.map +1 -1
  57. package/Misc/tools.js +1 -1
  58. package/Misc/tools.js.map +1 -1
  59. package/Particles/gpuParticleSystem.d.ts +35 -2
  60. package/Particles/gpuParticleSystem.js +272 -6
  61. package/Particles/gpuParticleSystem.js.map +1 -1
  62. package/Particles/thinParticleSystem.js +5 -0
  63. package/Particles/thinParticleSystem.js.map +1 -1
  64. package/Rendering/depthRenderer.d.ts +8 -0
  65. package/Rendering/depthRenderer.js +48 -13
  66. package/Rendering/depthRenderer.js.map +1 -1
  67. package/Rendering/depthRendererSceneComponent.d.ts +1 -0
  68. package/Rendering/depthRendererSceneComponent.js +26 -0
  69. package/Rendering/depthRendererSceneComponent.js.map +1 -1
  70. package/Shaders/gpuRenderParticles.vertex.js +7 -0
  71. package/Shaders/gpuRenderParticles.vertex.js.map +1 -1
  72. package/XR/features/WebXRBodyTracking.d.ts +952 -0
  73. package/XR/features/WebXRBodyTracking.js +2221 -0
  74. package/XR/features/WebXRBodyTracking.js.map +1 -0
  75. package/XR/features/index.d.ts +1 -0
  76. package/XR/features/index.js +1 -0
  77. package/XR/features/index.js.map +1 -1
  78. package/XR/webXRFeaturesManager.d.ts +7 -0
  79. package/XR/webXRFeaturesManager.js +4 -0
  80. package/XR/webXRFeaturesManager.js.map +1 -1
  81. package/package.json +1 -1
  82. package/sceneComponent.d.ts +1 -0
  83. package/sceneComponent.js +1 -0
  84. package/sceneComponent.js.map +1 -1
@@ -6,6 +6,7 @@ import { Color4 } from "../Maths/math.color.js";
6
6
  import { VertexBuffer } from "../Buffers/buffer.js";
7
7
  import { type IParticleSystem } from "./IParticleSystem.js";
8
8
  import { BaseParticleSystem } from "./baseParticleSystem.js";
9
+ import { ParticleSystem } from "./particleSystem.js";
9
10
  import { Attractor } from "./attractor.js";
10
11
  import { type IDisposable, Scene } from "../scene.js";
11
12
  import { type Effect } from "../Materials/effect.js";
@@ -45,6 +46,8 @@ export declare class GPUParticleSystem extends BaseParticleSystem implements IDi
45
46
  private _targetIndex;
46
47
  private _sourceBuffer;
47
48
  private _targetBuffer;
49
+ /** Set to true when any entry in `_colorGradients` has a `color2` (per-particle random color range). */
50
+ private _hasColorGradientColor2;
48
51
  private _currentRenderId;
49
52
  private _currentRenderingCameraUniqueId;
50
53
  private _started;
@@ -264,9 +267,10 @@ export declare class GPUParticleSystem extends BaseParticleSystem implements IDi
264
267
  * Adds a new color gradient
265
268
  * @param gradient defines the gradient to use (between 0 and 1)
266
269
  * @param color1 defines the color to affect to the specified gradient
270
+ * @param color2 defines an optional second color to be used to produce a random color per particle at the gradient (lerped with color1 using a per-particle random value)
267
271
  * @returns the current particle system
268
272
  */
269
- addColorGradient(gradient: number, color1: Color4): GPUParticleSystem;
273
+ addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem;
270
274
  private _refreshColorGradient;
271
275
  /** Force the system to rebuild all gradients that need to be resync */
272
276
  forceRefreshGradients(): void;
@@ -462,7 +466,7 @@ export declare class GPUParticleSystem extends BaseParticleSystem implements IDi
462
466
  /**
463
467
  * @internal
464
468
  */
465
- static _GetAttributeNamesOrOptions(hasColorGradients?: boolean, isAnimationSheetEnabled?: boolean, isBillboardBased?: boolean, isBillboardStretched?: boolean, isBillboardStretchedLocal?: boolean): string[];
469
+ static _GetAttributeNamesOrOptions(hasColorGradients?: boolean, isAnimationSheetEnabled?: boolean, isBillboardBased?: boolean, isBillboardStretched?: boolean, isBillboardStretchedLocal?: boolean, hasColorGradientColor2?: boolean): string[];
466
470
  /**
467
471
  * @internal
468
472
  */
@@ -523,6 +527,35 @@ export declare class GPUParticleSystem extends BaseParticleSystem implements IDi
523
527
  * @returns the cloned particle system
524
528
  */
525
529
  clone(name: string, newEmitter: any, cloneTexture?: boolean): GPUParticleSystem;
530
+ /**
531
+ * Creates a new GPUParticleSystem from an existing CPU ParticleSystem, copying all shared properties.
532
+ * Features that are not supported on the GPU (sub-emitters, custom `startDirectionFunction` /
533
+ * `startPositionFunction`, `customShader`, ramp/remap gradients) are logged as warnings and skipped.
534
+ * Flow maps are converted: the CPU `FlowMap` image data is uploaded to a new `RawTexture` which is
535
+ * assigned to the result.
536
+ *
537
+ * Note: a custom `updateFunction` on the source cannot be detected (the property is always assigned
538
+ * to a default) and has no equivalent on the GPU path, so any custom per-frame update logic will be
539
+ * silently dropped.
540
+ *
541
+ * Textures (particleTexture, noiseTexture) are shared by reference between the source and the result.
542
+ * All other mutable state (colors, vectors, emitter type, gradients, attractors) is cloned so that
543
+ * the two systems can be modified independently after the call.
544
+ *
545
+ * Note: unlike the GPUParticleSystem constructor, `emitRateControl` defaults to `true` here so that
546
+ * changes to `emitRate` on the converted system behave the same as on the CPU source. Pass
547
+ * `{ emitRateControl: false }` explicitly to opt out.
548
+ * @param source The CPU ParticleSystem to convert
549
+ * @param sceneOrEngine The scene or engine the new GPU particle system belongs to
550
+ * @param options Optional options forwarded to the new GPU particle system (capacity, randomTextureSize, emitRateControl, maxAttractors). `capacity` defaults to the source capacity and `emitRateControl` defaults to `true`.
551
+ * @returns A new GPUParticleSystem with shared properties copied from the source
552
+ */
553
+ static fromParticleSystem(source: ParticleSystem, sceneOrEngine: Scene | AbstractEngine, options?: Partial<{
554
+ capacity: number;
555
+ randomTextureSize: number;
556
+ emitRateControl: boolean;
557
+ maxAttractors: number;
558
+ }>): GPUParticleSystem;
526
559
  /**
527
560
  * Serializes the particle system to a JSON object
528
561
  * @param serializeTexture defines if the texture must be serialized as well
@@ -309,13 +309,14 @@ export class GPUParticleSystem extends BaseParticleSystem {
309
309
  * Adds a new color gradient
310
310
  * @param gradient defines the gradient to use (between 0 and 1)
311
311
  * @param color1 defines the color to affect to the specified gradient
312
+ * @param color2 defines an optional second color to be used to produce a random color per particle at the gradient (lerped with color1 using a per-particle random value)
312
313
  * @returns the current particle system
313
314
  */
314
- addColorGradient(gradient, color1) {
315
+ addColorGradient(gradient, color1, color2) {
315
316
  if (!this._colorGradients) {
316
317
  this._colorGradients = [];
317
318
  }
318
- const colorGradient = new ColorGradient(gradient, color1);
319
+ const colorGradient = new ColorGradient(gradient, color1, color2);
319
320
  this._colorGradients.push(colorGradient);
320
321
  this._refreshColorGradient(true);
321
322
  this._releaseBuffers();
@@ -334,11 +335,24 @@ export class GPUParticleSystem extends BaseParticleSystem {
334
335
  return 0;
335
336
  });
336
337
  }
338
+ // Recompute whether any stop uses a color2 range. Done here (not inside _createColorGradientTexture)
339
+ // so the flag is available to both define generation (fillDefines) and render vertex buffer layout
340
+ // (_createVertexBuffers), which can run before the texture is recreated.
341
+ this._hasColorGradientColor2 = false;
342
+ for (const g of this._colorGradients) {
343
+ if (g.color2) {
344
+ this._hasColorGradientColor2 = true;
345
+ break;
346
+ }
347
+ }
337
348
  if (this._colorGradientsTexture) {
338
349
  this._colorGradientsTexture.dispose();
339
350
  this._colorGradientsTexture = null;
340
351
  }
341
352
  }
353
+ else {
354
+ this._hasColorGradientColor2 = false;
355
+ }
342
356
  }
343
357
  /** Force the system to rebuild all gradients that need to be resync */
344
358
  forceRefreshGradients() {
@@ -358,6 +372,8 @@ export class GPUParticleSystem extends BaseParticleSystem {
358
372
  removeColorGradient(gradient) {
359
373
  this._removeGradientAndTexture(gradient, this._colorGradients, this._colorGradientsTexture);
360
374
  this._colorGradientsTexture = null;
375
+ // The set of remaining gradients may no longer contain a color2; recompute the flag.
376
+ this._refreshColorGradient();
361
377
  return this;
362
378
  }
363
379
  /**
@@ -659,6 +675,8 @@ export class GPUParticleSystem extends BaseParticleSystem {
659
675
  this._emitCount = 0;
660
676
  this._renderVertexBuffers = [];
661
677
  this._targetIndex = 0;
678
+ /** Set to true when any entry in `_colorGradients` has a `color2` (per-particle random color range). */
679
+ this._hasColorGradientColor2 = false;
662
680
  this._currentRenderId = -1;
663
681
  this._currentRenderingCameraUniqueId = -1;
664
682
  this._started = false;
@@ -824,6 +842,11 @@ export class GPUParticleSystem extends BaseParticleSystem {
824
842
  offset += 3;
825
843
  renderVertexBuffers["life"] = renderBuffer.createVertexBuffer("life", offset, 1, this._attributesStrideSize, true);
826
844
  offset += 1;
845
+ if (this._hasColorGradientColor2) {
846
+ // Expose `seed` to the render shader so it can pick a stable per-particle mix factor between
847
+ // the color1 and color2 rows of the color gradient texture.
848
+ renderVertexBuffers["seed"] = renderBuffer.createVertexBuffer("seed", offset, 4, this._attributesStrideSize, true);
849
+ }
827
850
  offset += 4; // seed
828
851
  if (this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL) {
829
852
  renderVertexBuffers["direction"] = renderBuffer.createVertexBuffer("direction", offset, 3, this._attributesStrideSize, true);
@@ -1159,11 +1182,16 @@ export class GPUParticleSystem extends BaseParticleSystem {
1159
1182
  /**
1160
1183
  * @internal
1161
1184
  */
1162
- static _GetAttributeNamesOrOptions(hasColorGradients = false, isAnimationSheetEnabled = false, isBillboardBased = false, isBillboardStretched = false, isBillboardStretchedLocal = false) {
1185
+ static _GetAttributeNamesOrOptions(hasColorGradients = false, isAnimationSheetEnabled = false, isBillboardBased = false, isBillboardStretched = false, isBillboardStretchedLocal = false, hasColorGradientColor2 = false) {
1163
1186
  const attributeNamesOrOptions = [VertexBuffer.PositionKind, "age", "life", "size", "angle"];
1164
1187
  if (!hasColorGradients) {
1165
1188
  attributeNamesOrOptions.push(VertexBuffer.ColorKind);
1166
1189
  }
1190
+ else if (hasColorGradientColor2) {
1191
+ // When packing a color1/color2 range into the gradient texture, the render shader needs the
1192
+ // particle's persistent random seed to pick a stable per-particle mix factor.
1193
+ attributeNamesOrOptions.push("seed");
1194
+ }
1167
1195
  if (isAnimationSheetEnabled) {
1168
1196
  attributeNamesOrOptions.push("cellIndex");
1169
1197
  }
@@ -1238,6 +1266,9 @@ export class GPUParticleSystem extends BaseParticleSystem {
1238
1266
  }
1239
1267
  if (this._colorGradientsTexture) {
1240
1268
  defines.push("#define COLORGRADIENTS");
1269
+ if (this._hasColorGradientColor2) {
1270
+ defines.push("#define COLORGRADIENTS_COLOR2");
1271
+ }
1241
1272
  }
1242
1273
  if (this.isAnimationSheetEnabled) {
1243
1274
  defines.push("#define ANIMATESHEET");
@@ -1257,7 +1288,7 @@ export class GPUParticleSystem extends BaseParticleSystem {
1257
1288
  * @param samplers Samplers array to fill
1258
1289
  */
1259
1290
  fillUniformsAttributesAndSamplerNames(uniforms, attributes, samplers) {
1260
- 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));
1291
+ 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, this._hasColorGradientColor2));
1261
1292
  uniforms.push(...GPUParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled, this.useLogarithmicDepth, this.applyFog));
1262
1293
  samplers.push("diffuseSampler", "colorGradientSampler");
1263
1294
  if (this._imageProcessingConfiguration) {
@@ -1421,7 +1452,11 @@ export class GPUParticleSystem extends BaseParticleSystem {
1421
1452
  if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
1422
1453
  return;
1423
1454
  }
1424
- const data = new Uint8Array(this._rawTextureWidth * 4);
1455
+ // When any stop has a color2, pack color1 into row 0 and color2 into row 1. The render shader
1456
+ // samples both rows and lerps using the particle's persistent seed.x for per-particle randomness.
1457
+ const hasColor2 = this._hasColorGradientColor2;
1458
+ const height = hasColor2 ? 2 : 1;
1459
+ const data = new Uint8Array(this._rawTextureWidth * 4 * height);
1425
1460
  const tmpColor = TmpColors.Color4[0];
1426
1461
  for (let x = 0; x < this._rawTextureWidth; x++) {
1427
1462
  const ratio = x / this._rawTextureWidth;
@@ -1433,7 +1468,23 @@ export class GPUParticleSystem extends BaseParticleSystem {
1433
1468
  data[x * 4 + 3] = tmpColor.a * 255;
1434
1469
  });
1435
1470
  }
1436
- this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, 1);
1471
+ if (hasColor2) {
1472
+ const rowOffset = this._rawTextureWidth * 4;
1473
+ for (let x = 0; x < this._rawTextureWidth; x++) {
1474
+ const ratio = x / this._rawTextureWidth;
1475
+ GradientHelper.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
1476
+ const cg = currentGradient;
1477
+ const ng = nextGradient;
1478
+ // Fall back to color1 for stops without a color2 so the row stays continuous.
1479
+ Color4.LerpToRef(cg.color2 ?? cg.color1, ng.color2 ?? ng.color1, scale, tmpColor);
1480
+ data[rowOffset + x * 4] = tmpColor.r * 255;
1481
+ data[rowOffset + x * 4 + 1] = tmpColor.g * 255;
1482
+ data[rowOffset + x * 4 + 2] = tmpColor.b * 255;
1483
+ data[rowOffset + x * 4 + 3] = tmpColor.a * 255;
1484
+ });
1485
+ }
1486
+ }
1487
+ this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, height, this._scene, false, false, 1);
1437
1488
  this._colorGradientsTexture.name = "colorGradients";
1438
1489
  }
1439
1490
  _render(blendMode, emitterWM) {
@@ -1881,6 +1932,10 @@ export class GPUParticleSystem extends BaseParticleSystem {
1881
1932
  this.noiseTexture.dispose();
1882
1933
  this.noiseTexture = null;
1883
1934
  }
1935
+ if (disposeTexture && this._flowMap) {
1936
+ this._flowMap.dispose();
1937
+ this._flowMap = null;
1938
+ }
1884
1939
  // Callback
1885
1940
  this.onStoppedObservable.clear();
1886
1941
  this.onDisposeObservable.notifyObservers(this);
@@ -1918,6 +1973,217 @@ export class GPUParticleSystem extends BaseParticleSystem {
1918
1973
  result.emitter = newEmitter;
1919
1974
  return result;
1920
1975
  }
1976
+ /**
1977
+ * Creates a new GPUParticleSystem from an existing CPU ParticleSystem, copying all shared properties.
1978
+ * Features that are not supported on the GPU (sub-emitters, custom `startDirectionFunction` /
1979
+ * `startPositionFunction`, `customShader`, ramp/remap gradients) are logged as warnings and skipped.
1980
+ * Flow maps are converted: the CPU `FlowMap` image data is uploaded to a new `RawTexture` which is
1981
+ * assigned to the result.
1982
+ *
1983
+ * Note: a custom `updateFunction` on the source cannot be detected (the property is always assigned
1984
+ * to a default) and has no equivalent on the GPU path, so any custom per-frame update logic will be
1985
+ * silently dropped.
1986
+ *
1987
+ * Textures (particleTexture, noiseTexture) are shared by reference between the source and the result.
1988
+ * All other mutable state (colors, vectors, emitter type, gradients, attractors) is cloned so that
1989
+ * the two systems can be modified independently after the call.
1990
+ *
1991
+ * Note: unlike the GPUParticleSystem constructor, `emitRateControl` defaults to `true` here so that
1992
+ * changes to `emitRate` on the converted system behave the same as on the CPU source. Pass
1993
+ * `{ emitRateControl: false }` explicitly to opt out.
1994
+ * @param source The CPU ParticleSystem to convert
1995
+ * @param sceneOrEngine The scene or engine the new GPU particle system belongs to
1996
+ * @param options Optional options forwarded to the new GPU particle system (capacity, randomTextureSize, emitRateControl, maxAttractors). `capacity` defaults to the source capacity and `emitRateControl` defaults to `true`.
1997
+ * @returns A new GPUParticleSystem with shared properties copied from the source
1998
+ */
1999
+ static fromParticleSystem(source, sceneOrEngine, options) {
2000
+ // Warn on features that cannot be represented on a GPU particle system.
2001
+ if (source.subEmitters && source.subEmitters.length > 0) {
2002
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'subEmitters' is not supported on GPUParticleSystem and will be skipped.");
2003
+ }
2004
+ if (source.startDirectionFunction) {
2005
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'startDirectionFunction' is not supported on GPUParticleSystem and will be skipped.");
2006
+ }
2007
+ if (source.startPositionFunction) {
2008
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'startPositionFunction' is not supported on GPUParticleSystem and will be skipped.");
2009
+ }
2010
+ if (source.customShader) {
2011
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'customShader' is not supported on GPUParticleSystem and will be skipped.");
2012
+ }
2013
+ const sourceRampGradients = source.getRampGradients();
2014
+ if (sourceRampGradients && sourceRampGradients.length > 0) {
2015
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'rampGradients' are not supported on GPUParticleSystem and will be skipped.");
2016
+ }
2017
+ const sourceColorRemapGradients = source.getColorRemapGradients();
2018
+ if (sourceColorRemapGradients && sourceColorRemapGradients.length > 0) {
2019
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'colorRemapGradients' are not supported on GPUParticleSystem and will be skipped.");
2020
+ }
2021
+ const sourceAlphaRemapGradients = source.getAlphaRemapGradients();
2022
+ if (sourceAlphaRemapGradients && sourceAlphaRemapGradients.length > 0) {
2023
+ Logger.Warn("GPUParticleSystem.fromParticleSystem: 'alphaRemapGradients' are not supported on GPUParticleSystem and will be skipped.");
2024
+ }
2025
+ const capacity = options?.capacity ?? source.getCapacity();
2026
+ const gpuOptions = { capacity };
2027
+ if (options?.randomTextureSize !== undefined) {
2028
+ gpuOptions.randomTextureSize = options.randomTextureSize;
2029
+ }
2030
+ // Default emitRateControl to true here: on a freshly constructed GPUParticleSystem the default is
2031
+ // false for backwards compatibility, but when converting an existing CPU system users expect
2032
+ // changes to emitRate to take effect — matching CPU behavior.
2033
+ gpuOptions.emitRateControl = options?.emitRateControl ?? true;
2034
+ if (options?.maxAttractors !== undefined) {
2035
+ gpuOptions.maxAttractors = options.maxAttractors;
2036
+ }
2037
+ const gpu = new GPUParticleSystem(source.name + " (GPU)", gpuOptions, sceneOrEngine, null, source.isAnimationSheetEnabled);
2038
+ gpu.id = source.id;
2039
+ // Emitter (shared by reference: mesh or Vector3 — users expect both systems to follow the same source).
2040
+ gpu.emitter = source.emitter;
2041
+ // Emitter type — cloned for independence.
2042
+ if (source.particleEmitterType) {
2043
+ gpu.particleEmitterType = source.particleEmitterType.clone();
2044
+ }
2045
+ // Textures — shared by reference.
2046
+ gpu.particleTexture = source.particleTexture;
2047
+ if (source.noiseTexture) {
2048
+ gpu.noiseTexture = source.noiseTexture;
2049
+ }
2050
+ // Colors.
2051
+ gpu.color1 = source.color1.clone();
2052
+ gpu.color2 = source.color2.clone();
2053
+ gpu.colorDead = source.colorDead.clone();
2054
+ gpu.textureMask = source.textureMask.clone();
2055
+ // Sizes.
2056
+ gpu.minSize = source.minSize;
2057
+ gpu.maxSize = source.maxSize;
2058
+ gpu.minScaleX = source.minScaleX;
2059
+ gpu.maxScaleX = source.maxScaleX;
2060
+ gpu.minScaleY = source.minScaleY;
2061
+ gpu.maxScaleY = source.maxScaleY;
2062
+ // Speeds / rotation.
2063
+ gpu.minEmitPower = source.minEmitPower;
2064
+ gpu.maxEmitPower = source.maxEmitPower;
2065
+ gpu.minAngularSpeed = source.minAngularSpeed;
2066
+ gpu.maxAngularSpeed = source.maxAngularSpeed;
2067
+ gpu.minInitialRotation = source.minInitialRotation;
2068
+ gpu.maxInitialRotation = source.maxInitialRotation;
2069
+ // Lifetime.
2070
+ gpu.minLifeTime = source.minLifeTime;
2071
+ gpu.maxLifeTime = source.maxLifeTime;
2072
+ // Emission.
2073
+ gpu.emitRate = source.emitRate;
2074
+ gpu.manualEmitCount = source.manualEmitCount;
2075
+ // Physics.
2076
+ gpu.gravity = source.gravity.clone();
2077
+ gpu.limitVelocityDamping = source.limitVelocityDamping;
2078
+ // Rendering.
2079
+ gpu.blendMode = source.blendMode;
2080
+ gpu.billboardMode = source.billboardMode;
2081
+ gpu.isBillboardBased = source.isBillboardBased;
2082
+ gpu.forceDepthWrite = source.forceDepthWrite;
2083
+ gpu.useLogarithmicDepth = source.useLogarithmicDepth;
2084
+ gpu.renderingGroupId = source.renderingGroupId;
2085
+ gpu.layerMask = source.layerMask;
2086
+ // Animation sheet.
2087
+ gpu.startSpriteCellID = source.startSpriteCellID;
2088
+ gpu.endSpriteCellID = source.endSpriteCellID;
2089
+ gpu.spriteCellWidth = source.spriteCellWidth;
2090
+ gpu.spriteCellHeight = source.spriteCellHeight;
2091
+ gpu.spriteCellChangeSpeed = source.spriteCellChangeSpeed;
2092
+ gpu.spriteCellLoop = source.spriteCellLoop;
2093
+ gpu.spriteRandomStartCell = source.spriteRandomStartCell;
2094
+ // Space.
2095
+ gpu.isLocal = source.isLocal;
2096
+ gpu.worldOffset = source.worldOffset.clone();
2097
+ gpu.translationPivot = source.translationPivot.clone();
2098
+ // Lifecycle.
2099
+ gpu.targetStopDuration = source.targetStopDuration;
2100
+ gpu.disposeOnStop = source.disposeOnStop;
2101
+ gpu.startDelay = source.startDelay;
2102
+ gpu.preWarmCycles = source.preWarmCycles;
2103
+ gpu.preWarmStepOffset = source.preWarmStepOffset;
2104
+ gpu.updateSpeed = source.updateSpeed;
2105
+ gpu.preventAutoStart = source.preventAutoStart;
2106
+ // Animations (shared by reference, matching the rest of the scene-graph convention).
2107
+ gpu.animations = source.animations;
2108
+ gpu.beginAnimationOnStart = source.beginAnimationOnStart;
2109
+ gpu.beginAnimationFrom = source.beginAnimationFrom;
2110
+ gpu.beginAnimationTo = source.beginAnimationTo;
2111
+ gpu.beginAnimationLoop = source.beginAnimationLoop;
2112
+ // Noise.
2113
+ gpu.noiseStrength = source.noiseStrength.clone();
2114
+ // Flow map — convert the CPU FlowMap (JS-side image data) into a RawTexture for GPU sampling.
2115
+ // The CPU FlowMap stores image data top-left origin and flips V in its sampler; to get the
2116
+ // same orientation under the GPU shader's non-flipped sampling, the uploaded texture needs invertY=true.
2117
+ if (source.flowMap) {
2118
+ const sourceFlowMap = source.flowMap;
2119
+ const flowTexture = new RawTexture(new Uint8Array(sourceFlowMap.data.buffer, sourceFlowMap.data.byteOffset, sourceFlowMap.data.byteLength), sourceFlowMap.width, sourceFlowMap.height, 5, sceneOrEngine, false, true, 2);
2120
+ gpu.flowMap = flowTexture;
2121
+ gpu.flowMapStrength = source.flowMapStrength;
2122
+ }
2123
+ // Gradients.
2124
+ const colorGradients = source.getColorGradients();
2125
+ if (colorGradients) {
2126
+ for (const g of colorGradients) {
2127
+ gpu.addColorGradient(g.gradient, g.color1.clone(), g.color2?.clone());
2128
+ }
2129
+ }
2130
+ const sizeGradients = source.getSizeGradients();
2131
+ if (sizeGradients) {
2132
+ for (const g of sizeGradients) {
2133
+ gpu.addSizeGradient(g.gradient, g.factor1, g.factor2);
2134
+ }
2135
+ }
2136
+ const angularSpeedGradients = source.getAngularSpeedGradients();
2137
+ if (angularSpeedGradients) {
2138
+ for (const g of angularSpeedGradients) {
2139
+ gpu.addAngularSpeedGradient(g.gradient, g.factor1, g.factor2);
2140
+ }
2141
+ }
2142
+ const velocityGradients = source.getVelocityGradients();
2143
+ if (velocityGradients) {
2144
+ for (const g of velocityGradients) {
2145
+ gpu.addVelocityGradient(g.gradient, g.factor1, g.factor2);
2146
+ }
2147
+ }
2148
+ const limitVelocityGradients = source.getLimitVelocityGradients();
2149
+ if (limitVelocityGradients) {
2150
+ for (const g of limitVelocityGradients) {
2151
+ gpu.addLimitVelocityGradient(g.gradient, g.factor1, g.factor2);
2152
+ }
2153
+ }
2154
+ const dragGradients = source.getDragGradients();
2155
+ if (dragGradients) {
2156
+ for (const g of dragGradients) {
2157
+ gpu.addDragGradient(g.gradient, g.factor1, g.factor2);
2158
+ }
2159
+ }
2160
+ const emitRateGradients = source.getEmitRateGradients();
2161
+ if (emitRateGradients) {
2162
+ for (const g of emitRateGradients) {
2163
+ gpu.addEmitRateGradient(g.gradient, g.factor1, g.factor2);
2164
+ }
2165
+ }
2166
+ const startSizeGradients = source.getStartSizeGradients();
2167
+ if (startSizeGradients) {
2168
+ for (const g of startSizeGradients) {
2169
+ gpu.addStartSizeGradient(g.gradient, g.factor1, g.factor2);
2170
+ }
2171
+ }
2172
+ const lifeTimeGradients = source.getLifeTimeGradients();
2173
+ if (lifeTimeGradients) {
2174
+ for (const g of lifeTimeGradients) {
2175
+ gpu.addLifeTimeGradient(g.gradient, g.factor1, g.factor2);
2176
+ }
2177
+ }
2178
+ // Attractors — cloned.
2179
+ for (const attractor of source.attractors) {
2180
+ const newAttractor = new Attractor();
2181
+ newAttractor.position = attractor.position.clone();
2182
+ newAttractor.strength = attractor.strength;
2183
+ gpu.addAttractor(newAttractor);
2184
+ }
2185
+ return gpu;
2186
+ }
1921
2187
  /**
1922
2188
  * Serializes the particle system to a JSON object
1923
2189
  * @param serializeTexture defines if the texture must be serialized as well