@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.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, "mode", "none");
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.mode !== "none" && this.texture !== null && this.bindGroup !== null;
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.mode = "cubemap";
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
- // ---- common ----
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
- const pipeline = this.mode === "cubemap" ? this.cubePipeline : this.equirectPipeline;
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.mode = "none";
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,