@myned-ai/gsplat-flame-avatar-renderer 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +6 -36
  2. package/dist/gsplat-flame-avatar-renderer.cjs.js +12875 -0
  3. package/dist/{gsplat-flame-avatar-renderer.umd.js.map → gsplat-flame-avatar-renderer.cjs.js.map} +1 -1
  4. package/dist/gsplat-flame-avatar-renderer.esm.js +1 -1
  5. package/package.json +6 -11
  6. package/src/api/index.js +7 -0
  7. package/src/buffers/SplatBuffer.js +1394 -0
  8. package/src/buffers/SplatBufferGenerator.js +41 -0
  9. package/src/buffers/SplatPartitioner.js +110 -0
  10. package/src/buffers/UncompressedSplatArray.js +106 -0
  11. package/src/buffers/index.js +11 -0
  12. package/src/core/SplatGeometry.js +48 -0
  13. package/src/core/SplatMesh.js +2620 -0
  14. package/src/core/SplatScene.js +43 -0
  15. package/src/core/SplatTree.js +200 -0
  16. package/src/core/Viewer.js +2895 -0
  17. package/src/core/index.js +13 -0
  18. package/src/enums/EngineConstants.js +58 -0
  19. package/src/enums/LogLevel.js +13 -0
  20. package/src/enums/RenderMode.js +11 -0
  21. package/src/enums/SceneFormat.js +21 -0
  22. package/src/enums/SceneRevealMode.js +11 -0
  23. package/src/enums/SplatRenderMode.js +10 -0
  24. package/src/enums/index.js +13 -0
  25. package/src/flame/FlameAnimator.js +271 -0
  26. package/src/flame/FlameConstants.js +21 -0
  27. package/src/flame/FlameTextureManager.js +293 -0
  28. package/src/flame/index.js +22 -0
  29. package/src/flame/utils.js +50 -0
  30. package/src/index.js +39 -0
  31. package/src/loaders/DirectLoadError.js +14 -0
  32. package/src/loaders/INRIAV1PlyParser.js +223 -0
  33. package/src/loaders/PlyLoader.js +261 -0
  34. package/src/loaders/PlyParser.js +19 -0
  35. package/src/loaders/PlyParserUtils.js +311 -0
  36. package/src/loaders/index.js +13 -0
  37. package/src/materials/SplatMaterial.js +1065 -0
  38. package/src/materials/SplatMaterial2D.js +358 -0
  39. package/src/materials/SplatMaterial3D.js +278 -0
  40. package/src/materials/index.js +11 -0
  41. package/src/raycaster/Hit.js +37 -0
  42. package/src/raycaster/Ray.js +123 -0
  43. package/src/raycaster/Raycaster.js +175 -0
  44. package/src/raycaster/index.js +10 -0
  45. package/src/renderer/AnimationManager.js +574 -0
  46. package/src/renderer/AppConstants.js +101 -0
  47. package/src/renderer/GaussianSplatRenderer.js +695 -0
  48. package/src/renderer/index.js +24 -0
  49. package/src/utils/LoaderUtils.js +65 -0
  50. package/src/utils/Util.js +375 -0
  51. package/src/utils/index.js +9 -0
  52. package/src/worker/SortWorker.js +284 -0
  53. package/src/worker/index.js +8 -0
  54. package/dist/gsplat-flame-avatar-renderer.esm.min.js +0 -2
  55. package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +0 -1
  56. package/dist/gsplat-flame-avatar-renderer.umd.js +0 -12876
  57. package/dist/gsplat-flame-avatar-renderer.umd.min.js +0 -2
  58. package/dist/gsplat-flame-avatar-renderer.umd.min.js.map +0 -1
  59. package/dist/gsplat-flame-avatar.esm.js +0 -12755
  60. package/dist/gsplat-flame-avatar.esm.js.map +0 -1
  61. package/dist/gsplat-flame-avatar.esm.min.js +0 -2
  62. package/dist/gsplat-flame-avatar.esm.min.js.map +0 -1
  63. package/dist/gsplat-flame-avatar.umd.js +0 -12876
  64. package/dist/gsplat-flame-avatar.umd.js.map +0 -1
  65. package/dist/gsplat-flame-avatar.umd.min.js +0 -2
  66. package/dist/gsplat-flame-avatar.umd.min.js.map +0 -1
