From 316ea87044d0c15b8530ed0204410165517116ac Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 31 Jan 2017 17:01:43 -0500 Subject: [PATCH 01/46] Orthographic projection WIP. --- Apps/Sandcastle/gallery/Hello World.html | 12 ++++++++++++ Source/Scene/GlobeSurfaceTileProvider.js | 11 +++++++++-- Source/Scene/QuadtreePrimitive.js | 4 +++- Source/Scene/ScreenSpaceCameraController.js | 15 ++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index 640e8e317705..2b510cb27583 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -28,6 +28,18 @@ 'use strict'; //Sandcastle_Begin var viewer = new Cesium.Viewer('cesiumContainer'); +viewer.extend(Cesium.viewerCesiumInspectorMixin); + +var camera = viewer.camera; +var height = camera.positionCartographic.height; +var ratio = viewer.canvas.clientWidth / viewer.canvas.clientHeight; + +var frustum = camera.frustum = new Cesium.OrthographicFrustum(); +frustum.right = height * 0.075; +frustum.left = -frustum.right; +frustum.top = ratio * frustum.right * 0.3; +frustum.bottom = -frustum.top; + //Sandcastle_End Sandcastle.finishedLoading(); } diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 7bec9532d203..a7e137df1367 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) { @@ -473,10 +475,15 @@ define([ * @returns {Visibility} The visibility of the tile. */ GlobeSurfaceTileProvider.prototype.computeTileVisibility = function(tile, frameState, occluders) { + // TODO ORTHO + return Visibility.FULL; + var distance = this.computeDistanceToTile(tile, frameState); tile._distance = distance; - if (frameState.fog.enabled) { + var ortho3D = frameState.mode !== SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum; + + if (frameState.fog.enabled && !ortho3D) { if (CesiumMath.fog(distance, frameState.fog.density) >= 1.0) { // Tile is completely in fog so return that it is not visible. return Visibility.NONE; @@ -502,7 +509,7 @@ define([ return Visibility.NONE; } - if (frameState.mode === SceneMode.SCENE3D) { + if (frameState.mode === SceneMode.SCENE3D && !ortho3D) { var occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace; if (!defined(occludeePointInScaledSpace)) { return intersection; diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 9149b60359e4..90289c65ea52 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -13,6 +13,7 @@ define([ '../Core/Ray', '../Core/Rectangle', '../Core/Visibility', + './OrthographicFrustum', './QuadtreeOccluders', './QuadtreeTile', './QuadtreeTileLoadState', @@ -32,6 +33,7 @@ define([ Ray, Rectangle, Visibility, + OrthographicFrustum, QuadtreeOccluders, QuadtreeTile, QuadtreeTileLoadState, @@ -669,7 +671,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); } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 7f0652571b0a..3b15deb2c428 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) { @@ -796,8 +798,10 @@ define([ return undefined; } + // TODO ORTHO var depthIntersection; - if (scene.pickPositionSupported) { + var orthoFrustum = camera.frustum instanceof OrthographicFrustum; + if (scene.pickPositionSupported && !orthoFrustum) { depthIntersection = scene.pickPosition(mousePosition, scratchDepthIntersection); } @@ -1532,6 +1536,15 @@ define([ var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); handleZoom(controller, startPosition, movement, controller._zoomFactor, distance, Cartesian3.dot(unitPosition, camera.direction)); + + var frustum = camera.frustum; + if (frustum instanceof OrthographicFrustum) { + var ratio = frustum.right / frustum.top; + frustum.right = height * 0.5; + frustum.left = -frustum.right; + frustum.top = ratio * frustum.right * 0.3; + frustum.bottom = -frustum.top; + } } var tilt3DWindowPos = new Cartesian2(); From ee297a3c546619daea74ab0e7c67854dc532f42a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 1 Feb 2017 14:24:36 -0500 Subject: [PATCH 02/46] Fix camera jittering when zooming. Temporarily disable atmosphere and enable frustum culling terrain. --- Source/Scene/Camera.js | 24 +++++++++++++++------ Source/Scene/GlobeSurfaceTileProvider.js | 2 +- Source/Scene/ScreenSpaceCameraController.js | 9 -------- Source/Scene/SkyAtmosphere.js | 12 ++++++++--- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 66ea1413be01..8c984d7d6d42 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -25,6 +25,7 @@ define([ '../Core/Transforms', './CameraFlightPath', './MapMode2D', + './OrthographicFrustum', './PerspectiveFrustum', './SceneMode' ], function( @@ -53,6 +54,7 @@ define([ Transforms, CameraFlightPath, MapMode2D, + OrthographicFrustum, PerspectiveFrustum, SceneMode) { 'use strict'; @@ -905,8 +907,10 @@ define([ updateFrustum = this._mode === SceneMode.SCENE2D; } + var frustum; + var ratio; if (updateFrustum) { - var frustum = this._max2Dfrustum = this.frustum.clone(); + frustum = this._max2Dfrustum = this.frustum.clone(); //>>includeStart('debug', pragmas.debug); if (!defined(frustum.left) || !defined(frustum.right) || !defined(frustum.top) || !defined(frustum.bottom)) { @@ -915,23 +919,31 @@ define([ //>>includeEnd('debug'); var maxZoomOut = 2.0; - var ratio = frustum.top / frustum.right; + ratio = frustum.top / frustum.right; frustum.right = this._maxCoord.x * maxZoomOut; frustum.left = -frustum.right; frustum.top = ratio * frustum.right; frustum.bottom = -frustum.top; } - if (this._mode === SceneMode.SCENE2D) { - clampMove2D(this, this.position); - } - var globe = this._scene.globe; var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0); if (this._suspendTerrainAdjustment) { this._suspendTerrainAdjustment = !globeFinishedUpdating; } this._adjustHeightForTerrain(); + + frustum = this.frustum; + if (this._mode === SceneMode.SCENE2D) { + clampMove2D(this, this.position); + } else if (frustum instanceof OrthographicFrustum) { + ratio = this._scene.drawingBufferWidth / this._scene.drawingBufferHeight; + frustum.right = this.positionCartographic.height * 0.5; + frustum.left = -frustum.right; + // TODO ORTHO + frustum.top = ratio * frustum.right * 0.3; + frustum.bottom = -frustum.top; + } }; var setTransformPosition = new Cartesian3(); diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index a7e137df1367..bc067e6d7288 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -476,7 +476,7 @@ define([ */ GlobeSurfaceTileProvider.prototype.computeTileVisibility = function(tile, frameState, occluders) { // TODO ORTHO - return Visibility.FULL; + //return Visibility.FULL; var distance = this.computeDistanceToTile(tile, frameState); tile._distance = distance; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 3b15deb2c428..b56afd8a62bd 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1536,15 +1536,6 @@ define([ var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); handleZoom(controller, startPosition, movement, controller._zoomFactor, distance, Cartesian3.dot(unitPosition, camera.direction)); - - var frustum = camera.frustum; - if (frustum instanceof OrthographicFrustum) { - var ratio = frustum.right / frustum.top; - frustum.right = height * 0.5; - frustum.left = -frustum.right; - frustum.top = ratio * frustum.right * 0.3; - frustum.bottom = -frustum.top; - } } var tilt3DWindowPos = new Cartesian2(); diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index bf0a1274ef2d..431636d79b8d 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -167,8 +167,14 @@ define([ return undefined; } - if ((frameState.mode !== SceneMode.SCENE3D) && - (frameState.mode !== SceneMode.MORPHING)) { + // TODO ORTHO + var mode = frameState.mode; + if (mode !== SceneMode.SCENE2D && !defined(frameState.camera.frustum.fov)) { + return undefined; + } + + if ((mode !== SceneMode.SCENE3D) && + (mode !== SceneMode.MORPHING)) { return undefined; } @@ -305,7 +311,7 @@ define([ * * @example * skyAtmosphere = skyAtmosphere && skyAtmosphere.destroy(); - * + * * @see SkyAtmosphere#isDestroyed */ SkyAtmosphere.prototype.destroy = function() { From 4b0788a97a9cca70c705439814a6ec4b089f9bff Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 2 Feb 2017 14:48:41 -0500 Subject: [PATCH 03/46] Disable horizon culling and fog. --- Apps/Sandcastle/gallery/Hello World.html | 5 +++++ Source/Scene/Fog.js | 6 +++++- Source/Scene/GlobeSurfaceTileProvider.js | 5 +---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index 2b510cb27583..db529a6d6b29 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -40,6 +40,11 @@ frustum.top = ratio * frustum.right * 0.3; frustum.bottom = -frustum.top; +camera.position = new Cesium.Cartesian3(1841647.0762382608, -6783164.237195379, 2471753.1074874406); +camera.direction = new Cesium.Cartesian3(-0.30125208680184823, 0.7555331113434306, 0.5817361067195737); +camera.up = new Cesium.Cartesian3(0.10227563420000141, -0.5809561222843004, 0.8074835469713154); +camera.right = new Cesium.Cartesian3(0.9480437093544365, 0.3027535328350511, 0.09774161605670559); + //Sandcastle_End Sandcastle.finishedLoading(); } diff --git a/Source/Scene/Fog.js b/Source/Scene/Fog.js index 15d9d4b2aa84..75dac42fe337 100644 --- a/Source/Scene/Fog.js +++ b/Source/Scene/Fog.js @@ -3,11 +3,13 @@ define([ '../Core/Cartesian3', '../Core/defined', '../Core/Math', + './OrthographicFrustum', './SceneMode' ], function( Cartesian3, defined, CesiumMath, + OrthographicFrustum, SceneMode) { 'use strict'; @@ -114,7 +116,9 @@ define([ var positionCartographic = camera.positionCartographic; // Turn off fog in space. - if (!defined(positionCartographic) || positionCartographic.height > 800000.0 || frameState.mode !== SceneMode.SCENE3D) { + if (!defined(positionCartographic) || positionCartographic.height > 800000.0 || + frameState.mode !== SceneMode.SCENE3D || + (frameState.mode === SceneMode.SCENE3D && camera.frustum instanceof OrthographicFrustum)) { frameState.fog.enabled = false; return; } diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index bc067e6d7288..6fea88dbebcf 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -475,13 +475,10 @@ define([ * @returns {Visibility} The visibility of the tile. */ GlobeSurfaceTileProvider.prototype.computeTileVisibility = function(tile, frameState, occluders) { - // TODO ORTHO - //return Visibility.FULL; - var distance = this.computeDistanceToTile(tile, frameState); tile._distance = distance; - var ortho3D = frameState.mode !== SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum; + var ortho3D = frameState.mode === SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum; if (frameState.fog.enabled && !ortho3D) { if (CesiumMath.fog(distance, frameState.fog.density) >= 1.0) { From 89fe621bd903d6dcee3f15caffd3720ebf89e216 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 2 Feb 2017 15:31:15 -0500 Subject: [PATCH 04/46] Fix picking culling volume, view rectangle distance. Disable OIT. --- Source/Scene/Camera.js | 11 +++++++++-- Source/Scene/Scene.js | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 8c984d7d6d42..b13f39607a3e 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -2087,8 +2087,15 @@ 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 tanPhi; + var tanTheta; + if (camera.frustum instanceof OrthographicFrustum) { + // TODO ORTHO + tanPhi = tanTheta = 0.5; + } else { + tanPhi = Math.tan(camera.frustum.fovy * 0.5); + tanTheta = camera.frustum.aspectRatio * tanPhi; + } var d = Math.max( computeD(direction, up, northWest, tanPhi), diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 1b60328e20f0..ae096d320551 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2296,6 +2296,8 @@ define([ // If supported, configure OIT to use the globe depth framebuffer and clear the OIT framebuffer. var useOIT = environmentState.useOIT = !picking && renderTranslucentCommands && defined(scene._oit) && scene._oit.isSupported(); + // TODO ORTHO + environmentState.useOIT = useOIT = useOIT && !(scene.camera.frustum instanceof OrthographicFrustum); if (useOIT) { scene._oit.update(context, scene._globeDepth.framebuffer); scene._oit.clear(context, passState, clearColor); @@ -2529,7 +2531,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); @@ -2577,7 +2581,7 @@ define([ } function getPickCullingVolume(scene, drawingBufferPosition, width, height) { - if (scene._mode === SceneMode.SCENE2D) { + if (scene.camera.frustum instanceof OrthographicFrustum) { return getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height); } From 1c57a5261f9df5ab9a9221c501097c286eae931c Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 2 Feb 2017 17:39:31 -0500 Subject: [PATCH 05/46] Fix picking from depth buffer with orthographic projection. Fix zooming to bounding spheres. --- Source/Scene/Camera.js | 2 +- Source/Scene/Scene.js | 4 +--- Source/Scene/SceneTransforms.js | 25 ++++++++++++++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index b13f39607a3e..a3c1278f8e2d 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -2718,7 +2718,7 @@ define([ if (radius === 0.0) { offset.range = MINIMUM_ZOOM; } else { - offset.range = camera._mode === SceneMode.SCENE2D ? distanceToBoundingSphere2D(camera, radius) : distanceToBoundingSphere3D(camera, radius); + offset.range = camera.frustum instanceof OrthographicFrustum ? distanceToBoundingSphere2D(camera, radius) : distanceToBoundingSphere3D(camera, radius); } } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index ae096d320551..ea2e71d9943c 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2685,9 +2685,7 @@ define([ } else if (defined(camera.frustum.infiniteProjectionMatrix)){ frustum = camera.frustum.clone(scratchPerspectiveOffCenterFrustum); } else { - //>>includeStart('debug', pragmas.debug); - throw new DeveloperError('2D is not supported. An orthographic projection matrix is not invertible.'); - //>>includeEnd('debug'); + frustum = camera.frustum.clone(scratchOrthographicFrustum); } var numFrustums = this.numberOfFrustums; diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index 8da7f9fbb523..c1d487533a46 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -10,6 +10,7 @@ define([ '../Core/Math', '../Core/Matrix4', '../Core/Transforms', + './OrthographicFrustum', './SceneMode' ], function( BoundingRectangle, @@ -22,6 +23,7 @@ define([ CesiumMath, Matrix4, Transforms, + OrthographicFrustum, SceneMode) { 'use strict'; @@ -320,11 +322,24 @@ define([ ndc.z = (depth * 2.0) - 1.0; ndc.w = 1.0; - var worldCoords = Matrix4.multiplyByVector(uniformState.inverseViewProjection, ndc, scratchWorldCoords); - - // Reverse perspective divide - var w = 1.0 / worldCoords.w; - Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); + var worldCoords; + var frustum = scene.camera.frustum; + if (frustum instanceof OrthographicFrustum) { + var currentFrustum = uniformState.currentFrustum; + worldCoords = scratchWorldCoords; + worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5; + worldCoords.y = (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5; + worldCoords.z = (ndc.z * (currentFrustum.x - currentFrustum.y) - currentFrustum.x - currentFrustum.y) * 0.5; + worldCoords.w = 1.0; + + worldCoords = Matrix4.multiplyByVector(uniformState.inverseView, worldCoords, worldCoords); + } else { + worldCoords = Matrix4.multiplyByVector(uniformState.inverseViewProjection, ndc, scratchWorldCoords); + + // Reverse perspective divide + var w = 1.0 / worldCoords.w; + Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); + } return Cartesian3.fromCartesian4(worldCoords, result); }; From 72d7441e03ace2d47619741a23d3be951c9c01f0 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 2 Feb 2017 19:17:35 -0500 Subject: [PATCH 06/46] Fix when camera is in a reference frame not the identity. --- Source/Scene/Camera.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index a3c1278f8e2d..67b9ae9133c9 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -937,8 +937,9 @@ define([ if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, this.position); } else if (frustum instanceof OrthographicFrustum) { + var distance = Matrix4.equals(Matrix4.IDENTITY, this._transform) ? this.positionCartographic.height : Cartesian3.magnitude(this.position); ratio = this._scene.drawingBufferWidth / this._scene.drawingBufferHeight; - frustum.right = this.positionCartographic.height * 0.5; + frustum.right = distance * 0.5; frustum.left = -frustum.right; // TODO ORTHO frustum.top = ratio * frustum.right * 0.3; From c19cf65327b6e35b99f374033de08fdc942ec6e3 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 2 Feb 2017 19:22:44 -0500 Subject: [PATCH 07/46] Fix panning on terrain. --- Source/Scene/ScreenSpaceCameraController.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index b56afd8a62bd..7f0652571b0a 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -22,7 +22,6 @@ define([ './CameraEventAggregator', './CameraEventType', './MapMode2D', - './OrthographicFrustum', './SceneMode', './SceneTransforms', './TweenCollection' @@ -49,7 +48,6 @@ define([ CameraEventAggregator, CameraEventType, MapMode2D, - OrthographicFrustum, SceneMode, SceneTransforms, TweenCollection) { @@ -798,10 +796,8 @@ define([ return undefined; } - // TODO ORTHO var depthIntersection; - var orthoFrustum = camera.frustum instanceof OrthographicFrustum; - if (scene.pickPositionSupported && !orthoFrustum) { + if (scene.pickPositionSupported) { depthIntersection = scene.pickPosition(mousePosition, scratchDepthIntersection); } From 47174b43b4a57d3252343df77f800ec537ed5676 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 3 Feb 2017 16:11:00 -0500 Subject: [PATCH 08/46] Add separate centered and off-center orthographic frustums. --- Apps/Sandcastle/gallery/Hello World.html | 9 +- Source/Scene/Camera.js | 24 +- Source/Scene/OrthographicFrustum.js | 279 ++++---------- Source/Scene/OrthographicOffCenterFrustum.js | 362 +++++++++++++++++++ Source/Scene/QuadtreePrimitive.js | 3 + Source/Scene/Scene.js | 19 +- Source/Scene/SceneTransforms.js | 5 +- Source/Scene/SceneTransitioner.js | 8 +- Source/Scene/ShadowMap.js | 6 +- 9 files changed, 468 insertions(+), 247 deletions(-) create mode 100644 Source/Scene/OrthographicOffCenterFrustum.js diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index db529a6d6b29..f011386ca948 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -31,14 +31,9 @@ viewer.extend(Cesium.viewerCesiumInspectorMixin); var camera = viewer.camera; -var height = camera.positionCartographic.height; -var ratio = viewer.canvas.clientWidth / viewer.canvas.clientHeight; - var frustum = camera.frustum = new Cesium.OrthographicFrustum(); -frustum.right = height * 0.075; -frustum.left = -frustum.right; -frustum.top = ratio * frustum.right * 0.3; -frustum.bottom = -frustum.top; +frustum.width = camera.positionCartographic.height; +frustum.aspectRatio = viewer.canvas.clientWidth / viewer.canvas.clientHeight *0.3; camera.position = new Cesium.Cartesian3(1841647.0762382608, -6783164.237195379, 2471753.1074874406); camera.direction = new Cesium.Cartesian3(-0.30125208680184823, 0.7555331113434306, 0.5817361067195737); diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 67b9ae9133c9..86dbca7b7aea 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -937,13 +937,8 @@ define([ if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, this.position); } else if (frustum instanceof OrthographicFrustum) { - var distance = Matrix4.equals(Matrix4.IDENTITY, this._transform) ? this.positionCartographic.height : Cartesian3.magnitude(this.position); - ratio = this._scene.drawingBufferWidth / this._scene.drawingBufferHeight; - frustum.right = distance * 0.5; - frustum.left = -frustum.right; - // TODO ORTHO - frustum.top = ratio * frustum.right * 0.3; - frustum.bottom = -frustum.top; + frustum.width = Matrix4.equals(Matrix4.IDENTITY, this._transform) ? this.positionCartographic.height : Cartesian3.magnitude(this.position); + frustum.aspectRatio = this._scene.drawingBufferWidth / this._scene.drawingBufferHeight * 0.3; } }; @@ -2354,10 +2349,14 @@ define([ var width = canvas.clientWidth; var height = canvas.clientHeight; + var frustum = camera.frustum; + if (!defined(frustum.top)) { + 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); @@ -2690,6 +2689,9 @@ define([ function distanceToBoundingSphere2D(camera, radius) { var frustum = camera.frustum; + if (!defined(frustum.top)) { + frustum = frustum._offCenterFrustum; + } var right, top; var ratio = frustum.right / frustum.top; @@ -2718,8 +2720,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.frustum instanceof OrthographicFrustum ? distanceToBoundingSphere2D(camera, radius) : distanceToBoundingSphere3D(camera, radius); + offset.range = distanceToBoundingSphere3D(camera, radius); } } diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js index 9c709b191809..084ca491f82a 100644 --- a/Source/Scene/OrthographicFrustum.js +++ b/Source/Scene/OrthographicFrustum.js @@ -1,20 +1,20 @@ /*global define*/ define([ - '../Core/Cartesian3', - '../Core/Cartesian4', - '../Core/defined', - '../Core/defineProperties', - '../Core/DeveloperError', - '../Core/Matrix4', - './CullingVolume' - ], function( - Cartesian3, - Cartesian4, - defined, - defineProperties, - DeveloperError, - Matrix4, - CullingVolume) { + '../Core/Cartesian3', + '../Core/Cartesian4', + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/Matrix4', + './OrthographicOffCenterFrustum' +], function( + Cartesian3, + Cartesian4, + defined, + defineProperties, + DeveloperError, + Matrix4, + OrthographicOffCenterFrustum) { 'use strict'; /** @@ -23,13 +23,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 +38,13 @@ define([ * frustum.far = 50.0 * maxRadii; */ function OrthographicFrustum() { - /** - * 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; + this._offCenterFrustum = new OrthographicOffCenterFrustum(); - /** - * 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. @@ -85,43 +61,39 @@ define([ */ 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.'); + 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.aspectRatio < 0) { + throw new DeveloperError('aspectRatio must be positive.'); } - if (frustum.bottom > frustum.top) { - throw new DeveloperError('top must be greater than bottom.'); - } - 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); + + f.right = frustum.width * 0.5; + f.left = -f.right; + f.top = frustum.aspectRatio * f.right; + f.bottom = -f.top; + f.near = frustum.near; + f.far = frustum.far; } } @@ -135,16 +107,11 @@ define([ projectionMatrix : { get : function() { update(this); - return this._orthographicMatrix; + return this._offCenterFrustum.projectionMatrix; } } }); - 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 +126,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 +149,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 +187,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 90289c65ea52..752f1fd6f37c 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -693,6 +693,9 @@ define([ function screenSpaceError2D(primitive, frameState, tile) { var camera = frameState.camera; var frustum = camera.frustum; + if (!defined(frustum.top)) { + frustum = frustum._offCenterFrustum; + } var context = frameState.context; var width = context.drawingBufferWidth; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index ea2e71d9943c..cafae26367ae 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -52,6 +52,7 @@ define([ './MapMode2D', './OIT', './OrthographicFrustum', + './OrthographicOffCenterFrustum', './PerformanceDisplay', './PerInstanceColorAppearance', './PerspectiveFrustum', @@ -119,6 +120,7 @@ define([ MapMode2D, OIT, OrthographicFrustum, + OrthographicOffCenterFrustum, PerformanceDisplay, PerInstanceColorAppearance, PerspectiveFrustum, @@ -1666,6 +1668,7 @@ define([ var scratchPerspectiveFrustum = new PerspectiveFrustum(); var scratchPerspectiveOffCenterFrustum = new PerspectiveOffCenterFrustum(); var scratchOrthographicFrustum = new OrthographicFrustum(); + var scratchOrthographicOffCenterFrustum = new OrthographicOffCenterFrustum(); function executeCommands(scene, passState, picking) { var camera = scene._camera; @@ -1680,8 +1683,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 @@ -2504,7 +2509,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(); @@ -2513,6 +2518,9 @@ define([ function getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height) { var camera = scene._camera; var frustum = camera.frustum; + if (!defined(frustum.top)) { + frustum = frustum._offCenterFrustum; + } var viewport = scene._passState.viewport; var x = 2.0 * (drawingBufferPosition.x - viewport.x) / viewport.width - 1.0; @@ -2581,7 +2589,8 @@ define([ } function getPickCullingVolume(scene, drawingBufferPosition, width, height) { - if (scene.camera.frustum instanceof OrthographicFrustum) { + var frustum = scene.camera.frustum; + if (frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum) { return getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height); } @@ -2684,8 +2693,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 c1d487533a46..156fe8cb92c4 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -324,7 +324,10 @@ define([ var worldCoords; var frustum = scene.camera.frustum; - if (frustum instanceof OrthographicFrustum) { + if (!defined(frustum.inverseProjection)) { + if (!defined(frustum.top)) { + 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..13c5318d94b7 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -13,7 +13,7 @@ define([ '../Core/ScreenSpaceEventType', '../Core/Transforms', './Camera', - './OrthographicFrustum', + './OrthographicOffCenterFrustum', './PerspectiveFrustum', './SceneMode' ], function( @@ -30,7 +30,7 @@ define([ ScreenSpaceEventType, Transforms, Camera, - OrthographicFrustum, + OrthographicOffCenterFrustum, PerspectiveFrustum, SceneMode) { 'use strict'; @@ -477,7 +477,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 = { @@ -574,7 +574,7 @@ define([ position2D : new Cartesian3(), direction2D : new Cartesian3(), up2D : new Cartesian3(), - frustum : new OrthographicFrustum() + frustum : new OrthographicOffCenterFrustum() }; var scratch3DTo2DEndCamera = { position : new Cartesian3(), diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js index cbb634f9e011..71e5d0ce3b79 100644 --- a/Source/Scene/ShadowMap.js +++ b/Source/Scene/ShadowMap.js @@ -46,7 +46,7 @@ define([ './CullFace', './CullingVolume', './DebugCameraPrimitive', - './OrthographicFrustum', + './OrthographicOffCenterFrustum', './PerInstanceColorAppearance', './PerspectiveFrustum', './Primitive', @@ -98,7 +98,7 @@ define([ CullFace, CullingVolume, DebugCameraPrimitive, - OrthographicFrustum, + OrthographicOffCenterFrustum, PerInstanceColorAppearance, PerspectiveFrustum, Primitive, @@ -256,7 +256,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; From 6daca541a1853e3678f2542d3003fc91069a1428 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 3 Feb 2017 16:45:42 -0500 Subject: [PATCH 09/46] Update aspect ratio on canvas resize. --- Source/Scene/Camera.js | 1 - Source/Scene/OrthographicFrustum.js | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 86dbca7b7aea..f31a051cfa4f 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -938,7 +938,6 @@ define([ clampMove2D(this, this.position); } else if (frustum instanceof OrthographicFrustum) { frustum.width = Matrix4.equals(Matrix4.IDENTITY, this._transform) ? this.positionCartographic.height : Cartesian3.magnitude(this.position); - frustum.aspectRatio = this._scene.drawingBufferWidth / this._scene.drawingBufferHeight * 0.3; } }; diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js index 084ca491f82a..37afc986ab90 100644 --- a/Source/Scene/OrthographicFrustum.js +++ b/Source/Scene/OrthographicFrustum.js @@ -88,9 +88,14 @@ define([ frustum._near = frustum.near; frustum._far = frustum.far; + 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 = frustum.aspectRatio * f.right; + f.top = ratio * f.right; f.bottom = -f.top; f.near = frustum.near; f.far = frustum.far; From d3aef4eadf685ca49e7b002cf41d6c88e53038eb Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 3 Feb 2017 17:11:30 -0500 Subject: [PATCH 10/46] Disable environmental effects. --- Apps/Sandcastle/gallery/Hello World.html | 1 - Source/Scene/OrthographicFrustum.js | 24 ++++++++------------ Source/Scene/Scene.js | 29 ++++++++++++++++-------- Source/Scene/SkyAtmosphere.js | 5 ---- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index f011386ca948..7ab0d4c94312 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -28,7 +28,6 @@ 'use strict'; //Sandcastle_Begin var viewer = new Cesium.Viewer('cesiumContainer'); -viewer.extend(Cesium.viewerCesiumInspectorMixin); var camera = viewer.camera; var frustum = camera.frustum = new Cesium.OrthographicFrustum(); diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js index 37afc986ab90..ffcf79f3bdfa 100644 --- a/Source/Scene/OrthographicFrustum.js +++ b/Source/Scene/OrthographicFrustum.js @@ -1,20 +1,14 @@ /*global define*/ define([ - '../Core/Cartesian3', - '../Core/Cartesian4', - '../Core/defined', - '../Core/defineProperties', - '../Core/DeveloperError', - '../Core/Matrix4', - './OrthographicOffCenterFrustum' -], function( - Cartesian3, - Cartesian4, - defined, - defineProperties, - DeveloperError, - Matrix4, - OrthographicOffCenterFrustum) { + '../Core/defined', + '../Core/defineProperties', + '../Core/DeveloperError', + './OrthographicOffCenterFrustum' + ], function( + defined, + defineProperties, + DeveloperError, + OrthographicOffCenterFrustum) { 'use strict'; /** diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index cafae26367ae..a16651234607 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2172,18 +2172,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; - } - 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; + + 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; + } var clearGlobeDepth = environmentState.clearGlobeDepth = defined(globe) && (!globe.depthTestAgainstTerrain || scene.mode === SceneMode.SCENE2D); var useDepthPlane = environmentState.useDepthPlane = clearGlobeDepth && scene.mode === SceneMode.SCENE3D; diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 431636d79b8d..89aa3410dab7 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -167,12 +167,7 @@ define([ return undefined; } - // TODO ORTHO var mode = frameState.mode; - if (mode !== SceneMode.SCENE2D && !defined(frameState.camera.frustum.fov)) { - return undefined; - } - if ((mode !== SceneMode.SCENE3D) && (mode !== SceneMode.MORPHING)) { return undefined; From dc6d9a739ce4da4d0e6387ef5d5a3fd15d7b541f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 3 Feb 2017 17:22:00 -0500 Subject: [PATCH 11/46] Fix OIT. --- Source/Renderer/UniformState.js | 9 +++++++-- Source/Scene/Scene.js | 2 -- Source/Shaders/Builtin/Functions/alphaWeight.glsl | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 759659750639..73d4eee0ef4e 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'; @@ -147,6 +149,7 @@ define([ this._frustum2DWidth = 0.0; this._eyeHeight2D = new Cartesian2(); this._resolutionScale = 1.0; + this._orthographicIn3D = false; this._fogDensity = undefined; @@ -896,6 +899,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; }; /** @@ -995,10 +1000,10 @@ define([ if (uniformState._inverseProjectionOITDirty) { uniformState._inverseProjectionOITDirty = false; - if (uniformState._mode !== SceneMode.SCENE2D && uniformState._mode !== SceneMode.MORPHING) { + if (uniformState._mode !== SceneMode.SCENE2D && uniformState._mode !== SceneMode.MORPHING && !uniformState._orthographicIn3D) { Matrix4.inverse(uniformState._projection, uniformState._inverseProjectionOIT); } else { - Matrix4.clone(Matrix4.IDENTITY, uniformState._inverseProjectionOIT); + Matrix4.clone(Matrix4.ZERO, uniformState._inverseProjectionOIT); } } } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index a16651234607..559dcf45a59c 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2310,8 +2310,6 @@ define([ // If supported, configure OIT to use the globe depth framebuffer and clear the OIT framebuffer. var useOIT = environmentState.useOIT = !picking && renderTranslucentCommands && defined(scene._oit) && scene._oit.isSupported(); - // TODO ORTHO - environmentState.useOIT = useOIT = useOIT && !(scene.camera.frustum instanceof OrthographicFrustum); if (useOIT) { scene._oit.update(context, scene._globeDepth.framebuffer); scene._oit.clear(context, passState, clearColor); diff --git a/Source/Shaders/Builtin/Functions/alphaWeight.glsl b/Source/Shaders/Builtin/Functions/alphaWeight.glsl index 74913da8c59e..d2d2ecddd9d9 100644 --- a/Source/Shaders/Builtin/Functions/alphaWeight.glsl +++ b/Source/Shaders/Builtin/Functions/alphaWeight.glsl @@ -4,7 +4,7 @@ float czm_alphaWeight(float a) { float z; - if (czm_sceneMode != czm_sceneMode2D) + if (all(equal(czm_inverseProjectionOIT, mat4(0.0)))) { 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; @@ -17,7 +17,7 @@ float czm_alphaWeight(float a) { 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)))); From 7b31fb9fa0bd41003a3f7b18518782a4a27f4c29 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 16 Feb 2017 13:44:33 -0500 Subject: [PATCH 12/46] Another fix for OIT. --- Source/Shaders/Builtin/Functions/alphaWeight.glsl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Shaders/Builtin/Functions/alphaWeight.glsl b/Source/Shaders/Builtin/Functions/alphaWeight.glsl index d2d2ecddd9d9..71cb44145cfc 100644 --- a/Source/Shaders/Builtin/Functions/alphaWeight.glsl +++ b/Source/Shaders/Builtin/Functions/alphaWeight.glsl @@ -4,7 +4,12 @@ float czm_alphaWeight(float a) { float z; - if (all(equal(czm_inverseProjectionOIT, mat4(0.0)))) + bool mode2D = all(equal(czm_inverseProjectionOIT[0], vec4(0.0))) && + all(equal(czm_inverseProjectionOIT[1], vec4(0.0))) && + all(equal(czm_inverseProjectionOIT[2], vec4(0.0))) && + all(equal(czm_inverseProjectionOIT[3], vec4(0.0))); + + if (!mode2D) { 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; From 08b1ff466036201a502d4a44d40f49a7daebc2a6 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 17 Feb 2017 14:46:53 -0500 Subject: [PATCH 13/46] Only adjust orthographic frustum width on zoom. Makes panning and tilting smoother. --- Apps/Sandcastle/gallery/Hello World.html | 16 +++++++++++++--- Source/Scene/Camera.js | 13 +++++++++++-- Source/Scene/ScreenSpaceCameraController.js | 8 ++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index 7ab0d4c94312..9ca3caae5910 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -30,15 +30,25 @@ var viewer = new Cesium.Viewer('cesiumContainer'); var camera = viewer.camera; -var frustum = camera.frustum = new Cesium.OrthographicFrustum(); -frustum.width = camera.positionCartographic.height; -frustum.aspectRatio = viewer.canvas.clientWidth / viewer.canvas.clientHeight *0.3; camera.position = new Cesium.Cartesian3(1841647.0762382608, -6783164.237195379, 2471753.1074874406); camera.direction = new Cesium.Cartesian3(-0.30125208680184823, 0.7555331113434306, 0.5817361067195737); camera.up = new Cesium.Cartesian3(0.10227563420000141, -0.5809561222843004, 0.8074835469713154); camera.right = new Cesium.Cartesian3(0.9480437093544365, 0.3027535328350511, 0.09774161605670559); +var frustum = camera.frustum = new Cesium.OrthographicFrustum(); +frustum.width = camera.positionCartographic.height; +frustum.aspectRatio = viewer.canvas.clientWidth / viewer.canvas.clientHeight *0.3; + +var modelEntity = viewer.entities.add({ + name : 'milktruck', + position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706), + model : { + uri : '../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.gltf' + } +}); +viewer.zoomTo(modelEntity); + //Sandcastle_End Sandcastle.finishedLoading(); } diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 105993724354..65878efa6f63 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -938,8 +938,6 @@ define([ frustum = this.frustum; if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, this.position); - } else if (frustum instanceof OrthographicFrustum) { - frustum.width = Matrix4.equals(Matrix4.IDENTITY, this._transform) ? this.positionCartographic.height : Cartesian3.magnitude(this.position); } }; @@ -965,6 +963,13 @@ define([ updateMembers(this); }; + function adjustOrthographicFrustum(camera) { + if (!(camera.frustum instanceof OrthographicFrustum)) { + return; + } + camera.frustum.width = Matrix4.equals(Matrix4.IDENTITY, camera.transform) ? camera.positionCartographic.height : Cartesian3.magnitude(camera.position); + } + var scratchSetViewCartesian = new Cartesian3(); var scratchSetViewTransform1 = new Matrix4(); var scratchSetViewTransform2 = new Matrix4(); @@ -988,6 +993,8 @@ define([ Cartesian3.cross(camera.direction, camera.up, camera.right); camera._setTransform(currentTransform); + + adjustOrthographicFrustum(camera); } function setViewCV(camera, position,hpr, convert) { @@ -1980,6 +1987,8 @@ define([ Cartesian3.normalize(this.right, this.right); Cartesian3.cross(this.right, this.direction, this.up); Cartesian3.normalize(this.up, this.up); + + adjustOrthographicFrustum(this); }; var viewRectangle3DCartographic1 = new Cartographic(); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 3d6e974b3ebc..c9faecc9a2a2 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,12 @@ define([ var camera = scene.camera; var mode = scene.mode; + if (camera.frustum instanceof OrthographicFrustum) { + camera.zoomIn(distance); + camera.frustum.width = Math.max(1.0, camera.frustum.width - distance); + return; + } + var sameStartPosition = Cartesian2.equals(startPosition, object._zoomMouseStart); var zoomingOnVector = object._zoomingOnVector; var rotatingZoom = object._rotatingZoom; From 34cf3e415f1bdf53cc748d341a00939020ca39e3 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 17 Feb 2017 16:06:28 -0500 Subject: [PATCH 14/46] Fix viewing a rectangle in 3d. --- Source/Scene/Camera.js | 87 ++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 65878efa6f63..70fbcf9ee9a5 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -2096,45 +2096,58 @@ define([ Cartesian3.normalize(right, right); var up = Cartesian3.cross(right, direction, cameraRF.up); - var tanPhi; - var tanTheta; + var d; if (camera.frustum instanceof OrthographicFrustum) { - // TODO ORTHO - tanPhi = tanTheta = 0.5; + 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; + } + + d = Math.max(rightScalar, topScalar); } else { - tanPhi = Math.tan(camera.frustum.fovy * 0.5); - tanTheta = camera.frustum.aspectRatio * tanPhi; - } - - 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)); + 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); From 4d92e0068e82b95ce1ece968c08865516e7de44e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 17 Feb 2017 17:41:13 -0500 Subject: [PATCH 15/46] Add projection picker widget. Still need svgs. --- Apps/Sandcastle/gallery/Hello World.html | 21 --- Source/Scene/Camera.js | 15 +- .../ProjectionPicker/ProjectionPicker.css | 59 ++++++ .../ProjectionPicker/ProjectionPicker.js | 175 ++++++++++++++++++ .../ProjectionPickerViewModel.js | 171 +++++++++++++++++ Source/Widgets/Viewer/Viewer.js | 28 +++ Source/Widgets/widgets.css | 1 + 7 files changed, 440 insertions(+), 30 deletions(-) create mode 100644 Source/Widgets/ProjectionPicker/ProjectionPicker.css create mode 100644 Source/Widgets/ProjectionPicker/ProjectionPicker.js create mode 100644 Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js diff --git a/Apps/Sandcastle/gallery/Hello World.html b/Apps/Sandcastle/gallery/Hello World.html index 9ca3caae5910..640e8e317705 100644 --- a/Apps/Sandcastle/gallery/Hello World.html +++ b/Apps/Sandcastle/gallery/Hello World.html @@ -28,27 +28,6 @@ 'use strict'; //Sandcastle_Begin var viewer = new Cesium.Viewer('cesiumContainer'); - -var camera = viewer.camera; - -camera.position = new Cesium.Cartesian3(1841647.0762382608, -6783164.237195379, 2471753.1074874406); -camera.direction = new Cesium.Cartesian3(-0.30125208680184823, 0.7555331113434306, 0.5817361067195737); -camera.up = new Cesium.Cartesian3(0.10227563420000141, -0.5809561222843004, 0.8074835469713154); -camera.right = new Cesium.Cartesian3(0.9480437093544365, 0.3027535328350511, 0.09774161605670559); - -var frustum = camera.frustum = new Cesium.OrthographicFrustum(); -frustum.width = camera.positionCartographic.height; -frustum.aspectRatio = viewer.canvas.clientWidth / viewer.canvas.clientHeight *0.3; - -var modelEntity = viewer.entities.add({ - name : 'milktruck', - position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706), - model : { - uri : '../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.gltf' - } -}); -viewer.zoomTo(modelEntity); - //Sandcastle_End Sandcastle.finishedLoading(); } diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 70fbcf9ee9a5..a2affff2dbdf 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -909,10 +909,8 @@ define([ updateFrustum = this._mode === SceneMode.SCENE2D; } - var frustum; - var ratio; if (updateFrustum) { - frustum = this._max2Dfrustum = this.frustum.clone(); + var frustum = this._max2Dfrustum = this.frustum.clone(); //>>includeStart('debug', pragmas.debug); if (!defined(frustum.left) || !defined(frustum.right) || !defined(frustum.top) || !defined(frustum.bottom)) { @@ -921,24 +919,23 @@ define([ //>>includeEnd('debug'); var maxZoomOut = 2.0; - ratio = frustum.top / frustum.right; + var ratio = frustum.top / frustum.right; frustum.right = this._maxCoord.x * maxZoomOut; frustum.left = -frustum.right; frustum.top = ratio * frustum.right; frustum.bottom = -frustum.top; } + if (this._mode === SceneMode.SCENE2D) { + clampMove2D(this, this.position); + } + var globe = this._scene.globe; var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0); if (this._suspendTerrainAdjustment) { this._suspendTerrainAdjustment = !globeFinishedUpdating; } this._adjustHeightForTerrain(); - - frustum = this.frustum; - if (this._mode === SceneMode.SCENE2D) { - clampMove2D(this, this.position); - } }; var setTransformPosition = new Cartesian3(); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css new file mode 100644 index 000000000000..40e0a409e353 --- /dev/null +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css @@ -0,0 +1,59 @@ +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-slide-svg { + -webkit-transition: left 2s; + -moz-transition: left 2s; + transition: left 2s; + top: 0; + left: 0; +} + +.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..3f3ef0e28d72 --- /dev/null +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -0,0 +1,175 @@ +/*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 flatMapPath = 'm 2.9825053,17.550598 0,1.368113 0,26.267766 0,1.368113 1.36811,0 54.9981397,0 1.36811,0 0,-1.368113 0,-26.267766 0,-1.368113 -1.36811,0 -54.9981397,0 -1.36811,0 z m 2.73623,2.736226 10.3292497,0 0,10.466063 -10.3292497,0 0,-10.466063 z m 13.0654697,0 11.69737,0 0,10.466063 -11.69737,0 0,-10.466063 z m 14.43359,0 11.69737,0 0,10.466063 -11.69737,0 0,-10.466063 z m 14.43359,0 10.32926,0 0,10.466063 -10.32926,0 0,-10.466063 z m -41.9326497,13.202288 10.3292497,0 0,10.329252 -10.3292497,0 0,-10.329252 z m 13.0654697,0 11.69737,0 0,10.329252 -11.69737,0 0,-10.329252 z m 14.43359,0 11.69737,0 0,10.329252 -11.69737,0 0,-10.329252 z m 14.43359,0 10.32926,0 0,10.329252 -10.32926,0 0,-10.329252 z'; + var columbusViewPath = 'm 14.723969,17.675598 -0.340489,0.817175 -11.1680536,26.183638 -0.817175,1.872692 2.076986,0 54.7506996,0 2.07698,0 -0.81717,-1.872692 -11.16805,-26.183638 -0.34049,-0.817175 -0.91933,0 -32.414586,0 -0.919322,0 z m 1.838643,2.723916 6.196908,0 -2.928209,10.418977 -7.729111,0 4.460412,-10.418977 z m 9.02297,0 4.903049,0 0,10.418977 -7.831258,0 2.928209,-10.418977 z m 7.626964,0 5.584031,0 2.62176,10.418977 -8.205791,0 0,-10.418977 z m 8.410081,0 5.51593,0 4.46042,10.418977 -7.38863,0 -2.58772,-10.418977 z m -30.678091,13.142892 8.103649,0 -2.89416,10.282782 -9.6018026,0 4.3923136,-10.282782 z m 10.929711,0 8.614384,0 0,10.282782 -11.508544,0 2.89416,-10.282782 z m 11.338299,0 8.852721,0 2.58772,10.282782 -11.440441,0 0,-10.282782 z m 11.678781,0 7.86531,0 4.39231,10.282782 -9.6699,0 -2.58772,-10.282782 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:

