diff --git a/CHANGES.md b/CHANGES.md index 5f6648d972ac..50c2b920ad6f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ Change Log ========== ### 1.31 - 2017-03-01 - +* Added support to `DebugCameraPrimitive` to draw multifrustum planes. The attribute `debugShowFrustumPlanes` of `Scene` and `frustumPlanes` of `CesiumInspector` toggles this. `FrameState` has been augmented to include `frustumSplits` which is a `Number[]` of the near/far planes of the camera frustums. * Enable rendering `GroundPrimitives` on hardware without the `EXT_frag_depth` extension; however, this could cause artifacts for certain viewing angles. * Added compressed texture support. [#4758](https://github.com/AnalyticalGraphicsInc/cesium/pull/4758) * glTF models and imagery layers can now reference [KTX](https://www.khronos.org/opengles/sdk/tools/KTX/) textures and textures compressed with [crunch](https://github.com/BinomialLLC/crunch). diff --git a/Source/Scene/DebugCameraPrimitive.js b/Source/Scene/DebugCameraPrimitive.js index ffcf283e6cf1..c282852684cd 100644 --- a/Source/Scene/DebugCameraPrimitive.js +++ b/Source/Scene/DebugCameraPrimitive.js @@ -92,24 +92,20 @@ define([ this._planesPrimitive = undefined; } - var frustumCornersNDC = new Array(8); - frustumCornersNDC[0] = new Cartesian4(-1.0, -1.0, -1.0, 1.0); - frustumCornersNDC[1] = new Cartesian4(1.0, -1.0, -1.0, 1.0); - frustumCornersNDC[2] = new Cartesian4(1.0, 1.0, -1.0, 1.0); - frustumCornersNDC[3] = new Cartesian4(-1.0, 1.0, -1.0, 1.0); - frustumCornersNDC[4] = new Cartesian4(-1.0, -1.0, 1.0, 1.0); - frustumCornersNDC[5] = new Cartesian4(1.0, -1.0, 1.0, 1.0); - frustumCornersNDC[6] = new Cartesian4(1.0, 1.0, 1.0, 1.0); - frustumCornersNDC[7] = new Cartesian4(-1.0, 1.0, 1.0, 1.0); + var frustumCornersNDC = new Array(4); + frustumCornersNDC[0] = new Cartesian4(-1.0, -1.0, 1.0, 1.0); + frustumCornersNDC[1] = new Cartesian4(1.0, -1.0, 1.0, 1.0); + frustumCornersNDC[2] = new Cartesian4(1.0, 1.0, 1.0, 1.0); + frustumCornersNDC[3] = new Cartesian4(-1.0, 1.0, 1.0, 1.0); var scratchMatrix = new Matrix4(); - var scratchFrustumCorners = new Array(8); - for (var i = 0; i < 8; ++i) { + var scratchFrustumCorners = new Array(4); + for (var i = 0; i < 4; ++i) { scratchFrustumCorners[i] = new Cartesian4(); } var scratchColor = new Color(); - + var scratchSplits = [1.0, 100000.0]; /** * @private */ @@ -130,14 +126,34 @@ define([ var viewProjection = Matrix4.multiply(projection, view, scratchMatrix); var inverseViewProjection = Matrix4.inverse(viewProjection, scratchMatrix); - var positions = new Float64Array(8 * 3); - for (var i = 0; i < 8; ++i) { - var corner = Cartesian4.clone(frustumCornersNDC[i], scratchFrustumCorners[i]); - Matrix4.multiplyByVector(inverseViewProjection, corner, corner); - Cartesian3.divideByScalar(corner, corner.w, corner); // Handle the perspective divide - positions[i * 3] = corner.x; - positions[i * 3 + 1] = corner.y; - positions[i * 3 + 2] = corner.z; + var frustumSplits = frameState.frustumSplits; + var numFrustums = frustumSplits.length - 1; + if (numFrustums <= 0) { + frustumSplits = scratchSplits; // Use near and far planes if no splits created + frustumSplits[0] = this._camera.frustum.near; + frustumSplits[1] = this._camera.frustum.far; + numFrustums = 1; + } + + 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); + + positions[12 * f + i * 3] = corner.x; + positions[12 * f + i * 3 + 1] = corner.y; + positions[12 * f + i * 3 + 2] = corner.z; + } } var boundingSphere = new BoundingSphere.fromVertices(positions); @@ -149,8 +165,38 @@ define([ values : positions }); + var offset, index; + // Create the outline primitive - var outlineIndices = new Uint16Array([0,1,1,2,2,3,3,0,0,4,4,7,7,3,7,6,6,2,2,1,1,5,5,4,5,6]); + var outlineIndices = new Uint16Array(8 * (2 * numFrustums + 1)); + // Build the far planes + for (f = 0; f < numFrustums + 1; ++f) { + offset = f * 8; + index = f * 4; + + outlineIndices[offset] = index; + outlineIndices[offset + 1] = index + 1; + outlineIndices[offset + 2] = index + 1; + outlineIndices[offset + 3] = index + 2; + outlineIndices[offset + 4] = index + 2; + outlineIndices[offset + 5] = index + 3; + outlineIndices[offset + 6] = index + 3; + outlineIndices[offset + 7] = index; + } + // Build the sides of the frustums + for (f = 0; f < numFrustums; ++f) { + offset = (numFrustums + 1 + f) * 8; + index = f * 4; + + outlineIndices[offset] = index; + outlineIndices[offset + 1] = index + 4; + outlineIndices[offset + 2] = index + 1; + outlineIndices[offset + 3] = index + 5; + outlineIndices[offset + 4] = index + 2; + outlineIndices[offset + 5] = index + 6; + outlineIndices[offset + 6] = index + 3; + outlineIndices[offset + 7] = index + 7; + } this._outlinePrimitive = new Primitive({ geometryInstances : new GeometryInstance({ @@ -174,7 +220,52 @@ define([ }); // Create the planes primitive - var planesIndices = new Uint16Array([4,5,6,4,6,7,5,1,2,5,2,6,7,6,2,7,2,3,0,1,5,0,5,4,0,4,7,0,7,3,1,0,3,1,3,2]); + var planesIndices = new Uint16Array(6 * (5 * numFrustums + 1)); + // Build the far planes + for (f = 0; f < numFrustums + 1; ++f) { + offset = f * 6; + index = f * 4; + + planesIndices[offset] = index; + planesIndices[offset + 1] = index + 1; + planesIndices[offset + 2] = index + 2; + planesIndices[offset + 3] = index; + planesIndices[offset + 4] = index + 2; + planesIndices[offset + 5] = index + 3; + } + // Build the sides of the frustums + for (f = 0; f < numFrustums; ++f) { + offset = (numFrustums + 1 + 4 * f) * 6; + index = f * 4; + + planesIndices[offset] = index + 4; + planesIndices[offset + 1] = index; + planesIndices[offset + 2] = index + 3; + planesIndices[offset + 3] = index + 4; + planesIndices[offset + 4] = index + 3; + planesIndices[offset + 5] = index + 7; + + planesIndices[offset + 6] = index + 4; + planesIndices[offset + 7] = index; + planesIndices[offset + 8] = index + 1; + planesIndices[offset + 9] = index + 4; + planesIndices[offset + 10] = index + 1; + planesIndices[offset + 11] = index + 5; + + planesIndices[offset + 12] = index + 7; + planesIndices[offset + 13] = index + 3; + planesIndices[offset + 14] = index + 2; + planesIndices[offset + 15] = index + 7; + planesIndices[offset + 16] = index + 2; + planesIndices[offset + 17] = index + 6; + + planesIndices[offset + 18] = index + 6; + planesIndices[offset + 19] = index + 2; + planesIndices[offset + 20] = index + 1; + planesIndices[offset + 21] = index + 6; + planesIndices[offset + 22] = index + 1; + planesIndices[offset + 23] = index + 5; + } this._planesPrimitive = new Primitive({ geometryInstances : new GeometryInstance({ diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index b29b83806062..f28094fd9c7b 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -235,6 +235,13 @@ define([ * @default 0.0 */ this.imagerySplitPosition = 0.0; + + /** + * Distances to the near and far planes of the camera frustums + * @type {Number[]} + * @default [] + */ + this.frustumSplits = []; } /** diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 8ebad9dda2d4..261ccb8c2c43 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -42,6 +42,7 @@ define([ './Camera', './CreditDisplay', './CullingVolume', + './DebugCameraPrimitive', './DepthPlane', './DeviceOrientationCameraController', './Fog', @@ -109,6 +110,7 @@ define([ Camera, CreditDisplay, CullingVolume, + DebugCameraPrimitive, DepthPlane, DeviceOrientationCameraController, Fog, @@ -515,6 +517,20 @@ define([ */ this.debugShowDepthFrustum = 1; + /** + * This property is for debugging only; it is not for production use. + *

