@d5techs/3dgs-lib 1.4.69 → 1.4.71
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 +7 -259
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +7 -259
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +0 -2
- package/dist/core/SkyboxRenderer.d.ts +2 -7
- package/dist/index.d.ts +0 -2
- package/package.json +1 -1
- package/dist/loaders/HdrLoader.d.ts +0 -6
package/dist/3dgs-lib.js
CHANGED
|
@@ -2096,103 +2096,6 @@ class SceneAidsRenderer {
|
|
|
2096
2096
|
this.axesBindGroup = null;
|
|
2097
2097
|
}
|
|
2098
2098
|
}
|
|
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 f32Buf = new Float32Array(1);
|
|
2185
|
-
const u32Buf = new Uint32Array(f32Buf.buffer);
|
|
2186
|
-
function float32ToFloat16(val) {
|
|
2187
|
-
f32Buf[0] = val;
|
|
2188
|
-
const f = u32Buf[0];
|
|
2189
|
-
const sign = f >> 16 & 32768;
|
|
2190
|
-
const exp = (f >> 23 & 255) - 127 + 15;
|
|
2191
|
-
const frac = f >> 13 & 1023;
|
|
2192
|
-
if (exp <= 0) return sign;
|
|
2193
|
-
if (exp >= 31) return sign | 31744;
|
|
2194
|
-
return sign | exp << 10 | frac;
|
|
2195
|
-
}
|
|
2196
2099
|
const CUBE_WGSL = (
|
|
2197
2100
|
/* wgsl */
|
|
2198
2101
|
`
|
|
@@ -2212,62 +2115,6 @@ struct V { @builtin(position) pos: vec4<f32>, @location(0) dir: vec3<f32> };
|
|
|
2212
2115
|
return vec4(textureSample(cubeTexture, cubeSampler, normalize(i.dir)).rgb, 1.);
|
|
2213
2116
|
}`
|
|
2214
2117
|
);
|
|
2215
|
-
const EQUIRECT_WGSL = (
|
|
2216
|
-
/* wgsl */
|
|
2217
|
-
`
|
|
2218
|
-
struct Uniforms { col0: vec4<f32>, col1: vec4<f32>, col2: vec4<f32>, cam: vec4<f32> };
|
|
2219
|
-
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
2220
|
-
@group(0) @binding(1) var envSampler: sampler;
|
|
2221
|
-
@group(0) @binding(2) var envTexture: texture_2d<f32>;
|
|
2222
|
-
struct V { @builtin(position) pos: vec4<f32>, @location(0) dir: vec3<f32> };
|
|
2223
|
-
const PI: f32 = 3.14159265358979;
|
|
2224
|
-
|
|
2225
|
-
fn dirToUv(d: vec3<f32>) -> vec2<f32> {
|
|
2226
|
-
return vec2(atan2(d.z, d.x) / (2.*PI) + .5, 1. - (asin(clamp(d.y,-1.,1.)) / PI + .5));
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
@vertex fn vs(@builtin(vertex_index) vi: u32) -> V {
|
|
2230
|
-
let ps = array<vec2<f32>,3>(vec2(-1.,-1.),vec2(3.,-1.),vec2(-1.,3.));
|
|
2231
|
-
var o: V; let p = ps[vi]; o.pos = vec4(p, 0., 1.);
|
|
2232
|
-
let e = vec3(p.x*u.col0.w, p.y*u.col1.w, -1.);
|
|
2233
|
-
o.dir = vec3(dot(u.col0.xyz,e), dot(u.col1.xyz,e), dot(u.col2.xyz,e));
|
|
2234
|
-
return o;
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
|
-
@fragment fn fs(i: V) -> @location(0) vec4<f32> {
|
|
2238
|
-
let d = normalize(i.dir);
|
|
2239
|
-
let cx = u.cam.x;
|
|
2240
|
-
let cy = max(u.cam.y, 0.1); // clamp: camera always above ground
|
|
2241
|
-
let cz = u.cam.z;
|
|
2242
|
-
let captureH = u.cam.w;
|
|
2243
|
-
|
|
2244
|
-
// --- sky: sample HDR with original direction ---
|
|
2245
|
-
let skyUv = dirToUv(d);
|
|
2246
|
-
|
|
2247
|
-
// --- ground: ray→plane at Y=0, camera-centered (no world XZ offset) ---
|
|
2248
|
-
let dyClamped = min(d.y, -0.0001);
|
|
2249
|
-
let t = -cy / dyClamped;
|
|
2250
|
-
let hitX = t * d.x;
|
|
2251
|
-
let hitZ = t * d.z;
|
|
2252
|
-
|
|
2253
|
-
// Direction from virtual capture point to ground hit point
|
|
2254
|
-
let groundDir = normalize(vec3(hitX, -captureH, hitZ));
|
|
2255
|
-
let groundUv = dirToUv(groundDir);
|
|
2256
|
-
|
|
2257
|
-
// --- sample both (uniform control flow) ---
|
|
2258
|
-
let skyColor = textureSample(envTexture, envSampler, skyUv).rgb;
|
|
2259
|
-
let groundColor = textureSample(envTexture, envSampler, groundUv).rgb;
|
|
2260
|
-
|
|
2261
|
-
// Sharp blend at horizon — cy is always > 0 so no aboveGround check needed
|
|
2262
|
-
let horizonBlend = smoothstep(0.002, -0.002, d.y);
|
|
2263
|
-
let c = mix(skyColor, groundColor, horizonBlend);
|
|
2264
|
-
|
|
2265
|
-
// Reinhard tone mapping + gamma
|
|
2266
|
-
let mapped = c / (c + vec3(1.));
|
|
2267
|
-
let gamma = pow(mapped, vec3(1./2.2));
|
|
2268
|
-
return vec4(gamma, 1.);
|
|
2269
|
-
}`
|
|
2270
|
-
);
|
|
2271
2118
|
class SkyboxRenderer {
|
|
2272
2119
|
constructor(device, format, depthFormat) {
|
|
2273
2120
|
__publicField(this, "device");
|
|
@@ -2276,14 +2123,10 @@ class SkyboxRenderer {
|
|
|
2276
2123
|
__publicField(this, "uniformData", new Float32Array(16));
|
|
2277
2124
|
__publicField(this, "cubePipeline");
|
|
2278
2125
|
__publicField(this, "cubeBindGroupLayout");
|
|
2279
|
-
__publicField(this, "equirectPipeline");
|
|
2280
|
-
__publicField(this, "equirectBindGroupLayout");
|
|
2281
2126
|
__publicField(this, "texture", null);
|
|
2282
2127
|
__publicField(this, "bindGroup", null);
|
|
2283
2128
|
__publicField(this, "frameReady", false);
|
|
2284
|
-
__publicField(this, "
|
|
2285
|
-
/** Virtual HDR capture height (world units). Controls ground texture spread. */
|
|
2286
|
-
__publicField(this, "groundRadius", 50);
|
|
2129
|
+
__publicField(this, "active", false);
|
|
2287
2130
|
this.device = device;
|
|
2288
2131
|
const depthStencil = {
|
|
2289
2132
|
format: depthFormat,
|
|
@@ -2304,20 +2147,6 @@ class SkyboxRenderer {
|
|
|
2304
2147
|
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2305
2148
|
depthStencil
|
|
2306
2149
|
});
|
|
2307
|
-
this.equirectBindGroupLayout = device.createBindGroupLayout({
|
|
2308
|
-
entries: [
|
|
2309
|
-
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
2310
|
-
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
|
|
2311
|
-
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float", viewDimension: "2d" } }
|
|
2312
|
-
]
|
|
2313
|
-
});
|
|
2314
|
-
this.equirectPipeline = device.createRenderPipeline({
|
|
2315
|
-
layout: device.createPipelineLayout({ bindGroupLayouts: [this.equirectBindGroupLayout] }),
|
|
2316
|
-
vertex: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "vs" },
|
|
2317
|
-
fragment: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "fs", targets: [{ format }] },
|
|
2318
|
-
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2319
|
-
depthStencil
|
|
2320
|
-
});
|
|
2321
2150
|
this.uniformBuffer = device.createBuffer({
|
|
2322
2151
|
size: 64,
|
|
2323
2152
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
@@ -2325,9 +2154,8 @@ class SkyboxRenderer {
|
|
|
2325
2154
|
this.sampler = device.createSampler({ magFilter: "linear", minFilter: "linear" });
|
|
2326
2155
|
}
|
|
2327
2156
|
get isActive() {
|
|
2328
|
-
return this.
|
|
2157
|
+
return this.active && this.texture !== null && this.bindGroup !== null;
|
|
2329
2158
|
}
|
|
2330
|
-
// ---- cubemap ----
|
|
2331
2159
|
async loadCubemap(faceUrls) {
|
|
2332
2160
|
this.clear();
|
|
2333
2161
|
const bitmaps = await Promise.all(
|
|
@@ -2368,41 +2196,9 @@ class SkyboxRenderer {
|
|
|
2368
2196
|
]
|
|
2369
2197
|
});
|
|
2370
2198
|
this.texture = tex;
|
|
2371
|
-
this.
|
|
2372
|
-
}
|
|
2373
|
-
// ---- equirectangular HDR ----
|
|
2374
|
-
async loadEquirectangular(url) {
|
|
2375
|
-
this.clear();
|
|
2376
|
-
const hdr = await loadHdr(url);
|
|
2377
|
-
const pixelCount = hdr.width * hdr.height * 4;
|
|
2378
|
-
const f16 = new Uint16Array(pixelCount);
|
|
2379
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
2380
|
-
f16[i] = float32ToFloat16(hdr.data[i]);
|
|
2381
|
-
}
|
|
2382
|
-
const tex = this.device.createTexture({
|
|
2383
|
-
size: [hdr.width, hdr.height],
|
|
2384
|
-
format: "rgba16float",
|
|
2385
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
2386
|
-
});
|
|
2387
|
-
this.device.queue.writeTexture(
|
|
2388
|
-
{ texture: tex },
|
|
2389
|
-
f16.buffer,
|
|
2390
|
-
{ bytesPerRow: hdr.width * 8, rowsPerImage: hdr.height },
|
|
2391
|
-
{ width: hdr.width, height: hdr.height }
|
|
2392
|
-
);
|
|
2393
|
-
this.bindGroup = this.device.createBindGroup({
|
|
2394
|
-
layout: this.equirectBindGroupLayout,
|
|
2395
|
-
entries: [
|
|
2396
|
-
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
2397
|
-
{ binding: 1, resource: this.sampler },
|
|
2398
|
-
{ binding: 2, resource: tex.createView() }
|
|
2399
|
-
]
|
|
2400
|
-
});
|
|
2401
|
-
this.texture = tex;
|
|
2402
|
-
this.mode = "equirect";
|
|
2199
|
+
this.active = true;
|
|
2403
2200
|
}
|
|
2404
|
-
|
|
2405
|
-
prepareFrame(viewMatrix, projectionMatrix, cameraPosition) {
|
|
2201
|
+
prepareFrame(viewMatrix, projectionMatrix) {
|
|
2406
2202
|
if (!this.isActive) {
|
|
2407
2203
|
this.frameReady = false;
|
|
2408
2204
|
return;
|
|
@@ -2420,17 +2216,12 @@ class SkyboxRenderer {
|
|
|
2420
2216
|
ud[9] = viewMatrix[9];
|
|
2421
2217
|
ud[10] = viewMatrix[10];
|
|
2422
2218
|
ud[11] = 0;
|
|
2423
|
-
ud[12] = cameraPosition ? cameraPosition[0] : 0;
|
|
2424
|
-
ud[13] = cameraPosition ? cameraPosition[1] : 0;
|
|
2425
|
-
ud[14] = cameraPosition ? cameraPosition[2] : 0;
|
|
2426
|
-
ud[15] = this.groundRadius;
|
|
2427
2219
|
this.device.queue.writeBuffer(this.uniformBuffer, 0, ud);
|
|
2428
2220
|
this.frameReady = true;
|
|
2429
2221
|
}
|
|
2430
2222
|
draw(pass) {
|
|
2431
2223
|
if (!this.frameReady || !this.bindGroup) return;
|
|
2432
|
-
|
|
2433
|
-
pass.setPipeline(pipeline);
|
|
2224
|
+
pass.setPipeline(this.cubePipeline);
|
|
2434
2225
|
pass.setBindGroup(0, this.bindGroup);
|
|
2435
2226
|
pass.draw(3);
|
|
2436
2227
|
}
|
|
@@ -2441,7 +2232,7 @@ class SkyboxRenderer {
|
|
|
2441
2232
|
}
|
|
2442
2233
|
this.bindGroup = null;
|
|
2443
2234
|
this.frameReady = false;
|
|
2444
|
-
this.
|
|
2235
|
+
this.active = false;
|
|
2445
2236
|
}
|
|
2446
2237
|
destroy() {
|
|
2447
2238
|
this.clear();
|
|
@@ -19060,36 +18851,9 @@ class App {
|
|
|
19060
18851
|
this.updateAdaptivePerformance();
|
|
19061
18852
|
this.hotspotManager.updateBillboards();
|
|
19062
18853
|
if ((_a2 = this.skyboxRenderer) == null ? void 0 : _a2.isActive) {
|
|
19063
|
-
const cam = this.camera.position;
|
|
19064
|
-
let groundLevel = 0;
|
|
19065
|
-
const gsRendererForGround = this.sceneManager.getGSRenderer();
|
|
19066
|
-
const bbox = (gsRendererForGround == null ? void 0 : gsRendererForGround.getBoundingBox()) ?? null;
|
|
19067
|
-
if (bbox) {
|
|
19068
|
-
const m = gsRendererForGround.getModelMatrix();
|
|
19069
|
-
groundLevel = m[5] * bbox.min[1] + m[13];
|
|
19070
|
-
}
|
|
19071
|
-
if (!this._hdrDebugLogged) {
|
|
19072
|
-
this._hdrDebugLogged = true;
|
|
19073
|
-
console.log(
|
|
19074
|
-
"[HDR Ground Debug]",
|
|
19075
|
-
"hasGsRenderer:",
|
|
19076
|
-
!!gsRendererForGround,
|
|
19077
|
-
"hasBbox:",
|
|
19078
|
-
!!bbox,
|
|
19079
|
-
"bbox:",
|
|
19080
|
-
bbox ? { min: [...bbox.min], max: [...bbox.max], center: [...bbox.center] } : null,
|
|
19081
|
-
"groundLevel:",
|
|
19082
|
-
groundLevel,
|
|
19083
|
-
"cam:",
|
|
19084
|
-
[cam[0].toFixed(2), cam[1].toFixed(2), cam[2].toFixed(2)]
|
|
19085
|
-
);
|
|
19086
|
-
}
|
|
19087
|
-
const camH = cam[1] - groundLevel;
|
|
19088
|
-
this.skyboxRenderer.groundRadius = 50;
|
|
19089
18854
|
this.skyboxRenderer.prepareFrame(
|
|
19090
18855
|
this.camera.viewMatrix,
|
|
19091
|
-
this.camera.projectionMatrix
|
|
19092
|
-
[cam[0], camH, cam[2]]
|
|
18856
|
+
this.camera.projectionMatrix
|
|
19093
18857
|
);
|
|
19094
18858
|
}
|
|
19095
18859
|
const pass = this.renderer.beginFrame();
|
|
@@ -19274,21 +19038,6 @@ class App {
|
|
|
19274
19038
|
faceUrls.negZ
|
|
19275
19039
|
]);
|
|
19276
19040
|
}
|
|
19277
|
-
async setHdrBackground(url) {
|
|
19278
|
-
if (!this.skyboxRenderer) {
|
|
19279
|
-
this.skyboxRenderer = new SkyboxRenderer(
|
|
19280
|
-
this.renderer.device,
|
|
19281
|
-
this.renderer.format,
|
|
19282
|
-
"depth24plus"
|
|
19283
|
-
);
|
|
19284
|
-
}
|
|
19285
|
-
await this.skyboxRenderer.loadEquirectangular(url);
|
|
19286
|
-
}
|
|
19287
|
-
setHdrGroundRadius(radius) {
|
|
19288
|
-
if (this.skyboxRenderer) {
|
|
19289
|
-
this.skyboxRenderer.groundRadius = radius;
|
|
19290
|
-
}
|
|
19291
|
-
}
|
|
19292
19041
|
clearSkybox() {
|
|
19293
19042
|
var _a2;
|
|
19294
19043
|
(_a2 = this.skyboxRenderer) == null ? void 0 : _a2.clear();
|
|
@@ -19781,7 +19530,6 @@ export {
|
|
|
19781
19530
|
getRecommendedDPR,
|
|
19782
19531
|
isMobileDevice,
|
|
19783
19532
|
isWebGPUSupported,
|
|
19784
|
-
loadHdr,
|
|
19785
19533
|
loadPLY,
|
|
19786
19534
|
loadPLYMobile,
|
|
19787
19535
|
loadSOG,
|