@babylonjs/core 9.3.0 → 9.3.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 (121) hide show
  1. package/Engines/abstractEngine.js +2 -2
  2. package/Engines/abstractEngine.js.map +1 -1
  3. package/Engines/engine.d.ts +49 -1118
  4. package/FlowGraph/flowGraph.d.ts +11 -0
  5. package/FlowGraph/flowGraph.js +20 -0
  6. package/FlowGraph/flowGraph.js.map +1 -1
  7. package/FlowGraph/flowGraphContext.d.ts +30 -0
  8. package/FlowGraph/flowGraphContext.js +42 -0
  9. package/FlowGraph/flowGraphContext.js.map +1 -1
  10. package/FlowGraph/flowGraphParser.js +13 -0
  11. package/FlowGraph/flowGraphParser.js.map +1 -1
  12. package/FlowGraph/typeDefinitions.d.ts +16 -0
  13. package/FlowGraph/typeDefinitions.js.map +1 -1
  14. package/Layers/thinSelectionOutlineLayer.js +25 -1
  15. package/Layers/thinSelectionOutlineLayer.js.map +1 -1
  16. package/Lights/Clustered/clusteredLightContainer.d.ts +1 -0
  17. package/Lights/Clustered/clusteredLightContainer.js +19 -0
  18. package/Lights/Clustered/clusteredLightContainer.js.map +1 -1
  19. package/Lights/light.d.ts +6 -0
  20. package/Lights/light.js +8 -0
  21. package/Lights/light.js.map +1 -1
  22. package/Lights/spotLight.d.ts +2 -0
  23. package/Lights/spotLight.js +10 -0
  24. package/Lights/spotLight.js.map +1 -1
  25. package/Materials/Background/backgroundMaterial.js +4 -1
  26. package/Materials/Background/backgroundMaterial.js.map +1 -1
  27. package/Materials/GaussianSplatting/gaussianSplattingMaterial.d.ts +18 -0
  28. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js +116 -3
  29. package/Materials/GaussianSplatting/gaussianSplattingMaterial.js.map +1 -1
  30. package/Materials/Node/Blocks/Dual/lightBlock.d.ts +8 -0
  31. package/Materials/Node/Blocks/Dual/lightBlock.js +16 -0
  32. package/Materials/Node/Blocks/Dual/lightBlock.js.map +1 -1
  33. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js +3 -0
  34. package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.js.map +1 -1
  35. package/Materials/Node/nodeMaterial.js +4 -1
  36. package/Materials/Node/nodeMaterial.js.map +1 -1
  37. package/Materials/PBR/openpbrMaterial.js +4 -1
  38. package/Materials/PBR/openpbrMaterial.js.map +1 -1
  39. package/Materials/PBR/pbrBaseMaterial.js +4 -1
  40. package/Materials/PBR/pbrBaseMaterial.js.map +1 -1
  41. package/Materials/Textures/baseTexture.d.ts +1 -0
  42. package/Materials/Textures/baseTexture.js +1 -0
  43. package/Materials/Textures/baseTexture.js.map +1 -1
  44. package/Materials/materialHelper.functions.d.ts +12 -0
  45. package/Materials/materialHelper.functions.js +24 -0
  46. package/Materials/materialHelper.functions.js.map +1 -1
  47. package/Materials/standardMaterial.js +4 -1
  48. package/Materials/standardMaterial.js.map +1 -1
  49. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +18 -1
  50. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +40 -4
  51. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  52. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.d.ts +27 -0
  53. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js +241 -10
  54. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.js.map +1 -1
  55. package/Misc/tools.js +1 -1
  56. package/Misc/tools.js.map +1 -1
  57. package/Particles/IParticleSystem.d.ts +7 -1
  58. package/Particles/IParticleSystem.js.map +1 -1
  59. package/Particles/baseParticleSystem.d.ts +18 -2
  60. package/Particles/baseParticleSystem.js +53 -11
  61. package/Particles/baseParticleSystem.js.map +1 -1
  62. package/Particles/computeShaderParticleSystem.js +16 -0
  63. package/Particles/computeShaderParticleSystem.js.map +1 -1
  64. package/Particles/gpuParticleSystem.d.ts +14 -72
  65. package/Particles/gpuParticleSystem.js +130 -106
  66. package/Particles/gpuParticleSystem.js.map +1 -1
  67. package/Particles/particleSystem.d.ts +0 -80
  68. package/Particles/particleSystem.functions.d.ts +16 -0
  69. package/Particles/particleSystem.functions.js +18 -0
  70. package/Particles/particleSystem.functions.js.map +1 -1
  71. package/Particles/particleSystem.js +0 -114
  72. package/Particles/particleSystem.js.map +1 -1
  73. package/Particles/webgl2ParticleSystem.js +12 -0
  74. package/Particles/webgl2ParticleSystem.js.map +1 -1
  75. package/Rendering/IBLShadows/iblShadowsRenderPipeline.js +17 -0
  76. package/Rendering/IBLShadows/iblShadowsRenderPipeline.js.map +1 -1
  77. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.d.ts +10 -0
  78. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.js +146 -24
  79. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.js.map +1 -1
  80. package/Rendering/depthRenderer.d.ts +8 -0
  81. package/Rendering/depthRenderer.js +48 -13
  82. package/Rendering/depthRenderer.js.map +1 -1
  83. package/Rendering/depthRendererSceneComponent.d.ts +1 -0
  84. package/Rendering/depthRendererSceneComponent.js +26 -0
  85. package/Rendering/depthRendererSceneComponent.js.map +1 -1
  86. package/Rendering/objectRenderer.d.ts +2 -0
  87. package/Rendering/objectRenderer.js +10 -0
  88. package/Rendering/objectRenderer.js.map +1 -1
  89. package/Shaders/ShadersInclude/gaussianSplatting.js +21 -1
  90. package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
  91. package/Shaders/gaussianSplattingVoxel.fragment.d.ts +5 -0
  92. package/Shaders/gaussianSplattingVoxel.fragment.js +27 -0
  93. package/Shaders/gaussianSplattingVoxel.fragment.js.map +1 -0
  94. package/Shaders/gaussianSplattingVoxel.vertex.d.ts +8 -0
  95. package/Shaders/gaussianSplattingVoxel.vertex.js +31 -0
  96. package/Shaders/gaussianSplattingVoxel.vertex.js.map +1 -0
  97. package/Shaders/gpuUpdateParticles.vertex.js +13 -0
  98. package/Shaders/gpuUpdateParticles.vertex.js.map +1 -1
  99. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +21 -1
  100. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
  101. package/ShadersWGSL/gaussianSplattingVoxel.fragment.d.ts +5 -0
  102. package/ShadersWGSL/gaussianSplattingVoxel.fragment.js +22 -0
  103. package/ShadersWGSL/gaussianSplattingVoxel.fragment.js.map +1 -0
  104. package/ShadersWGSL/gaussianSplattingVoxel.vertex.d.ts +8 -0
  105. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js +42 -0
  106. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js.map +1 -0
  107. package/ShadersWGSL/gpuUpdateParticles.compute.js +19 -0
  108. package/ShadersWGSL/gpuUpdateParticles.compute.js.map +1 -1
  109. package/XR/features/WebXRBodyTracking.d.ts +952 -0
  110. package/XR/features/WebXRBodyTracking.js +2221 -0
  111. package/XR/features/WebXRBodyTracking.js.map +1 -0
  112. package/XR/features/index.d.ts +1 -0
  113. package/XR/features/index.js +1 -0
  114. package/XR/features/index.js.map +1 -1
  115. package/XR/webXRFeaturesManager.d.ts +7 -0
  116. package/XR/webXRFeaturesManager.js +4 -0
  117. package/XR/webXRFeaturesManager.js.map +1 -1
  118. package/package.json +1 -1
  119. package/sceneComponent.d.ts +1 -0
  120. package/sceneComponent.js +1 -0
  121. package/sceneComponent.js.map +1 -1