+ * When true, draws outlines to show the boundaries of the camera frustums + *

+ * + * @type Boolean + * + * @default false + */ + this.debugShowFrustumPlanes = false; + this._debugShowFrustumPlanes = false; + this._debugFrustumPlanes = undefined; + /** * When true, enables Fast Approximate Anti-aliasing even when order independent translucency * is unsupported. @@ -1442,6 +1458,15 @@ define([ updateFrustums(near, far, farToNearRatio, numFrustums, frustumCommandsList, is2D, scene.nearToFarDistance2D); createPotentiallyVisibleSet(scene); } + + var frustumSplits = frameState.frustumSplits; + frustumSplits.length = numFrustums + 1; + for (var j = 0; j < numFrustums; ++j) { + frustumSplits[j] = frustumCommandsList[j].near; + if (j === numFrustums - 1) { + frustumSplits[j + 1] = frustumCommandsList[j].far; + } + } } function getAttributeLocations(shaderProgram) { @@ -2202,6 +2227,25 @@ define([ } } + function updateDebugFrustumPlanes(scene) { + var frameState = scene._frameState; + if (scene.debugShowFrustumPlanes !== scene._debugShowFrustumPlanes) { + if (scene.debugShowFrustumPlanes) { + scene._debugFrustumPlanes = new DebugCameraPrimitive({ + camera: scene.camera, + updateOnChange: false + }); + } else { + scene._debugFrustumPlanes = scene._debugFrustumPlanes && scene._debugFrustumPlanes.destroy(); + } + scene._debugShowFrustumPlanes = scene.debugShowFrustumPlanes; + } + + if (defined(scene._debugFrustumPlanes)) { + scene._debugFrustumPlanes.update(frameState); + } + } + function updateShadowMaps(scene) { var frameState = scene._frameState; var shadowMaps = frameState.shadowMaps; @@ -2253,6 +2297,7 @@ define([ scene._groundPrimitives.update(frameState); scene._primitives.update(frameState); + updateDebugFrustumPlanes(scene); updateShadowMaps(scene); if (scene._globe) { @@ -2908,6 +2953,7 @@ define([ this._sunPostProcess = this._sunPostProcess && this._sunPostProcess.destroy(); this._depthPlane = this._depthPlane && this._depthPlane.destroy(); this._transitioner.destroy(); + this._debugFrustumPlanes = this._debugFrustumPlanes && this._debugFrustumPlanes.destroy(); if (defined(this._globeDepth)) { this._globeDepth.destroy(); diff --git a/Source/Widgets/CesiumInspector/CesiumInspector.js b/Source/Widgets/CesiumInspector/CesiumInspector.js index 923ead6b0980..ec0864d7daf6 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspector.js +++ b/Source/Widgets/CesiumInspector/CesiumInspector.js @@ -80,6 +80,7 @@ define([ generalSection.className = 'cesium-cesiumInspector-section'; generalSection.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : generalVisible, "cesium-cesiumInspector-hide" : !generalVisible}'); panel.appendChild(generalSection); + var debugShowFrustums = document.createElement('div'); generalSection.appendChild(debugShowFrustums); var frustumStats = document.createElement('div'); @@ -92,6 +93,14 @@ define([ debugShowFrustums.appendChild(document.createTextNode('Show Frustums')); debugShowFrustums.appendChild(frustumStats); + var debugShowFrustumPlanes = document.createElement('div'); + generalSection.appendChild(debugShowFrustumPlanes); + var frustumPlanesCheckbox = document.createElement('input'); + frustumPlanesCheckbox.type = 'checkbox'; + frustumPlanesCheckbox.setAttribute('data-bind', 'checked: frustumPlanes'); + debugShowFrustumPlanes.appendChild(frustumPlanesCheckbox); + debugShowFrustumPlanes.appendChild(document.createTextNode('Show Frustum Planes')); + var performanceDisplay = document.createElement('div'); generalSection.appendChild(performanceDisplay); var pdCheckbox = document.createElement('input'); diff --git a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js index 8eb93556173a..f5e5b55330ac 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js @@ -107,6 +107,13 @@ define([ */ this.frustums = false; + /** + * Gets or sets the show frustum planes state. This property is observable. + * @type {Boolean} + * @default false + */ + this.frustumPlanes = false; + /** * Gets or sets the show performance display state. This property is observable. * @type {Boolean} @@ -305,6 +312,7 @@ define([ knockout.track(this, [ 'frustums', + 'frustumPlanes', 'performance', 'shaderCacheText', 'primitiveBoundingSphere', @@ -351,6 +359,10 @@ define([ that._scene.debugShowFrustums = val; }); + this._frustumPlanesSubscription = knockout.getObservable(this, 'frustumPlanes').subscribe(function(val) { + that._scene.debugShowFrustumPlanes = val; + }); + this._performanceSubscription = knockout.getObservable(this, 'performance').subscribe(function(val) { if (val) { that._performanceDisplay = new PerformanceDisplay({ @@ -940,6 +952,7 @@ define([ CesiumInspectorViewModel.prototype.destroy = function() { this._eventHandler.destroy(); this._frustumsSubscription.dispose(); + this._frustumPlanesSubscription.dispose(); this._performanceSubscription.dispose(); this._primitiveBoundingSphereSubscription.dispose(); this._primitiveReferenceFrameSubscription.dispose();