@onerjs/core 8.54.3 → 8.54.5
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.
- package/DeviceInput/webDeviceInputSystem.js +4 -0
- package/DeviceInput/webDeviceInputSystem.js.map +1 -1
- package/Engines/WebGPU/Extensions/engine.cubeTexture.pure.js +1 -1
- package/Engines/WebGPU/Extensions/engine.cubeTexture.pure.js.map +1 -1
- package/Engines/WebGPU/Extensions/engine.rawTexture.pure.js +2 -2
- package/Engines/WebGPU/Extensions/engine.rawTexture.pure.js.map +1 -1
- package/Engines/WebGPU/webgpuCacheRenderPipeline.d.ts +1 -0
- package/Engines/WebGPU/webgpuCacheRenderPipeline.js +18 -4
- package/Engines/WebGPU/webgpuCacheRenderPipeline.js.map +1 -1
- package/Engines/WebGPU/webgpuCacheTextureView.d.ts +13 -0
- package/Engines/WebGPU/webgpuCacheTextureView.js +32 -0
- package/Engines/WebGPU/webgpuCacheTextureView.js.map +1 -0
- package/Engines/WebGPU/webgpuRenderTargetWrapper.d.ts +10 -0
- package/Engines/WebGPU/webgpuRenderTargetWrapper.js +10 -0
- package/Engines/WebGPU/webgpuRenderTargetWrapper.js.map +1 -1
- package/Engines/WebGPU/webgpuShaderProcessorsWGSL.pure.js +57 -4
- package/Engines/WebGPU/webgpuShaderProcessorsWGSL.pure.js.map +1 -1
- package/Engines/WebGPU/webgpuTextureManager.d.ts +1 -1
- package/Engines/WebGPU/webgpuTextureManager.js +4 -2
- package/Engines/WebGPU/webgpuTextureManager.js.map +1 -1
- package/Engines/abstractEngine.pure.js +2 -2
- package/Engines/abstractEngine.pure.js.map +1 -1
- package/Engines/thinNativeEngine.pure.d.ts +5 -0
- package/Engines/thinNativeEngine.pure.js +29 -6
- package/Engines/thinNativeEngine.pure.js.map +1 -1
- package/Engines/webgpuEngine.pure.d.ts +1 -0
- package/Engines/webgpuEngine.pure.js +21 -8
- package/Engines/webgpuEngine.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Math/flowGraphMathBlocks.pure.d.ts +95 -1
- package/FlowGraph/Blocks/Data/Math/flowGraphMathBlocks.pure.js +169 -4
- package/FlowGraph/Blocks/Data/Math/flowGraphMathBlocks.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Math/flowGraphMathCombineExtractBlocks.pure.d.ts +21 -6
- package/FlowGraph/Blocks/Data/Math/flowGraphMathCombineExtractBlocks.pure.js +28 -26
- package/FlowGraph/Blocks/Data/Math/flowGraphMathCombineExtractBlocks.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Math/flowGraphMatrixMathBlocks.pure.d.ts +30 -2
- package/FlowGraph/Blocks/Data/Math/flowGraphMatrixMathBlocks.pure.js +111 -29
- package/FlowGraph/Blocks/Data/Math/flowGraphMatrixMathBlocks.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Math/flowGraphVectorMathBlocks.pure.d.ts +56 -2
- package/FlowGraph/Blocks/Data/Math/flowGraphVectorMathBlocks.pure.js +99 -14
- package/FlowGraph/Blocks/Data/Math/flowGraphVectorMathBlocks.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.pure.js +10 -2
- package/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/Utils/flowGraphArrayIndexBlock.pure.js +21 -1
- package/FlowGraph/Blocks/Data/Utils/flowGraphArrayIndexBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Data/flowGraphCachedOperationBlock.d.ts +18 -0
- package/FlowGraph/Blocks/Data/flowGraphCachedOperationBlock.js +29 -2
- package/FlowGraph/Blocks/Data/flowGraphCachedOperationBlock.js.map +1 -1
- package/FlowGraph/Blocks/Event/flowGraphReceiveCustomEventBlock.pure.d.ts +15 -0
- package/FlowGraph/Blocks/Event/flowGraphReceiveCustomEventBlock.pure.js +44 -7
- package/FlowGraph/Blocks/Event/flowGraphReceiveCustomEventBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Event/flowGraphSceneReadyEventBlock.pure.d.ts +9 -0
- package/FlowGraph/Blocks/Event/flowGraphSceneReadyEventBlock.pure.js +9 -1
- package/FlowGraph/Blocks/Event/flowGraphSceneReadyEventBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.d.ts +7 -0
- package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.js +8 -1
- package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.d.ts +5 -0
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.js +8 -0
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.js.map +1 -0
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.pure.d.ts +37 -0
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.pure.js +44 -0
- package/FlowGraph/Blocks/Event/flowGraphStopEventPropagationBlock.pure.js.map +1 -0
- package/FlowGraph/Blocks/Event/index.d.ts +1 -0
- package/FlowGraph/Blocks/Event/index.js +1 -0
- package/FlowGraph/Blocks/Event/index.js.map +1 -1
- package/FlowGraph/Blocks/Event/pure.d.ts +1 -0
- package/FlowGraph/Blocks/Event/pure.js +1 -0
- package/FlowGraph/Blocks/Event/pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/Animation/flowGraphBezierCurveEasingBlock.pure.d.ts +4 -1
- package/FlowGraph/Blocks/Execution/Animation/flowGraphBezierCurveEasingBlock.pure.js +67 -2
- package/FlowGraph/Blocks/Execution/Animation/flowGraphBezierCurveEasingBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.d.ts +13 -0
- package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.js +102 -7
- package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/Animation/flowGraphStopAnimationBlock.pure.js +5 -1
- package/FlowGraph/Blocks/Execution/Animation/flowGraphStopAnimationBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphCancelDelayBlock.pure.js +5 -1
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphCancelDelayBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphForLoopBlock.pure.d.ts +11 -2
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphForLoopBlock.pure.js +21 -3
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphForLoopBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphSetDelayBlock.pure.js +25 -4
- package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphSetDelayBlock.pure.js.map +1 -1
- package/FlowGraph/Blocks/flowGraphBlockFactory.js +18 -0
- package/FlowGraph/Blocks/flowGraphBlockFactory.js.map +1 -1
- package/FlowGraph/Blocks/flowGraphBlockNames.d.ts +9 -0
- package/FlowGraph/Blocks/flowGraphBlockNames.js +9 -0
- package/FlowGraph/Blocks/flowGraphBlockNames.js.map +1 -1
- package/FlowGraph/CustomTypes/flowGraphMatrix.d.ts +17 -3
- package/FlowGraph/CustomTypes/flowGraphMatrix.js +27 -21
- package/FlowGraph/CustomTypes/flowGraphMatrix.js.map +1 -1
- package/FlowGraph/flowGraphContext.d.ts +23 -0
- package/FlowGraph/flowGraphContext.js +29 -0
- package/FlowGraph/flowGraphContext.js.map +1 -1
- package/FlowGraph/flowGraphCoordinator.d.ts +58 -1
- package/FlowGraph/flowGraphCoordinator.js +72 -0
- package/FlowGraph/flowGraphCoordinator.js.map +1 -1
- package/FlowGraph/flowGraphDelayRegistry.d.ts +31 -0
- package/FlowGraph/flowGraphDelayRegistry.js +50 -0
- package/FlowGraph/flowGraphDelayRegistry.js.map +1 -0
- package/FlowGraph/flowGraphHostResolver.d.ts +69 -0
- package/FlowGraph/flowGraphHostResolver.js +25 -0
- package/FlowGraph/flowGraphHostResolver.js.map +1 -0
- package/FlowGraph/flowGraphMath.d.ts +48 -1
- package/FlowGraph/flowGraphMath.js +169 -4
- package/FlowGraph/flowGraphMath.js.map +1 -1
- package/FlowGraph/flowGraphPathConverterComponent.d.ts +14 -3
- package/FlowGraph/flowGraphPathConverterComponent.js +110 -18
- package/FlowGraph/flowGraphPathConverterComponent.js.map +1 -1
- package/FlowGraph/flowGraphSignalConnection.pure.js +7 -1
- package/FlowGraph/flowGraphSignalConnection.pure.js.map +1 -1
- package/FlowGraph/index.d.ts +2 -0
- package/FlowGraph/index.js +2 -0
- package/FlowGraph/index.js.map +1 -1
- package/FlowGraph/pure.d.ts +2 -0
- package/FlowGraph/pure.js +2 -0
- package/FlowGraph/pure.js.map +1 -1
- package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.pure.js +3 -1
- package/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.pure.js.map +1 -1
- package/Maths/math.vector.pure.d.ts +10 -0
- package/Maths/math.vector.pure.js +13 -0
- package/Maths/math.vector.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.d.ts +216 -1
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js +714 -67
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +81 -4
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +487 -54
- package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.pure.d.ts +19 -0
- package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.pure.js +24 -0
- package/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh.pure.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js +48 -10
- package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js.map +1 -1
- package/Meshes/GreasedLine/greasedLineRibbonMesh.pure.js +5 -1
- package/Meshes/GreasedLine/greasedLineRibbonMesh.pure.js.map +1 -1
- package/Meshes/abstractMesh.pure.js +5 -3
- package/Meshes/abstractMesh.pure.js.map +1 -1
- package/Misc/greasedLineTools.js +1 -1
- package/Misc/greasedLineTools.js.map +1 -1
- package/Misc/tools.pure.js +14 -2
- package/Misc/tools.pure.js.map +1 -1
- package/Particles/gpuParticleSystem.pure.d.ts +66 -1
- package/Particles/gpuParticleSystem.pure.js +189 -49
- package/Particles/gpuParticleSystem.pure.js.map +1 -1
- package/Physics/v2/Plugins/havokPlugin.d.ts +15 -0
- package/Physics/v2/Plugins/havokPlugin.js +16 -6
- package/Physics/v2/Plugins/havokPlugin.js.map +1 -1
- package/Physics/v2/characterController.js +71 -19
- package/Physics/v2/characterController.js.map +1 -1
- package/ShadersWGSL/ShadersInclude/clusteredLightingCompute.d.ts +5 -0
- package/ShadersWGSL/ShadersInclude/clusteredLightingCompute.js +29 -0
- package/ShadersWGSL/ShadersInclude/clusteredLightingCompute.js.map +1 -0
- package/ShadersWGSL/ShadersInclude/lightFragment.js +2 -3
- package/ShadersWGSL/ShadersInclude/lightFragment.js.map +1 -1
- package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.d.ts +1 -0
- package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js +2 -17
- package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js.map +1 -1
- package/ShadersWGSL/ShadersInclude/pbrClusteredLightingFunctions.d.ts +6 -0
- package/ShadersWGSL/ShadersInclude/pbrClusteredLightingFunctions.js +123 -0
- package/ShadersWGSL/ShadersInclude/pbrClusteredLightingFunctions.js.map +1 -0
- package/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.d.ts +1 -1
- package/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.js +2 -111
- package/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.js.map +1 -1
- package/ShadersWGSL/background.fragment.js +2 -1
- package/ShadersWGSL/background.fragment.js.map +1 -1
- package/ShadersWGSL/default.fragment.js +2 -1
- package/ShadersWGSL/default.fragment.js.map +1 -1
- package/ShadersWGSL/openpbr.fragment.js +2 -1
- package/ShadersWGSL/openpbr.fragment.js.map +1 -1
- package/ShadersWGSL/pbr.fragment.js +2 -1
- package/ShadersWGSL/pbr.fragment.js.map +1 -1
- package/XR/features/Layers/WebXRWebGPUCompositionLayer.d.ts +3 -9
- package/XR/features/Layers/WebXRWebGPUCompositionLayer.js +4 -11
- package/XR/features/Layers/WebXRWebGPUCompositionLayer.js.map +1 -1
- package/XR/features/WebXRAbstractFeature.d.ts +9 -0
- package/XR/features/WebXRAbstractFeature.js +16 -0
- package/XR/features/WebXRAbstractFeature.js.map +1 -1
- package/XR/features/WebXRDepthSensing.pure.d.ts +7 -0
- package/XR/features/WebXRDepthSensing.pure.js +350 -39
- package/XR/features/WebXRDepthSensing.pure.js.map +1 -1
- package/XR/features/WebXRImageTracking.pure.js.map +1 -1
- package/XR/features/WebXRLayers.pure.d.ts +6 -5
- package/XR/features/WebXRLayers.pure.js +65 -8
- package/XR/features/WebXRLayers.pure.js.map +1 -1
- package/XR/features/WebXRRawCameraAccess.pure.js +9 -2
- package/XR/features/WebXRRawCameraAccess.pure.js.map +1 -1
- package/XR/features/WebXRSpaceWarp.pure.d.ts +0 -1
- package/XR/features/WebXRSpaceWarp.pure.js +13 -4
- package/XR/features/WebXRSpaceWarp.pure.js.map +1 -1
- package/XR/features/WebXRWalkingLocomotion.pure.js.map +1 -1
- package/XR/webXRCamera.js +27 -0
- package/XR/webXRCamera.js.map +1 -1
- package/XR/webXRFeatureWarningRegistry.d.ts +14 -0
- package/XR/webXRFeatureWarningRegistry.js +19 -0
- package/XR/webXRFeatureWarningRegistry.js.map +1 -0
- package/XR/webXRFeaturesManager.d.ts +5 -0
- package/XR/webXRFeaturesManager.js +23 -4
- package/XR/webXRFeaturesManager.js.map +1 -1
- package/XR/webXRGraphicsBinding.d.ts +17 -10
- package/XR/webXRGraphicsBinding.js +8 -5
- package/XR/webXRGraphicsBinding.js.map +1 -1
- package/XR/webXRSessionManager.d.ts +3 -5
- package/XR/webXRSessionManager.js +1 -3
- package/XR/webXRSessionManager.js.map +1 -1
- package/XR/webXRWebGPURenderTargetTextureProvider.d.ts +21 -5
- package/XR/webXRWebGPURenderTargetTextureProvider.js +38 -10
- package/XR/webXRWebGPURenderTargetTextureProvider.js.map +1 -1
- package/package.json +1 -1
- package/scene.pure.d.ts +7 -0
- package/scene.pure.js +8 -0
- package/scene.pure.js.map +1 -1
|
@@ -7,6 +7,8 @@ import { Logger } from "../../Misc/logger.js";
|
|
|
7
7
|
import { Observable } from "../../Misc/observable.js";
|
|
8
8
|
import { GaussianSplattingMaterial } from "../../Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js";
|
|
9
9
|
import { RawTexture } from "../../Materials/Textures/rawTexture.js";
|
|
10
|
+
import { MultiRenderTarget } from "../../Materials/Textures/multiRenderTarget.pure.js";
|
|
11
|
+
import { Color4 } from "../../Maths/math.color.pure.js";
|
|
10
12
|
|
|
11
13
|
import { ToHalfFloat } from "../../Misc/textureTools.js";
|
|
12
14
|
import { Scalar } from "../../Maths/math.scalar.js";
|
|
@@ -436,14 +438,34 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
436
438
|
* @param colors color texture
|
|
437
439
|
* @param splatPositions stride-4 CPU centers for depth sorting (length vertexCount*4)
|
|
438
440
|
* @param vertexCount number of splats addressable in the work buffer
|
|
441
|
+
* @param shTextures optional baked higher-order SH textures (packed-u32) produced by the work buffer's SH decode.
|
|
442
|
+
* When provided, the draw path lights the decoded splats with view-dependent SH (the non-SOG `shTexture0..N`
|
|
443
|
+
* path). `shDegree` sets both the max and active SH degree.
|
|
444
|
+
* @param shDegree SH degree of the baked SH textures (0 = none). Ignored when `shTextures` is empty/omitted.
|
|
445
|
+
* @param rotationTextures optional decoded rotation/scale textures ([rotA, rotB, rotScale]) produced by the work
|
|
446
|
+
* buffer's rotation decode. When provided, they drive the voxel-IBL rotation/scale samplers so the streamed
|
|
447
|
+
* splats participate in voxel-based IBL shadowing.
|
|
439
448
|
*/
|
|
440
|
-
_setExternalWorkBuffer(centers, covariancesA, covariancesB, colors, splatPositions, vertexCount) {
|
|
449
|
+
_setExternalWorkBuffer(centers, covariancesA, covariancesB, colors, splatPositions, vertexCount, shTextures, shDegree = 0, rotationTextures) {
|
|
441
450
|
this._covariancesATexture = covariancesA;
|
|
442
451
|
this._covariancesBTexture = covariancesB;
|
|
443
452
|
this._centersTexture = centers;
|
|
444
453
|
this._colorsTexture = colors;
|
|
445
454
|
this._splatPositions = splatPositions;
|
|
446
455
|
this._vertexCount = vertexCount;
|
|
456
|
+
if (shTextures && shTextures.length) {
|
|
457
|
+
this._shTextures = shTextures;
|
|
458
|
+
this._maxShDegree = shDegree;
|
|
459
|
+
this._shDegree = shDegree;
|
|
460
|
+
}
|
|
461
|
+
// Point the voxel-IBL rotation/scale samplers at the work buffer's decoded rotation textures ([rotA, rotB,
|
|
462
|
+
// rotScale]) so a standalone stream can cast/receive voxel-IBL shadows.
|
|
463
|
+
if (rotationTextures && rotationTextures.length >= 3) {
|
|
464
|
+
this._rotationsATexture = rotationTextures[0];
|
|
465
|
+
this._rotationsBTexture = rotationTextures[1];
|
|
466
|
+
this._rotationScaleTexture = rotationTextures[2];
|
|
467
|
+
this._needsRotationScaleTextures = true;
|
|
468
|
+
}
|
|
447
469
|
this._activeSplatRanges = null;
|
|
448
470
|
this._activeSplatRangeKey = "";
|
|
449
471
|
this._activeSplatRenderCount = 0;
|
|
@@ -731,9 +753,28 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
731
753
|
this._covariancesBTexture = null;
|
|
732
754
|
this._centersTexture = null;
|
|
733
755
|
this._colorsTexture = null;
|
|
756
|
+
// When a streaming part is reserved, the four core data textures (centers, covA, covB, colors) are backed
|
|
757
|
+
// by this MRT's attachments instead of standalone RawTextures, so a streaming engine can GPU-decode into
|
|
758
|
+
// the reserved region while static parts still CPU-upload into it. Null in the normal
|
|
759
|
+
// (non-streaming) path, which keeps using RawTextures unchanged.
|
|
760
|
+
this._mrtAtlas = null;
|
|
761
|
+
this._useMrtAtlas = false;
|
|
762
|
+
// Higher-order SH atlas. When a streaming part needs baked SH, `_shTextures` become the single
|
|
763
|
+
// attachments of these render-targetable integer MRTs (one per packed-u32 SH texture) so the streaming engine
|
|
764
|
+
// GPU-decodes SH into the reserved region while static SH parts still CPU-upload into them. Null in the normal
|
|
765
|
+
// path (SH stays plain RawTextures). One MRT per attachment keeps within WebGPU's per-sample byte budget.
|
|
766
|
+
this._shMrtAtlas = null;
|
|
767
|
+
this._useShMrtAtlas = false;
|
|
768
|
+
this._shMrtAtlasTextureCount = 0;
|
|
734
769
|
this._rotationsATexture = null;
|
|
735
770
|
this._rotationsBTexture = null;
|
|
736
771
|
this._rotationScaleTexture = null;
|
|
772
|
+
// Rotation/scale atlas. When a streaming part needs voxel-IBL rotation/scale, the three rotation
|
|
773
|
+
// textures become the attachments of this render-targetable half-float MRT so the streaming engine GPU-decodes
|
|
774
|
+
// rotation/scale into the reserved region while static parts still CPU-upload into it. Null in the normal path
|
|
775
|
+
// (rotation stays plain RawTextures created per-splat by `_makeSplat`).
|
|
776
|
+
this._rotMrtAtlas = null;
|
|
777
|
+
this._useRotMrtAtlas = false;
|
|
737
778
|
this._rotationDataA = null;
|
|
738
779
|
this._rotationDataB = null;
|
|
739
780
|
this._rotationScaleData = null;
|
|
@@ -773,6 +814,19 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
773
814
|
/** Fired after part-removal validation passes but before the mesh is rebuilt.
|
|
774
815
|
* Payload is the original (pre-removal) part index. */
|
|
775
816
|
this.onPartRemovedObservable = new Observable();
|
|
817
|
+
/**
|
|
818
|
+
* Fired just before an existing MRT-backed atlas is recreated to grow it (e.g. adding a part while a streaming
|
|
819
|
+
* part exists). Payload is the OLD atlas. Streaming parts subscribe to back up their GPU-only region before it
|
|
820
|
+
* is disposed. Only fires when growing an existing MRT atlas (not on first creation).
|
|
821
|
+
* @internal
|
|
822
|
+
*/
|
|
823
|
+
this._onBeforeAtlasRebuildObservable = new Observable();
|
|
824
|
+
/**
|
|
825
|
+
* Fired after the new (grown) MRT-backed atlas has been created and its CPU data uploaded. Payload is the NEW
|
|
826
|
+
* atlas. Streaming parts subscribe to rebind to it and restore their backed-up region.
|
|
827
|
+
* @internal
|
|
828
|
+
*/
|
|
829
|
+
this._onAfterAtlasRebuildObservable = new Observable();
|
|
776
830
|
/**
|
|
777
831
|
* Cosine value of the angle threshold to update view dependent splat sorting. Default is 0.0001.
|
|
778
832
|
*/
|
|
@@ -1887,18 +1941,49 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
1887
1941
|
* @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
|
|
1888
1942
|
*/
|
|
1889
1943
|
dispose(doNotRecurse) {
|
|
1890
|
-
this.
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1944
|
+
if (this._mrtAtlas) {
|
|
1945
|
+
// The four data textures are attachments of this MRT — dispose the MRT, not each attachment.
|
|
1946
|
+
this._mrtAtlas.dispose();
|
|
1947
|
+
this._mrtAtlas = null;
|
|
1948
|
+
this._covariancesATexture = this._covariancesBTexture = this._centersTexture = this._colorsTexture = null;
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
this._covariancesATexture?.dispose();
|
|
1952
|
+
this._covariancesBTexture?.dispose();
|
|
1953
|
+
this._centersTexture?.dispose();
|
|
1954
|
+
this._colorsTexture?.dispose();
|
|
1955
|
+
}
|
|
1956
|
+
if (this._shMrtAtlas) {
|
|
1957
|
+
// SH attachments belong to these MRTs — dispose the MRTs, not each attachment (which is _shTextures[k]
|
|
1958
|
+
// for k < _shMrtAtlas.length).
|
|
1959
|
+
for (const mrt of this._shMrtAtlas) {
|
|
1960
|
+
mrt.dispose();
|
|
1961
|
+
}
|
|
1962
|
+
// Defence in depth: free any _shTextures entries beyond the MRT-backed set (not MRT-owned, so the loop
|
|
1963
|
+
// above misses them). The reuse guard keeps the atlas sized correctly, so this is normally a no-op.
|
|
1964
|
+
if (this._shTextures) {
|
|
1965
|
+
for (let k = this._shMrtAtlas.length; k < this._shTextures.length; k++) {
|
|
1966
|
+
this._shTextures[k].dispose();
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
this._shMrtAtlas = null;
|
|
1970
|
+
}
|
|
1971
|
+
else if (this._shTextures) {
|
|
1895
1972
|
for (const shTexture of this._shTextures) {
|
|
1896
1973
|
shTexture.dispose();
|
|
1897
1974
|
}
|
|
1898
1975
|
}
|
|
1899
|
-
this.
|
|
1900
|
-
this.
|
|
1901
|
-
|
|
1976
|
+
this._shTextures = null;
|
|
1977
|
+
if (this._rotMrtAtlas) {
|
|
1978
|
+
// The three rotation textures are attachments of this MRT — dispose the MRT, not each attachment.
|
|
1979
|
+
this._rotMrtAtlas.dispose();
|
|
1980
|
+
this._rotMrtAtlas = null;
|
|
1981
|
+
}
|
|
1982
|
+
else {
|
|
1983
|
+
this._rotationsATexture?.dispose();
|
|
1984
|
+
this._rotationsBTexture?.dispose();
|
|
1985
|
+
this._rotationScaleTexture?.dispose();
|
|
1986
|
+
}
|
|
1902
1987
|
this._sogParams?.codebookTexture?.dispose();
|
|
1903
1988
|
this._rotationsATexture = null;
|
|
1904
1989
|
this._rotationsBTexture = null;
|
|
@@ -1994,8 +2079,10 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
1994
2079
|
* @param maximum - accumulated bounding maximum (updated in-place)
|
|
1995
2080
|
* @param flipY - whether to negate the Y position
|
|
1996
2081
|
* @param srcIndex - source splat index (defaults to dstIndex when omitted)
|
|
2082
|
+
* @param dstArrayIndex - index into the covA/covB/colorArray transients (defaults to dstIndex). Lets a streaming
|
|
2083
|
+
* write pass count-sized transients indexed from 0 while dstIndex still addresses the atlas-sized _splatPositions.
|
|
1997
2084
|
*/
|
|
1998
|
-
_makeSplat(dstIndex, fBuffer, uBuffer, covA, covB, colorArray, minimum, maximum, flipY, srcIndex = dstIndex) {
|
|
2085
|
+
_makeSplat(dstIndex, fBuffer, uBuffer, covA, covB, colorArray, minimum, maximum, flipY, srcIndex = dstIndex, dstArrayIndex = dstIndex) {
|
|
1999
2086
|
const matrixRotation = TmpVectors.Matrix[0];
|
|
2000
2087
|
const matrixScale = TmpVectors.Matrix[1];
|
|
2001
2088
|
const quaternion = TmpVectors.Quaternion[0];
|
|
@@ -2013,10 +2100,13 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2013
2100
|
quaternion.toRotationMatrix(matrixRotation);
|
|
2014
2101
|
Matrix.ScalingToRef(fBuffer[8 * srcIndex + 3 + 0] * 2, fBuffer[8 * srcIndex + 3 + 1] * 2, fBuffer[8 * srcIndex + 3 + 2] * 2, matrixScale);
|
|
2015
2102
|
if (this._needsRotationScaleTextures) {
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2103
|
+
// Sized to the atlas (_splatPositions), not covA: these persistent arrays are indexed by the global
|
|
2104
|
+
// dstIndex, so a count-sized covA transient must not shrink them.
|
|
2105
|
+
const rotLength = this._splatPositions.length;
|
|
2106
|
+
if (!this._rotationDataA || this._rotationDataA.length < rotLength) {
|
|
2107
|
+
this._rotationDataA = new Uint16Array(rotLength);
|
|
2108
|
+
this._rotationDataB = new Uint16Array(rotLength);
|
|
2109
|
+
this._rotationScaleData = new Uint16Array(rotLength);
|
|
2020
2110
|
}
|
|
2021
2111
|
const rotDataA = this._rotationDataA;
|
|
2022
2112
|
const rotDataB = this._rotationDataB;
|
|
@@ -2051,12 +2141,12 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2051
2141
|
}
|
|
2052
2142
|
this._splatPositions[4 * dstIndex + 3] = factor;
|
|
2053
2143
|
const transform = factor;
|
|
2054
|
-
covA[
|
|
2055
|
-
covA[
|
|
2056
|
-
covA[
|
|
2057
|
-
covA[
|
|
2058
|
-
covB[
|
|
2059
|
-
covB[
|
|
2144
|
+
covA[dstArrayIndex * 4 + 0] = ToHalfFloat(covariances[0] / transform);
|
|
2145
|
+
covA[dstArrayIndex * 4 + 1] = ToHalfFloat(covariances[1] / transform);
|
|
2146
|
+
covA[dstArrayIndex * 4 + 2] = ToHalfFloat(covariances[2] / transform);
|
|
2147
|
+
covA[dstArrayIndex * 4 + 3] = ToHalfFloat(covariances[3] / transform);
|
|
2148
|
+
covB[dstArrayIndex * covBSItemSize + 0] = ToHalfFloat(covariances[4] / transform);
|
|
2149
|
+
covB[dstArrayIndex * covBSItemSize + 1] = ToHalfFloat(covariances[5] / transform);
|
|
2060
2150
|
const c0 = covariances[0];
|
|
2061
2151
|
const c1 = covariances[1];
|
|
2062
2152
|
const c2 = covariances[2];
|
|
@@ -2072,10 +2162,10 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2072
2162
|
this._splatSizeMax = splatSize;
|
|
2073
2163
|
}
|
|
2074
2164
|
// colors
|
|
2075
|
-
colorArray[
|
|
2076
|
-
colorArray[
|
|
2077
|
-
colorArray[
|
|
2078
|
-
colorArray[
|
|
2165
|
+
colorArray[dstArrayIndex * 4 + 0] = uBuffer[32 * srcIndex + 24 + 0];
|
|
2166
|
+
colorArray[dstArrayIndex * 4 + 1] = uBuffer[32 * srcIndex + 24 + 1];
|
|
2167
|
+
colorArray[dstArrayIndex * 4 + 2] = uBuffer[32 * srcIndex + 24 + 2];
|
|
2168
|
+
colorArray[dstArrayIndex * 4 + 3] = uBuffer[32 * srcIndex + 24 + 3];
|
|
2079
2169
|
}
|
|
2080
2170
|
_onUpdateTextures(_textureSize) { }
|
|
2081
2171
|
/**
|
|
@@ -2106,6 +2196,137 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2106
2196
|
_setDelayedTextureUpdate(covA, covB, colorArray, sh) {
|
|
2107
2197
|
this._delayedTextureUpdate = { covA, covB, colors: colorArray, centers: this._splatPositions, sh };
|
|
2108
2198
|
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Creates (or recreates) the MRT-backed data atlas at the given size and points the four core data
|
|
2201
|
+
* textures at its attachments. The attachments use the same decoded GS layout as {@link GaussianSplattingWorkBuffer}
|
|
2202
|
+
* — [centers F32, covA HALF_FLOAT, covB HALF_FLOAT, colors U8], all RGBA — so a streaming engine can render
|
|
2203
|
+
* (decode/relayout) directly into the same textures the GS material samples, while static parts CPU-upload
|
|
2204
|
+
* into them via {@link _updateSubTextures}. Covariance B is RGBA here (its extra channels unused), so the
|
|
2205
|
+
* atlas forces {@link _useRGBACovariants}.
|
|
2206
|
+
* @param textureSize atlas dimensions (width, height)
|
|
2207
|
+
*/
|
|
2208
|
+
_createMrtAtlas(textureSize) {
|
|
2209
|
+
// covB carries only Sigma12/Sigma22 but MRT attachments are RGBA, so switch to the RGBA covariant layout.
|
|
2210
|
+
this._useRGBACovariants = true;
|
|
2211
|
+
const engine = this.getEngine();
|
|
2212
|
+
// No real GPU (NullEngine): skip MRT allocation. The reservation's part/index/range logic still runs;
|
|
2213
|
+
// texture population requires a real (WebGL2/WebGPU) backend.
|
|
2214
|
+
const updateEngine = this._getTextureDataUpdateEngine();
|
|
2215
|
+
if (!updateEngine._gl && !updateEngine.isWebGPU) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
const covType = engine.getCaps().textureHalfFloatRender ? 2 : 1;
|
|
2219
|
+
const mrt = new MultiRenderTarget(this.name + "_gsAtlas", { width: textureSize.x, height: textureSize.y }, 4, this._scene, {
|
|
2220
|
+
types: [1, covType, covType, 0],
|
|
2221
|
+
formats: [5, 5, 5, 5],
|
|
2222
|
+
samplingModes: [
|
|
2223
|
+
1,
|
|
2224
|
+
1,
|
|
2225
|
+
1,
|
|
2226
|
+
1,
|
|
2227
|
+
],
|
|
2228
|
+
generateDepthBuffer: false,
|
|
2229
|
+
generateDepthTexture: false,
|
|
2230
|
+
generateMipMaps: false,
|
|
2231
|
+
}, [this.name + "_gsCenters", this.name + "_gsCovA", this.name + "_gsCovB", this.name + "_gsColors"]);
|
|
2232
|
+
mrt.clearColor = new Color4(0, 0, 0, 0);
|
|
2233
|
+
mrt.renderList = [];
|
|
2234
|
+
// Suppress clearing so a streaming engine's decode passes ACCUMULATE into the atlas (each decode writes
|
|
2235
|
+
// only its region and must not wipe static parts or previously-decoded splats). All texels are defined by
|
|
2236
|
+
// the initial full CPU sub-upload below (the reserved region's arrays are zero => invisible padding).
|
|
2237
|
+
mrt.onClearObservable.add(() => { });
|
|
2238
|
+
// Render once so the attachment textures are really allocated on the GPU before the CPU sub-uploads
|
|
2239
|
+
// below (on WebGPU, writeTexture requires the destination texture to already exist).
|
|
2240
|
+
mrt.render();
|
|
2241
|
+
this._mrtAtlas = mrt;
|
|
2242
|
+
this._centersTexture = mrt.textures[0];
|
|
2243
|
+
this._covariancesATexture = mrt.textures[1];
|
|
2244
|
+
this._covariancesBTexture = mrt.textures[2];
|
|
2245
|
+
this._colorsTexture = mrt.textures[3];
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Creates the shared higher-order SH atlas: `_shMrtAtlasTextureCount` single-attachment integer render targets
|
|
2249
|
+
* (RGBA_INTEGER / UNSIGNED_INTEGER, packed-u32), sized to the whole atlas, so a streaming engine can GPU-decode
|
|
2250
|
+
* baked SH into the reserved region and static SH parts CPU-upload into the same attachments. `_shTextures` (the
|
|
2251
|
+
* draw path's `shTexture0..N` samplers) are the attachments. One MRT per attachment keeps within WebGPU's
|
|
2252
|
+
* per-sample color-attachment byte budget. No-op without a real GPU backend (mirrors {@link _createMrtAtlas}).
|
|
2253
|
+
* @param textureSize atlas dimensions (width, height)
|
|
2254
|
+
*/
|
|
2255
|
+
_createShMrtAtlas(textureSize) {
|
|
2256
|
+
const updateEngine = this._getTextureDataUpdateEngine();
|
|
2257
|
+
if (!updateEngine._gl && !updateEngine.isWebGPU) {
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
const count = this._shMrtAtlasTextureCount;
|
|
2261
|
+
if (count <= 0) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
const mrts = [];
|
|
2265
|
+
const shTextures = [];
|
|
2266
|
+
for (let k = 0; k < count; k++) {
|
|
2267
|
+
const name = `${this.name}_gsShAtlas${k}`;
|
|
2268
|
+
const mrt = new MultiRenderTarget(name, { width: textureSize.x, height: textureSize.y }, 1, this._scene, {
|
|
2269
|
+
types: [7],
|
|
2270
|
+
formats: [11],
|
|
2271
|
+
samplingModes: [1],
|
|
2272
|
+
generateDepthBuffer: false,
|
|
2273
|
+
generateDepthTexture: false,
|
|
2274
|
+
generateMipMaps: false,
|
|
2275
|
+
}, [name]);
|
|
2276
|
+
mrt.clearColor = new Color4(0, 0, 0, 0);
|
|
2277
|
+
mrt.renderList = [];
|
|
2278
|
+
// Accumulate: each SH decode writes only its region; the rest stays whatever was there (neutral for a
|
|
2279
|
+
// fresh atlas — 128 == 0 lighting is guaranteed by decoding every splat's region, incl. no-SH files).
|
|
2280
|
+
mrt.onClearObservable.add(() => { });
|
|
2281
|
+
const attachment = mrt.textures[0];
|
|
2282
|
+
attachment.wrapU = 0;
|
|
2283
|
+
attachment.wrapV = 0;
|
|
2284
|
+
// Allocate on the GPU before any CPU sub-upload (WebGPU writeTexture needs the resource to exist).
|
|
2285
|
+
mrt.render();
|
|
2286
|
+
mrts.push(mrt);
|
|
2287
|
+
shTextures.push(attachment);
|
|
2288
|
+
}
|
|
2289
|
+
this._shMrtAtlas = mrts;
|
|
2290
|
+
this._shTextures = shTextures;
|
|
2291
|
+
}
|
|
2292
|
+
/**
|
|
2293
|
+
* Creates the shared rotation/scale atlas: one 3-attachment half-float render target ([rotA, rotB, rotScale])
|
|
2294
|
+
* sized to the whole atlas, so a streaming engine can GPU-decode rotation/scale into the reserved region and
|
|
2295
|
+
* static parts CPU-upload into the same attachments. `_rotationsATexture`/`_rotationsBTexture`/
|
|
2296
|
+
* `_rotationScaleTexture` (the voxel-IBL samplers) become the three attachments. No-op without a real GPU
|
|
2297
|
+
* backend (mirrors {@link _createMrtAtlas}).
|
|
2298
|
+
* @param textureSize atlas dimensions (width, height)
|
|
2299
|
+
*/
|
|
2300
|
+
_createRotMrtAtlas(textureSize) {
|
|
2301
|
+
const updateEngine = this._getTextureDataUpdateEngine();
|
|
2302
|
+
if (!updateEngine._gl && !updateEngine.isWebGPU) {
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
const rotType = this.getEngine().getCaps().textureHalfFloatRender ? 2 : 1;
|
|
2306
|
+
const name = this.name + "_gsRotAtlas";
|
|
2307
|
+
const mrt = new MultiRenderTarget(name, { width: textureSize.x, height: textureSize.y }, 3, this._scene, {
|
|
2308
|
+
types: [rotType, rotType, rotType],
|
|
2309
|
+
formats: [5, 5, 5],
|
|
2310
|
+
samplingModes: [1, 1, 1],
|
|
2311
|
+
generateDepthBuffer: false,
|
|
2312
|
+
generateDepthTexture: false,
|
|
2313
|
+
generateMipMaps: false,
|
|
2314
|
+
}, [name + "A", name + "B", name + "Scale"]);
|
|
2315
|
+
mrt.clearColor = new Color4(0, 0, 0, 0);
|
|
2316
|
+
mrt.renderList = [];
|
|
2317
|
+
// Accumulate: each rotation decode writes only its region; the rest stays whatever was there.
|
|
2318
|
+
mrt.onClearObservable.add(() => { });
|
|
2319
|
+
for (const attachment of mrt.textures) {
|
|
2320
|
+
attachment.wrapU = 0;
|
|
2321
|
+
attachment.wrapV = 0;
|
|
2322
|
+
}
|
|
2323
|
+
// Allocate on the GPU before any CPU sub-upload (WebGPU writeTexture needs the resource to exist).
|
|
2324
|
+
mrt.render();
|
|
2325
|
+
this._rotMrtAtlas = mrt;
|
|
2326
|
+
this._rotationsATexture = mrt.textures[0];
|
|
2327
|
+
this._rotationsBTexture = mrt.textures[1];
|
|
2328
|
+
this._rotationScaleTexture = mrt.textures[2];
|
|
2329
|
+
}
|
|
2109
2330
|
// NB: partIndices is assumed to be padded to a round texture size
|
|
2110
2331
|
_updateTextures(covA, covB, colorArray, sh) {
|
|
2111
2332
|
const textureSize = this._getTextureSize(this._vertexCount);
|
|
@@ -2124,7 +2345,21 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2124
2345
|
};
|
|
2125
2346
|
const firstTime = this._covariancesATexture === null;
|
|
2126
2347
|
const textureSizeChanged = this._textureSize.y != textureSize.y;
|
|
2127
|
-
|
|
2348
|
+
// The render-backed SH atlas must have one attachment per SH texture the material samples, i.e. sized to the
|
|
2349
|
+
// compound's MERGED SH degree (`sh` spans static + streaming), not just the streaming parts' requested count —
|
|
2350
|
+
// otherwise a lower-degree stream mixed with a higher-degree static part (or a later higher-degree stream)
|
|
2351
|
+
// leaves the atlas undersized. `sh` is always present when a streaming SH part is live (its degree makes it so).
|
|
2352
|
+
if (this._useShMrtAtlas && sh) {
|
|
2353
|
+
this._shMrtAtlasTextureCount = sh.length;
|
|
2354
|
+
}
|
|
2355
|
+
// First streaming-part reservation must convert the existing RawTexture atlas to the MRT-backed atlas,
|
|
2356
|
+
// which requires the full (re)build branch even when the texture height is unchanged. Same for the SH atlas:
|
|
2357
|
+
// a streaming SH part must convert the SH textures to render-targetable integer MRTs (and recreate them when
|
|
2358
|
+
// the required attachment count changes).
|
|
2359
|
+
const needsMrtConversion = (this._useMrtAtlas && !this._mrtAtlas) ||
|
|
2360
|
+
(this._useShMrtAtlas && (!this._shMrtAtlas || this._shMrtAtlas.length !== this._shMrtAtlasTextureCount)) ||
|
|
2361
|
+
(this._useRotMrtAtlas && !this._rotMrtAtlas);
|
|
2362
|
+
if (!firstTime && !textureSizeChanged && !needsMrtConversion) {
|
|
2128
2363
|
this._setDelayedTextureUpdate(covA, covB, colorArray, sh);
|
|
2129
2364
|
const positions = Float32Array.from(this._splatPositions);
|
|
2130
2365
|
const vertexCount = this._vertexCount;
|
|
@@ -2133,8 +2368,9 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2133
2368
|
// Re-sync the active interval set in case the source splat count changed.
|
|
2134
2369
|
this._postIntervalsToWorker();
|
|
2135
2370
|
}
|
|
2136
|
-
// Handle SH textures in update path - create if they don't exist
|
|
2137
|
-
|
|
2371
|
+
// Handle SH textures in update path - create if they don't exist. Skip when the SH atlas is
|
|
2372
|
+
// render-backed (_useShMrtAtlas): those targets are created/managed by _createShMrtAtlas, not here.
|
|
2373
|
+
if (sh && !this._shTextures && !this._useShMrtAtlas) {
|
|
2138
2374
|
this._shTextures = [];
|
|
2139
2375
|
for (let textureIndex = 0; textureIndex < sh.length; textureIndex++) {
|
|
2140
2376
|
const shTexture = createEmptyTextureU32(textureSize.x, textureSize.y, 11);
|
|
@@ -2169,40 +2405,126 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2169
2405
|
else {
|
|
2170
2406
|
// Full rebuild: the texture size changed (or this is a size-changing reload), so the existing
|
|
2171
2407
|
// GPU textures cannot be reused. Dispose them before recreating to avoid leaking the old ones.
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
this.
|
|
2175
|
-
|
|
2408
|
+
// Growing an EXISTING MRT atlas: let streaming parts back up their GPU-only region from the old atlas
|
|
2409
|
+
// before it is disposed (they restore it into the new atlas via _onAfterAtlasRebuildObservable).
|
|
2410
|
+
const growingMrtAtlas = this._useMrtAtlas && !!this._mrtAtlas;
|
|
2411
|
+
if (growingMrtAtlas) {
|
|
2412
|
+
this._onBeforeAtlasRebuildObservable.notifyObservers(this._mrtAtlas);
|
|
2413
|
+
}
|
|
2414
|
+
if (this._mrtAtlas) {
|
|
2415
|
+
// The four data textures are attachments of this MRT — dispose the MRT, not each attachment.
|
|
2416
|
+
this._mrtAtlas.dispose();
|
|
2417
|
+
this._mrtAtlas = null;
|
|
2418
|
+
this._covariancesATexture = this._covariancesBTexture = this._centersTexture = this._colorsTexture = null;
|
|
2419
|
+
}
|
|
2420
|
+
else {
|
|
2421
|
+
this._covariancesATexture?.dispose();
|
|
2422
|
+
this._covariancesBTexture?.dispose();
|
|
2423
|
+
this._centersTexture?.dispose();
|
|
2424
|
+
this._colorsTexture?.dispose();
|
|
2425
|
+
}
|
|
2426
|
+
// Dispose+null the rotation/scale textures before the MRT-branch _updateSubTextures below: that helper
|
|
2427
|
+
// also writes the rotation textures, so leaving them at the old (smaller) height would upload the taller
|
|
2428
|
+
// region out of bounds. Nulling them makes it skip rotation; the block further down recreates them.
|
|
2429
|
+
if (this._rotMrtAtlas) {
|
|
2430
|
+
// The three rotation textures are attachments of this MRT — dispose the MRT, not each attachment.
|
|
2431
|
+
this._rotMrtAtlas.dispose();
|
|
2432
|
+
this._rotMrtAtlas = null;
|
|
2433
|
+
this._rotationsATexture = null;
|
|
2434
|
+
this._rotationsBTexture = null;
|
|
2435
|
+
this._rotationScaleTexture = null;
|
|
2436
|
+
}
|
|
2437
|
+
else if (this._rotationsATexture) {
|
|
2438
|
+
this._rotationsATexture.dispose();
|
|
2439
|
+
this._rotationsBTexture?.dispose();
|
|
2440
|
+
this._rotationScaleTexture?.dispose();
|
|
2441
|
+
this._rotationsATexture = null;
|
|
2442
|
+
this._rotationsBTexture = null;
|
|
2443
|
+
this._rotationScaleTexture = null;
|
|
2444
|
+
}
|
|
2176
2445
|
if (this._shTextures) {
|
|
2177
|
-
|
|
2178
|
-
|
|
2446
|
+
// SH attachments belong to _shMrtAtlas MRTs (dispose the MRT, not each attachment) when render-backed.
|
|
2447
|
+
if (this._shMrtAtlas) {
|
|
2448
|
+
for (const mrt of this._shMrtAtlas) {
|
|
2449
|
+
mrt.dispose();
|
|
2450
|
+
}
|
|
2451
|
+
// Defence in depth: free any _shTextures entries beyond the MRT-backed set (see dispose()).
|
|
2452
|
+
for (let k = this._shMrtAtlas.length; k < this._shTextures.length; k++) {
|
|
2453
|
+
this._shTextures[k].dispose();
|
|
2454
|
+
}
|
|
2455
|
+
this._shMrtAtlas = null;
|
|
2456
|
+
}
|
|
2457
|
+
else {
|
|
2458
|
+
for (const shTexture of this._shTextures) {
|
|
2459
|
+
shTexture.dispose();
|
|
2460
|
+
}
|
|
2179
2461
|
}
|
|
2180
2462
|
this._shTextures = null;
|
|
2181
2463
|
}
|
|
2182
2464
|
this._textureSize = textureSize;
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
this.
|
|
2189
|
-
|
|
2190
|
-
const shTexture = createEmptyTextureU32(textureSize.x, textureSize.y, 11);
|
|
2191
|
-
shTexture.wrapU = 0;
|
|
2192
|
-
shTexture.wrapV = 0;
|
|
2193
|
-
this._shTextures.push(shTexture);
|
|
2465
|
+
if (this._useMrtAtlas) {
|
|
2466
|
+
// Back the four data textures with a render-targetable MRT so a streaming engine can decode
|
|
2467
|
+
// into the reserved region; static splats are CPU-uploaded into the same attachments below.
|
|
2468
|
+
this._createMrtAtlas(textureSize);
|
|
2469
|
+
// Same for the higher-order SH atlas (integer render targets), when a streaming part needs baked SH.
|
|
2470
|
+
if (this._useShMrtAtlas) {
|
|
2471
|
+
this._createShMrtAtlas(textureSize);
|
|
2194
2472
|
}
|
|
2195
|
-
for
|
|
2196
|
-
|
|
2473
|
+
// Same for the rotation/scale atlas (half-float), created before the _updateSubTextures below so
|
|
2474
|
+
// static parts' CPU rotation data lands in the shared attachments (streamed rows are decoded after).
|
|
2475
|
+
if (this._useRotMrtAtlas) {
|
|
2476
|
+
this._createRotMrtAtlas(textureSize);
|
|
2477
|
+
}
|
|
2478
|
+
if (this._mrtAtlas) {
|
|
2479
|
+
this._updateSubTextures(this._splatPositions, covA, covB, colorArray, 0, textureSize.y);
|
|
2480
|
+
// Render-backed SH: CPU-upload the static parts' SH into the shared SH atlas HERE — before the
|
|
2481
|
+
// streaming restore below — so a streamed region's restored SH wins over the bulk (neutral) fill,
|
|
2482
|
+
// exactly like the core atlas's _updateSubTextures-then-restore ordering. (`sh` covers the whole
|
|
2483
|
+
// atlas; streamed rows are neutral in it and get overwritten by the restore.)
|
|
2484
|
+
if (this._useShMrtAtlas && sh) {
|
|
2485
|
+
const shTex = this._shTextures;
|
|
2486
|
+
if (shTex) {
|
|
2487
|
+
for (let textureIndex = 0; textureIndex < sh.length && textureIndex < shTex.length; textureIndex++) {
|
|
2488
|
+
this._updateShTextureData(shTex[textureIndex], sh[textureIndex], textureSize.x, 0, textureSize.y);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
// New atlas is populated (streamed regions currently zeroed); let streaming parts rebind and
|
|
2493
|
+
// restore their backed-up region into it, overwriting those zeros with the preserved data.
|
|
2494
|
+
if (growingMrtAtlas) {
|
|
2495
|
+
this._onAfterAtlasRebuildObservable.notifyObservers(this._mrtAtlas);
|
|
2496
|
+
}
|
|
2197
2497
|
}
|
|
2198
2498
|
}
|
|
2199
|
-
|
|
2499
|
+
else {
|
|
2500
|
+
this._covariancesATexture = createTextureFromDataF16(covA, textureSize.x, textureSize.y, 5);
|
|
2501
|
+
this._covariancesBTexture = createTextureFromDataF16(covB, textureSize.x, textureSize.y, this._useRGBACovariants ? 5 : 7);
|
|
2502
|
+
this._centersTexture = createTextureFromData(this._splatPositions, textureSize.x, textureSize.y, 5);
|
|
2503
|
+
this._colorsTexture = createTextureFromDataU8(colorArray, textureSize.x, textureSize.y, 5);
|
|
2504
|
+
}
|
|
2505
|
+
// Render-backed SH (_useShMrtAtlas) was already CPU-uploaded above (before the streaming restore); only
|
|
2506
|
+
// the plain RawTexture SH path is handled here.
|
|
2507
|
+
if (sh && !this._useShMrtAtlas) {
|
|
2508
|
+
{
|
|
2509
|
+
this._shTextures = [];
|
|
2510
|
+
for (let textureIndex = 0; textureIndex < sh.length; textureIndex++) {
|
|
2511
|
+
const shTexture = createEmptyTextureU32(textureSize.x, textureSize.y, 11);
|
|
2512
|
+
shTexture.wrapU = 0;
|
|
2513
|
+
shTexture.wrapV = 0;
|
|
2514
|
+
this._shTextures.push(shTexture);
|
|
2515
|
+
}
|
|
2516
|
+
for (let textureIndex = 0; textureIndex < sh.length; textureIndex++) {
|
|
2517
|
+
this._updateShTextureData(this._shTextures[textureIndex], sh[textureIndex], textureSize.x, 0, textureSize.y);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
// Render-backed rotation (_useRotMrtAtlas) was already created + CPU-uploaded above (via _updateSubTextures,
|
|
2522
|
+
// before the streaming restore); only the plain RawTexture rotation path is handled here.
|
|
2523
|
+
if (this._needsRotationScaleTextures && !this._useRotMrtAtlas) {
|
|
2200
2524
|
const rotDataA = this._rotationDataA ?? new Uint16Array(covA.length);
|
|
2201
2525
|
const rotDataB = this._rotationDataB ?? new Uint16Array(covA.length);
|
|
2202
2526
|
const rotScaleData = this._rotationScaleData ?? new Uint16Array(covA.length);
|
|
2203
|
-
|
|
2204
|
-
this._rotationsBTexture?.dispose();
|
|
2205
|
-
this._rotationScaleTexture?.dispose();
|
|
2527
|
+
// Already disposed+nulled in the rebuild's disposal block above; just (re)create at the new size.
|
|
2206
2528
|
this._rotationsATexture = createTextureFromDataF16(rotDataA, textureSize.x, textureSize.y, 5);
|
|
2207
2529
|
this._rotationsBTexture = createTextureFromDataF16(rotDataB, textureSize.x, textureSize.y, 5);
|
|
2208
2530
|
this._rotationScaleTexture = createTextureFromDataF16(rotScaleData, textureSize.x, textureSize.y, 5);
|
|
@@ -2235,9 +2557,10 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2235
2557
|
* Requires that the GPU textures already exist and the texture height won't change.
|
|
2236
2558
|
* @param previousVertexCount - The number of splats previously committed to GPU
|
|
2237
2559
|
* @param vertexCount - The new total number of splats
|
|
2560
|
+
* @param requiredShTextureCount - SH texture count this update requires (the merged `sh.length`), 0 when no SH
|
|
2238
2561
|
* @returns true when only the new splat region needs to be uploaded
|
|
2239
2562
|
*/
|
|
2240
|
-
_canReuseCachedData(previousVertexCount, vertexCount) {
|
|
2563
|
+
_canReuseCachedData(previousVertexCount, vertexCount, requiredShTextureCount = 0) {
|
|
2241
2564
|
if (previousVertexCount <= 0 || previousVertexCount > vertexCount) {
|
|
2242
2565
|
return false;
|
|
2243
2566
|
}
|
|
@@ -2247,6 +2570,23 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2247
2570
|
if (this._covariancesATexture === null) {
|
|
2248
2571
|
return false;
|
|
2249
2572
|
}
|
|
2573
|
+
// Switching to (or resizing) a render-backed atlas requires a full rebuild so the affected textures are
|
|
2574
|
+
// recreated as MRT attachments — the incremental sub-upload path cannot convert them. These must mirror the
|
|
2575
|
+
// `needsMrtConversion` checks in _updateTextures: if they disagree, an incremental data prep (which fills only
|
|
2576
|
+
// the new splats) would be paired with a full-rebuild upload (which uploads the whole array), zeroing the
|
|
2577
|
+
// existing parts. Covers the first streaming reservation, a later part that introduces/grows SH, and rotation.
|
|
2578
|
+
if (this._useMrtAtlas && !this._mrtAtlas) {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
// Compare against the count this update requires, not `_shMrtAtlasTextureCount` — that field is refreshed
|
|
2582
|
+
// only in _updateTextures (skipped on the incremental path), so a higher-degree addPart would pass the guard
|
|
2583
|
+
// on a stale count and never grow the SH atlas.
|
|
2584
|
+
if (this._useShMrtAtlas && (!this._shMrtAtlas || this._shMrtAtlas.length !== requiredShTextureCount)) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
if (this._useRotMrtAtlas && !this._rotMrtAtlas) {
|
|
2588
|
+
return false;
|
|
2589
|
+
}
|
|
2250
2590
|
// Can only do an incremental GPU update if texture height doesn't need to grow
|
|
2251
2591
|
const newTextureSize = this._getTextureSize(vertexCount);
|
|
2252
2592
|
return newTextureSize.y === this._textureSize.y;
|
|
@@ -2286,6 +2626,80 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2286
2626
|
this._worker.postMessage({ command: GaussianSplattingSortWorkerCommand.POSITIONS_UPDATE, offset: floatOffset, data }, [data.buffer]);
|
|
2287
2627
|
this._sortIsDirty = true;
|
|
2288
2628
|
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Decodes raw `.splat` bytes (32 bytes/splat) into a contiguous sub-range of the already-allocated atlas,
|
|
2631
|
+
* uploading only the affected texels (never touching neighboring parts' texels) and patching just that
|
|
2632
|
+
* range of positions in the sort worker. Used to populate a region reserved by
|
|
2633
|
+
* {@link GaussianSplattingMesh.reserveStreamingPart} — the CPU path a streaming engine uses when GPU
|
|
2634
|
+
* decode/readback is unavailable, and the seam that verifies reservation + interleaving before the
|
|
2635
|
+
* streaming engine exists.
|
|
2636
|
+
*
|
|
2637
|
+
* The atlas textures and `_splatPositions` must already be sized to cover `[globalOffset, globalOffset+count)`
|
|
2638
|
+
* (guaranteed after `reserveStreamingPart`). Bounds of the written centers are accumulated into `min`/`max`
|
|
2639
|
+
* when provided (so the caller can grow the owning part's bounding info).
|
|
2640
|
+
* @param globalOffset first atlas splat index to write
|
|
2641
|
+
* @param count number of splats to write
|
|
2642
|
+
* @param splatsData raw `.splat` bytes for `count` splats (stride 32)
|
|
2643
|
+
* @param min optional running min accumulator for the written centers
|
|
2644
|
+
* @param max optional running max accumulator for the written centers
|
|
2645
|
+
*/
|
|
2646
|
+
_writeStreamingSplats(globalOffset, count, splatsData, min, max) {
|
|
2647
|
+
if (!this._splatPositions) {
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
// Validate the range against the atlas and the input length (independent of GPU state): an out-of-range
|
|
2651
|
+
// offset/count would write over another part's texels or allocate a huge transient (the CPU arrays are
|
|
2652
|
+
// atlas-indexed up to `end`); a short input would read past its end.
|
|
2653
|
+
const capacity = this._splatPositions.length / 4;
|
|
2654
|
+
const uBuffer = GaussianSplattingMeshBase._GetSplatDataBytes(splatsData);
|
|
2655
|
+
if (!Number.isInteger(globalOffset) || !Number.isInteger(count) || globalOffset < 0 || count < 0 || globalOffset + count > capacity) {
|
|
2656
|
+
throw new Error(`_writeStreamingSplats: range [${globalOffset}, ${globalOffset + count}) is outside the atlas bounds [0, ${capacity})`);
|
|
2657
|
+
}
|
|
2658
|
+
if (uBuffer.length < count * 32) {
|
|
2659
|
+
throw new Error(`_writeStreamingSplats: splatsData has ${uBuffer.length} bytes, need ${count * 32} for ${count} splats (stride 32)`);
|
|
2660
|
+
}
|
|
2661
|
+
if (count === 0 || !this._covariancesATexture) {
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
const textureSize = this._getTextureSize(this._vertexCount);
|
|
2665
|
+
const width = textureSize.x;
|
|
2666
|
+
const covBSItemSize = this._useRGBACovariants ? 4 : 2;
|
|
2667
|
+
const end = globalOffset + count;
|
|
2668
|
+
const fBuffer = GaussianSplattingMeshBase._GetSplatDataFloats(splatsData);
|
|
2669
|
+
// Transients are sized to `count`, not `end` (globalOffset + count): sizing by atlas position would allocate
|
|
2670
|
+
// tens of MB for a small write near a large atlas's tail. dstIndex still addresses the atlas _splatPositions.
|
|
2671
|
+
const covA = new Uint16Array(count * 4);
|
|
2672
|
+
const covB = new Uint16Array(count * covBSItemSize);
|
|
2673
|
+
const colorArray = new Uint8Array(count * 4);
|
|
2674
|
+
const localMin = min ?? new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
|
|
2675
|
+
const localMax = max ?? new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
|
|
2676
|
+
for (let i = 0; i < count; i++) {
|
|
2677
|
+
this._makeSplat(globalOffset + i, fBuffer, uBuffer, covA, covB, colorArray, localMin, localMax, this._flipY, i, i);
|
|
2678
|
+
}
|
|
2679
|
+
// Retain the written bytes so future full rebuilds / picking see the region's real data.
|
|
2680
|
+
if (this._splatsData) {
|
|
2681
|
+
const dst = new Uint8Array(GaussianSplattingMeshBase._GetSplatDataBytes(this._splatsData).buffer);
|
|
2682
|
+
dst.set(uBuffer.subarray(0, count * 32), globalOffset * 32);
|
|
2683
|
+
}
|
|
2684
|
+
// Upload only the written texels, one contiguous per-row run at a time (rectangular sub-uploads never
|
|
2685
|
+
// clobber texels outside [globalOffset, end), so neighboring parts on a shared boundary row are safe).
|
|
2686
|
+
const positions = this._splatPositions;
|
|
2687
|
+
let gi = globalOffset;
|
|
2688
|
+
while (gi < end) {
|
|
2689
|
+
const row = Math.floor(gi / width);
|
|
2690
|
+
const runEnd = Math.min(end, (row + 1) * width);
|
|
2691
|
+
const col = gi - row * width;
|
|
2692
|
+
const runLen = runEnd - gi;
|
|
2693
|
+
// covA/covB/colorArray are count-sized (0-based), so rebase by globalOffset; positions stays global.
|
|
2694
|
+
const li = gi - globalOffset;
|
|
2695
|
+
this._updateTextureFromDataRect(this._covariancesATexture, new Uint16Array(covA.buffer, li * 4 * 2, runLen * 4), col, row, runLen, 1);
|
|
2696
|
+
this._updateTextureFromDataRect(this._covariancesBTexture, new Uint16Array(covB.buffer, li * covBSItemSize * 2, runLen * covBSItemSize), col, row, runLen, 1);
|
|
2697
|
+
this._updateTextureFromDataRect(this._centersTexture, new Float32Array(positions.buffer, gi * 4 * 4, runLen * 4), col, row, runLen, 1);
|
|
2698
|
+
this._updateTextureFromDataRect(this._colorsTexture, new Uint8Array(colorArray.buffer, li * 4, runLen * 4), col, row, runLen, 1);
|
|
2699
|
+
gi = runEnd;
|
|
2700
|
+
}
|
|
2701
|
+
this._postWorkerPositionsRange(globalOffset, count);
|
|
2702
|
+
}
|
|
2289
2703
|
*_updateData(data, isAsync, sh, partIndices, { flipY = false, previousVertexCount = 0 } = {}, shDegree) {
|
|
2290
2704
|
if (!this._covariancesATexture) {
|
|
2291
2705
|
this._readyToDisplay = false;
|
|
@@ -2333,7 +2747,7 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2333
2747
|
// Incremental path: only upload the rows that contain new splats, leaving the already-committed
|
|
2334
2748
|
// GPU region untouched. Falls through to the full-rebuild path when textures don't exist yet
|
|
2335
2749
|
// or the texture height needs to grow.
|
|
2336
|
-
const incremental = this._canReuseCachedData(previousVertexCount, vertexCount);
|
|
2750
|
+
const incremental = this._canReuseCachedData(previousVertexCount, vertexCount, sh?.length ?? 0);
|
|
2337
2751
|
if (!incremental) {
|
|
2338
2752
|
this._splatSizeMin = Infinity;
|
|
2339
2753
|
this._splatSizeMax = -Infinity;
|
|
@@ -2476,8 +2890,10 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2476
2890
|
splatIndex[index++] = sourceIndex;
|
|
2477
2891
|
}
|
|
2478
2892
|
}
|
|
2893
|
+
// Pad with `vertexCount` itself, not 0: splat 0 is real data, not a reserved empty slot, and
|
|
2894
|
+
// range filtering can leave it fully visible — see gaussianSplattingSortWorker.ts.
|
|
2479
2895
|
for (; index < paddedVertexCount; index++) {
|
|
2480
|
-
splatIndex[index] =
|
|
2896
|
+
splatIndex[index] = vertexCount;
|
|
2481
2897
|
}
|
|
2482
2898
|
}
|
|
2483
2899
|
else {
|
|
@@ -2588,7 +3004,8 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2588
3004
|
type: "application/javascript",
|
|
2589
3005
|
})));
|
|
2590
3006
|
const positions = Float32Array.from(this._splatPositions);
|
|
2591
|
-
|
|
3007
|
+
const vertexCount = this._vertexCount;
|
|
3008
|
+
this._worker.postMessage({ command: GaussianSplattingSortWorkerCommand.POSITIONS, positions, vertexCount }, [positions.buffer]);
|
|
2592
3009
|
// The main thread owns the active interval set: send it explicitly (covering all indices when
|
|
2593
3010
|
// no LOD filter is active) rather than letting the worker assume the full source set.
|
|
2594
3011
|
this._postIntervalsToWorker();
|
|
@@ -2672,9 +3089,25 @@ export class GaussianSplattingMeshBase extends Mesh {
|
|
|
2672
3089
|
while (width * height < length) {
|
|
2673
3090
|
height *= 2;
|
|
2674
3091
|
}
|
|
3092
|
+
// See the WebGL2/WebGPU branch below for why this guarantees an extra row when `length`
|
|
3093
|
+
// exactly fills every row. A power-of-2 height has to double to add any slack at all.
|
|
3094
|
+
// Usually this is a no-op since it is rare that the asset exactly contains a power-of-2
|
|
3095
|
+
// number of splats.
|
|
3096
|
+
if (length > 0 && width * height === length) {
|
|
3097
|
+
height *= 2;
|
|
3098
|
+
}
|
|
2675
3099
|
}
|
|
2676
3100
|
else {
|
|
2677
3101
|
height = Math.ceil(length / width);
|
|
3102
|
+
// When `length` exactly fills every row, force one extra (fully zeroed) row so the atlas
|
|
3103
|
+
// always has at least one genuinely-empty splat slot past the real data, at index `length`.
|
|
3104
|
+
// The sort worker (gaussianSplattingSortWorker.ts) relies on that slot always existing to
|
|
3105
|
+
// safely pad the 16-aligned draw order — without it, the only "spare" index would be the
|
|
3106
|
+
// last real splat, which range-filtering (setSplatIndexRanges/opacity/size culling) can
|
|
3107
|
+
// leave fully visible and opaque, reproducing the same "stuck in front" bug on that splat.
|
|
3108
|
+
if (length > 0 && height * width === length) {
|
|
3109
|
+
height += 1;
|
|
3110
|
+
}
|
|
2678
3111
|
}
|
|
2679
3112
|
if (height > width) {
|
|
2680
3113
|
Logger.Error("GaussianSplatting texture size: (" + width + ", " + height + "), maxTextureSize: " + width);
|