恢复绕注视点轨道倾倒,用 ENU 直接求 heading/pitch 修复近天底奇点
This commit is contained in:
+77
-12
@@ -364,17 +364,64 @@ export class CesiumGlobe {
|
||||
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
||||
}
|
||||
|
||||
// 右键环视:相机“原地倾倒”(位置不动,只改视线方向)。左右 = 经度方向、上下 = 纬度方向,强制北朝上。
|
||||
// 关键:直接算 heading/pitch 传给 setView,绕过 setView 在近天底处用 up 反推 heading 的奇点——
|
||||
// 否则初期的小角度倾斜会被翻转(表现为“向北平移”)。
|
||||
// 右键环视:绕相机注视点(地形表面)做轨道倾倒。左右 = 经度方向、上下 = 纬度方向,强制北朝上。
|
||||
// 关键:把轨道后的视线方向转到 ENU 后直接求 heading/pitch 传给 setView,
|
||||
// 绕过 setView 在近天底处用 up 反推 heading 的奇点(否则小角度倾斜会被翻转)。
|
||||
_setupLookAround() {
|
||||
const scene = this.viewer.scene;
|
||||
const camera = this.viewer.camera;
|
||||
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
|
||||
this._lookState = null;
|
||||
this._orbitScratch = {
|
||||
offset: new Cesium.Cartesian3(),
|
||||
upAxis: new Cesium.Cartesian3(),
|
||||
eastAxis: new Cesium.Cartesian3(),
|
||||
northAxis: new Cesium.Cartesian3(),
|
||||
qAz: new Cesium.Quaternion(),
|
||||
qEl: new Cesium.Quaternion(),
|
||||
q: new Cesium.Quaternion(),
|
||||
rotMat: new Cesium.Matrix3(),
|
||||
newOffset: new Cesium.Cartesian3(),
|
||||
newPos: new Cesium.Cartesian3(),
|
||||
dir: new Cesium.Cartesian3(),
|
||||
enu: new Cesium.Matrix4(),
|
||||
invEnu: new Cesium.Matrix4(),
|
||||
localDir: new Cesium.Cartesian3(),
|
||||
};
|
||||
|
||||
handler.setInputAction((movement) => {
|
||||
// 旋转中心 = 相机当前注视的屏幕中心点(地形表面),而不是点击点
|
||||
const canvas = scene.canvas;
|
||||
const center = Cesium.Cartesian2.fromElements(
|
||||
canvas.clientWidth / 2,
|
||||
canvas.clientHeight / 2,
|
||||
);
|
||||
const ray = camera.getPickRay(center);
|
||||
let pivot = scene.globe.pick(ray, scene);
|
||||
if (!Cesium.defined(pivot)) {
|
||||
pivot = camera.pickEllipsoid(center, scene.globe.ellipsoid);
|
||||
}
|
||||
if (!Cesium.defined(pivot)) return;
|
||||
const sc = this._orbitScratch;
|
||||
const offset = Cesium.Cartesian3.subtract(camera.position, pivot, sc.offset);
|
||||
const upAxis = Cesium.Cartesian3.normalize(pivot, sc.upAxis);
|
||||
const eastAxis = Cesium.Cartesian3.cross(Cesium.Cartesian3.UNIT_Z, pivot, sc.eastAxis); // 上下拖动(纬度方向)
|
||||
if (Cesium.Cartesian3.magnitudeSquared(eastAxis) < 1e-12) {
|
||||
eastAxis.x = 1;
|
||||
eastAxis.y = 0;
|
||||
eastAxis.z = 0; // 极点退化兜底
|
||||
} else {
|
||||
Cesium.Cartesian3.normalize(eastAxis, eastAxis);
|
||||
}
|
||||
const northAxis = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.cross(upAxis, eastAxis, sc.northAxis), // 左右拖动(经度方向)
|
||||
sc.northAxis,
|
||||
);
|
||||
this._lookState = {
|
||||
pivot: Cesium.Cartesian3.clone(pivot),
|
||||
startOffset: Cesium.Cartesian3.clone(offset),
|
||||
eastAxis: Cesium.Cartesian3.clone(eastAxis),
|
||||
northAxis: Cesium.Cartesian3.clone(northAxis),
|
||||
position: Cesium.Cartesian3.clone(camera.position),
|
||||
direction: Cesium.Cartesian3.clone(camera.direction),
|
||||
up: Cesium.Cartesian3.clone(camera.up),
|
||||
@@ -394,7 +441,7 @@ export class CesiumGlobe {
|
||||
s.lastY = movement.endPosition.y;
|
||||
s.azimuth = Cesium.Math.clamp(s.azimuth + dx * LOOK_RATE, -MAX_LOOK_ROT, MAX_LOOK_ROT);
|
||||
s.elevation = Cesium.Math.clamp(s.elevation + dy * LOOK_RATE, -MAX_LOOK_TILT, MAX_LOOK_TILT);
|
||||
this._tiltCamera(s);
|
||||
this._orbitCamera(s);
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
|
||||
handler.setInputAction(() => {
|
||||
@@ -410,14 +457,32 @@ export class CesiumGlobe {
|
||||
this._lookHandler = handler;
|
||||
}
|
||||
|
||||
// 原地倾倒:由累计的经度角(azimuth)/纬度角(elevation)直接求 heading/pitch,roll 固定 0(北朝上)
|
||||
_tiltCamera(s) {
|
||||
const el = s.elevation;
|
||||
const az = s.azimuth;
|
||||
const heading = Math.atan2(-Math.cos(el) * Math.sin(az), Math.sin(el));
|
||||
const pitch = Math.asin(-Math.cos(el) * Math.cos(az));
|
||||
this.viewer.camera.setView({
|
||||
destination: s.position,
|
||||
// 轨道倾倒:左右绕南北轴、上下绕东西轴,位置沿轨道移动且始终看向注视点;直接算 heading/pitch 传 setView
|
||||
_orbitCamera(s) {
|
||||
const camera = this.viewer.camera;
|
||||
const sc = this._orbitScratch;
|
||||
Cesium.Quaternion.fromAxisAngle(s.northAxis, s.azimuth, sc.qAz);
|
||||
Cesium.Quaternion.fromAxisAngle(s.eastAxis, s.elevation, sc.qEl);
|
||||
Cesium.Quaternion.multiply(sc.qEl, sc.qAz, sc.q);
|
||||
Cesium.Matrix3.fromQuaternion(sc.q, sc.rotMat);
|
||||
Cesium.Matrix3.multiplyByVector(sc.rotMat, s.startOffset, sc.newOffset);
|
||||
Cesium.Cartesian3.add(s.pivot, sc.newOffset, sc.newPos);
|
||||
const dir = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(s.pivot, sc.newPos, sc.dir),
|
||||
sc.dir,
|
||||
);
|
||||
// 视线方向转到 newPos 处的 ENU,直接求 heading/pitch(避开 setView 近天底用 up 反推 heading 的翻转)
|
||||
Cesium.Transforms.eastNorthUpToFixedFrame(
|
||||
sc.newPos,
|
||||
this.viewer.scene.globe.ellipsoid,
|
||||
sc.enu,
|
||||
);
|
||||
Cesium.Matrix4.inverseTransformation(sc.enu, sc.invEnu);
|
||||
Cesium.Matrix4.multiplyByPointAsVector(sc.invEnu, dir, sc.localDir);
|
||||
const heading = Math.atan2(sc.localDir.x, sc.localDir.y);
|
||||
const pitch = Math.asin(sc.localDir.z);
|
||||
camera.setView({
|
||||
destination: Cesium.Cartesian3.clone(sc.newPos),
|
||||
orientation: { heading, pitch, roll: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user