@d5techs/3dgs-lib 1.4.85 → 1.4.86
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 +73 -26
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +73 -26
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +6 -4
- package/dist/core/OrbitControls.d.ts +7 -0
- package/package.json +1 -1
package/dist/3dgs-lib.cjs
CHANGED
|
@@ -793,6 +793,7 @@ const _OrbitControls = class _OrbitControls {
|
|
|
793
793
|
// 键盘移动
|
|
794
794
|
__publicField(this, "moveSpeed", 0.015);
|
|
795
795
|
__publicField(this, "pressedKeys", /* @__PURE__ */ new Set());
|
|
796
|
+
__publicField(this, "_wasKeyboardMoving", false);
|
|
796
797
|
// 触摸手势状态
|
|
797
798
|
__publicField(this, "touchMode", "none");
|
|
798
799
|
__publicField(this, "lastTouchDistance", 0);
|
|
@@ -969,7 +970,14 @@ const _OrbitControls = class _OrbitControls {
|
|
|
969
970
|
this.pressedKeys.delete(e.key.toLowerCase());
|
|
970
971
|
}
|
|
971
972
|
applyKeyboardMovement() {
|
|
972
|
-
if (this.pressedKeys.size === 0)
|
|
973
|
+
if (this.pressedKeys.size === 0) {
|
|
974
|
+
if (this._wasKeyboardMoving) {
|
|
975
|
+
this._wasKeyboardMoving = false;
|
|
976
|
+
this.recenterOrbitTarget();
|
|
977
|
+
}
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
this._wasKeyboardMoving = true;
|
|
973
981
|
const m = this.camera.viewMatrix;
|
|
974
982
|
const right = [m[0], m[4], m[8]];
|
|
975
983
|
const forward = [-m[2], -m[6], -m[10]];
|
|
@@ -1129,9 +1137,15 @@ const _OrbitControls = class _OrbitControls {
|
|
|
1129
1137
|
onTouchEnd(e) {
|
|
1130
1138
|
if (e.touches.length === 0) {
|
|
1131
1139
|
this.isDragging = false;
|
|
1140
|
+
if (this.touchMode === "zoom-pan") {
|
|
1141
|
+
this.recenterOrbitTarget();
|
|
1142
|
+
}
|
|
1132
1143
|
this.touchMode = "none";
|
|
1133
1144
|
this.lastTouchDistance = 0;
|
|
1134
1145
|
} else if (e.touches.length === 1) {
|
|
1146
|
+
if (this.touchMode === "zoom-pan") {
|
|
1147
|
+
this.recenterOrbitTarget();
|
|
1148
|
+
}
|
|
1135
1149
|
this.touchMode = "rotate";
|
|
1136
1150
|
this.lastX = e.touches[0].clientX;
|
|
1137
1151
|
this.lastY = e.touches[0].clientY;
|
|
@@ -1148,6 +1162,34 @@ const _OrbitControls = class _OrbitControls {
|
|
|
1148
1162
|
y: (touches[0].clientY + touches[1].clientY) / 2
|
|
1149
1163
|
};
|
|
1150
1164
|
}
|
|
1165
|
+
/**
|
|
1166
|
+
* 将 orbit 目标重新锚定到屏幕中心的模型表面点,
|
|
1167
|
+
* 保持相机世界坐标不变,仅重算 distance/theta/phi。
|
|
1168
|
+
* 用于 WASD 移动或触摸缩放结束后修复旋转中心偏移。
|
|
1169
|
+
*/
|
|
1170
|
+
recenterOrbitTarget() {
|
|
1171
|
+
if (!this.pickWorldPosition) return;
|
|
1172
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
1173
|
+
const hit = this.pickWorldPosition(
|
|
1174
|
+
rect.left + rect.width / 2,
|
|
1175
|
+
rect.top + rect.height / 2
|
|
1176
|
+
);
|
|
1177
|
+
if (!hit) return;
|
|
1178
|
+
const dx = this.camera.position[0] - hit[0];
|
|
1179
|
+
const dy = this.camera.position[1] - hit[1];
|
|
1180
|
+
const dz = this.camera.position[2] - hit[2];
|
|
1181
|
+
const newDist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1182
|
+
if (newDist < this.minDistance) return;
|
|
1183
|
+
this.camera.target[0] = hit[0];
|
|
1184
|
+
this.camera.target[1] = hit[1];
|
|
1185
|
+
this.camera.target[2] = hit[2];
|
|
1186
|
+
this.distance = newDist;
|
|
1187
|
+
this.theta = Math.atan2(dx, dz);
|
|
1188
|
+
this.phi = Math.acos(Math.min(1, Math.max(-1, dy / newDist)));
|
|
1189
|
+
this.deltaPanX = 0;
|
|
1190
|
+
this.deltaPanY = 0;
|
|
1191
|
+
this.deltaPanZ = 0;
|
|
1192
|
+
}
|
|
1151
1193
|
/**
|
|
1152
1194
|
* 将球坐标写入相机位置(内部方法,不处理阻尼)
|
|
1153
1195
|
*/
|
|
@@ -19249,8 +19291,8 @@ class App {
|
|
|
19249
19291
|
__publicField(this, "animationId", 0);
|
|
19250
19292
|
// 是否使用移动端渲染器
|
|
19251
19293
|
__publicField(this, "useMobileRenderer", false);
|
|
19252
|
-
//
|
|
19253
|
-
__publicField(this, "
|
|
19294
|
+
// 移动端优化开关(默认关闭,由外部显式启用)
|
|
19295
|
+
__publicField(this, "mobileOptimizationsEnabled", false);
|
|
19254
19296
|
// 移动端可见 splat 硬上限(保证稳定帧率)
|
|
19255
19297
|
__publicField(this, "mobileMaxVisibleCap", 0);
|
|
19256
19298
|
// 最近加载的 CompactSplatData(用于编辑器导出)
|
|
@@ -19284,7 +19326,6 @@ class App {
|
|
|
19284
19326
|
* 初始化应用
|
|
19285
19327
|
*/
|
|
19286
19328
|
async init() {
|
|
19287
|
-
var _a2;
|
|
19288
19329
|
this.renderer = new Renderer(this.canvas);
|
|
19289
19330
|
await this.renderer.init();
|
|
19290
19331
|
this.camera = new Camera();
|
|
@@ -19313,14 +19354,6 @@ class App {
|
|
|
19313
19354
|
if (this.renderer.isAppleGPU) {
|
|
19314
19355
|
this.applyAppleGPUDefaults();
|
|
19315
19356
|
}
|
|
19316
|
-
this.isMobile = isMobileDevice();
|
|
19317
|
-
if (this.isMobile) {
|
|
19318
|
-
createDebugOverlay();
|
|
19319
|
-
(_a2 = getDebugOverlay()) == null ? void 0 : _a2.info(
|
|
19320
|
-
`Mobile detected. DPR=${window.devicePixelRatio}, canvas=${this.canvas.width}x${this.canvas.height}`
|
|
19321
|
-
);
|
|
19322
|
-
this.applyMobileDefaults();
|
|
19323
|
-
}
|
|
19324
19357
|
}
|
|
19325
19358
|
/**
|
|
19326
19359
|
* Apple GPU (M1/M2/M3 等) 自动优化配置
|
|
@@ -19339,12 +19372,26 @@ class App {
|
|
|
19339
19372
|
);
|
|
19340
19373
|
}
|
|
19341
19374
|
/**
|
|
19342
|
-
*
|
|
19343
|
-
*
|
|
19375
|
+
* 启用/禁用移动端性能优化(默认关闭)
|
|
19376
|
+
* 开启后,加载模型时将自动应用:f16 半精度、16-bit 排序、像素剔除、动态分辨率等。
|
|
19377
|
+
* 应在 init() 之后、加载模型之前调用。
|
|
19344
19378
|
*/
|
|
19345
|
-
|
|
19346
|
-
|
|
19347
|
-
|
|
19379
|
+
enableMobileOptimizations(enabled = true) {
|
|
19380
|
+
var _a2;
|
|
19381
|
+
this.mobileOptimizationsEnabled = enabled;
|
|
19382
|
+
if (enabled) {
|
|
19383
|
+
this.dynamicResolutionEnabled = true;
|
|
19384
|
+
createDebugOverlay();
|
|
19385
|
+
(_a2 = getDebugOverlay()) == null ? void 0 : _a2.info(
|
|
19386
|
+
`Mobile optimizations enabled. DPR=${window.devicePixelRatio}, canvas=${this.canvas.width}x${this.canvas.height}`
|
|
19387
|
+
);
|
|
19388
|
+
console.log(`[3DGS] Mobile optimizations: f16, sortBits=16, pixelThreshold=1.5, dynamicRes=ON`);
|
|
19389
|
+
} else {
|
|
19390
|
+
this.dynamicResolutionEnabled = false;
|
|
19391
|
+
}
|
|
19392
|
+
}
|
|
19393
|
+
isMobileOptimized() {
|
|
19394
|
+
return this.mobileOptimizationsEnabled;
|
|
19348
19395
|
}
|
|
19349
19396
|
/**
|
|
19350
19397
|
* 创建 GSSplatRenderer 并自动应用平台优化
|
|
@@ -19407,7 +19454,7 @@ class App {
|
|
|
19407
19454
|
*/
|
|
19408
19455
|
async addPLY(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
|
|
19409
19456
|
try {
|
|
19410
|
-
const
|
|
19457
|
+
const useMobileOpt = this.mobileOptimizationsEnabled;
|
|
19411
19458
|
let buffer;
|
|
19412
19459
|
if (typeof urlOrBuffer === "string") {
|
|
19413
19460
|
buffer = await this.fetchWithProgress(
|
|
@@ -19430,16 +19477,16 @@ class App {
|
|
|
19430
19477
|
onProgress(50 + parseProgress, "parse");
|
|
19431
19478
|
}
|
|
19432
19479
|
};
|
|
19433
|
-
if (
|
|
19480
|
+
if (useMobileOpt) {
|
|
19434
19481
|
console.log(
|
|
19435
|
-
"[3DGS] Mobile
|
|
19482
|
+
"[3DGS] Mobile optimizations active, using SH L0"
|
|
19436
19483
|
);
|
|
19437
19484
|
}
|
|
19438
|
-
const gsRenderer = this.createGSRendererUnified(
|
|
19485
|
+
const gsRenderer = this.createGSRendererUnified(useMobileOpt);
|
|
19439
19486
|
this.useMobileRenderer = false;
|
|
19440
19487
|
const compactData = await this.parsePLYBuffer(buffer, {
|
|
19441
19488
|
maxSplats: Infinity,
|
|
19442
|
-
loadSH: !
|
|
19489
|
+
loadSH: !useMobileOpt,
|
|
19443
19490
|
onProgress: parseProgressCallback,
|
|
19444
19491
|
coordinateSystem
|
|
19445
19492
|
});
|
|
@@ -19460,7 +19507,7 @@ class App {
|
|
|
19460
19507
|
*/
|
|
19461
19508
|
async addSplat(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
|
|
19462
19509
|
try {
|
|
19463
|
-
const
|
|
19510
|
+
const useMobileOpt = this.mobileOptimizationsEnabled;
|
|
19464
19511
|
let buffer;
|
|
19465
19512
|
if (typeof urlOrBuffer === "string") {
|
|
19466
19513
|
buffer = await this.fetchWithProgress(
|
|
@@ -19491,7 +19538,7 @@ class App {
|
|
|
19491
19538
|
}
|
|
19492
19539
|
if (onProgress) onProgress(90, "parse");
|
|
19493
19540
|
if (onProgress) onProgress(90, "upload");
|
|
19494
|
-
const gsRenderer = this.createGSRendererUnified(
|
|
19541
|
+
const gsRenderer = this.createGSRendererUnified(useMobileOpt);
|
|
19495
19542
|
this.useMobileRenderer = false;
|
|
19496
19543
|
gsRenderer.setData(splats);
|
|
19497
19544
|
this.sceneManager.setGSRenderer(gsRenderer);
|
|
@@ -19538,7 +19585,7 @@ class App {
|
|
|
19538
19585
|
*/
|
|
19539
19586
|
async addSOG(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
|
|
19540
19587
|
try {
|
|
19541
|
-
const
|
|
19588
|
+
const useMobileOpt = this.mobileOptimizationsEnabled;
|
|
19542
19589
|
let buffer;
|
|
19543
19590
|
if (typeof urlOrBuffer === "string") {
|
|
19544
19591
|
buffer = await this.fetchWithProgress(
|
|
@@ -19580,7 +19627,7 @@ class App {
|
|
|
19580
19627
|
}
|
|
19581
19628
|
}
|
|
19582
19629
|
if (onProgress) onProgress(90, "upload");
|
|
19583
|
-
const gsRenderer = this.createGSRendererUnified(
|
|
19630
|
+
const gsRenderer = this.createGSRendererUnified(useMobileOpt);
|
|
19584
19631
|
this.useMobileRenderer = false;
|
|
19585
19632
|
gsRenderer.setCompactData(compactData);
|
|
19586
19633
|
this.lastCompactData = compactData;
|