@@ -0,0 +1,1394 @@
1
+ /**
2
+ * SplatBuffer
3
+ *
4
+ * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
+ * https://github.com/mkkellogg/GaussianSplats3D
6
+ *
7
+ * This file is functionally identical to the original.
8
+ * Import paths adjusted for gsplat-flame-avatar package structure.
9
+ */
10
+
11
+ import { DataUtils, Matrix3, Matrix4, Quaternion, Vector3 } from 'three';
12
+ import { UncompressedSplatArray } from './UncompressedSplatArray.js';
13
+ import { clamp, getSphericalHarmonicsComponentCountForDegree, DefaultSphericalHarmonics8BitCompressionHalfRange, convertBetweenCompressionLevels } from '../utils/Util.js';
14
+ import { Constants } from '../enums/EngineConstants.js';
15
+
16
+ /**
17
+ * Helper function to copy bytes between buffers
18
+ */
19
+ const copyBetweenBuffers = (srcBuffer, srcOffset, destBuffer, destOffset, byteCount = 0) => {
20
+ const src = new Uint8Array(srcBuffer, srcOffset);
21
+ const dest = new Uint8Array(destBuffer, destOffset);
22
+ for (let i = 0; i < byteCount; i++) {
23
+ dest[i] = src[i];
24
+ }
25
+ };
26
+
27
+ // Compression/decompression helper functions
28
+ const DefaultSphericalHarmonics8BitCompressionRange = Constants.SphericalHarmonics8BitCompressionRange;
29
+
30
+ const toHalfFloat = DataUtils.toHalfFloat.bind(DataUtils);
31
+ const fromHalfFloat = DataUtils.fromHalfFloat.bind(DataUtils);
32
+
33
+ const toUncompressedFloat = (f, compressionLevel, isSH = false, range8BitMin, range8BitMax) => {
34
+ if (compressionLevel === 0) {
35
+ return f;
36
+ } else if (compressionLevel === 1 || compressionLevel === 2 && !isSH) {
37
+ return DataUtils.fromHalfFloat(f);
38
+ } else if (compressionLevel === 2) {
39
+ return fromUint8(f, range8BitMin, range8BitMax);
40
+ }
41
+ };
42
+
43
+ const toUint8 = (v, rangeMin, rangeMax) => {
44
+ v = clamp(v, rangeMin, rangeMax);
45
+ const range = (rangeMax - rangeMin);
46
+ return clamp(Math.floor((v - rangeMin) / range * 255), 0, 255);
47
+ };
48
+
49
+ const fromUint8 = (v, rangeMin, rangeMax) => {
50
+ const range = (rangeMax - rangeMin);
51
+ return (v / 255 * range + rangeMin);
52
+ };
53
+
54
+ const fromHalfFloatToUint8 = (v, rangeMin, rangeMax) => {
55
+ return toUint8(fromHalfFloat(v), rangeMin, rangeMax);
56
+ };
57
+
58
+ const fromUint8ToHalfFloat = (v, rangeMin, rangeMax) => {
59
+ return toHalfFloat(fromUint8(v, rangeMin, rangeMax));
60
+ };
61
+
62
+ const dataViewFloatForCompressionLevel = (dataView, floatIndex, compressionLevel, isSH = false) => {
63
+ if (compressionLevel === 0) {
64
+ return dataView.getFloat32(floatIndex * 4, true);
65
+ } else if (compressionLevel === 1 || compressionLevel === 2 && !isSH) {
66
+ return dataView.getUint16(floatIndex * 2, true);
67
+ } else {
68
+ return dataView.getUint8(floatIndex, true);
69
+ }
70
+ };
71
+
72
+ export class SplatBuffer {
73
+
74
+ static CurrentMajorVersion = 0;
75
+ static CurrentMinorVersion = 1;
76
+
77
+ static CenterComponentCount = 3;
78
+ static ScaleComponentCount = 3;
79
+ static RotationComponentCount = 4;
80
+ static ColorComponentCount = 4;
81
+ static CovarianceComponentCount = 6;
82
+
83
+ static SplatScaleOffsetFloat = 3;
84
+ static SplatRotationOffsetFloat = 6;
85
+
86
+ static CompressionLevels = {
87
+ 0: {
88
+ BytesPerCenter: 12,
89
+ BytesPerScale: 12,
90
+ BytesPerRotation: 16,
91
+ BytesPerColor: 4,
92
+ ScaleOffsetBytes: 12,
93
+ RotationffsetBytes: 24,
94
+ ColorOffsetBytes: 40,
95
+ SphericalHarmonicsOffsetBytes: 44,
96
+ ScaleRange: 1,
97
+ BytesPerSphericalHarmonicsComponent: 4,
98
+ SphericalHarmonicsOffsetFloat: 11,
99
+ SphericalHarmonicsDegrees: {
100
+ 0: { BytesPerSplat: 44 },
101
+ 1: { BytesPerSplat: 80 },
102
+ 2: { BytesPerSplat: 140 }
103
+ },
104
+ },
105
+ 1: {
106
+ BytesPerCenter: 6,
107
+ BytesPerScale: 6,
108
+ BytesPerRotation: 8,
109
+ BytesPerColor: 4,
110
+ ScaleOffsetBytes: 6,
111
+ RotationffsetBytes: 12,
112
+ ColorOffsetBytes: 20,
113
+ SphericalHarmonicsOffsetBytes: 24,
114
+ ScaleRange: 32767,
115
+ BytesPerSphericalHarmonicsComponent: 2,
116
+ SphericalHarmonicsOffsetFloat: 12,
117
+ SphericalHarmonicsDegrees: {
118
+ 0: { BytesPerSplat: 24 },
119
+ 1: { BytesPerSplat: 42 },
120
+ 2: { BytesPerSplat: 72 }
121
+ },
122
+ },
123
+ 2: {
124
+ BytesPerCenter: 6,
125
+ BytesPerScale: 6,
126
+ BytesPerRotation: 8,
127
+ BytesPerColor: 4,
128
+ ScaleOffsetBytes: 6,
129
+ RotationffsetBytes: 12,
130
+ ColorOffsetBytes: 20,
131
+ SphericalHarmonicsOffsetBytes: 24,
132
+ ScaleRange: 32767,
133
+ BytesPerSphericalHarmonicsComponent: 1,
134
+ SphericalHarmonicsOffsetFloat: 12,
135
+ SphericalHarmonicsDegrees: {
136
+ 0: { BytesPerSplat: 24 },
137
+ 1: { BytesPerSplat: 33 },
138
+ 2: { BytesPerSplat: 48 }
139
+ },
140
+ }
141
+ };
142
+
143
+ static CovarianceSizeFloats = 6;
144
+
145
+ static HeaderSizeBytes = 4096;
146
+ static SectionHeaderSizeBytes = 1024;
147
+
148
+ static BucketStorageSizeBytes = 12;
149
+ static BucketStorageSizeFloats = 3;
150
+
151
+ static BucketBlockSize = 5.0;
152
+ static BucketSize = 256;
153
+
154
+ constructor(bufferData, secLoadedCountsToMax = true) {
155
+ this.constructFromBuffer(bufferData, secLoadedCountsToMax);
156
+ }
157
+
158
+ getSplatCount() {
159
+ return this.splatCount;
160
+ }
161
+
162
+ getMaxSplatCount() {
163
+ return this.maxSplatCount;
164
+ }
165
+
166
+ getMinSphericalHarmonicsDegree() {
167
+ let minSphericalHarmonicsDegree = 0;
168
+ for (let i = 0; i < this.sections.length; i++) {
169
+ const section = this.sections[i];
170
+ if (i === 0 || section.sphericalHarmonicsDegree < minSphericalHarmonicsDegree) {
171
+ minSphericalHarmonicsDegree = section.sphericalHarmonicsDegree;
172
+ }
173
+ }
174
+ return minSphericalHarmonicsDegree;
175
+ }
176
+
177
+ getBucketIndex(section, localSplatIndex) {
178
+ let bucketIndex;
179
+ const maxSplatIndexInFullBuckets = section.fullBucketCount * section.bucketSize;
180
+ if (localSplatIndex < maxSplatIndexInFullBuckets) {
181
+ bucketIndex = Math.floor(localSplatIndex / section.bucketSize);
182
+ } else {
183
+ let bucketSplatIndex = maxSplatIndexInFullBuckets;
184
+ bucketIndex = section.fullBucketCount;
185
+ let partiallyFullBucketIndex = 0;
186
+ while (bucketSplatIndex < section.splatCount) {
187
+ let currentPartiallyFilledBucketSize = section.partiallyFilledBucketLengths[partiallyFullBucketIndex];
188
+ if (localSplatIndex >= bucketSplatIndex && localSplatIndex < bucketSplatIndex + currentPartiallyFilledBucketSize) {
189
+ break;
190
+ }
191
+ bucketSplatIndex += currentPartiallyFilledBucketSize;
192
+ bucketIndex++;
193
+ partiallyFullBucketIndex++;
194
+ }
195
+ }
196
+ return bucketIndex;
197
+ }
198
+
199
+ getSplatCenter(morphedMesh, globalSplatIndex, outCenter, transform) {
200
+ const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
201
+ const section = this.sections[sectionIndex];
202
+ const localSplatIndex = globalSplatIndex - section.splatCountOffset;
203
+
204
+ const srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;
205
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);
206
+
207
+ const x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);
208
+ const y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);
209
+ const z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);
210
+ if (this.compressionLevel >= 1) {
211
+ const bucketIndex = this.getBucketIndex(section, localSplatIndex);
212
+ const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
213
+ const sf = section.compressionScaleFactor;
214
+ const sr = section.compressionScaleRange;
215
+ outCenter.x = (x - sr) * sf + section.bucketArray[bucketBase];
216
+ outCenter.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];
217
+ outCenter.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];
218
+ } else {
219
+ outCenter.x = x;
220
+ outCenter.y = y;
221
+ outCenter.z = z;
222
+ }
223
+
224
+ outCenter.x += morphedMesh[globalSplatIndex * 3 + 0];
225
+ outCenter.y += morphedMesh[globalSplatIndex * 3 + 1];
226
+ outCenter.z += morphedMesh[globalSplatIndex * 3 + 2];
227
+
228
+
229
+ if (transform) outCenter.applyMatrix4(transform);
230
+ }
231
+
232
+ getSplatScaleAndRotation = function() {
233
+
234
+ const scaleMatrix = new Matrix4();
235
+ const rotationMatrix = new Matrix4();
236
+ const tempMatrix = new Matrix4();
237
+ const tempPosition = new Vector3();
238
+ const scale = new Vector3();
239
+ const rotation = new Quaternion();
240
+
241
+ return function(index, outScale, outRotation, transform, scaleOverride) {
242
+ const sectionIndex = this.globalSplatIndexToSectionMap[index];
243
+ const section = this.sections[sectionIndex];
244
+ const localSplatIndex = index - section.splatCountOffset;
245
+
246
+ const srcSplatScalesBase = section.bytesPerSplat * localSplatIndex +
247
+ SplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;
248
+
249
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);
250
+
251
+ scale.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel), this.compressionLevel),
252
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel), this.compressionLevel),
253
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel), this.compressionLevel));
254
+ if (scaleOverride) {
255
+ if (scaleOverride.x !== undefined) scale.x = scaleOverride.x;
256
+ if (scaleOverride.y !== undefined) scale.y = scaleOverride.y;
257
+ if (scaleOverride.z !== undefined) scale.z = scaleOverride.z;
258
+ }
259
+
260
+ rotation.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel), this.compressionLevel),
261
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel), this.compressionLevel),
262
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel), this.compressionLevel),
263
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel), this.compressionLevel));
264
+
265
+ if (transform) {
266
+ scaleMatrix.makeScale(scale.x, scale.y, scale.z);
267
+ rotationMatrix.makeRotationFromQuaternion(rotation);
268
+ tempMatrix.copy(scaleMatrix).multiply(rotationMatrix).multiply(transform);
269
+ tempMatrix.decompose(tempPosition, outRotation, outScale);
270
+ } else {
271
+ outScale.copy(scale);
272
+ outRotation.copy(rotation);
273
+ }
274
+ };
275
+
276
+ }();
277
+
278
+ getSplatColor(globalSplatIndex, outColor) {
279
+ const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
280
+ const section = this.sections[sectionIndex];
281
+ const localSplatIndex = globalSplatIndex - section.splatCountOffset;
282
+
283
+ const srcSplatColorsBase = section.bytesPerSplat * localSplatIndex +
284
+ SplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;
285
+ const splatColorsArray = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase, 4);
286
+
287
+ outColor.set(splatColorsArray[0], splatColorsArray[1],
288
+ splatColorsArray[2], splatColorsArray[3]);
289
+ }
290
+
291
+ fillSplatCenterArray(morphedMesh, outCenterArray, transform, srcFrom, srcTo, destFrom) {
292
+ const splatCount = this.splatCount;
293
+
294
+ srcFrom = srcFrom || 0;
295
+ srcTo = srcTo || splatCount - 1;
296
+ if (destFrom === undefined) destFrom = srcFrom;
297
+
298
+ const center = new Vector3();
299
+ for (let i = srcFrom; i <= srcTo; i++) {
300
+ const sectionIndex = this.globalSplatIndexToSectionMap[i];
301
+ const section = this.sections[sectionIndex];
302
+ const localSplatIndex = i - section.splatCountOffset;
303
+ const centerDestBase = (i - srcFrom + destFrom) * SplatBuffer.CenterComponentCount;
304
+
305
+ const srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;
306
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);
307
+
308
+ const x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);
309
+ const y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);
310
+ const z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);
311
+ if (this.compressionLevel >= 1) {
312
+ const bucketIndex = this.getBucketIndex(section, localSplatIndex);
313
+ const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
314
+ const sf = section.compressionScaleFactor;
315
+ const sr = section.compressionScaleRange;
316
+ center.x = (x - sr) * sf + section.bucketArray[bucketBase];
317
+ center.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];
318
+ center.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];
319
+ } else {
320
+ center.x = x;
321
+ center.y = y;
322
+ center.z = z;
323
+ }
324
+ if (transform) {
325
+ center.applyMatrix4(transform);
326
+ }
327
+
328
+ outCenterArray[centerDestBase] = center.x + morphedMesh[i * 3 + 0];
329
+ outCenterArray[centerDestBase + 1] = center.y + morphedMesh[i * 3 + 1];
330
+ outCenterArray[centerDestBase + 2] = center.z + morphedMesh[i * 3 + 2];
331
+
332
+ // outCenterArray[centerDestBase] = morphedMesh[centerDestBase];
333
+ // outCenterArray[centerDestBase + 1] = morphedMesh[centerDestBase + 1];
334
+ // outCenterArray[centerDestBase + 2] = morphedMesh[centerDestBase + 2];
335
+
336
+ // outCenterArray[centerDestBase] = center.x;
337
+ // outCenterArray[centerDestBase + 1] = center.y;
338
+ // outCenterArray[centerDestBase + 2] = center.z;
339
+ }
340
+ }
341
+
342
+ fillSplatScaleRotationArray = function() {
343
+
344
+ const scaleMatrix = new Matrix4();
345
+ const rotationMatrix = new Matrix4();
346
+ const tempMatrix = new Matrix4();
347
+ const scale = new Vector3();
348
+ const rotation = new Quaternion();
349
+ const tempPosition = new Vector3();
350
+
351
+ const ensurePositiveW = (quaternion) => {
352
+ const flip = quaternion.w < 0 ? -1 : 1;
353
+ quaternion.x *= flip;
354
+ quaternion.y *= flip;
355
+ quaternion.z *= flip;
356
+ quaternion.w *= flip;
357
+ };
358
+
359
+ return function(outScaleArray, outRotationArray, transform, srcFrom, srcTo, destFrom,
360
+ desiredOutputCompressionLevel, scaleOverride) {
361
+ const splatCount = this.splatCount;
362
+
363
+ srcFrom = srcFrom || 0;
364
+ srcTo = srcTo || splatCount - 1;
365
+ if (destFrom === undefined) destFrom = srcFrom;
366
+
367
+ const outputConversion = (value, srcCompressionLevel) => {
368
+ if (srcCompressionLevel === undefined) srcCompressionLevel = this.compressionLevel;
369
+ return convertBetweenCompressionLevels(value, srcCompressionLevel, desiredOutputCompressionLevel);
370
+ };
371
+
372
+ for (let i = srcFrom; i <= srcTo; i++) {
373
+ const sectionIndex = this.globalSplatIndexToSectionMap[i];
374
+ const section = this.sections[sectionIndex];
375
+ const localSplatIndex = i - section.splatCountOffset;
376
+
377
+ const srcSplatScalesBase = section.bytesPerSplat * localSplatIndex +
378
+ SplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;
379
+
380
+ const scaleDestBase = (i - srcFrom + destFrom) * SplatBuffer.ScaleComponentCount;
381
+ const rotationDestBase = (i - srcFrom + destFrom) * SplatBuffer.RotationComponentCount;
382
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);
383
+
384
+ const srcScaleX = (scaleOverride && scaleOverride.x !== undefined) ? scaleOverride.x :
385
+ dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);
386
+ const srcScaleY = (scaleOverride && scaleOverride.y !== undefined) ? scaleOverride.y :
387
+ dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);
388
+ const srcScaleZ = (scaleOverride && scaleOverride.z !== undefined) ? scaleOverride.z :
389
+ dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);
390
+
391
+ const srcRotationW = dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel);
392
+ const srcRotationX = dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel);
393
+ const srcRotationY = dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel);
394
+ const srcRotationZ = dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel);
395
+
396
+ scale.set(toUncompressedFloat(srcScaleX, this.compressionLevel),
397
+ toUncompressedFloat(srcScaleY, this.compressionLevel),
398
+ toUncompressedFloat(srcScaleZ, this.compressionLevel));
399
+
400
+ rotation.set(toUncompressedFloat(srcRotationX, this.compressionLevel),
401
+ toUncompressedFloat(srcRotationY, this.compressionLevel),
402
+ toUncompressedFloat(srcRotationZ, this.compressionLevel),
403
+ toUncompressedFloat(srcRotationW, this.compressionLevel)).normalize();
404
+
405
+ if (transform) {
406
+ tempPosition.set(0, 0, 0);
407
+ scaleMatrix.makeScale(scale.x, scale.y, scale.z);
408
+ rotationMatrix.makeRotationFromQuaternion(rotation);
409
+ tempMatrix.identity().premultiply(scaleMatrix).premultiply(rotationMatrix);
410
+ tempMatrix.premultiply(transform);
411
+ tempMatrix.decompose(tempPosition, rotation, scale);
412
+ rotation.normalize();
413
+ }
414
+
415
+ ensurePositiveW(rotation);
416
+
417
+ if (outScaleArray) {
418
+ outScaleArray[scaleDestBase] = outputConversion(scale.x, 0);
419
+ outScaleArray[scaleDestBase + 1] = outputConversion(scale.y, 0);
420
+ outScaleArray[scaleDestBase + 2] = outputConversion(scale.z, 0);
421
+ }
422
+
423
+ if (outRotationArray) {
424
+ outRotationArray[rotationDestBase] = outputConversion(rotation.x, 0);
425
+ outRotationArray[rotationDestBase + 1] = outputConversion(rotation.y, 0);
426
+ outRotationArray[rotationDestBase + 2] = outputConversion(rotation.z, 0);
427
+ outRotationArray[rotationDestBase + 3] = outputConversion(rotation.w, 0);
428
+ }
429
+ }
430
+ };
431
+ }();
432
+
433
+ static computeCovariance = function() {
434
+
435
+ const tempMatrix4 = new Matrix4();
436
+ const scaleMatrix = new Matrix3();
437
+ const rotationMatrix = new Matrix3();
438
+ const covarianceMatrix = new Matrix3();
439
+ const transformedCovariance = new Matrix3();
440
+ const transform3x3 = new Matrix3();
441
+ const transform3x3Transpose = new Matrix3();
442
+
443
+ return function(scale, rotation, transform, outCovariance, outOffset = 0, desiredOutputCompressionLevel) {
444
+
445
+ tempMatrix4.makeScale(scale.x, scale.y, scale.z);
446
+ scaleMatrix.setFromMatrix4(tempMatrix4);
447
+
448
+ tempMatrix4.makeRotationFromQuaternion(rotation);
449
+ rotationMatrix.setFromMatrix4(tempMatrix4);
450
+
451
+ covarianceMatrix.copy(rotationMatrix).multiply(scaleMatrix);
452
+ transformedCovariance.copy(covarianceMatrix).transpose().premultiply(covarianceMatrix);
453
+
454
+ if (transform) {
455
+ transform3x3.setFromMatrix4(transform);
456
+ transform3x3Transpose.copy(transform3x3).transpose();
457
+ transformedCovariance.multiply(transform3x3Transpose);
458
+ transformedCovariance.premultiply(transform3x3);
459
+ }
460
+
461
+ if (desiredOutputCompressionLevel >= 1) {
462
+ outCovariance[outOffset] = toHalfFloat(transformedCovariance.elements[0]);
463
+ outCovariance[outOffset + 1] = toHalfFloat(transformedCovariance.elements[3]);
464
+ outCovariance[outOffset + 2] = toHalfFloat(transformedCovariance.elements[6]);
465
+ outCovariance[outOffset + 3] = toHalfFloat(transformedCovariance.elements[4]);
466
+ outCovariance[outOffset + 4] = toHalfFloat(transformedCovariance.elements[7]);
467
+ outCovariance[outOffset + 5] = toHalfFloat(transformedCovariance.elements[8]);
468
+ } else {
469
+ outCovariance[outOffset] = transformedCovariance.elements[0];
470
+ outCovariance[outOffset + 1] = transformedCovariance.elements[3];
471
+ outCovariance[outOffset + 2] = transformedCovariance.elements[6];
472
+ outCovariance[outOffset + 3] = transformedCovariance.elements[4];
473
+ outCovariance[outOffset + 4] = transformedCovariance.elements[7];
474
+ outCovariance[outOffset + 5] = transformedCovariance.elements[8];
475
+ }
476
+
477
+ };
478
+
479
+ }();
480
+
481
+ fillSplatCovarianceArray(covarianceArray, transform, srcFrom, srcTo, destFrom, desiredOutputCompressionLevel) {
482
+ const splatCount = this.splatCount;
483
+
484
+ const scale = new Vector3();
485
+ const rotation = new Quaternion();
486
+
487
+ srcFrom = srcFrom || 0;
488
+ srcTo = srcTo || splatCount - 1;
489
+ if (destFrom === undefined) destFrom = srcFrom;
490
+
491
+ for (let i = srcFrom; i <= srcTo; i++) {
492
+ const sectionIndex = this.globalSplatIndexToSectionMap[i];
493
+ const section = this.sections[sectionIndex];
494
+ const localSplatIndex = i - section.splatCountOffset;
495
+
496
+ const covarianceDestBase = (i - srcFrom + destFrom) * SplatBuffer.CovarianceComponentCount;
497
+ const srcSplatScalesBase = section.bytesPerSplat * localSplatIndex +
498
+ SplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;
499
+
500
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);
501
+
502
+ scale.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel), this.compressionLevel),
503
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel), this.compressionLevel),
504
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel), this.compressionLevel));
505
+
506
+ rotation.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel), this.compressionLevel),
507
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel), this.compressionLevel),
508
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel), this.compressionLevel),
509
+ toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel), this.compressionLevel));
510
+
511
+ SplatBuffer.computeCovariance(scale, rotation, transform, covarianceArray, covarianceDestBase, desiredOutputCompressionLevel);
512
+ }
513
+ }
514
+
515
+ fillSplatColorArray(outColorArray, minimumAlpha, srcFrom, srcTo, destFrom) {
516
+ const splatCount = this.splatCount;
517
+
518
+ srcFrom = srcFrom || 0;
519
+ srcTo = srcTo || splatCount - 1;
520
+ if (destFrom === undefined) destFrom = srcFrom;
521
+
522
+ for (let i = srcFrom; i <= srcTo; i++) {
523
+
524
+ const sectionIndex = this.globalSplatIndexToSectionMap[i];
525
+ const section = this.sections[sectionIndex];
526
+ const localSplatIndex = i - section.splatCountOffset;
527
+
528
+ const colorDestBase = (i - srcFrom + destFrom) * SplatBuffer.ColorComponentCount;
529
+ const srcSplatColorsBase = section.bytesPerSplat * localSplatIndex +
530
+ SplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;
531
+
532
+ const dataView = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase);
533
+
534
+ let alpha = dataView[3];
535
+ alpha = (alpha >= minimumAlpha) ? alpha : 0;
536
+
537
+ outColorArray[colorDestBase] = dataView[0];
538
+ outColorArray[colorDestBase + 1] = dataView[1];
539
+ outColorArray[colorDestBase + 2] = dataView[2];
540
+ outColorArray[colorDestBase + 3] = alpha;
541
+ }
542
+ }
543
+
544
+ fillSphericalHarmonicsArray = function() {
545
+
546
+ const sphericalHarmonicVectors = [];
547
+ for (let i = 0; i < 15; i++) {
548
+ sphericalHarmonicVectors[i] = new Vector3();
549
+ }
550
+
551
+ const tempMatrix3 = new Matrix3();
552
+ const tempMatrix4 = new Matrix4();
553
+
554
+ const tempTranslation = new Vector3();
555
+ const tempScale = new Vector3();
556
+ const tempRotation = new Quaternion();
557
+
558
+ const sh11 = [];
559
+ const sh12 = [];
560
+ const sh13 = [];
561
+
562
+ const sh21 = [];
563
+ const sh22 = [];
564
+ const sh23 = [];
565
+ const sh24 = [];
566
+ const sh25 = [];
567
+
568
+ const shIn1 = [];
569
+ const shIn2 = [];
570
+ const shIn3 = [];
571
+ const shIn4 = [];
572
+ const shIn5 = [];
573
+
574
+ const shOut1 = [];
575
+ const shOut2 = [];
576
+ const shOut3 = [];
577
+ const shOut4 = [];
578
+ const shOut5 = [];
579
+
580
+ const noop = (v) => v;
581
+
582
+ const set3 = (array, val1, val2, val3) => {
583
+ array[0] = val1;
584
+ array[1] = val2;
585
+ array[2] = val3;
586
+ };
587
+
588
+ const set3FromArray = (array, srcDestView, stride, srcBase, compressionLevel) => {
589
+ array[0] = dataViewFloatForCompressionLevel(srcDestView, srcBase, compressionLevel, true);
590
+ array[1] = dataViewFloatForCompressionLevel(srcDestView, srcBase + stride, compressionLevel, true);
591
+ array[2] = dataViewFloatForCompressionLevel(srcDestView, srcBase + stride + stride, compressionLevel, true);
592
+ };
593
+
594
+ const copy3 = (srcArray, destArray) => {
595
+ destArray[0] = srcArray[0];
596
+ destArray[1] = srcArray[1];
597
+ destArray[2] = srcArray[2];
598
+ };
599
+
600
+ const setOutput3 = (srcArray, destArray, destBase, conversionFunc) => {
601
+ destArray[destBase] = conversionFunc(srcArray[0]);
602
+ destArray[destBase + 1] = conversionFunc(srcArray[1]);
603
+ destArray[destBase + 2] = conversionFunc(srcArray[2]);
604
+ };
605
+
606
+ const toUncompressedFloatArray3 = (src, dest, compressionLevel, range8BitMin, range8BitMax) => {
607
+ dest[0] = toUncompressedFloat(src[0], compressionLevel, true, range8BitMin, range8BitMax);
608
+ dest[1] = toUncompressedFloat(src[1], compressionLevel, true, range8BitMin, range8BitMax);
609
+ dest[2] = toUncompressedFloat(src[2], compressionLevel, true, range8BitMin, range8BitMax);
610
+ return dest;
611
+ };
612
+
613
+ return function(outSphericalHarmonicsArray, outSphericalHarmonicsDegree, transform,
614
+ srcFrom, srcTo, destFrom, desiredOutputCompressionLevel) {
615
+ const splatCount = this.splatCount;
616
+
617
+ srcFrom = srcFrom || 0;
618
+ srcTo = srcTo || splatCount - 1;
619
+ if (destFrom === undefined) destFrom = srcFrom;
620
+
621
+ if (transform && outSphericalHarmonicsDegree >= 1) {
622
+ tempMatrix4.copy(transform);
623
+ tempMatrix4.decompose(tempTranslation, tempRotation, tempScale);
624
+ tempRotation.normalize();
625
+ tempMatrix4.makeRotationFromQuaternion(tempRotation);
626
+ tempMatrix3.setFromMatrix4(tempMatrix4);
627
+ set3(sh11, tempMatrix3.elements[4], -tempMatrix3.elements[7], tempMatrix3.elements[1]);
628
+ set3(sh12, -tempMatrix3.elements[5], tempMatrix3.elements[8], -tempMatrix3.elements[2]);
629
+ set3(sh13, tempMatrix3.elements[3], -tempMatrix3.elements[6], tempMatrix3.elements[0]);
630
+ }
631
+
632
+ const localFromHalfFloatToUint8 = (v) => {
633
+ return fromHalfFloatToUint8(v, this.minSphericalHarmonicsCoeff, this.maxSphericalHarmonicsCoeff);
634
+ };
635
+
636
+ const localToUint8 = (v) => {
637
+ return toUint8(v, this.minSphericalHarmonicsCoeff, this.maxSphericalHarmonicsCoeff);
638
+ };
639
+
640
+ for (let i = srcFrom; i <= srcTo; i++) {
641
+
642
+ const sectionIndex = this.globalSplatIndexToSectionMap[i];
643
+ const section = this.sections[sectionIndex];
644
+ outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, section.sphericalHarmonicsDegree);
645
+ const outSphericalHarmonicsComponentsCount = getSphericalHarmonicsComponentCountForDegree(outSphericalHarmonicsDegree);
646
+
647
+ const localSplatIndex = i - section.splatCountOffset;
648
+
649
+ const srcSplatSHBase = section.bytesPerSplat * localSplatIndex +
650
+ SplatBuffer.CompressionLevels[this.compressionLevel].SphericalHarmonicsOffsetBytes;
651
+
652
+ const dataView = new DataView(this.bufferData, section.dataBase + srcSplatSHBase);
653
+
654
+ const shDestBase = (i - srcFrom + destFrom) * outSphericalHarmonicsComponentsCount;
655
+
656
+ let compressionLevelForOutputConversion = transform ? 0 : this.compressionLevel;
657
+ let outputConversionFunc = noop;
658
+ if (compressionLevelForOutputConversion !== desiredOutputCompressionLevel) {
659
+ if (compressionLevelForOutputConversion === 1) {
660
+ if (desiredOutputCompressionLevel === 0) outputConversionFunc = fromHalfFloat;
661
+ else if (desiredOutputCompressionLevel == 2) outputConversionFunc = localFromHalfFloatToUint8;
662
+ } else if (compressionLevelForOutputConversion === 0) {
663
+ if (desiredOutputCompressionLevel === 1) outputConversionFunc = toHalfFloat;
664
+ else if (desiredOutputCompressionLevel == 2) outputConversionFunc = localToUint8;
665
+ }
666
+ }
667
+
668
+ const minShCoeff = this.minSphericalHarmonicsCoeff;
669
+ const maxShCoeff = this.maxSphericalHarmonicsCoeff;
670
+
671
+ if (outSphericalHarmonicsDegree >= 1) {
672
+
673
+ set3FromArray(shIn1, dataView, 3, 0, this.compressionLevel);
674
+ set3FromArray(shIn2, dataView, 3, 1, this.compressionLevel);
675
+ set3FromArray(shIn3, dataView, 3, 2, this.compressionLevel);
676
+
677
+ if (transform) {
678
+ toUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel, minShCoeff, maxShCoeff);
679
+ toUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel, minShCoeff, maxShCoeff);
680
+ toUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel, minShCoeff, maxShCoeff);
681
+ SplatBuffer.rotateSphericalHarmonics3(shIn1, shIn2, shIn3, sh11, sh12, sh13, shOut1, shOut2, shOut3);
682
+ } else {
683
+ copy3(shIn1, shOut1);
684
+ copy3(shIn2, shOut2);
685
+ copy3(shIn3, shOut3);
686
+ }
687
+
688
+ setOutput3(shOut1, outSphericalHarmonicsArray, shDestBase, outputConversionFunc);
689
+ setOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 3, outputConversionFunc);
690
+ setOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 6, outputConversionFunc);
691
+
692
+ if (outSphericalHarmonicsDegree >= 2) {
693
+
694
+ set3FromArray(shIn1, dataView, 5, 9, this.compressionLevel);
695
+ set3FromArray(shIn2, dataView, 5, 10, this.compressionLevel);
696
+ set3FromArray(shIn3, dataView, 5, 11, this.compressionLevel);
697
+ set3FromArray(shIn4, dataView, 5, 12, this.compressionLevel);
698
+ set3FromArray(shIn5, dataView, 5, 13, this.compressionLevel);
699
+
700
+ if (transform) {
701
+ toUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel, minShCoeff, maxShCoeff);
702
+ toUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel, minShCoeff, maxShCoeff);
703
+ toUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel, minShCoeff, maxShCoeff);
704
+ toUncompressedFloatArray3(shIn4, shIn4, this.compressionLevel, minShCoeff, maxShCoeff);
705
+ toUncompressedFloatArray3(shIn5, shIn5, this.compressionLevel, minShCoeff, maxShCoeff);
706
+ SplatBuffer.rotateSphericalHarmonics5(shIn1, shIn2, shIn3, shIn4, shIn5,
707
+ sh11, sh12, sh13, sh21, sh22, sh23, sh24, sh25,
708
+ shOut1, shOut2, shOut3, shOut4, shOut5);
709
+ } else {
710
+ copy3(shIn1, shOut1);
711
+ copy3(shIn2, shOut2);
712
+ copy3(shIn3, shOut3);
713
+ copy3(shIn4, shOut4);
714
+ copy3(shIn5, shOut5);
715
+ }
716
+
717
+ setOutput3(shOut1, outSphericalHarmonicsArray, shDestBase + 9, outputConversionFunc);
718
+ setOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 12, outputConversionFunc);
719
+ setOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 15, outputConversionFunc);
720
+ setOutput3(shOut4, outSphericalHarmonicsArray, shDestBase + 18, outputConversionFunc);
721
+ setOutput3(shOut5, outSphericalHarmonicsArray, shDestBase + 21, outputConversionFunc);
722
+ }
723
+ }
724
+ }
725
+ };
726
+
727
+ }();
728
+
729
+ static dot3 = (v1, v2, v3, transformRow, outArray) => {
730
+ outArray[0] = outArray[1] = outArray[2] = 0;
731
+ const t0 = transformRow[0];
732
+ const t1 = transformRow[1];
733
+ const t2 = transformRow[2];
734
+ SplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);
735
+ SplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);
736
+ SplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);
737
+ };
738
+
739
+ static addInto3 = (val1, val2, val3, destArray) => {
740
+ destArray[0] = destArray[0] + val1;
741
+ destArray[1] = destArray[1] + val2;
742
+ destArray[2] = destArray[2] + val3;
743
+ };
744
+
745
+ static dot5 = (v1, v2, v3, v4, v5, transformRow, outArray) => {
746
+ outArray[0] = outArray[1] = outArray[2] = 0;
747
+ const t0 = transformRow[0];
748
+ const t1 = transformRow[1];
749
+ const t2 = transformRow[2];
750
+ const t3 = transformRow[3];
751
+ const t4 = transformRow[4];
752
+ SplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);
753
+ SplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);
754
+ SplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);
755
+ SplatBuffer.addInto3(v4[0] * t3, v4[1] * t3, v4[2] * t3, outArray);
756
+ SplatBuffer.addInto3(v5[0] * t4, v5[1] * t4, v5[2] * t4, outArray);
757
+ };
758
+
759
+ static rotateSphericalHarmonics3 = (in1, in2, in3, tsh11, tsh12, tsh13, out1, out2, out3) => {
760
+ SplatBuffer.dot3(in1, in2, in3, tsh11, out1);
761
+ SplatBuffer.dot3(in1, in2, in3, tsh12, out2);
762
+ SplatBuffer.dot3(in1, in2, in3, tsh13, out3);
763
+ };
764
+
765
+ static rotateSphericalHarmonics5 = (in1, in2, in3, in4, in5, tsh11, tsh12, tsh13,
766
+ tsh21, tsh22, tsh23, tsh24, tsh25, out1, out2, out3, out4, out5) => {
767
+
768
+ const kSqrt0104 = Math.sqrt(1.0 / 4.0);
769
+ const kSqrt0304 = Math.sqrt(3.0 / 4.0);
770
+ const kSqrt0103 = Math.sqrt(1.0 / 3.0);
771
+ const kSqrt0403 = Math.sqrt(4.0 / 3.0);
772
+ const kSqrt0112 = Math.sqrt(1.0 / 12.0);
773
+
774
+ tsh21[0] = kSqrt0104 * ((tsh13[2] * tsh11[0] + tsh13[0] * tsh11[2]) + (tsh11[2] * tsh13[0] + tsh11[0] * tsh13[2]));
775
+ tsh21[1] = (tsh13[1] * tsh11[0] + tsh11[1] * tsh13[0]);
776
+ tsh21[2] = kSqrt0304 * (tsh13[1] * tsh11[1] + tsh11[1] * tsh13[1]);
777
+ tsh21[3] = (tsh13[1] * tsh11[2] + tsh11[1] * tsh13[2]);
778
+ tsh21[4] = kSqrt0104 * ((tsh13[2] * tsh11[2] - tsh13[0] * tsh11[0]) + (tsh11[2] * tsh13[2] - tsh11[0] * tsh13[0]));
779
+ SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh21, out1);
780
+
781
+ tsh22[0] = kSqrt0104 * ((tsh12[2] * tsh11[0] + tsh12[0] * tsh11[2]) + (tsh11[2] * tsh12[0] + tsh11[0] * tsh12[2]));
782
+ tsh22[1] = tsh12[1] * tsh11[0] + tsh11[1] * tsh12[0];
783
+ tsh22[2] = kSqrt0304 * (tsh12[1] * tsh11[1] + tsh11[1] * tsh12[1]);
784
+ tsh22[3] = tsh12[1] * tsh11[2] + tsh11[1] * tsh12[2];
785
+ tsh22[4] = kSqrt0104 * ((tsh12[2] * tsh11[2] - tsh12[0] * tsh11[0]) + (tsh11[2] * tsh12[2] - tsh11[0] * tsh12[0]));
786
+ SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh22, out2);
787
+
788
+ tsh23[0] = kSqrt0103 * (tsh12[2] * tsh12[0] + tsh12[0] * tsh12[2]) + -kSqrt0112 *
789
+ ((tsh13[2] * tsh13[0] + tsh13[0] * tsh13[2]) + (tsh11[2] * tsh11[0] + tsh11[0] * tsh11[2]));
790
+ tsh23[1] = kSqrt0403 * tsh12[1] * tsh12[0] + -kSqrt0103 * (tsh13[1] * tsh13[0] + tsh11[1] * tsh11[0]);
791
+ tsh23[2] = tsh12[1] * tsh12[1] + -kSqrt0104 * (tsh13[1] * tsh13[1] + tsh11[1] * tsh11[1]);
792
+ tsh23[3] = kSqrt0403 * tsh12[1] * tsh12[2] + -kSqrt0103 * (tsh13[1] * tsh13[2] + tsh11[1] * tsh11[2]);
793
+ tsh23[4] = kSqrt0103 * (tsh12[2] * tsh12[2] - tsh12[0] * tsh12[0]) + -kSqrt0112 *
794
+ ((tsh13[2] * tsh13[2] - tsh13[0] * tsh13[0]) + (tsh11[2] * tsh11[2] - tsh11[0] * tsh11[0]));
795
+ SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh23, out3);
796
+
797
+ tsh24[0] = kSqrt0104 * ((tsh12[2] * tsh13[0] + tsh12[0] * tsh13[2]) + (tsh13[2] * tsh12[0] + tsh13[0] * tsh12[2]));
798
+ tsh24[1] = tsh12[1] * tsh13[0] + tsh13[1] * tsh12[0];
799
+ tsh24[2] = kSqrt0304 * (tsh12[1] * tsh13[1] + tsh13[1] * tsh12[1]);
800
+ tsh24[3] = tsh12[1] * tsh13[2] + tsh13[1] * tsh12[2];
801
+ tsh24[4] = kSqrt0104 * ((tsh12[2] * tsh13[2] - tsh12[0] * tsh13[0]) + (tsh13[2] * tsh12[2] - tsh13[0] * tsh12[0]));
802
+ SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh24, out4);
803
+
804
+ tsh25[0] = kSqrt0104 * ((tsh13[2] * tsh13[0] + tsh13[0] * tsh13[2]) - (tsh11[2] * tsh11[0] + tsh11[0] * tsh11[2]));
805
+ tsh25[1] = (tsh13[1] * tsh13[0] - tsh11[1] * tsh11[0]);
806
+ tsh25[2] = kSqrt0304 * (tsh13[1] * tsh13[1] - tsh11[1] * tsh11[1]);
807
+ tsh25[3] = (tsh13[1] * tsh13[2] - tsh11[1] * tsh11[2]);
808
+ tsh25[4] = kSqrt0104 * ((tsh13[2] * tsh13[2] - tsh13[0] * tsh13[0]) - (tsh11[2] * tsh11[2] - tsh11[0] * tsh11[0]));
809
+ SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh25, out5);
810
+ };
811
+
812
+ static parseHeader(buffer) {
813
+ const headerArrayUint8 = new Uint8Array(buffer, 0, SplatBuffer.HeaderSizeBytes);
814
+ const headerArrayUint16 = new Uint16Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 2);
815
+ const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
816
+ const headerArrayFloat32 = new Float32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
817
+ const versionMajor = headerArrayUint8[0];
818
+ const versionMinor = headerArrayUint8[1];
819
+ const maxSectionCount = headerArrayUint32[1];
820
+ const sectionCount = headerArrayUint32[2];
821
+ const maxSplatCount = headerArrayUint32[3];
822
+ const splatCount = headerArrayUint32[4];
823
+ const compressionLevel = headerArrayUint16[10];
824
+ const sceneCenter = new Vector3(headerArrayFloat32[6], headerArrayFloat32[7], headerArrayFloat32[8]);
825
+
826
+ const minSphericalHarmonicsCoeff = headerArrayFloat32[9] || -DefaultSphericalHarmonics8BitCompressionHalfRange;
827
+ const maxSphericalHarmonicsCoeff = headerArrayFloat32[10] || DefaultSphericalHarmonics8BitCompressionHalfRange;
828
+
829
+ return {
830
+ versionMajor,
831
+ versionMinor,
832
+ maxSectionCount,
833
+ sectionCount,
834
+ maxSplatCount,
835
+ splatCount,
836
+ compressionLevel,
837
+ sceneCenter,
838
+ minSphericalHarmonicsCoeff,
839
+ maxSphericalHarmonicsCoeff
840
+ };
841
+ }
842
+
843
+ static writeHeaderCountsToBuffer(sectionCount, splatCount, buffer) {
844
+ const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
845
+ headerArrayUint32[2] = sectionCount;
846
+ headerArrayUint32[4] = splatCount;
847
+ }
848
+
849
+ static writeHeaderToBuffer(header, buffer) {
850
+ const headerArrayUint8 = new Uint8Array(buffer, 0, SplatBuffer.HeaderSizeBytes);
851
+ const headerArrayUint16 = new Uint16Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 2);
852
+ const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
853
+ const headerArrayFloat32 = new Float32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
854
+ headerArrayUint8[0] = header.versionMajor;
855
+ headerArrayUint8[1] = header.versionMinor;
856
+ headerArrayUint8[2] = 0; // unused for now
857
+ headerArrayUint8[3] = 0; // unused for now
858
+ headerArrayUint32[1] = header.maxSectionCount;
859
+ headerArrayUint32[2] = header.sectionCount;
860
+ headerArrayUint32[3] = header.maxSplatCount;
861
+ headerArrayUint32[4] = header.splatCount;
862
+ headerArrayUint16[10] = header.compressionLevel;
863
+ headerArrayFloat32[6] = header.sceneCenter.x;
864
+ headerArrayFloat32[7] = header.sceneCenter.y;
865
+ headerArrayFloat32[8] = header.sceneCenter.z;
866
+ headerArrayFloat32[9] = header.minSphericalHarmonicsCoeff || -DefaultSphericalHarmonics8BitCompressionHalfRange;
867
+ headerArrayFloat32[10] = header.maxSphericalHarmonicsCoeff || DefaultSphericalHarmonics8BitCompressionHalfRange;
868
+ }
869
+
870
+ static parseSectionHeaders(header, buffer, offset = 0, secLoadedCountsToMax) {
871
+ const compressionLevel = header.compressionLevel;
872
+
873
+ const maxSectionCount = header.maxSectionCount;
874
+ const sectionHeaderArrayUint16 = new Uint16Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 2);
875
+ const sectionHeaderArrayUint32 = new Uint32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
876
+ const sectionHeaderArrayFloat32 = new Float32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
877
+
878
+ const sectionHeaders = [];
879
+ let sectionHeaderBase = 0;
880
+ let sectionHeaderBaseUint16 = sectionHeaderBase / 2;
881
+ let sectionHeaderBaseUint32 = sectionHeaderBase / 4;
882
+ let sectionBase = SplatBuffer.HeaderSizeBytes + header.maxSectionCount * SplatBuffer.SectionHeaderSizeBytes;
883
+ let splatCountOffset = 0;
884
+ for (let i = 0; i < maxSectionCount; i++) {
885
+ const maxSplatCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 1];
886
+ const bucketSize = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 2];
887
+ const bucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 3];
888
+ const bucketBlockSize = sectionHeaderArrayFloat32[sectionHeaderBaseUint32 + 4];
889
+ const halfBucketBlockSize = bucketBlockSize / 2.0;
890
+ const bucketStorageSizeBytes = sectionHeaderArrayUint16[sectionHeaderBaseUint16 + 10];
891
+ const compressionScaleRange = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 6] ||
892
+ SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
893
+ const fullBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 8];
894
+ const partiallyFilledBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 9];
895
+ const bucketsMetaDataSizeBytes = partiallyFilledBucketCount * 4;
896
+ const bucketsStorageSizeBytes = bucketStorageSizeBytes * bucketCount + bucketsMetaDataSizeBytes;
897
+
898
+ const sphericalHarmonicsDegree = sectionHeaderArrayUint16[sectionHeaderBaseUint16 + 20];
899
+ const { bytesPerSplat } = SplatBuffer.calculateComponentStorage(compressionLevel, sphericalHarmonicsDegree);
900
+
901
+ const splatDataStorageSizeBytes = bytesPerSplat * maxSplatCount;
902
+ const storageSizeBytes = splatDataStorageSizeBytes + bucketsStorageSizeBytes;
903
+ const sectionHeader = {
904
+ bytesPerSplat: bytesPerSplat,
905
+ splatCountOffset: splatCountOffset,
906
+ splatCount: secLoadedCountsToMax ? maxSplatCount : 0,
907
+ maxSplatCount: maxSplatCount,
908
+ bucketSize: bucketSize,
909
+ bucketCount: bucketCount,
910
+ bucketBlockSize: bucketBlockSize,
911
+ halfBucketBlockSize: halfBucketBlockSize,
912
+ bucketStorageSizeBytes: bucketStorageSizeBytes,
913
+ bucketsStorageSizeBytes: bucketsStorageSizeBytes,
914
+ splatDataStorageSizeBytes: splatDataStorageSizeBytes,
915
+ storageSizeBytes: storageSizeBytes,
916
+ compressionScaleRange: compressionScaleRange,
917
+ compressionScaleFactor: halfBucketBlockSize / compressionScaleRange,
918
+ base: sectionBase,
919
+ bucketsBase: sectionBase + bucketsMetaDataSizeBytes,
920
+ dataBase: sectionBase + bucketsStorageSizeBytes,
921
+ fullBucketCount: fullBucketCount,
922
+ partiallyFilledBucketCount: partiallyFilledBucketCount,
923
+ sphericalHarmonicsDegree: sphericalHarmonicsDegree
924
+ };
925
+ sectionHeaders[i] = sectionHeader;
926
+ sectionBase += storageSizeBytes;
927
+ sectionHeaderBase += SplatBuffer.SectionHeaderSizeBytes;
928
+ sectionHeaderBaseUint16 = sectionHeaderBase / 2;
929
+ sectionHeaderBaseUint32 = sectionHeaderBase / 4;
930
+ splatCountOffset += maxSplatCount;
931
+ }
932
+
933
+ return sectionHeaders;
934
+ }
935
+
936
+
937
+ static writeSectionHeaderToBuffer(sectionHeader, compressionLevel, buffer, offset = 0) {
938
+ const sectionHeadeArrayUint16 = new Uint16Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 2);
939
+ const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
940
+ const sectionHeadeArrayFloat32 = new Float32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
941
+
942
+ sectionHeadeArrayUint32[0] = sectionHeader.splatCount;
943
+ sectionHeadeArrayUint32[1] = sectionHeader.maxSplatCount;
944
+ sectionHeadeArrayUint32[2] = compressionLevel >= 1 ? sectionHeader.bucketSize : 0;
945
+ sectionHeadeArrayUint32[3] = compressionLevel >= 1 ? sectionHeader.bucketCount : 0;
946
+ sectionHeadeArrayFloat32[4] = compressionLevel >= 1 ? sectionHeader.bucketBlockSize : 0.0;
947
+ sectionHeadeArrayUint16[10] = compressionLevel >= 1 ? SplatBuffer.BucketStorageSizeBytes : 0;
948
+ sectionHeadeArrayUint32[6] = compressionLevel >= 1 ? sectionHeader.compressionScaleRange : 0;
949
+ sectionHeadeArrayUint32[7] = sectionHeader.storageSizeBytes;
950
+ sectionHeadeArrayUint32[8] = compressionLevel >= 1 ? sectionHeader.fullBucketCount : 0;
951
+ sectionHeadeArrayUint32[9] = compressionLevel >= 1 ? sectionHeader.partiallyFilledBucketCount : 0;
952
+ sectionHeadeArrayUint16[20] = sectionHeader.sphericalHarmonicsDegree;
953
+
954
+ }
955
+
956
+ static writeSectionHeaderSplatCountToBuffer(splatCount, buffer, offset = 0) {
957
+ const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
958
+ sectionHeadeArrayUint32[0] = splatCount;
959
+ }
960
+
961
+ constructFromBuffer(bufferData, secLoadedCountsToMax) {
962
+ this.bufferData = bufferData;
963
+
964
+ this.globalSplatIndexToLocalSplatIndexMap = [];
965
+ this.globalSplatIndexToSectionMap = [];
966
+
967
+ const header = SplatBuffer.parseHeader(this.bufferData);
968
+ this.versionMajor = header.versionMajor;
969
+ this.versionMinor = header.versionMinor;
970
+ this.maxSectionCount = header.maxSectionCount;
971
+ this.sectionCount = secLoadedCountsToMax ? header.maxSectionCount : 0;
972
+ this.maxSplatCount = header.maxSplatCount;
973
+ this.splatCount = secLoadedCountsToMax ? header.maxSplatCount : 0;
974
+ this.compressionLevel = header.compressionLevel;
975
+ this.sceneCenter = new Vector3().copy(header.sceneCenter);
976
+ this.minSphericalHarmonicsCoeff = header.minSphericalHarmonicsCoeff;
977
+ this.maxSphericalHarmonicsCoeff = header.maxSphericalHarmonicsCoeff;
978
+
979
+ this.sections = SplatBuffer.parseSectionHeaders(header, this.bufferData, SplatBuffer.HeaderSizeBytes, secLoadedCountsToMax);
980
+
981
+ this.linkBufferArrays();
982
+ this.buildMaps();
983
+ }
984
+
985
+ static calculateComponentStorage(compressionLevel, sphericalHarmonicsDegree) {
986
+ const bytesPerCenter = SplatBuffer.CompressionLevels[compressionLevel].BytesPerCenter;
987
+ const bytesPerScale = SplatBuffer.CompressionLevels[compressionLevel].BytesPerScale;
988
+ const bytesPerRotation = SplatBuffer.CompressionLevels[compressionLevel].BytesPerRotation;
989
+ const bytesPerColor = SplatBuffer.CompressionLevels[compressionLevel].BytesPerColor;
990
+ const sphericalHarmonicsComponentsPerSplat = getSphericalHarmonicsComponentCountForDegree(sphericalHarmonicsDegree);
991
+ const sphericalHarmonicsBytesPerSplat = SplatBuffer.CompressionLevels[compressionLevel].BytesPerSphericalHarmonicsComponent *
992
+ sphericalHarmonicsComponentsPerSplat;
993
+ const bytesPerSplat = bytesPerCenter + bytesPerScale + bytesPerRotation +
994
+ bytesPerColor + sphericalHarmonicsBytesPerSplat;
995
+ return {
996
+ bytesPerCenter,
997
+ bytesPerScale,
998
+ bytesPerRotation,
999
+ bytesPerColor,
1000
+ sphericalHarmonicsComponentsPerSplat,
1001
+ sphericalHarmonicsBytesPerSplat,
1002
+ bytesPerSplat
1003
+ };
1004
+ }
1005
+
1006
+ linkBufferArrays() {
1007
+ for (let i = 0; i < this.maxSectionCount; i++) {
1008
+ const section = this.sections[i];
1009
+ section.bucketArray = new Float32Array(this.bufferData, section.bucketsBase,
1010
+ section.bucketCount * SplatBuffer.BucketStorageSizeFloats);
1011
+ if (section.partiallyFilledBucketCount > 0) {
1012
+ section.partiallyFilledBucketLengths = new Uint32Array(this.bufferData, section.base,
1013
+ section.partiallyFilledBucketCount);
1014
+ }
1015
+ }
1016
+ }
1017
+
1018
+ buildMaps() {
1019
+ let cumulativeSplatCount = 0;
1020
+ for (let i = 0; i < this.maxSectionCount; i++) {
1021
+ const section = this.sections[i];
1022
+ for (let j = 0; j < section.maxSplatCount; j++) {
1023
+ const globalSplatIndex = cumulativeSplatCount + j;
1024
+ this.globalSplatIndexToLocalSplatIndexMap[globalSplatIndex] = j;
1025
+ this.globalSplatIndexToSectionMap[globalSplatIndex] = i;
1026
+ }
1027
+ cumulativeSplatCount += section.maxSplatCount;
1028
+ }
1029
+ }
1030
+
1031
+ updateLoadedCounts(newSectionCount, newSplatCount) {
1032
+ SplatBuffer.writeHeaderCountsToBuffer(newSectionCount, newSplatCount, this.bufferData);
1033
+ this.sectionCount = newSectionCount;
1034
+ this.splatCount = newSplatCount;
1035
+ }
1036
+
1037
+ updateSectionLoadedCounts(sectionIndex, newSplatCount) {
1038
+ const sectionHeaderOffset = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * sectionIndex;
1039
+ SplatBuffer.writeSectionHeaderSplatCountToBuffer(newSplatCount, this.bufferData, sectionHeaderOffset);
1040
+ this.sections[sectionIndex].splatCount = newSplatCount;
1041
+ }
1042
+
1043
+ static writeSplatDataToSectionBuffer = function() {
1044
+
1045
+ const tempCenterBuffer = new ArrayBuffer(12);
1046
+ const tempScaleBuffer = new ArrayBuffer(12);
1047
+ const tempRotationBuffer = new ArrayBuffer(16);
1048
+ const tempColorBuffer = new ArrayBuffer(4);
1049
+ const tempSHBuffer = new ArrayBuffer(256);
1050
+ const tempRot = new Quaternion();
1051
+ const tempScale = new Vector3();
1052
+ const bucketCenterDelta = new Vector3();
1053
+
1054
+ const {
1055
+ X: OFFSET_X, Y: OFFSET_Y, Z: OFFSET_Z,
1056
+ SCALE0: OFFSET_SCALE0, SCALE1: OFFSET_SCALE1, SCALE2: OFFSET_SCALE2,
1057
+ ROTATION0: OFFSET_ROT0, ROTATION1: OFFSET_ROT1, ROTATION2: OFFSET_ROT2, ROTATION3: OFFSET_ROT3,
1058
+ FDC0: OFFSET_FDC0, FDC1: OFFSET_FDC1, FDC2: OFFSET_FDC2, OPACITY: OFFSET_OPACITY,
1059
+ FRC0: OFFSET_FRC0, FRC9: OFFSET_FRC9,
1060
+ } = UncompressedSplatArray.OFFSET;
1061
+
1062
+ const compressPositionOffset = (v, compressionScaleFactor, compressionScaleRange) => {
1063
+ const doubleCompressionScaleRange = compressionScaleRange * 2 + 1;
1064
+ v = Math.round(v * compressionScaleFactor) + compressionScaleRange;
1065
+ return clamp(v, 0, doubleCompressionScaleRange);
1066
+ };
1067
+
1068
+ return function(targetSplat, sectionBuffer, bufferOffset, compressionLevel, sphericalHarmonicsDegree,
1069
+ bucketCenter, compressionScaleFactor, compressionScaleRange,
1070
+ minSphericalHarmonicsCoeff = -DefaultSphericalHarmonics8BitCompressionHalfRange,
1071
+ maxSphericalHarmonicsCoeff = DefaultSphericalHarmonics8BitCompressionHalfRange) {
1072
+
1073
+ const sphericalHarmonicsComponentsPerSplat = getSphericalHarmonicsComponentCountForDegree(sphericalHarmonicsDegree);
1074
+ const bytesPerCenter = SplatBuffer.CompressionLevels[compressionLevel].BytesPerCenter;
1075
+ const bytesPerScale = SplatBuffer.CompressionLevels[compressionLevel].BytesPerScale;
1076
+ const bytesPerRotation = SplatBuffer.CompressionLevels[compressionLevel].BytesPerRotation;
1077
+ const bytesPerColor = SplatBuffer.CompressionLevels[compressionLevel].BytesPerColor;
1078
+
1079
+ const centerBase = bufferOffset;
1080
+ const scaleBase = centerBase + bytesPerCenter;
1081
+ const rotationBase = scaleBase + bytesPerScale;
1082
+ const colorBase = rotationBase + bytesPerRotation;
1083
+ const sphericalHarmonicsBase = colorBase + bytesPerColor;
1084
+
1085
+ if (targetSplat[OFFSET_ROT0] !== undefined) {
1086
+ tempRot.set(targetSplat[OFFSET_ROT0], targetSplat[OFFSET_ROT1], targetSplat[OFFSET_ROT2], targetSplat[OFFSET_ROT3]);
1087
+ tempRot.normalize();
1088
+ } else {
1089
+ tempRot.set(1.0, 0.0, 0.0, 0.0);
1090
+ }
1091
+
1092
+ if (targetSplat[OFFSET_SCALE0] !== undefined) {
1093
+ tempScale.set(targetSplat[OFFSET_SCALE0] || 0,
1094
+ targetSplat[OFFSET_SCALE1] || 0,
1095
+ targetSplat[OFFSET_SCALE2] || 0);
1096
+ } else {
1097
+ tempScale.set(0, 0, 0);
1098
+ }
1099
+
1100
+ if (compressionLevel === 0) {
1101
+ const center = new Float32Array(sectionBuffer, centerBase, SplatBuffer.CenterComponentCount);
1102
+ const rot = new Float32Array(sectionBuffer, rotationBase, SplatBuffer.RotationComponentCount);
1103
+ const scale = new Float32Array(sectionBuffer, scaleBase, SplatBuffer.ScaleComponentCount);
1104
+
1105
+ rot.set([tempRot.x, tempRot.y, tempRot.z, tempRot.w]);
1106
+ scale.set([tempScale.x, tempScale.y, tempScale.z]);
1107
+ center.set([targetSplat[OFFSET_X], targetSplat[OFFSET_Y], targetSplat[OFFSET_Z]]);
1108
+
1109
+ if (sphericalHarmonicsDegree > 0) {
1110
+ const shOut = new Float32Array(sectionBuffer, sphericalHarmonicsBase, sphericalHarmonicsComponentsPerSplat);
1111
+ if (sphericalHarmonicsDegree >= 1) {
1112
+ for (let s = 0; s < 9; s++) shOut[s] = targetSplat[OFFSET_FRC0 + s] || 0;
1113
+ if (sphericalHarmonicsDegree >= 2) {
1114
+ for (let s = 0; s < 15; s++) shOut[s + 9] = targetSplat[OFFSET_FRC9 + s] || 0;
1115
+ }
1116
+ }
1117
+ }
1118
+ } else {
1119
+ const center = new Uint16Array(tempCenterBuffer, 0, SplatBuffer.CenterComponentCount);
1120
+ const rot = new Uint16Array(tempRotationBuffer, 0, SplatBuffer.RotationComponentCount);
1121
+ const scale = new Uint16Array(tempScaleBuffer, 0, SplatBuffer.ScaleComponentCount);
1122
+
1123
+ rot.set([toHalfFloat(tempRot.x), toHalfFloat(tempRot.y), toHalfFloat(tempRot.z), toHalfFloat(tempRot.w)]);
1124
+ scale.set([toHalfFloat(tempScale.x), toHalfFloat(tempScale.y), toHalfFloat(tempScale.z)]);
1125
+
1126
+ bucketCenterDelta.set(targetSplat[OFFSET_X], targetSplat[OFFSET_Y], targetSplat[OFFSET_Z]).sub(bucketCenter);
1127
+ bucketCenterDelta.x = compressPositionOffset(bucketCenterDelta.x, compressionScaleFactor, compressionScaleRange);
1128
+ bucketCenterDelta.y = compressPositionOffset(bucketCenterDelta.y, compressionScaleFactor, compressionScaleRange);
1129
+ bucketCenterDelta.z = compressPositionOffset(bucketCenterDelta.z, compressionScaleFactor, compressionScaleRange);
1130
+ center.set([bucketCenterDelta.x, bucketCenterDelta.y, bucketCenterDelta.z]);
1131
+
1132
+ if (sphericalHarmonicsDegree > 0) {
1133
+ const SHArrayType = compressionLevel === 1 ? Uint16Array : Uint8Array;
1134
+ const bytesPerSHComponent = compressionLevel === 1 ? 2 : 1;
1135
+ const shOut = new SHArrayType(tempSHBuffer, 0, sphericalHarmonicsComponentsPerSplat);
1136
+ if (sphericalHarmonicsDegree >= 1) {
1137
+ for (let s = 0; s < 9; s++) {
1138
+ const srcVal = targetSplat[OFFSET_FRC0 + s] || 0;
1139
+ shOut[s] = compressionLevel === 1 ? toHalfFloat(srcVal) :
1140
+ toUint8(srcVal, minSphericalHarmonicsCoeff, maxSphericalHarmonicsCoeff);
1141
+ }
1142
+ const degree1ByteCount = 9 * bytesPerSHComponent;
1143
+ copyBetweenBuffers(shOut.buffer, 0, sectionBuffer, sphericalHarmonicsBase, degree1ByteCount);
1144
+ if (sphericalHarmonicsDegree >= 2) {
1145
+ for (let s = 0; s < 15; s++) {
1146
+ const srcVal = targetSplat[OFFSET_FRC9 + s] || 0;
1147
+ shOut[s + 9] = compressionLevel === 1 ? toHalfFloat(srcVal) :
1148
+ toUint8(srcVal, minSphericalHarmonicsCoeff, maxSphericalHarmonicsCoeff);
1149
+ }
1150
+ copyBetweenBuffers(shOut.buffer, degree1ByteCount, sectionBuffer,
1151
+ sphericalHarmonicsBase + degree1ByteCount, 15 * bytesPerSHComponent);
1152
+ }
1153
+ }
1154
+ }
1155
+
1156
+ copyBetweenBuffers(center.buffer, 0, sectionBuffer, centerBase, 6);
1157
+ copyBetweenBuffers(scale.buffer, 0, sectionBuffer, scaleBase, 6);
1158
+ copyBetweenBuffers(rot.buffer, 0, sectionBuffer, rotationBase, 8);
1159
+ }
1160
+
1161
+ const rgba = new Uint8ClampedArray(tempColorBuffer, 0, 4);
1162
+ rgba.set([targetSplat[OFFSET_FDC0] || 0, targetSplat[OFFSET_FDC1] || 0, targetSplat[OFFSET_FDC2] || 0]);
1163
+ rgba[3] = targetSplat[OFFSET_OPACITY] || 0;
1164
+
1165
+ copyBetweenBuffers(rgba.buffer, 0, sectionBuffer, colorBase, 4);
1166
+ };
1167
+
1168
+ }();
1169
+
1170
+ static generateFromUncompressedSplatArrays(splatArrays, minimumAlpha, compressionLevel,
1171
+ sceneCenter, blockSize, bucketSize, options = []) {
1172
+
1173
+ let shDegree = 0;
1174
+ for (let sa = 0; sa < splatArrays.length; sa ++) {
1175
+ const splatArray = splatArrays[sa];
1176
+ shDegree = Math.max(splatArray.sphericalHarmonicsDegree, shDegree);
1177
+ }
1178
+
1179
+ let minSphericalHarmonicsCoeff;
1180
+ let maxSphericalHarmonicsCoeff;
1181
+
1182
+ for (let sa = 0; sa < splatArrays.length; sa ++) {
1183
+ const splatArray = splatArrays[sa];
1184
+ for (let i = 0; i < splatArray.splats.length; i++) {
1185
+ const splat = splatArray.splats[i];
1186
+ for (let sc = UncompressedSplatArray.OFFSET.FRC0; sc < UncompressedSplatArray.OFFSET.FRC23 && sc < splat.length; sc++) {
1187
+ if (!minSphericalHarmonicsCoeff || splat[sc] < minSphericalHarmonicsCoeff) {
1188
+ minSphericalHarmonicsCoeff = splat[sc];
1189
+ }
1190
+ if (!maxSphericalHarmonicsCoeff || splat[sc] > maxSphericalHarmonicsCoeff) {
1191
+ maxSphericalHarmonicsCoeff = splat[sc];
1192
+ }
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ minSphericalHarmonicsCoeff = minSphericalHarmonicsCoeff || -DefaultSphericalHarmonics8BitCompressionHalfRange;
1198
+ maxSphericalHarmonicsCoeff = maxSphericalHarmonicsCoeff || DefaultSphericalHarmonics8BitCompressionHalfRange;
1199
+
1200
+ const { bytesPerSplat } = SplatBuffer.calculateComponentStorage(compressionLevel, shDegree);
1201
+ const compressionScaleRange = SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
1202
+
1203
+ const sectionBuffers = [];
1204
+ const sectionHeaderBuffers = [];
1205
+ let totalSplatCount = 0;
1206
+
1207
+ for (let sa = 0; sa < splatArrays.length; sa ++) {
1208
+ const splatArray = splatArrays[sa];
1209
+ const validSplats = new UncompressedSplatArray(shDegree);
1210
+ for (let i = 0; i < splatArray.splatCount; i++) {
1211
+ const targetSplat = splatArray.splats[i];
1212
+ if ((targetSplat[UncompressedSplatArray.OFFSET.OPACITY] || 0) >= minimumAlpha) {
1213
+ validSplats.addSplat(targetSplat);
1214
+ }
1215
+ }
1216
+
1217
+ const sectionOptions = options[sa] || {};
1218
+ const sectionBlockSize = (sectionOptions.blockSizeFactor || 1) * (blockSize || SplatBuffer.BucketBlockSize);
1219
+ const sectionBucketSize = Math.ceil((sectionOptions.bucketSizeFactor || 1) * (bucketSize || SplatBuffer.BucketSize));
1220
+
1221
+ const bucketInfo = SplatBuffer.computeBucketsForUncompressedSplatArray(validSplats, sectionBlockSize, sectionBucketSize);
1222
+ const fullBucketCount = bucketInfo.fullBuckets.length;
1223
+ const partiallyFullBucketLengths = bucketInfo.partiallyFullBuckets.map((bucket) => bucket.splats.length);
1224
+ const partiallyFilledBucketCount = partiallyFullBucketLengths.length;
1225
+ const buckets = [...bucketInfo.fullBuckets, ...bucketInfo.partiallyFullBuckets];
1226
+
1227
+ const sectionDataSizeBytes = validSplats.splats.length * bytesPerSplat;
1228
+ const bucketMetaDataSizeBytes = partiallyFilledBucketCount * 4;
1229
+ const bucketDataBytes = compressionLevel >= 1 ? buckets.length *
1230
+ SplatBuffer.BucketStorageSizeBytes + bucketMetaDataSizeBytes : 0;
1231
+ const sectionSizeBytes = sectionDataSizeBytes + bucketDataBytes;
1232
+ const sectionBuffer = new ArrayBuffer(sectionSizeBytes);
1233
+
1234
+ const compressionScaleFactor = compressionScaleRange / (sectionBlockSize * 0.5);
1235
+ const bucketCenter = new Vector3();
1236
+
1237
+ let outSplatCount = 0;
1238
+ for (let b = 0; b < buckets.length; b++) {
1239
+ const bucket = buckets[b];
1240
+ bucketCenter.fromArray(bucket.center);
1241
+ for (let i = 0; i < bucket.splats.length; i++) {
1242
+ let row = bucket.splats[i];
1243
+ const targetSplat = validSplats.splats[row];
1244
+ const bufferOffset = bucketDataBytes + outSplatCount * bytesPerSplat;
1245
+ SplatBuffer.writeSplatDataToSectionBuffer(targetSplat, sectionBuffer, bufferOffset, compressionLevel, shDegree,
1246
+ bucketCenter, compressionScaleFactor, compressionScaleRange,
1247
+ minSphericalHarmonicsCoeff, maxSphericalHarmonicsCoeff);
1248
+ outSplatCount++;
1249
+ }
1250
+ }
1251
+ totalSplatCount += outSplatCount;
1252
+
1253
+ if (compressionLevel >= 1) {
1254
+ const bucketMetaDataArray = new Uint32Array(sectionBuffer, 0, partiallyFullBucketLengths.length * 4);
1255
+ for (let pfb = 0; pfb < partiallyFullBucketLengths.length; pfb ++) {
1256
+ bucketMetaDataArray[pfb] = partiallyFullBucketLengths[pfb];
1257
+ }
1258
+ const bucketArray = new Float32Array(sectionBuffer, bucketMetaDataSizeBytes,
1259
+ buckets.length * SplatBuffer.BucketStorageSizeFloats);
1260
+ for (let b = 0; b < buckets.length; b++) {
1261
+ const bucket = buckets[b];
1262
+ const base = b * 3;
1263
+ bucketArray[base] = bucket.center[0];
1264
+ bucketArray[base + 1] = bucket.center[1];
1265
+ bucketArray[base + 2] = bucket.center[2];
1266
+ }
1267
+ }
1268
+ sectionBuffers.push(sectionBuffer);
1269
+
1270
+ const sectionHeaderBuffer = new ArrayBuffer(SplatBuffer.SectionHeaderSizeBytes);
1271
+ SplatBuffer.writeSectionHeaderToBuffer({
1272
+ maxSplatCount: outSplatCount,
1273
+ splatCount: outSplatCount,
1274
+ bucketSize: sectionBucketSize,
1275
+ bucketCount: buckets.length,
1276
+ bucketBlockSize: sectionBlockSize,
1277
+ compressionScaleRange: compressionScaleRange,
1278
+ storageSizeBytes: sectionSizeBytes,
1279
+ fullBucketCount: fullBucketCount,
1280
+ partiallyFilledBucketCount: partiallyFilledBucketCount,
1281
+ sphericalHarmonicsDegree: shDegree
1282
+ }, compressionLevel, sectionHeaderBuffer, 0);
1283
+ sectionHeaderBuffers.push(sectionHeaderBuffer);
1284
+
1285
+ }
1286
+
1287
+ let sectionsCumulativeSizeBytes = 0;
1288
+ for (let sectionBuffer of sectionBuffers) sectionsCumulativeSizeBytes += sectionBuffer.byteLength;
1289
+ const unifiedBufferSize = SplatBuffer.HeaderSizeBytes +
1290
+ SplatBuffer.SectionHeaderSizeBytes * sectionBuffers.length + sectionsCumulativeSizeBytes;
1291
+ const unifiedBuffer = new ArrayBuffer(unifiedBufferSize);
1292
+
1293
+ SplatBuffer.writeHeaderToBuffer({
1294
+ versionMajor: 0,
1295
+ versionMinor: 1,
1296
+ maxSectionCount: sectionBuffers.length,
1297
+ sectionCount: sectionBuffers.length,
1298
+ maxSplatCount: totalSplatCount,
1299
+ splatCount: totalSplatCount,
1300
+ compressionLevel: compressionLevel,
1301
+ sceneCenter: sceneCenter,
1302
+ minSphericalHarmonicsCoeff: minSphericalHarmonicsCoeff,
1303
+ maxSphericalHarmonicsCoeff: maxSphericalHarmonicsCoeff
1304
+ }, unifiedBuffer);
1305
+
1306
+ let currentUnifiedBase = SplatBuffer.HeaderSizeBytes;
1307
+ for (let sectionHeaderBuffer of sectionHeaderBuffers) {
1308
+ new Uint8Array(unifiedBuffer, currentUnifiedBase, SplatBuffer.SectionHeaderSizeBytes).set(new Uint8Array(sectionHeaderBuffer));
1309
+ currentUnifiedBase += SplatBuffer.SectionHeaderSizeBytes;
1310
+ }
1311
+
1312
+ for (let sectionBuffer of sectionBuffers) {
1313
+ new Uint8Array(unifiedBuffer, currentUnifiedBase, sectionBuffer.byteLength).set(new Uint8Array(sectionBuffer));
1314
+ currentUnifiedBase += sectionBuffer.byteLength;
1315
+ }
1316
+
1317
+ const splatBuffer = new SplatBuffer(unifiedBuffer);
1318
+ return splatBuffer;
1319
+ }
1320
+
1321
+ static computeBucketsForUncompressedSplatArray(splatArray, blockSize, bucketSize) {
1322
+ let splatCount = splatArray.splatCount;
1323
+ const halfBlockSize = blockSize / 2.0;
1324
+
1325
+ const min = new Vector3();
1326
+ const max = new Vector3();
1327
+
1328
+ for (let i = 0; i < splatCount; i++) {
1329
+ const targetSplat = splatArray.splats[i];
1330
+ const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
1331
+ targetSplat[UncompressedSplatArray.OFFSET.Y],
1332
+ targetSplat[UncompressedSplatArray.OFFSET.Z]];
1333
+ if (i === 0 || center[0] < min.x) min.x = center[0];
1334
+ if (i === 0 || center[0] > max.x) max.x = center[0];
1335
+ if (i === 0 || center[1] < min.y) min.y = center[1];
1336
+ if (i === 0 || center[1] > max.y) max.y = center[1];
1337
+ if (i === 0 || center[2] < min.z) min.z = center[2];
1338
+ if (i === 0 || center[2] > max.z) max.z = center[2];
1339
+ }
1340
+
1341
+ const dimensions = new Vector3().copy(max).sub(min);
1342
+ const yBlocks = Math.ceil(dimensions.y / blockSize);
1343
+ const zBlocks = Math.ceil(dimensions.z / blockSize);
1344
+
1345
+ const blockCenter = new Vector3();
1346
+ const fullBuckets = [];
1347
+ const partiallyFullBuckets = {};
1348
+
1349
+ for (let i = 0; i < splatCount; i++) {
1350
+ const targetSplat = splatArray.splats[i];
1351
+ const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
1352
+ targetSplat[UncompressedSplatArray.OFFSET.Y],
1353
+ targetSplat[UncompressedSplatArray.OFFSET.Z]];
1354
+ const xBlock = Math.floor((center[0] - min.x) / blockSize);
1355
+ const yBlock = Math.floor((center[1] - min.y) / blockSize);
1356
+ const zBlock = Math.floor((center[2] - min.z) / blockSize);
1357
+
1358
+ blockCenter.x = xBlock * blockSize + min.x + halfBlockSize;
1359
+ blockCenter.y = yBlock * blockSize + min.y + halfBlockSize;
1360
+ blockCenter.z = zBlock * blockSize + min.z + halfBlockSize;
1361
+
1362
+ const bucketId = xBlock * (yBlocks * zBlocks) + yBlock * zBlocks + zBlock;
1363
+ let bucket = partiallyFullBuckets[bucketId];
1364
+ if (!bucket) {
1365
+ partiallyFullBuckets[bucketId] = bucket = {
1366
+ 'splats': [],
1367
+ 'center': blockCenter.toArray()
1368
+ };
1369
+ }
1370
+
1371
+ bucket.splats.push(i);
1372
+ if (bucket.splats.length >= bucketSize) {
1373
+ fullBuckets.push(bucket);
1374
+ partiallyFullBuckets[bucketId] = null;
1375
+ }
1376
+ }
1377
+
1378
+ const partiallyFullBucketArray = [];
1379
+ for (let bucketId in partiallyFullBuckets) {
1380
+ if (Object.hasOwn(partiallyFullBuckets, bucketId)) {
1381
+ const bucket = partiallyFullBuckets[bucketId];
1382
+ if (bucket) {
1383
+ partiallyFullBucketArray.push(bucket);
1384
+ }
1385
+ }
1386
+ }
1387
+
1388
+ return {
1389
+ 'fullBuckets': fullBuckets,
1390
+ 'partiallyFullBuckets': partiallyFullBucketArray,
1391
+ };
1392
+ }
1393
+
1394
+ }