通过查看Cesium源代码解释了相机如何进行碰撞检测。
缩放
首先,cesium针对基本的鼠标事件在初始化viewer时会注册相关事件。
ScreenSpaceCameraController.js:L1921
function update3D(controller) {
reactToInput(controller, controller.enableRotate, controller.rotateEventTypes, spin3D, controller.inertiaSpin, '_lastInertiaSpinMovement');
//缩放
reactToInput(controller, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement');
reactToInput(controller, controller.enableTilt, controller.tiltEventTypes, tilt3D, controller.inertiaSpin, '_lastInertiaTiltMovement');
reactToInput(controller, controller.enableLook, controller.lookEventTypes, look3D);
}
接着,拾取屏幕坐标系中心点的世界坐标(cartesian3,即障碍点),得到该点与相机的距离。
ScreenSpaceCameraController.js:L1518
function zoom3D(controller, startPosition, movement) {
if (defined(movement.distance)) {
movement = movement.distance;
}
var ellipsoid = controller._ellipsoid;
var scene = controller._scene;
var camera = scene.camera;
var canvas = scene.canvas;
var windowPosition = zoomCVWindowPos;
windowPosition.x = canvas.clientWidth / 2;
windowPosition.y = canvas.clientHeight / 2;
//实际为相机位置为原点,方向等于相机方向的射线
var ray = camera.getPickRay(windowPosition, zoomCVWindowRay);
var intersection;
var height = ellipsoid.cartesianToCartographic(camera.position, zoom3DCartographic).height;
if (height < controller._minimumPickingTerrainHeight) {
//根据屏幕坐标拾取世界坐标
intersection = pickGlobe(controller, windowPosition, zoomCVIntersection);
}
var distance;
if (defined(intersection)) {
//得到与相机位置与障碍点的距离
distance = Cartesian3.distance(ray.origin, intersection);
} else {
distance = height;
}
var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition);
handleZoom(controller, startPosition, movement, controller._zoomFactor, distance, Cartesian3.dot(unitPosition, camera.direction));
}
最后,根据允许的最小缩放距离进行检测。
ScreenSpaceCameraController.js:L426
function handleZoom(object, startPosition, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) {
var percentage = 1.0;
if (defined(unitPositionDotDirection)) {
percentage = CesiumMath.clamp(Math.abs(unitPositionDotDirection), 0.25, 1.0);
}
// distanceMeasure should be the height above the ellipsoid.
// The zoomRate slows as it approaches the surface and stops minimumZoomDistance above it.
var minHeight = object.minimumZoomDistance * percentage;
var maxHeight = object.maximumZoomDistance;
var minDistance = distanceMeasure - minHeight;
var zoomRate = zoomFactor * minDistance;
zoomRate = CesiumMath.clamp(zoomRate, object._minimumZoomRate, object._maximumZoomRate);
var diff = movement.endPosition.y - movement.startPosition.y;
var rangeWindowRatio = diff / object._scene.canvas.clientHeight;
rangeWindowRatio = Math.min(rangeWindowRatio, object.maximumMovementRatio);
var distance = zoomRate * rangeWindowRatio;
//相机到障碍点的位置与允许的最小缩放距离的差值小于1.0,停止缩放。
if (distance > 0.0 && Math.abs(distanceMeasure - minHeight) < 1.0) {
return;
}
if (distance < 0.0 && Math.abs(distanceMeasure - maxHeight) < 1.0) {
return;
}
....
}
旋转(针对地形)
关键是使用adjustHeightForTerrain函数
ScreenSpaceCameraController.js:L1931
function adjustHeightForTerrain(controller) {
controller._adjustedHeightForTerrain = true;
var scene = controller._scene;
var mode = scene.mode;
var globe = scene.globe;
...
var cartographic = scratchAdjustHeightCartographic;
if (mode === SceneMode.SCENE3D) {
ellipsoid.cartesianToCartographic(camera.position, cartographic);
} else {
projection.unproject(camera.position, cartographic);
}
var heightUpdated = false;
if (cartographic.height < controller._minimumCollisionTerrainHeight) {
//根据经纬度,获取该位置所在地形表面的高度
var height = globe.getHeight(cartographic);
if (defined(height)) {
height += controller.minimumZoomDistance;
if (cartographic.height < height) {
//若小于地形高度与允许最小的缩放距离之和,则修改相机高度
cartographic.height = height;
if (mode === SceneMode.SCENE3D) {
ellipsoid.cartographicToCartesian(cartographic, camera.position);
} else {
projection.project(cartographic, camera.position);
}
heightUpdated = true;
}
}
}
...
}