Skip to content

Commit

Permalink
Merge pull request #4105 from AnalyticalGraphicsInc/camera-terrain
Browse files Browse the repository at this point in the history
Fix camera view/flight issues when colliding with terrain
  • Loading branch information
mramato authored Aug 2, 2016
2 parents 2671660 + 7c324c6 commit 950eee4
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 111 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
114 changes: 80 additions & 34 deletions Source/Scene/Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -2442,7 +2522,6 @@ define([
};

var scratchFlyToDestination = new Cartesian3();
var scratchFlyToCarto = new Cartographic();
var newOptions = {
destination : undefined,
heading : undefined,
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 0 additions & 4 deletions Source/Scene/CameraFlightPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
5 changes: 3 additions & 2 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
73 changes: 3 additions & 70 deletions Source/Scene/ScreenSpaceCameraController.js
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,6 @@ define([
}

function rotateCVOnTerrain(controller, startPosition, movement) {
var ellipsoid = controller._ellipsoid;
var scene = controller._scene;
var camera = scene.camera;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -1962,8 +1897,6 @@ define([
update3D(this);
}

adjustHeightForTerrain(this);

this._aggregator.reset();
};

Expand Down
10 changes: 9 additions & 1 deletion Specs/Scene/ModelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1834,7 +1834,15 @@ defineSuite([
getHeight : function() {
return 0.0;
},
_surface : {},
_surface : {
tileProvider : {
ready : true
},
_tileLoadQueue : {},
_debug : {
tilesWaitingForChildren : 0
}
},
destroy : function() {}
};

Expand Down
13 changes: 13 additions & 0 deletions Specs/Scene/ScreenSpaceCameraControllerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,23 @@ defineSuite([
this.globe = undefined;
this.mapProjection = new GeographicProjection(ellipsoid);
this.terrainExaggeration = 1.0;
this.screenSpaceCameraController = undefined;
}

function MockGlobe(ellipsoid) {
this.ellipsoid = ellipsoid;
this.getHeight = function(cartographic) {
return 0.0;
};
this._surface = {
tileProvider : {
ready : true
},
_tileLoadQueue : {},
_debug : {
tilesWaitingForChildren : 0
}
};
}
beforeAll(function() {
canvas = createCanvas(1024, 768);
Expand All @@ -86,6 +96,9 @@ defineSuite([

scene = new MockScene(canvas, camera, Ellipsoid.WGS84);
controller = new ScreenSpaceCameraController(scene);

scene.screenSpaceCameraController = controller;
camera._scene = scene;
});

afterEach(function() {
Expand Down

0 comments on commit 950eee4

Please # to comment.