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