@d5techs/3dgs-lib 1.1.3 → 1.3.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 +492 -18
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +492 -18
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +13 -1
- package/dist/core/SceneAidsRenderer.d.ts +32 -0
- package/dist/gs/PLYLoader.d.ts +7 -1
- package/dist/gs/PLYLoaderMobile.d.ts +8 -0
- package/dist/index.d.ts +3 -2
- package/package.json +1 -1
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");
|
|
@@ -3990,7 +4413,8 @@ function readProperty$1(dataView, baseOffset, prop, littleEndian) {
|
|
|
3990
4413
|
function sigmoid$1(x) {
|
|
3991
4414
|
return 1 / (1 + Math.exp(-x));
|
|
3992
4415
|
}
|
|
3993
|
-
async function loadPLY(url) {
|
|
4416
|
+
async function loadPLY(url, options = {}) {
|
|
4417
|
+
const { coordinateSystem = "blender" } = options;
|
|
3994
4418
|
const response = await fetch(url);
|
|
3995
4419
|
if (!response.ok) {
|
|
3996
4420
|
throw new Error(`无法加载 PLY 文件: ${url}`);
|
|
@@ -4002,6 +4426,7 @@ async function loadPLY(url) {
|
|
|
4002
4426
|
throw new Error("不支持 ASCII 格式的 PLY 文件,请使用 binary_little_endian 或 binary_big_endian 格式");
|
|
4003
4427
|
}
|
|
4004
4428
|
const littleEndian = format === "binary_little_endian";
|
|
4429
|
+
const swapYZ = coordinateSystem === "blender";
|
|
4005
4430
|
const propMap = buildPropertyMap(properties);
|
|
4006
4431
|
const props = {
|
|
4007
4432
|
x: propMap.get("x"),
|
|
@@ -4067,9 +4492,14 @@ async function loadPLY(url) {
|
|
|
4067
4492
|
shRest[dstBase + 2] = srcB < shRestProps.length ? readProperty$1(dataView, base, shRestProps[srcB], littleEndian) : 0;
|
|
4068
4493
|
}
|
|
4069
4494
|
splats[i] = {
|
|
4070
|
-
mean: [x, y, z],
|
|
4071
|
-
scale: [scale_0, scale_1, scale_2],
|
|
4072
|
-
rotation: [
|
|
4495
|
+
mean: swapYZ ? [x, z, y] : [x, y, z],
|
|
4496
|
+
scale: swapYZ ? [scale_0, scale_2, scale_1] : [scale_0, scale_1, scale_2],
|
|
4497
|
+
rotation: swapYZ ? [
|
|
4498
|
+
-rot_0 * qnorm,
|
|
4499
|
+
rot_1 * qnorm,
|
|
4500
|
+
rot_3 * qnorm,
|
|
4501
|
+
rot_2 * qnorm
|
|
4502
|
+
] : [
|
|
4073
4503
|
rot_0 * qnorm,
|
|
4074
4504
|
rot_1 * qnorm,
|
|
4075
4505
|
rot_2 * qnorm,
|
|
@@ -4285,7 +4715,8 @@ async function parsePLYBuffer(buffer, options = {}) {
|
|
|
4285
4715
|
const {
|
|
4286
4716
|
maxSplats = 2e5,
|
|
4287
4717
|
loadSH = false,
|
|
4288
|
-
onProgress
|
|
4718
|
+
onProgress,
|
|
4719
|
+
coordinateSystem = "blender"
|
|
4289
4720
|
} = options;
|
|
4290
4721
|
const seed = options.seed ?? buffer.byteLength;
|
|
4291
4722
|
const { headerText, dataOffset } = extractHeader(buffer);
|
|
@@ -4375,27 +4806,41 @@ async function parsePLYBuffer(buffer, options = {}) {
|
|
|
4375
4806
|
const opacities = new Float32Array(actualCount);
|
|
4376
4807
|
const shCoeffs = loadSH ? new Float32Array(actualCount * 45) : void 0;
|
|
4377
4808
|
const dataView = new DataView(buffer, dataOffset);
|
|
4809
|
+
const swapYZ = coordinateSystem === "blender";
|
|
4378
4810
|
let outputIdx = 0;
|
|
4379
4811
|
let lastProgress = 0;
|
|
4380
4812
|
for (let i = 0; i < actualCount; i++) {
|
|
4381
4813
|
const srcIdx = sampleIndices ? sampleIndices[i] : i;
|
|
4382
4814
|
const base = srcIdx * stride;
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4815
|
+
const px = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;
|
|
4816
|
+
const py = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;
|
|
4817
|
+
const pz = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;
|
|
4818
|
+
positions[outputIdx * 3 + 0] = px;
|
|
4819
|
+
positions[outputIdx * 3 + 1] = swapYZ ? pz : py;
|
|
4820
|
+
positions[outputIdx * 3 + 2] = swapYZ ? py : pz;
|
|
4821
|
+
const sx = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;
|
|
4822
|
+
const sy = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;
|
|
4823
|
+
const sz = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;
|
|
4824
|
+
scales[outputIdx * 3 + 0] = sx;
|
|
4825
|
+
scales[outputIdx * 3 + 1] = swapYZ ? sz : sy;
|
|
4826
|
+
scales[outputIdx * 3 + 2] = swapYZ ? sy : sz;
|
|
4389
4827
|
const rot_0 = offsets.rot_0 >= 0 ? readProperty(dataView, base + offsets.rot_0, types.rot_0, littleEndian) : 1;
|
|
4390
4828
|
const rot_1 = offsets.rot_1 >= 0 ? readProperty(dataView, base + offsets.rot_1, types.rot_1, littleEndian) : 0;
|
|
4391
4829
|
const rot_2 = offsets.rot_2 >= 0 ? readProperty(dataView, base + offsets.rot_2, types.rot_2, littleEndian) : 0;
|
|
4392
4830
|
const rot_3 = offsets.rot_3 >= 0 ? readProperty(dataView, base + offsets.rot_3, types.rot_3, littleEndian) : 0;
|
|
4393
4831
|
const qlen = Math.sqrt(rot_0 * rot_0 + rot_1 * rot_1 + rot_2 * rot_2 + rot_3 * rot_3);
|
|
4394
4832
|
const qnorm = qlen > 0 ? 1 / qlen : 1;
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4833
|
+
if (swapYZ) {
|
|
4834
|
+
rotations[outputIdx * 4 + 0] = -rot_0 * qnorm;
|
|
4835
|
+
rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
|
|
4836
|
+
rotations[outputIdx * 4 + 2] = rot_3 * qnorm;
|
|
4837
|
+
rotations[outputIdx * 4 + 3] = rot_2 * qnorm;
|
|
4838
|
+
} else {
|
|
4839
|
+
rotations[outputIdx * 4 + 0] = rot_0 * qnorm;
|
|
4840
|
+
rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
|
|
4841
|
+
rotations[outputIdx * 4 + 2] = rot_2 * qnorm;
|
|
4842
|
+
rotations[outputIdx * 4 + 3] = rot_3 * qnorm;
|
|
4843
|
+
}
|
|
4399
4844
|
const f_dc_0 = offsets.f_dc_0 >= 0 ? readProperty(dataView, base + offsets.f_dc_0, types.f_dc_0, littleEndian) : 0;
|
|
4400
4845
|
const f_dc_1 = offsets.f_dc_1 >= 0 ? readProperty(dataView, base + offsets.f_dc_1, types.f_dc_1, littleEndian) : 0;
|
|
4401
4846
|
const f_dc_2 = offsets.f_dc_2 >= 0 ? readProperty(dataView, base + offsets.f_dc_2, types.f_dc_2, littleEndian) : 0;
|
|
@@ -13473,6 +13918,7 @@ class App {
|
|
|
13473
13918
|
__publicField(this, "sceneManager");
|
|
13474
13919
|
__publicField(this, "gizmoManager");
|
|
13475
13920
|
__publicField(this, "hotspotManager");
|
|
13921
|
+
__publicField(this, "sceneAids");
|
|
13476
13922
|
__publicField(this, "isRunning", false);
|
|
13477
13923
|
__publicField(this, "animationId", 0);
|
|
13478
13924
|
// 是否使用移动端渲染器
|
|
@@ -13508,6 +13954,7 @@ class App {
|
|
|
13508
13954
|
this.controls,
|
|
13509
13955
|
this.meshRenderer
|
|
13510
13956
|
);
|
|
13957
|
+
this.sceneAids = new SceneAidsRenderer(this.renderer, this.camera);
|
|
13511
13958
|
window.addEventListener("resize", this.boundOnResize);
|
|
13512
13959
|
}
|
|
13513
13960
|
// ============================================
|
|
@@ -13545,8 +13992,12 @@ class App {
|
|
|
13545
13992
|
}
|
|
13546
13993
|
/**
|
|
13547
13994
|
* 加载 PLY 文件 (3D Gaussian Splatting)
|
|
13995
|
+
* @param urlOrBuffer PLY 文件 URL 或 ArrayBuffer
|
|
13996
|
+
* @param onProgress 进度回调
|
|
13997
|
+
* @param isLocalFile 是否为本地文件
|
|
13998
|
+
* @param coordinateSystem 源数据坐标系,默认 'blender'(Z-up → Y-up 自动转换)
|
|
13548
13999
|
*/
|
|
13549
|
-
async addPLY(urlOrBuffer, onProgress, isLocalFile = false) {
|
|
14000
|
+
async addPLY(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
|
|
13550
14001
|
try {
|
|
13551
14002
|
const isMobile = isMobileDevice();
|
|
13552
14003
|
let buffer;
|
|
@@ -13575,7 +14026,8 @@ class App {
|
|
|
13575
14026
|
const compactData = await this.parsePLYBuffer(buffer, {
|
|
13576
14027
|
maxSplats: Infinity,
|
|
13577
14028
|
loadSH: false,
|
|
13578
|
-
onProgress: parseProgressCallback
|
|
14029
|
+
onProgress: parseProgressCallback,
|
|
14030
|
+
coordinateSystem
|
|
13579
14031
|
});
|
|
13580
14032
|
if (onProgress) onProgress(90, "upload");
|
|
13581
14033
|
gsRenderer.setCompactData(compactData);
|
|
@@ -13589,7 +14041,8 @@ class App {
|
|
|
13589
14041
|
const compactData = await this.parsePLYBuffer(buffer, {
|
|
13590
14042
|
maxSplats: Infinity,
|
|
13591
14043
|
loadSH: true,
|
|
13592
|
-
onProgress: parseProgressCallback
|
|
14044
|
+
onProgress: parseProgressCallback,
|
|
14045
|
+
coordinateSystem
|
|
13593
14046
|
});
|
|
13594
14047
|
if (onProgress) onProgress(90, "upload");
|
|
13595
14048
|
gsRenderer.setCompactData(compactData);
|
|
@@ -13728,6 +14181,7 @@ class App {
|
|
|
13728
14181
|
gsRenderer.render(pass);
|
|
13729
14182
|
}
|
|
13730
14183
|
this.meshRenderer.render(pass);
|
|
14184
|
+
this.sceneAids.render(pass);
|
|
13731
14185
|
this.gizmoManager.render(pass);
|
|
13732
14186
|
this.renderer.endFrame();
|
|
13733
14187
|
this.hotspotManager.consumeGPUResult();
|
|
@@ -14004,6 +14458,24 @@ class App {
|
|
|
14004
14458
|
const { parsePLYBuffer: parsePLYBuffer2 } = await Promise.resolve().then(() => PLYLoaderMobile);
|
|
14005
14459
|
return parsePLYBuffer2(buffer, options);
|
|
14006
14460
|
}
|
|
14461
|
+
// ============================================
|
|
14462
|
+
// 场景辅助(网格 + 坐标轴)
|
|
14463
|
+
// ============================================
|
|
14464
|
+
setGridVisible(visible) {
|
|
14465
|
+
this.sceneAids.gridVisible = visible;
|
|
14466
|
+
}
|
|
14467
|
+
getGridVisible() {
|
|
14468
|
+
return this.sceneAids.gridVisible;
|
|
14469
|
+
}
|
|
14470
|
+
setAxesVisible(visible) {
|
|
14471
|
+
this.sceneAids.axesVisible = visible;
|
|
14472
|
+
}
|
|
14473
|
+
getAxesVisible() {
|
|
14474
|
+
return this.sceneAids.axesVisible;
|
|
14475
|
+
}
|
|
14476
|
+
getSceneAidsRenderer() {
|
|
14477
|
+
return this.sceneAids;
|
|
14478
|
+
}
|
|
14007
14479
|
/**
|
|
14008
14480
|
* 销毁应用及所有资源
|
|
14009
14481
|
*/
|
|
@@ -14013,6 +14485,7 @@ class App {
|
|
|
14013
14485
|
this.sceneManager.destroy();
|
|
14014
14486
|
this.gizmoManager.destroy();
|
|
14015
14487
|
this.hotspotManager.destroy();
|
|
14488
|
+
this.sceneAids.destroy();
|
|
14016
14489
|
if (this.meshRenderer) {
|
|
14017
14490
|
this.meshRenderer.destroy();
|
|
14018
14491
|
}
|
|
@@ -14046,6 +14519,7 @@ exports.OBJParser = OBJParser;
|
|
|
14046
14519
|
exports.OrbitControls = OrbitControls;
|
|
14047
14520
|
exports.Renderer = Renderer;
|
|
14048
14521
|
exports.SHMode = SHMode;
|
|
14522
|
+
exports.SceneAidsRenderer = SceneAidsRenderer;
|
|
14049
14523
|
exports.SceneManager = SceneManager;
|
|
14050
14524
|
exports.SplatBoundingBoxProvider = SplatBoundingBoxProvider;
|
|
14051
14525
|
exports.SplatTransformProxy = SplatTransformProxy;
|