@d5techs/3dgs-lib 1.4.19 → 1.4.21
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/3dgs-lib.cjs +287 -88
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +287 -88
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/editor/SplatEditor.d.ts +1 -1
- package/dist/gs/SOGEncoder.d.ts +11 -6
- package/dist/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/3dgs-lib.js
CHANGED
|
@@ -5975,7 +5975,7 @@ function unzipSync(data, opts) {
|
|
|
5975
5975
|
return files;
|
|
5976
5976
|
}
|
|
5977
5977
|
const SH_C0$1 = 0.28209479177387814;
|
|
5978
|
-
const COEFFS_PER_BAND = [3, 8, 15];
|
|
5978
|
+
const COEFFS_PER_BAND$1 = [3, 8, 15];
|
|
5979
5979
|
function lerp(a, b, t) {
|
|
5980
5980
|
return a + (b - a) * t;
|
|
5981
5981
|
}
|
|
@@ -6005,7 +6005,7 @@ async function decodeWebP(bytes) {
|
|
|
6005
6005
|
return { width: w, height: h, data: ctx.getImageData(0, 0, w, h).data };
|
|
6006
6006
|
}
|
|
6007
6007
|
function decodeSHPalette(centroidsImg, codebook, paletteSize, bands) {
|
|
6008
|
-
const numCoeffs = COEFFS_PER_BAND[bands - 1];
|
|
6008
|
+
const numCoeffs = COEFFS_PER_BAND$1[bands - 1];
|
|
6009
6009
|
const palette = new Array(paletteSize);
|
|
6010
6010
|
for (let n = 0; n < paletteSize; n++) {
|
|
6011
6011
|
const entry = new Float32Array(numCoeffs * 3);
|
|
@@ -6072,7 +6072,7 @@ async function deserializeSOG(data, onProgress) {
|
|
|
6072
6072
|
let shPalette = null;
|
|
6073
6073
|
let shBandCoeffs = 0;
|
|
6074
6074
|
if (meta.shN && centroidsImg) {
|
|
6075
|
-
shBandCoeffs = COEFFS_PER_BAND[meta.shN.bands - 1];
|
|
6075
|
+
shBandCoeffs = COEFFS_PER_BAND$1[meta.shN.bands - 1];
|
|
6076
6076
|
shPalette = decodeSHPalette(
|
|
6077
6077
|
centroidsImg,
|
|
6078
6078
|
meta.shN.codebook,
|
|
@@ -6145,10 +6145,57 @@ async function deserializeSOG(data, onProgress) {
|
|
|
6145
6145
|
return { count, positions, scales, rotations, colors, opacities, shCoeffs };
|
|
6146
6146
|
}
|
|
6147
6147
|
const SH_C0 = 0.28209479177387814;
|
|
6148
|
+
const COEFFS_PER_BAND = [3, 8, 15];
|
|
6148
6149
|
function symLog(x) {
|
|
6149
6150
|
return Math.sign(x) * Math.log(1 + Math.abs(x));
|
|
6150
6151
|
}
|
|
6151
|
-
function
|
|
6152
|
+
function inverseSHTransformYZSwap(sh, base, perChannel) {
|
|
6153
|
+
const SQRT3_2 = Math.sqrt(3) / 2;
|
|
6154
|
+
{
|
|
6155
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
6156
|
+
const a = sh[base + ch];
|
|
6157
|
+
const b = sh[base + 3 + ch];
|
|
6158
|
+
sh[base + ch] = -b;
|
|
6159
|
+
sh[base + 3 + ch] = a;
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
{
|
|
6163
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
6164
|
+
const g0 = sh[base + 9 + ch];
|
|
6165
|
+
const g1 = sh[base + 12 + ch];
|
|
6166
|
+
const g2 = sh[base + 15 + ch];
|
|
6167
|
+
const g3 = sh[base + 18 + ch];
|
|
6168
|
+
const g4 = sh[base + 21 + ch];
|
|
6169
|
+
sh[base + 9 + ch] = -g3;
|
|
6170
|
+
sh[base + 12 + ch] = -g1;
|
|
6171
|
+
sh[base + 15 + ch] = -0.5 * g2 - SQRT3_2 * g4;
|
|
6172
|
+
sh[base + 18 + ch] = g0;
|
|
6173
|
+
sh[base + 21 + ch] = -SQRT3_2 * g2 + 0.5 * g4;
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
{
|
|
6177
|
+
const A = Math.sqrt(10) / 4;
|
|
6178
|
+
const B = Math.sqrt(6) / 4;
|
|
6179
|
+
const P = Math.sqrt(15) / 4;
|
|
6180
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
6181
|
+
const g0 = sh[base + 24 + ch];
|
|
6182
|
+
const g1 = sh[base + 27 + ch];
|
|
6183
|
+
const g2 = sh[base + 30 + ch];
|
|
6184
|
+
const g3 = sh[base + 33 + ch];
|
|
6185
|
+
const g4 = sh[base + 36 + ch];
|
|
6186
|
+
const g5 = sh[base + 39 + ch];
|
|
6187
|
+
const g6 = sh[base + 42 + ch];
|
|
6188
|
+
sh[base + 24 + ch] = A * g3 - B * g5;
|
|
6189
|
+
sh[base + 27 + ch] = -g1;
|
|
6190
|
+
sh[base + 30 + ch] = B * g3 + A * g5;
|
|
6191
|
+
sh[base + 33 + ch] = -A * g0 - B * g2;
|
|
6192
|
+
sh[base + 36 + ch] = -0.25 * g4 - P * g6;
|
|
6193
|
+
sh[base + 39 + ch] = B * g0 - A * g2;
|
|
6194
|
+
sh[base + 42 + ch] = -P * g4 + 0.25 * g6;
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
function kmeansppInit1D(values, k) {
|
|
6152
6199
|
const n = values.length;
|
|
6153
6200
|
const centers = new Float64Array(k);
|
|
6154
6201
|
centers[0] = values[Math.floor(Math.random() * n)];
|
|
@@ -6174,18 +6221,19 @@ function kmeansppInit(values, k) {
|
|
|
6174
6221
|
}
|
|
6175
6222
|
return centers;
|
|
6176
6223
|
}
|
|
6177
|
-
function buildCodebook(values, size = 256, iterations =
|
|
6224
|
+
function buildCodebook(values, size = 256, iterations = 10) {
|
|
6178
6225
|
if (values.length === 0) return new Array(size).fill(0);
|
|
6179
6226
|
if (values.length <= size) {
|
|
6180
6227
|
const cb = Array.from(new Set(Array.from(values))).sort((a, b) => a - b);
|
|
6181
6228
|
while (cb.length < size) cb.push(cb[cb.length - 1]);
|
|
6182
6229
|
return cb.slice(0, size);
|
|
6183
6230
|
}
|
|
6184
|
-
const codebook =
|
|
6185
|
-
const assignments = new Uint8Array(values.length);
|
|
6231
|
+
const codebook = kmeansppInit1D(values, size);
|
|
6186
6232
|
const sums = new Float64Array(size);
|
|
6187
6233
|
const counts = new Uint32Array(size);
|
|
6188
6234
|
for (let iter = 0; iter < iterations; iter++) {
|
|
6235
|
+
sums.fill(0);
|
|
6236
|
+
counts.fill(0);
|
|
6189
6237
|
for (let i = 0; i < values.length; i++) {
|
|
6190
6238
|
let bestIdx = 0;
|
|
6191
6239
|
let bestDist = Math.abs(values[i] - codebook[0]);
|
|
@@ -6196,20 +6244,15 @@ function buildCodebook(values, size = 256, iterations = 20) {
|
|
|
6196
6244
|
bestIdx = j;
|
|
6197
6245
|
}
|
|
6198
6246
|
}
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
sums.fill(0);
|
|
6202
|
-
counts.fill(0);
|
|
6203
|
-
for (let i = 0; i < values.length; i++) {
|
|
6204
|
-
sums[assignments[i]] += values[i];
|
|
6205
|
-
counts[assignments[i]]++;
|
|
6247
|
+
sums[bestIdx] += values[i];
|
|
6248
|
+
counts[bestIdx]++;
|
|
6206
6249
|
}
|
|
6207
6250
|
let changed = false;
|
|
6208
6251
|
for (let j = 0; j < size; j++) {
|
|
6209
6252
|
if (counts[j] > 0) {
|
|
6210
|
-
const
|
|
6211
|
-
if (
|
|
6212
|
-
codebook[j] =
|
|
6253
|
+
const v = sums[j] / counts[j];
|
|
6254
|
+
if (v !== codebook[j]) {
|
|
6255
|
+
codebook[j] = v;
|
|
6213
6256
|
changed = true;
|
|
6214
6257
|
}
|
|
6215
6258
|
}
|
|
@@ -6220,18 +6263,114 @@ function buildCodebook(values, size = 256, iterations = 20) {
|
|
|
6220
6263
|
result.sort((a, b) => a - b);
|
|
6221
6264
|
return result;
|
|
6222
6265
|
}
|
|
6223
|
-
function findNearest(
|
|
6224
|
-
let lo = 0, hi =
|
|
6266
|
+
function findNearest(sortedCB, value) {
|
|
6267
|
+
let lo = 0, hi = sortedCB.length - 1;
|
|
6225
6268
|
while (lo < hi) {
|
|
6226
6269
|
const mid = lo + hi >> 1;
|
|
6227
|
-
if (
|
|
6270
|
+
if (sortedCB[mid] < value) lo = mid + 1;
|
|
6228
6271
|
else hi = mid;
|
|
6229
6272
|
}
|
|
6230
|
-
if (lo > 0 && Math.abs(
|
|
6273
|
+
if (lo > 0 && Math.abs(sortedCB[lo - 1] - value) <= Math.abs(sortedCB[lo] - value)) {
|
|
6231
6274
|
return lo - 1;
|
|
6232
6275
|
}
|
|
6233
6276
|
return lo;
|
|
6234
6277
|
}
|
|
6278
|
+
function kmeansVectors(vectors, dim, k, iterations) {
|
|
6279
|
+
const n = vectors.length;
|
|
6280
|
+
const labels = new Uint16Array(n);
|
|
6281
|
+
if (n <= k) {
|
|
6282
|
+
const centroids2 = [];
|
|
6283
|
+
for (let i = 0; i < n; i++) {
|
|
6284
|
+
centroids2.push(new Float32Array(vectors[i]));
|
|
6285
|
+
labels[i] = i;
|
|
6286
|
+
}
|
|
6287
|
+
while (centroids2.length < k) centroids2.push(new Float32Array(dim));
|
|
6288
|
+
return { centroids: centroids2, labels };
|
|
6289
|
+
}
|
|
6290
|
+
const centroids = [];
|
|
6291
|
+
centroids.push(new Float32Array(vectors[Math.floor(Math.random() * n)]));
|
|
6292
|
+
const dist = new Float64Array(n);
|
|
6293
|
+
dist.fill(Infinity);
|
|
6294
|
+
for (let c = 1; c < k; c++) {
|
|
6295
|
+
const last = centroids[c - 1];
|
|
6296
|
+
let totalDist = 0;
|
|
6297
|
+
for (let i = 0; i < n; i++) {
|
|
6298
|
+
let d2 = 0;
|
|
6299
|
+
for (let d = 0; d < dim; d++) {
|
|
6300
|
+
const diff = vectors[i][d] - last[d];
|
|
6301
|
+
d2 += diff * diff;
|
|
6302
|
+
}
|
|
6303
|
+
if (d2 < dist[i]) dist[i] = d2;
|
|
6304
|
+
totalDist += dist[i];
|
|
6305
|
+
}
|
|
6306
|
+
let target = Math.random() * totalDist;
|
|
6307
|
+
let chosen = 0;
|
|
6308
|
+
for (let i = 0; i < n; i++) {
|
|
6309
|
+
target -= dist[i];
|
|
6310
|
+
if (target <= 0) {
|
|
6311
|
+
chosen = i;
|
|
6312
|
+
break;
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
centroids.push(new Float32Array(vectors[chosen]));
|
|
6316
|
+
}
|
|
6317
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
6318
|
+
for (let i = 0; i < n; i++) {
|
|
6319
|
+
let bestK = 0;
|
|
6320
|
+
let bestD = Infinity;
|
|
6321
|
+
for (let c = 0; c < k; c++) {
|
|
6322
|
+
let d2 = 0;
|
|
6323
|
+
for (let d = 0; d < dim; d++) {
|
|
6324
|
+
const diff = vectors[i][d] - centroids[c][d];
|
|
6325
|
+
d2 += diff * diff;
|
|
6326
|
+
}
|
|
6327
|
+
if (d2 < bestD) {
|
|
6328
|
+
bestD = d2;
|
|
6329
|
+
bestK = c;
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
labels[i] = bestK;
|
|
6333
|
+
}
|
|
6334
|
+
const sums = [];
|
|
6335
|
+
for (let c = 0; c < k; c++) sums.push(new Float64Array(dim));
|
|
6336
|
+
const counts = new Uint32Array(k);
|
|
6337
|
+
for (let i = 0; i < n; i++) {
|
|
6338
|
+
const l = labels[i];
|
|
6339
|
+
counts[l]++;
|
|
6340
|
+
for (let d = 0; d < dim; d++) sums[l][d] += vectors[i][d];
|
|
6341
|
+
}
|
|
6342
|
+
let changed = false;
|
|
6343
|
+
for (let c = 0; c < k; c++) {
|
|
6344
|
+
if (counts[c] > 0) {
|
|
6345
|
+
for (let d = 0; d < dim; d++) {
|
|
6346
|
+
const v = sums[c][d] / counts[c];
|
|
6347
|
+
if (v !== centroids[c][d]) {
|
|
6348
|
+
centroids[c][d] = v;
|
|
6349
|
+
changed = true;
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
if (!changed) break;
|
|
6355
|
+
}
|
|
6356
|
+
for (let i = 0; i < n; i++) {
|
|
6357
|
+
let bestK = 0;
|
|
6358
|
+
let bestD = Infinity;
|
|
6359
|
+
for (let c = 0; c < k; c++) {
|
|
6360
|
+
let d2 = 0;
|
|
6361
|
+
for (let d = 0; d < dim; d++) {
|
|
6362
|
+
const diff = vectors[i][d] - centroids[c][d];
|
|
6363
|
+
d2 += diff * diff;
|
|
6364
|
+
}
|
|
6365
|
+
if (d2 < bestD) {
|
|
6366
|
+
bestD = d2;
|
|
6367
|
+
bestK = c;
|
|
6368
|
+
}
|
|
6369
|
+
}
|
|
6370
|
+
labels[i] = bestK;
|
|
6371
|
+
}
|
|
6372
|
+
return { centroids: centroids.map((c) => new Float32Array(c)), labels };
|
|
6373
|
+
}
|
|
6235
6374
|
function mortonSort(positions, count) {
|
|
6236
6375
|
let minx = Infinity, miny = Infinity, minz = Infinity;
|
|
6237
6376
|
let maxx = -Infinity, maxy = -Infinity, maxz = -Infinity;
|
|
@@ -6301,9 +6440,11 @@ async function encodeWebP(width, height, pixelData) {
|
|
|
6301
6440
|
}
|
|
6302
6441
|
return new Uint8Array(await blob.arrayBuffer());
|
|
6303
6442
|
}
|
|
6304
|
-
async function serializeSOG(data, coordinateSystem = "blender") {
|
|
6443
|
+
async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
6444
|
+
const { maxSHBands = 3, iterations = 10, paletteSize = 1024 } = options;
|
|
6305
6445
|
const { count, colors, opacities } = data;
|
|
6306
6446
|
let { positions, scales, rotations } = data;
|
|
6447
|
+
let shCoeffs = data.shCoeffs ? new Float32Array(data.shCoeffs) : void 0;
|
|
6307
6448
|
if (coordinateSystem === "blender") {
|
|
6308
6449
|
positions = new Float32Array(positions);
|
|
6309
6450
|
scales = new Float32Array(scales);
|
|
@@ -6320,43 +6461,51 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6320
6461
|
rotations[i4 + 2] = -rz;
|
|
6321
6462
|
rotations[i4 + 3] = ry;
|
|
6322
6463
|
}
|
|
6464
|
+
if (shCoeffs) {
|
|
6465
|
+
for (let i = 0; i < count; i++) {
|
|
6466
|
+
inverseSHTransformYZSwap(shCoeffs, i * 45);
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6323
6469
|
}
|
|
6324
6470
|
const sortOrder = mortonSort(positions, count);
|
|
6325
|
-
const
|
|
6326
|
-
const
|
|
6327
|
-
const
|
|
6328
|
-
const
|
|
6329
|
-
const
|
|
6471
|
+
const sPos = new Float32Array(count * 3);
|
|
6472
|
+
const sSca = new Float32Array(count * 3);
|
|
6473
|
+
const sRot = new Float32Array(count * 4);
|
|
6474
|
+
const sCol = new Float32Array(count * 3);
|
|
6475
|
+
const sOpa = new Float32Array(count);
|
|
6476
|
+
const sSH = shCoeffs ? new Float32Array(count * 45) : void 0;
|
|
6330
6477
|
for (let dst = 0; dst < count; dst++) {
|
|
6331
6478
|
const src = sortOrder[dst];
|
|
6332
6479
|
const s3 = src * 3, d3 = dst * 3;
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6480
|
+
sPos[d3] = positions[s3];
|
|
6481
|
+
sPos[d3 + 1] = positions[s3 + 1];
|
|
6482
|
+
sPos[d3 + 2] = positions[s3 + 2];
|
|
6483
|
+
sSca[d3] = scales[s3];
|
|
6484
|
+
sSca[d3 + 1] = scales[s3 + 1];
|
|
6485
|
+
sSca[d3 + 2] = scales[s3 + 2];
|
|
6486
|
+
sCol[d3] = colors[s3];
|
|
6487
|
+
sCol[d3 + 1] = colors[s3 + 1];
|
|
6488
|
+
sCol[d3 + 2] = colors[s3 + 2];
|
|
6342
6489
|
const s4 = src * 4, d4 = dst * 4;
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6490
|
+
sRot[d4] = rotations[s4];
|
|
6491
|
+
sRot[d4 + 1] = rotations[s4 + 1];
|
|
6492
|
+
sRot[d4 + 2] = rotations[s4 + 2];
|
|
6493
|
+
sRot[d4 + 3] = rotations[s4 + 3];
|
|
6494
|
+
sOpa[dst] = opacities[src];
|
|
6495
|
+
if (sSH && shCoeffs) {
|
|
6496
|
+
const sb = src * 45, db = dst * 45;
|
|
6497
|
+
for (let j = 0; j < 45; j++) sSH[db + j] = shCoeffs[sb + j];
|
|
6498
|
+
}
|
|
6348
6499
|
}
|
|
6349
6500
|
const { width, height } = calcImageDimensions(count);
|
|
6350
6501
|
const totalPixels = width * height;
|
|
6351
|
-
const
|
|
6352
|
-
for (let i = 0; i < count * 3; i++)
|
|
6353
|
-
logPositions[i] = symLog(sortedPositions[i]);
|
|
6354
|
-
}
|
|
6502
|
+
const logPos = new Float32Array(count * 3);
|
|
6503
|
+
for (let i = 0; i < count * 3; i++) logPos[i] = symLog(sPos[i]);
|
|
6355
6504
|
const mins = [Infinity, Infinity, Infinity];
|
|
6356
6505
|
const maxs = [-Infinity, -Infinity, -Infinity];
|
|
6357
6506
|
for (let i = 0; i < count; i++) {
|
|
6358
6507
|
for (let c = 0; c < 3; c++) {
|
|
6359
|
-
const v =
|
|
6508
|
+
const v = logPos[i * 3 + c];
|
|
6360
6509
|
if (v < mins[c]) mins[c] = v;
|
|
6361
6510
|
if (v > maxs[c]) maxs[c] = v;
|
|
6362
6511
|
}
|
|
@@ -6369,7 +6518,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6369
6518
|
const off = i * 4;
|
|
6370
6519
|
for (let c = 0; c < 3; c++) {
|
|
6371
6520
|
const range = maxs[c] - mins[c];
|
|
6372
|
-
const norm = range < 1e-10 ? 0 : (
|
|
6521
|
+
const norm = range < 1e-10 ? 0 : (logPos[i * 3 + c] - mins[c]) / range;
|
|
6373
6522
|
const q16 = Math.round(norm * 65535);
|
|
6374
6523
|
meansLData[off + c] = q16 & 255;
|
|
6375
6524
|
meansUData[off + c] = q16 >> 8 & 255;
|
|
@@ -6378,15 +6527,13 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6378
6527
|
meansUData[off + 3] = 255;
|
|
6379
6528
|
}
|
|
6380
6529
|
const logScales = new Float32Array(count * 3);
|
|
6381
|
-
for (let i = 0; i < count * 3; i++)
|
|
6382
|
-
|
|
6383
|
-
}
|
|
6384
|
-
const scaleCodebook = buildCodebook(logScales);
|
|
6530
|
+
for (let i = 0; i < count * 3; i++) logScales[i] = Math.log(Math.max(1e-8, sSca[i]));
|
|
6531
|
+
const scaleCodebook = buildCodebook(logScales, 256, iterations);
|
|
6385
6532
|
const scalesData = new Uint8ClampedArray(totalPixels * 4);
|
|
6386
6533
|
scalesData.fill(255);
|
|
6387
6534
|
for (let i = 0; i < count; i++) {
|
|
6388
6535
|
const off = i * 4;
|
|
6389
|
-
scalesData[off
|
|
6536
|
+
scalesData[off] = findNearest(scaleCodebook, logScales[i * 3]);
|
|
6390
6537
|
scalesData[off + 1] = findNearest(scaleCodebook, logScales[i * 3 + 1]);
|
|
6391
6538
|
scalesData[off + 2] = findNearest(scaleCodebook, logScales[i * 3 + 2]);
|
|
6392
6539
|
scalesData[off + 3] = 255;
|
|
@@ -6396,10 +6543,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6396
6543
|
const SQRT2 = Math.SQRT2;
|
|
6397
6544
|
for (let i = 0; i < count; i++) {
|
|
6398
6545
|
const off = i * 4;
|
|
6399
|
-
let qw =
|
|
6400
|
-
let qx = sortedRotations[i * 4 + 1];
|
|
6401
|
-
let qy = sortedRotations[i * 4 + 2];
|
|
6402
|
-
let qz = sortedRotations[i * 4 + 3];
|
|
6546
|
+
let qw = sRot[i * 4], qx = sRot[i * 4 + 1], qy = sRot[i * 4 + 2], qz = sRot[i * 4 + 3];
|
|
6403
6547
|
const len = Math.sqrt(qw * qw + qx * qx + qy * qy + qz * qz);
|
|
6404
6548
|
if (len > 0) {
|
|
6405
6549
|
const inv = 1 / len;
|
|
@@ -6409,8 +6553,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6409
6553
|
qz *= inv;
|
|
6410
6554
|
}
|
|
6411
6555
|
const comps = [qw, qx, qy, qz];
|
|
6412
|
-
let largest = 0;
|
|
6413
|
-
let largestAbs = Math.abs(comps[0]);
|
|
6556
|
+
let largest = 0, largestAbs = Math.abs(comps[0]);
|
|
6414
6557
|
for (let j = 1; j < 4; j++) {
|
|
6415
6558
|
const a = Math.abs(comps[j]);
|
|
6416
6559
|
if (a > largestAbs) {
|
|
@@ -6442,65 +6585,115 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6442
6585
|
r1 = qx;
|
|
6443
6586
|
r2 = qy;
|
|
6444
6587
|
}
|
|
6445
|
-
quatsData[off
|
|
6588
|
+
quatsData[off] = Math.round(Math.max(0, Math.min(255, (r0 / SQRT2 + 0.5) * 255)));
|
|
6446
6589
|
quatsData[off + 1] = Math.round(Math.max(0, Math.min(255, (r1 / SQRT2 + 0.5) * 255)));
|
|
6447
6590
|
quatsData[off + 2] = Math.round(Math.max(0, Math.min(255, (r2 / SQRT2 + 0.5) * 255)));
|
|
6448
6591
|
quatsData[off + 3] = largest + 252;
|
|
6449
6592
|
}
|
|
6450
6593
|
const dcValues = new Float32Array(count * 3);
|
|
6451
6594
|
for (let i = 0; i < count; i++) {
|
|
6452
|
-
dcValues[i * 3
|
|
6453
|
-
dcValues[i * 3 + 1] = (
|
|
6454
|
-
dcValues[i * 3 + 2] = (
|
|
6595
|
+
dcValues[i * 3] = (sCol[i * 3] - 0.5) / SH_C0;
|
|
6596
|
+
dcValues[i * 3 + 1] = (sCol[i * 3 + 1] - 0.5) / SH_C0;
|
|
6597
|
+
dcValues[i * 3 + 2] = (sCol[i * 3 + 2] - 0.5) / SH_C0;
|
|
6455
6598
|
}
|
|
6456
|
-
const dcCodebook = buildCodebook(dcValues);
|
|
6599
|
+
const dcCodebook = buildCodebook(dcValues, 256, iterations);
|
|
6457
6600
|
const sh0Data = new Uint8ClampedArray(totalPixels * 4);
|
|
6458
6601
|
sh0Data.fill(255);
|
|
6459
6602
|
for (let i = 0; i < count; i++) {
|
|
6460
6603
|
const off = i * 4;
|
|
6461
|
-
sh0Data[off
|
|
6604
|
+
sh0Data[off] = findNearest(dcCodebook, dcValues[i * 3]);
|
|
6462
6605
|
sh0Data[off + 1] = findNearest(dcCodebook, dcValues[i * 3 + 1]);
|
|
6463
6606
|
sh0Data[off + 2] = findNearest(dcCodebook, dcValues[i * 3 + 2]);
|
|
6464
|
-
sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255,
|
|
6607
|
+
sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255, sOpa[i] * 255)));
|
|
6465
6608
|
}
|
|
6466
|
-
const
|
|
6609
|
+
const actualBands = sSH && maxSHBands > 0 ? maxSHBands : 0;
|
|
6610
|
+
const numCoeffs = actualBands > 0 ? COEFFS_PER_BAND[actualBands - 1] : 0;
|
|
6611
|
+
const shDim = numCoeffs * 3;
|
|
6612
|
+
let shNMeta;
|
|
6613
|
+
const webpPromises = [
|
|
6467
6614
|
encodeWebP(width, height, meansLData),
|
|
6468
6615
|
encodeWebP(width, height, meansUData),
|
|
6469
6616
|
encodeWebP(width, height, scalesData),
|
|
6470
6617
|
encodeWebP(width, height, quatsData),
|
|
6471
6618
|
encodeWebP(width, height, sh0Data)
|
|
6472
|
-
]
|
|
6619
|
+
];
|
|
6620
|
+
if (actualBands > 0 && sSH) {
|
|
6621
|
+
const vectors = new Array(count);
|
|
6622
|
+
for (let i = 0; i < count; i++) {
|
|
6623
|
+
const v = new Float32Array(shDim);
|
|
6624
|
+
const b = i * 45;
|
|
6625
|
+
for (let j = 0; j < shDim; j++) v[j] = sSH[b + j];
|
|
6626
|
+
vectors[i] = v;
|
|
6627
|
+
}
|
|
6628
|
+
const effectivePaletteSize = Math.min(paletteSize, count);
|
|
6629
|
+
const { centroids, labels } = kmeansVectors(vectors, shDim, effectivePaletteSize, iterations);
|
|
6630
|
+
const allCoeffValues = new Float32Array(centroids.length * shDim);
|
|
6631
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
6632
|
+
for (let d = 0; d < shDim; d++) allCoeffValues[c * shDim + d] = centroids[c][d];
|
|
6633
|
+
}
|
|
6634
|
+
const shCodebook = buildCodebook(allCoeffValues, 256, iterations);
|
|
6635
|
+
const centW = 64 * numCoeffs;
|
|
6636
|
+
const centH = Math.ceil(effectivePaletteSize / 64);
|
|
6637
|
+
const centData = new Uint8ClampedArray(centW * centH * 4);
|
|
6638
|
+
centData.fill(255);
|
|
6639
|
+
for (let n = 0; n < effectivePaletteSize; n++) {
|
|
6640
|
+
const entry = centroids[n];
|
|
6641
|
+
for (let c = 0; c < numCoeffs; c++) {
|
|
6642
|
+
const u = n % 64 * numCoeffs + c;
|
|
6643
|
+
const v = Math.floor(n / 64);
|
|
6644
|
+
const off = (v * centW + u) * 4;
|
|
6645
|
+
centData[off] = findNearest(shCodebook, entry[c * 3]);
|
|
6646
|
+
centData[off + 1] = findNearest(shCodebook, entry[c * 3 + 1]);
|
|
6647
|
+
centData[off + 2] = findNearest(shCodebook, entry[c * 3 + 2]);
|
|
6648
|
+
centData[off + 3] = 255;
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
const labelsData = new Uint8ClampedArray(totalPixels * 4);
|
|
6652
|
+
labelsData.fill(255);
|
|
6653
|
+
for (let i = 0; i < count; i++) {
|
|
6654
|
+
const off = i * 4;
|
|
6655
|
+
labelsData[off] = labels[i] & 255;
|
|
6656
|
+
labelsData[off + 1] = labels[i] >> 8 & 255;
|
|
6657
|
+
labelsData[off + 2] = 255;
|
|
6658
|
+
labelsData[off + 3] = 255;
|
|
6659
|
+
}
|
|
6660
|
+
webpPromises.push(
|
|
6661
|
+
encodeWebP(centW, centH, centData),
|
|
6662
|
+
encodeWebP(width, height, labelsData)
|
|
6663
|
+
);
|
|
6664
|
+
shNMeta = {
|
|
6665
|
+
count: effectivePaletteSize,
|
|
6666
|
+
bands: actualBands,
|
|
6667
|
+
codebook: shCodebook,
|
|
6668
|
+
files: ["sh_centroids.webp", "sh_labels.webp"]
|
|
6669
|
+
};
|
|
6670
|
+
}
|
|
6671
|
+
const webps = await Promise.all(webpPromises);
|
|
6672
|
+
const [meansLWebP, meansUWebP, scalesWebP, quatsWebP, sh0WebP] = webps;
|
|
6473
6673
|
const meta = {
|
|
6474
6674
|
version: 2,
|
|
6475
6675
|
asset: { generator: "d5techs/3dgs-lib" },
|
|
6476
6676
|
count,
|
|
6477
6677
|
antialias: false,
|
|
6478
|
-
means: {
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
},
|
|
6483
|
-
scales: {
|
|
6484
|
-
codebook: scaleCodebook,
|
|
6485
|
-
files: ["scales.webp"]
|
|
6486
|
-
},
|
|
6487
|
-
quats: {
|
|
6488
|
-
files: ["quats.webp"]
|
|
6489
|
-
},
|
|
6490
|
-
sh0: {
|
|
6491
|
-
codebook: dcCodebook,
|
|
6492
|
-
files: ["sh0.webp"]
|
|
6493
|
-
}
|
|
6678
|
+
means: { mins, maxs, files: ["means_l.webp", "means_u.webp"] },
|
|
6679
|
+
scales: { codebook: scaleCodebook, files: ["scales.webp"] },
|
|
6680
|
+
quats: { files: ["quats.webp"] },
|
|
6681
|
+
sh0: { codebook: dcCodebook, files: ["sh0.webp"] }
|
|
6494
6682
|
};
|
|
6495
|
-
|
|
6496
|
-
const
|
|
6497
|
-
"meta.json":
|
|
6683
|
+
if (shNMeta) meta.shN = shNMeta;
|
|
6684
|
+
const zipEntries = {
|
|
6685
|
+
"meta.json": new TextEncoder().encode(JSON.stringify(meta)),
|
|
6498
6686
|
"means_l.webp": meansLWebP,
|
|
6499
6687
|
"means_u.webp": meansUWebP,
|
|
6500
6688
|
"scales.webp": scalesWebP,
|
|
6501
6689
|
"quats.webp": quatsWebP,
|
|
6502
6690
|
"sh0.webp": sh0WebP
|
|
6503
|
-
}
|
|
6691
|
+
};
|
|
6692
|
+
if (webps.length > 5) {
|
|
6693
|
+
zipEntries["sh_centroids.webp"] = webps[5];
|
|
6694
|
+
zipEntries["sh_labels.webp"] = webps[6];
|
|
6695
|
+
}
|
|
6696
|
+
const zipData = zipSync(zipEntries, { level: 0 });
|
|
6504
6697
|
return zipData.buffer;
|
|
6505
6698
|
}
|
|
6506
6699
|
const WORKGROUP_SIZE$1 = 256;
|
|
@@ -17327,6 +17520,8 @@ class SplatEditor {
|
|
|
17327
17520
|
const rotations = new Float32Array(keepCount * 4);
|
|
17328
17521
|
const colors = new Float32Array(keepCount * 3);
|
|
17329
17522
|
const opacities = new Float32Array(keepCount);
|
|
17523
|
+
const hasSH = !!src.shCoeffs;
|
|
17524
|
+
const shCoeffs = hasSH ? new Float32Array(keepCount * 45) : void 0;
|
|
17330
17525
|
let dst = 0;
|
|
17331
17526
|
for (let i = 0; i < count; i++) {
|
|
17332
17527
|
if (data[i] & State.deleted) continue;
|
|
@@ -17346,9 +17541,13 @@ class SplatEditor {
|
|
|
17346
17541
|
rotations[d4 + 2] = src.rotations[i4 + 2];
|
|
17347
17542
|
rotations[d4 + 3] = src.rotations[i4 + 3];
|
|
17348
17543
|
opacities[dst] = src.opacities[i];
|
|
17544
|
+
if (shCoeffs && src.shCoeffs) {
|
|
17545
|
+
const sb = i * 45, db = dst * 45;
|
|
17546
|
+
for (let j = 0; j < 45; j++) shCoeffs[db + j] = src.shCoeffs[sb + j];
|
|
17547
|
+
}
|
|
17349
17548
|
dst++;
|
|
17350
17549
|
}
|
|
17351
|
-
return { count: keepCount, positions, scales, rotations, colors, opacities };
|
|
17550
|
+
return { count: keepCount, positions, scales, rotations, colors, opacities, shCoeffs };
|
|
17352
17551
|
}
|
|
17353
17552
|
downloadPLY(filename = "edited.ply") {
|
|
17354
17553
|
const buffer = this.exportPLY();
|