@myned-ai/gsplat-flame-avatar-renderer 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +30 -0
  2. package/dist/gsplat-flame-avatar-renderer.cjs.js +38 -33
  3. package/dist/gsplat-flame-avatar-renderer.cjs.min.js +1 -1
  4. package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -1
  5. package/dist/gsplat-flame-avatar-renderer.esm.js +38 -33
  6. package/dist/gsplat-flame-avatar-renderer.esm.min.js +1 -1
  7. package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -1
  8. package/package.json +5 -2
  9. package/src/api/index.js +0 -7
  10. package/src/buffers/SplatBuffer.js +0 -1394
  11. package/src/buffers/SplatBufferGenerator.js +0 -41
  12. package/src/buffers/SplatPartitioner.js +0 -110
  13. package/src/buffers/UncompressedSplatArray.js +0 -106
  14. package/src/buffers/index.js +0 -11
  15. package/src/core/SplatGeometry.js +0 -48
  16. package/src/core/SplatMesh.js +0 -2627
  17. package/src/core/SplatScene.js +0 -43
  18. package/src/core/SplatTree.js +0 -200
  19. package/src/core/Viewer.js +0 -2746
  20. package/src/core/index.js +0 -13
  21. package/src/enums/EngineConstants.js +0 -58
  22. package/src/enums/LogLevel.js +0 -13
  23. package/src/enums/RenderMode.js +0 -11
  24. package/src/enums/SceneFormat.js +0 -21
  25. package/src/enums/SceneRevealMode.js +0 -11
  26. package/src/enums/SplatRenderMode.js +0 -10
  27. package/src/enums/index.js +0 -13
  28. package/src/errors/ApplicationError.js +0 -185
  29. package/src/errors/index.js +0 -17
  30. package/src/flame/FlameAnimator.js +0 -496
  31. package/src/flame/FlameConstants.js +0 -21
  32. package/src/flame/FlameTextureManager.js +0 -293
  33. package/src/flame/index.js +0 -22
  34. package/src/flame/utils.js +0 -50
  35. package/src/index.js +0 -39
  36. package/src/loaders/DirectLoadError.js +0 -14
  37. package/src/loaders/INRIAV1PlyParser.js +0 -223
  38. package/src/loaders/PlyLoader.js +0 -519
  39. package/src/loaders/PlyParser.js +0 -19
  40. package/src/loaders/PlyParserUtils.js +0 -311
  41. package/src/loaders/index.js +0 -13
  42. package/src/materials/SplatMaterial.js +0 -1068
  43. package/src/materials/SplatMaterial2D.js +0 -358
  44. package/src/materials/SplatMaterial3D.js +0 -323
  45. package/src/materials/index.js +0 -11
  46. package/src/raycaster/Hit.js +0 -37
  47. package/src/raycaster/Ray.js +0 -123
  48. package/src/raycaster/Raycaster.js +0 -175
  49. package/src/raycaster/index.js +0 -10
  50. package/src/renderer/AnimationManager.js +0 -577
  51. package/src/renderer/AppConstants.js +0 -101
  52. package/src/renderer/GaussianSplatRenderer.js +0 -1146
  53. package/src/renderer/index.js +0 -24
  54. package/src/utils/BlobUrlManager.js +0 -294
  55. package/src/utils/EventEmitter.js +0 -349
  56. package/src/utils/LoaderUtils.js +0 -66
  57. package/src/utils/Logger.js +0 -171
  58. package/src/utils/ObjectPool.js +0 -248
  59. package/src/utils/RenderLoop.js +0 -306
  60. package/src/utils/Util.js +0 -416
  61. package/src/utils/ValidationUtils.js +0 -331
  62. package/src/utils/index.js +0 -18
  63. package/src/worker/SortWorker.js +0 -284
  64. package/src/worker/index.js +0 -8
