69 lines
3.2 KiB
JavaScript
69 lines
3.2 KiB
JavaScript
/* 轨道相机与指针交互(旋转/平移/缩放/自适应取景) */
|
|||
|
|
/* ---- camera orbit ---- */
|
||
|
|
let az = -0.9, po = 1.15, rd = 620, tx = 120, ty = 190, tz = -30;
|
||
|
|
// lookAt with the world up-axis degenerate-rolls the image when looking near-vertically,
|
||
|
|
// so top view pins the screen-up to -Z: model X runs right, depth runs down-screen
|
||
|
|
const UP_Y = new V3(0, 1, 0), UP_TOP = new V3(0, 0, -1);
|
||
|
|
let camUp = UP_Y.clone();
|
||
|
|
function fitTo(b) {
|
||
|
|
const c = [(b[0][0]+b[1][0])/2, (b[0][1]+b[1][1])/2, (b[0][2]+b[1][2])/2];
|
||
|
|
tx = c[0]; ty = c[1]; tz = c[2];
|
||
|
|
const s = Math.max(b[1][0]-b[0][0], b[1][1]-b[0][1], b[1][2]-b[0][2]);
|
||
|
|
rd = s * 1.78;
|
||
|
|
requestRender();
|
||
|
|
}
|
||
|
|
let BB = [[1e9,1e9,1e9],[-1e9,-1e9,-1e9]];
|
||
|
|
/* robust global bounds in the viewer frame (x, z, -y) */
|
||
|
|
(function(){
|
||
|
|
let lo=[1e9,1e9,1e9], hi=[-1e9,-1e9,-1e9];
|
||
|
|
for (const m of meshes) { if (m.userData.name.slice(0,4) === 'ENV_') continue; // 取景只按手臂本体,包络不入包围盒
|
||
|
|
const b=m.geometry.boundingBox;
|
||
|
|
const a=[b.min.x+m.base.x,b.min.y+m.base.y,b.min.z+m.base.z];
|
||
|
|
const c=[b.max.x+m.base.x,b.max.y+m.base.y,b.max.z+m.base.z];
|
||
|
|
for(let i=0;i<3;i++){lo[i]=Math.min(lo[i],a[i]);hi[i]=Math.max(hi[i],c[i]);} }
|
||
|
|
BB=[lo,hi];
|
||
|
|
})();
|
||
|
|
fitTo(BB);
|
||
|
|
function updCam() {
|
||
|
|
cam.up.copy(camUp);
|
||
|
|
cam.position.set(tx + rd*Math.sin(po)*Math.cos(az), ty + rd*Math.cos(po), tz + rd*Math.sin(po)*Math.sin(az));
|
||
|
|
cam.lookAt(tx, ty, tz);
|
||
|
|
}
|
||
|
|
let drag = null, panDist = 0;
|
||
|
|
cv.addEventListener('pointerdown', e => {
|
||
|
|
drag = {x:e.offsetX, y:e.offsetY, b:e.button};
|
||
|
|
panDist = 0;
|
||
|
|
cv.setPointerCapture(e.pointerId);
|
||
|
|
});
|
||
|
|
/* 悬浮拾取限流:射线要对 34 万三角面全量求交(实测 ≈0.5~1.5 ms/次),
|
||
|
|
旧版每个 pointermove 都算(rAF 合帧后仍≈显示器刷新率次数);
|
||
|
|
这里加 60 ms 时间配额 + 位置去重,指针静止时一次都不算,尾随仍会补最新位置,
|
||
|
|
tooltip 观感不变。点击 / 右键拾取不经过此函数,仍即时求交。 */
|
||
|
|
let hoverPending = false, lastHoverEv = null, hoverT = -1e9, hoverX = -1, hoverY = -1;
|
||
|
|
const HOVER_MS = 60;
|
||
|
|
function schedHover(e) {
|
||
|
|
if (e.clientX === hoverX && e.clientY === hoverY) return; // 没移动:结果不可能变
|
||
|
|
hoverX = e.clientX; hoverY = e.clientY; lastHoverEv = e;
|
||
|
|
if (hoverPending) return;
|
||
|
|
hoverPending = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
hoverPending = false;
|
||
|
|
if (!lastHoverEv || drag) return;
|
||
|
|
hoverT = performance.now();
|
||
|
|
hover(lastHoverEv);
|
||
|
|
}, Math.max(0, HOVER_MS - (performance.now() - hoverT)));
|
||
|
|
}
|
||
|
|
cv.addEventListener('pointermove', e => {
|
||
|
|
if (!drag) { schedHover(e); return; }
|
||
|
|
const dx = e.offsetX - drag.x, dy = e.offsetY - drag.y;
|
||
|
|
panDist += Math.abs(dx) + Math.abs(dy);
|
||
|
|
drag = {x:e.offsetX, y:e.offsetY, b:drag.b};
|
||
|
|
if (drag.b === 0) { az -= dx*0.008; po = Math.min(3.04, Math.max(.05, po - dy*0.008)); }
|
||
|
|
else { const r = rd*0.0016; const f = new V3(Math.sin(az),0,Math.cos(az)), ri = new V3().crossVectors(new V3(0,1,0), f).normalize();
|
||
|
|
tx -= ri.x*dx*r + f.x*dy*r*0.0; tz -= ri.z*dx*r + f.z*dy*r*0.0; ty += dy*r; }
|
||
|
|
requestRender();
|
||
|
|
});
|
||
|
|
addEventListener('pointerup', () => drag = null);
|
||
|
|
cv.addEventListener('wheel', e => { e.preventDefault(); rd *= (1 + Math.sign(e.deltaY)*0.09); requestRender(); }, {passive:false});
|
||
|
|
|