@babylonjs/core 9.9.1 → 9.9.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 (105) hide show
  1. package/AudioV2/abstractAudio/audioEngineV2.d.ts +34 -1
  2. package/AudioV2/abstractAudio/audioEngineV2.js +54 -0
  3. package/AudioV2/abstractAudio/audioEngineV2.js.map +1 -1
  4. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.d.ts +12 -0
  5. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.js +18 -0
  6. package/AudioV2/abstractAudio/components/spatialAudioAttacherComponent.js.map +1 -1
  7. package/AudioV2/abstractAudio/index.d.ts +1 -0
  8. package/AudioV2/abstractAudio/index.js +1 -0
  9. package/AudioV2/abstractAudio/index.js.map +1 -1
  10. package/AudioV2/abstractAudio/pure.d.ts +1 -0
  11. package/AudioV2/abstractAudio/pure.js +1 -0
  12. package/AudioV2/abstractAudio/pure.js.map +1 -1
  13. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.d.ts +7 -1
  14. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.js +12 -0
  15. package/AudioV2/abstractAudio/subNodes/spatialAudioSubNode.js.map +1 -1
  16. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudio.d.ts +14 -0
  17. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudio.js.map +1 -1
  18. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudioListener.d.ts +4 -0
  19. package/AudioV2/abstractAudio/subProperties/abstractSpatialAudioListener.js.map +1 -1
  20. package/AudioV2/abstractAudio/subProperties/spatialAudio.d.ts +6 -0
  21. package/AudioV2/abstractAudio/subProperties/spatialAudio.js +12 -0
  22. package/AudioV2/abstractAudio/subProperties/spatialAudio.js.map +1 -1
  23. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.d.ts +2 -0
  24. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.js +4 -0
  25. package/AudioV2/abstractAudio/subProperties/spatialAudioListener.js.map +1 -1
  26. package/AudioV2/webAudio/webAudioEngine.js +2 -1
  27. package/AudioV2/webAudio/webAudioEngine.js.map +1 -1
  28. package/Engines/abstractEngine.pure.js +2 -2
  29. package/Engines/abstractEngine.pure.js.map +1 -1
  30. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.d.ts +3 -0
  31. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.js +16 -1
  32. package/FrameGraph/Node/Blocks/Layers/selectionOutlineLayerBlock.pure.js.map +1 -1
  33. package/FrameGraph/Tasks/Layers/selectionOutlineTask.d.ts +2 -1
  34. package/FrameGraph/Tasks/Layers/selectionOutlineTask.js +5 -2
  35. package/FrameGraph/Tasks/Layers/selectionOutlineTask.js.map +1 -1
  36. package/Gizmos/index.d.ts +1 -0
  37. package/Gizmos/index.js +1 -0
  38. package/Gizmos/index.js.map +1 -1
  39. package/Gizmos/pure.d.ts +1 -0
  40. package/Gizmos/pure.js +1 -0
  41. package/Gizmos/pure.js.map +1 -1
  42. package/Gizmos/spatialAudioGizmo.d.ts +55 -0
  43. package/Gizmos/spatialAudioGizmo.js +151 -0
  44. package/Gizmos/spatialAudioGizmo.js.map +1 -0
  45. package/Layers/selectionOutlineLayer.pure.d.ts +9 -2
  46. package/Layers/selectionOutlineLayer.pure.js +29 -6
  47. package/Layers/selectionOutlineLayer.pure.js.map +1 -1
  48. package/Layers/thinEffectLayer.d.ts +1 -0
  49. package/Layers/thinEffectLayer.js +7 -4
  50. package/Layers/thinEffectLayer.js.map +1 -1
  51. package/Layers/thinSelectionOutlineLayer.d.ts +17 -3
  52. package/Layers/thinSelectionOutlineLayer.js +82 -17
  53. package/Layers/thinSelectionOutlineLayer.js.map +1 -1
  54. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.d.ts +5 -0
  55. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js +54 -1
  56. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js.map +1 -1
  57. package/Materials/PBR/openpbrMaterial.pure.d.ts +13 -0
  58. package/Materials/PBR/openpbrMaterial.pure.js +17 -0
  59. package/Materials/PBR/openpbrMaterial.pure.js.map +1 -1
  60. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.d.ts +2 -1
  61. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.js +3 -2
  62. package/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.js.map +1 -1
  63. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.d.ts +32 -2
  64. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js +180 -22
  65. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js.map +1 -1
  66. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +54 -10
  67. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +130 -13
  68. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
  69. package/Meshes/abstractMesh.pure.d.ts +5 -1
  70. package/Meshes/abstractMesh.pure.js +92 -2
  71. package/Meshes/abstractMesh.pure.js.map +1 -1
  72. package/Meshes/thinInstanceMesh.pure.js +143 -2
  73. package/Meshes/thinInstanceMesh.pure.js.map +1 -1
  74. package/Meshes/thinInstanceMesh.types.d.ts +2 -1
  75. package/Meshes/thinInstanceMesh.types.js.map +1 -1
  76. package/Misc/tools.pure.js +1 -1
  77. package/Misc/tools.pure.js.map +1 -1
  78. package/Rendering/IBLShadows/iblShadowsRenderPipeline.pure.js +12 -1
  79. package/Rendering/IBLShadows/iblShadowsRenderPipeline.pure.js.map +1 -1
  80. package/Rendering/geometryBufferRenderer.pure.js +8 -0
  81. package/Rendering/geometryBufferRenderer.pure.js.map +1 -1
  82. package/Rendering/objectRenderer.js +4 -2
  83. package/Rendering/objectRenderer.js.map +1 -1
  84. package/Shaders/ShadersInclude/gaussianSplatting.js +54 -5
  85. package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
  86. package/Shaders/gaussianSplatting.vertex.js +14 -3
  87. package/Shaders/gaussianSplatting.vertex.js.map +1 -1
  88. package/Shaders/gaussianSplattingVoxel.vertex.js +1 -0
  89. package/Shaders/gaussianSplattingVoxel.vertex.js.map +1 -1
  90. package/Shaders/selectionOutline.fragment.js +16 -4
  91. package/Shaders/selectionOutline.fragment.js.map +1 -1
  92. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +56 -5
  93. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
  94. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js +2 -3
  95. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js.map +1 -1
  96. package/ShadersWGSL/gaussianSplatting.vertex.js +14 -3
  97. package/ShadersWGSL/gaussianSplatting.vertex.js.map +1 -1
  98. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js +1 -0
  99. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js.map +1 -1
  100. package/ShadersWGSL/selectionOutline.fragment.js +14 -2
  101. package/ShadersWGSL/selectionOutline.fragment.js.map +1 -1
  102. package/XR/features/WebXRBodyTracking.pure.d.ts +16 -0
  103. package/XR/features/WebXRBodyTracking.pure.js +167 -30
  104. package/XR/features/WebXRBodyTracking.pure.js.map +1 -1
  105. package/package.json +1 -1
