@d5techs/3dgs-lib 1.4.52 → 1.4.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/3dgs-lib.cjs +212 -96
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +212 -96
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +1 -0
- package/dist/core/SkyboxRenderer.d.ts +8 -4
- package/dist/index.d.ts +2 -0
- package/dist/loaders/HdrLoader.d.ts +6 -0
- package/package.json +1 -1
package/dist/3dgs-lib.js
CHANGED
|
@@ -2096,122 +2096,199 @@ class SceneAidsRenderer {
|
|
|
2096
2096
|
this.axesBindGroup = null;
|
|
2097
2097
|
}
|
|
2098
2098
|
}
|
|
2099
|
-
|
|
2099
|
+
async function loadHdr(url) {
|
|
2100
|
+
const res = await fetch(url);
|
|
2101
|
+
if (!res.ok) throw new Error(`HdrLoader: fetch failed ${url} (${res.status})`);
|
|
2102
|
+
const buf = await res.arrayBuffer();
|
|
2103
|
+
return parseHdr(new Uint8Array(buf));
|
|
2104
|
+
}
|
|
2105
|
+
function parseHdr(bytes) {
|
|
2106
|
+
let pos = 0;
|
|
2107
|
+
const readLine = () => {
|
|
2108
|
+
let line = "";
|
|
2109
|
+
while (pos < bytes.length) {
|
|
2110
|
+
const c = bytes[pos++];
|
|
2111
|
+
if (c === 10) return line;
|
|
2112
|
+
line += String.fromCharCode(c);
|
|
2113
|
+
}
|
|
2114
|
+
return line;
|
|
2115
|
+
};
|
|
2116
|
+
const magic = readLine();
|
|
2117
|
+
if (!magic.startsWith("#?")) throw new Error("HdrLoader: not a Radiance HDR file");
|
|
2118
|
+
while (pos < bytes.length) {
|
|
2119
|
+
const line = readLine();
|
|
2120
|
+
if (line.length === 0) break;
|
|
2121
|
+
}
|
|
2122
|
+
const resLine = readLine();
|
|
2123
|
+
const m = resLine.match(/-Y\s+(\d+)\s+\+X\s+(\d+)/);
|
|
2124
|
+
if (!m) throw new Error("HdrLoader: unsupported resolution format: " + resLine);
|
|
2125
|
+
const height = parseInt(m[1], 10);
|
|
2126
|
+
const width = parseInt(m[2], 10);
|
|
2127
|
+
const rgbe = new Uint8Array(width * height * 4);
|
|
2128
|
+
for (let y = 0; y < height; y++) {
|
|
2129
|
+
const scanline = decodeScanline(bytes, pos, width);
|
|
2130
|
+
pos = scanline.newPos;
|
|
2131
|
+
rgbe.set(scanline.pixels, y * width * 4);
|
|
2132
|
+
}
|
|
2133
|
+
const data = new Float32Array(width * height * 4);
|
|
2134
|
+
for (let i = 0; i < width * height; i++) {
|
|
2135
|
+
const r = rgbe[i * 4];
|
|
2136
|
+
const g = rgbe[i * 4 + 1];
|
|
2137
|
+
const b = rgbe[i * 4 + 2];
|
|
2138
|
+
const e = rgbe[i * 4 + 3];
|
|
2139
|
+
if (e === 0) {
|
|
2140
|
+
data[i * 4] = 0;
|
|
2141
|
+
data[i * 4 + 1] = 0;
|
|
2142
|
+
data[i * 4 + 2] = 0;
|
|
2143
|
+
} else {
|
|
2144
|
+
const scale = Math.pow(2, e - 128 - 8);
|
|
2145
|
+
data[i * 4] = r * scale;
|
|
2146
|
+
data[i * 4 + 1] = g * scale;
|
|
2147
|
+
data[i * 4 + 2] = b * scale;
|
|
2148
|
+
}
|
|
2149
|
+
data[i * 4 + 3] = 1;
|
|
2150
|
+
}
|
|
2151
|
+
return { width, height, data };
|
|
2152
|
+
}
|
|
2153
|
+
function decodeScanline(bytes, pos, width) {
|
|
2154
|
+
const pixels = new Uint8Array(width * 4);
|
|
2155
|
+
if (width < 8 || width > 32767) {
|
|
2156
|
+
for (let i = 0; i < width * 4; i++) pixels[i] = bytes[pos++];
|
|
2157
|
+
return { pixels, newPos: pos };
|
|
2158
|
+
}
|
|
2159
|
+
const b0 = bytes[pos], b1 = bytes[pos + 1], b22 = bytes[pos + 2], b3 = bytes[pos + 3];
|
|
2160
|
+
if (b0 !== 2 || b1 !== 2 || (b22 & 128) !== 0) {
|
|
2161
|
+
for (let i = 0; i < width * 4; i++) pixels[i] = bytes[pos++];
|
|
2162
|
+
return { pixels, newPos: pos };
|
|
2163
|
+
}
|
|
2164
|
+
const lineWidth = b22 << 8 | b3;
|
|
2165
|
+
if (lineWidth !== width) throw new Error("HdrLoader: scanline width mismatch");
|
|
2166
|
+
pos += 4;
|
|
2167
|
+
for (let ch = 0; ch < 4; ch++) {
|
|
2168
|
+
let x = 0;
|
|
2169
|
+
while (x < width) {
|
|
2170
|
+
const code = bytes[pos++];
|
|
2171
|
+
if (code > 128) {
|
|
2172
|
+
const count = code - 128;
|
|
2173
|
+
const val = bytes[pos++];
|
|
2174
|
+
for (let i = 0; i < count; i++) pixels[(x + i) * 4 + ch] = val;
|
|
2175
|
+
x += count;
|
|
2176
|
+
} else {
|
|
2177
|
+
for (let i = 0; i < code; i++) pixels[(x + i) * 4 + ch] = bytes[pos++];
|
|
2178
|
+
x += code;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return { pixels, newPos: pos };
|
|
2183
|
+
}
|
|
2184
|
+
const CUBE_WGSL = (
|
|
2100
2185
|
/* wgsl */
|
|
2101
2186
|
`
|
|
2102
|
-
struct Uniforms {
|
|
2103
|
-
col0: vec4<f32>,
|
|
2104
|
-
col1: vec4<f32>,
|
|
2105
|
-
col2: vec4<f32>,
|
|
2106
|
-
};
|
|
2107
|
-
|
|
2187
|
+
struct Uniforms { col0: vec4<f32>, col1: vec4<f32>, col2: vec4<f32> };
|
|
2108
2188
|
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
2109
2189
|
@group(0) @binding(1) var cubeSampler: sampler;
|
|
2110
2190
|
@group(0) @binding(2) var cubeTexture: texture_cube<f32>;
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
fn vs(@builtin(vertex_index) vi: u32) -> VSOut {
|
|
2119
|
-
let positions = array<vec2<f32>, 3>(
|
|
2120
|
-
vec2<f32>(-1.0, -1.0),
|
|
2121
|
-
vec2<f32>(3.0, -1.0),
|
|
2122
|
-
vec2<f32>(-1.0, 3.0),
|
|
2123
|
-
);
|
|
2124
|
-
var out: VSOut;
|
|
2125
|
-
let p = positions[vi];
|
|
2126
|
-
out.position = vec4<f32>(p, 0.0, 1.0);
|
|
2127
|
-
|
|
2128
|
-
let eyeDir = vec3<f32>(p.x * u.col0.w, p.y * u.col1.w, -1.0);
|
|
2129
|
-
|
|
2130
|
-
out.dir = vec3<f32>(
|
|
2131
|
-
dot(u.col0.xyz, eyeDir),
|
|
2132
|
-
dot(u.col1.xyz, eyeDir),
|
|
2133
|
-
dot(u.col2.xyz, eyeDir),
|
|
2134
|
-
);
|
|
2135
|
-
return out;
|
|
2191
|
+
struct V { @builtin(position) pos: vec4<f32>, @location(0) dir: vec3<f32> };
|
|
2192
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32) -> V {
|
|
2193
|
+
let ps = array<vec2<f32>,3>(vec2(-1.,-1.),vec2(3.,-1.),vec2(-1.,3.));
|
|
2194
|
+
var o: V; let p = ps[vi]; o.pos = vec4(p, 0., 1.);
|
|
2195
|
+
let e = vec3(p.x*u.col0.w, p.y*u.col1.w, -1.);
|
|
2196
|
+
o.dir = vec3(dot(u.col0.xyz,e), dot(u.col1.xyz,e), dot(u.col2.xyz,e));
|
|
2197
|
+
return o;
|
|
2136
2198
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2199
|
+
@fragment fn fs(i: V) -> @location(0) vec4<f32> {
|
|
2200
|
+
return vec4(textureSample(cubeTexture, cubeSampler, normalize(i.dir)).rgb, 1.);
|
|
2201
|
+
}`
|
|
2202
|
+
);
|
|
2203
|
+
const EQUIRECT_WGSL = (
|
|
2204
|
+
/* wgsl */
|
|
2205
|
+
`
|
|
2206
|
+
struct Uniforms { col0: vec4<f32>, col1: vec4<f32>, col2: vec4<f32> };
|
|
2207
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
2208
|
+
@group(0) @binding(1) var envSampler: sampler;
|
|
2209
|
+
@group(0) @binding(2) var envTexture: texture_2d<f32>;
|
|
2210
|
+
struct V { @builtin(position) pos: vec4<f32>, @location(0) dir: vec3<f32> };
|
|
2211
|
+
const PI: f32 = 3.14159265358979;
|
|
2212
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32) -> V {
|
|
2213
|
+
let ps = array<vec2<f32>,3>(vec2(-1.,-1.),vec2(3.,-1.),vec2(-1.,3.));
|
|
2214
|
+
var o: V; let p = ps[vi]; o.pos = vec4(p, 0., 1.);
|
|
2215
|
+
let e = vec3(p.x*u.col0.w, p.y*u.col1.w, -1.);
|
|
2216
|
+
o.dir = vec3(dot(u.col0.xyz,e), dot(u.col1.xyz,e), dot(u.col2.xyz,e));
|
|
2217
|
+
return o;
|
|
2142
2218
|
}
|
|
2143
|
-
|
|
2219
|
+
@fragment fn fs(i: V) -> @location(0) vec4<f32> {
|
|
2220
|
+
let d = normalize(i.dir);
|
|
2221
|
+
let uv = vec2(atan2(d.z, d.x) / (2.*PI) + .5, asin(clamp(d.y,-1.,1.)) / PI + .5);
|
|
2222
|
+
let c = textureSample(envTexture, envSampler, vec2(uv.x, 1. - uv.y)).rgb;
|
|
2223
|
+
let mapped = c / (c + vec3(1.));
|
|
2224
|
+
let gamma = pow(mapped, vec3(1./2.2));
|
|
2225
|
+
return vec4(gamma, 1.);
|
|
2226
|
+
}`
|
|
2144
2227
|
);
|
|
2145
2228
|
class SkyboxRenderer {
|
|
2146
2229
|
constructor(device, format, depthFormat) {
|
|
2147
2230
|
__publicField(this, "device");
|
|
2148
|
-
__publicField(this, "pipeline");
|
|
2149
2231
|
__publicField(this, "uniformBuffer");
|
|
2150
2232
|
__publicField(this, "sampler");
|
|
2151
|
-
__publicField(this, "bindGroupLayout");
|
|
2152
2233
|
__publicField(this, "uniformData", new Float32Array(12));
|
|
2153
|
-
__publicField(this, "
|
|
2154
|
-
__publicField(this, "
|
|
2234
|
+
__publicField(this, "cubePipeline");
|
|
2235
|
+
__publicField(this, "cubeBindGroupLayout");
|
|
2236
|
+
__publicField(this, "equirectPipeline");
|
|
2237
|
+
__publicField(this, "equirectBindGroupLayout");
|
|
2238
|
+
__publicField(this, "texture", null);
|
|
2239
|
+
__publicField(this, "bindGroup", null);
|
|
2155
2240
|
__publicField(this, "frameReady", false);
|
|
2241
|
+
__publicField(this, "mode", "none");
|
|
2156
2242
|
this.device = device;
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2243
|
+
const depthStencil = {
|
|
2244
|
+
format: depthFormat,
|
|
2245
|
+
depthWriteEnabled: false,
|
|
2246
|
+
depthCompare: "always"
|
|
2247
|
+
};
|
|
2248
|
+
this.cubeBindGroupLayout = device.createBindGroupLayout({
|
|
2159
2249
|
entries: [
|
|
2160
|
-
{
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
buffer: { type: "uniform" }
|
|
2164
|
-
},
|
|
2165
|
-
{
|
|
2166
|
-
binding: 1,
|
|
2167
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
2168
|
-
sampler: { type: "filtering" }
|
|
2169
|
-
},
|
|
2170
|
-
{
|
|
2171
|
-
binding: 2,
|
|
2172
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
2173
|
-
texture: { sampleType: "float", viewDimension: "cube" }
|
|
2174
|
-
}
|
|
2250
|
+
{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } },
|
|
2251
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
|
|
2252
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float", viewDimension: "cube" } }
|
|
2175
2253
|
]
|
|
2176
2254
|
});
|
|
2177
|
-
this.
|
|
2178
|
-
layout: device.createPipelineLayout({
|
|
2179
|
-
|
|
2180
|
-
}),
|
|
2181
|
-
vertex: { module: shaderModule, entryPoint: "vs" },
|
|
2182
|
-
fragment: {
|
|
2183
|
-
module: shaderModule,
|
|
2184
|
-
entryPoint: "fs",
|
|
2185
|
-
targets: [{ format }]
|
|
2186
|
-
},
|
|
2255
|
+
this.cubePipeline = device.createRenderPipeline({
|
|
2256
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [this.cubeBindGroupLayout] }),
|
|
2257
|
+
vertex: { module: device.createShaderModule({ code: CUBE_WGSL }), entryPoint: "vs" },
|
|
2258
|
+
fragment: { module: device.createShaderModule({ code: CUBE_WGSL }), entryPoint: "fs", targets: [{ format }] },
|
|
2187
2259
|
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2188
|
-
depthStencil
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2260
|
+
depthStencil
|
|
2261
|
+
});
|
|
2262
|
+
this.equirectBindGroupLayout = device.createBindGroupLayout({
|
|
2263
|
+
entries: [
|
|
2264
|
+
{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } },
|
|
2265
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
|
|
2266
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float", viewDimension: "2d" } }
|
|
2267
|
+
]
|
|
2268
|
+
});
|
|
2269
|
+
this.equirectPipeline = device.createRenderPipeline({
|
|
2270
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [this.equirectBindGroupLayout] }),
|
|
2271
|
+
vertex: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "vs" },
|
|
2272
|
+
fragment: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "fs", targets: [{ format }] },
|
|
2273
|
+
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2274
|
+
depthStencil
|
|
2193
2275
|
});
|
|
2194
2276
|
this.uniformBuffer = device.createBuffer({
|
|
2195
2277
|
size: 48,
|
|
2196
2278
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
2197
2279
|
});
|
|
2198
|
-
this.sampler = device.createSampler({
|
|
2199
|
-
magFilter: "linear",
|
|
2200
|
-
minFilter: "linear"
|
|
2201
|
-
});
|
|
2280
|
+
this.sampler = device.createSampler({ magFilter: "linear", minFilter: "linear" });
|
|
2202
2281
|
}
|
|
2203
2282
|
get isActive() {
|
|
2204
|
-
return this.
|
|
2283
|
+
return this.mode !== "none" && this.texture !== null && this.bindGroup !== null;
|
|
2205
2284
|
}
|
|
2285
|
+
// ---- cubemap ----
|
|
2206
2286
|
async loadCubemap(faceUrls) {
|
|
2207
2287
|
this.clear();
|
|
2208
2288
|
const bitmaps = await Promise.all(
|
|
2209
2289
|
faceUrls.map(async (url) => {
|
|
2210
2290
|
const res = await fetch(url);
|
|
2211
|
-
if (!res.ok)
|
|
2212
|
-
throw new Error(
|
|
2213
|
-
`SkyboxRenderer: failed to fetch ${url} (${res.status})`
|
|
2214
|
-
);
|
|
2291
|
+
if (!res.ok) throw new Error(`SkyboxRenderer: failed to fetch ${url} (${res.status})`);
|
|
2215
2292
|
return createImageBitmap(await res.blob());
|
|
2216
2293
|
})
|
|
2217
2294
|
);
|
|
@@ -2220,9 +2297,7 @@ class SkyboxRenderer {
|
|
|
2220
2297
|
for (let i = 1; i < 6; i++) {
|
|
2221
2298
|
if (bitmaps[i].width !== width || bitmaps[i].height !== height) {
|
|
2222
2299
|
bitmaps.forEach((b) => b.close());
|
|
2223
|
-
throw new Error(
|
|
2224
|
-
"SkyboxRenderer: all cubemap faces must share the same dimensions"
|
|
2225
|
-
);
|
|
2300
|
+
throw new Error("SkyboxRenderer: all cubemap faces must share the same dimensions");
|
|
2226
2301
|
}
|
|
2227
2302
|
}
|
|
2228
2303
|
const tex = this.device.createTexture({
|
|
@@ -2239,16 +2314,44 @@ class SkyboxRenderer {
|
|
|
2239
2314
|
);
|
|
2240
2315
|
}
|
|
2241
2316
|
bitmaps.forEach((b) => b.close());
|
|
2242
|
-
this.
|
|
2243
|
-
layout: this.
|
|
2317
|
+
this.bindGroup = this.device.createBindGroup({
|
|
2318
|
+
layout: this.cubeBindGroupLayout,
|
|
2244
2319
|
entries: [
|
|
2245
2320
|
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
2246
2321
|
{ binding: 1, resource: this.sampler },
|
|
2247
2322
|
{ binding: 2, resource: tex.createView({ dimension: "cube" }) }
|
|
2248
2323
|
]
|
|
2249
2324
|
});
|
|
2250
|
-
this.
|
|
2325
|
+
this.texture = tex;
|
|
2326
|
+
this.mode = "cubemap";
|
|
2251
2327
|
}
|
|
2328
|
+
// ---- equirectangular HDR ----
|
|
2329
|
+
async loadEquirectangular(url) {
|
|
2330
|
+
this.clear();
|
|
2331
|
+
const hdr = await loadHdr(url);
|
|
2332
|
+
const tex = this.device.createTexture({
|
|
2333
|
+
size: [hdr.width, hdr.height],
|
|
2334
|
+
format: "rgba32float",
|
|
2335
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
2336
|
+
});
|
|
2337
|
+
this.device.queue.writeTexture(
|
|
2338
|
+
{ texture: tex },
|
|
2339
|
+
hdr.data.buffer,
|
|
2340
|
+
{ bytesPerRow: hdr.width * 16, rowsPerImage: hdr.height },
|
|
2341
|
+
{ width: hdr.width, height: hdr.height }
|
|
2342
|
+
);
|
|
2343
|
+
this.bindGroup = this.device.createBindGroup({
|
|
2344
|
+
layout: this.equirectBindGroupLayout,
|
|
2345
|
+
entries: [
|
|
2346
|
+
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
2347
|
+
{ binding: 1, resource: this.sampler },
|
|
2348
|
+
{ binding: 2, resource: tex.createView() }
|
|
2349
|
+
]
|
|
2350
|
+
});
|
|
2351
|
+
this.texture = tex;
|
|
2352
|
+
this.mode = "equirect";
|
|
2353
|
+
}
|
|
2354
|
+
// ---- common ----
|
|
2252
2355
|
prepareFrame(viewMatrix, projectionMatrix) {
|
|
2253
2356
|
if (!this.isActive) {
|
|
2254
2357
|
this.frameReady = false;
|
|
@@ -2271,18 +2374,20 @@ class SkyboxRenderer {
|
|
|
2271
2374
|
this.frameReady = true;
|
|
2272
2375
|
}
|
|
2273
2376
|
draw(pass) {
|
|
2274
|
-
if (!this.frameReady || !this.
|
|
2275
|
-
|
|
2276
|
-
pass.
|
|
2377
|
+
if (!this.frameReady || !this.bindGroup) return;
|
|
2378
|
+
const pipeline = this.mode === "cubemap" ? this.cubePipeline : this.equirectPipeline;
|
|
2379
|
+
pass.setPipeline(pipeline);
|
|
2380
|
+
pass.setBindGroup(0, this.bindGroup);
|
|
2277
2381
|
pass.draw(3);
|
|
2278
2382
|
}
|
|
2279
2383
|
clear() {
|
|
2280
|
-
if (this.
|
|
2281
|
-
this.
|
|
2282
|
-
this.
|
|
2384
|
+
if (this.texture) {
|
|
2385
|
+
this.texture.destroy();
|
|
2386
|
+
this.texture = null;
|
|
2283
2387
|
}
|
|
2284
|
-
this.
|
|
2388
|
+
this.bindGroup = null;
|
|
2285
2389
|
this.frameReady = false;
|
|
2390
|
+
this.mode = "none";
|
|
2286
2391
|
}
|
|
2287
2392
|
destroy() {
|
|
2288
2393
|
this.clear();
|
|
@@ -19088,6 +19193,16 @@ class App {
|
|
|
19088
19193
|
faceUrls.negZ
|
|
19089
19194
|
]);
|
|
19090
19195
|
}
|
|
19196
|
+
async setHdrBackground(url) {
|
|
19197
|
+
if (!this.skyboxRenderer) {
|
|
19198
|
+
this.skyboxRenderer = new SkyboxRenderer(
|
|
19199
|
+
this.renderer.device,
|
|
19200
|
+
this.renderer.format,
|
|
19201
|
+
"depth24plus"
|
|
19202
|
+
);
|
|
19203
|
+
}
|
|
19204
|
+
await this.skyboxRenderer.loadEquirectangular(url);
|
|
19205
|
+
}
|
|
19091
19206
|
clearSkybox() {
|
|
19092
19207
|
var _a2;
|
|
19093
19208
|
(_a2 = this.skyboxRenderer) == null ? void 0 : _a2.clear();
|
|
@@ -19580,6 +19695,7 @@ export {
|
|
|
19580
19695
|
getRecommendedDPR,
|
|
19581
19696
|
isMobileDevice,
|
|
19582
19697
|
isWebGPUSupported,
|
|
19698
|
+
loadHdr,
|
|
19583
19699
|
loadPLY,
|
|
19584
19700
|
loadPLYMobile,
|
|
19585
19701
|
loadSOG,
|