@cesium/engine 18.0.0 → 18.0.1-splats.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-WTFZHPUW.js → chunk-2AXKROJA.js} +5 -5
- package/Build/Workers/{chunk-UK33ZN4K.js → chunk-2J5ZZ7CY.js} +4 -4
- package/Build/Workers/{chunk-2AXPWBJG.js → chunk-2SYY3UBI.js} +1 -1
- package/Build/Workers/{chunk-ZIP27IX6.js → chunk-2WKW3BEB.js} +6 -6
- package/Build/Workers/{chunk-LIPZUYNR.js → chunk-2ZO6BCHN.js} +11 -11
- package/Build/Workers/{chunk-KGXTCGX3.js → chunk-3UKG5X7F.js} +12 -12
- package/Build/Workers/{chunk-IWGMVQGG.js → chunk-4GYIDPXP.js} +9 -9
- package/Build/Workers/{chunk-NR7KSD56.js → chunk-6EP7ED36.js} +3 -3
- package/Build/Workers/{chunk-J4MAVE6J.js → chunk-6QOBORNG.js} +6 -6
- package/Build/Workers/{chunk-GM55YFOB.js → chunk-6WWHK2ER.js} +14 -14
- package/Build/Workers/{chunk-JF4IYQYC.js → chunk-6Y6TVYHS.js} +7 -7
- package/Build/Workers/{chunk-4MIY63FM.js → chunk-744VWE2U.js} +13 -13
- package/Build/Workers/{chunk-ZADJ6FRX.js → chunk-7PQ52XCW.js} +4 -4
- package/Build/Workers/{chunk-2NKHPWDY.js → chunk-A5UFJW2M.js} +12 -12
- package/Build/Workers/{chunk-2CAYUY2B.js → chunk-AQKVFTWV.js} +8 -8
- package/Build/Workers/{chunk-F4ZAKN4V.js → chunk-BKTEHJFD.js} +2 -2
- package/Build/Workers/{chunk-GAUF4F5A.js → chunk-CQXYKPAW.js} +16 -16
- package/Build/Workers/{chunk-QN7JEOQV.js → chunk-DI7PHKC6.js} +5 -5
- package/Build/Workers/{chunk-NWSAYFZG.js → chunk-DKKIQS6F.js} +6 -6
- package/Build/Workers/{chunk-4DSTDGY3.js → chunk-DTVWE5YL.js} +2 -2
- package/Build/Workers/{chunk-EXEZHW3P.js → chunk-ERPS2WZC.js} +4 -4
- package/Build/Workers/chunk-EYMZ5FSQ.js +280 -0
- package/Build/Workers/{chunk-HQE7KSR4.js → chunk-F43TQW5B.js} +2 -2
- package/Build/Workers/{chunk-W6CPO62M.js → chunk-FOMRN6II.js} +6 -6
- package/Build/Workers/{chunk-VGJSKEIB.js → chunk-FOTXJYKS.js} +4 -4
- package/Build/Workers/{chunk-2QNKQVUT.js → chunk-G2Y7EA4W.js} +5 -5
- package/Build/Workers/{chunk-LZFUG3GO.js → chunk-GY4E4ZP6.js} +10 -10
- package/Build/Workers/{chunk-WYCR5DWQ.js → chunk-HDG3VHYL.js} +2 -2
- package/Build/Workers/{chunk-YV37YFUM.js → chunk-HOEUXPBL.js} +9 -9
- package/Build/Workers/{chunk-OATG6PKE.js → chunk-IWDYVJ56.js} +12 -12
- package/Build/Workers/{chunk-AEGAQ6FD.js → chunk-JA4KPQJL.js} +12 -12
- package/Build/Workers/{chunk-DINSLSJD.js → chunk-JZZQEZMA.js} +6 -6
- package/Build/Workers/{chunk-QUSP3NWA.js → chunk-KPLU3WDY.js} +5 -5
- package/Build/Workers/{chunk-5WVF4BK7.js → chunk-LHM6V4D6.js} +5 -5
- package/Build/Workers/{chunk-FPYBD2P5.js → chunk-LMTGCD6P.js} +1 -1
- package/Build/Workers/{chunk-5OL6XFNS.js → chunk-LWZOYSSF.js} +1 -1
- package/Build/Workers/{chunk-T6B6H3GR.js → chunk-M6U7SV4T.js} +7 -7
- package/Build/Workers/{chunk-NVJ5JRIM.js → chunk-O25M56DO.js} +2 -2
- package/Build/Workers/{chunk-OIHAAHBK.js → chunk-OE2WXO2Z.js} +10 -10
- package/Build/Workers/{chunk-LWJWWCZ4.js → chunk-PFD462OE.js} +6 -6
- package/Build/Workers/{chunk-WGHUCZMX.js → chunk-PPI33PXH.js} +7 -7
- package/Build/Workers/{chunk-CES6DHHV.js → chunk-PRQQHLUB.js} +1 -1
- package/Build/Workers/{chunk-HS76VTVY.js → chunk-RTUZ7MJB.js} +2 -2
- package/Build/Workers/{chunk-EHB2OCQX.js → chunk-S6QBTP5U.js} +4 -4
- package/Build/Workers/{chunk-IWZK2AIH.js → chunk-SMFCIGM3.js} +5 -5
- package/Build/Workers/{chunk-MFGVMUN3.js → chunk-TC3LEKFJ.js} +5 -5
- package/Build/Workers/{chunk-I3BAKR4S.js → chunk-UTLYCPFY.js} +16 -16
- package/Build/Workers/{chunk-GS55WTRX.js → chunk-V7LEBY53.js} +6 -6
- package/Build/Workers/{chunk-RWFYLWSF.js → chunk-VE3UKRDB.js} +9 -9
- package/Build/Workers/{chunk-3RKVDGRF.js → chunk-W3FBD4FE.js} +8 -8
- package/Build/Workers/{chunk-KNA65INJ.js → chunk-YRR6CJGG.js} +4 -4
- package/Build/Workers/{chunk-SIOVJ4L5.js → chunk-ZVTKMLU6.js} +4 -4
- package/Build/Workers/combineGeometry.js +21 -21
- package/Build/Workers/createBoxGeometry.js +15 -15
- package/Build/Workers/createBoxOutlineGeometry.js +13 -13
- package/Build/Workers/createCircleGeometry.js +23 -23
- package/Build/Workers/createCircleOutlineGeometry.js +16 -16
- package/Build/Workers/createCoplanarPolygonGeometry.js +30 -30
- package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +28 -28
- package/Build/Workers/createCorridorGeometry.js +26 -26
- package/Build/Workers/createCorridorOutlineGeometry.js +25 -25
- package/Build/Workers/createCylinderGeometry.js +17 -17
- package/Build/Workers/createCylinderOutlineGeometry.js +15 -15
- package/Build/Workers/createEllipseGeometry.js +23 -23
- package/Build/Workers/createEllipseOutlineGeometry.js +16 -16
- package/Build/Workers/createEllipsoidGeometry.js +16 -16
- package/Build/Workers/createEllipsoidOutlineGeometry.js +15 -15
- package/Build/Workers/createFrustumGeometry.js +15 -15
- package/Build/Workers/createFrustumOutlineGeometry.js +15 -15
- package/Build/Workers/createGeometry.js +23 -21
- package/Build/Workers/createGroundPolylineGeometry.js +19 -19
- package/Build/Workers/createPlaneGeometry.js +13 -13
- package/Build/Workers/createPlaneOutlineGeometry.js +12 -12
- package/Build/Workers/createPolygonGeometry.js +29 -29
- package/Build/Workers/createPolygonOutlineGeometry.js +27 -27
- package/Build/Workers/createPolylineGeometry.js +22 -22
- package/Build/Workers/createPolylineVolumeGeometry.js +28 -28
- package/Build/Workers/createPolylineVolumeOutlineGeometry.js +24 -24
- package/Build/Workers/createRectangleGeometry.js +24 -24
- package/Build/Workers/createRectangleOutlineGeometry.js +17 -17
- package/Build/Workers/createSimplePolylineGeometry.js +20 -20
- package/Build/Workers/createSphereGeometry.js +16 -16
- package/Build/Workers/createSphereOutlineGeometry.js +15 -15
- package/Build/Workers/createTaskProcessorWorker.js +3 -3
- package/Build/Workers/createVectorTileClampedPolylines.js +12 -12
- package/Build/Workers/createVectorTileGeometries.js +21 -21
- package/Build/Workers/createVectorTilePoints.js +11 -11
- package/Build/Workers/createVectorTilePolygons.js +19 -19
- package/Build/Workers/createVectorTilePolylines.js +12 -12
- package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +19 -19
- package/Build/Workers/createVerticesFromHeightmap.js +19 -19
- package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +16 -16
- package/Build/Workers/createWallGeometry.js +21 -21
- package/Build/Workers/createWallOutlineGeometry.js +20 -20
- package/Build/Workers/decodeDraco.js +10 -10
- package/Build/Workers/decodeGoogleEarthEnterprisePacket.js +5 -5
- package/Build/Workers/decodeI3S.js +9 -9
- package/Build/Workers/gaussianSplatSorter.js +62 -0
- package/Build/Workers/gaussianSplatTextureGenerator.js +68 -0
- package/Build/Workers/transcodeKTX2.js +59 -7
- package/Build/Workers/transferTypedArrayTest.js +1 -1
- package/Build/Workers/upsampleQuantizedTerrainMesh.js +19 -19
- package/Source/Core/PixelFormat.js +62 -0
- package/Source/Renderer/Context.js +2 -0
- package/Source/Renderer/Pass.js +3 -2
- package/Source/Renderer/Texture.js +1 -0
- package/Source/Renderer/VertexArray.js +33 -8
- package/Source/Renderer/createUniform.js +2 -0
- package/Source/Scene/Cesium3DTileContentFactory.js +23 -0
- package/Source/Scene/Cesium3DTileset.js +59 -2
- package/Source/Scene/GaussianSplat3DTileContent.js +504 -0
- package/Source/Scene/GaussianSplatPrimitive.js +932 -0
- package/Source/Scene/GaussianSplatRenderResources.js +81 -0
- package/Source/Scene/GaussianSplatSorter.js +73 -0
- package/Source/Scene/GaussianSplatTextureGenerator.js +64 -0
- package/Source/Scene/GltfLoader.js +82 -4
- package/Source/Scene/GltfSpzLoader.js +222 -0
- package/Source/Scene/GltfVertexBufferLoader.js +98 -3
- package/Source/Scene/Model/ModelRuntimePrimitive.js +1 -0
- package/Source/Scene/Model/ModelUtility.js +2 -0
- package/Source/Scene/PrimitiveLoadPlan.js +59 -0
- package/Source/Scene/ResourceCache.js +60 -3
- package/Source/Scene/ResourceCacheKey.js +39 -1
- package/Source/Scene/Scene.js +32 -1
- package/Source/Scene/VertexAttributeSemantic.js +30 -0
- package/Source/Shaders/Builtin/Constants/passGaussianSplats.glsl +9 -0
- package/Source/Shaders/Builtin/Constants/passGaussianSplats.js +11 -0
- package/Source/Shaders/Builtin/Constants/passOverlay.glsl +1 -1
- package/Source/Shaders/Builtin/Constants/passOverlay.js +1 -1
- package/Source/Shaders/Builtin/CzmBuiltins.js +2 -0
- package/Source/Shaders/Model/ModelFS.glsl +3 -1
- package/Source/Shaders/Model/ModelFS.js +3 -1
- package/Source/Shaders/Model/ModelVS.glsl +1 -1
- package/Source/Shaders/Model/ModelVS.js +1 -1
- package/Source/Shaders/Model/PointCloudStylingStageVS.glsl +1 -1
- package/Source/Shaders/Model/PointCloudStylingStageVS.js +2 -1
- package/Source/Shaders/PrimitiveGaussianSplatFS.glsl +18 -0
- package/Source/Shaders/PrimitiveGaussianSplatFS.js +20 -0
- package/Source/Shaders/PrimitiveGaussianSplatVS.glsl +108 -0
- package/Source/Shaders/PrimitiveGaussianSplatVS.js +109 -0
- package/Source/ThirdParty/wasm_splats_bg.wasm +0 -0
- package/Source/Workers/gaussianSplatSorter.js +34 -0
- package/Source/Workers/gaussianSplatTextureGenerator.js +39 -0
- package/index.d.ts +16 -0
- package/index.js +70 -61
- package/package.json +4 -2
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
import Frozen from "../Core/Frozen.js";
|
|
2
|
+
import Matrix4 from "../Core/Matrix4.js";
|
|
3
|
+
import ModelUtility from "./Model/ModelUtility.js";
|
|
4
|
+
import GaussianSplatSorter from "./GaussianSplatSorter.js";
|
|
5
|
+
import GaussianSplatTextureGenerator from "./GaussianSplatTextureGenerator.js";
|
|
6
|
+
import Check from "../Core/Check.js";
|
|
7
|
+
import ComponentDatatype from "../Core/ComponentDatatype.js";
|
|
8
|
+
import PixelDatatype from "../Renderer/PixelDatatype.js";
|
|
9
|
+
import PixelFormat from "../Core/PixelFormat.js";
|
|
10
|
+
import Sampler from "../Renderer/Sampler.js";
|
|
11
|
+
import Texture from "../Renderer/Texture.js";
|
|
12
|
+
import GaussianSplatRenderResources from "./GaussianSplatRenderResources.js";
|
|
13
|
+
import BlendingState from "./BlendingState.js";
|
|
14
|
+
import Pass from "../Renderer/Pass.js";
|
|
15
|
+
import ShaderDestination from "../Renderer/ShaderDestination.js";
|
|
16
|
+
import GaussianSplatVS from "../Shaders/PrimitiveGaussianSplatVS.js";
|
|
17
|
+
import GaussianSplatFS from "../Shaders/PrimitiveGaussianSplatFS.js";
|
|
18
|
+
import PrimitiveType from "../Core/PrimitiveType.js";
|
|
19
|
+
import DrawCommand from "../Renderer/DrawCommand.js";
|
|
20
|
+
import Geometry from "../Core/Geometry.js";
|
|
21
|
+
import GeometryAttribute from "../Core/GeometryAttribute.js";
|
|
22
|
+
import VertexArray from "../Renderer/VertexArray.js";
|
|
23
|
+
import BufferUsage from "../Renderer/BufferUsage.js";
|
|
24
|
+
import RenderState from "../Renderer/RenderState.js";
|
|
25
|
+
import clone from "../Core/clone.js";
|
|
26
|
+
import defined from "../Core/defined.js";
|
|
27
|
+
import VertexAttributeSemantic from "./VertexAttributeSemantic.js";
|
|
28
|
+
import AttributeType from "./AttributeType.js";
|
|
29
|
+
import ModelComponents from "./ModelComponents.js";
|
|
30
|
+
import Axis from "./Axis.js";
|
|
31
|
+
import Cartesian3 from "../Core/Cartesian3.js";
|
|
32
|
+
import Transforms from "../Core/Transforms.js";
|
|
33
|
+
import Quaternion from "../Core/Quaternion.js";
|
|
34
|
+
import SplitDirection from "./SplitDirection.js";
|
|
35
|
+
import destroyObject from "../Core/destroyObject.js";
|
|
36
|
+
|
|
37
|
+
const scratchMatrix4A = new Matrix4();
|
|
38
|
+
const scratchMatrix4B = new Matrix4();
|
|
39
|
+
const scratchMatrix4C = new Matrix4();
|
|
40
|
+
|
|
41
|
+
const GaussianSplatSortingState = {
|
|
42
|
+
IDLE: 0,
|
|
43
|
+
WAITING: 1,
|
|
44
|
+
SORTING: 2,
|
|
45
|
+
SORTED: 3,
|
|
46
|
+
ERROR: 4,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function createGaussianSplatTexture(context, splatTextureData) {
|
|
50
|
+
return new Texture({
|
|
51
|
+
context: context,
|
|
52
|
+
source: {
|
|
53
|
+
width: splatTextureData.width,
|
|
54
|
+
height: splatTextureData.height,
|
|
55
|
+
arrayBufferView: splatTextureData.data,
|
|
56
|
+
},
|
|
57
|
+
preMultiplyAlpha: false,
|
|
58
|
+
skipColorSpaceConversion: true,
|
|
59
|
+
pixelFormat: PixelFormat.RGBA_INTEGER,
|
|
60
|
+
pixelDatatype: PixelDatatype.UNSIGNED_INT,
|
|
61
|
+
flipY: false,
|
|
62
|
+
sampler: Sampler.NEAREST,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A primitive that renders Gaussian splats.
|
|
67
|
+
* <p>
|
|
68
|
+
* This primitive is used to render Gaussian splats in a 3D Tileset.
|
|
69
|
+
* It is designed to work with the KHR_spz_gaussian_splats_compression extension.
|
|
70
|
+
* </p>
|
|
71
|
+
* @alias GaussianSplatPrimitive
|
|
72
|
+
* @constructor
|
|
73
|
+
* @param {Object} options An object with the following properties:
|
|
74
|
+
* @param {Cesium3DTileset} options.tileset The tileset that this primitive belongs to.
|
|
75
|
+
* @param {boolean} [options.debugShowBoundingVolume=false] Whether to show the bounding volume of the primitive for debugging purposes.
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
function GaussianSplatPrimitive(options) {
|
|
80
|
+
options = options ?? Frozen.EMPTY_OBJECT;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The positions of the Gaussian splats in the primitive.
|
|
84
|
+
* @type {undefined|Float32Array}
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
this._positions = undefined;
|
|
88
|
+
/**
|
|
89
|
+
* The rotations of the Gaussian splats in the primitive.
|
|
90
|
+
* @type {undefined|Float32Array}
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
this._rotations = undefined;
|
|
94
|
+
/**
|
|
95
|
+
* The scales of the Gaussian splats in the primitive.
|
|
96
|
+
* @type {undefined|Float32Array}
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
this._scales = undefined;
|
|
100
|
+
/**
|
|
101
|
+
* The colors of the Gaussian splats in the primitive.
|
|
102
|
+
* @type {undefined|Uint8Array}
|
|
103
|
+
* @private
|
|
104
|
+
*/
|
|
105
|
+
this._colors = undefined;
|
|
106
|
+
/**
|
|
107
|
+
* The indexes of the Gaussian splats in the primitive.
|
|
108
|
+
* Used to index into the splat attribute texture in the vertex shader.
|
|
109
|
+
* @type {undefined|Uint32Array}
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
this._indexes = undefined;
|
|
113
|
+
/**
|
|
114
|
+
* The number of splats in the primitive.
|
|
115
|
+
* This is the total number of splats across all selected tiles.
|
|
116
|
+
* @type {number}
|
|
117
|
+
* @private
|
|
118
|
+
*/
|
|
119
|
+
this._numSplats = 0;
|
|
120
|
+
/**
|
|
121
|
+
* Indicates whether or not the primitive needs a Gaussian splat texture.
|
|
122
|
+
* This is set to true when the primitive is first created or when the splat attributes change.
|
|
123
|
+
* @type {boolean}
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
this._needsGaussianSplatTexture = true;
|
|
127
|
+
/**
|
|
128
|
+
* The scale of the Gaussian splats.
|
|
129
|
+
* This is used to control the size of the splats when rendered.
|
|
130
|
+
* @type {number}
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
this._splatScale = 1.0;
|
|
134
|
+
/**
|
|
135
|
+
* The previous view matrix used to determine if the primitive needs to be updated.
|
|
136
|
+
* This is used to avoid unnecessary updates when the view matrix hasn't changed.
|
|
137
|
+
* @type {Matrix4}
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
this._prevViewMatrix = new Matrix4();
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Indicates whether or not to show the bounding volume of the primitive for debugging purposes.
|
|
144
|
+
* This is used to visualize the bounding volume of the primitive in the scene.
|
|
145
|
+
* @type {boolean}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
this._debugShowBoundingVolume = options.debugShowBoundingVolume ?? false;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The texture used to store the Gaussian splat attributes.
|
|
152
|
+
* This texture is created from the splat attributes (positions, scales, rotations, colors)
|
|
153
|
+
* and is used in the vertex shader to render the splats.
|
|
154
|
+
* @type {undefined|Texture}
|
|
155
|
+
* @private
|
|
156
|
+
* @see {@link GaussianSplatTextureGenerator}
|
|
157
|
+
*/
|
|
158
|
+
this.gaussianSplatTexture = undefined;
|
|
159
|
+
/**
|
|
160
|
+
* The last width of the Gaussian splat texture.
|
|
161
|
+
* This is used to track changes in the texture size and update the primitive accordingly.
|
|
162
|
+
* @type {number}
|
|
163
|
+
* @private
|
|
164
|
+
*/
|
|
165
|
+
this._lastTextureWidth = 0;
|
|
166
|
+
/**
|
|
167
|
+
* The last height of the Gaussian splat texture.
|
|
168
|
+
* This is used to track changes in the texture size and update the primitive accordingly.
|
|
169
|
+
* @type {number}
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
this._lastTextureHeight = 0;
|
|
173
|
+
/**
|
|
174
|
+
* The vertex array used to render the Gaussian splats.
|
|
175
|
+
* This vertex array contains the attributes needed to render the splats, such as positions and indexes.
|
|
176
|
+
* @type {undefined|VertexArray}
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
179
|
+
this._vertexArray = undefined;
|
|
180
|
+
/**
|
|
181
|
+
* The length of the vertex array, used to track changes in the number of splats.
|
|
182
|
+
* This is used to determine if the vertex array needs to be rebuilt.
|
|
183
|
+
* @type {number}
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
this._vertexArrayLen = -1;
|
|
187
|
+
this._splitDirection = SplitDirection.NONE;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The dirty flag forces the primitive to render this frame.
|
|
191
|
+
* @type {boolean}
|
|
192
|
+
* @private
|
|
193
|
+
*/
|
|
194
|
+
this._dirty = false;
|
|
195
|
+
|
|
196
|
+
this._tileset = options.tileset;
|
|
197
|
+
|
|
198
|
+
this._baseTilesetUpdate = this._tileset.update;
|
|
199
|
+
this._tileset.update = this._wrappedUpdate.bind(this);
|
|
200
|
+
|
|
201
|
+
this._tileset.tileLoad.addEventListener(this.onTileLoad, this);
|
|
202
|
+
this._tileset.tileVisible.addEventListener(this.onTileVisible, this);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Tracks current count of selected tiles.
|
|
206
|
+
* This is used to determine if the primitive needs to be rebuilt.
|
|
207
|
+
* @type {number}
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
this.selectedTileLength = 0;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Indicates whether or not the primitive is ready for use.
|
|
214
|
+
* @type {boolean}
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
217
|
+
this._ready = false;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Indicates whether or not the primitive has a Gaussian splat texture.
|
|
221
|
+
* @type {boolean}
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
this._hasGaussianSplatTexture = false;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Indicates whether or not the primitive is currently generating a Gaussian splat texture.
|
|
228
|
+
* @type {boolean}
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
this._gaussianSplatTexturePending = false;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The draw command used to render the Gaussian splats.
|
|
235
|
+
* @type {undefined|DrawCommand}
|
|
236
|
+
* @private
|
|
237
|
+
*/
|
|
238
|
+
this._drawCommand = undefined;
|
|
239
|
+
/**
|
|
240
|
+
* The root transform of the tileset.
|
|
241
|
+
* This is used to transform the splats into world space.
|
|
242
|
+
* @type {undefined|Matrix4}
|
|
243
|
+
* @private
|
|
244
|
+
*/
|
|
245
|
+
this._rootTransform = undefined;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The axis correction matrix to transform the splats from Y-up to Z-up.
|
|
249
|
+
* @type {Matrix4}
|
|
250
|
+
* @private
|
|
251
|
+
*/
|
|
252
|
+
this._axisCorrectionMatrix = ModelUtility.getAxisCorrectionMatrix(
|
|
253
|
+
Axis.Y,
|
|
254
|
+
Axis.X,
|
|
255
|
+
new Matrix4(),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Indicates whether or not the primitive has been destroyed.
|
|
260
|
+
* @type {boolean}
|
|
261
|
+
* @private
|
|
262
|
+
*/
|
|
263
|
+
this._isDestroyed = false;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* The state of the Gaussian splat sorting process.
|
|
267
|
+
* This is used to track the progress of the sorting operation.
|
|
268
|
+
* @type {GaussianSplatSortingState}
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
this._sorterState = GaussianSplatSortingState.IDLE;
|
|
272
|
+
/**
|
|
273
|
+
* A promise that resolves when the Gaussian splat sorting operation is complete.
|
|
274
|
+
* This is used to track the progress of the sorting operation.
|
|
275
|
+
* @type {undefined|Promise}
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
this._sorterPromise = undefined;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* An error that occurred during the Gaussian splat sorting operation.
|
|
282
|
+
* Thrown when state is ERROR.
|
|
283
|
+
* @type {undefined|Error}
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
this._sorterError = undefined;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
Object.defineProperties(GaussianSplatPrimitive.prototype, {
|
|
290
|
+
/**
|
|
291
|
+
* Indicates whether the primitive is ready for use.
|
|
292
|
+
* @memberof GaussianSplatPrimitive.prototype
|
|
293
|
+
* @type {boolean}
|
|
294
|
+
* @readonly
|
|
295
|
+
*/
|
|
296
|
+
ready: {
|
|
297
|
+
get: function () {
|
|
298
|
+
return this._ready;
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
/**
|
|
302
|
+
* Scaling factor applied to the Gaussian splats indepdendent of the
|
|
303
|
+
* Gaussian splat scale attribute. Applied uniformly to all splats.
|
|
304
|
+
* @memberof GaussianSplatPrimitive.prototype
|
|
305
|
+
* @type {number}
|
|
306
|
+
* @default 1.0
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
splatScale: {
|
|
310
|
+
get: function () {
|
|
311
|
+
return this._splatScale;
|
|
312
|
+
},
|
|
313
|
+
set: function (splatScale) {
|
|
314
|
+
//>>includeStart('debug', pragmas.debug);
|
|
315
|
+
Check.typeOf.number("splatScale", splatScale);
|
|
316
|
+
//>>includeEnd('debug');
|
|
317
|
+
|
|
318
|
+
this._splatScale = splatScale;
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The {@link SplitDirection} to apply to this point.
|
|
324
|
+
* @memberof GaussianSplatPrimitive.prototype
|
|
325
|
+
* @type {SplitDirection}
|
|
326
|
+
* @default {@link SplitDirection.NONE}
|
|
327
|
+
*/
|
|
328
|
+
splitDirection: {
|
|
329
|
+
get: function () {
|
|
330
|
+
return this._splitDirection;
|
|
331
|
+
},
|
|
332
|
+
set: function (value) {
|
|
333
|
+
if (this._splitDirection !== value) {
|
|
334
|
+
this._splitDirection = value;
|
|
335
|
+
this._dirty = true;
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Since we aren't visible at the scene level, we need to wrap the tileset update
|
|
343
|
+
* so we not only get called but ensure we update immediately after the tileset.
|
|
344
|
+
* @param {FrameState} frameState
|
|
345
|
+
* @private
|
|
346
|
+
*
|
|
347
|
+
*/
|
|
348
|
+
GaussianSplatPrimitive.prototype._wrappedUpdate = function (frameState) {
|
|
349
|
+
this._baseTilesetUpdate.call(this._tileset, frameState);
|
|
350
|
+
this.update(frameState);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Destroys the primitive and releases its resources in a deterministic manner.
|
|
355
|
+
* @private
|
|
356
|
+
*/
|
|
357
|
+
GaussianSplatPrimitive.prototype.destroy = function () {
|
|
358
|
+
this._positions = undefined;
|
|
359
|
+
this._rotations = undefined;
|
|
360
|
+
this._scales = undefined;
|
|
361
|
+
this._colors = undefined;
|
|
362
|
+
this._indexes = undefined;
|
|
363
|
+
if (defined(this.gaussianSplatTexture)) {
|
|
364
|
+
this.gaussianSplatTexture.destroy();
|
|
365
|
+
this.gaussianSplatTexture = undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const drawCommand = this._drawCommand;
|
|
369
|
+
if (defined(drawCommand)) {
|
|
370
|
+
drawCommand.shaderProgram =
|
|
371
|
+
drawCommand.shaderProgram && drawCommand.shaderProgram.destroy();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (defined(this._vertexArray)) {
|
|
375
|
+
this._vertexArray.destroy();
|
|
376
|
+
this._vertexArray = undefined;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this._tileset.update = this._baseTilesetUpdate.bind(this._tileset);
|
|
380
|
+
|
|
381
|
+
return destroyObject(this);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Returns true if this object was destroyed; otherwise, false.
|
|
386
|
+
* <br /><br />
|
|
387
|
+
* If this object was destroyed, it should not be used; calling any function other than
|
|
388
|
+
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
|
|
389
|
+
* @returns {boolean} Returns true if the primitive has been destroyed, otherwise false.
|
|
390
|
+
* @private
|
|
391
|
+
*/
|
|
392
|
+
GaussianSplatPrimitive.prototype.isDestroyed = function () {
|
|
393
|
+
return this._isDestroyed;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Event callback for when a tile is loaded.
|
|
398
|
+
* This method is called when a tile is loaded and the primitive needs to be updated.
|
|
399
|
+
* It sets the dirty flag to true, indicating that the primitive needs to be rebuilt.
|
|
400
|
+
* @param {Cesium3DTile} tile
|
|
401
|
+
* @private
|
|
402
|
+
*/
|
|
403
|
+
GaussianSplatPrimitive.prototype.onTileLoad = function (tile) {
|
|
404
|
+
this._dirty = true;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Callback for visible tiles.
|
|
409
|
+
* @param {Cesium3DTile} tile
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
GaussianSplatPrimitive.prototype.onTileVisible = function (tile) {};
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Transforms the tile's splat primitive attributes into world space.
|
|
416
|
+
* <br /><br />
|
|
417
|
+
* This method applies the computed transform of the tile and the tileset's bounding sphere
|
|
418
|
+
* to the splat primitive's position, rotation, and scale attributes.
|
|
419
|
+
* It modifies the attributes in place, transforming them from local space to world space.
|
|
420
|
+
*
|
|
421
|
+
* @param {Cesium3DTile} tile
|
|
422
|
+
* @private
|
|
423
|
+
*/
|
|
424
|
+
GaussianSplatPrimitive.transformTile = function (tile) {
|
|
425
|
+
const computedTransform = tile.computedTransform;
|
|
426
|
+
const splatPrimitive = tile.content.splatPrimitive;
|
|
427
|
+
const gaussianSplatPrimitive = tile.tileset.gaussianSplatPrimitive;
|
|
428
|
+
|
|
429
|
+
const computedModelMatrix = Matrix4.multiplyTransformation(
|
|
430
|
+
computedTransform,
|
|
431
|
+
gaussianSplatPrimitive._axisCorrectionMatrix,
|
|
432
|
+
scratchMatrix4A,
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
Matrix4.multiplyTransformation(
|
|
436
|
+
computedModelMatrix,
|
|
437
|
+
tile.content.worldTransform,
|
|
438
|
+
computedModelMatrix,
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
const center = tile.tileset.boundingSphere.center;
|
|
442
|
+
const toGlobal = Transforms.eastNorthUpToFixedFrame(center);
|
|
443
|
+
const toLocal = Matrix4.inverse(toGlobal, scratchMatrix4B);
|
|
444
|
+
|
|
445
|
+
const transform = Matrix4.multiplyTransformation(
|
|
446
|
+
toLocal,
|
|
447
|
+
computedModelMatrix,
|
|
448
|
+
scratchMatrix4A,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const positions = ModelUtility.getAttributeBySemantic(
|
|
452
|
+
splatPrimitive,
|
|
453
|
+
VertexAttributeSemantic.POSITION,
|
|
454
|
+
).typedArray;
|
|
455
|
+
|
|
456
|
+
const rotations = ModelUtility.getAttributeBySemantic(
|
|
457
|
+
splatPrimitive,
|
|
458
|
+
VertexAttributeSemantic.ROTATION,
|
|
459
|
+
).typedArray;
|
|
460
|
+
|
|
461
|
+
const scales = ModelUtility.getAttributeBySemantic(
|
|
462
|
+
splatPrimitive,
|
|
463
|
+
VertexAttributeSemantic.SCALE,
|
|
464
|
+
).typedArray;
|
|
465
|
+
|
|
466
|
+
const position = new Cartesian3();
|
|
467
|
+
const rotation = new Quaternion();
|
|
468
|
+
const scale = new Cartesian3();
|
|
469
|
+
for (let i = 0; i < positions.length / 3; ++i) {
|
|
470
|
+
position.x = positions[i * 3];
|
|
471
|
+
position.y = positions[i * 3 + 1];
|
|
472
|
+
position.z = positions[i * 3 + 2];
|
|
473
|
+
|
|
474
|
+
rotation.x = rotations[i * 4];
|
|
475
|
+
rotation.y = rotations[i * 4 + 1];
|
|
476
|
+
rotation.z = rotations[i * 4 + 2];
|
|
477
|
+
rotation.w = rotations[i * 4 + 3];
|
|
478
|
+
|
|
479
|
+
scale.x = scales[i * 3];
|
|
480
|
+
scale.y = scales[i * 3 + 1];
|
|
481
|
+
scale.z = scales[i * 3 + 2];
|
|
482
|
+
|
|
483
|
+
Matrix4.fromTranslationQuaternionRotationScale(
|
|
484
|
+
position,
|
|
485
|
+
rotation,
|
|
486
|
+
scale,
|
|
487
|
+
scratchMatrix4C,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
Matrix4.multiplyTransformation(transform, scratchMatrix4C, scratchMatrix4C);
|
|
491
|
+
|
|
492
|
+
Matrix4.getTranslation(scratchMatrix4C, position);
|
|
493
|
+
Matrix4.getRotation(scratchMatrix4C, rotation);
|
|
494
|
+
Matrix4.getScale(scratchMatrix4C, scale);
|
|
495
|
+
|
|
496
|
+
positions[i * 3] = position.x;
|
|
497
|
+
positions[i * 3 + 1] = position.y;
|
|
498
|
+
positions[i * 3 + 2] = position.z;
|
|
499
|
+
|
|
500
|
+
rotations[i * 4] = rotation.x;
|
|
501
|
+
rotations[i * 4 + 1] = rotation.y;
|
|
502
|
+
rotations[i * 4 + 2] = rotation.z;
|
|
503
|
+
rotations[i * 4 + 3] = rotation.w;
|
|
504
|
+
|
|
505
|
+
scales[i * 3] = scale.x;
|
|
506
|
+
scales[i * 3 + 1] = scale.y;
|
|
507
|
+
scales[i * 3 + 2] = scale.z;
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Generates the Gaussian splat texture for the primitive.
|
|
513
|
+
* This method creates a texture from the splat attributes (positions, scales, rotations, colors)
|
|
514
|
+
* and updates the primitive's state accordingly.
|
|
515
|
+
*
|
|
516
|
+
* @see {@link GaussianSplatTextureGenerator}
|
|
517
|
+
*
|
|
518
|
+
* @param {GaussianSplatPrimitive} primitive
|
|
519
|
+
* @param {FrameState} frameState
|
|
520
|
+
* @private
|
|
521
|
+
*/
|
|
522
|
+
GaussianSplatPrimitive.generateSplatTexture = function (primitive, frameState) {
|
|
523
|
+
primitive._gaussianSplatTexturePending = true;
|
|
524
|
+
const promise = GaussianSplatTextureGenerator.generateFromAttributes({
|
|
525
|
+
attributes: {
|
|
526
|
+
positions: new Float32Array(primitive._positions),
|
|
527
|
+
scales: new Float32Array(primitive._scales),
|
|
528
|
+
rotations: new Float32Array(primitive._rotations),
|
|
529
|
+
colors: new Uint8Array(primitive._colors),
|
|
530
|
+
},
|
|
531
|
+
count: primitive._numSplats,
|
|
532
|
+
});
|
|
533
|
+
if (!defined(promise)) {
|
|
534
|
+
primitive._gaussianSplatTexturePending = false;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
promise
|
|
538
|
+
.then((splatTextureData) => {
|
|
539
|
+
if (!primitive._gaussianSplatTexture) {
|
|
540
|
+
// First frame, so create the texture.
|
|
541
|
+
primitive.gaussianSplatTexture = createGaussianSplatTexture(
|
|
542
|
+
frameState.context,
|
|
543
|
+
splatTextureData,
|
|
544
|
+
);
|
|
545
|
+
} else if (
|
|
546
|
+
primitive._lastTextureHeight !== splatTextureData.height ||
|
|
547
|
+
primitive._lastTextureWidth !== splatTextureData.width
|
|
548
|
+
) {
|
|
549
|
+
const oldTex = primitive.gaussianSplatTexture;
|
|
550
|
+
primitive._gaussianSplatTexture = createGaussianSplatTexture(
|
|
551
|
+
frameState.context,
|
|
552
|
+
splatTextureData,
|
|
553
|
+
);
|
|
554
|
+
oldTex.destroy();
|
|
555
|
+
} else {
|
|
556
|
+
primitive.gaussianSplatTexture.copyFrom({
|
|
557
|
+
source: {
|
|
558
|
+
width: splatTextureData.width,
|
|
559
|
+
height: splatTextureData.height,
|
|
560
|
+
arrayBufferView: splatTextureData.data,
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
primitive._lastTextureHeight = splatTextureData.height;
|
|
566
|
+
primitive._lastTextureWidth = splatTextureData.width;
|
|
567
|
+
|
|
568
|
+
primitive._hasGaussianSplatTexture = true;
|
|
569
|
+
primitive._needsGaussianSplatTexture = false;
|
|
570
|
+
primitive._gaussianSplatTexturePending = false;
|
|
571
|
+
|
|
572
|
+
if (
|
|
573
|
+
!defined(primitive._indexes) ||
|
|
574
|
+
primitive._indexes.length < primitive._numSplats
|
|
575
|
+
) {
|
|
576
|
+
primitive._indexes = new Uint32Array(primitive._numSplats);
|
|
577
|
+
}
|
|
578
|
+
for (let i = 0; i < primitive._numSplats; ++i) {
|
|
579
|
+
primitive._indexes[i] = i;
|
|
580
|
+
}
|
|
581
|
+
})
|
|
582
|
+
.catch((error) => {
|
|
583
|
+
console.error("Error generating Gaussian splat texture:", error);
|
|
584
|
+
primitive._gaussianSplatTexturePending = false;
|
|
585
|
+
});
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Builds the draw command for the Gaussian splat primitive.
|
|
590
|
+
* This method sets up the shader program, render state, and vertex array for rendering the Gaussian splats.
|
|
591
|
+
* It also configures the attributes and uniforms required for rendering.
|
|
592
|
+
*
|
|
593
|
+
* @param {GaussianSplatPrimitive} primitive
|
|
594
|
+
* @param {FrameState} frameState
|
|
595
|
+
*
|
|
596
|
+
* @private
|
|
597
|
+
*/
|
|
598
|
+
GaussianSplatPrimitive.buildGSplatDrawCommand = function (
|
|
599
|
+
primitive,
|
|
600
|
+
frameState,
|
|
601
|
+
) {
|
|
602
|
+
const tileset = primitive._tileset;
|
|
603
|
+
const renderResources = new GaussianSplatRenderResources(primitive);
|
|
604
|
+
const { shaderBuilder } = renderResources;
|
|
605
|
+
const renderStateOptions = renderResources.renderStateOptions;
|
|
606
|
+
renderStateOptions.cull.enabled = false;
|
|
607
|
+
renderStateOptions.depthMask = false;
|
|
608
|
+
renderStateOptions.depthTest.enabled = true;
|
|
609
|
+
renderStateOptions.blending = BlendingState.PRE_MULTIPLIED_ALPHA_BLEND;
|
|
610
|
+
renderResources.alphaOptions.pass = Pass.GAUSSIAN_SPLATS;
|
|
611
|
+
|
|
612
|
+
shaderBuilder.addAttribute("vec2", "a_screenQuadPosition");
|
|
613
|
+
shaderBuilder.addAttribute("float", "a_splatIndex");
|
|
614
|
+
shaderBuilder.addVarying("vec4", "v_splatColor");
|
|
615
|
+
shaderBuilder.addVarying("vec2", "v_vertPos");
|
|
616
|
+
shaderBuilder.addUniform(
|
|
617
|
+
"float",
|
|
618
|
+
"u_splitDirection",
|
|
619
|
+
ShaderDestination.VERTEX,
|
|
620
|
+
);
|
|
621
|
+
shaderBuilder.addVarying("float", "v_splitDirection");
|
|
622
|
+
shaderBuilder.addUniform(
|
|
623
|
+
"highp usampler2D",
|
|
624
|
+
"u_splatAttributeTexture",
|
|
625
|
+
ShaderDestination.VERTEX,
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
shaderBuilder.addUniform("float", "u_splatScale", ShaderDestination.VERTEX);
|
|
629
|
+
|
|
630
|
+
const uniformMap = renderResources.uniformMap;
|
|
631
|
+
|
|
632
|
+
uniformMap.u_splatScale = function () {
|
|
633
|
+
return primitive.splatScale;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
uniformMap.u_splatAttributeTexture = function () {
|
|
637
|
+
return primitive.gaussianSplatTexture;
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
uniformMap.u_splitDirection = function () {
|
|
641
|
+
return primitive.splitDirection;
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
renderResources.instanceCount = primitive._numSplats;
|
|
645
|
+
renderResources.count = 4;
|
|
646
|
+
renderResources.primitiveType = PrimitiveType.TRIANGLE_STRIP;
|
|
647
|
+
shaderBuilder.addVertexLines(GaussianSplatVS);
|
|
648
|
+
shaderBuilder.addFragmentLines(GaussianSplatFS);
|
|
649
|
+
const shaderProgram = shaderBuilder.buildShaderProgram(frameState.context);
|
|
650
|
+
let renderState = clone(
|
|
651
|
+
RenderState.fromCache(renderResources.renderStateOptions),
|
|
652
|
+
true,
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
renderState.cull.face = ModelUtility.getCullFace(
|
|
656
|
+
tileset.modelMatrix,
|
|
657
|
+
PrimitiveType.TRIANGLE_STRIP,
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
renderState = RenderState.fromCache(renderState);
|
|
661
|
+
const splatQuadAttrLocations = {
|
|
662
|
+
screenQuadPosition: 0,
|
|
663
|
+
splatIndex: 2,
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
const idxAttr = new ModelComponents.Attribute();
|
|
667
|
+
idxAttr.name = "_SPLAT_INDEXES";
|
|
668
|
+
idxAttr.typedArray = primitive._indexes;
|
|
669
|
+
idxAttr.componentDatatype = ComponentDatatype.UNSIGNED_INT;
|
|
670
|
+
idxAttr.type = AttributeType.SCALAR;
|
|
671
|
+
idxAttr.normalized = false;
|
|
672
|
+
idxAttr.count = renderResources.instanceCount;
|
|
673
|
+
idxAttr.constant = 0;
|
|
674
|
+
idxAttr.instanceDivisor = 1;
|
|
675
|
+
|
|
676
|
+
if (
|
|
677
|
+
!defined(primitive._vertexArray) ||
|
|
678
|
+
primitive._indexes.length > primitive._vertexArrayLen
|
|
679
|
+
) {
|
|
680
|
+
const geometry = new Geometry({
|
|
681
|
+
attributes: {
|
|
682
|
+
screenQuadPosition: new GeometryAttribute({
|
|
683
|
+
componentDatatype: ComponentDatatype.FLOAT,
|
|
684
|
+
componentsPerAttribute: 2,
|
|
685
|
+
values: [-1, -1, 1, -1, 1, 1, -1, 1],
|
|
686
|
+
name: "_SCREEN_QUAD_POS",
|
|
687
|
+
variableName: "screenQuadPosition",
|
|
688
|
+
}),
|
|
689
|
+
splatIndex: { ...idxAttr, variableName: "splatIndex" },
|
|
690
|
+
},
|
|
691
|
+
primitiveType: PrimitiveType.TRIANGLE_STRIP,
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
primitive._vertexArray = VertexArray.fromGeometry({
|
|
695
|
+
context: frameState.context,
|
|
696
|
+
geometry: geometry,
|
|
697
|
+
attributeLocations: splatQuadAttrLocations,
|
|
698
|
+
bufferUsage: BufferUsage.DYNAMIC_DRAW,
|
|
699
|
+
interleave: false,
|
|
700
|
+
});
|
|
701
|
+
} else {
|
|
702
|
+
primitive._vertexArray
|
|
703
|
+
.getAttribute(1)
|
|
704
|
+
.vertexBuffer.copyFromArrayView(primitive._indexes);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
primitive._vertexArrayLen = primitive._indexes.length;
|
|
708
|
+
|
|
709
|
+
const center = tileset.boundingSphere.center;
|
|
710
|
+
const modelMatrix = Transforms.eastNorthUpToFixedFrame(center);
|
|
711
|
+
|
|
712
|
+
const command = new DrawCommand({
|
|
713
|
+
boundingVolume: tileset.boundingSphere,
|
|
714
|
+
modelMatrix: modelMatrix,
|
|
715
|
+
uniformMap: uniformMap,
|
|
716
|
+
renderState: renderState,
|
|
717
|
+
vertexArray: primitive._vertexArray,
|
|
718
|
+
shaderProgram: shaderProgram,
|
|
719
|
+
cull: renderStateOptions.cull.enabled,
|
|
720
|
+
pass: Pass.GAUSSIAN_SPLATS,
|
|
721
|
+
count: renderResources.count,
|
|
722
|
+
owner: this,
|
|
723
|
+
instanceCount: renderResources.instanceCount,
|
|
724
|
+
primitiveType: PrimitiveType.TRIANGLE_STRIP,
|
|
725
|
+
debugShowBoundingVolume: tileset.debugShowBoundingVolume,
|
|
726
|
+
castShadows: false,
|
|
727
|
+
receiveShadows: false,
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
primitive._drawCommand = command;
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Updates the Gaussian splat primitive for the current frame.
|
|
735
|
+
* This method checks if the primitive needs to be updated based on the current frame state,
|
|
736
|
+
* and if so, it processes the selected tiles, aggregates their attributes,
|
|
737
|
+
* and generates the Gaussian splat texture if necessary.
|
|
738
|
+
* It also handles the sorting of splat indexes and builds the draw command for rendering.
|
|
739
|
+
*
|
|
740
|
+
* @param {FrameState} frameState
|
|
741
|
+
* @private
|
|
742
|
+
*/
|
|
743
|
+
GaussianSplatPrimitive.prototype.update = function (frameState) {
|
|
744
|
+
const tileset = this._tileset;
|
|
745
|
+
|
|
746
|
+
if (!defined(this._rootTransform)) {
|
|
747
|
+
this._rootTransform = tileset.root.computedTransform;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (this._drawCommand) {
|
|
751
|
+
frameState.commandList.push(this._drawCommand);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (frameState.passes.pick === true) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (this.splitDirection !== tileset.splitDirection) {
|
|
759
|
+
this.splitDirection = tileset.splitDirection;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (this._sorterState === GaussianSplatSortingState.IDLE) {
|
|
763
|
+
if (
|
|
764
|
+
!this._dirty &&
|
|
765
|
+
Matrix4.equals(frameState.camera.viewMatrix, this._prevViewMatrix)
|
|
766
|
+
) {
|
|
767
|
+
// No need to update if the view matrix hasn't changed and the primitive isn't dirty.
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (
|
|
772
|
+
tileset._selectedTiles.length !== 0 &&
|
|
773
|
+
tileset._selectedTiles.length !== this.selectedTileLength
|
|
774
|
+
) {
|
|
775
|
+
this._numSplats = 0;
|
|
776
|
+
this._positions = undefined;
|
|
777
|
+
this._rotations = undefined;
|
|
778
|
+
this._scales = undefined;
|
|
779
|
+
this._colors = undefined;
|
|
780
|
+
this._indexes = undefined;
|
|
781
|
+
this._needsGaussianSplatTexture = true;
|
|
782
|
+
this._gaussianSplatTexturePending = false;
|
|
783
|
+
|
|
784
|
+
const tiles = tileset._selectedTiles;
|
|
785
|
+
const totalElements = tiles.reduce(
|
|
786
|
+
(total, tile) => total + tile.content.pointsLength,
|
|
787
|
+
0,
|
|
788
|
+
);
|
|
789
|
+
const aggregateAttributeValues = (
|
|
790
|
+
componentDatatype,
|
|
791
|
+
getAttributeCallback,
|
|
792
|
+
) => {
|
|
793
|
+
let aggregate;
|
|
794
|
+
let offset = 0;
|
|
795
|
+
for (const tile of tiles) {
|
|
796
|
+
const primitive = tile.content.splatPrimitive;
|
|
797
|
+
const attribute = getAttributeCallback(primitive);
|
|
798
|
+
if (!defined(aggregate)) {
|
|
799
|
+
aggregate = ComponentDatatype.createTypedArray(
|
|
800
|
+
componentDatatype,
|
|
801
|
+
totalElements *
|
|
802
|
+
AttributeType.getNumberOfComponents(attribute.type),
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
aggregate.set(attribute.typedArray, offset);
|
|
806
|
+
offset += attribute.typedArray.length;
|
|
807
|
+
}
|
|
808
|
+
return aggregate;
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
this._positions = aggregateAttributeValues(
|
|
812
|
+
ComponentDatatype.FLOAT,
|
|
813
|
+
(splatPrimitive) =>
|
|
814
|
+
ModelUtility.getAttributeBySemantic(
|
|
815
|
+
splatPrimitive,
|
|
816
|
+
VertexAttributeSemantic.POSITION,
|
|
817
|
+
),
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
this._scales = aggregateAttributeValues(
|
|
821
|
+
ComponentDatatype.FLOAT,
|
|
822
|
+
(splatPrimitive) =>
|
|
823
|
+
ModelUtility.getAttributeBySemantic(
|
|
824
|
+
splatPrimitive,
|
|
825
|
+
VertexAttributeSemantic.SCALE,
|
|
826
|
+
),
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
this._rotations = aggregateAttributeValues(
|
|
830
|
+
ComponentDatatype.FLOAT,
|
|
831
|
+
(splatPrimitive) =>
|
|
832
|
+
ModelUtility.getAttributeBySemantic(
|
|
833
|
+
splatPrimitive,
|
|
834
|
+
VertexAttributeSemantic.ROTATION,
|
|
835
|
+
),
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
this._colors = aggregateAttributeValues(
|
|
839
|
+
ComponentDatatype.UNSIGNED_BYTE,
|
|
840
|
+
(splatPrimitive) =>
|
|
841
|
+
ModelUtility.getAttributeBySemantic(
|
|
842
|
+
splatPrimitive,
|
|
843
|
+
VertexAttributeSemantic.COLOR,
|
|
844
|
+
),
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
this._numSplats = totalElements;
|
|
848
|
+
this.selectedTileLength = tileset._selectedTiles.length;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (this._numSplats === 0) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (this._needsGaussianSplatTexture) {
|
|
856
|
+
if (!this._gaussianSplatTexturePending) {
|
|
857
|
+
GaussianSplatPrimitive.generateSplatTexture(this, frameState);
|
|
858
|
+
}
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
Matrix4.clone(frameState.camera.viewMatrix, this._prevViewMatrix);
|
|
863
|
+
Matrix4.multiply(
|
|
864
|
+
frameState.camera.viewMatrix,
|
|
865
|
+
this._rootTransform,
|
|
866
|
+
scratchMatrix4A,
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
if (!defined(this._sorterPromise)) {
|
|
870
|
+
this._sorterPromise = GaussianSplatSorter.radixSortIndexes({
|
|
871
|
+
primitive: {
|
|
872
|
+
positions: new Float32Array(this._positions),
|
|
873
|
+
modelView: Float32Array.from(scratchMatrix4A),
|
|
874
|
+
count: this._numSplats,
|
|
875
|
+
},
|
|
876
|
+
sortType: "Index",
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (!defined(this._sorterPromise)) {
|
|
881
|
+
this._sorterState = GaussianSplatSortingState.WAITING;
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
this._sorterPromise.catch((err) => {
|
|
885
|
+
this._sorterState = GaussianSplatSortingState.ERROR;
|
|
886
|
+
this._sorterError = err;
|
|
887
|
+
});
|
|
888
|
+
this._sorterPromise.then((sortedData) => {
|
|
889
|
+
this._indexes = sortedData;
|
|
890
|
+
this._sorterState = GaussianSplatSortingState.SORTED;
|
|
891
|
+
});
|
|
892
|
+
} else if (this._sorterState === GaussianSplatSortingState.WAITING) {
|
|
893
|
+
if (!defined(this._sorterPromise)) {
|
|
894
|
+
this._sorterPromise = GaussianSplatSorter.radixSortIndexes({
|
|
895
|
+
primitive: {
|
|
896
|
+
positions: new Float32Array(this._positions),
|
|
897
|
+
modelView: Float32Array.from(scratchMatrix4A),
|
|
898
|
+
count: this._numSplats,
|
|
899
|
+
},
|
|
900
|
+
sortType: "Index",
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
if (!defined(this._sorterPromise)) {
|
|
904
|
+
this._sorterState = GaussianSplatSortingState.WAITING;
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
this._sorterPromise.catch((err) => {
|
|
908
|
+
this._sorterState = GaussianSplatSortingState.ERROR;
|
|
909
|
+
this._sorterError = err;
|
|
910
|
+
});
|
|
911
|
+
this._sorterPromise.then((sortedData) => {
|
|
912
|
+
this._indexes = sortedData;
|
|
913
|
+
this._sorterState = GaussianSplatSortingState.SORTED;
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
this._sorterState = GaussianSplatSortingState.SORTING; //set state to sorting
|
|
917
|
+
} else if (this._sorterState === GaussianSplatSortingState.SORTING) {
|
|
918
|
+
return; //still sorting, wait for next frame
|
|
919
|
+
} else if (this._sorterState === GaussianSplatSortingState.SORTED) {
|
|
920
|
+
//update the draw command if sorted
|
|
921
|
+
GaussianSplatPrimitive.buildGSplatDrawCommand(this, frameState);
|
|
922
|
+
this._sorterState = GaussianSplatSortingState.IDLE; //reset state for next frame
|
|
923
|
+
this._dirty = false;
|
|
924
|
+
this._sorterPromise = undefined; //reset promise for next frame
|
|
925
|
+
} else if (this._sorterState === GaussianSplatSortingState.ERROR) {
|
|
926
|
+
throw this._sorterError;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
this._dirty = false;
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
export default GaussianSplatPrimitive;
|