@@ -19,8 +19,9 @@ export declare class GaussianSplattingCompoundMesh extends GaussianSplattingMesh
19
19
  * @param url optional URL to load a Gaussian Splatting file from
20
20
  * @param scene the hosting scene
21
21
  * @param keepInRam whether to keep the raw splat data in RAM after uploading to GPU
22
+ * @param needsRotationScaleTextures generate rotation and scale matrix textures required for voxel-based IBL shadows
22
23
  */
23
- constructor(name: string, url?: Nullable<string>, scene?: Nullable<Scene>, keepInRam?: boolean);
24
+ constructor(name: string, url?: Nullable<string>, scene?: Nullable<Scene>, keepInRam?: boolean, needsRotationScaleTextures?: boolean);
24
25
  /**
25
26
  * Add another mesh to this compound mesh as a new part.
26
27
  * The source mesh's splat data is read directly and copied into the compound's retained source buffers.
@@ -17,9 +17,10 @@ export class GaussianSplattingCompoundMesh extends GaussianSplattingMesh {
17
17
  * @param url optional URL to load a Gaussian Splatting file from
18
18
  * @param scene the hosting scene
19
19
  * @param keepInRam whether to keep the raw splat data in RAM after uploading to GPU
20
+ * @param needsRotationScaleTextures generate rotation and scale matrix textures required for voxel-based IBL shadows
20
21
  */
