@d5techs/3dgs-lib 1.4.18 → 1.4.20
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 +382 -76
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +382 -76
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/gs/SOGEncoder.d.ts +15 -9
- 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,18 +6147,95 @@ 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
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
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
|
+
}
|
|
6159
6177
|
}
|
|
6160
|
-
|
|
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) {
|
|
6201
|
+
const n = values.length;
|
|
6202
|
+
const centers = new Float64Array(k);
|
|
6203
|
+
centers[0] = values[Math.floor(Math.random() * n)];
|
|
6204
|
+
const dist = new Float64Array(n);
|
|
6205
|
+
dist.fill(Infinity);
|
|
6206
|
+
for (let c = 1; c < k; c++) {
|
|
6207
|
+
let totalDist = 0;
|
|
6208
|
+
for (let i = 0; i < n; i++) {
|
|
6209
|
+
const d = (values[i] - centers[c - 1]) ** 2;
|
|
6210
|
+
if (d < dist[i]) dist[i] = d;
|
|
6211
|
+
totalDist += dist[i];
|
|
6212
|
+
}
|
|
6213
|
+
let target = Math.random() * totalDist;
|
|
6214
|
+
let chosen = 0;
|
|
6215
|
+
for (let i = 0; i < n; i++) {
|
|
6216
|
+
target -= dist[i];
|
|
6217
|
+
if (target <= 0) {
|
|
6218
|
+
chosen = i;
|
|
6219
|
+
break;
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
centers[c] = values[chosen];
|
|
6223
|
+
}
|
|
6224
|
+
return centers;
|
|
6225
|
+
}
|
|
6226
|
+
function buildCodebook(values, size = 256, iterations = 10) {
|
|
6227
|
+
if (values.length === 0) return new Array(size).fill(0);
|
|
6228
|
+
if (values.length <= size) {
|
|
6229
|
+
const cb = Array.from(new Set(Array.from(values))).sort((a, b) => a - b);
|
|
6230
|
+
while (cb.length < size) cb.push(cb[cb.length - 1]);
|
|
6231
|
+
return cb.slice(0, size);
|
|
6232
|
+
}
|
|
6233
|
+
const codebook = kmeansppInit1D(values, size);
|
|
6234
|
+
const sums = new Float64Array(size);
|
|
6235
|
+
const counts = new Uint32Array(size);
|
|
6161
6236
|
for (let iter = 0; iter < iterations; iter++) {
|
|
6237
|
+
sums.fill(0);
|
|
6238
|
+
counts.fill(0);
|
|
6162
6239
|
for (let i = 0; i < values.length; i++) {
|
|
6163
6240
|
let bestIdx = 0;
|
|
6164
6241
|
let bestDist = Math.abs(values[i] - codebook[0]);
|
|
@@ -6169,31 +6246,167 @@ function buildCodebook(values, size = 256, iterations = 8) {
|
|
|
6169
6246
|
bestIdx = j;
|
|
6170
6247
|
}
|
|
6171
6248
|
}
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
const sums = new Float64Array(size);
|
|
6175
|
-
const counts = new Uint32Array(size);
|
|
6176
|
-
for (let i = 0; i < values.length; i++) {
|
|
6177
|
-
sums[assignments[i]] += values[i];
|
|
6178
|
-
counts[assignments[i]]++;
|
|
6249
|
+
sums[bestIdx] += values[i];
|
|
6250
|
+
counts[bestIdx]++;
|
|
6179
6251
|
}
|
|
6252
|
+
let changed = false;
|
|
6180
6253
|
for (let j = 0; j < size; j++) {
|
|
6181
|
-
if (counts[j] > 0)
|
|
6254
|
+
if (counts[j] > 0) {
|
|
6255
|
+
const v = sums[j] / counts[j];
|
|
6256
|
+
if (v !== codebook[j]) {
|
|
6257
|
+
codebook[j] = v;
|
|
6258
|
+
changed = true;
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6182
6261
|
}
|
|
6262
|
+
if (!changed) break;
|
|
6263
|
+
}
|
|
6264
|
+
const result = Array.from(codebook);
|
|
6265
|
+
result.sort((a, b) => a - b);
|
|
6266
|
+
return result;
|
|
6267
|
+
}
|
|
6268
|
+
function findNearest(sortedCB, value) {
|
|
6269
|
+
let lo = 0, hi = sortedCB.length - 1;
|
|
6270
|
+
while (lo < hi) {
|
|
6271
|
+
const mid = lo + hi >> 1;
|
|
6272
|
+
if (sortedCB[mid] < value) lo = mid + 1;
|
|
6273
|
+
else hi = mid;
|
|
6183
6274
|
}
|
|
6184
|
-
|
|
6275
|
+
if (lo > 0 && Math.abs(sortedCB[lo - 1] - value) <= Math.abs(sortedCB[lo] - value)) {
|
|
6276
|
+
return lo - 1;
|
|
6277
|
+
}
|
|
6278
|
+
return lo;
|
|
6185
6279
|
}
|
|
6186
|
-
function
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
const
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
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
|
+
}
|
|
6194
6371
|
}
|
|
6372
|
+
labels[i] = bestK;
|
|
6195
6373
|
}
|
|
6196
|
-
return
|
|
6374
|
+
return { centroids: centroids.map((c) => new Float32Array(c)), labels };
|
|
6375
|
+
}
|
|
6376
|
+
function mortonSort(positions, count) {
|
|
6377
|
+
let minx = Infinity, miny = Infinity, minz = Infinity;
|
|
6378
|
+
let maxx = -Infinity, maxy = -Infinity, maxz = -Infinity;
|
|
6379
|
+
for (let i = 0; i < count; i++) {
|
|
6380
|
+
const x = positions[i * 3], y = positions[i * 3 + 1], z = positions[i * 3 + 2];
|
|
6381
|
+
if (x < minx) minx = x;
|
|
6382
|
+
if (x > maxx) maxx = x;
|
|
6383
|
+
if (y < miny) miny = y;
|
|
6384
|
+
if (y > maxy) maxy = y;
|
|
6385
|
+
if (z < minz) minz = z;
|
|
6386
|
+
if (z > maxz) maxz = z;
|
|
6387
|
+
}
|
|
6388
|
+
const xlen = maxx - minx || 1;
|
|
6389
|
+
const ylen = maxy - miny || 1;
|
|
6390
|
+
const zlen = maxz - minz || 1;
|
|
6391
|
+
const part1By2 = (x) => {
|
|
6392
|
+
x &= 1023;
|
|
6393
|
+
x = (x ^ x << 16) & 4278190335;
|
|
6394
|
+
x = (x ^ x << 8) & 50393103;
|
|
6395
|
+
x = (x ^ x << 4) & 51130563;
|
|
6396
|
+
x = (x ^ x << 2) & 153391689;
|
|
6397
|
+
return x;
|
|
6398
|
+
};
|
|
6399
|
+
const morton = new Uint32Array(count);
|
|
6400
|
+
const indices = new Uint32Array(count);
|
|
6401
|
+
for (let i = 0; i < count; i++) {
|
|
6402
|
+
const ix = Math.min(1023, Math.floor(1024 * (positions[i * 3] - minx) / xlen));
|
|
6403
|
+
const iy = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 1] - miny) / ylen));
|
|
6404
|
+
const iz = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 2] - minz) / zlen));
|
|
6405
|
+
morton[i] = (part1By2(iz) << 2) + (part1By2(iy) << 1) + part1By2(ix);
|
|
6406
|
+
indices[i] = i;
|
|
6407
|
+
}
|
|
6408
|
+
indices.sort((a, b) => morton[a] - morton[b]);
|
|
6409
|
+
return indices;
|
|
6197
6410
|
}
|
|
6198
6411
|
function calcImageDimensions(count) {
|
|
6199
6412
|
const width = Math.ceil(Math.sqrt(count));
|
|
@@ -6229,9 +6442,11 @@ async function encodeWebP(width, height, pixelData) {
|
|
|
6229
6442
|
}
|
|
6230
6443
|
return new Uint8Array(await blob.arrayBuffer());
|
|
6231
6444
|
}
|
|
6232
|
-
async function serializeSOG(data, coordinateSystem = "blender") {
|
|
6445
|
+
async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
|
|
6446
|
+
const { maxSHBands = 3, iterations = 10, paletteSize = 1024 } = options;
|
|
6233
6447
|
const { count, colors, opacities } = data;
|
|
6234
6448
|
let { positions, scales, rotations } = data;
|
|
6449
|
+
let shCoeffs = data.shCoeffs ? new Float32Array(data.shCoeffs) : void 0;
|
|
6235
6450
|
if (coordinateSystem === "blender") {
|
|
6236
6451
|
positions = new Float32Array(positions);
|
|
6237
6452
|
scales = new Float32Array(scales);
|
|
@@ -6248,18 +6463,51 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6248
6463
|
rotations[i4 + 2] = -rz;
|
|
6249
6464
|
rotations[i4 + 3] = ry;
|
|
6250
6465
|
}
|
|
6466
|
+
if (shCoeffs) {
|
|
6467
|
+
for (let i = 0; i < count; i++) {
|
|
6468
|
+
inverseSHTransformYZSwap(shCoeffs, i * 45);
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
const sortOrder = mortonSort(positions, count);
|
|
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;
|
|
6479
|
+
for (let dst = 0; dst < count; dst++) {
|
|
6480
|
+
const src = sortOrder[dst];
|
|
6481
|
+
const s3 = src * 3, d3 = dst * 3;
|
|
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];
|
|
6491
|
+
const s4 = src * 4, d4 = dst * 4;
|
|
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
|
+
}
|
|
6251
6501
|
}
|
|
6252
6502
|
const { width, height } = calcImageDimensions(count);
|
|
6253
6503
|
const totalPixels = width * height;
|
|
6254
|
-
const
|
|
6255
|
-
for (let i = 0; i < count * 3; i++)
|
|
6256
|
-
logPositions[i] = symLog(positions[i]);
|
|
6257
|
-
}
|
|
6504
|
+
const logPos = new Float32Array(count * 3);
|
|
6505
|
+
for (let i = 0; i < count * 3; i++) logPos[i] = symLog(sPos[i]);
|
|
6258
6506
|
const mins = [Infinity, Infinity, Infinity];
|
|
6259
6507
|
const maxs = [-Infinity, -Infinity, -Infinity];
|
|
6260
6508
|
for (let i = 0; i < count; i++) {
|
|
6261
6509
|
for (let c = 0; c < 3; c++) {
|
|
6262
|
-
const v =
|
|
6510
|
+
const v = logPos[i * 3 + c];
|
|
6263
6511
|
if (v < mins[c]) mins[c] = v;
|
|
6264
6512
|
if (v > maxs[c]) maxs[c] = v;
|
|
6265
6513
|
}
|
|
@@ -6272,7 +6520,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6272
6520
|
const off = i * 4;
|
|
6273
6521
|
for (let c = 0; c < 3; c++) {
|
|
6274
6522
|
const range = maxs[c] - mins[c];
|
|
6275
|
-
const norm = range < 1e-10 ? 0 : (
|
|
6523
|
+
const norm = range < 1e-10 ? 0 : (logPos[i * 3 + c] - mins[c]) / range;
|
|
6276
6524
|
const q16 = Math.round(norm * 65535);
|
|
6277
6525
|
meansLData[off + c] = q16 & 255;
|
|
6278
6526
|
meansUData[off + c] = q16 >> 8 & 255;
|
|
@@ -6281,15 +6529,13 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6281
6529
|
meansUData[off + 3] = 255;
|
|
6282
6530
|
}
|
|
6283
6531
|
const logScales = new Float32Array(count * 3);
|
|
6284
|
-
for (let i = 0; i < count * 3; i++)
|
|
6285
|
-
|
|
6286
|
-
}
|
|
6287
|
-
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);
|
|
6288
6534
|
const scalesData = new Uint8ClampedArray(totalPixels * 4);
|
|
6289
6535
|
scalesData.fill(255);
|
|
6290
6536
|
for (let i = 0; i < count; i++) {
|
|
6291
6537
|
const off = i * 4;
|
|
6292
|
-
scalesData[off
|
|
6538
|
+
scalesData[off] = findNearest(scaleCodebook, logScales[i * 3]);
|
|
6293
6539
|
scalesData[off + 1] = findNearest(scaleCodebook, logScales[i * 3 + 1]);
|
|
6294
6540
|
scalesData[off + 2] = findNearest(scaleCodebook, logScales[i * 3 + 2]);
|
|
6295
6541
|
scalesData[off + 3] = 255;
|
|
@@ -6299,13 +6545,17 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6299
6545
|
const SQRT2 = Math.SQRT2;
|
|
6300
6546
|
for (let i = 0; i < count; i++) {
|
|
6301
6547
|
const off = i * 4;
|
|
6302
|
-
let qw =
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6548
|
+
let qw = sRot[i * 4], qx = sRot[i * 4 + 1], qy = sRot[i * 4 + 2], qz = sRot[i * 4 + 3];
|
|
6549
|
+
const len = Math.sqrt(qw * qw + qx * qx + qy * qy + qz * qz);
|
|
6550
|
+
if (len > 0) {
|
|
6551
|
+
const inv = 1 / len;
|
|
6552
|
+
qw *= inv;
|
|
6553
|
+
qx *= inv;
|
|
6554
|
+
qy *= inv;
|
|
6555
|
+
qz *= inv;
|
|
6556
|
+
}
|
|
6306
6557
|
const comps = [qw, qx, qy, qz];
|
|
6307
|
-
let largest = 0;
|
|
6308
|
-
let largestAbs = Math.abs(comps[0]);
|
|
6558
|
+
let largest = 0, largestAbs = Math.abs(comps[0]);
|
|
6309
6559
|
for (let j = 1; j < 4; j++) {
|
|
6310
6560
|
const a = Math.abs(comps[j]);
|
|
6311
6561
|
if (a > largestAbs) {
|
|
@@ -6337,65 +6587,115 @@ async function serializeSOG(data, coordinateSystem = "blender") {
|
|
|
6337
6587
|
r1 = qx;
|
|
6338
6588
|
r2 = qy;
|
|
6339
6589
|
}
|
|
6340
|
-
quatsData[off
|
|
6590
|
+
quatsData[off] = Math.round(Math.max(0, Math.min(255, (r0 / SQRT2 + 0.5) * 255)));
|
|
6341
6591
|
quatsData[off + 1] = Math.round(Math.max(0, Math.min(255, (r1 / SQRT2 + 0.5) * 255)));
|
|
6342
6592
|
quatsData[off + 2] = Math.round(Math.max(0, Math.min(255, (r2 / SQRT2 + 0.5) * 255)));
|
|
6343
6593
|
quatsData[off + 3] = largest + 252;
|
|
6344
6594
|
}
|
|
6345
6595
|
const dcValues = new Float32Array(count * 3);
|
|
6346
6596
|
for (let i = 0; i < count; i++) {
|
|
6347
|
-
dcValues[i * 3
|
|
6348
|
-
dcValues[i * 3 + 1] = (
|
|
6349
|
-
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;
|
|
6350
6600
|
}
|
|
6351
|
-
const dcCodebook = buildCodebook(dcValues);
|
|
6601
|
+
const dcCodebook = buildCodebook(dcValues, 256, iterations);
|
|
6352
6602
|
const sh0Data = new Uint8ClampedArray(totalPixels * 4);
|
|
6353
6603
|
sh0Data.fill(255);
|
|
6354
6604
|
for (let i = 0; i < count; i++) {
|
|
6355
6605
|
const off = i * 4;
|
|
6356
|
-
sh0Data[off
|
|
6606
|
+
sh0Data[off] = findNearest(dcCodebook, dcValues[i * 3]);
|
|
6357
6607
|
sh0Data[off + 1] = findNearest(dcCodebook, dcValues[i * 3 + 1]);
|
|
6358
6608
|
sh0Data[off + 2] = findNearest(dcCodebook, dcValues[i * 3 + 2]);
|
|
6359
|
-
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)));
|
|
6360
6610
|
}
|
|
6361
|
-
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 = [
|
|
6362
6616
|
encodeWebP(width, height, meansLData),
|
|
6363
6617
|
encodeWebP(width, height, meansUData),
|
|
6364
6618
|
encodeWebP(width, height, scalesData),
|
|
6365
6619
|
encodeWebP(width, height, quatsData),
|
|
6366
6620
|
encodeWebP(width, height, sh0Data)
|
|
6367
|
-
]
|
|
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;
|
|
6368
6675
|
const meta = {
|
|
6369
6676
|
version: 2,
|
|
6370
6677
|
asset: { generator: "d5techs/3dgs-lib" },
|
|
6371
6678
|
count,
|
|
6372
6679
|
antialias: false,
|
|
6373
|
-
means: {
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
},
|
|
6378
|
-
scales: {
|
|
6379
|
-
codebook: scaleCodebook,
|
|
6380
|
-
files: ["scales.webp"]
|
|
6381
|
-
},
|
|
6382
|
-
quats: {
|
|
6383
|
-
files: ["quats.webp"]
|
|
6384
|
-
},
|
|
6385
|
-
sh0: {
|
|
6386
|
-
codebook: dcCodebook,
|
|
6387
|
-
files: ["sh0.webp"]
|
|
6388
|
-
}
|
|
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"] }
|
|
6389
6684
|
};
|
|
6390
|
-
|
|
6391
|
-
const
|
|
6392
|
-
"meta.json":
|
|
6685
|
+
if (shNMeta) meta.shN = shNMeta;
|
|
6686
|
+
const zipEntries = {
|
|
6687
|
+
"meta.json": new TextEncoder().encode(JSON.stringify(meta)),
|
|
6393
6688
|
"means_l.webp": meansLWebP,
|
|
6394
6689
|
"means_u.webp": meansUWebP,
|
|
6395
6690
|
"scales.webp": scalesWebP,
|
|
6396
6691
|
"quats.webp": quatsWebP,
|
|
6397
6692
|
"sh0.webp": sh0WebP
|
|
6398
|
-
}
|
|
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 });
|
|
6399
6699
|
return zipData.buffer;
|
|
6400
6700
|
}
|
|
6401
6701
|
const WORKGROUP_SIZE$1 = 256;
|
|
@@ -17222,6 +17522,8 @@ class SplatEditor {
|
|
|
17222
17522
|
const rotations = new Float32Array(keepCount * 4);
|
|
17223
17523
|
const colors = new Float32Array(keepCount * 3);
|
|
17224
17524
|
const opacities = new Float32Array(keepCount);
|
|
17525
|
+
const hasSH = !!src.shCoeffs;
|
|
17526
|
+
const shCoeffs = hasSH ? new Float32Array(keepCount * 45) : void 0;
|
|
17225
17527
|
let dst = 0;
|
|
17226
17528
|
for (let i = 0; i < count; i++) {
|
|
17227
17529
|
if (data[i] & State.deleted) continue;
|
|
@@ -17241,9 +17543,13 @@ class SplatEditor {
|
|
|
17241
17543
|
rotations[d4 + 2] = src.rotations[i4 + 2];
|
|
17242
17544
|
rotations[d4 + 3] = src.rotations[i4 + 3];
|
|
17243
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
|
+
}
|
|
17244
17550
|
dst++;
|
|
17245
17551
|
}
|
|
17246
|
-
return { count: keepCount, positions, scales, rotations, colors, opacities };
|
|
17552
|
+
return { count: keepCount, positions, scales, rotations, colors, opacities, shCoeffs };
|
|
17247
17553
|
}
|
|
17248
17554
|
downloadPLY(filename = "edited.ply") {
|
|
17249
17555
|
const buffer = this.exportPLY();
|