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