21
- constructor(name, url = null, scene = null, keepInRam = false) {
22
- super(name, url, scene, keepInRam);
22
+ constructor(name, url = null, scene = null, keepInRam = false, needsRotationScaleTextures = false) {
23
+ super(name, url, scene, keepInRam, needsRotationScaleTextures);
23
24
  }
24
25
  /**
25
26
  * Add another mesh to this compound mesh as a new part.
@@ -1 +1 @@
1
- {"version":3,"file":"gaussianSplattingCompoundMesh.pure.js","sourceRoot":"","sources":["../../../../../dev/core/src/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAI7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,OAAO,6BAA8B,SAAQ,qBAAqB;IACpE;;;;;;OAMG;IACH,YAAY,IAAY,EAAE,MAAwB,IAAI,EAAE,QAAyB,IAAI,EAAE,YAAqB,KAAK;QAC7G,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACa,OAAO,CAAC,KAA4B,EAAE,eAAwB,IAAI;QAC9E,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,MAA+B,EAAE,gBAAyB,IAAI;QAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACtE,OAAO,WAAW,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACa,UAAU,CAAC,KAAa;QACpC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACa,SAAS,CAAC,sBAA2B,EAAE,EAAE,WAAmB,QAAQ;QAChF,mBAAmB,GAAG,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACrE,mDAAmD;QACnD,2FAA2F;QAC3F,6EAA6E;QAC7E,mBAAmB,CAAC,WAAW,GAAG,IAAI,CAAC;QACvC,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAU,KAAK,CAAC,UAAe,EAAE,KAAY;QACtD,OAAO,qBAAqB,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE,6BAA6B,CAAC,CAAC;IAClG,CAAC;CACJ;AAED,IAAI,WAAW,GAAG,KAAK,CAAC;AACxB;;;GAGG;AACH,MAAM,UAAU,qCAAqC;IACjD,IAAI,WAAW,EAAE,CAAC;QACd,OAAO;IACX,CAAC;IACD,WAAW,GAAG,IAAI,CAAC;IAEnB,IAAI,CAAC,oCAAoC,GAAG,6BAA6B,CAAC,KAAK,CAAC;AACpF,CAAC","sourcesContent":["/** This file must only contain pure code and pure imports */\r\n\r\nimport { type Nullable } from \"core/types\";\r\nimport { type Scene } from \"core/scene.pure\";\r\nimport { GaussianSplattingMesh } from \"./gaussianSplattingMesh.pure\";\r\nimport { type GaussianSplattingPartProxyMesh } from \"./gaussianSplattingPartProxyMesh.pure\";\r\nimport { Mesh } from \"../mesh.pure\";\r\n\r\n/**\r\n * Class used to compose multiple Gaussian Splatting meshes into a single draw call,\r\n * with per-part world-matrix and visibility control via addPart/addParts/removePart.\r\n *\r\n * This is the recommended class for multi-part Gaussian Splatting use cases.\r\n *\r\n * Next major version: the compound mesh API (addPart/addParts/removePart) will\r\n * move exclusively to this class and will be removed from GaussianSplattingMesh.\r\n */\r\nexport class GaussianSplattingCompoundMesh extends GaussianSplattingMesh {\r\n /**\r\n * Creates a new GaussianSplattingCompoundMesh\r\n * @param name the name of the mesh\r\n * @param url optional URL to load a Gaussian Splatting file from\r\n * @param scene the hosting scene\r\n * @param keepInRam whether to keep the raw splat data in RAM after uploading to GPU\r\n */\r\n constructor(name: string, url: Nullable<string> = null, scene: Nullable<Scene> = null, keepInRam: boolean = false) {\r\n super(name, url, scene, keepInRam);\r\n }\r\n\r\n /**\r\n * Add another mesh to this compound mesh as a new part.\r\n * The source mesh's splat data is read directly and copied into the compound's retained source buffers.\r\n * @param other - The other mesh to add. Must be fully loaded before calling this method.\r\n * @param disposeOther - Whether to dispose the other mesh after adding it.\r\n * @returns a placeholder mesh that can be used to manipulate the part transform\r\n */\r\n public override addPart(other: GaussianSplattingMesh, disposeOther: boolean = true): GaussianSplattingPartProxyMesh {\r\n return super.addPart(other, disposeOther);\r\n }\r\n\r\n /**\r\n * Add multiple meshes to this compound mesh as new parts in a single operation.\r\n * Splat data is written into texture arrays while the compound refreshes its retained merged source buffers.\r\n * @param others - The meshes to add. Each must be fully loaded and must not be a compound.\r\n * @param disposeOthers - Whether to dispose the other meshes after adding them.\r\n * @returns an array of placeholder meshes that can be used to manipulate the part transforms\r\n */\r\n public addParts(others: GaussianSplattingMesh[], disposeOthers: boolean = true): GaussianSplattingPartProxyMesh[] {\r\n if (others.length === 0) {\r\n return [];\r\n }\r\n const { proxyMeshes } = this._addPartsInternal(others, disposeOthers);\r\n return proxyMeshes;\r\n }\r\n\r\n /**\r\n * Remove a part from this compound mesh.\r\n * The remaining parts are rebuilt directly from the compound mesh's retained source buffers.\r\n * @param index - The index of the part to remove\r\n */\r\n public override removePart(index: number): void {\r\n super.removePart(index);\r\n }\r\n\r\n /**\r\n * Serialize current GaussianSplattingMesh\r\n * @param serializationObject defines the object which will receive the serialization data\r\n * @param encoding the encoding of binary data, defaults to base64 for json serialize,\r\n * kept for future internal use like cloning where base64 encoding wastes cycles and memory\r\n * @returns the serialized object\r\n */\r\n public override serialize(serializationObject: any = {}, encoding: string = \"base64\"): any {\r\n serializationObject = super.serialize(serializationObject, encoding);\r\n // Note here, the getClassName() is not overridden,\r\n // as a lot of code currently depend on `getClassName() === \"GaussianSplattingMesh\"` check,\r\n // to not break those code, serialization uses `_isCompound` to mark the type\r\n serializationObject._isCompound = true;\r\n return serializationObject;\r\n }\r\n\r\n /**\r\n * Parses a serialized GaussianSplattingCompoundMesh\r\n * @param parsedMesh the serialized mesh\r\n * @param scene the scene to create the GaussianSplattingCompoundMesh in\r\n * @returns the created GaussianSplattingCompoundMesh\r\n */\r\n public static override Parse(parsedMesh: any, scene: Scene): GaussianSplattingCompoundMesh {\r\n return GaussianSplattingMesh._ParseInternal(parsedMesh, scene, GaussianSplattingCompoundMesh);\r\n }\r\n}\r\n\r\nlet _Registered = false;\r\n/**\r\n * Register side effects for gaussianSplattingCompoundMesh.\r\n * Safe to call multiple times; only the first call has an effect.\r\n */\r\nexport function RegisterGaussianSplattingCompoundMesh(): void {\r\n if (_Registered) {\r\n return;\r\n }\r\n _Registered = true;\r\n\r\n Mesh._GaussianSplattingCompoundMeshParser = GaussianSplattingCompoundMesh.Parse;\r\n}\r\n"]}
1
+ {"version":3,"file":"gaussianSplattingCompoundMesh.pure.js","sourceRoot":"","sources":["../../../../../dev/core/src/Meshes/GaussianSplatting/gaussianSplattingCompoundMesh.pure.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAI7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,OAAO,6BAA8B,SAAQ,qBAAqB;IACpE;;;;;;;OAOG;IACH,YAAY,IAAY,EAAE,MAAwB,IAAI,EAAE,QAAyB,IAAI,EAAE,YAAqB,KAAK,EAAE,6BAAsC,KAAK;QAC1J,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,0BAA0B,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;OAMG;IACa,OAAO,CAAC,KAA4B,EAAE,eAAwB,IAAI;QAC9E,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,MAA+B,EAAE,gBAAyB,IAAI;QAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACtE,OAAO,WAAW,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACa,UAAU,CAAC,KAAa;QACpC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACa,SAAS,CAAC,sBAA2B,EAAE,EAAE,WAAmB,QAAQ;QAChF,mBAAmB,GAAG,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACrE,mDAAmD;QACnD,2FAA2F;QAC3F,6EAA6E;QAC7E,mBAAmB,CAAC,WAAW,GAAG,IAAI,CAAC;QACvC,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAU,KAAK,CAAC,UAAe,EAAE,KAAY;QACtD,OAAO,qBAAqB,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE,6BAA6B,CAAC,CAAC;IAClG,CAAC;CACJ;AAED,IAAI,WAAW,GAAG,KAAK,CAAC;AACxB;;;GAGG;AACH,MAAM,UAAU,qCAAqC;IACjD,IAAI,WAAW,EAAE,CAAC;QACd,OAAO;IACX,CAAC;IACD,WAAW,GAAG,IAAI,CAAC;IAEnB,IAAI,CAAC,oCAAoC,GAAG,6BAA6B,CAAC,KAAK,CAAC;AACpF,CAAC","sourcesContent":["/** This file must only contain pure code and pure imports */\r\n\r\nimport { type Nullable } from \"core/types\";\r\nimport { type Scene } from \"core/scene.pure\";\r\nimport { GaussianSplattingMesh } from \"./gaussianSplattingMesh.pure\";\r\nimport { type GaussianSplattingPartProxyMesh } from \"./gaussianSplattingPartProxyMesh.pure\";\r\nimport { Mesh } from \"../mesh.pure\";\r\n\r\n/**\r\n * Class used to compose multiple Gaussian Splatting meshes into a single draw call,\r\n * with per-part world-matrix and visibility control via addPart/addParts/removePart.\r\n *\r\n * This is the recommended class for multi-part Gaussian Splatting use cases.\r\n *\r\n * Next major version: the compound mesh API (addPart/addParts/removePart) will\r\n * move exclusively to this class and will be removed from GaussianSplattingMesh.\r\n */\r\nexport class GaussianSplattingCompoundMesh extends GaussianSplattingMesh {\r\n /**\r\n * Creates a new GaussianSplattingCompoundMesh\r\n * @param name the name of the mesh\r\n * @param url optional URL to load a Gaussian Splatting file from\r\n * @param scene the hosting scene\r\n * @param keepInRam whether to keep the raw splat data in RAM after uploading to GPU\r\n * @param needsRotationScaleTextures generate rotation and scale matrix textures required for voxel-based IBL shadows\r\n */\r\n constructor(name: string, url: Nullable<string> = null, scene: Nullable<Scene> = null, keepInRam: boolean = false, needsRotationScaleTextures: boolean = false) {\r\n super(name, url, scene, keepInRam, needsRotationScaleTextures);\r\n }\r\n\r\n /**\r\n * Add another mesh to this compound mesh as a new part.\r\n * The source mesh's splat data is read directly and copied into the compound's retained source buffers.\r\n * @param other - The other mesh to add. Must be fully loaded before calling this method.\r\n * @param disposeOther - Whether to dispose the other mesh after adding it.\r\n * @returns a placeholder mesh that can be used to manipulate the part transform\r\n */\r\n public override addPart(other: GaussianSplattingMesh, disposeOther: boolean = true): GaussianSplattingPartProxyMesh {\r\n return super.addPart(other, disposeOther);\r\n }\r\n\r\n /**\r\n * Add multiple meshes to this compound mesh as new parts in a single operation.\r\n * Splat data is written into texture arrays while the compound refreshes its retained merged source buffers.\r\n * @param others - The meshes to add. Each must be fully loaded and must not be a compound.\r\n * @param disposeOthers - Whether to dispose the other meshes after adding them.\r\n * @returns an array of placeholder meshes that can be used to manipulate the part transforms\r\n */\r\n public addParts(others: GaussianSplattingMesh[], disposeOthers: boolean = true): GaussianSplattingPartProxyMesh[] {\r\n if (others.length === 0) {\r\n return [];\r\n }\r\n const { proxyMeshes } = this._addPartsInternal(others, disposeOthers);\r\n return proxyMeshes;\r\n }\r\n\r\n /**\r\n * Remove a part from this compound mesh.\r\n * The remaining parts are rebuilt directly from the compound mesh's retained source buffers.\r\n * @param index - The index of the part to remove\r\n */\r\n public override removePart(index: number): void {\r\n super.removePart(index);\r\n }\r\n\r\n /**\r\n * Serialize current GaussianSplattingMesh\r\n * @param serializationObject defines the object which will receive the serialization data\r\n * @param encoding the encoding of binary data, defaults to base64 for json serialize,\r\n * kept for future internal use like cloning where base64 encoding wastes cycles and memory\r\n * @returns the serialized object\r\n */\r\n public override serialize(serializationObject: any = {}, encoding: string = \"base64\"): any {\r\n serializationObject = super.serialize(serializationObject, encoding);\r\n // Note here, the getClassName() is not overridden,\r\n // as a lot of code currently depend on `getClassName() === \"GaussianSplattingMesh\"` check,\r\n // to not break those code, serialization uses `_isCompound` to mark the type\r\n serializationObject._isCompound = true;\r\n return serializationObject;\r\n }\r\n\r\n /**\r\n * Parses a serialized GaussianSplattingCompoundMesh\r\n * @param parsedMesh the serialized mesh\r\n * @param scene the scene to create the GaussianSplattingCompoundMesh in\r\n * @returns the created GaussianSplattingCompoundMesh\r\n */\r\n public static override Parse(parsedMesh: any, scene: Scene): GaussianSplattingCompoundMesh {\r\n return GaussianSplattingMesh._ParseInternal(parsedMesh, scene, GaussianSplattingCompoundMesh);\r\n }\r\n}\r\n\r\nlet _Registered = false;\r\n/**\r\n * Register side effects for gaussianSplattingCompoundMesh.\r\n * Safe to call multiple times; only the first call has an effect.\r\n */\r\nexport function RegisterGaussianSplattingCompoundMesh(): void {\r\n if (_Registered) {\r\n return;\r\n }\r\n _Registered = true;\r\n\r\n Mesh._GaussianSplattingCompoundMeshParser = GaussianSplattingCompoundMesh.Parse;\r\n}\r\n"]}
@@ -1,12 +1,14 @@
1
1
  /** This file must only contain pure code and pure imports */
2
2
  import { type Nullable } from "../../types.js";
3
3
  import { type Scene } from "../../scene.pure.js";
4
- import { type Matrix, type Vector2 } from "../../Maths/math.vector.js";
4
+ import { Matrix, Vector3 } from "../../Maths/math.vector.pure.js";
5
+ import { type Vector2 } from "../../Maths/math.vector.js";
5
6
  import { type Effect } from "../../Materials/effect.pure.js";
6
7
  import { GaussianSplattingMeshBase } from "./gaussianSplattingMeshBase.pure.js";
7
8
  import { GaussianSplattingPartProxyMesh } from "./gaussianSplattingPartProxyMesh.pure.js";
8
- import { type BoundingInfo } from "../../Culling/boundingInfo.js";
9
+ import { BoundingInfo } from "../../Culling/boundingInfo.js";
9
10
  import { type BaseTexture } from "../../Materials/Textures/baseTexture.pure.js";
11
+ import { type AbstractMesh } from "../abstractMesh.pure.js";
10
12
  interface IGaussianSplattingPartSource {
11
13
  name: string;
12
14
  _vertexCount: number;
@@ -29,6 +31,9 @@ export declare class GaussianSplattingMesh extends GaussianSplattingMeshBase {
29
31
  * Proxy meshes indexed by part index. Maintained in sync with _partMatrices.
30
32
  */
31
33
  private _partProxies;
34
+ /** Part 0 local-space AABB when owned directly (not proxied). Set on first addPart, cleared on dispose/reset. */
35
+ private _part0LocalMin;
36
+ private _part0LocalMax;
32
37
  /**
33
38
  * World matrices for each part, indexed by part index.
34
39
  */
@@ -70,6 +75,31 @@ export declare class GaussianSplattingMesh extends GaussianSplattingMeshBase {
70
75
  * @returns true when ready
71
76
  */
72
77
  isReady(completeCheck?: boolean): boolean;
78
+ /**
79
+ * Recomputes compound local-space bounds from part 0's stored AABB (if unproxied) plus all
80
+ * proxy world AABBs inverse-transformed to compound-local space. All 8 corners of each proxy
81
+ * AABB are transformed so the result is correct under non-identity compound rotation/scale.
82
+ */
83
+ private _updateBoundingInfoFromProxies;
84
+ /**
85
+ * Override for compound meshes: recomputes bounds from proxy world extents instead of
86
+ * local bounds × world matrix, which is wrong for proxied parts with independent transforms.
87
+ * @returns this mesh
88
+ */
89
+ _updateBoundingInfo(): AbstractMesh;
90
+ /**
91
+ * Replaces the base hierarchy bounds computation for compound meshes: computes world bounds
92
+ * from scratch by iterating part 0's local AABB and all proxy meshes, rather than delegating
93
+ * to the base _children traversal which never reaches proxies (they are not parented to the
94
+ * compound). Visibility per-part is respected; invisible parts are excluded.
95
+ * @param includeDescendants when true, includes descendants (default: true)
96
+ * @param predicate optional filter predicate
97
+ * @returns world-space min/max of the hierarchy bounding box
98
+ */
99
+ getHierarchyBoundingVectors(includeDescendants?: boolean, predicate?: Nullable<(abstractMesh: AbstractMesh) => boolean>): {
100
+ min: Vector3;
101
+ max: Vector3;
102
+ };
73
103
  /**
74
104
  * Disposes proxy meshes and clears part data in addition to the base class GPU resources.
75
105
  * @param doNotRecurse Set to true to not recurse into each children
@@ -1,12 +1,13 @@
1
1
  /** This file must only contain pure code and pure imports */
2
- import { Quaternion, Vector3 } from "../../Maths/math.vector.pure.js";
2
+ import { Matrix, Quaternion, Vector3 } from "../../Maths/math.vector.pure.js";
3
3
  import { GetGaussianSplattingMaxPartCount } from "../../Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js";
4
- import { GaussianSplattingMeshBase } from "./gaussianSplattingMeshBase.pure.js";
4
+ import { GaussianSplattingMeshBase, AllocateShBuffers } from "./gaussianSplattingMeshBase.pure.js";
5
5
  import { RawTexture } from "../../Materials/Textures/rawTexture.js";
6
6
 
7
7
  import { DecodeBase64ToBinary, EncodeArrayBufferToBase64 } from "../../Misc/stringTools.js";
8
8
  import { Mesh } from "../mesh.pure.js";
9
9
  import { GaussianSplattingPartProxyMesh } from "./gaussianSplattingPartProxyMesh.pure.js";
10
+ import { BoundingInfo } from "../../Culling/boundingInfo.js";
10
11
  const _GaussianSplattingBytesPerSplat = 32;
11
12
  const _GaussianSplattingBytesPerShTexel = 16;
12
13
  /**
@@ -78,6 +79,9 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
78
79
  * Proxy meshes indexed by part index. Maintained in sync with _partMatrices.
79
80
  */
80
81
  this._partProxies = [];
82
+ /** Part 0 local-space AABB when owned directly (not proxied). Set on first addPart, cleared on dispose/reset. */
83
+ this._part0LocalMin = null;
84
+ this._part0LocalMax = null;
81
85
  /**
82
86
  * World matrices for each part, indexed by part index.
83
87
  */
@@ -123,6 +127,101 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
123
127
  }
124
128
  return super.isReady(completeCheck);
125
129
  }
130
+ /**
131
+ * Recomputes compound local-space bounds from part 0's stored AABB (if unproxied) plus all
132
+ * proxy world AABBs inverse-transformed to compound-local space. All 8 corners of each proxy
133
+ * AABB are transformed so the result is correct under non-identity compound rotation/scale.
134
+ */
135
+ _updateBoundingInfoFromProxies() {
136
+ const compoundWorld = this.getWorldMatrix();
137
+ const invCompoundWorld = Matrix.Invert(compoundWorld);
138
+ const localMin = this._part0LocalMin ? this._part0LocalMin.clone() : new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
139
+ const localMax = this._part0LocalMax ? this._part0LocalMax.clone() : new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
140
+ const corner = new Vector3();
141
+ for (const proxy of this._partProxies) {
142
+ if (!proxy) {
143
+ continue;
144
+ }
145
+ // Proxies have no geometry — getHierarchyBoundingVectors returns sentinels. Use boundingBox directly.
146
+ proxy.computeWorldMatrix(false);
147
+ const bb = proxy.getBoundingInfo().boundingBox;
148
+ const wMin = bb.minimumWorld;
149
+ const wMax = bb.maximumWorld;
150
+ for (let b = 0; b < 8; b++) {
151
+ corner.set(b & 1 ? wMax.x : wMin.x, b & 2 ? wMax.y : wMin.y, b & 4 ? wMax.z : wMin.z);
152
+ Vector3.TransformCoordinatesToRef(corner, invCompoundWorld, corner);
153
+ localMin.minimizeInPlace(corner);
154
+ localMax.maximizeInPlace(corner);
155
+ }
156
+ }
157
+ if (localMin.x <= localMax.x) {
158
+ // Direct access avoids getBoundingInfo() → _updateBoundingInfo() recursion.
159
+ if (this._boundingInfo) {
160
+ this._boundingInfo.reConstruct(localMin, localMax, compoundWorld);
161
+ }
162
+ else {
163
+ this._boundingInfo = new BoundingInfo(localMin, localMax, compoundWorld);
164
+ }
165
+ this._cachedBoundingMin = localMin.clone();
166
+ this._cachedBoundingMax = localMax.clone();
167
+ }
168
+ }
169
+ /**
170
+ * Override for compound meshes: recomputes bounds from proxy world extents instead of
171
+ * local bounds × world matrix, which is wrong for proxied parts with independent transforms.
172
+ * @returns this mesh
173
+ */
174
+ _updateBoundingInfo() {
175
+ if (this.isCompound) {
176
+ this._updateBoundingInfoFromProxies();
177
+ this._updateSubMeshesBoundingInfo(this.worldMatrixFromCache);
178
+ return this;
179
+ }
180
+ return super._updateBoundingInfo();
181
+ }
182
+ /**
183
+ * Replaces the base hierarchy bounds computation for compound meshes: computes world bounds
184
+ * from scratch by iterating part 0's local AABB and all proxy meshes, rather than delegating
185
+ * to the base _children traversal which never reaches proxies (they are not parented to the
186
+ * compound). Visibility per-part is respected; invisible parts are excluded.
187
+ * @param includeDescendants when true, includes descendants (default: true)
188
+ * @param predicate optional filter predicate
189
+ * @returns world-space min/max of the hierarchy bounding box
190
+ */
191
+ getHierarchyBoundingVectors(includeDescendants = true, predicate = null) {
192
+ if (!this.isCompound) {
193
+ return super.getHierarchyBoundingVectors(includeDescendants, predicate);
194
+ }
195
+ // For compound meshes, compute visible-only world bounds from scratch so that
196
+ // invisible parts don't inflate the result (e.g. for voxelization scene bounds).
197
+ const min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
198
+ const max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
199
+ // Unproxied part 0: the compound mesh owns this geometry directly (no proxy node).
200
+ // Transform its local AABB to world space if visible.
201
+ if (this._part0LocalMin && (this._partVisibility[0] ?? 1.0) > 0) {
202
+ const wm = this.getWorldMatrix();
203
+ const lMin = this._part0LocalMin;
204
+ const lMax = this._part0LocalMax;
205
+ const corner = new Vector3();
206
+ for (let b = 0; b < 8; b++) {
207
+ corner.set(b & 1 ? lMax.x : lMin.x, b & 2 ? lMax.y : lMin.y, b & 4 ? lMax.z : lMin.z);
208
+ Vector3.TransformCoordinatesToRef(corner, wm, corner);
209
+ min.minimizeInPlace(corner);
210
+ max.maximizeInPlace(corner);
211
+ }
212
+ }
213
+ for (let i = 0; i < this._partProxies.length; i++) {
214
+ const proxy = this._partProxies[i];
215
+ if (!proxy || (this._partVisibility[i] ?? 1.0) === 0) {
216
+ continue;
217
+ }
218
+ proxy.computeWorldMatrix(false);
219
+ const bb = proxy.getBoundingInfo().boundingBox;
220
+ min.minimizeInPlace(bb.minimumWorld);
221
+ max.maximizeInPlace(bb.maximumWorld);
222
+ }
223
+ return { min, max };
224
+ }
126
225
  /**
127
226
  * Disposes proxy meshes and clears part data in addition to the base class GPU resources.
128
227
  * @param doNotRecurse Set to true to not recurse into each children
@@ -136,6 +235,8 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
136
235
  this._partMatrices = [];
137
236
  this._partVisibility = [];
138
237
  this._partIndicesTexture = null;
238
+ this._part0LocalMin = null;
239
+ this._part0LocalMax = null;
139
240
  super.dispose(doNotRecurse);
140
241
  }
141
242
  // ---------------------------------------------------------------------------
@@ -385,7 +486,7 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
385
486
  _vertexCount: proxy._vertexCount,
386
487
  _splatsData: splatBytes.subarray(splatByteOffset, splatByteOffset + splatByteLength),
387
488
  _shData: this._shData?.map((texture) => texture.subarray(shByteOffset, shByteOffset + shByteLength)) ?? null,
388
- _shDegree: this._shData?.length ?? 0,
489
+ _shDegree: this._shData ? this._shDegree : 0,
389
490
  isCompound: false,
390
491
  getWorldMatrix: () => proxy.getWorldMatrix(),
391
492
  getBoundingInfo: () => proxy.getBoundingInfo(),
@@ -417,10 +518,11 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
417
518
  this._shData = null;
418
519
  return;
419
520
  }
420
- const mergedShData = [];
421
- for (let textureIndex = 0; textureIndex < shDegree; textureIndex++) {
422
- mergedShData.push(new Uint8Array(totalCount * _GaussianSplattingBytesPerShTexel));
423
- }
521
+ // Each SH texture holds one texel per splat; each texel is _GaussianSplattingBytesPerShTexel
522
+ // bytes with one byte per scalar, so it carries that many scalars. Degree d has
523
+ // ((d+1)^2 - 1) higher-order coefficients × 3 RGB = total scalars per splat; divide by texel capacity.
524
+ const shTextureCount = Math.ceil((((shDegree + 1) * (shDegree + 1) - 1) * 3) / _GaussianSplattingBytesPerShTexel);
525
+ const mergedShData = AllocateShBuffers(shTextureCount, totalCount * _GaussianSplattingBytesPerShTexel);
424
526
  let shByteOffset = 0;
425
527
  if (this._shData && existingVertexCount > 0) {
426
528
  const existingShByteLength = existingVertexCount * _GaussianSplattingBytesPerShTexel;
@@ -476,16 +578,23 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
476
578
  const covA = new Uint16Array(textureLength * 4);
477
579
  const covB = new Uint16Array(covBSItemSize * textureLength);
478
580
  const colorArray = new Uint8Array(textureLength * 4);
479
- // Determine merged SH degree
480
- const hasSH = (splatCountA === 0 || this._shData !== null) && others.every((o) => o._shData !== null);
581
+ // Determine merged SH degree.
582
+ // hasSH is true when the merged result will carry SH:
583
+ // - Existing compound already has SH (_shDegree>0): preserve it even if new parts
584
+ // have no SH — their texel region is pre-filled with 128 (neutral) by AllocateShBuffers.
585
+ // - At least one new part carries SH: enable SH for the whole compound; existing
586
+ // parts that had no SH also get neutral fill.
587
+ // Deliberately excludes the case where the existing compound has no SH and no new part
588
+ // has SH either (shDegreeNew stays 0, no SH textures allocated).
589
+ const hasSH = this._shDegree > 0 || others.some((o) => o._shData !== null);
481
590
  const shDegreeNew = hasSH ? Math.max(this._shDegree, ...others.map((o) => o._shDegree)) : 0;
482
591
  let sh = undefined;
483
592
  if (hasSH && shDegreeNew > 0) {
484
- const bytesPerTexel = 16;
485
- sh = [];
486
- for (let i = 0; i < shDegreeNew; i++) {
487
- sh.push(new Uint8Array(textureLength * bytesPerTexel));
488
- }
593
+ // Each SH texture holds one texel per splat; each texel is _GaussianSplattingBytesPerShTexel
594
+ // bytes with one byte per scalar, so it carries that many scalars. Degree d has
595
+ // ((d+1)^2 - 1) higher-order coefficients × 3 RGB = total scalars per splat; divide by texel capacity.
596
+ const shTextureCount = Math.ceil((((shDegreeNew + 1) * (shDegreeNew + 1) - 1) * 3) / _GaussianSplattingBytesPerShTexel);
597
+ sh = AllocateShBuffers(shTextureCount, textureLength * _GaussianSplattingBytesPerShTexel);
489
598
  }
490
599
  // --- Incremental path: can we reuse the already-committed GPU region? ---
491
600
  const incremental = this._canReuseCachedData(splatCountA, totalCount);
@@ -559,10 +668,9 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
559
668
  this._makeSplat(i, fBufA, uBufA, covA, covB, colorArray, minimum, maximum, false);
560
669
  }
561
670
  if (sh && this._shData) {
562
- const bytesPerTexel = 16;
563
671
  for (let texIdx = 0; texIdx < sh.length; texIdx++) {
564
672
  if (texIdx < this._shData.length) {
565
- sh[texIdx].set(this._shData[texIdx].subarray(0, part0Count * bytesPerTexel), 0);
673
+ sh[texIdx].set(this._shData[texIdx].subarray(0, part0Count * _GaussianSplattingBytesPerShTexel), 0);
566
674
  }
567
675
  }
568
676
  }
@@ -597,10 +705,9 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
597
705
  this._makeSplat(i, fBufA, uBufA, covA, covB, colorArray, minimum, maximum, false);
598
706
  }
599
707
  if (sh && this._shData) {
600
- const bytesPerTexel = 16;
601
708
  for (let texIdx = 0; texIdx < sh.length; texIdx++) {
602
709
  if (texIdx < this._shData.length) {
603
- sh[texIdx].set(this._shData[texIdx].subarray(0, splatCountA * bytesPerTexel), 0);
710
+ sh[texIdx].set(this._shData[texIdx].subarray(0, splatCountA * _GaussianSplattingBytesPerShTexel), 0);
604
711
  }
605
712
  }
606
713
  }
@@ -612,6 +719,7 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
612
719
  // so _updateSubTextures does not upload stale zeros over those already-committed texels.
613
720
  // The base-class _updateData always re-processes from firstNewTexel for the same reason;
614
721
  // the compound path must do the same.
722
+ // Boundary-row SH is restored after _retainMergedPartData (see below), where _shData is ready.
615
723
  if (incremental) {
616
724
  const firstNewTexel = firstNewLine * textureSize.x;
617
725
  if (firstNewTexel < splatCountA) {
@@ -704,6 +812,39 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
704
812
  try {
705
813
  // --- Upload to GPU ---
706
814
  if (incremental) {
815
+ // Create missing SH GPU textures: either the compound just gained SH for the first
816
+ // time (_shTextures===null) or the degree increased (sh.length > _shTextures.length).
817
+ // Use _shData when available (contains correct merged values for all rows);
818
+ // fall back to sh[idx] (pre-filled with 128) when _shData is absent (keepInRam=false).
819
+ // _updateSubTextures will re-upload from firstNewLine, which is redundant but harmless.
820
+ if (sh && (!this._shTextures || sh.length > this._shTextures.length)) {
821
+ if (!this._shTextures) {
822
+ this._shTextures = [];
823
+ }
824
+ while (this._shTextures.length < sh.length) {
825
+ const idx = this._shTextures.length;
826
+ const shTexture = new RawTexture(null, textureSize.x, textureSize.y, 11, this._scene, false, false, 1, 7);
827
+ shTexture.wrapU = 0;
828
+ shTexture.wrapV = 0;
829
+ this._shTextures.push(shTexture);
830
+ const src = this._shData && idx < this._shData.length ? this._shData[idx] : sh[idx];
831
+ this._updateShTextureData(shTexture, src, textureSize.x, 0, textureSize.y);
832
+ }
833
+ }
834
+ // Restore boundary-row SH: sh is freshly filled with 128, and _updateSubTextures
835
+ // starts at firstNewLine — existing splats on that row need their values from _shData.
836
+ if (sh && this._shData) {
837
+ const firstNewTexel = firstNewLine * textureSize.x;
838
+ if (firstNewTexel < splatCountA) {
839
+ const byteStart = firstNewTexel * _GaussianSplattingBytesPerShTexel;
840
+ const byteEnd = splatCountA * _GaussianSplattingBytesPerShTexel;
841
+ for (let texIdx = 0; texIdx < sh.length; texIdx++) {
842
+ if (texIdx < this._shData.length) {
843
+ sh[texIdx].set(this._shData[texIdx].subarray(byteStart, byteEnd), byteStart);
844
+ }
845
+ }
846
+ }
847
+ }
707
848
  // Update the part-indices texture (handles both create and update-in-place).
708
849
  // _ensurePartIndicesTexture is a no-op when the texture already exists, so on the
709
850
  // second+ addPart the partIndices would be stale without this call.
@@ -713,11 +854,23 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
713
854
  else {
714
855
  this._updateTextures(covA, covB, colorArray, sh);
715
856
  }
716
- this.getBoundingInfo().reConstruct(minimum, maximum, this.getWorldMatrix());
717
857
  this.setEnabled(true);
718
- this._cachedBoundingMin = minimum.clone();
719
- this._cachedBoundingMax = maximum.clone();
720
858
  this._notifyWorkerNewData();
859
+ // Bounding info is updated via _updateBoundingInfoFromProxies (called below, after proxy
860
+ // world matrices are known), which needs part 0's local-space AABB as an input:
861
+ // • For unproxied part 0 (legacy layout A: compound loaded its own splat data before
862
+ // any addPart call, so no _partProxies[0]), capture the local-space AABB from the
863
+ // compound mesh's existing _boundingInfo — set when the mesh loaded its own data via
864
+ // URL/updateData — so _updateBoundingInfoFromProxies can include part 0's geometry.
865
+ // • For proxied part 0, skip — its bounds are already on the proxy's getBoundingInfo()
866
+ // and _updateBoundingInfoFromProxies picks it up there.
867
+ // Guard splatCountA > 0 avoids reading a stale bounding box on a fresh empty mesh.
868
+ // Guard !this._part0LocalMin ensures we only store once; subsequent addPart calls must
869
+ // not overwrite it, because by then _boundingInfo reflects the full merged dataset.
870
+ if (!this._partProxies[0] && splatCountA > 0 && !this._part0LocalMin) {
871
+ this._part0LocalMin = this.getBoundingInfo().minimum.clone();
872
+ this._part0LocalMax = this.getBoundingInfo().maximum.clone();
873
+ }
721
874
  // --- Create proxy meshes ---
722
875
  const proxyMeshes = [];
723
876
  for (let i = 0; i < others.length; i++) {
@@ -736,6 +889,8 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
736
889
  this._partProxies[newPartIndex] = proxyMesh;
737
890
  proxyMeshes.push(proxyMesh);
738
891
  }
892
+ // Update compound bounds now that all proxy world matrices are known.
893
+ this._updateBoundingInfoFromProxies();
739
894
  // Restore the rebuild gate and post the now-complete partMatrices in one message, then trigger a single sort pass.
740
895
  // This ensures the worker sees a consistent partMatrices array that matches the partIndices for every splat.
741
896
  if (needsWorkerGate) {
@@ -850,6 +1005,8 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
850
1005
  this._partVisibility = [];
851
1006
  this._cachedBoundingMin = null;
852
1007
  this._cachedBoundingMax = null;
1008
+ this._part0LocalMin = null;
1009
+ this._part0LocalMax = null;
853
1010
  this._splatsData = null;
854
1011
  this._shData = null;
855
1012
  this._shDegree = 0;
@@ -933,6 +1090,7 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
933
1090
  }
934
1091
  if (this._shData) {
935
1092
  serializationObject.shData = encoding === "base64" ? this._shData.map(EncodeArrayBufferToBase64) : this._shData;
1093
+ serializationObject.shDegree = this._shDegree;
936
1094
  }
937
1095
  if (this._partIndices) {
938
1096
  const compressedIndices = CompressPartIndices(this._partIndices.subarray(0, this._vertexCount));
@@ -984,7 +1142,7 @@ export class GaussianSplattingMesh extends GaussianSplattingMeshBase {
984
1142
  }
985
1143
  if (splatsData) {
986
1144
  const flipY = parsedMesh._flipY ?? false;
987
- mesh.updateData(splatsData, parsedShData, { flipY }, parsedPartIndices);
1145
+ mesh.updateData(splatsData, parsedShData, { flipY }, parsedPartIndices, parsedMesh.shDegree);
988
1146
  }
989
1147
  if (parsedMesh.partProxies) {
990
1148
  for (const serializedPart of parsedMesh.partProxies) {