@@ -1,2627 +0,0 @@
1
- /**
2
- * SplatMesh
3
- *
4
- * Based on @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * HEAVILY MODIFIED for FLAME avatar support:
8
- * - Added FLAME bone texture handling
9
- * - Added expression blendshape support
10
- * - Extended with skinning data management
11
- * - Additional ~800 lines of FLAME-specific code
12
- */
13
-
14
- import { Box3, BufferGeometry, DataTexture, FloatType, HalfFloatType, Matrix4, Mesh, MeshBasicMaterial, Quaternion, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, RGBIntegerFormat, Scene, UnsignedByteType, UnsignedIntType, Vector2, Vector3, Vector4, WebGLRenderer } from 'three';
15
- import { SplatRenderMode } from '../enums/SplatRenderMode.js';
16
- import { LogLevel } from '../enums/LogLevel.js';
17
- import { SceneRevealMode } from '../enums/SceneRevealMode.js';
18
- import { Constants } from '../enums/EngineConstants.js';
19
- import { SplatScene } from './SplatScene.js';
20
- import { SplatGeometry } from './SplatGeometry.js';
21
- import { SplatTree } from './SplatTree.js';
22
- import { SplatMaterial3D } from '../materials/SplatMaterial3D.js';
23
- import { SplatMaterial2D } from '../materials/SplatMaterial2D.js';
24
- import { getSphericalHarmonicsComponentCountForDegree, uintEncodedFloat, rgbaArrayToInteger, clamp } from '../utils/Util.js';
25
- import { getLogger } from '../utils/Logger.js';
26
-
27
- const logger = getLogger('SplatMesh');
28
-
29
- // Dummy geometry and material for initial Mesh construction
30
- const dummyGeometry = new BufferGeometry();
31
- const dummyMaterial = new MeshBasicMaterial();
32
-
33
- /**
34
- * WebGL Extensions helper (from Three.js internals)
35
- */
36
- function WebGLExtensions$1(gl) {
37
- const extensions = {};
38
-
39
- function getExtension(name) {
40
- if (extensions[name] !== undefined) {
41
- return extensions[name];
42
- }
43
-
44
- let extension;
45
- switch (name) {
46
- case 'WEBGL_depth_texture':
47
- extension = gl.getExtension('WEBGL_depth_texture') || gl.getExtension('MOZ_WEBGL_depth_texture') ||
48
- gl.getExtension('WEBKIT_WEBGL_depth_texture');
49
- break;
50
- case 'EXT_texture_filter_anisotropic':
51
- extension = gl.getExtension('EXT_texture_filter_anisotropic') ||
52
- gl.getExtension('MOZ_EXT_texture_filter_anisotropic') ||
53
- gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
54
- break;
55
- case 'WEBGL_compressed_texture_s3tc':
56
- extension = gl.getExtension('WEBGL_compressed_texture_s3tc') ||
57
- gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ||
58
- gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
59
- break;
60
- case 'WEBGL_compressed_texture_pvrtc':
61
- extension = gl.getExtension('WEBGL_compressed_texture_pvrtc') ||
62
- gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
63
- break;
64
- default:
65
- extension = gl.getExtension(name);
66
- }
67
-
68
- extensions[name] = extension;
69
- return extension;
70
- }
71
-
72
- return {
73
- has: function(name) {
74
- return getExtension(name) !== null;
75
- },
76
- init: function(capabilities) {
77
- if (capabilities.isWebGL2) {
78
- getExtension('EXT_color_buffer_float');
79
- getExtension('WEBGL_clip_cull_distance');
80
- } else {
81
- getExtension('WEBGL_depth_texture');
82
- getExtension('OES_texture_float');
83
- getExtension('OES_texture_half_float');
84
- getExtension('OES_texture_half_float_linear');
85
- getExtension('OES_standard_derivatives');
86
- getExtension('OES_element_index_uint');
87
- getExtension('OES_vertex_array_object');
88
- getExtension('ANGLE_instanced_arrays');
89
- }
90
- getExtension('OES_texture_float_linear');
91
- getExtension('EXT_color_buffer_half_float');
92
- getExtension('WEBGL_multisampled_render_to_texture');
93
- },
94
- get: function(name) {
95
- const extension = getExtension(name);
96
- if (extension === null) {
97
- logger.warn('THREE.WebGLRenderer: ' + name + ' extension not supported.');
98
- }
99
- return extension;
100
- }
101
- };
102
- }
103
-
104
- /**
105
- * WebGL Capabilities helper (from Three.js internals)
106
- */
107
- function WebGLCapabilities$1(gl, extensions, parameters) {
108
- let maxAnisotropy;
109
-
110
- function getMaxAnisotropy() {
111
- if (maxAnisotropy !== undefined) return maxAnisotropy;
112
- if (extensions.has('EXT_texture_filter_anisotropic') === true) {
113
- const extension = extensions.get('EXT_texture_filter_anisotropic');
114
- maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
115
- } else {
116
- maxAnisotropy = 0;
117
- }
118
- return maxAnisotropy;
119
- }
120
-
121
- function getMaxPrecision(precision) {
122
- if (precision === 'highp') {
123
- if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 &&
124
- gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
125
- return 'highp';
126
- }
127
- precision = 'mediump';
128
- }
129
- if (precision === 'mediump') {
130
- if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 &&
131
- gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
132
- return 'mediump';
133
- }
134
- }
135
- return 'lowp';
136
- }
137
-
138
- const isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext';
139
-
140
- let precision = parameters.precision !== undefined ? parameters.precision : 'highp';
141
- const maxPrecision = getMaxPrecision(precision);
142
-
143
- if (maxPrecision !== precision) {
144
- logger.warn('THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.');
145
- precision = maxPrecision;
146
- }
147
-
148
- const drawBuffers = isWebGL2 || extensions.has('WEBGL_draw_buffers');
149
- const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
150
-
151
- const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
152
- const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
153
- const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
154
- const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
155
-
156
- const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
157
- const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
158
- const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS);
159
- const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
160
-
161
- const vertexTextures = maxVertexTextures > 0;
162
- const floatFragmentTextures = isWebGL2 || extensions.has('OES_texture_float');
163
- const floatVertexTextures = vertexTextures && floatFragmentTextures;
164
-
165
- const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0;
166
-
167
- return {
168
- isWebGL2: isWebGL2,
169
- drawBuffers: drawBuffers,
170
- getMaxAnisotropy: getMaxAnisotropy,
171
- getMaxPrecision: getMaxPrecision,
172
- precision: precision,
173
- logarithmicDepthBuffer: logarithmicDepthBuffer,
174
- maxTextures: maxTextures,
175
- maxVertexTextures: maxVertexTextures,
176
- maxTextureSize: maxTextureSize,
177
- maxCubemapSize: maxCubemapSize,
178
- maxAttributes: maxAttributes,
179
- maxVertexUniforms: maxVertexUniforms,
180
- maxVaryings: maxVaryings,
181
- maxFragmentUniforms: maxFragmentUniforms,
182
- vertexTextures: vertexTextures,
183
- floatFragmentTextures: floatFragmentTextures,
184
- floatVertexTextures: floatVertexTextures,
185
- maxSamples: maxSamples
186
- };
187
- }
188
-
189
- /**
190
- * WebGL Utils helper (from Three.js internals)
191
- */
192
- function WebGLUtils$1(gl, extensions, capabilities) {
193
- const isWebGL2 = capabilities.isWebGL2;
194
-
195
- function convert(p, colorSpace) {
196
- let extension;
197
-
198
- if (p === UnsignedByteType) return gl.UNSIGNED_BYTE;
199
- if (p === 1017) return gl.UNSIGNED_SHORT_4_4_4_4;
200
- if (p === 1018) return gl.UNSIGNED_SHORT_5_5_5_1;
201
- if (p === 1010) return gl.BYTE;
202
- if (p === 1011) return gl.SHORT;
203
- if (p === 1012) return gl.UNSIGNED_SHORT;
204
- if (p === 1013) return gl.INT;
205
- if (p === 1014) return gl.UNSIGNED_INT;
206
- if (p === FloatType) return gl.FLOAT;
207
-
208
- if (p === HalfFloatType) {
209
- if (isWebGL2) return gl.HALF_FLOAT;
210
- extension = extensions.get('OES_texture_half_float');
211
- if (extension !== null) {
212
- return extension.HALF_FLOAT_OES;
213
- } else {
214
- return null;
215
- }
216
- }
217
-
218
- if (p === 1021) return gl.ALPHA;
219
- if (p === 1022) return gl.RGB;
220
- if (p === RGBAFormat) return gl.RGBA;
221
- if (p === 1024) return gl.LUMINANCE;
222
- if (p === 1025) return gl.LUMINANCE_ALPHA;
223
- if (p === 1026) return gl.DEPTH_COMPONENT;
224
- if (p === 1027) return gl.DEPTH_STENCIL;
225
-
226
- // WebGL2 formats
227
- if (p === RedFormat) return gl.RED;
228
- if (p === RedIntegerFormat) return gl.RED_INTEGER;
229
- if (p === RGFormat) return gl.RG;
230
- if (p === RGIntegerFormat) return gl.RG_INTEGER;
231
- if (p === RGBIntegerFormat) return gl.RGB_INTEGER;
232
- if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER;
233
-
234
- // S3TC
235
- if (p === 33776 || p === 33777 || p === 33778 || p === 33779) {
236
- extension = extensions.get('WEBGL_compressed_texture_s3tc');
237
- if (extension !== null) {
238
- if (p === 33776) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
239
- if (p === 33777) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
240
- if (p === 33778) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
241
- if (p === 33779) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
242
- } else {
243
- return null;
244
- }
245
- }
246
-
247
- // PVRTC
248
- if (p === 35840 || p === 35841 || p === 35842 || p === 35843) {
249
- extension = extensions.get('WEBGL_compressed_texture_pvrtc');
250
- if (extension !== null) {
251
- if (p === 35840) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
252
- if (p === 35841) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
253
- if (p === 35842) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
254
- if (p === 35843) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
255
- } else {
256
- return null;
257
- }
258
- }
259
-
260
- // ETC
261
- if (p === 36196) {
262
- extension = extensions.get('WEBGL_compressed_texture_etc1');
263
- if (extension !== null) {
264
- return extension.COMPRESSED_RGB_ETC1_WEBGL;
265
- } else {
266
- return null;
267
- }
268
- }
269
-
270
- // ASTC
271
- if (p >= 37808 && p <= 37814 || p >= 37840 && p <= 37846) {
272
- extension = extensions.get('WEBGL_compressed_texture_astc');
273
- if (extension !== null) {
274
- return p;
275
- } else {
276
- return null;
277
- }
278
- }
279
-
280
- // BPTC
281
- if (p === 36492 || p === 36494 || p === 36495) {
282
- extension = extensions.get('EXT_texture_compression_bptc');
283
- if (extension !== null) {
284
- return p;
285
- } else {
286
- return null;
287
- }
288
- }
289
-
290
- if (p === 34042) return gl.UNSIGNED_INT_24_8;
291
-
292
- // Internal formats for float/half-float textures
293
- if (isWebGL2) {
294
- if (p === 6407) return gl.RGB;
295
- if (p === 6408) return gl.RGBA;
296
- }
297
-
298
- return (gl[p] !== undefined) ? gl[p] : null;
299
- }
300
-
301
- return {
302
- convert: convert
303
- };
304
- }
305
-
306
- // Constants for texture element counts
307
- const COVARIANCES_ELEMENTS_PER_SPLAT = 6;
308
- const CENTER_COLORS_ELEMENTS_PER_SPLAT = 4;
309
-
310
- const COVARIANCES_ELEMENTS_PER_TEXEL_STORED = 4;
311
- const COVARIANCES_ELEMENTS_PER_TEXEL_ALLOCATED = 4;
312
- const COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_STORED = 6;
313
- const COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_ALLOCATED = 8;
314
- const SCALES_ROTATIONS_ELEMENTS_PER_TEXEL = 4;
315
- const CENTER_COLORS_ELEMENTS_PER_TEXEL = 4;
316
- const SCENE_INDEXES_ELEMENTS_PER_TEXEL = 1;
317
-
318
- const SCENE_FADEIN_RATE_FAST = 0.012;
319
- const SCENE_FADEIN_RATE_GRADUAL = 0.003;
320
-
321
- const VISIBLE_REGION_EXPANSION_DELTA = 1;
322
-
323
- const MAX_TEXTURE_TEXELS = 16777216;
324
-
325
- export class SplatMesh extends Mesh {
326
-
327
- constructor(splatRenderMode = SplatRenderMode.ThreeD, dynamicMode = false, enableOptionalEffects = false,
328
- halfPrecisionCovariancesOnGPU = false, devicePixelRatio = 1, enableDistancesComputationOnGPU = true,
329
- integerBasedDistancesComputation = false, antialiased = false, maxScreenSpaceSplatSize = 1024, logLevel = LogLevel.None,
330
- sphericalHarmonicsDegree = 0, sceneFadeInRateMultiplier = 1.0, kernel2DSize = 0.3) {
331
- super(dummyGeometry, dummyMaterial);
332
-
333
- // Reference to a Three.js renderer
334
- this.renderer = undefined;
335
-
336
- // Determine how the splats are rendered
337
- this.splatRenderMode = splatRenderMode;
338
-
339
- // When 'dynamicMode' is true, scenes are assumed to be non-static. Dynamic scenes are handled differently
340
- // and certain optimizations cannot be made for them. Additionally, by default, all splat data retrieved from
341
- // this splat mesh will not have their scene transform applied to them if the splat mesh is dynamic. That
342
- // can be overriden via parameters to the individual functions that are used to retrieve splat data.
343
- this.dynamicMode = dynamicMode;
344
-
345
- // When true, allows for usage of extra properties and attributes during rendering for effects such as opacity adjustment.
346
- // Default is false for performance reasons. These properties are separate from transform properties (scale, rotation, position)
347
- // that are enabled by the 'dynamicScene' parameter.
348
- this.enableOptionalEffects = enableOptionalEffects;
349
-
350
- // Use 16-bit floating point values when storing splat covariance data in textures, instead of 32-bit
351
- this.halfPrecisionCovariancesOnGPU = halfPrecisionCovariancesOnGPU;
352
-
353
- // Ratio of the resolution in physical pixels to the resolution in CSS pixels for the current display device
354
- this.devicePixelRatio = devicePixelRatio;
355
-
356
- // Use a transform feedback to calculate splat distances from the camera
357
- this.enableDistancesComputationOnGPU = enableDistancesComputationOnGPU;
358
-
359
- // Use a faster integer-based approach for calculating splat distances from the camera
360
- this.integerBasedDistancesComputation = integerBasedDistancesComputation;
361
-
362
- // When true, will perform additional steps during rendering to address artifacts caused by the rendering of gaussians at a
363
- // substantially different resolution than that at which they were rendered during training. This will only work correctly
364
- // for models that were trained using a process that utilizes this compensation calculation. For more details:
365
- // https://github.com/nerfstudio-project/gsplat/pull/117
366
- // https://github.com/graphdeco-inria/gaussian-splatting/issues/294#issuecomment-1772688093
367
- this.antialiased = antialiased;
368
-
369
- // The size of the 2D kernel used for splat rendering
370
- // This will adjust the 2D kernel size after the projection
371
- this.kernel2DSize = kernel2DSize;
372
-
373
- // Specify the maximum clip space splat size, can help deal with large splats that get too unwieldy
374
- this.maxScreenSpaceSplatSize = maxScreenSpaceSplatSize;
375
-
376
- // The verbosity of console logging
377
- this.logLevel = logLevel;
378
-
379
- // Degree 0 means no spherical harmonics
380
- this.sphericalHarmonicsDegree = sphericalHarmonicsDegree;
381
- this.minSphericalHarmonicsDegree = 0;
382
-
383
- this.sceneFadeInRateMultiplier = sceneFadeInRateMultiplier;
384
-
385
- // The individual splat scenes stored in this splat mesh, each containing their own transform
386
- this.scenes = [];
387
-
388
- // Special octree tailored to SplatMesh instances
389
- this.splatTree = null;
390
- this.baseSplatTree = null;
391
-
392
- // Cache textures and the intermediate data used to populate them
393
- this.splatDataTextures = {};
394
- this.flameModel = null;
395
- this.expressionBSNum = 0;
396
- this.bsWeight = [];
397
-
398
- this.bonesMatrix = null;
399
- this.bonesNum = null;
400
- this.bonesWeight = null;
401
- this.gaussianSplatCount = null;
402
-
403
- this.morphTargetDictionary = null;
404
- this.distancesTransformFeedback = {
405
- 'id': null,
406
- 'vertexShader': null,
407
- 'fragmentShader': null,
408
- 'program': null,
409
- 'centersBuffer': null,
410
- 'sceneIndexesBuffer': null,
411
- 'outDistancesBuffer': null,
412
- 'centersLoc': -1,
413
- 'modelViewProjLoc': -1,
414
- 'sceneIndexesLoc': -1,
415
- 'transformsLocs': []
416
- };
417
-
418
- this.globalSplatIndexToLocalSplatIndexMap = [];
419
- this.globalSplatIndexToSceneIndexMap = [];
420
-
421
- // Optional iris occlusion configuration from avatar ZIP
422
- this.irisOcclusionConfig = null;
423
-
424
- this.lastBuildSplatCount = 0;
425
- this.lastBuildScenes = [];
426
- this.lastBuildMaxSplatCount = 0;
427
- this.lastBuildSceneCount = 0;
428
- this.firstRenderTime = -1;
429
- this.finalBuild = false;
430
-
431
- this.webGLUtils = null;
432
-
433
- this.boundingBox = new Box3();
434
- this.calculatedSceneCenter = new Vector3();
435
- this.maxSplatDistanceFromSceneCenter = 0;
436
- this.visibleRegionBufferRadius = 0;
437
- this.visibleRegionRadius = 0;
438
- this.visibleRegionFadeStartRadius = 0;
439
- this.visibleRegionChanging = false;
440
-
441
- this.splatScale = 1.0;
442
- this.pointCloudModeEnabled = false;
443
-
444
- this.disposed = false;
445
- this.lastRenderer = null;
446
- this.visible = false;
447
- }
448
-
449
- /**
450
- * Build a container for each scene managed by this splat mesh based on an instance of SplatBuffer, along with optional
451
- * transform data (position, scale, rotation) passed to the splat mesh during the build process.
452
- * @param {Array<THREE.Matrix4>} splatBuffers SplatBuffer instances containing splats for each scene
453
- * @param {Array<object>} sceneOptions Array of options objects: {
454
- *
455
- * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
456
- *
457
- * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
458
- *
459
- * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
460
- * }
461
- * @return {Array<THREE.Matrix4>}
462
- */
463
- static buildScenes(parentObject, splatBuffers, sceneOptions) {
464
- const scenes = [];
465
- scenes.length = splatBuffers.length;
466
- for (let i = 0; i < splatBuffers.length; i++) {
467
- const splatBuffer = splatBuffers[i];
468
- const options = sceneOptions[i] || {};
469
- let positionArray = options['position'] || [0, 0, 0];
470
- let rotationArray = options['rotation'] || [0, 0, 0, 1];
471
- let scaleArray = options['scale'] || [1, 1, 1];
472
- const position = new Vector3().fromArray(positionArray);
473
- const rotation = new Quaternion().fromArray(rotationArray);
474
- const scale = new Vector3().fromArray(scaleArray);
475
- const scene = SplatMesh.createScene(splatBuffer, position, rotation, scale,
476
- options.splatAlphaRemovalThreshold || 1, options.opacity, options.visible);
477
- parentObject.add(scene);
478
- scenes[i] = scene;
479
- }
480
- return scenes;
481
- }
482
-
483
-
484
-
485
- static createScene(splatBuffer, position, rotation, scale, minimumAlpha, opacity = 1.0, visible = true) {
486
- return new SplatScene(splatBuffer, position, rotation, scale, minimumAlpha, opacity, visible);
487
- }
488
-
489
- /**
490
- * Build data structures that map global splat indexes (based on a unified index across all splat buffers) to
491
- * local data within a single scene.
492
- * @param {Array<SplatBuffer>} splatBuffers Instances of SplatBuffer off which to build the maps
493
- * @return {object}
494
- */
495
- static buildSplatIndexMaps(splatBuffers) {
496
- const localSplatIndexMap = [];
497
- const sceneIndexMap = [];
498
- let totalSplatCount = 0;
499
- for (let s = 0; s < splatBuffers.length; s++) {
500
- const splatBuffer = splatBuffers[s];
501
- const maxSplatCount = splatBuffer.getMaxSplatCount();
502
- for (let i = 0; i < maxSplatCount; i++) {
503
- localSplatIndexMap[totalSplatCount] = i;
504
- sceneIndexMap[totalSplatCount] = s;
505
- totalSplatCount++;
506
- }
507
- }
508
- return {
509
- localSplatIndexMap,
510
- sceneIndexMap
511
- };
512
- }
513
-
514
- /**
515
- * Build an instance of SplatTree (a specialized octree) for the given splat mesh.
516
- * @param {Array<number>} minAlphas Array of minimum splat slphas for each scene
517
- * @param {function} onSplatTreeIndexesUpload Function to be called when the upload of splat centers to the splat tree
518
- * builder worker starts and finishes.
519
- * @param {function} onSplatTreeConstruction Function to be called when the conversion of the local splat tree from
520
- * the format produced by the splat tree builder worker starts and ends.
521
- * @return {SplatTree}
522
- */
523
- buildSplatTree = (minAlphas = [], onSplatTreeIndexesUpload, onSplatTreeConstruction) => {
524
- return new Promise((resolve) => {
525
- this.disposeSplatTree();
526
- // TODO: expose SplatTree constructor parameters (maximumDepth and maxCentersPerNode) so that they can
527
- // be configured on a per-scene basis
528
- this.baseSplatTree = new SplatTree(8, 1000);
529
- const buildStartTime = performance.now();
530
- const splatColor = new Vector4();
531
- this.baseSplatTree.processSplatMesh(this, (splatIndex) => {
532
- this.getSplatColor(splatIndex, splatColor);
533
- const sceneIndex = this.getSceneIndexForSplat(splatIndex);
534
- const minAlpha = minAlphas[sceneIndex] || 1;
535
- return splatColor.w >= minAlpha;
536
- }, onSplatTreeIndexesUpload, onSplatTreeConstruction)
537
- .then(() => {
538
- const buildTime = performance.now() - buildStartTime;
539
- if (this.logLevel >= LogLevel.Info) logger.info('SplatTree build: ' + buildTime + ' ms');
540
- if (this.disposed) {
541
- resolve();
542
- } else {
543
-
544
- this.splatTree = this.baseSplatTree;
545
- this.baseSplatTree = null;
546
-
547
- let leavesWithVertices = 0;
548
- let avgSplatCount = 0;
549
- let maxSplatCount = 0;
550
- let nodeCount = 0;
551
-
552
- this.splatTree.visitLeaves((node) => {
553
- const nodeSplatCount = node.data.indexes.length;
554
- if (nodeSplatCount > 0) {
555
- avgSplatCount += nodeSplatCount;
556
- maxSplatCount = Math.max(maxSplatCount, nodeSplatCount);
557
- nodeCount++;
558
- leavesWithVertices++;
559
- }
560
- });
561
- if (this.logLevel >= LogLevel.Info) {
562
- logger.info(`SplatTree leaves: ${this.splatTree.countLeaves()}`);
563
- logger.info(`SplatTree leaves with splats:${leavesWithVertices}`);
564
- avgSplatCount = avgSplatCount / nodeCount;
565
- logger.info(`Avg splat count per node: ${avgSplatCount}`);
566
- logger.info(`Total splat count: ${this.getSplatCount()}`);
567
- }
568
- resolve();
569
- }
570
- });
571
- });
572
- };
573
-
574
- /**
575
- * Construct this instance of SplatMesh.
576
- * @param {Array<SplatBuffer>} splatBuffers The base splat data, instances of SplatBuffer
577
- * @param {Array<object>} sceneOptions Dynamic options for each scene {
578
- *
579
- * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
580
- * value (valid range: 0 - 255), defaults to 1
581
- *
582
- * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
583
- *
584
- * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
585
- *
586
- * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
587
- *
588
- * }
589
- * @param {boolean} keepSceneTransforms For a scene that already exists and is being overwritten, this flag
590
- * says to keep the transform from the existing scene.
591
- * @param {boolean} finalBuild Will the splat mesh be in its final state after this build?
592
- * @param {function} onSplatTreeIndexesUpload Function to be called when the upload of splat centers to the splat tree
593
- * builder worker starts and finishes.
594
- * @param {function} onSplatTreeConstruction Function to be called when the conversion of the local splat tree from
595
- * the format produced by the splat tree builder worker starts and ends.
596
- * @return {object} Object containing info about the splats that are updated
597
- */
598
- build(splatBuffers, sceneOptions, keepSceneTransforms = true, finalBuild = false,
599
- onSplatTreeIndexesUpload, onSplatTreeConstruction, preserveVisibleRegion = true) {
600
-
601
- this.sceneOptions = sceneOptions;
602
- this.finalBuild = finalBuild;
603
-
604
- const maxSplatCount = SplatMesh.getTotalMaxSplatCountForSplatBuffers(splatBuffers);
605
-
606
- const newScenes = SplatMesh.buildScenes(this, splatBuffers, sceneOptions);
607
- if (keepSceneTransforms) {
608
- for (let i = 0; i < this.scenes.length && i < newScenes.length; i++) {
609
- const newScene = newScenes[i];
610
- const existingScene = this.getScene(i);
611
- newScene.copyTransformData(existingScene);
612
- }
613
- }
614
- this.scenes = newScenes;
615
-
616
- let minSphericalHarmonicsDegree = 3;
617
- for (let splatBuffer of splatBuffers) {
618
- const splatBufferSphericalHarmonicsDegree = splatBuffer.getMinSphericalHarmonicsDegree();
619
- if (splatBufferSphericalHarmonicsDegree < minSphericalHarmonicsDegree) {
620
- minSphericalHarmonicsDegree = splatBufferSphericalHarmonicsDegree;
621
- }
622
- }
623
- this.minSphericalHarmonicsDegree = Math.min(minSphericalHarmonicsDegree, this.sphericalHarmonicsDegree);
624
-
625
- let splatBuffersChanged = false;
626
- if (splatBuffers.length !== this.lastBuildScenes.length) {
627
- splatBuffersChanged = true;
628
- } else {
629
- for (let i = 0; i < splatBuffers.length; i++) {
630
- const splatBuffer = splatBuffers[i];
631
- if (splatBuffer !== this.lastBuildScenes[i].splatBuffer) {
632
- splatBuffersChanged = true;
633
- break;
634
- }
635
- }
636
- }
637
-
638
- let isUpdateBuild = true;
639
- if (this.scenes.length !== 1 ||
640
- this.lastBuildSceneCount !== this.scenes.length ||
641
- this.lastBuildMaxSplatCount !== maxSplatCount ||
642
- splatBuffersChanged) {
643
- isUpdateBuild = false;
644
- }
645
-
646
- if (!isUpdateBuild) {
647
- this.boundingBox = new Box3();
648
- if (!preserveVisibleRegion) {
649
- this.maxSplatDistanceFromSceneCenter = 0;
650
- this.visibleRegionBufferRadius = 0;
651
- this.visibleRegionRadius = 0;
652
- this.visibleRegionFadeStartRadius = 0;
653
- this.firstRenderTime = -1;
654
- }
655
- this.lastBuildScenes = [];
656
- this.lastBuildSplatCount = 0;
657
- this.lastBuildMaxSplatCount = 0;
658
- this.disposeMeshData();
659
- this.geometry = SplatGeometry.build(maxSplatCount);
660
- if (this.splatRenderMode === SplatRenderMode.ThreeD) {
661
- this.material = SplatMaterial3D.build(this.dynamicMode, this.enableOptionalEffects, this.antialiased,
662
- this.maxScreenSpaceSplatSize, this.splatScale, this.pointCloudModeEnabled,
663
- this.minSphericalHarmonicsDegree, this.kernel2DSize, this.irisOcclusionConfig);
664
- } else {
665
- this.material = SplatMaterial2D.build(this.dynamicMode, this.enableOptionalEffects,
666
- this.splatScale, this.pointCloudModeEnabled, this.minSphericalHarmonicsDegree);
667
- }
668
-
669
- const indexMaps = SplatMesh.buildSplatIndexMaps(splatBuffers);
670
- this.globalSplatIndexToLocalSplatIndexMap = indexMaps.localSplatIndexMap;
671
- this.globalSplatIndexToSceneIndexMap = indexMaps.sceneIndexMap;
672
- }
673
-
674
- const splatBufferSplatCount = this.getSplatCount(true);
675
- if (this.enableDistancesComputationOnGPU) this.setupDistancesComputationTransformFeedback();
676
- const dataUpdateResults = this.refreshGPUDataFromSplatBuffers(isUpdateBuild);
677
-
678
- for (let i = 0; i < this.scenes.length; i++) {
679
- this.lastBuildScenes[i] = this.scenes[i];
680
- }
681
- this.lastBuildSplatCount = splatBufferSplatCount;
682
- this.lastBuildMaxSplatCount = this.getMaxSplatCount();
683
- this.lastBuildSceneCount = this.scenes.length;
684
-
685
- // if (finalBuild && this.scenes.length > 0) {
686
- // this.buildSplatTree(sceneOptions.map(options => options.splatAlphaRemovalThreshold || 1),
687
- // onSplatTreeIndexesUpload, onSplatTreeConstruction)
688
- // .then(() => {
689
- // if (this.onSplatTreeReadyCallback) this.onSplatTreeReadyCallback(this.splatTree);
690
- // this.onSplatTreeReadyCallback = null;
691
- // });
692
- // }
693
-
694
- this.visible = (this.scenes.length > 0);
695
-
696
- return dataUpdateResults;
697
- }
698
-
699
- freeIntermediateSplatData() {
700
-
701
- const deleteTextureData = (texture) => {
702
- delete texture.source.data;
703
- delete texture.image;
704
- texture.onUpdate = null;
705
- };
706
-
707
- delete this.splatDataTextures.baseData.covariances;
708
- delete this.splatDataTextures.baseData.centers;
709
- delete this.splatDataTextures.baseData.colors;
710
- delete this.splatDataTextures.baseData.sphericalHarmonics;
711
-
712
- delete this.splatDataTextures.centerColors.data;
713
- delete this.splatDataTextures.covariances.data;
714
- if (this.splatDataTextures.sphericalHarmonics) {
715
- delete this.splatDataTextures.sphericalHarmonics.data;
716
- }
717
- if (this.splatDataTextures.sceneIndexes) {
718
- delete this.splatDataTextures.sceneIndexes.data;
719
- }
720
-
721
- this.splatDataTextures.centerColors.texture.needsUpdate = true;
722
- this.splatDataTextures.centerColors.texture.onUpdate = () => {
723
- deleteTextureData(this.splatDataTextures.centerColors.texture);
724
- };
725
-
726
- this.splatDataTextures.flameModelPosTexture.texture.needsUpdate = true;
727
- this.splatDataTextures.flameModelPosTexture.texture.onUpdate = () => {
728
- deleteTextureData(this.splatDataTextures.flameModelPosTexture.texture);
729
- };
730
-
731
- this.splatDataTextures.covariances.texture.needsUpdate = true;
732
- this.splatDataTextures.covariances.texture.onUpdate = () => {
733
- deleteTextureData(this.splatDataTextures.covariances.texture);
734
- };
735
-
736
- if (this.splatDataTextures.sphericalHarmonics) {
737
- if (this.splatDataTextures.sphericalHarmonics.texture) {
738
- this.splatDataTextures.sphericalHarmonics.texture.needsUpdate = true;
739
- this.splatDataTextures.sphericalHarmonics.texture.onUpdate = () => {
740
- deleteTextureData(this.splatDataTextures.sphericalHarmonics.texture);
741
- };
742
- } else {
743
- this.splatDataTextures.sphericalHarmonics.textures.forEach((texture) => {
744
- texture.needsUpdate = true;
745
- texture.onUpdate = () => {
746
- deleteTextureData(texture);
747
- };
748
- });
749
- }
750
- }
751
- if (this.splatDataTextures.sceneIndexes) {
752
- this.splatDataTextures.sceneIndexes.texture.needsUpdate = true;
753
- this.splatDataTextures.sceneIndexes.texture.onUpdate = () => {
754
- deleteTextureData(this.splatDataTextures.sceneIndexes.texture);
755
- };
756
- }
757
- }
758
- /**
759
- * Dispose all resources held by the splat mesh
760
- */
761
- dispose() {
762
- this.disposeMeshData();
763
- this.disposeTextures();
764
- this.disposeSplatTree();
765
- if (this.enableDistancesComputationOnGPU) {
766
- if (this.computeDistancesOnGPUSyncTimeout) {
767
- clearTimeout(this.computeDistancesOnGPUSyncTimeout);
768
- this.computeDistancesOnGPUSyncTimeout = null;
769
- }
770
- this.disposeDistancesComputationGPUResources();
771
- }
772
- this.scenes = [];
773
- this.distancesTransformFeedback = {
774
- 'id': null,
775
- 'vertexShader': null,
776
- 'fragmentShader': null,
777
- 'program': null,
778
- 'centersBuffer': null,
779
- 'sceneIndexesBuffer': null,
780
- 'outDistancesBuffer': null,
781
- 'centersLoc': -1,
782
- 'modelViewProjLoc': -1,
783
- 'sceneIndexesLoc': -1,
784
- 'transformsLocs': []
785
- };
786
- this.renderer = null;
787
-
788
- this.globalSplatIndexToLocalSplatIndexMap = [];
789
- this.globalSplatIndexToSceneIndexMap = [];
790
-
791
- this.lastBuildSplatCount = 0;
792
- this.lastBuildScenes = [];
793
- this.lastBuildMaxSplatCount = 0;
794
- this.lastBuildSceneCount = 0;
795
- this.firstRenderTime = -1;
796
- this.finalBuild = false;
797
-
798
- this.webGLUtils = null;
799
-
800
- this.boundingBox = new Box3();
801
- this.calculatedSceneCenter = new Vector3();
802
- this.maxSplatDistanceFromSceneCenter = 0;
803
- this.visibleRegionBufferRadius = 0;
804
- this.visibleRegionRadius = 0;
805
- this.visibleRegionFadeStartRadius = 0;
806
- this.visibleRegionChanging = false;
807
-
808
- this.splatScale = 1.0;
809
- this.pointCloudModeEnabled = false;
810
-
811
- this.disposed = true;
812
- this.lastRenderer = null;
813
- this.visible = false;
814
- }
815
-
816
- /**
817
- * Dispose of only the Three.js mesh resources (geometry, material, and texture)
818
- */
819
- disposeMeshData() {
820
- if (this.geometry && this.geometry !== dummyGeometry) {
821
- this.geometry.dispose();
822
- this.geometry = null;
823
- }
824
- if (this.material) {
825
- this.material.dispose();
826
- this.material = null;
827
- }
828
- }
829
-
830
- disposeTextures() {
831
- for (let textureKey in this.splatDataTextures) {
832
- if (Object.hasOwn(this.splatDataTextures, textureKey)) {
833
- const textureContainer = this.splatDataTextures[textureKey];
834
- if (textureContainer.texture) {
835
- textureContainer.texture.dispose();
836
- textureContainer.texture = null;
837
- }
838
- }
839
- }
840
- this.splatDataTextures = null;
841
- }
842
-
843
- disposeSplatTree() {
844
- if (this.splatTree) {
845
- this.splatTree.dispose();
846
- this.splatTree = null;
847
- }
848
- if (this.baseSplatTree) {
849
- this.baseSplatTree.dispose();
850
- this.baseSplatTree = null;
851
- }
852
- }
853
-
854
- getSplatTree() {
855
- return this.splatTree;
856
- }
857
-
858
- onSplatTreeReady(callback) {
859
- this.onSplatTreeReadyCallback = callback;
860
- }
861
-
862
- /**
863
- * Get copies of data that are necessary for splat distance computation: splat center positions and splat
864
- * scene indexes (necessary for applying dynamic scene transformations during distance computation)
865
- * @param {*} start The index at which to start copying data
866
- * @param {*} end The index at which to stop copying data
867
- * @return {object}
868
- */
869
- getDataForDistancesComputation(start, end) {
870
- const centers = this.integerBasedDistancesComputation ?
871
- this.getIntegerCenters(start, end, true) :
872
- this.getFloatCenters(start, end, true);
873
- const sceneIndexes = this.getSceneIndexes(start, end);
874
- return {
875
- centers,
876
- sceneIndexes
877
- };
878
- }
879
-
880
- /**
881
- * Refresh data textures and GPU buffers with splat data from the splat buffers belonging to this mesh.
882
- * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
883
- * @return {object}
884
- */
885
- refreshGPUDataFromSplatBuffers(sinceLastBuildOnly) {
886
- const splatCount = this.getSplatCount(true);
887
- this.refreshDataTexturesFromSplatBuffers(sinceLastBuildOnly);
888
- const updateStart = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
889
- const { centers, sceneIndexes } = this.getDataForDistancesComputation(updateStart, splatCount - 1);
890
- if (this.enableDistancesComputationOnGPU) {
891
- this.refreshGPUBuffersForDistancesComputation(centers, sceneIndexes, sinceLastBuildOnly);
892
- }
893
- return {
894
- 'from': updateStart,
895
- 'to': splatCount - 1,
896
- 'count': splatCount - updateStart,
897
- 'centers': centers,
898
- 'sceneIndexes': sceneIndexes
899
- };
900
- }
901
-
902
- /**
903
- * Update the GPU buffers that are used for computing splat distances on the GPU.
904
- * @param {Array<number>} centers Splat center positions
905
- * @param {Array<number>} sceneIndexes Indexes of the scene to which each splat belongs
906
- * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
907
- */
908
- refreshGPUBuffersForDistancesComputation(centers, sceneIndexes, sinceLastBuildOnly = false) {
909
- const offset = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
910
- this.updateGPUCentersBufferForDistancesComputation(sinceLastBuildOnly, centers, offset);
911
- this.updateGPUTransformIndexesBufferForDistancesComputation(sinceLastBuildOnly, sceneIndexes, offset);
912
- }
913
-
914
- /**
915
- * Refresh data textures with data from the splat buffers for this mesh.
916
- * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
917
- */
918
- refreshDataTexturesFromSplatBuffers(sinceLastBuildOnly) {
919
- const splatCount = this.getSplatCount(true);
920
- const fromSplat = this.lastBuildSplatCount;
921
- const toSplat = splatCount - 1;
922
-
923
- if (!sinceLastBuildOnly) {
924
- this.setupDataTextures();
925
- this.updateBaseDataFromSplatBuffers();
926
- } else {
927
- this.updateBaseDataFromSplatBuffers(fromSplat, toSplat);
928
- }
929
-
930
- this.updateDataTexturesFromBaseData(fromSplat, toSplat);
931
- this.updateVisibleRegion(sinceLastBuildOnly);
932
- }
933
-
934
- setupDataTextures() {
935
- const maxSplatCount = this.getMaxSplatCount();
936
- const splatCount = this.getSplatCount(true);
937
-
938
- this.disposeTextures();
939
-
940
- const computeDataTextureSize = (elementsPerTexel, elementsPerSplat) => {
941
- const texSize = new Vector2(4096, 1024);
942
- while (texSize.x * texSize.y * elementsPerTexel < maxSplatCount * elementsPerSplat) texSize.y *= 2;
943
- return texSize;
944
- };
945
-
946
- const getCovariancesElementsPertexelStored = (compressionLevel) => {
947
- return compressionLevel >= 1 ? COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_STORED : COVARIANCES_ELEMENTS_PER_TEXEL_STORED;
948
- };
949
-
950
- const getCovariancesInitialTextureSpecs = (compressionLevel) => {
951
- const elementsPerTexelStored = getCovariancesElementsPertexelStored(compressionLevel);
952
- const texSize = computeDataTextureSize(elementsPerTexelStored, 6);
953
- return {elementsPerTexelStored, texSize};
954
- };
955
-
956
- let covarianceCompressionLevel = this.getTargetCovarianceCompressionLevel();
957
- const scaleRotationCompressionLevel = 0;
958
- const shCompressionLevel = this.getTargetSphericalHarmonicsCompressionLevel();
959
-
960
- let covariances;
961
- let scales;
962
- let rotations;
963
- if (this.splatRenderMode === SplatRenderMode.ThreeD) {
964
- const initialCovTexSpecs = getCovariancesInitialTextureSpecs(covarianceCompressionLevel);
965
- if (initialCovTexSpecs.texSize.x * initialCovTexSpecs.texSize.y > MAX_TEXTURE_TEXELS && covarianceCompressionLevel === 0) {
966
- covarianceCompressionLevel = 1;
967
- }
968
- covariances = new Float32Array(maxSplatCount * COVARIANCES_ELEMENTS_PER_SPLAT);
969
- } else {
970
- scales = new Float32Array(maxSplatCount * 3);
971
- rotations = new Float32Array(maxSplatCount * 4);
972
- }
973
-
974
- const centers = new Float32Array(maxSplatCount * 3);
975
- const colors = new Uint8Array(maxSplatCount * 4);
976
-
977
- let SphericalHarmonicsArrayType = Float32Array;
978
- if (shCompressionLevel === 1) SphericalHarmonicsArrayType = Uint16Array;
979
- else if (shCompressionLevel === 2) SphericalHarmonicsArrayType = Uint8Array;
980
- const shComponentCount = getSphericalHarmonicsComponentCountForDegree(this.minSphericalHarmonicsDegree);
981
- const shData = this.minSphericalHarmonicsDegree ? new SphericalHarmonicsArrayType(maxSplatCount * shComponentCount) : undefined;
982
-
983
- // set up centers/colors data texture
984
- const centersColsTexSize = computeDataTextureSize(CENTER_COLORS_ELEMENTS_PER_TEXEL, 4);
985
- const paddedCentersCols = new Uint32Array(centersColsTexSize.x * centersColsTexSize.y * CENTER_COLORS_ELEMENTS_PER_TEXEL);
986
- SplatMesh.updateCenterColorsPaddedData(0, splatCount - 1, centers, colors, paddedCentersCols);
987
-
988
- const centersColsTex = new DataTexture(paddedCentersCols, centersColsTexSize.x, centersColsTexSize.y,
989
- RGBAIntegerFormat, UnsignedIntType);
990
- centersColsTex.internalFormat = 'RGBA32UI';
991
- centersColsTex.needsUpdate = true;
992
- this.material.uniforms.centersColorsTexture.value = centersColsTex;
993
- this.material.uniforms.centersColorsTextureSize.value.copy(centersColsTexSize);
994
- this.material.uniformsNeedUpdate = true;
995
-
996
- this.splatDataTextures = {
997
- 'baseData': {
998
- 'covariances': covariances,
999
- 'scales': scales,
1000
- 'rotations': rotations,
1001
- 'centers': centers,
1002
- 'colors': colors,
1003
- 'sphericalHarmonics': shData
1004
- },
1005
- 'centerColors': {
1006
- 'data': paddedCentersCols,
1007
- 'texture': centersColsTex,
1008
- 'size': centersColsTexSize
1009
- }
1010
- };
1011
-
1012
- if (this.splatRenderMode === SplatRenderMode.ThreeD) {
1013
- // set up covariances data texture
1014
-
1015
- const covTexSpecs = getCovariancesInitialTextureSpecs(covarianceCompressionLevel);
1016
- const covariancesElementsPerTexelStored = covTexSpecs.elementsPerTexelStored;
1017
- const covTexSize = covTexSpecs.texSize;
1018
-
1019
- let CovariancesDataType = covarianceCompressionLevel >= 1 ? Uint32Array : Float32Array;
1020
- const covariancesElementsPerTexelAllocated = covarianceCompressionLevel >= 1 ?
1021
- COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_ALLOCATED :
1022
- COVARIANCES_ELEMENTS_PER_TEXEL_ALLOCATED;
1023
- const covariancesTextureData = new CovariancesDataType(covTexSize.x * covTexSize.y * covariancesElementsPerTexelAllocated);
1024
-
1025
- if (covarianceCompressionLevel === 0) {
1026
- covariancesTextureData.set(covariances);
1027
- } else {
1028
- SplatMesh.updatePaddedCompressedCovariancesTextureData(covariances, covariancesTextureData, 0, 0, covariances.length);
1029
- }
1030
-
1031
- let covTex;
1032
- if (covarianceCompressionLevel >= 1) {
1033
- covTex = new DataTexture(covariancesTextureData, covTexSize.x, covTexSize.y,
1034
- RGBAIntegerFormat, UnsignedIntType);
1035
- covTex.internalFormat = 'RGBA32UI';
1036
- this.material.uniforms.covariancesTextureHalfFloat.value = covTex;
1037
- } else {
1038
- covTex = new DataTexture(covariancesTextureData, covTexSize.x, covTexSize.y, RGBAFormat, FloatType);
1039
- this.material.uniforms.covariancesTexture.value = covTex;
1040
-
1041
- // For some reason a usampler2D needs to have a valid texture attached or WebGL complains
1042
- const dummyTex = new DataTexture(new Uint32Array(32), 2, 2, RGBAIntegerFormat, UnsignedIntType);
1043
- dummyTex.internalFormat = 'RGBA32UI';
1044
- this.material.uniforms.covariancesTextureHalfFloat.value = dummyTex;
1045
- dummyTex.needsUpdate = true;
1046
- }
1047
- covTex.needsUpdate = true;
1048
-
1049
- this.material.uniforms.covariancesAreHalfFloat.value = (covarianceCompressionLevel >= 1) ? 1 : 0;
1050
- this.material.uniforms.covariancesTextureSize.value.copy(covTexSize);
1051
-
1052
- this.splatDataTextures['covariances'] = {
1053
- 'data': covariancesTextureData,
1054
- 'texture': covTex,
1055
- 'size': covTexSize,
1056
- 'compressionLevel': covarianceCompressionLevel,
1057
- 'elementsPerTexelStored': covariancesElementsPerTexelStored,
1058
- 'elementsPerTexelAllocated': covariancesElementsPerTexelAllocated
1059
- };
1060
- } else {
1061
- // set up scale & rotations data texture
1062
- const elementsPerSplat = 6;
1063
- const scaleRotationsTexSize = computeDataTextureSize(SCALES_ROTATIONS_ELEMENTS_PER_TEXEL, elementsPerSplat);
1064
- let ScaleRotationsDataType = scaleRotationCompressionLevel >= 1 ? Uint16Array : Float32Array;
1065
- let scaleRotationsTextureType = scaleRotationCompressionLevel >= 1 ? HalfFloatType : FloatType;
1066
- const paddedScaleRotations = new ScaleRotationsDataType(scaleRotationsTexSize.x * scaleRotationsTexSize.y *
1067
- SCALES_ROTATIONS_ELEMENTS_PER_TEXEL);
1068
-
1069
- SplatMesh.updateScaleRotationsPaddedData(0, splatCount - 1, scales, rotations, paddedScaleRotations);
1070
-
1071
- const scaleRotationsTex = new DataTexture(paddedScaleRotations, scaleRotationsTexSize.x, scaleRotationsTexSize.y,
1072
- RGBAFormat, scaleRotationsTextureType);
1073
- scaleRotationsTex.needsUpdate = true;
1074
- this.material.uniforms.scaleRotationsTexture.value = scaleRotationsTex;
1075
- this.material.uniforms.scaleRotationsTextureSize.value.copy(scaleRotationsTexSize);
1076
-
1077
- this.splatDataTextures['scaleRotations'] = {
1078
- 'data': paddedScaleRotations,
1079
- 'texture': scaleRotationsTex,
1080
- 'size': scaleRotationsTexSize,
1081
- 'compressionLevel': scaleRotationCompressionLevel
1082
- };
1083
- }
1084
-
1085
- if (shData) {
1086
- const shTextureType = shCompressionLevel === 2 ? UnsignedByteType : HalfFloatType;
1087
-
1088
- let paddedSHComponentCount = shComponentCount;
1089
- if (paddedSHComponentCount % 2 !== 0) paddedSHComponentCount++;
1090
- const shElementsPerTexel = this.minSphericalHarmonicsDegree === 2 ? 4 : 2;
1091
- const texelFormat = shElementsPerTexel === 4 ? RGBAFormat : RGFormat;
1092
- let shTexSize = computeDataTextureSize(shElementsPerTexel, paddedSHComponentCount);
1093
-
1094
- // Use one texture for all spherical harmonics data
1095
- if (shTexSize.x * shTexSize.y <= MAX_TEXTURE_TEXELS) {
1096
- const paddedSHArraySize = shTexSize.x * shTexSize.y * shElementsPerTexel;
1097
- const paddedSHArray = new SphericalHarmonicsArrayType(paddedSHArraySize);
1098
- for (let c = 0; c < splatCount; c++) {
1099
- const srcBase = shComponentCount * c;
1100
- const destBase = paddedSHComponentCount * c;
1101
- for (let i = 0; i < shComponentCount; i++) {
1102
- paddedSHArray[destBase + i] = shData[srcBase + i];
1103
- }
1104
- }
1105
-
1106
- const shTexture = new DataTexture(paddedSHArray, shTexSize.x, shTexSize.y, texelFormat, shTextureType);
1107
- shTexture.needsUpdate = true;
1108
- this.material.uniforms.sphericalHarmonicsTexture.value = shTexture;
1109
- this.splatDataTextures['sphericalHarmonics'] = {
1110
- 'componentCount': shComponentCount,
1111
- 'paddedComponentCount': paddedSHComponentCount,
1112
- 'data': paddedSHArray,
1113
- 'textureCount': 1,
1114
- 'texture': shTexture,
1115
- 'size': shTexSize,
1116
- 'compressionLevel': shCompressionLevel,
1117
- 'elementsPerTexel': shElementsPerTexel
1118
- };
1119
- // Use three textures for spherical harmonics data, one per color channel
1120
- } else {
1121
- const shComponentCountPerChannel = shComponentCount / 3;
1122
- paddedSHComponentCount = shComponentCountPerChannel;
1123
- if (paddedSHComponentCount % 2 !== 0) paddedSHComponentCount++;
1124
- shTexSize = computeDataTextureSize(shElementsPerTexel, paddedSHComponentCount);
1125
-
1126
- const paddedSHArraySize = shTexSize.x * shTexSize.y * shElementsPerTexel;
1127
- const textureUniforms = [this.material.uniforms.sphericalHarmonicsTextureR,
1128
- this.material.uniforms.sphericalHarmonicsTextureG,
1129
- this.material.uniforms.sphericalHarmonicsTextureB];
1130
- const paddedSHArrays = [];
1131
- const shTextures = [];
1132
- for (let t = 0; t < 3; t++) {
1133
- const paddedSHArray = new SphericalHarmonicsArrayType(paddedSHArraySize);
1134
- paddedSHArrays.push(paddedSHArray);
1135
- for (let c = 0; c < splatCount; c++) {
1136
- const srcBase = shComponentCount * c;
1137
- const destBase = paddedSHComponentCount * c;
1138
- if (shComponentCountPerChannel >= 3) {
1139
- for (let i = 0; i < 3; i++) paddedSHArray[destBase + i] = shData[srcBase + t * 3 + i];
1140
- if (shComponentCountPerChannel >= 8) {
1141
- for (let i = 0; i < 5; i++) paddedSHArray[destBase + 3 + i] = shData[srcBase + 9 + t * 5 + i];
1142
- }
1143
- }
1144
- }
1145
-
1146
- const shTexture = new DataTexture(paddedSHArray, shTexSize.x, shTexSize.y, texelFormat, shTextureType);
1147
- shTextures.push(shTexture);
1148
- shTexture.needsUpdate = true;
1149
- textureUniforms[t].value = shTexture;
1150
- }
1151
-
1152
- this.material.uniforms.sphericalHarmonicsMultiTextureMode.value = 1;
1153
- this.splatDataTextures['sphericalHarmonics'] = {
1154
- 'componentCount': shComponentCount,
1155
- 'componentCountPerChannel': shComponentCountPerChannel,
1156
- 'paddedComponentCount': paddedSHComponentCount,
1157
- 'data': paddedSHArrays,
1158
- 'textureCount': 3,
1159
- 'textures': shTextures,
1160
- 'size': shTexSize,
1161
- 'compressionLevel': shCompressionLevel,
1162
- 'elementsPerTexel': shElementsPerTexel
1163
- };
1164
- }
1165
-
1166
- this.material.uniforms.sphericalHarmonicsTextureSize.value.copy(shTexSize);
1167
- this.material.uniforms.sphericalHarmonics8BitMode.value = shCompressionLevel === 2 ? 1 : 0;
1168
- for (let s = 0; s < this.scenes.length; s++) {
1169
- const splatBuffer = this.scenes[s].splatBuffer;
1170
- this.material.uniforms.sphericalHarmonics8BitCompressionRangeMin.value[s] =
1171
- splatBuffer.minSphericalHarmonicsCoeff;
1172
- this.material.uniforms.sphericalHarmonics8BitCompressionRangeMax.value[s] =
1173
- splatBuffer.maxSphericalHarmonicsCoeff;
1174
- }
1175
- this.material.uniformsNeedUpdate = true;
1176
- }
1177
-
1178
- const sceneIndexesTexSize = computeDataTextureSize(SCENE_INDEXES_ELEMENTS_PER_TEXEL, 4);
1179
- const paddedTransformIndexes = new Uint32Array(sceneIndexesTexSize.x *
1180
- sceneIndexesTexSize.y * SCENE_INDEXES_ELEMENTS_PER_TEXEL);
1181
- for (let c = 0; c < splatCount; c++) paddedTransformIndexes[c] = this.globalSplatIndexToSceneIndexMap[c];
1182
- const sceneIndexesTexture = new DataTexture(paddedTransformIndexes, sceneIndexesTexSize.x, sceneIndexesTexSize.y,
1183
- RedIntegerFormat, UnsignedIntType);
1184
- sceneIndexesTexture.internalFormat = 'R32UI';
1185
- sceneIndexesTexture.needsUpdate = true;
1186
- this.material.uniforms.sceneIndexesTexture.value = sceneIndexesTexture;
1187
- this.material.uniforms.sceneIndexesTextureSize.value.copy(sceneIndexesTexSize);
1188
- this.material.uniformsNeedUpdate = true;
1189
- this.splatDataTextures['sceneIndexes'] = {
1190
- 'data': paddedTransformIndexes,
1191
- 'texture': sceneIndexesTexture,
1192
- 'size': sceneIndexesTexSize
1193
- };
1194
- this.material.uniforms.sceneCount.value = this.scenes.length;
1195
-
1196
- // FLAME-specific setup (only if flameModel has required data)
1197
- if (this.flameModel && this.flameModel.geometry && this.flameModel.geometry.morphAttributes && this.flameModel.skeleton) {
1198
- this.expressionBSNum = this.flameModel.geometry.morphAttributes.position.length;
1199
- this.material.uniforms.bsCount.value = this.expressionBSNum;
1200
-
1201
- this.flameModel.skeleton.bones.forEach((bone, index) => {
1202
- if (bone.name == 'head')
1203
- this.material.uniforms.headBoneIndex.value = index;
1204
- });
1205
-
1206
- this.buildModelTexture(this.flameModel);
1207
- this.buildBoneMatrixTexture();
1208
- this.buildBoneWeightTexture(this.flameModel);
1209
- }
1210
- }
1211
-
1212
- buildBoneMatrixTexture() {
1213
- if (!this.bsWeight)
1214
- return
1215
-
1216
- // Guard: Requires FLAME model with morphTargetDictionary
1217
- if (!this.flameModel || !this.flameModel.morphTargetDictionary) {
1218
- return;
1219
- }
1220
-
1221
- //this.bonesNum + this.expressionBSNum / 4 = 30, so 32
1222
- const boneTextureSize = new Vector2(4, 32);
1223
- let boneMatrixTextureData = new Float32Array(this.bonesMatrix);
1224
- let boneMatrixTextureDataInt = new Uint32Array(boneTextureSize.x * boneTextureSize.y * 4);
1225
- this.morphTargetDictionary = this.flameModel.morphTargetDictionary;
1226
-
1227
- for (let c = 0; c < this.bonesNum * 16; c++) {
1228
- boneMatrixTextureDataInt[c] = uintEncodedFloat(boneMatrixTextureData[c]);
1229
- }
1230
- if (this.flameModel && this.flameModel.skeleton) {
1231
- this.material.uniforms.boneTexture0.value = this.flameModel.skeleton.boneTexture;
1232
- this.material.uniforms.bindMatrix.value = this.flameModel.bindMatrix;
1233
- this.material.uniforms.bindMatrixInverse.value = this.flameModel.bindMatrixInverse;
1234
- }
1235
- for (const key in this.bsWeight) {
1236
- if (Object.hasOwn(this.bsWeight, key)) {
1237
- const value = this.bsWeight[key];
1238
- const idx = this.morphTargetDictionary[key];
1239
- boneMatrixTextureDataInt[idx + this.bonesNum * 16] = uintEncodedFloat(value);
1240
- }
1241
- }
1242
-
1243
- // for (let c = 0; c < this.bsWeight.length; c++) {
1244
- // this.morphTargetDictionary
1245
- // boneMatrixTextureDataInt[c + this.bonesNum * 16] = uintEncodedFloat(this.bsWeight[c]);
1246
- // }
1247
-
1248
- const boneMatrixTex = new DataTexture(boneMatrixTextureDataInt, boneTextureSize.x, boneTextureSize.y,
1249
- RGBAIntegerFormat, UnsignedIntType);
1250
- boneMatrixTex.internalFormat = 'RGBA32UI';
1251
- boneMatrixTex.needsUpdate = true;
1252
- this.material.uniforms.boneTexture.value = boneMatrixTex;
1253
- this.material.uniforms.boneTextureSize.value.copy(boneTextureSize);
1254
-
1255
- this.material.uniformsNeedUpdate = true;
1256
-
1257
- this.splatDataTextures['boneMatrix'] = {
1258
- 'data': boneMatrixTextureDataInt,
1259
- 'texture': boneMatrixTex,
1260
- 'size': boneTextureSize,
1261
- };
1262
- this.splatDataTextures.baseData['boneMatrix'] = boneMatrixTextureDataInt;
1263
- }
1264
-
1265
- updateBoneMatrixTexture() {
1266
- if (!this.bsWeight || !this.morphTargetDictionary)
1267
- return
1268
-
1269
- for (const key in this.bsWeight) {
1270
- if (Object.hasOwn(this.bsWeight, key)) {
1271
- const value = this.bsWeight[key];
1272
- const idx = this.morphTargetDictionary[key];
1273
- this.splatDataTextures.baseData['boneMatrix'][idx + this.bonesNum * 16] = uintEncodedFloat(value);
1274
- }
1275
- }
1276
-
1277
- // for (let c = 0; c < this.bsWeight.length; c++) {
1278
- // this.splatDataTextures.baseData['boneMatrix'][c + this.bonesNum * 16] = uintEncodedFloat(this.bsWeight[c]);
1279
- // }
1280
- this.splatDataTextures['boneMatrix']['texture'].data = this.splatDataTextures.baseData['boneMatrix'];
1281
-
1282
- this.splatDataTextures['boneMatrix']['texture'].needsUpdate = true;
1283
- this.material.uniforms.boneTexture.value = this.splatDataTextures['boneMatrix']['texture'];
1284
-
1285
- if (this.flameModel.skeleton) {
1286
- this.material.uniforms.boneTexture0.value = this.flameModel.skeleton.boneTexture;
1287
- this.material.uniforms.bindMatrix.value = this.flameModel.bindMatrix;
1288
- this.material.uniforms.bindMatrixInverse.value = this.flameModel.bindMatrixInverse;
1289
- }
1290
-
1291
- this.material.uniformsNeedUpdate = true;
1292
- }
1293
-
1294
- buildBoneWeightTexture(flameModel) {
1295
- // Guard: bonesWeight is required for FLAME bone weight texture building
1296
- if (!this.bonesWeight) {
1297
- return;
1298
- }
1299
-
1300
- let shapedMesh = flameModel.geometry.attributes.position.array;
1301
-
1302
- let pointNum = shapedMesh.length / 3;
1303
- const boneWeightTextureSize = new Vector2(512, 512);
1304
- let boneWeightTextureData = new Float32Array(boneWeightTextureSize.x * boneWeightTextureSize.y * 4);
1305
- let boneWeightTextureDataInt = new Uint32Array(boneWeightTextureSize.x * boneWeightTextureSize.y * 4);
1306
- for (let i = 0; i < pointNum; i++) {
1307
- boneWeightTextureData[i * 8 + 0] = this.bonesWeight[i][0];
1308
- boneWeightTextureData[i * 8 + 1] = this.bonesWeight[i][1];
1309
- boneWeightTextureData[i * 8 + 2] = this.bonesWeight[i][2];
1310
- boneWeightTextureData[i * 8 + 3] = this.bonesWeight[i][3];
1311
- boneWeightTextureData[i * 8 + 4] = this.bonesWeight[i][4];
1312
-
1313
- boneWeightTextureDataInt[i * 8 + 0] = uintEncodedFloat(this.bonesWeight[i][0]);
1314
- boneWeightTextureDataInt[i * 8 + 1] = uintEncodedFloat(this.bonesWeight[i][1]);
1315
- boneWeightTextureDataInt[i * 8 + 2] = uintEncodedFloat(this.bonesWeight[i][2]);
1316
- boneWeightTextureDataInt[i * 8 + 3] = uintEncodedFloat(this.bonesWeight[i][3]);
1317
- boneWeightTextureDataInt[i * 8 + 4] = uintEncodedFloat(this.bonesWeight[i][4]);
1318
-
1319
- }
1320
- const boneWeightTex = new DataTexture(boneWeightTextureDataInt, boneWeightTextureSize.x, boneWeightTextureSize.y,
1321
- RGBAIntegerFormat, UnsignedIntType);
1322
-
1323
- boneWeightTex.internalFormat = 'RGBA32UI';
1324
- boneWeightTex.needsUpdate = true;
1325
- this.material.uniforms.boneWeightTexture.value = boneWeightTex;
1326
- this.material.uniforms.boneWeightTextureSize.value.copy(boneWeightTextureSize);
1327
- this.material.uniformsNeedUpdate = true;
1328
-
1329
- this.splatDataTextures['boneWeight'] = {
1330
- 'data': boneWeightTextureDataInt,
1331
- 'texture': boneWeightTex,
1332
- 'size': boneWeightTextureSize,
1333
- };
1334
- this.splatDataTextures.baseData['boneWeight'] = boneWeightTextureDataInt;
1335
- }
1336
-
1337
-
1338
- buildModelTexture(flameModel) {
1339
- // Guard: Requires FLAME model with morph attributes
1340
- if (!flameModel || !flameModel.geometry || !flameModel.geometry.morphAttributes || !flameModel.morphTargetDictionary) {
1341
- return;
1342
- }
1343
-
1344
- const flameModelTexSize = new Vector2(4096, 2048);
1345
-
1346
- var shapedMesh = flameModel.geometry.attributes.position.array;
1347
- var shapedMeshArray = [];//Array.from(shapedMesh);
1348
- let pointNum = shapedMesh.length / 3;
1349
-
1350
- let bsLength = flameModel.geometry.morphAttributes.position.length;
1351
-
1352
- const morphTargetNames = Object.keys(flameModel.morphTargetDictionary);
1353
- morphTargetNames.forEach((name, newIndex) => {
1354
- const originalIndex = flameModel.morphTargetDictionary[name];
1355
- var bsMesh = flameModel.geometry.morphAttributes.position[originalIndex];
1356
- shapedMeshArray = shapedMeshArray.concat(Array.from(bsMesh.array));
1357
-
1358
- });
1359
- shapedMeshArray = shapedMeshArray.concat(Array.from(shapedMesh));
1360
-
1361
- let flameModelData = new Float32Array(flameModelTexSize.x * flameModelTexSize.y * 4);
1362
- let flameModelDataInt = new Uint32Array(flameModelTexSize.x * flameModelTexSize.y * 4);
1363
- for (let c = 0; c < pointNum * (bsLength + 1); c++) {
1364
- flameModelData[c * 4 + 0] = shapedMeshArray[c * 3 + 0];
1365
- flameModelData[c * 4 + 1] = shapedMeshArray[c * 3 + 1];
1366
- flameModelData[c * 4 + 2] = shapedMeshArray[c * 3 + 2];
1367
-
1368
- flameModelDataInt[c * 4 + 0] = uintEncodedFloat(flameModelData[c * 4 + 0]);
1369
- flameModelDataInt[c * 4 + 1] = uintEncodedFloat(flameModelData[c * 4 + 1]);
1370
- flameModelDataInt[c * 4 + 2] = uintEncodedFloat(flameModelData[c * 4 + 2]);
1371
- }
1372
-
1373
- const flameModelTex = new DataTexture(flameModelDataInt, flameModelTexSize.x, flameModelTexSize.y,
1374
- RGBAIntegerFormat, UnsignedIntType);
1375
- flameModelTex.internalFormat = 'RGBA32UI';
1376
- flameModelTex.needsUpdate = true;
1377
- this.material.uniforms.flameModelTexture.value = flameModelTex;
1378
- this.material.uniforms.flameModelTextureSize.value.copy(flameModelTexSize);
1379
- this.material.uniformsNeedUpdate = true;
1380
- this.material.uniforms.gaussianSplatCount.value = this.gaussianSplatCount;
1381
-
1382
- this.splatDataTextures['flameModel'] = {
1383
- 'data': flameModelDataInt,
1384
- 'texture': flameModelTex,
1385
- 'size': flameModelTexSize,
1386
- };
1387
- this.splatDataTextures.baseData['flameModelPos'] = flameModelData;
1388
- }
1389
-
1390
- updateTetureAfterBSAndSkeleton(fromSplat, toSplat) {
1391
- const sceneTransform = new Matrix4();
1392
-
1393
- this.getSceneTransform(0, sceneTransform);
1394
- this.getScene(0).splatBuffer.fillSplatCenterArray(this.morphedMesh, this.splatDataTextures.baseData.centers, sceneTransform, fromSplat, toSplat, 0);
1395
-
1396
- // Update center & color data texture
1397
- const centerColorsTextureDescriptor = this.splatDataTextures['centerColors'];
1398
- const paddedCenterColors = centerColorsTextureDescriptor.data;
1399
- const centerColorsTexture = centerColorsTextureDescriptor.texture;
1400
- SplatMesh.updateCenterColorsPaddedData(fromSplat, toSplat, this.splatDataTextures.baseData.centers,
1401
- this.splatDataTextures.baseData.colors, paddedCenterColors);
1402
- const centerColorsTextureProps = this.renderer ? this.renderer.properties.get(centerColorsTexture) : null;
1403
- if (!centerColorsTextureProps || !centerColorsTextureProps.__webglTexture) {
1404
- centerColorsTexture.needsUpdate = true;
1405
- } else {
1406
- this.updateDataTexture(paddedCenterColors, centerColorsTextureDescriptor.texture, centerColorsTextureDescriptor.size,
1407
- centerColorsTextureProps, CENTER_COLORS_ELEMENTS_PER_TEXEL, CENTER_COLORS_ELEMENTS_PER_SPLAT, 4,
1408
- fromSplat, toSplat);
1409
- }
1410
-
1411
- this.updateBoneMatrixTexture();
1412
- }
1413
-
1414
- updateBaseDataFromSplatBuffers(fromSplat, toSplat) {
1415
- const covarancesTextureDesc = this.splatDataTextures['covariances'];
1416
- const covarianceCompressionLevel = covarancesTextureDesc ? covarancesTextureDesc.compressionLevel : undefined;
1417
- const scaleRotationsTextureDesc = this.splatDataTextures['scaleRotations'];
1418
- const scaleRotationCompressionLevel = scaleRotationsTextureDesc ? scaleRotationsTextureDesc.compressionLevel : undefined;
1419
- const shITextureDesc = this.splatDataTextures['sphericalHarmonics'];
1420
- const shCompressionLevel = shITextureDesc ? shITextureDesc.compressionLevel : 0;
1421
-
1422
- this.fillSplatDataArrays(this.splatDataTextures.baseData.covariances, this.splatDataTextures.baseData.scales,
1423
- this.splatDataTextures.baseData.rotations, this.splatDataTextures.baseData.centers,
1424
- this.splatDataTextures.baseData.colors, this.splatDataTextures.baseData.sphericalHarmonics,
1425
- this.splatDataTextures.baseData.flameModelPos,
1426
- undefined,
1427
- covarianceCompressionLevel, scaleRotationCompressionLevel, shCompressionLevel,
1428
- fromSplat, toSplat, fromSplat);
1429
- }
1430
-
1431
- updateDataTexturesFromBaseData(fromSplat, toSplat) {
1432
- const covarancesTextureDesc = this.splatDataTextures['covariances'];
1433
- const covarianceCompressionLevel = covarancesTextureDesc ? covarancesTextureDesc.compressionLevel : undefined;
1434
- const scaleRotationsTextureDesc = this.splatDataTextures['scaleRotations'];
1435
- const scaleRotationCompressionLevel = scaleRotationsTextureDesc ? scaleRotationsTextureDesc.compressionLevel : undefined;
1436
- const shTextureDesc = this.splatDataTextures['sphericalHarmonics'];
1437
- const shCompressionLevel = shTextureDesc ? shTextureDesc.compressionLevel : 0;
1438
-
1439
- // Update flame data texture
1440
- const flameModelTextureDescriptor = this.splatDataTextures['flameModel'];
1441
- const flameModelPos = flameModelTextureDescriptor.data;
1442
- const flameModelPosTexture = flameModelTextureDescriptor.texture;
1443
-
1444
- const flameModelPosTextureProps = this.renderer ? this.renderer.properties.get(flameModelPosTexture) : null;
1445
- if (!flameModelPosTextureProps || !flameModelPosTextureProps.__webglTexture) {
1446
- flameModelPosTexture.needsUpdate = true;
1447
- } else {
1448
- this.updateDataTexture(flameModelPos, flameModelTextureDescriptor.texture, flameModelTextureDescriptor.size,
1449
- flameModelPosTextureProps, CENTER_COLORS_ELEMENTS_PER_TEXEL, CENTER_COLORS_ELEMENTS_PER_SPLAT, 3,
1450
- fromSplat, toSplat);
1451
- }
1452
-
1453
- // Update center & color data texture
1454
- const centerColorsTextureDescriptor = this.splatDataTextures['centerColors'];
1455
- const paddedCenterColors = centerColorsTextureDescriptor.data;
1456
- const centerColorsTexture = centerColorsTextureDescriptor.texture;
1457
- SplatMesh.updateCenterColorsPaddedData(fromSplat, toSplat, this.splatDataTextures.baseData.centers,
1458
- this.splatDataTextures.baseData.colors, paddedCenterColors);
1459
- const centerColorsTextureProps = this.renderer ? this.renderer.properties.get(centerColorsTexture) : null;
1460
- if (!centerColorsTextureProps || !centerColorsTextureProps.__webglTexture) {
1461
- centerColorsTexture.needsUpdate = true;
1462
- } else {
1463
- this.updateDataTexture(paddedCenterColors, centerColorsTextureDescriptor.texture, centerColorsTextureDescriptor.size,
1464
- centerColorsTextureProps, CENTER_COLORS_ELEMENTS_PER_TEXEL, CENTER_COLORS_ELEMENTS_PER_SPLAT, 4,
1465
- fromSplat, toSplat);
1466
- }
1467
-
1468
- // update covariance data texture
1469
- if (covarancesTextureDesc) {
1470
- const covariancesTexture = covarancesTextureDesc.texture;
1471
- const covarancesStartElement = fromSplat * COVARIANCES_ELEMENTS_PER_SPLAT;
1472
- const covariancesEndElement = toSplat * COVARIANCES_ELEMENTS_PER_SPLAT;
1473
-
1474
- if (covarianceCompressionLevel === 0) {
1475
- for (let i = covarancesStartElement; i <= covariancesEndElement; i++) {
1476
- const covariance = this.splatDataTextures.baseData.covariances[i];
1477
- covarancesTextureDesc.data[i] = covariance;
1478
- }
1479
- } else {
1480
- SplatMesh.updatePaddedCompressedCovariancesTextureData(this.splatDataTextures.baseData.covariances,
1481
- covarancesTextureDesc.data,
1482
- fromSplat * covarancesTextureDesc.elementsPerTexelAllocated,
1483
- covarancesStartElement, covariancesEndElement);
1484
- }
1485
-
1486
- const covariancesTextureProps = this.renderer ? this.renderer.properties.get(covariancesTexture) : null;
1487
- if (!covariancesTextureProps || !covariancesTextureProps.__webglTexture) {
1488
- covariancesTexture.needsUpdate = true;
1489
- } else {
1490
- if (covarianceCompressionLevel === 0) {
1491
- this.updateDataTexture(covarancesTextureDesc.data, covarancesTextureDesc.texture, covarancesTextureDesc.size,
1492
- covariancesTextureProps, covarancesTextureDesc.elementsPerTexelStored,
1493
- COVARIANCES_ELEMENTS_PER_SPLAT, 4, fromSplat, toSplat);
1494
- } else {
1495
- this.updateDataTexture(covarancesTextureDesc.data, covarancesTextureDesc.texture, covarancesTextureDesc.size,
1496
- covariancesTextureProps, covarancesTextureDesc.elementsPerTexelAllocated,
1497
- covarancesTextureDesc.elementsPerTexelAllocated, 2, fromSplat, toSplat);
1498
- }
1499
- }
1500
- }
1501
-
1502
- // update scale and rotation data texture
1503
- if (scaleRotationsTextureDesc) {
1504
- const paddedScaleRotations = scaleRotationsTextureDesc.data;
1505
- const scaleRotationsTexture = scaleRotationsTextureDesc.texture;
1506
- const elementsPerSplat = 6;
1507
- const bytesPerElement = scaleRotationCompressionLevel === 0 ? 4 : 2;
1508
-
1509
- SplatMesh.updateScaleRotationsPaddedData(fromSplat, toSplat, this.splatDataTextures.baseData.scales,
1510
- this.splatDataTextures.baseData.rotations, paddedScaleRotations);
1511
- const scaleRotationsTextureProps = this.renderer ? this.renderer.properties.get(scaleRotationsTexture) : null;
1512
- if (!scaleRotationsTextureProps || !scaleRotationsTextureProps.__webglTexture) {
1513
- scaleRotationsTexture.needsUpdate = true;
1514
- } else {
1515
- this.updateDataTexture(paddedScaleRotations, scaleRotationsTextureDesc.texture, scaleRotationsTextureDesc.size,
1516
- scaleRotationsTextureProps, SCALES_ROTATIONS_ELEMENTS_PER_TEXEL, elementsPerSplat, bytesPerElement,
1517
- fromSplat, toSplat);
1518
- }
1519
- }
1520
-
1521
- // update spherical harmonics data texture
1522
- const shData = this.splatDataTextures.baseData.sphericalHarmonics;
1523
- if (shData) {
1524
- let shBytesPerElement = 4;
1525
- if (shCompressionLevel === 1) shBytesPerElement = 2;
1526
- else if (shCompressionLevel === 2) shBytesPerElement = 1;
1527
-
1528
- const updateTexture = (shTexture, shTextureSize, elementsPerTexel, paddedSHArray, paddedSHComponentCount) => {
1529
- const shTextureProps = this.renderer ? this.renderer.properties.get(shTexture) : null;
1530
- if (!shTextureProps || !shTextureProps.__webglTexture) {
1531
- shTexture.needsUpdate = true;
1532
- } else {
1533
- this.updateDataTexture(paddedSHArray, shTexture, shTextureSize, shTextureProps, elementsPerTexel,
1534
- paddedSHComponentCount, shBytesPerElement, fromSplat, toSplat);
1535
- }
1536
- };
1537
-
1538
- const shComponentCount = shTextureDesc.componentCount;
1539
- const paddedSHComponentCount = shTextureDesc.paddedComponentCount;
1540
-
1541
- // Update for the case of a single texture for all spherical harmonics data
1542
- if (shTextureDesc.textureCount === 1) {
1543
- const paddedSHArray = shTextureDesc.data;
1544
- for (let c = fromSplat; c <= toSplat; c++) {
1545
- const srcBase = shComponentCount * c;
1546
- const destBase = paddedSHComponentCount * c;
1547
- for (let i = 0; i < shComponentCount; i++) {
1548
- paddedSHArray[destBase + i] = shData[srcBase + i];
1549
- }
1550
- }
1551
- updateTexture(shTextureDesc.texture, shTextureDesc.size,
1552
- shTextureDesc.elementsPerTexel, paddedSHArray, paddedSHComponentCount);
1553
- // Update for the case of spherical harmonics data split among three textures, one for each color channel
1554
- } else {
1555
- const shComponentCountPerChannel = shTextureDesc.componentCountPerChannel;
1556
- for (let t = 0; t < 3; t++) {
1557
- const paddedSHArray = shTextureDesc.data[t];
1558
- for (let c = fromSplat; c <= toSplat; c++) {
1559
- const srcBase = shComponentCount * c;
1560
- const destBase = paddedSHComponentCount * c;
1561
- if (shComponentCountPerChannel >= 3) {
1562
- for (let i = 0; i < 3; i++) paddedSHArray[destBase + i] = shData[srcBase + t * 3 + i];
1563
- if (shComponentCountPerChannel >= 8) {
1564
- for (let i = 0; i < 5; i++) paddedSHArray[destBase + 3 + i] = shData[srcBase + 9 + t * 5 + i];
1565
- }
1566
- }
1567
- }
1568
- updateTexture(shTextureDesc.textures[t], shTextureDesc.size,
1569
- shTextureDesc.elementsPerTexel, paddedSHArray, paddedSHComponentCount);
1570
- }
1571
- }
1572
- }
1573
-
1574
- // update scene index & transform data
1575
- const sceneIndexesTexDesc = this.splatDataTextures['sceneIndexes'];
1576
- const paddedSceneIndexes = sceneIndexesTexDesc.data;
1577
- for (let c = this.lastBuildSplatCount; c <= toSplat; c++) {
1578
- paddedSceneIndexes[c] = this.globalSplatIndexToSceneIndexMap[c];
1579
- }
1580
- const sceneIndexesTexture = sceneIndexesTexDesc.texture;
1581
- const sceneIndexesTextureProps = this.renderer ? this.renderer.properties.get(sceneIndexesTexture) : null;
1582
- if (!sceneIndexesTextureProps || !sceneIndexesTextureProps.__webglTexture) {
1583
- sceneIndexesTexture.needsUpdate = true;
1584
- } else {
1585
- this.updateDataTexture(paddedSceneIndexes, sceneIndexesTexDesc.texture, sceneIndexesTexDesc.size,
1586
- sceneIndexesTextureProps, 1, 1, 1, this.lastBuildSplatCount, toSplat);
1587
- }
1588
- }
1589
-
1590
- getTargetCovarianceCompressionLevel() {
1591
- return this.halfPrecisionCovariancesOnGPU ? 1 : 0;
1592
- }
1593
-
1594
- getTargetSphericalHarmonicsCompressionLevel() {
1595
- return Math.max(1, this.getMaximumSplatBufferCompressionLevel());
1596
- }
1597
-
1598
- getMaximumSplatBufferCompressionLevel() {
1599
- let maxCompressionLevel;
1600
- for (let i = 0; i < this.scenes.length; i++) {
1601
- const scene = this.getScene(i);
1602
- const splatBuffer = scene.splatBuffer;
1603
- if (i === 0 || splatBuffer.compressionLevel > maxCompressionLevel) {
1604
- maxCompressionLevel = splatBuffer.compressionLevel;
1605
- }
1606
- }
1607
- return maxCompressionLevel;
1608
- }
1609
-
1610
- getMinimumSplatBufferCompressionLevel() {
1611
- let minCompressionLevel;
1612
- for (let i = 0; i < this.scenes.length; i++) {
1613
- const scene = this.getScene(i);
1614
- const splatBuffer = scene.splatBuffer;
1615
- if (i === 0 || splatBuffer.compressionLevel < minCompressionLevel) {
1616
- minCompressionLevel = splatBuffer.compressionLevel;
1617
- }
1618
- }
1619
- return minCompressionLevel;
1620
- }
1621
-
1622
- static computeTextureUpdateRegion(startSplat, endSplat, textureWidth, elementsPerTexel, elementsPerSplat) {
1623
- const texelsPerSplat = elementsPerSplat / elementsPerTexel;
1624
-
1625
- const startSplatTexels = startSplat * texelsPerSplat;
1626
- const startRow = Math.floor(startSplatTexels / textureWidth);
1627
- const startRowElement = startRow * textureWidth * elementsPerTexel;
1628
-
1629
- const endSplatTexels = endSplat * texelsPerSplat;
1630
- const endRow = Math.floor(endSplatTexels / textureWidth);
1631
- const endRowEndElement = endRow * textureWidth * elementsPerTexel + (textureWidth * elementsPerTexel);
1632
-
1633
- return {
1634
- 'dataStart': startRowElement,
1635
- 'dataEnd': endRowEndElement,
1636
- 'startRow': startRow,
1637
- 'endRow': endRow
1638
- };
1639
- }
1640
-
1641
- updateDataTexture(paddedData, texture, textureSize, textureProps, elementsPerTexel, elementsPerSplat, bytesPerElement, from, to) {
1642
- const gl = this.renderer.getContext();
1643
- const updateRegion = SplatMesh.computeTextureUpdateRegion(from, to, textureSize.x, elementsPerTexel, elementsPerSplat);
1644
- const updateElementCount = updateRegion.dataEnd - updateRegion.dataStart;
1645
- const updateDataView = new paddedData.constructor(paddedData.buffer,
1646
- updateRegion.dataStart * bytesPerElement, updateElementCount);
1647
- const updateHeight = updateRegion.endRow - updateRegion.startRow + 1;
1648
- const glType = this.webGLUtils.convert(texture.type);
1649
- const glFormat = this.webGLUtils.convert(texture.format, texture.colorSpace);
1650
- const currentTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
1651
- gl.bindTexture(gl.TEXTURE_2D, textureProps.__webglTexture);
1652
- gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, updateRegion.startRow,
1653
- textureSize.x, updateHeight, glFormat, glType, updateDataView);
1654
- gl.bindTexture(gl.TEXTURE_2D, currentTexture);
1655
- }
1656
-
1657
- static updatePaddedCompressedCovariancesTextureData(sourceData, textureData, textureDataStartIndex, fromElement, toElement) {
1658
- let textureDataView = new DataView(textureData.buffer);
1659
- let textureDataIndex = textureDataStartIndex;
1660
- let sequentialCount = 0;
1661
- for (let i = fromElement; i <= toElement; i+=2) {
1662
- textureDataView.setUint16(textureDataIndex * 2, sourceData[i], true);
1663
- textureDataView.setUint16(textureDataIndex * 2 + 2, sourceData[i + 1], true);
1664
- textureDataIndex += 2;
1665
- sequentialCount++;
1666
- if (sequentialCount >= 3) {
1667
- textureDataIndex += 2;
1668
- sequentialCount = 0;
1669
- }
1670
- }
1671
- }
1672
-
1673
- static updateCenterColorsPaddedData(from, to, centers, colors, paddedCenterColors) {
1674
- for (let c = from; c <= to; c++) {
1675
- const colorsBase = c * 4;
1676
- const centersBase = c * 3;
1677
- const centerColorsBase = c * 4;
1678
- paddedCenterColors[centerColorsBase] = rgbaArrayToInteger(colors, colorsBase);
1679
- paddedCenterColors[centerColorsBase + 1] = uintEncodedFloat(centers[centersBase]);
1680
- paddedCenterColors[centerColorsBase + 2] = uintEncodedFloat(centers[centersBase + 1]);
1681
- paddedCenterColors[centerColorsBase + 3] = uintEncodedFloat(centers[centersBase + 2]);
1682
- }
1683
- }
1684
-
1685
- static updateScaleRotationsPaddedData(from, to, scales, rotations, paddedScaleRotations) {
1686
- const combinedSize = 6;
1687
- for (let c = from; c <= to; c++) {
1688
- const scaleBase = c * 3;
1689
- const rotationBase = c * 4;
1690
- const scaleRotationsBase = c * combinedSize;
1691
-
1692
- paddedScaleRotations[scaleRotationsBase] = scales[scaleBase];
1693
- paddedScaleRotations[scaleRotationsBase + 1] = scales[scaleBase + 1];
1694
- paddedScaleRotations[scaleRotationsBase + 2] = scales[scaleBase + 2];
1695
-
1696
- paddedScaleRotations[scaleRotationsBase + 3] = rotations[rotationBase];
1697
- paddedScaleRotations[scaleRotationsBase + 4] = rotations[rotationBase + 1];
1698
- paddedScaleRotations[scaleRotationsBase + 5] = rotations[rotationBase + 2];
1699
- }
1700
- }
1701
-
1702
- updateVisibleRegion(sinceLastBuildOnly) {
1703
- const splatCount = this.getSplatCount(true);
1704
- const tempCenter = new Vector3();
1705
- if (!sinceLastBuildOnly) {
1706
- const avgCenter = new Vector3();
1707
- this.scenes.forEach((scene) => {
1708
- avgCenter.add(scene.splatBuffer.sceneCenter);
1709
- });
1710
- avgCenter.multiplyScalar(1.0 / this.scenes.length);
1711
- this.calculatedSceneCenter.copy(avgCenter);
1712
- this.material.uniforms.sceneCenter.value.copy(this.calculatedSceneCenter);
1713
- this.material.uniformsNeedUpdate = true;
1714
- }
1715
-
1716
- const startSplatFormMaxDistanceCalc = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
1717
- for (let i = startSplatFormMaxDistanceCalc; i < splatCount; i++) {
1718
- this.getSplatCenter(this.morphedMesh, i, tempCenter, true);
1719
- const distFromCSceneCenter = tempCenter.sub(this.calculatedSceneCenter).length();
1720
- if (distFromCSceneCenter > this.maxSplatDistanceFromSceneCenter) this.maxSplatDistanceFromSceneCenter = distFromCSceneCenter;
1721
- }
1722
-
1723
- if (this.maxSplatDistanceFromSceneCenter - this.visibleRegionBufferRadius > VISIBLE_REGION_EXPANSION_DELTA) {
1724
- this.visibleRegionBufferRadius = this.maxSplatDistanceFromSceneCenter;
1725
- this.visibleRegionRadius = Math.max(this.visibleRegionBufferRadius - VISIBLE_REGION_EXPANSION_DELTA, 0.0);
1726
- }
1727
- if (this.finalBuild) this.visibleRegionRadius = this.visibleRegionBufferRadius = this.maxSplatDistanceFromSceneCenter;
1728
- this.updateVisibleRegionFadeDistance();
1729
- }
1730
-
1731
- updateVisibleRegionFadeDistance(sceneRevealMode = SceneRevealMode.Default) {
1732
- const fastFadeRate = SCENE_FADEIN_RATE_FAST * this.sceneFadeInRateMultiplier;
1733
- const gradualFadeRate = SCENE_FADEIN_RATE_GRADUAL * this.sceneFadeInRateMultiplier;
1734
- const defaultFadeInRate = this.finalBuild ? fastFadeRate : gradualFadeRate;
1735
- const fadeInRate = sceneRevealMode === SceneRevealMode.Default ? defaultFadeInRate : gradualFadeRate;
1736
- this.visibleRegionFadeStartRadius = (this.visibleRegionRadius - this.visibleRegionFadeStartRadius) *
1737
- fadeInRate + this.visibleRegionFadeStartRadius;
1738
- const fadeInPercentage = (this.visibleRegionBufferRadius > 0) ?
1739
- (this.visibleRegionFadeStartRadius / this.visibleRegionBufferRadius) : 0;
1740
- const fadeInComplete = fadeInPercentage > 0.99;
1741
- const shaderFadeInComplete = (fadeInComplete || sceneRevealMode === SceneRevealMode.Instant) ? 1 : 0;
1742
-
1743
- this.material.uniforms.visibleRegionFadeStartRadius.value = this.visibleRegionFadeStartRadius;
1744
- this.material.uniforms.visibleRegionRadius.value = this.visibleRegionRadius;
1745
- this.material.uniforms.firstRenderTime.value = this.firstRenderTime;
1746
- this.material.uniforms.currentTime.value = performance.now();
1747
- this.material.uniforms.fadeInComplete.value = shaderFadeInComplete;
1748
- this.material.uniformsNeedUpdate = true;
1749
- this.visibleRegionChanging = !fadeInComplete;
1750
- }
1751
-
1752
- /**
1753
- * Set the indexes of splats that should be rendered; should be sorted in desired render order.
1754
- * @param {Uint32Array} globalIndexes Sorted index list of splats to be rendered
1755
- * @param {number} renderSplatCount Total number of splats to be rendered. Necessary because we may not want to render
1756
- * every splat.
1757
- */
1758
- updateRenderIndexes(globalIndexes, renderSplatCount) {
1759
- const geometry = this.geometry;
1760
- geometry.attributes.splatIndex.set(globalIndexes);
1761
- geometry.attributes.splatIndex.needsUpdate = true;
1762
- if (renderSplatCount > 0 && this.firstRenderTime === -1) this.firstRenderTime = performance.now();
1763
- geometry.instanceCount = renderSplatCount;
1764
- geometry.setDrawRange(0, renderSplatCount);
1765
- }
1766
-
1767
- /**
1768
- * Update the transforms for each scene in this splat mesh from their individual components (position,
1769
- * quaternion, and scale)
1770
- */
1771
- updateTransforms() {
1772
- for (let i = 0; i < this.scenes.length; i++) {
1773
- const scene = this.getScene(i);
1774
- scene.updateTransform(this.dynamicMode);
1775
- }
1776
- }
1777
-
1778
- updateUniforms = function() {
1779
-
1780
- const viewport = new Vector2();
1781
-
1782
- return function(renderDimensions, cameraFocalLengthX, cameraFocalLengthY,
1783
- orthographicMode, orthographicZoom, inverseFocalAdjustment) {
1784
- const splatCount = this.getSplatCount();
1785
- if (splatCount > 0) {
1786
- viewport.set(renderDimensions.x * this.devicePixelRatio,
1787
- renderDimensions.y * this.devicePixelRatio);
1788
- this.material.uniforms.viewport.value.copy(viewport);
1789
- this.material.uniforms.basisViewport.value.set(1.0 / viewport.x, 1.0 / viewport.y);
1790
- this.material.uniforms.focal.value.set(cameraFocalLengthX, cameraFocalLengthY);
1791
- this.material.uniforms.orthographicMode.value = orthographicMode ? 1 : 0;
1792
- this.material.uniforms.orthoZoom.value = orthographicZoom;
1793
- this.material.uniforms.inverseFocalAdjustment.value = inverseFocalAdjustment;
1794
- if (this.dynamicMode) {
1795
- for (let i = 0; i < this.scenes.length; i++) {
1796
- this.material.uniforms.transforms.value[i].copy(this.getScene(i).transform);
1797
- }
1798
- }
1799
- if (this.enableOptionalEffects) {
1800
- for (let i = 0; i < this.scenes.length; i++) {
1801
- this.material.uniforms.sceneOpacity.value[i] = clamp(this.getScene(i).opacity, 0.0, 1.0);
1802
- this.material.uniforms.sceneVisibility.value[i] = this.getScene(i).visible ? 1 : 0;
1803
- this.material.uniformsNeedUpdate = true;
1804
- }
1805
- }
1806
- this.material.uniformsNeedUpdate = true;
1807
- }
1808
- };
1809
-
1810
- }();
1811
-
1812
- setSplatScale(splatScale = 1) {
1813
- this.splatScale = splatScale;
1814
- this.material.uniforms.splatScale.value = splatScale;
1815
- this.material.uniformsNeedUpdate = true;
1816
- }
1817
-
1818
- getSplatScale() {
1819
- return this.splatScale;
1820
- }
1821
-
1822
- setPointCloudModeEnabled(enabled) {
1823
- this.pointCloudModeEnabled = enabled;
1824
- this.material.uniforms.pointCloudModeEnabled.value = enabled ? 1 : 0;
1825
- this.material.uniformsNeedUpdate = true;
1826
- }
1827
-
1828
- getPointCloudModeEnabled() {
1829
- return this.pointCloudModeEnabled;
1830
- }
1831
-
1832
- getSplatDataTextures() {
1833
- return this.splatDataTextures;
1834
- }
1835
-
1836
- getSplatCount(includeSinceLastBuild = false) {
1837
- if (!includeSinceLastBuild) return this.lastBuildSplatCount;
1838
- else return SplatMesh.getTotalSplatCountForScenes(this.scenes);
1839
- }
1840
-
1841
- static getTotalSplatCountForScenes(scenes) {
1842
- let totalSplatCount = 0;
1843
- for (let scene of scenes) {
1844
- if (scene && scene.splatBuffer) totalSplatCount += scene.splatBuffer.getSplatCount();
1845
- }
1846
- return totalSplatCount;
1847
- }
1848
-
1849
- static getTotalSplatCountForSplatBuffers(splatBuffers) {
1850
- let totalSplatCount = 0;
1851
- for (let splatBuffer of splatBuffers) totalSplatCount += splatBuffer.getSplatCount();
1852
- return totalSplatCount;
1853
- }
1854
-
1855
- getMaxSplatCount() {
1856
- return SplatMesh.getTotalMaxSplatCountForScenes(this.scenes);
1857
- }
1858
-
1859
- static getTotalMaxSplatCountForScenes(scenes) {
1860
- let totalSplatCount = 0;
1861
- for (let scene of scenes) {
1862
- if (scene && scene.splatBuffer) totalSplatCount += scene.splatBuffer.getMaxSplatCount();
1863
- }
1864
- return totalSplatCount;
1865
- }
1866
-
1867
- static getTotalMaxSplatCountForSplatBuffers(splatBuffers) {
1868
- let totalSplatCount = 0;
1869
- for (let splatBuffer of splatBuffers) totalSplatCount += splatBuffer.getMaxSplatCount();
1870
- return totalSplatCount;
1871
- }
1872
-
1873
- disposeDistancesComputationGPUResources() {
1874
-
1875
- if (!this.renderer) return;
1876
-
1877
- const gl = this.renderer.getContext();
1878
-
1879
- if (this.distancesTransformFeedback.vao) {
1880
- gl.deleteVertexArray(this.distancesTransformFeedback.vao);
1881
- this.distancesTransformFeedback.vao = null;
1882
- }
1883
- if (this.distancesTransformFeedback.program) {
1884
- gl.deleteProgram(this.distancesTransformFeedback.program);
1885
- gl.deleteShader(this.distancesTransformFeedback.vertexShader);
1886
- gl.deleteShader(this.distancesTransformFeedback.fragmentShader);
1887
- this.distancesTransformFeedback.program = null;
1888
- this.distancesTransformFeedback.vertexShader = null;
1889
- this.distancesTransformFeedback.fragmentShader = null;
1890
- }
1891
- this.disposeDistancesComputationGPUBufferResources();
1892
- if (this.distancesTransformFeedback.id) {
1893
- gl.deleteTransformFeedback(this.distancesTransformFeedback.id);
1894
- this.distancesTransformFeedback.id = null;
1895
- }
1896
- }
1897
-
1898
- disposeDistancesComputationGPUBufferResources() {
1899
-
1900
- if (!this.renderer) return;
1901
-
1902
- const gl = this.renderer.getContext();
1903
-
1904
- if (this.distancesTransformFeedback.centersBuffer) {
1905
- this.distancesTransformFeedback.centersBuffer = null;
1906
- gl.deleteBuffer(this.distancesTransformFeedback.centersBuffer);
1907
- }
1908
- if (this.distancesTransformFeedback.outDistancesBuffer) {
1909
- gl.deleteBuffer(this.distancesTransformFeedback.outDistancesBuffer);
1910
- this.distancesTransformFeedback.outDistancesBuffer = null;
1911
- }
1912
- }
1913
-
1914
- /**
1915
- * Set the Three.js renderer used by this splat mesh
1916
- * @param {THREE.WebGLRenderer} renderer Instance of THREE.WebGLRenderer
1917
- */
1918
- setRenderer(renderer) {
1919
- if (renderer !== this.renderer) {
1920
- this.renderer = renderer;
1921
- const gl = this.renderer.getContext();
1922
- const extensions = new WebGLExtensions$1(gl);
1923
- const capabilities = new WebGLCapabilities$1(gl, extensions, {});
1924
- extensions.init(capabilities);
1925
- this.webGLUtils = new WebGLUtils$1(gl, extensions, capabilities);
1926
- if (this.enableDistancesComputationOnGPU && this.getSplatCount() > 0) {
1927
- this.setupDistancesComputationTransformFeedback();
1928
- const { centers, sceneIndexes } = this.getDataForDistancesComputation(0, this.getSplatCount() - 1);
1929
- this.refreshGPUBuffersForDistancesComputation(centers, sceneIndexes);
1930
- }
1931
- }
1932
- }
1933
-
1934
- setupDistancesComputationTransformFeedback = function() {
1935
-
1936
- let currentMaxSplatCount;
1937
-
1938
- return () => {
1939
- const maxSplatCount = this.getMaxSplatCount();
1940
-
1941
- if (!this.renderer) return;
1942
-
1943
- const rebuildGPUObjects = (this.lastRenderer !== this.renderer);
1944
- const rebuildBuffers = currentMaxSplatCount !== maxSplatCount;
1945
-
1946
- if (!rebuildGPUObjects && !rebuildBuffers) return;
1947
-
1948
- if (rebuildGPUObjects) {
1949
- this.disposeDistancesComputationGPUResources();
1950
- } else if (rebuildBuffers) {
1951
- this.disposeDistancesComputationGPUBufferResources();
1952
- }
1953
-
1954
- const gl = this.renderer.getContext();
1955
-
1956
- const createShader = (gl, type, source) => {
1957
- const shader = gl.createShader(type);
1958
- if (!shader) {
1959
- logger.error('Fatal error: gl could not create a shader object.');
1960
- return null;
1961
- }
1962
-
1963
- gl.shaderSource(shader, source);
1964
- gl.compileShader(shader);
1965
-
1966
- const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
1967
- if (!compiled) {
1968
- let typeName = 'unknown';
1969
- if (type === gl.VERTEX_SHADER) typeName = 'vertex shader';
1970
- else if (type === gl.FRAGMENT_SHADER) typeName = 'fragement shader';
1971
- const errors = gl.getShaderInfoLog(shader);
1972
- logger.error('Failed to compile ' + typeName + ' with these errors:' + errors);
1973
- gl.deleteShader(shader);
1974
- return null;
1975
- }
1976
-
1977
- return shader;
1978
- };
1979
-
1980
- let vsSource;
1981
- if (this.integerBasedDistancesComputation) {
1982
- vsSource =
1983
- `#version 300 es
1984
- in ivec4 center;
1985
- flat out int distance;`;
1986
- if (this.dynamicMode) {
1987
- vsSource += `
1988
- in uint sceneIndex;
1989
- uniform ivec4 transforms[${Constants.MaxScenes}];
1990
- void main(void) {
1991
- ivec4 transform = transforms[sceneIndex];
1992
- distance = center.x * transform.x + center.y * transform.y + center.z * transform.z + transform.w * center.w;
1993
- }
1994
- `;
1995
- } else {
1996
- vsSource += `
1997
- uniform ivec3 modelViewProj;
1998
- void main(void) {
1999
- distance = center.x * modelViewProj.x + center.y * modelViewProj.y + center.z * modelViewProj.z;
2000
- }
2001
- `;
2002
- }
2003
- } else {
2004
- vsSource =
2005
- `#version 300 es
2006
- in vec4 center;
2007
- flat out float distance;`;
2008
- if (this.dynamicMode) {
2009
- vsSource += `
2010
- in uint sceneIndex;
2011
- uniform mat4 transforms[${Constants.MaxScenes}];
2012
- void main(void) {
2013
- vec4 transformedCenter = transforms[sceneIndex] * vec4(center.xyz, 1.0);
2014
- distance = transformedCenter.z;
2015
- }
2016
- `;
2017
- } else {
2018
- vsSource += `
2019
- uniform vec3 modelViewProj;
2020
- void main(void) {
2021
- distance = center.x * modelViewProj.x + center.y * modelViewProj.y + center.z * modelViewProj.z;
2022
- }
2023
- `;
2024
- }
2025
- }
2026
-
2027
- const fsSource =
2028
- `#version 300 es
2029
- precision lowp float;
2030
- out vec4 fragColor;
2031
- void main(){}
2032
- `;
2033
-
2034
- const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
2035
- const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
2036
- const currentProgramDeleted = currentProgram ? gl.getProgramParameter(currentProgram, gl.DELETE_STATUS) : false;
2037
-
2038
- if (rebuildGPUObjects) {
2039
- this.distancesTransformFeedback.vao = gl.createVertexArray();
2040
- }
2041
-
2042
- gl.bindVertexArray(this.distancesTransformFeedback.vao);
2043
-
2044
- if (rebuildGPUObjects) {
2045
- const program = gl.createProgram();
2046
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
2047
- const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
2048
- if (!vertexShader || !fragmentShader) {
2049
- throw new Error('Could not compile shaders for distances computation on GPU.');
2050
- }
2051
- gl.attachShader(program, vertexShader);
2052
- gl.attachShader(program, fragmentShader);
2053
- gl.transformFeedbackVaryings(program, ['distance'], gl.SEPARATE_ATTRIBS);
2054
- gl.linkProgram(program);
2055
-
2056
- const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
2057
- if (!linked) {
2058
- const error = gl.getProgramInfoLog(program);
2059
- logger.error('Fatal error: Failed to link program: ' + error);
2060
- gl.deleteProgram(program);
2061
- gl.deleteShader(fragmentShader);
2062
- gl.deleteShader(vertexShader);
2063
- throw new Error('Could not link shaders for distances computation on GPU.');
2064
- }
2065
-
2066
- this.distancesTransformFeedback.program = program;
2067
- this.distancesTransformFeedback.vertexShader = vertexShader;
2068
- this.distancesTransformFeedback.vertexShader = fragmentShader;
2069
- }
2070
-
2071
- gl.useProgram(this.distancesTransformFeedback.program);
2072
-
2073
- this.distancesTransformFeedback.centersLoc =
2074
- gl.getAttribLocation(this.distancesTransformFeedback.program, 'center');
2075
- if (this.dynamicMode) {
2076
- this.distancesTransformFeedback.sceneIndexesLoc =
2077
- gl.getAttribLocation(this.distancesTransformFeedback.program, 'sceneIndex');
2078
- for (let i = 0; i < this.scenes.length; i++) {
2079
- this.distancesTransformFeedback.transformsLocs[i] =
2080
- gl.getUniformLocation(this.distancesTransformFeedback.program, `transforms[${i}]`);
2081
- }
2082
- } else {
2083
- this.distancesTransformFeedback.modelViewProjLoc =
2084
- gl.getUniformLocation(this.distancesTransformFeedback.program, 'modelViewProj');
2085
- }
2086
-
2087
- if (rebuildGPUObjects || rebuildBuffers) {
2088
- this.distancesTransformFeedback.centersBuffer = gl.createBuffer();
2089
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
2090
- gl.enableVertexAttribArray(this.distancesTransformFeedback.centersLoc);
2091
- if (this.integerBasedDistancesComputation) {
2092
- gl.vertexAttribIPointer(this.distancesTransformFeedback.centersLoc, 4, gl.INT, 0, 0);
2093
- } else {
2094
- gl.vertexAttribPointer(this.distancesTransformFeedback.centersLoc, 4, gl.FLOAT, false, 0, 0);
2095
- }
2096
-
2097
- if (this.dynamicMode) {
2098
- this.distancesTransformFeedback.sceneIndexesBuffer = gl.createBuffer();
2099
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.sceneIndexesBuffer);
2100
- gl.enableVertexAttribArray(this.distancesTransformFeedback.sceneIndexesLoc);
2101
- gl.vertexAttribIPointer(this.distancesTransformFeedback.sceneIndexesLoc, 1, gl.UNSIGNED_INT, 0, 0);
2102
- }
2103
- }
2104
-
2105
- if (rebuildGPUObjects || rebuildBuffers) {
2106
- this.distancesTransformFeedback.outDistancesBuffer = gl.createBuffer();
2107
- }
2108
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.outDistancesBuffer);
2109
- gl.bufferData(gl.ARRAY_BUFFER, maxSplatCount * 4, gl.STATIC_READ);
2110
-
2111
- if (rebuildGPUObjects) {
2112
- this.distancesTransformFeedback.id = gl.createTransformFeedback();
2113
- }
2114
- gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, this.distancesTransformFeedback.id);
2115
- gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.distancesTransformFeedback.outDistancesBuffer);
2116
-
2117
- if (currentProgram && currentProgramDeleted !== true) gl.useProgram(currentProgram);
2118
- if (currentVao) gl.bindVertexArray(currentVao);
2119
-
2120
- this.lastRenderer = this.renderer;
2121
- currentMaxSplatCount = maxSplatCount;
2122
- };
2123
-
2124
- }();
2125
-
2126
- /**
2127
- * Refresh GPU buffers used for computing splat distances with centers data from the scenes for this mesh.
2128
- * @param {boolean} isUpdate Specify whether or not to update the GPU buffer or to initialize & fill
2129
- * @param {Array<number>} centers The splat centers data
2130
- * @param {number} offsetSplats Offset in the GPU buffer at which to start updating data, specified in splats
2131
- */
2132
- updateGPUCentersBufferForDistancesComputation(isUpdate, centers, offsetSplats) {
2133
-
2134
- if (!this.renderer) return;
2135
-
2136
- const gl = this.renderer.getContext();
2137
-
2138
- const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
2139
- gl.bindVertexArray(this.distancesTransformFeedback.vao);
2140
-
2141
- const ArrayType = this.integerBasedDistancesComputation ? Uint32Array : Float32Array;
2142
- const attributeBytesPerCenter = 16;
2143
- const subBufferOffset = offsetSplats * attributeBytesPerCenter;
2144
-
2145
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
2146
-
2147
- if (isUpdate) {
2148
- gl.bufferSubData(gl.ARRAY_BUFFER, subBufferOffset, centers);
2149
- } else {
2150
- const maxArray = new ArrayType(this.getMaxSplatCount() * attributeBytesPerCenter);
2151
- maxArray.set(centers);
2152
- gl.bufferData(gl.ARRAY_BUFFER, maxArray, gl.STATIC_DRAW);
2153
- }
2154
-
2155
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
2156
-
2157
- if (currentVao) gl.bindVertexArray(currentVao);
2158
- }
2159
-
2160
- /**
2161
- * Refresh GPU buffers used for pre-computing splat distances with centers data from the scenes for this mesh.
2162
- * @param {boolean} isUpdate Specify whether or not to update the GPU buffer or to initialize & fill
2163
- * @param {Array<number>} sceneIndexes The splat scene indexes
2164
- * @param {number} offsetSplats Offset in the GPU buffer at which to start updating data, specified in splats
2165
- */
2166
- updateGPUTransformIndexesBufferForDistancesComputation(isUpdate, sceneIndexes, offsetSplats) {
2167
-
2168
- if (!this.renderer || !this.dynamicMode) return;
2169
-
2170
- const gl = this.renderer.getContext();
2171
-
2172
- const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
2173
- gl.bindVertexArray(this.distancesTransformFeedback.vao);
2174
-
2175
- const subBufferOffset = offsetSplats * 4;
2176
-
2177
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.sceneIndexesBuffer);
2178
-
2179
- if (isUpdate) {
2180
- gl.bufferSubData(gl.ARRAY_BUFFER, subBufferOffset, sceneIndexes);
2181
- } else {
2182
- const maxArray = new Uint32Array(this.getMaxSplatCount() * 4);
2183
- maxArray.set(sceneIndexes);
2184
- gl.bufferData(gl.ARRAY_BUFFER, maxArray, gl.STATIC_DRAW);
2185
- }
2186
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
2187
-
2188
- if (currentVao) gl.bindVertexArray(currentVao);
2189
- }
2190
-
2191
- /**
2192
- * Get a typed array containing a mapping from global splat indexes to their scene index.
2193
- * @param {number} start Starting splat index to store
2194
- * @param {number} end Ending splat index to store
2195
- * @return {Uint32Array}
2196
- */
2197
- getSceneIndexes(start, end) {
2198
-
2199
- let sceneIndexes;
2200
- const fillCount = end - start + 1;
2201
- sceneIndexes = new Uint32Array(fillCount);
2202
- for (let i = start; i <= end; i++) {
2203
- sceneIndexes[i] = this.globalSplatIndexToSceneIndexMap[i];
2204
- }
2205
-
2206
- return sceneIndexes;
2207
- }
2208
-
2209
- /**
2210
- * Fill 'array' with the transforms for each scene in this splat mesh.
2211
- * @param {Array} array Empty array to be filled with scene transforms. If not empty, contents will be overwritten.
2212
- */
2213
- fillTransformsArray = function() {
2214
-
2215
- const tempArray = [];
2216
-
2217
- return function(array) {
2218
- if (tempArray.length !== array.length) tempArray.length = array.length;
2219
- for (let i = 0; i < this.scenes.length; i++) {
2220
- const sceneTransform = this.getScene(i).transform;
2221
- const sceneTransformElements = sceneTransform.elements;
2222
- for (let j = 0; j < 16; j++) {
2223
- tempArray[i * 16 + j] = sceneTransformElements[j];
2224
- }
2225
- }
2226
- array.set(tempArray);
2227
- };
2228
-
2229
- }();
2230
-
2231
- computeDistancesOnGPU = function() {
2232
-
2233
- const tempMatrix = new Matrix4();
2234
-
2235
- return (modelViewProjMatrix, outComputedDistances) => {
2236
- if (!this.renderer) return;
2237
-
2238
- const gl = this.renderer.getContext();
2239
-
2240
- const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
2241
- const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
2242
- const currentProgramDeleted = currentProgram ? gl.getProgramParameter(currentProgram, gl.DELETE_STATUS) : false;
2243
-
2244
- gl.bindVertexArray(this.distancesTransformFeedback.vao);
2245
- gl.useProgram(this.distancesTransformFeedback.program);
2246
-
2247
- gl.enable(gl.RASTERIZER_DISCARD);
2248
-
2249
- if (this.dynamicMode) {
2250
- for (let i = 0; i < this.scenes.length; i++) {
2251
- tempMatrix.copy(this.getScene(i).transform);
2252
- tempMatrix.premultiply(modelViewProjMatrix);
2253
-
2254
- if (this.integerBasedDistancesComputation) {
2255
- const iTempMatrix = SplatMesh.getIntegerMatrixArray(tempMatrix);
2256
- const iTransform = [iTempMatrix[2], iTempMatrix[6], iTempMatrix[10], iTempMatrix[14]];
2257
- gl.uniform4i(this.distancesTransformFeedback.transformsLocs[i], iTransform[0], iTransform[1],
2258
- iTransform[2], iTransform[3]);
2259
- } else {
2260
- gl.uniformMatrix4fv(this.distancesTransformFeedback.transformsLocs[i], false, tempMatrix.elements);
2261
- }
2262
- }
2263
- } else {
2264
- if (this.integerBasedDistancesComputation) {
2265
- const iViewProjMatrix = SplatMesh.getIntegerMatrixArray(modelViewProjMatrix);
2266
- const iViewProj = [iViewProjMatrix[2], iViewProjMatrix[6], iViewProjMatrix[10]];
2267
- gl.uniform3i(this.distancesTransformFeedback.modelViewProjLoc, iViewProj[0], iViewProj[1], iViewProj[2]);
2268
- } else {
2269
- const viewProj = [modelViewProjMatrix.elements[2], modelViewProjMatrix.elements[6], modelViewProjMatrix.elements[10]];
2270
- gl.uniform3f(this.distancesTransformFeedback.modelViewProjLoc, viewProj[0], viewProj[1], viewProj[2]);
2271
- }
2272
- }
2273
-
2274
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
2275
- gl.enableVertexAttribArray(this.distancesTransformFeedback.centersLoc);
2276
- if (this.integerBasedDistancesComputation) {
2277
- gl.vertexAttribIPointer(this.distancesTransformFeedback.centersLoc, 4, gl.INT, 0, 0);
2278
- } else {
2279
- gl.vertexAttribPointer(this.distancesTransformFeedback.centersLoc, 4, gl.FLOAT, false, 0, 0);
2280
- }
2281
-
2282
- if (this.dynamicMode) {
2283
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.sceneIndexesBuffer);
2284
- gl.enableVertexAttribArray(this.distancesTransformFeedback.sceneIndexesLoc);
2285
- gl.vertexAttribIPointer(this.distancesTransformFeedback.sceneIndexesLoc, 1, gl.UNSIGNED_INT, 0, 0);
2286
- }
2287
-
2288
- gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, this.distancesTransformFeedback.id);
2289
- gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.distancesTransformFeedback.outDistancesBuffer);
2290
-
2291
- gl.beginTransformFeedback(gl.POINTS);
2292
- gl.drawArrays(gl.POINTS, 0, this.getSplatCount());
2293
- gl.endTransformFeedback();
2294
-
2295
- gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
2296
- gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
2297
-
2298
- gl.disable(gl.RASTERIZER_DISCARD);
2299
-
2300
- const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
2301
- gl.flush();
2302
-
2303
- const promise = new Promise((resolve) => {
2304
- const checkSync = () => {
2305
- if (this.disposed) {
2306
- resolve();
2307
- } else {
2308
- const timeout = 0;
2309
- const bitflags = 0;
2310
- const status = gl.clientWaitSync(sync, bitflags, timeout);
2311
- switch (status) {
2312
- case gl.TIMEOUT_EXPIRED:
2313
- this.computeDistancesOnGPUSyncTimeout = setTimeout(checkSync);
2314
- return this.computeDistancesOnGPUSyncTimeout;
2315
- case gl.WAIT_FAILED:
2316
- throw new Error('should never get here');
2317
- default: {
2318
- this.computeDistancesOnGPUSyncTimeout = null;
2319
- gl.deleteSync(sync);
2320
- const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
2321
- gl.bindVertexArray(this.distancesTransformFeedback.vao);
2322
- gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.outDistancesBuffer);
2323
- gl.getBufferSubData(gl.ARRAY_BUFFER, 0, outComputedDistances);
2324
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
2325
-
2326
- if (currentVao) gl.bindVertexArray(currentVao);
2327
-
2328
- resolve();
2329
- }
2330
- }
2331
- }
2332
- };
2333
- this.computeDistancesOnGPUSyncTimeout = setTimeout(checkSync);
2334
- });
2335
-
2336
- if (currentProgram && currentProgramDeleted !== true) gl.useProgram(currentProgram);
2337
- if (currentVao) gl.bindVertexArray(currentVao);
2338
-
2339
- return promise;
2340
- };
2341
-
2342
- }();
2343
-
2344
- /**
2345
- * Given a global splat index, return corresponding local data (splat buffer, index of splat in that splat
2346
- * buffer, and the corresponding transform)
2347
- * @param {number} globalIndex Global splat index
2348
- * @param {object} paramsObj Object in which to store local data
2349
- * @param {boolean} returnSceneTransform By default, the transform of the scene to which the splat at 'globalIndex' belongs will be
2350
- * returned via the 'sceneTransform' property of 'paramsObj' only if the splat mesh is static.
2351
- * If 'returnSceneTransform' is true, the 'sceneTransform' property will always contain the scene
2352
- * transform, and if 'returnSceneTransform' is false, the 'sceneTransform' property will always
2353
- * be null.
2354
- */
2355
- getLocalSplatParameters(globalIndex, paramsObj, returnSceneTransform) {
2356
- if (returnSceneTransform === undefined || returnSceneTransform === null) {
2357
- returnSceneTransform = this.dynamicMode ? false : true;
2358
- }
2359
- paramsObj.splatBuffer = this.getSplatBufferForSplat(globalIndex);
2360
- paramsObj.localIndex = this.getSplatLocalIndex(globalIndex);
2361
- paramsObj.sceneTransform = returnSceneTransform ? this.getSceneTransformForSplat(globalIndex) : null;
2362
- }
2363
-
2364
- /**
2365
- * Fill arrays with splat data and apply transforms if appropriate. Each array is optional.
2366
- * @param {Float32Array} covariances Target storage for splat covariances
2367
- * @param {Float32Array} scales Target storage for splat scales
2368
- * @param {Float32Array} rotations Target storage for splat rotations
2369
- * @param {Float32Array} centers Target storage for splat centers
2370
- * @param {Uint8Array} colors Target storage for splat colors
2371
- * @param {Float32Array} sphericalHarmonics Target storage for spherical harmonics
2372
- * @param {boolean} applySceneTransform By default, scene transforms are applied to relevant splat data only if the splat mesh is
2373
- * static. If 'applySceneTransform' is true, scene transforms will always be applied and if
2374
- * it is false, they will never be applied. If undefined, the default behavior will apply.
2375
- * @param {number} covarianceCompressionLevel The compression level for covariances in the destination array
2376
- * @param {number} sphericalHarmonicsCompressionLevel The compression level for spherical harmonics in the destination array
2377
- * @param {number} srcStart The start location from which to pull source data
2378
- * @param {number} srcEnd The end location from which to pull source data
2379
- * @param {number} destStart The start location from which to write data
2380
- */
2381
- fillSplatDataArrays(covariances, scales, rotations, centers, colors, sphericalHarmonics, flameModelPos,
2382
- applySceneTransform,
2383
- covarianceCompressionLevel = 0, scaleRotationCompressionLevel = 0, sphericalHarmonicsCompressionLevel = 1,
2384
- srcStart, srcEnd, destStart = 0, sceneIndex) {
2385
- const scaleOverride = new Vector3();
2386
- scaleOverride.x = undefined;
2387
- scaleOverride.y = undefined;
2388
- if (this.splatRenderMode === SplatRenderMode.ThreeD) {
2389
- scaleOverride.z = undefined;
2390
- } else {
2391
- scaleOverride.z = 1;
2392
- }
2393
- const tempTransform = new Matrix4();
2394
-
2395
- let startSceneIndex = 0;
2396
- let endSceneIndex = this.scenes.length - 1;
2397
- if (sceneIndex !== undefined && sceneIndex !== null && sceneIndex >= 0 && sceneIndex <= this.scenes.length) {
2398
- startSceneIndex = sceneIndex;
2399
- endSceneIndex = sceneIndex;
2400
- }
2401
- for (let i = startSceneIndex; i <= endSceneIndex; i++) {
2402
- if (applySceneTransform === undefined || applySceneTransform === null) {
2403
- applySceneTransform = this.dynamicMode ? false : true;
2404
- }
2405
-
2406
- const scene = this.getScene(i);
2407
- const splatBuffer = scene.splatBuffer;
2408
- let sceneTransform;
2409
- if (applySceneTransform) {
2410
- this.getSceneTransform(i, tempTransform);
2411
- sceneTransform = tempTransform;
2412
- }
2413
- if (covariances) {
2414
- splatBuffer.fillSplatCovarianceArray(covariances, sceneTransform, srcStart, srcEnd, destStart, covarianceCompressionLevel);
2415
- }
2416
- if (scales || rotations) {
2417
- if (!scales || !rotations) {
2418
- throw new Error('SplatMesh::fillSplatDataArrays() -> "scales" and "rotations" must both be valid.');
2419
- }
2420
- splatBuffer.fillSplatScaleRotationArray(scales, rotations, sceneTransform,
2421
- srcStart, srcEnd, destStart, scaleRotationCompressionLevel, scaleOverride);
2422
- }
2423
- if (centers) splatBuffer.fillSplatCenterArray(this.morphedMesh, centers, sceneTransform, srcStart, srcEnd, destStart);
2424
- if (colors) splatBuffer.fillSplatColorArray(colors, scene.minimumAlpha, srcStart, srcEnd, destStart);
2425
- if (sphericalHarmonics) {
2426
- splatBuffer.fillSphericalHarmonicsArray(sphericalHarmonics, this.minSphericalHarmonicsDegree,
2427
- sceneTransform, srcStart, srcEnd, destStart, sphericalHarmonicsCompressionLevel);
2428
- }
2429
-
2430
- destStart += splatBuffer.getSplatCount();
2431
- }
2432
- }
2433
-
2434
- morphedMesh;
2435
- /**
2436
- * Convert splat centers, which are floating point values, to an array of integers and multiply
2437
- * each by 1000. Centers will get transformed as appropriate before conversion to integer.
2438
- * @param {number} start The index at which to start retrieving data
2439
- * @param {number} end The index at which to stop retrieving data
2440
- * @param {boolean} padFour Enforce alignment of 4 by inserting a 1 after every 3 values
2441
- * @return {Int32Array}
2442
- */
2443
- getIntegerCenters(start, end, padFour = false) {
2444
- const splatCount = end - start + 1;
2445
- const floatCenters = new Float32Array(splatCount * 3);
2446
- this.fillSplatDataArrays(null, null, null, floatCenters, null, null, undefined, undefined, undefined, undefined, start);
2447
- let intCenters;
2448
- let componentCount = padFour ? 4 : 3;
2449
- intCenters = new Int32Array(splatCount * componentCount);
2450
- for (let i = 0; i < splatCount; i++) {
2451
- for (let t = 0; t < 3; t++) {
2452
- intCenters[i * componentCount + t] = Math.round(floatCenters[i * 3 + t] * 1000.0);
2453
- }
2454
- if (padFour) intCenters[i * componentCount + 3] = 1000;
2455
- }
2456
- return intCenters;
2457
- }
2458
-
2459
- /**
2460
- * Returns an array of splat centers, transformed as appropriate, optionally padded.
2461
- * @param {number} start The index at which to start retrieving data
2462
- * @param {number} end The index at which to stop retrieving data
2463
- * @param {boolean} padFour Enforce alignment of 4 by inserting a 1 after every 3 values
2464
- * @return {Float32Array}
2465
- */
2466
- getFloatCenters(start, end, padFour = false) {
2467
- const splatCount = end - start + 1;
2468
- const floatCenters = new Float32Array(splatCount * 3);
2469
- this.fillSplatDataArrays(null, null, null, floatCenters, null, null, undefined, undefined, undefined, undefined, start);
2470
- if (!padFour) return floatCenters;
2471
- let paddedFloatCenters = new Float32Array(splatCount * 4);
2472
- for (let i = 0; i < splatCount; i++) {
2473
- for (let t = 0; t < 3; t++) {
2474
- paddedFloatCenters[i * 4 + t] = floatCenters[i * 3 + t];
2475
- }
2476
- paddedFloatCenters[i * 4 + 3] = 1.0;
2477
- }
2478
- return paddedFloatCenters;
2479
- }
2480
-
2481
- /**
2482
- * Get the center for a splat, transformed as appropriate.
2483
- * @param {number} globalIndex Global index of splat
2484
- * @param {THREE.Vector3} outCenter THREE.Vector3 instance in which to store splat center
2485
- * @param {boolean} applySceneTransform By default, if the splat mesh is static, the transform of the scene to which the splat at
2486
- * 'globalIndex' belongs will be applied to the splat center. If 'applySceneTransform' is true,
2487
- * the scene transform will always be applied and if 'applySceneTransform' is false, the
2488
- * scene transform will never be applied. If undefined, the default behavior will apply.
2489
- */
2490
- getSplatCenter = function() {
2491
-
2492
- const paramsObj = {};
2493
-
2494
- return function(morphedMesh, globalIndex, outCenter, applySceneTransform) {
2495
- this.getLocalSplatParameters(globalIndex, paramsObj, applySceneTransform);
2496
- paramsObj.splatBuffer.getSplatCenter(morphedMesh, paramsObj.localIndex, outCenter, paramsObj.sceneTransform);
2497
- };
2498
-
2499
- }();
2500
-
2501
- /**
2502
- * Get the scale and rotation for a splat, transformed as appropriate.
2503
- * @param {number} globalIndex Global index of splat
2504
- * @param {THREE.Vector3} outScale THREE.Vector3 instance in which to store splat scale
2505
- * @param {THREE.Quaternion} outRotation THREE.Quaternion instance in which to store splat rotation
2506
- * @param {boolean} applySceneTransform By default, if the splat mesh is static, the transform of the scene to which the splat at
2507
- * 'globalIndex' belongs will be applied to the splat scale and rotation. If
2508
- * 'applySceneTransform' is true, the scene transform will always be applied and if
2509
- * 'applySceneTransform' is false, the scene transform will never be applied. If undefined,
2510
- * the default behavior will apply.
2511
- */
2512
- getSplatScaleAndRotation = function() {
2513
-
2514
- const paramsObj = {};
2515
- const scaleOverride = new Vector3();
2516
-
2517
- return function(globalIndex, outScale, outRotation, applySceneTransform) {
2518
- this.getLocalSplatParameters(globalIndex, paramsObj, applySceneTransform);
2519
- scaleOverride.x = undefined;
2520
- scaleOverride.y = undefined;
2521
- scaleOverride.z = undefined;
2522
- if (this.splatRenderMode === SplatRenderMode.TwoD) scaleOverride.z = 0;
2523
- paramsObj.splatBuffer.getSplatScaleAndRotation(paramsObj.localIndex, outScale, outRotation,
2524
- paramsObj.sceneTransform, scaleOverride);
2525
- };
2526
-
2527
- }();
2528
-
2529
- /**
2530
- * Get the color for a splat.
2531
- * @param {number} globalIndex Global index of splat
2532
- * @param {THREE.Vector4} outColor THREE.Vector4 instance in which to store splat color
2533
- */
2534
- getSplatColor = function() {
2535
-
2536
- const paramsObj = {};
2537
-
2538
- return function(globalIndex, outColor) {
2539
- this.getLocalSplatParameters(globalIndex, paramsObj);
2540
- paramsObj.splatBuffer.getSplatColor(paramsObj.localIndex, outColor);
2541
- };
2542
-
2543
- }();
2544
-
2545
- /**
2546
- * Store the transform of the scene at 'sceneIndex' in 'outTransform'.
2547
- * @param {number} sceneIndex Index of the desired scene
2548
- * @param {THREE.Matrix4} outTransform Instance of THREE.Matrix4 in which to store the scene's transform
2549
- */
2550
- getSceneTransform(sceneIndex, outTransform) {
2551
- const scene = this.getScene(sceneIndex);
2552
- scene.updateTransform(this.dynamicMode);
2553
- outTransform.copy(scene.transform);
2554
- }
2555
-
2556
- /**
2557
- * Get the scene at 'sceneIndex'.
2558
- * @param {number} sceneIndex Index of the desired scene
2559
- * @return {SplatScene}
2560
- */
2561
- getScene(sceneIndex) {
2562
- if (sceneIndex < 0 || sceneIndex >= this.scenes.length) {
2563
- throw new Error('SplatMesh::getScene() -> Invalid scene index.');
2564
- }
2565
- return this.scenes[sceneIndex];
2566
- }
2567
-
2568
- getSceneCount() {
2569
- return this.scenes.length;
2570
- }
2571
-
2572
- getSplatBufferForSplat(globalIndex) {
2573
- return this.getScene(this.globalSplatIndexToSceneIndexMap[globalIndex]).splatBuffer;
2574
- }
2575
-
2576
- getSceneIndexForSplat(globalIndex) {
2577
- return this.globalSplatIndexToSceneIndexMap[globalIndex];
2578
- }
2579
-
2580
- getSceneTransformForSplat(globalIndex) {
2581
- return this.getScene(this.globalSplatIndexToSceneIndexMap[globalIndex]).transform;
2582
- }
2583
-
2584
- getSplatLocalIndex(globalIndex) {
2585
- return this.globalSplatIndexToLocalSplatIndexMap[globalIndex];
2586
- }
2587
-
2588
- static getIntegerMatrixArray(matrix) {
2589
- const matrixElements = matrix.elements;
2590
- const intMatrixArray = [];
2591
- for (let i = 0; i < 16; i++) {
2592
- intMatrixArray[i] = Math.round(matrixElements[i] * 1000.0);
2593
- }
2594
- return intMatrixArray;
2595
- }
2596
-
2597
- computeBoundingBox(applySceneTransforms = false, sceneIndex) {
2598
- let splatCount = this.getSplatCount();
2599
- if (sceneIndex !== undefined && sceneIndex !== null) {
2600
- if (sceneIndex < 0 || sceneIndex >= this.scenes.length) {
2601
- throw new Error('SplatMesh::computeBoundingBox() -> Invalid scene index.');
2602
- }
2603
- splatCount = this.scenes[sceneIndex].splatBuffer.getSplatCount();
2604
- }
2605
-
2606
- const floatCenters = new Float32Array(splatCount * 3);
2607
- this.fillSplatDataArrays(null, null, null, floatCenters, null, null, applySceneTransforms,
2608
- undefined, undefined, undefined, undefined, sceneIndex);
2609
-
2610
- const min = new Vector3();
2611
- const max = new Vector3();
2612
- for (let i = 0; i < splatCount; i++) {
2613
- const offset = i * 3;
2614
- const x = floatCenters[offset];
2615
- const y = floatCenters[offset + 1];
2616
- const z = floatCenters[offset + 2];
2617
- if (i === 0 || x < min.x) min.x = x;
2618
- if (i === 0 || y < min.y) min.y = y;
2619
- if (i === 0 || z < min.z) min.z = z;
2620
- if (i === 0 || x > max.x) max.x = x;
2621
- if (i === 0 || y > max.y) max.y = y;
2622
- if (i === 0 || z > max.z) max.z = z;
2623
- }
2624
-
2625
- return new Box3(min, max);
2626
- }
2627
- }