Cesium相机碰撞检测源码解析

通过查看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;
                }
            }
        }

       ...
    }

猜你喜欢

转载自blog.csdn.net/u010447508/article/details/104613632