@loaders.gl/splats 4.5.0-alpha.1
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/LICENSE +41 -0
- package/README.md +3 -0
- package/dist/bundled.d.ts +2 -0
- package/dist/bundled.d.ts.map +1 -0
- package/dist/bundled.js +5 -0
- package/dist/bundled.js.map +1 -0
- package/dist/dist.dev.js +33741 -0
- package/dist/dist.min.js +82 -0
- package/dist/index.cjs +1786 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/ksplat-loader-types.d.ts +19 -0
- package/dist/ksplat-loader-types.d.ts.map +1 -0
- package/dist/ksplat-loader-types.js +20 -0
- package/dist/ksplat-loader-types.js.map +1 -0
- package/dist/ksplat-loader.d.ts +21 -0
- package/dist/ksplat-loader.d.ts.map +1 -0
- package/dist/ksplat-loader.js +12 -0
- package/dist/ksplat-loader.js.map +1 -0
- package/dist/lib/parse-ksplat.d.ts +7 -0
- package/dist/lib/parse-ksplat.d.ts.map +1 -0
- package/dist/lib/parse-ksplat.js +312 -0
- package/dist/lib/parse-ksplat.js.map +1 -0
- package/dist/lib/parse-rad-chunk.d.ts +19 -0
- package/dist/lib/parse-rad-chunk.d.ts.map +1 -0
- package/dist/lib/parse-rad-chunk.js +514 -0
- package/dist/lib/parse-rad-chunk.js.map +1 -0
- package/dist/lib/parse-rad.d.ts +136 -0
- package/dist/lib/parse-rad.d.ts.map +1 -0
- package/dist/lib/parse-rad.js +110 -0
- package/dist/lib/parse-rad.js.map +1 -0
- package/dist/lib/parse-splat.d.ts +7 -0
- package/dist/lib/parse-splat.d.ts.map +1 -0
- package/dist/lib/parse-splat.js +58 -0
- package/dist/lib/parse-splat.js.map +1 -0
- package/dist/lib/parse-spz.d.ts +7 -0
- package/dist/lib/parse-spz.d.ts.map +1 -0
- package/dist/lib/parse-spz.js +268 -0
- package/dist/lib/parse-spz.js.map +1 -0
- package/dist/lib/splat-utils.d.ts +12 -0
- package/dist/lib/splat-utils.d.ts.map +1 -0
- package/dist/lib/splat-utils.js +38 -0
- package/dist/lib/splat-utils.js.map +1 -0
- package/dist/lib/splats-arrow-table.d.ts +5 -0
- package/dist/lib/splats-arrow-table.d.ts.map +1 -0
- package/dist/lib/splats-arrow-table.js +133 -0
- package/dist/lib/splats-arrow-table.js.map +1 -0
- package/dist/rad-loader-types.d.ts +16 -0
- package/dist/rad-loader-types.d.ts.map +1 -0
- package/dist/rad-loader-types.js +16 -0
- package/dist/rad-loader-types.js.map +1 -0
- package/dist/rad-loader.d.ts +20 -0
- package/dist/rad-loader.d.ts.map +1 -0
- package/dist/rad-loader.js +14 -0
- package/dist/rad-loader.js.map +1 -0
- package/dist/rad-metadata.schema.json +133 -0
- package/dist/rad-source-loader.d.ts +112 -0
- package/dist/rad-source-loader.d.ts.map +1 -0
- package/dist/rad-source-loader.js +329 -0
- package/dist/rad-source-loader.js.map +1 -0
- package/dist/rad-zod-schema.cjs +81 -0
- package/dist/rad-zod-schema.cjs.map +7 -0
- package/dist/rad-zod-schema.d.ts +88 -0
- package/dist/rad-zod-schema.d.ts.map +1 -0
- package/dist/rad-zod-schema.js +70 -0
- package/dist/rad-zod-schema.js.map +1 -0
- package/dist/radc-metadata.schema.json +112 -0
- package/dist/splat-loader-types.d.ts +19 -0
- package/dist/splat-loader-types.d.ts.map +1 -0
- package/dist/splat-loader-types.js +20 -0
- package/dist/splat-loader-types.js.map +1 -0
- package/dist/splat-loader.d.ts +21 -0
- package/dist/splat-loader.d.ts.map +1 -0
- package/dist/splat-loader.js +12 -0
- package/dist/splat-loader.js.map +1 -0
- package/dist/splats-format.d.ts +39 -0
- package/dist/splats-format.d.ts.map +1 -0
- package/dist/splats-format.js +42 -0
- package/dist/splats-format.js.map +1 -0
- package/dist/spz-loader-types.d.ts +20 -0
- package/dist/spz-loader-types.d.ts.map +1 -0
- package/dist/spz-loader-types.js +20 -0
- package/dist/spz-loader-types.js.map +1 -0
- package/dist/spz-loader.d.ts +22 -0
- package/dist/spz-loader.d.ts.map +1 -0
- package/dist/spz-loader.js +11 -0
- package/dist/spz-loader.js.map +1 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/unbundled.d.ts +2 -0
- package/dist/unbundled.d.ts.map +1 -0
- package/dist/unbundled.js +5 -0
- package/dist/unbundled.js.map +1 -0
- package/package.json +94 -0
- package/src/bundled.ts +5 -0
- package/src/index.ts +28 -0
- package/src/ksplat-loader-types.ts +25 -0
- package/src/ksplat-loader.ts +16 -0
- package/src/lib/parse-ksplat.ts +540 -0
- package/src/lib/parse-rad-chunk.ts +721 -0
- package/src/lib/parse-rad.ts +318 -0
- package/src/lib/parse-splat.ts +77 -0
- package/src/lib/parse-spz.ts +368 -0
- package/src/lib/splat-utils.ts +49 -0
- package/src/lib/splats-arrow-table.ts +190 -0
- package/src/rad-loader-types.ts +20 -0
- package/src/rad-loader.ts +22 -0
- package/src/rad-source-loader.ts +491 -0
- package/src/rad-zod-schema.ts +83 -0
- package/src/splat-loader-types.ts +25 -0
- package/src/splat-loader.ts +16 -0
- package/src/splats-format.ts +47 -0
- package/src/spz-loader-types.ts +25 -0
- package/src/spz-loader.ts +16 -0
- package/src/types.ts +39 -0
- package/src/unbundled.ts +5 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1786 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// dist/index.js
|
|
31
|
+
var dist_exports = {};
|
|
32
|
+
__export(dist_exports, {
|
|
33
|
+
KSPLATFormat: () => KSPLATFormat,
|
|
34
|
+
KSPLATLoader: () => KSPLATLoaderWithParser,
|
|
35
|
+
RADFormat: () => RADFormat,
|
|
36
|
+
RADLoader: () => RADLoaderWithParser,
|
|
37
|
+
RADSource: () => RADSource,
|
|
38
|
+
RADSourceLoader: () => RADSourceLoader,
|
|
39
|
+
SPLATFormat: () => SPLATFormat,
|
|
40
|
+
SPLATLoader: () => SPLATLoaderWithParser,
|
|
41
|
+
SPZFormat: () => SPZFormat,
|
|
42
|
+
SPZLoader: () => SPZLoaderWithParser,
|
|
43
|
+
resolveRADChunkUrl: () => resolveRADChunkUrl
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(dist_exports);
|
|
46
|
+
|
|
47
|
+
// dist/splats-format.js
|
|
48
|
+
var SPLATFormat = {
|
|
49
|
+
name: "SPLAT",
|
|
50
|
+
id: "splat",
|
|
51
|
+
module: "splats",
|
|
52
|
+
extensions: ["splat"],
|
|
53
|
+
mimeTypes: ["application/octet-stream"],
|
|
54
|
+
binary: true
|
|
55
|
+
};
|
|
56
|
+
var KSPLATFormat = {
|
|
57
|
+
name: "KSPLAT",
|
|
58
|
+
id: "ksplat",
|
|
59
|
+
module: "splats",
|
|
60
|
+
extensions: ["ksplat"],
|
|
61
|
+
mimeTypes: ["application/octet-stream"],
|
|
62
|
+
binary: true
|
|
63
|
+
};
|
|
64
|
+
var SPZFormat = {
|
|
65
|
+
name: "SPZ",
|
|
66
|
+
id: "spz",
|
|
67
|
+
module: "splats",
|
|
68
|
+
extensions: ["spz"],
|
|
69
|
+
mimeTypes: ["application/octet-stream"],
|
|
70
|
+
binary: true,
|
|
71
|
+
tests: [new Uint8Array([78, 71, 83, 80]).buffer]
|
|
72
|
+
};
|
|
73
|
+
var RADFormat = {
|
|
74
|
+
name: "RAD",
|
|
75
|
+
id: "rad",
|
|
76
|
+
module: "splats",
|
|
77
|
+
extensions: ["rad"],
|
|
78
|
+
mimeTypes: ["application/octet-stream"],
|
|
79
|
+
binary: true,
|
|
80
|
+
tests: [new Uint8Array([82, 65, 68, 48]).buffer]
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// dist/lib/splats-arrow-table.js
|
|
84
|
+
var arrow = __toESM(require("apache-arrow"), 1);
|
|
85
|
+
|
|
86
|
+
// dist/lib/splat-utils.js
|
|
87
|
+
var SH_C0 = 0.28209479177387814;
|
|
88
|
+
function convertColorByteToSphericalHarmonicDc(colorByte) {
|
|
89
|
+
return (colorByte / 255 - 0.5) / SH_C0;
|
|
90
|
+
}
|
|
91
|
+
function convertAlphaByteToLinearOpacity(alphaByte) {
|
|
92
|
+
return Math.min(Math.max(alphaByte / 255, 0), 1);
|
|
93
|
+
}
|
|
94
|
+
function normalizeQuaternion(w, x, y, z2) {
|
|
95
|
+
const length = Math.hypot(w, x, y, z2);
|
|
96
|
+
if (!Number.isFinite(length) || length <= Number.EPSILON) {
|
|
97
|
+
return [1, 0, 0, 0];
|
|
98
|
+
}
|
|
99
|
+
return [w / length, x / length, y / length, z2 / length];
|
|
100
|
+
}
|
|
101
|
+
function decodeQuaternionByte(byte) {
|
|
102
|
+
return (byte - 128) / 128;
|
|
103
|
+
}
|
|
104
|
+
function decodeFloat16(value) {
|
|
105
|
+
const sign = value & 32768 ? -1 : 1;
|
|
106
|
+
const exponent = value >> 10 & 31;
|
|
107
|
+
const fraction = value & 1023;
|
|
108
|
+
if (exponent === 0) {
|
|
109
|
+
return sign * Math.pow(2, -14) * (fraction / 1024);
|
|
110
|
+
}
|
|
111
|
+
if (exponent === 31) {
|
|
112
|
+
return fraction ? Number.NaN : sign * Number.POSITIVE_INFINITY;
|
|
113
|
+
}
|
|
114
|
+
return sign * Math.pow(2, exponent - 15) * (1 + fraction / 1024);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// dist/lib/splats-arrow-table.js
|
|
118
|
+
function makeGaussianSplatsArrowTable(splats) {
|
|
119
|
+
const columns = {};
|
|
120
|
+
const fields = [];
|
|
121
|
+
const schemaFields = [];
|
|
122
|
+
addColumn(columns, fields, schemaFields, "POSITION", makeFixedSizeListVector(splats.positions, 3), {
|
|
123
|
+
type: "fixed-size-list",
|
|
124
|
+
listSize: 3,
|
|
125
|
+
children: [{ name: "value", type: "float32" }]
|
|
126
|
+
});
|
|
127
|
+
const colorCoefficients = getColorCoefficients(splats);
|
|
128
|
+
addScalarColumn(columns, fields, schemaFields, "f_dc_0", colorCoefficients[0], {
|
|
129
|
+
"loaders_gl.gaussian_splats.semantic": "spherical_harmonic_dc",
|
|
130
|
+
"loaders_gl.gaussian_splats.component": "0",
|
|
131
|
+
"loaders_gl.gaussian_splats.encoding": "coefficient"
|
|
132
|
+
});
|
|
133
|
+
addScalarColumn(columns, fields, schemaFields, "f_dc_1", colorCoefficients[1], {
|
|
134
|
+
"loaders_gl.gaussian_splats.semantic": "spherical_harmonic_dc",
|
|
135
|
+
"loaders_gl.gaussian_splats.component": "1",
|
|
136
|
+
"loaders_gl.gaussian_splats.encoding": "coefficient"
|
|
137
|
+
});
|
|
138
|
+
addScalarColumn(columns, fields, schemaFields, "f_dc_2", colorCoefficients[2], {
|
|
139
|
+
"loaders_gl.gaussian_splats.semantic": "spherical_harmonic_dc",
|
|
140
|
+
"loaders_gl.gaussian_splats.component": "2",
|
|
141
|
+
"loaders_gl.gaussian_splats.encoding": "coefficient"
|
|
142
|
+
});
|
|
143
|
+
addScalarColumn(columns, fields, schemaFields, "opacity", splats.opacities, {
|
|
144
|
+
"loaders_gl.gaussian_splats.semantic": "opacity",
|
|
145
|
+
"loaders_gl.gaussian_splats.encoding": "linear"
|
|
146
|
+
});
|
|
147
|
+
addVectorComponents(columns, fields, schemaFields, "scale", splats.scales, 3, {
|
|
148
|
+
"loaders_gl.gaussian_splats.semantic": "scale",
|
|
149
|
+
"loaders_gl.gaussian_splats.encoding": "linear"
|
|
150
|
+
});
|
|
151
|
+
addVectorComponents(columns, fields, schemaFields, "rot", splats.rotations, 4, {
|
|
152
|
+
"loaders_gl.gaussian_splats.semantic": "rotation",
|
|
153
|
+
"loaders_gl.gaussian_splats.encoding": "quaternion"
|
|
154
|
+
});
|
|
155
|
+
if (splats.sphericalHarmonics && splats.sphericalHarmonicsComponentCount) {
|
|
156
|
+
addSphericalHarmonicRestColumns(columns, fields, schemaFields, splats.sphericalHarmonics, splats.sphericalHarmonicsComponentCount);
|
|
157
|
+
}
|
|
158
|
+
const metadata = /* @__PURE__ */ new Map([
|
|
159
|
+
["loaders_gl.semantic_type", "gaussian-splats"],
|
|
160
|
+
["loaders_gl.gaussian_splats.version", "1"],
|
|
161
|
+
["loaders_gl.gaussian_splats.source_format", splats.format],
|
|
162
|
+
["topology", "point-list"],
|
|
163
|
+
["mode", "0"]
|
|
164
|
+
]);
|
|
165
|
+
const arrowSchema = new arrow.Schema(fields, metadata);
|
|
166
|
+
const schema = {
|
|
167
|
+
fields: schemaFields,
|
|
168
|
+
metadata: Object.fromEntries(metadata)
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
shape: "arrow-table",
|
|
172
|
+
schema,
|
|
173
|
+
data: new arrow.Table(arrowSchema, columns),
|
|
174
|
+
topology: "point-list",
|
|
175
|
+
loaderData: splats.loaderData
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function addVectorComponents(columns, fields, schemaFields, prefix, values, size, metadata) {
|
|
179
|
+
for (let component = 0; component < size; component++) {
|
|
180
|
+
const componentValues = new Float32Array(values.length / size);
|
|
181
|
+
for (let rowIndex = 0; rowIndex < componentValues.length; rowIndex++) {
|
|
182
|
+
componentValues[rowIndex] = values[rowIndex * size + component];
|
|
183
|
+
}
|
|
184
|
+
addScalarColumn(columns, fields, schemaFields, `${prefix}_${component}`, componentValues, {
|
|
185
|
+
...metadata,
|
|
186
|
+
"loaders_gl.gaussian_splats.component": String(component)
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function addSphericalHarmonicRestColumns(columns, fields, schemaFields, sphericalHarmonics, componentCount) {
|
|
191
|
+
for (let component = 0; component < componentCount; component++) {
|
|
192
|
+
const componentValues = new Float32Array(sphericalHarmonics.length / componentCount);
|
|
193
|
+
for (let rowIndex = 0; rowIndex < componentValues.length; rowIndex++) {
|
|
194
|
+
componentValues[rowIndex] = sphericalHarmonics[rowIndex * componentCount + component];
|
|
195
|
+
}
|
|
196
|
+
addScalarColumn(columns, fields, schemaFields, `f_rest_${component}`, componentValues, {
|
|
197
|
+
"loaders_gl.gaussian_splats.semantic": "spherical_harmonic_rest",
|
|
198
|
+
"loaders_gl.gaussian_splats.component": String(component),
|
|
199
|
+
"loaders_gl.gaussian_splats.encoding": "coefficient"
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function addScalarColumn(columns, fields, schemaFields, name, values, metadata) {
|
|
204
|
+
addColumn(columns, fields, schemaFields, name, arrow.makeVector(values), "float32", metadata);
|
|
205
|
+
}
|
|
206
|
+
function addColumn(columns, fields, schemaFields, name, column, schemaType, metadata = {}) {
|
|
207
|
+
columns[name] = column;
|
|
208
|
+
fields.push(new arrow.Field(name, column.type, false, new Map(Object.entries(metadata))));
|
|
209
|
+
schemaFields.push({ name, type: schemaType, nullable: false, metadata });
|
|
210
|
+
}
|
|
211
|
+
function makeFixedSizeListVector(value, size) {
|
|
212
|
+
const values = arrow.makeVector(value);
|
|
213
|
+
const child = values.data[0];
|
|
214
|
+
const type = new arrow.FixedSizeList(size, new arrow.Field("value", child.type, false));
|
|
215
|
+
const data = new arrow.Data(type, 0, value.length / size, 0, {}, [child]);
|
|
216
|
+
return new arrow.Vector([data]);
|
|
217
|
+
}
|
|
218
|
+
function getColorCoefficients(splats) {
|
|
219
|
+
const rowCount = splats.splatCount;
|
|
220
|
+
const fdc0 = new Float32Array(rowCount);
|
|
221
|
+
const fdc1 = new Float32Array(rowCount);
|
|
222
|
+
const fdc2 = new Float32Array(rowCount);
|
|
223
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
224
|
+
if (splats.sphericalHarmonicDcs) {
|
|
225
|
+
fdc0[rowIndex] = splats.sphericalHarmonicDcs[rowIndex * 3 + 0];
|
|
226
|
+
fdc1[rowIndex] = splats.sphericalHarmonicDcs[rowIndex * 3 + 1];
|
|
227
|
+
fdc2[rowIndex] = splats.sphericalHarmonicDcs[rowIndex * 3 + 2];
|
|
228
|
+
} else {
|
|
229
|
+
fdc0[rowIndex] = convertColorByteToSphericalHarmonicDc(splats.colors[rowIndex * 3 + 0]);
|
|
230
|
+
fdc1[rowIndex] = convertColorByteToSphericalHarmonicDc(splats.colors[rowIndex * 3 + 1]);
|
|
231
|
+
fdc2[rowIndex] = convertColorByteToSphericalHarmonicDc(splats.colors[rowIndex * 3 + 2]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return [fdc0, fdc1, fdc2];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// dist/lib/parse-splat.js
|
|
238
|
+
var SPLAT_ROW_BYTE_LENGTH = 32;
|
|
239
|
+
function parseSPLAT(data) {
|
|
240
|
+
return makeGaussianSplatsArrowTable(parseSPLATToGaussianSplats(data));
|
|
241
|
+
}
|
|
242
|
+
function parseSPLATToGaussianSplats(data) {
|
|
243
|
+
if (data.byteLength % SPLAT_ROW_BYTE_LENGTH !== 0) {
|
|
244
|
+
throw new Error(`SPLATLoader: byte length must be a multiple of ${SPLAT_ROW_BYTE_LENGTH}.`);
|
|
245
|
+
}
|
|
246
|
+
const splatCount = data.byteLength / SPLAT_ROW_BYTE_LENGTH;
|
|
247
|
+
const dataView = new DataView(data);
|
|
248
|
+
const positions = new Float32Array(splatCount * 3);
|
|
249
|
+
const scales = new Float32Array(splatCount * 3);
|
|
250
|
+
const rotations = new Float32Array(splatCount * 4);
|
|
251
|
+
const colors = new Uint8Array(splatCount * 3);
|
|
252
|
+
const opacities = new Float32Array(splatCount);
|
|
253
|
+
for (let splatIndex = 0; splatIndex < splatCount; splatIndex++) {
|
|
254
|
+
const byteOffset = splatIndex * SPLAT_ROW_BYTE_LENGTH;
|
|
255
|
+
const positionOffset = splatIndex * 3;
|
|
256
|
+
const rotationOffset = splatIndex * 4;
|
|
257
|
+
positions[positionOffset + 0] = dataView.getFloat32(byteOffset + 0, true);
|
|
258
|
+
positions[positionOffset + 1] = dataView.getFloat32(byteOffset + 4, true);
|
|
259
|
+
positions[positionOffset + 2] = dataView.getFloat32(byteOffset + 8, true);
|
|
260
|
+
scales[positionOffset + 0] = dataView.getFloat32(byteOffset + 12, true);
|
|
261
|
+
scales[positionOffset + 1] = dataView.getFloat32(byteOffset + 16, true);
|
|
262
|
+
scales[positionOffset + 2] = dataView.getFloat32(byteOffset + 20, true);
|
|
263
|
+
colors[positionOffset + 0] = dataView.getUint8(byteOffset + 24);
|
|
264
|
+
colors[positionOffset + 1] = dataView.getUint8(byteOffset + 25);
|
|
265
|
+
colors[positionOffset + 2] = dataView.getUint8(byteOffset + 26);
|
|
266
|
+
opacities[splatIndex] = convertAlphaByteToLinearOpacity(dataView.getUint8(byteOffset + 27));
|
|
267
|
+
const [w, x, y, z2] = normalizeQuaternion(decodeQuaternionByte(dataView.getUint8(byteOffset + 28)), decodeQuaternionByte(dataView.getUint8(byteOffset + 29)), decodeQuaternionByte(dataView.getUint8(byteOffset + 30)), decodeQuaternionByte(dataView.getUint8(byteOffset + 31)));
|
|
268
|
+
rotations[rotationOffset + 0] = w;
|
|
269
|
+
rotations[rotationOffset + 1] = x;
|
|
270
|
+
rotations[rotationOffset + 2] = y;
|
|
271
|
+
rotations[rotationOffset + 3] = z2;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
format: "splat",
|
|
275
|
+
splatCount,
|
|
276
|
+
positions,
|
|
277
|
+
scales,
|
|
278
|
+
rotations,
|
|
279
|
+
colors,
|
|
280
|
+
opacities,
|
|
281
|
+
loaderData: {
|
|
282
|
+
format: "splat",
|
|
283
|
+
splatCount,
|
|
284
|
+
rowByteLength: SPLAT_ROW_BYTE_LENGTH
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// dist/splat-loader-types.js
|
|
290
|
+
var VERSION = true ? "4.5.0-alpha.1" : "latest";
|
|
291
|
+
var SPLATLoader = {
|
|
292
|
+
dataType: null,
|
|
293
|
+
batchType: null,
|
|
294
|
+
...SPLATFormat,
|
|
295
|
+
version: VERSION,
|
|
296
|
+
options: {
|
|
297
|
+
splats: {
|
|
298
|
+
shape: "arrow-table"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// dist/splat-loader.js
|
|
304
|
+
var SPLATLoaderWithParser = {
|
|
305
|
+
...SPLATLoader,
|
|
306
|
+
parse: async (arrayBuffer) => parseSPLAT(arrayBuffer),
|
|
307
|
+
parseSync: (arrayBuffer) => parseSPLAT(arrayBuffer)
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// dist/lib/parse-ksplat.js
|
|
311
|
+
var CURRENT_MAJOR_VERSION = 0;
|
|
312
|
+
var CURRENT_MINOR_VERSION = 1;
|
|
313
|
+
var HEADER_BYTE_LENGTH = 4096;
|
|
314
|
+
var SECTION_HEADER_BYTE_LENGTH = 1024;
|
|
315
|
+
var DEFAULT_SH_8BIT_COMPRESSION_RANGE = 3;
|
|
316
|
+
var DEFAULT_SH_8BIT_COMPRESSION_HALF_RANGE = DEFAULT_SH_8BIT_COMPRESSION_RANGE / 2;
|
|
317
|
+
var COMPRESSION_LAYOUTS = {
|
|
318
|
+
0: {
|
|
319
|
+
bytesPerCenter: 12,
|
|
320
|
+
bytesPerScale: 12,
|
|
321
|
+
bytesPerRotation: 16,
|
|
322
|
+
bytesPerColor: 4,
|
|
323
|
+
scaleOffsetBytes: 12,
|
|
324
|
+
colorOffsetBytes: 40,
|
|
325
|
+
sphericalHarmonicsOffsetBytes: 44,
|
|
326
|
+
scaleRange: 1,
|
|
327
|
+
bytesPerSphericalHarmonicsComponent: 4
|
|
328
|
+
},
|
|
329
|
+
1: {
|
|
330
|
+
bytesPerCenter: 6,
|
|
331
|
+
bytesPerScale: 6,
|
|
332
|
+
bytesPerRotation: 8,
|
|
333
|
+
bytesPerColor: 4,
|
|
334
|
+
scaleOffsetBytes: 6,
|
|
335
|
+
colorOffsetBytes: 20,
|
|
336
|
+
sphericalHarmonicsOffsetBytes: 24,
|
|
337
|
+
scaleRange: 32767,
|
|
338
|
+
bytesPerSphericalHarmonicsComponent: 2
|
|
339
|
+
},
|
|
340
|
+
2: {
|
|
341
|
+
bytesPerCenter: 6,
|
|
342
|
+
bytesPerScale: 6,
|
|
343
|
+
bytesPerRotation: 8,
|
|
344
|
+
bytesPerColor: 4,
|
|
345
|
+
scaleOffsetBytes: 6,
|
|
346
|
+
colorOffsetBytes: 20,
|
|
347
|
+
sphericalHarmonicsOffsetBytes: 24,
|
|
348
|
+
scaleRange: 32767,
|
|
349
|
+
bytesPerSphericalHarmonicsComponent: 1
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
function parseKSPLAT(data) {
|
|
353
|
+
return makeGaussianSplatsArrowTable(parseKSPLATToGaussianSplats(data));
|
|
354
|
+
}
|
|
355
|
+
function parseKSPLATToGaussianSplats(data) {
|
|
356
|
+
if (data.byteLength < HEADER_BYTE_LENGTH) {
|
|
357
|
+
throw new Error(`KSPLATLoader: file must contain a ${HEADER_BYTE_LENGTH}-byte header.`);
|
|
358
|
+
}
|
|
359
|
+
const header = parseKSPLATHeader(data);
|
|
360
|
+
checkKSPLATVersion(header);
|
|
361
|
+
const sections = parseKSPLATSections(data, header);
|
|
362
|
+
const splatCount = getTotalSplatCount(header, sections);
|
|
363
|
+
const sphericalHarmonicsComponentCount = Math.max(0, ...sections.slice(0, getActiveSectionCount(header)).map((section) => getSphericalHarmonicsComponentCount(section.sphericalHarmonicsDegree)));
|
|
364
|
+
const positions = new Float32Array(splatCount * 3);
|
|
365
|
+
const scales = new Float32Array(splatCount * 3);
|
|
366
|
+
const rotations = new Float32Array(splatCount * 4);
|
|
367
|
+
const colors = new Uint8Array(splatCount * 3);
|
|
368
|
+
const opacities = new Float32Array(splatCount);
|
|
369
|
+
const sphericalHarmonics = sphericalHarmonicsComponentCount ? new Float32Array(splatCount * sphericalHarmonicsComponentCount) : void 0;
|
|
370
|
+
let outputSplatIndex = 0;
|
|
371
|
+
for (const section of sections.slice(0, getActiveSectionCount(header))) {
|
|
372
|
+
validateSectionBounds(data, section);
|
|
373
|
+
for (let localSplatIndex = 0; localSplatIndex < section.splatCount && outputSplatIndex < splatCount; localSplatIndex++) {
|
|
374
|
+
decodeKSPLATSplat(data, header, section, localSplatIndex, outputSplatIndex, positions, scales, rotations, colors, opacities, sphericalHarmonics, sphericalHarmonicsComponentCount);
|
|
375
|
+
outputSplatIndex++;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
format: "ksplat",
|
|
380
|
+
splatCount,
|
|
381
|
+
positions,
|
|
382
|
+
scales,
|
|
383
|
+
rotations,
|
|
384
|
+
colors,
|
|
385
|
+
opacities,
|
|
386
|
+
sphericalHarmonics,
|
|
387
|
+
sphericalHarmonicsComponentCount,
|
|
388
|
+
loaderData: {
|
|
389
|
+
format: "ksplat",
|
|
390
|
+
...header,
|
|
391
|
+
sections: sections.slice(0, getActiveSectionCount(header)).map((section) => ({
|
|
392
|
+
splatCount: section.splatCount,
|
|
393
|
+
maxSplatCount: section.maxSplatCount,
|
|
394
|
+
sphericalHarmonicsDegree: section.sphericalHarmonicsDegree
|
|
395
|
+
}))
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function decodeKSPLATSplat(data, header, section, localSplatIndex, outputSplatIndex, positions, scales, rotations, colors, opacities, sphericalHarmonics, sphericalHarmonicsComponentCount) {
|
|
400
|
+
const layout = COMPRESSION_LAYOUTS[header.compressionLevel];
|
|
401
|
+
const splatBase = section.dataBase + section.bytesPerSplat * localSplatIndex;
|
|
402
|
+
const positionOffset = outputSplatIndex * 3;
|
|
403
|
+
const rotationOffset = outputSplatIndex * 4;
|
|
404
|
+
const bucketCenter = getBucketCenter(data, section, localSplatIndex);
|
|
405
|
+
positions[positionOffset + 0] = decodeKSPLATCenterComponent(data, splatBase, 0, header, section, bucketCenter[0]);
|
|
406
|
+
positions[positionOffset + 1] = decodeKSPLATCenterComponent(data, splatBase, 1, header, section, bucketCenter[1]);
|
|
407
|
+
positions[positionOffset + 2] = decodeKSPLATCenterComponent(data, splatBase, 2, header, section, bucketCenter[2]);
|
|
408
|
+
scales[positionOffset + 0] = readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 0, header.compressionLevel);
|
|
409
|
+
scales[positionOffset + 1] = readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 1, header.compressionLevel);
|
|
410
|
+
scales[positionOffset + 2] = readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 2, header.compressionLevel);
|
|
411
|
+
const [w, x, y, z2] = normalizeQuaternion(readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 3, header.compressionLevel), readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 4, header.compressionLevel), readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 5, header.compressionLevel), readCompressedFloat(data, splatBase + layout.scaleOffsetBytes, 6, header.compressionLevel));
|
|
412
|
+
rotations[rotationOffset + 0] = w;
|
|
413
|
+
rotations[rotationOffset + 1] = x;
|
|
414
|
+
rotations[rotationOffset + 2] = y;
|
|
415
|
+
rotations[rotationOffset + 3] = z2;
|
|
416
|
+
const colorBase = splatBase + layout.colorOffsetBytes;
|
|
417
|
+
const colorView = new Uint8Array(data, colorBase, 4);
|
|
418
|
+
colors[positionOffset + 0] = colorView[0];
|
|
419
|
+
colors[positionOffset + 1] = colorView[1];
|
|
420
|
+
colors[positionOffset + 2] = colorView[2];
|
|
421
|
+
opacities[outputSplatIndex] = convertAlphaByteToLinearOpacity(colorView[3]);
|
|
422
|
+
if (sphericalHarmonics && sphericalHarmonicsComponentCount) {
|
|
423
|
+
decodeSphericalHarmonics(data, splatBase + layout.sphericalHarmonicsOffsetBytes, header, section, outputSplatIndex, sphericalHarmonics, sphericalHarmonicsComponentCount);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function parseKSPLATHeader(data) {
|
|
427
|
+
const dataView = new DataView(data, 0, HEADER_BYTE_LENGTH);
|
|
428
|
+
const compressionLevel = dataView.getUint16(20, true);
|
|
429
|
+
if (compressionLevel !== 0 && compressionLevel !== 1 && compressionLevel !== 2) {
|
|
430
|
+
throw new Error(`KSPLATLoader: unsupported compression level ${compressionLevel}.`);
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
versionMajor: dataView.getUint8(0),
|
|
434
|
+
versionMinor: dataView.getUint8(1),
|
|
435
|
+
maxSectionCount: dataView.getUint32(4, true),
|
|
436
|
+
sectionCount: dataView.getUint32(8, true),
|
|
437
|
+
maxSplatCount: dataView.getUint32(12, true),
|
|
438
|
+
splatCount: dataView.getUint32(16, true),
|
|
439
|
+
compressionLevel,
|
|
440
|
+
minSphericalHarmonicsCoeff: dataView.getFloat32(36, true) || -DEFAULT_SH_8BIT_COMPRESSION_HALF_RANGE,
|
|
441
|
+
maxSphericalHarmonicsCoeff: dataView.getFloat32(40, true) || DEFAULT_SH_8BIT_COMPRESSION_HALF_RANGE
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function checkKSPLATVersion(header) {
|
|
445
|
+
if (header.versionMajor === CURRENT_MAJOR_VERSION ? header.versionMinor >= CURRENT_MINOR_VERSION : header.versionMajor > CURRENT_MAJOR_VERSION) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
throw new Error(`KSPLATLoader: version ${header.versionMajor}.${header.versionMinor} is not supported.`);
|
|
449
|
+
}
|
|
450
|
+
function parseKSPLATSections(data, header) {
|
|
451
|
+
if (data.byteLength < HEADER_BYTE_LENGTH + header.maxSectionCount * SECTION_HEADER_BYTE_LENGTH) {
|
|
452
|
+
throw new Error("KSPLATLoader: file does not contain all section headers.");
|
|
453
|
+
}
|
|
454
|
+
const sections = [];
|
|
455
|
+
let sectionBase = HEADER_BYTE_LENGTH + header.maxSectionCount * SECTION_HEADER_BYTE_LENGTH;
|
|
456
|
+
let splatCountOffset = 0;
|
|
457
|
+
for (let sectionIndex = 0; sectionIndex < header.maxSectionCount; sectionIndex++) {
|
|
458
|
+
const sectionHeaderBase = HEADER_BYTE_LENGTH + sectionIndex * SECTION_HEADER_BYTE_LENGTH;
|
|
459
|
+
const dataView = new DataView(data, sectionHeaderBase, SECTION_HEADER_BYTE_LENGTH);
|
|
460
|
+
const splatCount = dataView.getUint32(0, true);
|
|
461
|
+
const maxSplatCount = dataView.getUint32(4, true);
|
|
462
|
+
const bucketSize = dataView.getUint32(8, true);
|
|
463
|
+
const bucketCount = dataView.getUint32(12, true);
|
|
464
|
+
const bucketBlockSize = dataView.getFloat32(16, true);
|
|
465
|
+
const bucketStorageSizeBytes = dataView.getUint16(20, true);
|
|
466
|
+
const compressionScaleRange = dataView.getUint32(24, true) || COMPRESSION_LAYOUTS[header.compressionLevel].scaleRange;
|
|
467
|
+
const fullBucketCount = dataView.getUint32(32, true);
|
|
468
|
+
const partiallyFilledBucketCount = dataView.getUint32(36, true);
|
|
469
|
+
const sphericalHarmonicsDegree = dataView.getUint16(40, true);
|
|
470
|
+
const bytesPerSplat = getBytesPerSplat(header.compressionLevel, sphericalHarmonicsDegree);
|
|
471
|
+
const bucketsMetaDataSizeBytes = partiallyFilledBucketCount * 4;
|
|
472
|
+
const bucketsStorageSizeBytes = bucketStorageSizeBytes * bucketCount + bucketsMetaDataSizeBytes;
|
|
473
|
+
const storageSizeBytes = bytesPerSplat * maxSplatCount + bucketsStorageSizeBytes;
|
|
474
|
+
sections.push({
|
|
475
|
+
bytesPerSplat,
|
|
476
|
+
splatCountOffset,
|
|
477
|
+
splatCount: splatCount || maxSplatCount,
|
|
478
|
+
maxSplatCount,
|
|
479
|
+
bucketSize,
|
|
480
|
+
bucketCount,
|
|
481
|
+
bucketStorageSizeBytes,
|
|
482
|
+
bucketsStorageSizeBytes,
|
|
483
|
+
compressionScaleRange,
|
|
484
|
+
compressionScaleFactor: bucketBlockSize / 2 / compressionScaleRange,
|
|
485
|
+
base: sectionBase,
|
|
486
|
+
bucketsBase: sectionBase + bucketsMetaDataSizeBytes,
|
|
487
|
+
dataBase: sectionBase + bucketsStorageSizeBytes,
|
|
488
|
+
fullBucketCount,
|
|
489
|
+
partiallyFilledBucketCount,
|
|
490
|
+
sphericalHarmonicsDegree
|
|
491
|
+
});
|
|
492
|
+
sectionBase += storageSizeBytes;
|
|
493
|
+
splatCountOffset += maxSplatCount;
|
|
494
|
+
}
|
|
495
|
+
return sections;
|
|
496
|
+
}
|
|
497
|
+
function decodeKSPLATCenterComponent(data, splatBase, component, header, section, bucketCenter) {
|
|
498
|
+
const value = readCompressedCenterValue(data, splatBase, component, header.compressionLevel);
|
|
499
|
+
return header.compressionLevel >= 1 ? (value - section.compressionScaleRange) * section.compressionScaleFactor + bucketCenter : value;
|
|
500
|
+
}
|
|
501
|
+
function readCompressedCenterValue(data, byteOffset, component, compressionLevel) {
|
|
502
|
+
const dataView = new DataView(data);
|
|
503
|
+
return compressionLevel === 0 ? dataView.getFloat32(byteOffset + component * 4, true) : dataView.getUint16(byteOffset + component * 2, true);
|
|
504
|
+
}
|
|
505
|
+
function getBucketCenter(data, section, localSplatIndex) {
|
|
506
|
+
if (!section.bucketCount) {
|
|
507
|
+
return [0, 0, 0];
|
|
508
|
+
}
|
|
509
|
+
const bucketIndex = getBucketIndex(data, section, localSplatIndex);
|
|
510
|
+
const bucketBase = section.bucketsBase + bucketIndex * section.bucketStorageSizeBytes;
|
|
511
|
+
const dataView = new DataView(data, bucketBase, 12);
|
|
512
|
+
return [dataView.getFloat32(0, true), dataView.getFloat32(4, true), dataView.getFloat32(8, true)];
|
|
513
|
+
}
|
|
514
|
+
function getBucketIndex(data, section, localSplatIndex) {
|
|
515
|
+
const maxSplatIndexInFullBuckets = section.fullBucketCount * section.bucketSize;
|
|
516
|
+
if (localSplatIndex < maxSplatIndexInFullBuckets) {
|
|
517
|
+
return Math.floor(localSplatIndex / section.bucketSize);
|
|
518
|
+
}
|
|
519
|
+
let bucketSplatIndex = maxSplatIndexInFullBuckets;
|
|
520
|
+
let bucketIndex = section.fullBucketCount;
|
|
521
|
+
for (let partiallyFilledBucketIndex = 0; partiallyFilledBucketIndex < section.partiallyFilledBucketCount; partiallyFilledBucketIndex++) {
|
|
522
|
+
const bucketLength = new DataView(data, section.base + partiallyFilledBucketIndex * 4, 4).getUint32(0, true);
|
|
523
|
+
if (localSplatIndex < bucketSplatIndex + bucketLength) {
|
|
524
|
+
return bucketIndex;
|
|
525
|
+
}
|
|
526
|
+
bucketSplatIndex += bucketLength;
|
|
527
|
+
bucketIndex++;
|
|
528
|
+
}
|
|
529
|
+
return Math.max(bucketIndex - 1, 0);
|
|
530
|
+
}
|
|
531
|
+
function decodeSphericalHarmonics(data, byteOffset, header, section, outputSplatIndex, output, outputComponentCount) {
|
|
532
|
+
const componentCount = getSphericalHarmonicsComponentCount(section.sphericalHarmonicsDegree);
|
|
533
|
+
const componentBase = outputSplatIndex * outputComponentCount;
|
|
534
|
+
for (let component = 0; component < componentCount; component++) {
|
|
535
|
+
output[componentBase + component] = readCompressedFloat(data, byteOffset, component, header.compressionLevel, true, header.minSphericalHarmonicsCoeff, header.maxSphericalHarmonicsCoeff);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function readCompressedFloat(data, byteOffset, component, compressionLevel, sphericalHarmonic = false, sphericalHarmonicRangeMin = 0, sphericalHarmonicRangeMax = 0) {
|
|
539
|
+
const dataView = new DataView(data);
|
|
540
|
+
if (compressionLevel === 0) {
|
|
541
|
+
return dataView.getFloat32(byteOffset + component * 4, true);
|
|
542
|
+
}
|
|
543
|
+
if (compressionLevel === 1 || !sphericalHarmonic) {
|
|
544
|
+
return decodeFloat16(dataView.getUint16(byteOffset + component * 2, true));
|
|
545
|
+
}
|
|
546
|
+
const value = dataView.getUint8(byteOffset + component);
|
|
547
|
+
return value / 255 * (sphericalHarmonicRangeMax - sphericalHarmonicRangeMin) + sphericalHarmonicRangeMin;
|
|
548
|
+
}
|
|
549
|
+
function getBytesPerSplat(compressionLevel, sphericalHarmonicsDegree) {
|
|
550
|
+
const layout = COMPRESSION_LAYOUTS[compressionLevel];
|
|
551
|
+
return layout.bytesPerCenter + layout.bytesPerScale + layout.bytesPerRotation + layout.bytesPerColor + layout.bytesPerSphericalHarmonicsComponent * getSphericalHarmonicsComponentCount(sphericalHarmonicsDegree);
|
|
552
|
+
}
|
|
553
|
+
function getSphericalHarmonicsComponentCount(sphericalHarmonicsDegree) {
|
|
554
|
+
switch (sphericalHarmonicsDegree) {
|
|
555
|
+
case 0:
|
|
556
|
+
return 0;
|
|
557
|
+
case 1:
|
|
558
|
+
return 9;
|
|
559
|
+
case 2:
|
|
560
|
+
return 24;
|
|
561
|
+
case 3:
|
|
562
|
+
return 45;
|
|
563
|
+
default:
|
|
564
|
+
throw new Error(`KSPLATLoader: unsupported spherical harmonics degree ${sphericalHarmonicsDegree}.`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function getActiveSectionCount(header) {
|
|
568
|
+
return header.sectionCount || header.maxSectionCount;
|
|
569
|
+
}
|
|
570
|
+
function getTotalSplatCount(header, sections) {
|
|
571
|
+
const sectionSplatCount = sections.slice(0, getActiveSectionCount(header)).reduce((splatCount, section) => splatCount + section.splatCount, 0);
|
|
572
|
+
return header.splatCount ? Math.min(header.splatCount, sectionSplatCount || header.splatCount) : sectionSplatCount;
|
|
573
|
+
}
|
|
574
|
+
function validateSectionBounds(data, section) {
|
|
575
|
+
const requiredByteLength = section.dataBase + section.bytesPerSplat * section.splatCount;
|
|
576
|
+
if (requiredByteLength > data.byteLength) {
|
|
577
|
+
throw new Error("KSPLATLoader: section data extends beyond the file length.");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// dist/ksplat-loader-types.js
|
|
582
|
+
var VERSION2 = true ? "4.5.0-alpha.1" : "latest";
|
|
583
|
+
var KSPLATLoader = {
|
|
584
|
+
dataType: null,
|
|
585
|
+
batchType: null,
|
|
586
|
+
...KSPLATFormat,
|
|
587
|
+
version: VERSION2,
|
|
588
|
+
options: {
|
|
589
|
+
splats: {
|
|
590
|
+
shape: "arrow-table"
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// dist/ksplat-loader.js
|
|
596
|
+
var KSPLATLoaderWithParser = {
|
|
597
|
+
...KSPLATLoader,
|
|
598
|
+
parse: async (arrayBuffer) => parseKSPLAT(arrayBuffer),
|
|
599
|
+
parseSync: (arrayBuffer) => parseKSPLAT(arrayBuffer)
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// dist/lib/parse-spz.js
|
|
603
|
+
var import_compression = require("@loaders.gl/compression");
|
|
604
|
+
var SPZ_HEADER_BYTE_LENGTH = 32;
|
|
605
|
+
var SPZ_MAGIC = 1347635022;
|
|
606
|
+
var SPZ_VERSION = 4;
|
|
607
|
+
var SPZ_EXTENSION_FLAG = 2;
|
|
608
|
+
var SPZ_COLOR_SCALE = 0.15;
|
|
609
|
+
var SPZ_ROTATION_COMPONENT_SCALE = Math.SQRT1_2;
|
|
610
|
+
var SPZ_ROTATION_COMPONENT_MASK = (1 << 9) - 1;
|
|
611
|
+
async function parseSPZ(data, options) {
|
|
612
|
+
return makeGaussianSplatsArrowTable(await parseSPZToGaussianSplats(data, options));
|
|
613
|
+
}
|
|
614
|
+
async function parseSPZToGaussianSplats(data, options) {
|
|
615
|
+
const header = parseSPZHeader(data);
|
|
616
|
+
const streamInfos = parseSPZStreamInfos(data, header, getSPZStreamByteLengths(header));
|
|
617
|
+
const compressedStreams = streamInfos.map((streamInfo) => data.slice(streamInfo.compressedOffset, streamInfo.compressedOffset + streamInfo.compressedSize));
|
|
618
|
+
const compression = new import_compression.ZstdCompression({ modules: options == null ? void 0 : options.modules });
|
|
619
|
+
const streams = await Promise.all(compressedStreams.map((compressedStream, streamIndex) => compression.decompress(compressedStream, streamInfos[streamIndex].uncompressedSize)));
|
|
620
|
+
validateSPZStreamLengths(streams, streamInfos);
|
|
621
|
+
return decodeSPZStreams(header, streams, data);
|
|
622
|
+
}
|
|
623
|
+
function parseSPZHeader(data) {
|
|
624
|
+
if (data.byteLength < SPZ_HEADER_BYTE_LENGTH) {
|
|
625
|
+
throw new Error(`SPZLoader: file must contain a ${SPZ_HEADER_BYTE_LENGTH}-byte header.`);
|
|
626
|
+
}
|
|
627
|
+
const dataView = new DataView(data);
|
|
628
|
+
const header = {
|
|
629
|
+
magic: dataView.getUint32(0, true),
|
|
630
|
+
version: dataView.getUint32(4, true),
|
|
631
|
+
numPoints: dataView.getUint32(8, true),
|
|
632
|
+
shDegree: dataView.getUint8(12),
|
|
633
|
+
fractionalBits: dataView.getUint8(13),
|
|
634
|
+
flags: dataView.getUint8(14),
|
|
635
|
+
numStreams: dataView.getUint8(15),
|
|
636
|
+
tocByteOffset: dataView.getUint32(16, true)
|
|
637
|
+
};
|
|
638
|
+
if (header.magic !== SPZ_MAGIC) {
|
|
639
|
+
throw new Error("SPZLoader: NGSP magic header not found.");
|
|
640
|
+
}
|
|
641
|
+
if (header.version !== SPZ_VERSION) {
|
|
642
|
+
throw new Error(`SPZLoader: version ${header.version} is not supported.`);
|
|
643
|
+
}
|
|
644
|
+
if (header.shDegree > 4) {
|
|
645
|
+
throw new Error(`SPZLoader: spherical harmonics degree ${header.shDegree} is not supported.`);
|
|
646
|
+
}
|
|
647
|
+
if (header.tocByteOffset < SPZ_HEADER_BYTE_LENGTH || header.tocByteOffset > data.byteLength) {
|
|
648
|
+
throw new Error("SPZLoader: invalid table of contents byte offset.");
|
|
649
|
+
}
|
|
650
|
+
return header;
|
|
651
|
+
}
|
|
652
|
+
function parseSPZStreamInfos(data, header, expectedUncompressedSizes) {
|
|
653
|
+
if (header.numStreams !== expectedUncompressedSizes.length) {
|
|
654
|
+
throw new Error(`SPZLoader: expected ${expectedUncompressedSizes.length} streams, found ${header.numStreams}.`);
|
|
655
|
+
}
|
|
656
|
+
const tocByteLength = header.numStreams * 16;
|
|
657
|
+
const tocEnd = header.tocByteOffset + tocByteLength;
|
|
658
|
+
if (tocEnd > data.byteLength) {
|
|
659
|
+
throw new Error("SPZLoader: table of contents exceeds file byte length.");
|
|
660
|
+
}
|
|
661
|
+
const dataView = new DataView(data);
|
|
662
|
+
let compressedOffset = tocEnd;
|
|
663
|
+
const streamInfos = [];
|
|
664
|
+
for (let streamIndex = 0; streamIndex < header.numStreams; streamIndex++) {
|
|
665
|
+
const tocOffset = header.tocByteOffset + streamIndex * 16;
|
|
666
|
+
const compressedSize = readSafeUint64(dataView, tocOffset);
|
|
667
|
+
const uncompressedSize = readSafeUint64(dataView, tocOffset + 8);
|
|
668
|
+
const expectedUncompressedSize = expectedUncompressedSizes[streamIndex];
|
|
669
|
+
if (uncompressedSize !== expectedUncompressedSize) {
|
|
670
|
+
throw new Error(`SPZLoader: stream ${streamIndex} has ${uncompressedSize} uncompressed bytes, expected ${expectedUncompressedSize}.`);
|
|
671
|
+
}
|
|
672
|
+
if (compressedOffset + compressedSize > data.byteLength) {
|
|
673
|
+
throw new Error(`SPZLoader: stream ${streamIndex} exceeds file byte length.`);
|
|
674
|
+
}
|
|
675
|
+
streamInfos.push({ compressedSize, uncompressedSize, compressedOffset });
|
|
676
|
+
compressedOffset += compressedSize;
|
|
677
|
+
}
|
|
678
|
+
if (compressedOffset !== data.byteLength) {
|
|
679
|
+
throw new Error("SPZLoader: compressed stream byte lengths do not match file byte length.");
|
|
680
|
+
}
|
|
681
|
+
return streamInfos;
|
|
682
|
+
}
|
|
683
|
+
function readSafeUint64(dataView, byteOffset) {
|
|
684
|
+
const value = dataView.getBigUint64(byteOffset, true);
|
|
685
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
686
|
+
throw new Error("SPZLoader: stream byte length exceeds Number.MAX_SAFE_INTEGER.");
|
|
687
|
+
}
|
|
688
|
+
return Number(value);
|
|
689
|
+
}
|
|
690
|
+
function getSPZStreamByteLengths(header) {
|
|
691
|
+
return [
|
|
692
|
+
header.numPoints * 9,
|
|
693
|
+
header.numPoints,
|
|
694
|
+
header.numPoints * 3,
|
|
695
|
+
header.numPoints * 3,
|
|
696
|
+
header.numPoints * 4,
|
|
697
|
+
header.numPoints * getSphericalHarmonicsComponentCount2(header.shDegree)
|
|
698
|
+
].filter((byteLength) => byteLength > 0);
|
|
699
|
+
}
|
|
700
|
+
function validateSPZStreamLengths(streams, streamInfos) {
|
|
701
|
+
for (let streamIndex = 0; streamIndex < streams.length; streamIndex++) {
|
|
702
|
+
if (streams[streamIndex].byteLength !== streamInfos[streamIndex].uncompressedSize) {
|
|
703
|
+
throw new Error(`SPZLoader: decompressed stream ${streamIndex} has unexpected byte length.`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function decodeSPZStreams(header, streams, sourceData) {
|
|
708
|
+
const splatCount = header.numPoints;
|
|
709
|
+
const positions = new Float32Array(splatCount * 3);
|
|
710
|
+
const scales = new Float32Array(splatCount * 3);
|
|
711
|
+
const rotations = new Float32Array(splatCount * 4);
|
|
712
|
+
const colors = new Uint8Array(splatCount * 3);
|
|
713
|
+
const sphericalHarmonicDcs = new Float32Array(splatCount * 3);
|
|
714
|
+
const opacities = new Float32Array(splatCount);
|
|
715
|
+
const sphericalHarmonicsComponentCount = getSphericalHarmonicsComponentCount2(header.shDegree);
|
|
716
|
+
const sphericalHarmonics = sphericalHarmonicsComponentCount ? new Float32Array(splatCount * sphericalHarmonicsComponentCount) : void 0;
|
|
717
|
+
const [positionStream, alphaStream, colorStream, scaleStream, rotationStream, sphericalHarmonicsStream] = streams.map((stream) => new Uint8Array(stream));
|
|
718
|
+
decodeSPZPositions(positionStream, positions, header.fractionalBits);
|
|
719
|
+
decodeSPZScales(scaleStream, scales);
|
|
720
|
+
decodeSPZRotations(rotationStream, rotations);
|
|
721
|
+
decodeSPZColors(colorStream, colors, sphericalHarmonicDcs);
|
|
722
|
+
decodeSPZOpacities(alphaStream, opacities);
|
|
723
|
+
if (sphericalHarmonics && sphericalHarmonicsStream) {
|
|
724
|
+
decodeSPZSphericalHarmonics(sphericalHarmonicsStream, sphericalHarmonics);
|
|
725
|
+
}
|
|
726
|
+
return {
|
|
727
|
+
format: "spz",
|
|
728
|
+
splatCount,
|
|
729
|
+
positions,
|
|
730
|
+
scales,
|
|
731
|
+
rotations,
|
|
732
|
+
colors,
|
|
733
|
+
sphericalHarmonicDcs,
|
|
734
|
+
opacities,
|
|
735
|
+
sphericalHarmonics,
|
|
736
|
+
sphericalHarmonicsComponentCount,
|
|
737
|
+
loaderData: {
|
|
738
|
+
format: "spz",
|
|
739
|
+
...header,
|
|
740
|
+
antialiased: Boolean(header.flags & 1),
|
|
741
|
+
extensionByteLength: getSPZExtensionByteLength(header),
|
|
742
|
+
extensionBytes: getSPZExtensionBytes(sourceData, header)
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function decodeSPZPositions(positionStream, positions, fractionalBits) {
|
|
747
|
+
const scale = 1 / 2 ** fractionalBits;
|
|
748
|
+
for (let componentIndex = 0; componentIndex < positions.length; componentIndex++) {
|
|
749
|
+
const byteOffset = componentIndex * 3;
|
|
750
|
+
let fixed32 = positionStream[byteOffset] | positionStream[byteOffset + 1] << 8 | positionStream[byteOffset + 2] << 16;
|
|
751
|
+
fixed32 |= fixed32 & 8388608 ? 4278190080 : 0;
|
|
752
|
+
positions[componentIndex] = fixed32 * scale;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function decodeSPZScales(scaleStream, scales) {
|
|
756
|
+
for (let componentIndex = 0; componentIndex < scales.length; componentIndex++) {
|
|
757
|
+
scales[componentIndex] = Math.exp(scaleStream[componentIndex] / 16 - 10);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function decodeSPZRotations(rotationStream, rotations) {
|
|
761
|
+
for (let splatIndex = 0; splatIndex < rotations.length / 4; splatIndex++) {
|
|
762
|
+
const byteOffset = splatIndex * 4;
|
|
763
|
+
const component = rotationStream[byteOffset] | rotationStream[byteOffset + 1] << 8 | rotationStream[byteOffset + 2] << 16 | rotationStream[byteOffset + 3] << 24;
|
|
764
|
+
const xyzw = decodeSPZQuaternion(component >>> 0);
|
|
765
|
+
const [w, x, y, z2] = normalizeQuaternion(xyzw[3], xyzw[0], xyzw[1], xyzw[2]);
|
|
766
|
+
const rotationOffset = splatIndex * 4;
|
|
767
|
+
rotations[rotationOffset + 0] = w;
|
|
768
|
+
rotations[rotationOffset + 1] = x;
|
|
769
|
+
rotations[rotationOffset + 2] = y;
|
|
770
|
+
rotations[rotationOffset + 3] = z2;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function decodeSPZQuaternion(component) {
|
|
774
|
+
const rotation = [0, 0, 0, 0];
|
|
775
|
+
const largestComponent = component >>> 30;
|
|
776
|
+
let remainingBits = component;
|
|
777
|
+
let sumSquares = 0;
|
|
778
|
+
for (let componentIndex = 3; componentIndex >= 0; componentIndex--) {
|
|
779
|
+
if (componentIndex !== largestComponent) {
|
|
780
|
+
const magnitude = remainingBits & SPZ_ROTATION_COMPONENT_MASK;
|
|
781
|
+
const isNegative = remainingBits >>> 9 & 1;
|
|
782
|
+
remainingBits >>>= 10;
|
|
783
|
+
let value = SPZ_ROTATION_COMPONENT_SCALE * magnitude / SPZ_ROTATION_COMPONENT_MASK;
|
|
784
|
+
if (isNegative) {
|
|
785
|
+
value = -value;
|
|
786
|
+
}
|
|
787
|
+
rotation[componentIndex] = value;
|
|
788
|
+
sumSquares += value * value;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
rotation[largestComponent] = Math.sqrt(Math.max(0, 1 - sumSquares));
|
|
792
|
+
return rotation;
|
|
793
|
+
}
|
|
794
|
+
function decodeSPZColors(colorStream, colors, sphericalHarmonicDcs) {
|
|
795
|
+
for (let componentIndex = 0; componentIndex < colors.length; componentIndex++) {
|
|
796
|
+
sphericalHarmonicDcs[componentIndex] = (colorStream[componentIndex] / 255 - 0.5) / SPZ_COLOR_SCALE;
|
|
797
|
+
colors[componentIndex] = normalizeColorByte(sphericalHarmonicDcs[componentIndex]);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function decodeSPZOpacities(alphaStream, opacities) {
|
|
801
|
+
for (let splatIndex = 0; splatIndex < opacities.length; splatIndex++) {
|
|
802
|
+
opacities[splatIndex] = alphaStream[splatIndex] / 255;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function decodeSPZSphericalHarmonics(sphericalHarmonicsStream, sphericalHarmonics) {
|
|
806
|
+
for (let componentIndex = 0; componentIndex < sphericalHarmonics.length; componentIndex++) {
|
|
807
|
+
sphericalHarmonics[componentIndex] = (sphericalHarmonicsStream[componentIndex] - 128) / 128;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function normalizeColorByte(sphericalHarmonicDc) {
|
|
811
|
+
return Math.min(Math.max(Math.round((sphericalHarmonicDc * SPZ_COLOR_SCALE + 0.5) * 255), 0), 255);
|
|
812
|
+
}
|
|
813
|
+
function getSphericalHarmonicsComponentCount2(shDegree) {
|
|
814
|
+
switch (shDegree) {
|
|
815
|
+
case 0:
|
|
816
|
+
return 0;
|
|
817
|
+
case 1:
|
|
818
|
+
return 9;
|
|
819
|
+
case 2:
|
|
820
|
+
return 24;
|
|
821
|
+
case 3:
|
|
822
|
+
return 45;
|
|
823
|
+
case 4:
|
|
824
|
+
return 72;
|
|
825
|
+
default:
|
|
826
|
+
throw new Error(`SPZLoader: spherical harmonics degree ${shDegree} is not supported.`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
function getSPZExtensionByteLength(header) {
|
|
830
|
+
return header.flags & SPZ_EXTENSION_FLAG ? header.tocByteOffset - SPZ_HEADER_BYTE_LENGTH : 0;
|
|
831
|
+
}
|
|
832
|
+
function getSPZExtensionBytes(data, header) {
|
|
833
|
+
const extensionByteLength = getSPZExtensionByteLength(header);
|
|
834
|
+
return extensionByteLength ? new Uint8Array(data.slice(SPZ_HEADER_BYTE_LENGTH, header.tocByteOffset)) : void 0;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// dist/spz-loader-types.js
|
|
838
|
+
var VERSION3 = true ? "4.5.0-alpha.1" : "latest";
|
|
839
|
+
var SPZLoader = {
|
|
840
|
+
dataType: null,
|
|
841
|
+
batchType: null,
|
|
842
|
+
...SPZFormat,
|
|
843
|
+
version: VERSION3,
|
|
844
|
+
options: {
|
|
845
|
+
splats: {
|
|
846
|
+
shape: "arrow-table"
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
// dist/spz-loader.js
|
|
852
|
+
var SPZLoaderWithParser = {
|
|
853
|
+
...SPZLoader,
|
|
854
|
+
parse: async (arrayBuffer, options) => parseSPZ(arrayBuffer, options)
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
// dist/rad-zod-schema.js
|
|
858
|
+
var import_zod = require("zod");
|
|
859
|
+
var nonNegativeSafeIntegerSchema = import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
860
|
+
var RADSplatEncodingSchema = import_zod.z.object({
|
|
861
|
+
rgbMin: import_zod.z.number().finite().optional(),
|
|
862
|
+
rgbMax: import_zod.z.number().finite().optional(),
|
|
863
|
+
lnScaleMin: import_zod.z.number().finite().optional(),
|
|
864
|
+
lnScaleMax: import_zod.z.number().finite().optional(),
|
|
865
|
+
sh1Max: import_zod.z.number().finite().optional(),
|
|
866
|
+
sh2Max: import_zod.z.number().finite().optional(),
|
|
867
|
+
sh3Max: import_zod.z.number().finite().optional(),
|
|
868
|
+
lodOpacity: import_zod.z.boolean().optional()
|
|
869
|
+
}).passthrough();
|
|
870
|
+
var RADChunkRangeSchema = import_zod.z.object({
|
|
871
|
+
offset: nonNegativeSafeIntegerSchema,
|
|
872
|
+
bytes: nonNegativeSafeIntegerSchema,
|
|
873
|
+
base: nonNegativeSafeIntegerSchema.optional(),
|
|
874
|
+
count: nonNegativeSafeIntegerSchema.optional(),
|
|
875
|
+
filename: import_zod.z.string().optional()
|
|
876
|
+
}).passthrough();
|
|
877
|
+
var RADMetadataJSONSchema = import_zod.z.object({
|
|
878
|
+
version: import_zod.z.literal(1),
|
|
879
|
+
type: import_zod.z.literal("gsplat"),
|
|
880
|
+
count: nonNegativeSafeIntegerSchema,
|
|
881
|
+
maxSh: nonNegativeSafeIntegerSchema.optional(),
|
|
882
|
+
lodTree: import_zod.z.boolean().optional(),
|
|
883
|
+
chunkSize: nonNegativeSafeIntegerSchema.optional(),
|
|
884
|
+
allChunkBytes: nonNegativeSafeIntegerSchema.optional(),
|
|
885
|
+
chunks: import_zod.z.array(RADChunkRangeSchema),
|
|
886
|
+
splatEncoding: RADSplatEncodingSchema.optional(),
|
|
887
|
+
shCodeCount: nonNegativeSafeIntegerSchema.optional(),
|
|
888
|
+
comment: import_zod.z.string().optional()
|
|
889
|
+
}).passthrough();
|
|
890
|
+
var RADChunkPropertySchema = import_zod.z.object({
|
|
891
|
+
offset: nonNegativeSafeIntegerSchema,
|
|
892
|
+
bytes: nonNegativeSafeIntegerSchema,
|
|
893
|
+
property: import_zod.z.string(),
|
|
894
|
+
encoding: import_zod.z.string(),
|
|
895
|
+
compression: import_zod.z.string().optional(),
|
|
896
|
+
min: import_zod.z.number().finite().optional(),
|
|
897
|
+
max: import_zod.z.number().finite().optional()
|
|
898
|
+
}).passthrough();
|
|
899
|
+
var RADChunkMetadataJSONSchema = import_zod.z.object({
|
|
900
|
+
version: import_zod.z.literal(1),
|
|
901
|
+
base: nonNegativeSafeIntegerSchema,
|
|
902
|
+
count: nonNegativeSafeIntegerSchema,
|
|
903
|
+
payloadBytes: nonNegativeSafeIntegerSchema,
|
|
904
|
+
maxSh: nonNegativeSafeIntegerSchema.optional(),
|
|
905
|
+
lodTree: import_zod.z.boolean().optional(),
|
|
906
|
+
splatEncoding: RADSplatEncodingSchema.optional(),
|
|
907
|
+
properties: import_zod.z.array(RADChunkPropertySchema)
|
|
908
|
+
}).passthrough();
|
|
909
|
+
|
|
910
|
+
// dist/lib/parse-rad.js
|
|
911
|
+
var RAD_MAGIC = 809779538;
|
|
912
|
+
var RAD_CHUNK_MAGIC = 1128546642;
|
|
913
|
+
var RAD_HEADER_BYTE_LENGTH = 8;
|
|
914
|
+
var RAD_CHUNK_PAYLOAD_LENGTH_BYTE_LENGTH = 8;
|
|
915
|
+
var TEXT_DECODER = new TextDecoder();
|
|
916
|
+
function parseRADHeader(data) {
|
|
917
|
+
const metadata = tryParseRADHeader(data);
|
|
918
|
+
if (!metadata) {
|
|
919
|
+
throw new Error("RADLoader: file must contain a complete RAD metadata header.");
|
|
920
|
+
}
|
|
921
|
+
return metadata;
|
|
922
|
+
}
|
|
923
|
+
function tryParseRADHeader(data) {
|
|
924
|
+
const bytes = getUint8Array(data);
|
|
925
|
+
if (bytes.byteLength < RAD_HEADER_BYTE_LENGTH) {
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
929
|
+
const magic = dataView.getUint32(0, true);
|
|
930
|
+
if (magic !== RAD_MAGIC) {
|
|
931
|
+
throw new Error(`RADLoader: RAD0 magic header not found, received 0x${magic.toString(16)}.`);
|
|
932
|
+
}
|
|
933
|
+
const headerJsonByteLength = dataView.getUint32(4, true);
|
|
934
|
+
const headerJsonEnd = RAD_HEADER_BYTE_LENGTH + headerJsonByteLength;
|
|
935
|
+
if (bytes.byteLength < headerJsonEnd) {
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
const rawMetadata = RADMetadataJSONSchema.parse(parseJSON(bytes.subarray(RAD_HEADER_BYTE_LENGTH, headerJsonEnd), "RADLoader: failed to parse RAD metadata JSON."));
|
|
939
|
+
return normalizeRADMetadata(rawMetadata, headerJsonByteLength);
|
|
940
|
+
}
|
|
941
|
+
function parseRADChunkHeader(data) {
|
|
942
|
+
const bytes = getUint8Array(data);
|
|
943
|
+
if (bytes.byteLength < RAD_HEADER_BYTE_LENGTH) {
|
|
944
|
+
throw new Error("RADLoader: RADC chunk must contain an 8-byte metadata header.");
|
|
945
|
+
}
|
|
946
|
+
const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
947
|
+
const magic = dataView.getUint32(0, true);
|
|
948
|
+
if (magic !== RAD_CHUNK_MAGIC) {
|
|
949
|
+
throw new Error(`RADLoader: RADC magic header not found, received 0x${magic.toString(16)}.`);
|
|
950
|
+
}
|
|
951
|
+
const headerJsonByteLength = dataView.getUint32(4, true);
|
|
952
|
+
const headerJsonEnd = RAD_HEADER_BYTE_LENGTH + headerJsonByteLength;
|
|
953
|
+
const payloadByteLengthOffset = RAD_HEADER_BYTE_LENGTH + roundUpToEight(headerJsonByteLength);
|
|
954
|
+
const payloadByteOffset = payloadByteLengthOffset + RAD_CHUNK_PAYLOAD_LENGTH_BYTE_LENGTH;
|
|
955
|
+
if (bytes.byteLength < payloadByteOffset) {
|
|
956
|
+
throw new Error("RADLoader: RADC chunk must contain a complete metadata header.");
|
|
957
|
+
}
|
|
958
|
+
const rawMetadata = RADChunkMetadataJSONSchema.parse(parseJSON(bytes.subarray(RAD_HEADER_BYTE_LENGTH, headerJsonEnd), "RADLoader: failed to parse RADC metadata JSON."));
|
|
959
|
+
const payloadBytes = readSafeUint642(dataView, payloadByteLengthOffset, "RADC payload byte length");
|
|
960
|
+
return normalizeRADChunkMetadata(rawMetadata, headerJsonByteLength, payloadByteOffset, payloadBytes);
|
|
961
|
+
}
|
|
962
|
+
function roundUpToEight(byteLength) {
|
|
963
|
+
return byteLength + 7 & ~7;
|
|
964
|
+
}
|
|
965
|
+
function normalizeRADMetadata(rawMetadata, headerJsonByteLength) {
|
|
966
|
+
return {
|
|
967
|
+
...rawMetadata,
|
|
968
|
+
headerJsonByteLength,
|
|
969
|
+
chunksByteOffset: RAD_HEADER_BYTE_LENGTH + roundUpToEight(headerJsonByteLength)
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function normalizeRADChunkMetadata(rawMetadata, headerJsonByteLength, payloadByteOffset, payloadBytes) {
|
|
973
|
+
if (rawMetadata.payloadBytes !== payloadBytes) {
|
|
974
|
+
throw new Error("RADLoader: RADC metadata payload byte length does not match binary header.");
|
|
975
|
+
}
|
|
976
|
+
return {
|
|
977
|
+
...rawMetadata,
|
|
978
|
+
payloadBytes,
|
|
979
|
+
headerJsonByteLength,
|
|
980
|
+
payloadByteOffset,
|
|
981
|
+
chunkByteLength: payloadByteOffset + payloadBytes
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function parseJSON(bytes, message) {
|
|
985
|
+
try {
|
|
986
|
+
return JSON.parse(TEXT_DECODER.decode(bytes));
|
|
987
|
+
} catch {
|
|
988
|
+
throw new Error(message);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
function readSafeUint642(dataView, byteOffset, fieldName) {
|
|
992
|
+
const value = dataView.getBigUint64(byteOffset, true);
|
|
993
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
994
|
+
throw new Error(`RADLoader: ${fieldName} exceeds Number.MAX_SAFE_INTEGER.`);
|
|
995
|
+
}
|
|
996
|
+
return Number(value);
|
|
997
|
+
}
|
|
998
|
+
function getUint8Array(data) {
|
|
999
|
+
return data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// dist/rad-loader-types.js
|
|
1003
|
+
var VERSION4 = true ? "4.5.0-alpha.1" : "latest";
|
|
1004
|
+
var RADLoader = {
|
|
1005
|
+
dataType: null,
|
|
1006
|
+
batchType: null,
|
|
1007
|
+
...RADFormat,
|
|
1008
|
+
version: VERSION4,
|
|
1009
|
+
options: {}
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
// dist/lib/parse-rad-chunk.js
|
|
1013
|
+
var import_compression2 = require("@loaders.gl/compression");
|
|
1014
|
+
var RAD_PROPERTY_DEFLATE = new import_compression2.DeflateCompression();
|
|
1015
|
+
var RAD_PROPERTY_RAW_DEFLATE = new import_compression2.DeflateCompression({ raw: true });
|
|
1016
|
+
function parseRADChunk(data, options) {
|
|
1017
|
+
return makeGaussianSplatsArrowTable(parseRADChunkToGaussianSplats(data, options));
|
|
1018
|
+
}
|
|
1019
|
+
function parseRADChunkToGaussianSplats(data, options) {
|
|
1020
|
+
var _a, _b;
|
|
1021
|
+
const bytes = getUint8Array2(data);
|
|
1022
|
+
const metadata = getRADChunkDecodeMetadata(parseRADChunkHeader(bytes), options);
|
|
1023
|
+
if (bytes.byteLength < metadata.chunkByteLength) {
|
|
1024
|
+
throw new Error("RADLoader: RADC chunk payload is incomplete.");
|
|
1025
|
+
}
|
|
1026
|
+
const splatCount = metadata.count;
|
|
1027
|
+
const positions = decodeRADFloatProperty(bytes, metadata, requireRADProperty(metadata, "center"), 3);
|
|
1028
|
+
const opacities = decodeRADAlpha(bytes, metadata, splatCount);
|
|
1029
|
+
const rgb = decodeRADRgb(bytes, metadata, splatCount);
|
|
1030
|
+
const scales = decodeRADScales(bytes, metadata, splatCount);
|
|
1031
|
+
const rotations = decodeRADRotations(bytes, metadata, splatCount);
|
|
1032
|
+
const { colors, sphericalHarmonicDcs } = convertRgbToColorColumns(rgb);
|
|
1033
|
+
const includeLoDTree = ((_a = options == null ? void 0 : options.radChunk) == null ? void 0 : _a.includeLoDTree) ?? true;
|
|
1034
|
+
const childCounts = includeLoDTree ? decodeOptionalRADUint16(bytes, metadata, "child_count") : null;
|
|
1035
|
+
const childStarts = includeLoDTree ? decodeOptionalRADUint32(bytes, metadata, "child_start") : null;
|
|
1036
|
+
const includeSphericalHarmonics = ((_b = options == null ? void 0 : options.radChunk) == null ? void 0 : _b.includeSphericalHarmonics) ?? false;
|
|
1037
|
+
const sphericalHarmonics = includeSphericalHarmonics ? decodeRADSphericalHarmonics(bytes, metadata) : null;
|
|
1038
|
+
return {
|
|
1039
|
+
format: "rad",
|
|
1040
|
+
splatCount,
|
|
1041
|
+
positions,
|
|
1042
|
+
scales,
|
|
1043
|
+
rotations,
|
|
1044
|
+
colors,
|
|
1045
|
+
sphericalHarmonicDcs,
|
|
1046
|
+
opacities,
|
|
1047
|
+
sphericalHarmonics: sphericalHarmonics == null ? void 0 : sphericalHarmonics.values,
|
|
1048
|
+
sphericalHarmonicsComponentCount: sphericalHarmonics == null ? void 0 : sphericalHarmonics.componentCount,
|
|
1049
|
+
loaderData: {
|
|
1050
|
+
format: "rad",
|
|
1051
|
+
base: metadata.base,
|
|
1052
|
+
count: metadata.count,
|
|
1053
|
+
maxSh: metadata.maxSh ?? 0,
|
|
1054
|
+
lodTree: metadata.lodTree ?? false,
|
|
1055
|
+
splatEncoding: metadata.splatEncoding,
|
|
1056
|
+
childCounts: childCounts ?? void 0,
|
|
1057
|
+
childStarts: childStarts ?? void 0
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function decodeRADAlpha(bytes, metadata, splatCount) {
|
|
1062
|
+
const property = findRADProperty(metadata, "alpha");
|
|
1063
|
+
if (!property) {
|
|
1064
|
+
return new Float32Array(splatCount).fill(1);
|
|
1065
|
+
}
|
|
1066
|
+
const opacities = decodeRADFloatProperty(bytes, metadata, property, 1);
|
|
1067
|
+
for (let splatIndex = 0; splatIndex < opacities.length; splatIndex++) {
|
|
1068
|
+
opacities[splatIndex] = decodeRADOpacity(opacities[splatIndex], metadata, property);
|
|
1069
|
+
}
|
|
1070
|
+
return opacities;
|
|
1071
|
+
}
|
|
1072
|
+
function decodeRADOpacity(opacity, metadata, property) {
|
|
1073
|
+
var _a;
|
|
1074
|
+
const normalizedOpacity = Math.max(opacity, 0);
|
|
1075
|
+
if (!((_a = metadata.splatEncoding) == null ? void 0 : _a.lodOpacity) || property.encoding !== "r8" && property.encoding !== "r8_delta") {
|
|
1076
|
+
return normalizedOpacity;
|
|
1077
|
+
}
|
|
1078
|
+
return normalizedOpacity * 2;
|
|
1079
|
+
}
|
|
1080
|
+
function decodeRADRgb(bytes, metadata, splatCount) {
|
|
1081
|
+
const property = findRADProperty(metadata, "rgb");
|
|
1082
|
+
if (!property) {
|
|
1083
|
+
return new Float32Array(splatCount * 3).fill(1);
|
|
1084
|
+
}
|
|
1085
|
+
return decodeRADFloatProperty(bytes, metadata, property, 3);
|
|
1086
|
+
}
|
|
1087
|
+
function decodeRADScales(bytes, metadata, splatCount) {
|
|
1088
|
+
const property = findRADProperty(metadata, "scales");
|
|
1089
|
+
if (!property) {
|
|
1090
|
+
return new Float32Array(splatCount * 3).fill(1);
|
|
1091
|
+
}
|
|
1092
|
+
return decodeRADFloatProperty(bytes, metadata, property, 3);
|
|
1093
|
+
}
|
|
1094
|
+
function decodeRADRotations(bytes, metadata, splatCount) {
|
|
1095
|
+
const property = findRADProperty(metadata, "orientation");
|
|
1096
|
+
if (!property) {
|
|
1097
|
+
return makeIdentityRotations(splatCount);
|
|
1098
|
+
}
|
|
1099
|
+
const propertyBytes = getRADPropertyBytes(bytes, metadata, property);
|
|
1100
|
+
switch (property.encoding) {
|
|
1101
|
+
case "oct88r8":
|
|
1102
|
+
return decodeRADOct88R8Rotations(propertyBytes, splatCount);
|
|
1103
|
+
case "f32":
|
|
1104
|
+
case "f16": {
|
|
1105
|
+
const xyz = decodeRADFloatProperty(bytes, metadata, property, 3);
|
|
1106
|
+
const rotations = new Float32Array(splatCount * 4);
|
|
1107
|
+
for (let splatIndex = 0; splatIndex < splatCount; splatIndex++) {
|
|
1108
|
+
const xyzOffset = splatIndex * 3;
|
|
1109
|
+
const rotationOffset = splatIndex * 4;
|
|
1110
|
+
const x = xyz[xyzOffset + 0];
|
|
1111
|
+
const y = xyz[xyzOffset + 1];
|
|
1112
|
+
const z2 = xyz[xyzOffset + 2];
|
|
1113
|
+
const w = Math.sqrt(Math.max(0, 1 - x * x - y * y - z2 * z2));
|
|
1114
|
+
const normalized = normalizeQuaternion(w, x, y, z2);
|
|
1115
|
+
rotations.set(normalized, rotationOffset);
|
|
1116
|
+
}
|
|
1117
|
+
return rotations;
|
|
1118
|
+
}
|
|
1119
|
+
default:
|
|
1120
|
+
throw new Error(`RADLoader: unsupported orientation encoding ${property.encoding}.`);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function decodeRADSphericalHarmonics(bytes, metadata) {
|
|
1124
|
+
const shProperties = [
|
|
1125
|
+
{ property: findRADProperty(metadata, "sh1"), dimensions: 9 },
|
|
1126
|
+
{ property: findRADProperty(metadata, "sh2"), dimensions: 15 },
|
|
1127
|
+
{ property: findRADProperty(metadata, "sh3"), dimensions: 21 }
|
|
1128
|
+
].filter((entry) => entry.property !== void 0);
|
|
1129
|
+
if (shProperties.length === 0) {
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
const decodedProperties = shProperties.map((entry) => ({
|
|
1133
|
+
values: decodeRADFloatProperty(bytes, metadata, entry.property, entry.dimensions),
|
|
1134
|
+
dimensions: entry.dimensions
|
|
1135
|
+
}));
|
|
1136
|
+
const componentCount = decodedProperties.reduce((total, entry) => total + entry.dimensions, 0);
|
|
1137
|
+
const values = new Float32Array(metadata.count * componentCount);
|
|
1138
|
+
for (let splatIndex = 0; splatIndex < metadata.count; splatIndex++) {
|
|
1139
|
+
let outputOffset = splatIndex * componentCount;
|
|
1140
|
+
for (const decodedProperty of decodedProperties) {
|
|
1141
|
+
const inputOffset = splatIndex * decodedProperty.dimensions;
|
|
1142
|
+
values.set(decodedProperty.values.subarray(inputOffset, inputOffset + decodedProperty.dimensions), outputOffset);
|
|
1143
|
+
outputOffset += decodedProperty.dimensions;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return { values, componentCount };
|
|
1147
|
+
}
|
|
1148
|
+
function decodeRADFloatProperty(bytes, metadata, property, dimensions) {
|
|
1149
|
+
const propertyBytes = getRADPropertyBytes(bytes, metadata, property);
|
|
1150
|
+
switch (property.encoding) {
|
|
1151
|
+
case "f32":
|
|
1152
|
+
return decodePlanarFloat32(propertyBytes, dimensions, metadata.count);
|
|
1153
|
+
case "f16":
|
|
1154
|
+
return decodePlanarFloat16(propertyBytes, dimensions, metadata.count);
|
|
1155
|
+
case "f32_lebytes":
|
|
1156
|
+
return decodeLeBytesFloat32(propertyBytes, dimensions, metadata.count);
|
|
1157
|
+
case "f16_lebytes":
|
|
1158
|
+
return decodeLeBytesFloat16(propertyBytes, dimensions, metadata.count);
|
|
1159
|
+
case "r8":
|
|
1160
|
+
return decodeR8(propertyBytes, dimensions, metadata.count, getPropertyMin(metadata, property), getPropertyMax(metadata, property));
|
|
1161
|
+
case "r8_delta":
|
|
1162
|
+
return decodeR8Delta(propertyBytes, dimensions, metadata.count, getPropertyMin(metadata, property), getPropertyMax(metadata, property));
|
|
1163
|
+
case "s8":
|
|
1164
|
+
return decodeS8(propertyBytes, dimensions, metadata.count, getPropertyMax(metadata, property));
|
|
1165
|
+
case "s8_delta":
|
|
1166
|
+
return decodeS8Delta(propertyBytes, dimensions, metadata.count, getPropertyMax(metadata, property));
|
|
1167
|
+
case "ln_0r8":
|
|
1168
|
+
return decodeScaleR8(propertyBytes, dimensions, metadata.count, getPropertyMin(metadata, property), getPropertyMax(metadata, property));
|
|
1169
|
+
case "ln_f16":
|
|
1170
|
+
return decodeLogFloat16(propertyBytes, dimensions, metadata.count);
|
|
1171
|
+
default:
|
|
1172
|
+
throw new Error(`RADLoader: unsupported ${property.property} encoding ${property.encoding}.`);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function decodeOptionalRADUint16(bytes, metadata, propertyName) {
|
|
1176
|
+
const property = findRADProperty(metadata, propertyName);
|
|
1177
|
+
if (!property) {
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
if (property.encoding !== "u16") {
|
|
1181
|
+
throw new Error(`RADLoader: unsupported ${propertyName} encoding ${property.encoding}.`);
|
|
1182
|
+
}
|
|
1183
|
+
const propertyBytes = getRADPropertyBytes(bytes, metadata, property);
|
|
1184
|
+
const values = new Uint16Array(metadata.count);
|
|
1185
|
+
for (let splatIndex = 0; splatIndex < metadata.count; splatIndex++) {
|
|
1186
|
+
values[splatIndex] = propertyBytes[splatIndex * 2] | propertyBytes[splatIndex * 2 + 1] << 8;
|
|
1187
|
+
}
|
|
1188
|
+
return values;
|
|
1189
|
+
}
|
|
1190
|
+
function decodeOptionalRADUint32(bytes, metadata, propertyName) {
|
|
1191
|
+
const property = findRADProperty(metadata, propertyName);
|
|
1192
|
+
if (!property) {
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
if (property.encoding !== "u32") {
|
|
1196
|
+
throw new Error(`RADLoader: unsupported ${propertyName} encoding ${property.encoding}.`);
|
|
1197
|
+
}
|
|
1198
|
+
const propertyBytes = getRADPropertyBytes(bytes, metadata, property);
|
|
1199
|
+
const dataView = new DataView(propertyBytes.buffer, propertyBytes.byteOffset, propertyBytes.byteLength);
|
|
1200
|
+
const values = new Uint32Array(metadata.count);
|
|
1201
|
+
for (let splatIndex = 0; splatIndex < metadata.count; splatIndex++) {
|
|
1202
|
+
values[splatIndex] = dataView.getUint32(splatIndex * 4, true);
|
|
1203
|
+
}
|
|
1204
|
+
return values;
|
|
1205
|
+
}
|
|
1206
|
+
function getRADPropertyBytes(bytes, metadata, property) {
|
|
1207
|
+
const paddedByteLength = roundUpToEight(property.bytes);
|
|
1208
|
+
const byteOffset = metadata.payloadByteOffset + property.offset;
|
|
1209
|
+
const byteEnd = byteOffset + paddedByteLength;
|
|
1210
|
+
if (byteEnd > bytes.byteLength || property.offset + paddedByteLength > metadata.payloadBytes) {
|
|
1211
|
+
throw new Error(`RADLoader: property ${property.property} exceeds RADC payload byte length.`);
|
|
1212
|
+
}
|
|
1213
|
+
const propertyBytes = bytes.subarray(byteOffset, byteOffset + property.bytes);
|
|
1214
|
+
if (!property.compression) {
|
|
1215
|
+
return propertyBytes;
|
|
1216
|
+
}
|
|
1217
|
+
if (property.compression !== "gz") {
|
|
1218
|
+
throw new Error(`RADLoader: unsupported property compression ${property.compression}.`);
|
|
1219
|
+
}
|
|
1220
|
+
return decompressRADProperty(propertyBytes, property.property);
|
|
1221
|
+
}
|
|
1222
|
+
function decompressRADProperty(bytes, propertyName) {
|
|
1223
|
+
const arrayBuffer = copyToArrayBuffer(bytes);
|
|
1224
|
+
try {
|
|
1225
|
+
return new Uint8Array(RAD_PROPERTY_DEFLATE.decompressSync(arrayBuffer));
|
|
1226
|
+
} catch {
|
|
1227
|
+
try {
|
|
1228
|
+
return new Uint8Array(RAD_PROPERTY_RAW_DEFLATE.decompressSync(arrayBuffer));
|
|
1229
|
+
} catch {
|
|
1230
|
+
throw new Error(`RADLoader: failed to decompress RADC property ${propertyName}.`);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
function decodePlanarFloat32(bytes, dimensions, count) {
|
|
1235
|
+
const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1236
|
+
const values = new Float32Array(count * dimensions);
|
|
1237
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1238
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1239
|
+
values[rowIndex * dimensions + dimension] = dataView.getFloat32((dimension * count + rowIndex) * 4, true);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return values;
|
|
1243
|
+
}
|
|
1244
|
+
function decodePlanarFloat16(bytes, dimensions, count) {
|
|
1245
|
+
const values = new Float32Array(count * dimensions);
|
|
1246
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1247
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1248
|
+
const byteOffset = (dimension * count + rowIndex) * 2;
|
|
1249
|
+
values[rowIndex * dimensions + dimension] = decodeFloat16(bytes[byteOffset] | bytes[byteOffset + 1] << 8);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return values;
|
|
1253
|
+
}
|
|
1254
|
+
function decodeLeBytesFloat32(bytes, dimensions, count) {
|
|
1255
|
+
const values = new Float32Array(count * dimensions);
|
|
1256
|
+
const stride = count * dimensions;
|
|
1257
|
+
const scratch = new Uint8Array(4);
|
|
1258
|
+
const scratchView = new DataView(scratch.buffer);
|
|
1259
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1260
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1261
|
+
const byteOffset = dimension * count + rowIndex;
|
|
1262
|
+
for (let byteIndex = 0; byteIndex < 4; byteIndex++) {
|
|
1263
|
+
scratch[byteIndex] = bytes[byteOffset + stride * byteIndex];
|
|
1264
|
+
}
|
|
1265
|
+
values[rowIndex * dimensions + dimension] = scratchView.getFloat32(0, true);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return values;
|
|
1269
|
+
}
|
|
1270
|
+
function decodeLeBytesFloat16(bytes, dimensions, count) {
|
|
1271
|
+
const values = new Float32Array(count * dimensions);
|
|
1272
|
+
const stride = count * dimensions;
|
|
1273
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1274
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1275
|
+
const byteOffset = dimension * count + rowIndex;
|
|
1276
|
+
values[rowIndex * dimensions + dimension] = decodeFloat16(bytes[byteOffset] | bytes[byteOffset + stride] << 8);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return values;
|
|
1280
|
+
}
|
|
1281
|
+
function decodeR8(bytes, dimensions, count, min, max) {
|
|
1282
|
+
const values = new Float32Array(count * dimensions);
|
|
1283
|
+
const range = max - min;
|
|
1284
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1285
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1286
|
+
values[rowIndex * dimensions + dimension] = bytes[dimension * count + rowIndex] / 255 * range + min;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return values;
|
|
1290
|
+
}
|
|
1291
|
+
function decodeR8Delta(bytes, dimensions, count, min, max) {
|
|
1292
|
+
const values = new Float32Array(count * dimensions);
|
|
1293
|
+
const range = max - min;
|
|
1294
|
+
const last = new Uint8Array(dimensions);
|
|
1295
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1296
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1297
|
+
const byteOffset = dimension * count + rowIndex;
|
|
1298
|
+
const value = last[dimension] + bytes[byteOffset] & 255;
|
|
1299
|
+
last[dimension] = value;
|
|
1300
|
+
values[rowIndex * dimensions + dimension] = value / 255 * range + min;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return values;
|
|
1304
|
+
}
|
|
1305
|
+
function decodeS8(bytes, dimensions, count, max) {
|
|
1306
|
+
const values = new Float32Array(count * dimensions);
|
|
1307
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1308
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1309
|
+
const byte = bytes[dimension * count + rowIndex];
|
|
1310
|
+
values[rowIndex * dimensions + dimension] = (byte << 24 >> 24) / 127 * max;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return values;
|
|
1314
|
+
}
|
|
1315
|
+
function decodeS8Delta(bytes, dimensions, count, max) {
|
|
1316
|
+
const values = new Float32Array(count * dimensions);
|
|
1317
|
+
const last = new Uint8Array(dimensions);
|
|
1318
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1319
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1320
|
+
const byteOffset = dimension * count + rowIndex;
|
|
1321
|
+
const value = last[dimension] + bytes[byteOffset] & 255;
|
|
1322
|
+
last[dimension] = value;
|
|
1323
|
+
values[rowIndex * dimensions + dimension] = (value << 24 >> 24) / 127 * max;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return values;
|
|
1327
|
+
}
|
|
1328
|
+
function decodeScaleR8(bytes, dimensions, count, min, max) {
|
|
1329
|
+
const values = new Float32Array(count * dimensions);
|
|
1330
|
+
for (let rowIndex = 0; rowIndex < count; rowIndex++) {
|
|
1331
|
+
for (let dimension = 0; dimension < dimensions; dimension++) {
|
|
1332
|
+
values[rowIndex * dimensions + dimension] = decodeScale8(bytes[dimension * count + rowIndex], min, max);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return values;
|
|
1336
|
+
}
|
|
1337
|
+
function decodeLogFloat16(bytes, dimensions, count) {
|
|
1338
|
+
const logValues = decodePlanarFloat16(bytes, dimensions, count);
|
|
1339
|
+
for (let valueIndex = 0; valueIndex < logValues.length; valueIndex++) {
|
|
1340
|
+
logValues[valueIndex] = Math.exp(logValues[valueIndex]);
|
|
1341
|
+
}
|
|
1342
|
+
return logValues;
|
|
1343
|
+
}
|
|
1344
|
+
function decodeRADOct88R8Rotations(bytes, count) {
|
|
1345
|
+
const rotations = new Float32Array(count * 4);
|
|
1346
|
+
for (let splatIndex = 0; splatIndex < count; splatIndex++) {
|
|
1347
|
+
const byteOffset = splatIndex * 3;
|
|
1348
|
+
const [x, y, z2, w] = decodeQuatOct888(bytes[byteOffset], bytes[byteOffset + 1], bytes[byteOffset + 2]);
|
|
1349
|
+
rotations.set(normalizeQuaternion(w, x, y, z2), splatIndex * 4);
|
|
1350
|
+
}
|
|
1351
|
+
return rotations;
|
|
1352
|
+
}
|
|
1353
|
+
function convertRgbToColorColumns(rgb) {
|
|
1354
|
+
const colors = new Uint8Array(rgb.length);
|
|
1355
|
+
const sphericalHarmonicDcs = new Float32Array(rgb.length);
|
|
1356
|
+
for (let componentIndex = 0; componentIndex < rgb.length; componentIndex++) {
|
|
1357
|
+
const component = rgb[componentIndex];
|
|
1358
|
+
colors[componentIndex] = normalizeColorByte2(component);
|
|
1359
|
+
sphericalHarmonicDcs[componentIndex] = (component - 0.5) / SH_C0;
|
|
1360
|
+
}
|
|
1361
|
+
return { colors, sphericalHarmonicDcs };
|
|
1362
|
+
}
|
|
1363
|
+
function makeIdentityRotations(count) {
|
|
1364
|
+
const rotations = new Float32Array(count * 4);
|
|
1365
|
+
for (let splatIndex = 0; splatIndex < count; splatIndex++) {
|
|
1366
|
+
rotations[splatIndex * 4] = 1;
|
|
1367
|
+
}
|
|
1368
|
+
return rotations;
|
|
1369
|
+
}
|
|
1370
|
+
function decodeScale8(scale, min, max) {
|
|
1371
|
+
if (scale === 0) {
|
|
1372
|
+
return 0;
|
|
1373
|
+
}
|
|
1374
|
+
const logScaleStep = (max - min) / 254;
|
|
1375
|
+
return Math.exp(min + (scale - 1) * logScaleStep);
|
|
1376
|
+
}
|
|
1377
|
+
function decodeQuatOct888(u, v, r) {
|
|
1378
|
+
let x = u / 255 * 2 - 1;
|
|
1379
|
+
let y = v / 255 * 2 - 1;
|
|
1380
|
+
const z2 = 1 - Math.abs(x) - Math.abs(y);
|
|
1381
|
+
const t = Math.max(-z2, 0);
|
|
1382
|
+
x = x >= 0 ? x - t : x + t;
|
|
1383
|
+
y = y >= 0 ? y - t : y + t;
|
|
1384
|
+
const length = Math.hypot(x, y, z2);
|
|
1385
|
+
const axisX = length > Number.EPSILON ? x / length : 1;
|
|
1386
|
+
const axisY = length > Number.EPSILON ? y / length : 0;
|
|
1387
|
+
const axisZ = length > Number.EPSILON ? z2 / length : 0;
|
|
1388
|
+
const halfTheta = r / 255 * 0.5 * Math.PI;
|
|
1389
|
+
const sinHalfTheta = Math.sin(halfTheta);
|
|
1390
|
+
return [axisX * sinHalfTheta, axisY * sinHalfTheta, axisZ * sinHalfTheta, Math.cos(halfTheta)];
|
|
1391
|
+
}
|
|
1392
|
+
function requireRADProperty(metadata, propertyName) {
|
|
1393
|
+
const property = findRADProperty(metadata, propertyName);
|
|
1394
|
+
if (!property) {
|
|
1395
|
+
throw new Error(`RADLoader: RADC chunk is missing ${propertyName} property.`);
|
|
1396
|
+
}
|
|
1397
|
+
return property;
|
|
1398
|
+
}
|
|
1399
|
+
function findRADProperty(metadata, propertyName) {
|
|
1400
|
+
return metadata.properties.find((property) => property.property === propertyName);
|
|
1401
|
+
}
|
|
1402
|
+
function getRADChunkDecodeMetadata(metadata, options) {
|
|
1403
|
+
var _a;
|
|
1404
|
+
const splatEncoding = (_a = options == null ? void 0 : options.radChunk) == null ? void 0 : _a.splatEncoding;
|
|
1405
|
+
if (!splatEncoding) {
|
|
1406
|
+
return metadata;
|
|
1407
|
+
}
|
|
1408
|
+
return {
|
|
1409
|
+
...metadata,
|
|
1410
|
+
splatEncoding: {
|
|
1411
|
+
...splatEncoding,
|
|
1412
|
+
...metadata.splatEncoding
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
function getPropertyMin(metadata, property) {
|
|
1417
|
+
if (property.min === void 0) {
|
|
1418
|
+
const fallback = getSplatEncodingMinimum(metadata.splatEncoding, property.property);
|
|
1419
|
+
if (fallback !== void 0) {
|
|
1420
|
+
return fallback;
|
|
1421
|
+
}
|
|
1422
|
+
throw new Error(`RADLoader: RADC property ${property.property} is missing min.`);
|
|
1423
|
+
}
|
|
1424
|
+
return property.min;
|
|
1425
|
+
}
|
|
1426
|
+
function getPropertyMax(metadata, property) {
|
|
1427
|
+
if (property.max === void 0) {
|
|
1428
|
+
const fallback = getSplatEncodingMaximum(metadata.splatEncoding, property.property);
|
|
1429
|
+
if (fallback !== void 0) {
|
|
1430
|
+
return fallback;
|
|
1431
|
+
}
|
|
1432
|
+
throw new Error(`RADLoader: RADC property ${property.property} is missing max.`);
|
|
1433
|
+
}
|
|
1434
|
+
return property.max;
|
|
1435
|
+
}
|
|
1436
|
+
function getSplatEncodingMinimum(splatEncoding, propertyName) {
|
|
1437
|
+
switch (propertyName) {
|
|
1438
|
+
case "rgb":
|
|
1439
|
+
return splatEncoding == null ? void 0 : splatEncoding.rgbMin;
|
|
1440
|
+
case "scales":
|
|
1441
|
+
return splatEncoding == null ? void 0 : splatEncoding.lnScaleMin;
|
|
1442
|
+
default:
|
|
1443
|
+
return void 0;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
function getSplatEncodingMaximum(splatEncoding, propertyName) {
|
|
1447
|
+
switch (propertyName) {
|
|
1448
|
+
case "rgb":
|
|
1449
|
+
return splatEncoding == null ? void 0 : splatEncoding.rgbMax;
|
|
1450
|
+
case "scales":
|
|
1451
|
+
return splatEncoding == null ? void 0 : splatEncoding.lnScaleMax;
|
|
1452
|
+
case "sh1":
|
|
1453
|
+
return splatEncoding == null ? void 0 : splatEncoding.sh1Max;
|
|
1454
|
+
case "sh2":
|
|
1455
|
+
return splatEncoding == null ? void 0 : splatEncoding.sh2Max;
|
|
1456
|
+
case "sh3":
|
|
1457
|
+
return splatEncoding == null ? void 0 : splatEncoding.sh3Max;
|
|
1458
|
+
default:
|
|
1459
|
+
return void 0;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
function normalizeColorByte2(value) {
|
|
1463
|
+
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
|
|
1464
|
+
}
|
|
1465
|
+
function copyToArrayBuffer(bytes) {
|
|
1466
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
1467
|
+
copy.set(bytes);
|
|
1468
|
+
return copy.buffer;
|
|
1469
|
+
}
|
|
1470
|
+
function getUint8Array2(data) {
|
|
1471
|
+
return data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// dist/rad-loader.js
|
|
1475
|
+
var RADLoaderWithParser = {
|
|
1476
|
+
...RADLoader,
|
|
1477
|
+
parse: async (arrayBuffer) => parseRADHeader(arrayBuffer),
|
|
1478
|
+
parseSync: (arrayBuffer) => parseRADHeader(arrayBuffer)
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
// dist/rad-source-loader.js
|
|
1482
|
+
var import_loader_utils = require("@loaders.gl/loader-utils");
|
|
1483
|
+
var DEFAULT_RAD_HEADER_BYTE_LENGTHS = [64 * 1024, 256 * 1024, 1024 * 1024];
|
|
1484
|
+
var DEFAULT_RAD_MAX_CONCURRENT_CHUNK_REQUESTS = 4;
|
|
1485
|
+
var VERSION5 = true ? "4.5.0-alpha.1" : "latest";
|
|
1486
|
+
var RADSourceLoader = {
|
|
1487
|
+
dataType: null,
|
|
1488
|
+
batchType: null,
|
|
1489
|
+
...RADFormat,
|
|
1490
|
+
name: "RADSourceLoader",
|
|
1491
|
+
version: VERSION5,
|
|
1492
|
+
type: "rad",
|
|
1493
|
+
fromUrl: true,
|
|
1494
|
+
fromBlob: true,
|
|
1495
|
+
options: {
|
|
1496
|
+
rad: {
|
|
1497
|
+
headers: void 0,
|
|
1498
|
+
withCredentials: false,
|
|
1499
|
+
headerByteLengths: void 0
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
defaultOptions: {
|
|
1503
|
+
rad: {
|
|
1504
|
+
headers: void 0,
|
|
1505
|
+
withCredentials: false,
|
|
1506
|
+
headerByteLengths: void 0
|
|
1507
|
+
}
|
|
1508
|
+
},
|
|
1509
|
+
testURL: (url) => /\.rad(?:$|[?#])/i.test(url),
|
|
1510
|
+
createDataSource: (data, options) => new RADSource(data, options)
|
|
1511
|
+
};
|
|
1512
|
+
var RADSource = class extends import_loader_utils.DataSource {
|
|
1513
|
+
/** Shared metadata promise exposed for source-style integrations. */
|
|
1514
|
+
metadata;
|
|
1515
|
+
/** Lazily initialized RAD header promise. */
|
|
1516
|
+
_metadataPromise = null;
|
|
1517
|
+
/** Lazily initialized per-chunk metadata promises. */
|
|
1518
|
+
_chunkMetadataPromises = /* @__PURE__ */ new Map();
|
|
1519
|
+
constructor(data, options) {
|
|
1520
|
+
super(data, options, RADSourceLoader.defaultOptions);
|
|
1521
|
+
this.metadata = this.getMetadata();
|
|
1522
|
+
}
|
|
1523
|
+
/** Ensures the RAD header metadata has been loaded. */
|
|
1524
|
+
async initialize() {
|
|
1525
|
+
await this.getMetadata();
|
|
1526
|
+
}
|
|
1527
|
+
/** Returns parsed Spark RAD top-level metadata. */
|
|
1528
|
+
async getMetadata() {
|
|
1529
|
+
if (!this._metadataPromise) {
|
|
1530
|
+
this._metadataPromise = this._loadMetadata();
|
|
1531
|
+
}
|
|
1532
|
+
return await this._metadataPromise;
|
|
1533
|
+
}
|
|
1534
|
+
/** Returns the number of chunks declared by the RAD metadata. */
|
|
1535
|
+
async getChunkCount() {
|
|
1536
|
+
const metadata = await this.getMetadata();
|
|
1537
|
+
return metadata.chunks.length;
|
|
1538
|
+
}
|
|
1539
|
+
/** Returns the URL used for a chunk, or `null` for Blob-backed inline chunks. */
|
|
1540
|
+
async getChunkUrl(chunkIndex) {
|
|
1541
|
+
const metadata = await this.getMetadata();
|
|
1542
|
+
const chunk = this._getChunkRange(metadata, chunkIndex);
|
|
1543
|
+
if (chunk.filename) {
|
|
1544
|
+
return resolveRADChunkUrl(this.url, chunk.filename);
|
|
1545
|
+
}
|
|
1546
|
+
return typeof this.data === "string" ? this.url : null;
|
|
1547
|
+
}
|
|
1548
|
+
/** Fetches one raw RADC chunk by index. */
|
|
1549
|
+
async getChunk(chunkIndex, options = {}) {
|
|
1550
|
+
const metadata = await this.getMetadata();
|
|
1551
|
+
const chunk = this._getChunkRange(metadata, chunkIndex);
|
|
1552
|
+
if (chunk.filename) {
|
|
1553
|
+
if (typeof this.data !== "string") {
|
|
1554
|
+
throw new Error("RADSource: sidecar RADC chunks require a URL-backed RAD source.");
|
|
1555
|
+
}
|
|
1556
|
+
return await this._fetchArrayBuffer(resolveRADChunkUrl(this.url, chunk.filename), void 0, {
|
|
1557
|
+
signal: options.signal
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
if (chunk.bytes === 0) {
|
|
1561
|
+
return new ArrayBuffer(0);
|
|
1562
|
+
}
|
|
1563
|
+
const byteOffset = metadata.chunksByteOffset + chunk.offset;
|
|
1564
|
+
if (typeof this.data !== "string") {
|
|
1565
|
+
return await this.data.slice(byteOffset, byteOffset + chunk.bytes).arrayBuffer();
|
|
1566
|
+
}
|
|
1567
|
+
return await this._fetchArrayBuffer(this.url, { offset: byteOffset, bytes: chunk.bytes }, { signal: options.signal });
|
|
1568
|
+
}
|
|
1569
|
+
/** Fetches and parses metadata for one RADC chunk. */
|
|
1570
|
+
async getChunkMetadata(chunkIndex, options = {}) {
|
|
1571
|
+
let chunkMetadataPromise = this._chunkMetadataPromises.get(chunkIndex);
|
|
1572
|
+
if (!chunkMetadataPromise) {
|
|
1573
|
+
chunkMetadataPromise = this.getChunk(chunkIndex, options).then((chunk) => parseRADChunkHeader(chunk));
|
|
1574
|
+
this._chunkMetadataPromises.set(chunkIndex, chunkMetadataPromise);
|
|
1575
|
+
}
|
|
1576
|
+
return await chunkMetadataPromise;
|
|
1577
|
+
}
|
|
1578
|
+
/** Fetches and decodes one RADC chunk into raw Gaussian splat arrays. */
|
|
1579
|
+
async getChunkSplats(chunkIndex, options = {}) {
|
|
1580
|
+
const metadata = await this.getMetadata();
|
|
1581
|
+
return parseRADChunkToGaussianSplats(await this.getChunk(chunkIndex, options), this._getChunkDecodeOptions(metadata, options));
|
|
1582
|
+
}
|
|
1583
|
+
/** Fetches and decodes one RADC chunk into a Mesh Arrow table. */
|
|
1584
|
+
async getChunkTable(chunkIndex, options = {}) {
|
|
1585
|
+
const metadata = await this.getMetadata();
|
|
1586
|
+
return parseRADChunk(await this.getChunk(chunkIndex, options), this._getChunkDecodeOptions(metadata, options));
|
|
1587
|
+
}
|
|
1588
|
+
/** Iterates decoded RADC chunks as Mesh Arrow tables. */
|
|
1589
|
+
async *getChunkTables(options = {}) {
|
|
1590
|
+
const metadata = await this.getMetadata();
|
|
1591
|
+
const startChunkIndex = options.startChunkIndex ?? 0;
|
|
1592
|
+
const maxChunks = options.maxChunks ?? Number.POSITIVE_INFINITY;
|
|
1593
|
+
const maxSplats = options.maxSplats ?? Number.POSITIVE_INFINITY;
|
|
1594
|
+
let chunkCount = 0;
|
|
1595
|
+
let splatCount = 0;
|
|
1596
|
+
if (options.pruneLoadedLoDParents) {
|
|
1597
|
+
const chunkIndices = getRADChunkTableIteratorIndices(metadata, startChunkIndex, maxChunks, maxSplats);
|
|
1598
|
+
const splatChunks = await this._getChunkSplatsConcurrently(chunkIndices, options);
|
|
1599
|
+
let loadedGlobalSplatEnd = 0;
|
|
1600
|
+
for (const splats of splatChunks) {
|
|
1601
|
+
chunkCount++;
|
|
1602
|
+
splatCount += splats.splatCount;
|
|
1603
|
+
const base = getRADChunkSplatBase(splats);
|
|
1604
|
+
loadedGlobalSplatEnd = Math.max(loadedGlobalSplatEnd, base + splats.splatCount);
|
|
1605
|
+
}
|
|
1606
|
+
for (const splats of splatChunks) {
|
|
1607
|
+
yield makeGaussianSplatsArrowTable(pruneLoadedLoDParents(splats, loadedGlobalSplatEnd));
|
|
1608
|
+
}
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
for (let chunkIndex = startChunkIndex; chunkIndex < metadata.chunks.length && chunkCount < maxChunks && splatCount < maxSplats; chunkIndex++) {
|
|
1612
|
+
const table = await this.getChunkTable(chunkIndex, options);
|
|
1613
|
+
chunkCount++;
|
|
1614
|
+
splatCount += table.data.numRows;
|
|
1615
|
+
yield table;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
/** Fetches and decodes RAD chunks with bounded concurrency while preserving chunk order. */
|
|
1619
|
+
async _getChunkSplatsConcurrently(chunkIndices, options) {
|
|
1620
|
+
const splatChunks = new Array(chunkIndices.length);
|
|
1621
|
+
const maxConcurrentChunkRequests = getRADMaxConcurrentChunkRequests(options.maxConcurrentChunkRequests);
|
|
1622
|
+
let nextResultIndex = 0;
|
|
1623
|
+
async function loadNextChunk(source) {
|
|
1624
|
+
while (nextResultIndex < chunkIndices.length) {
|
|
1625
|
+
const resultIndex = nextResultIndex++;
|
|
1626
|
+
const chunkIndex = chunkIndices[resultIndex];
|
|
1627
|
+
splatChunks[resultIndex] = await source.getChunkSplats(chunkIndex, options);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
const workerCount = Math.min(maxConcurrentChunkRequests, chunkIndices.length);
|
|
1631
|
+
await Promise.all(Array.from({ length: workerCount }, () => loadNextChunk(this)));
|
|
1632
|
+
return splatChunks;
|
|
1633
|
+
}
|
|
1634
|
+
/** Adds source-level RAD decode metadata to chunk decode options. */
|
|
1635
|
+
_getChunkDecodeOptions(metadata, options) {
|
|
1636
|
+
return {
|
|
1637
|
+
...options,
|
|
1638
|
+
radChunk: {
|
|
1639
|
+
splatEncoding: metadata.splatEncoding,
|
|
1640
|
+
...options.radChunk
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
/** Loads and parses the top-level RAD metadata. */
|
|
1645
|
+
async _loadMetadata() {
|
|
1646
|
+
var _a, _b;
|
|
1647
|
+
const headerByteLengths = ((_b = (_a = this.options.rad) == null ? void 0 : _a.headerByteLengths) == null ? void 0 : _b.length) ? this.options.rad.headerByteLengths : DEFAULT_RAD_HEADER_BYTE_LENGTHS;
|
|
1648
|
+
if (typeof this.data !== "string") {
|
|
1649
|
+
const byteLength = Math.min(this.data.size, Math.max(...headerByteLengths));
|
|
1650
|
+
const metadata = tryParseRADHeader(await this.data.slice(0, byteLength).arrayBuffer());
|
|
1651
|
+
if (metadata) {
|
|
1652
|
+
return metadata;
|
|
1653
|
+
}
|
|
1654
|
+
throw new Error("RADSource: Blob does not contain a complete RAD metadata header.");
|
|
1655
|
+
}
|
|
1656
|
+
for (const byteLength of headerByteLengths) {
|
|
1657
|
+
const metadata = tryParseRADHeader(await this._fetchArrayBuffer(this.url, { offset: 0, bytes: byteLength }));
|
|
1658
|
+
if (metadata) {
|
|
1659
|
+
return metadata;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
throw new Error("RADSource: failed to load a complete RAD metadata header.");
|
|
1663
|
+
}
|
|
1664
|
+
/** Returns a validated chunk table entry. */
|
|
1665
|
+
_getChunkRange(metadata, chunkIndex) {
|
|
1666
|
+
if (!Number.isInteger(chunkIndex) || chunkIndex < 0 || chunkIndex >= metadata.chunks.length) {
|
|
1667
|
+
throw new Error(`RADSource: chunk index ${chunkIndex} is outside 0-${metadata.chunks.length - 1}.`);
|
|
1668
|
+
}
|
|
1669
|
+
return metadata.chunks[chunkIndex];
|
|
1670
|
+
}
|
|
1671
|
+
/** Fetches a full or ranged ArrayBuffer while tolerating non-206 range fallbacks. */
|
|
1672
|
+
async _fetchArrayBuffer(url, range, options = {}) {
|
|
1673
|
+
const response = await this._fetchResponse(url, this._getRequestInit(range, options));
|
|
1674
|
+
if (!response.ok) {
|
|
1675
|
+
throw new Error(`RADSource: failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
1676
|
+
}
|
|
1677
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1678
|
+
if (range && response.status !== 206 && arrayBuffer.byteLength >= range.offset + range.bytes) {
|
|
1679
|
+
return arrayBuffer.slice(range.offset, range.offset + range.bytes);
|
|
1680
|
+
}
|
|
1681
|
+
if (range && arrayBuffer.byteLength > range.bytes) {
|
|
1682
|
+
return arrayBuffer.slice(0, range.bytes);
|
|
1683
|
+
}
|
|
1684
|
+
return arrayBuffer;
|
|
1685
|
+
}
|
|
1686
|
+
/** Fetches through custom source fetch hooks or the injected core fetchFile helper. */
|
|
1687
|
+
async _fetchResponse(url, requestInit) {
|
|
1688
|
+
return await this.fetch(url, requestInit);
|
|
1689
|
+
}
|
|
1690
|
+
/** Builds request options for RAD fetches. */
|
|
1691
|
+
_getRequestInit(range, options = {}) {
|
|
1692
|
+
var _a, _b;
|
|
1693
|
+
const headers = new Headers((_a = this.options.rad) == null ? void 0 : _a.headers);
|
|
1694
|
+
if (range && range.bytes > 0) {
|
|
1695
|
+
headers.set("Range", `bytes=${range.offset}-${range.offset + range.bytes - 1}`);
|
|
1696
|
+
}
|
|
1697
|
+
return {
|
|
1698
|
+
headers,
|
|
1699
|
+
credentials: ((_b = this.options.rad) == null ? void 0 : _b.withCredentials) ? "include" : "same-origin",
|
|
1700
|
+
signal: options.signal
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
function getRADChunkTableIteratorIndices(metadata, startChunkIndex, maxChunks, maxSplats) {
|
|
1705
|
+
const chunkIndices = [];
|
|
1706
|
+
let estimatedSplatCount = 0;
|
|
1707
|
+
for (let chunkIndex = startChunkIndex; chunkIndex < metadata.chunks.length && chunkIndices.length < maxChunks && estimatedSplatCount < maxSplats; chunkIndex++) {
|
|
1708
|
+
const chunk = metadata.chunks[chunkIndex];
|
|
1709
|
+
chunkIndices.push(chunkIndex);
|
|
1710
|
+
estimatedSplatCount += (chunk == null ? void 0 : chunk.count) ?? metadata.chunkSize ?? 0;
|
|
1711
|
+
}
|
|
1712
|
+
return chunkIndices;
|
|
1713
|
+
}
|
|
1714
|
+
function getRADMaxConcurrentChunkRequests(maxConcurrentChunkRequests) {
|
|
1715
|
+
return Number.isFinite(maxConcurrentChunkRequests) && maxConcurrentChunkRequests > 0 ? Math.floor(maxConcurrentChunkRequests) : DEFAULT_RAD_MAX_CONCURRENT_CHUNK_REQUESTS;
|
|
1716
|
+
}
|
|
1717
|
+
function getRADChunkSplatBase(splats) {
|
|
1718
|
+
var _a;
|
|
1719
|
+
return typeof ((_a = splats.loaderData) == null ? void 0 : _a.base) === "number" ? splats.loaderData.base : 0;
|
|
1720
|
+
}
|
|
1721
|
+
function pruneLoadedLoDParents(splats, loadedGlobalSplatEnd) {
|
|
1722
|
+
var _a, _b;
|
|
1723
|
+
const childCounts = (_a = splats.loaderData) == null ? void 0 : _a.childCounts;
|
|
1724
|
+
const childStarts = (_b = splats.loaderData) == null ? void 0 : _b.childStarts;
|
|
1725
|
+
if (!(childCounts instanceof Uint16Array) || !(childStarts instanceof Uint32Array)) {
|
|
1726
|
+
return splats;
|
|
1727
|
+
}
|
|
1728
|
+
const keepRows = new Uint32Array(splats.splatCount);
|
|
1729
|
+
let keepCount = 0;
|
|
1730
|
+
for (let rowIndex = 0; rowIndex < splats.splatCount; rowIndex++) {
|
|
1731
|
+
const childCount = childCounts[rowIndex];
|
|
1732
|
+
const childStart = childStarts[rowIndex];
|
|
1733
|
+
if (childCount > 0 && childStart < loadedGlobalSplatEnd) {
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
keepRows[keepCount++] = rowIndex;
|
|
1737
|
+
}
|
|
1738
|
+
if (keepCount === splats.splatCount) {
|
|
1739
|
+
return splats;
|
|
1740
|
+
}
|
|
1741
|
+
const keptRows = keepRows.subarray(0, keepCount);
|
|
1742
|
+
const sphericalHarmonicsComponentCount = splats.sphericalHarmonicsComponentCount ?? 0;
|
|
1743
|
+
return {
|
|
1744
|
+
...splats,
|
|
1745
|
+
splatCount: keepCount,
|
|
1746
|
+
positions: copyInterleavedRows(splats.positions, 3, keptRows),
|
|
1747
|
+
scales: copyInterleavedRows(splats.scales, 3, keptRows),
|
|
1748
|
+
rotations: copyInterleavedRows(splats.rotations, 4, keptRows),
|
|
1749
|
+
colors: copyInterleavedRows(splats.colors, 3, keptRows),
|
|
1750
|
+
sphericalHarmonicDcs: splats.sphericalHarmonicDcs ? copyInterleavedRows(splats.sphericalHarmonicDcs, 3, keptRows) : void 0,
|
|
1751
|
+
opacities: copyInterleavedRows(splats.opacities, 1, keptRows),
|
|
1752
|
+
sphericalHarmonics: splats.sphericalHarmonics && sphericalHarmonicsComponentCount ? copyInterleavedRows(splats.sphericalHarmonics, sphericalHarmonicsComponentCount, keptRows) : void 0,
|
|
1753
|
+
loaderData: {
|
|
1754
|
+
...splats.loaderData,
|
|
1755
|
+
count: keepCount,
|
|
1756
|
+
childCounts: copyInterleavedRows(childCounts, 1, keptRows),
|
|
1757
|
+
childStarts: copyInterleavedRows(childStarts, 1, keptRows)
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function copyInterleavedRows(values, itemSize, rows) {
|
|
1762
|
+
const copiedValues = new values.constructor(rows.length * itemSize);
|
|
1763
|
+
for (let outputRowIndex = 0; outputRowIndex < rows.length; outputRowIndex++) {
|
|
1764
|
+
const inputOffset = rows[outputRowIndex] * itemSize;
|
|
1765
|
+
const outputOffset = outputRowIndex * itemSize;
|
|
1766
|
+
copiedValues.set(values.subarray(inputOffset, inputOffset + itemSize), outputOffset);
|
|
1767
|
+
}
|
|
1768
|
+
return copiedValues;
|
|
1769
|
+
}
|
|
1770
|
+
function resolveRADChunkUrl(rootUrl, chunkFilename) {
|
|
1771
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(chunkFilename) || chunkFilename.startsWith("/")) {
|
|
1772
|
+
return chunkFilename;
|
|
1773
|
+
}
|
|
1774
|
+
if (!rootUrl) {
|
|
1775
|
+
throw new Error("RADSource: cannot resolve a sidecar RADC chunk without a RAD URL.");
|
|
1776
|
+
}
|
|
1777
|
+
try {
|
|
1778
|
+
return new URL(chunkFilename, rootUrl).toString();
|
|
1779
|
+
} catch {
|
|
1780
|
+
const [rootPath, suffix = ""] = rootUrl.split(/([?#].*)/, 2);
|
|
1781
|
+
const lastSlashIndex = Math.max(rootPath.lastIndexOf("/"), rootPath.lastIndexOf("\\"));
|
|
1782
|
+
const rootDirectory = lastSlashIndex >= 0 ? rootPath.slice(0, lastSlashIndex + 1) : "";
|
|
1783
|
+
return `${rootDirectory}${chunkFilename}${suffix}`;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
//# sourceMappingURL=index.cjs.map
|