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