@d5techs/3dgs-lib 1.4.70 → 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 -258
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +7 -258
- 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,61 +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: camera-centered, height-independent projection ---
|
|
2248
|
-
let dyClamped = min(d.y, -0.0001);
|
|
2249
|
-
let ratio = d.x / dyClamped;
|
|
2250
|
-
let ratioZ = d.z / dyClamped;
|
|
2251
|
-
|
|
2252
|
-
// K = captureH controls ground spread (smaller = more horizon, larger = more nadir)
|
|
2253
|
-
let groundDir = normalize(vec3(-ratio, -captureH, -ratioZ));
|
|
2254
|
-
let groundUv = dirToUv(groundDir);
|
|
2255
|
-
|
|
2256
|
-
// --- sample both (uniform control flow) ---
|
|
2257
|
-
let skyColor = textureSample(envTexture, envSampler, skyUv).rgb;
|
|
2258
|
-
let groundColor = textureSample(envTexture, envSampler, groundUv).rgb;
|
|
2259
|
-
|
|
2260
|
-
// Sharp blend at horizon — cy is always > 0 so no aboveGround check needed
|
|
2261
|
-
let horizonBlend = smoothstep(0.002, -0.002, d.y);
|
|
2262
|
-
let c = mix(skyColor, groundColor, horizonBlend);
|
|
2263
|
-
|
|
2264
|
-
// Reinhard tone mapping + gamma
|
|
2265
|
-
let mapped = c / (c + vec3(1.));
|
|
2266
|
-
let gamma = pow(mapped, vec3(1./2.2));
|
|
2267
|
-
return vec4(gamma, 1.);
|
|
2268
|
-
}`
|
|
2269
|
-
);
|
|
2270
2118
|
class SkyboxRenderer {
|
|
2271
2119
|
constructor(device, format, depthFormat) {
|
|
2272
2120
|
__publicField(this, "device");
|
|
@@ -2275,14 +2123,10 @@ class SkyboxRenderer {
|
|
|
2275
2123
|
__publicField(this, "uniformData", new Float32Array(16));
|
|
2276
2124
|
__publicField(this, "cubePipeline");
|
|
2277
2125
|
__publicField(this, "cubeBindGroupLayout");
|
|
2278
|
-
__publicField(this, "equirectPipeline");
|
|
2279
|
-
__publicField(this, "equirectBindGroupLayout");
|
|
2280
2126
|
__publicField(this, "texture", null);
|
|
2281
2127
|
__publicField(this, "bindGroup", null);
|
|
2282
2128
|
__publicField(this, "frameReady", false);
|
|
2283
|
-
__publicField(this, "
|
|
2284
|
-
/** Virtual HDR capture height (world units). Controls ground texture spread. */
|
|
2285
|
-
__publicField(this, "groundRadius", 50);
|
|
2129
|
+
__publicField(this, "active", false);
|
|
2286
2130
|
this.device = device;
|
|
2287
2131
|
const depthStencil = {
|
|
2288
2132
|
format: depthFormat,
|
|
@@ -2303,20 +2147,6 @@ class SkyboxRenderer {
|
|
|
2303
2147
|
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2304
2148
|
depthStencil
|
|
2305
2149
|
});
|
|
2306
|
-
this.equirectBindGroupLayout = device.createBindGroupLayout({
|
|
2307
|
-
entries: [
|
|
2308
|
-
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
2309
|
-
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
|
|
2310
|
-
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float", viewDimension: "2d" } }
|
|
2311
|
-
]
|
|
2312
|
-
});
|
|
2313
|
-
this.equirectPipeline = device.createRenderPipeline({
|
|
2314
|
-
layout: device.createPipelineLayout({ bindGroupLayouts: [this.equirectBindGroupLayout] }),
|
|
2315
|
-
vertex: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "vs" },
|
|
2316
|
-
fragment: { module: device.createShaderModule({ code: EQUIRECT_WGSL }), entryPoint: "fs", targets: [{ format }] },
|
|
2317
|
-
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
2318
|
-
depthStencil
|
|
2319
|
-
});
|
|
2320
2150
|
this.uniformBuffer = device.createBuffer({
|
|
2321
2151
|
size: 64,
|
|
2322
2152
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
@@ -2324,9 +2154,8 @@ class SkyboxRenderer {
|
|
|
2324
2154
|
this.sampler = device.createSampler({ magFilter: "linear", minFilter: "linear" });
|
|
2325
2155
|
}
|
|
2326
2156
|
get isActive() {
|
|
2327
|
-
return this.
|
|
2157
|
+
return this.active && this.texture !== null && this.bindGroup !== null;
|
|
2328
2158
|
}
|
|
2329
|
-
// ---- cubemap ----
|
|
2330
2159
|
async loadCubemap(faceUrls) {
|
|
2331
2160
|
this.clear();
|
|
2332
2161
|
const bitmaps = await Promise.all(
|
|
@@ -2367,41 +2196,9 @@ class SkyboxRenderer {
|
|
|
2367
2196
|
]
|
|
2368
2197
|
});
|
|
2369
2198
|
this.texture = tex;
|
|
2370
|
-
this.
|
|
2371
|
-
}
|
|
2372
|
-
// ---- equirectangular HDR ----
|
|
2373
|
-
async loadEquirectangular(url) {
|
|
2374
|
-
this.clear();
|
|
2375
|
-
const hdr = await loadHdr(url);
|
|
2376
|
-
const pixelCount = hdr.width * hdr.height * 4;
|
|
2377
|
-
const f16 = new Uint16Array(pixelCount);
|
|
2378
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
2379
|
-
f16[i] = float32ToFloat16(hdr.data[i]);
|
|
2380
|
-
}
|
|
2381
|
-
const tex = this.device.createTexture({
|
|
2382
|
-
size: [hdr.width, hdr.height],
|
|
2383
|
-
format: "rgba16float",
|
|
2384
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
2385
|
-
});
|
|
2386
|
-
this.device.queue.writeTexture(
|
|
2387
|
-
{ texture: tex },
|
|
2388
|
-
f16.buffer,
|
|
2389
|
-
{ bytesPerRow: hdr.width * 8, rowsPerImage: hdr.height },
|
|
2390
|
-
{ width: hdr.width, height: hdr.height }
|
|
2391
|
-
);
|
|
2392
|
-
this.bindGroup = this.device.createBindGroup({
|
|
2393
|
-
layout: this.equirectBindGroupLayout,
|
|
2394
|
-
entries: [
|
|
2395
|
-
{ binding: 0, resource: { buffer: this.uniformBuffer } },
|
|
2396
|
-
{ binding: 1, resource: this.sampler },
|
|
2397
|
-
{ binding: 2, resource: tex.createView() }
|
|
2398
|
-
]
|
|
2399
|
-
});
|
|
2400
|
-
this.texture = tex;
|
|
2401
|
-
this.mode = "equirect";
|
|
2199
|
+
this.active = true;
|
|
2402
2200
|
}
|
|
2403
|
-
|
|
2404
|
-
prepareFrame(viewMatrix, projectionMatrix, cameraPosition) {
|
|
2201
|
+
prepareFrame(viewMatrix, projectionMatrix) {
|
|
2405
2202
|
if (!this.isActive) {
|
|
2406
2203
|
this.frameReady = false;
|
|
2407
2204
|
return;
|
|
@@ -2419,17 +2216,12 @@ class SkyboxRenderer {
|
|
|
2419
2216
|
ud[9] = viewMatrix[9];
|
|
2420
2217
|
ud[10] = viewMatrix[10];
|
|
2421
2218
|
ud[11] = 0;
|
|
2422
|
-
ud[12] = cameraPosition ? cameraPosition[0] : 0;
|
|
2423
|
-
ud[13] = cameraPosition ? cameraPosition[1] : 0;
|
|
2424
|
-
ud[14] = cameraPosition ? cameraPosition[2] : 0;
|
|
2425
|
-
ud[15] = this.groundRadius;
|
|
2426
2219
|
this.device.queue.writeBuffer(this.uniformBuffer, 0, ud);
|
|
2427
2220
|
this.frameReady = true;
|
|
2428
2221
|
}
|
|
2429
2222
|
draw(pass) {
|
|
2430
2223
|
if (!this.frameReady || !this.bindGroup) return;
|
|
2431
|
-
|
|
2432
|
-
pass.setPipeline(pipeline);
|
|
2224
|
+
pass.setPipeline(this.cubePipeline);
|
|
2433
2225
|
pass.setBindGroup(0, this.bindGroup);
|
|
2434
2226
|
pass.draw(3);
|
|
2435
2227
|
}
|
|
@@ -2440,7 +2232,7 @@ class SkyboxRenderer {
|
|
|
2440
2232
|
}
|
|
2441
2233
|
this.bindGroup = null;
|
|
2442
2234
|
this.frameReady = false;
|
|
2443
|
-
this.
|
|
2235
|
+
this.active = false;
|
|
2444
2236
|
}
|
|
2445
2237
|
destroy() {
|
|
2446
2238
|
this.clear();
|
|
@@ -19059,36 +18851,9 @@ class App {
|
|
|
19059
18851
|
this.updateAdaptivePerformance();
|
|
19060
18852
|
this.hotspotManager.updateBillboards();
|
|
19061
18853
|
if ((_a2 = this.skyboxRenderer) == null ? void 0 : _a2.isActive) {
|
|
19062
|
-
const cam = this.camera.position;
|
|
19063
|
-
let groundLevel = 0;
|
|
19064
|
-
const gsRendererForGround = this.sceneManager.getGSRenderer();
|
|
19065
|
-
const bbox = (gsRendererForGround == null ? void 0 : gsRendererForGround.getBoundingBox()) ?? null;
|
|
19066
|
-
if (bbox) {
|
|
19067
|
-
const m = gsRendererForGround.getModelMatrix();
|
|
19068
|
-
groundLevel = m[5] * bbox.min[1] + m[13];
|
|
19069
|
-
}
|
|
19070
|
-
if (!this._hdrDebugLogged) {
|
|
19071
|
-
this._hdrDebugLogged = true;
|
|
19072
|
-
console.log(
|
|
19073
|
-
"[HDR Ground Debug]",
|
|
19074
|
-
"hasGsRenderer:",
|
|
19075
|
-
!!gsRendererForGround,
|
|
19076
|
-
"hasBbox:",
|
|
19077
|
-
!!bbox,
|
|
19078
|
-
"bbox:",
|
|
19079
|
-
bbox ? { min: [...bbox.min], max: [...bbox.max], center: [...bbox.center] } : null,
|
|
19080
|
-
"groundLevel:",
|
|
19081
|
-
groundLevel,
|
|
19082
|
-
"cam:",
|
|
19083
|
-
[cam[0].toFixed(2), cam[1].toFixed(2), cam[2].toFixed(2)]
|
|
19084
|
-
);
|
|
19085
|
-
}
|
|
19086
|
-
const camH = cam[1] - groundLevel;
|
|
19087
|
-
this.skyboxRenderer.groundRadius = 0.3;
|
|
19088
18854
|
this.skyboxRenderer.prepareFrame(
|
|
19089
18855
|
this.camera.viewMatrix,
|
|
19090
|
-
this.camera.projectionMatrix
|
|
19091
|
-
[cam[0], camH, cam[2]]
|
|
18856
|
+
this.camera.projectionMatrix
|
|
19092
18857
|
);
|
|
19093
18858
|
}
|
|
19094
18859
|
const pass = this.renderer.beginFrame();
|
|
@@ -19273,21 +19038,6 @@ class App {
|
|
|
19273
19038
|
faceUrls.negZ
|
|
19274
19039
|
]);
|
|
19275
19040
|
}
|
|
19276
|
-
async setHdrBackground(url) {
|
|
19277
|
-
if (!this.skyboxRenderer) {
|
|
19278
|
-
this.skyboxRenderer = new SkyboxRenderer(
|
|
19279
|
-
this.renderer.device,
|
|
19280
|
-
this.renderer.format,
|
|
19281
|
-
"depth24plus"
|
|
19282
|
-
);
|
|
19283
|
-
}
|
|
19284
|
-
await this.skyboxRenderer.loadEquirectangular(url);
|
|
19285
|
-
}
|
|
19286
|
-
setHdrGroundRadius(radius) {
|
|
19287
|
-
if (this.skyboxRenderer) {
|
|
19288
|
-
this.skyboxRenderer.groundRadius = radius;
|
|
19289
|
-
}
|
|
19290
|
-
}
|
|
19291
19041
|
clearSkybox() {
|
|
19292
19042
|
var _a2;
|
|
19293
19043
|
(_a2 = this.skyboxRenderer) == null ? void 0 : _a2.clear();
|
|
@@ -19780,7 +19530,6 @@ export {
|
|
|
19780
19530
|
getRecommendedDPR,
|
|
19781
19531
|
isMobileDevice,
|
|
19782
19532
|
isWebGPUSupported,
|
|
19783
|
-
loadHdr,
|
|
19784
19533
|
loadPLY,
|
|
19785
19534
|
loadPLYMobile,
|
|
19786
19535
|
loadSOG,
|