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