@threepipe/plugin-gaussian-splatting 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{worker-BYCkgQO3.js.map → worker-CZa3bSRr.js.map} +1 -1
- package/dist/gaussian-splats-3d/GaussianSplatsPlugin.d.ts +1 -0
- package/dist/gaussian-splats-3d/GaussianSplatsPlugin.d.ts.map +1 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/index.mjs.map +1 -1
- package/dist/three-gaussian-splat/ThreeGaussianSplatPlugin.d.ts +9 -9
- package/dist/three-gaussian-splat/ThreeGaussianSplatPlugin.d.ts.map +1 -1
- package/dist/three-gaussian-splat/cpp-sorter/SortWorkerManager.d.ts +1 -1
- package/dist/three-gaussian-splat/cpp-sorter/worker.d.ts +1 -2
- package/dist/three-gaussian-splat/cpp-sorter/worker.d.ts.map +1 -1
- package/dist/three-gaussian-splat/geometry/GaussianSplatGeometry.d.ts +6 -5
- package/dist/three-gaussian-splat/geometry/GaussianSplatGeometry.d.ts.map +1 -1
- package/package.json +13 -2
- package/src/three-gaussian-splat/geometry/GaussianSplatGeometry.ts +1 -0
- package/src/gaussian-splats-3d/Constants.js +0 -10
- package/src/gaussian-splats-3d/Util.js +0 -147
- package/src/gaussian-splats-3d/loaders/KSplatLoader.js +0 -238
- package/src/gaussian-splats-3d/loaders/LoaderStatus.js +0 -6
- package/src/gaussian-splats-3d/loaders/PlyLoader.js +0 -32
- package/src/gaussian-splats-3d/loaders/PlyParser.js +0 -188
- package/src/gaussian-splats-3d/loaders/SceneFormat.js +0 -5
- package/src/gaussian-splats-3d/loaders/SplatBuffer.js +0 -817
- package/src/gaussian-splats-3d/loaders/SplatBufferGenerator.js +0 -31
- package/src/gaussian-splats-3d/loaders/SplatLoader.js +0 -129
- package/src/gaussian-splats-3d/loaders/SplatParser.js +0 -88
- package/src/gaussian-splats-3d/loaders/SplatPartitioner.js +0 -99
- package/src/gaussian-splats-3d/loaders/UncompressedSplatArray.js +0 -49
- package/src/gaussian-splats-3d/loaders/Utils.js +0 -8
- package/src/gaussian-splats-3d/loaders-ts/SplatLoader.ts +0 -33
|
@@ -1,817 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'threepipe';
|
|
2
|
-
import {UncompressedSplatArray} from './UncompressedSplatArray.js';
|
|
3
|
-
import {clamp} from '../Util.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* SplatBuffer: Container for splat data from a single scene/file and capable of (mediocre) compression.
|
|
7
|
-
*/
|
|
8
|
-
export class SplatBuffer {
|
|
9
|
-
|
|
10
|
-
static CenterComponentCount = 3;
|
|
11
|
-
static ScaleComponentCount = 3;
|
|
12
|
-
static RotationComponentCount = 4;
|
|
13
|
-
static ColorComponentCount = 4;
|
|
14
|
-
static CovarianceComponentCount = 6;
|
|
15
|
-
|
|
16
|
-
static SplatScaleOffsetFloat = 3;
|
|
17
|
-
static SplatRotationOffsetFloat = 6;
|
|
18
|
-
|
|
19
|
-
static CompressionLevels = {
|
|
20
|
-
0: {
|
|
21
|
-
BytesPerCenter: 12,
|
|
22
|
-
BytesPerColor: 4,
|
|
23
|
-
BytesPerScale: 12,
|
|
24
|
-
BytesPerRotation: 16,
|
|
25
|
-
BytesPerSplat: 44,
|
|
26
|
-
ScaleRange: 1,
|
|
27
|
-
},
|
|
28
|
-
1: {
|
|
29
|
-
BytesPerCenter: 6,
|
|
30
|
-
BytesPerColor: 4,
|
|
31
|
-
BytesPerScale: 6,
|
|
32
|
-
BytesPerRotation: 8,
|
|
33
|
-
BytesPerSplat: 24,
|
|
34
|
-
ScaleRange: 32767,
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
static CovarianceSizeFloats = 6;
|
|
39
|
-
|
|
40
|
-
static HeaderSizeBytes = 4096;
|
|
41
|
-
static SectionHeaderSizeBytes = 1024;
|
|
42
|
-
|
|
43
|
-
static BucketStorageSizeBytes = 12;
|
|
44
|
-
static BucketStorageSizeFloats = 3;
|
|
45
|
-
|
|
46
|
-
static BucketBlockSize = 5.0;
|
|
47
|
-
static BucketSize = 256;
|
|
48
|
-
|
|
49
|
-
constructor (bufferData, secLoadedCountsToMax = true) {
|
|
50
|
-
this.constructFromBuffer(bufferData, secLoadedCountsToMax);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
fbf (f) {
|
|
54
|
-
if (this.compressionLevel === 0) {
|
|
55
|
-
return f;
|
|
56
|
-
} else {
|
|
57
|
-
return THREE.DataUtils.fromHalfFloat(f);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
getSplatCount () {
|
|
62
|
-
return this.splatCount;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
getMaxSplatCount () {
|
|
66
|
-
return this.maxSplatCount;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getBucketIndex (section, localSplatIndex) {
|
|
70
|
-
let bucketIndex;
|
|
71
|
-
const maxSplatIndexInFullBuckets = section.fullBucketCount * section.bucketSize;
|
|
72
|
-
if (localSplatIndex < maxSplatIndexInFullBuckets) {
|
|
73
|
-
bucketIndex = Math.floor(localSplatIndex / section.bucketSize);
|
|
74
|
-
} else {
|
|
75
|
-
let bucketSplatIndex = maxSplatIndexInFullBuckets;
|
|
76
|
-
bucketIndex = section.fullBucketCount;
|
|
77
|
-
let partiallyFullBucketIndex = 0;
|
|
78
|
-
while (bucketSplatIndex < section.splatCount) {
|
|
79
|
-
let currentPartiallyFilledBucketSize = section.partiallyFilledBucketLengths[partiallyFullBucketIndex];
|
|
80
|
-
if (localSplatIndex >= bucketSplatIndex && localSplatIndex < bucketSplatIndex + currentPartiallyFilledBucketSize) {
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
bucketSplatIndex += currentPartiallyFilledBucketSize;
|
|
84
|
-
bucketIndex++;
|
|
85
|
-
partiallyFullBucketIndex++;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return bucketIndex;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
getSplatCenter (globalSplatIndex, outCenter, transform) {
|
|
92
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
|
|
93
|
-
const section = this.sections[sectionIndex];
|
|
94
|
-
const localSplatIndex = globalSplatIndex - section.splatCountOffset;
|
|
95
|
-
if (this.compressionLevel === 1) {
|
|
96
|
-
const centerBase = localSplatIndex * this.uint16PerSplat;
|
|
97
|
-
const bucketIndex = this.getBucketIndex(section, localSplatIndex);
|
|
98
|
-
const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
|
|
99
|
-
const sf = section.compressionScaleFactor;
|
|
100
|
-
const sr = section.compressionScaleRange;
|
|
101
|
-
outCenter.x = (section.dataArrayUint16[centerBase] - sr) * sf + section.bucketArray[bucketBase];
|
|
102
|
-
outCenter.y = (section.dataArrayUint16[centerBase + 1] - sr) * sf + section.bucketArray[bucketBase + 1];
|
|
103
|
-
outCenter.z = (section.dataArrayUint16[centerBase + 2] - sr) * sf + section.bucketArray[bucketBase + 2];
|
|
104
|
-
} else {
|
|
105
|
-
const centerBase = localSplatIndex * this.float32PerSplat;
|
|
106
|
-
outCenter.x = section.dataArrayFloat32[centerBase];
|
|
107
|
-
outCenter.y = section.dataArrayFloat32[centerBase + 1];
|
|
108
|
-
outCenter.z = section.dataArrayFloat32[centerBase + 2];
|
|
109
|
-
}
|
|
110
|
-
if (transform) outCenter.applyMatrix4(transform);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getSplatScaleAndRotation = function () {
|
|
114
|
-
|
|
115
|
-
const scaleMatrix = new THREE.Matrix4();
|
|
116
|
-
const rotationMatrix = new THREE.Matrix4();
|
|
117
|
-
const tempMatrix = new THREE.Matrix4();
|
|
118
|
-
const tempPosition = new THREE.Vector3();
|
|
119
|
-
|
|
120
|
-
return function (index, outScale, outRotation, transform) {
|
|
121
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[index];
|
|
122
|
-
const section = this.sections[sectionIndex];
|
|
123
|
-
const localSplatIndex = index - section.splatCountOffset;
|
|
124
|
-
|
|
125
|
-
const floatsPerSplat = this.compressionLevel === 1 ? this.uint16PerSplat : this.float32PerSplat;
|
|
126
|
-
|
|
127
|
-
const sectionFloatArray = this.compressionLevel === 1 ? section.dataArrayUint16 : section.dataArrayFloat32;
|
|
128
|
-
const splatFloatBase = floatsPerSplat * localSplatIndex;
|
|
129
|
-
|
|
130
|
-
const scaleBase = splatFloatBase + SplatBuffer.SplatScaleOffsetFloat;
|
|
131
|
-
outScale.set(this.fbf(sectionFloatArray[scaleBase]),
|
|
132
|
-
this.fbf(sectionFloatArray[scaleBase + 1]),
|
|
133
|
-
this.fbf(sectionFloatArray[scaleBase + 2]));
|
|
134
|
-
|
|
135
|
-
const rotationBase = splatFloatBase + SplatBuffer.SplatRotationOffsetFloat;
|
|
136
|
-
outRotation.set(this.fbf(sectionFloatArray[rotationBase + 1]),
|
|
137
|
-
this.fbf(sectionFloatArray[rotationBase + 2]),
|
|
138
|
-
this.fbf(sectionFloatArray[rotationBase + 3]),
|
|
139
|
-
this.fbf(sectionFloatArray[rotationBase]));
|
|
140
|
-
|
|
141
|
-
if (transform) {
|
|
142
|
-
scaleMatrix.makeScale(outScale.x, outScale.y, outScale.z);
|
|
143
|
-
rotationMatrix.makeRotationFromQuaternion(outRotation);
|
|
144
|
-
tempMatrix.copy(scaleMatrix).multiply(rotationMatrix).multiply(transform);
|
|
145
|
-
tempMatrix.decompose(tempPosition, outRotation, outScale);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
}();
|
|
150
|
-
|
|
151
|
-
getSplatColor (globalSplatIndex, outColor, transform) {
|
|
152
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
|
|
153
|
-
const section = this.sections[sectionIndex];
|
|
154
|
-
const localSplatIndex = globalSplatIndex - section.splatCountOffset;
|
|
155
|
-
const colorBase = this.bytesPerSplat * localSplatIndex + this.bytesPerCenter + this.bytesPerScale + this.bytesPerRotation;
|
|
156
|
-
outColor.set(section.dataArrayUint8[colorBase], section.dataArrayUint8[colorBase + 1],
|
|
157
|
-
section.dataArrayUint8[colorBase + 2], section.dataArrayUint8[colorBase + 3]);
|
|
158
|
-
// TODO: apply transform for spherical harmonics
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
fillSplatCenterArray (outCenterArray, transform, srcFrom, srcTo, destFrom) {
|
|
162
|
-
const splatCount = this.splatCount;
|
|
163
|
-
|
|
164
|
-
srcFrom = srcFrom || 0;
|
|
165
|
-
srcTo = srcTo || splatCount - 1;
|
|
166
|
-
if (destFrom === undefined) destFrom = srcFrom;
|
|
167
|
-
|
|
168
|
-
const center = new THREE.Vector3();
|
|
169
|
-
for (let i = srcFrom; i <= srcTo; i++) {
|
|
170
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[i];
|
|
171
|
-
const section = this.sections[sectionIndex];
|
|
172
|
-
const localSplatIndex = i - section.splatCountOffset;
|
|
173
|
-
const centerDestBase = (i - srcFrom + destFrom) * SplatBuffer.CenterComponentCount;
|
|
174
|
-
if (this.compressionLevel === 1) {
|
|
175
|
-
const centerBase = localSplatIndex * this.uint16PerSplat;
|
|
176
|
-
const bucketIndex = this.getBucketIndex(section, localSplatIndex);
|
|
177
|
-
const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
|
|
178
|
-
const sf = section.compressionScaleFactor;
|
|
179
|
-
const sr = section.compressionScaleRange;
|
|
180
|
-
center.x = (section.dataArrayUint16[centerBase] - sr) * sf + section.bucketArray[bucketBase];
|
|
181
|
-
center.y = (section.dataArrayUint16[centerBase + 1] - sr) * sf + section.bucketArray[bucketBase + 1];
|
|
182
|
-
center.z = (section.dataArrayUint16[centerBase + 2] - sr) * sf + section.bucketArray[bucketBase + 2];
|
|
183
|
-
} else {
|
|
184
|
-
const centerBase = localSplatIndex * this.float32PerSplat;
|
|
185
|
-
center.x = section.dataArrayFloat32[centerBase];
|
|
186
|
-
center.y = section.dataArrayFloat32[centerBase + 1];
|
|
187
|
-
center.z = section.dataArrayFloat32[centerBase + 2];
|
|
188
|
-
}
|
|
189
|
-
if (transform) {
|
|
190
|
-
center.applyMatrix4(transform);
|
|
191
|
-
}
|
|
192
|
-
outCenterArray[centerDestBase] = center.x;
|
|
193
|
-
outCenterArray[centerDestBase + 1] = center.y;
|
|
194
|
-
outCenterArray[centerDestBase + 2] = center.z;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
static computeCovariance = function () {
|
|
199
|
-
|
|
200
|
-
const tempMatrix4 = new THREE.Matrix4();
|
|
201
|
-
const scaleMatrix = new THREE.Matrix3();
|
|
202
|
-
const rotationMatrix = new THREE.Matrix3();
|
|
203
|
-
const covarianceMatrix = new THREE.Matrix3();
|
|
204
|
-
const transformedCovariance = new THREE.Matrix3();
|
|
205
|
-
const transform3x3 = new THREE.Matrix3();
|
|
206
|
-
const transform3x3Transpose = new THREE.Matrix3();
|
|
207
|
-
const thf = THREE.DataUtils.toHalfFloat.bind(THREE.DataUtils);
|
|
208
|
-
|
|
209
|
-
return function (scale, rotation, transform, outCovariance, outOffset = 0, desiredOutputCompressionLevel) {
|
|
210
|
-
|
|
211
|
-
tempMatrix4.makeScale(scale.x, scale.y, scale.z);
|
|
212
|
-
scaleMatrix.setFromMatrix4(tempMatrix4);
|
|
213
|
-
|
|
214
|
-
tempMatrix4.makeRotationFromQuaternion(rotation);
|
|
215
|
-
rotationMatrix.setFromMatrix4(tempMatrix4);
|
|
216
|
-
|
|
217
|
-
covarianceMatrix.copy(rotationMatrix).multiply(scaleMatrix);
|
|
218
|
-
transformedCovariance.copy(covarianceMatrix).transpose().premultiply(covarianceMatrix);
|
|
219
|
-
|
|
220
|
-
if (transform) {
|
|
221
|
-
transform3x3.setFromMatrix4(transform);
|
|
222
|
-
transform3x3Transpose.copy(transform3x3).transpose();
|
|
223
|
-
transformedCovariance.multiply(transform3x3Transpose);
|
|
224
|
-
transformedCovariance.premultiply(transform3x3);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (desiredOutputCompressionLevel === 1) {
|
|
228
|
-
outCovariance[outOffset] = thf(transformedCovariance.elements[0]);
|
|
229
|
-
outCovariance[outOffset + 1] = thf(transformedCovariance.elements[3]);
|
|
230
|
-
outCovariance[outOffset + 2] = thf(transformedCovariance.elements[6]);
|
|
231
|
-
outCovariance[outOffset + 3] = thf(transformedCovariance.elements[4]);
|
|
232
|
-
outCovariance[outOffset + 4] = thf(transformedCovariance.elements[7]);
|
|
233
|
-
outCovariance[outOffset + 5] = thf(transformedCovariance.elements[8]);
|
|
234
|
-
} else {
|
|
235
|
-
outCovariance[outOffset] = transformedCovariance.elements[0];
|
|
236
|
-
outCovariance[outOffset + 1] = transformedCovariance.elements[3];
|
|
237
|
-
outCovariance[outOffset + 2] = transformedCovariance.elements[6];
|
|
238
|
-
outCovariance[outOffset + 3] = transformedCovariance.elements[4];
|
|
239
|
-
outCovariance[outOffset + 4] = transformedCovariance.elements[7];
|
|
240
|
-
outCovariance[outOffset + 5] = transformedCovariance.elements[8];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
}();
|
|
246
|
-
|
|
247
|
-
fillSplatCovarianceArray (covarianceArray, transform, srcFrom, srcTo, destFrom, desiredOutputCompressionLevel) {
|
|
248
|
-
const splatCount = this.splatCount;
|
|
249
|
-
|
|
250
|
-
const scale = new THREE.Vector3();
|
|
251
|
-
const rotation = new THREE.Quaternion();
|
|
252
|
-
|
|
253
|
-
srcFrom = srcFrom || 0;
|
|
254
|
-
srcTo = srcTo || splatCount - 1;
|
|
255
|
-
if (destFrom === undefined) destFrom = srcFrom;
|
|
256
|
-
const floatsPerSplat = this.compressionLevel === 1 ? this.uint16PerSplat : this.float32PerSplat;
|
|
257
|
-
|
|
258
|
-
for (let i = srcFrom; i <= srcTo; i++) {
|
|
259
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[i];
|
|
260
|
-
const section = this.sections[sectionIndex];
|
|
261
|
-
const localSplatIndex = i - section.splatCountOffset;
|
|
262
|
-
|
|
263
|
-
const sectionFloatArray = this.compressionLevel === 1 ? section.dataArrayUint16 : section.dataArrayFloat32;
|
|
264
|
-
const splatFloatBase = floatsPerSplat * localSplatIndex;
|
|
265
|
-
const covarianceDestBase = (i - srcFrom + destFrom) * SplatBuffer.CovarianceComponentCount;
|
|
266
|
-
|
|
267
|
-
const scaleBase = splatFloatBase + SplatBuffer.SplatScaleOffsetFloat;
|
|
268
|
-
scale.set(this.fbf(sectionFloatArray[scaleBase]),
|
|
269
|
-
this.fbf(sectionFloatArray[scaleBase + 1]),
|
|
270
|
-
this.fbf(sectionFloatArray[scaleBase + 2]));
|
|
271
|
-
|
|
272
|
-
const rotationBase = splatFloatBase + SplatBuffer.SplatRotationOffsetFloat;
|
|
273
|
-
rotation.set(this.fbf(sectionFloatArray[rotationBase + 1]),
|
|
274
|
-
this.fbf(sectionFloatArray[rotationBase + 2]),
|
|
275
|
-
this.fbf(sectionFloatArray[rotationBase + 3]),
|
|
276
|
-
this.fbf(sectionFloatArray[rotationBase]));
|
|
277
|
-
|
|
278
|
-
SplatBuffer.computeCovariance(scale, rotation, transform, covarianceArray, covarianceDestBase, desiredOutputCompressionLevel);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
fillSplatColorArray (outColorArray, minimumAlpha, transform, srcFrom, srcTo, destFrom) {
|
|
283
|
-
const splatCount = this.splatCount;
|
|
284
|
-
|
|
285
|
-
srcFrom = srcFrom || 0;
|
|
286
|
-
srcTo = srcTo || splatCount - 1;
|
|
287
|
-
if (destFrom === undefined) destFrom = srcFrom;
|
|
288
|
-
const splatColorOffset = this.bytesPerCenter + this.bytesPerScale + this.bytesPerRotation;
|
|
289
|
-
|
|
290
|
-
for (let i = srcFrom; i <= srcTo; i++) {
|
|
291
|
-
|
|
292
|
-
const sectionIndex = this.globalSplatIndexToSectionMap[i];
|
|
293
|
-
const section = this.sections[sectionIndex];
|
|
294
|
-
const localSplatIndex = i - section.splatCountOffset;
|
|
295
|
-
|
|
296
|
-
const colorSrcBase = this.bytesPerSplat * localSplatIndex + splatColorOffset;
|
|
297
|
-
const colorDestBase = (i - srcFrom + destFrom) * SplatBuffer.ColorComponentCount;
|
|
298
|
-
|
|
299
|
-
let alpha = section.dataArrayUint8[colorSrcBase + 3];
|
|
300
|
-
alpha = (alpha >= minimumAlpha) ? alpha : 0;
|
|
301
|
-
|
|
302
|
-
outColorArray[colorDestBase] = section.dataArrayUint8[colorSrcBase];
|
|
303
|
-
outColorArray[colorDestBase + 1] = section.dataArrayUint8[colorSrcBase + 1];
|
|
304
|
-
outColorArray[colorDestBase + 2] = section.dataArrayUint8[colorSrcBase + 2];
|
|
305
|
-
outColorArray[colorDestBase + 3] = alpha;
|
|
306
|
-
|
|
307
|
-
// TODO: implement application of transform for spherical harmonics
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
static parseHeader (buffer) {
|
|
312
|
-
const headerArrayUint8 = new Uint8Array(buffer, 0, SplatBuffer.HeaderSizeBytes);
|
|
313
|
-
const headerArrayUint16 = new Uint16Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 2);
|
|
314
|
-
const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
|
|
315
|
-
const headerArrayFloat32 = new Float32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
|
|
316
|
-
const versionMajor = headerArrayUint8[0];
|
|
317
|
-
const versionMinor = headerArrayUint8[1];
|
|
318
|
-
const maxSectionCount = headerArrayUint32[1];
|
|
319
|
-
const sectionCount = headerArrayUint32[2];
|
|
320
|
-
const maxSplatCount = headerArrayUint32[3];
|
|
321
|
-
const splatCount = headerArrayUint32[4];
|
|
322
|
-
const compressionLevel = headerArrayUint16[10];
|
|
323
|
-
const sceneCenter = new THREE.Vector3(headerArrayFloat32[6], headerArrayFloat32[7], headerArrayFloat32[8]);
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
versionMajor,
|
|
327
|
-
versionMinor,
|
|
328
|
-
maxSectionCount,
|
|
329
|
-
sectionCount,
|
|
330
|
-
maxSplatCount,
|
|
331
|
-
splatCount,
|
|
332
|
-
compressionLevel,
|
|
333
|
-
sceneCenter,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
static writeHeaderCountsToBuffer (sectionCount, splatCount, buffer) {
|
|
338
|
-
const headerArrayUint32 = new Uint32Array(buffer);
|
|
339
|
-
headerArrayUint32[2] = sectionCount;
|
|
340
|
-
headerArrayUint32[4] = splatCount;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
static writeHeaderToBuffer (header, buffer) {
|
|
344
|
-
const headerArrayUint8 = new Uint8Array(buffer);
|
|
345
|
-
const headerArrayUint32 = new Uint32Array(buffer);
|
|
346
|
-
const headerArrayFloat32 = new Float32Array(buffer);
|
|
347
|
-
const headerArrayUint16 = new Uint16Array(buffer);
|
|
348
|
-
headerArrayUint8[0] = header.versionMajor;
|
|
349
|
-
headerArrayUint8[1] = header.versionMinor;
|
|
350
|
-
headerArrayUint8[2] = 0; // unused for now
|
|
351
|
-
headerArrayUint8[3] = 0; // unused for now
|
|
352
|
-
headerArrayUint32[1] = header.maxSectionCount;
|
|
353
|
-
headerArrayUint32[2] = header.sectionCount;
|
|
354
|
-
headerArrayUint32[3] = header.maxSplatCount;
|
|
355
|
-
headerArrayUint32[4] = header.splatCount;
|
|
356
|
-
headerArrayUint16[10] = header.compressionLevel;
|
|
357
|
-
headerArrayFloat32[6] = header.sceneCenter.x;
|
|
358
|
-
headerArrayFloat32[7] = header.sceneCenter.y;
|
|
359
|
-
headerArrayFloat32[8] = header.sceneCenter.z;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
static parseSectionHeaders (header, buffer, offset = 0, secLoadedCountsToMax) {
|
|
363
|
-
const compressionLevel = header.compressionLevel;
|
|
364
|
-
const bytesPerCenter = SplatBuffer.CompressionLevels[compressionLevel].BytesPerCenter;
|
|
365
|
-
const bytesPerColor = SplatBuffer.CompressionLevels[compressionLevel].BytesPerColor;
|
|
366
|
-
const bytesPerScale = SplatBuffer.CompressionLevels[compressionLevel].BytesPerScale;
|
|
367
|
-
const bytesPerRotation = SplatBuffer.CompressionLevels[compressionLevel].BytesPerRotation;
|
|
368
|
-
|
|
369
|
-
const maxSectionCount = header.maxSectionCount;
|
|
370
|
-
const sectionHeaderArrayUint16 = new Uint16Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 2);
|
|
371
|
-
const sectionHeaderArrayUint32 = new Uint32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
|
|
372
|
-
const sectionHeaderArrayFloat32 = new Float32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
|
|
373
|
-
|
|
374
|
-
const sectionHeaders = [];
|
|
375
|
-
let sectionHeaderBase = 0;
|
|
376
|
-
let sectionHeaderBaseUint16 = sectionHeaderBase / 2;
|
|
377
|
-
let sectionHeaderBaseUint32 = sectionHeaderBase / 4;
|
|
378
|
-
let sectionBase = SplatBuffer.HeaderSizeBytes + header.maxSectionCount * SplatBuffer.SectionHeaderSizeBytes;
|
|
379
|
-
let splatCountOffset = 0;
|
|
380
|
-
for (let i = 0; i < maxSectionCount; i++) {
|
|
381
|
-
const maxSplatCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 1];
|
|
382
|
-
const bucketSize = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 2];
|
|
383
|
-
const bucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 3];
|
|
384
|
-
const bucketBlockSize = sectionHeaderArrayFloat32[sectionHeaderBaseUint32 + 4];
|
|
385
|
-
const halfBucketBlockSize = bucketBlockSize / 2.0;
|
|
386
|
-
const bucketStorageSizeBytes = sectionHeaderArrayUint16[sectionHeaderBaseUint16 + 10];
|
|
387
|
-
const compressionScaleRange = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 6] ||
|
|
388
|
-
SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
|
|
389
|
-
const fullBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 8];
|
|
390
|
-
const partiallyFilledBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 9];
|
|
391
|
-
const bucketsMetaDataSizeBytes = partiallyFilledBucketCount * 4;
|
|
392
|
-
const bucketsStorageSizeBytes = bucketStorageSizeBytes * bucketCount + bucketsMetaDataSizeBytes;
|
|
393
|
-
|
|
394
|
-
const splatDataStorageSizeBytes = (bytesPerCenter + bytesPerScale + bytesPerRotation + bytesPerColor) * maxSplatCount;
|
|
395
|
-
const storageSizeBytes = splatDataStorageSizeBytes + bucketsStorageSizeBytes;
|
|
396
|
-
const sectionHeader = {
|
|
397
|
-
splatCountOffset: splatCountOffset,
|
|
398
|
-
splatCount: secLoadedCountsToMax ? maxSplatCount : 0,
|
|
399
|
-
maxSplatCount: maxSplatCount,
|
|
400
|
-
bucketSize: bucketSize,
|
|
401
|
-
bucketCount: bucketCount,
|
|
402
|
-
bucketBlockSize: bucketBlockSize,
|
|
403
|
-
halfBucketBlockSize: halfBucketBlockSize,
|
|
404
|
-
bucketStorageSizeBytes: bucketStorageSizeBytes,
|
|
405
|
-
bucketsStorageSizeBytes: bucketsStorageSizeBytes,
|
|
406
|
-
splatDataStorageSizeBytes: splatDataStorageSizeBytes,
|
|
407
|
-
storageSizeBytes: storageSizeBytes,
|
|
408
|
-
compressionScaleRange: compressionScaleRange,
|
|
409
|
-
compressionScaleFactor: halfBucketBlockSize / compressionScaleRange,
|
|
410
|
-
base: sectionBase,
|
|
411
|
-
bucketsBase: sectionBase + bucketsMetaDataSizeBytes,
|
|
412
|
-
dataBase: sectionBase + bucketsStorageSizeBytes,
|
|
413
|
-
fullBucketCount: fullBucketCount,
|
|
414
|
-
partiallyFilledBucketCount: partiallyFilledBucketCount,
|
|
415
|
-
};
|
|
416
|
-
sectionHeaders[i] = sectionHeader;
|
|
417
|
-
sectionBase += storageSizeBytes;
|
|
418
|
-
sectionHeaderBase += SplatBuffer.SectionHeaderSizeBytes;
|
|
419
|
-
sectionHeaderBaseUint16 = sectionHeaderBase / 2;
|
|
420
|
-
sectionHeaderBaseUint32 = sectionHeaderBase / 4;
|
|
421
|
-
splatCountOffset += maxSplatCount;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return sectionHeaders;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
static writeSectionHeaderToBuffer (sectionHeader, compressionLevel, buffer, offset = 0) {
|
|
429
|
-
const sectionHeadeArrayUint16 = new Uint16Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 2);
|
|
430
|
-
const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
|
|
431
|
-
const sectionHeadeArrayFloat32 = new Float32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
|
|
432
|
-
|
|
433
|
-
sectionHeadeArrayUint32[0] = sectionHeader.splatCount;
|
|
434
|
-
sectionHeadeArrayUint32[1] = sectionHeader.maxSplatCount;
|
|
435
|
-
sectionHeadeArrayUint32[2] = compressionLevel === 1 ? sectionHeader.bucketSize : 0;
|
|
436
|
-
sectionHeadeArrayUint32[3] = compressionLevel === 1 ? sectionHeader.bucketCount : 0;
|
|
437
|
-
sectionHeadeArrayFloat32[4] = compressionLevel === 1 ? sectionHeader.bucketBlockSize : 0.0;
|
|
438
|
-
sectionHeadeArrayUint16[10] = compressionLevel === 1 ? SplatBuffer.BucketStorageSizeBytes : 0;
|
|
439
|
-
sectionHeadeArrayUint32[6] = compressionLevel === 1 ? sectionHeader.compressionScaleRange : 0;
|
|
440
|
-
sectionHeadeArrayUint32[7] = sectionHeader.storageSizeBytes;
|
|
441
|
-
sectionHeadeArrayUint32[8] = compressionLevel === 1 ? sectionHeader.fullBucketCount : 0;
|
|
442
|
-
sectionHeadeArrayUint32[9] = compressionLevel === 1 ? sectionHeader.partiallyFilledBucketCount : 0;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
static writeSectionHeaderSplatCountToBuffer (splatCount, buffer, offset = 0) {
|
|
446
|
-
const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
|
|
447
|
-
sectionHeadeArrayUint32[0] = splatCount;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
constructFromBuffer (bufferData, secLoadedCountsToMax) {
|
|
451
|
-
this.bufferData = bufferData;
|
|
452
|
-
|
|
453
|
-
this.globalSplatIndexToLocalSplatIndexMap = [];
|
|
454
|
-
this.globalSplatIndexToSectionMap = [];
|
|
455
|
-
|
|
456
|
-
const header = SplatBuffer.parseHeader(this.bufferData);
|
|
457
|
-
this.versionMajor = header.versionMajor;
|
|
458
|
-
this.versionMinor = header.versionMinor;
|
|
459
|
-
this.maxSectionCount = header.maxSectionCount;
|
|
460
|
-
this.sectionCount = secLoadedCountsToMax ? header.maxSectionCount : 0;
|
|
461
|
-
this.maxSplatCount = header.maxSplatCount;
|
|
462
|
-
this.splatCount = secLoadedCountsToMax ? header.maxSplatCount : 0;
|
|
463
|
-
this.compressionLevel = header.compressionLevel;
|
|
464
|
-
this.sceneCenter = new THREE.Vector3().copy(header.sceneCenter);
|
|
465
|
-
|
|
466
|
-
this.bytesPerCenter = SplatBuffer.CompressionLevels[this.compressionLevel].BytesPerCenter;
|
|
467
|
-
this.bytesPerScale = SplatBuffer.CompressionLevels[this.compressionLevel].BytesPerScale;
|
|
468
|
-
this.bytesPerRotation = SplatBuffer.CompressionLevels[this.compressionLevel].BytesPerRotation;
|
|
469
|
-
this.bytesPerColor = SplatBuffer.CompressionLevels[this.compressionLevel].BytesPerColor;
|
|
470
|
-
this.bytesPerSplat = this.bytesPerCenter + this.bytesPerScale + this.bytesPerRotation + this.bytesPerColor;
|
|
471
|
-
|
|
472
|
-
this.float32PerSplat = this.bytesPerSplat / 4;
|
|
473
|
-
this.uint32PerSplat = this.bytesPerSplat / 4;
|
|
474
|
-
this.uint16PerSplat = this.bytesPerSplat / 2;
|
|
475
|
-
|
|
476
|
-
this.sections = SplatBuffer.parseSectionHeaders(header, this.bufferData, SplatBuffer.HeaderSizeBytes, secLoadedCountsToMax);
|
|
477
|
-
|
|
478
|
-
this.linkBufferArrays();
|
|
479
|
-
this.buildMaps();
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
linkBufferArrays () {
|
|
484
|
-
for (let i = 0; i < this.maxSectionCount; i++) {
|
|
485
|
-
const section = this.sections[i];
|
|
486
|
-
section.dataArrayUint8 = new Uint8Array(this.bufferData, section.dataBase, section.maxSplatCount * this.bytesPerSplat);
|
|
487
|
-
section.dataArrayUint16 = new Uint16Array(this.bufferData, section.dataBase, section.maxSplatCount * this.uint16PerSplat);
|
|
488
|
-
section.dataArrayUint32 = new Uint32Array(this.bufferData, section.dataBase, section.maxSplatCount * this.uint32PerSplat);
|
|
489
|
-
section.dataArrayFloat32 = new Float32Array(this.bufferData, section.dataBase, section.maxSplatCount * this.float32PerSplat);
|
|
490
|
-
section.bucketArray = new Float32Array(this.bufferData, section.bucketsBase,
|
|
491
|
-
section.bucketCount * SplatBuffer.BucketStorageSizeFloats);
|
|
492
|
-
if (section.partiallyFilledBucketCount > 0) {
|
|
493
|
-
section.partiallyFilledBucketLengths = new Uint32Array(this.bufferData, section.base,
|
|
494
|
-
section.partiallyFilledBucketCount);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
buildMaps () {
|
|
500
|
-
let cumulativeSplatCount = 0;
|
|
501
|
-
for (let i = 0; i < this.maxSectionCount; i++) {
|
|
502
|
-
const section = this.sections[i];
|
|
503
|
-
for (let j = 0; j < section.maxSplatCount; j++) {
|
|
504
|
-
const globalSplatIndex = cumulativeSplatCount + j;
|
|
505
|
-
this.globalSplatIndexToLocalSplatIndexMap[globalSplatIndex] = j;
|
|
506
|
-
this.globalSplatIndexToSectionMap[globalSplatIndex] = i;
|
|
507
|
-
}
|
|
508
|
-
cumulativeSplatCount += section.maxSplatCount;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
updateLoadedCounts (newSectionCount, newSplatCount) {
|
|
513
|
-
SplatBuffer.writeHeaderCountsToBuffer(newSectionCount, newSplatCount, this.bufferData);
|
|
514
|
-
this.sectionCount = newSectionCount;
|
|
515
|
-
this.splatCount = newSplatCount;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
updateSectionLoadedCounts (sectionIndex, newSplatCount) {
|
|
519
|
-
const sectionHeaderOffset = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * sectionIndex;
|
|
520
|
-
SplatBuffer.writeSectionHeaderSplatCountToBuffer(newSplatCount, this.bufferData, sectionHeaderOffset);
|
|
521
|
-
this.sections[sectionIndex].splatCount = newSplatCount;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
static generateFromUncompressedSplatArrays (splatArrays, minimumAlpha, compressionLevel,
|
|
525
|
-
sceneCenter, blockSize, bucketSize, options = []) {
|
|
526
|
-
|
|
527
|
-
const bytesPerCenter = SplatBuffer.CompressionLevels[compressionLevel].BytesPerCenter;
|
|
528
|
-
const bytesPerScale = SplatBuffer.CompressionLevels[compressionLevel].BytesPerScale;
|
|
529
|
-
const bytesPerRotation = SplatBuffer.CompressionLevels[compressionLevel].BytesPerRotation;
|
|
530
|
-
const bytesPerColor = SplatBuffer.CompressionLevels[compressionLevel].BytesPerColor;
|
|
531
|
-
const bytesPerSplat = bytesPerCenter + bytesPerScale + bytesPerRotation + bytesPerColor;
|
|
532
|
-
const compressionScaleRange = SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
|
|
533
|
-
|
|
534
|
-
const sectionBuffers = [];
|
|
535
|
-
const sectionHeaderBuffers = [];
|
|
536
|
-
let totalSplatCount = 0;
|
|
537
|
-
|
|
538
|
-
const tempRotation = new THREE.Quaternion();
|
|
539
|
-
const thf = THREE.DataUtils.toHalfFloat.bind(THREE.DataUtils);
|
|
540
|
-
|
|
541
|
-
for (let sa = 0; sa < splatArrays.length; sa++) {
|
|
542
|
-
const splatArray = splatArrays[sa];
|
|
543
|
-
|
|
544
|
-
const sectionOptions = options[sa] || {};
|
|
545
|
-
|
|
546
|
-
const sectionBlockSize = (sectionOptions.blockSizeFactor || 1) * (blockSize || SplatBuffer.BucketBlockSize);
|
|
547
|
-
const sectionBucketSize = Math.ceil((sectionOptions.bucketSizeFactor || 1) * (bucketSize || SplatBuffer.BucketSize));
|
|
548
|
-
|
|
549
|
-
const validSplats = new UncompressedSplatArray();
|
|
550
|
-
|
|
551
|
-
for (let i = 0; i < splatArray.splatCount; i++) {
|
|
552
|
-
const targetSplat = splatArray.splats[i];
|
|
553
|
-
let alpha;
|
|
554
|
-
if (targetSplat[UncompressedSplatArray.OFFSET.OPACITY]) {
|
|
555
|
-
alpha = targetSplat[UncompressedSplatArray.OFFSET.OPACITY];
|
|
556
|
-
} else {
|
|
557
|
-
alpha = 255;
|
|
558
|
-
}
|
|
559
|
-
if (alpha >= minimumAlpha) {
|
|
560
|
-
validSplats.addSplatFromComonents(targetSplat[UncompressedSplatArray.OFFSET.X],
|
|
561
|
-
targetSplat[UncompressedSplatArray.OFFSET.Y],
|
|
562
|
-
targetSplat[UncompressedSplatArray.OFFSET.Z],
|
|
563
|
-
targetSplat[UncompressedSplatArray.OFFSET.SCALE0],
|
|
564
|
-
targetSplat[UncompressedSplatArray.OFFSET.SCALE1],
|
|
565
|
-
targetSplat[UncompressedSplatArray.OFFSET.SCALE2],
|
|
566
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION0],
|
|
567
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION1],
|
|
568
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION2],
|
|
569
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION3],
|
|
570
|
-
targetSplat[UncompressedSplatArray.OFFSET.FDC0],
|
|
571
|
-
targetSplat[UncompressedSplatArray.OFFSET.FDC1],
|
|
572
|
-
targetSplat[UncompressedSplatArray.OFFSET.FDC2],
|
|
573
|
-
targetSplat[UncompressedSplatArray.OFFSET.OPACITY]);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const bucketInfo = SplatBuffer.computeBucketsForUncompressedSplatArray(validSplats, sectionBlockSize, sectionBucketSize);
|
|
578
|
-
const fullBucketCount = bucketInfo.fullBuckets.length;
|
|
579
|
-
const partiallyFullBucketLengths = bucketInfo.partiallyFullBuckets.map((bucket) => bucket.splats.length);
|
|
580
|
-
const partiallyFilledBucketCount = partiallyFullBucketLengths.length;
|
|
581
|
-
const buckets = [...bucketInfo.fullBuckets, ...bucketInfo.partiallyFullBuckets];
|
|
582
|
-
|
|
583
|
-
const sectionDataSizeBytes = validSplats.splats.length * bytesPerSplat;
|
|
584
|
-
const bucketMetaDataSizeBytes = partiallyFilledBucketCount * 4;
|
|
585
|
-
const bucketDataBytes = compressionLevel === 1 ? buckets.length *
|
|
586
|
-
SplatBuffer.BucketStorageSizeBytes + bucketMetaDataSizeBytes : 0;
|
|
587
|
-
const sectionSizeBytes = sectionDataSizeBytes + bucketDataBytes;
|
|
588
|
-
const sectionBuffer = new ArrayBuffer(sectionSizeBytes);
|
|
589
|
-
|
|
590
|
-
const blockHalfSize = sectionBlockSize / 2.0;
|
|
591
|
-
const compressionScaleFactor = compressionScaleRange / blockHalfSize;
|
|
592
|
-
const doubleCompressionScaleRange = compressionScaleRange * 2 + 1;
|
|
593
|
-
|
|
594
|
-
const bucketCenter = new THREE.Vector3();
|
|
595
|
-
const bucketCenterDelta = new THREE.Vector3();
|
|
596
|
-
let outSplatCount = 0;
|
|
597
|
-
for (let b = 0; b < buckets.length; b++) {
|
|
598
|
-
const bucket = buckets[b];
|
|
599
|
-
bucketCenter.fromArray(bucket.center);
|
|
600
|
-
for (let i = 0; i < bucket.splats.length; i++) {
|
|
601
|
-
let row = bucket.splats[i];
|
|
602
|
-
const targetSplat = validSplats.splats[row];
|
|
603
|
-
|
|
604
|
-
const centerBase = bucketDataBytes + outSplatCount * bytesPerSplat;
|
|
605
|
-
const scaleBase = centerBase + bytesPerCenter;
|
|
606
|
-
const rotationBase = scaleBase + bytesPerScale;
|
|
607
|
-
const colorBase = rotationBase + bytesPerRotation;
|
|
608
|
-
if (compressionLevel === 0) {
|
|
609
|
-
const center = new Float32Array(sectionBuffer, centerBase, SplatBuffer.CenterComponentCount);
|
|
610
|
-
const rot = new Float32Array(sectionBuffer, rotationBase, SplatBuffer.RotationComponentCount);
|
|
611
|
-
const scale = new Float32Array(sectionBuffer, scaleBase, SplatBuffer.ScaleComponentCount);
|
|
612
|
-
if (targetSplat[UncompressedSplatArray.OFFSET.SCALE0] !== undefined) {
|
|
613
|
-
tempRotation.set(targetSplat[UncompressedSplatArray.OFFSET.ROTATION1],
|
|
614
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION2],
|
|
615
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION3],
|
|
616
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION0]);
|
|
617
|
-
tempRotation.normalize();
|
|
618
|
-
rot.set([tempRotation.w, tempRotation.x, tempRotation.y, tempRotation.z]);
|
|
619
|
-
scale.set([targetSplat[UncompressedSplatArray.OFFSET.SCALE0],
|
|
620
|
-
targetSplat[UncompressedSplatArray.OFFSET.SCALE1],
|
|
621
|
-
targetSplat[UncompressedSplatArray.OFFSET.SCALE2]]);
|
|
622
|
-
} else {
|
|
623
|
-
rot.set([1.0, 0.0, 0.0, 0.0]);
|
|
624
|
-
scale.set([0.01, 0.01, 0.01]);
|
|
625
|
-
}
|
|
626
|
-
center.set([targetSplat[UncompressedSplatArray.OFFSET.X],
|
|
627
|
-
targetSplat[UncompressedSplatArray.OFFSET.Y],
|
|
628
|
-
targetSplat[UncompressedSplatArray.OFFSET.Z]]);
|
|
629
|
-
} else {
|
|
630
|
-
const center = new Uint16Array(sectionBuffer, centerBase, SplatBuffer.CenterComponentCount);
|
|
631
|
-
const rot = new Uint16Array(sectionBuffer, rotationBase, SplatBuffer.RotationComponentCount);
|
|
632
|
-
const scale = new Uint16Array(sectionBuffer, scaleBase, SplatBuffer.ScaleComponentCount);
|
|
633
|
-
|
|
634
|
-
if (targetSplat[UncompressedSplatArray.OFFSET.SCALE0] !== undefined) {
|
|
635
|
-
tempRotation.set(targetSplat[UncompressedSplatArray.OFFSET.ROTATION1],
|
|
636
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION2],
|
|
637
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION3],
|
|
638
|
-
targetSplat[UncompressedSplatArray.OFFSET.ROTATION0]);
|
|
639
|
-
tempRotation.normalize();
|
|
640
|
-
rot.set([thf(tempRotation.w), thf(tempRotation.x), thf(tempRotation.y), thf(tempRotation.z)]);
|
|
641
|
-
scale.set([thf(targetSplat[UncompressedSplatArray.OFFSET.SCALE0]),
|
|
642
|
-
thf(targetSplat[UncompressedSplatArray.OFFSET.SCALE1]),
|
|
643
|
-
thf(targetSplat[UncompressedSplatArray.OFFSET.SCALE2])]);
|
|
644
|
-
} else {
|
|
645
|
-
rot.set([thf(1.), 0, 0, 0]);
|
|
646
|
-
scale.set([thf(0.01), thf(0.01), thf(0.01)]);
|
|
647
|
-
}
|
|
648
|
-
bucketCenterDelta.set(targetSplat[UncompressedSplatArray.OFFSET.X],
|
|
649
|
-
targetSplat[UncompressedSplatArray.OFFSET.Y],
|
|
650
|
-
targetSplat[UncompressedSplatArray.OFFSET.Z]).sub(bucketCenter);
|
|
651
|
-
bucketCenterDelta.x = Math.round(bucketCenterDelta.x * compressionScaleFactor) + compressionScaleRange;
|
|
652
|
-
bucketCenterDelta.x = clamp(bucketCenterDelta.x, 0, doubleCompressionScaleRange);
|
|
653
|
-
bucketCenterDelta.y = Math.round(bucketCenterDelta.y * compressionScaleFactor) + compressionScaleRange;
|
|
654
|
-
bucketCenterDelta.y = clamp(bucketCenterDelta.y, 0, doubleCompressionScaleRange);
|
|
655
|
-
bucketCenterDelta.z = Math.round(bucketCenterDelta.z * compressionScaleFactor) + compressionScaleRange;
|
|
656
|
-
bucketCenterDelta.z = clamp(bucketCenterDelta.z, 0, doubleCompressionScaleRange);
|
|
657
|
-
center.set([bucketCenterDelta.x, bucketCenterDelta.y, bucketCenterDelta.z]);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
const rgba = new Uint8ClampedArray(sectionBuffer, colorBase, 4);
|
|
661
|
-
|
|
662
|
-
if (targetSplat[UncompressedSplatArray.OFFSET.FDC0] !== undefined) {
|
|
663
|
-
rgba.set([targetSplat[UncompressedSplatArray.OFFSET.FDC0],
|
|
664
|
-
targetSplat[UncompressedSplatArray.OFFSET.FDC1],
|
|
665
|
-
targetSplat[UncompressedSplatArray.OFFSET.FDC2]]);
|
|
666
|
-
} else {
|
|
667
|
-
rgba.set([255, 0, 0]);
|
|
668
|
-
}
|
|
669
|
-
if (targetSplat[UncompressedSplatArray.OFFSET.OPACITY] !== undefined) {
|
|
670
|
-
rgba[3] = targetSplat[UncompressedSplatArray.OFFSET.OPACITY];
|
|
671
|
-
} else {
|
|
672
|
-
rgba[3] = 255;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
outSplatCount++;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
totalSplatCount += outSplatCount;
|
|
679
|
-
|
|
680
|
-
if (compressionLevel === 1) {
|
|
681
|
-
const bucketMetaDataArray = new Uint32Array(sectionBuffer);
|
|
682
|
-
for (let pfb = 0; pfb < partiallyFullBucketLengths.length; pfb++) {
|
|
683
|
-
bucketMetaDataArray[pfb] = partiallyFullBucketLengths[pfb];
|
|
684
|
-
}
|
|
685
|
-
const bucketArray = new Float32Array(sectionBuffer, bucketMetaDataSizeBytes,
|
|
686
|
-
buckets.length * SplatBuffer.BucketStorageSizeFloats);
|
|
687
|
-
for (let b = 0; b < buckets.length; b++) {
|
|
688
|
-
const bucket = buckets[b];
|
|
689
|
-
const base = b * 3;
|
|
690
|
-
bucketArray[base] = bucket.center[0];
|
|
691
|
-
bucketArray[base + 1] = bucket.center[1];
|
|
692
|
-
bucketArray[base + 2] = bucket.center[2];
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
sectionBuffers.push(sectionBuffer);
|
|
696
|
-
|
|
697
|
-
const sectionHeaderBuffer = new ArrayBuffer(SplatBuffer.SectionHeaderSizeBytes);
|
|
698
|
-
SplatBuffer.writeSectionHeaderToBuffer({
|
|
699
|
-
maxSplatCount: outSplatCount,
|
|
700
|
-
splatCount: outSplatCount,
|
|
701
|
-
bucketSize: sectionBucketSize,
|
|
702
|
-
bucketCount: buckets.length,
|
|
703
|
-
bucketBlockSize: sectionBlockSize,
|
|
704
|
-
compressionScaleRange: compressionScaleRange,
|
|
705
|
-
storageSizeBytes: sectionSizeBytes,
|
|
706
|
-
fullBucketCount: fullBucketCount,
|
|
707
|
-
partiallyFilledBucketCount: partiallyFilledBucketCount,
|
|
708
|
-
}, compressionLevel, sectionHeaderBuffer, 0);
|
|
709
|
-
sectionHeaderBuffers.push(sectionHeaderBuffer);
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
let sectionsCumulativeSizeBytes = 0;
|
|
714
|
-
for (let sectionBuffer of sectionBuffers) sectionsCumulativeSizeBytes += sectionBuffer.byteLength;
|
|
715
|
-
const unifiedBufferSize = SplatBuffer.HeaderSizeBytes +
|
|
716
|
-
SplatBuffer.SectionHeaderSizeBytes * sectionBuffers.length + sectionsCumulativeSizeBytes;
|
|
717
|
-
const unifiedBuffer = new ArrayBuffer(unifiedBufferSize);
|
|
718
|
-
|
|
719
|
-
SplatBuffer.writeHeaderToBuffer({
|
|
720
|
-
versionMajor: 0,
|
|
721
|
-
versionMinor: 1,
|
|
722
|
-
maxSectionCount: sectionBuffers.length,
|
|
723
|
-
sectionCount: sectionBuffers.length,
|
|
724
|
-
maxSplatCount: totalSplatCount,
|
|
725
|
-
splatCount: totalSplatCount,
|
|
726
|
-
compressionLevel: compressionLevel,
|
|
727
|
-
sceneCenter: sceneCenter,
|
|
728
|
-
}, unifiedBuffer);
|
|
729
|
-
|
|
730
|
-
let currentUnifiedBase = SplatBuffer.HeaderSizeBytes;
|
|
731
|
-
for (let sectionHeaderBuffer of sectionHeaderBuffers) {
|
|
732
|
-
new Uint8Array(unifiedBuffer, currentUnifiedBase, SplatBuffer.SectionHeaderSizeBytes).set(new Uint8Array(sectionHeaderBuffer));
|
|
733
|
-
currentUnifiedBase += SplatBuffer.SectionHeaderSizeBytes;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
for (let sectionBuffer of sectionBuffers) {
|
|
737
|
-
new Uint8Array(unifiedBuffer, currentUnifiedBase, sectionBuffer.byteLength).set(new Uint8Array(sectionBuffer));
|
|
738
|
-
currentUnifiedBase += sectionBuffer.byteLength;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
const splatBuffer = new SplatBuffer(unifiedBuffer);
|
|
742
|
-
return splatBuffer;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
static computeBucketsForUncompressedSplatArray (splatArray, blockSize, bucketSize) {
|
|
746
|
-
let splatCount = splatArray.splatCount;
|
|
747
|
-
const halfBlockSize = blockSize / 2.0;
|
|
748
|
-
|
|
749
|
-
const min = new THREE.Vector3();
|
|
750
|
-
const max = new THREE.Vector3();
|
|
751
|
-
|
|
752
|
-
for (let i = 0; i < splatCount; i++) {
|
|
753
|
-
const targetSplat = splatArray.splats[i];
|
|
754
|
-
const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
|
|
755
|
-
targetSplat[UncompressedSplatArray.OFFSET.Y],
|
|
756
|
-
targetSplat[UncompressedSplatArray.OFFSET.Z]];
|
|
757
|
-
if (i === 0 || center[0] < min.x) min.x = center[0];
|
|
758
|
-
if (i === 0 || center[0] > max.x) max.x = center[0];
|
|
759
|
-
if (i === 0 || center[1] < min.y) min.y = center[1];
|
|
760
|
-
if (i === 0 || center[1] > max.y) max.y = center[1];
|
|
761
|
-
if (i === 0 || center[2] < min.z) min.z = center[2];
|
|
762
|
-
if (i === 0 || center[2] > max.z) max.z = center[2];
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
const dimensions = new THREE.Vector3().copy(max).sub(min);
|
|
766
|
-
const yBlocks = Math.ceil(dimensions.y / blockSize);
|
|
767
|
-
const zBlocks = Math.ceil(dimensions.z / blockSize);
|
|
768
|
-
|
|
769
|
-
const blockCenter = new THREE.Vector3();
|
|
770
|
-
const fullBuckets = [];
|
|
771
|
-
const partiallyFullBuckets = {};
|
|
772
|
-
|
|
773
|
-
for (let i = 0; i < splatCount; i++) {
|
|
774
|
-
const targetSplat = splatArray.splats[i];
|
|
775
|
-
const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
|
|
776
|
-
targetSplat[UncompressedSplatArray.OFFSET.Y],
|
|
777
|
-
targetSplat[UncompressedSplatArray.OFFSET.Z]];
|
|
778
|
-
const xBlock = Math.floor((center[0] - min.x) / blockSize);
|
|
779
|
-
const yBlock = Math.floor((center[1] - min.y) / blockSize);
|
|
780
|
-
const zBlock = Math.floor((center[2] - min.z) / blockSize);
|
|
781
|
-
|
|
782
|
-
blockCenter.x = xBlock * blockSize + min.x + halfBlockSize;
|
|
783
|
-
blockCenter.y = yBlock * blockSize + min.y + halfBlockSize;
|
|
784
|
-
blockCenter.z = zBlock * blockSize + min.z + halfBlockSize;
|
|
785
|
-
|
|
786
|
-
const bucketId = xBlock * (yBlocks * zBlocks) + yBlock * zBlocks + zBlock;
|
|
787
|
-
let bucket = partiallyFullBuckets[bucketId];
|
|
788
|
-
if (!bucket) {
|
|
789
|
-
partiallyFullBuckets[bucketId] = bucket = {
|
|
790
|
-
'splats': [],
|
|
791
|
-
'center': blockCenter.toArray(),
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
bucket.splats.push(i);
|
|
796
|
-
if (bucket.splats.length >= bucketSize) {
|
|
797
|
-
fullBuckets.push(bucket);
|
|
798
|
-
partiallyFullBuckets[bucketId] = null;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
const partiallyFullBucketArray = [];
|
|
803
|
-
for (let bucketId in partiallyFullBuckets) {
|
|
804
|
-
if (partiallyFullBuckets.hasOwnProperty(bucketId)) {
|
|
805
|
-
const bucket = partiallyFullBuckets[bucketId];
|
|
806
|
-
if (bucket) {
|
|
807
|
-
partiallyFullBucketArray.push(bucket);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
return {
|
|
813
|
-
'fullBuckets': fullBuckets,
|
|
814
|
-
'partiallyFullBuckets': partiallyFullBucketArray,
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
}
|