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