Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix camera view/flight issues when colliding with terrain #4105

Merged
merged 7 commits into from
Aug 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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