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