@d5techs/3dgs-lib 1.1.2 → 1.2.0

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
@@ -1698,6 +1698,429 @@ class BoundingBoxRenderer {
1698
1698
  this.bindGroup = null;
1699
1699
  }
1700
1700
  }
1701
+ class SceneAidsRenderer {
1702
+ constructor(renderer, camera) {
1703
+ __publicField(this, "renderer");
1704
+ __publicField(this, "camera");
1705
+ // 可见性
1706
+ __publicField(this, "_gridVisible", true);
1707
+ __publicField(this, "_axesVisible", true);
1708
+ // Grid GPU 资源
1709
+ __publicField(this, "gridPipeline", null);
1710
+ __publicField(this, "gridUniformBuffer", null);
1711
+ __publicField(this, "gridBindGroup", null);
1712
+ // Axes GPU 资源
1713
+ __publicField(this, "axesPipeline", null);
1714
+ __publicField(this, "axesUniformBuffer", null);
1715
+ __publicField(this, "axesBindGroup", null);
1716
+ __publicField(this, "axesVertexBuffer", null);
1717
+ this.renderer = renderer;
1718
+ this.camera = camera;
1719
+ this.createGridPipeline();
1720
+ this.createAxesPipeline();
1721
+ }
1722
+ // ─── Grid Pipeline ──────────────────────────────────────
1723
+ createGridPipeline() {
1724
+ const device = this.renderer.device;
1725
+ const shaderCode = (
1726
+ /* wgsl */
1727
+ `
1728
+ struct Uniforms {
1729
+ viewProjection: mat4x4<f32>,
1730
+ invViewProjection: mat4x4<f32>,
1731
+ cameraPos: vec3<f32>,
1732
+ _pad: f32,
1733
+ }
1734
+
1735
+ @group(0) @binding(0) var<uniform> u: Uniforms;
1736
+
1737
+ struct VSOut {
1738
+ @builtin(position) position: vec4<f32>,
1739
+ @location(0) nearPoint: vec3<f32>,
1740
+ @location(1) farPoint: vec3<f32>,
1741
+ }
1742
+
1743
+ // fullscreen triangle positions (NDC)
1744
+ var<private> triPos: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
1745
+ vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(1.0, 1.0),
1746
+ vec2(-1.0, -1.0), vec2(1.0, 1.0), vec2(-1.0, 1.0),
1747
+ );
1748
+
1749
+ fn unprojectPoint(p: vec3<f32>) -> vec3<f32> {
1750
+ let clip = u.invViewProjection * vec4(p, 1.0);
1751
+ return clip.xyz / clip.w;
1752
+ }
1753
+
1754
+ @vertex
1755
+ fn vertexMain(@builtin(vertex_index) vid: u32) -> VSOut {
1756
+ var out: VSOut;
1757
+ let xy = triPos[vid];
1758
+ out.nearPoint = unprojectPoint(vec3(xy, 0.0));
1759
+ out.farPoint = unprojectPoint(vec3(xy, 1.0));
1760
+ out.position = vec4(xy, 0.0, 1.0);
1761
+ return out;
1762
+ }
1763
+
1764
+ struct FSOut {
1765
+ @location(0) color: vec4<f32>,
1766
+ @builtin(frag_depth) depth: f32,
1767
+ }
1768
+
1769
+ fn gridLine(coord: vec2<f32>, scale: f32) -> f32 {
1770
+ let d = abs(fract(coord * scale - 0.5) - 0.5) / fwidth(coord * scale);
1771
+ return 1.0 - min(min(d.x, d.y), 1.0);
1772
+ }
1773
+
1774
+ fn computeDepth(worldPos: vec3<f32>) -> f32 {
1775
+ let clip = u.viewProjection * vec4(worldPos, 1.0);
1776
+ return clip.z / clip.w;
1777
+ }
1778
+
1779
+ @fragment
1780
+ fn fragmentMain(input: VSOut) -> FSOut {
1781
+ // ray-plane intersection with Y=0
1782
+ let t = -input.nearPoint.y / (input.farPoint.y - input.nearPoint.y);
1783
+ let worldPos = input.nearPoint + t * (input.farPoint - input.nearPoint);
1784
+
1785
+ var out: FSOut;
1786
+ out.depth = computeDepth(worldPos);
1787
+
1788
+ // only draw if we actually hit the plane (t > 0) and depth is valid
1789
+ let visible = t > 0.0 && out.depth > 0.0 && out.depth < 1.0;
1790
+
1791
+ if (!visible) {
1792
+ discard;
1793
+ }
1794
+
1795
+ // minor grid (1 unit)
1796
+ let minorGrid = gridLine(worldPos.xz, 1.0);
1797
+ // major grid (10 units)
1798
+ let majorGrid = gridLine(worldPos.xz, 0.1);
1799
+
1800
+ // fade by distance from camera
1801
+ let dist = length(worldPos.xz - u.cameraPos.xz);
1802
+ let fadeFar = 1.0 - smoothstep(30.0, 150.0, dist);
1803
+
1804
+ // base color
1805
+ var color = vec3(0.35);
1806
+ var alpha = minorGrid * 0.15 * fadeFar;
1807
+
1808
+ // major lines brighter
1809
+ alpha = max(alpha, majorGrid * 0.3 * fadeFar);
1810
+
1811
+ // X axis (red line at z=0)
1812
+ let xAxisDist = abs(worldPos.z);
1813
+ let xAxisLine = 1.0 - smoothstep(0.0, fwidth(worldPos.z) * 1.5, xAxisDist);
1814
+ if (xAxisLine > 0.01) {
1815
+ color = mix(color, vec3(0.8, 0.2, 0.2), xAxisLine);
1816
+ alpha = max(alpha, xAxisLine * 0.8 * fadeFar);
1817
+ }
1818
+
1819
+ // Z axis (blue line at x=0)
1820
+ let zAxisDist = abs(worldPos.x);
1821
+ let zAxisLine = 1.0 - smoothstep(0.0, fwidth(worldPos.x) * 1.5, zAxisDist);
1822
+ if (zAxisLine > 0.01) {
1823
+ color = mix(color, vec3(0.2, 0.4, 0.9), zAxisLine);
1824
+ alpha = max(alpha, zAxisLine * 0.8 * fadeFar);
1825
+ }
1826
+
1827
+ out.color = vec4(color, alpha);
1828
+ return out;
1829
+ }
1830
+ `
1831
+ );
1832
+ const shaderModule = device.createShaderModule({ code: shaderCode });
1833
+ this.gridUniformBuffer = device.createBuffer({
1834
+ size: 144,
1835
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
1836
+ });
1837
+ const bindGroupLayout = device.createBindGroupLayout({
1838
+ entries: [{
1839
+ binding: 0,
1840
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
1841
+ buffer: { type: "uniform" }
1842
+ }]
1843
+ });
1844
+ this.gridBindGroup = device.createBindGroup({
1845
+ layout: bindGroupLayout,
1846
+ entries: [{ binding: 0, resource: { buffer: this.gridUniformBuffer } }]
1847
+ });
1848
+ const pipelineLayout = device.createPipelineLayout({
1849
+ bindGroupLayouts: [bindGroupLayout]
1850
+ });
1851
+ this.gridPipeline = device.createRenderPipeline({
1852
+ layout: pipelineLayout,
1853
+ vertex: {
1854
+ module: shaderModule,
1855
+ entryPoint: "vertexMain"
1856
+ },
1857
+ fragment: {
1858
+ module: shaderModule,
1859
+ entryPoint: "fragmentMain",
1860
+ targets: [{
1861
+ format: this.renderer.format,
1862
+ blend: {
1863
+ color: {
1864
+ srcFactor: "src-alpha",
1865
+ dstFactor: "one-minus-src-alpha",
1866
+ operation: "add"
1867
+ },
1868
+ alpha: {
1869
+ srcFactor: "one",
1870
+ dstFactor: "one-minus-src-alpha",
1871
+ operation: "add"
1872
+ }
1873
+ }
1874
+ }]
1875
+ },
1876
+ primitive: {
1877
+ topology: "triangle-list",
1878
+ cullMode: "none"
1879
+ },
1880
+ depthStencil: {
1881
+ format: this.renderer.depthFormat,
1882
+ depthWriteEnabled: true,
1883
+ depthCompare: "less-equal"
1884
+ }
1885
+ });
1886
+ }
1887
+ // ─── Axes Pipeline ──────────────────────────────────────
1888
+ createAxesPipeline() {
1889
+ const device = this.renderer.device;
1890
+ const shaderCode = (
1891
+ /* wgsl */
1892
+ `
1893
+ struct Uniforms {
1894
+ viewProjection: mat4x4<f32>,
1895
+ }
1896
+
1897
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
1898
+
1899
+ struct VertexInput {
1900
+ @location(0) position: vec3<f32>,
1901
+ @location(1) color: vec3<f32>,
1902
+ }
1903
+
1904
+ struct VertexOutput {
1905
+ @builtin(position) position: vec4<f32>,
1906
+ @location(0) color: vec3<f32>,
1907
+ }
1908
+
1909
+ @vertex
1910
+ fn vertexMain(input: VertexInput) -> VertexOutput {
1911
+ var output: VertexOutput;
1912
+ output.position = uniforms.viewProjection * vec4(input.position, 1.0);
1913
+ output.color = input.color;
1914
+ return output;
1915
+ }
1916
+
1917
+ @fragment
1918
+ fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
1919
+ return vec4(input.color, 1.0);
1920
+ }
1921
+ `
1922
+ );
1923
+ const shaderModule = device.createShaderModule({ code: shaderCode });
1924
+ this.axesUniformBuffer = device.createBuffer({
1925
+ size: 64,
1926
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
1927
+ });
1928
+ const bindGroupLayout = device.createBindGroupLayout({
1929
+ entries: [{
1930
+ binding: 0,
1931
+ visibility: GPUShaderStage.VERTEX,
1932
+ buffer: { type: "uniform" }
1933
+ }]
1934
+ });
1935
+ this.axesBindGroup = device.createBindGroup({
1936
+ layout: bindGroupLayout,
1937
+ entries: [{ binding: 0, resource: { buffer: this.axesUniformBuffer } }]
1938
+ });
1939
+ const pipelineLayout = device.createPipelineLayout({
1940
+ bindGroupLayouts: [bindGroupLayout]
1941
+ });
1942
+ this.axesPipeline = device.createRenderPipeline({
1943
+ layout: pipelineLayout,
1944
+ vertex: {
1945
+ module: shaderModule,
1946
+ entryPoint: "vertexMain",
1947
+ buffers: [{
1948
+ arrayStride: 24,
1949
+ attributes: [
1950
+ { shaderLocation: 0, offset: 0, format: "float32x3" },
1951
+ { shaderLocation: 1, offset: 12, format: "float32x3" }
1952
+ ]
1953
+ }]
1954
+ },
1955
+ fragment: {
1956
+ module: shaderModule,
1957
+ entryPoint: "fragmentMain",
1958
+ targets: [{ format: this.renderer.format }]
1959
+ },
1960
+ primitive: {
1961
+ topology: "line-list",
1962
+ cullMode: "none"
1963
+ },
1964
+ depthStencil: {
1965
+ format: this.renderer.depthFormat,
1966
+ depthWriteEnabled: true,
1967
+ depthCompare: "less-equal"
1968
+ }
1969
+ });
1970
+ this.createAxesVertexBuffer();
1971
+ }
1972
+ createAxesVertexBuffer() {
1973
+ const device = this.renderer.device;
1974
+ const L = 100;
1975
+ const verts = new Float32Array([
1976
+ // X axis: red
1977
+ 0,
1978
+ 0,
1979
+ 0,
1980
+ 0.9,
1981
+ 0.2,
1982
+ 0.2,
1983
+ L,
1984
+ 0,
1985
+ 0,
1986
+ 0.9,
1987
+ 0.2,
1988
+ 0.2,
1989
+ // Y axis: green
1990
+ 0,
1991
+ 0,
1992
+ 0,
1993
+ 0.2,
1994
+ 0.8,
1995
+ 0.2,
1996
+ 0,
1997
+ L,
1998
+ 0,
1999
+ 0.2,
2000
+ 0.8,
2001
+ 0.2,
2002
+ // Z axis: blue
2003
+ 0,
2004
+ 0,
2005
+ 0,
2006
+ 0.3,
2007
+ 0.4,
2008
+ 0.9,
2009
+ 0,
2010
+ 0,
2011
+ L,
2012
+ 0.3,
2013
+ 0.4,
2014
+ 0.9
2015
+ ]);
2016
+ this.axesVertexBuffer = device.createBuffer({
2017
+ size: verts.byteLength,
2018
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
2019
+ });
2020
+ device.queue.writeBuffer(this.axesVertexBuffer, 0, verts);
2021
+ }
2022
+ // ─── Public API ─────────────────────────────────────────
2023
+ get gridVisible() {
2024
+ return this._gridVisible;
2025
+ }
2026
+ set gridVisible(v) {
2027
+ this._gridVisible = v;
2028
+ }
2029
+ get axesVisible() {
2030
+ return this._axesVisible;
2031
+ }
2032
+ set axesVisible(v) {
2033
+ this._axesVisible = v;
2034
+ }
2035
+ // ─── Render ─────────────────────────────────────────────
2036
+ render(pass) {
2037
+ if (this._gridVisible) {
2038
+ this.renderGrid(pass);
2039
+ }
2040
+ if (this._axesVisible) {
2041
+ this.renderAxes(pass);
2042
+ }
2043
+ }
2044
+ renderGrid(pass) {
2045
+ if (!this.gridPipeline || !this.gridBindGroup || !this.gridUniformBuffer) return;
2046
+ const device = this.renderer.device;
2047
+ const vp = new Float32Array(this.camera.viewProjectionMatrix);
2048
+ const invVP = this.invertMatrix4(vp);
2049
+ const camPos = new Float32Array(this.camera.position);
2050
+ const buf = new Float32Array(36);
2051
+ buf.set(vp, 0);
2052
+ buf.set(invVP, 16);
2053
+ buf.set(camPos, 32);
2054
+ device.queue.writeBuffer(this.gridUniformBuffer, 0, buf);
2055
+ pass.setPipeline(this.gridPipeline);
2056
+ pass.setBindGroup(0, this.gridBindGroup);
2057
+ pass.draw(6);
2058
+ }
2059
+ renderAxes(pass) {
2060
+ if (!this.axesPipeline || !this.axesBindGroup || !this.axesUniformBuffer || !this.axesVertexBuffer) return;
2061
+ const device = this.renderer.device;
2062
+ const vpMatrix = new Float32Array(this.camera.viewProjectionMatrix);
2063
+ device.queue.writeBuffer(this.axesUniformBuffer, 0, vpMatrix);
2064
+ pass.setPipeline(this.axesPipeline);
2065
+ pass.setBindGroup(0, this.axesBindGroup);
2066
+ pass.setVertexBuffer(0, this.axesVertexBuffer);
2067
+ pass.draw(6);
2068
+ }
2069
+ // ─── Math helpers ───────────────────────────────────────
2070
+ invertMatrix4(m) {
2071
+ const out = new Float32Array(16);
2072
+ const a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3];
2073
+ const a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7];
2074
+ const a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11];
2075
+ const a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15];
2076
+ const b00 = a00 * a11 - a01 * a10;
2077
+ const b01 = a00 * a12 - a02 * a10;
2078
+ const b02 = a00 * a13 - a03 * a10;
2079
+ const b03 = a01 * a12 - a02 * a11;
2080
+ const b04 = a01 * a13 - a03 * a11;
2081
+ const b05 = a02 * a13 - a03 * a12;
2082
+ const b06 = a20 * a31 - a21 * a30;
2083
+ const b07 = a20 * a32 - a22 * a30;
2084
+ const b08 = a20 * a33 - a23 * a30;
2085
+ const b09 = a21 * a32 - a22 * a31;
2086
+ const b10 = a21 * a33 - a23 * a31;
2087
+ const b11 = a22 * a33 - a23 * a32;
2088
+ let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
2089
+ if (Math.abs(det) < 1e-10) return out;
2090
+ det = 1 / det;
2091
+ out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
2092
+ out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
2093
+ out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
2094
+ out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
2095
+ out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
2096
+ out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
2097
+ out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
2098
+ out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
2099
+ out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
2100
+ out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
2101
+ out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
2102
+ out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
2103
+ out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
2104
+ out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
2105
+ out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
2106
+ out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
2107
+ return out;
2108
+ }
2109
+ // ─── Cleanup ────────────────────────────────────────────
2110
+ destroy() {
2111
+ var _a2, _b2, _c;
2112
+ (_a2 = this.gridUniformBuffer) == null ? void 0 : _a2.destroy();
2113
+ (_b2 = this.axesUniformBuffer) == null ? void 0 : _b2.destroy();
2114
+ (_c = this.axesVertexBuffer) == null ? void 0 : _c.destroy();
2115
+ this.gridUniformBuffer = null;
2116
+ this.axesUniformBuffer = null;
2117
+ this.axesVertexBuffer = null;
2118
+ this.gridPipeline = null;
2119
+ this.axesPipeline = null;
2120
+ this.gridBindGroup = null;
2121
+ this.axesBindGroup = null;
2122
+ }
2123
+ }
1701
2124
  class Mesh {
1702
2125
  constructor(vertexBuffer, vertexCount, indexBuffer = null, indexCount = 0, boundingBox) {
1703
2126
  __publicField(this, "vertexBuffer");
@@ -13134,7 +13557,7 @@ class HotspotManager {
13134
13557
  console.warn("HotspotManager: OBJ 加载无网格");
13135
13558
  return;
13136
13559
  }
13137
- const meshStartIndex = this.meshRenderer.getMeshCount();
13560
+ const meshStartIndex = this.meshRenderer.getOverlayMeshCount();
13138
13561
  let placedScale = 1;
13139
13562
  let placedLocalCenter = [0, 0, 0];
13140
13563
  const firstBbox = loadedMeshes[0].mesh.getLocalBoundingBox();
@@ -13471,6 +13894,7 @@ class App {
13471
13894
  __publicField(this, "sceneManager");
13472
13895
  __publicField(this, "gizmoManager");
13473
13896
  __publicField(this, "hotspotManager");
13897
+ __publicField(this, "sceneAids");
13474
13898
  __publicField(this, "isRunning", false);
13475
13899
  __publicField(this, "animationId", 0);
13476
13900
  // 是否使用移动端渲染器
@@ -13506,6 +13930,7 @@ class App {
13506
13930
  this.controls,
13507
13931
  this.meshRenderer
13508
13932
  );
13933
+ this.sceneAids = new SceneAidsRenderer(this.renderer, this.camera);
13509
13934
  window.addEventListener("resize", this.boundOnResize);
13510
13935
  }
13511
13936
  // ============================================
@@ -13726,6 +14151,7 @@ class App {
13726
14151
  gsRenderer.render(pass);
13727
14152
  }
13728
14153
  this.meshRenderer.render(pass);
14154
+ this.sceneAids.render(pass);
13729
14155
  this.gizmoManager.render(pass);
13730
14156
  this.renderer.endFrame();
13731
14157
  this.hotspotManager.consumeGPUResult();
@@ -14002,6 +14428,24 @@ class App {
14002
14428
  const { parsePLYBuffer: parsePLYBuffer2 } = await Promise.resolve().then(() => PLYLoaderMobile);
14003
14429
  return parsePLYBuffer2(buffer, options);
14004
14430
  }
14431
+ // ============================================
14432
+ // 场景辅助(网格 + 坐标轴)
14433
+ // ============================================
14434
+ setGridVisible(visible) {
14435
+ this.sceneAids.gridVisible = visible;
14436
+ }
14437
+ getGridVisible() {
14438
+ return this.sceneAids.gridVisible;
14439
+ }
14440
+ setAxesVisible(visible) {
14441
+ this.sceneAids.axesVisible = visible;
14442
+ }
14443
+ getAxesVisible() {
14444
+ return this.sceneAids.axesVisible;
14445
+ }
14446
+ getSceneAidsRenderer() {
14447
+ return this.sceneAids;
14448
+ }
14005
14449
  /**
14006
14450
  * 销毁应用及所有资源
14007
14451
  */
@@ -14011,6 +14455,7 @@ class App {
14011
14455
  this.sceneManager.destroy();
14012
14456
  this.gizmoManager.destroy();
14013
14457
  this.hotspotManager.destroy();
14458
+ this.sceneAids.destroy();
14014
14459
  if (this.meshRenderer) {
14015
14460
  this.meshRenderer.destroy();
14016
14461
  }
@@ -14045,6 +14490,7 @@ export {
14045
14490
  OrbitControls,
14046
14491
  Renderer,
14047
14492
  SHMode,
14493
+ SceneAidsRenderer,
14048
14494
  SceneManager,
14049
14495
  SplatBoundingBoxProvider,
14050
14496
  SplatTransformProxy,