+ * // Note: This code assumes you already have a Scene instance. + * + * var projectionPicker = new Cesium.ProjectionPicker('projectionPickerContainer', scene); + */ + function ProjectionPicker(container, scene) { + //>>includeStart('debug', pragmas.debug); + if (!defined(container)) { + throw new DeveloperError('container is required.'); + } + if (!defined(scene)) { + throw new DeveloperError('scene is required.'); + } + //>>includeEnd('debug'); + + container = getElement(container); + + var viewModel = new ProjectionPickerViewModel(scene); + + viewModel._perspectivePath = flatMapPath; + viewModel._orthographicPath = columbusViewPath; + + var wrapper = document.createElement('span'); + wrapper.className = 'cesium-projectionPicker-wrapper cesium-toolbar-button'; + container.appendChild(wrapper); + + var button = document.createElement('button'); + button.type = 'button'; + button.className = 'cesium-button cesium-toolbar-button'; + button.setAttribute('data-bind', '\ +css: { "cesium-projectionPicker-buttonPerspective": !_orthographic,\ + "cesium-projectionPicker-buttonOrthographic": _orthographic,\ + "cesium-projectionPicker-selected": dropDownVisible },\ +attr: { title: selectedTooltip },\ +click: toggleDropDown'); + button.innerHTML = '\ +\ +'; + wrapper.appendChild(button); + + var perspectiveButton = document.createElement('button'); + perspectiveButton.type = 'button'; + perspectiveButton.className = 'cesium-button cesium-toolbar-button cesium-projectionPicker-dropDown-icon'; + perspectiveButton.setAttribute('data-bind', '\ +css: { "cesium-projectionPicker-visible" : (dropDownVisible && _orthographic),\ + "cesium-projectionPicker-none" : !_orthographic,\ + "cesium-projectionPicker-hidden" : !dropDownVisible },\ +attr: { title: tooltipPerspective },\ +click: switchToPerspective,\ +cesiumSvgPath: { path: _perspectivePath, width: 64, height: 64 }'); + wrapper.appendChild(perspectiveButton); + + var orthographicButton = document.createElement('button'); + orthographicButton.type = 'button'; + orthographicButton.className = 'cesium-button cesium-toolbar-button cesium-projectionPicker-dropDown-icon'; + orthographicButton.setAttribute('data-bind', '\ +css: { "cesium-projectionPicker-visible" : (dropDownVisible && !_orthographic),\ + "cesium-projectionPicker-none" : _orthographic,\ + "cesium-projectionPicker-hidden" : !dropDownVisible},\ +attr: { title: tooltipOrthographic },\ +click: switchToOrthographic,\ +cesiumSvgPath: { path: _orthographicPath, width: 64, height: 64 }'); + wrapper.appendChild(orthographicButton); + + knockout.applyBindings(viewModel, wrapper); + + this._viewModel = viewModel; + this._container = container; + this._wrapper = wrapper; + + this._closeDropDown = function(e) { + if (!wrapper.contains(e.target)) { + viewModel.dropDownVisible = false; + } + }; + if (FeatureDetection.supportsPointerEvents()) { + document.addEventListener('pointerdown', this._closeDropDown, true); + } else { + document.addEventListener('mousedown', this._closeDropDown, true); + document.addEventListener('touchstart', this._closeDropDown, true); + } + } + + defineProperties(ProjectionPicker.prototype, { + /** + * Gets the parent container. + * @memberof ProjectionPicker.prototype + * + * @type {Element} + */ + container : { + get : function() { + return this._container; + } + }, + + /** + * Gets the view model. + * @memberof ProjectionPicker.prototype + * + * @type {ProjectionPickerViewModel} + */ + viewModel : { + get : function() { + return this._viewModel; + } + } + }); + + /** + * @returns {Boolean} true if the object has been destroyed, false otherwise. + */ + ProjectionPicker.prototype.isDestroyed = function() { + return false; + }; + + /** + * Destroys the widget. Should be called if permanently + * removing the widget from layout. + */ + ProjectionPicker.prototype.destroy = function() { + this._viewModel.destroy(); + + if (FeatureDetection.supportsPointerEvents()) { + document.removeEventListener('pointerdown', this._closeDropDown, true); + } else { + document.removeEventListener('mousedown', this._closeDropDown, true); + document.removeEventListener('touchstart', this._closeDropDown, true); + } + + knockout.cleanNode(this._wrapper); + this._container.removeChild(this._wrapper); + + return destroyObject(this); + }; + + return ProjectionPicker; +}); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js new file mode 100644 index 000000000000..956a86ea4713 --- /dev/null +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -0,0 +1,171 @@ +/*global define*/ +define([ + '../../Core/Cartesian3', + '../../Core/defaultValue', + '../../Core/defined', + '../../Core/defineProperties', + '../../Core/destroyObject', + '../../Core/DeveloperError', + '../../Core/Math', + '../../Core/Matrix4', + '../../Scene/OrthographicFrustum', + '../../Scene/PerspectiveFrustum', + '../../ThirdParty/knockout', + '../createCommand' + ], function( + Cartesian3, + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + CesiumMath, + Matrix4, + OrthographicFrustum, + PerspectiveFrustum, + knockout, + createCommand) { + 'use strict'; + + /** + * The view model for {@link ProjectionPicker}. + * @alias ProjectionPickerViewModel + * @constructor + * + * @param {Scene} scene The Scene to switch projections. + */ + function ProjectionPickerViewModel(scene) { + //>>includeStart('debug', pragmas.debug); + if (!defined(scene)) { + throw new DeveloperError('scene is required.'); + } + //>>includeEnd('debug'); + + this._scene = scene; + this._orthographic = scene.camera.frustum instanceof OrthographicFrustum; + + /** + * Gets or sets whether the button drop-down is currently visible. This property is observable. + * @type {Boolean} + * @default false + */ + this.dropDownVisible = false; + + /** + * Gets or sets the perspective projection tooltip. This property is observable. + * @type {String} + * @default 'Perspective Projection' + */ + this.tooltipPerspective = 'Perspective Projection'; + + /** + * Gets or sets the orthographic projection tooltip. This property is observable. + * @type {String} + * @default 'Orthographic Projection' + */ + this.tooltipOrthographic = 'Orthographic Projection'; + + knockout.track(this, ['_orthographic', 'dropDownVisible', 'tooltipPerspective', 'tooltipOrthographic']); + + /** + * Gets the currently active tooltip. This property is observable. + * @type {String} + */ + this.selectedTooltip = undefined; + + var that = this; + knockout.defineProperty(this, 'selectedTooltip', function() { + if (that._scene.camera.frustum instanceof OrthographicFrustum) { + return that.tooltipOrthographic; + } + return that.tooltipPerspective; + }); + + this._toggleDropDown = createCommand(function() { + that.dropDownVisible = !that.dropDownVisible; + }); + + this._switchToPerspective = createCommand(function() { + var scene = that._scene; + var camera = that._scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + that._orthographic = false; + }); + + this._switchToOrthographic = createCommand(function() { + var scene = that._scene; + var camera = that._scene.camera; + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight * 0.3; + camera.frustum.width = Matrix4.equals(Matrix4.IDENTITY, camera.transform) ? camera.positionCartographic.height : Cartesian3.magnitude(camera.position); + that._orthographic = true; + }); + } + + defineProperties(ProjectionPickerViewModel.prototype, { + /** + * Gets the scene + * @memberof ProjectionPickerViewModel.prototype + * @type {Scene} + */ + scene : { + get : function() { + return this._scene; + } + }, + + /** + * Gets the command to toggle the drop down box. + * @memberof ProjectionPickerViewModel.prototype + * + * @type {Command} + */ + toggleDropDown : { + get : function() { + return this._toggleDropDown; + } + }, + + /** + * Gets the command to switch to a perspective projection. + * @memberof ProjectionPickerViewModel.prototype + * + * @type {Command} + */ + switchToPerspective : { + get : function() { + return this._switchToPerspective; + } + }, + + /** + * Gets the command to switch to orthographic projection. + * @memberof ProjectionPickerViewModel.prototype + * + * @type {Command} + */ + switchToOrthographic : { + get : function() { + return this._switchToOrthographic; + } + } + }); + + /** + * @returns {Boolean} true if the object has been destroyed, false otherwise. + */ + ProjectionPickerViewModel.prototype.isDestroyed = function() { + return false; + }; + + /** + * Destroys the view model. + */ + ProjectionPickerViewModel.prototype.destroy = function() { + destroyObject(this); + }; + + return ProjectionPickerViewModel; +}); diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 63a858b38c9b..2f0c6c8046d7 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -36,6 +36,7 @@ define([ '../HomeButton/HomeButton', '../InfoBox/InfoBox', '../NavigationHelpButton/NavigationHelpButton', + '../ProjectionPicker/ProjectionPicker', '../SceneModePicker/SceneModePicker', '../SelectionIndicator/SelectionIndicator', '../subscribeAndEvaluate', @@ -78,6 +79,7 @@ define([ HomeButton, InfoBox, NavigationHelpButton, + ProjectionPicker, SceneModePicker, SelectionIndicator, subscribeAndEvaluate, @@ -184,6 +186,7 @@ define([ var geocoder = viewer._geocoder; var homeButton = viewer._homeButton; var sceneModePicker = viewer._sceneModePicker; + var projectionPicker = viewer._projectionPicker; var baseLayerPicker = viewer._baseLayerPicker; var animation = viewer._animation; var timeline = viewer._timeline; @@ -202,6 +205,9 @@ define([ if(defined(sceneModePicker)) { sceneModePicker.container.style.visibility = visibility; } + if(defined(projectionPicker)) { + projectionPicker.container.style.visibility = visibility; + } if(defined(baseLayerPicker)) { baseLayerPicker.container.style.visibility = visibility; } @@ -507,6 +513,11 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to sceneModePicker = new SceneModePicker(toolbar, cesiumWidget.scene); } + var projectionPicker; + if (!defined(options.projectionPicker) || options.projectionPicker !== false) { + projectionPicker = new ProjectionPicker(toolbar, cesiumWidget.scene); + } + // BaseLayerPicker var baseLayerPicker; var baseLayerPickerDropDown; @@ -638,6 +649,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._toolbar = toolbar; this._homeButton = homeButton; this._sceneModePicker = sceneModePicker; + this._projectionPicker = projectionPicker; this._baseLayerPicker = baseLayerPicker; this._navigationHelpButton = navigationHelpButton; this._animation = animation; @@ -804,6 +816,18 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to } }, + /** + * Gets the ProjectionPicker. + * @memberof Viewer.prototype + * @type {ProjectionPicker} + * @readonly + */ + projectionPicker : { + get : function() { + return this._projectionPicker; + } + }, + /** * Gets the BaseLayerPicker. * @memberof Viewer.prototype @@ -1397,6 +1421,10 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to this._sceneModePicker = this._sceneModePicker.destroy(); } + if (defined(this._projectionPicker)) { + this._projectionPicker = this._projectionPicker.destroy(); + } + if (defined(this._baseLayerPicker)) { this._baseLayerPicker = this._baseLayerPicker.destroy(); } diff --git a/Source/Widgets/widgets.css b/Source/Widgets/widgets.css index 324a8a597af3..f291135b644a 100644 --- a/Source/Widgets/widgets.css +++ b/Source/Widgets/widgets.css @@ -8,6 +8,7 @@ @import url(./Geocoder/Geocoder.css); @import url(./InfoBox/InfoBox.css); @import url(./SceneModePicker/SceneModePicker.css); +@import url(./ProjectionPicker/ProjectionPicker.css); @import url(./PerformanceWatchdog/PerformanceWatchdog.css); @import url(./NavigationHelpButton/NavigationHelpButton.css); @import url(./SelectionIndicator/SelectionIndicator.css); From 50f97b070a48bdcbf5d0a7f14de549d66fc28f72 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 20 Feb 2017 14:18:27 -0500 Subject: [PATCH 16/46] Fix mouse input camera interaction when in columbus view with an orthographic projection. --- Source/Scene/Camera.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index a2affff2dbdf..90ef5cd861cb 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -2391,6 +2391,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; } From 4ebcc64ba3a8880f794b090716ff95b85cc61ac8 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 20 Feb 2017 15:32:54 -0500 Subject: [PATCH 17/46] Tweak adjusting the frustum width based on depth buffer pick or terrain pick. Fix flying to rectangle in columbus view. --- Source/Scene/Camera.js | 65 ++++++++++++++++--- .../ProjectionPickerViewModel.js | 1 + 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 90ef5cd861cb..21d43af4bae1 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -960,12 +960,49 @@ define([ updateMembers(this); }; - function adjustOrthographicFrustum(camera) { - if (!(camera.frustum instanceof OrthographicFrustum)) { + var scratchDepthIntersection = new Cartesian3(); + var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2(); + var pickGlobeScratchRay = new Ray(); + var scratchRayIntersection = new Cartesian3(); + + Camera.prototype._adjustOrthographicFrustum = function() { + if (!(this.frustum instanceof OrthographicFrustum)) { return; } - camera.frustum.width = Matrix4.equals(Matrix4.IDENTITY, camera.transform) ? camera.positionCartographic.height : Cartesian3.magnitude(camera.position); - } + + if (!Matrix4.equals(Matrix4.IDENTITY, this.transform)) { + this.frustum.width = this.positionCartographic.height; + return; + } + + var scene = this._scene; + var globe = scene._globe; + + var depthIntersection; + var rayIntersection; + + if (defined(globe)) { + var mousePosition = scratchAdjustOrtghographicFrustumMousePosition; + mousePosition.x = scene.drawingBufferWidth / 2.0; + mousePosition.y = scene.drawingBufferHeight / 2.0; + + if (scene.pickPositionSupported) { + depthIntersection = scene.pickPositionWorldCoordinates(mousePosition, scratchDepthIntersection); + } + + var ray = this.getPickRay(mousePosition, pickGlobeScratchRay); + rayIntersection = globe.pick(ray, scene, scratchRayIntersection); + + var pickDistance = defined(depthIntersection) ? Cartesian3.distance(depthIntersection, this.positionWC) : Number.POSITIVE_INFINITY; + var rayDistance = defined(rayIntersection) ? Cartesian3.distance(rayIntersection, this.positionWC) : Number.POSITIVE_INFINITY; + + this.frustum.width = pickDistance < rayDistance ? pickDistance : rayDistance; + } + + if (!defined(globe) || (!defined(depthIntersection) && !defined(rayIntersection))) { + this.frustum.width = Cartesian3.magnitude(this.position); + } + }; var scratchSetViewCartesian = new Cartesian3(); var scratchSetViewTransform1 = new Matrix4(); @@ -991,7 +1028,7 @@ define([ camera._setTransform(currentTransform); - adjustOrthographicFrustum(camera); + camera._adjustOrthographicFrustum(); } function setViewCV(camera, position,hpr, convert) { @@ -1016,6 +1053,8 @@ define([ Cartesian3.cross(camera.direction, camera.up, camera.right); camera._setTransform(currentTransform); + + camera._adjustOrthographicFrustum(); } function setView2D(camera, position, hpr, convert) { @@ -1985,7 +2024,7 @@ define([ Cartesian3.cross(this.right, this.direction, this.up); Cartesian3.normalize(this.up, this.up); - adjustOrthographicFrustum(this); + this._adjustOrthographicFrustum(); }; var viewRectangle3DCartographic1 = new Cartographic(); @@ -2174,12 +2213,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; } diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index 956a86ea4713..37db630109ce 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -95,6 +95,7 @@ define([ }); this._switchToOrthographic = createCommand(function() { + // TODO: set width based on distance in depth buffer var scene = that._scene; var camera = that._scene.camera; camera.frustum = new OrthographicFrustum(); From f7ee9198d73d46ccb5817f6743237cd368d597ba Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 20 Feb 2017 16:21:08 -0500 Subject: [PATCH 18/46] Fix issue with zoom where the camera wouldn't focus on the target position. --- Source/Scene/Camera.js | 4 ++-- Source/Scene/ScreenSpaceCameraController.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 21d43af4bae1..e48ad236b552 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -971,7 +971,7 @@ define([ } if (!Matrix4.equals(Matrix4.IDENTITY, this.transform)) { - this.frustum.width = this.positionCartographic.height; + this.frustum.width = Cartesian3.magnitude(this.position); return; } @@ -1000,7 +1000,7 @@ define([ } if (!defined(globe) || (!defined(depthIntersection) && !defined(rayIntersection))) { - this.frustum.width = Cartesian3.magnitude(this.position); + this.frustum.width = this.positionCartographic.height; } }; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index c9faecc9a2a2..0f9c7f8209f5 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -487,8 +487,10 @@ define([ var mode = scene.mode; if (camera.frustum instanceof OrthographicFrustum) { - camera.zoomIn(distance); - camera.frustum.width = Math.max(1.0, camera.frustum.width - distance); + if (Math.abs(distance) > 0.0) { + camera.zoomIn(distance); + camera._adjustOrthographicFrustum(); + } return; } From 303b568cd85913f84488141096af225b46b98b4f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 20 Feb 2017 16:50:05 -0500 Subject: [PATCH 19/46] Fix height and zoom when switching to orthographic mode. --- .../ProjectionPickerViewModel.js | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index 37db630109ce..e17fadd439ad 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../../Core/Cartesian2', '../../Core/Cartesian3', '../../Core/defaultValue', '../../Core/defined', @@ -8,11 +9,13 @@ define([ '../../Core/DeveloperError', '../../Core/Math', '../../Core/Matrix4', + '../../Core/Ray', '../../Scene/OrthographicFrustum', '../../Scene/PerspectiveFrustum', '../../ThirdParty/knockout', '../createCommand' ], function( + Cartesian2, Cartesian3, defaultValue, defined, @@ -21,6 +24,7 @@ define([ DeveloperError, CesiumMath, Matrix4, + Ray, OrthographicFrustum, PerspectiveFrustum, knockout, @@ -94,13 +98,47 @@ define([ that._orthographic = false; }); + var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2(); + var scratchDepthIntersection = new Cartesian3(); + var pickGlobeScratchRay = new Ray(); + var scratchRayIntersection = new Cartesian3(); + this._switchToOrthographic = createCommand(function() { - // TODO: set width based on distance in depth buffer var scene = that._scene; var camera = that._scene.camera; + var globe = scene._globe; + + var distance; + if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) { + distance = Cartesian3.magnitude(camera.position); + } else if (defined(globe)) { + var depthIntersection; + var rayIntersection; + + var mousePosition = scratchAdjustOrtghographicFrustumMousePosition; + mousePosition.x = scene.drawingBufferWidth / 2.0; + mousePosition.y = scene.drawingBufferHeight / 2.0; + + if (scene.pickPositionSupported) { + depthIntersection = scene.pickPositionWorldCoordinates(mousePosition, scratchDepthIntersection); + } + + var ray = camera.getPickRay(mousePosition, pickGlobeScratchRay); + rayIntersection = globe.pick(ray, scene, scratchRayIntersection); + + var pickDistance = defined(depthIntersection) ? Cartesian3.distance(depthIntersection, camera.positionWC) : Number.POSITIVE_INFINITY; + var rayDistance = defined(rayIntersection) ? Cartesian3.distance(rayIntersection, camera.positionWC) : Number.POSITIVE_INFINITY; + + distance = pickDistance < rayDistance ? pickDistance : rayDistance; + } + + if (!defined(distance)) { + distance = camera.positionCartographic.height; + } + camera.frustum = new OrthographicFrustum(); camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight * 0.3; - camera.frustum.width = Matrix4.equals(Matrix4.IDENTITY, camera.transform) ? camera.positionCartographic.height : Cartesian3.magnitude(camera.position); + camera.frustum.width = distance; that._orthographic = true; }); } From b059be9d5bdd8dd63c70ef7b143af68b77fa3363 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 21 Feb 2017 16:46:06 -0500 Subject: [PATCH 20/46] New icons for perspective and orthographic. --- Source/Widgets/ProjectionPicker/ProjectionPicker.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js index 3f3ef0e28d72..3c666c5f0c8a 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -19,8 +19,8 @@ define([ ProjectionPickerViewModel) { 'use strict'; - var flatMapPath = 'm 2.9825053,17.550598 0,1.368113 0,26.267766 0,1.368113 1.36811,0 54.9981397,0 1.36811,0 0,-1.368113 0,-26.267766 0,-1.368113 -1.36811,0 -54.9981397,0 -1.36811,0 z m 2.73623,2.736226 10.3292497,0 0,10.466063 -10.3292497,0 0,-10.466063 z m 13.0654697,0 11.69737,0 0,10.466063 -11.69737,0 0,-10.466063 z m 14.43359,0 11.69737,0 0,10.466063 -11.69737,0 0,-10.466063 z m 14.43359,0 10.32926,0 0,10.466063 -10.32926,0 0,-10.466063 z m -41.9326497,13.202288 10.3292497,0 0,10.329252 -10.3292497,0 0,-10.329252 z m 13.0654697,0 11.69737,0 0,10.329252 -11.69737,0 0,-10.329252 z m 14.43359,0 11.69737,0 0,10.329252 -11.69737,0 0,-10.329252 z m 14.43359,0 10.32926,0 0,10.329252 -10.32926,0 0,-10.329252 z'; - var columbusViewPath = 'm 14.723969,17.675598 -0.340489,0.817175 -11.1680536,26.183638 -0.817175,1.872692 2.076986,0 54.7506996,0 2.07698,0 -0.81717,-1.872692 -11.16805,-26.183638 -0.34049,-0.817175 -0.91933,0 -32.414586,0 -0.919322,0 z m 1.838643,2.723916 6.196908,0 -2.928209,10.418977 -7.729111,0 4.460412,-10.418977 z m 9.02297,0 4.903049,0 0,10.418977 -7.831258,0 2.928209,-10.418977 z m 7.626964,0 5.584031,0 2.62176,10.418977 -8.205791,0 0,-10.418977 z m 8.410081,0 5.51593,0 4.46042,10.418977 -7.38863,0 -2.58772,-10.418977 z m -30.678091,13.142892 8.103649,0 -2.89416,10.282782 -9.6018026,0 4.3923136,-10.282782 z m 10.929711,0 8.614384,0 0,10.282782 -11.508544,0 2.89416,-10.282782 z m 11.338299,0 8.852721,0 2.58772,10.282782 -11.440441,0 0,-10.282782 z m 11.678781,0 7.86531,0 4.39231,10.282782 -9.6699,0 -2.58772,-10.282782 z'; + 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. @@ -54,8 +54,8 @@ define([ var viewModel = new ProjectionPickerViewModel(scene); - viewModel._perspectivePath = flatMapPath; - viewModel._orthographicPath = columbusViewPath; + viewModel._perspectivePath = perspectivePath; + viewModel._orthographicPath = orthographicPath; var wrapper = document.createElement('span'); wrapper.className = 'cesium-projectionPicker-wrapper cesium-toolbar-button'; From 51b53b8aad031bfce7462216a2677bebed933294 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 21 Feb 2017 17:42:53 -0500 Subject: [PATCH 21/46] Rename OrthographicFrustumSpec -> OrthographicOffCenterFrustumSpec. --- ...ographicFrustumSpec.js => OrthographicOffCenterFrustumSpec.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Specs/Scene/{OrthographicFrustumSpec.js => OrthographicOffCenterFrustumSpec.js} (100%) diff --git a/Specs/Scene/OrthographicFrustumSpec.js b/Specs/Scene/OrthographicOffCenterFrustumSpec.js similarity index 100% rename from Specs/Scene/OrthographicFrustumSpec.js rename to Specs/Scene/OrthographicOffCenterFrustumSpec.js From fbf5d7763db4b7c0d142f24c062d6ab548168594 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 21 Feb 2017 17:57:43 -0500 Subject: [PATCH 22/46] Fix broken tests. --- .../gallery/development/Shadows.html | 2 +- Specs/Renderer/AutomaticUniformSpec.js | 14 +++--- Specs/Scene/BillboardCollectionSpec.js | 6 +-- Specs/Scene/CameraFlightPathSpec.js | 6 +-- Specs/Scene/CameraSpec.js | 44 +++++++++---------- .../Scene/OrthographicOffCenterFrustumSpec.js | 10 ++--- .../Scene/ScreenSpaceCameraControllerSpec.js | 6 +-- Specs/Scene/ShadowMapSpec.js | 6 +-- 8 files changed, 47 insertions(+), 47 deletions(-) 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/Specs/Renderer/AutomaticUniformSpec.js b/Specs/Renderer/AutomaticUniformSpec.js index c0398499b01b..1923a832734b 100644 --- a/Specs/Renderer/AutomaticUniformSpec.js +++ b/Specs/Renderer/AutomaticUniformSpec.js @@ -6,7 +6,7 @@ defineSuite([ 'Core/Matrix4', 'Renderer/Pass', 'Renderer/Texture', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/SceneMode', 'Specs/createCamera', 'Specs/createContext', @@ -18,7 +18,7 @@ defineSuite([ Matrix4, Pass, Texture, - OrthographicFrustum, + OrthographicOffCenterFrustum, SceneMode, createCamera, createContext, @@ -444,10 +444,10 @@ defineSuite([ var fs = 'void main() { ' + - ' bool b0 = (czm_inverseProjectionOIT[0][0] == 1.0) && (czm_inverseProjectionOIT[1][0] == 0.0) && (czm_inverseProjectionOIT[2][0] == 0.0) && (czm_inverseProjectionOIT[3][0] == 0.0); ' + - ' bool b1 = (czm_inverseProjectionOIT[0][1] == 0.0) && (czm_inverseProjectionOIT[1][1] == 1.0) && (czm_inverseProjectionOIT[2][1] == 0.0) && (czm_inverseProjectionOIT[3][1] == 0.0); ' + - ' bool b2 = (czm_inverseProjectionOIT[0][2] == 0.0) && (czm_inverseProjectionOIT[1][2] == 0.0) && (czm_inverseProjectionOIT[2][2] == 1.0) && (czm_inverseProjectionOIT[3][2] == 0.0); ' + - ' bool b3 = (czm_inverseProjectionOIT[0][3] == 0.0) && (czm_inverseProjectionOIT[1][3] == 0.0) && (czm_inverseProjectionOIT[2][3] == 0.0) && (czm_inverseProjectionOIT[3][3] == 1.0); ' + + ' bool b0 = (czm_inverseProjectionOIT[0][0] == 0.0) && (czm_inverseProjectionOIT[1][0] == 0.0) && (czm_inverseProjectionOIT[2][0] == 0.0) && (czm_inverseProjectionOIT[3][0] == 0.0); ' + + ' bool b1 = (czm_inverseProjectionOIT[0][1] == 0.0) && (czm_inverseProjectionOIT[1][1] == 0.0) && (czm_inverseProjectionOIT[2][1] == 0.0) && (czm_inverseProjectionOIT[3][1] == 0.0); ' + + ' bool b2 = (czm_inverseProjectionOIT[0][2] == 0.0) && (czm_inverseProjectionOIT[1][2] == 0.0) && (czm_inverseProjectionOIT[2][2] == 0.0) && (czm_inverseProjectionOIT[3][2] == 0.0); ' + + ' bool b3 = (czm_inverseProjectionOIT[0][3] == 0.0) && (czm_inverseProjectionOIT[1][3] == 0.0) && (czm_inverseProjectionOIT[2][3] == 0.0) && (czm_inverseProjectionOIT[3][3] == 0.0); ' + ' gl_FragColor = vec4(b0 && b1 && b2 && b3); ' + '}'; expect({ @@ -1195,7 +1195,7 @@ defineSuite([ it('has czm_eyeHeight2D in Scene2D', function() { var us = context.uniformState; var camera = createCamera(); - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index d91a50742312..61a1808cd7cd 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -16,7 +16,7 @@ defineSuite([ 'Scene/BlendOption', 'Scene/HeightReference', 'Scene/HorizontalOrigin', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/TextureAtlas', 'Scene/VerticalOrigin', 'Specs/createGlobe', @@ -40,7 +40,7 @@ defineSuite([ BlendOption, HeightReference, HorizontalOrigin, - OrthographicFrustum, + OrthographicOffCenterFrustum, TextureAtlas, VerticalOrigin, createGlobe, @@ -1357,7 +1357,7 @@ defineSuite([ }); var maxRadii = ellipsoid.maximumRadius; - var orthoFrustum = new OrthographicFrustum(); + var orthoFrustum = new OrthographicOffCenterFrustum(); orthoFrustum.right = maxRadii * Math.PI; orthoFrustum.left = -orthoFrustum.right; orthoFrustum.top = orthoFrustum.right; diff --git a/Specs/Scene/CameraFlightPathSpec.js b/Specs/Scene/CameraFlightPathSpec.js index 2b5ecc5dff4e..99819f8d4d95 100644 --- a/Specs/Scene/CameraFlightPathSpec.js +++ b/Specs/Scene/CameraFlightPathSpec.js @@ -3,14 +3,14 @@ defineSuite([ 'Scene/CameraFlightPath', 'Core/Cartesian3', 'Core/Math', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/SceneMode', 'Specs/createScene' ], function( CameraFlightPath, Cartesian3, CesiumMath, - OrthographicFrustum, + OrthographicOffCenterFrustum, SceneMode, createScene) { 'use strict'; @@ -27,7 +27,7 @@ defineSuite([ function createOrthographicFrustum() { var current = scene.camera.frustum; - var f = new OrthographicFrustum(); + var f = new OrthographicOffCenterFrustum(); f.near = current.near; f.far = current.far; diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 87626ef2df70..21c7215496a6 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -18,7 +18,7 @@ defineSuite([ 'Core/WebMercatorProjection', 'Scene/CameraFlightPath', 'Scene/MapMode2D', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/PerspectiveFrustum', 'Scene/SceneMode', 'Scene/TweenCollection' @@ -41,7 +41,7 @@ defineSuite([ WebMercatorProjection, CameraFlightPath, MapMode2D, - OrthographicFrustum, + OrthographicOffCenterFrustum, PerspectiveFrustum, SceneMode, TweenCollection) { @@ -570,7 +570,7 @@ defineSuite([ camera._mode = SceneMode.SCENE2D; camera._projection = projection; - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = maxRadii * Math.PI; frustum.left = -frustum.right; frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); @@ -930,7 +930,7 @@ defineSuite([ }); it('move clamps position in 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1140,7 +1140,7 @@ defineSuite([ }); it('zooms out 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1159,7 +1159,7 @@ defineSuite([ }); it('zooms in 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1178,7 +1178,7 @@ defineSuite([ }); it('clamps zoom out in 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1202,7 +1202,7 @@ defineSuite([ }); it('clamps zoom in in 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1240,7 +1240,7 @@ defineSuite([ it('zooms in throws with undefined OrthogrphicFrustum properties 2D', function() { camera._mode = SceneMode.SCENE2D; - camera.frustum = new OrthographicFrustum(); + camera.frustum = new OrthographicOffCenterFrustum(); expect(function () { camera.zoomIn(zoomAmount); }).toThrowDeveloperError(); @@ -1296,7 +1296,7 @@ defineSuite([ }); it('lookAt in 2D mode', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1321,7 +1321,7 @@ defineSuite([ }); it('lookAt in 2D mode with heading, pitch and range', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1425,7 +1425,7 @@ defineSuite([ }); it('lookAtTransform in 2D mode', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1450,7 +1450,7 @@ defineSuite([ }); it('lookAtTransform in 2D mode with heading, pitch and range', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -1553,7 +1553,7 @@ defineSuite([ }); it('setView rectangle in 2D with larger longitude', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.left = -10.0; frustum.right = 10.0; frustum.bottom = -10.0; @@ -1584,7 +1584,7 @@ defineSuite([ }); it('setView rectangle in 2D with larger latitude', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.left = -10.0; frustum.right = 10.0; frustum.bottom = -10.0; @@ -1680,7 +1680,7 @@ defineSuite([ CesiumMath.PI_OVER_TWO); var projection = new GeographicProjection(); var cam = new Camera(scene); - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = 1.0; frustum.left = -1.0; frustum.top = 1.0; @@ -1797,7 +1797,7 @@ defineSuite([ camera.direction = Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3()); camera.up = Cartesian3.clone(Cartesian3.UNIT_Y); - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = maxRadii * Math.PI; frustum.left = -frustum.right; frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); @@ -1824,7 +1824,7 @@ defineSuite([ camera.direction = Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3()); camera.up = Cartesian3.clone(Cartesian3.UNIT_Y); - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = maxRadii * Math.PI; frustum.left = -frustum.right; frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); @@ -1918,7 +1918,7 @@ defineSuite([ }); it('get pick ray orthographic', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.left = -10.0; frustum.right = 10.0; frustum.bottom = -10.0; @@ -1944,7 +1944,7 @@ defineSuite([ camera._mode = SceneMode.SCENE2D; camera._projection = projection; - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = maxRadii * Math.PI; frustum.left = -frustum.right; frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); @@ -1976,7 +1976,7 @@ defineSuite([ }); it('does not animate in 2D', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 2.0; frustum.left = -2.0; @@ -2452,7 +2452,7 @@ defineSuite([ camera._mode = SceneMode.SCENE2D; - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.left = -10.0; frustum.right = 10.0; frustum.bottom = -10.0; diff --git a/Specs/Scene/OrthographicOffCenterFrustumSpec.js b/Specs/Scene/OrthographicOffCenterFrustumSpec.js index bfac1d812e5a..7897e568b5ac 100644 --- a/Specs/Scene/OrthographicOffCenterFrustumSpec.js +++ b/Specs/Scene/OrthographicOffCenterFrustumSpec.js @@ -1,13 +1,13 @@ /*global defineSuite*/ defineSuite([ - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Cartesian4', 'Core/Math', 'Core/Matrix4' ], function( - OrthographicFrustum, + OrthographicOffCenterFrustum, Cartesian2, Cartesian3, Cartesian4, @@ -18,7 +18,7 @@ defineSuite([ var frustum, planes; beforeEach(function() { - frustum = new OrthographicFrustum(); + frustum = new OrthographicOffCenterFrustum(); frustum.near = 1.0; frustum.far = 3.0; frustum.right = 1.0; @@ -152,7 +152,7 @@ defineSuite([ }); it('throws with undefined frustum parameters', function() { - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); expect(function() { return frustum.projectionMatrix; }).toThrowDeveloperError(); @@ -164,7 +164,7 @@ defineSuite([ }); it('clone with result parameter', function() { - var result = new OrthographicFrustum(); + var result = new OrthographicOffCenterFrustum(); var frustum2 = frustum.clone(result); expect(frustum2).toBe(result); expect(frustum).toEqual(frustum2); diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index ab6d81d87287..3ce5872d71e3 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -14,7 +14,7 @@ defineSuite([ 'Core/Transforms', 'Scene/CameraEventType', 'Scene/MapMode2D', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/SceneMode', 'Specs/createCamera', 'Specs/createCanvas', @@ -34,7 +34,7 @@ defineSuite([ Transforms, CameraEventType, MapMode2D, - OrthographicFrustum, + OrthographicOffCenterFrustum, SceneMode, createCamera, createCanvas, @@ -183,7 +183,7 @@ defineSuite([ }; var maxRadii = ellipsoid.maximumRadius; - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.right = maxRadii * Math.PI; frustum.left = -frustum.right; frustum.top = frustum.right * (canvas.clientHeight / canvas.clientWidth); diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index b5c2dc127bd5..0d211a7bdd57 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -25,7 +25,7 @@ defineSuite([ 'Scene/Camera', 'Scene/Globe', 'Scene/Model', - 'Scene/OrthographicFrustum', + 'Scene/OrthographicOffCenterFrustum', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', 'Scene/ShadowMode', @@ -58,7 +58,7 @@ defineSuite([ Camera, Globe, Model, - OrthographicFrustum, + OrthographicOffCenterFrustum, PerInstanceColorAppearance, Primitive, ShadowMode, @@ -299,7 +299,7 @@ defineSuite([ var center = new Cartesian3.fromRadians(longitude, latitude, height); scene.camera.lookAt(center, new HeadingPitchRange(0.0, CesiumMath.toRadians(-70.0), 5.0)); - var frustum = new OrthographicFrustum(); + var frustum = new OrthographicOffCenterFrustum(); frustum.left = -50.0; frustum.right = 50.0; frustum.bottom = -50.0; From 0af61cc05f14a1feeb3f66758d177c884bf4e573 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 22 Feb 2017 15:27:44 -0500 Subject: [PATCH 23/46] Remove slide animation when switching projection and correctly hide drop down. --- Source/Widgets/ProjectionPicker/ProjectionPicker.css | 8 -------- Source/Widgets/ProjectionPicker/ProjectionPicker.js | 4 ++-- .../Widgets/ProjectionPicker/ProjectionPickerViewModel.js | 6 +++++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css index 40e0a409e353..ec2ceb0c20e3 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.css +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css @@ -24,14 +24,6 @@ span.cesium-projectionPicker-wrapper { display: none; } -.cesium-projectionPicker-slide-svg { - -webkit-transition: left 2s; - -moz-transition: left 2s; - transition: left 2s; - top: 0; - left: 0; -} - .cesium-projectionPicker-wrapper .cesium-projectionPicker-dropDown-icon { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js index 3c666c5f0c8a..c06000948599 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -71,8 +71,8 @@ css: { "cesium-projectionPicker-buttonPerspective": !_orthographic,\ attr: { title: selectedTooltip },\ click: toggleDropDown'); button.innerHTML = '\ -\ -'; +\ +'; wrapper.appendChild(button); var perspectiveButton = document.createElement('button'); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index e17fadd439ad..c4e67da2b9bb 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -95,7 +95,9 @@ define([ camera.frustum = new PerspectiveFrustum(); camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; camera.frustum.fov = CesiumMath.toRadians(60.0); + that._orthographic = false; + that.dropDownVisible = false; }); var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2(); @@ -137,9 +139,11 @@ define([ } camera.frustum = new OrthographicFrustum(); - camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight * 0.3; + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; camera.frustum.width = distance; + that._orthographic = true; + that.dropDownVisible = false; }); } From 4ab7c6d77096a55ac6a75d1baa81e92e9bb8faa4 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 22 Feb 2017 15:49:15 -0500 Subject: [PATCH 24/46] Grey out the button when in 2D. --- .../ProjectionPicker/ProjectionPicker.css | 4 +++ .../ProjectionPicker/ProjectionPicker.js | 1 + .../ProjectionPickerViewModel.js | 31 +++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css index ec2ceb0c20e3..c895d7b764d9 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.css +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css @@ -49,3 +49,7 @@ span.cesium-projectionPicker-wrapper { border-color: #2e2; box-shadow: 0 0 8px #fff, 0 0 8px #fff; } + +.cesium-projectionPicker-wrapper .cesium-projectionPicker-grey-out { + opacity: 0.5; +} diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js index c06000948599..2f9f966f134f 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -67,6 +67,7 @@ define([ button.setAttribute('data-bind', '\ css: { "cesium-projectionPicker-buttonPerspective": !_orthographic,\ "cesium-projectionPicker-buttonOrthographic": _orthographic,\ + "cesium-projectionPicker-grey-out" : sceneMode === _sceneMode.SCENE2D, \ "cesium-projectionPicker-selected": dropDownVisible },\ attr: { title: selectedTooltip },\ click: toggleDropDown'); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index c4e67da2b9bb..e23726c5685c 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -7,11 +7,13 @@ define([ '../../Core/defineProperties', '../../Core/destroyObject', '../../Core/DeveloperError', + '../../Core/EventHelper', '../../Core/Math', '../../Core/Matrix4', '../../Core/Ray', '../../Scene/OrthographicFrustum', '../../Scene/PerspectiveFrustum', + '../../Scene/SceneMode', '../../ThirdParty/knockout', '../createCommand' ], function( @@ -22,11 +24,13 @@ define([ defineProperties, destroyObject, DeveloperError, + EventHelper, CesiumMath, Matrix4, Ray, OrthographicFrustum, PerspectiveFrustum, + SceneMode, knockout, createCommand) { 'use strict'; @@ -69,14 +73,20 @@ define([ */ this.tooltipOrthographic = 'Orthographic Projection'; - knockout.track(this, ['_orthographic', 'dropDownVisible', 'tooltipPerspective', 'tooltipOrthographic']); - /** * Gets the currently active tooltip. This property is observable. * @type {String} */ this.selectedTooltip = undefined; + /** + * Gets or sets the current SceneMode. This property is observable. + * @type {SceneMode} + */ + this.sceneMode = scene.mode; + + knockout.track(this, ['_orthographic', 'sceneMode', 'dropDownVisible', 'tooltipPerspective', 'tooltipOrthographic']); + var that = this; knockout.defineProperty(this, 'selectedTooltip', function() { if (that._scene.camera.frustum instanceof OrthographicFrustum) { @@ -89,7 +99,17 @@ define([ that.dropDownVisible = !that.dropDownVisible; }); + var morphStart = function(transitioner, oldMode, newMode, isMorphing) { + that.sceneMode = newMode; + }; + this._eventHelper = new EventHelper(); + this._eventHelper.add(scene.morphStart, morphStart); + this._switchToPerspective = createCommand(function() { + if (that.sceneMode === SceneMode.SCENE2D) { + return; + } + var scene = that._scene; var camera = that._scene.camera; camera.frustum = new PerspectiveFrustum(); @@ -106,6 +126,10 @@ define([ var scratchRayIntersection = new Cartesian3(); this._switchToOrthographic = createCommand(function() { + if (that.sceneMode === SceneMode.SCENE2D) { + return; + } + var scene = that._scene; var camera = that._scene.camera; var globe = scene._globe; @@ -145,6 +169,9 @@ define([ that._orthographic = true; that.dropDownVisible = false; }); + + //Used by knockout + this._sceneMode = SceneMode; } defineProperties(ProjectionPickerViewModel.prototype, { From b09ab6e54cc53928896bc7a9d43b77afd75ab2e5 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 22 Feb 2017 16:18:09 -0500 Subject: [PATCH 25/46] Disable button, disable drop down and display orthographic icon in 2D. --- Source/Widgets/ProjectionPicker/ProjectionPicker.css | 4 ---- Source/Widgets/ProjectionPicker/ProjectionPicker.js | 2 +- Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js | 5 +++++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.css b/Source/Widgets/ProjectionPicker/ProjectionPicker.css index c895d7b764d9..ec2ceb0c20e3 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.css +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.css @@ -49,7 +49,3 @@ span.cesium-projectionPicker-wrapper { border-color: #2e2; box-shadow: 0 0 8px #fff, 0 0 8px #fff; } - -.cesium-projectionPicker-wrapper .cesium-projectionPicker-grey-out { - opacity: 0.5; -} diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js index 2f9f966f134f..4fd14c5777c1 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -67,7 +67,7 @@ define([ button.setAttribute('data-bind', '\ css: { "cesium-projectionPicker-buttonPerspective": !_orthographic,\ "cesium-projectionPicker-buttonOrthographic": _orthographic,\ - "cesium-projectionPicker-grey-out" : sceneMode === _sceneMode.SCENE2D, \ + "cesium-button-disabled" : sceneMode === _sceneMode.SCENE2D, \ "cesium-projectionPicker-selected": dropDownVisible },\ attr: { title: selectedTooltip },\ click: toggleDropDown'); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index e23726c5685c..f1c029cc1d3b 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -96,11 +96,16 @@ define([ }); this._toggleDropDown = createCommand(function() { + if (that.sceneMode === SceneMode.SCENE2D) { + return; + } + that.dropDownVisible = !that.dropDownVisible; }); var morphStart = function(transitioner, oldMode, newMode, isMorphing) { that.sceneMode = newMode; + that._orthographic = newMode === SceneMode.SCENE2D; }; this._eventHelper = new EventHelper(); this._eventHelper.add(scene.morphStart, morphStart); From e51da6a3c35299c0645a0043673083b514bbfb52 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 22 Feb 2017 16:50:12 -0500 Subject: [PATCH 26/46] Fix icon after morphs. Keep orthographic when morphing from 3D to 2D/CV or CV to 3D/2D, but always change to perspective when morphing from 2D to 3D/CV. --- Source/Scene/SceneTransitioner.js | 7 ++++++- .../Widgets/ProjectionPicker/ProjectionPickerViewModel.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js index 13c5318d94b7..0ac51910741b 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -13,6 +13,7 @@ define([ '../Core/ScreenSpaceEventType', '../Core/Transforms', './Camera', + './OrthographicFrustum', './OrthographicOffCenterFrustum', './PerspectiveFrustum', './SceneMode' @@ -30,6 +31,7 @@ define([ ScreenSpaceEventType, Transforms, Camera, + OrthographicFrustum, OrthographicOffCenterFrustum, PerspectiveFrustum, SceneMode) { @@ -443,6 +445,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); @@ -859,7 +865,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/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index f1c029cc1d3b..ffc6794c2e1d 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -105,7 +105,7 @@ define([ var morphStart = function(transitioner, oldMode, newMode, isMorphing) { that.sceneMode = newMode; - that._orthographic = newMode === SceneMode.SCENE2D; + that._orthographic = newMode === SceneMode.SCENE2D || that._scene.camera.frustum instanceof OrthographicFrustum; }; this._eventHelper = new EventHelper(); this._eventHelper.add(scene.morphStart, morphStart); From b335fcc92e500d001bf7be0e3e4f6e711bf910f6 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 22 Feb 2017 17:23:01 -0500 Subject: [PATCH 27/46] Add checks for write types of frustums in the current scene mode. --- Source/Scene/Camera.js | 9 +++++++++ Specs/Scene/CameraSpec.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index e48ad236b552..232ca9987d39 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -27,6 +27,7 @@ define([ './CameraFlightPath', './MapMode2D', './OrthographicFrustum', + './OrthographicOffCenterFrustum', './PerspectiveFrustum', './SceneMode' ], function( @@ -57,6 +58,7 @@ define([ CameraFlightPath, MapMode2D, OrthographicFrustum, + OrthographicOffCenterFrustum, PerspectiveFrustum, SceneMode) { 'use strict'; @@ -900,6 +902,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; diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 21c7215496a6..4d13839ace23 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -562,6 +562,21 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('update throws with frustum not supported in given mode', function() { + camera.frustum = new PerspectiveFrustum(); + expect(function() { + camera.update(SceneMode.SCENE2D); + }).toThrowDeveloperError(); + + camera.frustum = new OrthographicOffCenterFrustum(); + expect(function() { + camera.update(SceneMode.SCENE3D); + }).toThrowDeveloperError(); + expect(function() { + camera.update(SceneMode.COLUMBUS_VIEW); + }).toThrowDeveloperError(); + }); + it('setView with cartesian in 2D', function() { var ellipsoid = Ellipsoid.WGS84; var projection = new GeographicProjection(ellipsoid); From 5389dab56c02229e66f4049b2354f82518d96a5d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 23 Feb 2017 15:08:47 -0500 Subject: [PATCH 28/46] Fix shadows and OIT in orthographic. --- Source/Renderer/AutomaticUniforms.js | 11 ---- Source/Renderer/UniformState.js | 27 +--------- Source/Scene/Model.js | 8 ++- .../Builtin/Functions/alphaWeight.glsl | 41 ++++++++------- .../Functions/windowToEyeCoordinates.glsl | 25 +++++++++- Specs/Renderer/AutomaticUniformSpec.js | 50 ------------------- 6 files changed, 55 insertions(+), 107 deletions(-) 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 73d4eee0ef4e..18640699ac90 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -82,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(); @@ -396,17 +393,6 @@ define([ } }, - /** - * @memberof UniformState.prototype - * @private - */ - inverseProjectionOIT : { - get : function() { - cleanInverseProjectionOIT(this); - return this._inverseProjectionOIT; - } - }, - /** * @memberof UniformState.prototype * @type {Matrix4} @@ -837,7 +823,6 @@ define([ Matrix4.clone(matrix, uniformState._projection); uniformState._inverseProjectionDirty = true; - uniformState._inverseProjectionOITDirty = true; uniformState._viewProjectionDirty = true; uniformState._inverseViewProjectionDirty = true; uniformState._modelViewProjectionDirty = true; @@ -992,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 && !uniformState._orthographicIn3D) { - Matrix4.inverse(uniformState._projection, uniformState._inverseProjectionOIT); + Matrix4.inverse(uniformState._projection, uniformState._inverseProjection); } else { - Matrix4.clone(Matrix4.ZERO, uniformState._inverseProjectionOIT); + Matrix4.clone(Matrix4.ZERO, uniformState._inverseProjection); } } } diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 98f23b653444..dffe6e810dc6 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1439,13 +1439,17 @@ define([ function parseTextures(model, context) { var images = model.gltf.images; var textures = model.gltf.textures; + + var binary; + var uri; + for (var id in textures) { if (textures.hasOwnProperty(id)) { var gltfImage = images[textures[id].source]; var extras = gltfImage.extras; - var binary = undefined; - var uri = undefined; + binary = undefined; + uri = undefined; // First check for a compressed texture if (defined(extras) && defined(extras.compressedImage3DTiles)) { diff --git a/Source/Shaders/Builtin/Functions/alphaWeight.glsl b/Source/Shaders/Builtin/Functions/alphaWeight.glsl index 71cb44145cfc..c6d9c4555381 100644 --- a/Source/Shaders/Builtin/Functions/alphaWeight.glsl +++ b/Source/Shaders/Builtin/Functions/alphaWeight.glsl @@ -3,24 +3,31 @@ */ float czm_alphaWeight(float a) { - float z; - bool mode2D = all(equal(czm_inverseProjectionOIT[0], vec4(0.0))) && - all(equal(czm_inverseProjectionOIT[1], vec4(0.0))) && - all(equal(czm_inverseProjectionOIT[2], vec4(0.0))) && - all(equal(czm_inverseProjectionOIT[3], vec4(0.0))); + 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 (!mode2D) - { - 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; - } - else - { - z = gl_FragCoord.z * (czm_currentFrustum.y - czm_currentFrustum.x) + czm_currentFrustum.x; + bool usingOrthographic = all(equal(czm_inverseProjection[0], vec4(0.0))) && + all(equal(czm_inverseProjection[1], vec4(0.0))) && + all(equal(czm_inverseProjection[2], vec4(0.0))) && + all(equal(czm_inverseProjection[3], vec4(0.0))); + if (!usingOrthographic) { + 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; } // See Weighted Blended Order-Independent Transparency for examples of different weighting functions: diff --git a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl index 5cb12e9e56fe..8b6ef3f6040f 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,27 @@ 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; + + bool usingOrthographic = all(equal(czm_inverseProjection[0], vec4(0.0))) && + all(equal(czm_inverseProjection[1], vec4(0.0))) && + all(equal(czm_inverseProjection[2], vec4(0.0))) && + all(equal(czm_inverseProjection[3], vec4(0.0))); + if (!usingOrthographic) { + 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/Specs/Renderer/AutomaticUniformSpec.js b/Specs/Renderer/AutomaticUniformSpec.js index 1923a832734b..1f323cb8c682 100644 --- a/Specs/Renderer/AutomaticUniformSpec.js +++ b/Specs/Renderer/AutomaticUniformSpec.js @@ -406,56 +406,6 @@ defineSuite([ }).contextToRender(); }); - it('has czm_inverseProjectionOIT', function() { - var us = context.uniformState; - us.update(createFrameState(context, createMockCamera( - undefined, - new Matrix4( - 0.0, -1.0, 0.0, 1.0, - 1.0, 0.0, 0.0, 2.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0)))); - - var fs = - 'void main() { ' + - ' bool b0 = (czm_inverseProjectionOIT[0][0] == 0.0) && (czm_inverseProjectionOIT[1][0] == 1.0) && (czm_inverseProjectionOIT[2][0] == 0.0) && (czm_inverseProjectionOIT[3][0] == -2.0); ' + - ' bool b1 = (czm_inverseProjectionOIT[0][1] == -1.0) && (czm_inverseProjectionOIT[1][1] == 0.0) && (czm_inverseProjectionOIT[2][1] == 0.0) && (czm_inverseProjectionOIT[3][1] == 1.0); ' + - ' bool b2 = (czm_inverseProjectionOIT[0][2] == 0.0) && (czm_inverseProjectionOIT[1][2] == 0.0) && (czm_inverseProjectionOIT[2][2] == 1.0) && (czm_inverseProjectionOIT[3][2] == 0.0); ' + - ' bool b3 = (czm_inverseProjectionOIT[0][3] == 0.0) && (czm_inverseProjectionOIT[1][3] == 0.0) && (czm_inverseProjectionOIT[2][3] == 0.0) && (czm_inverseProjectionOIT[3][3] == 1.0); ' + - ' gl_FragColor = vec4(b0 && b1 && b2 && b3); ' + - '}'; - expect({ - context : context, - fragmentShader : fs - }).contextToRender(); - }); - - it('has czm_inverseProjectionOIT in 2D', function() { - var us = context.uniformState; - var frameState = createFrameState(context, createMockCamera( - undefined, - new Matrix4( - 0.0, -1.0, 0.0, 1.0, - 1.0, 0.0, 0.0, 2.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0))); - frameState.mode = SceneMode.SCENE2D; - us.update(frameState); - - var fs = - 'void main() { ' + - ' bool b0 = (czm_inverseProjectionOIT[0][0] == 0.0) && (czm_inverseProjectionOIT[1][0] == 0.0) && (czm_inverseProjectionOIT[2][0] == 0.0) && (czm_inverseProjectionOIT[3][0] == 0.0); ' + - ' bool b1 = (czm_inverseProjectionOIT[0][1] == 0.0) && (czm_inverseProjectionOIT[1][1] == 0.0) && (czm_inverseProjectionOIT[2][1] == 0.0) && (czm_inverseProjectionOIT[3][1] == 0.0); ' + - ' bool b2 = (czm_inverseProjectionOIT[0][2] == 0.0) && (czm_inverseProjectionOIT[1][2] == 0.0) && (czm_inverseProjectionOIT[2][2] == 0.0) && (czm_inverseProjectionOIT[3][2] == 0.0); ' + - ' bool b3 = (czm_inverseProjectionOIT[0][3] == 0.0) && (czm_inverseProjectionOIT[1][3] == 0.0) && (czm_inverseProjectionOIT[2][3] == 0.0) && (czm_inverseProjectionOIT[3][3] == 0.0); ' + - ' gl_FragColor = vec4(b0 && b1 && b2 && b3); ' + - '}'; - expect({ - context : context, - fragmentShader : fs - }).contextToRender(); - }); - it('has czm_infiniteProjection', function() { var us = context.uniformState; us.update(createFrameState(context, createMockCamera(undefined, undefined, From 4040a1be3ba71df4f6f04c2ad72e15524c2fc25a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 24 Feb 2017 16:35:41 -0500 Subject: [PATCH 29/46] Fix zooming issue. --- Source/Scene/Camera.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 232ca9987d39..eee93ac91a80 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -969,7 +969,6 @@ define([ updateMembers(this); }; - var scratchDepthIntersection = new Cartesian3(); var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2(); var pickGlobeScratchRay = new Ray(); var scratchRayIntersection = new Cartesian3(); @@ -986,8 +985,6 @@ define([ var scene = this._scene; var globe = scene._globe; - - var depthIntersection; var rayIntersection; if (defined(globe)) { @@ -995,21 +992,16 @@ define([ mousePosition.x = scene.drawingBufferWidth / 2.0; mousePosition.y = scene.drawingBufferHeight / 2.0; - if (scene.pickPositionSupported) { - depthIntersection = scene.pickPositionWorldCoordinates(mousePosition, scratchDepthIntersection); - } - var ray = this.getPickRay(mousePosition, pickGlobeScratchRay); rayIntersection = globe.pick(ray, scene, scratchRayIntersection); - - var pickDistance = defined(depthIntersection) ? Cartesian3.distance(depthIntersection, this.positionWC) : Number.POSITIVE_INFINITY; - var rayDistance = defined(rayIntersection) ? Cartesian3.distance(rayIntersection, this.positionWC) : Number.POSITIVE_INFINITY; - - this.frustum.width = pickDistance < rayDistance ? pickDistance : rayDistance; + if (defined(rayIntersection)) { + this.frustum.width = Cartesian3.distance(rayIntersection, this.positionWC); + } } - if (!defined(globe) || (!defined(depthIntersection) && !defined(rayIntersection))) { - this.frustum.width = this.positionCartographic.height; + if (!defined(globe) || (!defined(rayIntersection))) { + var distance = this.positionCartographic.height; + this.frustum.width = distance; } }; From d5d8ebdf99b55e5cb5eca6653889f8c65efb2d06 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 27 Feb 2017 14:09:00 -0500 Subject: [PATCH 30/46] Fix WGS84 to window coordinates. --- Source/Scene/SceneTransforms.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index 0546b7d22ac9..9d5aa4f120a7 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -11,6 +11,7 @@ define([ '../Core/Matrix4', '../Core/Transforms', './OrthographicFrustum', + './OrthographicOffCenterFrustum', './SceneMode' ], function( BoundingRectangle, @@ -24,6 +25,7 @@ define([ Matrix4, Transforms, OrthographicFrustum, + OrthographicOffCenterFrustum, SceneMode) { 'use strict'; @@ -186,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; } From 0ada6b0bef41ec59fec0b5666b1c94ddc3321970 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 27 Feb 2017 16:18:30 -0500 Subject: [PATCH 31/46] Enable fog with an orthographic projection. --- Source/Scene/Fog.js | 6 +----- Source/Scene/GlobeSurfaceTileProvider.js | 5 ++--- Source/Scene/QuadtreePrimitive.js | 8 +++++++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Fog.js b/Source/Scene/Fog.js index 75dac42fe337..15d9d4b2aa84 100644 --- a/Source/Scene/Fog.js +++ b/Source/Scene/Fog.js @@ -3,13 +3,11 @@ define([ '../Core/Cartesian3', '../Core/defined', '../Core/Math', - './OrthographicFrustum', './SceneMode' ], function( Cartesian3, defined, CesiumMath, - OrthographicFrustum, SceneMode) { 'use strict'; @@ -116,9 +114,7 @@ define([ var positionCartographic = camera.positionCartographic; // Turn off fog in space. - if (!defined(positionCartographic) || positionCartographic.height > 800000.0 || - frameState.mode !== SceneMode.SCENE3D || - (frameState.mode === SceneMode.SCENE3D && camera.frustum instanceof OrthographicFrustum)) { + if (!defined(positionCartographic) || positionCartographic.height > 800000.0 || frameState.mode !== SceneMode.SCENE3D) { frameState.fog.enabled = false; return; } diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 6fea88dbebcf..15b234d32be3 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -478,9 +478,7 @@ define([ var distance = this.computeDistanceToTile(tile, frameState); tile._distance = distance; - var ortho3D = frameState.mode === SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum; - - if (frameState.fog.enabled && !ortho3D) { + if (frameState.fog.enabled) { if (CesiumMath.fog(distance, frameState.fog.density) >= 1.0) { // Tile is completely in fog so return that it is not visible. return Visibility.NONE; @@ -506,6 +504,7 @@ define([ return Visibility.NONE; } + var ortho3D = frameState.mode === SceneMode.SCENE3D && frameState.camera.frustum instanceof OrthographicFrustum; if (frameState.mode === SceneMode.SCENE3D && !ortho3D) { var occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace; if (!defined(occludeePointInScaledSpace)) { diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 752f1fd6f37c..d10d087ef759 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -703,7 +703,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) { From 45e26f838a44bef0e9b5c1c2cac7c1fecbbbd823 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 28 Feb 2017 16:47:58 -0500 Subject: [PATCH 32/46] Add tests. --- Specs/Renderer/AutomaticUniformSpec.js | 59 +++++ Specs/Scene/CameraSpec.js | 233 +++++++++++++++++- Specs/Scene/PickSpec.js | 13 +- Specs/Scene/SceneTransformsSpec.js | 25 ++ .../Scene/ScreenSpaceCameraControllerSpec.js | 50 ++++ 5 files changed, 376 insertions(+), 4 deletions(-) diff --git a/Specs/Renderer/AutomaticUniformSpec.js b/Specs/Renderer/AutomaticUniformSpec.js index 1f323cb8c682..685c61329027 100644 --- a/Specs/Renderer/AutomaticUniformSpec.js +++ b/Specs/Renderer/AutomaticUniformSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/Matrix4', 'Renderer/Pass', 'Renderer/Texture', + 'Scene/OrthographicFrustum', 'Scene/OrthographicOffCenterFrustum', 'Scene/SceneMode', 'Specs/createCamera', @@ -18,6 +19,7 @@ defineSuite([ Matrix4, Pass, Texture, + OrthographicFrustum, OrthographicOffCenterFrustum, SceneMode, createCamera, @@ -406,6 +408,63 @@ defineSuite([ }).contextToRender(); }); + it('has czm_inverseProjection in 2D', function() { + var frameState = createFrameState(context, createMockCamera( + undefined, + new Matrix4( + 0.0, -1.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 2.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0))); + frameState.mode = SceneMode.SCENE2D; + + var us = context.uniformState; + us.update(frameState); + + var fs = + 'void main() { ' + + ' bool b0 = (czm_inverseProjection[0][0] == 0.0) && (czm_inverseProjection[1][0] == 0.0) && (czm_inverseProjection[2][0] == 0.0) && (czm_inverseProjection[3][0] == 0.0); ' + + ' bool b1 = (czm_inverseProjection[0][1] == 0.0) && (czm_inverseProjection[1][1] == 0.0) && (czm_inverseProjection[2][1] == 0.0) && (czm_inverseProjection[3][1] == 0.0); ' + + ' bool b2 = (czm_inverseProjection[0][2] == 0.0) && (czm_inverseProjection[1][2] == 0.0) && (czm_inverseProjection[2][2] == 0.0) && (czm_inverseProjection[3][2] == 0.0); ' + + ' bool b3 = (czm_inverseProjection[0][3] == 0.0) && (czm_inverseProjection[1][3] == 0.0) && (czm_inverseProjection[2][3] == 0.0) && (czm_inverseProjection[3][3] == 0.0); ' + + ' gl_FragColor = vec4(b0 && b1 && b2 && b3); ' + + '}'; + expect({ + context : context, + fragmentShader : fs + }).contextToRender(); + }); + + it('has czm_inverseProjection in 3D with orthographic projection', function() { + var frameState = createFrameState(context, createMockCamera( + undefined, + new Matrix4( + 0.0, -1.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 2.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0))); + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 1.0; + frameState.camera.frustum = frustum; + + var us = context.uniformState; + us.update(frameState); + + var fs = + 'void main() { ' + + ' bool b0 = (czm_inverseProjection[0][0] == 0.0) && (czm_inverseProjection[1][0] == 0.0) && (czm_inverseProjection[2][0] == 0.0) && (czm_inverseProjection[3][0] == 0.0); ' + + ' bool b1 = (czm_inverseProjection[0][1] == 0.0) && (czm_inverseProjection[1][1] == 0.0) && (czm_inverseProjection[2][1] == 0.0) && (czm_inverseProjection[3][1] == 0.0); ' + + ' bool b2 = (czm_inverseProjection[0][2] == 0.0) && (czm_inverseProjection[1][2] == 0.0) && (czm_inverseProjection[2][2] == 0.0) && (czm_inverseProjection[3][2] == 0.0); ' + + ' bool b3 = (czm_inverseProjection[0][3] == 0.0) && (czm_inverseProjection[1][3] == 0.0) && (czm_inverseProjection[2][3] == 0.0) && (czm_inverseProjection[3][3] == 0.0); ' + + ' gl_FragColor = vec4(b0 && b1 && b2 && b3); ' + + '}'; + expect({ + context : context, + fragmentShader : fs + }).contextToRender(); + }); + it('has czm_infiniteProjection', function() { var us = context.uniformState; us.update(createFrameState(context, createMockCamera(undefined, undefined, diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 4d13839ace23..bea95f2732fe 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -18,6 +18,7 @@ defineSuite([ 'Core/WebMercatorProjection', 'Scene/CameraFlightPath', 'Scene/MapMode2D', + 'Scene/OrthographicFrustum', 'Scene/OrthographicOffCenterFrustum', 'Scene/PerspectiveFrustum', 'Scene/SceneMode', @@ -41,6 +42,7 @@ defineSuite([ WebMercatorProjection, CameraFlightPath, MapMode2D, + OrthographicFrustum, OrthographicOffCenterFrustum, PerspectiveFrustum, SceneMode, @@ -646,6 +648,54 @@ defineSuite([ expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up, new Cartesian3()), CesiumMath.EPSILON6); }); + it('setView with cartesian in Columbus View and orthographic frustum', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + + camera._mode = SceneMode.COLUMBUS_VIEW; + camera._projection = projection; + + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.width = camera.positionCartographic.height; + + var cartesian = Cartesian3.fromDegrees(-75.0, 42.0, 100.0); + camera.setView({ + destination : cartesian + }); + + var cart = ellipsoid.cartesianToCartographic(cartesian); + expect(camera.positionCartographic).toEqualEpsilon(cart, CesiumMath.EPSILON11); + expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON6); + expect(camera.right).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON6); + expect(camera.frustum.width).toEqual(cart.height); + }); + + it('setView with cartesian in 3D and orthographic frustum', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + + camera._mode = SceneMode.SCENE3D; + camera._projection = projection; + + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.width = camera.positionCartographic.height; + + var cartesian = Cartesian3.fromDegrees(-75.0, 0.0, 100.0); + camera.setView({ + destination : cartesian + }); + + var cart = ellipsoid.cartesianToCartographic(cartesian); + expect(camera.positionCartographic).toEqualEpsilon(cart, CesiumMath.EPSILON6); + expect(camera.direction).toEqualEpsilon(Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON6); + expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up, new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.frustum.width).toEqual(cart.height); + }); + it('setView right rotation order', function() { var position = Cartesian3.fromDegrees(-117.16, 32.71, 0.0); var heading = CesiumMath.toRadians(180.0); @@ -1494,6 +1544,30 @@ defineSuite([ expect(tempCamera.frustum.left).toEqual(-range * 0.5); }); + it('lookAtTransform in 3D with orthographic projection', function() { + var target = new Cartesian3(-1.0, -1.0, 0.0); + var offset = new Cartesian3(1.0, 1.0, 0.0); + var transform = Transforms.eastNorthUpToFixedFrame(target, Ellipsoid.UNIT_SPHERE); + + var tempCamera = Camera.clone(camera); + tempCamera.frustum = new OrthographicFrustum(); + tempCamera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + tempCamera.frustum.width = tempCamera.positionCartographic.height; + + tempCamera.lookAtTransform(transform, offset); + + expect(tempCamera.position).toEqualEpsilon(offset, CesiumMath.EPSILON11); + expect(tempCamera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.normalize(offset, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON11); + expect(tempCamera.right).toEqualEpsilon(Cartesian3.cross(tempCamera.direction, Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON11); + expect(tempCamera.up).toEqualEpsilon(Cartesian3.cross(tempCamera.right, tempCamera.direction, new Cartesian3()), CesiumMath.EPSILON11); + + expect(1.0 - Cartesian3.magnitude(tempCamera.direction)).toBeLessThan(CesiumMath.EPSILON14); + expect(1.0 - Cartesian3.magnitude(tempCamera.up)).toBeLessThan(CesiumMath.EPSILON14); + expect(1.0 - Cartesian3.magnitude(tempCamera.right)).toBeLessThan(CesiumMath.EPSILON14); + + expect(tempCamera.frustum.width).toEqual(Cartesian3.magnitude(tempCamera.position)); + }); + it('lookAtTransform throws when morphing', function() { camera.update(SceneMode.MORPHING); @@ -1645,6 +1719,64 @@ defineSuite([ expect(camera.right).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON10); }); + it('setView rectangle in 3D with orthographic frustum', function() { + camera.setView({ + destination : Cartesian3.fromDegrees(-75.0, 42.0, 100.0) + }); + + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = 1135 / 630; + camera.frustum.width = camera.positionCartographic.height; + + // force update of off-center frustum + expect(camera.frustum.projectionMatrix).toBeDefined(); + + var rectangle = new Rectangle( + CesiumMath.toRadians(21.25), + CesiumMath.toRadians(41.23), + CesiumMath.toRadians(21.51), + CesiumMath.toRadians(41.38)); + + var projection = new GeographicProjection(); + camera._mode = SceneMode.SCENE3D; + camera._projection = projection; + camera.setView({destination: rectangle}); + + expect(camera.position).toEqualEpsilon(new Cartesian3(4489090.849577177, 1757448.0638960265, 4207738.07588144), CesiumMath.EPSILON6); + expect(camera.direction).toEqualEpsilon(new Cartesian3(-0.6995012374560863, -0.2738499033887593, -0.6600789719506079), CesiumMath.EPSILON10); + expect(camera.up).toEqualEpsilon(new Cartesian3(-0.6146543999545513, -0.2406329524979527, 0.7511962132416727), CesiumMath.EPSILON10); + expect(camera.right).toEqualEpsilon(new Cartesian3(-0.36455176232452197, 0.931183125161794, 0.0), CesiumMath.EPSILON10); + }); + + it('setView rectangle in Columbus view with orthographic frustum', function() { + camera.setView({ + destination : Cartesian3.fromDegrees(-75.0, 42.0, 100.0) + }); + + camera.frustum = new OrthographicFrustum(); + camera.frustum.aspectRatio = 1135 / 630; + camera.frustum.width = camera.positionCartographic.height; + + // force update of off-center frustum + expect(camera.frustum.projectionMatrix).toBeDefined(); + + var rectangle = new Rectangle( + CesiumMath.toRadians(21.25), + CesiumMath.toRadians(41.23), + CesiumMath.toRadians(21.51), + CesiumMath.toRadians(41.38)); + + var projection = new GeographicProjection(); + camera._mode = SceneMode.COLUMBUS_VIEW; + camera._projection = projection; + camera.setView({destination: rectangle}); + + expect(camera.position).toEqualEpsilon(new Cartesian3(2380010.713160189, 4598051.567216165, 28943.06760625122), CesiumMath.EPSILON6); + expect(camera.direction).toEqualEpsilon(new Cartesian3(0.0, 0.0, -1.0), CesiumMath.EPSILON10); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON10); + expect(camera.right).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON10); + }); + it('getRectangleCameraCoordinates throws without rectangle', function() { expect(function () { camera.getRectangleCameraCoordinates(); @@ -1932,7 +2064,7 @@ defineSuite([ expect(ray.direction).toEqualEpsilon(expectedDirection, CesiumMath.EPSILON15); }); - it('get pick ray orthographic', function() { + it('get pick ray orthographic in 2D', function() { var frustum = new OrthographicOffCenterFrustum(); frustum.left = -10.0; frustum.right = 10.0; @@ -1942,13 +2074,59 @@ defineSuite([ frustum.far = 21.0; camera.frustum = frustum; + camera.update(SceneMode.SCENE2D); + var windowCoord = new Cartesian2((3.0 / 5.0) * scene.canvas.clientWidth, (1.0 - (3.0 / 5.0)) * scene.canvas.clientHeight); var ray = camera.getPickRay(windowCoord); var cameraPosition = camera.position; var expectedPosition = new Cartesian3(cameraPosition.x + 2.0, cameraPosition.y + 2, cameraPosition.z); expect(ray.origin).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON14); - expect(ray.direction).toEqual(camera.direction); + expect(ray.direction).toEqual(camera.directionWC); + }); + + it('get pick ray orthographic in 3D', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + frustum.near = 1.0; + frustum.far = 21.0; + camera.frustum = frustum; + + // force off center frustum to update + expect(frustum.projectionMatrix).toBeDefined(); + + camera.update(SceneMode.SCENE3D); + + var windowCoord = new Cartesian2((3.0 / 5.0) * scene.canvas.clientWidth, (1.0 - (3.0 / 5.0)) * scene.canvas.clientHeight); + var ray = camera.getPickRay(windowCoord); + + var cameraPosition = camera.position; + var expectedPosition = new Cartesian3(cameraPosition.x + 2.0, cameraPosition.y + 2, cameraPosition.z); + expect(ray.origin).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON14); + expect(ray.direction).toEqual(camera.directionWC); + }); + + it('get pick ray orthographic in Columbus view', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + frustum.near = 1.0; + frustum.far = 21.0; + camera.frustum = frustum; + + // force off center frustum to update + expect(frustum.projectionMatrix).toBeDefined(); + + camera.update(SceneMode.COLUMBUS_VIEW); + + var windowCoord = new Cartesian2((3.0 / 5.0) * scene.canvas.clientWidth, (1.0 - (3.0 / 5.0)) * scene.canvas.clientHeight); + var ray = camera.getPickRay(windowCoord); + + var cameraPosition = camera.position; + var expectedPosition = new Cartesian3(cameraPosition.z, cameraPosition.x + 2.0, cameraPosition.y + 2); + expect(ray.origin).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON14); + expect(ray.direction).toEqual(camera.directionWC); }); it('gets magnitude in 2D', function() { @@ -2289,6 +2467,49 @@ defineSuite([ expect(camera.pitch).toEqualEpsilon(pitch, CesiumMath.EPSILON5); }); + it('viewBoundingSphere in 2D', function() { + var frustum = new OrthographicOffCenterFrustum(); + frustum.left = -10.0; + frustum.right = 10.0; + frustum.bottom = -10.0; + frustum.top = 10.0; + frustum.near = 1.0; + frustum.far = 21.0; + camera.frustum = frustum; + + camera.update(SceneMode.SCENE2D); + + var sphere = new BoundingSphere(Cartesian3.fromDegrees(-117.16, 32.71, 0.0), 10000.0); + camera.viewBoundingSphere(sphere); + camera._setTransform(Matrix4.IDENTITY); + + var distance = frustum.right - frustum.left; + expect(distance).toBeGreaterThan(sphere.radius); + expect(distance).toBeLessThan(sphere.radius * 3.0); + }); + + it('viewBoundingSphere in 3D with orthographic', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + frustum.near = 1.0; + frustum.far = 21.0; + camera.frustum = frustum; + + camera.update(SceneMode.SCENE3D); + + // force off center update + expect(frustum.projectionMatrix).toBeDefined(); + + var sphere = new BoundingSphere(Cartesian3.fromDegrees(-117.16, 32.71, 0.0), 10000.0); + camera.viewBoundingSphere(sphere); + camera._setTransform(Matrix4.IDENTITY); + + var distance = frustum.width; + expect(distance).toBeGreaterThan(sphere.radius); + expect(distance).toBeLessThan(sphere.radius * 3.0); + }); + it('viewBoundingSphere throws when morphing', function() { camera._mode = SceneMode.MORPHING; @@ -2297,6 +2518,14 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('viewBoundingSphere throws without bounding sphere', function() { + camera._mode = SceneMode.MORPHING; + + expect(function() { + camera.viewBoundingSphere(undefined); + }).toThrowDeveloperError(); + }); + it('flyToBoundingSphere uses CameraFlightPath', function() { spyOn(CameraFlightPath, 'createTween').and.returnValue({ startObject : {}, diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 45b4e9fa4777..57714c896d3b 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -8,6 +8,7 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/ShowGeometryInstanceAttribute', 'Scene/EllipsoidSurfaceAppearance', + 'Scene/OrthographicFrustum', 'Scene/PerspectiveFrustum', 'Scene/Primitive', 'Scene/SceneMode', @@ -21,6 +22,7 @@ defineSuite([ RectangleGeometry, ShowGeometryInstanceAttribute, EllipsoidSurfaceAppearance, + OrthographicFrustum, PerspectiveFrustum, Primitive, SceneMode, @@ -312,8 +314,15 @@ defineSuite([ scene.morphTo3D(0.0); }); - it('picks in 2D when rotated', function() { - scene.morphTo2D(0.0); + it('picks in 3D with orthographic projection', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + camera.frustum = frustum; + + // force off center update + expect(frustum.projectionMatrix).toBeDefined(); + camera.setView({ destination : primitiveRectangle }); var rectangle = createRectangle(); scene.initializeFrame(); diff --git a/Specs/Scene/SceneTransformsSpec.js b/Specs/Scene/SceneTransformsSpec.js index 3592c301d076..3cd8c0cba0ff 100644 --- a/Specs/Scene/SceneTransformsSpec.js +++ b/Specs/Scene/SceneTransformsSpec.js @@ -7,6 +7,7 @@ defineSuite([ 'Core/Math', 'Core/Rectangle', 'Scene/Camera', + 'Scene/OrthographicFrustum', 'Scene/SceneMode', 'Specs/createScene' ], function( @@ -17,6 +18,7 @@ defineSuite([ CesiumMath, Rectangle, Camera, + OrthographicFrustum, SceneMode, createScene) { 'use strict'; @@ -181,6 +183,29 @@ defineSuite([ expect(windowCoordinates.y).toBeLessThan(1.0); }); + it('returns correct window position in 3D with orthographic frustum', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + scene.camera.frustum = frustum; + + // Update scene state + scene.renderForSpecs(); + + scene.camera.setView({ + destination : Rectangle.fromDegrees(-0.000001, -0.000001, 0.000001, 0.000001) + }); + + var position = Cartesian3.fromDegrees(0,0); + var windowCoordinates = SceneTransforms.wgs84ToWindowCoordinates(scene, position); + + expect(windowCoordinates.x).toBeGreaterThan(0.0); + expect(windowCoordinates.y).toBeGreaterThan(0.0); + + expect(windowCoordinates.x).toBeLessThan(1.0); + expect(windowCoordinates.y).toBeLessThan(1.0); + }); + it('returns correct drawing buffer position in 2D', function() { scene.camera.setView({ destination : Rectangle.fromDegrees(-0.000001, -0.000001, 0.000001, 0.000001) diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 3ce5872d71e3..b4d18ff24de7 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -12,8 +12,10 @@ defineSuite([ 'Core/Math', 'Core/Ray', 'Core/Transforms', + 'Scene/Camera', 'Scene/CameraEventType', 'Scene/MapMode2D', + 'Scene/OrthographicFrustum', 'Scene/OrthographicOffCenterFrustum', 'Scene/SceneMode', 'Specs/createCamera', @@ -32,8 +34,10 @@ defineSuite([ CesiumMath, Ray, Transforms, + Camera, CameraEventType, MapMode2D, + OrthographicFrustum, OrthographicOffCenterFrustum, SceneMode, createCamera, @@ -861,6 +865,52 @@ defineSuite([ expect(Cartesian3.magnitude(position)).toBeLessThan(Cartesian3.magnitude(camera.position)); }); + it('zoom in 3D with orthographic projection', function() { + setUp3D(); + + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + camera.frustum = frustum; + + expect(frustum.projectionMatrix).toBeDefined(); + + camera.setView({ destination : Camera.DEFAULT_VIEW_RECTANGLE }); + + var position = Cartesian3.clone(camera.position); + var frustumWidth = camera.frustum.width; + var startPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 4); + var endPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); + + moveMouse(MouseButtons.RIGHT, startPosition, endPosition); + updateController(); + expect(Cartesian3.magnitude(position)).toBeGreaterThan(Cartesian3.magnitude(camera.position)); + expect(frustumWidth).toBeGreaterThan(camera.frustum.width); + }); + + it('zoom out in 3D with orthographic projection', function() { + setUp3D(); + + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + camera.frustum = frustum; + + expect(frustum.projectionMatrix).toBeDefined(); + + camera.setView({ destination : Camera.DEFAULT_VIEW_RECTANGLE }); + + var position = Cartesian3.clone(camera.position); + var frustumWidth = camera.frustum.width; + var startPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); + var endPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 4); + + moveMouse(MouseButtons.RIGHT, startPosition, endPosition); + updateController(); + expect(Cartesian3.magnitude(position)).toBeLessThan(Cartesian3.magnitude(camera.position)); + expect(frustumWidth).toBeLessThan(camera.frustum.width); + }); + it('tilts in 3D', function() { setUp3D(); var position = Cartesian3.clone(camera.position); From fa2200e3bcdcd96fbaceea4bebbfa75aa77e210b Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 7 Mar 2017 17:24:25 -0500 Subject: [PATCH 33/46] slightly extend skirts away from the mesh instead of dropping them straight down. --- .../createVerticesFromQuantizedTerrainMesh.js | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/Source/Workers/createVerticesFromQuantizedTerrainMesh.js b/Source/Workers/createVerticesFromQuantizedTerrainMesh.js index 3a9d5118dad0..1dbafbed456a 100644 --- a/Source/Workers/createVerticesFromQuantizedTerrainMesh.js +++ b/Source/Workers/createVerticesFromQuantizedTerrainMesh.js @@ -97,6 +97,11 @@ define([ maximum.y = Number.NEGATIVE_INFINITY; maximum.z = Number.NEGATIVE_INFINITY; + var minLongitude = Number.POSITIVE_INFINITY; + var maxLongitude = Number.NEGATIVE_INFINITY; + var minLatitude = Number.POSITIVE_INFINITY; + var maxLatitude = Number.NEGATIVE_INFINITY; + for (var i = 0; i < quantizedVertexCount; ++i) { var u = uBuffer[i] / maxShort; var v = vBuffer[i] / maxShort; @@ -106,6 +111,11 @@ define([ cartographicScratch.latitude = CesiumMath.lerp(south, north, v); cartographicScratch.height = height; + minLongitude = Math.min(cartographicScratch.longitude, minLongitude); + maxLongitude = Math.max(cartographicScratch.longitude, maxLongitude); + minLatitude = Math.min(cartographicScratch.latitude, minLatitude); + maxLatitude = Math.max(cartographicScratch.latitude, maxLatitude); + var position = ellipsoid.cartographicToCartesian(cartographicScratch); uvs[i] = new Cartesian2(u, v); @@ -174,16 +184,28 @@ define([ var indexBuffer = IndexDatatype.createTypedArray(quantizedVertexCount + edgeVertexCount, indexBufferLength); indexBuffer.set(parameters.indices, 0); + var percentage = 0.001; + var lonOffset = (maxLongitude - minLongitude) * percentage; + var latOffset = (maxLatitude - minLatitude) * percentage; + var westLongitudeOffset = -lonOffset; + var westLatitudeOffset = 0.0; + var eastLongitudeOffset = lonOffset; + var eastLatitudeOffset = 0.0; + var northLongitudeOffset = 0.0; + var northLatitudeOffset = latOffset; + var southLongitudeOffset = 0.0; + var southLatitudeOffset = -latOffset; + // Add skirts. var vertexBufferIndex = quantizedVertexCount * vertexStride; var indexBufferIndex = parameters.indices.length; - indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.westIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.westSkirtHeight, true, exaggeration, southMercatorY, oneOverMercatorHeight); + indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.westIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.westSkirtHeight, true, exaggeration, southMercatorY, oneOverMercatorHeight, westLongitudeOffset, westLatitudeOffset); vertexBufferIndex += parameters.westIndices.length * vertexStride; - indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.southIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.southSkirtHeight, false, exaggeration, southMercatorY, oneOverMercatorHeight); + indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.southIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.southSkirtHeight, false, exaggeration, southMercatorY, oneOverMercatorHeight, southLongitudeOffset, southLatitudeOffset); vertexBufferIndex += parameters.southIndices.length * vertexStride; - indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.eastIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.eastSkirtHeight, false, exaggeration, southMercatorY, oneOverMercatorHeight); + indexBufferIndex = addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.eastIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.eastSkirtHeight, false, exaggeration, southMercatorY, oneOverMercatorHeight, eastLongitudeOffset, eastLatitudeOffset); vertexBufferIndex += parameters.eastIndices.length * vertexStride; - addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.northIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.northSkirtHeight, true, exaggeration, southMercatorY, oneOverMercatorHeight); + addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, parameters.northIndices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.northSkirtHeight, true, exaggeration, southMercatorY, oneOverMercatorHeight, northLongitudeOffset, northLatitudeOffset); transferableObjects.push(vertexBuffer.buffer, indexBuffer.buffer); @@ -234,7 +256,7 @@ define([ return hMin; } - function addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, edgeVertices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, skirtLength, isWestOrNorthEdge, exaggeration, southMercatorY, oneOverMercatorHeight) { + function addSkirt(vertexBuffer, vertexBufferIndex, indexBuffer, indexBufferIndex, edgeVertices, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, skirtLength, isWestOrNorthEdge, exaggeration, southMercatorY, oneOverMercatorHeight, longitudeOffset, latitudeOffset) { var start, end, increment; if (isWestOrNorthEdge) { start = edgeVertices.length - 1; @@ -266,8 +288,8 @@ define([ var h = heights[index]; var uv = uvs[index]; - cartographicScratch.longitude = CesiumMath.lerp(west, east, uv.x); - cartographicScratch.latitude = CesiumMath.lerp(south, north, uv.y); + cartographicScratch.longitude = CesiumMath.lerp(west, east, uv.x) + longitudeOffset; + cartographicScratch.latitude = CesiumMath.lerp(south, north, uv.y) + latitudeOffset; cartographicScratch.height = h - skirtLength; var position = ellipsoid.cartographicToCartesian(cartographicScratch, cartesian3Scratch); From f32f814c30d24cea933b7650927141738b5c5fe4 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 8 Mar 2017 14:27:31 -0500 Subject: [PATCH 34/46] Add flanges to heightmap tessellator. --- Source/Core/HeightmapTessellator.js | 30 +++++++++++++++---- .../createVerticesFromQuantizedTerrainMesh.js | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) 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/Workers/createVerticesFromQuantizedTerrainMesh.js b/Source/Workers/createVerticesFromQuantizedTerrainMesh.js index 1dbafbed456a..2c81e05a22b5 100644 --- a/Source/Workers/createVerticesFromQuantizedTerrainMesh.js +++ b/Source/Workers/createVerticesFromQuantizedTerrainMesh.js @@ -184,7 +184,7 @@ define([ var indexBuffer = IndexDatatype.createTypedArray(quantizedVertexCount + edgeVertexCount, indexBufferLength); indexBuffer.set(parameters.indices, 0); - var percentage = 0.001; + var percentage = 0.0001; var lonOffset = (maxLongitude - minLongitude) * percentage; var latOffset = (maxLatitude - minLatitude) * percentage; var westLongitudeOffset = -lonOffset; From b1b29ed51756ba97202034ade4699454de3201a6 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 8 Mar 2017 15:31:10 -0500 Subject: [PATCH 35/46] Preserve projection when morphing. --- Source/Scene/SceneTransitioner.js | 71 +++++++++++++------ .../ProjectionPickerViewModel.js | 4 +- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js index 0ac51910741b..f7829e7fd9de 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -52,6 +52,7 @@ define([ this._morphHandler = undefined; this._morphCancelled = false; this._completeMorph = undefined; + this._morphToOrthographic = false; } SceneTransitioner.prototype.completeMorph = function() { @@ -67,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; @@ -96,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, @@ -156,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; @@ -354,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) { @@ -378,8 +386,6 @@ define([ camera3D = getColumbusViewTo3DCamera(transitioner, ellipsoid); } - camera3D.frustum = frustum; - var complete = complete3DCallback(camera3D); createMorphHandler(transitioner, complete); @@ -410,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, @@ -423,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(); } } @@ -706,7 +730,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); @@ -736,7 +761,13 @@ define([ } }); transitioner._currentTweens.push(tween); - }); + } + + if (transitioner._morphToOrthographic) { + morph(); + } else { + morphOrthographicToPerspective(transitioner, 0.0, cameraCV, morph); + } } var scratch3DToCVStartPos = new Cartesian3(); @@ -820,10 +851,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); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index ffc6794c2e1d..fc9c23296362 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -103,12 +103,12 @@ define([ that.dropDownVisible = !that.dropDownVisible; }); - var morphStart = function(transitioner, oldMode, newMode, isMorphing) { + var morphComplete = function(transitioner, oldMode, newMode, isMorphing) { that.sceneMode = newMode; that._orthographic = newMode === SceneMode.SCENE2D || that._scene.camera.frustum instanceof OrthographicFrustum; }; this._eventHelper = new EventHelper(); - this._eventHelper.add(scene.morphStart, morphStart); + this._eventHelper.add(scene.morphComplete, morphComplete); this._switchToPerspective = createCommand(function() { if (that.sceneMode === SceneMode.SCENE2D) { From d8481d8c43be4bc16c0acb826953af299eea45c9 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 8 Mar 2017 16:52:53 -0500 Subject: [PATCH 36/46] Adjust ortho frustum when moving camera, fix camera changed event, disable projection button during a camera flight. --- Source/Scene/Camera.js | 11 +++++++++-- Source/Scene/Model.js | 8 ++------ .../Widgets/ProjectionPicker/ProjectionPicker.js | 4 ++-- .../ProjectionPickerViewModel.js | 16 ++++++++++------ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index aa1eabd9da99..cebe7b20f98d 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -265,7 +265,7 @@ define([ Camera.DEFAULT_VIEW_FACTOR = 0.5; /** - * The default heading/pitch/range that is used when the camera flies to a location that contains a bounding sphere. + * The default heading/pitch/range that is used when the camera flies to a location that contains a bounding sphere. * @type HeadingPitchRange */ Camera.DEFAULT_OFFSET = new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_FOUR, 0.0); @@ -339,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; @@ -1469,6 +1475,7 @@ define([ if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, cameraPosition); } + this._adjustOrthographicFrustum(); }; /** diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index dffe6e810dc6..98f23b653444 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1439,17 +1439,13 @@ define([ function parseTextures(model, context) { var images = model.gltf.images; var textures = model.gltf.textures; - - var binary; - var uri; - for (var id in textures) { if (textures.hasOwnProperty(id)) { var gltfImage = images[textures[id].source]; var extras = gltfImage.extras; - binary = undefined; - uri = undefined; + var binary = undefined; + var uri = undefined; // First check for a compressed texture if (defined(extras) && defined(extras.compressedImage3DTiles)) { diff --git a/Source/Widgets/ProjectionPicker/ProjectionPicker.js b/Source/Widgets/ProjectionPicker/ProjectionPicker.js index 4fd14c5777c1..1405115e4113 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPicker.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPicker.js @@ -23,7 +23,7 @@ define([ 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. + * The ProjectionPicker is a single button widget for switching between perspective and orthographic projections. * * @alias ProjectionPicker * @constructor @@ -67,7 +67,7 @@ define([ button.setAttribute('data-bind', '\ css: { "cesium-projectionPicker-buttonPerspective": !_orthographic,\ "cesium-projectionPicker-buttonOrthographic": _orthographic,\ - "cesium-button-disabled" : sceneMode === _sceneMode.SCENE2D, \ + "cesium-button-disabled" : sceneMode === _sceneMode.SCENE2D || _flightInProgress, \ "cesium-projectionPicker-selected": dropDownVisible },\ attr: { title: selectedTooltip },\ click: toggleDropDown'); diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index fc9c23296362..d839dcc443b9 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -51,6 +51,7 @@ define([ this._scene = scene; this._orthographic = scene.camera.frustum instanceof OrthographicFrustum; + this._flightInProgress = false; /** * Gets or sets whether the button drop-down is currently visible. This property is observable. @@ -85,7 +86,7 @@ define([ */ this.sceneMode = scene.mode; - knockout.track(this, ['_orthographic', 'sceneMode', 'dropDownVisible', 'tooltipPerspective', 'tooltipOrthographic']); + knockout.track(this, ['_orthographic', '_flightInProgress', 'sceneMode', 'dropDownVisible', 'tooltipPerspective', 'tooltipOrthographic']); var that = this; knockout.defineProperty(this, 'selectedTooltip', function() { @@ -96,19 +97,21 @@ define([ }); this._toggleDropDown = createCommand(function() { - if (that.sceneMode === SceneMode.SCENE2D) { + if (that.sceneMode === SceneMode.SCENE2D || that._flightInProgress) { return; } that.dropDownVisible = !that.dropDownVisible; }); - var morphComplete = function(transitioner, oldMode, newMode, isMorphing) { + this._eventHelper = new EventHelper(); + this._eventHelper.add(scene.morphComplete, function(transitioner, oldMode, newMode, isMorphing) { that.sceneMode = newMode; that._orthographic = newMode === SceneMode.SCENE2D || that._scene.camera.frustum instanceof OrthographicFrustum; - }; - this._eventHelper = new EventHelper(); - this._eventHelper.add(scene.morphComplete, morphComplete); + }); + this._eventHelper.add(scene.preRender, function() { + that._flightInProgress = defined(scene.camera._currentFlight); + }); this._switchToPerspective = createCommand(function() { if (that.sceneMode === SceneMode.SCENE2D) { @@ -239,6 +242,7 @@ define([ * Destroys the view model. */ ProjectionPickerViewModel.prototype.destroy = function() { + this._eventHelper.removeAll(); destroyObject(this); }; From 0ab72925a72bb6c517df320f2876e59584a07e81 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 14:19:07 -0500 Subject: [PATCH 37/46] Fix tests. --- Specs/Scene/BillboardCollectionSpec.js | 6 ++++++ Specs/Scene/GeometryRenderingSpec.js | 11 ++++++++++ Specs/Scene/ModelSpec.js | 7 +++++++ Specs/Scene/PickSpec.js | 2 -- Specs/Scene/PrimitiveCullingSpec.js | 13 ++++++++++++ Specs/Scene/PrimitiveSpec.js | 8 ++++++++ Specs/Scene/SceneSpec.js | 7 +++++++ .../SceneModePickerViewModelSpec.js | 20 +++++++++---------- 8 files changed, 62 insertions(+), 12 deletions(-) diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index 61a1808cd7cd..d4ab1557d34a 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -17,6 +17,7 @@ defineSuite([ 'Scene/HeightReference', 'Scene/HorizontalOrigin', 'Scene/OrthographicOffCenterFrustum', + 'Scene/PerspectiveFrustum', 'Scene/TextureAtlas', 'Scene/VerticalOrigin', 'Specs/createGlobe', @@ -41,6 +42,7 @@ defineSuite([ HeightReference, HorizontalOrigin, OrthographicOffCenterFrustum, + PerspectiveFrustum, TextureAtlas, VerticalOrigin, createGlobe, @@ -90,6 +92,10 @@ defineSuite([ camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()); camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + billboards = new BillboardCollection(); scene.primitives.add(billboards); }); diff --git a/Specs/Scene/GeometryRenderingSpec.js b/Specs/Scene/GeometryRenderingSpec.js index a0052eb22384..4f43998b967b 100644 --- a/Specs/Scene/GeometryRenderingSpec.js +++ b/Specs/Scene/GeometryRenderingSpec.js @@ -33,6 +33,7 @@ defineSuite([ 'Scene/EllipsoidSurfaceAppearance', 'Scene/Material', 'Scene/PerInstanceColorAppearance', + 'Scene/PerspectiveFrustum', 'Scene/PolylineColorAppearance', 'Scene/Primitive', 'Scene/SceneMode', @@ -72,6 +73,7 @@ defineSuite([ EllipsoidSurfaceAppearance, Material, PerInstanceColorAppearance, + PerspectiveFrustum, PolylineColorAppearance, Primitive, SceneMode, @@ -96,6 +98,15 @@ defineSuite([ scene.destroyForSpecs(); }); + beforeEach(function() { + scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + }); + afterEach(function() { scene.primitives.removeAll(); primitive = primitive && !primitive.isDestroyed() && primitive.destroy(); diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 271643969cde..36af82b173af 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -32,6 +32,7 @@ defineSuite([ 'Scene/ColorBlendMode', 'Scene/HeightReference', 'Scene/ModelAnimationLoop', + 'Scene/PerspectiveFrustum', 'Specs/createScene', 'Specs/pollToPromise', 'ThirdParty/when' @@ -68,6 +69,7 @@ defineSuite([ ColorBlendMode, HeightReference, ModelAnimationLoop, + PerspectiveFrustum, createScene, pollToPromise, when) { @@ -156,6 +158,11 @@ defineSuite([ beforeEach(function() { scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); }); function addZoomTo(model) { diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 152bd8283848..bcae4bafb71f 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -309,7 +309,6 @@ defineSuite([ var rectangle = createRectangle(); scene.initializeFrame(); expect(scene).toPickPrimitive(rectangle); - scene.morphTo3D(0.0); }); it('picks in 3D with orthographic projection', function() { @@ -325,6 +324,5 @@ defineSuite([ var rectangle = createRectangle(); scene.initializeFrame(); expect(scene).toPickPrimitive(rectangle); - scene.morphTo3D(0.0); }); }, 'WebGL'); diff --git a/Specs/Scene/PrimitiveCullingSpec.js b/Specs/Scene/PrimitiveCullingSpec.js index f54234048764..ac57eeaaee8f 100644 --- a/Specs/Scene/PrimitiveCullingSpec.js +++ b/Specs/Scene/PrimitiveCullingSpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/defaultValue', 'Core/GeometryInstance', 'Core/loadImage', + 'Core/Math', 'Core/Rectangle', 'Core/RectangleGeometry', 'Core/Transforms', @@ -15,6 +16,7 @@ defineSuite([ 'Scene/LabelCollection', 'Scene/Material', 'Scene/PerInstanceColorAppearance', + 'Scene/PerspectiveFrustum', 'Scene/PolylineCollection', 'Scene/Primitive', 'Scene/SceneMode', @@ -27,6 +29,7 @@ defineSuite([ defaultValue, GeometryInstance, loadImage, + CesiumMath, Rectangle, RectangleGeometry, Transforms, @@ -36,6 +39,7 @@ defineSuite([ LabelCollection, Material, PerInstanceColorAppearance, + PerspectiveFrustum, PolylineCollection, Primitive, SceneMode, @@ -61,6 +65,15 @@ defineSuite([ scene.destroyForSpecs(); }); + beforeEach(function() { + scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + }); + afterEach(function() { scene.primitives.removeAll(); primitive = primitive && primitive.destroy(); diff --git a/Specs/Scene/PrimitiveSpec.js b/Specs/Scene/PrimitiveSpec.js index e362215f4f3e..ca981609044b 100644 --- a/Specs/Scene/PrimitiveSpec.js +++ b/Specs/Scene/PrimitiveSpec.js @@ -26,6 +26,7 @@ defineSuite([ 'Scene/Camera', 'Scene/MaterialAppearance', 'Scene/PerInstanceColorAppearance', + 'Scene/PerspectiveFrustum', 'Scene/SceneMode', 'Specs/BadGeometry', 'Specs/createContext', @@ -59,6 +60,7 @@ defineSuite([ Camera, MaterialAppearance, PerInstanceColorAppearance, + PerspectiveFrustum, SceneMode, BadGeometry, createContext, @@ -99,6 +101,12 @@ defineSuite([ beforeEach(function() { scene.morphTo3D(0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); + scene.frameState.passes.render = true; scene.frameState.passes.pick = false; diff --git a/Specs/Scene/SceneSpec.js b/Specs/Scene/SceneSpec.js index df8e6649e9d9..03beddb71f27 100644 --- a/Specs/Scene/SceneSpec.js +++ b/Specs/Scene/SceneSpec.js @@ -27,6 +27,7 @@ defineSuite([ 'Scene/EllipsoidSurfaceAppearance', 'Scene/FrameState', 'Scene/Globe', + 'Scene/PerspectiveFrustum', 'Scene/Primitive', 'Scene/PrimitiveCollection', 'Scene/Scene', @@ -65,6 +66,7 @@ defineSuite([ EllipsoidSurfaceAppearance, FrameState, Globe, + PerspectiveFrustum, Primitive, PrimitiveCollection, Scene, @@ -101,6 +103,11 @@ defineSuite([ scene.fxaa = false; scene.primitives.removeAll(); scene.morphTo3D(0.0); + + var camera = scene.camera; + camera.frustum = new PerspectiveFrustum(); + camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; + camera.frustum.fov = CesiumMath.toRadians(60.0); }); afterAll(function() { diff --git a/Specs/Widgets/SceneModePicker/SceneModePickerViewModelSpec.js b/Specs/Widgets/SceneModePicker/SceneModePickerViewModelSpec.js index a43856889f9a..e07968aec502 100644 --- a/Specs/Widgets/SceneModePicker/SceneModePickerViewModelSpec.js +++ b/Specs/Widgets/SceneModePicker/SceneModePickerViewModelSpec.js @@ -52,7 +52,7 @@ defineSuite([ var viewModel = new SceneModePickerViewModel(scene); viewModel.dropDownVisible = true; - viewModel.morphTo2D(); + viewModel.morphToColumbusView(); expect(viewModel.dropDownVisible).toEqual(false); viewModel.dropDownVisible = true; @@ -60,7 +60,7 @@ defineSuite([ expect(viewModel.dropDownVisible).toEqual(false); viewModel.dropDownVisible = true; - viewModel.morphToColumbusView(); + viewModel.morphTo2D(); expect(viewModel.dropDownVisible).toEqual(false); viewModel.destroy(); @@ -71,17 +71,17 @@ defineSuite([ expect(scene.mode).toEqual(SceneMode.SCENE3D); - viewModel.morphTo2D(); + viewModel.morphToColumbusView(); scene.completeMorph(); - expect(scene.mode).toEqual(SceneMode.SCENE2D); + expect(scene.mode).toEqual(SceneMode.COLUMBUS_VIEW); viewModel.morphTo3D(); scene.completeMorph(); expect(scene.mode).toEqual(SceneMode.SCENE3D); - viewModel.morphToColumbusView(); + viewModel.morphTo2D(); scene.completeMorph(); - expect(scene.mode).toEqual(SceneMode.COLUMBUS_VIEW); + expect(scene.mode).toEqual(SceneMode.SCENE2D); viewModel.destroy(); }); @@ -89,14 +89,14 @@ defineSuite([ it('selectedTooltip changes on transition', function() { var viewModel = new SceneModePickerViewModel(scene); - viewModel.morphTo2D(); - expect(viewModel.selectedTooltip).toEqual(viewModel.tooltip2D); + viewModel.morphToColumbusView(); + expect(viewModel.selectedTooltip).toEqual(viewModel.tooltipColumbusView); viewModel.morphTo3D(); expect(viewModel.selectedTooltip).toEqual(viewModel.tooltip3D); - viewModel.morphToColumbusView(); - expect(viewModel.selectedTooltip).toEqual(viewModel.tooltipColumbusView); + viewModel.morphTo2D(); + expect(viewModel.selectedTooltip).toEqual(viewModel.tooltip2D); viewModel.destroy(); }); From dd5e8ce58f4a0acf15c7857cde92d3b66ae471c8 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 15:13:03 -0500 Subject: [PATCH 38/46] Add projection picker tests. --- .../ProjectionPickerViewModel.js | 14 +- .../ProjectionPicker/ProjectionPickerSpec.js | 128 ++++++++++++++++++ .../ProjectionPickerViewModelSpec.js | 98 ++++++++++++++ 3 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 Specs/Widgets/ProjectionPicker/ProjectionPickerSpec.js create mode 100644 Specs/Widgets/ProjectionPicker/ProjectionPickerViewModelSpec.js diff --git a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js index d839dcc443b9..7dd27365b8b3 100644 --- a/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js +++ b/Source/Widgets/ProjectionPicker/ProjectionPickerViewModel.js @@ -90,7 +90,7 @@ define([ var that = this; knockout.defineProperty(this, 'selectedTooltip', function() { - if (that._scene.camera.frustum instanceof OrthographicFrustum) { + if (that._orthographic) { return that.tooltipOrthographic; } return that.tooltipPerspective; @@ -228,6 +228,18 @@ define([ get : function() { return this._switchToOrthographic; } + }, + + /** + * Gets whether the scene is currently using an orthographic projection. + * @memberof ProjectionPickerViewModel.prototype + * + * @type {Command} + */ + isOrthographicProjection : { + get : function() { + return this._orthographic; + } } }); diff --git a/Specs/Widgets/ProjectionPicker/ProjectionPickerSpec.js b/Specs/Widgets/ProjectionPicker/ProjectionPickerSpec.js new file mode 100644 index 000000000000..b0928bb52cf7 --- /dev/null +++ b/Specs/Widgets/ProjectionPicker/ProjectionPickerSpec.js @@ -0,0 +1,128 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/ProjectionPicker/ProjectionPicker', + 'Core/FeatureDetection', + 'Specs/createScene', + 'Specs/DomEventSimulator' + ], function( + ProjectionPicker, + FeatureDetection, + createScene, + DomEventSimulator) { + 'use strict'; + + var scene; + + beforeAll(function() { + scene = createScene(); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + it('can create and destroy', function() { + var container = document.createElement('span'); + container.id = 'testContainer'; + document.body.appendChild(container); + + var widget = new ProjectionPicker('testContainer', scene); + expect(widget.container.id).toBe(container.id); + expect(widget.isDestroyed()).toEqual(false); + + widget.destroy(); + expect(widget.isDestroyed()).toEqual(true); + + document.body.removeChild(container); + }); + + function addCloseOnInputSpec(name, func) { + it(name + ' event closes dropdown if target is not inside container', function() { + var container = document.createElement('span'); + container.id = 'testContainer'; + document.body.appendChild(container); + + var widget = new ProjectionPicker('testContainer', scene); + + widget.viewModel.dropDownVisible = true; + func(document.body); + expect(widget.viewModel.dropDownVisible).toEqual(false); + + widget.viewModel.dropDownVisible = true; + func(container.firstChild); + expect(widget.viewModel.dropDownVisible).toEqual(true); + + widget.destroy(); + document.body.removeChild(container); + }); + } + + function addDisabledDuringFlightSpec(name, func) { + it(name + ' event does nothing during camera flight', function() { + var container = document.createElement('span'); + container.id = 'testContainer'; + document.body.appendChild(container); + + var widget = new ProjectionPicker('testContainer', scene); + + scene.camera.flyHome(100.0); + + func(container.firstChild); + expect(widget.viewModel.dropDownVisible).toEqual(false); + + scene.camera.cancelFlight(); + + widget.destroy(); + document.body.removeChild(container); + }); + } + + function addDisabledIn2DSpec(name, func) { + it(name + ' event does nothing in 2D', function() { + var container = document.createElement('span'); + container.id = 'testContainer'; + document.body.appendChild(container); + + var widget = new ProjectionPicker('testContainer', scene); + + scene.morphTo2D(0.0); + + func(container.firstChild); + expect(widget.viewModel.dropDownVisible).toEqual(false); + + widget.destroy(); + document.body.removeChild(container); + }); + } + + if (FeatureDetection.supportsPointerEvents()) { + addCloseOnInputSpec('pointerDown', DomEventSimulator.firePointerDown); + addDisabledDuringFlightSpec('pointerDown', DomEventSimulator.firePointerDown); + addDisabledIn2DSpec('pointerDown', DomEventSimulator.firePointerDown); + } else { + addCloseOnInputSpec('mousedown', DomEventSimulator.fireMouseDown); + addCloseOnInputSpec('touchstart', DomEventSimulator.fireTouchStart); + addDisabledDuringFlightSpec('mousedown', DomEventSimulator.fireMouseDown); + addDisabledDuringFlightSpec('touchstart', DomEventSimulator.fireTouchStart); + addDisabledIn2DSpec('mousedown', DomEventSimulator.fireMouseDown); + addDisabledIn2DSpec('touchstart', DomEventSimulator.fireTouchStart); + } + + it('constructor throws with no scene', function() { + expect(function() { + return new ProjectionPicker(document.body, undefined); + }).toThrowDeveloperError(); + }); + + it('constructor throws with no element', function() { + expect(function() { + return new ProjectionPicker(undefined, scene); + }).toThrowDeveloperError(); + }); + + it('constructor throws with string element that does not exist', function() { + expect(function() { + return new ProjectionPicker('does not exist', scene); + }).toThrowDeveloperError(); + }); +}, 'WebGL'); diff --git a/Specs/Widgets/ProjectionPicker/ProjectionPickerViewModelSpec.js b/Specs/Widgets/ProjectionPicker/ProjectionPickerViewModelSpec.js new file mode 100644 index 000000000000..4657b90dcfc2 --- /dev/null +++ b/Specs/Widgets/ProjectionPicker/ProjectionPickerViewModelSpec.js @@ -0,0 +1,98 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/ProjectionPicker/ProjectionPickerViewModel', + 'Scene/OrthographicFrustum', + 'Scene/PerspectiveFrustum', + 'Scene/SceneMode', + 'Specs/createScene' + ], function( + ProjectionPickerViewModel, + OrthographicFrustum, + PerspectiveFrustum, + SceneMode, + createScene) { + 'use strict'; + + var scene; + + beforeEach(function() { + scene = createScene(); + }); + + afterEach(function() { + scene.destroyForSpecs(); + }); + + it('Can construct and destroy', function() { + var viewModel = new ProjectionPickerViewModel(scene); + expect(viewModel.scene).toBe(scene); + expect(scene.morphComplete.numberOfListeners).toEqual(1); + expect(scene.preRender.numberOfListeners).toEqual(1); + expect(viewModel.isDestroyed()).toEqual(false); + viewModel.destroy(); + expect(viewModel.isDestroyed()).toEqual(true); + expect(scene.morphComplete.numberOfListeners).toEqual(0); + expect(scene.preRender.numberOfListeners).toEqual(0); + }); + + it('dropDownVisible and toggleDropDown work', function() { + var viewModel = new ProjectionPickerViewModel(scene); + + expect(viewModel.dropDownVisible).toEqual(false); + viewModel.toggleDropDown(); + expect(viewModel.dropDownVisible).toEqual(true); + viewModel.dropDownVisible = false; + expect(viewModel.dropDownVisible).toEqual(false); + + viewModel.destroy(); + }); + + it('morphing to 2D calls correct transition', function() { + var viewModel = new ProjectionPickerViewModel(scene); + + expect(scene.mode).toEqual(SceneMode.SCENE3D); + expect(viewModel.isOrthographicProjection).toEqual(false); + + scene.morphTo2D(0); + expect(scene.mode).toEqual(SceneMode.SCENE2D); + expect(viewModel.isOrthographicProjection).toEqual(true); + + viewModel.destroy(); + }); + + it('switching projection calls correct transition', function() { + var viewModel = new ProjectionPickerViewModel(scene); + + expect(scene.mode).toEqual(SceneMode.SCENE3D); + expect(viewModel.isOrthographicProjection).toEqual(false); + expect(scene.camera.frustum instanceof PerspectiveFrustum).toEqual(true); + + viewModel.switchToOrthographic(); + expect(viewModel.isOrthographicProjection).toEqual(true); + expect(scene.camera.frustum instanceof OrthographicFrustum).toEqual(true); + + viewModel.switchToPerspective(); + expect(viewModel.isOrthographicProjection).toEqual(false); + expect(scene.camera.frustum instanceof PerspectiveFrustum).toEqual(true); + + viewModel.destroy(); + }); + + it('selectedTooltip changes on transition', function() { + var viewModel = new ProjectionPickerViewModel(scene); + + viewModel.switchToOrthographic(); + expect(viewModel.selectedTooltip).toEqual(viewModel.tooltipOrthographic); + + viewModel.switchToPerspective(); + expect(viewModel.selectedTooltip).toEqual(viewModel.tooltipPerspective); + + viewModel.destroy(); + }); + + it('create throws with undefined scene', function() { + expect(function() { + return new ProjectionPickerViewModel(); + }).toThrowDeveloperError(); + }); +}, 'WebGL'); From cf7112342c8e4a4a29a52ab1e958967f1b86c936 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 16:28:08 -0500 Subject: [PATCH 39/46] Disable VR button when in orthographic. --- Source/Widgets/VRButton/VRButton.js | 1 + Source/Widgets/VRButton/VRButtonViewModel.js | 28 +++++++++++++++++-- Specs/Widgets/VRButton/VRButtonSpec.js | 26 ++++++++++++----- .../Widgets/VRButton/VRButtonViewModelSpec.js | 12 ++++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/Source/Widgets/VRButton/VRButton.js b/Source/Widgets/VRButton/VRButton.js index 1945fb2df8be..795f50212461 100644 --- a/Source/Widgets/VRButton/VRButton.js +++ b/Source/Widgets/VRButton/VRButton.js @@ -53,6 +53,7 @@ define([ element.type = 'button'; element.className = 'cesium-button cesium-vrButton'; element.setAttribute('data-bind', '\ +css: { "cesium-button-disabled" : _isOrthographic }, \ attr: { title: tooltip },\ click: command,\ enable: isVREnabled,\ diff --git a/Source/Widgets/VRButton/VRButtonViewModel.js b/Source/Widgets/VRButton/VRButtonViewModel.js index ccf9e2067aeb..a5ec9bfbc66e 100644 --- a/Source/Widgets/VRButton/VRButtonViewModel.js +++ b/Source/Widgets/VRButton/VRButtonViewModel.js @@ -5,7 +5,9 @@ define([ '../../Core/defineProperties', '../../Core/destroyObject', '../../Core/DeveloperError', + '../../Core/EventHelper', '../../Core/Fullscreen', + '../../Scene/OrthographicFrustum', '../../ThirdParty/knockout', '../../ThirdParty/NoSleep', '../createCommand', @@ -16,7 +18,9 @@ define([ defineProperties, destroyObject, DeveloperError, + EventHelper, Fullscreen, + OrthographicFrustum, knockout, NoSleep, createCommand, @@ -55,7 +59,11 @@ define([ } } - function toggleVR(viewModel, scene, isVRMode) { + function toggleVR(viewModel, scene, isVRMode, isOrthographic) { + if (isOrthographic()) { + return; + } + if (isVRMode()) { scene.useWebVR = false; if (viewModel._locked) { @@ -139,11 +147,25 @@ define([ return isVRMode() ? 'Exit VR mode' : 'Enter VR mode'; }); + var isOrthographic = knockout.observable(false); + + this._isOrthographic = undefined; + knockout.defineProperty(this, '_isOrthographic', { + get : function() { + return isOrthographic(); + } + }); + + this._eventHelper = new EventHelper(); + this._eventHelper.add(scene.preRender, function() { + isOrthographic(scene.camera.frustum instanceof OrthographicFrustum); + }); + this._locked = false; this._noSleep = new NoSleep(); this._command = createCommand(function() { - toggleVR(that, scene, isVRMode); + toggleVR(that, scene, isVRMode, isOrthographic); }, knockout.getObservable(this, 'isVREnabled')); this._vrElement = defaultValue(getElement(vrElement), document.body); @@ -211,6 +233,8 @@ define([ * properly clean up the view model when it is no longer needed. */ VRButtonViewModel.prototype.destroy = function() { + this._eventHelper.removeAll(); + document.removeEventListener(Fullscreen.changeEventName, this._callback); destroyObject(this); }; diff --git a/Specs/Widgets/VRButton/VRButtonSpec.js b/Specs/Widgets/VRButton/VRButtonSpec.js index 8cf74e0b9b36..7b1293736ce9 100644 --- a/Specs/Widgets/VRButton/VRButtonSpec.js +++ b/Specs/Widgets/VRButton/VRButtonSpec.js @@ -1,12 +1,24 @@ /*global defineSuite*/ defineSuite([ - 'Widgets/VRButton/VRButton' + 'Widgets/VRButton/VRButton', + 'Specs/createScene' ], function( - VRButton) { + VRButton, + createScene) { 'use strict'; + var scene; + + beforeEach(function() { + scene = createScene(); + }); + + afterEach(function() { + scene.destroyForSpecs(); + }); + it('constructor sets default values', function() { - var vrButton = new VRButton(document.body, {}); + var vrButton = new VRButton(document.body, scene); expect(vrButton.container).toBe(document.body); expect(vrButton.viewModel.vrElement).toBe(document.body); expect(vrButton.isDestroyed()).toEqual(false); @@ -16,7 +28,7 @@ defineSuite([ it('constructor sets expected values', function() { var testElement = document.createElement('span'); - var vrButton = new VRButton(document.body, {}, testElement); + var vrButton = new VRButton(document.body, scene, testElement); expect(vrButton.container).toBe(document.body); expect(vrButton.viewModel.vrElement).toBe(testElement); vrButton.destroy(); @@ -26,7 +38,7 @@ defineSuite([ var testElement = document.createElement('span'); testElement.id = 'testElement'; document.body.appendChild(testElement); - var vrButton = new VRButton('testElement', {}); + var vrButton = new VRButton('testElement', scene); expect(vrButton.container).toBe(testElement); document.body.removeChild(testElement); vrButton.destroy(); @@ -34,13 +46,13 @@ defineSuite([ it('throws if container is undefined', function() { expect(function() { - return new VRButton(undefined, {}); + return new VRButton(undefined, scene); }).toThrowDeveloperError(); }); it('throws if container string is undefined', function() { expect(function() { - return new VRButton('testElement', {}); + return new VRButton('testElement', scene); }).toThrowDeveloperError(); }); diff --git a/Specs/Widgets/VRButton/VRButtonViewModelSpec.js b/Specs/Widgets/VRButton/VRButtonViewModelSpec.js index 7821f1e74037..2fdd4ea0ba0c 100644 --- a/Specs/Widgets/VRButton/VRButtonViewModelSpec.js +++ b/Specs/Widgets/VRButton/VRButtonViewModelSpec.js @@ -1,16 +1,22 @@ /*global defineSuite*/ defineSuite([ 'Widgets/VRButton/VRButtonViewModel', - 'Core/Fullscreen' + 'Core/Fullscreen', + 'Specs/createScene' ], function( VRButtonViewModel, - Fullscreen) { + Fullscreen, + createScene) { 'use strict'; var scene; beforeEach(function() { - scene = {}; + scene = createScene(); + }); + + afterEach(function() { + scene.destroyForSpecs(); }); it('constructor sets default values', function() { From 3937422959988052d34d68b4d65acd743d2c467f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 17:01:37 -0500 Subject: [PATCH 40/46] Deprecate OrthographicFrustum plane distance properties. --- CHANGES.md | 2 + Source/Renderer/UniformState.js | 2 +- Source/Scene/Camera.js | 9 +-- Source/Scene/OrthographicFrustum.js | 94 ++++++++++++++++++++++++++--- Source/Scene/QuadtreePrimitive.js | 2 +- Source/Scene/Scene.js | 7 ++- Source/Scene/SceneTransforms.js | 2 +- 7 files changed, 100 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4e424327b4aa..206273838cef 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 the event `Viewer.trackedEntityChanged`, which is raised when the value of `viewer.trackedEntity` changes. [#5060](https://github.com/AnalyticalGraphicsInc/cesium/pull/5060) * Added `Camera.DEFAULT_OFFSET` for default view of objects with bounding spheres [#4936](https://github.com/AnalyticalGraphicsInc/cesium/pull/4936) * Fix crunch compressed textures in IE11. [#5057](https://github.com/AnalyticalGraphicsInc/cesium/pull/5057) diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 18640699ac90..89eacb030303 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -903,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; } diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index cebe7b20f98d..1bcb8287270b 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -934,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'); @@ -1792,7 +1792,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'); @@ -2432,7 +2433,7 @@ define([ var height = canvas.clientHeight; var frustum = camera.frustum; - if (!defined(frustum.top)) { + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } var x = (2.0 / width) * windowPosition.x - 1.0; @@ -2775,7 +2776,7 @@ define([ function distanceToBoundingSphere2D(camera, radius) { var frustum = camera.frustum; - if (!defined(frustum.top)) { + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js index ffcf79f3bdfa..5271d1daa5dd 100644 --- a/Source/Scene/OrthographicFrustum.js +++ b/Source/Scene/OrthographicFrustum.js @@ -2,11 +2,13 @@ define([ '../Core/defined', '../Core/defineProperties', + '../Core/deprecationWarning', '../Core/DeveloperError', './OrthographicOffCenterFrustum' ], function( defined, defineProperties, + deprecationWarning, DeveloperError, OrthographicOffCenterFrustum) { 'use strict'; @@ -55,6 +57,8 @@ define([ */ this.far = 500000000.0; this._far = this.far; + + this._useDeprecated = false; } function update(frustum) { @@ -82,17 +86,19 @@ define([ frustum._near = frustum.near; frustum._far = frustum.far; - var ratio = frustum.aspectRatio; - if (ratio > 1.0) { - ratio = 1.0 / frustum.aspectRatio; - } + 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; + 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; + } } } @@ -108,6 +114,74 @@ define([ update(this); 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; + } } }); diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 8826affbb5de..634e98218253 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -691,7 +691,7 @@ define([ function screenSpaceError2D(primitive, frameState, tile) { var camera = frameState.camera; var frustum = camera.frustum; - if (!defined(frustum.top)) { + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index dea4a22ebe42..29197780a636 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -1095,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'; @@ -2634,7 +2639,7 @@ define([ function getPickOrthographicCullingVolume(scene, drawingBufferPosition, width, height) { var camera = scene._camera; var frustum = camera.frustum; - if (!defined(frustum.top)) { + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index 9d5aa4f120a7..ec2654e2bd94 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -327,7 +327,7 @@ define([ var worldCoords; var frustum = scene.camera.frustum; if (!defined(frustum.fovy)) { - if (!defined(frustum.top)) { + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } var currentFrustum = uniformState.currentFrustum; From 620c090eb4d39f8c599d8b2b558c4059929da996 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 17:13:25 -0500 Subject: [PATCH 41/46] Code updates from review. --- Source/Shaders/Builtin/Functions/alphaWeight.glsl | 6 +----- .../Shaders/Builtin/Functions/windowToEyeCoordinates.glsl | 6 +----- Source/Widgets/Viewer/Viewer.js | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Source/Shaders/Builtin/Functions/alphaWeight.glsl b/Source/Shaders/Builtin/Functions/alphaWeight.glsl index c6d9c4555381..ed18c4285cb7 100644 --- a/Source/Shaders/Builtin/Functions/alphaWeight.glsl +++ b/Source/Shaders/Builtin/Functions/alphaWeight.glsl @@ -9,11 +9,7 @@ float czm_alphaWeight(float a) vec4 q = vec4(x, y, z, 0.0); q /= gl_FragCoord.w; - bool usingOrthographic = all(equal(czm_inverseProjection[0], vec4(0.0))) && - all(equal(czm_inverseProjection[1], vec4(0.0))) && - all(equal(czm_inverseProjection[2], vec4(0.0))) && - all(equal(czm_inverseProjection[3], vec4(0.0))); - if (!usingOrthographic) { + if (czm_inverseProjection != mat4(0.0)) { q = czm_inverseProjection * q; } else { float top = czm_frustumPlanes.x; diff --git a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl index 8b6ef3f6040f..f429c26875ec 100644 --- a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl +++ b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl @@ -31,11 +31,7 @@ vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate) vec4 q = vec4(x, y, z, 1.0); q /= fragmentCoordinate.w; - bool usingOrthographic = all(equal(czm_inverseProjection[0], vec4(0.0))) && - all(equal(czm_inverseProjection[1], vec4(0.0))) && - all(equal(czm_inverseProjection[2], vec4(0.0))) && - all(equal(czm_inverseProjection[3], vec4(0.0))); - if (!usingOrthographic) { + if (czm_inverseProjection != mat4(0.0)) { q = czm_inverseProjection * q; } else { float top = czm_frustumPlanes.x; diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 7073f8beba56..59d4d49cddc6 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -207,7 +207,7 @@ define([ if(defined(sceneModePicker)) { sceneModePicker.container.style.visibility = visibility; } - if(defined(projectionPicker)) { + if (defined(projectionPicker)) { projectionPicker.container.style.visibility = visibility; } if(defined(baseLayerPicker)) { From b0f06c577b31ee244be95b1d94c29ae5cd364c0f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 9 Mar 2017 17:51:29 -0500 Subject: [PATCH 42/46] Update debug camera for orthographic projections. --- Source/Scene/DebugCameraPrimitive.js | 61 ++++++++++++++++++++++------ Source/Scene/SceneTransforms.js | 14 ------- Source/Scene/Sun.js | 8 ++-- 3 files changed, 53 insertions(+), 30 deletions(-) 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/SceneTransforms.js b/Source/Scene/SceneTransforms.js index ec2654e2bd94..9156130c26f8 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -283,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 */ 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() { From e13bc3d9628a768700f84216f14ca30df9599da3 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 13 Mar 2017 15:11:27 -0400 Subject: [PATCH 43/46] Adjust frustum when morphing and when the camera is above a certain height. --- Source/Scene/Camera.js | 18 ++++++++++++------ Source/Scene/SceneTransitioner.js | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 1bcb8287270b..bee7dfa09523 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -985,11 +985,15 @@ define([ var pickGlobeScratchRay = new Ray(); var scratchRayIntersection = new Cartesian3(); - Camera.prototype._adjustOrthographicFrustum = function() { + 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; @@ -1012,7 +1016,7 @@ define([ } if (!defined(globe) || (!defined(rayIntersection))) { - var distance = this.positionCartographic.height; + var distance = Math.max(this.positionCartographic.height, 0.0); this.frustum.width = distance; } }; @@ -1041,7 +1045,7 @@ define([ camera._setTransform(currentTransform); - camera._adjustOrthographicFrustum(); + camera._adjustOrthographicFrustum(true); } function setViewCV(camera, position,hpr, convert) { @@ -1067,7 +1071,7 @@ define([ camera._setTransform(currentTransform); - camera._adjustOrthographicFrustum(); + camera._adjustOrthographicFrustum(true); } function setView2D(camera, position, hpr, convert) { @@ -1475,7 +1479,7 @@ define([ if (this._mode === SceneMode.SCENE2D) { clampMove2D(this, cameraPosition); } - this._adjustOrthographicFrustum(); + this._adjustOrthographicFrustum(true); }; /** @@ -1691,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); }; /** @@ -2039,7 +2045,7 @@ define([ Cartesian3.cross(this.right, this.direction, this.up); Cartesian3.normalize(this.up, this.up); - this._adjustOrthographicFrustum(); + this._adjustOrthographicFrustum(true); }; var viewRectangle3DCartographic1 = new Cartographic(); diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js index f7829e7fd9de..7c0f2fb6fe85 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -573,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) { @@ -795,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, From 6c7f5bba63eb67d0904b0a4e029478eca3c84314 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 13 Mar 2017 15:27:45 -0400 Subject: [PATCH 44/46] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 206273838cef..ac658b7e461f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Change Log * Fixed a bug in `Quaternion.fromHeadingPitchRoll` that made it erroneously throw an exception when passed individual angles in an unminified / debug build. * Fix `GroundPrimitive` rendering in 2D and Columbus View [#5078](https://github.com/AnalyticalGraphicsInc/cesium/pull/5078) * Fixed a bug in `ModelAnimationCache` causing different animations to reference the same animation. [#5064](https://github.com/AnalyticalGraphicsInc/cesium/pull/5064) +* 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 From e2ecaa01f6fd8294e30f09904426f775269a9866 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 15 Mar 2017 14:19:32 -0400 Subject: [PATCH 45/46] Fix camera when click-dragging space close to the surface. --- Source/Scene/ScreenSpaceCameraController.js | 47 +++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 0f9c7f8209f5..6d07334507e4 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1820,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; @@ -1843,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); From 9182136ecb0c8c126d164c25da282a29f3ca1ef4 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 15 Mar 2017 14:24:57 -0400 Subject: [PATCH 46/46] Default to not creating the projection picker. --- Source/Widgets/Viewer/Viewer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 59d4d49cddc6..dad0543c9b1b 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -285,6 +285,7 @@ define([ * @param {Boolean} [options.shadows=false] Determines if shadows are cast by the sun. * @param {ShadowMode} [options.terrainShadows=ShadowMode.RECEIVE_ONLY] Determines if the terrain casts or receives shadows from the sun. * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. + * @param {Boolean} [options.projectionPicker=false] If set to true, the ProjectionPicker widget will be created. * * @exception {DeveloperError} Element with id "container" does not exist in the document. * @exception {DeveloperError} options.imageryProvider is not available when using the BaseLayerPicker widget, specify options.selectedImageryProviderViewModel instead. @@ -516,7 +517,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to } var projectionPicker; - if (!defined(options.projectionPicker) || options.projectionPicker !== false) { + if (options.projectionPicker) { projectionPicker = new ProjectionPicker(toolbar, cesiumWidget.scene); }