diff --git a/CHANGES.md b/CHANGES.md index 6e44781010c8..cc97904fc7bd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ Change Log ========== +### 1.25 - 2016-09-01 + +* Camera flights now disable collision with the terrain until all of the terrain in the area has finished loading. This prevents the camera from being moved to be above lower resolution terrain when flying to a position close to higher resolution terrain. [#4075](https://github.com/AnalyticalGraphicsInc/cesium/issues/4075) + ### 1.24 - 2016-08-01 * Added support in CZML for expressing `BillboardGraphics.alignedAxis` as the velocity vector of an entity, using `velocityReference` syntax. diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 0a1629fb0213..ac0e65bffedc 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -219,6 +219,7 @@ define([ this._projection = projection; this._maxCoord = projection.project(new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO)); this._max2Dfrustum = undefined; + this._suspendTerrainAdjustment = false; // set default view rectangleCameraPosition3D(this, Camera.DEFAULT_VIEW_RECTANGLE, this.position, true); @@ -338,6 +339,76 @@ define([ } }; + var scratchAdjustHeightTransform = new Matrix4(); + var scratchAdjustHeightCartographic = new Cartographic(); + + Camera.prototype._adjustHeightForTerrain = function() { + var scene = this._scene; + + var screenSpaceCameraController = scene.screenSpaceCameraController; + var enableCollisionDetection = screenSpaceCameraController.enableCollisionDetection; + var minimumCollisionTerrainHeight = screenSpaceCameraController.minimumCollisionTerrainHeight; + var minimumZoomDistance = screenSpaceCameraController.minimumZoomDistance; + + if (this._suspendTerrainAdjustment || !enableCollisionDetection) { + return; + } + + var mode = this._mode; + var globe = scene.globe; + + if (!defined(globe) || mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { + return; + } + + var ellipsoid = globe.ellipsoid; + var projection = scene.mapProjection; + + var transform; + var mag; + if (!Matrix4.equals(this.transform, Matrix4.IDENTITY)) { + transform = Matrix4.clone(this.transform, scratchAdjustHeightTransform); + mag = Cartesian3.magnitude(this.position); + this._setTransform(Matrix4.IDENTITY); + } + + var cartographic = scratchAdjustHeightCartographic; + if (mode === SceneMode.SCENE3D) { + ellipsoid.cartesianToCartographic(this.position, cartographic); + } else { + projection.unproject(this.position, cartographic); + } + + var heightUpdated = false; + if (cartographic.height < minimumCollisionTerrainHeight) { + var height = globe.getHeight(cartographic); + if (defined(height)) { + height += minimumZoomDistance; + if (cartographic.height < height) { + cartographic.height = height; + if (mode === SceneMode.SCENE3D) { + ellipsoid.cartographicToCartesian(cartographic, this.position); + } else { + projection.project(cartographic, this.position); + } + heightUpdated = true; + } + } + } + + if (defined(transform)) { + this._setTransform(transform); + if (heightUpdated) { + Cartesian3.normalize(this.position, this.position); + Cartesian3.negate(this.position, this.direction); + Cartesian3.multiplyByScalar(this.position, Math.max(mag, minimumZoomDistance), this.position); + Cartesian3.normalize(this.direction, this.direction); + Cartesian3.cross(this.direction, this.up, this.right); + Cartesian3.cross(this.right, this.direction, this.up); + } + } + }; + function convertTransformForColumbusView(camera) { Transforms.basisTo2D(camera._projection, camera._transform, camera._actualTransform); } @@ -854,6 +925,13 @@ define([ if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, this.position); } + + var globe = this._scene.globe; + var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && !defined(globe._surface._tileLoadQueue.head) && globe._surface._debug.tilesWaitingForChildren === 0); + if (this._suspendTerrainAdjustment) { + this._suspendTerrainAdjustment = !globeFinishedUpdating; + } + this._adjustHeightForTerrain(); }; var setTransformPosition = new Cartesian3(); @@ -1082,6 +1160,8 @@ define([ var pitch = defaultValue(orientation.pitch, -CesiumMath.PI_OVER_TWO); var roll = defaultValue(orientation.roll, 0.0); + this._suspendTerrainAdjustment = true; + if (mode === SceneMode.SCENE3D) { setView3D(this, destination, heading, pitch, roll); } else if (mode === SceneMode.SCENE2D) { @@ -2442,7 +2522,6 @@ define([ }; var scratchFlyToDestination = new Cartesian3(); - var scratchFlyToCarto = new Cartographic(); var newOptions = { destination : undefined, heading : undefined, @@ -2542,39 +2621,6 @@ define([ destination = this.getRectangleCameraCoordinates(destination, scratchFlyToDestination); } - var sscc = this._scene.screenSpaceCameraController; - - if (defined(sscc) || mode === SceneMode.SCENE2D) { - var ellipsoid = this._scene.mapProjection.ellipsoid; - var destinationCartographic = ellipsoid.cartesianToCartographic(destination, scratchFlyToCarto); - var height = destinationCartographic.height; - - // Make sure camera doesn't zoom outside set limits - if (defined(sscc)) { - //The computed height for rectangle in 2D/CV is stored in the 'z' component of Cartesian3 - if (mode !== SceneMode.SCENE3D && isRectangle) { - destination.z = CesiumMath.clamp(destination.z, sscc.minimumZoomDistance, sscc.maximumZoomDistance); - } else { - destinationCartographic.height = CesiumMath.clamp(destinationCartographic.height, sscc.minimumZoomDistance, sscc.maximumZoomDistance); - } - } - - // The max height in 2D might be lower than the max height for sscc. - if (mode === SceneMode.SCENE2D) { - var maxHeight = ellipsoid.maximumRadius * Math.PI * 2.0; - if (isRectangle) { - destination.z = Math.min(destination.z, maxHeight); - } else { - destinationCartographic.height = Math.min(destinationCartographic.height, maxHeight); - } - } - - //Only change if we clamped the height - if (destinationCartographic.height !== height) { - destination = ellipsoid.cartographicToCartesian(destinationCartographic, scratchFlyToDestination); - } - } - newOptions.destination = destination; newOptions.heading = orientation.heading; newOptions.pitch = orientation.pitch; diff --git a/Source/Scene/CameraFlightPath.js b/Source/Scene/CameraFlightPath.js index 4b7b28df3e94..3aacb634f9b1 100644 --- a/Source/Scene/CameraFlightPath.js +++ b/Source/Scene/CameraFlightPath.js @@ -151,10 +151,6 @@ define([ var startRoll = adjustAngleForLERP(camera.roll, roll); var destCart = ellipsoid.cartesianToCartographic(destination, scratchEndCart); - if (destCart.height <= 0.0) { - destCart.height = startCart.height; - } - startCart.longitude = CesiumMath.zeroToTwoPi(startCart.longitude); destCart.longitude = CesiumMath.zeroToTwoPi(destCart.longitude); diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index fcfd53fc76d5..0f47731b8191 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2303,13 +2303,14 @@ define([ } this._tweens.update(); - this._camera.update(this._mode); - this._camera._updateCameraChanged(); this._screenSpaceCameraController.update(); if (defined(this._deviceOrientationCameraController)) { this._deviceOrientationCameraController.update(); } + + this._camera.update(this._mode); + this._camera._updateCameraChanged(); }; var scratchEyeTranslation = new Cartesian3(); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 23af144ee69d..02a98707494e 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -991,7 +991,6 @@ define([ } function rotateCVOnTerrain(controller, startPosition, movement) { - var ellipsoid = controller._ellipsoid; var scene = controller._scene; var camera = scene.camera; @@ -1046,7 +1045,7 @@ define([ var verticalCenter = IntersectionTests.rayPlane(ray, plane, rotateCVVerticalCenter); var projection = camera._projection; - ellipsoid = projection.ellipsoid; + var ellipsoid = projection.ellipsoid; Cartesian3.fromElements(center.y, center.z, center.x, center); var cart = projection.unproject(center, rotateCVCart); @@ -1122,7 +1121,7 @@ define([ controller._rotateRateRangeAdjustment = radius; var originalPosition = Cartesian3.clone(camera.positionWC, rotateCVCartesian3); - adjustHeightForTerrain(controller); + camera._adjustHeightForTerrain(); if (!Cartesian3.equals(camera.positionWC, originalPosition)) { camera._setTransform(verticalTransform); @@ -1753,7 +1752,7 @@ define([ controller._rotateRateRangeAdjustment = radius; var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3); - adjustHeightForTerrain(controller); + camera._adjustHeightForTerrain(); if (!Cartesian3.equals(camera.positionWC, originalPosition)) { camera._setTransform(verticalTransform); @@ -1866,70 +1865,6 @@ define([ reactToInput(controller, controller.enableLook, controller.lookEventTypes, look3D); } - var scratchAdjustHeightCartographic = new Cartographic(); - - function adjustHeightForTerrain(controller) { - if (!controller.enableCollisionDetection) { - return; - } - - var scene = controller._scene; - var mode = scene.mode; - var globe = scene.globe; - - if (!defined(globe) || mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { - return; - } - - var camera = scene.camera; - var ellipsoid = globe.ellipsoid; - var projection = scene.mapProjection; - - var transform; - var mag; - if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) { - transform = Matrix4.clone(camera.transform); - mag = Cartesian3.magnitude(camera.position); - camera._setTransform(Matrix4.IDENTITY); - } - - 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; - } - } - } - - if (defined(transform)) { - camera._setTransform(transform); - if (heightUpdated) { - Cartesian3.normalize(camera.position, camera.position); - Cartesian3.negate(camera.position, camera.direction); - Cartesian3.multiplyByScalar(camera.position, Math.max(mag, controller.minimumZoomDistance), camera.position); - Cartesian3.normalize(camera.direction, camera.direction); - Cartesian3.cross(camera.direction, camera.up, camera.right); - Cartesian3.cross(camera.right, camera.direction, camera.up); - } - } - } - /** * @private */ @@ -1962,8 +1897,6 @@ define([ update3D(this); } - adjustHeightForTerrain(this); - this._aggregator.reset(); }; diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 8756b4e7773f..30148d615ea1 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -1834,7 +1834,15 @@ defineSuite([ getHeight : function() { return 0.0; }, - _surface : {}, + _surface : { + tileProvider : { + ready : true + }, + _tileLoadQueue : {}, + _debug : { + tilesWaitingForChildren : 0 + } + }, destroy : function() {} }; diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 015875efd384..0bb6a625bbda 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -57,6 +57,7 @@ defineSuite([ this.globe = undefined; this.mapProjection = new GeographicProjection(ellipsoid); this.terrainExaggeration = 1.0; + this.screenSpaceCameraController = undefined; } function MockGlobe(ellipsoid) { @@ -64,6 +65,15 @@ defineSuite([ this.getHeight = function(cartographic) { return 0.0; }; + this._surface = { + tileProvider : { + ready : true + }, + _tileLoadQueue : {}, + _debug : { + tilesWaitingForChildren : 0 + } + }; } beforeAll(function() { canvas = createCanvas(1024, 768); @@ -86,6 +96,9 @@ defineSuite([ scene = new MockScene(canvas, camera, Ellipsoid.WGS84); controller = new ScreenSpaceCameraController(scene); + + scene.screenSpaceCameraController = controller; + camera._scene = scene; }); afterEach(function() {