@cesium/engine 22.3.0 → 23.0.0
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/Build/Workers/{chunk-EAAH543Q.js → chunk-2GL2EUEE.js} +11 -11
- package/Build/Workers/{chunk-EUDHHS7K.js → chunk-2WI4HGJU.js} +6 -6
- package/Build/Workers/{chunk-LWZHJWYK.js → chunk-36LBFITA.js} +665 -366
- package/Build/Workers/{chunk-7U5YNLF3.js → chunk-3B65YFXC.js} +1 -1
- package/Build/Workers/{chunk-D63Z7KKL.js → chunk-4LGFX2P5.js} +11 -11
- package/Build/Workers/{chunk-D6OKR3BQ.js → chunk-4UNQX2MP.js} +2 -2
- package/Build/Workers/{chunk-BO22JHBX.js → chunk-52K3GJKG.js} +3 -3
- package/Build/Workers/{chunk-XLIJV3YZ.js → chunk-56OHFSLH.js} +10 -10
- package/Build/Workers/{chunk-AWELU5C6.js → chunk-5A7YSAXT.js} +14 -14
- package/Build/Workers/{chunk-T5MJDW43.js → chunk-5JP5FG7U.js} +7 -7
- package/Build/Workers/{chunk-NVL7JXAQ.js → chunk-5R7SBUTW.js} +10 -10
- package/Build/Workers/chunk-5VKZVPO5.js +1829 -0
- package/Build/Workers/{chunk-YHPBT4I5.js → chunk-6FVFM2KB.js} +5 -5
- package/Build/Workers/{chunk-WHLGBMBC.js → chunk-6SLGTBB2.js} +5 -5
- package/Build/Workers/{chunk-VIQIKMTZ.js → chunk-7KRN6JPQ.js} +14 -14
- package/Build/Workers/{chunk-TCOUGKXF.js → chunk-AA33B6PU.js} +4 -4
- package/Build/Workers/{chunk-S2KVL7OJ.js → chunk-AP6XWET5.js} +8 -8
- package/Build/Workers/{chunk-5PJB7ASO.js → chunk-APDKMX7W.js} +6 -6
- package/Build/Workers/{chunk-IXL46IYV.js → chunk-ARCFIOKA.js} +12 -12
- package/Build/Workers/{chunk-KIJR26N3.js → chunk-BNBD2YZK.js} +16 -16
- package/Build/Workers/{chunk-4R6FGUFP.js → chunk-DT5SJBKA.js} +29 -15
- package/Build/Workers/{chunk-FUDC3KOT.js → chunk-ESD4IEOU.js} +5 -5
- package/Build/Workers/{chunk-K2PLKJJG.js → chunk-FSDVXW7M.js} +5 -5
- package/Build/Workers/{chunk-4BBANFU6.js → chunk-G3YT7M7A.js} +1 -1
- package/Build/Workers/{chunk-K4PGSAR5.js → chunk-GN5NEUDT.js} +8 -8
- package/Build/Workers/{chunk-OOYICVOK.js → chunk-HYLEKKO5.js} +1 -1
- package/Build/Workers/{chunk-FDOLXORR.js → chunk-HYTFXWMY.js} +7 -7
- package/Build/Workers/{chunk-7SLNBIZS.js → chunk-IFA7VHHP.js} +560 -277
- package/Build/Workers/{chunk-N66PIK46.js → chunk-IYVDOB5V.js} +4 -4
- package/Build/Workers/{chunk-ZWWN2CQH.js → chunk-JUGY5AZN.js} +4 -4
- package/Build/Workers/{chunk-4O2ARYS7.js → chunk-KPYU6YZG.js} +1 -1
- package/Build/Workers/{chunk-TY5JXMVO.js → chunk-L7RDJ4V7.js} +1 -1
- package/Build/Workers/{chunk-L4OFWHNI.js → chunk-LDQWNMEC.js} +1 -1
- package/Build/Workers/{chunk-N6XBJBI7.js → chunk-MQJG522T.js} +6 -6
- package/Build/Workers/{chunk-FS42VX2H.js → chunk-MT5M7RLZ.js} +2 -2
- package/Build/Workers/{chunk-7ATKQRBM.js → chunk-N4AWE7TZ.js} +10 -10
- package/Build/Workers/{chunk-EOSX23OS.js → chunk-NAYRNPEP.js} +4 -4
- package/Build/Workers/{chunk-NO3LMU7T.js → chunk-NNHU33JK.js} +7 -7
- package/Build/Workers/{chunk-HWGP4GVC.js → chunk-NQCAZU7S.js} +13 -13
- package/Build/Workers/{chunk-6GBGEUWR.js → chunk-NUYLNQKV.js} +2 -2
- package/Build/Workers/{chunk-E4YDCSQK.js → chunk-OC6X22PG.js} +9 -9
- package/Build/Workers/{chunk-ZGGVEGTA.js → chunk-OHBM4YG4.js} +12 -12
- package/Build/Workers/{chunk-6WDMPXQ7.js → chunk-OMXN7YF4.js} +12 -12
- package/Build/Workers/{chunk-LK7GEFGB.js → chunk-OQ3EAJNW.js} +2 -2
- package/Build/Workers/{chunk-M5DVYXJJ.js → chunk-OQBPOKPZ.js} +6 -6
- package/Build/Workers/{chunk-WUWPKBOZ.js → chunk-PQVNT5CJ.js} +6 -6
- package/Build/Workers/{chunk-W5NV72VR.js → chunk-QA24Q62E.js} +17 -17
- package/Build/Workers/{chunk-PJLRXIMS.js → chunk-QQX7S5QT.js} +17 -17
- package/Build/Workers/{chunk-D2PEV362.js → chunk-QTOHIXGF.js} +10 -10
- package/Build/Workers/{chunk-MASEF4OV.js → chunk-R7M4XNPP.js} +8 -8
- package/Build/Workers/{chunk-2MLJ46ZZ.js → chunk-SGKTBHFQ.js} +13 -13
- package/Build/Workers/{chunk-EY5HOJFQ.js → chunk-SQTRWKO2.js} +5 -5
- package/Build/Workers/{chunk-AFGP5BXS.js → chunk-T43FXHNC.js} +7 -7
- package/Build/Workers/{chunk-IHFAFGR2.js → chunk-UEEGWJJP.js} +2 -2
- package/Build/Workers/{chunk-TMGLPEZW.js → chunk-WKXDCVBO.js} +4 -4
- package/Build/Workers/{chunk-VRAAQ3FS.js → chunk-Y5AJSEEN.js} +2 -2
- package/Build/Workers/{chunk-Y5MRBKQN.js → chunk-ZIW5FCIW.js} +18 -18
- package/Build/Workers/combineGeometry.js +24 -24
- package/Build/Workers/createBoxGeometry.js +18 -18
- package/Build/Workers/createBoxOutlineGeometry.js +16 -16
- package/Build/Workers/createCircleGeometry.js +26 -26
- package/Build/Workers/createCircleOutlineGeometry.js +19 -19
- package/Build/Workers/createCoplanarPolygonGeometry.js +33 -33
- package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +31 -31
- package/Build/Workers/createCorridorGeometry.js +29 -29
- package/Build/Workers/createCorridorOutlineGeometry.js +28 -28
- package/Build/Workers/createCylinderGeometry.js +20 -20
- package/Build/Workers/createCylinderOutlineGeometry.js +18 -18
- package/Build/Workers/createEllipseGeometry.js +26 -26
- package/Build/Workers/createEllipseOutlineGeometry.js +19 -19
- package/Build/Workers/createEllipsoidGeometry.js +19 -19
- package/Build/Workers/createEllipsoidOutlineGeometry.js +18 -18
- package/Build/Workers/createFrustumGeometry.js +18 -18
- package/Build/Workers/createFrustumOutlineGeometry.js +18 -18
- package/Build/Workers/createGeometry.js +24 -24
- package/Build/Workers/createGroundPolylineGeometry.js +22 -22
- package/Build/Workers/createPlaneGeometry.js +16 -16
- package/Build/Workers/createPlaneOutlineGeometry.js +15 -15
- package/Build/Workers/createPolygonGeometry.js +32 -32
- package/Build/Workers/createPolygonOutlineGeometry.js +30 -30
- package/Build/Workers/createPolylineGeometry.js +25 -25
- package/Build/Workers/createPolylineVolumeGeometry.js +31 -31
- package/Build/Workers/createPolylineVolumeOutlineGeometry.js +27 -27
- package/Build/Workers/createRectangleGeometry.js +27 -27
- package/Build/Workers/createRectangleOutlineGeometry.js +20 -20
- package/Build/Workers/createSimplePolylineGeometry.js +23 -23
- package/Build/Workers/createSphereGeometry.js +19 -19
- package/Build/Workers/createSphereOutlineGeometry.js +18 -18
- package/Build/Workers/createTaskProcessorWorker.js +3 -3
- package/Build/Workers/createVectorTileClampedPolylines.js +14 -14
- package/Build/Workers/createVectorTileGeometries.js +24 -24
- package/Build/Workers/createVectorTilePoints.js +13 -13
- package/Build/Workers/createVectorTilePolygons.js +22 -22
- package/Build/Workers/createVectorTilePolylines.js +14 -14
- package/Build/Workers/createVerticesFromCesium3DTilesTerrain.js +25 -25
- package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +22 -22
- package/Build/Workers/createVerticesFromHeightmap.js +22 -22
- package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +20 -20
- package/Build/Workers/createWallGeometry.js +24 -24
- package/Build/Workers/createWallOutlineGeometry.js +23 -23
- package/Build/Workers/decodeDraco.js +10 -10
- package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
- package/Build/Workers/decodeI3S.js +10 -10
- package/Build/Workers/gaussianSplatSorter.js +4 -4
- package/Build/Workers/gaussianSplatTextureGenerator.js +4 -4
- package/Build/Workers/incrementallyBuildTerrainPicker.js +10 -10
- package/Build/Workers/transcodeKTX2.js +6 -6
- package/Build/Workers/transferTypedArrayTest.js +1 -1
- package/Build/Workers/upsampleQuantizedTerrainMesh.js +22 -22
- package/Build/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +25 -25
- package/Source/Core/AttributeCompression.js +53 -6
- package/Source/Core/Cartesian2.js +689 -684
- package/Source/Core/Cartesian3.js +1044 -1043
- package/Source/Core/Cartesian4.js +816 -813
- package/Source/Core/GoogleMaps.js +23 -2
- package/Source/Core/Ion.js +1 -1
- package/Source/Core/OpenCageGeocoderService.js +2 -3
- package/Source/Core/globalTypes.js +16 -0
- package/Source/Renderer/ShaderDestination.js +54 -13
- package/Source/Scene/ArcGisMapService.js +1 -1
- package/Source/Scene/Billboard.js +7 -4
- package/Source/Scene/BillboardCollection.js +64 -25
- package/Source/Scene/BufferLoader.js +65 -74
- package/Source/Scene/CubeMapPanorama.js +351 -0
- package/Source/Scene/DerivedCommand.js +8 -31
- package/Source/Scene/DracoLoader.js +79 -78
- package/Source/Scene/EquirectangularPanorama.js +264 -0
- package/Source/Scene/FrameState.js +7 -0
- package/Source/Scene/GaussianSplatPrimitive.js +906 -167
- package/Source/Scene/GlobeSurfaceShaderSet.js +36 -32
- package/Source/Scene/GltfBufferViewLoader.js +109 -118
- package/Source/Scene/GltfDracoLoader.js +167 -176
- package/Source/Scene/GltfImageLoader.js +91 -101
- package/Source/Scene/GltfIndexBufferLoader.js +228 -241
- package/Source/Scene/GltfJsonLoader.js +90 -99
- package/Source/Scene/GltfLoader.js +313 -324
- package/Source/Scene/GltfSpzLoader.js +127 -134
- package/Source/Scene/GltfStructuralMetadataLoader.js +181 -186
- package/Source/Scene/GltfTextureLoader.js +217 -231
- package/Source/Scene/GltfVertexBufferLoader.js +267 -276
- package/Source/Scene/GoogleStreetViewCubeMapPanoramaProvider.js +341 -0
- package/Source/Scene/Label.js +6 -4
- package/Source/Scene/LabelCollection.js +59 -1
- package/Source/Scene/MetadataClassProperty.js +212 -7
- package/Source/Scene/MetadataComponentType.js +162 -127
- package/Source/Scene/MetadataSchemaLoader.js +60 -67
- package/Source/Scene/MetadataTable.js +14 -0
- package/Source/Scene/MetadataTableProperty.js +14 -0
- package/Source/Scene/Model/B3dmLoader.js +216 -224
- package/Source/Scene/Model/CustomShader.js +6 -1
- package/Source/Scene/Model/CustomShaderPipelineStage.js +122 -7
- package/Source/Scene/Model/GeoJsonLoader.js +53 -61
- package/Source/Scene/Model/GeometryPipelineStage.js +17 -3
- package/Source/Scene/Model/I3dmLoader.js +232 -240
- package/Source/Scene/Model/MaterialPipelineStage.js +1 -1
- package/Source/Scene/Model/MetadataPipelineStage.js +438 -22
- package/Source/Scene/Model/PntsLoader.js +78 -89
- package/Source/Scene/Panorama.js +53 -0
- package/Source/Scene/PanoramaProvider.js +43 -0
- package/Source/Scene/PointPrimitiveCollection.js +14 -22
- package/Source/Scene/PropertyTable.js +43 -0
- package/Source/Scene/PropertyTexture.js +1 -1
- package/Source/Scene/PropertyTextureProperty.js +0 -97
- package/Source/Scene/ResourceLoader.js +84 -92
- package/Source/Scene/Scene.js +12 -0
- package/Source/Scene/SkyBox.js +29 -164
- package/Source/Scene/StructuralMetadata.js +18 -3
- package/Source/Scene/parseStructuralMetadata.js +244 -0
- package/Source/Shaders/BillboardCollectionFS.glsl +66 -40
- package/Source/Shaders/BillboardCollectionFS.js +67 -40
- package/Source/Shaders/BillboardCollectionVS.glsl +22 -31
- package/Source/Shaders/BillboardCollectionVS.js +22 -31
- package/Source/Shaders/Builtin/CzmBuiltins.js +4 -0
- package/Source/Shaders/Builtin/Functions/decodeRGB8.glsl +22 -0
- package/Source/Shaders/Builtin/Functions/decodeRGB8.js +24 -0
- package/Source/Shaders/Builtin/Functions/unpackTexture.glsl +35 -0
- package/Source/Shaders/Builtin/Functions/unpackTexture.js +37 -0
- package/Source/Shaders/CubeMapPanoramaVS.glsl +11 -0
- package/Source/Shaders/CubeMapPanoramaVS.js +13 -0
- package/Source/Shaders/Model/MetadataStageFS.glsl +2 -1
- package/Source/Shaders/Model/MetadataStageFS.js +2 -1
- package/Source/Shaders/Model/MetadataStageVS.glsl +2 -1
- package/Source/Shaders/Model/MetadataStageVS.js +2 -1
- package/Source/Shaders/Model/ModelFS.glsl +2 -2
- package/Source/Shaders/Model/ModelFS.js +2 -2
- package/Source/Shaders/Model/ModelVS.glsl +2 -2
- package/Source/Shaders/Model/ModelVS.js +2 -2
- package/Source/Shaders/PointPrimitiveCollectionVS.glsl +8 -39
- package/Source/Shaders/PointPrimitiveCollectionVS.js +8 -39
- package/index.d.ts +564 -189
- package/index.js +127 -119
- package/package.json +1 -1
- package/Build/Workers/chunk-LYBNPUEI.js +0 -1443
|
@@ -23,6 +23,7 @@ import BufferUsage from "../Renderer/BufferUsage.js";
|
|
|
23
23
|
import RenderState from "../Renderer/RenderState.js";
|
|
24
24
|
import clone from "../Core/clone.js";
|
|
25
25
|
import defined from "../Core/defined.js";
|
|
26
|
+
import DeveloperError from "../Core/DeveloperError.js";
|
|
26
27
|
import VertexAttributeSemantic from "./VertexAttributeSemantic.js";
|
|
27
28
|
import AttributeType from "./AttributeType.js";
|
|
28
29
|
import ModelComponents from "./ModelComponents.js";
|
|
@@ -39,14 +40,568 @@ const scratchMatrix4B = new Matrix4();
|
|
|
39
40
|
const scratchMatrix4C = new Matrix4();
|
|
40
41
|
const scratchMatrix4D = new Matrix4();
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Runtime state machine for steady-state re-sorting of an already committed snapshot.
|
|
45
|
+
*
|
|
46
|
+
* The transition points are in {@link GaussianSplatPrimitive#update}:
|
|
47
|
+
* - IDLE -> WAITING/SORTING when a new steady sort request is scheduled
|
|
48
|
+
* - WAITING -> SORTING when a sorter promise becomes available
|
|
49
|
+
* - SORTING -> SORTED when the active promise resolves with a valid result
|
|
50
|
+
* - SORTED -> IDLE after rebuilding the draw command with fresh indexes
|
|
51
|
+
* - Any state -> ERROR when the active sort promise rejects
|
|
52
|
+
*
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
42
55
|
const GaussianSplatSortingState = {
|
|
56
|
+
// No steady sort request is in flight. update() may decide to start one.
|
|
43
57
|
IDLE: 0,
|
|
58
|
+
// A steady sort was requested, but sorter capacity was unavailable this frame.
|
|
44
59
|
WAITING: 1,
|
|
60
|
+
// A steady sort promise is in flight and results are pending.
|
|
45
61
|
SORTING: 2,
|
|
62
|
+
// A valid sorted index buffer is available and waiting to be committed to draw command.
|
|
46
63
|
SORTED: 3,
|
|
64
|
+
// The active sort request failed; update() throws the captured error.
|
|
47
65
|
ERROR: 4,
|
|
48
66
|
};
|
|
49
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Snapshot lifecycle for rebuilding aggregated splat data.
|
|
70
|
+
*
|
|
71
|
+
* Transition order:
|
|
72
|
+
* BUILDING -> TEXTURE_PENDING -> TEXTURE_READY -> SORTING -> READY
|
|
73
|
+
*
|
|
74
|
+
* The transition points are split across two functions:
|
|
75
|
+
* - {@link GaussianSplatPrimitive#update} drives BUILDING/SORTING/READY
|
|
76
|
+
* - {@link GaussianSplatPrimitive.generateSplatTexture} drives TEXTURE_PENDING/TEXTURE_READY
|
|
77
|
+
*
|
|
78
|
+
* A snapshot is committed only when it reaches READY, so all GPU resources
|
|
79
|
+
* and sorted indexes swap atomically as one unit.
|
|
80
|
+
*
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
const SnapshotState = {
|
|
84
|
+
// CPU aggregation is complete and this snapshot is ready to start async texture generation.
|
|
85
|
+
BUILDING: "BUILDING",
|
|
86
|
+
// Async texture generation/upload is in flight for this snapshot.
|
|
87
|
+
TEXTURE_PENDING: "TEXTURE_PENDING",
|
|
88
|
+
// Attribute textures are ready; snapshot can now request index sorting.
|
|
89
|
+
TEXTURE_READY: "TEXTURE_READY",
|
|
90
|
+
// Sort request is in flight for this snapshot generation.
|
|
91
|
+
SORTING: "SORTING",
|
|
92
|
+
// Sorted indexes were validated for this generation and may be committed.
|
|
93
|
+
READY: "READY",
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Aggregated Gaussian splat snapshot data that is built asynchronously and
|
|
98
|
+
* atomically committed once all required resources are ready.
|
|
99
|
+
*
|
|
100
|
+
* @typedef {object} GaussianSplatPrimitive.Snapshot
|
|
101
|
+
* @property {number} generation Monotonic data generation token.
|
|
102
|
+
* @property {Float32Array} positions Packed splat positions (xyz).
|
|
103
|
+
* @property {Float32Array} rotations Packed splat rotations (xyzw).
|
|
104
|
+
* @property {Float32Array} scales Packed splat scales (xyz).
|
|
105
|
+
* @property {Uint8Array} colors Packed splat colors (rgba).
|
|
106
|
+
* @property {Uint32Array|undefined} shData Packed spherical harmonics data.
|
|
107
|
+
* @property {number} sphericalHarmonicsDegree Spherical harmonics degree.
|
|
108
|
+
* @property {number} shCoefficientCount Coefficients per splat.
|
|
109
|
+
* @property {number} numSplats Total splat count in this snapshot.
|
|
110
|
+
* @property {Uint32Array|undefined} indexes Sorted index buffer when READY.
|
|
111
|
+
* @property {Texture|undefined} gaussianSplatTexture Packed splat attribute texture.
|
|
112
|
+
* @property {Texture|undefined} sphericalHarmonicsTexture Packed SH texture.
|
|
113
|
+
* @property {number} lastTextureWidth Last splat texture width.
|
|
114
|
+
* @property {number} lastTextureHeight Last splat texture height.
|
|
115
|
+
* @property {string} state Current snapshot lifecycle state from {@link SnapshotState}.
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Packed spherical harmonics texture payload.
|
|
121
|
+
*
|
|
122
|
+
* @typedef {object} GaussianSplatPrimitive.SphericalHarmonicsTextureData
|
|
123
|
+
* @property {number} width Texture width in texels.
|
|
124
|
+
* @property {number} height Texture height in texels.
|
|
125
|
+
* @property {ArrayBufferView} data Packed unsigned integer texture data.
|
|
126
|
+
* @private
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Packed Gaussian splat attribute texture payload.
|
|
131
|
+
*
|
|
132
|
+
* @typedef {object} GaussianSplatPrimitive.AttributeTextureData
|
|
133
|
+
* @property {number} width Texture width in texels.
|
|
134
|
+
* @property {number} height Texture height in texels.
|
|
135
|
+
* @property {ArrayBufferView} data Packed unsigned integer texture data.
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
// Two stable frames avoids rebuilding during brief selected-tile jitter.
|
|
140
|
+
const DEFAULT_STABLE_FRAMES = 2;
|
|
141
|
+
// If selection keeps changing, force a rebuild after ~0.5s at 60fps to guarantee progress.
|
|
142
|
+
// Lower values react faster but can thrash on noisy LOD transitions.
|
|
143
|
+
// Higher values reduce rebuild churn but keep stale snapshots visible longer.
|
|
144
|
+
const DEFAULT_MAX_SNAPSHOT_STALL_FRAMES = 30;
|
|
145
|
+
// Minimum delay between steady re-sort requests once the camera is moving.
|
|
146
|
+
const DEFAULT_SORT_MIN_FRAME_INTERVAL = 3;
|
|
147
|
+
// ~0.5 degree camera direction change threshold before triggering steady re-sort.
|
|
148
|
+
const DEFAULT_SORT_MIN_ANGLE_RADIANS = 0.008726646259971648;
|
|
149
|
+
// Minimum camera movement in world units before triggering steady re-sort.
|
|
150
|
+
const DEFAULT_SORT_MIN_POSITION_DELTA = 1.0;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Determines whether the camera has moved or rotated enough since the last
|
|
154
|
+
* steady sort to justify scheduling a new one.
|
|
155
|
+
*
|
|
156
|
+
* Returns {@code true} when any of the following hold:
|
|
157
|
+
* - No previous steady sort has been recorded yet.
|
|
158
|
+
* - The camera position has moved by at least {@link DEFAULT_SORT_MIN_POSITION_DELTA} world units.
|
|
159
|
+
* - The camera direction has changed by at least {@link DEFAULT_SORT_MIN_ANGLE_RADIANS} radians.
|
|
160
|
+
*
|
|
161
|
+
* A minimum frame interval ({@link DEFAULT_SORT_MIN_FRAME_INTERVAL}) is
|
|
162
|
+
* enforced to prevent re-sorting every single frame.
|
|
163
|
+
*
|
|
164
|
+
* @param {GaussianSplatPrimitive} primitive The splat primitive to check.
|
|
165
|
+
* @param {FrameState} frameState The current frame state.
|
|
166
|
+
* @returns {boolean} Whether a new steady sort should begin.
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
function shouldStartSteadySort(primitive, frameState) {
|
|
170
|
+
const framesSinceLastSort =
|
|
171
|
+
primitive._lastSteadySortFrameNumber >= 0
|
|
172
|
+
? frameState.frameNumber - primitive._lastSteadySortFrameNumber
|
|
173
|
+
: Number.POSITIVE_INFINITY;
|
|
174
|
+
if (
|
|
175
|
+
primitive._lastSteadySortFrameNumber >= 0 &&
|
|
176
|
+
framesSinceLastSort < DEFAULT_SORT_MIN_FRAME_INTERVAL
|
|
177
|
+
) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const camera = frameState.camera;
|
|
182
|
+
if (!defined(camera)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (
|
|
186
|
+
!primitive._hasLastSteadySortCameraPosition ||
|
|
187
|
+
!primitive._hasLastSteadySortCameraDirection
|
|
188
|
+
) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const positionDelta = Cartesian3.distance(
|
|
193
|
+
camera.positionWC,
|
|
194
|
+
primitive._lastSteadySortCameraPosition,
|
|
195
|
+
);
|
|
196
|
+
if (positionDelta >= DEFAULT_SORT_MIN_POSITION_DELTA) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const angleDelta = Cartesian3.angleBetween(
|
|
201
|
+
camera.directionWC,
|
|
202
|
+
primitive._lastSteadySortCameraDirection,
|
|
203
|
+
);
|
|
204
|
+
return angleDelta >= DEFAULT_SORT_MIN_ANGLE_RADIANS;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Records the frame number and camera pose at the start of a steady sort so
|
|
209
|
+
* that {@link shouldStartSteadySort} can later compute deltas.
|
|
210
|
+
*
|
|
211
|
+
* @param {GaussianSplatPrimitive} primitive The splat primitive to update.
|
|
212
|
+
* @param {FrameState} frameState The current frame state.
|
|
213
|
+
* @private
|
|
214
|
+
*/
|
|
215
|
+
function markSteadySortStart(primitive, frameState) {
|
|
216
|
+
primitive._lastSteadySortFrameNumber = frameState.frameNumber;
|
|
217
|
+
const camera = frameState.camera;
|
|
218
|
+
if (!defined(camera)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
Cartesian3.clone(camera.positionWC, primitive._lastSteadySortCameraPosition);
|
|
222
|
+
primitive._hasLastSteadySortCameraPosition = true;
|
|
223
|
+
Cartesian3.clone(
|
|
224
|
+
camera.directionWC,
|
|
225
|
+
primitive._lastSteadySortCameraDirection,
|
|
226
|
+
);
|
|
227
|
+
primitive._hasLastSteadySortCameraDirection = true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Checks whether the set of currently selected tiles differs from the set
|
|
232
|
+
* recorded on the primitive. This is used to detect LOD transitions that
|
|
233
|
+
* require a snapshot rebuild.
|
|
234
|
+
*
|
|
235
|
+
* @param {GaussianSplatPrimitive} primitive The splat primitive.
|
|
236
|
+
* @param {Cesium3DTile[]} selectedTiles The tiles selected this frame.
|
|
237
|
+
* @returns {boolean} {@code true} if the tile set has changed.
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
function haveSelectedTilesChanged(primitive, selectedTiles) {
|
|
241
|
+
const prevSet = primitive._selectedTileSet;
|
|
242
|
+
if (!defined(prevSet) || prevSet.size !== selectedTiles.length) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (let i = 0; i < selectedTiles.length; i++) {
|
|
247
|
+
if (!prevSet.has(selectedTiles[i])) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Returns whether the given sort result still matches the primitive's current
|
|
257
|
+
* sort request and data generation, i.e. it has not been superseded.
|
|
258
|
+
*
|
|
259
|
+
* @param {GaussianSplatPrimitive} primitive The splat primitive.
|
|
260
|
+
* @param {object} activeSort The sort result to validate.
|
|
261
|
+
* @returns {boolean} {@code true} if the sort result is still current.
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
function isActiveSort(primitive, activeSort) {
|
|
265
|
+
return (
|
|
266
|
+
defined(activeSort) &&
|
|
267
|
+
activeSort.requestId === primitive._sortRequestId &&
|
|
268
|
+
activeSort.dataGeneration === primitive._splatDataGeneration
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Destroys the GPU textures owned by a snapshot, if any, and clears the
|
|
274
|
+
* references so they are not used after destruction.
|
|
275
|
+
*
|
|
276
|
+
* @param {GaussianSplatPrimitive.Snapshot|undefined} snapshot The snapshot whose textures should be destroyed.
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
function destroySnapshotTextures(snapshot) {
|
|
280
|
+
if (!defined(snapshot)) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (defined(snapshot.gaussianSplatTexture)) {
|
|
284
|
+
snapshot.gaussianSplatTexture.destroy();
|
|
285
|
+
snapshot.gaussianSplatTexture = undefined;
|
|
286
|
+
}
|
|
287
|
+
if (defined(snapshot.sphericalHarmonicsTexture)) {
|
|
288
|
+
snapshot.sphericalHarmonicsTexture.destroy();
|
|
289
|
+
snapshot.sphericalHarmonicsTexture = undefined;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Schedules a GPU texture for deferred destruction. The texture is kept alive
|
|
295
|
+
* for one additional frame so that any in-flight draw commands that reference
|
|
296
|
+
* it can finish before the underlying GPU resource is released.
|
|
297
|
+
*
|
|
298
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
299
|
+
* @param {Texture|undefined} texture The texture to retire.
|
|
300
|
+
* @param {number} frameNumber The frame number at which the texture was retired.
|
|
301
|
+
* @private
|
|
302
|
+
*/
|
|
303
|
+
function retireTexture(primitive, texture, frameNumber) {
|
|
304
|
+
if (!defined(texture)) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const retired = primitive._retiredTextures;
|
|
308
|
+
retired.push({
|
|
309
|
+
texture: texture,
|
|
310
|
+
frameNumber: frameNumber,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Destroys any retired textures whose grace period (one frame) has elapsed.
|
|
316
|
+
* Called once per frame to reclaim GPU memory from textures that were replaced
|
|
317
|
+
* by a newer snapshot.
|
|
318
|
+
*
|
|
319
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
320
|
+
* @param {number} frameNumber The current frame number.
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
function releaseRetiredTextures(primitive, frameNumber) {
|
|
324
|
+
const retired = primitive._retiredTextures;
|
|
325
|
+
if (!defined(retired) || retired.length === 0) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const next = [];
|
|
329
|
+
for (let i = 0; i < retired.length; i++) {
|
|
330
|
+
const entry = retired[i];
|
|
331
|
+
if (frameNumber - entry.frameNumber > 0) {
|
|
332
|
+
entry.texture.destroy();
|
|
333
|
+
} else {
|
|
334
|
+
next.push(entry);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
primitive._retiredTextures = next;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Atomically promotes a fully-built snapshot to be the active splat data for
|
|
342
|
+
* the primitive. This includes swapping attribute arrays, GPU textures, and
|
|
343
|
+
* sorted indexes, as well as retiring any previously active textures so they
|
|
344
|
+
* can be safely destroyed after the current frame finishes.
|
|
345
|
+
*
|
|
346
|
+
* The snapshot <b>must</b> be in the {@link SnapshotState.READY} state;
|
|
347
|
+
* otherwise a {@link DeveloperError} is thrown.
|
|
348
|
+
*
|
|
349
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
350
|
+
* @param {GaussianSplatPrimitive.Snapshot} snapshot The snapshot to commit.
|
|
351
|
+
* @param {FrameState} frameState The current frame state.
|
|
352
|
+
* @throws {DeveloperError} If the snapshot is not READY.
|
|
353
|
+
* @private
|
|
354
|
+
*/
|
|
355
|
+
function commitSnapshot(primitive, snapshot, frameState) {
|
|
356
|
+
if (!defined(snapshot.indexes) || snapshot.state !== SnapshotState.READY) {
|
|
357
|
+
throw new DeveloperError("Committing snapshot before it is READY.");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const frameNumber = frameState.frameNumber;
|
|
361
|
+
const currentSnapshot = primitive._snapshot;
|
|
362
|
+
const splatTexture = defined(currentSnapshot)
|
|
363
|
+
? currentSnapshot.gaussianSplatTexture
|
|
364
|
+
: primitive.gaussianSplatTexture;
|
|
365
|
+
if (defined(splatTexture) && splatTexture !== snapshot.gaussianSplatTexture) {
|
|
366
|
+
retireTexture(primitive, splatTexture, frameNumber);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const sphericalHarmonicsTexture = defined(currentSnapshot)
|
|
370
|
+
? currentSnapshot.sphericalHarmonicsTexture
|
|
371
|
+
: primitive.sphericalHarmonicsTexture;
|
|
372
|
+
if (
|
|
373
|
+
defined(sphericalHarmonicsTexture) &&
|
|
374
|
+
sphericalHarmonicsTexture !== snapshot.sphericalHarmonicsTexture
|
|
375
|
+
) {
|
|
376
|
+
retireTexture(primitive, sphericalHarmonicsTexture, frameNumber);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
primitive._snapshot = snapshot;
|
|
380
|
+
primitive._positions = snapshot.positions;
|
|
381
|
+
primitive._rotations = snapshot.rotations;
|
|
382
|
+
primitive._scales = snapshot.scales;
|
|
383
|
+
primitive._colors = snapshot.colors;
|
|
384
|
+
primitive._shData = snapshot.shData;
|
|
385
|
+
primitive._sphericalHarmonicsDegree = snapshot.sphericalHarmonicsDegree;
|
|
386
|
+
primitive._numSplats = snapshot.numSplats;
|
|
387
|
+
primitive._indexes = snapshot.indexes;
|
|
388
|
+
primitive.gaussianSplatTexture = snapshot.gaussianSplatTexture;
|
|
389
|
+
primitive.sphericalHarmonicsTexture = snapshot.sphericalHarmonicsTexture;
|
|
390
|
+
primitive._lastTextureWidth = snapshot.lastTextureWidth;
|
|
391
|
+
primitive._lastTextureHeight = snapshot.lastTextureHeight;
|
|
392
|
+
primitive._hasGaussianSplatTexture = defined(snapshot.gaussianSplatTexture);
|
|
393
|
+
primitive._needsGaussianSplatTexture = false;
|
|
394
|
+
primitive._gaussianSplatTexturePending = false;
|
|
395
|
+
|
|
396
|
+
primitive._vertexArray = undefined;
|
|
397
|
+
primitive._vertexArrayLen = -1;
|
|
398
|
+
primitive._drawCommand = undefined;
|
|
399
|
+
primitive._sorterPromise = undefined;
|
|
400
|
+
primitive._activeSort = undefined;
|
|
401
|
+
primitive._sorterState = GaussianSplatSortingState.IDLE;
|
|
402
|
+
primitive._dirty = false;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Finalizes async splat texture generation for a snapshot. The resolved data
|
|
407
|
+
* updates or recreates GPU textures, and the snapshot transitions to
|
|
408
|
+
* {@link SnapshotState.TEXTURE_READY} when complete.
|
|
409
|
+
*
|
|
410
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
411
|
+
* @param {FrameState} frameState The current frame state.
|
|
412
|
+
* @param {GaussianSplatPrimitive.Snapshot} snapshot Snapshot being populated.
|
|
413
|
+
* @param {Promise<GaussianSplatPrimitive.AttributeTextureData>} promise Promise that resolves to packed splat texture data.
|
|
414
|
+
* @returns {Promise<void>}
|
|
415
|
+
* @private
|
|
416
|
+
*/
|
|
417
|
+
async function processGeneratedSplatTextureData(
|
|
418
|
+
primitive,
|
|
419
|
+
frameState,
|
|
420
|
+
snapshot,
|
|
421
|
+
promise,
|
|
422
|
+
) {
|
|
423
|
+
try {
|
|
424
|
+
const splatTextureData = await promise;
|
|
425
|
+
if (primitive._pendingSnapshot !== snapshot) {
|
|
426
|
+
snapshot.state = SnapshotState.BUILDING;
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (!defined(snapshot.gaussianSplatTexture)) {
|
|
430
|
+
snapshot.gaussianSplatTexture = createGaussianSplatTexture(
|
|
431
|
+
frameState.context,
|
|
432
|
+
splatTextureData,
|
|
433
|
+
);
|
|
434
|
+
} else if (
|
|
435
|
+
snapshot.lastTextureHeight !== splatTextureData.height ||
|
|
436
|
+
snapshot.lastTextureWidth !== splatTextureData.width
|
|
437
|
+
) {
|
|
438
|
+
const oldTex = snapshot.gaussianSplatTexture;
|
|
439
|
+
snapshot.gaussianSplatTexture = createGaussianSplatTexture(
|
|
440
|
+
frameState.context,
|
|
441
|
+
splatTextureData,
|
|
442
|
+
);
|
|
443
|
+
oldTex.destroy();
|
|
444
|
+
} else {
|
|
445
|
+
snapshot.gaussianSplatTexture.copyFrom({
|
|
446
|
+
source: {
|
|
447
|
+
width: splatTextureData.width,
|
|
448
|
+
height: splatTextureData.height,
|
|
449
|
+
arrayBufferView: splatTextureData.data,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
snapshot.lastTextureHeight = splatTextureData.height;
|
|
454
|
+
snapshot.lastTextureWidth = splatTextureData.width;
|
|
455
|
+
|
|
456
|
+
if (defined(snapshot.shData) && snapshot.sphericalHarmonicsDegree > 0) {
|
|
457
|
+
const oldTex = snapshot.sphericalHarmonicsTexture;
|
|
458
|
+
const width = ContextLimits.maximumTextureSize;
|
|
459
|
+
const dims = snapshot.shCoefficientCount / 3;
|
|
460
|
+
const splatsPerRow = Math.floor(width / dims);
|
|
461
|
+
const floatsPerRow = splatsPerRow * (dims * 2);
|
|
462
|
+
const texBuf = new Uint32Array(
|
|
463
|
+
width * Math.ceil(snapshot.numSplats / splatsPerRow) * 2,
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
let dataIndex = 0;
|
|
467
|
+
for (let i = 0; dataIndex < snapshot.shData.length; i += width * 2) {
|
|
468
|
+
texBuf.set(
|
|
469
|
+
snapshot.shData.subarray(dataIndex, dataIndex + floatsPerRow),
|
|
470
|
+
i,
|
|
471
|
+
);
|
|
472
|
+
dataIndex += floatsPerRow;
|
|
473
|
+
}
|
|
474
|
+
snapshot.sphericalHarmonicsTexture = createSphericalHarmonicsTexture(
|
|
475
|
+
frameState.context,
|
|
476
|
+
{
|
|
477
|
+
data: texBuf,
|
|
478
|
+
width: width,
|
|
479
|
+
height: Math.ceil(snapshot.numSplats / splatsPerRow),
|
|
480
|
+
},
|
|
481
|
+
);
|
|
482
|
+
if (defined(oldTex)) {
|
|
483
|
+
oldTex.destroy();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
snapshot.state = SnapshotState.TEXTURE_READY;
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error("Error generating Gaussian splat texture:", error);
|
|
490
|
+
snapshot.state = SnapshotState.BUILDING;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Resolves an in-flight sort for a pending snapshot, validates that the result
|
|
496
|
+
* still matches the active generation/request, and commits the snapshot when
|
|
497
|
+
* valid.
|
|
498
|
+
*
|
|
499
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
500
|
+
* @param {FrameState} frameState The current frame state.
|
|
501
|
+
* @param {object|undefined} pendingSort Pending sort metadata.
|
|
502
|
+
* @param {Promise<Uint32Array>} sortPromise Promise that resolves to sorted indexes.
|
|
503
|
+
* @returns {Promise<void>}
|
|
504
|
+
* @private
|
|
505
|
+
*/
|
|
506
|
+
async function resolvePendingSnapshotSort(
|
|
507
|
+
primitive,
|
|
508
|
+
frameState,
|
|
509
|
+
pendingSort,
|
|
510
|
+
sortPromise,
|
|
511
|
+
) {
|
|
512
|
+
try {
|
|
513
|
+
const sortedData = await sortPromise;
|
|
514
|
+
if (
|
|
515
|
+
!defined(pendingSort) ||
|
|
516
|
+
pendingSort.snapshot !== primitive._pendingSnapshot
|
|
517
|
+
) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const expectedCount = pendingSort.expectedCount;
|
|
521
|
+
const currentCount = expectedCount;
|
|
522
|
+
const sortedLen = sortedData?.length;
|
|
523
|
+
if (expectedCount !== currentCount || sortedLen !== expectedCount) {
|
|
524
|
+
primitive._pendingSortPromise = undefined;
|
|
525
|
+
primitive._pendingSort = undefined;
|
|
526
|
+
if (pendingSort.snapshot.state === SnapshotState.SORTING) {
|
|
527
|
+
pendingSort.snapshot.state = SnapshotState.TEXTURE_READY;
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const pending = pendingSort.snapshot;
|
|
533
|
+
pending.indexes = sortedData;
|
|
534
|
+
pending.state = SnapshotState.READY;
|
|
535
|
+
primitive._pendingSortPromise = undefined;
|
|
536
|
+
primitive._pendingSort = undefined;
|
|
537
|
+
commitSnapshot(primitive, pending, frameState);
|
|
538
|
+
primitive._pendingSnapshot = undefined;
|
|
539
|
+
GaussianSplatPrimitive.buildGSplatDrawCommand(primitive, frameState);
|
|
540
|
+
} catch (err) {
|
|
541
|
+
if (
|
|
542
|
+
!defined(pendingSort) ||
|
|
543
|
+
pendingSort.snapshot !== primitive._pendingSnapshot
|
|
544
|
+
) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
primitive._pendingSortPromise = undefined;
|
|
548
|
+
primitive._pendingSort = undefined;
|
|
549
|
+
if (pendingSort.snapshot.state === SnapshotState.SORTING) {
|
|
550
|
+
pendingSort.snapshot.state = SnapshotState.TEXTURE_READY;
|
|
551
|
+
}
|
|
552
|
+
primitive._sorterState = GaussianSplatSortingState.ERROR;
|
|
553
|
+
primitive._sorterError = err;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Resolves an in-flight steady-state sort for the current committed snapshot.
|
|
559
|
+
* Results are ignored when superseded; otherwise, they advance sorting state
|
|
560
|
+
* to {@link GaussianSplatSortingState.SORTED}.
|
|
561
|
+
*
|
|
562
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
563
|
+
* @param {object|undefined} activeSort Active sort metadata.
|
|
564
|
+
* @param {Promise<Uint32Array>} sortPromise Promise that resolves to sorted indexes.
|
|
565
|
+
* @returns {Promise<void>}
|
|
566
|
+
* @private
|
|
567
|
+
*/
|
|
568
|
+
async function resolveSteadySort(primitive, activeSort, sortPromise) {
|
|
569
|
+
try {
|
|
570
|
+
const sortedData = await sortPromise;
|
|
571
|
+
const isActive = isActiveSort(primitive, activeSort);
|
|
572
|
+
const expectedCount = activeSort?.expectedCount;
|
|
573
|
+
const currentCount = expectedCount;
|
|
574
|
+
const sortedLen = sortedData?.length;
|
|
575
|
+
const isMismatch =
|
|
576
|
+
expectedCount !== currentCount || sortedLen !== expectedCount;
|
|
577
|
+
if (!isActive || isMismatch) {
|
|
578
|
+
if (isActive) {
|
|
579
|
+
primitive._sorterPromise = undefined;
|
|
580
|
+
primitive._sorterState = GaussianSplatSortingState.IDLE;
|
|
581
|
+
}
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
primitive._indexes = sortedData;
|
|
585
|
+
primitive._sorterState = GaussianSplatSortingState.SORTED;
|
|
586
|
+
} catch (err) {
|
|
587
|
+
if (!isActiveSort(primitive, activeSort)) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
primitive._sorterState = GaussianSplatSortingState.ERROR;
|
|
591
|
+
primitive._sorterError = err;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Creates a GPU texture that stores packed spherical harmonics coefficient
|
|
597
|
+
* data for all splats. The texture uses a two-channel unsigned-integer format
|
|
598
|
+
* ({@link PixelFormat.RG_INTEGER}) and nearest-neighbor sampling.
|
|
599
|
+
*
|
|
600
|
+
* @param {Context} context The WebGL context.
|
|
601
|
+
* @param {GaussianSplatPrimitive.SphericalHarmonicsTextureData} shData Packed SH texture payload.
|
|
602
|
+
* @returns {Texture} The created texture.
|
|
603
|
+
* @private
|
|
604
|
+
*/
|
|
50
605
|
function createSphericalHarmonicsTexture(context, shData) {
|
|
51
606
|
const texture = new Texture({
|
|
52
607
|
context: context,
|
|
@@ -66,6 +621,17 @@ function createSphericalHarmonicsTexture(context, shData) {
|
|
|
66
621
|
return texture;
|
|
67
622
|
}
|
|
68
623
|
|
|
624
|
+
/**
|
|
625
|
+
* Creates a GPU texture that stores the packed Gaussian splat attributes
|
|
626
|
+
* (positions, scales, rotations, colors). The texture uses an RGBA
|
|
627
|
+
* unsigned-integer format ({@link PixelFormat.RGBA_INTEGER}) and
|
|
628
|
+
* nearest-neighbor sampling.
|
|
629
|
+
*
|
|
630
|
+
* @param {Context} context The WebGL context.
|
|
631
|
+
* @param {GaussianSplatPrimitive.AttributeTextureData} splatTextureData Packed splat texture payload.
|
|
632
|
+
* @returns {Texture} The created texture.
|
|
633
|
+
* @private
|
|
634
|
+
*/
|
|
69
635
|
function createGaussianSplatTexture(context, splatTextureData) {
|
|
70
636
|
return new Texture({
|
|
71
637
|
context: context,
|
|
@@ -144,6 +710,21 @@ function GaussianSplatPrimitive(options) {
|
|
|
144
710
|
* @private
|
|
145
711
|
*/
|
|
146
712
|
this._needsGaussianSplatTexture = true;
|
|
713
|
+
this._snapshot = undefined;
|
|
714
|
+
this._pendingSnapshot = undefined;
|
|
715
|
+
this._retiredTextures = [];
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Scratch buffer re-used across frames for aggregating packed spherical
|
|
719
|
+
* harmonics data so that a fresh typed-array allocation is avoided on
|
|
720
|
+
* every tile-selection change.
|
|
721
|
+
* @type {undefined|Uint32Array}
|
|
722
|
+
* @private
|
|
723
|
+
*/
|
|
724
|
+
this._scratchAggregateShBuffer = undefined;
|
|
725
|
+
this._selectedTilesStableFrames = 0;
|
|
726
|
+
this._needsSnapshotRebuild = false;
|
|
727
|
+
this._snapshotRebuildStallFrames = 0;
|
|
147
728
|
|
|
148
729
|
/**
|
|
149
730
|
* The previous view matrix used to determine if the primitive needs to be updated.
|
|
@@ -230,6 +811,7 @@ function GaussianSplatPrimitive(options) {
|
|
|
230
811
|
* @private
|
|
231
812
|
*/
|
|
232
813
|
this.selectedTileLength = 0;
|
|
814
|
+
this._selectedTileSet = new Set();
|
|
233
815
|
|
|
234
816
|
/**
|
|
235
817
|
* Indicates whether or not the primitive is ready for use.
|
|
@@ -258,6 +840,7 @@ function GaussianSplatPrimitive(options) {
|
|
|
258
840
|
* @private
|
|
259
841
|
*/
|
|
260
842
|
this._drawCommand = undefined;
|
|
843
|
+
this._drawCommandModelMatrix = new Matrix4();
|
|
261
844
|
/**
|
|
262
845
|
* The root transform of the tileset.
|
|
263
846
|
* This is used to transform the splats into world space.
|
|
@@ -298,6 +881,16 @@ function GaussianSplatPrimitive(options) {
|
|
|
298
881
|
* @private
|
|
299
882
|
*/
|
|
300
883
|
this._sorterPromise = undefined;
|
|
884
|
+
this._splatDataGeneration = 0;
|
|
885
|
+
this._sortRequestId = 0;
|
|
886
|
+
this._activeSort = undefined;
|
|
887
|
+
this._pendingSortPromise = undefined;
|
|
888
|
+
this._pendingSort = undefined;
|
|
889
|
+
this._lastSteadySortFrameNumber = -1;
|
|
890
|
+
this._lastSteadySortCameraPosition = new Cartesian3();
|
|
891
|
+
this._hasLastSteadySortCameraPosition = false;
|
|
892
|
+
this._lastSteadySortCameraDirection = new Cartesian3();
|
|
893
|
+
this._hasLastSteadySortCameraDirection = false;
|
|
301
894
|
|
|
302
895
|
/**
|
|
303
896
|
* An error that occurred during the Gaussian splat sorting operation.
|
|
@@ -321,6 +914,23 @@ Object.defineProperties(GaussianSplatPrimitive.prototype, {
|
|
|
321
914
|
},
|
|
322
915
|
},
|
|
323
916
|
|
|
917
|
+
/**
|
|
918
|
+
* Indicates whether the primitive has completed loading and sorting.
|
|
919
|
+
* @memberof GaussianSplatPrimitive.prototype
|
|
920
|
+
* @type {boolean}
|
|
921
|
+
* @private
|
|
922
|
+
* @readonly
|
|
923
|
+
*/
|
|
924
|
+
isStable: {
|
|
925
|
+
get: function () {
|
|
926
|
+
return (
|
|
927
|
+
!this._dirty &&
|
|
928
|
+
(!defined(this._pendingSnapshot) ||
|
|
929
|
+
this._pendingSnapshot.state === SnapshotState.READY)
|
|
930
|
+
);
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
|
|
324
934
|
/**
|
|
325
935
|
* The {@link SplitDirection} to apply to this point.
|
|
326
936
|
* @memberof GaussianSplatPrimitive.prototype
|
|
@@ -362,10 +972,18 @@ GaussianSplatPrimitive.prototype.destroy = function () {
|
|
|
362
972
|
this._scales = undefined;
|
|
363
973
|
this._colors = undefined;
|
|
364
974
|
this._indexes = undefined;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
975
|
+
destroySnapshotTextures(this._pendingSnapshot);
|
|
976
|
+
destroySnapshotTextures(this._snapshot);
|
|
977
|
+
if (defined(this._retiredTextures)) {
|
|
978
|
+
for (let i = 0; i < this._retiredTextures.length; i++) {
|
|
979
|
+
this._retiredTextures[i].texture.destroy();
|
|
980
|
+
}
|
|
368
981
|
}
|
|
982
|
+
this._retiredTextures = [];
|
|
983
|
+
this._pendingSnapshot = undefined;
|
|
984
|
+
this._snapshot = undefined;
|
|
985
|
+
this.gaussianSplatTexture = undefined;
|
|
986
|
+
this.sphericalHarmonicsTexture = undefined;
|
|
369
987
|
|
|
370
988
|
const drawCommand = this._drawCommand;
|
|
371
989
|
if (defined(drawCommand)) {
|
|
@@ -528,74 +1146,39 @@ GaussianSplatPrimitive.transformTile = function (tile) {
|
|
|
528
1146
|
*
|
|
529
1147
|
* @see {@link GaussianSplatTextureGenerator}
|
|
530
1148
|
*
|
|
531
|
-
* @param {GaussianSplatPrimitive} primitive
|
|
532
|
-
* @param {FrameState} frameState
|
|
1149
|
+
* @param {GaussianSplatPrimitive} primitive The owning primitive.
|
|
1150
|
+
* @param {FrameState} frameState The current frame state.
|
|
1151
|
+
* @param {GaussianSplatPrimitive.Snapshot} snapshot Snapshot being populated.
|
|
533
1152
|
* @private
|
|
534
1153
|
*/
|
|
535
|
-
GaussianSplatPrimitive.generateSplatTexture = function (
|
|
536
|
-
primitive
|
|
1154
|
+
GaussianSplatPrimitive.generateSplatTexture = function (
|
|
1155
|
+
primitive,
|
|
1156
|
+
frameState,
|
|
1157
|
+
snapshot,
|
|
1158
|
+
) {
|
|
1159
|
+
if (!defined(snapshot) || snapshot.state !== SnapshotState.BUILDING) {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
snapshot.state = SnapshotState.TEXTURE_PENDING;
|
|
537
1163
|
const promise = GaussianSplatTextureGenerator.generateFromAttributes({
|
|
538
1164
|
attributes: {
|
|
539
|
-
positions: new Float32Array(
|
|
540
|
-
scales: new Float32Array(
|
|
541
|
-
rotations: new Float32Array(
|
|
542
|
-
colors: new Uint8Array(
|
|
1165
|
+
positions: new Float32Array(snapshot.positions),
|
|
1166
|
+
scales: new Float32Array(snapshot.scales),
|
|
1167
|
+
rotations: new Float32Array(snapshot.rotations),
|
|
1168
|
+
colors: new Uint8Array(snapshot.colors),
|
|
543
1169
|
},
|
|
544
|
-
count:
|
|
1170
|
+
count: snapshot.numSplats,
|
|
545
1171
|
});
|
|
546
1172
|
if (!defined(promise)) {
|
|
547
|
-
|
|
1173
|
+
snapshot.state = SnapshotState.BUILDING;
|
|
548
1174
|
return;
|
|
549
1175
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
splatTextureData,
|
|
557
|
-
);
|
|
558
|
-
} else if (
|
|
559
|
-
primitive._lastTextureHeight !== splatTextureData.height ||
|
|
560
|
-
primitive._lastTextureWidth !== splatTextureData.width
|
|
561
|
-
) {
|
|
562
|
-
const oldTex = primitive.gaussianSplatTexture;
|
|
563
|
-
primitive._gaussianSplatTexture = createGaussianSplatTexture(
|
|
564
|
-
frameState.context,
|
|
565
|
-
splatTextureData,
|
|
566
|
-
);
|
|
567
|
-
oldTex.destroy();
|
|
568
|
-
} else {
|
|
569
|
-
primitive.gaussianSplatTexture.copyFrom({
|
|
570
|
-
source: {
|
|
571
|
-
width: splatTextureData.width,
|
|
572
|
-
height: splatTextureData.height,
|
|
573
|
-
arrayBufferView: splatTextureData.data,
|
|
574
|
-
},
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
primitive._vertexArray = undefined;
|
|
578
|
-
primitive._lastTextureHeight = splatTextureData.height;
|
|
579
|
-
primitive._lastTextureWidth = splatTextureData.width;
|
|
580
|
-
|
|
581
|
-
primitive._hasGaussianSplatTexture = true;
|
|
582
|
-
primitive._needsGaussianSplatTexture = false;
|
|
583
|
-
primitive._gaussianSplatTexturePending = false;
|
|
584
|
-
|
|
585
|
-
if (
|
|
586
|
-
!defined(primitive._indexes) ||
|
|
587
|
-
primitive._indexes.length < primitive._numSplats
|
|
588
|
-
) {
|
|
589
|
-
primitive._indexes = new Uint32Array(primitive._numSplats);
|
|
590
|
-
}
|
|
591
|
-
for (let i = 0; i < primitive._numSplats; ++i) {
|
|
592
|
-
primitive._indexes[i] = i;
|
|
593
|
-
}
|
|
594
|
-
})
|
|
595
|
-
.catch((error) => {
|
|
596
|
-
console.error("Error generating Gaussian splat texture:", error);
|
|
597
|
-
primitive._gaussianSplatTexturePending = false;
|
|
598
|
-
});
|
|
1176
|
+
void processGeneratedSplatTextureData(
|
|
1177
|
+
primitive,
|
|
1178
|
+
frameState,
|
|
1179
|
+
snapshot,
|
|
1180
|
+
promise,
|
|
1181
|
+
);
|
|
599
1182
|
};
|
|
600
1183
|
|
|
601
1184
|
/**
|
|
@@ -706,7 +1289,10 @@ GaussianSplatPrimitive.buildGSplatDrawCommand = function (
|
|
|
706
1289
|
return primitive.splitDirection;
|
|
707
1290
|
};
|
|
708
1291
|
|
|
709
|
-
|
|
1292
|
+
const instanceCount = defined(primitive._indexes)
|
|
1293
|
+
? primitive._indexes.length
|
|
1294
|
+
: primitive._numSplats;
|
|
1295
|
+
renderResources.instanceCount = instanceCount;
|
|
710
1296
|
renderResources.count = 4;
|
|
711
1297
|
renderResources.primitiveType = PrimitiveType.TRIANGLE_STRIP;
|
|
712
1298
|
shaderBuilder.addVertexLines(GaussianSplatVS);
|
|
@@ -738,10 +1324,10 @@ GaussianSplatPrimitive.buildGSplatDrawCommand = function (
|
|
|
738
1324
|
idxAttr.constant = 0;
|
|
739
1325
|
idxAttr.instanceDivisor = 1;
|
|
740
1326
|
|
|
741
|
-
|
|
1327
|
+
const needsRebuild =
|
|
742
1328
|
!defined(primitive._vertexArray) ||
|
|
743
|
-
primitive._indexes.length > primitive._vertexArrayLen
|
|
744
|
-
) {
|
|
1329
|
+
primitive._indexes.length > primitive._vertexArrayLen;
|
|
1330
|
+
if (needsRebuild) {
|
|
745
1331
|
const geometry = new Geometry({
|
|
746
1332
|
attributes: {
|
|
747
1333
|
screenQuadPosition: new GeometryAttribute({
|
|
@@ -774,7 +1360,7 @@ GaussianSplatPrimitive.buildGSplatDrawCommand = function (
|
|
|
774
1360
|
const modelMatrix = Matrix4.multiply(
|
|
775
1361
|
tileset.modelMatrix,
|
|
776
1362
|
primitive._rootTransform,
|
|
777
|
-
|
|
1363
|
+
primitive._drawCommandModelMatrix,
|
|
778
1364
|
);
|
|
779
1365
|
|
|
780
1366
|
const vertexArrayCache = primitive._vertexArray;
|
|
@@ -812,7 +1398,9 @@ GaussianSplatPrimitive.buildGSplatDrawCommand = function (
|
|
|
812
1398
|
GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
813
1399
|
const tileset = this._tileset;
|
|
814
1400
|
|
|
815
|
-
|
|
1401
|
+
releaseRetiredTextures(this, frameState.frameNumber);
|
|
1402
|
+
|
|
1403
|
+
if (!tileset.show) {
|
|
816
1404
|
return;
|
|
817
1405
|
}
|
|
818
1406
|
|
|
@@ -824,6 +1412,7 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
824
1412
|
this._dirty = true;
|
|
825
1413
|
return;
|
|
826
1414
|
}
|
|
1415
|
+
const hasRootTransform = defined(this._rootTransform);
|
|
827
1416
|
|
|
828
1417
|
if (frameState.passes.pick === true) {
|
|
829
1418
|
return;
|
|
@@ -832,29 +1421,72 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
832
1421
|
if (this.splitDirection !== tileset.splitDirection) {
|
|
833
1422
|
this.splitDirection = tileset.splitDirection;
|
|
834
1423
|
}
|
|
1424
|
+
const camera = frameState.camera;
|
|
1425
|
+
if (!defined(camera)) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
835
1428
|
|
|
836
1429
|
if (this._sorterState === GaussianSplatSortingState.IDLE) {
|
|
1430
|
+
const selectedTilesChanged =
|
|
1431
|
+
tileset._selectedTiles.length !== 0 &&
|
|
1432
|
+
haveSelectedTilesChanged(this, tileset._selectedTiles);
|
|
1433
|
+
if (tileset._selectedTiles.length === 0) {
|
|
1434
|
+
this._selectedTilesStableFrames = 0;
|
|
1435
|
+
this._needsSnapshotRebuild = false;
|
|
1436
|
+
this._snapshotRebuildStallFrames = 0;
|
|
1437
|
+
} else if (selectedTilesChanged) {
|
|
1438
|
+
this._selectedTilesStableFrames = 0;
|
|
1439
|
+
} else {
|
|
1440
|
+
this._selectedTilesStableFrames++;
|
|
1441
|
+
}
|
|
1442
|
+
if (selectedTilesChanged || this._dirty) {
|
|
1443
|
+
this._needsSnapshotRebuild = true;
|
|
1444
|
+
}
|
|
1445
|
+
const isStable = this._selectedTilesStableFrames >= DEFAULT_STABLE_FRAMES;
|
|
1446
|
+
const isBootstrap =
|
|
1447
|
+
!defined(this._snapshot) &&
|
|
1448
|
+
!defined(this._pendingSnapshot) &&
|
|
1449
|
+
!defined(this._drawCommand);
|
|
1450
|
+
// This prevents an indefinite wait if selected tiles never settle completely.
|
|
1451
|
+
// In practice, this is the upper bound on "wait-for-stability" before forcing
|
|
1452
|
+
// a rebuild to avoid visible starvation.
|
|
1453
|
+
if (this._needsSnapshotRebuild && tileset._selectedTiles.length !== 0) {
|
|
1454
|
+
this._snapshotRebuildStallFrames++;
|
|
1455
|
+
} else {
|
|
1456
|
+
this._snapshotRebuildStallFrames = 0;
|
|
1457
|
+
}
|
|
1458
|
+
const allowRebuild =
|
|
1459
|
+
isStable ||
|
|
1460
|
+
isBootstrap ||
|
|
1461
|
+
this._snapshotRebuildStallFrames >= DEFAULT_MAX_SNAPSHOT_STALL_FRAMES;
|
|
1462
|
+
const hasPendingWork =
|
|
1463
|
+
this._dirty ||
|
|
1464
|
+
this._needsSnapshotRebuild ||
|
|
1465
|
+
selectedTilesChanged ||
|
|
1466
|
+
defined(this._pendingSnapshot) ||
|
|
1467
|
+
defined(this._pendingSortPromise) ||
|
|
1468
|
+
!defined(this._drawCommand);
|
|
837
1469
|
if (
|
|
838
|
-
!
|
|
839
|
-
Matrix4.equals(
|
|
1470
|
+
!hasPendingWork &&
|
|
1471
|
+
Matrix4.equals(camera.viewMatrix, this._prevViewMatrix)
|
|
840
1472
|
) {
|
|
841
|
-
// No need to update if the view matrix hasn't changed and the primitive isn't dirty.
|
|
842
1473
|
return;
|
|
843
1474
|
}
|
|
844
1475
|
|
|
845
1476
|
if (
|
|
846
1477
|
tileset._selectedTiles.length !== 0 &&
|
|
847
|
-
|
|
1478
|
+
this._needsSnapshotRebuild &&
|
|
1479
|
+
allowRebuild
|
|
848
1480
|
) {
|
|
849
|
-
this.
|
|
850
|
-
this.
|
|
851
|
-
this.
|
|
852
|
-
this.
|
|
853
|
-
this.
|
|
854
|
-
this.
|
|
855
|
-
this.
|
|
856
|
-
|
|
857
|
-
|
|
1481
|
+
this._splatDataGeneration++;
|
|
1482
|
+
this._activeSort = undefined;
|
|
1483
|
+
this._sorterPromise = undefined;
|
|
1484
|
+
this._sorterState = GaussianSplatSortingState.IDLE;
|
|
1485
|
+
this._pendingSortPromise = undefined;
|
|
1486
|
+
this._pendingSort = undefined;
|
|
1487
|
+
if (defined(this._pendingSnapshot)) {
|
|
1488
|
+
destroySnapshotTextures(this._pendingSnapshot);
|
|
1489
|
+
}
|
|
858
1490
|
|
|
859
1491
|
const tiles = tileset._selectedTiles;
|
|
860
1492
|
const totalElements = tiles.reduce(
|
|
@@ -890,49 +1522,78 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
890
1522
|
};
|
|
891
1523
|
|
|
892
1524
|
const aggregateShData = () => {
|
|
893
|
-
|
|
1525
|
+
// Determine the SH degree from the first tile with SH data so we can
|
|
1526
|
+
// pre-allocate the aggregate buffer once, outside the tile loop.
|
|
1527
|
+
let coefs = 0;
|
|
894
1528
|
for (const tile of tiles) {
|
|
895
|
-
const shData = tile.content.packedSphericalHarmonicsData;
|
|
896
1529
|
if (tile.content.sphericalHarmonicsDegree > 0) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
coefs = 45;
|
|
908
|
-
}
|
|
909
|
-
this._shData = new Uint32Array(totalElements * (coefs * (2 / 3)));
|
|
1530
|
+
switch (tile.content.sphericalHarmonicsDegree) {
|
|
1531
|
+
case 1:
|
|
1532
|
+
coefs = 9;
|
|
1533
|
+
break;
|
|
1534
|
+
case 2:
|
|
1535
|
+
coefs = 24;
|
|
1536
|
+
break;
|
|
1537
|
+
case 3:
|
|
1538
|
+
coefs = 45;
|
|
1539
|
+
break;
|
|
910
1540
|
}
|
|
911
|
-
|
|
912
|
-
offset += shData.length;
|
|
1541
|
+
break;
|
|
913
1542
|
}
|
|
914
1543
|
}
|
|
1544
|
+
|
|
1545
|
+
if (coefs === 0) {
|
|
1546
|
+
return undefined;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const requiredLength = totalElements * (coefs * (2 / 3));
|
|
1550
|
+
|
|
1551
|
+
// Re-use the class-level scratch buffer when it is already large
|
|
1552
|
+
// enough, avoiding a fresh allocation (and eventual GC) every frame.
|
|
1553
|
+
if (
|
|
1554
|
+
!defined(this._scratchAggregateShBuffer) ||
|
|
1555
|
+
this._scratchAggregateShBuffer.length < requiredLength
|
|
1556
|
+
) {
|
|
1557
|
+
this._scratchAggregateShBuffer = new Uint32Array(requiredLength);
|
|
1558
|
+
}
|
|
1559
|
+
const aggregate = this._scratchAggregateShBuffer;
|
|
1560
|
+
|
|
1561
|
+
let offset = 0;
|
|
1562
|
+
for (const tile of tiles) {
|
|
1563
|
+
const tileShData = tile.content.packedSphericalHarmonicsData;
|
|
1564
|
+
if (tile.content.sphericalHarmonicsDegree > 0) {
|
|
1565
|
+
aggregate.set(tileShData, offset);
|
|
1566
|
+
offset += tileShData.length;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// Return a correctly-sized view so downstream consumers see the
|
|
1571
|
+
// exact element count they expect.
|
|
1572
|
+
if (offset < aggregate.length) {
|
|
1573
|
+
return aggregate.subarray(0, offset);
|
|
1574
|
+
}
|
|
1575
|
+
return aggregate;
|
|
915
1576
|
};
|
|
916
1577
|
|
|
917
|
-
|
|
1578
|
+
const positions = aggregateAttributeValues(
|
|
918
1579
|
ComponentDatatype.FLOAT,
|
|
919
1580
|
(content) => content.positions,
|
|
920
1581
|
3,
|
|
921
1582
|
);
|
|
922
1583
|
|
|
923
|
-
|
|
1584
|
+
const scales = aggregateAttributeValues(
|
|
924
1585
|
ComponentDatatype.FLOAT,
|
|
925
1586
|
(content) => content.scales,
|
|
926
1587
|
3,
|
|
927
1588
|
);
|
|
928
1589
|
|
|
929
|
-
|
|
1590
|
+
const rotations = aggregateAttributeValues(
|
|
930
1591
|
ComponentDatatype.FLOAT,
|
|
931
1592
|
(content) => content.rotations,
|
|
932
1593
|
4,
|
|
933
1594
|
);
|
|
934
1595
|
|
|
935
|
-
|
|
1596
|
+
const colors = aggregateAttributeValues(
|
|
936
1597
|
ComponentDatatype.UNSIGNED_BYTE,
|
|
937
1598
|
(content) =>
|
|
938
1599
|
ModelUtility.getAttributeBySemantic(
|
|
@@ -941,66 +1602,133 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
941
1602
|
),
|
|
942
1603
|
);
|
|
943
1604
|
|
|
944
|
-
|
|
945
|
-
this._sphericalHarmonicsDegree =
|
|
1605
|
+
const sphericalHarmonicsDegree =
|
|
946
1606
|
tiles[0].content.sphericalHarmonicsDegree;
|
|
1607
|
+
const shCoefficientCount =
|
|
1608
|
+
sphericalHarmonicsDegree > 0
|
|
1609
|
+
? tiles[0].content.sphericalHarmonicsCoefficientCount
|
|
1610
|
+
: 0;
|
|
1611
|
+
const shData = aggregateShData();
|
|
1612
|
+
|
|
1613
|
+
this._pendingSnapshot = {
|
|
1614
|
+
generation: this._splatDataGeneration,
|
|
1615
|
+
positions: positions,
|
|
1616
|
+
rotations: rotations,
|
|
1617
|
+
scales: scales,
|
|
1618
|
+
colors: colors,
|
|
1619
|
+
shData: shData,
|
|
1620
|
+
sphericalHarmonicsDegree: sphericalHarmonicsDegree,
|
|
1621
|
+
shCoefficientCount: shCoefficientCount,
|
|
1622
|
+
numSplats: totalElements,
|
|
1623
|
+
indexes: undefined,
|
|
1624
|
+
gaussianSplatTexture: undefined,
|
|
1625
|
+
sphericalHarmonicsTexture: undefined,
|
|
1626
|
+
lastTextureWidth: 0,
|
|
1627
|
+
lastTextureHeight: 0,
|
|
1628
|
+
state: SnapshotState.BUILDING,
|
|
1629
|
+
};
|
|
947
1630
|
|
|
948
|
-
this._numSplats = totalElements;
|
|
949
1631
|
this.selectedTileLength = tileset._selectedTiles.length;
|
|
1632
|
+
this._selectedTileSet = new Set(tileset._selectedTiles);
|
|
1633
|
+
this._dirty = false;
|
|
1634
|
+
this._needsSnapshotRebuild = false;
|
|
1635
|
+
this._snapshotRebuildStallFrames = 0;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
if (defined(this._pendingSnapshot)) {
|
|
1639
|
+
const pending = this._pendingSnapshot;
|
|
1640
|
+
if (pending.state === SnapshotState.BUILDING) {
|
|
1641
|
+
GaussianSplatPrimitive.generateSplatTexture(this, frameState, pending);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
if (pending.state === SnapshotState.TEXTURE_PENDING) {
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
if (
|
|
1648
|
+
pending.state === SnapshotState.TEXTURE_READY &&
|
|
1649
|
+
!defined(pending.gaussianSplatTexture)
|
|
1650
|
+
) {
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
if (!hasRootTransform) {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
Matrix4.clone(camera.viewMatrix, this._prevViewMatrix);
|
|
1659
|
+
Matrix4.multiply(camera.viewMatrix, this._rootTransform, scratchMatrix4A);
|
|
1660
|
+
|
|
1661
|
+
if (
|
|
1662
|
+
pending.state === SnapshotState.TEXTURE_READY &&
|
|
1663
|
+
!defined(this._pendingSortPromise)
|
|
1664
|
+
) {
|
|
1665
|
+
const requestId = ++this._sortRequestId;
|
|
1666
|
+
const dataGeneration = this._splatDataGeneration;
|
|
1667
|
+
this._pendingSort = {
|
|
1668
|
+
requestId: requestId,
|
|
1669
|
+
dataGeneration: dataGeneration,
|
|
1670
|
+
expectedCount: pending.numSplats,
|
|
1671
|
+
snapshot: pending,
|
|
1672
|
+
};
|
|
1673
|
+
const sortPromise = GaussianSplatSorter.radixSortIndexes({
|
|
1674
|
+
primitive: {
|
|
1675
|
+
positions: new Float32Array(pending.positions),
|
|
1676
|
+
modelView: Float32Array.from(scratchMatrix4A),
|
|
1677
|
+
count: pending.numSplats,
|
|
1678
|
+
},
|
|
1679
|
+
sortType: "Index",
|
|
1680
|
+
});
|
|
1681
|
+
if (!defined(sortPromise)) {
|
|
1682
|
+
this._pendingSortPromise = undefined;
|
|
1683
|
+
this._pendingSort = undefined;
|
|
1684
|
+
pending.state = SnapshotState.TEXTURE_READY;
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
this._pendingSortPromise = sortPromise;
|
|
1688
|
+
pending.state = SnapshotState.SORTING;
|
|
1689
|
+
const pendingSort = this._pendingSort;
|
|
1690
|
+
void resolvePendingSnapshotSort(
|
|
1691
|
+
this,
|
|
1692
|
+
frameState,
|
|
1693
|
+
pendingSort,
|
|
1694
|
+
sortPromise,
|
|
1695
|
+
);
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
if (!defined(this._pendingSortPromise)) {
|
|
1700
|
+
if (pending.state === SnapshotState.SORTING) {
|
|
1701
|
+
pending.state = SnapshotState.TEXTURE_READY;
|
|
1702
|
+
}
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
return;
|
|
950
1706
|
}
|
|
951
1707
|
|
|
952
1708
|
if (this._numSplats === 0) {
|
|
953
1709
|
return;
|
|
954
1710
|
}
|
|
955
1711
|
|
|
956
|
-
if (
|
|
957
|
-
if (!this._gaussianSplatTexturePending) {
|
|
958
|
-
GaussianSplatPrimitive.generateSplatTexture(this, frameState);
|
|
959
|
-
if (defined(this._shData)) {
|
|
960
|
-
const oldTex = this.sphericalHarmonicsTexture;
|
|
961
|
-
const width = ContextLimits.maximumTextureSize;
|
|
962
|
-
const dims =
|
|
963
|
-
tileset._selectedTiles[0].content
|
|
964
|
-
.sphericalHarmonicsCoefficientCount / 3;
|
|
965
|
-
const splatsPerRow = Math.floor(width / dims);
|
|
966
|
-
const floatsPerRow = splatsPerRow * (dims * 2);
|
|
967
|
-
const texBuf = new Uint32Array(
|
|
968
|
-
width * Math.ceil(this._numSplats / splatsPerRow) * 2,
|
|
969
|
-
);
|
|
970
|
-
|
|
971
|
-
let dataIndex = 0;
|
|
972
|
-
for (let i = 0; dataIndex < this._shData.length; i += width * 2) {
|
|
973
|
-
texBuf.set(
|
|
974
|
-
this._shData.subarray(dataIndex, dataIndex + floatsPerRow),
|
|
975
|
-
i,
|
|
976
|
-
);
|
|
977
|
-
dataIndex += floatsPerRow;
|
|
978
|
-
}
|
|
979
|
-
this.sphericalHarmonicsTexture = createSphericalHarmonicsTexture(
|
|
980
|
-
frameState.context,
|
|
981
|
-
{
|
|
982
|
-
data: texBuf,
|
|
983
|
-
width: width,
|
|
984
|
-
height: Math.ceil(this._numSplats / splatsPerRow),
|
|
985
|
-
},
|
|
986
|
-
);
|
|
987
|
-
if (defined(oldTex)) {
|
|
988
|
-
oldTex.destroy();
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
1712
|
+
if (!hasRootTransform) {
|
|
992
1713
|
return;
|
|
993
1714
|
}
|
|
994
1715
|
|
|
995
|
-
Matrix4.clone(
|
|
996
|
-
Matrix4.multiply(
|
|
997
|
-
frameState.camera.viewMatrix,
|
|
998
|
-
this._rootTransform,
|
|
999
|
-
scratchMatrix4A,
|
|
1000
|
-
);
|
|
1716
|
+
Matrix4.clone(camera.viewMatrix, this._prevViewMatrix);
|
|
1717
|
+
Matrix4.multiply(camera.viewMatrix, this._rootTransform, scratchMatrix4A);
|
|
1001
1718
|
|
|
1002
1719
|
if (!defined(this._sorterPromise)) {
|
|
1003
|
-
this
|
|
1720
|
+
if (!shouldStartSteadySort(this, frameState)) {
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
const requestId = ++this._sortRequestId;
|
|
1724
|
+
const dataGeneration = this._splatDataGeneration;
|
|
1725
|
+
const expectedCount = this._numSplats;
|
|
1726
|
+
this._activeSort = {
|
|
1727
|
+
requestId: requestId,
|
|
1728
|
+
dataGeneration: dataGeneration,
|
|
1729
|
+
expectedCount: expectedCount,
|
|
1730
|
+
};
|
|
1731
|
+
const rawPromise = GaussianSplatSorter.radixSortIndexes({
|
|
1004
1732
|
primitive: {
|
|
1005
1733
|
positions: new Float32Array(this._positions),
|
|
1006
1734
|
modelView: Float32Array.from(scratchMatrix4A),
|
|
@@ -1008,23 +1736,33 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
1008
1736
|
},
|
|
1009
1737
|
sortType: "Index",
|
|
1010
1738
|
});
|
|
1739
|
+
this._sorterPromise = rawPromise;
|
|
1740
|
+
if (defined(rawPromise)) {
|
|
1741
|
+
markSteadySortStart(this, frameState);
|
|
1742
|
+
const activeSort = this._activeSort;
|
|
1743
|
+
this._sorterState = GaussianSplatSortingState.SORTING;
|
|
1744
|
+
void resolveSteadySort(this, activeSort, rawPromise);
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1011
1747
|
}
|
|
1012
1748
|
|
|
1013
1749
|
if (!defined(this._sorterPromise)) {
|
|
1014
1750
|
this._sorterState = GaussianSplatSortingState.WAITING;
|
|
1015
1751
|
return;
|
|
1016
1752
|
}
|
|
1017
|
-
this.
|
|
1018
|
-
|
|
1019
|
-
this._sorterError = err;
|
|
1020
|
-
});
|
|
1021
|
-
this._sorterPromise.then((sortedData) => {
|
|
1022
|
-
this._indexes = sortedData;
|
|
1023
|
-
this._sorterState = GaussianSplatSortingState.SORTED;
|
|
1024
|
-
});
|
|
1753
|
+
this._sorterState = GaussianSplatSortingState.SORTING;
|
|
1754
|
+
return;
|
|
1025
1755
|
} else if (this._sorterState === GaussianSplatSortingState.WAITING) {
|
|
1026
1756
|
if (!defined(this._sorterPromise)) {
|
|
1027
|
-
|
|
1757
|
+
const requestId = ++this._sortRequestId;
|
|
1758
|
+
const dataGeneration = this._splatDataGeneration;
|
|
1759
|
+
const expectedCount = this._numSplats;
|
|
1760
|
+
this._activeSort = {
|
|
1761
|
+
requestId: requestId,
|
|
1762
|
+
dataGeneration: dataGeneration,
|
|
1763
|
+
expectedCount: expectedCount,
|
|
1764
|
+
};
|
|
1765
|
+
const rawPromise = GaussianSplatSorter.radixSortIndexes({
|
|
1028
1766
|
primitive: {
|
|
1029
1767
|
positions: new Float32Array(this._positions),
|
|
1030
1768
|
modelView: Float32Array.from(scratchMatrix4A),
|
|
@@ -1032,21 +1770,21 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
1032
1770
|
},
|
|
1033
1771
|
sortType: "Index",
|
|
1034
1772
|
});
|
|
1773
|
+
this._sorterPromise = rawPromise;
|
|
1774
|
+
if (defined(rawPromise)) {
|
|
1775
|
+
markSteadySortStart(this, frameState);
|
|
1776
|
+
const activeSort = this._activeSort;
|
|
1777
|
+
this._sorterState = GaussianSplatSortingState.SORTING;
|
|
1778
|
+
void resolveSteadySort(this, activeSort, rawPromise);
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1035
1781
|
}
|
|
1036
1782
|
if (!defined(this._sorterPromise)) {
|
|
1037
1783
|
this._sorterState = GaussianSplatSortingState.WAITING;
|
|
1038
1784
|
return;
|
|
1039
1785
|
}
|
|
1040
|
-
this.
|
|
1041
|
-
|
|
1042
|
-
this._sorterError = err;
|
|
1043
|
-
});
|
|
1044
|
-
this._sorterPromise.then((sortedData) => {
|
|
1045
|
-
this._indexes = sortedData;
|
|
1046
|
-
this._sorterState = GaussianSplatSortingState.SORTED;
|
|
1047
|
-
});
|
|
1048
|
-
|
|
1049
|
-
this._sorterState = GaussianSplatSortingState.SORTING; //set state to sorting
|
|
1786
|
+
this._sorterState = GaussianSplatSortingState.SORTING;
|
|
1787
|
+
return;
|
|
1050
1788
|
} else if (this._sorterState === GaussianSplatSortingState.SORTING) {
|
|
1051
1789
|
return; //still sorting, wait for next frame
|
|
1052
1790
|
} else if (this._sorterState === GaussianSplatSortingState.SORTED) {
|
|
@@ -1055,6 +1793,7 @@ GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
|
1055
1793
|
this._sorterState = GaussianSplatSortingState.IDLE; //reset state for next frame
|
|
1056
1794
|
this._dirty = false;
|
|
1057
1795
|
this._sorterPromise = undefined; //reset promise for next frame
|
|
1796
|
+
this._activeSort = undefined;
|
|
1058
1797
|
} else if (this._sorterState === GaussianSplatSortingState.ERROR) {
|
|
1059
1798
|
throw this._sorterError;
|
|
1060
1799
|
}
|