diff --git a/Apps/Sandcastle/gallery/development/Shadows.html b/Apps/Sandcastle/gallery/development/Shadows.html
index 1f907c7ce13b..2138e5fa77e3 100644
--- a/Apps/Sandcastle/gallery/development/Shadows.html
+++ b/Apps/Sandcastle/gallery/development/Shadows.html
@@ -525,7 +525,7 @@
var frustumSize = 55.0;
var frustumNear = 1.0;
var frustumFar = 400.0;
- var frustum = new Cesium.OrthographicFrustum();
+ var frustum = new Cesium.OrthographicOffCenterFrustum();
frustum.left = -frustumSize;
frustum.right = frustumSize;
frustum.bottom = -frustumSize;
diff --git a/CHANGES.md b/CHANGES.md
index 3443c7973224..e4306e63cb68 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,8 @@ Change Log
### 1.32 - 2017-04-03
+* Deprecated
+ * The `left`, `right`, `bottom`, and `top` properties of `OrthographicFrustum` are deprecated and will be removed in 1.33. Use `OrthographicOffCenterFrustum` instead.
* Added `Camera.flyTo` and `Camera.flyToBoundingSphere` options [#5070](https://github.com/AnalyticalGraphicsInc/cesium/pull/5070)
* `flyOverLongitude` to select one of two possible on Globe paths which camera should fly.
* `flyOverLongitudeWeight` to set a threshold: how many times the `flyOverLongitude` way can be than shortest path.
@@ -15,6 +17,7 @@ Change Log
* Fixed an issue with `PinBuilder` where inset images could have low-alpha fringes against an opaque background. [#5099](https://github.com/AnalyticalGraphicsInc/cesium/pull/5099)
* Fixed a bug in `ModelAnimationCache` causing different animations to reference the same animation. [#5064](https://github.com/AnalyticalGraphicsInc/cesium/pull/5064)
* Fixed a bug that caused an exception in `CesiumInspectorViewModel` when using the NW / NE / SW / SE / Parent buttons to navigate to a terrain tile that is not yet loaded.
+* Added support for an orthographic projection in 3D and Columbus view. Set `projectionPicker` to `true` in the options when creating a `Viewer` to add a widget that will switch projections. [#5021](https://github.com/AnalyticalGraphicsInc/cesium/pull/5021)
### 1.31 - 2017-03-01
diff --git a/Source/Core/HeightmapTessellator.js b/Source/Core/HeightmapTessellator.js
index fa222bf44dde..2195f819a985 100644
--- a/Source/Core/HeightmapTessellator.js
+++ b/Source/Core/HeightmapTessellator.js
@@ -214,8 +214,11 @@ define([
var elementMultiplier = defaultValue(structure.elementMultiplier, HeightmapTessellator.DEFAULT_STRUCTURE.elementMultiplier);
var isBigEndian = defaultValue(structure.isBigEndian, HeightmapTessellator.DEFAULT_STRUCTURE.isBigEndian);
- var granularityX = Rectangle.computeWidth(nativeRectangle) / (width - 1);
- var granularityY = Rectangle.computeHeight(nativeRectangle) / (height - 1);
+ var rectangleWidth = Rectangle.computeWidth(nativeRectangle);
+ var rectangleHeight = Rectangle.computeHeight(nativeRectangle);
+
+ var granularityX = rectangleWidth / (width - 1);
+ var granularityY = rectangleHeight / (height - 1);
var radiiSquared = ellipsoid.radiiSquared;
var radiiSquaredX = radiiSquared.x;
@@ -337,10 +340,29 @@ define([
heightSample = (heightSample * heightScale + heightOffset) * exaggeration;
+ var u = (longitude - geographicWest) / (geographicEast - geographicWest);
+ u = CesiumMath.clamp(u, 0.0, 1.0);
+ uvs[index] = new Cartesian2(u, v);
+
maximumHeight = Math.max(maximumHeight, heightSample);
minimumHeight = Math.min(minimumHeight, heightSample);
if (colIndex !== col || rowIndex !== row) {
+ var percentage = 0.00001;
+ if (colIndex < 0) {
+ longitude -= percentage * rectangleWidth;
+ } else {
+ longitude += percentage * rectangleWidth;
+ }
+ if (rowIndex < 0) {
+ latitude += percentage * rectangleHeight;
+ } else {
+ latitude -= percentage * rectangleHeight;
+ }
+
+ cosLatitude = cos(latitude);
+ nZ = sin(latitude);
+ kZ = radiiSquaredZ * nZ;
heightSample -= skirtHeight;
}
@@ -365,10 +387,6 @@ define([
positions[index] = position;
heights[index] = heightSample;
- var u = (longitude - geographicWest) / (geographicEast - geographicWest);
- u = CesiumMath.clamp(u, 0.0, 1.0);
- uvs[index] = new Cartesian2(u, v);
-
if (includeWebMercatorT) {
webMercatorTs[index] = webMercatorT;
}
diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js
index f326d01d7ac6..6f6512789715 100644
--- a/Source/Renderer/AutomaticUniforms.js
+++ b/Source/Renderer/AutomaticUniforms.js
@@ -530,17 +530,6 @@ define([
}
}),
- /**
- * @private
- */
- czm_inverseProjectionOIT : new AutomaticUniform({
- size : 1,
- datatype : WebGLConstants.FLOAT_MAT4,
- getValue : function(uniformState) {
- return uniformState.inverseProjectionOIT;
- }
- }),
-
/**
* An automatic GLSL uniform representing a 4x4 projection transformation matrix with the far plane at infinity,
* that transforms eye coordinates to clip coordinates. Clip coordinates is the
diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js
index 759659750639..89eacb030303 100644
--- a/Source/Renderer/UniformState.js
+++ b/Source/Renderer/UniformState.js
@@ -13,6 +13,7 @@ define([
'../Core/Matrix4',
'../Core/Simon1994PlanetaryPositions',
'../Core/Transforms',
+ '../Scene/OrthographicFrustum',
'../Scene/SceneMode'
], function(
BoundingRectangle,
@@ -28,6 +29,7 @@ define([
Matrix4,
Simon1994PlanetaryPositions,
Transforms,
+ OrthographicFrustum,
SceneMode) {
'use strict';
@@ -80,9 +82,6 @@ define([
this._inverseProjectionDirty = true;
this._inverseProjection = new Matrix4();
- this._inverseProjectionOITDirty = true;
- this._inverseProjectionOIT = new Matrix4();
-
this._modelViewDirty = true;
this._modelView = new Matrix4();
@@ -147,6 +146,7 @@ define([
this._frustum2DWidth = 0.0;
this._eyeHeight2D = new Cartesian2();
this._resolutionScale = 1.0;
+ this._orthographicIn3D = false;
this._fogDensity = undefined;
@@ -393,17 +393,6 @@ define([
}
},
- /**
- * @memberof UniformState.prototype
- * @private
- */
- inverseProjectionOIT : {
- get : function() {
- cleanInverseProjectionOIT(this);
- return this._inverseProjectionOIT;
- }
- },
-
/**
* @memberof UniformState.prototype
* @type {Matrix4}
@@ -834,7 +823,6 @@ define([
Matrix4.clone(matrix, uniformState._projection);
uniformState._inverseProjectionDirty = true;
- uniformState._inverseProjectionOITDirty = true;
uniformState._viewProjectionDirty = true;
uniformState._inverseViewProjectionDirty = true;
uniformState._modelViewProjectionDirty = true;
@@ -896,6 +884,8 @@ define([
this._entireFrustum.x = camera.frustum.near;
this._entireFrustum.y = camera.frustum.far;
this.updateFrustum(camera.frustum);
+
+ this._orthographicIn3D = this._mode !== SceneMode.SCENE2D && camera.frustum instanceof OrthographicFrustum;
};
/**
@@ -913,7 +903,7 @@ define([
this._currentFrustum.x = frustum.near;
this._currentFrustum.y = frustum.far;
- if (!defined(frustum.top)) {
+ if (defined(frustum._offCenterFrustum)) {
frustum = frustum._offCenterFrustum;
}
@@ -987,18 +977,10 @@ define([
if (uniformState._inverseProjectionDirty) {
uniformState._inverseProjectionDirty = false;
- Matrix4.inverse(uniformState._projection, uniformState._inverseProjection);
- }
- }
-
- function cleanInverseProjectionOIT(uniformState) {
- if (uniformState._inverseProjectionOITDirty) {
- uniformState._inverseProjectionOITDirty = false;
-
- if (uniformState._mode !== SceneMode.SCENE2D && uniformState._mode !== SceneMode.MORPHING) {
- Matrix4.inverse(uniformState._projection, uniformState._inverseProjectionOIT);
+ if (uniformState._mode !== SceneMode.SCENE2D && uniformState._mode !== SceneMode.MORPHING && !uniformState._orthographicIn3D) {
+ Matrix4.inverse(uniformState._projection, uniformState._inverseProjection);
} else {
- Matrix4.clone(Matrix4.IDENTITY, uniformState._inverseProjectionOIT);
+ Matrix4.clone(Matrix4.ZERO, uniformState._inverseProjection);
}
}
}
diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js
index d482b7ce700c..7589178a8b61 100644
--- a/Source/Scene/Camera.js
+++ b/Source/Scene/Camera.js
@@ -26,6 +26,8 @@ define([
'../Core/Transforms',
'./CameraFlightPath',
'./MapMode2D',
+ './OrthographicFrustum',
+ './OrthographicOffCenterFrustum',
'./PerspectiveFrustum',
'./SceneMode'
], function(
@@ -55,6 +57,8 @@ define([
Transforms,
CameraFlightPath,
MapMode2D,
+ OrthographicFrustum,
+ OrthographicOffCenterFrustum,
PerspectiveFrustum,
SceneMode) {
'use strict';
@@ -335,7 +339,13 @@ define([
}
var dirAngle = CesiumMath.acosClamped(Cartesian3.dot(camera.directionWC, camera._changedDirection));
- var dirPercentage = dirAngle / (camera.frustum.fovy * 0.5);
+
+ var dirPercentage;
+ if (defined(camera.frustum.fovy)) {
+ dirPercentage = dirAngle / (camera.frustum.fovy * 0.5);
+ } else {
+ dirPercentage = dirAngle;
+ }
var distance = Cartesian3.distance(camera.positionWC, camera._changedPosition);
var heightPercentage = distance / camera.positionCartographic.height;
@@ -904,6 +914,13 @@ define([
if (!defined(mode)) {
throw new DeveloperError('mode is required.');
}
+ if (mode === SceneMode.SCENE2D && !(this.frustum instanceof OrthographicOffCenterFrustum)) {
+ throw new DeveloperError('An OrthographicOffCenterFrustum is required in 2D.');
+ }
+ if ((mode === SceneMode.SCENE3D || mode === SceneMode.COLUMBUS_VIEW) &&
+ (!(this.frustum instanceof PerspectiveFrustum) && !(this.frustum instanceof OrthographicFrustum))) {
+ throw new DeveloperError('A PerspectiveFrustum or OrthographicFrustum is required in 3D and Columbus view');
+ }
//>>includeEnd('debug');
var updateFrustum = false;
@@ -917,7 +934,7 @@ define([
var frustum = this._max2Dfrustum = this.frustum.clone();
//>>includeStart('debug', pragmas.debug);
- if (!defined(frustum.left) || !defined(frustum.right) || !defined(frustum.top) || !defined(frustum.bottom)) {
+ if (!(frustum instanceof OrthographicOffCenterFrustum)) {
throw new DeveloperError('The camera frustum is expected to be orthographic for 2D camera control.');
}
//>>includeEnd('debug');
@@ -964,6 +981,46 @@ define([
updateMembers(this);
};
+ var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2();
+ var pickGlobeScratchRay = new Ray();
+ var scratchRayIntersection = new Cartesian3();
+
+ Camera.prototype._adjustOrthographicFrustum = function(zooming) {
+ if (!(this.frustum instanceof OrthographicFrustum)) {
+ return;
+ }
+
+ if (!zooming && this._positionCartographic.height < 150000.0) {
+ return;
+ }
+
+ if (!Matrix4.equals(Matrix4.IDENTITY, this.transform)) {
+ this.frustum.width = Cartesian3.magnitude(this.position);
+ return;
+ }
+
+ var scene = this._scene;
+ var globe = scene._globe;
+ var rayIntersection;
+
+ if (defined(globe)) {
+ var mousePosition = scratchAdjustOrtghographicFrustumMousePosition;
+ mousePosition.x = scene.drawingBufferWidth / 2.0;
+ mousePosition.y = scene.drawingBufferHeight / 2.0;
+
+ var ray = this.getPickRay(mousePosition, pickGlobeScratchRay);
+ rayIntersection = globe.pick(ray, scene, scratchRayIntersection);
+ if (defined(rayIntersection)) {
+ this.frustum.width = Cartesian3.distance(rayIntersection, this.positionWC);
+ }
+ }
+
+ if (!defined(globe) || (!defined(rayIntersection))) {
+ var distance = Math.max(this.positionCartographic.height, 0.0);
+ this.frustum.width = distance;
+ }
+ };
+
var scratchSetViewCartesian = new Cartesian3();
var scratchSetViewTransform1 = new Matrix4();
var scratchSetViewTransform2 = new Matrix4();
@@ -987,6 +1044,8 @@ define([
Cartesian3.cross(camera.direction, camera.up, camera.right);
camera._setTransform(currentTransform);
+
+ camera._adjustOrthographicFrustum(true);
}
function setViewCV(camera, position,hpr, convert) {
@@ -1011,6 +1070,8 @@ define([
Cartesian3.cross(camera.direction, camera.up, camera.right);
camera._setTransform(currentTransform);
+
+ camera._adjustOrthographicFrustum(true);
}
function setView2D(camera, position, hpr, convert) {
@@ -1418,6 +1479,7 @@ define([
if (this._mode === SceneMode.SCENE2D) {
clampMove2D(this, cameraPosition);
}
+ this._adjustOrthographicFrustum(true);
};
/**
@@ -1633,6 +1695,8 @@ define([
Matrix3.multiplyByVector(rotation, this.up, this.up);
Cartesian3.cross(this.direction, this.up, this.right);
Cartesian3.cross(this.right, this.direction, this.up);
+
+ this._adjustOrthographicFrustum(false);
};
/**
@@ -1734,7 +1798,8 @@ define([
var frustum = camera.frustum;
//>>includeStart('debug', pragmas.debug);
- if (!defined(frustum.left) || !defined(frustum.right) || !defined(frustum.top) || !defined(frustum.bottom)) {
+ if (!(frustum instanceof OrthographicOffCenterFrustum) || !defined(frustum.left) || !defined(frustum.right) ||
+ !defined(frustum.bottom) || !defined(frustum.top)) {
throw new DeveloperError('The camera frustum is expected to be orthographic for 2D camera control.');
}
//>>includeEnd('debug');
@@ -1979,6 +2044,8 @@ define([
Cartesian3.normalize(this.right, this.right);
Cartesian3.cross(this.right, this.direction, this.up);
Cartesian3.normalize(this.up, this.up);
+
+ this._adjustOrthographicFrustum(true);
};
var viewRectangle3DCartographic1 = new Cartographic();
@@ -2086,38 +2153,58 @@ define([
Cartesian3.normalize(right, right);
var up = Cartesian3.cross(right, direction, cameraRF.up);
- var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
- var tanTheta = camera.frustum.aspectRatio * tanPhi;
+ var d;
+ if (camera.frustum instanceof OrthographicFrustum) {
+ var width = Math.max(Cartesian3.distance(northEast, northWest), Cartesian3.distance(southEast, southWest));
+ var height = Math.max(Cartesian3.distance(northEast, southEast), Cartesian3.distance(northWest, southWest));
+
+ var rightScalar;
+ var topScalar;
+ var ratio = camera.frustum._offCenterFrustum.right / camera.frustum._offCenterFrustum.top;
+ var heightRatio = height * ratio;
+ if (width > heightRatio) {
+ rightScalar = width;
+ topScalar = rightScalar / ratio;
+ } else {
+ topScalar = height;
+ rightScalar = heightRatio;
+ }
- var d = Math.max(
- computeD(direction, up, northWest, tanPhi),
- computeD(direction, up, southEast, tanPhi),
- computeD(direction, up, northEast, tanPhi),
- computeD(direction, up, southWest, tanPhi),
- computeD(direction, up, northCenter, tanPhi),
- computeD(direction, up, southCenter, tanPhi),
- computeD(direction, right, northWest, tanTheta),
- computeD(direction, right, southEast, tanTheta),
- computeD(direction, right, northEast, tanTheta),
- computeD(direction, right, southWest, tanTheta),
- computeD(direction, right, northCenter, tanTheta),
- computeD(direction, right, southCenter, tanTheta));
-
- // If the rectangle crosses the equator, compute D at the equator, too, because that's the
- // widest part of the rectangle when projected onto the globe.
- if (south < 0 && north > 0) {
- var equatorCartographic = viewRectangle3DCartographic1;
- equatorCartographic.longitude = west;
- equatorCartographic.latitude = 0.0;
- equatorCartographic.height = 0.0;
- var equatorPosition = ellipsoid.cartographicToCartesian(equatorCartographic, viewRectangle3DEquator);
- Cartesian3.subtract(equatorPosition, center, equatorPosition);
- d = Math.max(d, computeD(direction, up, equatorPosition, tanPhi), computeD(direction, right, equatorPosition, tanTheta));
-
- equatorCartographic.longitude = east;
- equatorPosition = ellipsoid.cartographicToCartesian(equatorCartographic, viewRectangle3DEquator);
- Cartesian3.subtract(equatorPosition, center, equatorPosition);
- d = Math.max(d, computeD(direction, up, equatorPosition, tanPhi), computeD(direction, right, equatorPosition, tanTheta));
+ d = Math.max(rightScalar, topScalar);
+ } else {
+ var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
+ var tanTheta = camera.frustum.aspectRatio * tanPhi;
+
+ d = Math.max(
+ computeD(direction, up, northWest, tanPhi),
+ computeD(direction, up, southEast, tanPhi),
+ computeD(direction, up, northEast, tanPhi),
+ computeD(direction, up, southWest, tanPhi),
+ computeD(direction, up, northCenter, tanPhi),
+ computeD(direction, up, southCenter, tanPhi),
+ computeD(direction, right, northWest, tanTheta),
+ computeD(direction, right, southEast, tanTheta),
+ computeD(direction, right, northEast, tanTheta),
+ computeD(direction, right, southWest, tanTheta),
+ computeD(direction, right, northCenter, tanTheta),
+ computeD(direction, right, southCenter, tanTheta));
+
+ // If the rectangle crosses the equator, compute D at the equator, too, because that's the
+ // widest part of the rectangle when projected onto the globe.
+ if (south < 0 && north > 0) {
+ var equatorCartographic = viewRectangle3DCartographic1;
+ equatorCartographic.longitude = west;
+ equatorCartographic.latitude = 0.0;
+ equatorCartographic.height = 0.0;
+ var equatorPosition = ellipsoid.cartographicToCartesian(equatorCartographic, viewRectangle3DEquator);
+ Cartesian3.subtract(equatorPosition, center, equatorPosition);
+ d = Math.max(d, computeD(direction, up, equatorPosition, tanPhi), computeD(direction, right, equatorPosition, tanTheta));
+
+ equatorCartographic.longitude = east;
+ equatorPosition = ellipsoid.cartographicToCartesian(equatorCartographic, viewRectangle3DEquator);
+ Cartesian3.subtract(equatorPosition, center, equatorPosition);
+ d = Math.max(d, computeD(direction, up, equatorPosition, tanPhi), computeD(direction, right, equatorPosition, tanTheta));
+ }
}
return Cartesian3.add(center, Cartesian3.multiplyByScalar(direction, -d, viewRectangle3DEquator), result);
@@ -2147,12 +2234,18 @@ define([
Matrix4.multiplyByPoint(transform, southWest, southWest);
Matrix4.multiplyByPoint(invTransform, southWest, southWest);
- var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
- var tanTheta = camera.frustum.aspectRatio * tanPhi;
-
result.x = (northEast.x - southWest.x) * 0.5 + southWest.x;
result.y = (northEast.y - southWest.y) * 0.5 + southWest.y;
- result.z = Math.max((northEast.x - southWest.x) / tanTheta, (northEast.y - southWest.y) / tanPhi) * 0.5;
+
+ if (defined(camera.frustum.fovy)) {
+ var tanPhi = Math.tan(camera.frustum.fovy * 0.5);
+ var tanTheta = camera.frustum.aspectRatio * tanPhi;
+ result.z = Math.max((northEast.x - southWest.x) / tanTheta, (northEast.y - southWest.y) / tanPhi) * 0.5;
+ } else {
+ var width = northEast.x - southWest.x;
+ var height = northEast.y - southWest.y;
+ result.z = Math.max(width, height);
+ }
return result;
}
@@ -2345,10 +2438,14 @@ define([
var width = canvas.clientWidth;
var height = canvas.clientHeight;
+ var frustum = camera.frustum;
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
var x = (2.0 / width) * windowPosition.x - 1.0;
- x *= (camera.frustum.right - camera.frustum.left) * 0.5;
+ x *= (frustum.right - frustum.left) * 0.5;
var y = (2.0 / height) * (height - windowPosition.y) - 1.0;
- y *= (camera.frustum.top - camera.frustum.bottom) * 0.5;
+ y *= (frustum.top - frustum.bottom) * 0.5;
var origin = result.origin;
Cartesian3.clone(camera.position, origin);
@@ -2360,6 +2457,10 @@ define([
Cartesian3.clone(camera.directionWC, result.direction);
+ if (camera._mode === SceneMode.COLUMBUS_VIEW) {
+ Cartesian3.fromElements(result.origin.z, result.origin.x, result.origin.y, result.origin);
+ }
+
return result;
}
@@ -2687,6 +2788,9 @@ define([
function distanceToBoundingSphere2D(camera, radius) {
var frustum = camera.frustum;
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
var right, top;
var ratio = frustum.right / frustum.top;
@@ -2714,8 +2818,10 @@ define([
var radius = boundingSphere.radius;
if (radius === 0.0) {
offset.range = MINIMUM_ZOOM;
+ } else if (camera.frustum instanceof OrthographicFrustum || camera._mode === SceneMode.SCENE2D) {
+ offset.range = distanceToBoundingSphere2D(camera, radius);
} else {
- offset.range = camera._mode === SceneMode.SCENE2D ? distanceToBoundingSphere2D(camera, radius) : distanceToBoundingSphere3D(camera, radius);
+ offset.range = distanceToBoundingSphere3D(camera, radius);
}
}
diff --git a/Source/Scene/DebugCameraPrimitive.js b/Source/Scene/DebugCameraPrimitive.js
index c282852684cd..90030e73a2f9 100644
--- a/Source/Scene/DebugCameraPrimitive.js
+++ b/Source/Scene/DebugCameraPrimitive.js
@@ -121,10 +121,8 @@ define([
}
if (!defined(this._outlinePrimitive)) {
- var view = this._camera.viewMatrix;
- var projection = this._camera.frustum.projectionMatrix;
- var viewProjection = Matrix4.multiply(projection, view, scratchMatrix);
- var inverseViewProjection = Matrix4.inverse(viewProjection, scratchMatrix);
+ var camera = this._camera;
+ var frustum = camera.frustum;
var frustumSplits = frameState.frustumSplits;
var numFrustums = frustumSplits.length - 1;
@@ -135,20 +133,59 @@ define([
numFrustums = 1;
}
+ var view = this._camera.viewMatrix;
+ var inverseView;
+ var inverseViewProjection;
+ if (defined(camera.frustum.fovy)) {
+ var projection = this._camera.frustum.projectionMatrix;
+ var viewProjection = Matrix4.multiply(projection, view, scratchMatrix);
+ inverseViewProjection = Matrix4.inverse(viewProjection, scratchMatrix);
+ } else {
+ inverseView = Matrix4.inverseTransformation(view, scratchMatrix);
+ }
+
+
var positions = new Float64Array(3 * 4 * (numFrustums + 1));
var f;
for (f = 0; f < numFrustums + 1; ++f) {
for (var i = 0; i < 4; ++i) {
var corner = Cartesian4.clone(frustumCornersNDC[i], scratchFrustumCorners[i]);
- Matrix4.multiplyByVector(inverseViewProjection, corner, corner);
- Cartesian3.divideByScalar(corner, corner.w, corner); // Handle the perspective divide
- Cartesian3.subtract(corner, this._camera.positionWC, corner);
- Cartesian3.normalize(corner, corner);
-
- var fac = Cartesian3.dot(this._camera.directionWC, corner);
- Cartesian3.multiplyByScalar(corner, frustumSplits[f] / fac, corner);
- Cartesian3.add(corner, this._camera.positionWC, corner);
+ var worldCoords;
+ if (!defined(inverseViewProjection)) {
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
+
+ var near;
+ var far;
+ if (f === numFrustums) {
+ near = frustumSplits[f - 1];
+ far = frustumSplits[f];
+ } else {
+ near = frustumSplits[f];
+ far = frustumSplits[f + 1];
+ }
+ corner.x = (corner.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5;
+ corner.y = (corner.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5;
+ corner.z = (corner.z * (near - far) - near - far) * 0.5;
+ corner.w = 1.0;
+
+ worldCoords = Matrix4.multiplyByVector(inverseView, corner, corner);
+ } else {
+ corner = Matrix4.multiplyByVector(inverseViewProjection, corner, corner);
+
+ // Reverse perspective divide
+ var w = 1.0 / corner.w;
+ Cartesian3.multiplyByScalar(corner, w, corner);
+
+ Cartesian3.subtract(corner, this._camera.positionWC, corner);
+ Cartesian3.normalize(corner, corner);
+
+ var fac = Cartesian3.dot(this._camera.directionWC, corner);
+ Cartesian3.multiplyByScalar(corner, frustumSplits[f] / fac, corner);
+ Cartesian3.add(corner, this._camera.positionWC, corner);
+ }
positions[12 * f + i * 3] = corner.x;
positions[12 * f + i * 3 + 1] = corner.y;
diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js
index 7bec9532d203..15b234d32be3 100644
--- a/Source/Scene/GlobeSurfaceTileProvider.js
+++ b/Source/Scene/GlobeSurfaceTileProvider.js
@@ -39,6 +39,7 @@ define([
'../Scene/Primitive',
'./GlobeSurfaceTile',
'./ImageryLayer',
+ './OrthographicFrustum',
'./QuadtreeTileLoadState',
'./SceneMode',
'./ShadowMode'
@@ -82,6 +83,7 @@ define([
Primitive,
GlobeSurfaceTile,
ImageryLayer,
+ OrthographicFrustum,
QuadtreeTileLoadState,
SceneMode,
ShadowMode) {
@@ -502,7 +504,8 @@ define([
return Visibility.NONE;
}
- if (frameState.mode === SceneMode.SCENE3D) {
+ var ortho3D = frameState.mode === SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum;
+ if (frameState.mode === SceneMode.SCENE3D && !ortho3D) {
var occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace;
if (!defined(occludeePointInScaledSpace)) {
return intersection;
diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js
index 9c709b191809..5271d1daa5dd 100644
--- a/Source/Scene/OrthographicFrustum.js
+++ b/Source/Scene/OrthographicFrustum.js
@@ -1,20 +1,16 @@
/*global define*/
define([
- '../Core/Cartesian3',
- '../Core/Cartesian4',
'../Core/defined',
'../Core/defineProperties',
+ '../Core/deprecationWarning',
'../Core/DeveloperError',
- '../Core/Matrix4',
- './CullingVolume'
+ './OrthographicOffCenterFrustum'
], function(
- Cartesian3,
- Cartesian4,
defined,
defineProperties,
+ deprecationWarning,
DeveloperError,
- Matrix4,
- CullingVolume) {
+ OrthographicOffCenterFrustum) {
'use strict';
/**
@@ -23,13 +19,13 @@ define([
* define the unit vector normal to the plane, and the w component is the distance of the
* plane from the origin/camera position.
*
- * @alias OrthographicFrustum
+ * @alias OrthographicOffCenterFrustum
* @constructor
*
* @example
* var maxRadii = ellipsoid.maximumRadius;
*
- * var frustum = new Cesium.OrthographicFrustum();
+ * var frustum = new Cesium.OrthographicOffCenterFrustum();
* frustum.right = maxRadii * Cesium.Math.PI;
* frustum.left = -c.frustum.right;
* frustum.top = c.frustum.right * (canvas.clientHeight / canvas.clientWidth);
@@ -38,37 +34,13 @@ define([
* frustum.far = 50.0 * maxRadii;
*/
function OrthographicFrustum() {
- /**
- * The left clipping plane.
- * @type {Number}
- * @default undefined
- */
- this.left = undefined;
- this._left = undefined;
+ this._offCenterFrustum = new OrthographicOffCenterFrustum();
- /**
- * The right clipping plane.
- * @type {Number}
- * @default undefined
- */
- this.right = undefined;
- this._right = undefined;
-
- /**
- * The top clipping plane.
- * @type {Number}
- * @default undefined
- */
- this.top = undefined;
- this._top = undefined;
+ this.width = undefined;
+ this._width = undefined;
- /**
- * The bottom clipping plane.
- * @type {Number}
- * @default undefined
- */
- this.bottom = undefined;
- this._bottom = undefined;
+ this.aspectRatio = undefined;
+ this._aspectRatio = undefined;
/**
* The distance of the near plane.
@@ -86,42 +58,47 @@ define([
this.far = 500000000.0;
this._far = this.far;
- this._cullingVolume = new CullingVolume();
- this._orthographicMatrix = new Matrix4();
+ this._useDeprecated = false;
}
function update(frustum) {
//>>includeStart('debug', pragmas.debug);
- if (!defined(frustum.right) || !defined(frustum.left) ||
- !defined(frustum.top) || !defined(frustum.bottom) ||
- !defined(frustum.near) || !defined(frustum.far)) {
- throw new DeveloperError('right, left, top, bottom, near, or far parameters are not set.');
+ if (!defined(frustum.width) || !defined(frustum.aspectRatio) || !defined(frustum.near) || !defined(frustum.far)) {
+ throw new DeveloperError('width, aspectRatio, near, or far parameters are not set.');
}
//>>includeEnd('debug');
- if (frustum.top !== frustum._top || frustum.bottom !== frustum._bottom ||
- frustum.left !== frustum._left || frustum.right !== frustum._right ||
- frustum.near !== frustum._near || frustum.far !== frustum._far) {
+ var f = frustum._offCenterFrustum;
+ if (frustum.width !== frustum._width || frustum.aspectRatio !== frustum._aspectRatio ||
+ frustum.near !== frustum._near || frustum.far !== frustum._far) {
//>>includeStart('debug', pragmas.debug);
- if (frustum.left > frustum.right) {
- throw new DeveloperError('right must be greater than left.');
- }
- if (frustum.bottom > frustum.top) {
- throw new DeveloperError('top must be greater than bottom.');
+ if (frustum.aspectRatio < 0) {
+ throw new DeveloperError('aspectRatio must be positive.');
}
- if (frustum.near <= 0 || frustum.near > frustum.far) {
+ if (frustum.near < 0 || frustum.near > frustum.far) {
throw new DeveloperError('near must be greater than zero and less than far.');
}
//>>includeEnd('debug');
- frustum._left = frustum.left;
- frustum._right = frustum.right;
- frustum._top = frustum.top;
- frustum._bottom = frustum.bottom;
+ frustum._aspectRatio = frustum.aspectRatio;
+ frustum._width = frustum.width;
frustum._near = frustum.near;
frustum._far = frustum.far;
- frustum._orthographicMatrix = Matrix4.computeOrthographicOffCenter(frustum.left, frustum.right, frustum.bottom, frustum.top, frustum.near, frustum.far, frustum._orthographicMatrix);
+
+ if (!frustum._useDeprecated) {
+ var ratio = frustum.aspectRatio;
+ if (ratio > 1.0) {
+ ratio = 1.0 / frustum.aspectRatio;
+ }
+
+ f.right = frustum.width * 0.5;
+ f.left = -f.right;
+ f.top = ratio * f.right;
+ f.bottom = -f.top;
+ f.near = frustum.near;
+ f.far = frustum.far;
+ }
}
}
@@ -135,16 +112,79 @@ define([
projectionMatrix : {
get : function() {
update(this);
- return this._orthographicMatrix;
+ return this._offCenterFrustum.projectionMatrix;
+ }
+ },
+
+ /**
+ * The left clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ left : {
+ get : function() {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ return this._offCenterFrustum.left;
+ },
+ set : function(value) {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ this._useDeprecated = true;
+ this._offCenterFrustum.left = value;
+ }
+ },
+
+ /**
+ * The right clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ right : {
+ get : function() {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ return this._offCenterFrustum.right;
+ },
+ set : function(value) {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ this._useDeprecated = true;
+ this._offCenterFrustum.right = value;
+ }
+ },
+
+ /**
+ * The top clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ top : {
+ get : function() {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ return this._offCenterFrustum.top;
+ },
+ set : function(value) {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ this._useDeprecated = true;
+ this._offCenterFrustum.top = value;
+ }
+ },
+
+ /**
+ * The bottom clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ bottom : {
+ get : function() {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ return this._offCenterFrustum.bottom;
+ },
+ set : function(value) {
+ deprecationWarning('OrthographicFrustum', 'OrthographicFrustum left, right, bottom and top properties were deprecated in 1.32 and will be removed in 1.33.');
+ this._useDeprecated = true;
+ this._offCenterFrustum.bottom = value;
}
}
});
- var getPlanesRight = new Cartesian3();
- var getPlanesNearCenter = new Cartesian3();
- var getPlanesPoint = new Cartesian3();
- var negateScratch = new Cartesian3();
-
/**
* Creates a culling volume for this frustum.
*
@@ -159,109 +199,8 @@ define([
* var intersect = cullingVolume.computeVisibility(boundingVolume);
*/
OrthographicFrustum.prototype.computeCullingVolume = function(position, direction, up) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(position)) {
- throw new DeveloperError('position is required.');
- }
- if (!defined(direction)) {
- throw new DeveloperError('direction is required.');
- }
- if (!defined(up)) {
- throw new DeveloperError('up is required.');
- }
- //>>includeEnd('debug');
-
- var planes = this._cullingVolume.planes;
- var t = this.top;
- var b = this.bottom;
- var r = this.right;
- var l = this.left;
- var n = this.near;
- var f = this.far;
-
- var right = Cartesian3.cross(direction, up, getPlanesRight);
- var nearCenter = getPlanesNearCenter;
- Cartesian3.multiplyByScalar(direction, n, nearCenter);
- Cartesian3.add(position, nearCenter, nearCenter);
-
- var point = getPlanesPoint;
-
- // Left plane
- Cartesian3.multiplyByScalar(right, l, point);
- Cartesian3.add(nearCenter, point, point);
-
- var plane = planes[0];
- if (!defined(plane)) {
- plane = planes[0] = new Cartesian4();
- }
- plane.x = right.x;
- plane.y = right.y;
- plane.z = right.z;
- plane.w = -Cartesian3.dot(right, point);
-
- // Right plane
- Cartesian3.multiplyByScalar(right, r, point);
- Cartesian3.add(nearCenter, point, point);
-
- plane = planes[1];
- if (!defined(plane)) {
- plane = planes[1] = new Cartesian4();
- }
- plane.x = -right.x;
- plane.y = -right.y;
- plane.z = -right.z;
- plane.w = -Cartesian3.dot(Cartesian3.negate(right, negateScratch), point);
-
- // Bottom plane
- Cartesian3.multiplyByScalar(up, b, point);
- Cartesian3.add(nearCenter, point, point);
-
- plane = planes[2];
- if (!defined(plane)) {
- plane = planes[2] = new Cartesian4();
- }
- plane.x = up.x;
- plane.y = up.y;
- plane.z = up.z;
- plane.w = -Cartesian3.dot(up, point);
-
- // Top plane
- Cartesian3.multiplyByScalar(up, t, point);
- Cartesian3.add(nearCenter, point, point);
-
- plane = planes[3];
- if (!defined(plane)) {
- plane = planes[3] = new Cartesian4();
- }
- plane.x = -up.x;
- plane.y = -up.y;
- plane.z = -up.z;
- plane.w = -Cartesian3.dot(Cartesian3.negate(up, negateScratch), point);
-
- // Near plane
- plane = planes[4];
- if (!defined(plane)) {
- plane = planes[4] = new Cartesian4();
- }
- plane.x = direction.x;
- plane.y = direction.y;
- plane.z = direction.z;
- plane.w = -Cartesian3.dot(direction, nearCenter);
-
- // Far plane
- Cartesian3.multiplyByScalar(direction, f, point);
- Cartesian3.add(position, point, point);
-
- plane = planes[5];
- if (!defined(plane)) {
- plane = planes[5] = new Cartesian4();
- }
- plane.x = -direction.x;
- plane.y = -direction.y;
- plane.z = -direction.z;
- plane.w = -Cartesian3.dot(Cartesian3.negate(direction, negateScratch), point);
-
- return this._cullingVolume;
+ update(this);
+ return this._offCenterFrustum.computeCullingVolume(position, direction, up);
};
/**
@@ -283,61 +222,33 @@ define([
*/
OrthographicFrustum.prototype.getPixelDimensions = function(drawingBufferWidth, drawingBufferHeight, distance, result) {
update(this);
-
- //>>includeStart('debug', pragmas.debug);
- if (!defined(drawingBufferWidth) || !defined(drawingBufferHeight)) {
- throw new DeveloperError('Both drawingBufferWidth and drawingBufferHeight are required.');
- }
- if (drawingBufferWidth <= 0) {
- throw new DeveloperError('drawingBufferWidth must be greater than zero.');
- }
- if (drawingBufferHeight <= 0) {
- throw new DeveloperError('drawingBufferHeight must be greater than zero.');
- }
- if (!defined(distance)) {
- throw new DeveloperError('distance is required.');
- }
- if (!defined(result)) {
- throw new DeveloperError('A result object is required.');
- }
- //>>includeEnd('debug');
-
- var frustumWidth = this.right - this.left;
- var frustumHeight = this.top - this.bottom;
- var pixelWidth = frustumWidth / drawingBufferWidth;
- var pixelHeight = frustumHeight / drawingBufferHeight;
-
- result.x = pixelWidth;
- result.y = pixelHeight;
- return result;
+ return this._offCenterFrustum.getPixelDimensions(drawingBufferWidth, drawingBufferHeight, distance, result);
};
/**
* Returns a duplicate of a OrthographicFrustum instance.
*
* @param {OrthographicFrustum} [result] The object onto which to store the result.
- * @returns {OrthographicFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided.
+ * @returns {OrthographicFrustum} The modified result parameter or a new OrthographicFrustum instance if one was not provided.
*/
OrthographicFrustum.prototype.clone = function(result) {
if (!defined(result)) {
result = new OrthographicFrustum();
}
- result.left = this.left;
- result.right = this.right;
- result.top = this.top;
- result.bottom = this.bottom;
+ result.aspectRatio = this.aspectRatio;
+ result.width = this.width;
result.near = this.near;
result.far = this.far;
// force update of clone to compute matrices
- result._left = undefined;
- result._right = undefined;
- result._top = undefined;
- result._bottom = undefined;
+ result._aspectRatio = undefined;
+ result._width = undefined;
result._near = undefined;
result._far = undefined;
+ this._offCenterFrustum.clone(result._offCenterFrustum);
+
return result;
};
@@ -349,13 +260,18 @@ define([
* @returns {Boolean} true
if they are equal, false
otherwise.
*/
OrthographicFrustum.prototype.equals = function(other) {
- return (defined(other) &&
- this.right === other.right &&
- this.left === other.left &&
- this.top === other.top &&
- this.bottom === other.bottom &&
+ if (!defined(other)) {
+ return false;
+ }
+
+ update(this);
+ update(other);
+
+ return (this.width === other.width &&
+ this.aspectRatio === other.aspectRatio &&
this.near === other.near &&
- this.far === other.far);
+ this.far === other.far &&
+ this._offCenterFrustum.equals(other._offCenterFrustum));
};
return OrthographicFrustum;
diff --git a/Source/Scene/OrthographicOffCenterFrustum.js b/Source/Scene/OrthographicOffCenterFrustum.js
new file mode 100644
index 000000000000..d14053b6ec51
--- /dev/null
+++ b/Source/Scene/OrthographicOffCenterFrustum.js
@@ -0,0 +1,362 @@
+/*global define*/
+define([
+ '../Core/Cartesian3',
+ '../Core/Cartesian4',
+ '../Core/defined',
+ '../Core/defineProperties',
+ '../Core/DeveloperError',
+ '../Core/Matrix4',
+ './CullingVolume'
+ ], function(
+ Cartesian3,
+ Cartesian4,
+ defined,
+ defineProperties,
+ DeveloperError,
+ Matrix4,
+ CullingVolume) {
+ 'use strict';
+
+ /**
+ * The viewing frustum is defined by 6 planes.
+ * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components
+ * define the unit vector normal to the plane, and the w component is the distance of the
+ * plane from the origin/camera position.
+ *
+ * @alias OrthographicOffCenterFrustum
+ * @constructor
+ *
+ * @example
+ * var maxRadii = ellipsoid.maximumRadius;
+ *
+ * var frustum = new Cesium.OrthographicOffCenterFrustum();
+ * frustum.right = maxRadii * Cesium.Math.PI;
+ * frustum.left = -c.frustum.right;
+ * frustum.top = c.frustum.right * (canvas.clientHeight / canvas.clientWidth);
+ * frustum.bottom = -c.frustum.top;
+ * frustum.near = 0.01 * maxRadii;
+ * frustum.far = 50.0 * maxRadii;
+ */
+ function OrthographicOffCenterFrustum() {
+ /**
+ * The left clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ this.left = undefined;
+ this._left = undefined;
+
+ /**
+ * The right clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ this.right = undefined;
+ this._right = undefined;
+
+ /**
+ * The top clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ this.top = undefined;
+ this._top = undefined;
+
+ /**
+ * The bottom clipping plane.
+ * @type {Number}
+ * @default undefined
+ */
+ this.bottom = undefined;
+ this._bottom = undefined;
+
+ /**
+ * The distance of the near plane.
+ * @type {Number}
+ * @default 1.0
+ */
+ this.near = 1.0;
+ this._near = this.near;
+
+ /**
+ * The distance of the far plane.
+ * @type {Number}
+ * @default 500000000.0;
+ */
+ this.far = 500000000.0;
+ this._far = this.far;
+
+ this._cullingVolume = new CullingVolume();
+ this._orthographicMatrix = new Matrix4();
+ }
+
+ function update(frustum) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(frustum.right) || !defined(frustum.left) ||
+ !defined(frustum.top) || !defined(frustum.bottom) ||
+ !defined(frustum.near) || !defined(frustum.far)) {
+ throw new DeveloperError('right, left, top, bottom, near, or far parameters are not set.');
+ }
+ //>>includeEnd('debug');
+
+ if (frustum.top !== frustum._top || frustum.bottom !== frustum._bottom ||
+ frustum.left !== frustum._left || frustum.right !== frustum._right ||
+ frustum.near !== frustum._near || frustum.far !== frustum._far) {
+
+ //>>includeStart('debug', pragmas.debug);
+ if (frustum.left > frustum.right) {
+ throw new DeveloperError('right must be greater than left.');
+ }
+ if (frustum.bottom > frustum.top) {
+ throw new DeveloperError('top must be greater than bottom.');
+ }
+ if (frustum.near <= 0 || frustum.near > frustum.far) {
+ throw new DeveloperError('near must be greater than zero and less than far.');
+ }
+ //>>includeEnd('debug');
+
+ frustum._left = frustum.left;
+ frustum._right = frustum.right;
+ frustum._top = frustum.top;
+ frustum._bottom = frustum.bottom;
+ frustum._near = frustum.near;
+ frustum._far = frustum.far;
+ frustum._orthographicMatrix = Matrix4.computeOrthographicOffCenter(frustum.left, frustum.right, frustum.bottom, frustum.top, frustum.near, frustum.far, frustum._orthographicMatrix);
+ }
+ }
+
+ defineProperties(OrthographicOffCenterFrustum.prototype, {
+ /**
+ * Gets the orthographic projection matrix computed from the view frustum.
+ * @memberof OrthographicOffCenterFrustum.prototype
+ * @type {Matrix4}
+ * @readonly
+ */
+ projectionMatrix : {
+ get : function() {
+ update(this);
+ return this._orthographicMatrix;
+ }
+ }
+ });
+
+ var getPlanesRight = new Cartesian3();
+ var getPlanesNearCenter = new Cartesian3();
+ var getPlanesPoint = new Cartesian3();
+ var negateScratch = new Cartesian3();
+
+ /**
+ * Creates a culling volume for this frustum.
+ *
+ * @param {Cartesian3} position The eye position.
+ * @param {Cartesian3} direction The view direction.
+ * @param {Cartesian3} up The up direction.
+ * @returns {CullingVolume} A culling volume at the given position and orientation.
+ *
+ * @example
+ * // Check if a bounding volume intersects the frustum.
+ * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp);
+ * var intersect = cullingVolume.computeVisibility(boundingVolume);
+ */
+ OrthographicOffCenterFrustum.prototype.computeCullingVolume = function(position, direction, up) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(position)) {
+ throw new DeveloperError('position is required.');
+ }
+ if (!defined(direction)) {
+ throw new DeveloperError('direction is required.');
+ }
+ if (!defined(up)) {
+ throw new DeveloperError('up is required.');
+ }
+ //>>includeEnd('debug');
+
+ var planes = this._cullingVolume.planes;
+ var t = this.top;
+ var b = this.bottom;
+ var r = this.right;
+ var l = this.left;
+ var n = this.near;
+ var f = this.far;
+
+ var right = Cartesian3.cross(direction, up, getPlanesRight);
+ var nearCenter = getPlanesNearCenter;
+ Cartesian3.multiplyByScalar(direction, n, nearCenter);
+ Cartesian3.add(position, nearCenter, nearCenter);
+
+ var point = getPlanesPoint;
+
+ // Left plane
+ Cartesian3.multiplyByScalar(right, l, point);
+ Cartesian3.add(nearCenter, point, point);
+
+ var plane = planes[0];
+ if (!defined(plane)) {
+ plane = planes[0] = new Cartesian4();
+ }
+ plane.x = right.x;
+ plane.y = right.y;
+ plane.z = right.z;
+ plane.w = -Cartesian3.dot(right, point);
+
+ // Right plane
+ Cartesian3.multiplyByScalar(right, r, point);
+ Cartesian3.add(nearCenter, point, point);
+
+ plane = planes[1];
+ if (!defined(plane)) {
+ plane = planes[1] = new Cartesian4();
+ }
+ plane.x = -right.x;
+ plane.y = -right.y;
+ plane.z = -right.z;
+ plane.w = -Cartesian3.dot(Cartesian3.negate(right, negateScratch), point);
+
+ // Bottom plane
+ Cartesian3.multiplyByScalar(up, b, point);
+ Cartesian3.add(nearCenter, point, point);
+
+ plane = planes[2];
+ if (!defined(plane)) {
+ plane = planes[2] = new Cartesian4();
+ }
+ plane.x = up.x;
+ plane.y = up.y;
+ plane.z = up.z;
+ plane.w = -Cartesian3.dot(up, point);
+
+ // Top plane
+ Cartesian3.multiplyByScalar(up, t, point);
+ Cartesian3.add(nearCenter, point, point);
+
+ plane = planes[3];
+ if (!defined(plane)) {
+ plane = planes[3] = new Cartesian4();
+ }
+ plane.x = -up.x;
+ plane.y = -up.y;
+ plane.z = -up.z;
+ plane.w = -Cartesian3.dot(Cartesian3.negate(up, negateScratch), point);
+
+ // Near plane
+ plane = planes[4];
+ if (!defined(plane)) {
+ plane = planes[4] = new Cartesian4();
+ }
+ plane.x = direction.x;
+ plane.y = direction.y;
+ plane.z = direction.z;
+ plane.w = -Cartesian3.dot(direction, nearCenter);
+
+ // Far plane
+ Cartesian3.multiplyByScalar(direction, f, point);
+ Cartesian3.add(position, point, point);
+
+ plane = planes[5];
+ if (!defined(plane)) {
+ plane = planes[5] = new Cartesian4();
+ }
+ plane.x = -direction.x;
+ plane.y = -direction.y;
+ plane.z = -direction.z;
+ plane.w = -Cartesian3.dot(Cartesian3.negate(direction, negateScratch), point);
+
+ return this._cullingVolume;
+ };
+
+ /**
+ * Returns the pixel's width and height in meters.
+ *
+ * @param {Number} drawingBufferWidth The width of the drawing buffer.
+ * @param {Number} drawingBufferHeight The height of the drawing buffer.
+ * @param {Number} distance The distance to the near plane in meters.
+ * @param {Cartesian2} result The object onto which to store the result.
+ * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively.
+ *
+ * @exception {DeveloperError} drawingBufferWidth must be greater than zero.
+ * @exception {DeveloperError} drawingBufferHeight must be greater than zero.
+ *
+ * @example
+ * // Example 1
+ * // Get the width and height of a pixel.
+ * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 0.0, new Cesium.Cartesian2());
+ */
+ OrthographicOffCenterFrustum.prototype.getPixelDimensions = function(drawingBufferWidth, drawingBufferHeight, distance, result) {
+ update(this);
+
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(drawingBufferWidth) || !defined(drawingBufferHeight)) {
+ throw new DeveloperError('Both drawingBufferWidth and drawingBufferHeight are required.');
+ }
+ if (drawingBufferWidth <= 0) {
+ throw new DeveloperError('drawingBufferWidth must be greater than zero.');
+ }
+ if (drawingBufferHeight <= 0) {
+ throw new DeveloperError('drawingBufferHeight must be greater than zero.');
+ }
+ if (!defined(distance)) {
+ throw new DeveloperError('distance is required.');
+ }
+ if (!defined(result)) {
+ throw new DeveloperError('A result object is required.');
+ }
+ //>>includeEnd('debug');
+
+ var frustumWidth = this.right - this.left;
+ var frustumHeight = this.top - this.bottom;
+ var pixelWidth = frustumWidth / drawingBufferWidth;
+ var pixelHeight = frustumHeight / drawingBufferHeight;
+
+ result.x = pixelWidth;
+ result.y = pixelHeight;
+ return result;
+ };
+
+ /**
+ * Returns a duplicate of a OrthographicOffCenterFrustum instance.
+ *
+ * @param {OrthographicOffCenterFrustum} [result] The object onto which to store the result.
+ * @returns {OrthographicOffCenterFrustum} The modified result parameter or a new OrthographicOffCenterFrustum instance if one was not provided.
+ */
+ OrthographicOffCenterFrustum.prototype.clone = function(result) {
+ if (!defined(result)) {
+ result = new OrthographicOffCenterFrustum();
+ }
+
+ result.left = this.left;
+ result.right = this.right;
+ result.top = this.top;
+ result.bottom = this.bottom;
+ result.near = this.near;
+ result.far = this.far;
+
+ // force update of clone to compute matrices
+ result._left = undefined;
+ result._right = undefined;
+ result._top = undefined;
+ result._bottom = undefined;
+ result._near = undefined;
+ result._far = undefined;
+
+ return result;
+ };
+
+ /**
+ * Compares the provided OrthographicOffCenterFrustum componentwise and returns
+ * true
if they are equal, false
otherwise.
+ *
+ * @param {OrthographicOffCenterFrustum} [other] The right hand side OrthographicOffCenterFrustum.
+ * @returns {Boolean} true
if they are equal, false
otherwise.
+ */
+ OrthographicOffCenterFrustum.prototype.equals = function(other) {
+ return (defined(other) &&
+ this.right === other.right &&
+ this.left === other.left &&
+ this.top === other.top &&
+ this.bottom === other.bottom &&
+ this.near === other.near &&
+ this.far === other.far);
+ };
+
+ return OrthographicOffCenterFrustum;
+});
diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js
index 78c09c7be8a4..634e98218253 100644
--- a/Source/Scene/QuadtreePrimitive.js
+++ b/Source/Scene/QuadtreePrimitive.js
@@ -12,6 +12,7 @@ define([
'../Core/Ray',
'../Core/Rectangle',
'../Core/Visibility',
+ './OrthographicFrustum',
'./QuadtreeOccluders',
'./QuadtreeTile',
'./QuadtreeTileLoadState',
@@ -30,6 +31,7 @@ define([
Ray,
Rectangle,
Visibility,
+ OrthographicFrustum,
QuadtreeOccluders,
QuadtreeTile,
QuadtreeTileLoadState,
@@ -667,7 +669,7 @@ define([
}
function screenSpaceError(primitive, frameState, tile) {
- if (frameState.mode === SceneMode.SCENE2D) {
+ if (frameState.mode === SceneMode.SCENE2D || frameState.camera.frustum instanceof OrthographicFrustum) {
return screenSpaceError2D(primitive, frameState, tile);
}
@@ -689,6 +691,9 @@ define([
function screenSpaceError2D(primitive, frameState, tile) {
var camera = frameState.camera;
var frustum = camera.frustum;
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
var context = frameState.context;
var width = context.drawingBufferWidth;
@@ -696,7 +701,13 @@ define([
var maxGeometricError = primitive._tileProvider.getLevelMaximumGeometricError(tile.level);
var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height);
- return maxGeometricError / pixelSize;
+ var error = maxGeometricError / pixelSize;
+
+ if (frameState.fog.enabled && frameState.mode !== SceneMode.SCENE2D) {
+ error = error - CesiumMath.fog(tile._distance, frameState.fog.density) * frameState.fog.sse;
+ }
+
+ return error;
}
function addTileToRenderList(primitive, tile) {
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index e32dcc4afe31..29197780a636 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -58,6 +58,7 @@ define([
'./MapMode2D',
'./OIT',
'./OrthographicFrustum',
+ './OrthographicOffCenterFrustum',
'./PerformanceDisplay',
'./PerInstanceColorAppearance',
'./PerspectiveFrustum',
@@ -131,6 +132,7 @@ define([
MapMode2D,
OIT,
OrthographicFrustum,
+ OrthographicOffCenterFrustum,
PerformanceDisplay,
PerInstanceColorAppearance,
PerspectiveFrustum,
@@ -1093,6 +1095,11 @@ define([
return this._useWebVR;
},
set : function(value) {
+ //>>includeStart('debug', pragmas.debug);
+ if (this.camera.frustum instanceof OrthographicFrustum) {
+ throw new DeveloperError('VR is unsupported with an orthographic projection.');
+ }
+ //>>includeEnd('debug');
this._useWebVR = value;
if (this._useWebVR) {
this._frameState.creditDisplay.container.style.visibility = 'hidden';
@@ -1735,6 +1742,7 @@ define([
var scratchPerspectiveFrustum = new PerspectiveFrustum();
var scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum();
var scratchOrthographicFrustum = new OrthographicFrustum();
+ var scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum();
function executeCommands(scene, passState) {
var camera = scene._camera;
@@ -1749,8 +1757,10 @@ define([
frustum = camera.frustum.clone(scratchPerspectiveFrustum);
} else if (defined(camera.frustum.infiniteProjectionMatrix)){
frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum);
- } else {
+ } else if (defined(camera.frustum.width)) {
frustum = camera.frustum.clone(scratchOrthographicFrustum);
+ } else {
+ frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum);
}
// Ideally, we would render the sky box and atmosphere last for
@@ -2254,18 +2264,27 @@ define([
// Update celestial and terrestrial environment effects.
var environmentState = scene._environmentState;
var renderPass = frameState.passes.render;
- environmentState.skyBoxCommand = (renderPass && defined(scene.skyBox)) ? scene.skyBox.update(frameState) : undefined;
var skyAtmosphere = scene.skyAtmosphere;
var globe = scene.globe;
- if (defined(skyAtmosphere) && defined(globe)) {
- skyAtmosphere.setDynamicAtmosphereColor(globe.enableLighting);
- environmentState.isReadyForAtmosphere = environmentState.isReadyForAtmosphere || globe._surface._tilesToRender.length > 0;
+
+ if (!renderPass || (scene._mode !== SceneMode.SCENE2D && frameState.camera.frustum instanceof OrthographicFrustum)) {
+ environmentState.skyAtmosphereCommand = undefined;
+ environmentState.skyBoxCommand = undefined;
+ environmentState.sunDrawCommand = undefined;
+ environmentState.sunComputeCommand = undefined;
+ environmentState.moonCommand = undefined;
+ } else {
+ if (defined(skyAtmosphere) && defined(globe)) {
+ skyAtmosphere.setDynamicAtmosphereColor(globe.enableLighting);
+ environmentState.isReadyForAtmosphere = environmentState.isReadyForAtmosphere || globe._surface._tilesToRender.length > 0;
+ }
+ environmentState.skyAtmosphereCommand = defined(skyAtmosphere) ? skyAtmosphere.update(frameState) : undefined;
+ environmentState.skyBoxCommand = defined(scene.skyBox) ? scene.skyBox.update(frameState) : undefined;
+ var sunCommands = defined(scene.sun) ? scene.sun.update(scene) : undefined;
+ environmentState.sunDrawCommand = defined(sunCommands) ? sunCommands.drawCommand : undefined;
+ environmentState.sunComputeCommand = defined(sunCommands) ? sunCommands.computeCommand : undefined;
+ environmentState.moonCommand = defined(scene.moon) ? scene.moon.update(frameState) : undefined;
}
- environmentState.skyAtmosphereCommand = (renderPass && defined(skyAtmosphere)) ? skyAtmosphere.update(frameState) : undefined;
- var sunCommands = (renderPass && defined(scene.sun)) ? scene.sun.update(scene) : undefined;
- environmentState.sunDrawCommand = defined(sunCommands) ? sunCommands.drawCommand : undefined;
- environmentState.sunComputeCommand = defined(sunCommands) ? sunCommands.computeCommand : undefined;
- environmentState.moonCommand = (renderPass && defined(scene.moon)) ? scene.moon.update(frameState) : undefined;
var clearGlobeDepth = environmentState.clearGlobeDepth = defined(globe) && (!globe.depthTestAgainstTerrain || scene.mode === SceneMode.SCENE2D);
var useDepthPlane = environmentState.useDepthPlane = clearGlobeDepth && scene.mode === SceneMode.SCENE3D;
@@ -2611,7 +2630,7 @@ define([
return Math.max(ContextLimits.minimumAliasedLineWidth, Math.min(width, ContextLimits.maximumAliasedLineWidth));
};
- var orthoPickingFrustum = new OrthographicFrustum();
+ var orthoPickingFrustum = new OrthographicOffCenterFrustum();
var scratchOrigin = new Cartesian3();
var scratchDirection = new Cartesian3();
var scratchPixelSize = new Cartesian2();
@@ -2620,6 +2639,9 @@ define([
function getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height) {
var camera = scene._camera;
var frustum = camera.frustum;
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
var viewport = scene._passState.viewport;
var x = 2.0 * (drawingBufferPosition.x - viewport.x) / viewport.width - 1.0;
@@ -2638,7 +2660,9 @@ define([
camera._setTransform(transform);
- Cartesian3.fromElements(origin.z, origin.x, origin.y, origin);
+ if (scene.mode === SceneMode.SCENE2D) {
+ Cartesian3.fromElements(origin.z, origin.x, origin.y, origin);
+ }
var pixelSize = frustum.getPixelDimensions(viewport.width, viewport.height, 1.0, scratchPixelSize);
@@ -2686,7 +2710,8 @@ define([
}
function getPickCullingVolume(scene, drawingBufferPosition, width, height) {
- if (scene._mode === SceneMode.SCENE2D) {
+ var frustum = scene.camera.frustum;
+ if (frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum) {
return getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height);
}
@@ -2942,8 +2967,10 @@ define([
frustum = camera.frustum.clone(scratchPerspectiveFrustum);
} else if (defined(camera.frustum.infiniteProjectionMatrix)){
frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum);
- } else {
+ } else if (defined(camera.frustum.width)) {
frustum = camera.frustum.clone(scratchOrthographicFrustum);
+ } else {
+ frustum = camera.frustum.clone(scratchOrthographicOffCenterFrustum);
}
var numFrustums = this.numberOfFrustums;
diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js
index afe452a05399..9156130c26f8 100644
--- a/Source/Scene/SceneTransforms.js
+++ b/Source/Scene/SceneTransforms.js
@@ -10,6 +10,8 @@ define([
'../Core/Math',
'../Core/Matrix4',
'../Core/Transforms',
+ './OrthographicFrustum',
+ './OrthographicOffCenterFrustum',
'./SceneMode'
], function(
BoundingRectangle,
@@ -22,6 +24,8 @@ define([
CesiumMath,
Matrix4,
Transforms,
+ OrthographicFrustum,
+ OrthographicOffCenterFrustum,
SceneMode) {
'use strict';
@@ -184,7 +188,7 @@ define([
if (frameState.mode !== SceneMode.SCENE2D || cameraCentered) {
// View-projection matrix to transform from world coordinates to clip coordinates
positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC);
- if (positionCC.z < 0 && frameState.mode !== SceneMode.SCENE2D) {
+ if (positionCC.z < 0 && !(camera.frustum instanceof OrthographicFrustum) && !(camera.frustum instanceof OrthographicOffCenterFrustum)) {
return undefined;
}
@@ -279,20 +283,6 @@ define([
return Cartesian2.fromCartesian3(positionWC, result);
};
- /**
- * @private
- */
- SceneTransforms.clipToDrawingBufferCoordinates = function(viewport, position, result) {
- // Perspective divide to transform from clip coordinates to normalized device coordinates
- Cartesian3.divideByScalar(position, position.w, positionNDC);
-
- // Viewport transform to transform from clip coordinates to drawing buffer coordinates
- Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform);
- Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC);
-
- return Cartesian2.fromCartesian3(positionWC, result);
- };
-
/**
* @private
*/
@@ -323,6 +313,9 @@ define([
var worldCoords;
var frustum = scene.camera.frustum;
if (!defined(frustum.fovy)) {
+ if (defined(frustum._offCenterFrustum)) {
+ frustum = frustum._offCenterFrustum;
+ }
var currentFrustum = uniformState.currentFrustum;
worldCoords = scratchWorldCoords;
worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5;
diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js
index 76c0989a6786..7c0f2fb6fe85 100644
--- a/Source/Scene/SceneTransitioner.js
+++ b/Source/Scene/SceneTransitioner.js
@@ -14,6 +14,7 @@ define([
'../Core/Transforms',
'./Camera',
'./OrthographicFrustum',
+ './OrthographicOffCenterFrustum',
'./PerspectiveFrustum',
'./SceneMode'
], function(
@@ -31,6 +32,7 @@ define([
Transforms,
Camera,
OrthographicFrustum,
+ OrthographicOffCenterFrustum,
PerspectiveFrustum,
SceneMode) {
'use strict';
@@ -50,6 +52,7 @@ define([
this._morphHandler = undefined;
this._morphCancelled = false;
this._completeMorph = undefined;
+ this._morphToOrthographic = false;
}
SceneTransitioner.prototype.completeMorph = function() {
@@ -65,6 +68,7 @@ define([
var scene = this._scene;
this._previousMode = scene.mode;
+ this._morphToOrthographic = scene.camera.frustum instanceof OrthographicFrustum;
if (this._previousMode === SceneMode.SCENE2D || this._previousMode === SceneMode.MORPHING) {
return;
@@ -94,7 +98,8 @@ define([
var scratchToCVSurfacePosition = new Cartesian3();
var scratchToCVCartographic = new Cartographic();
var scratchToCVToENU = new Matrix4();
- var scratchToCVFrustum = new PerspectiveFrustum();
+ var scratchToCVFrustumPerspective = new PerspectiveFrustum();
+ var scratchToCVFrustumOrthographic = new OrthographicFrustum();
var scratchToCVCamera = {
position : undefined,
direction : undefined,
@@ -154,9 +159,16 @@ define([
}
}
- var frustum = scratchToCVFrustum;
- frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
- frustum.fov = CesiumMath.toRadians(60.0);
+ var frustum;
+ if (this._morphToOrthographic) {
+ frustum = scratchToCVFrustumOrthographic;
+ frustum.width = scene.camera.frustum.right - scene.camera.frustum.left;
+ frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
+ } else {
+ frustum = scratchToCVFrustumPerspective;
+ frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
+ frustum.fov = CesiumMath.toRadians(60.0);
+ }
var cameraCV = scratchToCVCamera;
cameraCV.position = position;
@@ -352,16 +364,14 @@ define([
transitioner._currentTweens.push(tween);
}
- var scratch2DTo3DFrustum = new PerspectiveFrustum();
+ var scratch2DTo3DFrustumPersp = new PerspectiveFrustum();
+ var scratch2DTo3DFrustumOrtho = new OrthographicFrustum();
function morphFrom2DTo3D(transitioner, duration, ellipsoid) {
duration /= 3.0;
var scene = transitioner._scene;
var camera = scene.camera;
- var frustum = scratch2DTo3DFrustum;
- frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
- frustum.fov = CesiumMath.toRadians(60.0);
var camera3D;
if (duration > 0.0) {
@@ -376,8 +386,6 @@ define([
camera3D = getColumbusViewTo3DCamera(transitioner, ellipsoid);
}
- camera3D.frustum = frustum;
-
var complete = complete3DCallback(camera3D);
createMorphHandler(transitioner, complete);
@@ -408,6 +416,28 @@ define([
camera.position.z = 2.0 * scene.mapProjection.ellipsoid.maximumRadius;
}
+ var morph;
+ var frustum;
+ if (transitioner._morphToOrthographic) {
+ morph = function() {
+ frustum = scratch2DTo3DFrustumOrtho;
+ frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
+ frustum.width = camera.frustum.right - camera.frustum.left;
+ camera.frustum = frustum;
+ morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
+ };
+ } else {
+ morph = function() {
+ frustum = scratch2DTo3DFrustumPersp;
+ frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight;
+ frustum.fov = CesiumMath.toRadians(60.0);
+ camera3D.frustum = frustum;
+ morphOrthographicToPerspective(transitioner, duration, camera3D, function() {
+ morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
+ });
+ };
+ }
+
if (duration > 0.0) {
var tween = scene.tweens.add({
duration : duration,
@@ -421,16 +451,12 @@ define([
update : update,
complete : function() {
scene._mode = SceneMode.MORPHING;
- morphOrthographicToPerspective(transitioner, duration, camera3D, function() {
- morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
- });
+ morph();
}
});
transitioner._currentTweens.push(tween);
} else {
- morphOrthographicToPerspective(transitioner, duration, camera3D, function() {
- morphFromColumbusViewTo3D(transitioner, duration, camera3D, complete);
- });
+ morph();
}
}
@@ -443,6 +469,10 @@ define([
var scene = transitioner._scene;
var camera = scene.camera;
+ if (camera.frustum instanceof OrthographicFrustum) {
+ return;
+ }
+
var startFOV = camera.frustum.fov;
var endFOV = CesiumMath.RADIANS_PER_DEGREE * 0.5;
var d = endCamera.position.z * Math.tan(startFOV * 0.5);
@@ -477,7 +507,7 @@ define([
var scratchCVTo2DEndPos = new Cartesian3();
var scratchCVTo2DEndDir = new Cartesian3();
var scratchCVTo2DEndUp = new Cartesian3();
- var scratchCVTo2DFrustum = new OrthographicFrustum();
+ var scratchCVTo2DFrustum = new OrthographicOffCenterFrustum();
var scratchCVTo2DRay = new Ray();
var scratchCVTo2DPickPos = new Cartesian3();
var scratchCVTo2DCamera = {
@@ -543,6 +573,7 @@ define([
columbusViewMorph(startUp, endUp, value.time, camera.up);
Cartesian3.cross(camera.direction, camera.up, camera.right);
Cartesian3.normalize(camera.right, camera.right);
+ camera._adjustOrthographicFrustum(true);
}
function updateHeight(camera, height) {
@@ -574,7 +605,7 @@ define([
position2D : new Cartesian3(),
direction2D : new Cartesian3(),
up2D : new Cartesian3(),
- frustum : new OrthographicFrustum()
+ frustum : new OrthographicOffCenterFrustum()
};
var scratch3DTo2DEndCamera = {
position : new Cartesian3(),
@@ -700,7 +731,8 @@ define([
var endUp = Cartesian3.clone(cameraCV.up, scratch3DToCVEndUp);
scene._mode = SceneMode.MORPHING;
- morphOrthographicToPerspective(transitioner, 0.0, cameraCV, function() {
+
+ function morph() {
camera.frustum = cameraCV.frustum.clone();
var startPos = Cartesian3.clone(camera.position, scratch3DToCVStartPos);
@@ -730,7 +762,13 @@ define([
}
});
transitioner._currentTweens.push(tween);
- });
+ }
+
+ if (transitioner._morphToOrthographic) {
+ morph();
+ } else {
+ morphOrthographicToPerspective(transitioner, 0.0, cameraCV, morph);
+ }
}
var scratch3DToCVStartPos = new Cartesian3();
@@ -758,6 +796,7 @@ define([
columbusViewMorph(startUp, endUp, value.time, camera.up);
Cartesian3.cross(camera.direction, camera.up, camera.right);
Cartesian3.normalize(camera.right, camera.right);
+ camera._adjustOrthographicFrustum(true);
}
var tween = scene.tweens.add({
duration : duration,
@@ -814,10 +853,6 @@ define([
Cartesian3.clone(camera3D.up, camera.up);
Cartesian3.cross(camera.direction, camera.up, camera.right);
Cartesian3.normalize(camera.right, camera.right);
-
- if (defined(camera3D.frustum)) {
- camera.frustum = camera3D.frustum.clone();
- }
}
var wasMorphing = defined(transitioner._completeMorph);
@@ -859,7 +894,6 @@ define([
scene.morphTime = SceneMode.getMorphTime(SceneMode.COLUMBUS_VIEW);
destroyMorphHandler(transitioner);
- scene.camera.frustum = cameraCV.frustum.clone();
if (transitioner._previousModeMode !== SceneMode.MORPHING || transitioner._morphCancelled) {
transitioner._morphCancelled = false;
diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js
index 3d6e974b3ebc..6d07334507e4 100644
--- a/Source/Scene/ScreenSpaceCameraController.js
+++ b/Source/Scene/ScreenSpaceCameraController.js
@@ -22,6 +22,7 @@ define([
'./CameraEventAggregator',
'./CameraEventType',
'./MapMode2D',
+ './OrthographicFrustum',
'./SceneMode',
'./SceneTransforms',
'./TweenCollection'
@@ -48,6 +49,7 @@ define([
CameraEventAggregator,
CameraEventType,
MapMode2D,
+ OrthographicFrustum,
SceneMode,
SceneTransforms,
TweenCollection) {
@@ -484,6 +486,14 @@ define([
var camera = scene.camera;
var mode = scene.mode;
+ if (camera.frustum instanceof OrthographicFrustum) {
+ if (Math.abs(distance) > 0.0) {
+ camera.zoomIn(distance);
+ camera._adjustOrthographicFrustum();
+ }
+ return;
+ }
+
var sameStartPosition = Cartesian2.equals(startPosition, object._zoomMouseStart);
var zoomingOnVector = object._zoomingOnVector;
var rotatingZoom = object._rotatingZoom;
@@ -1810,14 +1820,35 @@ define([
var endPos = look3DEndPos;
endPos.x = movement.endPosition.x;
endPos.y = 0.0;
- var start = camera.getPickRay(startPos, look3DStartRay).direction;
- var end = camera.getPickRay(endPos, look3DEndRay).direction;
+ var startRay = camera.getPickRay(startPos, look3DStartRay);
+ var endRay = camera.getPickRay(endPos, look3DEndRay);
var angle = 0.0;
+ var start;
+ var end;
+
+ if (camera.frustum instanceof OrthographicFrustum) {
+ start = startRay.origin;
+ end = endRay.origin;
+
+ Cartesian3.add(camera.direction, start, start);
+ Cartesian3.add(camera.direction, end, end);
+
+ Cartesian3.subtract(start, camera.position, start);
+ Cartesian3.subtract(end, camera.position, end);
+
+ Cartesian3.normalize(start, start);
+ Cartesian3.normalize(end, end);
+ } else {
+ start = startRay.direction;
+ end = endRay.direction;
+ }
+
var dot = Cartesian3.dot(start, end);
if (dot < 1.0) { // dot is in [0, 1]
angle = Math.acos(dot);
}
+
angle = (movement.startPosition.x > movement.endPosition.x) ? -angle : angle;
var horizontalRotationAxis = controller._horizontalRotationAxis;
@@ -1833,10 +1864,28 @@ define([
startPos.y = movement.startPosition.y;
endPos.x = 0.0;
endPos.y = movement.endPosition.y;
- start = camera.getPickRay(startPos, look3DStartRay).direction;
- end = camera.getPickRay(endPos, look3DEndRay).direction;
+ startRay = camera.getPickRay(startPos, look3DStartRay);
+ endRay = camera.getPickRay(endPos, look3DEndRay);
angle = 0.0;
+
+ if (camera.frustum instanceof OrthographicFrustum) {
+ start = startRay.origin;
+ end = endRay.origin;
+
+ Cartesian3.add(camera.direction, start, start);
+ Cartesian3.add(camera.direction, end, end);
+
+ Cartesian3.subtract(start, camera.position, start);
+ Cartesian3.subtract(end, camera.position, end);
+
+ Cartesian3.normalize(start, start);
+ Cartesian3.normalize(end, end);
+ } else {
+ start = startRay.direction;
+ end = endRay.direction;
+ }
+
dot = Cartesian3.dot(start, end);
if (dot < 1.0) { // dot is in [0, 1]
angle = Math.acos(dot);
diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js
index 171b6a9bb7a6..d65cf65528a8 100644
--- a/Source/Scene/ShadowMap.js
+++ b/Source/Scene/ShadowMap.js
@@ -45,7 +45,7 @@ define([
'./CullFace',
'./CullingVolume',
'./DebugCameraPrimitive',
- './OrthographicFrustum',
+ './OrthographicOffCenterFrustum',
'./PerInstanceColorAppearance',
'./PerspectiveFrustum',
'./Primitive',
@@ -96,7 +96,7 @@ define([
CullFace,
CullingVolume,
DebugCameraPrimitive,
- OrthographicFrustum,
+ OrthographicOffCenterFrustum,
PerInstanceColorAppearance,
PerspectiveFrustum,
Primitive,
@@ -254,7 +254,7 @@ define([
this._isSpotLight = false;
if (this._cascadesEnabled) {
// Cascaded shadows are always orthographic. The frustum dimensions are calculated on the fly.
- this._shadowMapCamera.frustum = new OrthographicFrustum();
+ this._shadowMapCamera.frustum = new OrthographicOffCenterFrustum();
} else if (defined(this._lightCamera.frustum.fov)) {
// If the light camera uses a perspective frustum, then the light source is a spot light
this._isSpotLight = true;
diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js
index bf0a1274ef2d..89aa3410dab7 100644
--- a/Source/Scene/SkyAtmosphere.js
+++ b/Source/Scene/SkyAtmosphere.js
@@ -167,8 +167,9 @@ define([
return undefined;
}
- if ((frameState.mode !== SceneMode.SCENE3D) &&
- (frameState.mode !== SceneMode.MORPHING)) {
+ var mode = frameState.mode;
+ if ((mode !== SceneMode.SCENE3D) &&
+ (mode !== SceneMode.MORPHING)) {
return undefined;
}
@@ -305,7 +306,7 @@ define([
*
* @example
* skyAtmosphere = skyAtmosphere && skyAtmosphere.destroy();
- *
+ *
* @see SkyAtmosphere#isDestroyed
*/
SkyAtmosphere.prototype.destroy = function() {
diff --git a/Source/Scene/Sun.js b/Source/Scene/Sun.js
index 78d22a8b40b1..e0e13a079827 100644
--- a/Source/Scene/Sun.js
+++ b/Source/Scene/Sun.js
@@ -67,7 +67,7 @@ define([
*
* @example
* scene.sun = new Cesium.Sun();
- *
+ *
* @see Scene#sun
*/
function Sun() {
@@ -299,11 +299,11 @@ define([
positionEC.w = 1;
var positionCC = Matrix4.multiplyByVector(projMatrix, positionEC, scratchCartesian4);
- var positionWC = SceneTransforms.clipToDrawingBufferCoordinates(passState.viewport, positionCC, scratchPositionWC);
+ var positionWC = SceneTransforms.clipToGLWindowCoordinates(passState.viewport, positionCC, scratchPositionWC);
positionEC.x = CesiumMath.SOLAR_RADIUS;
var limbCC = Matrix4.multiplyByVector(projMatrix, positionEC, scratchCartesian4);
- var limbWC = SceneTransforms.clipToDrawingBufferCoordinates(passState.viewport, limbCC, scratchLimbWC);
+ var limbWC = SceneTransforms.clipToGLWindowCoordinates(passState.viewport, limbCC, scratchLimbWC);
this._size = Math.ceil(Cartesian2.magnitude(Cartesian2.subtract(limbWC, positionWC, scratchCartesian4)));
this._size = 2.0 * this._size * (1.0 + 2.0 * this._glowLengthTS);
@@ -340,7 +340,7 @@ define([
*
* @example
* sun = sun && sun.destroy();
- *
+ *
* @see Sun#isDestroyed
*/
Sun.prototype.destroy = function() {
diff --git a/Source/Shaders/Builtin/Functions/alphaWeight.glsl b/Source/Shaders/Builtin/Functions/alphaWeight.glsl
index 74913da8c59e..ed18c4285cb7 100644
--- a/Source/Shaders/Builtin/Functions/alphaWeight.glsl
+++ b/Source/Shaders/Builtin/Functions/alphaWeight.glsl
@@ -3,21 +3,29 @@
*/
float czm_alphaWeight(float a)
{
- float z;
- if (czm_sceneMode != czm_sceneMode2D)
- {
- float x = 2.0 * (gl_FragCoord.x - czm_viewport.x) / czm_viewport.z - 1.0;
- float y = 2.0 * (gl_FragCoord.y - czm_viewport.y) / czm_viewport.w - 1.0;
- z = (gl_FragCoord.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];
- vec4 q = vec4(x, y, z, 0.0);
- q /= gl_FragCoord.w;
- z = (czm_inverseProjectionOIT * q).z;
+ float x = 2.0 * (gl_FragCoord.x - czm_viewport.x) / czm_viewport.z - 1.0;
+ float y = 2.0 * (gl_FragCoord.y - czm_viewport.y) / czm_viewport.w - 1.0;
+ float z = (gl_FragCoord.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];
+ vec4 q = vec4(x, y, z, 0.0);
+ q /= gl_FragCoord.w;
+
+ if (czm_inverseProjection != mat4(0.0)) {
+ q = czm_inverseProjection * q;
+ } else {
+ float top = czm_frustumPlanes.x;
+ float bottom = czm_frustumPlanes.y;
+ float left = czm_frustumPlanes.z;
+ float right = czm_frustumPlanes.w;
+
+ float near = czm_currentFrustum.x;
+ float far = czm_currentFrustum.y;
+
+ q.x = (q.x * (right - left) + left + right) * 0.5;
+ q.y = (q.y * (top - bottom) + bottom + top) * 0.5;
+ q.z = (q.z * (near - far) - near - far) * 0.5;
+ q.w = 1.0;
}
- else
- {
- z = gl_FragCoord.z * (czm_currentFrustum.y - czm_currentFrustum.x) + czm_currentFrustum.x;
- }
-
+
// See Weighted Blended Order-Independent Transparency for examples of different weighting functions:
// http://jcgt.org/published/0002/02/09/
return pow(a + 0.01, 4.0) + max(1e-2, min(3.0 * 1e3, 100.0 / (1e-5 + pow(abs(z) / 10.0, 3.0) + pow(abs(z) / 200.0, 6.0))));
diff --git a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl
index 5cb12e9e56fe..f429c26875ec 100644
--- a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl
+++ b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl
@@ -2,7 +2,7 @@
* Transforms a position from window to eye coordinates.
* The transform from window to normalized device coordinates is done using components
* of (@link czm_viewport} and {@link czm_viewportTransformation} instead of calculating
- * the inverse of czm_viewportTransformation
. The transformation from
+ * the inverse of czm_viewportTransformation
. The transformation from
* normalized device coordinates to clip coordinates is done using positionWC.w
,
* which is expected to be the scalar used in the perspective divide. The transformation
* from clip to eye coordinates is done using {@link czm_inverseProjection}.
@@ -30,6 +30,23 @@ vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate)
float z = (fragmentCoordinate.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];
vec4 q = vec4(x, y, z, 1.0);
q /= fragmentCoordinate.w;
- q = czm_inverseProjection * q;
+
+ if (czm_inverseProjection != mat4(0.0)) {
+ q = czm_inverseProjection * q;
+ } else {
+ float top = czm_frustumPlanes.x;
+ float bottom = czm_frustumPlanes.y;
+ float left = czm_frustumPlanes.z;
+ float right = czm_frustumPlanes.w;
+
+ float near = czm_currentFrustum.x;
+ float far = czm_currentFrustum.y;
+
+ q.x = (q.x * (right - left) + left + right) * 0.5;
+ q.y = (q.y * (top - bottom) + bottom + top) * 0.5;
+ q.z = (q.z * (near - far) - near - far) * 0.5;
+ q.w = 1.0;
+ }
+
return q;
}
diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css
new file mode 100644
index 000000000000..ec2ceb0c20e3
--- /dev/null
+++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css
@@ -0,0 +1,51 @@
+span.cesium-projectionPicker-wrapper {
+ display: inline-block;
+ position: relative;
+ margin: 0 3px;
+}
+
+.cesium-projectionPicker-visible {
+ visibility: visible;
+ opacity: 1;
+ transition: opacity 0.25s linear;
+ -webkit-transition: opacity 0.25s linear;
+ -moz-transition: opacity 0.25s linear;
+}
+
+.cesium-projectionPicker-hidden {
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s 0.25s, opacity 0.25s linear;
+ -webkit-transition: visibility 0s 0.25s, opacity 0.25s linear;
+ -moz-transition: visibility 0s 0.25s, opacity 0.25s linear;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-none {
+ display: none;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-dropDown-icon {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 3px 0;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-buttonPerspective,
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-buttonOrthographic {
+ margin: 0 0 3px 0;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-buttonPerspective .cesium-projectionPicker-iconOrthographic {
+ left: 100%;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-buttonOrthographic .cesium-projectionPicker-iconPerspective {
+ left: -100%;
+}
+
+.cesium-projectionPicker-wrapper .cesium-projectionPicker-selected {
+ border-color: #2e2;
+ box-shadow: 0 0 8px #fff, 0 0 8px #fff;
+}
diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js
new file mode 100644
index 000000000000..1405115e4113
--- /dev/null
+++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js
@@ -0,0 +1,176 @@
+/*global define*/
+define([
+ '../../Core/defined',
+ '../../Core/defineProperties',
+ '../../Core/destroyObject',
+ '../../Core/DeveloperError',
+ '../../Core/FeatureDetection',
+ '../../ThirdParty/knockout',
+ '../getElement',
+ './ProjectionPickerViewModel'
+ ], function(
+ defined,
+ defineProperties,
+ destroyObject,
+ DeveloperError,
+ FeatureDetection,
+ knockout,
+ getElement,
+ ProjectionPickerViewModel) {
+ 'use strict';
+
+ var perspectivePath = 'M 28.15625,10.4375 9.125,13.21875 13.75,43.25 41.75,55.09375 50.8125,37 54.5,11.9375 z m 0.125,3 19.976451,0.394265 L 43.03125,16.875 22.6875,14.28125 z M 50.971746,15.705477 47.90625,36.03125 42.53125,46 44.84375,19.3125 z M 12.625,16.03125 l 29.15625,3.6875 -2.65625,31 L 16.4375,41.125 z';
+ var orthographicPath = 'm 31.560594,6.5254438 -20.75,12.4687502 0.1875,24.5625 22.28125,11.8125 19.5,-12 0.65625,-0.375 0,-0.75 0.0312,-23.21875 z m 0.0625,3.125 16.65625,9.5000002 -16.125,10.28125 -17.34375,-9.71875 z m 18.96875,11.1875002 0.15625,20.65625 -17.46875,10.59375 0.15625,-20.28125 z m -37.0625,1.25 17.21875,9.625 -0.15625,19.21875 -16.9375,-9 z';
+
+ /**
+ * The ProjectionPicker is a single button widget for switching between perspective and orthographic projections.
+ *
+ * @alias ProjectionPicker
+ * @constructor
+ *
+ * @param {Element|String} container The DOM element or ID that will contain the widget.
+ * @param {Scene} scene The Scene instance to use.
+ *
+ * @exception {DeveloperError} Element with id "container" does not exist in the document.
+ *
+ * @example
+ * // In HTML head, include a link to the ProjectionPicker.css stylesheet,
+ * // and in the body, include: