@d5techs/3dgs-lib 1.4.74 → 1.4.76
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 +532 -222
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +532 -222
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +7 -0
- package/dist/editor/SelectionVolumeRenderer.d.ts +32 -0
- package/dist/editor/SplatEditor.d.ts +42 -6
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/tools/BoxSelection.d.ts +18 -18
- package/dist/editor/tools/SphereSelection.d.ts +11 -15
- package/dist/index.d.ts +2 -0
- package/package.json +1 -1
package/dist/3dgs-lib.js
CHANGED
|
@@ -809,6 +809,7 @@ const _OrbitControls = class _OrbitControls {
|
|
|
809
809
|
onKeyDown(e) {
|
|
810
810
|
var _a2;
|
|
811
811
|
if (!this.enabled) return;
|
|
812
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
812
813
|
const tag = (_a2 = e.target) == null ? void 0 : _a2.tagName;
|
|
813
814
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
814
815
|
const key = e.key.toLowerCase();
|
|
@@ -17565,213 +17566,212 @@ class EyedropperSelection {
|
|
|
17565
17566
|
}
|
|
17566
17567
|
}
|
|
17567
17568
|
class SphereSelection {
|
|
17568
|
-
constructor(parent,
|
|
17569
|
+
constructor(parent, callbacks) {
|
|
17569
17570
|
__publicField(this, "parent");
|
|
17570
|
-
__publicField(this, "
|
|
17571
|
-
__publicField(this, "
|
|
17572
|
-
__publicField(this, "
|
|
17573
|
-
__publicField(this, "
|
|
17574
|
-
__publicField(this, "radiusPx", 0);
|
|
17575
|
-
__publicField(this, "dragId");
|
|
17571
|
+
__publicField(this, "toolbar");
|
|
17572
|
+
__publicField(this, "callbacks");
|
|
17573
|
+
__publicField(this, "_radius", 1);
|
|
17574
|
+
__publicField(this, "radiusInput");
|
|
17576
17575
|
this.parent = parent;
|
|
17577
|
-
this.
|
|
17578
|
-
this.
|
|
17579
|
-
this.
|
|
17580
|
-
this.
|
|
17581
|
-
|
|
17582
|
-
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
|
|
17588
|
-
this.
|
|
17589
|
-
this.
|
|
17590
|
-
|
|
17591
|
-
|
|
17592
|
-
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17576
|
+
this.callbacks = callbacks;
|
|
17577
|
+
this.toolbar = document.createElement("div");
|
|
17578
|
+
this.toolbar.className = "volume-select-toolbar";
|
|
17579
|
+
this.toolbar.style.cssText = `
|
|
17580
|
+
position:absolute; bottom:12px; left:50%; transform:translateX(-50%);
|
|
17581
|
+
display:none; z-index:20; background:rgba(30,30,30,0.92);
|
|
17582
|
+
border-radius:8px; padding:6px 10px; gap:6px;
|
|
17583
|
+
align-items:center; font-size:13px; color:#ddd;
|
|
17584
|
+
backdrop-filter:blur(6px); user-select:none; white-space:nowrap;
|
|
17585
|
+
box-shadow:0 2px 12px rgba(0,0,0,0.4);
|
|
17586
|
+
`;
|
|
17587
|
+
this.toolbar.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
17588
|
+
this.toolbar.addEventListener("wheel", (e) => e.stopPropagation());
|
|
17589
|
+
const mkBtn = (label2, op) => {
|
|
17590
|
+
const btn = document.createElement("button");
|
|
17591
|
+
btn.textContent = label2;
|
|
17592
|
+
btn.style.cssText = `
|
|
17593
|
+
padding:4px 12px; border:1px solid #555; border-radius:4px;
|
|
17594
|
+
background:#333; color:#ddd; cursor:pointer; font-size:13px;
|
|
17595
|
+
transition: background 0.15s;
|
|
17596
|
+
`;
|
|
17597
|
+
btn.addEventListener("mouseenter", () => {
|
|
17598
|
+
btn.style.background = "#555";
|
|
17599
|
+
});
|
|
17600
|
+
btn.addEventListener("mouseleave", () => {
|
|
17601
|
+
btn.style.background = "#333";
|
|
17602
|
+
});
|
|
17603
|
+
btn.addEventListener("pointerdown", (e) => {
|
|
17604
|
+
e.stopPropagation();
|
|
17605
|
+
this.callbacks.onApply(op);
|
|
17606
|
+
});
|
|
17607
|
+
return btn;
|
|
17608
|
+
};
|
|
17609
|
+
const setBtn = mkBtn("Set", "set");
|
|
17610
|
+
const addBtn = mkBtn("Add", "add");
|
|
17611
|
+
const removeBtn = mkBtn("Remove", "remove");
|
|
17612
|
+
const inputWrap = document.createElement("span");
|
|
17613
|
+
inputWrap.style.cssText = "display:inline-flex; align-items:center; gap:2px;";
|
|
17614
|
+
const input = document.createElement("input");
|
|
17615
|
+
input.type = "number";
|
|
17616
|
+
input.min = "0.01";
|
|
17617
|
+
input.step = "0.1";
|
|
17618
|
+
input.value = this._radius.toFixed(2);
|
|
17619
|
+
input.style.cssText = `
|
|
17620
|
+
width:50px; padding:3px 4px; border:1px solid #555; border-radius:4px;
|
|
17621
|
+
background:#222; color:#ddd; font-size:12px; text-align:center;
|
|
17622
|
+
`;
|
|
17623
|
+
const label = document.createElement("span");
|
|
17624
|
+
label.textContent = "Radius";
|
|
17625
|
+
label.style.cssText = "color:#888; font-size:11px;";
|
|
17626
|
+
input.addEventListener("change", () => {
|
|
17627
|
+
const v = Math.max(0.01, parseFloat(input.value) || 0.01);
|
|
17628
|
+
input.value = v.toFixed(2);
|
|
17629
|
+
this._radius = v;
|
|
17630
|
+
this.callbacks.onRadiusChanged(v);
|
|
17631
|
+
});
|
|
17632
|
+
inputWrap.appendChild(input);
|
|
17633
|
+
inputWrap.appendChild(label);
|
|
17634
|
+
this.radiusInput = input;
|
|
17635
|
+
this.toolbar.appendChild(setBtn);
|
|
17636
|
+
this.toolbar.appendChild(addBtn);
|
|
17637
|
+
this.toolbar.appendChild(removeBtn);
|
|
17638
|
+
this.toolbar.appendChild(inputWrap);
|
|
17639
|
+
parent.appendChild(this.toolbar);
|
|
17615
17640
|
}
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
if (e.pointerType === "mouse" ? e.button !== 0 : !e.isPrimary) return;
|
|
17619
|
-
e.preventDefault();
|
|
17620
|
-
e.stopPropagation();
|
|
17621
|
-
this.dragId = e.pointerId;
|
|
17622
|
-
this.parent.setPointerCapture(this.dragId);
|
|
17623
|
-
this.center.x = e.offsetX;
|
|
17624
|
-
this.center.y = e.offsetY;
|
|
17625
|
-
this.radiusPx = 0;
|
|
17626
|
-
this.updateCircle();
|
|
17627
|
-
this.circle.style.display = "block";
|
|
17641
|
+
get radius() {
|
|
17642
|
+
return this._radius;
|
|
17628
17643
|
}
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
e.stopPropagation();
|
|
17633
|
-
const dx = e.offsetX - this.center.x;
|
|
17634
|
-
const dy = e.offsetY - this.center.y;
|
|
17635
|
-
this.radiusPx = Math.sqrt(dx * dx + dy * dy);
|
|
17636
|
-
this.updateCircle();
|
|
17644
|
+
setRadius(radius) {
|
|
17645
|
+
this._radius = radius;
|
|
17646
|
+
this.radiusInput.value = radius.toFixed(2);
|
|
17637
17647
|
}
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
this.parent.releasePointerCapture(this.dragId);
|
|
17641
|
-
this.dragId = void 0;
|
|
17642
|
-
}
|
|
17643
|
-
this.circle.style.display = "none";
|
|
17648
|
+
activate() {
|
|
17649
|
+
this.toolbar.style.display = "flex";
|
|
17644
17650
|
}
|
|
17645
|
-
|
|
17646
|
-
|
|
17647
|
-
e.preventDefault();
|
|
17648
|
-
e.stopPropagation();
|
|
17649
|
-
const selectOp = e.shiftKey ? "add" : e.ctrlKey ? "remove" : "set";
|
|
17650
|
-
if (this.radiusPx < 3) this.radiusPx = 20;
|
|
17651
|
-
this.onSelect(selectOp, { x: this.center.x, y: this.center.y }, this.radiusPx);
|
|
17652
|
-
this.dragEnd();
|
|
17651
|
+
deactivate() {
|
|
17652
|
+
this.toolbar.style.display = "none";
|
|
17653
17653
|
}
|
|
17654
17654
|
}
|
|
17655
17655
|
class BoxSelection {
|
|
17656
|
-
constructor(parent,
|
|
17656
|
+
constructor(parent, callbacks) {
|
|
17657
17657
|
__publicField(this, "parent");
|
|
17658
|
-
__publicField(this, "
|
|
17659
|
-
__publicField(this, "
|
|
17660
|
-
__publicField(this, "
|
|
17661
|
-
__publicField(this, "
|
|
17662
|
-
__publicField(this, "
|
|
17663
|
-
__publicField(this, "
|
|
17664
|
-
__publicField(this, "
|
|
17665
|
-
__publicField(this, "
|
|
17666
|
-
__publicField(this, "dragId");
|
|
17658
|
+
__publicField(this, "toolbar");
|
|
17659
|
+
__publicField(this, "callbacks");
|
|
17660
|
+
__publicField(this, "_lenX", 2);
|
|
17661
|
+
__publicField(this, "_lenY", 2);
|
|
17662
|
+
__publicField(this, "_lenZ", 2);
|
|
17663
|
+
__publicField(this, "inputX");
|
|
17664
|
+
__publicField(this, "inputY");
|
|
17665
|
+
__publicField(this, "inputZ");
|
|
17667
17666
|
this.parent = parent;
|
|
17668
|
-
this.
|
|
17669
|
-
this.
|
|
17670
|
-
this.
|
|
17671
|
-
this.
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
this.
|
|
17680
|
-
this.
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17667
|
+
this.callbacks = callbacks;
|
|
17668
|
+
this.toolbar = document.createElement("div");
|
|
17669
|
+
this.toolbar.className = "volume-select-toolbar";
|
|
17670
|
+
this.toolbar.style.cssText = `
|
|
17671
|
+
position:absolute; bottom:12px; left:50%; transform:translateX(-50%);
|
|
17672
|
+
display:none; z-index:20; background:rgba(30,30,30,0.92);
|
|
17673
|
+
border-radius:8px; padding:6px 10px; gap:6px;
|
|
17674
|
+
align-items:center; font-size:13px; color:#ddd;
|
|
17675
|
+
backdrop-filter:blur(6px); user-select:none; white-space:nowrap;
|
|
17676
|
+
box-shadow:0 2px 12px rgba(0,0,0,0.4);
|
|
17677
|
+
`;
|
|
17678
|
+
this.toolbar.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
17679
|
+
this.toolbar.addEventListener("wheel", (e) => e.stopPropagation());
|
|
17680
|
+
const mkBtn = (label, op) => {
|
|
17681
|
+
const btn = document.createElement("button");
|
|
17682
|
+
btn.textContent = label;
|
|
17683
|
+
btn.style.cssText = `
|
|
17684
|
+
padding:4px 12px; border:1px solid #555; border-radius:4px;
|
|
17685
|
+
background:#333; color:#ddd; cursor:pointer; font-size:13px;
|
|
17686
|
+
transition: background 0.15s;
|
|
17687
|
+
`;
|
|
17688
|
+
btn.addEventListener("mouseenter", () => {
|
|
17689
|
+
btn.style.background = "#555";
|
|
17690
|
+
});
|
|
17691
|
+
btn.addEventListener("mouseleave", () => {
|
|
17692
|
+
btn.style.background = "#333";
|
|
17693
|
+
});
|
|
17694
|
+
btn.addEventListener("pointerdown", (e) => {
|
|
17695
|
+
e.stopPropagation();
|
|
17696
|
+
this.callbacks.onApply(op);
|
|
17697
|
+
});
|
|
17698
|
+
return btn;
|
|
17691
17699
|
};
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
|
|
17695
|
-
|
|
17696
|
-
|
|
17700
|
+
const mkInput = (placeholder, initial, onChange) => {
|
|
17701
|
+
const wrap = document.createElement("span");
|
|
17702
|
+
wrap.style.cssText = "display:inline-flex; align-items:center; gap:2px;";
|
|
17703
|
+
const input = document.createElement("input");
|
|
17704
|
+
input.type = "number";
|
|
17705
|
+
input.min = "0.01";
|
|
17706
|
+
input.step = "0.1";
|
|
17707
|
+
input.value = initial.toFixed(2);
|
|
17708
|
+
input.style.cssText = `
|
|
17709
|
+
width:50px; padding:3px 4px; border:1px solid #555; border-radius:4px;
|
|
17710
|
+
background:#222; color:#ddd; font-size:12px; text-align:center;
|
|
17711
|
+
`;
|
|
17712
|
+
const label = document.createElement("span");
|
|
17713
|
+
label.textContent = placeholder;
|
|
17714
|
+
label.style.cssText = "color:#888; font-size:11px;";
|
|
17715
|
+
input.addEventListener("change", () => {
|
|
17716
|
+
const v = Math.max(0.01, parseFloat(input.value) || 0.01);
|
|
17717
|
+
input.value = v.toFixed(2);
|
|
17718
|
+
onChange(v);
|
|
17719
|
+
});
|
|
17720
|
+
wrap.appendChild(input);
|
|
17721
|
+
wrap.appendChild(label);
|
|
17722
|
+
return { wrap, input };
|
|
17723
|
+
};
|
|
17724
|
+
const setBtn = mkBtn("Set", "set");
|
|
17725
|
+
const addBtn = mkBtn("Add", "add");
|
|
17726
|
+
const removeBtn = mkBtn("Remove", "remove");
|
|
17727
|
+
const xInput = mkInput("LenX", this._lenX, (v) => {
|
|
17728
|
+
this._lenX = v;
|
|
17729
|
+
this.emitDims();
|
|
17730
|
+
});
|
|
17731
|
+
const yInput = mkInput("LenY", this._lenY, (v) => {
|
|
17732
|
+
this._lenY = v;
|
|
17733
|
+
this.emitDims();
|
|
17734
|
+
});
|
|
17735
|
+
const zInput = mkInput("LenZ", this._lenZ, (v) => {
|
|
17736
|
+
this._lenZ = v;
|
|
17737
|
+
this.emitDims();
|
|
17738
|
+
});
|
|
17739
|
+
this.inputX = xInput.input;
|
|
17740
|
+
this.inputY = yInput.input;
|
|
17741
|
+
this.inputZ = zInput.input;
|
|
17742
|
+
this.toolbar.appendChild(setBtn);
|
|
17743
|
+
this.toolbar.appendChild(addBtn);
|
|
17744
|
+
this.toolbar.appendChild(removeBtn);
|
|
17745
|
+
this.toolbar.appendChild(xInput.wrap);
|
|
17746
|
+
this.toolbar.appendChild(yInput.wrap);
|
|
17747
|
+
this.toolbar.appendChild(zInput.wrap);
|
|
17748
|
+
parent.appendChild(this.toolbar);
|
|
17749
|
+
}
|
|
17750
|
+
get lenX() {
|
|
17751
|
+
return this._lenX;
|
|
17752
|
+
}
|
|
17753
|
+
get lenY() {
|
|
17754
|
+
return this._lenY;
|
|
17755
|
+
}
|
|
17756
|
+
get lenZ() {
|
|
17757
|
+
return this._lenZ;
|
|
17758
|
+
}
|
|
17759
|
+
setDimensions(lenX, lenY, lenZ) {
|
|
17760
|
+
this._lenX = lenX;
|
|
17761
|
+
this._lenY = lenY;
|
|
17762
|
+
this._lenZ = lenZ;
|
|
17763
|
+
this.inputX.value = lenX.toFixed(2);
|
|
17764
|
+
this.inputY.value = lenY.toFixed(2);
|
|
17765
|
+
this.inputZ.value = lenZ.toFixed(2);
|
|
17697
17766
|
}
|
|
17698
17767
|
activate() {
|
|
17699
|
-
this.
|
|
17700
|
-
this.parent.style.cursor = "crosshair";
|
|
17701
|
-
this.parent.addEventListener("pointerdown", this.pointerdown);
|
|
17702
|
-
this.parent.addEventListener("pointermove", this.pointermove);
|
|
17703
|
-
this.parent.addEventListener("pointerup", this.pointerup);
|
|
17768
|
+
this.toolbar.style.display = "flex";
|
|
17704
17769
|
}
|
|
17705
17770
|
deactivate() {
|
|
17706
|
-
|
|
17707
|
-
this.svg.style.display = "none";
|
|
17708
|
-
this.parent.style.cursor = "";
|
|
17709
|
-
this.parent.removeEventListener("pointerdown", this.pointerdown);
|
|
17710
|
-
this.parent.removeEventListener("pointermove", this.pointermove);
|
|
17711
|
-
this.parent.removeEventListener("pointerup", this.pointerup);
|
|
17712
|
-
}
|
|
17713
|
-
updateVisuals() {
|
|
17714
|
-
const x = this.center.x - this.halfW;
|
|
17715
|
-
const y = this.center.y - this.halfH;
|
|
17716
|
-
const w = this.halfW * 2;
|
|
17717
|
-
const h = this.halfH * 2;
|
|
17718
|
-
this.rect.setAttribute("x", x.toString());
|
|
17719
|
-
this.rect.setAttribute("y", y.toString());
|
|
17720
|
-
this.rect.setAttribute("width", Math.max(1, w).toString());
|
|
17721
|
-
this.rect.setAttribute("height", Math.max(1, h).toString());
|
|
17722
|
-
this.crossV.setAttribute("x1", this.center.x.toString());
|
|
17723
|
-
this.crossV.setAttribute("y1", y.toString());
|
|
17724
|
-
this.crossV.setAttribute("x2", this.center.x.toString());
|
|
17725
|
-
this.crossV.setAttribute("y2", (y + h).toString());
|
|
17726
|
-
this.crossH.setAttribute("x1", x.toString());
|
|
17727
|
-
this.crossH.setAttribute("y1", this.center.y.toString());
|
|
17728
|
-
this.crossH.setAttribute("x2", (x + w).toString());
|
|
17729
|
-
this.crossH.setAttribute("y2", this.center.y.toString());
|
|
17730
|
-
}
|
|
17731
|
-
pointerdown(e) {
|
|
17732
|
-
if (this.dragId !== void 0) return;
|
|
17733
|
-
if (e.pointerType === "mouse" ? e.button !== 0 : !e.isPrimary) return;
|
|
17734
|
-
e.preventDefault();
|
|
17735
|
-
e.stopPropagation();
|
|
17736
|
-
this.dragId = e.pointerId;
|
|
17737
|
-
this.parent.setPointerCapture(this.dragId);
|
|
17738
|
-
this.center.x = e.offsetX;
|
|
17739
|
-
this.center.y = e.offsetY;
|
|
17740
|
-
this.halfW = 0;
|
|
17741
|
-
this.halfH = 0;
|
|
17742
|
-
this.updateVisuals();
|
|
17743
|
-
this.rect.style.display = "block";
|
|
17744
|
-
this.crossV.style.display = "block";
|
|
17745
|
-
this.crossH.style.display = "block";
|
|
17771
|
+
this.toolbar.style.display = "none";
|
|
17746
17772
|
}
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
e.preventDefault();
|
|
17750
|
-
e.stopPropagation();
|
|
17751
|
-
this.halfW = Math.abs(e.offsetX - this.center.x);
|
|
17752
|
-
this.halfH = Math.abs(e.offsetY - this.center.y);
|
|
17753
|
-
this.updateVisuals();
|
|
17754
|
-
}
|
|
17755
|
-
dragEnd() {
|
|
17756
|
-
if (this.dragId !== void 0) {
|
|
17757
|
-
this.parent.releasePointerCapture(this.dragId);
|
|
17758
|
-
this.dragId = void 0;
|
|
17759
|
-
}
|
|
17760
|
-
this.rect.style.display = "none";
|
|
17761
|
-
this.crossV.style.display = "none";
|
|
17762
|
-
this.crossH.style.display = "none";
|
|
17763
|
-
}
|
|
17764
|
-
pointerup(e) {
|
|
17765
|
-
if (e.pointerId !== this.dragId) return;
|
|
17766
|
-
e.preventDefault();
|
|
17767
|
-
e.stopPropagation();
|
|
17768
|
-
const selectOp = e.shiftKey ? "add" : e.ctrlKey ? "remove" : "set";
|
|
17769
|
-
if (this.halfW < 3 && this.halfH < 3) {
|
|
17770
|
-
this.halfW = 20;
|
|
17771
|
-
this.halfH = 20;
|
|
17772
|
-
}
|
|
17773
|
-
this.onSelect(selectOp, { x: this.center.x, y: this.center.y }, this.halfW, this.halfH);
|
|
17774
|
-
this.dragEnd();
|
|
17773
|
+
emitDims() {
|
|
17774
|
+
this.callbacks.onDimensionsChanged(this._lenX, this._lenY, this._lenZ);
|
|
17775
17775
|
}
|
|
17776
17776
|
}
|
|
17777
17777
|
function exportEditedPLY(positions, scales, rotations, colors, opacities, shCoeffs, state) {
|
|
@@ -17875,9 +17875,205 @@ function exportEditedPLY(positions, scales, rotations, colors, opacities, shCoef
|
|
|
17875
17875
|
}
|
|
17876
17876
|
return buffer;
|
|
17877
17877
|
}
|
|
17878
|
+
const SPHERE_SEGMENTS = 64;
|
|
17879
|
+
const SPHERE_RINGS = 3;
|
|
17880
|
+
class SelectionVolumeRenderer {
|
|
17881
|
+
constructor(renderer, camera) {
|
|
17882
|
+
__publicField(this, "renderer");
|
|
17883
|
+
__publicField(this, "camera");
|
|
17884
|
+
__publicField(this, "pipeline", null);
|
|
17885
|
+
__publicField(this, "uniformBuffer", null);
|
|
17886
|
+
__publicField(this, "bindGroup", null);
|
|
17887
|
+
__publicField(this, "vertexBuffer", null);
|
|
17888
|
+
__publicField(this, "_volume", { type: null, center: [0, 0, 0], dimensions: [2, 2, 2] });
|
|
17889
|
+
__publicField(this, "vertexCount", 0);
|
|
17890
|
+
__publicField(this, "maxVertices");
|
|
17891
|
+
__publicField(this, "lineColor", [0.2, 0.8, 1]);
|
|
17892
|
+
this.renderer = renderer;
|
|
17893
|
+
this.camera = camera;
|
|
17894
|
+
this.maxVertices = Math.max(
|
|
17895
|
+
24,
|
|
17896
|
+
SPHERE_SEGMENTS * 2 * SPHERE_RINGS
|
|
17897
|
+
);
|
|
17898
|
+
this.createPipeline();
|
|
17899
|
+
this.createVertexBuffer();
|
|
17900
|
+
}
|
|
17901
|
+
get volume() {
|
|
17902
|
+
return this._volume;
|
|
17903
|
+
}
|
|
17904
|
+
setVolume(type, center, dimensions) {
|
|
17905
|
+
this._volume.type = type;
|
|
17906
|
+
if (center) this._volume.center = center;
|
|
17907
|
+
if (dimensions) this._volume.dimensions = dimensions;
|
|
17908
|
+
}
|
|
17909
|
+
setCenter(x, y, z) {
|
|
17910
|
+
this._volume.center = [x, y, z];
|
|
17911
|
+
}
|
|
17912
|
+
setDimensions(a, b, c) {
|
|
17913
|
+
this._volume.dimensions = [a, b, c];
|
|
17914
|
+
}
|
|
17915
|
+
createPipeline() {
|
|
17916
|
+
const device = this.renderer.device;
|
|
17917
|
+
const shaderCode = (
|
|
17918
|
+
/* wgsl */
|
|
17919
|
+
`
|
|
17920
|
+
struct Uniforms { viewProjection: mat4x4<f32> }
|
|
17921
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
17922
|
+
|
|
17923
|
+
struct VI { @location(0) position: vec3<f32>, @location(1) color: vec3<f32> }
|
|
17924
|
+
struct VO { @builtin(position) position: vec4<f32>, @location(0) color: vec3<f32> }
|
|
17925
|
+
|
|
17926
|
+
@vertex fn vs(i: VI) -> VO {
|
|
17927
|
+
var o: VO;
|
|
17928
|
+
o.position = uniforms.viewProjection * vec4(i.position, 1.0);
|
|
17929
|
+
o.color = i.color;
|
|
17930
|
+
return o;
|
|
17931
|
+
}
|
|
17932
|
+
@fragment fn fs(i: VO) -> @location(0) vec4<f32> {
|
|
17933
|
+
return vec4(i.color, 0.8);
|
|
17934
|
+
}
|
|
17935
|
+
`
|
|
17936
|
+
);
|
|
17937
|
+
const sm = device.createShaderModule({ code: shaderCode });
|
|
17938
|
+
this.uniformBuffer = device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
17939
|
+
const bgl = device.createBindGroupLayout({
|
|
17940
|
+
entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } }]
|
|
17941
|
+
});
|
|
17942
|
+
this.bindGroup = device.createBindGroup({ layout: bgl, entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }] });
|
|
17943
|
+
this.pipeline = device.createRenderPipeline({
|
|
17944
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [bgl] }),
|
|
17945
|
+
vertex: {
|
|
17946
|
+
module: sm,
|
|
17947
|
+
entryPoint: "vs",
|
|
17948
|
+
buffers: [{
|
|
17949
|
+
arrayStride: 24,
|
|
17950
|
+
attributes: [
|
|
17951
|
+
{ shaderLocation: 0, offset: 0, format: "float32x3" },
|
|
17952
|
+
{ shaderLocation: 1, offset: 12, format: "float32x3" }
|
|
17953
|
+
]
|
|
17954
|
+
}]
|
|
17955
|
+
},
|
|
17956
|
+
fragment: {
|
|
17957
|
+
module: sm,
|
|
17958
|
+
entryPoint: "fs",
|
|
17959
|
+
targets: [{
|
|
17960
|
+
format: this.renderer.format,
|
|
17961
|
+
blend: {
|
|
17962
|
+
color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha", operation: "add" },
|
|
17963
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha", operation: "add" }
|
|
17964
|
+
}
|
|
17965
|
+
}]
|
|
17966
|
+
},
|
|
17967
|
+
primitive: { topology: "line-list", cullMode: "none" },
|
|
17968
|
+
depthStencil: { format: this.renderer.depthFormat, depthWriteEnabled: false, depthCompare: "always" }
|
|
17969
|
+
});
|
|
17970
|
+
}
|
|
17971
|
+
createVertexBuffer() {
|
|
17972
|
+
const size = this.maxVertices * 24;
|
|
17973
|
+
this.vertexBuffer = this.renderer.device.createBuffer({
|
|
17974
|
+
size,
|
|
17975
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
17976
|
+
});
|
|
17977
|
+
}
|
|
17978
|
+
generateBoxVertices() {
|
|
17979
|
+
const [cx, cy, cz] = this._volume.center;
|
|
17980
|
+
const [lx, ly, lz] = this._volume.dimensions;
|
|
17981
|
+
const hx = lx / 2, hy = ly / 2, hz = lz / 2;
|
|
17982
|
+
const [r, g, b] = this.lineColor;
|
|
17983
|
+
const v = [];
|
|
17984
|
+
const addLine = (x12, y12, z12, x2, y2, z2) => {
|
|
17985
|
+
v.push(x12, y12, z12, r, g, b, x2, y2, z2, r, g, b);
|
|
17986
|
+
};
|
|
17987
|
+
const x0 = cx - hx, x1 = cx + hx;
|
|
17988
|
+
const y0 = cy - hy, y1 = cy + hy;
|
|
17989
|
+
const z0 = cz - hz, z1 = cz + hz;
|
|
17990
|
+
addLine(x0, y0, z0, x1, y0, z0);
|
|
17991
|
+
addLine(x1, y0, z0, x1, y0, z1);
|
|
17992
|
+
addLine(x1, y0, z1, x0, y0, z1);
|
|
17993
|
+
addLine(x0, y0, z1, x0, y0, z0);
|
|
17994
|
+
addLine(x0, y1, z0, x1, y1, z0);
|
|
17995
|
+
addLine(x1, y1, z0, x1, y1, z1);
|
|
17996
|
+
addLine(x1, y1, z1, x0, y1, z1);
|
|
17997
|
+
addLine(x0, y1, z1, x0, y1, z0);
|
|
17998
|
+
addLine(x0, y0, z0, x0, y1, z0);
|
|
17999
|
+
addLine(x1, y0, z0, x1, y1, z0);
|
|
18000
|
+
addLine(x1, y0, z1, x1, y1, z1);
|
|
18001
|
+
addLine(x0, y0, z1, x0, y1, z1);
|
|
18002
|
+
return new Float32Array(v);
|
|
18003
|
+
}
|
|
18004
|
+
generateSphereVertices() {
|
|
18005
|
+
const [cx, cy, cz] = this._volume.center;
|
|
18006
|
+
const radius = this._volume.dimensions[0];
|
|
18007
|
+
const [r, g, b] = this.lineColor;
|
|
18008
|
+
const v = [];
|
|
18009
|
+
const seg = SPHERE_SEGMENTS;
|
|
18010
|
+
const addCircle = (axis) => {
|
|
18011
|
+
for (let i = 0; i < seg; i++) {
|
|
18012
|
+
const a0 = i / seg * Math.PI * 2;
|
|
18013
|
+
const a1 = (i + 1) / seg * Math.PI * 2;
|
|
18014
|
+
let x0, y0, z0, x1, y1, z1;
|
|
18015
|
+
if (axis === "y") {
|
|
18016
|
+
x0 = cx + Math.cos(a0) * radius;
|
|
18017
|
+
y0 = cy;
|
|
18018
|
+
z0 = cz + Math.sin(a0) * radius;
|
|
18019
|
+
x1 = cx + Math.cos(a1) * radius;
|
|
18020
|
+
y1 = cy;
|
|
18021
|
+
z1 = cz + Math.sin(a1) * radius;
|
|
18022
|
+
} else if (axis === "x") {
|
|
18023
|
+
x0 = cx;
|
|
18024
|
+
y0 = cy + Math.cos(a0) * radius;
|
|
18025
|
+
z0 = cz + Math.sin(a0) * radius;
|
|
18026
|
+
x1 = cx;
|
|
18027
|
+
y1 = cy + Math.cos(a1) * radius;
|
|
18028
|
+
z1 = cz + Math.sin(a1) * radius;
|
|
18029
|
+
} else {
|
|
18030
|
+
x0 = cx + Math.cos(a0) * radius;
|
|
18031
|
+
y0 = cy + Math.sin(a0) * radius;
|
|
18032
|
+
z0 = cz;
|
|
18033
|
+
x1 = cx + Math.cos(a1) * radius;
|
|
18034
|
+
y1 = cy + Math.sin(a1) * radius;
|
|
18035
|
+
z1 = cz;
|
|
18036
|
+
}
|
|
18037
|
+
v.push(x0, y0, z0, r, g, b, x1, y1, z1, r, g, b);
|
|
18038
|
+
}
|
|
18039
|
+
};
|
|
18040
|
+
addCircle("x");
|
|
18041
|
+
addCircle("y");
|
|
18042
|
+
addCircle("z");
|
|
18043
|
+
return new Float32Array(v);
|
|
18044
|
+
}
|
|
18045
|
+
render(pass) {
|
|
18046
|
+
if (!this._volume.type || !this.pipeline || !this.bindGroup || !this.vertexBuffer || !this.uniformBuffer) return;
|
|
18047
|
+
const device = this.renderer.device;
|
|
18048
|
+
const verts = this._volume.type === "box" ? this.generateBoxVertices() : this.generateSphereVertices();
|
|
18049
|
+
this.vertexCount = verts.length / 6;
|
|
18050
|
+
if (this.vertexCount === 0) return;
|
|
18051
|
+
const needed = this.vertexCount * 24;
|
|
18052
|
+
if (needed > this.vertexBuffer.size) {
|
|
18053
|
+
this.vertexBuffer.destroy();
|
|
18054
|
+
this.vertexBuffer = device.createBuffer({ size: needed, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST });
|
|
18055
|
+
}
|
|
18056
|
+
device.queue.writeBuffer(this.vertexBuffer, 0, verts.buffer);
|
|
18057
|
+
device.queue.writeBuffer(this.uniformBuffer, 0, new Float32Array(this.camera.viewProjectionMatrix));
|
|
18058
|
+
pass.setPipeline(this.pipeline);
|
|
18059
|
+
pass.setBindGroup(0, this.bindGroup);
|
|
18060
|
+
pass.setVertexBuffer(0, this.vertexBuffer);
|
|
18061
|
+
pass.draw(this.vertexCount);
|
|
18062
|
+
}
|
|
18063
|
+
destroy() {
|
|
18064
|
+
var _a2, _b2;
|
|
18065
|
+
(_a2 = this.vertexBuffer) == null ? void 0 : _a2.destroy();
|
|
18066
|
+
(_b2 = this.uniformBuffer) == null ? void 0 : _b2.destroy();
|
|
18067
|
+
this.vertexBuffer = null;
|
|
18068
|
+
this.uniformBuffer = null;
|
|
18069
|
+
this.pipeline = null;
|
|
18070
|
+
this.bindGroup = null;
|
|
18071
|
+
}
|
|
18072
|
+
}
|
|
17878
18073
|
class SplatEditor {
|
|
17879
|
-
constructor(camera, gsRenderer, container, callbacks = {}) {
|
|
18074
|
+
constructor(camera, gsRenderer, container, callbacks = {}, renderer) {
|
|
17880
18075
|
__publicField(this, "camera");
|
|
18076
|
+
__publicField(this, "gpuRenderer");
|
|
17881
18077
|
__publicField(this, "gsRenderer");
|
|
17882
18078
|
__publicField(this, "container");
|
|
17883
18079
|
__publicField(this, "splatState");
|
|
@@ -17892,6 +18088,10 @@ class SplatEditor {
|
|
|
17892
18088
|
__publicField(this, "projDirty", true);
|
|
17893
18089
|
__publicField(this, "_active", false);
|
|
17894
18090
|
__publicField(this, "overlayCleanup", []);
|
|
18091
|
+
/** 体积选择线框渲染器 */
|
|
18092
|
+
__publicField(this, "volumeRenderer", null);
|
|
18093
|
+
__publicField(this, "boxTool", null);
|
|
18094
|
+
__publicField(this, "sphereTool", null);
|
|
17895
18095
|
// ============================================
|
|
17896
18096
|
// 键盘快捷键
|
|
17897
18097
|
// ============================================
|
|
@@ -17900,6 +18100,7 @@ class SplatEditor {
|
|
|
17900
18100
|
this.gsRenderer = gsRenderer;
|
|
17901
18101
|
this.container = container;
|
|
17902
18102
|
this.callbacks = callbacks;
|
|
18103
|
+
this.gpuRenderer = renderer;
|
|
17903
18104
|
}
|
|
17904
18105
|
get active() {
|
|
17905
18106
|
return this._active;
|
|
@@ -17946,9 +18147,20 @@ class SplatEditor {
|
|
|
17946
18147
|
this.container.style.position = "relative";
|
|
17947
18148
|
}
|
|
17948
18149
|
this.toolManager = new ToolManager((name) => {
|
|
17949
|
-
var _a2, _b2;
|
|
17950
|
-
|
|
17951
|
-
|
|
18150
|
+
var _a2, _b2, _c, _d, _e, _f;
|
|
18151
|
+
const isVolumeTool = name === "box" || name === "sphere";
|
|
18152
|
+
this.toolOverlay.style.display = name && !isVolumeTool ? "block" : "none";
|
|
18153
|
+
if (isVolumeTool && this.volumeRenderer) {
|
|
18154
|
+
const volType = name;
|
|
18155
|
+
const center = this.getModelCenter();
|
|
18156
|
+
const dims = volType === "box" ? [this.boxTool.lenX, this.boxTool.lenY, this.boxTool.lenZ] : [this.sphereTool.radius, 0, 0];
|
|
18157
|
+
this.volumeRenderer.setVolume(volType, center, dims);
|
|
18158
|
+
(_b2 = (_a2 = this.callbacks).onVolumeToolActivated) == null ? void 0 : _b2.call(_a2, this.volumeRenderer.volume);
|
|
18159
|
+
} else {
|
|
18160
|
+
if (this.volumeRenderer) this.volumeRenderer.setVolume(null);
|
|
18161
|
+
(_d = (_c = this.callbacks).onVolumeToolDeactivated) == null ? void 0 : _d.call(_c);
|
|
18162
|
+
}
|
|
18163
|
+
(_f = (_e = this.callbacks).onToolChanged) == null ? void 0 : _f.call(_e, name);
|
|
17952
18164
|
});
|
|
17953
18165
|
this.toolManager.register("rect", new RectSelection(
|
|
17954
18166
|
this.toolOverlay,
|
|
@@ -17984,24 +18196,42 @@ class SplatEditor {
|
|
|
17984
18196
|
this.toolOverlay,
|
|
17985
18197
|
(op, pt, threshold) => this.selectByColor(op, pt, threshold)
|
|
17986
18198
|
));
|
|
17987
|
-
this.
|
|
17988
|
-
this.
|
|
17989
|
-
|
|
17990
|
-
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
|
|
17994
|
-
|
|
18199
|
+
if (this.gpuRenderer) {
|
|
18200
|
+
this.volumeRenderer = new SelectionVolumeRenderer(this.gpuRenderer, this.camera);
|
|
18201
|
+
}
|
|
18202
|
+
this.sphereTool = new SphereSelection(this.container, {
|
|
18203
|
+
onApply: (op) => this.applyVolumeSelection(op),
|
|
18204
|
+
onRadiusChanged: (radius) => {
|
|
18205
|
+
var _a2;
|
|
18206
|
+
(_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(radius, 0, 0);
|
|
18207
|
+
}
|
|
18208
|
+
});
|
|
18209
|
+
this.toolManager.register("sphere", this.sphereTool);
|
|
18210
|
+
this.boxTool = new BoxSelection(this.container, {
|
|
18211
|
+
onApply: (op) => this.applyVolumeSelection(op),
|
|
18212
|
+
onDimensionsChanged: (lx, ly, lz) => {
|
|
18213
|
+
var _a2;
|
|
18214
|
+
(_a2 = this.volumeRenderer) == null ? void 0 : _a2.setDimensions(lx, ly, lz);
|
|
18215
|
+
}
|
|
18216
|
+
});
|
|
18217
|
+
this.toolManager.register("box", this.boxTool);
|
|
17995
18218
|
this.gsRenderer.setEditorState(this.splatState.data);
|
|
17996
18219
|
this._keyHandler = this._onKeyDown.bind(this);
|
|
17997
18220
|
window.addEventListener("keydown", this._keyHandler);
|
|
17998
18221
|
this.projDirty = true;
|
|
17999
18222
|
}
|
|
18223
|
+
/**
|
|
18224
|
+
* 渲染体积选择线框(在主渲染 pass 内调用)
|
|
18225
|
+
*/
|
|
18226
|
+
renderVolume(pass) {
|
|
18227
|
+
var _a2;
|
|
18228
|
+
(_a2 = this.volumeRenderer) == null ? void 0 : _a2.render(pass);
|
|
18229
|
+
}
|
|
18000
18230
|
/**
|
|
18001
18231
|
* 退出编辑模式,将编辑结果永久写入渲染数据
|
|
18002
18232
|
*/
|
|
18003
18233
|
exit() {
|
|
18004
|
-
var _a2, _b2;
|
|
18234
|
+
var _a2, _b2, _c;
|
|
18005
18235
|
if (!this._active) return;
|
|
18006
18236
|
this._active = false;
|
|
18007
18237
|
window.removeEventListener("keydown", this._keyHandler);
|
|
@@ -18010,6 +18240,8 @@ class SplatEditor {
|
|
|
18010
18240
|
this.overlayCleanup = [];
|
|
18011
18241
|
(_a2 = this.toolOverlay) == null ? void 0 : _a2.remove();
|
|
18012
18242
|
(_b2 = this.maskCanvas) == null ? void 0 : _b2.remove();
|
|
18243
|
+
(_c = this.volumeRenderer) == null ? void 0 : _c.destroy();
|
|
18244
|
+
this.volumeRenderer = null;
|
|
18013
18245
|
this.applyEditsToRenderer();
|
|
18014
18246
|
this.editHistory.clear();
|
|
18015
18247
|
this.gsRenderer.clearEditorState();
|
|
@@ -18245,17 +18477,14 @@ class SplatEditor {
|
|
|
18245
18477
|
this.editHistory.add(editOp);
|
|
18246
18478
|
}
|
|
18247
18479
|
/**
|
|
18248
|
-
*
|
|
18480
|
+
* 世界空间球体选择
|
|
18249
18481
|
*/
|
|
18250
|
-
|
|
18251
|
-
const pick = this.pickWorldPosition(centerPx.x, centerPx.y);
|
|
18252
|
-
if (!pick) return;
|
|
18253
|
-
const worldRadius = radiusPx * pick.pixelToWorld;
|
|
18482
|
+
selectByWorldSphere(op, center, radius) {
|
|
18254
18483
|
const cpuPos = this.gsRenderer.getCPUPositions();
|
|
18255
18484
|
if (!cpuPos) return;
|
|
18256
18485
|
const modelMat = this.gsRenderer.getModelMatrix();
|
|
18257
|
-
const
|
|
18258
|
-
const
|
|
18486
|
+
const r2 = radius * radius;
|
|
18487
|
+
const [cx, cy, cz] = center;
|
|
18259
18488
|
const editOp = new SelectOp(this.splatState, op, (i) => {
|
|
18260
18489
|
if (this.splatState.data[i] & (State.hidden | State.deleted)) return false;
|
|
18261
18490
|
const i3 = i * 3;
|
|
@@ -18264,25 +18493,19 @@ class SplatEditor {
|
|
|
18264
18493
|
const wy = modelMat[1] * lx + modelMat[5] * ly + modelMat[9] * lz + modelMat[13];
|
|
18265
18494
|
const wz = modelMat[2] * lx + modelMat[6] * ly + modelMat[10] * lz + modelMat[14];
|
|
18266
18495
|
const dx = wx - cx, dy = wy - cy, dz = wz - cz;
|
|
18267
|
-
return dx * dx + dy * dy + dz * dz
|
|
18496
|
+
return dx * dx + dy * dy + dz * dz <= r2;
|
|
18268
18497
|
});
|
|
18269
18498
|
this.editHistory.add(editOp);
|
|
18270
18499
|
}
|
|
18271
18500
|
/**
|
|
18272
|
-
*
|
|
18273
|
-
* X/Y 半宽由屏幕拖拽距离转换,Z 半深度取 max(halfW, halfH)
|
|
18501
|
+
* 世界空间 AABB 选择
|
|
18274
18502
|
*/
|
|
18275
|
-
|
|
18276
|
-
const pick = this.pickWorldPosition(centerPx.x, centerPx.y);
|
|
18277
|
-
if (!pick) return;
|
|
18278
|
-
const s = pick.pixelToWorld;
|
|
18279
|
-
const hx = halfWPx * s;
|
|
18280
|
-
const hy = halfHPx * s;
|
|
18281
|
-
const hz = Math.max(hx, hy);
|
|
18503
|
+
selectByWorldBox(op, center, lenX, lenY, lenZ) {
|
|
18282
18504
|
const cpuPos = this.gsRenderer.getCPUPositions();
|
|
18283
18505
|
if (!cpuPos) return;
|
|
18284
18506
|
const modelMat = this.gsRenderer.getModelMatrix();
|
|
18285
|
-
const cx
|
|
18507
|
+
const [cx, cy, cz] = center;
|
|
18508
|
+
const hx = lenX / 2, hy = lenY / 2, hz = lenZ / 2;
|
|
18286
18509
|
const editOp = new SelectOp(this.splatState, op, (i) => {
|
|
18287
18510
|
if (this.splatState.data[i] & (State.hidden | State.deleted)) return false;
|
|
18288
18511
|
const i3 = i * 3;
|
|
@@ -18290,10 +18513,83 @@ class SplatEditor {
|
|
|
18290
18513
|
const wx = modelMat[0] * lx + modelMat[4] * ly + modelMat[8] * lz + modelMat[12];
|
|
18291
18514
|
const wy = modelMat[1] * lx + modelMat[5] * ly + modelMat[9] * lz + modelMat[13];
|
|
18292
18515
|
const wz = modelMat[2] * lx + modelMat[6] * ly + modelMat[10] * lz + modelMat[14];
|
|
18293
|
-
|
|
18516
|
+
const rx = wx - cx, ry = wy - cy, rz = wz - cz;
|
|
18517
|
+
return rx >= -hx && rx <= hx && ry >= -hy && ry <= hy && rz >= -hz && rz <= hz;
|
|
18294
18518
|
});
|
|
18295
18519
|
this.editHistory.add(editOp);
|
|
18296
18520
|
}
|
|
18521
|
+
/**
|
|
18522
|
+
* 应用当前体积选择
|
|
18523
|
+
*/
|
|
18524
|
+
applyVolumeSelection(op) {
|
|
18525
|
+
if (!this.volumeRenderer) return;
|
|
18526
|
+
const vol = this.volumeRenderer.volume;
|
|
18527
|
+
if (!vol.type) return;
|
|
18528
|
+
if (vol.type === "sphere") {
|
|
18529
|
+
this.selectByWorldSphere(op, vol.center, vol.dimensions[0]);
|
|
18530
|
+
} else {
|
|
18531
|
+
this.selectByWorldBox(op, vol.center, vol.dimensions[0], vol.dimensions[1], vol.dimensions[2]);
|
|
18532
|
+
}
|
|
18533
|
+
}
|
|
18534
|
+
/**
|
|
18535
|
+
* 设置体积选择的中心位置(供外部 gizmo 调用)
|
|
18536
|
+
*/
|
|
18537
|
+
setVolumeCenter(x, y, z) {
|
|
18538
|
+
var _a2;
|
|
18539
|
+
(_a2 = this.volumeRenderer) == null ? void 0 : _a2.setCenter(x, y, z);
|
|
18540
|
+
}
|
|
18541
|
+
/**
|
|
18542
|
+
* 获取当前体积状态
|
|
18543
|
+
*/
|
|
18544
|
+
getVolumeState() {
|
|
18545
|
+
var _a2;
|
|
18546
|
+
return ((_a2 = this.volumeRenderer) == null ? void 0 : _a2.volume) ?? null;
|
|
18547
|
+
}
|
|
18548
|
+
/**
|
|
18549
|
+
* 创建体积位置的 TransformableObject 代理,供 gizmo 控制
|
|
18550
|
+
*/
|
|
18551
|
+
createVolumeTransformProxy() {
|
|
18552
|
+
if (!this.volumeRenderer) return null;
|
|
18553
|
+
const vol = this.volumeRenderer;
|
|
18554
|
+
return {
|
|
18555
|
+
get position() {
|
|
18556
|
+
return vol.volume.center;
|
|
18557
|
+
},
|
|
18558
|
+
get rotation() {
|
|
18559
|
+
return [0, 0, 0];
|
|
18560
|
+
},
|
|
18561
|
+
get scale() {
|
|
18562
|
+
return [1, 1, 1];
|
|
18563
|
+
},
|
|
18564
|
+
setPosition: (x, y, z) => {
|
|
18565
|
+
vol.setCenter(x, y, z);
|
|
18566
|
+
},
|
|
18567
|
+
setRotation: () => {
|
|
18568
|
+
},
|
|
18569
|
+
setScale: () => {
|
|
18570
|
+
}
|
|
18571
|
+
};
|
|
18572
|
+
}
|
|
18573
|
+
/**
|
|
18574
|
+
* 获取模型包围盒中心(世界空间)
|
|
18575
|
+
*/
|
|
18576
|
+
getModelCenter() {
|
|
18577
|
+
const cpuPos = this.gsRenderer.getCPUPositions();
|
|
18578
|
+
if (!cpuPos || cpuPos.length === 0) return [0, 0, 0];
|
|
18579
|
+
const modelMat = this.gsRenderer.getModelMatrix();
|
|
18580
|
+
const count = this.splatState.count;
|
|
18581
|
+
let sx = 0, sy = 0, sz = 0, n = 0;
|
|
18582
|
+
const step = Math.max(1, Math.floor(count / 1e3));
|
|
18583
|
+
for (let i = 0; i < count; i += step) {
|
|
18584
|
+
const i3 = i * 3;
|
|
18585
|
+
const lx = cpuPos[i3], ly = cpuPos[i3 + 1], lz = cpuPos[i3 + 2];
|
|
18586
|
+
sx += modelMat[0] * lx + modelMat[4] * ly + modelMat[8] * lz + modelMat[12];
|
|
18587
|
+
sy += modelMat[1] * lx + modelMat[5] * ly + modelMat[9] * lz + modelMat[13];
|
|
18588
|
+
sz += modelMat[2] * lx + modelMat[6] * ly + modelMat[10] * lz + modelMat[14];
|
|
18589
|
+
n++;
|
|
18590
|
+
}
|
|
18591
|
+
return n > 0 ? [sx / n, sy / n, sz / n] : [0, 0, 0];
|
|
18592
|
+
}
|
|
18297
18593
|
/**
|
|
18298
18594
|
* 根据屏幕像素坐标,找到最近的可见 splat 并返回其世界坐标及深度换算系数
|
|
18299
18595
|
*/
|
|
@@ -18498,6 +18794,8 @@ class App {
|
|
|
18498
18794
|
__publicField(this, "baseRenderScale", 1);
|
|
18499
18795
|
// 绑定的事件处理函数
|
|
18500
18796
|
__publicField(this, "boundOnResize");
|
|
18797
|
+
/** 额外渲染回调(在 gizmo 之前、场景辅助之后执行) */
|
|
18798
|
+
__publicField(this, "extraRenderCallbacks", []);
|
|
18501
18799
|
this.canvas = canvas;
|
|
18502
18800
|
this.boundOnResize = this.onResize.bind(this);
|
|
18503
18801
|
}
|
|
@@ -18943,6 +19241,7 @@ class App {
|
|
|
18943
19241
|
}
|
|
18944
19242
|
this.meshRenderer.render(pass);
|
|
18945
19243
|
this.sceneAids.render(pass);
|
|
19244
|
+
for (const cb of this.extraRenderCallbacks) cb(pass);
|
|
18946
19245
|
this.gizmoManager.render(pass);
|
|
18947
19246
|
this.renderer.endFrame();
|
|
18948
19247
|
if (this.hotspotManager.isActive()) {
|
|
@@ -19185,6 +19484,16 @@ class App {
|
|
|
19185
19484
|
getRenderer() {
|
|
19186
19485
|
return this.renderer;
|
|
19187
19486
|
}
|
|
19487
|
+
/**
|
|
19488
|
+
* 注册额外的渲染回调(在场景辅助之后、gizmo 之前执行)
|
|
19489
|
+
*/
|
|
19490
|
+
addRenderCallback(cb) {
|
|
19491
|
+
this.extraRenderCallbacks.push(cb);
|
|
19492
|
+
}
|
|
19493
|
+
removeRenderCallback(cb) {
|
|
19494
|
+
const idx = this.extraRenderCallbacks.indexOf(cb);
|
|
19495
|
+
if (idx >= 0) this.extraRenderCallbacks.splice(idx, 1);
|
|
19496
|
+
}
|
|
19188
19497
|
/**
|
|
19189
19498
|
* CPU splat 拾取:在屏幕坐标 (clientX, clientY) 处找最近的 splat,返回世界坐标或 null
|
|
19190
19499
|
*/
|
|
@@ -19584,6 +19893,7 @@ export {
|
|
|
19584
19893
|
SHMode,
|
|
19585
19894
|
SceneAidsRenderer,
|
|
19586
19895
|
SceneManager,
|
|
19896
|
+
SelectionVolumeRenderer,
|
|
19587
19897
|
SkyboxRenderer,
|
|
19588
19898
|
SplatBoundingBoxProvider,
|
|
19589
19899
|
SplatEditor,
|