@@ -221,10 +221,19 @@ export declare class GaussianSplattingMeshBase extends Mesh {
221
221
  private _depthMix;
222
222
  protected _canPostToWorker: boolean;
223
223
  private _readyToDisplay;
224
+ private _sortRequestId;
225
+ private _hasRenderedOnce;
224
226
  protected _covariancesATexture: Nullable<BaseTexture>;
225
227
  protected _covariancesBTexture: Nullable<BaseTexture>;
226
228
  protected _centersTexture: Nullable<BaseTexture>;
227
229
  protected _colorsTexture: Nullable<BaseTexture>;
230
+ protected _rotationsATexture: Nullable<BaseTexture>;
231
+ protected _rotationsBTexture: Nullable<BaseTexture>;
232
+ protected _rotationScaleTexture: Nullable<BaseTexture>;
233
+ private _rotationDataA;
234
+ private _rotationDataB;
235
+ private _rotationScaleData;
236
+ protected _needsRotationScaleTextures: boolean;
228
237
  protected _splatPositions: Nullable<Float32Array>;
229
238
  private _splatIndex;
230
239
  protected _shTextures: Nullable<BaseTexture[]>;
@@ -313,6 +322,23 @@ export declare class GaussianSplattingMeshBase extends Mesh {
313
322
  * Gets the colors texture
314
323
  */
315
324
  get colorsTexture(): Nullable<BaseTexture>;
325
+ /**
326
+ * Gets the rotation matrix A texture (rotation elements m[0],m[1],m[2],m[4])
327
+ */
328
+ get rotationsATexture(): Nullable<BaseTexture>;
329
+ /**
330
+ * Gets the rotation matrix B texture (rotation elements m[5],m[6],m[8],m[9])
331
+ */
332
+ get rotationsBTexture(): Nullable<BaseTexture>;
333
+ /**
334
+ * Gets the rotation scale texture (rotation element m[10] followed by scale diagonal sx,sy,sz)
335
+ */
336
+ get rotationScaleTexture(): Nullable<BaseTexture>;
337
+ /**
338
+ * Enables or disables generation of rotation and scale matrix textures, required for voxel-based IBL shadows.
339
+ */
340
+ get needsRotationScaleTextures(): boolean;
341
+ set needsRotationScaleTextures(value: boolean);
316
342
  /**
317
343
  * Gets the SH textures
318
344
  */
@@ -368,6 +394,7 @@ export declare class GaussianSplattingMeshBase extends Mesh {
368
394
  */
369
395
  isReady(completeCheck?: boolean): boolean;
370
396
  _getCameraDirection(camera: Camera): Vector3;
397
+ private _isSortStateDirty;
371
398
  /** @internal */
372
399
  _postToWorker(forced?: boolean): void;
373
400
  /**
@@ -282,6 +282,45 @@ export class GaussianSplattingMeshBase extends Mesh {
282
282
  get colorsTexture() {
283
283
  return this._colorsTexture;
284
284
  }
285
+ /**
286
+ * Gets the rotation matrix A texture (rotation elements m[0],m[1],m[2],m[4])
287
+ */
288
+ get rotationsATexture() {
289
+ return this._rotationsATexture;
290
+ }
291
+ /**
292
+ * Gets the rotation matrix B texture (rotation elements m[5],m[6],m[8],m[9])
293
+ */
294
+ get rotationsBTexture() {
295
+ return this._rotationsBTexture;
296
+ }
297
+ /**
298
+ * Gets the rotation scale texture (rotation element m[10] followed by scale diagonal sx,sy,sz)
299
+ */
300
+ get rotationScaleTexture() {
301
+ return this._rotationScaleTexture;
302
+ }
303
+ /**
304
+ * Enables or disables generation of rotation and scale matrix textures, required for voxel-based IBL shadows.
305
+ */
306
+ get needsRotationScaleTextures() {
307
+ return this._needsRotationScaleTextures;
308
+ }
309
+ set needsRotationScaleTextures(value) {
310
+ if (this._needsRotationScaleTextures === value) {
311
+ return;
312
+ }
313
+ this._needsRotationScaleTextures = value;
314
+ if (value && this._covariancesATexture) {
315
+ if (this._splatsData) {
316
+ this.updateData(this._splatsData, this._shData ?? undefined, { flipY: false });
317
+ }
318
+ else {
319
+ Logger.Error("GaussianSplattingMeshBase: needsRotationScaleTextures was enabled after the mesh was already loaded, but the splat data is not kept in RAM. " +
320
+ "The rotation and scale matrix textures cannot be initialized. Please reload the mesh data via updateData() or construct with keepInRam=true.");
321
+ }
322
+ }
323
+ }
285
324
  /**
286
325
  * Gets the SH textures
287
326
  */
@@ -354,10 +393,19 @@ export class GaussianSplattingMeshBase extends Mesh {
354
393
  this._modelViewProjectionMatrix = Matrix.Identity();
355
394
  this._canPostToWorker = true;
356
395
  this._readyToDisplay = false;
396
+ this._sortRequestId = 0;
397
+ this._hasRenderedOnce = false;
357
398
  this._covariancesATexture = null;
358
399
  this._covariancesBTexture = null;
359
400
  this._centersTexture = null;
360
401
  this._colorsTexture = null;
402
+ this._rotationsATexture = null;
403
+ this._rotationsBTexture = null;
404
+ this._rotationScaleTexture = null;
405
+ this._rotationDataA = null;
406
+ this._rotationDataB = null;
407
+ this._rotationScaleData = null;
408
+ this._needsRotationScaleTextures = false;
361
409
  this._splatPositions = null;
362
410
  this._splatIndex = null;
363
411
  this._shTextures = null;
@@ -450,6 +498,66 @@ export class GaussianSplattingMeshBase extends Mesh {
450
498
  this._postToWorker(true);
451
499
  return false;
452
500
  }
501
+ // Before the first successful render, apply strict sort-state checks to ensure
502
+ // the first rendered frame uses correct splat ordering. Once the mesh has been
503
+ // rendered at least once, skip these checks — the render loop will continuously
504
+ // re-sort as the camera/world changes via _postToWorker() in render().
505
+ if (!this._hasRenderedOnce && !this._disableDepthSort) {
506
+ const cameras = this._scene.activeCameras?.length ? this._scene.activeCameras : [this._scene.activeCamera];
507
+ const worldMatrix = this.computeWorldMatrix(true);
508
+ let anyDirty = false;
509
+ for (const camera of cameras) {
510
+ if (!camera) {
511
+ continue;
512
+ }
513
+ const cameraViewInfo = this._cameraViewInfos.get(camera.uniqueId);
514
+ if (!cameraViewInfo || !cameraViewInfo.splatIndexBufferSet) {
515
+ anyDirty = true;
516
+ continue;
517
+ }
518
+ // Wait for the most recently requested sort to be applied so that the splat indices
519
+ // match the latest world/camera state.
520
+ if (cameraViewInfo.sortAppliedId !== cameraViewInfo.sortRequestId) {
521
+ anyDirty = true;
522
+ continue;
523
+ }
524
+ // Also detect drift: if the world or camera state has changed since the last post,
525
+ // mark dirty so the next render does not silently queue a new sort that completes
526
+ // after isReady has reported true.
527
+ if (this._isSortStateDirty(cameraViewInfo, worldMatrix, camera)) {
528
+ anyDirty = true;
529
+ }
530
+ }
531
+ if (anyDirty) {
532
+ // Try to post any pending sort so subsequent polling iterations make progress.
533
+ this._postToWorker(true);
534
+ return false;
535
+ }
536
+ }
537
+ // Attach the splat geometry to the GS top mesh so that the shadow generator (which renders
538
+ // shadow casters via the top mesh's subMeshes, NOT through this mesh's render() override)
539
+ // has valid geometry on the very first shadow pass. Without this, the first shadow render
540
+ // happens before render() is called and the GS produces no shadow caster output.
541
+ if (!this._geometry && this._cameraViewInfos.size) {
542
+ this._geometry = this._cameraViewInfos.values().next().value.mesh.geometry;
543
+ }
544
+ // If the material declares a shadow depth wrapper, make sure its effect is compiled for
545
+ // each subMesh against the scene's shadow generators. Otherwise the first shadow pass
546
+ // would be skipped (ShadowGenerator.isReady would return false) and we'd miss the shadow
547
+ // on a renderCount=1 capture.
548
+ if (this.material && this.material.shadowDepthWrapper) {
549
+ for (const light of this._scene.lights) {
550
+ const shadowGenerator = light.getShadowGenerator();
551
+ if (!shadowGenerator) {
552
+ continue;
553
+ }
554
+ for (const subMesh of this.subMeshes) {
555
+ if (!shadowGenerator.isReady(subMesh, true, false)) {
556
+ return false;
557
+ }
558
+ }
559
+ }
560
+ }
453
561
  return true;
454
562
  }
455
563
  _getCameraDirection(camera) {
@@ -457,7 +565,7 @@ export class GaussianSplattingMeshBase extends Mesh {
457
565
  const cameraProjectionMatrix = camera.getProjectionMatrix();
458
566
  const cameraViewProjectionMatrix = TmpVectors.Matrix[0];
459
567
  cameraViewMatrix.multiplyToRef(cameraProjectionMatrix, cameraViewProjectionMatrix);
460
- const modelMatrix = this.getWorldMatrix();
568
+ const modelMatrix = this.computeWorldMatrix(true);
461
569
  const modelViewMatrix = TmpVectors.Matrix[1];
462
570
  modelMatrix.multiplyToRef(cameraViewMatrix, modelViewMatrix);
463
571
  modelMatrix.multiplyToRef(cameraViewProjectionMatrix, this._modelViewProjectionMatrix);
@@ -467,6 +575,25 @@ export class GaussianSplattingMeshBase extends Mesh {
467
575
  localDirection.normalize();
468
576
  return localDirection;
469
577
  }
578
+ _isSortStateDirty(cameraViewInfo, worldMatrix, camera) {
579
+ const world = worldMatrix.m;
580
+ const previousWorld = cameraViewInfo.sortWorldMatrix.m;
581
+ for (let i = 0; i < previousWorld.length; i++) {
582
+ if (!Scalar.WithinEpsilon(previousWorld[i], world[i], this.viewUpdateThreshold)) {
583
+ return true;
584
+ }
585
+ }
586
+ const cameraViewMatrix = camera.getViewMatrix();
587
+ if (!Scalar.WithinEpsilon(cameraViewInfo.sortCameraForward.x, cameraViewMatrix.m[2], this.viewUpdateThreshold) ||
588
+ !Scalar.WithinEpsilon(cameraViewInfo.sortCameraForward.y, cameraViewMatrix.m[6], this.viewUpdateThreshold) ||
589
+ !Scalar.WithinEpsilon(cameraViewInfo.sortCameraForward.z, cameraViewMatrix.m[10], this.viewUpdateThreshold)) {
590
+ return true;
591
+ }
592
+ const cameraPosition = camera.globalPosition;
593
+ return (!Scalar.WithinEpsilon(cameraViewInfo.sortCameraPosition.x, cameraPosition.x, this.viewUpdateThreshold) ||
594
+ !Scalar.WithinEpsilon(cameraViewInfo.sortCameraPosition.y, cameraPosition.y, this.viewUpdateThreshold) ||
595
+ !Scalar.WithinEpsilon(cameraViewInfo.sortCameraPosition.z, cameraPosition.z, this.viewUpdateThreshold));
596
+ }
470
597
  /** @internal */
471
598
  _postToWorker(forced = false) {
472
599
  const scene = this._scene;
@@ -513,6 +640,11 @@ export class GaussianSplattingMeshBase extends Mesh {
513
640
  const newViewInfos = {
514
641
  camera: camera,
515
642
  cameraDirection: new Vector3(0, 0, 0),
643
+ sortWorldMatrix: Matrix.Identity(),
644
+ sortCameraForward: new Vector3(0, 0, 0),
645
+ sortCameraPosition: new Vector3(0, 0, 0),
646
+ sortRequestId: 0,
647
+ sortAppliedId: 0,
516
648
  mesh: cameraMesh,
517
649
  frameIdLastUpdate: frameId,
518
650
  splatIndexBufferSet: false,
@@ -521,28 +653,39 @@ export class GaussianSplattingMeshBase extends Mesh {
521
653
  this._cameraViewInfos.set(cameraId, newViewInfos);
522
654
  }
523
655
  });
524
- // sort view infos by last updated frame id: first item is the least recently updated
525
- activeViewInfos.sort((a, b) => a.frameIdLastUpdate - b.frameIdLastUpdate);
656
+ // sort view infos: cameras without an initial splat-index buffer come first so they don't get starved
657
+ // by a `forced` re-sort of an already-initialized camera (which would consume `_canPostToWorker`).
658
+ // Among initialized cameras, the least recently updated comes first.
659
+ activeViewInfos.sort((a, b) => {
660
+ if (a.splatIndexBufferSet !== b.splatIndexBufferSet) {
661
+ return a.splatIndexBufferSet ? 1 : -1;
662
+ }
663
+ return a.frameIdLastUpdate - b.frameIdLastUpdate;
664
+ });
526
665
  const hasSortFunction = this._worker || Native?.sortSplats || this._disableDepthSort;
527
666
  if ((forced || outdated) && hasSortFunction && (this._scene.activeCameras?.length || this._scene.activeCamera) && this._canPostToWorker) {
667
+ const worldMatrix = this.computeWorldMatrix(true);
528
668
  // view infos sorted by least recent updated frame id
529
669
  activeViewInfos.forEach((cameraViewInfos) => {
530
670
  const camera = cameraViewInfos.camera;
531
671
  const cameraDirection = this._getCameraDirection(camera);
532
- const previousCameraDirection = cameraViewInfos.cameraDirection;
533
- const dot = Vector3.Dot(cameraDirection, previousCameraDirection);
534
- if ((forced || Math.abs(dot - 1) >= this.viewUpdateThreshold) && this._canPostToWorker) {
672
+ if ((forced || this._isSortStateDirty(cameraViewInfos, worldMatrix, camera)) && this._canPostToWorker) {
673
+ const cameraViewMatrix = camera.getViewMatrix();
535
674
  cameraViewInfos.cameraDirection.copyFrom(cameraDirection);
675
+ cameraViewInfos.sortWorldMatrix.copyFrom(worldMatrix);
676
+ cameraViewInfos.sortCameraForward.set(cameraViewMatrix.m[2], cameraViewMatrix.m[6], cameraViewMatrix.m[10]);
677
+ cameraViewInfos.sortCameraPosition.copyFrom(camera.globalPosition);
678
+ cameraViewInfos.sortRequestId = ++this._sortRequestId;
536
679
  cameraViewInfos.frameIdLastUpdate = frameId;
537
680
  this._canPostToWorker = false;
538
681
  if (this._worker) {
539
- const cameraViewMatrix = camera.getViewMatrix();
540
682
  this._worker.postMessage({
541
- worldMatrix: this.getWorldMatrix().m,
683
+ worldMatrix: worldMatrix.m,
542
684
  cameraForward: [cameraViewMatrix.m[2], cameraViewMatrix.m[6], cameraViewMatrix.m[10]],
543
685
  cameraPosition: [camera.globalPosition.x, camera.globalPosition.y, camera.globalPosition.z],
544
686
  depthMix: this._depthMix,
545
687
  cameraId: camera.uniqueId,
688
+ sortRequestId: cameraViewInfos.sortRequestId,
546
689
  }, [this._depthMix.buffer]);
547
690
  }
548
691
  else if (Native?.sortSplats) {
@@ -554,6 +697,7 @@ export class GaussianSplattingMeshBase extends Mesh {
554
697
  cameraViewInfos.mesh.thinInstanceSetBuffer("splatIndex", this._splatIndex, 16, false);
555
698
  cameraViewInfos.splatIndexBufferSet = true;
556
699
  }
700
+ cameraViewInfos.sortAppliedId = cameraViewInfos.sortRequestId;
557
701
  this._canPostToWorker = true;
558
702
  this._readyToDisplay = true;
559
703
  }
@@ -604,6 +748,7 @@ export class GaussianSplattingMeshBase extends Mesh {
604
748
  mesh.setMaterialForRenderPass(renderPassId, renderPassMaterial);
605
749
  }
606
750
  const ret = mesh.render(subMesh, enableAlphaMode, effectiveMeshReplacement);
751
+ this._hasRenderedOnce = true;
607
752
  // Clean up the temporary override to avoid affecting other render passes
608
753
  if (renderPassMaterial) {
609
754
  mesh.setMaterialForRenderPass(renderPassId, undefined);
@@ -1355,6 +1500,15 @@ export class GaussianSplattingMeshBase extends Mesh {
1355
1500
  shTexture.dispose();
1356
1501
  }
1357
1502
  }
1503
+ this._rotationsATexture?.dispose();
1504
+ this._rotationsBTexture?.dispose();
1505
+ this._rotationScaleTexture?.dispose();
1506
+ this._rotationsATexture = null;
1507
+ this._rotationsBTexture = null;
1508
+ this._rotationScaleTexture = null;
1509
+ this._rotationDataA = null;
1510
+ this._rotationDataB = null;
1511
+ this._rotationScaleData = null;
1358
1512
  this._covariancesATexture = null;
1359
1513
  this._covariancesBTexture = null;
1360
1514
  this._centersTexture = null;
@@ -1384,6 +1538,11 @@ export class GaussianSplattingMeshBase extends Mesh {
1384
1538
  this._shTextures?.push(shTexture.clone());
1385
1539
  }
1386
1540
  }
1541
+ if (source._rotationsATexture) {
1542
+ this._rotationsATexture = source._rotationsATexture.clone();
1543
+ this._rotationsBTexture = source._rotationsBTexture?.clone();
1544
+ this._rotationScaleTexture = source._rotationScaleTexture?.clone();
1545
+ }
1387
1546
  }
1388
1547
  /**
1389
1548
  * Returns a new Mesh object generated from the current mesh properties.
@@ -1399,6 +1558,7 @@ export class GaussianSplattingMeshBase extends Mesh {
1399
1558
  newGS._modelViewProjectionMatrix = Matrix.Identity();
1400
1559
  newGS._splatPositions = this._splatPositions;
1401
1560
  newGS._readyToDisplay = false;
1561
+ newGS._hasRenderedOnce = false;
1402
1562
  newGS._disableDepthSort = this._disableDepthSort;
1403
1563
  newGS._instantiateWorker();
1404
1564
  const binfo = this.getBoundingInfo();
@@ -1452,6 +1612,30 @@ export class GaussianSplattingMeshBase extends Mesh {
1452
1612
  quaternion.normalize();
1453
1613
  quaternion.toRotationMatrix(matrixRotation);
1454
1614
  Matrix.ScalingToRef(fBuffer[8 * srcIndex + 3 + 0] * 2, fBuffer[8 * srcIndex + 3 + 1] * 2, fBuffer[8 * srcIndex + 3 + 2] * 2, matrixScale);
1615
+ if (this._needsRotationScaleTextures) {
1616
+ if (!this._rotationDataA || this._rotationDataA.length < covA.length) {
1617
+ this._rotationDataA = new Uint16Array(covA.length);
1618
+ this._rotationDataB = new Uint16Array(covA.length);
1619
+ this._rotationScaleData = new Uint16Array(covA.length);
1620
+ }
1621
+ const rotDataA = this._rotationDataA;
1622
+ const rotDataB = this._rotationDataB;
1623
+ const rotScaleData = this._rotationScaleData;
1624
+ const rm = matrixRotation.m;
1625
+ const sm = matrixScale.m;
1626
+ rotDataA[dstIndex * 4 + 0] = ToHalfFloat(rm[0]);
1627
+ rotDataA[dstIndex * 4 + 1] = ToHalfFloat(rm[1]);
1628
+ rotDataA[dstIndex * 4 + 2] = ToHalfFloat(rm[2]);
1629
+ rotDataA[dstIndex * 4 + 3] = ToHalfFloat(rm[4]);
1630
+ rotDataB[dstIndex * 4 + 0] = ToHalfFloat(rm[5]);
1631
+ rotDataB[dstIndex * 4 + 1] = ToHalfFloat(rm[6]);
1632
+ rotDataB[dstIndex * 4 + 2] = ToHalfFloat(rm[8]);
1633
+ rotDataB[dstIndex * 4 + 3] = ToHalfFloat(rm[9]);
1634
+ rotScaleData[dstIndex * 4 + 0] = ToHalfFloat(rm[10]);
1635
+ rotScaleData[dstIndex * 4 + 1] = ToHalfFloat(sm[0]);
1636
+ rotScaleData[dstIndex * 4 + 2] = ToHalfFloat(sm[5]);
1637
+ rotScaleData[dstIndex * 4 + 3] = ToHalfFloat(sm[10]);
1638
+ }
1455
1639
  const m = matrixRotation.multiplyToRef(matrixScale, TmpVectors.Matrix[0]).m;
1456
1640
  const covariances = this._tmpCovariances;
1457
1641
  covariances[0] = m[0] * m[0] + m[1] * m[1] + m[2] * m[2];
@@ -1544,6 +1728,25 @@ export class GaussianSplattingMeshBase extends Mesh {
1544
1728
  this._shTextures.push(shTexture);
1545
1729
  }
1546
1730
  }
1731
+ if (this._needsRotationScaleTextures && this._rotationDataA) {
1732
+ if (this._rotationsATexture) {
1733
+ this._updateTextureFromData(this._rotationsATexture, this._rotationDataA, textureSize.x, 0, textureSize.y);
1734
+ this._updateTextureFromData(this._rotationsBTexture, this._rotationDataB, textureSize.x, 0, textureSize.y);
1735
+ this._updateTextureFromData(this._rotationScaleTexture, this._rotationScaleData, textureSize.x, 0, textureSize.y);
1736
+ }
1737
+ else {
1738
+ // Rotation textures not yet created (needsRotationScaleTextures was enabled after initial load).
1739
+ this._rotationsATexture = createTextureFromDataF16(this._rotationDataA, textureSize.x, textureSize.y, 5);
1740
+ this._rotationsBTexture = createTextureFromDataF16(this._rotationDataB, textureSize.x, textureSize.y, 5);
1741
+ this._rotationScaleTexture = createTextureFromDataF16(this._rotationScaleData, textureSize.x, textureSize.y, 5);
1742
+ this._rotationsATexture.wrapU = 0;
1743
+ this._rotationsATexture.wrapV = 0;
1744
+ this._rotationsBTexture.wrapU = 0;
1745
+ this._rotationsBTexture.wrapV = 0;
1746
+ this._rotationScaleTexture.wrapU = 0;
1747
+ this._rotationScaleTexture.wrapV = 0;
1748
+ }
1749
+ }
1547
1750
  this._onUpdateTextures(textureSize);
1548
1751
  this._postToWorker(true);
1549
1752
  }
@@ -1563,6 +1766,23 @@ export class GaussianSplattingMeshBase extends Mesh {
1563
1766
  this._shTextures.push(shTexture);
1564
1767
  }
1565
1768
  }
1769
+ if (this._needsRotationScaleTextures) {
1770
+ const rotDataA = this._rotationDataA ?? new Uint16Array(covA.length);
1771
+ const rotDataB = this._rotationDataB ?? new Uint16Array(covA.length);
1772
+ const rotScaleData = this._rotationScaleData ?? new Uint16Array(covA.length);
1773
+ this._rotationsATexture?.dispose();
1774
+ this._rotationsBTexture?.dispose();
1775
+ this._rotationScaleTexture?.dispose();
1776
+ this._rotationsATexture = createTextureFromDataF16(rotDataA, textureSize.x, textureSize.y, 5);
1777
+ this._rotationsBTexture = createTextureFromDataF16(rotDataB, textureSize.x, textureSize.y, 5);
1778
+ this._rotationScaleTexture = createTextureFromDataF16(rotScaleData, textureSize.x, textureSize.y, 5);
1779
+ this._rotationsATexture.wrapU = 0;
1780
+ this._rotationsATexture.wrapV = 0;
1781
+ this._rotationsBTexture.wrapU = 0;
1782
+ this._rotationsBTexture.wrapV = 0;
1783
+ this._rotationScaleTexture.wrapU = 0;
1784
+ this._rotationScaleTexture.wrapV = 0;
1785
+ }
1566
1786
  this._onUpdateTextures(textureSize);
1567
1787
  if (firstTime) {
1568
1788
  this._instantiateWorker();
@@ -1800,6 +2020,14 @@ export class GaussianSplattingMeshBase extends Mesh {
1800
2020
  this._updateTextureFromData(this._covariancesBTexture, covBView, textureSize.x, lineStart, lineCount);
1801
2021
  this._updateTextureFromData(this._centersTexture, centersView, textureSize.x, lineStart, lineCount);
1802
2022
  this._updateTextureFromData(this._colorsTexture, colorsView, textureSize.x, lineStart, lineCount);
2023
+ if (this._rotationsATexture && this._rotationDataA) {
2024
+ const rotAView = new Uint16Array(this._rotationDataA.buffer, texelStart * 4 * Uint16Array.BYTES_PER_ELEMENT, texelCount * 4);
2025
+ const rotBView = new Uint16Array(this._rotationDataB.buffer, texelStart * 4 * Uint16Array.BYTES_PER_ELEMENT, texelCount * 4);
2026
+ const rotScaleView = new Uint16Array(this._rotationScaleData.buffer, texelStart * 4 * Uint16Array.BYTES_PER_ELEMENT, texelCount * 4);
2027
+ this._updateTextureFromData(this._rotationsATexture, rotAView, textureSize.x, lineStart, lineCount);
2028
+ this._updateTextureFromData(this._rotationsBTexture, rotBView, textureSize.x, lineStart, lineCount);
2029
+ this._updateTextureFromData(this._rotationScaleTexture, rotScaleView, textureSize.x, lineStart, lineCount);
2030
+ }
1803
2031
  if (sh) {
1804
2032
  for (let i = 0; i < sh.length; i++) {
1805
2033
  const componentCount = 4;
@@ -1848,6 +2076,7 @@ export class GaussianSplattingMeshBase extends Mesh {
1848
2076
  }
1849
2077
  this._depthMix = e.data.depthMix;
1850
2078
  const cameraId = e.data.cameraId;
2079
+ const sortRequestId = e.data.sortRequestId;
1851
2080
  const indexMix = new Uint32Array(e.data.depthMix.buffer);
1852
2081
  if (this._splatIndex) {
1853
2082
  for (let j = 0; j < vertexCountPadded; j++) {
@@ -1869,6 +2098,7 @@ export class GaussianSplattingMeshBase extends Mesh {
1869
2098
  cameraViewInfos.mesh.thinInstanceSetBuffer("splatIndex", this._splatIndex, 16, false);
1870
2099
  cameraViewInfos.splatIndexBufferSet = true;
1871
2100
  }
2101
+ cameraViewInfos.sortAppliedId = sortRequestId;
1872
2102
  }
1873
2103
  this._canPostToWorker = true;
1874
2104
  this._readyToDisplay = true;
@@ -2056,13 +2286,14 @@ GaussianSplattingMeshBase._CreateWorker = function (self) {
2056
2286
  // update on view changed
2057
2287
  else {
2058
2288
  const cameraId = e.data.cameraId;
2289
+ const sortRequestId = e.data.sortRequestId;
2059
2290
  const globalWorldMatrix = e.data.worldMatrix;
2060
2291
  const cameraForward = e.data.cameraForward;
2061
2292
  const cameraPosition = e.data.cameraPosition;
2062
2293
  depthMix = e.data.depthMix;
2063
2294
  if (!positions || !cameraForward) {
2064
2295
  // Sort request arrived before positions were initialized — return the buffer unchanged so the main thread can unlock _canPostToWorker.
2065
- self.postMessage({ depthMix, cameraId }, [depthMix.buffer]);
2296
+ self.postMessage({ depthMix, cameraId, sortRequestId }, [depthMix.buffer]);
2066
2297
  return;
2067
2298
  }
2068
2299
  const vertexCountPadded = (positions.length / 4 + 15) & ~0xf;
@@ -2115,7 +2346,7 @@ GaussianSplattingMeshBase._CreateWorker = function (self) {
2115
2346
  // eslint-disable-next-line no-console
2116
2347
  console.error("Gaussian splat sort worker encountered an error (will retry next frame):", sortError);
2117
2348
  }
2118
- self.postMessage({ depthMix, cameraId }, [depthMix.buffer]);
2349
+ self.postMessage({ depthMix, cameraId, sortRequestId }, [depthMix.buffer]);
2119
2350
  }
2120
2351
  };
2121
2352
  };