@d5techs/3dgs-lib 1.4.82 → 1.4.84

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
@@ -17566,188 +17566,31 @@ class EyedropperSelection {
17566
17566
  }
17567
17567
  }
17568
17568
  class SphereSelection {
17569
- constructor(parent, callbacks) {
17570
- __publicField(this, "parent");
17571
- __publicField(this, "toolbar");
17572
- __publicField(this, "callbacks");
17569
+ constructor(onRadiusChanged) {
17573
17570
  __publicField(this, "_radius", 1);
17574
- __publicField(this, "radiusInput");
17575
- this.parent = parent;
17576
- this.callbacks = callbacks;
17577
- this.toolbar = document.createElement("div");
17578
- this.toolbar.className = "select-toolbar";
17579
- this.toolbar.style.cssText = `
17580
- position:absolute; bottom:100px; left:50%; transform:translateX(-50%);
17581
- height:54px; display:none; z-index:20;
17582
- padding:0 8px; border-radius:8px;
17583
- background:rgba(30,30,30,0.92);
17584
- align-items:center; font-size:13px; color:#ddd;
17585
- backdrop-filter:blur(6px); user-select:none; white-space:nowrap;
17586
- box-shadow:0 2px 12px rgba(0,0,0,0.4);
17587
- `;
17588
- this.toolbar.addEventListener("pointerdown", (e) => e.stopPropagation());
17589
- this.toolbar.addEventListener("wheel", (e) => e.stopPropagation());
17590
- const mkBtn = (label2, op) => {
17591
- const btn = document.createElement("button");
17592
- btn.textContent = label2;
17593
- btn.style.cssText = `
17594
- height:38px; padding:0 16px; border:none; border-radius:2px;
17595
- background:#444; color:#ddd; cursor:pointer; font-size:13px;
17596
- transition: background 0.15s;
17597
- `;
17598
- btn.addEventListener("mouseenter", () => {
17599
- btn.style.background = "#555";
17600
- });
17601
- btn.addEventListener("mouseleave", () => {
17602
- btn.style.background = "#444";
17603
- });
17604
- btn.addEventListener("pointerdown", (e) => {
17605
- e.stopPropagation();
17606
- this.callbacks.onApply(op);
17607
- });
17608
- return btn;
17609
- };
17610
- const setBtn = mkBtn("Set", "set");
17611
- const addBtn = mkBtn("Add", "add");
17612
- const removeBtn = mkBtn("Remove", "remove");
17613
- const inputWrap = document.createElement("span");
17614
- inputWrap.style.cssText = "display:inline-flex; align-items:center; gap:4px; margin-left:4px;";
17615
- const label = document.createElement("span");
17616
- label.textContent = "Radius";
17617
- label.style.cssText = "color:#888; font-size:11px;";
17618
- const input = document.createElement("input");
17619
- input.type = "number";
17620
- input.min = "0.01";
17621
- input.step = "0.01";
17622
- input.value = this._radius.toFixed(2);
17623
- input.style.cssText = `
17624
- width:80px; padding:4px 6px; border:1px solid #555; border-radius:4px;
17625
- background:#222; color:#ddd; font-size:12px; text-align:center;
17626
- `;
17627
- input.addEventListener("change", () => {
17628
- const v = Math.max(0.01, parseFloat(input.value) || 0.01);
17629
- input.value = v.toFixed(2);
17630
- this._radius = v;
17631
- this.callbacks.onRadiusChanged(v);
17632
- });
17633
- inputWrap.appendChild(label);
17634
- inputWrap.appendChild(input);
17635
- this.radiusInput = input;
17636
- this.toolbar.appendChild(setBtn);
17637
- this.toolbar.appendChild(addBtn);
17638
- this.toolbar.appendChild(removeBtn);
17639
- this.toolbar.appendChild(inputWrap);
17640
- parent.appendChild(this.toolbar);
17571
+ __publicField(this, "_onRadiusChanged");
17572
+ this._onRadiusChanged = onRadiusChanged;
17641
17573
  }
17642
17574
  get radius() {
17643
17575
  return this._radius;
17644
17576
  }
17645
17577
  setRadius(radius) {
17578
+ var _a2;
17646
17579
  this._radius = radius;
17647
- this.radiusInput.value = radius.toFixed(2);
17580
+ (_a2 = this._onRadiusChanged) == null ? void 0 : _a2.call(this, radius);
17648
17581
  }
17649
17582
  activate() {
17650
- this.toolbar.style.display = "flex";
17651
17583
  }
17652
17584
  deactivate() {
17653
- this.toolbar.style.display = "none";
17654
17585
  }
17655
17586
  }
17656
17587
  class BoxSelection {
17657
- constructor(parent, callbacks) {
17658
- __publicField(this, "parent");
17659
- __publicField(this, "toolbar");
17660
- __publicField(this, "callbacks");
17588
+ constructor(onDimensionsChanged) {
17661
17589
  __publicField(this, "_lenX", 2);
17662
17590
  __publicField(this, "_lenY", 2);
17663
17591
  __publicField(this, "_lenZ", 2);
17664
- __publicField(this, "inputX");
17665
- __publicField(this, "inputY");
17666
- __publicField(this, "inputZ");
17667
- this.parent = parent;
17668
- this.callbacks = callbacks;
17669
- this.toolbar = document.createElement("div");
17670
- this.toolbar.className = "select-toolbar";
17671
- this.toolbar.style.cssText = `
17672
- position:absolute; bottom:100px; left:50%; transform:translateX(-50%);
17673
- height:54px; display:none; z-index:20;
17674
- padding:0 8px; border-radius:8px;
17675
- background:rgba(30,30,30,0.92);
17676
- align-items:center; font-size:13px; color:#ddd;
17677
- backdrop-filter:blur(6px); user-select:none; white-space:nowrap;
17678
- box-shadow:0 2px 12px rgba(0,0,0,0.4);
17679
- `;
17680
- this.toolbar.addEventListener("pointerdown", (e) => e.stopPropagation());
17681
- this.toolbar.addEventListener("wheel", (e) => e.stopPropagation());
17682
- const mkBtn = (label, op) => {
17683
- const btn = document.createElement("button");
17684
- btn.textContent = label;
17685
- btn.style.cssText = `
17686
- height:38px; padding:0 16px; border:none; border-radius:2px;
17687
- background:#444; color:#ddd; cursor:pointer; font-size:13px;
17688
- transition: background 0.15s;
17689
- `;
17690
- btn.addEventListener("mouseenter", () => {
17691
- btn.style.background = "#555";
17692
- });
17693
- btn.addEventListener("mouseleave", () => {
17694
- btn.style.background = "#444";
17695
- });
17696
- btn.addEventListener("pointerdown", (e) => {
17697
- e.stopPropagation();
17698
- this.callbacks.onApply(op);
17699
- });
17700
- return btn;
17701
- };
17702
- const mkInput = (placeholder, initial, onChange) => {
17703
- const wrap = document.createElement("span");
17704
- wrap.style.cssText = "display:inline-flex; align-items:center; gap:4px; margin-left:4px;";
17705
- const label = document.createElement("span");
17706
- label.textContent = placeholder;
17707
- label.style.cssText = "color:#888; font-size:11px;";
17708
- const input = document.createElement("input");
17709
- input.type = "number";
17710
- input.min = "0.01";
17711
- input.step = "0.01";
17712
- input.value = initial.toFixed(2);
17713
- input.style.cssText = `
17714
- width:80px; padding:4px 6px; border:1px solid #555; border-radius:4px;
17715
- background:#222; color:#ddd; font-size:12px; text-align:center;
17716
- `;
17717
- input.addEventListener("change", () => {
17718
- const v = Math.max(0.01, parseFloat(input.value) || 0.01);
17719
- input.value = v.toFixed(2);
17720
- onChange(v);
17721
- });
17722
- wrap.appendChild(label);
17723
- wrap.appendChild(input);
17724
- return { wrap, input };
17725
- };
17726
- const setBtn = mkBtn("Set", "set");
17727
- const addBtn = mkBtn("Add", "add");
17728
- const removeBtn = mkBtn("Remove", "remove");
17729
- const xInput = mkInput("LenX", this._lenX, (v) => {
17730
- this._lenX = v;
17731
- this.emitDims();
17732
- });
17733
- const yInput = mkInput("LenY", this._lenY, (v) => {
17734
- this._lenY = v;
17735
- this.emitDims();
17736
- });
17737
- const zInput = mkInput("LenZ", this._lenZ, (v) => {
17738
- this._lenZ = v;
17739
- this.emitDims();
17740
- });
17741
- this.inputX = xInput.input;
17742
- this.inputY = yInput.input;
17743
- this.inputZ = zInput.input;
17744
- this.toolbar.appendChild(setBtn);
17745
- this.toolbar.appendChild(addBtn);
17746
- this.toolbar.appendChild(removeBtn);
17747
- this.toolbar.appendChild(xInput.wrap);
17748
- this.toolbar.appendChild(yInput.wrap);
17749
- this.toolbar.appendChild(zInput.wrap);
17750
- parent.appendChild(this.toolbar);
17592
+ __publicField(this, "_onDimensionsChanged");
17593
+ this._onDimensionsChanged = onDimensionsChanged;
17751
17594
  }
17752
17595
  get lenX() {
17753
17596
  return this._lenX;
@@ -17759,21 +17602,15 @@ class BoxSelection {
17759
17602
  return this._lenZ;
17760
17603
  }
17761
17604
  setDimensions(lenX, lenY, lenZ) {
17605
+ var _a2;
17762
17606
  this._lenX = lenX;
17763
17607
  this._lenY = lenY;
17764
17608
  this._lenZ = lenZ;
17765
- this.inputX.value = lenX.toFixed(2);
17766
- this.inputY.value = lenY.toFixed(2);
17767
- this.inputZ.value = lenZ.toFixed(2);
17609
+ (_a2 = this._onDimensionsChanged) == null ? void 0 : _a2.call(this, lenX, lenY, lenZ);
17768
17610
  }
17769
17611
  activate() {
17770
- this.toolbar.style.display = "flex";
17771
17612
  }
17772
17613
  deactivate() {
17773
- this.toolbar.style.display = "none";
17774
- }
17775
- emitDims() {
17776
- this.callbacks.onDimensionsChanged(this._lenX, this._lenY, this._lenZ);
17777
17614
  }
17778
17615
  }
17779
17616
  function exportEditedPLY(positions, scales, rotations, colors, opacities, shCoeffs, state) {
@@ -17877,311 +17714,29 @@ function exportEditedPLY(positions, scales, rotations, colors, opacities, shCoef
17877
17714
  }
17878
17715
  return buffer;
17879
17716
  }
17880
- const BOX_VERTS = new Float32Array([
17881
- -0.5,
17882
- -0.5,
17883
- 0.5,
17884
- 0.5,
17885
- -0.5,
17886
- 0.5,
17887
- 0.5,
17888
- 0.5,
17889
- 0.5,
17890
- -0.5,
17891
- -0.5,
17892
- 0.5,
17893
- 0.5,
17894
- 0.5,
17895
- 0.5,
17896
- -0.5,
17897
- 0.5,
17898
- 0.5,
17899
- 0.5,
17900
- -0.5,
17901
- -0.5,
17902
- -0.5,
17903
- -0.5,
17904
- -0.5,
17905
- -0.5,
17906
- 0.5,
17907
- -0.5,
17908
- 0.5,
17909
- -0.5,
17910
- -0.5,
17911
- -0.5,
17912
- 0.5,
17913
- -0.5,
17914
- 0.5,
17915
- 0.5,
17916
- -0.5,
17917
- -0.5,
17918
- 0.5,
17919
- 0.5,
17920
- 0.5,
17921
- 0.5,
17922
- 0.5,
17923
- 0.5,
17924
- 0.5,
17925
- -0.5,
17926
- -0.5,
17927
- 0.5,
17928
- 0.5,
17929
- 0.5,
17930
- 0.5,
17931
- -0.5,
17932
- -0.5,
17933
- 0.5,
17934
- -0.5,
17935
- -0.5,
17936
- -0.5,
17937
- -0.5,
17938
- 0.5,
17939
- -0.5,
17940
- -0.5,
17941
- 0.5,
17942
- -0.5,
17943
- 0.5,
17944
- -0.5,
17945
- -0.5,
17946
- -0.5,
17947
- 0.5,
17948
- -0.5,
17949
- 0.5,
17950
- -0.5,
17951
- -0.5,
17952
- 0.5,
17953
- 0.5,
17954
- -0.5,
17955
- 0.5,
17956
- 0.5,
17957
- -0.5,
17958
- -0.5,
17959
- 0.5,
17960
- 0.5,
17961
- -0.5,
17962
- 0.5,
17963
- -0.5,
17964
- 0.5,
17965
- 0.5,
17966
- 0.5,
17967
- -0.5,
17968
- 0.5,
17969
- 0.5,
17970
- 0.5,
17971
- -0.5,
17972
- -0.5,
17973
- -0.5,
17974
- -0.5,
17975
- -0.5,
17976
- 0.5,
17977
- -0.5,
17978
- 0.5,
17979
- 0.5,
17980
- -0.5,
17981
- -0.5,
17982
- -0.5,
17983
- -0.5,
17984
- 0.5,
17985
- 0.5,
17986
- -0.5,
17987
- 0.5,
17988
- -0.5
17989
- ]);
17990
- const COMMON_WGSL = (
17991
- /* wgsl */
17992
- `
17993
- struct Uniforms {
17994
- viewProjMatrix: mat4x4<f32>,
17995
- modelMatrix: mat4x4<f32>,
17996
- nearOrigin: vec4<f32>,
17997
- nearX: vec4<f32>,
17998
- nearY: vec4<f32>,
17999
- farOrigin: vec4<f32>,
18000
- farX: vec4<f32>,
18001
- farY: vec4<f32>,
18002
- targetSize: vec4<f32>,
18003
- shapeP0: vec4<f32>,
18004
- shapeP1: vec4<f32>,
18005
- }
18006
- @group(0) @binding(0) var<uniform> u: Uniforms;
18007
-
18008
- struct VO { @builtin(position) position: vec4<f32> }
18009
- struct FO {
18010
- @location(0) color: vec4<f32>,
18011
- @builtin(frag_depth) depth: f32,
18012
- }
18013
-
18014
- @vertex fn vs(@location(0) pos: vec3<f32>) -> VO {
18015
- var o: VO;
18016
- o.position = u.viewProjMatrix * u.modelMatrix * vec4(pos, 1.0);
18017
- return o;
18018
- }
18019
-
18020
- fn interleavedGradientNoise(p: vec2<f32>) -> f32 {
18021
- return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715))));
18022
- }
18023
-
18024
- fn writeDepth(alpha: f32, fc: vec2<f32>) -> bool {
18025
- return alpha > interleavedGradientNoise(fc);
18026
- }
18027
-
18028
- fn calcDepth(wp: vec3<f32>) -> f32 {
18029
- let v = u.viewProjMatrix * vec4(wp, 1.0);
18030
- return clamp(v.z / v.w, 0.0, 1.0);
18031
- }
18032
-
18033
- fn reconstructRay(fragPos: vec4<f32>) -> array<vec3<f32>, 2> {
18034
- let clip = vec2(fragPos.x / u.targetSize.x, 1.0 - fragPos.y / u.targetSize.y);
18035
- let wNear = u.nearOrigin.xyz + u.nearX.xyz * clip.x + u.nearY.xyz * clip.y;
18036
- let wFar = u.farOrigin.xyz + u.farX.xyz * clip.x + u.farY.xyz * clip.y;
18037
- return array(wNear, normalize(wFar - wNear));
18038
- }
18039
- `
18040
- );
18041
- const BOX_FRAG_WGSL = (
18042
- /* wgsl */
18043
- `
18044
- fn intersectBox(pos: vec3<f32>, dir: vec3<f32>, cen: vec3<f32>, halfLen: vec3<f32>) -> vec4<f32> {
18045
- let vx = select(0.0, 1.0, dir.x != 0.0);
18046
- let vy = select(0.0, 1.0, dir.y != 0.0);
18047
- let vz = select(0.0, 1.0, dir.z != 0.0);
18048
- let valid = vec3<f32>(vx, vy, vz);
18049
- let m = vec3<f32>(
18050
- select(0.0, sign(dir.x) / abs(dir.x), dir.x != 0.0),
18051
- select(0.0, sign(dir.y) / abs(dir.y), dir.y != 0.0),
18052
- select(0.0, sign(dir.z) / abs(dir.z), dir.z != 0.0),
18053
- );
18054
- let n = m * (pos - cen);
18055
- let k = abs(m) * halfLen;
18056
- var v0 = -n - k;
18057
- var v1 = -n + k;
18058
- v0 = mix(vec3(-1e10), v0, valid);
18059
- v1 = mix(vec3( 1e10), v1, valid);
18060
-
18061
- var axis0: i32; var axis1: i32;
18062
- if v0.x > v0.y { axis0 = select(2, 0, v0.x > v0.z); }
18063
- else { axis0 = select(2, 1, v0.y > v0.z); }
18064
- if v1.x < v1.y { axis1 = select(2, 0, v1.x < v1.z); }
18065
- else { axis1 = select(2, 1, v1.y < v1.z); }
18066
-
18067
- let t0 = select(select(v0.z, v0.y, axis0==1), v0.x, axis0==0);
18068
- let t1 = select(select(v1.z, v1.y, axis1==1), v1.x, axis1==0);
18069
- if t0 > t1 || t1 < 0.0 { return vec4(-1.0); }
18070
- return vec4(t0, t1, f32(axis0), f32(axis1));
18071
- }
18072
-
18073
- fn strips(pos: vec3<f32>, axis: i32) -> bool {
18074
- let f = fract(pos * 2.0 + vec3(0.015));
18075
- let b = f < vec3(0.03);
18076
- if axis == 0 { return b.y || b.z; }
18077
- if axis == 1 { return b.x || b.z; }
18078
- return b.x || b.y;
18079
- }
18080
-
18081
- @fragment fn fs(i: VO) -> FO {
18082
- var o: FO;
18083
- o.color = vec4(0.0);
18084
- o.depth = 1.0;
18085
-
18086
- let ray = reconstructRay(i.position);
18087
- let hit = intersectBox(ray[0], ray[1], u.shapeP0.xyz, u.shapeP1.xyz);
18088
- if hit.x < 0.0 { discard; return o; }
18089
-
18090
- let t0 = hit.x; let t1 = hit.y;
18091
- let a0 = i32(hit.z); let a1 = i32(hit.w);
18092
- let frontPos = ray[0] + ray[1] * t0;
18093
- let backPos = ray[0] + ray[1] * t1;
18094
- let front = t0 > 0.0 && strips(frontPos - u.shapeP0.xyz, a0);
18095
- let back = strips(backPos - u.shapeP0.xyz, a1);
18096
-
18097
- if front {
18098
- o.color = vec4(1.0, 1.0, 1.0, 0.6);
18099
- o.depth = select(1.0, calcDepth(frontPos), writeDepth(0.6, i.position.xy));
18100
- return o;
18101
- }
18102
- if back {
18103
- o.color = vec4(0.0, 0.0, 0.0, 0.6);
18104
- o.depth = select(1.0, calcDepth(backPos), writeDepth(0.6, i.position.xy));
18105
- return o;
18106
- }
18107
- discard;
18108
- return o;
18109
- }
18110
- `
18111
- );
18112
- const SPHERE_FRAG_WGSL = (
18113
- /* wgsl */
18114
- `
18115
- fn intersectSphere(pos: vec3<f32>, dir: vec3<f32>, sph: vec4<f32>) -> vec2<f32> {
18116
- let L = sph.xyz - pos;
18117
- let tca = dot(L, dir);
18118
- let d2 = sph.w * sph.w - (dot(L, L) - tca * tca);
18119
- if d2 <= 0.0 { return vec2(-1.0); }
18120
- let thc = sqrt(d2);
18121
- let t0 = tca - thc;
18122
- let t1 = tca + thc;
18123
- if t1 <= 0.0 { return vec2(-1.0); }
18124
- return vec2(t0, t1);
18125
- }
18126
-
18127
- fn calcAzimuthElev(dir: vec3<f32>) -> vec2<f32> {
18128
- let azimuth = atan2(dir.z, dir.x);
18129
- let elev = asin(clamp(dir.y, -1.0, 1.0));
18130
- return vec2(azimuth, elev) * 180.0 / 3.14159265;
18131
- }
18132
-
18133
- fn sphereStrips(lp: vec3<f32>, radius: f32) -> bool {
18134
- let ae = calcAzimuthElev(normalize(lp));
18135
- let spacing = 180.0 / (2.0 * 3.14159265 * radius);
18136
- let sz = 0.03;
18137
- return fract(ae.x / spacing) < sz || fract(ae.y / spacing) < sz;
18138
- }
18139
-
18140
- @fragment fn fs(i: VO) -> FO {
18141
- var o: FO;
18142
- o.color = vec4(0.0);
18143
- o.depth = 1.0;
18144
-
18145
- let sph = u.shapeP0;
18146
- let ray = reconstructRay(i.position);
18147
- let hit = intersectSphere(ray[0], ray[1], sph);
18148
- if hit.x < 0.0 && hit.y < 0.0 { discard; return o; }
18149
-
18150
- let t0 = hit.x; let t1 = hit.y;
18151
- let frontPos = ray[0] + ray[1] * t0;
18152
- let backPos = ray[0] + ray[1] * t1;
18153
- let front = t0 > 0.0 && sphereStrips(frontPos - sph.xyz, sph.w);
18154
- let back = sphereStrips(backPos - sph.xyz, sph.w);
18155
-
18156
- if front {
18157
- o.color = vec4(1.0, 1.0, 1.0, 0.6);
18158
- o.depth = select(1.0, calcDepth(frontPos), writeDepth(0.6, i.position.xy));
18159
- return o;
18160
- }
18161
- if back {
18162
- o.color = vec4(0.0, 0.0, 0.0, 0.6);
18163
- o.depth = select(1.0, calcDepth(backPos), writeDepth(0.6, i.position.xy));
18164
- return o;
18165
- }
18166
- discard;
18167
- return o;
18168
- }
18169
- `
18170
- );
18171
- const UNIFORM_SIZE = 272;
17717
+ const SEG = 64;
17718
+ const LAT_COUNT = 8;
17719
+ const LON_COUNT = 12;
17720
+ const BOX_GRID_SUBDIV = 4;
18172
17721
  class SelectionVolumeRenderer {
18173
17722
  constructor(renderer, camera) {
18174
17723
  __publicField(this, "renderer");
18175
17724
  __publicField(this, "camera");
18176
- __publicField(this, "boxPipeline", null);
18177
- __publicField(this, "spherePipeline", null);
17725
+ __publicField(this, "frontPipeline", null);
17726
+ __publicField(this, "behindPipeline", null);
18178
17727
  __publicField(this, "uniformBuffer", null);
18179
17728
  __publicField(this, "bindGroup", null);
18180
17729
  __publicField(this, "vertexBuffer", null);
18181
17730
  __publicField(this, "_volume", { type: null, center: [0, 0, 0], dimensions: [2, 2, 2] });
17731
+ __publicField(this, "vertexCount", 0);
17732
+ __publicField(this, "frontColor", [0.4, 0.9, 1]);
18182
17733
  this.renderer = renderer;
18183
17734
  this.camera = camera;
18184
- this.createResources();
17735
+ this.createPipelines();
17736
+ this.vertexBuffer = this.renderer.device.createBuffer({
17737
+ size: 65536,
17738
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
17739
+ });
18185
17740
  }
18186
17741
  get volume() {
18187
17742
  return this._volume;
@@ -18197,187 +17752,225 @@ class SelectionVolumeRenderer {
18197
17752
  setDimensions(a, b, c) {
18198
17753
  this._volume.dimensions = [a, b, c];
18199
17754
  }
18200
- createResources() {
17755
+ createPipelines() {
18201
17756
  const device = this.renderer.device;
18202
- this.vertexBuffer = device.createBuffer({
18203
- size: BOX_VERTS.byteLength,
18204
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
18205
- });
18206
- device.queue.writeBuffer(this.vertexBuffer, 0, BOX_VERTS);
18207
- this.uniformBuffer = device.createBuffer({
18208
- size: UNIFORM_SIZE,
18209
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
18210
- });
17757
+ const shaderCode = (
17758
+ /* wgsl */
17759
+ `
17760
+ struct Uniforms {
17761
+ viewMatrix: mat4x4<f32>,
17762
+ projMatrix: mat4x4<f32>,
17763
+ modelMatrix: mat4x4<f32>,
17764
+ }
17765
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
17766
+
17767
+ struct VI { @location(0) position: vec3<f32>, @location(1) color: vec3<f32> }
17768
+ struct VO { @builtin(position) position: vec4<f32>, @location(0) color: vec3<f32> }
17769
+
17770
+ @vertex fn vs(i: VI) -> VO {
17771
+ var o: VO;
17772
+ let worldPos = uniforms.modelMatrix * vec4(i.position, 1.0);
17773
+ let viewPos = uniforms.viewMatrix * worldPos;
17774
+ o.position = uniforms.projMatrix * viewPos;
17775
+ o.color = i.color;
17776
+ return o;
17777
+ }
17778
+ @fragment fn fs(i: VO) -> @location(0) vec4<f32> {
17779
+ return vec4(i.color, 0.7);
17780
+ }
17781
+ @fragment fn fsBehind(i: VO) -> @location(0) vec4<f32> {
17782
+ return vec4(i.color * 0.35, 0.2);
17783
+ }
17784
+ `
17785
+ );
17786
+ const sm = device.createShaderModule({ code: shaderCode });
17787
+ this.uniformBuffer = device.createBuffer({ size: 192, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
18211
17788
  const bgl = device.createBindGroupLayout({
18212
- entries: [{
18213
- binding: 0,
18214
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
18215
- buffer: { type: "uniform" }
18216
- }]
18217
- });
18218
- this.bindGroup = device.createBindGroup({
18219
- layout: bgl,
18220
- entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }]
17789
+ entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }]
18221
17790
  });
17791
+ this.bindGroup = device.createBindGroup({ layout: bgl, entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }] });
18222
17792
  const layout = device.createPipelineLayout({ bindGroupLayouts: [bgl] });
18223
- const mkVertexState = (mod) => ({
18224
- module: mod,
17793
+ const vertexState = {
17794
+ module: sm,
18225
17795
  entryPoint: "vs",
18226
17796
  buffers: [{
18227
- arrayStride: 12,
18228
- attributes: [{ shaderLocation: 0, offset: 0, format: "float32x3" }]
17797
+ arrayStride: 24,
17798
+ attributes: [
17799
+ { shaderLocation: 0, offset: 0, format: "float32x3" },
17800
+ { shaderLocation: 1, offset: 12, format: "float32x3" }
17801
+ ]
18229
17802
  }]
18230
- });
18231
- const blend = {
17803
+ };
17804
+ const blendState = {
18232
17805
  color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha", operation: "add" },
18233
17806
  alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha", operation: "add" }
18234
17807
  };
18235
- const depthStencil = {
18236
- format: this.renderer.depthFormat,
18237
- depthWriteEnabled: true,
18238
- depthCompare: "less-equal"
18239
- };
18240
- const boxMod = device.createShaderModule({ code: COMMON_WGSL + BOX_FRAG_WGSL });
18241
- this.boxPipeline = device.createRenderPipeline({
17808
+ this.frontPipeline = device.createRenderPipeline({
18242
17809
  layout,
18243
- vertex: mkVertexState(boxMod),
17810
+ vertex: vertexState,
18244
17811
  fragment: {
18245
- module: boxMod,
17812
+ module: sm,
18246
17813
  entryPoint: "fs",
18247
- targets: [{ format: this.renderer.format, blend }]
17814
+ targets: [{ format: this.renderer.format, blend: blendState }]
18248
17815
  },
18249
- primitive: { topology: "triangle-list", cullMode: "front" },
18250
- depthStencil
17816
+ primitive: { topology: "line-list", cullMode: "none" },
17817
+ depthStencil: { format: this.renderer.depthFormat, depthWriteEnabled: false, depthCompare: "less-equal" }
18251
17818
  });
18252
- const sphereMod = device.createShaderModule({ code: COMMON_WGSL + SPHERE_FRAG_WGSL });
18253
- this.spherePipeline = device.createRenderPipeline({
17819
+ this.behindPipeline = device.createRenderPipeline({
18254
17820
  layout,
18255
- vertex: mkVertexState(sphereMod),
17821
+ vertex: vertexState,
18256
17822
  fragment: {
18257
- module: sphereMod,
18258
- entryPoint: "fs",
18259
- targets: [{ format: this.renderer.format, blend }]
17823
+ module: sm,
17824
+ entryPoint: "fsBehind",
17825
+ targets: [{ format: this.renderer.format, blend: blendState }]
18260
17826
  },
18261
- primitive: { topology: "triangle-list", cullMode: "front" },
18262
- depthStencil
17827
+ primitive: { topology: "line-list", cullMode: "none" },
17828
+ depthStencil: { format: this.renderer.depthFormat, depthWriteEnabled: false, depthCompare: "greater" }
18263
17829
  });
18264
17830
  }
18265
- // ---------- Frustum corners ----------
18266
- computeFrustumUniforms(buf) {
18267
- const cam = this.camera;
18268
- const vm = cam.viewMatrix;
18269
- const right = [vm[0], vm[4], vm[8]];
18270
- const up = [vm[1], vm[5], vm[9]];
18271
- const zAxis = [vm[2], vm[6], vm[10]];
18272
- const eye = cam.position;
18273
- const d = 100;
18274
- const halfH = d * Math.tan(cam.fov / 2);
18275
- const halfW = halfH * cam.aspect;
18276
- const fcx = eye[0] - d * zAxis[0];
18277
- const fcy = eye[1] - d * zAxis[1];
18278
- const fcz = eye[2] - d * zAxis[2];
18279
- buf[0] = eye[0];
18280
- buf[1] = eye[1];
18281
- buf[2] = eye[2];
18282
- buf[3] = 0;
18283
- buf[4] = 0;
18284
- buf[5] = 0;
18285
- buf[6] = 0;
18286
- buf[7] = 0;
18287
- buf[8] = 0;
18288
- buf[9] = 0;
18289
- buf[10] = 0;
18290
- buf[11] = 0;
18291
- buf[12] = fcx - halfW * right[0] - halfH * up[0];
18292
- buf[13] = fcy - halfW * right[1] - halfH * up[1];
18293
- buf[14] = fcz - halfW * right[2] - halfH * up[2];
18294
- buf[15] = 0;
18295
- buf[16] = 2 * halfW * right[0];
18296
- buf[17] = 2 * halfW * right[1];
18297
- buf[18] = 2 * halfW * right[2];
18298
- buf[19] = 0;
18299
- buf[20] = 2 * halfH * up[0];
18300
- buf[21] = 2 * halfH * up[1];
18301
- buf[22] = 2 * halfH * up[2];
18302
- buf[23] = 0;
17831
+ // ========== Box: 12 edges + grid lines on all 6 faces ==========
17832
+ generateBoxVertices(color) {
17833
+ const [lx, ly, lz] = this._volume.dimensions;
17834
+ const hx = lx / 2, hy = ly / 2, hz = lz / 2;
17835
+ const [r, g, b] = color;
17836
+ const v = [];
17837
+ const line = (x12, y12, z12, x2, y2, z2) => {
17838
+ v.push(x12, y12, z12, r, g, b, x2, y2, z2, r, g, b);
17839
+ };
17840
+ const x0 = -hx, x1 = hx;
17841
+ const y0 = -hy, y1 = hy;
17842
+ const z0 = -hz, z1 = hz;
17843
+ line(x0, y0, z0, x1, y0, z0);
17844
+ line(x1, y0, z0, x1, y0, z1);
17845
+ line(x1, y0, z1, x0, y0, z1);
17846
+ line(x0, y0, z1, x0, y0, z0);
17847
+ line(x0, y1, z0, x1, y1, z0);
17848
+ line(x1, y1, z0, x1, y1, z1);
17849
+ line(x1, y1, z1, x0, y1, z1);
17850
+ line(x0, y1, z1, x0, y1, z0);
17851
+ line(x0, y0, z0, x0, y1, z0);
17852
+ line(x1, y0, z0, x1, y1, z0);
17853
+ line(x1, y0, z1, x1, y1, z1);
17854
+ line(x0, y0, z1, x0, y1, z1);
17855
+ const n = BOX_GRID_SUBDIV;
17856
+ for (let i = 1; i < n; i++) {
17857
+ const t = i / n;
17858
+ const px = x0 + (x1 - x0) * t;
17859
+ const pz = z0 + (z1 - z0) * t;
17860
+ line(px, y0, z0, px, y0, z1);
17861
+ line(px, y1, z0, px, y1, z1);
17862
+ line(x0, y0, pz, x1, y0, pz);
17863
+ line(x0, y1, pz, x1, y1, pz);
17864
+ }
17865
+ for (let i = 1; i < n; i++) {
17866
+ const t = i / n;
17867
+ const px = x0 + (x1 - x0) * t;
17868
+ const py = y0 + (y1 - y0) * t;
17869
+ line(px, y0, z0, px, y1, z0);
17870
+ line(px, y0, z1, px, y1, z1);
17871
+ line(x0, py, z0, x1, py, z0);
17872
+ line(x0, py, z1, x1, py, z1);
17873
+ }
17874
+ for (let i = 1; i < n; i++) {
17875
+ const t = i / n;
17876
+ const py = y0 + (y1 - y0) * t;
17877
+ const pz = z0 + (z1 - z0) * t;
17878
+ line(x0, py, z0, x0, py, z1);
17879
+ line(x1, py, z0, x1, py, z1);
17880
+ line(x0, y0, pz, x0, y1, pz);
17881
+ line(x1, y0, pz, x1, y1, pz);
17882
+ }
17883
+ return new Float32Array(v);
17884
+ }
17885
+ // ========== Sphere: latitude + longitude rings ==========
17886
+ generateSphereVertices(color) {
17887
+ const radius = this._volume.dimensions[0];
17888
+ const [r, g, b] = color;
17889
+ const v = [];
17890
+ const addRing = (centerY, ringRadius) => {
17891
+ for (let i = 0; i < SEG; i++) {
17892
+ const a0 = i / SEG * Math.PI * 2;
17893
+ const a1 = (i + 1) / SEG * Math.PI * 2;
17894
+ v.push(
17895
+ Math.cos(a0) * ringRadius,
17896
+ centerY,
17897
+ Math.sin(a0) * ringRadius,
17898
+ r,
17899
+ g,
17900
+ b,
17901
+ Math.cos(a1) * ringRadius,
17902
+ centerY,
17903
+ Math.sin(a1) * ringRadius,
17904
+ r,
17905
+ g,
17906
+ b
17907
+ );
17908
+ }
17909
+ };
17910
+ for (let i = 0; i <= LAT_COUNT; i++) {
17911
+ const phi = -Math.PI / 2 + i / LAT_COUNT * Math.PI;
17912
+ const y = Math.sin(phi) * radius;
17913
+ const ringR = Math.cos(phi) * radius;
17914
+ if (ringR < 1e-3) continue;
17915
+ addRing(y, ringR);
17916
+ }
17917
+ for (let j = 0; j < LON_COUNT; j++) {
17918
+ const theta = j / LON_COUNT * Math.PI;
17919
+ for (let i = 0; i < SEG; i++) {
17920
+ const phi0 = i / SEG * Math.PI * 2;
17921
+ const phi1 = (i + 1) / SEG * Math.PI * 2;
17922
+ v.push(
17923
+ Math.sin(phi0) * Math.sin(theta) * radius,
17924
+ Math.cos(phi0) * radius,
17925
+ Math.sin(phi0) * Math.cos(theta) * radius,
17926
+ r,
17927
+ g,
17928
+ b,
17929
+ Math.sin(phi1) * Math.sin(theta) * radius,
17930
+ Math.cos(phi1) * radius,
17931
+ Math.sin(phi1) * Math.cos(theta) * radius,
17932
+ r,
17933
+ g,
17934
+ b
17935
+ );
17936
+ }
17937
+ }
17938
+ return new Float32Array(v);
18303
17939
  }
18304
17940
  render(pass) {
18305
- const vol = this._volume;
18306
- if (!vol.type || !this.uniformBuffer || !this.vertexBuffer || !this.bindGroup) return;
18307
- const pipeline = vol.type === "box" ? this.boxPipeline : this.spherePipeline;
18308
- if (!pipeline) return;
17941
+ if (!this._volume.type || !this.frontPipeline || !this.behindPipeline || !this.bindGroup || !this.vertexBuffer || !this.uniformBuffer) return;
18309
17942
  const device = this.renderer.device;
18310
- const uniforms = new Float32Array(UNIFORM_SIZE / 4);
18311
- uniforms.set(this.camera.viewProjectionMatrix, 0);
18312
- const [cx, cy, cz] = vol.center;
18313
- if (vol.type === "box") {
18314
- const [lx, ly, lz] = vol.dimensions;
18315
- uniforms[16] = lx;
18316
- uniforms[17] = 0;
18317
- uniforms[18] = 0;
18318
- uniforms[19] = 0;
18319
- uniforms[20] = 0;
18320
- uniforms[21] = ly;
18321
- uniforms[22] = 0;
18322
- uniforms[23] = 0;
18323
- uniforms[24] = 0;
18324
- uniforms[25] = 0;
18325
- uniforms[26] = lz;
18326
- uniforms[27] = 0;
18327
- uniforms[28] = cx;
18328
- uniforms[29] = cy;
18329
- uniforms[30] = cz;
18330
- uniforms[31] = 1;
18331
- } else {
18332
- const r2 = vol.dimensions[0] * 2;
18333
- uniforms[16] = r2;
18334
- uniforms[17] = 0;
18335
- uniforms[18] = 0;
18336
- uniforms[19] = 0;
18337
- uniforms[20] = 0;
18338
- uniforms[21] = r2;
18339
- uniforms[22] = 0;
18340
- uniforms[23] = 0;
18341
- uniforms[24] = 0;
18342
- uniforms[25] = 0;
18343
- uniforms[26] = r2;
18344
- uniforms[27] = 0;
18345
- uniforms[28] = cx;
18346
- uniforms[29] = cy;
18347
- uniforms[30] = cz;
18348
- uniforms[31] = 1;
18349
- }
18350
- const frustumBuf = new Float32Array(24);
18351
- this.computeFrustumUniforms(frustumBuf);
18352
- uniforms.set(frustumBuf, 32);
18353
- uniforms[56] = this.renderer.width;
18354
- uniforms[57] = this.renderer.height;
18355
- uniforms[58] = 0;
18356
- uniforms[59] = 0;
18357
- if (vol.type === "box") {
18358
- uniforms[60] = cx;
18359
- uniforms[61] = cy;
18360
- uniforms[62] = cz;
18361
- uniforms[63] = 0;
18362
- uniforms[64] = vol.dimensions[0] * 0.5;
18363
- uniforms[65] = vol.dimensions[1] * 0.5;
18364
- uniforms[66] = vol.dimensions[2] * 0.5;
18365
- uniforms[67] = 0;
18366
- } else {
18367
- uniforms[60] = cx;
18368
- uniforms[61] = cy;
18369
- uniforms[62] = cz;
18370
- uniforms[63] = vol.dimensions[0];
18371
- uniforms[64] = 0;
18372
- uniforms[65] = 0;
18373
- uniforms[66] = 0;
18374
- uniforms[67] = 0;
18375
- }
18376
- device.queue.writeBuffer(this.uniformBuffer, 0, uniforms.buffer);
18377
- pass.setPipeline(pipeline);
17943
+ const [cx, cy, cz] = this._volume.center;
17944
+ const modelMatrix = new Float32Array(16);
17945
+ modelMatrix[0] = 1;
17946
+ modelMatrix[5] = 1;
17947
+ modelMatrix[10] = 1;
17948
+ modelMatrix[15] = 1;
17949
+ modelMatrix[12] = cx;
17950
+ modelMatrix[13] = cy;
17951
+ modelMatrix[14] = cz;
17952
+ const uniforms = new Float32Array(48);
17953
+ uniforms.set(this.camera.viewMatrix, 0);
17954
+ uniforms.set(this.camera.projectionMatrix, 16);
17955
+ uniforms.set(modelMatrix, 32);
17956
+ device.queue.writeBuffer(this.uniformBuffer, 0, uniforms);
17957
+ const frontVerts = this._volume.type === "box" ? this.generateBoxVertices(this.frontColor) : this.generateSphereVertices(this.frontColor);
17958
+ this.vertexCount = frontVerts.length / 6;
17959
+ if (this.vertexCount === 0) return;
17960
+ const needed = this.vertexCount * 24;
17961
+ if (needed > this.vertexBuffer.size) {
17962
+ this.vertexBuffer.destroy();
17963
+ this.vertexBuffer = device.createBuffer({ size: needed, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST });
17964
+ }
17965
+ device.queue.writeBuffer(this.vertexBuffer, 0, frontVerts.buffer);
17966
+ pass.setPipeline(this.frontPipeline);
18378
17967
  pass.setBindGroup(0, this.bindGroup);
18379
17968
  pass.setVertexBuffer(0, this.vertexBuffer);
18380
- pass.draw(36);
17969
+ pass.draw(this.vertexCount);
17970
+ pass.setPipeline(this.behindPipeline);
17971
+ pass.setBindGroup(0, this.bindGroup);
17972
+ pass.setVertexBuffer(0, this.vertexBuffer);
17973
+ pass.draw(this.vertexCount);
18381
17974
  }
18382
17975
  destroy() {
18383
17976
  var _a2, _b2;
@@ -18385,8 +17978,8 @@ class SelectionVolumeRenderer {
18385
17978
  (_b2 = this.uniformBuffer) == null ? void 0 : _b2.destroy();
18386
17979
  this.vertexBuffer = null;
18387
17980
  this.uniformBuffer = null;
18388
- this.boxPipeline = null;
18389
- this.spherePipeline = null;
17981
+ this.frontPipeline = null;
17982
+ this.behindPipeline = null;
18390
17983
  this.bindGroup = null;
18391
17984
  }
18392
17985
  }
@@ -18524,20 +18117,14 @@ class SplatEditor {
18524
18117
  if (this.gpuRenderer) {
18525
18118
  this.volumeRenderer = new SelectionVolumeRenderer(this.gpuRenderer, this.camera);
18526
18119
  }
18527
- this.sphereTool = new SphereSelection(this.container, {
18528
- onApply: (op) => this.applyVolumeSelection(op),
18529
- onRadiusChanged: (radius) => {
18530
- var _a2;
18531
- (_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(radius, 0, 0);
18532
- }
18120
+ this.sphereTool = new SphereSelection((radius) => {
18121
+ var _a2;
18122
+ (_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(radius, 0, 0);
18533
18123
  });
18534
18124
  this.toolManager.register("sphere", this.sphereTool);
18535
- this.boxTool = new BoxSelection(this.container, {
18536
- onApply: (op) => this.applyVolumeSelection(op),
18537
- onDimensionsChanged: (lx, ly, lz) => {
18538
- var _a2;
18539
- (_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(lx, ly, lz);
18540
- }
18125
+ this.boxTool = new BoxSelection((lx, ly, lz) => {
18126
+ var _a2;
18127
+ (_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(lx, ly, lz);
18541
18128
  });
18542
18129
  this.toolManager.register("box", this.boxTool);
18543
18130
  this.gsRenderer.setEditorState(this.splatState.data);
@@ -18895,6 +18482,24 @@ class SplatEditor {
18895
18482
  }
18896
18483
  };
18897
18484
  }
18485
+ // ============================================
18486
+ // 体积工具参数 API(供前端 UI 调用)
18487
+ // ============================================
18488
+ setBoxDimensions(lx, ly, lz) {
18489
+ var _a2;
18490
+ (_a2 = this.boxTool) == null ? void 0 : _a2.setDimensions(lx, ly, lz);
18491
+ }
18492
+ getBoxDimensions() {
18493
+ return this.boxTool ? [this.boxTool.lenX, this.boxTool.lenY, this.boxTool.lenZ] : [2, 2, 2];
18494
+ }
18495
+ setSphereRadius(radius) {
18496
+ var _a2;
18497
+ (_a2 = this.sphereTool) == null ? void 0 : _a2.setRadius(radius);
18498
+ }
18499
+ getSphereRadius() {
18500
+ var _a2;
18501
+ return ((_a2 = this.sphereTool) == null ? void 0 : _a2.radius) ?? 1;
18502
+ }
18898
18503
  /**
18899
18504
  * 获取模型世界空间包围盒(中心 + 尺寸)
18900
18505
  */