From 8e7b0a6ca479400a747b1dd8173b65a1d00cbe22 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 18 Sep 2017 15:51:56 -0400 Subject: [PATCH 01/14] Add option to invert classification. --- Apps/Sandcastle/gallery/Classification.html | 252 ++++++------ Source/Renderer/AutomaticUniforms.js | 14 + Source/Renderer/Pass.js | 9 +- Source/Renderer/UniformState.js | 16 + Source/Scene/Cesium3DTileset.js | 2 +- Source/Scene/ClassificationPrimitive.js | 92 ++++- Source/Scene/FrameState.js | 14 + Source/Scene/GroundPrimitive.js | 24 +- Source/Scene/InvertClassification.js | 369 ++++++++++++++++++ Source/Scene/OIT.js | 59 ++- Source/Scene/Scene.js | 190 +++++++-- ...sCesium3DTileClassificationIgnoreShow.glsl | 9 + .../Shaders/Builtin/Constants/passOpaque.glsl | 2 +- .../Builtin/Constants/passOverlay.glsl | 2 +- .../Builtin/Constants/passTranslucent.glsl | 2 +- Specs/Scene/ClassificationPrimitiveSpec.js | 81 +++- Specs/Scene/GroundPrimitiveSpec.js | 82 +++- 17 files changed, 1021 insertions(+), 198 deletions(-) create mode 100644 Source/Scene/InvertClassification.js create mode 100644 Source/Shaders/Builtin/Constants/passCesium3DTileClassificationIgnoreShow.glsl diff --git a/Apps/Sandcastle/gallery/Classification.html b/Apps/Sandcastle/gallery/Classification.html index b5ec8301a0bc..842d684a7013 100644 --- a/Apps/Sandcastle/gallery/Classification.html +++ b/Apps/Sandcastle/gallery/Classification.html @@ -31,50 +31,111 @@ var scene = viewer.scene; var camera = scene.camera; -var buildingHighlight; -var treeHighlight1; -var treeHighlight2; -var treeHighlight3; -var treeHighlight4; - -function removePrimitives() { - if (Cesium.defined(buildingHighlight)) { - scene.primitives.remove(buildingHighlight); - } - if (Cesium.defined(treeHighlight1)) { - scene.primitives.remove(treeHighlight1); - scene.primitives.remove(treeHighlight2); - scene.primitives.remove(treeHighlight3); - scene.primitives.remove(treeHighlight4); - } -} +var center = new Cesium.Cartesian3(1216378.730451297, -4736275.917774027, 4081266.871000864); +var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); +var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(2.619728786416368, 0.0, 0.0)); +var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.0, 0.0, -2.0)); +Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); + +var buildingHighlight = scene.primitives.add(new Cesium.ClassificationPrimitive({ + geometryInstances : new Cesium.GeometryInstance({ + geometry : Cesium.BoxGeometry.fromDimensions({ + vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, + dimensions : new Cesium.Cartesian3(8.0, 5.0, 8.0) + }), + modelMatrix : modelMatrix, + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 0.0, 0.0, 0.5)), + show : new Cesium.ShowGeometryInstanceAttribute(true) + }, + id : 'volume' + }), + classificationType : Cesium.ClassificationType.CESIUM_3D_TILE +})); -function highlightBuilding() { - Sandcastle.declare(highlightBuilding); - - removePrimitives(); - - var center = new Cesium.Cartesian3(1216378.730451297, -4736275.917774027, 4081266.871000864); - var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); - var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(2.619728786416368, 0.0, 0.0)); - var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.0, 0.0, -2.0)); - Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); - - buildingHighlight = scene.primitives.add(new Cesium.ClassificationPrimitive({ - geometryInstances : new Cesium.GeometryInstance({ - geometry : Cesium.BoxGeometry.fromDimensions({ - vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, - dimensions : new Cesium.Cartesian3(8.0, 5.0, 8.0) - }), - modelMatrix : modelMatrix, - attributes : { - color : Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 0.0, 0.0, 0.5)) - }, - id : 'volume' +center = new Cesium.Cartesian3(1216398.6054139996, -4736204.533089285, 4081338.6585485404); +modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); +hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.4, 0.0, -2.0)); +Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); + +var treeHighlight1 = scene.primitives.add(new Cesium.ClassificationPrimitive({ + geometryInstances : new Cesium.GeometryInstance({ + geometry : new Cesium.EllipsoidGeometry({ + radii : new Cesium.Cartesian3(3.25, 5.0, 4.0) }), - classificationType : Cesium.ClassificationType.CESIUM_3D_TILE - })); + modelMatrix : modelMatrix, + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#F26419').withAlpha(0.5)), + show : new Cesium.ShowGeometryInstanceAttribute(true) + }, + id : 'volume 1' + }), + classificationType : Cesium.ClassificationType.CESIUM_3D_TILE +})); +center = new Cesium.Cartesian3(1216394.3346955755, -4736207.431365568, 4081336.7768881875); +modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); +hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(-0.25, 0.0, -2.0)); +Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); + +var treeHighlight2 = scene.primitives.add(new Cesium.ClassificationPrimitive({ + geometryInstances : new Cesium.GeometryInstance({ + geometry : new Cesium.EllipsoidGeometry({ + radii : new Cesium.Cartesian3(3.25, 5.0, 4.0) + }), + modelMatrix : modelMatrix, + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#F03A47').withAlpha(0.5)), + show : new Cesium.ShowGeometryInstanceAttribute(true) + }, + id : 'volume 2' + }), + classificationType : Cesium.ClassificationType.CESIUM_3D_TILE +})); + +center = new Cesium.Cartesian3(1216388.1664430483, -4736210.034324032, 4081332.9324705894); +modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); +var translation = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0.0, 0.0, -2.0)); +Cesium.Matrix4.multiply(modelMatrix, translation, modelMatrix); + +var treeHighlight3 = scene.primitives.add(new Cesium.ClassificationPrimitive({ + geometryInstances : new Cesium.GeometryInstance({ + geometry : new Cesium.EllipsoidGeometry({ + radii : new Cesium.Cartesian3(2.45, 2.45, 3.0) + }), + modelMatrix : modelMatrix, + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#004FFF').withAlpha(0.5)), + show : new Cesium.ShowGeometryInstanceAttribute(true) + }, + id : 'volume 3' + }), + classificationType : Cesium.ClassificationType.CESIUM_3D_TILE +})); + +center = new Cesium.Cartesian3(1216383.1478702603, -4736211.716097012, 4081329.551077661); +modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); +translation = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0.0, 0.0, -1.0)); +Cesium.Matrix4.multiply(modelMatrix, translation, modelMatrix); + +var treeHighlight4 = scene.primitives.add(new Cesium.ClassificationPrimitive({ + geometryInstances : new Cesium.GeometryInstance({ + geometry : new Cesium.SphereGeometry({ + radius : 2.0 + }), + modelMatrix : modelMatrix, + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#55DDE0').withAlpha(0.5)), + show : new Cesium.ShowGeometryInstanceAttribute(true) + }, + id : 'volume 4' + }), + classificationType : Cesium.ClassificationType.CESIUM_3D_TILE +})); + +function highlightBuilding() { camera.setView({ destination : new Cesium.Cartesian3(1216390.8470847877, -4736277.616363206, 4081242.6450737054), orientation : { @@ -85,88 +146,6 @@ } function highlightTrees() { - Sandcastle.declare(highlightTrees); - - removePrimitives(); - - var center = new Cesium.Cartesian3(1216398.6054139996, -4736204.533089285, 4081338.6585485404); - var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); - var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); - var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.4, 0.0, -2.0)); - Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); - - treeHighlight1 = scene.primitives.add(new Cesium.ClassificationPrimitive({ - geometryInstances : new Cesium.GeometryInstance({ - geometry : new Cesium.EllipsoidGeometry({ - radii : new Cesium.Cartesian3(3.25, 5.0, 4.0) - }), - modelMatrix : modelMatrix, - attributes : { - color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#F26419').withAlpha(0.5)) - }, - id : 'volume 1' - }), - classificationType : Cesium.ClassificationType.CESIUM_3D_TILE - })); - - center = new Cesium.Cartesian3(1216394.3346955755, -4736207.431365568, 4081336.7768881875); - modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); - hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); - hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(-0.25, 0.0, -2.0)); - Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); - - treeHighlight2 = scene.primitives.add(new Cesium.ClassificationPrimitive({ - geometryInstances : new Cesium.GeometryInstance({ - geometry : new Cesium.EllipsoidGeometry({ - radii : new Cesium.Cartesian3(3.25, 5.0, 4.0) - }), - modelMatrix : modelMatrix, - attributes : { - color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#F03A47').withAlpha(0.5)) - }, - id : 'volume 2' - }), - classificationType : Cesium.ClassificationType.CESIUM_3D_TILE - })); - - center = new Cesium.Cartesian3(1216388.1664430483, -4736210.034324032, 4081332.9324705894); - modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); - var translation = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0.0, 0.0, -2.0)); - Cesium.Matrix4.multiply(modelMatrix, translation, modelMatrix); - - treeHighlight3 = scene.primitives.add(new Cesium.ClassificationPrimitive({ - geometryInstances : new Cesium.GeometryInstance({ - geometry : new Cesium.EllipsoidGeometry({ - radii : new Cesium.Cartesian3(2.45, 2.45, 3.0) - }), - modelMatrix : modelMatrix, - attributes : { - color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#004FFF').withAlpha(0.5)) - }, - id : 'volume 3' - }), - classificationType : Cesium.ClassificationType.CESIUM_3D_TILE - })); - - center = new Cesium.Cartesian3(1216383.1478702603, -4736211.716097012, 4081329.551077661); - modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); - translation = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0.0, 0.0, -1.0)); - Cesium.Matrix4.multiply(modelMatrix, translation, modelMatrix); - - treeHighlight4 = scene.primitives.add(new Cesium.ClassificationPrimitive({ - geometryInstances : new Cesium.GeometryInstance({ - geometry : new Cesium.SphereGeometry({ - radius : 2.0 - }), - modelMatrix : modelMatrix, - attributes : { - color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('#55DDE0').withAlpha(0.5)) - }, - id : 'volume 4' - }), - classificationType : Cesium.ClassificationType.CESIUM_3D_TILE - })); - camera.setView({ destination : new Cesium.Cartesian3(1216424.420697336, -4736234.517874706, 4081307.8699144847), orientation : { @@ -176,6 +155,17 @@ }); } +function invertClassification(checked) { + viewer.scene.invertClassification = checked; + viewer.scene.invertClassificationColor = new Cesium.Color(0.25, 0.25, 0.25, 1.0); + + buildingHighlight.getGeometryInstanceAttributes('volume').show = Cesium.ShowGeometryInstanceAttribute.toValue(!checked); + treeHighlight1.getGeometryInstanceAttributes('volume 1').show = Cesium.ShowGeometryInstanceAttribute.toValue(!checked); + treeHighlight2.getGeometryInstanceAttributes('volume 2').show = Cesium.ShowGeometryInstanceAttribute.toValue(!checked); + treeHighlight3.getGeometryInstanceAttributes('volume 3').show = Cesium.ShowGeometryInstanceAttribute.toValue(!checked); + treeHighlight4.getGeometryInstanceAttributes('volume 4').show = Cesium.ShowGeometryInstanceAttribute.toValue(!checked); +} + var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url : 'https://beta.cesium.com/api/assets/1458?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxYmJiNTAxOC1lOTg5LTQzN2EtODg1OC0zMWJjM2IxNGNlYmMiLCJpZCI6NDQsImFzc2V0cyI6WzE0NThdLCJpYXQiOjE0OTkyNjM4MjB9.1WKijRa-ILkmG6utrhDWX6rDgasjD7dZv-G5ZyCmkKg' })); @@ -183,6 +173,7 @@ tileset.readyPromise.then(function() { Sandcastle.addToolbarButton('Highlight building face', highlightBuilding); Sandcastle.addToolbarButton('Highlight trees', highlightTrees); + Sandcastle.addToggleButton('Invert classification', false, invertClassification); highlightTrees(); }).otherwise(function(error) { @@ -192,6 +183,8 @@ var currentObjectId; var currentPrimitive; var currentColor; +var currentShow; +var attributes; var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); handler.setInputAction(function(movement) { @@ -202,21 +195,28 @@ } if (Cesium.defined(currentObjectId)) { - currentPrimitive.getGeometryInstanceAttributes(currentObjectId).color = currentColor; + attributes = currentPrimitive.getGeometryInstanceAttributes(currentObjectId); + attributes.color = currentColor; + attributes.show = currentShow; currentObjectId = undefined; currentPrimitive = undefined; currentColor = undefined; + currentShow = undefined; } } if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.primitive) && Cesium.defined(pickedObject.id) && Cesium.defined(pickedObject.primitive.getGeometryInstanceAttributes)) { currentObjectId = pickedObject.id; currentPrimitive = pickedObject.primitive; - var attributes = currentPrimitive.getGeometryInstanceAttributes(currentObjectId); + attributes = currentPrimitive.getGeometryInstanceAttributes(currentObjectId); currentColor = attributes.color; + currentShow = attributes.show; attributes.color = [255, 0, 255, 128]; + attributes.show = [1]; } else if (Cesium.defined(currentObjectId)) { - currentPrimitive.getGeometryInstanceAttributes(currentObjectId).color = currentColor; + attributes = currentPrimitive.getGeometryInstanceAttributes(currentObjectId); + attributes.color = currentColor; + attributes.show = currentShow; currentObjectId = undefined; currentPrimitive = undefined; currentColor = undefined; diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index 89dafdc31d3e..3e218afd105e 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -1588,6 +1588,20 @@ define([ getValue : function(uniformState) { return uniformState.minimumDisableDepthTestDistance; } + }), + + /** + * An automatic GLSL uniform that will be the highlight color of unclassified 3D Tiles. + * + * @alias czm_invertClassificationColor + * @glslUniform + */ + czm_invertClassificationColor : new AutomaticUniform({ + size : 1, + datatype : WebGLConstants.FLOAT_VEC4, + getValue : function(uniformState) { + return uniformState.invertClassificationColor; + } }) }; diff --git a/Source/Renderer/Pass.js b/Source/Renderer/Pass.js index 3b3758834de2..f623fab44eec 100644 --- a/Source/Renderer/Pass.js +++ b/Source/Renderer/Pass.js @@ -23,10 +23,11 @@ define([ TERRAIN_CLASSIFICATION : 3, CESIUM_3D_TILE : 4, CESIUM_3D_TILE_CLASSIFICATION : 5, - OPAQUE : 6, - TRANSLUCENT : 7, - OVERLAY : 8, - NUMBER_OF_PASSES : 9 + CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW : 6, + OPAQUE : 7, + TRANSLUCENT : 8, + OVERLAY : 9, + NUMBER_OF_PASSES : 10 }; return freezeObject(Pass); diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 0b2850daae4a..99698d308755 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -159,6 +159,8 @@ define([ this._fogDensity = undefined; + this._invertClassificationColor = undefined; + this._imagerySplitPosition = 0.0; this._pixelSizePerMeter = undefined; this._geometricToleranceOverMeter = undefined; @@ -847,6 +849,18 @@ define([ get : function() { return this._minimumDisableDepthTestDistance; } + }, + + /** + * The highlight color of unclassified 3D Tiles. + * + * @memberof UniformState.prototype + * @type {Number} + */ + invertClassificationColor : { + get : function() { + return this._invertClassificationColor; + } } }); @@ -1012,6 +1026,8 @@ define([ this._fogDensity = frameState.fog.density; + this._invertClassificationColor = frameState.invertClassificationColor; + this._frameState = frameState; this._temeToPseudoFixed = Transforms.computeTemeToPseudoFixedMatrix(frameState.time, this._temeToPseudoFixed); diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index cca0bd85e6f7..c31894683961 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -1495,7 +1495,7 @@ define([ var addedCommandsLength = (lengthAfterUpdate - lengthBeforeUpdate); var backfaceCommandsLength = backfaceCommands.length; - commandList.length += backfaceCommands.length; + commandList.length += backfaceCommandsLength; // copy commands to the back of the commandList for (i = addedCommandsLength - 1; i >= 0; --i) { diff --git a/Source/Scene/ClassificationPrimitive.js b/Source/Scene/ClassificationPrimitive.js index 49da56ec4403..beb724aa572f 100644 --- a/Source/Scene/ClassificationPrimitive.js +++ b/Source/Scene/ClassificationPrimitive.js @@ -164,6 +164,7 @@ define([ this._uniformMap = options._uniformMap; this._sp = undefined; + this._spStencil = undefined; this._spPick = undefined; this._rsStencilPreloadPass = undefined; @@ -171,6 +172,8 @@ define([ this._rsColorPass = undefined; this._rsPickPass = undefined; + this._commandsIgnoreShow = []; + this._ready = false; this._readyPromise = when.defer(); @@ -344,6 +347,9 @@ define([ return scene.context.stencilBuffer; }; + var stencilReference = 0; + var stencilMask = 0x0F; + function getStencilPreloadRenderState(enableStencil) { return { colorMask : { @@ -366,8 +372,8 @@ define([ zFail : StencilOperation.INCREMENT_WRAP, zPass : StencilOperation.INCREMENT_WRAP }, - reference : 0, - mask : ~0 + reference : stencilReference, + mask : stencilMask }, depthTest : { enabled : false @@ -398,8 +404,8 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : 0, - mask : ~0 + reference : stencilReference, + mask : stencilMask }, depthTest : { enabled : true, @@ -426,8 +432,8 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : 0, - mask : ~0 + reference : stencilReference, + mask : stencilMask }, depthTest : { enabled : false @@ -452,8 +458,8 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : 0, - mask : ~0 + reference : stencilReference, + mask : stencilMask }, depthTest : { enabled : false @@ -510,7 +516,6 @@ define([ var primitive = classificationPrimitive._primitive; var vs = ShadowVolumeVS; vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(vs); - vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs); vs = Primitive._modifyShaderPosition(classificationPrimitive, vs, frameState.scene3DOnly); vs = Primitive._updateColorAttribute(primitive, vs); @@ -530,9 +535,9 @@ define([ }); var attributeLocations = classificationPrimitive._primitive._attributeLocations; - classificationPrimitive._sp = ShaderProgram.replaceCache({ + classificationPrimitive._spStencil = ShaderProgram.replaceCache({ context : context, - shaderProgram : classificationPrimitive._sp, + shaderProgram : classificationPrimitive._spStencil, vertexShaderSource : vsSource, fragmentShaderSource : fsSource, attributeLocations : attributeLocations @@ -567,6 +572,20 @@ define([ attributeLocations : attributeLocations }); } + + vs = Primitive._appendShowToShader(primitive, vs); + vsSource = new ShaderSource({ + defines : [extrudedDefine], + sources : [vs] + }); + + classificationPrimitive._sp = ShaderProgram.replaceCache({ + context : context, + shaderProgram : classificationPrimitive._sp, + vertexShaderSource : vsSource, + fragmentShaderSource : fsSource, + attributeLocations : attributeLocations + }); } function createColorCommands(classificationPrimitive, colorCommands) { @@ -632,6 +651,24 @@ define([ command = colorCommands[length + i] = DrawCommand.shallowClone(colorCommands[i], colorCommands[length + i]); command.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; } + + var commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow; + var spStencil = classificationPrimitive._spStencil; + + var commandIndex = 0; + length = commandsIgnoreShow.length = length / 3 * 2; + + for (var j = 0; j < length; j += 2) { + var commandIgnoreShow = commandsIgnoreShow[j] = DrawCommand.shallowClone(colorCommands[commandIndex], commandsIgnoreShow[j]); + commandIgnoreShow.shaderProgram = spStencil; + commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; + + commandIgnoreShow = commandsIgnoreShow[j + 1] = DrawCommand.shallowClone(colorCommands[commandIndex + 1], commandsIgnoreShow[j + 1]); + commandIgnoreShow.shaderProgram = spStencil; + commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; + + commandIndex += 3; + } } function createPickCommands(classificationPrimitive, pickCommands) { @@ -665,7 +702,7 @@ define([ command.offset = offset; command.count = count; command.renderState = classificationPrimitive._rsStencilPreloadPass; - command.shaderProgram = classificationPrimitive._sp; + command.shaderProgram = classificationPrimitive._spStencil; command.uniformMap = uniformMap; command.pass = Pass.TERRAIN_CLASSIFICATION; @@ -682,7 +719,7 @@ define([ command.offset = offset; command.count = count; command.renderState = classificationPrimitive._rsStencilDepthPass; - command.shaderProgram = classificationPrimitive._sp; + command.shaderProgram = classificationPrimitive._spStencil; command.uniformMap = uniformMap; command.pass = Pass.TERRAIN_CLASSIFICATION; @@ -761,19 +798,21 @@ define([ var commandList = frameState.commandList; var passes = frameState.passes; + var i; var indices; var startIndex; var endIndex; var classificationType = classificationPrimitive.classificationType; if (passes.render) { + var colorCommand; var colorLength = colorCommands.length; indices = getCommandIndices(classificationType, colorLength); startIndex = indices.start; endIndex = indices.end; - for (var i = startIndex; i < endIndex; ++i) { - var colorCommand = colorCommands[i]; + for (i = startIndex; i < endIndex; ++i) { + colorCommand = colorCommands[i]; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; colorCommand.cull = cull; @@ -781,6 +820,23 @@ define([ commandList.push(colorCommand); } + + if (frameState.invertClassification) { + var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; + startIndex = 0; + endIndex = ignoreShowCommands.length; + + for (i = startIndex; i < endIndex; ++i) { + var bvIndex = Math.floor(i / 2); + colorCommand = ignoreShowCommands[i]; + colorCommand.modelMatrix = modelMatrix; + colorCommand.boundingVolume = boundingVolumes[bvIndex]; + colorCommand.cull = cull; + colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; + + commandList.push(colorCommand); + } + } } if (passes.pick) { @@ -790,9 +846,9 @@ define([ endIndex = indices.end; var pickOffsets = primitive._pickOffsets; - for (var j = startIndex; j < endIndex; ++j) { - var pickOffset = pickOffsets[boundingVolumeIndex(j, pickLength)]; - var pickCommand = pickCommands[j]; + for (i = startIndex; i < endIndex; ++i) { + var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)]; + var pickCommand = pickCommands[i]; pickCommand.modelMatrix = modelMatrix; pickCommand.boundingVolume = boundingVolumes[pickOffset.index]; pickCommand.cull = cull; diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index 89e9d1343059..afb1a1d11bfd 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -296,6 +296,20 @@ define([ * @type {Number} */ this.minimumDisableDepthTestDistance = undefined; + + /** + * When false, 3D Tiles will render normally. When true, classified 3D Tile geometry will render opaque and + * unclassified 3D Tile geometry will render translucent with the alpha set to {@link FrameState#invertClassificationAlpha}. + * @type {Boolean} + * @default false + */ + this.invertClassification = false; + + /** + * The highlight color of unclassified 3D Tile geometry when {@link FrameState#invertClassification} is true. + * @type {Color} + */ + this.invertClassificationColor = undefined; } /** diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index c024c6212fb3..75f1f7abee81 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -614,8 +614,11 @@ define([ startIndex = indices.start; endIndex = indices.end; - for (var i = startIndex; i < endIndex; ++i) { - var colorCommand = colorCommands[i]; + var i; + var colorCommand; + + for (i = startIndex; i < endIndex; ++i) { + colorCommand = colorCommands[i]; colorCommand.owner = groundPrimitive; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; @@ -624,6 +627,23 @@ define([ commandList.push(colorCommand); } + + if (frameState.invertClassification) { + var ignoreShowCommands = groundPrimitive._primitive._commandsIgnoreShow; + startIndex = 0; + endIndex = ignoreShowCommands.length; + + for (i = startIndex; i < endIndex; ++i) { + var bvIndex = Math.floor(i / 2); + colorCommand = ignoreShowCommands[i]; + colorCommand.modelMatrix = modelMatrix; + colorCommand.boundingVolume = boundingVolumes[bvIndex]; + colorCommand.cull = cull; + colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; + + commandList.push(colorCommand); + } + } } if (passes.pick) { diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js new file mode 100644 index 000000000000..53974d2d8a54 --- /dev/null +++ b/Source/Scene/InvertClassification.js @@ -0,0 +1,369 @@ +define([ + '../Core/Color', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/PixelFormat', + '../Renderer/ClearCommand', + '../Renderer/Framebuffer', + '../Renderer/PixelDatatype', + '../Renderer/RenderState', + '../Renderer/Sampler', + '../Renderer/ShaderSource', + '../Renderer/Texture', + '../Renderer/TextureMagnificationFilter', + '../Renderer/TextureMinificationFilter', + '../Renderer/TextureWrap', + '../Shaders/PostProcessFilters/PassThrough', + './BlendingState', + './StencilFunction', + './StencilOperation' + ], function( + Color, + defined, + defineProperties, + destroyObject, + PixelFormat, + ClearCommand, + Framebuffer, + PixelDatatype, + RenderState, + Sampler, + ShaderSource, + Texture, + TextureMagnificationFilter, + TextureMinificationFilter, + TextureWrap, + PassThrough, + BlendingState, + StencilFunction, + StencilOperation) { + 'use strict'; + + /** + * @private + */ + function InvertClassification() { + this.previousFramebuffer = undefined; + this._previousFramebuffer = undefined; + + this._texture = undefined; + this._classifiedTexture = undefined; + this._depthStencilTexture = undefined; + this._fbo = undefined; + this._fboClassified = undefined; + + this._rsUnclassified = undefined; + this._rsClassified = undefined; + + this._unclassifiedCommand = undefined; + this._classifiedCommand = undefined; + this._translucentCommand = undefined; + + this._clearColorCommand = new ClearCommand({ + color : new Color(0.0, 0.0, 0.0, 0.0), + owner : this + }); + this._clearCommand = new ClearCommand({ + color : new Color(0.0, 0.0, 0.0, 0.0), + depth : 1.0, + stencil : 0 + }); + + var that = this; + this._uniformMap = { + u_texture : function() { + return that._texture; + }, + u_depth : function() { + return that._depthStencilTexture; + }, + u_classified : function() { + return that._classifiedTexture; + } + }; + } + + defineProperties(InvertClassification.prototype, { + unclassifiedCommand : { + get : function() { + return this._unclassifiedCommand; + } + } + }); + + InvertClassification.isTranslucencySupported = function(context) { + return context.depthTexture && context.fragmentDepth; + }; + + var stencilReference = 0; + var stencilMask = 0x0F; + + var rsUnclassified = { + depthMask : false, + stencilTest : { + enabled : true, + frontFunction : StencilFunction.EQUAL, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.KEEP + }, + backFunction : StencilFunction.NEVER, + reference : stencilReference, + mask : stencilMask + }, + blending : BlendingState.ALPHA_BLEND + }; + + var rsClassified = { + depthMask : false, + stencilTest : { + enabled : true, + frontFunction : StencilFunction.NOT_EQUAL, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.KEEP + }, + backFunction : StencilFunction.NEVER, + reference : stencilReference, + mask : stencilMask + }, + blending : BlendingState.ALPHA_BLEND + }; + + var rsDefault = { + depthMask : true, + depthTest : { + enabled : true + }, + blending : BlendingState.ALPHA_BLEND + }; + + var translucentFS = + '#extension GL_EXT_frag_depth : enable\n'+ + 'uniform sampler2D u_texture;\n' + + 'uniform sampler2D u_depth;\n' + + 'uniform sampler2D u_classified;\n' + + 'varying vec2 v_textureCoordinates;\n' + + 'void main()\n' + + '{\n' + + ' vec4 color = texture2D(u_texture, v_textureCoordinates);\n' + + ' if (color.a == 0.0)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + ' bool isClassified = all(equal(texture2D(u_classified, v_textureCoordinates), vec4(0.0)));\n' + + '#ifdef UNCLASSIFIED\n' + + ' vec4 highlightColor = czm_invertClassificationColor;\n' + + ' if (isClassified)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + '#else\n' + + ' vec4 highlightColor = vec4(1.0);\n' + + ' if (!isClassified)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + '#endif\n' + + ' gl_FragColor = color * highlightColor;\n' + + ' gl_FragDepthEXT = texture2D(u_depth, v_textureCoordinates).r;\n' + + '}\n'; + + var opaqueFS = + 'uniform sampler2D u_texture;\n' + + 'varying vec2 v_textureCoordinates;\n' + + 'void main()\n' + + '{\n' + + ' vec4 color = texture2D(u_texture, v_textureCoordinates);\n' + + ' if (color.a == 0.0)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + '#ifdef UNCLASSIFIED\n' + + ' gl_FragColor = color * czm_invertClassificationColor;\n' + + '#else\n' + + ' gl_FragColor = color;\n' + + '#endif\n' + + '}\n'; + + InvertClassification.prototype.update = function(context) { + var previousFramebufferChanged = this.previousFramebuffer !== this._previousFramebuffer; + this._previousFramebuffer = this.previousFramebuffer; + + var width = context.drawingBufferWidth; + var height = context.drawingBufferHeight; + + var texture = this._texture; + var textureChanged = !defined(texture) || texture.width !== width || texture.height !== height; + if (textureChanged || previousFramebufferChanged) { + this._texture = this._texture && this._texture.destroy(); + this._classifiedTexture = this._classifiedTexture && this._classifiedTexture.destroy(); + this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy(); + + this._texture = new Texture({ + context : context, + width : width, + height : height, + pixelFormat : PixelFormat.RGBA, + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + sampler : new Sampler({ + wrapS : TextureWrap.CLAMP_TO_EDGE, + wrapT : TextureWrap.CLAMP_TO_EDGE, + minificationFilter : TextureMinificationFilter.LINEAR, + magnificationFilter : TextureMagnificationFilter.LINEAR + }) + }); + + if (previousFramebufferChanged && !defined(this._previousFramebuffer)) { + this._classifiedTexture = new Texture({ + context : context, + width : width, + height : height, + pixelFormat : PixelFormat.RGBA, + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + sampler : new Sampler({ + wrapS : TextureWrap.CLAMP_TO_EDGE, + wrapT : TextureWrap.CLAMP_TO_EDGE, + minificationFilter : TextureMinificationFilter.LINEAR, + magnificationFilter : TextureMagnificationFilter.LINEAR + }) + }); + this._depthStencilTexture = new Texture({ + context : context, + width : width, + height : height, + pixelFormat : PixelFormat.DEPTH_STENCIL, + pixelDatatype : PixelDatatype.UNSIGNED_INT_24_8 + }); + } + } + + if (!defined(this._fbo) || textureChanged || previousFramebufferChanged) { + this._fbo = this._fbo && this._fbo.destroy(); + this._fboClassified = this._fboClassified && this._fboClassified.destroy(); + + var depthStencilTexture; + var depthStencilRenderbuffer; + if (defined(this._previousFramebuffer)) { + depthStencilTexture = this._previousFramebuffer.depthStencilTexture; + depthStencilRenderbuffer = this._previousFramebuffer.depthStencilRenderbuffer; + } else { + depthStencilTexture = this._depthStencilTexture; + } + + this._fbo = new Framebuffer({ + context : context, + colorTextures : [this._texture], + depthStencilTexture : depthStencilTexture, + depthStencilRenderbuffer : depthStencilRenderbuffer, + destroyAttachments : false + }); + + if (!defined(this._previousFramebuffer)) { + this._fboClassified = new Framebuffer({ + context : context, + colorTextures : [this._classifiedTexture], + depthStencilTexture : depthStencilTexture, + destroyAttachments : false + }); + } + } + + if (!defined(this._rsUnclassified)) { + this._rsUnclassified = RenderState.fromCache(rsUnclassified); + this._rsClassified = RenderState.fromCache(rsClassified); + this._rsDefault = RenderState.fromCache(rsDefault); + } + + if (!defined(this._unclassifiedCommand) || previousFramebufferChanged) { + if (defined(this._unclassifiedCommand)) { + this._unclassifiedCommand.shaderProgram = this._unclassifiedCommand.shaderProgram && this._unclassifiedCommand.shaderProgram.destroy(); + this._classifiedCommand.shaderProgram = this._classifiedCommand.shaderProgram && this._classifiedCommand.shaderProgram.destroy(); + } + + var fs = defined(this._previousFramebuffer) ? opaqueFS : translucentFS; + var unclassifiedFSSource = new ShaderSource({ + defines : ['UNCLASSIFIED'], + sources : [fs] + }); + var classifiedFSSource = new ShaderSource({ + sources : [fs] + }); + this._unclassifiedCommand = context.createViewportQuadCommand(unclassifiedFSSource, { + renderState : defined(this._previousFramebuffer) ? this._rsUnclassified : this._rsDefault, + uniformMap : this._uniformMap, + owner : this + }); + this._classifiedCommand = context.createViewportQuadCommand(classifiedFSSource, { + renderState : defined(this._previousFramebuffer) ? this._rsClassified : this._rsDefault, + uniformMap : this._uniformMap, + owner : this + }); + + if (defined(this._translucentCommand)) { + this._translucentCommand.shaderProgram = this._translucentCommand.shaderProgram && this._translucentCommand.shaderProgram.destroy(); + } + if (!defined(this._previousFramebuffer)) { + this._translucentCommand = context.createViewportQuadCommand(PassThrough, { + renderState : this._rsUnclassified, + uniformMap : this._uniformMap, + owner : this + }); + } + } + }; + + InvertClassification.prototype.clear = function(context, passState) { + var framebuffer = passState.framebuffer; + + if (defined(this._previousFramebuffer)) { + passState.framebuffer = this._fbo; + this._clearColorCommand.execute(context, passState); + } else { + passState.framebuffer = this._fbo; + this._clearCommand.execute(context, passState); + passState.framebuffer = this._fboClassified; + this._clearCommand.execute(context, passState); + } + + passState.framebuffer = framebuffer; + }; + + InvertClassification.prototype.executeClassified = function(context, passState) { + if (!defined(this._previousFramebuffer)) { + var framebuffer = passState.framebuffer; + + passState.framebuffer = this._fboClassified; + this._translucentCommand.execute(context, passState); + + passState.framebuffer = framebuffer; + } + this._classifiedCommand.execute(context, passState); + }; + + InvertClassification.prototype.executeUnclassified = function(context, passState) { + this._unclassifiedCommand.execute(context, passState); + }; + + InvertClassification.prototype.isDestroyed = function() { + return false; + }; + + InvertClassification.prototype.destroy = function() { + this._fbo = this._fbo && this._fbo.destroy(); + this._texture = this._texture && this._texture.destroy(); + this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy(); + + if (defined(this._unclassifiedCommand)) { + this._unclassifiedCommand.shaderProgram = this._unclassifiedCommand.shaderProgram && this._unclassifiedCommand.shaderProgram.destroy(); + this._classifiedCommand.shaderProgram = this._classifiedCommand.shaderProgram && this._classifiedCommand.shaderProgram.destroy(); + } + + return destroyObject(this); + }; + + return InvertClassification; +}); diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index ba47d94211eb..c7a38d68700d 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -441,20 +441,20 @@ define([ // shader compilation errors. fs.sources.splice(0, 0, - (source.indexOf('gl_FragData') !== -1 ? '#extension GL_EXT_draw_buffers : enable \n' : '') + - 'vec4 czm_gl_FragColor;\n' + - 'bool czm_discard = false;\n'); + (source.indexOf('gl_FragData') !== -1 ? '#extension GL_EXT_draw_buffers : enable \n' : '') + + 'vec4 czm_gl_FragColor;\n' + + 'bool czm_discard = false;\n'); fs.sources.push( - 'void main()\n' + - '{\n' + - ' czm_translucent_main();\n' + - ' if (czm_discard)\n' + - ' {\n' + - ' discard;\n' + - ' }\n' + - source + - '}\n'); + 'void main()\n' + + '{\n' + + ' czm_translucent_main();\n' + + ' if (czm_discard)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + source + + '}\n'); shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, { vertexShaderSource : shaderProgram.vertexShaderSource, @@ -533,7 +533,7 @@ define([ return result; }; - function executeTranslucentCommandsSortedMultipass(oit, scene, executeFunction, passState, commands) { + function executeTranslucentCommandsSortedMultipass(oit, scene, executeFunction, passState, commands, invertClassification) { var command; var derivedCommand; var j; @@ -558,6 +558,12 @@ define([ executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } + if (defined(invertClassification)) { + command = invertClassification.unclassifiedCommand; + derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; + executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); + } + passState.framebuffer = oit._alphaFBO; for (j = 0; j < length; ++j) { @@ -566,10 +572,16 @@ define([ executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } + if (defined(invertClassification)) { + command = invertClassification.unclassifiedCommand; + derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.alphaCommand : command.derivedCommands.oit.alphaCommand; + executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); + } + passState.framebuffer = framebuffer; } - function executeTranslucentCommandsSortedMRT(oit, scene, executeFunction, passState, commands) { + function executeTranslucentCommandsSortedMRT(oit, scene, executeFunction, passState, commands, invertClassification) { var context = scene.context; var framebuffer = passState.framebuffer; var length = commands.length; @@ -582,22 +594,31 @@ define([ var debugFramebuffer = oit._opaqueFBO; passState.framebuffer = oit._translucentFBO; + var command; + var derivedCommand; + for (var j = 0; j < length; ++j) { - var command = commands[j]; - var derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; + command = commands[j]; + derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; + executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); + } + + if (defined(invertClassification)) { + command = invertClassification.unclassifiedCommand; + derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } passState.framebuffer = framebuffer; } - OIT.prototype.executeCommands = function(scene, executeFunction, passState, commands) { + OIT.prototype.executeCommands = function(scene, executeFunction, passState, commands, invertClassification) { if (this._translucentMRTSupport) { - executeTranslucentCommandsSortedMRT(this, scene, executeFunction, passState, commands); + executeTranslucentCommandsSortedMRT(this, scene, executeFunction, passState, commands, invertClassification); return; } - executeTranslucentCommandsSortedMultipass(this, scene, executeFunction, passState, commands); + executeTranslucentCommandsSortedMultipass(this, scene, executeFunction, passState, commands, invertClassification); }; OIT.prototype.execute = function(context, passState) { diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 2e649fa00944..9dee6307ad55 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -63,6 +63,7 @@ define([ './FrustumCommands', './FXAA', './GlobeDepth', + './InvertClassification', './JobScheduler', './MapMode2D', './OIT', @@ -143,6 +144,7 @@ define([ FrustumCommands, FXAA, GlobeDepth, + InvertClassification, JobScheduler, MapMode2D, OIT, @@ -631,6 +633,24 @@ define([ enabled : defaultValue(options.shadows, false) }); + /** + * When false, 3D Tiles will render normally. When true, classified 3D Tile geometry will render normally and + * unclassified 3D Tile geometry will render with the color multiplied by {@link Scene#invertClassificationColor}. + * @type {Boolean} + * @default false + */ + this.invertClassification = false; + + /** + * The highlight color of unclassified 3D Tile geometry when {@link Scene#invertClassification} is true. + * @type {Color} + * @default Color.WHITE + */ + this.invertClassificationColor = Color.clone(Color.WHITE); + + this._actualInvertClassificationColor = Color.clone(this._invertClassificationColor); + this._invertClassification = new InvertClassification(); + this._brdfLutGenerator = new BrdfLutGenerator(); this._terrainExaggeration = defaultValue(options.terrainExaggeration, 1.0); @@ -1164,7 +1184,7 @@ define([ } }, - /** + /** * Gets or sets the position of the Imagery splitter within the viewport. Valid values are between 0.0 and 1.0. * @memberof Scene.prototype * @@ -1229,10 +1249,10 @@ define([ Cartesian3.multiplyByScalar(camera0.position, scalar, scratchPosition0); Cartesian3.multiplyByScalar(camera1.position, scalar, scratchPosition1); return Cartesian3.equalsEpsilon(scratchPosition0, scratchPosition1, epsilon) && - Cartesian3.equalsEpsilon(camera0.direction, camera1.direction, epsilon) && - Cartesian3.equalsEpsilon(camera0.up, camera1.up, epsilon) && - Cartesian3.equalsEpsilon(camera0.right, camera1.right, epsilon) && - Matrix4.equalsEpsilon(camera0.transform, camera1.transform, epsilon); + Cartesian3.equalsEpsilon(camera0.direction, camera1.direction, epsilon) && + Cartesian3.equalsEpsilon(camera0.up, camera1.up, epsilon) && + Cartesian3.equalsEpsilon(camera0.right, camera1.right, epsilon) && + Matrix4.equalsEpsilon(camera0.transform, camera1.transform, epsilon); } function updateDerivedCommands(scene, command) { @@ -1315,6 +1335,15 @@ define([ frameState.occluder = getOccluder(scene); frameState.terrainExaggeration = scene._terrainExaggeration; frameState.minimumDisableDepthTestDistance = scene._minimumDisableDepthTestDistance; + frameState.invertClassification = scene.invertClassification; + + scene._actualInvertClassificationColor = Color.clone(scene.invertClassificationColor, scene._actualInvertClassificationColor); + if (!InvertClassification.isTranslucencySupported(scene._context)) { + scene._actualInvertClassificationColor.alpha = 1.0; + } + + frameState.invertClassificationColor = scene._actualInvertClassificationColor; + if (defined(scene.globe)) { frameState.maximumScreenSpaceError = scene.globe.maximumScreenSpaceError; } else { @@ -1767,11 +1796,15 @@ define([ return b.boundingVolume.distanceSquaredTo(position) - a.boundingVolume.distanceSquaredTo(position); } - function executeTranslucentCommandsSorted(scene, executeFunction, passState, commands) { + function executeTranslucentCommandsSorted(scene, executeFunction, passState, commands, invertClassification) { var context = scene.context; mergeSort(commands, translucentCompare, scene._camera.positionWC); + if (defined(invertClassification)) { + executeFunction(invertClassification.unclassifiedCommand, scene, context, passState); + } + var length = commands.length; for (var j = 0; j < length; ++j) { executeFunction(commands[j], scene, context, passState); @@ -1870,8 +1903,8 @@ define([ var executeTranslucentCommands; if (environmentState.useOIT) { if (!defined(scene._executeOITFunction)) { - scene._executeOITFunction = function(scene, executeFunction, passState, commands) { - scene._oit.executeCommands(scene, executeFunction, passState, commands); + scene._executeOITFunction = function(scene, executeFunction, passState, commands, invertClassification) { + scene._oit.executeCommands(scene, executeFunction, passState, commands, invertClassification); }; } executeTranslucentCommands = scene._executeOITFunction; @@ -1919,6 +1952,7 @@ define([ } clearDepth.execute(context, passState); + scene._stencilClearCommand.execute(context, passState); us.updatePass(Pass.GLOBE); var commands = frustumCommands.commands[Pass.GLOBE]; @@ -1947,31 +1981,112 @@ define([ clearDepth.execute(context, passState); } - us.updatePass(Pass.CESIUM_3D_TILE); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); + if (!scene.frameState.invertClassification || picking) { + // Common/fastest path. Draw 3D Tiles and classification normally. + + // Draw 3D Tiles + us.updatePass(Pass.CESIUM_3D_TILE); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + + // Draw classifications. Modifies 3D Tiles color. + us.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + } else { + // When the invert classification color is opaque: + // Main FBO (FBO1): Main_Color + Main_DepthStencil + // Invert classification FBO (FBO2) : Invert_Color + Main_DepthStencil + // + // 1. Clear FBO2 color to vec4(0.0) for each frustum + // 2. Draw 3D Tiles to FBO2 + // 3. Draw classification to FBO2 + // 4. Fullscreen pass to FBO1, draw Invert_Color when: + // * Main_DepthStencil has the stencil bit set > 0 (classified) + // 5. Fullscreen pass to FBO1, draw Invert_Color * czm_invertClassificationColor when: + // * Main_DepthStencil has stencil bit set to 0 (unclassified) and + // * Invert_Color !== vec4(0.0) + // + // When the invert classification color is translucent: + // Main FBO (FBO1): Main_Color + Main_DepthStencil + // Invert classification FBO (FBO2): Invert_Color + Invert_DepthStencil + // IsClassified FBO (FBO3): IsClassified_Color + Invert_DepthStencil + // + // 1. Clear FBO2 and FBO3 color to vec4(0.0), stencil to 0, and depth to 1.0 + // 2. Draw 3D Tiles to FBO2 + // 3. Draw classification to FBO2 + // 4. Fullscreen pass to FBO3, draw any color when + // * Invert_DepthStencil has the stencil bit set > 0 (classified) + // 5. Fullscreen pass to FBO1, draw Invert_Color when: + // * Invert_Color !== vec4(0.0) and + // * IsClassified_Color !== vec4(0.0) + // 6. Fullscreen pass to FBO1, draw Invert_Color * czm_invertClassificationColor when: + // * Invert_Color !== vec4(0.0) and + // * IsClassified_Color === vec4(0.0) + // + // NOTE: Step six when translucent invert color occurs after the TRANSLUCENT pass + // + scene._invertClassification.clear(context, passState); + + var opaqueClassificationFramebuffer = passState.framebuffer; + passState.framebuffer = scene._invertClassification._fbo; + + // Draw normally + us.updatePass(Pass.CESIUM_3D_TILE); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + + // Set stencil + us.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } + + passState.framebuffer = opaqueClassificationFramebuffer; + + // Fullscreen pass to copy classified fragments + scene._invertClassification.executeClassified(context, passState); + if (scene.frameState.invertClassificationColor.alpha === 1.0) { + // Fullscreen pass to copy unclassified fragments when alpha == 1.0 + scene._invertClassification.executeUnclassified(context, passState); + } + + // Clear stencil set by the classification for the next classification pass + if (length > 0 && context.stencilBuffer) { + scene._stencilClearCommand.execute(context, passState); + } + + // Draw style over classification. + us.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); + commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; + length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; + for (j = 0; j < length; ++j) { + executeCommand(commands[j], scene, context, passState); + } } if (length > 0 && context.stencilBuffer) { scene._stencilClearCommand.execute(context, passState); } - us.updatePass(Pass.CESIUM_3D_TILE_CLASSIFICATION); - commands = frustumCommands.commands[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - length = frustumCommands.indices[Pass.CESIUM_3D_TILE_CLASSIFICATION]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } - if (clearGlobeDepth && useDepthPlane) { depthPlane.execute(context, passState); } // Execute commands in order by pass up to the translucent pass. // Translucent geometry needs special handling (sorting/OIT). - var startPass = Pass.CESIUM_3D_TILE_CLASSIFICATION + 1; + var startPass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW + 1; var endPass = Pass.TRANSLUCENT; for (var pass = startPass; pass < endPass; ++pass) { us.updatePass(pass); @@ -1988,10 +2103,17 @@ define([ us.updateFrustum(frustum); } + var invertClassification; + if (!picking && scene.frameState.invertClassification && scene.frameState.invertClassificationColor.alpha < 1.0) { + // Fullscreen pass to copy unclassified fragments when alpha < 0.0. + // Not executed when undefined. + invertClassification = scene._invertClassification; + } + us.updatePass(Pass.TRANSLUCENT); commands = frustumCommands.commands[Pass.TRANSLUCENT]; commands.length = frustumCommands.indices[Pass.TRANSLUCENT]; - executeTranslucentCommands(scene, executeCommand, passState, commands); + executeTranslucentCommands(scene, executeCommand, passState, commands, invertClassification); if (defined(globeDepth) && (environmentState.useGlobeDepthFramebuffer || depthOnly) && scene.useDepthPicking) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. @@ -2527,6 +2649,27 @@ define([ if (defined(passState.framebuffer)) { clear.execute(context, passState); + + if (scene.invertClassification) { + var depthFramebuffer; + if (scene.frameState.invertClassificationColor.alpha === 1.0) { + if (environmentState.useGlobeDepthFramebuffer) { + depthFramebuffer = scene._globeDepth.framebuffer; + } else if (environmentState.useFXAA) { + depthFramebuffer = scene._fxaa.getColorFramebuffer(); + } + } + + scene._invertClassification.previousFramebuffer = depthFramebuffer; + scene._invertClassification.update(context); + scene._invertClassification.clear(context, passState); + + if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { + var command = scene._invertClassification.unclassifiedCommand; + var derivedCommands = command.derivedCommands; + derivedCommands.oit = scene._oit.createDerivedCommands(command, context, derivedCommands.oit); + } + } } } @@ -2860,6 +3003,7 @@ define([ // Update with previous frame's number and time, assuming that render is called before picking. updateFrameState(this, frameState.frameNumber, frameState.time); frameState.cullingVolume = getPickCullingVolume(this, drawingBufferPosition, rectangleWidth, rectangleHeight); + frameState.invertClassification = false; frameState.passes.pick = true; us.update(frameState); @@ -3377,7 +3521,7 @@ define([ return destroyObject(this); }; - /** + /** * Transforms a position in cartesian coordinates to canvas coordinates. This is commonly used to place an * HTML element at the same screen position as an object in the scene. * diff --git a/Source/Shaders/Builtin/Constants/passCesium3DTileClassificationIgnoreShow.glsl b/Source/Shaders/Builtin/Constants/passCesium3DTileClassificationIgnoreShow.glsl new file mode 100644 index 000000000000..fee859e683ae --- /dev/null +++ b/Source/Shaders/Builtin/Constants/passCesium3DTileClassificationIgnoreShow.glsl @@ -0,0 +1,9 @@ +/** + * The automatic GLSL constant for {@link Pass#CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW} + * + * @name czm_passCesium3DTileClassificationIgnoreShow + * @glslConstant + * + * @see czm_pass + */ +const float czm_passCesium3DTileClassificationIgnoreShow = 6.0; diff --git a/Source/Shaders/Builtin/Constants/passOpaque.glsl b/Source/Shaders/Builtin/Constants/passOpaque.glsl index 4e3994f4ff3c..8465b01838d1 100644 --- a/Source/Shaders/Builtin/Constants/passOpaque.glsl +++ b/Source/Shaders/Builtin/Constants/passOpaque.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passOpaque = 6.0; +const float czm_passOpaque = 7.0; diff --git a/Source/Shaders/Builtin/Constants/passOverlay.glsl b/Source/Shaders/Builtin/Constants/passOverlay.glsl index e04091b33848..e104cb08dd74 100644 --- a/Source/Shaders/Builtin/Constants/passOverlay.glsl +++ b/Source/Shaders/Builtin/Constants/passOverlay.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passOverlay = 8.0; +const float czm_passOverlay = 9.0; diff --git a/Source/Shaders/Builtin/Constants/passTranslucent.glsl b/Source/Shaders/Builtin/Constants/passTranslucent.glsl index 2f25f8c07a7d..78cf93eb138f 100644 --- a/Source/Shaders/Builtin/Constants/passTranslucent.glsl +++ b/Source/Shaders/Builtin/Constants/passTranslucent.glsl @@ -6,4 +6,4 @@ * * @see czm_pass */ -const float czm_passTranslucent = 7.0; +const float czm_passTranslucent = 8.0; diff --git a/Specs/Scene/ClassificationPrimitiveSpec.js b/Specs/Scene/ClassificationPrimitiveSpec.js index 0bbbba8058ad..641c8d48eaa6 100644 --- a/Specs/Scene/ClassificationPrimitiveSpec.js +++ b/Specs/Scene/ClassificationPrimitiveSpec.js @@ -17,6 +17,7 @@ defineSuite([ 'Core/ShowGeometryInstanceAttribute', 'Core/Transforms', 'Renderer/Pass', + 'Scene/InvertClassification', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', 'Specs/createScene', @@ -40,6 +41,7 @@ defineSuite([ ShowGeometryInstanceAttribute, Transforms, Pass, + InvertClassification, PerInstanceColorAppearance, Primitive, createScene, @@ -71,6 +73,7 @@ defineSuite([ function MockGlobePrimitive(primitive) { this._primitive = primitive; + this.pass = Pass.GLOBE; } MockGlobePrimitive.prototype.update = function(frameState) { var commandList = frameState.commandList; @@ -79,7 +82,7 @@ defineSuite([ for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = Pass.GLOBE; + command.pass = this.pass; } }; @@ -416,6 +419,82 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColorAttribute.value); }); + it('renders with invert classification and an opaque color', function() { + if (!ClassificationPrimitive.isSupported(scene)) { + return; + } + + scene.invertClassification = true; + scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 1.0); + + depthPrimitive.pass = Pass.CESIUM_3D_TILE; + boxInstance.attributes.show = new ShowGeometryInstanceAttribute(true); + + primitive = new ClassificationPrimitive({ + geometryInstances : boxInstance, + asynchronous : false + }); + + scene.camera.setView({ destination : rectangle }); + + var invertedColor = new Array(4); + invertedColor[0] = Color.floatToByte(Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red); + invertedColor[1] = Color.floatToByte(Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green); + invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue); + invertedColor[3] = 255; + + scene.groundPrimitives.add(depthPrimitive); + expect(scene).toRender(invertedColor); + + scene.groundPrimitives.add(primitive); + expect(scene).toRender(boxColor); + + primitive.getGeometryInstanceAttributes('box').show = [0]; + expect(scene).toRender(depthColor); + + scene.invertClassification = false; + }); + + it('renders with invert classification and a translucent color', function() { + if (!ClassificationPrimitive.isSupported(scene)) { + return; + } + + if (!InvertClassification.isTranslucencySupported(scene.context)) { + return; + } + + scene.invertClassification = true; + scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 0.25); + + depthPrimitive.pass = Pass.CESIUM_3D_TILE; + boxInstance.attributes.show = new ShowGeometryInstanceAttribute(true); + + primitive = new ClassificationPrimitive({ + geometryInstances : boxInstance, + asynchronous : false + }); + + scene.camera.setView({ destination : rectangle }); + + var invertedColor = new Array(4); + invertedColor[0] = Color.floatToByte(Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red * scene.invertClassificationColor.alpha); + invertedColor[1] = Color.floatToByte(Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green * scene.invertClassificationColor.alpha); + invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue * scene.invertClassificationColor.alpha); + invertedColor[3] = 255; + + scene.groundPrimitives.add(depthPrimitive); + expect(scene).toRender(invertedColor); + + scene.groundPrimitives.add(primitive); + expect(scene).toRender(boxColor); + + primitive.getGeometryInstanceAttributes('box').show = [0]; + expect(scene).toRender(depthColor); + + scene.invertClassification = false; + }); + it('renders bounding volume with debugShowBoundingVolume', function() { if (!ClassificationPrimitive.isSupported(scene)) { return; diff --git a/Specs/Scene/GroundPrimitiveSpec.js b/Specs/Scene/GroundPrimitiveSpec.js index f80c0fcdb7fa..d329bf66028b 100644 --- a/Specs/Scene/GroundPrimitiveSpec.js +++ b/Specs/Scene/GroundPrimitiveSpec.js @@ -13,6 +13,7 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/ShowGeometryInstanceAttribute', 'Renderer/Pass', + 'Scene/InvertClassification', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', 'Specs/createScene', @@ -32,6 +33,7 @@ defineSuite([ RectangleGeometry, ShowGeometryInstanceAttribute, Pass, + InvertClassification, PerInstanceColorAppearance, Primitive, createScene, @@ -72,7 +74,9 @@ defineSuite([ function MockGlobePrimitive(primitive) { this._primitive = primitive; + this.pass = Pass.GLOBE; } + MockGlobePrimitive.prototype.update = function(frameState) { var commandList = frameState.commandList; var startLength = commandList.length; @@ -80,7 +84,7 @@ defineSuite([ for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = Pass.GLOBE; + command.pass = this.pass; } }; @@ -387,6 +391,82 @@ defineSuite([ verifyGroundPrimitiveRender(primitive, rectColorAttribute.value); }); + it('renders with invert classification and an opaque color', function() { + if (!GroundPrimitive.isSupported(scene)) { + return; + } + + scene.invertClassification = true; + scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 1.0); + + depthPrimitive.pass = Pass.CESIUM_3D_TILE; + rectangleInstance.attributes.show = new ShowGeometryInstanceAttribute(true); + + primitive = new GroundPrimitive({ + geometryInstances : rectangleInstance, + asynchronous : false + }); + + scene.camera.setView({ destination : rectangle }); + + var invertedColor = new Array(4); + invertedColor[0] = Color.floatToByte(Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red); + invertedColor[1] = Color.floatToByte(Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green); + invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue); + invertedColor[3] = 255; + + scene.groundPrimitives.add(depthPrimitive); + expect(scene).toRender(invertedColor); + + scene.groundPrimitives.add(primitive); + expect(scene).toRender(rectColor); + + primitive.getGeometryInstanceAttributes('rectangle').show = [0]; + expect(scene).toRender(depthColor); + + scene.invertClassification = false; + }); + + it('renders with invert classification and a translucent color', function() { + if (!GroundPrimitive.isSupported(scene)) { + return; + } + + if (!InvertClassification.isTranslucencySupported(scene.context)) { + return; + } + + scene.invertClassification = true; + scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 0.25); + + depthPrimitive.pass = Pass.CESIUM_3D_TILE; + rectangleInstance.attributes.show = new ShowGeometryInstanceAttribute(true); + + primitive = new GroundPrimitive({ + geometryInstances : rectangleInstance, + asynchronous : false + }); + + scene.camera.setView({ destination : rectangle }); + + var invertedColor = new Array(4); + invertedColor[0] = Color.floatToByte(Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red * scene.invertClassificationColor.alpha); + invertedColor[1] = Color.floatToByte(Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green * scene.invertClassificationColor.alpha); + invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue * scene.invertClassificationColor.alpha); + invertedColor[3] = 255; + + scene.groundPrimitives.add(depthPrimitive); + expect(scene).toRender(invertedColor); + + scene.groundPrimitives.add(primitive); + expect(scene).toRender(rectColor); + + primitive.getGeometryInstanceAttributes('rectangle').show = [0]; + expect(scene).toRender(depthColor); + + scene.invertClassification = false; + }); + it('renders bounding volume with debugShowBoundingVolume', function() { if (!GroundPrimitive.isSupported(scene)) { return; From f56364ce31aa85d8b90f74dc04d9c4c5a169aa16 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 18 Sep 2017 15:58:44 -0400 Subject: [PATCH 02/14] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6a04cd71c154..2acc137ca58b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Change Log * Added ability to add an animation to `ModelAnimationCollection` by its index. [#5815](https://github.com/AnalyticalGraphicsInc/cesium/pull/5815) * Fixed a bug in `ModelAnimationCollection` that caused adding an animation by its name to throw an error. [#5815](https://github.com/AnalyticalGraphicsInc/cesium/pull/5815) * Zoom about mouse now maintains camera heading, pitch, and roll [#4639](https://github.com/AnalyticalGraphicsInc/cesium/pull/5603) +* Adds `invertClassification` and `invertClassificationColor` to `Scene`. When `invertClassification` is `true`, any 3D Tiles geometry that is not classified will have its color multiplied by `invertClassificationColor`. [#5836](https://github.com/AnalyticalGraphicsInc/cesium/pull/5836) ### 1.37 - 2017-09-01 From 87088d414e8f3c026f16b8098082702d0a8ae2cc Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 18 Sep 2017 16:32:15 -0400 Subject: [PATCH 03/14] Fix tests. --- Source/Scene/Scene.js | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 9dee6307ad55..3ba4d566dd6d 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -686,7 +686,8 @@ define([ originalFramebuffer : undefined, useGlobeDepthFramebuffer : false, useOIT : false, - useFXAA : false + useFXAA : false, + useInvertClassification : false }; this._useWebVR = false; @@ -1981,7 +1982,7 @@ define([ clearDepth.execute(context, passState); } - if (!scene.frameState.invertClassification || picking) { + if (!environmentState.useInvertClassification || picking) { // Common/fastest path. Draw 3D Tiles and classification normally. // Draw 3D Tiles @@ -2104,7 +2105,7 @@ define([ } var invertClassification; - if (!picking && scene.frameState.invertClassification && scene.frameState.invertClassificationColor.alpha < 1.0) { + if (!picking && environmentState.useInvertClassification && scene.frameState.invertClassificationColor.alpha < 1.0) { // Fullscreen pass to copy unclassified fragments when alpha < 0.0. // Not executed when undefined. invertClassification = scene._invertClassification; @@ -2649,26 +2650,27 @@ define([ if (defined(passState.framebuffer)) { clear.execute(context, passState); + } - if (scene.invertClassification) { - var depthFramebuffer; - if (scene.frameState.invertClassificationColor.alpha === 1.0) { - if (environmentState.useGlobeDepthFramebuffer) { - depthFramebuffer = scene._globeDepth.framebuffer; - } else if (environmentState.useFXAA) { - depthFramebuffer = scene._fxaa.getColorFramebuffer(); - } + var useInvertClassification = environmentState.useInvertClassification = defined(passState.framebuffer) && scene.invertClassification; + if (useInvertClassification) { + var depthFramebuffer; + if (scene.frameState.invertClassificationColor.alpha === 1.0) { + if (environmentState.useGlobeDepthFramebuffer) { + depthFramebuffer = scene._globeDepth.framebuffer; + } else if (environmentState.useFXAA) { + depthFramebuffer = scene._fxaa.getColorFramebuffer(); } + } - scene._invertClassification.previousFramebuffer = depthFramebuffer; - scene._invertClassification.update(context); - scene._invertClassification.clear(context, passState); + scene._invertClassification.previousFramebuffer = depthFramebuffer; + scene._invertClassification.update(context); + scene._invertClassification.clear(context, passState); - if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { - var command = scene._invertClassification.unclassifiedCommand; - var derivedCommands = command.derivedCommands; - derivedCommands.oit = scene._oit.createDerivedCommands(command, context, derivedCommands.oit); - } + if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { + var command = scene._invertClassification.unclassifiedCommand; + var derivedCommands = command.derivedCommands; + derivedCommands.oit = scene._oit.createDerivedCommands(command, context, derivedCommands.oit); } } } From 601e050b0f5901d72b20b91e0a9a90acf51549e9 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 18 Sep 2017 16:36:04 -0400 Subject: [PATCH 04/14] Fix blinking when using skip LODs. Missing update to 3D Tiles stencil test. --- Source/Scene/Cesium3DTileBatchTable.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index f2b10bc06534..b28926c508a5 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1343,7 +1343,8 @@ define([ derivedCommand = DrawCommand.shallowClone(command); var rs = clone(derivedCommand.renderState, true); rs.stencilTest.enabled = true; - rs.stencilTest.reference = reference; + rs.stencilTest.mask = 0xF0; + rs.stencilTest.reference = reference << 4; rs.stencilTest.frontFunction = StencilFunction.GREATER_OR_EQUAL; rs.stencilTest.frontOperation.zPass = StencilOperation.REPLACE; derivedCommand.renderState = RenderState.fromCache(rs); From 24582c387d8445d6b15989c4a8bdc5f65815f476 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 18 Sep 2017 16:41:20 -0400 Subject: [PATCH 05/14] Add comment about 3D Tiles stencil test. --- Source/Scene/Cesium3DTileBatchTable.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index b28926c508a5..531ce6b2b9b2 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1342,6 +1342,9 @@ define([ // selection depth to the stencil buffer to prevent ancestor tiles from drawing on top derivedCommand = DrawCommand.shallowClone(command); var rs = clone(derivedCommand.renderState, true); + // Stencil test is masked to the most significant 4 bits so the reference is shifted. + // This is to prevent clearing the stencil before classification which needs the least significant + // bits for increment/decrement operations. rs.stencilTest.enabled = true; rs.stencilTest.mask = 0xF0; rs.stencilTest.reference = reference << 4; From 3bdf457b9acb9e5bc6c851558b8f5cbcddc7a780 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 28 Sep 2017 15:50:11 -0400 Subject: [PATCH 06/14] Updates from review. --- Source/Renderer/UniformState.js | 2 +- Source/Scene/FrameState.js | 4 ++-- Source/Scene/Scene.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 99698d308755..89c87d7a88b8 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -855,7 +855,7 @@ define([ * The highlight color of unclassified 3D Tiles. * * @memberof UniformState.prototype - * @type {Number} + * @type {Color} */ invertClassificationColor : { get : function() { diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index afb1a1d11bfd..948a75d78637 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -298,8 +298,8 @@ define([ this.minimumDisableDepthTestDistance = undefined; /** - * When false, 3D Tiles will render normally. When true, classified 3D Tile geometry will render opaque and - * unclassified 3D Tile geometry will render translucent with the alpha set to {@link FrameState#invertClassificationAlpha}. + * When false, 3D Tiles will render normally. When true, classified 3D Tile geometry will render normally and + * unclassified 3D Tile geometry will render with the color multiplied with {@link FrameState#invertClassificationColor}. * @type {Boolean} * @default false */ diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 3ba4d566dd6d..c8355fefdc2b 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2106,7 +2106,7 @@ define([ var invertClassification; if (!picking && environmentState.useInvertClassification && scene.frameState.invertClassificationColor.alpha < 1.0) { - // Fullscreen pass to copy unclassified fragments when alpha < 0.0. + // Fullscreen pass to copy unclassified fragments when alpha < 1.0. // Not executed when undefined. invertClassification = scene._invertClassification; } From a89214c1abb0e95e3033701aec35cf44ab5ff381 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 28 Sep 2017 15:58:55 -0400 Subject: [PATCH 07/14] Fix crash. --- Source/Scene/InvertClassification.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index 53974d2d8a54..ed3bae5c2c52 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -190,13 +190,13 @@ define([ '}\n'; InvertClassification.prototype.update = function(context) { - var previousFramebufferChanged = this.previousFramebuffer !== this._previousFramebuffer; + var texture = this._texture; + var previousFramebufferChanged = !defined(texture) || this.previousFramebuffer !== this._previousFramebuffer; this._previousFramebuffer = this.previousFramebuffer; var width = context.drawingBufferWidth; var height = context.drawingBufferHeight; - var texture = this._texture; var textureChanged = !defined(texture) || texture.width !== width || texture.height !== height; if (textureChanged || previousFramebufferChanged) { this._texture = this._texture && this._texture.destroy(); From a05fc1c9d0ff5b33e3e807da935e6e6c2384a756 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 28 Sep 2017 17:00:54 -0400 Subject: [PATCH 08/14] Update doc. --- Source/Scene/Scene.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c8355fefdc2b..1efd809ba65c 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -643,6 +643,7 @@ define([ /** * The highlight color of unclassified 3D Tile geometry when {@link Scene#invertClassification} is true. + *

When the color's alpha is less than 1.0, the unclassified portions of the 3D Tiles will not blend correctly with the classified positions of the 3D Tiles

* @type {Color} * @default Color.WHITE */ From abaed6ef688af790781decdf5ba0bf2284e73034 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 2 Oct 2017 14:43:31 -0400 Subject: [PATCH 09/14] Update Sandcastle example after merge. --- Apps/Sandcastle/gallery/Classification.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/Classification.html b/Apps/Sandcastle/gallery/Classification.html index 842d684a7013..9a7209308373 100644 --- a/Apps/Sandcastle/gallery/Classification.html +++ b/Apps/Sandcastle/gallery/Classification.html @@ -33,7 +33,7 @@ var center = new Cesium.Cartesian3(1216378.730451297, -4736275.917774027, 4081266.871000864); var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(2.619728786416368, 0.0, 0.0)); +var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-2.619728786416368, 0.0, 0.0)); var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.0, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); @@ -55,7 +55,7 @@ center = new Cesium.Cartesian3(1216398.6054139996, -4736204.533089285, 4081338.6585485404); modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-5.785339046755887, 0.0, 0.0)); hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.4, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); @@ -76,7 +76,7 @@ center = new Cesium.Cartesian3(1216394.3346955755, -4736207.431365568, 4081336.7768881875); modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-5.785339046755887, 0.0, 0.0)); hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(-0.25, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); From b9ab3a8bb5279b9142a5c23b7ec145f4a1239312 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 3 Oct 2017 14:18:53 -0400 Subject: [PATCH 10/14] Update Sandcastle example after merge. --- Apps/Sandcastle/gallery/Classification.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/Classification.html b/Apps/Sandcastle/gallery/Classification.html index 9a7209308373..842d684a7013 100644 --- a/Apps/Sandcastle/gallery/Classification.html +++ b/Apps/Sandcastle/gallery/Classification.html @@ -33,7 +33,7 @@ var center = new Cesium.Cartesian3(1216378.730451297, -4736275.917774027, 4081266.871000864); var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-2.619728786416368, 0.0, 0.0)); +var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(2.619728786416368, 0.0, 0.0)); var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.0, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); @@ -55,7 +55,7 @@ center = new Cesium.Cartesian3(1216398.6054139996, -4736204.533089285, 4081338.6585485404); modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-5.785339046755887, 0.0, 0.0)); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(0.4, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); @@ -76,7 +76,7 @@ center = new Cesium.Cartesian3(1216394.3346955755, -4736207.431365568, 4081336.7768881875); modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); -hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(-5.785339046755887, 0.0, 0.0)); +hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(5.785339046755887, 0.0, 0.0)); hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, new Cesium.Cartesian3(-0.25, 0.0, -2.0)); Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix); From 9f4cab0edfca1388ba756dd28032acaf3caf2bef Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 3 Oct 2017 14:21:43 -0400 Subject: [PATCH 11/14] Update CHANGES.md. --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 44a24fe1ac01..ebad7f6d3c69 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ Change Log ========== +### 1.39 - 2017-11-01 + +* Adds `invertClassification` and `invertClassificationColor` to `Scene`. When `invertClassification` is `true`, any 3D Tiles geometry that is not classified by a `ClassificationPrimitive` or `GroundPrimitive` will have its color multiplied by `invertClassificationColor`. [#5836](https://github.com/AnalyticalGraphicsInc/cesium/pull/5836) + ### 1.38 - 2017-10-02 * Breaking changes @@ -19,7 +23,6 @@ Change Log * Fixed removing multiple event listeners within event callbacks. [#5827](https://github.com/AnalyticalGraphicsInc/cesium/issues/5827) * Running `buildApps` now creates a built version of Sandcastle which uses the built version of Cesium for better performance. * Fixed a tileset traversal bug when the `skipLevelOfDetail` optimization is off. [#5869](https://github.com/AnalyticalGraphicsInc/cesium/issues/5869) -* Adds `invertClassification` and `invertClassificationColor` to `Scene`. When `invertClassification` is `true`, any 3D Tiles geometry that is not classified will have its color multiplied by `invertClassificationColor`. [#5836](https://github.com/AnalyticalGraphicsInc/cesium/pull/5836) ### 1.37 - 2017-09-01 From 5f60fa320fb50648cac19c9d2787ecbd784be9f7 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 18 Oct 2017 15:26:29 -0400 Subject: [PATCH 12/14] Updates from review. --- Source/Scene/ClassificationPrimitive.js | 5 ++++- Source/Scene/InvertClassification.js | 5 ++++- Source/Scene/Scene.js | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Scene/ClassificationPrimitive.js b/Source/Scene/ClassificationPrimitive.js index beb724aa572f..7ca451d9bc5c 100644 --- a/Source/Scene/ClassificationPrimitive.js +++ b/Source/Scene/ClassificationPrimitive.js @@ -347,8 +347,11 @@ define([ return scene.context.stencilBuffer; }; - var stencilReference = 0; + // The stencil mask only uses the least significant 4 bits. + // This is so 3D Tiles with the skip LOD optimization, which uses the most significant 4 bits, + // can be classified. var stencilMask = 0x0F; + var stencilReference = 0; function getStencilPreloadRenderState(enableStencil) { return { diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index ed3bae5c2c52..e0cfd4d6776f 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -96,8 +96,11 @@ define([ return context.depthTexture && context.fragmentDepth; }; - var stencilReference = 0; + // The stencil mask only uses the least significant 4 bits. + // This is so 3D Tiles with the skip LOD optimization, which uses the most significant 4 bits, + // can be classified. var stencilMask = 0x0F; + var stencilReference = 0; var rsUnclassified = { depthMask : false, diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 1efd809ba65c..f93620ea04bf 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -643,7 +643,8 @@ define([ /** * The highlight color of unclassified 3D Tile geometry when {@link Scene#invertClassification} is true. - *

When the color's alpha is less than 1.0, the unclassified portions of the 3D Tiles will not blend correctly with the classified positions of the 3D Tiles

+ *

When the color's alpha is less than 1.0, the unclassified portions of the 3D Tiles will not blend correctly with the classified positions of the 3D Tiles.

+ *

Also, when the color's alpha is less than 1.0, the WEBGL_depth_texture and EXT_frag_depth WebGL extensions must be supported.

* @type {Color} * @default Color.WHITE */ From d6175561084c6c47bcc0ff618e19b3ea9c26c543 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 18 Oct 2017 16:18:16 -0400 Subject: [PATCH 13/14] Update Sandcastle example. --- Apps/Sandcastle/gallery/Classification.html | 55 ++++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/Apps/Sandcastle/gallery/Classification.html b/Apps/Sandcastle/gallery/Classification.html index 842d684a7013..6ea05df8bb97 100644 --- a/Apps/Sandcastle/gallery/Classification.html +++ b/Apps/Sandcastle/gallery/Classification.html @@ -19,10 +19,35 @@

Loading...

-
+
+ + + + + + + + + + + + + + + +
invert classification
inverted color alpha + + +
+