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