diff --git a/Apps/Sandcastle/gallery/Classification Types.html b/Apps/Sandcastle/gallery/Classification Types.html index a874c9a8a687..f818b0c10787 100644 --- a/Apps/Sandcastle/gallery/Classification Types.html +++ b/Apps/Sandcastle/gallery/Classification Types.html @@ -30,7 +30,6 @@ var viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain() }); -viewer.scene.globe.depthTestAgainstTerrain = false; var tileset = new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(6074) }); viewer.scene.primitives.add(tileset); diff --git a/CHANGES.md b/CHANGES.md index f8589d5e9e38..30522b7c7b57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ Change Log ========== +### 1.45 - 2019-02-01 + +##### Fixes :wrench: +* Fixed an issue where classification primitives with the `CESIUM_3D_TILE` classification type would render on terrain. [#6568](https://github.com/AnalyticalGraphicsInc/cesium/issues/6568) +* Fixed an issue where 3D Tiles would show through the globe. [#6867](https://github.com/AnalyticalGraphicsInc/cesium/issues/6867) + ### 1.53 - 2019-01-02 ##### Fixes :wrench: diff --git a/Source/Renderer/Pass.js b/Source/Renderer/Pass.js index 3ab04a936286..f623fab44eec 100644 --- a/Source/Renderer/Pass.js +++ b/Source/Renderer/Pass.js @@ -24,11 +24,10 @@ define([ CESIUM_3D_TILE : 4, CESIUM_3D_TILE_CLASSIFICATION : 5, CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW : 6, - CLASSIFICATION : 7, - OPAQUE : 8, - TRANSLUCENT : 9, - OVERLAY : 10, - NUMBER_OF_PASSES : 11 + OPAQUE : 7, + TRANSLUCENT : 8, + OVERLAY : 9, + NUMBER_OF_PASSES : 10 }; return freezeObject(Pass); diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index de62af8f2ac4..0b8807144ee8 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -31,6 +31,7 @@ define([ './Cesium3DTileColorBlendMode', './CullFace', './getBinaryAccessor', + './StencilConstants', './StencilFunction', './StencilOperation' ], function( @@ -66,6 +67,7 @@ define([ Cesium3DTileColorBlendMode, CullFace, getBinaryAccessor, + StencilConstants, StencilFunction, StencilOperation) { 'use strict'; @@ -1261,30 +1263,42 @@ define([ derivedCommands.originalCommand = deriveCommand(command); command.dirty = false; } + var originalCommand = derivedCommands.originalCommand; - if (styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE) { + if (styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE && command.pass !== Pass.TRANSLUCENT) { if (!defined(derivedCommands.translucent)) { - derivedCommands.translucent = deriveTranslucentCommand(derivedCommands.originalCommand); + derivedCommands.translucent = deriveTranslucentCommand(originalCommand); } } - if (bivariateVisibilityTest) { - if (command.pass !== Pass.TRANSLUCENT && !finalResolution) { - if (!defined(derivedCommands.zback)) { - derivedCommands.zback = deriveZBackfaceCommand(frameState.context, derivedCommands.originalCommand); - } - tileset._backfaceCommands.push(derivedCommands.zback); + if (styleCommandsNeeded !== StyleCommandsNeeded.ALL_TRANSLUCENT && command.pass !== Pass.TRANSLUCENT) { + if (!defined(derivedCommands.opaque)) { + derivedCommands.opaque = deriveOpaqueCommand(originalCommand); } - if (!defined(derivedCommands.stencil) || tile._selectionDepth !== getLastSelectionDepth(derivedCommands.stencil)) { - derivedCommands.stencil = deriveStencilCommand(derivedCommands.originalCommand, tile._selectionDepth); + + if (bivariateVisibilityTest) { + if (!finalResolution) { + if (!defined(derivedCommands.zback)) { + derivedCommands.zback = deriveZBackfaceCommand(frameState.context, originalCommand); + } + tileset._backfaceCommands.push(derivedCommands.zback); + } + if (!defined(derivedCommands.stencil) || (tile._selectionDepth !== getLastSelectionDepth(derivedCommands.stencil))) { + if (command.renderState.depthMask) { + derivedCommands.stencil = deriveStencilCommand(originalCommand, tile._selectionDepth); + } else { + // Ignore if tile does not write depth + derivedCommands.stencil = derivedCommands.opaque; + } + } } } - var opaqueCommand = bivariateVisibilityTest ? derivedCommands.stencil : derivedCommands.originalCommand; + var opaqueCommand = bivariateVisibilityTest ? derivedCommands.stencil : derivedCommands.opaque; var translucentCommand = derivedCommands.translucent; // If the command was originally opaque: - // * If the styling applied to the tile is all opaque, use the original command + // * If the styling applied to the tile is all opaque, use the opaque command // (with one additional uniform needed for the shader). // * If the styling is all translucent, use new (cached) derived commands (front // and back faces) with a translucent render state. @@ -1308,7 +1322,7 @@ define([ // as of now, a style can't change an originally translucent feature to // opaque since the style's alpha is modulated, not a replacement. When // this changes, we need to derive new opaque commands here. - commandList[i] = opaqueCommand; + commandList[i] = originalCommand; } } }; @@ -1348,6 +1362,12 @@ define([ return derivedCommand; } + function deriveOpaqueCommand(command) { + var derivedCommand = DrawCommand.shallowClone(command); + derivedCommand.renderState = getOpaqueRenderState(command.renderState); + return derivedCommand; + } + function getDisableLogDepthFragmentShaderProgram(context, shaderProgram) { var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'zBackfaceLogDepth'); if (!defined(shader)) { @@ -1385,6 +1405,10 @@ define([ factor : 5.0, units : 5.0 }; + // Set the 3D Tiles bit + rs.stencilTest = StencilConstants.setCesium3DTileBit(); + rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK; + derivedCommand.renderState = RenderState.fromCache(rs); derivedCommand.castShadows = false; derivedCommand.receiveShadows = false; @@ -1395,27 +1419,27 @@ define([ } function deriveStencilCommand(command, reference) { - var derivedCommand = command; - if (command.renderState.depthMask) { // ignore if tile does not write depth (ex. translucent) - // Tiles only draw if their selection depth is >= the tile drawn already. They write their - // 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; - rs.stencilTest.frontFunction = StencilFunction.GREATER_OR_EQUAL; - rs.stencilTest.frontOperation.zPass = StencilOperation.REPLACE; - derivedCommand.renderState = RenderState.fromCache(rs); - } + // Tiles only draw if their selection depth is >= the tile drawn already. They write their + // selection depth to the stencil buffer to prevent ancestor tiles from drawing on top + var derivedCommand = DrawCommand.shallowClone(command); + var rs = clone(derivedCommand.renderState, true); + // Stencil test is masked to the most significant 3 bits so the reference is shifted. Writes 0 for the terrain bit + rs.stencilTest.enabled = true; + rs.stencilTest.mask = StencilConstants.SKIP_LOD_MASK; + rs.stencilTest.reference = StencilConstants.CESIUM_3D_TILE_MASK | (reference << StencilConstants.SKIP_LOD_BIT_SHIFT); + rs.stencilTest.frontFunction = StencilFunction.GREATER_OR_EQUAL; + rs.stencilTest.frontOperation.zPass = StencilOperation.REPLACE; + rs.stencilTest.backFunction = StencilFunction.GREATER_OR_EQUAL; + rs.stencilTest.backOperation.zPass = StencilOperation.REPLACE; + rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK | StencilConstants.SKIP_LOD_MASK; + derivedCommand.renderState = RenderState.fromCache(rs); return derivedCommand; } function getLastSelectionDepth(stencilCommand) { - return stencilCommand.renderState.stencilTest.reference >>> 4; + // Isolate the selection depth from the stencil reference. + var reference = stencilCommand.renderState.stencilTest.reference; + return (reference & StencilConstants.SKIP_LOD_MASK) >>> StencilConstants.SKIP_LOD_BIT_SHIFT; } function getTranslucentRenderState(renderState) { @@ -1428,6 +1452,14 @@ define([ return RenderState.fromCache(rs); } + function getOpaqueRenderState(renderState) { + var rs = clone(renderState, true); + rs.stencilTest = StencilConstants.setCesium3DTileBit(); + rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK; + + return RenderState.fromCache(rs); + } + /////////////////////////////////////////////////////////////////////////// function createTexture(batchTable, context, bytes) { diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index c374c278f179..6f8cf41488b0 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -22,6 +22,7 @@ define([ '../Core/Transforms', '../Renderer/ClearCommand', '../Renderer/Pass', + '../Renderer/RenderState', '../ThirdParty/when', './Axis', './Cesium3DTile', @@ -40,6 +41,7 @@ define([ './PointCloudShading', './SceneMode', './ShadowMode', + './StencilConstants', './TileBoundingRegion', './TileBoundingSphere', './TileOrientedBoundingBox' @@ -67,6 +69,7 @@ define([ Transforms, ClearCommand, Pass, + RenderState, when, Axis, Cesium3DTile, @@ -85,6 +88,7 @@ define([ PointCloudShading, SceneMode, ShadowMode, + StencilConstants, TileBoundingRegion, TileBoundingSphere, TileOrientedBoundingBox) { @@ -195,6 +199,7 @@ define([ this._hasMixedContent = false; + this._stencilClearCommand = undefined; this._backfaceCommands = new ManagedArray(); this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16); @@ -1703,11 +1708,6 @@ define([ tileset._tileDebugLabels.update(frameState); } - var stencilClearCommand = new ClearCommand({ - stencil : 0, - pass : Pass.CESIUM_3D_TILE - }); - function updateTiles(tileset, frameState) { tileset._styleEngine.applyStyle(tileset, frameState); @@ -1729,7 +1729,16 @@ define([ tileset._backfaceCommands.length = 0; if (bivariateVisibilityTest) { - commandList.push(stencilClearCommand); + if (!defined(tileset._stencilClearCommand)) { + tileset._stencilClearCommand = new ClearCommand({ + stencil : 0, + pass : Pass.CESIUM_3D_TILE, + renderState : RenderState.fromCache({ + stencilMask : StencilConstants.SKIP_LOD_MASK + }) + }); + } + commandList.push(tileset._stencilClearCommand); } var lengthBeforeUpdate = commandList.length; diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index c206ac8a31b1..7b26cea5462a 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -526,12 +526,8 @@ define([ * the children's z-depth and the ancestor's z-depth. We cannot rely on Z because we want the child to appear on top * of ancestor regardless of true depth. The stencil tests used require children to be drawn first. * - * NOTE: this will no longer work when there is a chain of selected tiles that is longer than the size of the - * stencil buffer (usually 8 bits). In other words, the subset of the tree containing only selected tiles must be - * no deeper than 255. It is very, very unlikely this will cause a problem. - * - * NOTE: when the scene has inverted classification enabled, the stencil buffer will be masked to 4 bits. So, the - * selected tiles must be no deeper than 15. This is still very unlikely. + * NOTE: 3D Tiles uses 3 bits from the stencil buffer meaning this will not work when there is a chain of + * selected tiles that is deeper than 7. This is not very likely. */ function traverseAndSelect(tileset, root, frameState) { var stack = selectionTraversal.stack; diff --git a/Source/Scene/ClassificationPrimitive.js b/Source/Scene/ClassificationPrimitive.js index bc5b8614e469..d2c1ca987238 100644 --- a/Source/Scene/ClassificationPrimitive.js +++ b/Source/Scene/ClassificationPrimitive.js @@ -23,6 +23,7 @@ define([ './Primitive', './SceneMode', './ShadowVolumeAppearance', + './StencilConstants', './StencilFunction', './StencilOperation' ], function( @@ -50,6 +51,7 @@ define([ Primitive, SceneMode, ShadowVolumeAppearance, + StencilConstants, StencilFunction, StencilOperation) { 'use strict'; @@ -180,7 +182,9 @@ define([ this._spColor2D = undefined; // only derived if necessary this._rsStencilPreloadPass = undefined; + this._rsStencilPreloadPass3DTiles = undefined; this._rsStencilDepthPass = undefined; + this._rsStencilDepthPass3DTiles = undefined; this._rsColorPass = undefined; this._rsPickPass = undefined; @@ -379,13 +383,8 @@ define([ return scene.context.stencilBuffer; }; - // 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) { + function getStencilPreloadRenderState(enableStencil, mask3DTiles) { + var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS; return { colorMask : { red : false, @@ -395,21 +394,22 @@ define([ }, stencilTest : { enabled : enableStencil, - frontFunction : StencilFunction.ALWAYS, + frontFunction : stencilFunction, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.DECREMENT_WRAP, zPass : StencilOperation.DECREMENT_WRAP }, - backFunction : StencilFunction.ALWAYS, + backFunction : stencilFunction, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.INCREMENT_WRAP, zPass : StencilOperation.INCREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : StencilConstants.CESIUM_3D_TILE_MASK, + mask : StencilConstants.CESIUM_3D_TILE_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : false }, @@ -417,7 +417,8 @@ define([ }; } - function getStencilDepthRenderState(enableStencil) { + function getStencilDepthRenderState(enableStencil, mask3DTiles) { + var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS; return { colorMask : { red : false, @@ -427,21 +428,22 @@ define([ }, stencilTest : { enabled : enableStencil, - frontFunction : StencilFunction.ALWAYS, + frontFunction : stencilFunction, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.INCREMENT_WRAP }, - backFunction : StencilFunction.ALWAYS, + backFunction : stencilFunction, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : StencilConstants.CESIUM_3D_TILE_MASK, + mask : StencilConstants.CESIUM_3D_TILE_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : true, func : DepthFunction.LESS_OR_EQUAL @@ -466,9 +468,10 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : false }, @@ -492,9 +495,10 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : false }, @@ -507,9 +511,11 @@ define([ } var stencilEnabled = !classificationPrimitive.debugShowShadowVolume; - classificationPrimitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled)); - classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled)); - classificationPrimitive._rsColorPass = RenderState.fromCache(getColorRenderState(stencilEnabled)); + classificationPrimitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled, false)); + classificationPrimitive._rsStencilPreloadPass3DTiles = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled, true)); + classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled, false)); + classificationPrimitive._rsStencilDepthPass3DTiles = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled, true)); + classificationPrimitive._rsColorPass = RenderState.fromCache(getColorRenderState(stencilEnabled, false)); classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState); } @@ -676,6 +682,7 @@ define([ var i; var command; + var derivedCommand; var vaIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap); @@ -684,7 +691,7 @@ define([ for (i = 0; i < length; i += 3) { var vertexArray = primitive._va[vaIndex++]; - // stencil preload command + // Stencil preload command command = colorCommands[i]; if (!defined(command)) { command = colorCommands[i] = new DrawCommand({ @@ -697,8 +704,14 @@ define([ command.renderState = classificationPrimitive._rsStencilPreloadPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; + command.pass = Pass.TERRAIN_CLASSIFICATION; + + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.renderState = classificationPrimitive._rsStencilPreloadPass3DTiles; + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; - // stencil depth command + // Stencil depth command command = colorCommands[i + 1]; if (!defined(command)) { command = colorCommands[i + 1] = new DrawCommand({ @@ -711,8 +724,14 @@ define([ command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; + command.pass = Pass.TERRAIN_CLASSIFICATION; + + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.renderState = classificationPrimitive._rsStencilDepthPass3DTiles; + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; - // color command + // Color command command = colorCommands[i + 2]; if (!defined(command)) { command = colorCommands[i + 2] = new DrawCommand({ @@ -724,6 +743,7 @@ define([ command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsColorPass; command.shaderProgram = classificationPrimitive._spColor; + command.pass = Pass.TERRAIN_CLASSIFICATION; var appearance = classificationPrimitive.appearance; var material = appearance.material; @@ -733,17 +753,22 @@ define([ command.uniformMap = uniformMap; - // derive for 2D if texture coordinates are ever computed + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; + + // Derive for 2D if texture coordinates are ever computed if (needs2DShader) { - var derivedColorCommand = command.derivedCommands.appearance2D; - if (!defined(derivedColorCommand)) { - derivedColorCommand = DrawCommand.shallowClone(command); - command.derivedCommands.appearance2D = derivedColorCommand; - } - derivedColorCommand.vertexArray = vertexArray; - derivedColorCommand.renderState = classificationPrimitive._rsColorPass; - derivedColorCommand.shaderProgram = classificationPrimitive._spColor2D; - derivedColorCommand.uniformMap = uniformMap; + // First derive from the terrain command + var derived2DCommand = DrawCommand.shallowClone(command, command.derivedCommands.appearance2D); + derived2DCommand.shaderProgram = classificationPrimitive._spColor2D; + command.derivedCommands.appearance2D = derived2DCommand; + + // Then derive from the 3D Tiles command + derived2DCommand = DrawCommand.shallowClone(derivedCommand, derivedCommand.derivedCommands.appearance2D); + derived2DCommand.shaderProgram = classificationPrimitive._spColor2D; + derived2DCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + derivedCommand.derivedCommands.appearance2D = derived2DCommand; } } @@ -785,6 +810,7 @@ define([ var j; var command; + var derivedCommand; var vaIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap); @@ -797,7 +823,7 @@ define([ vertexArray = primitive._va[pickOffset.index]; } - // stencil preload command + // Stencil preload command command = pickCommands[j]; if (!defined(command)) { command = pickCommands[j] = new DrawCommand({ @@ -811,12 +837,19 @@ define([ command.renderState = classificationPrimitive._rsStencilPreloadPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; + command.pass = Pass.TERRAIN_CLASSIFICATION; if (usePickOffsets) { command.offset = pickOffset.offset; command.count = pickOffset.count; } - // stencil depth command + // Derive for 3D Tiles classification + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.renderState = classificationPrimitive._rsStencilPreloadPass3DTiles; + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; + + // Stencil depth command command = pickCommands[j + 1]; if (!defined(command)) { command = pickCommands[j + 1] = new DrawCommand({ @@ -830,12 +863,19 @@ define([ command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; + command.pass = Pass.TERRAIN_CLASSIFICATION; if (usePickOffsets) { command.offset = pickOffset.offset; command.count = pickOffset.count; } - // pick color command + // Derive for 3D Tiles classification + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.renderState = classificationPrimitive._rsStencilDepthPass3DTiles; + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; + + // Pick color command command = pickCommands[j + 2]; if (!defined(command)) { command = pickCommands[j + 2] = new DrawCommand({ @@ -849,22 +889,28 @@ define([ command.renderState = classificationPrimitive._rsPickPass; command.shaderProgram = classificationPrimitive._spPick; command.uniformMap = uniformMap; + command.pass = Pass.TERRAIN_CLASSIFICATION; if (usePickOffsets) { command.offset = pickOffset.offset; command.count = pickOffset.count; } - // derive for 2D if texture coordinates are ever computed + derivedCommand = DrawCommand.shallowClone(command, command.derivedCommands.tileset); + derivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + command.derivedCommands.tileset = derivedCommand; + + // Derive for 2D if texture coordinates are ever computed if (needs2DShader) { - var derivedPickCommand = command.derivedCommands.pick2D; - if (!defined(derivedPickCommand)) { - derivedPickCommand = DrawCommand.shallowClone(command); - command.derivedCommands.pick2D = derivedPickCommand; - } - derivedPickCommand.vertexArray = vertexArray; - derivedPickCommand.renderState = classificationPrimitive._rsPickPass; - derivedPickCommand.shaderProgram = classificationPrimitive._spPick2D; - derivedPickCommand.uniformMap = uniformMap; + // First derive from the terrain command + var derived2DCommand = DrawCommand.shallowClone(command, command.derivedCommands.pick2D); + derived2DCommand.shaderProgram = classificationPrimitive._spPick2D; + command.derivedCommands.pick2D = derived2DCommand; + + // Then derive from the 3D Tiles command + derived2DCommand = DrawCommand.shallowClone(derivedCommand, derivedCommand.derivedCommands.pick2D); + derived2DCommand.shaderProgram = classificationPrimitive._spPick2D; + derived2DCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + derivedCommand.derivedCommands.pick2D = derived2DCommand; } } } @@ -878,6 +924,23 @@ define([ return Math.floor((commandIndex % length) / 3); } + function updateAndQueueRenderCommand(command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume) { + command.modelMatrix = modelMatrix; + command.boundingVolume = boundingVolume; + command.cull = cull; + command.debugShowBoundingVolume = debugShowBoundingVolume; + + frameState.commandList.push(command); + } + + function updateAndQueuePickCommand(command, frameState, modelMatrix, cull, boundingVolume) { + command.modelMatrix = modelMatrix; + command.boundingVolume = boundingVolume; + command.cull = cull; + + frameState.commandList.push(command); + } + function updateAndQueueCommands(classificationPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { var primitive = classificationPrimitive._primitive; Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); @@ -893,49 +956,37 @@ define([ boundingVolumes = primitive._boundingSphereMorph; } - var commandList = frameState.commandList; + var classificationType = classificationPrimitive.classificationType; + var queueTerrainCommands = (classificationType !== ClassificationType.CESIUM_3D_TILE); + var queue3DTilesCommands = (classificationType !== ClassificationType.TERRAIN); + var passes = frameState.passes; var i; - var pass; - switch (classificationPrimitive.classificationType) { - case ClassificationType.TERRAIN: - pass = Pass.TERRAIN_CLASSIFICATION; - break; - case ClassificationType.CESIUM_3D_TILE: - pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; - break; - default: - pass = Pass.CLASSIFICATION; - } + var boundingVolume; + var command; if (passes.render) { - var colorCommand; var colorLength = colorCommands.length; for (i = 0; i < colorLength; ++i) { - colorCommand = colorCommands[i]; - colorCommand.modelMatrix = modelMatrix; - colorCommand.boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; - colorCommand.cull = cull; - colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; - colorCommand.pass = pass; - - commandList.push(colorCommand); + boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; + if (queueTerrainCommands) { + command = colorCommands[i]; + updateAndQueueRenderCommand(command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); + } + if (queue3DTilesCommands) { + command = colorCommands[i].derivedCommands.tileset; + updateAndQueueRenderCommand(command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); + } } if (frameState.invertClassification) { var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; var ignoreShowCommandsLength = ignoreShowCommands.length; - for (i = 0; i < ignoreShowCommandsLength; ++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); + boundingVolume = boundingVolumes[Math.floor(i / 2)]; + command = ignoreShowCommands[i]; + updateAndQueueRenderCommand(command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); } } } @@ -945,13 +996,15 @@ define([ var pickOffsets = primitive._pickOffsets; for (i = 0; i < pickLength; ++i) { var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)]; - var pickCommand = pickCommands[i]; - pickCommand.modelMatrix = modelMatrix; - pickCommand.boundingVolume = boundingVolumes[pickOffset.index]; - pickCommand.cull = cull; - pickCommand.pass = pass; - - commandList.push(pickCommand); + boundingVolume = boundingVolumes[pickOffset.index]; + if (queueTerrainCommands) { + command = pickCommands[i]; + updateAndQueuePickCommand(command, frameState, modelMatrix, cull, boundingVolume); + } + if (queue3DTilesCommands) { + command = pickCommands[i].derivedCommands.tileset; + updateAndQueuePickCommand(command, frameState, modelMatrix, cull, boundingVolume); + } } } } @@ -1107,13 +1160,17 @@ define([ if (this.debugShowShadowVolume && !this._debugShowShadowVolume && this._ready) { this._debugShowShadowVolume = true; - this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false)); - this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false)); + this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false, false)); + this._rsStencilPreloadPass3DTiles = RenderState.fromCache(getStencilPreloadRenderState(false, true)); + this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false, false)); + this._rsStencilDepthPass3DTiles = RenderState.fromCache(getStencilDepthRenderState(false, true)); this._rsColorPass = RenderState.fromCache(getColorRenderState(false)); } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) { this._debugShowShadowVolume = false; - this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(true)); - this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(true)); + this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(true, false)); + this._rsStencilPreloadPass3DTiles = RenderState.fromCache(getStencilPreloadRenderState(true, true)); + this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(true, false)); + this._rsStencilDepthPass3DTiles = RenderState.fromCache(getStencilDepthRenderState(true, true)); this._rsColorPass = RenderState.fromCache(getColorRenderState(true)); } // Update primitive appearance diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 0117fdf3b1ad..29b0411c7d85 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -35,13 +35,13 @@ define([ '../Renderer/Pass', '../Renderer/RenderState', '../Renderer/VertexArray', - '../Scene/BlendingState', - '../Scene/DepthFunction', - '../Scene/PerInstanceColorAppearance', - '../Scene/Primitive', + './BlendingState', './ClippingPlaneCollection', + './DepthFunction', './GlobeSurfaceTile', './ImageryLayer', + './PerInstanceColorAppearance', + './Primitive', './QuadtreeTileLoadState', './SceneMode', './ShadowMode' @@ -83,12 +83,12 @@ define([ RenderState, VertexArray, BlendingState, - DepthFunction, - PerInstanceColorAppearance, - Primitive, ClippingPlaneCollection, + DepthFunction, GlobeSurfaceTile, ImageryLayer, + PerInstanceColorAppearance, + Primitive, QuadtreeTileLoadState, SceneMode, ShadowMode) { diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index 2df59183af5a..3efbaac2bd55 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -514,6 +514,41 @@ define([ return Math.floor((commandIndex % length) / 3); } + function updateAndQueueRenderCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume) { + // Use derived appearance command for 2D if needed + var classificationPrimitive = groundPrimitive._primitive; + if (frameState.mode !== SceneMode.SCENE3D && + command.shaderProgram === classificationPrimitive._spColor && + classificationPrimitive._needs2DShader) { + command = command.derivedCommands.appearance2D; + } + + command.owner = groundPrimitive; + command.modelMatrix = modelMatrix; + command.boundingVolume = boundingVolume; + command.cull = cull; + command.debugShowBoundingVolume = debugShowBoundingVolume; + + frameState.commandList.push(command); + } + + function updateAndQueuePickCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume) { + // Use derived pick command for 2D if needed + var classificationPrimitive = groundPrimitive._primitive; + if (frameState.mode !== SceneMode.SCENE3D && + command.shaderProgram === classificationPrimitive._spPick && + classificationPrimitive._needs2DShader) { + command = command.derivedCommands.pick2D; + } + + command.owner = groundPrimitive; + command.modelMatrix = modelMatrix; + command.boundingVolume = boundingVolume; + command.cull = cull; + + frameState.commandList.push(command); + } + function updateAndQueueCommands(groundPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { var boundingVolumes; if (frameState.mode === SceneMode.SCENE3D) { @@ -522,59 +557,39 @@ define([ boundingVolumes = groundPrimitive._boundingVolumes2D; } - var pass; - switch (groundPrimitive.classificationType) { - case ClassificationType.TERRAIN: - pass = Pass.TERRAIN_CLASSIFICATION; - break; - case ClassificationType.CESIUM_3D_TILE: - pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; - break; - default: - pass = Pass.CLASSIFICATION; - } + var classificationType = groundPrimitive.classificationType; + var queueTerrainCommands = (classificationType !== ClassificationType.CESIUM_3D_TILE); + var queue3DTilesCommands = (classificationType !== ClassificationType.TERRAIN); - var commandList = frameState.commandList; var passes = frameState.passes; var classificationPrimitive = groundPrimitive._primitive; + + var i; + var boundingVolume; + var command; + if (passes.render) { var colorLength = colorCommands.length; - var i; - var colorCommand; for (i = 0; i < colorLength; ++i) { - colorCommand = colorCommands[i]; - - // Use derived appearance command for 2D if needed - if (frameState.mode !== SceneMode.SCENE3D && - colorCommand.shaderProgram === classificationPrimitive._spColor && - classificationPrimitive._needs2DShader) { - colorCommand = colorCommand.derivedCommands.appearance2D; + boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; + if (queueTerrainCommands) { + command = colorCommands[i]; + updateAndQueueRenderCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); + } + if (queue3DTilesCommands) { + command = colorCommands[i].derivedCommands.tileset; + updateAndQueueRenderCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); } - - colorCommand.owner = groundPrimitive; - colorCommand.modelMatrix = modelMatrix; - colorCommand.boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; - colorCommand.cull = cull; - colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; - colorCommand.pass = pass; - - commandList.push(colorCommand); } if (frameState.invertClassification) { var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; var ignoreShowCommandsLength = ignoreShowCommands.length; - for (i = 0; i < ignoreShowCommandsLength; ++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); + boundingVolume = boundingVolumes[Math.floor(i / 2)]; + command = ignoreShowCommands[i]; + updateAndQueueRenderCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume); } } } @@ -585,31 +600,22 @@ define([ var pickOffsets; if (!groundPrimitive._useFragmentCulling) { // Must be using pick offsets - classificationPrimitive = groundPrimitive._primitive; pickOffsets = classificationPrimitive._primitive._pickOffsets; } - for (var j = 0; j < pickLength; ++j) { - var pickCommand = pickCommands[j]; - - // Use derived pick command for 2D if needed - if (frameState.mode !== SceneMode.SCENE3D && - pickCommand.shaderProgram === classificationPrimitive._spPick && - classificationPrimitive._needs2DShader) { - pickCommand = pickCommand.derivedCommands.pick2D; - } - var bv = boundingVolumes[boundingVolumeIndex(j, pickLength)]; + for (i = 0; i < pickLength; ++i) { + boundingVolume = boundingVolumes[boundingVolumeIndex(i, pickLength)]; if (!groundPrimitive._useFragmentCulling) { - var pickOffset = pickOffsets[boundingVolumeIndex(j, pickLength)]; - bv = boundingVolumes[pickOffset.index]; + var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)]; + boundingVolume = boundingVolumes[pickOffset.index]; + } + if (queueTerrainCommands) { + command = pickCommands[i]; + updateAndQueuePickCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume); + } + if (queue3DTilesCommands) { + command = pickCommands[i].derivedCommands.tileset; + updateAndQueuePickCommand(groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume); } - - pickCommand.owner = groundPrimitive; - pickCommand.modelMatrix = modelMatrix; - pickCommand.boundingVolume = bv; - pickCommand.cull = cull; - pickCommand.pass = pass; - - commandList.push(pickCommand); } } } diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index b7b7f66889c9..25b4dff62510 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -16,6 +16,7 @@ define([ '../Renderer/TextureWrap', '../Shaders/PostProcessStages/PassThrough', './BlendingState', + './StencilConstants', './StencilFunction', './StencilOperation' ], function( @@ -36,6 +37,7 @@ define([ TextureWrap, PassThrough, BlendingState, + StencilConstants, StencilFunction, StencilOperation) { 'use strict'; @@ -96,12 +98,6 @@ define([ return context.depthTexture && context.fragmentDepth; }; - // 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, stencilTest : { @@ -113,8 +109,8 @@ define([ zPass : StencilOperation.KEEP }, backFunction : StencilFunction.NEVER, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, blending : BlendingState.ALPHA_BLEND }; @@ -130,17 +126,22 @@ define([ zPass : StencilOperation.KEEP }, backFunction : StencilFunction.NEVER, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, blending : BlendingState.ALPHA_BLEND }; + // Set the 3D Tiles bit when rendering back into the scene's framebuffer. This is only needed if + // invert classification does not use the scene's depth-stencil texture, which is the case if the invert + // classification color is translucent. var rsDefault = { depthMask : true, depthTest : { enabled : true }, + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, blending : BlendingState.ALPHA_BLEND }; diff --git a/Source/Scene/PointCloud.js b/Source/Scene/PointCloud.js index 252764a23f4e..c297f51138d3 100644 --- a/Source/Scene/PointCloud.js +++ b/Source/Scene/PointCloud.js @@ -38,7 +38,8 @@ define([ './getClipAndStyleCode', './getClippingFunction', './SceneMode', - './ShadowMode' + './ShadowMode', + './StencilConstants' ], function( arraySlice, BoundingSphere, @@ -79,7 +80,8 @@ define([ getClipAndStyleCode, getClippingFunction, SceneMode, - ShadowMode) { + ShadowMode, + StencilConstants) { 'use strict'; // Bail out if the browser doesn't support typed arrays, to prevent the setup function @@ -737,11 +739,18 @@ define([ attributes : attributes }); - pointCloud._opaqueRenderState = RenderState.fromCache({ + var opaqueRenderState = { depthTest : { enabled : true } - }); + }; + + if (pointCloud._opaquePass === Pass.CESIUM_3D_TILE) { + opaqueRenderState.stencilTest = StencilConstants.setCesium3DTileBit(); + opaqueRenderState.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK; + } + + pointCloud._opaqueRenderState = RenderState.fromCache(opaqueRenderState); pointCloud._translucentRenderState = RenderState.fromCache({ depthTest : { diff --git a/Source/Scene/PointCloudEyeDomeLighting.js b/Source/Scene/PointCloudEyeDomeLighting.js index 16d5daa5d46b..e43c7230eda6 100644 --- a/Source/Scene/PointCloudEyeDomeLighting.js +++ b/Source/Scene/PointCloudEyeDomeLighting.js @@ -19,6 +19,7 @@ define([ '../Renderer/TextureMinificationFilter', '../Renderer/TextureWrap', '../Scene/BlendingState', + '../Scene/StencilConstants', '../Shaders/PostProcessStages/PointCloudEyeDomeLighting' ], function( Cartesian3, @@ -41,6 +42,7 @@ define([ TextureMinificationFilter, TextureWrap, BlendingState, + StencilConstants, PointCloudEyeDomeLightingShader) { 'use strict'; @@ -160,7 +162,9 @@ define([ depthMask : true, depthTest : { enabled : true - } + }, + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK }); processor._drawCommand = context.createViewportQuadCommand(blendFS, { diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 4741c2d2ca3a..92823beab81b 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -46,6 +46,7 @@ define([ '../Renderer/Framebuffer', '../Renderer/Pass', '../Renderer/PixelDatatype', + '../Renderer/RenderState', '../Renderer/ShaderProgram', '../Renderer/ShaderSource', '../Renderer/Texture', @@ -76,6 +77,7 @@ define([ './SceneTransitioner', './ScreenSpaceCameraController', './ShadowMap', + './StencilConstants', './SunPostProcess', './TweenCollection', './View' @@ -127,6 +129,7 @@ define([ Framebuffer, Pass, PixelDatatype, + RenderState, ShaderProgram, ShaderSource, Texture, @@ -157,6 +160,7 @@ define([ SceneTransitioner, ScreenSpaceCameraController, ShadowMap, + StencilConstants, SunPostProcess, TweenCollection, View) { @@ -322,6 +326,12 @@ define([ this._stencilClearCommand = new ClearCommand({ stencil : 0 }); + this._classificationStencilClearCommand = new ClearCommand({ + stencil : 0, + renderState : RenderState.fromCache({ + stencilMask : StencilConstants.CLASSIFICATION_MASK + }) + }); this._depthOnlyRenderStateCache = {}; this._pickRenderStateCache = {}; @@ -2146,6 +2156,7 @@ define([ var useDepthPlane = environmentState.useDepthPlane; var clearDepth = scene._depthClearCommand; var clearStencil = scene._stencilClearCommand; + var clearClassificationStencil = scene._classificationStencilClearCommand; var depthPlane = scene._depthPlane; var usePostProcessSelected = environmentState.usePostProcessSelected; @@ -2214,16 +2225,11 @@ define([ executeCommand(commands[j], scene, context, passState); } - // Draw classification marked for both terrain and 3D Tiles classification - us.updatePass(Pass.CLASSIFICATION); - commands = frustumCommands.commands[Pass.CLASSIFICATION]; - length = frustumCommands.indices[Pass.CLASSIFICATION]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } - if (clearGlobeDepth) { clearDepth.execute(context, passState); + if (useDepthPlane) { + depthPlane.execute(context, passState); + } } if (!environmentState.useInvertClassification || picking) { @@ -2244,14 +2250,6 @@ define([ for (j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } - - // Draw classification marked for both terrain and 3D Tiles classification - us.updatePass(Pass.CLASSIFICATION); - commands = frustumCommands.commands[Pass.CLASSIFICATION]; - length = frustumCommands.indices[Pass.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 @@ -2317,7 +2315,7 @@ define([ // Clear stencil set by the classification for the next classification pass if (length > 0 && context.stencilBuffer) { - clearStencil.execute(context, passState); + clearClassificationStencil.execute(context, passState); } // Draw style over classification. @@ -2327,24 +2325,12 @@ define([ for (j = 0; j < length; ++j) { executeCommand(commands[j], scene, context, passState); } - - // Draw style over classification marked for both terrain and 3D Tiles classification - us.updatePass(Pass.CLASSIFICATION); - commands = frustumCommands.commands[Pass.CLASSIFICATION]; - length = frustumCommands.indices[Pass.CLASSIFICATION]; - for (j = 0; j < length; ++j) { - executeCommand(commands[j], scene, context, passState); - } } if (length > 0 && context.stencilBuffer) { clearStencil.execute(context, passState); } - if (clearGlobeDepth && useDepthPlane) { - depthPlane.execute(context, passState); - } - us.updatePass(Pass.OPAQUE); commands = frustumCommands.commands[Pass.OPAQUE]; length = frustumCommands.indices[Pass.OPAQUE]; diff --git a/Source/Scene/StencilConstants.js b/Source/Scene/StencilConstants.js new file mode 100644 index 000000000000..9ad789e88091 --- /dev/null +++ b/Source/Scene/StencilConstants.js @@ -0,0 +1,46 @@ +define([ + '../Core/freezeObject', + './StencilFunction', + './StencilOperation' + ], function( + freezeObject, + StencilFunction, + StencilOperation) { + 'use strict'; + + /** + * The most significant bit is used to identify whether the pixel is 3D Tiles. + * The next three bits store selection depth for the skip LODs optimization. + * The last four bits are for increment/decrement shadow volume operations for classification. + * + * @private + */ + var StencilConstants = { + CESIUM_3D_TILE_MASK : 0x80, + SKIP_LOD_MASK : 0x70, + SKIP_LOD_BIT_SHIFT : 4, + CLASSIFICATION_MASK : 0x0F + }; + + StencilConstants.setCesium3DTileBit = function() { + return { + enabled : true, + frontFunction : StencilFunction.ALWAYS, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.REPLACE + }, + backFunction : StencilFunction.ALWAYS, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.REPLACE + }, + reference : StencilConstants.CESIUM_3D_TILE_MASK, + mask : StencilConstants.CESIUM_3D_TILE_MASK + }; + }; + + return freezeObject(StencilConstants); +}); diff --git a/Source/Scene/Vector3DTileContent.js b/Source/Scene/Vector3DTileContent.js index 4db34f4c8e2f..131e04f606a1 100644 --- a/Source/Scene/Vector3DTileContent.js +++ b/Source/Scene/Vector3DTileContent.js @@ -511,6 +511,7 @@ define([ var ready = true; if (defined(this._polygons)) { this._polygons.classificationType = this._tileset.classificationType; + this._polygons.debugWireframe = this._tileset.debugWireframe; this._polygons.update(frameState); ready = ready && this._polygons._ready; } diff --git a/Source/Scene/Vector3DTileGeometry.js b/Source/Scene/Vector3DTileGeometry.js index bb1e7e2e7084..8569dc92ce95 100644 --- a/Source/Scene/Vector3DTileGeometry.js +++ b/Source/Scene/Vector3DTileGeometry.js @@ -114,9 +114,9 @@ define([ /** * What this tile will classify. * @type {ClassificationType} - * @default ClassificationType.CESIUM_3D_TILE + * @default ClassificationType.BOTH */ - this.classificationType = ClassificationType.CESIUM_3D_TILE; + this.classificationType = ClassificationType.BOTH; } defineProperties(Vector3DTileGeometry.prototype, { diff --git a/Source/Scene/Vector3DTilePolygons.js b/Source/Scene/Vector3DTilePolygons.js index dd1dce43fba2..a74b8988cd67 100644 --- a/Source/Scene/Vector3DTilePolygons.js +++ b/Source/Scene/Vector3DTilePolygons.js @@ -120,9 +120,9 @@ define([ /** * What this tile will classify. * @type {ClassificationType} - * @default ClassificationType.CESIUM_3D_TILE + * @default ClassificationType.BOTH */ - this.classificationType = ClassificationType.CESIUM_3D_TILE; + this.classificationType = ClassificationType.BOTH; } defineProperties(Vector3DTilePolygons.prototype, { diff --git a/Source/Scene/Vector3DTilePrimitive.js b/Source/Scene/Vector3DTilePrimitive.js index 41459814c3a5..d0f147849e64 100644 --- a/Source/Scene/Vector3DTilePrimitive.js +++ b/Source/Scene/Vector3DTilePrimitive.js @@ -24,6 +24,7 @@ define([ './ClassificationType', './DepthFunction', './Expression', + './StencilConstants', './StencilFunction', './StencilOperation', './Vector3DTileBatch' @@ -53,6 +54,7 @@ define([ ClassificationType, DepthFunction, Expression, + StencilConstants, StencilFunction, StencilOperation, Vector3DTileBatch) { @@ -113,7 +115,9 @@ define([ this._vaSwap = undefined; this._rsStencilPreloadPass = undefined; + this._rsStencilPreloadPass3DTiles = undefined; this._rsStencilDepthPass = undefined; + this._rsStencilDepthPass3DTiles = undefined; this._rsColorPass = undefined; this._rsPickPass = undefined; this._rsWireframe = undefined; @@ -365,69 +369,74 @@ define([ }); } - var stencilReference = 0; - var stencilMask = 0x0F; - - var stencilPreloadRenderState = { - colorMask : { - red : false, - green : false, - blue : false, - alpha : false - }, - stencilTest : { - enabled : true, - frontFunction : StencilFunction.ALWAYS, - frontOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.DECREMENT_WRAP, - zPass : StencilOperation.DECREMENT_WRAP + function getStencilPreloadRenderState(mask3DTiles) { + var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS; + return { + colorMask : { + red : false, + green : false, + blue : false, + alpha : false }, - backFunction : StencilFunction.ALWAYS, - backOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.INCREMENT_WRAP, - zPass : StencilOperation.INCREMENT_WRAP + stencilTest : { + enabled : true, + frontFunction : stencilFunction, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.DECREMENT_WRAP, + zPass : StencilOperation.DECREMENT_WRAP + }, + backFunction : stencilFunction, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.INCREMENT_WRAP, + zPass : StencilOperation.INCREMENT_WRAP + }, + reference : StencilConstants.CESIUM_3D_TILE_MASK, + mask : StencilConstants.CESIUM_3D_TILE_MASK }, - reference : stencilReference, - mask : stencilMask - }, - depthTest : { - enabled : false - }, - depthMask : false - }; + stencilMask : StencilConstants.CLASSIFICATION_MASK, + depthTest : { + enabled : false + }, + depthMask : false + }; + } - var stencilDepthRenderState = { - colorMask : { - red : false, - green : false, - blue : false, - alpha : false - }, - stencilTest : { - enabled : true, - frontFunction : StencilFunction.ALWAYS, - frontOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.INCREMENT_WRAP + function getStencilDepthRenderState(mask3DTiles) { + var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS; + return { + colorMask : { + red : false, + green : false, + blue : false, + alpha : false }, - backFunction : StencilFunction.ALWAYS, - backOperation : { - fail : StencilOperation.KEEP, - zFail : StencilOperation.KEEP, - zPass : StencilOperation.DECREMENT_WRAP + stencilTest : { + enabled : true, + frontFunction : stencilFunction, + frontOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.INCREMENT_WRAP + }, + backFunction : stencilFunction, + backOperation : { + fail : StencilOperation.KEEP, + zFail : StencilOperation.KEEP, + zPass : StencilOperation.DECREMENT_WRAP + }, + reference : StencilConstants.CESIUM_3D_TILE_MASK, + mask : StencilConstants.CESIUM_3D_TILE_MASK }, - reference : stencilReference, - mask : stencilMask - }, - depthTest : { - enabled : true, - func : DepthFunction.LESS_OR_EQUAL - }, - depthMask : false - }; + stencilMask : StencilConstants.CLASSIFICATION_MASK, + depthTest : { + enabled : true, + func : DepthFunction.LESS_OR_EQUAL + }, + depthMask : false + }; + } var colorRenderState = { stencilTest : { @@ -444,9 +453,10 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : false }, @@ -469,9 +479,10 @@ define([ zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, - reference : stencilReference, - mask : stencilMask + reference : 0, + mask : StencilConstants.CLASSIFICATION_MASK }, + stencilMask : StencilConstants.CLASSIFICATION_MASK, depthTest : { enabled : false }, @@ -483,8 +494,10 @@ define([ return; } - primitive._rsStencilPreloadPass = RenderState.fromCache(stencilPreloadRenderState); - primitive._rsStencilDepthPass = RenderState.fromCache(stencilDepthRenderState); + primitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false)); + primitive._rsStencilPreloadPass3DTiles = RenderState.fromCache(getStencilPreloadRenderState(true)); + primitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false)); + primitive._rsStencilDepthPass3DTiles = RenderState.fromCache(getStencilDepthRenderState(true)); primitive._rsColorPass = RenderState.fromCache(colorRenderState); primitive._rsPickPass = RenderState.fromCache(pickRenderState); } @@ -727,6 +740,12 @@ define([ stencilPreloadCommand.uniformMap = uniformMap; stencilPreloadCommand.boundingVolume = bv; stencilPreloadCommand.cull = false; + stencilPreloadCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var stencilPreloadDerivedCommand = DrawCommand.shallowClone(stencilPreloadCommand, stencilPreloadCommand.derivedCommands.tileset); + stencilPreloadDerivedCommand.renderState = primitive._rsStencilPreloadPass3DTiles; + stencilPreloadDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + stencilPreloadCommand.derivedCommands.tileset = stencilPreloadDerivedCommand; var stencilDepthCommand = commands[j * 3 + 1]; if (!defined(stencilDepthCommand)) { @@ -744,6 +763,12 @@ define([ stencilDepthCommand.uniformMap = uniformMap; stencilDepthCommand.boundingVolume = bv; stencilDepthCommand.cull = false; + stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var stencilDepthDerivedCommand = DrawCommand.shallowClone(stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset); + stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles; + stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand; var colorCommand = commands[j * 3 + 2]; if (!defined(colorCommand)) { @@ -761,6 +786,11 @@ define([ colorCommand.uniformMap = uniformMap; colorCommand.boundingVolume = bv; colorCommand.cull = false; + colorCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var colorDerivedCommand = DrawCommand.shallowClone(colorCommand, colorCommand.derivedCommands.tileset); + colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + colorCommand.derivedCommands.tileset = colorDerivedCommand; } primitive._commandsDirty = true; @@ -832,6 +862,12 @@ define([ stencilPreloadCommand.shaderProgram = spStencil; stencilPreloadCommand.uniformMap = uniformMap; stencilPreloadCommand.boundingVolume = bv; + stencilPreloadCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var stencilPreloadDerivedCommand = DrawCommand.shallowClone(stencilPreloadCommand, stencilPreloadCommand.derivedCommands.tileset); + stencilPreloadDerivedCommand.renderState = primitive._rsStencilPreloadPass3DTiles; + stencilPreloadDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + stencilPreloadCommand.derivedCommands.tileset = stencilPreloadDerivedCommand; var stencilDepthCommand = pickCommands[j * 3 + 1]; if (!defined(stencilDepthCommand)) { @@ -849,6 +885,12 @@ define([ stencilDepthCommand.shaderProgram = spStencil; stencilDepthCommand.uniformMap = uniformMap; stencilDepthCommand.boundingVolume = bv; + stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var stencilDepthDerivedCommand = DrawCommand.shallowClone(stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset); + stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles; + stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand; var colorCommand = pickCommands[j * 3 + 2]; if (!defined(colorCommand)) { @@ -866,6 +908,11 @@ define([ colorCommand.shaderProgram = spPick; colorCommand.uniformMap = uniformMap; colorCommand.boundingVolume = bv; + colorCommand.pass = Pass.TERRAIN_CLASSIFICATION; + + var colorDerivedCommand = DrawCommand.shallowClone(colorCommand, colorCommand.derivedCommands.tileset); + colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + colorCommand.derivedCommands.tileset = colorDerivedCommand; } primitive._pickCommandsDirty = false; @@ -1053,14 +1100,26 @@ define([ this._batchDirty = true; }; - function queueCommands(frameState, pass, commands, commandsIgnoreShow) { + function queueCommands(primitive, frameState, commands, commandsIgnoreShow) { + var classificationType = primitive.classificationType; + var queueTerrainCommands = (classificationType !== ClassificationType.CESIUM_3D_TILE); + var queue3DTilesCommands = (classificationType !== ClassificationType.TERRAIN); + var commandList = frameState.commandList; var commandLength = commands.length; + var command; var i; for (i = 0; i < commandLength; ++i) { - var command = commands[i]; - command.pass = pass; - commandList.push(command); + if (queueTerrainCommands) { + command = commands[i]; + command.pass = Pass.TERRAIN_CLASSIFICATION; + commandList.push(command); + } + if (queue3DTilesCommands) { + command = commands[i].derivedCommands.tileset; + command.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; + commandList.push(command); + } } if (!frameState.invertClassification || !defined(commandsIgnoreShow)) { @@ -1130,18 +1189,6 @@ define([ createRenderStates(this); createUniformMap(this, context); - var pass; - switch (this.classificationType) { - case ClassificationType.TERRAIN: - pass = Pass.TERRAIN_CLASSIFICATION; - break; - case ClassificationType.CESIUM_3D_TILE: - pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; - break; - default: - pass = Pass.CLASSIFICATION; - } - var passes = frameState.passes; if (passes.render) { createColorCommands(this, context); @@ -1151,13 +1198,13 @@ define([ if (this._debugWireframe) { queueWireframeCommands(frameState, this._commands); } else { - queueCommands(frameState, pass, this._commands, this._commandsIgnoreShow); + queueCommands(this, frameState, this._commands, this._commandsIgnoreShow); } } if (passes.pick) { createPickCommands(this); - queueCommands(frameState, pass, this._pickCommands); + queueCommands(this, frameState, this._pickCommands); } }; diff --git a/Source/Shaders/Builtin/Constants/passOpaque.glsl b/Source/Shaders/Builtin/Constants/passOpaque.glsl index 8dfb5126f13a..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 = 8.0; +const float czm_passOpaque = 7.0; diff --git a/Source/Shaders/Builtin/Constants/passOverlay.glsl b/Source/Shaders/Builtin/Constants/passOverlay.glsl index 6aea11eb1ee0..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 = 10.0; +const float czm_passOverlay = 9.0; diff --git a/Source/Shaders/Builtin/Constants/passTranslucent.glsl b/Source/Shaders/Builtin/Constants/passTranslucent.glsl index 1fd11409cc8b..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 = 9.0; +const float czm_passTranslucent = 8.0; diff --git a/Specs/Scene/Batched3DModel3DTileContentClassificationSpec.js b/Specs/Scene/Batched3DModel3DTileContentClassificationSpec.js index a9ebbd728136..eceb751f6df5 100644 --- a/Specs/Scene/Batched3DModel3DTileContentClassificationSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentClassificationSpec.js @@ -13,10 +13,12 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/Batched3DModel3DTileContent', 'Scene/ClassificationType', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/Cesium3DTilesTester', 'Specs/createScene' ], 'Scene/Batched3DModel3DTileContentClassification', function( @@ -34,112 +36,193 @@ defineSuite([ RectangleGeometry, Transforms, Pass, + RenderState, Batched3DModel3DTileContent, ClassificationType, PerInstanceColorAppearance, Primitive, + StencilConstants, Cesium3DTilesTester, createScene) { 'use strict'; var scene; + var modelMatrix; var centerLongitude = -1.31968; var centerLatitude = 0.698874; var withBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json'; var withBatchTableBinaryUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/tileset.json'; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + function setCamera(longitude, latitude) { // One feature is located at the center, point the camera there var center = Cartesian3.fromRadians(longitude, latitude); scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 15.0)); } - beforeAll(function() { - scene = createScene(); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 0.0, 1.0)); + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { - setCamera(centerLongitude, centerLatitude); + beforeAll(function() { + scene = createScene(); + + var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); + Cartesian3.multiplyByScalar(translation, -5.0, translation); + modelMatrix = Matrix4.fromTranslation(translation); var offset = CesiumMath.toRadians(0.01); var rectangle = new Rectangle(centerLongitude - offset, centerLatitude - offset, centerLongitude + offset, centerLatitude + offset); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : Ellipsoid.WGS84, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); + + beforeEach(function() { + setCamera(centerLongitude, centerLatitude); + + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - scene.primitives.add(new MockGlobePrimitive(primitive)); + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); }); afterEach(function() { scene.primitives.removeAll(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); }); - it('renders with batch table', function() { - var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); - Cartesian3.multiplyByScalar(translation, -5.0, translation); - + it('classifies 3D Tiles', function() { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl, { classificationType : ClassificationType.CESIUM_3D_TILE, - modelMatrix : Matrix4.fromTranslation(translation) + modelMatrix : modelMatrix }).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; Cesium3DTilesTester.expectRenderTileset(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = false; + Cesium3DTilesTester.expectRenderBlank(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = true; }); }); - it('renders with binary batch table', function() { - var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); - Cartesian3.multiplyByScalar(translation, -5.0, translation); + it('classifies globe', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl, { + classificationType : ClassificationType.TERRAIN, + modelMatrix : modelMatrix + }).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + Cesium3DTilesTester.expectRenderBlank(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = false; + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + + it('classifies both 3D Tiles and globe', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl, { + classificationType : ClassificationType.BOTH, + modelMatrix : modelMatrix + }).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = false; + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + it('renders with batch table', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl, { + classificationType : ClassificationType.BOTH, + modelMatrix : modelMatrix + }).then(function(tileset) { + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + }); + }); + + it('renders with binary batch table', function() { return Cesium3DTilesTester.loadTileset(scene, withBatchTableBinaryUrl, { - classificationType : ClassificationType.CESIUM_3D_TILE, - modelMatrix : Matrix4.fromTranslation(translation) + classificationType : ClassificationType.BOTH, + modelMatrix : modelMatrix }).then(function(tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); diff --git a/Specs/Scene/ClassificationModelSpec.js b/Specs/Scene/ClassificationModelSpec.js index f45d4d3fae8e..45fc1f661e3d 100644 --- a/Specs/Scene/ClassificationModelSpec.js +++ b/Specs/Scene/ClassificationModelSpec.js @@ -15,9 +15,11 @@ defineSuite([ 'Core/Resource', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/ClassificationType', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/createScene', 'Specs/pollToPromise', 'ThirdParty/GltfPipeline/addDefaults', @@ -40,9 +42,11 @@ defineSuite([ Resource, Transforms, Pass, + RenderState, ClassificationType, PerInstanceColorAppearance, Primitive, + StencilConstants, createScene, pollToPromise, addDefaults, @@ -51,103 +55,153 @@ defineSuite([ 'use strict'; var scene; + var modelMatrix; var centerLongitude = -1.31968; var centerLatitude = 0.698874; var batchedModel = './Data/Models/Classification/batched.glb'; var quantizedModel = './Data/Models/Classification/batchedQuantization.glb'; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + function setCamera(longitude, latitude) { // One feature is located at the center, point the camera there var center = Cartesian3.fromRadians(longitude, latitude); scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 15.0)); } - function loadModel(model) { - return Resource.fetchArrayBuffer(model).then(function(arrayBuffer) { - var gltf = new Uint8Array(arrayBuffer); - gltf = parseGlb(gltf); - updateVersion(gltf); - addDefaults(gltf); - return gltf; + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 0.0, 1.0)); + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false }); } - beforeAll(function() { - scene = createScene(); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); - - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { - setCamera(centerLongitude, centerLatitude); + beforeAll(function() { + scene = createScene(); + + var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); + Cartesian3.multiplyByScalar(translation, -5.0, translation); + modelMatrix = Matrix4.fromTranslation(translation); var offset = CesiumMath.toRadians(0.01); var rectangle = new Rectangle(centerLongitude - offset, centerLatitude - offset, centerLongitude + offset, centerLatitude + offset); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : Ellipsoid.WGS84, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - scene.primitives.add(new MockGlobePrimitive(primitive)); + beforeEach(function() { + setCamera(centerLongitude, centerLatitude); + + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); + + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); }); afterEach(function() { scene.primitives.removeAll(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); }); - it('renders batched model', function() { - var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); - Cartesian3.multiplyByScalar(translation, -5.0, translation); + function expectRender(scene, model) { + model.show = false; + expect(scene).toRender([0, 0, 0, 255]); + model.show = true; + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba).not.toEqual([0, 0, 0, 255]); + }); + } + + function expectRenderBlank(scene, model) { + model.show = false; + expect(scene).toRender([0, 0, 0, 255]); + model.show = true; + expect(scene).toRender([0, 0, 0, 255]); + } + + function loadGltf(model) { + return Resource.fetchArrayBuffer(model).then(function(arrayBuffer) { + var gltf = new Uint8Array(arrayBuffer); + gltf = parseGlb(gltf); + updateVersion(gltf); + addDefaults(gltf); + return gltf; + }); + } - return Resource.fetchArrayBuffer(batchedModel).then(function(arrayBuffer) { + function loadClassificationModel(url, classificationType) { + return Resource.fetchArrayBuffer(url).then(function(arrayBuffer) { var model = scene.primitives.add(new ClassificationModel({ gltf : arrayBuffer, - classificationType : ClassificationType.CESIUM_3D_TILE, - modelMatrix : Matrix4.fromTranslation(translation) + classificationType : classificationType, + modelMatrix : modelMatrix })); var ready = false; @@ -159,48 +213,64 @@ defineSuite([ scene.renderForSpecs(); return ready; }).then(function() { - model.show = false; - expect(scene).toRender([0, 0, 0, 255]); - model.show = true; - expect(scene).toRenderAndCall(function(rgba) { - expect(rgba).not.toEqual([0, 0, 0, 255]); - }); + return model; }); }); + } + + it('classifies 3D Tiles', function() { + return loadClassificationModel(batchedModel, ClassificationType.CESIUM_3D_TILE).then(function(model) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRenderBlank(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); }); - it('renders batched model with quantization', function() { - var translation = Ellipsoid.WGS84.geodeticSurfaceNormalCartographic(new Cartographic(centerLongitude, centerLatitude)); - Cartesian3.multiplyByScalar(translation, -5.0, translation); + it('classifies globe', function() { + return loadClassificationModel(batchedModel, ClassificationType.TERRAIN).then(function(model) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRenderBlank(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); - return Resource.fetchArrayBuffer(quantizedModel).then(function(arrayBuffer) { - var model = scene.primitives.add(new ClassificationModel({ - gltf : arrayBuffer, - classificationType : ClassificationType.CESIUM_3D_TILE, - modelMatrix : Matrix4.fromTranslation(translation) - })); + it('classifies both 3D Tiles and globe', function() { + return loadClassificationModel(batchedModel, ClassificationType.BOTH).then(function(model) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(scene, model); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); - var ready = false; - model.readyPromise.then(function() { - ready = true; - }); + it('renders batched model', function() { + return loadClassificationModel(batchedModel, ClassificationType.BOTH).then(function(model) { + expectRender(scene, model); + }); + }); - return pollToPromise(function() { - scene.renderForSpecs(); - return ready; - }).then(function() { - model.show = false; - expect(scene).toRender([0, 0, 0, 255]); - model.show = true; - expect(scene).toRenderAndCall(function(rgba) { - expect(rgba).not.toEqual([0, 0, 0, 255]); - }); - }); + it('renders batched model with quantization', function() { + return loadClassificationModel(quantizedModel, ClassificationType.BOTH).then(function(model) { + expectRender(scene, model); }); }); it('throws with invalid number of nodes', function() { - return loadModel(batchedModel).then(function(gltf) { + return loadGltf(batchedModel).then(function(gltf) { gltf.nodes.push({}); expect(function() { return new ClassificationModel({ @@ -211,7 +281,7 @@ defineSuite([ }); it('throws with invalid number of meshes', function() { - return loadModel(batchedModel).then(function(gltf) { + return loadGltf(batchedModel).then(function(gltf) { gltf.meshes.push({}); expect(function() { return new ClassificationModel({ @@ -222,7 +292,7 @@ defineSuite([ }); it('throws with invalid number of primitives', function() { - return loadModel(batchedModel).then(function(gltf) { + return loadGltf(batchedModel).then(function(gltf) { gltf.meshes[0].primitives.push({}); expect(function() { return new ClassificationModel({ @@ -233,7 +303,7 @@ defineSuite([ }); it('throws with position semantic', function() { - return loadModel(batchedModel).then(function(gltf) { + return loadGltf(batchedModel).then(function(gltf) { gltf.meshes[0].primitives[0].attributes.POSITION = undefined; expect(function() { return new ClassificationModel({ @@ -244,7 +314,7 @@ defineSuite([ }); it('throws with batch id semantic', function() { - return loadModel(batchedModel).then(function(gltf) { + return loadGltf(batchedModel).then(function(gltf) { gltf.meshes[0].primitives[0].attributes._BATCHID = undefined; expect(function() { return new ClassificationModel({ diff --git a/Specs/Scene/ClassificationPrimitiveSpec.js b/Specs/Scene/ClassificationPrimitiveSpec.js index e3c278aa3470..d1afb517bcea 100644 --- a/Specs/Scene/ClassificationPrimitiveSpec.js +++ b/Specs/Scene/ClassificationPrimitiveSpec.js @@ -13,11 +13,14 @@ defineSuite([ 'Core/ShowGeometryInstanceAttribute', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', + 'Scene/ClassificationType', 'Scene/InvertClassification', 'Scene/MaterialAppearance', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', 'Scene/ShadowVolumeAppearance', + 'Scene/StencilConstants', 'Specs/createScene', 'Specs/pollToPromise' ], function( @@ -35,11 +38,14 @@ defineSuite([ ShowGeometryInstanceAttribute, Transforms, Pass, + RenderState, + ClassificationType, InvertClassification, MaterialAppearance, PerInstanceColorAppearance, Primitive, ShadowVolumeAppearance, + StencilConstants, createScene, pollToPromise) { 'use strict'; @@ -54,72 +60,97 @@ defineSuite([ var boxInstance; var primitive; - var depthPrimitive; - - beforeAll(function() { - scene = createScene(); - scene.postProcessStages.fxaa.enabled = false; - - ellipsoid = Ellipsoid.WGS84; - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); + depthColor = depthColorAttribute.value; + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.GLOBE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { - scene.morphTo3D(0); - scene.render(); // clear any afterRender commands + beforeAll(function() { + scene = createScene(); + scene.postProcessStages.fxaa.enabled = false; + + ellipsoid = Ellipsoid.WGS84; rectangle = Rectangle.fromDegrees(-75.0, 25.0, -70.0, 30.0); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); - depthColor = depthColorAttribute.value; - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); + + beforeEach(function() { + scene.morphTo3D(0); + scene.render(); // clear any afterRender commands - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(primitive); + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); var center = Rectangle.center(rectangle); var origin = ellipsoid.cartographicToCartesian(center); @@ -142,9 +173,11 @@ defineSuite([ }); afterEach(function() { + scene.primitives.removeAll(); scene.groundPrimitives.removeAll(); primitive = primitive && !primitive.isDestroyed() && primitive.destroy(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); }); it('default constructs', function() { @@ -328,17 +361,53 @@ defineSuite([ expect(frameState.commandList.length).toEqual(0); }); - function verifyClassificationPrimitiveRender(primitive, color) { - scene.camera.setView({ destination : rectangle }); + function expectRender(color) { + expect(scene).toRender(color); + } - scene.groundPrimitives.add(depthPrimitive); + function expectRenderBlank() { expect(scene).toRenderAndCall(function(rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); }); + } + + function verifyClassificationPrimitiveRender(primitive, color) { + scene.camera.setView({ destination : rectangle }); + + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + + expectRenderBlank(); scene.groundPrimitives.add(primitive); - expect(scene).toRender(color); + + primitive.classificationType = ClassificationType.BOTH; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(color); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(color); + + primitive.classificationType = ClassificationType.CESIUM_3D_TILE; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(color); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRenderBlank(); + + primitive.classificationType = ClassificationType.TERRAIN; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRenderBlank(); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(color); + + globePrimitive.show = true; + tilesetPrimitive.show = true; } it('renders in 3D', function() { @@ -450,7 +519,6 @@ defineSuite([ 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({ @@ -466,7 +534,7 @@ defineSuite([ invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue); invertedColor[3] = 255; - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); @@ -490,7 +558,6 @@ defineSuite([ 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({ @@ -506,7 +573,7 @@ defineSuite([ invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue * scene.invertClassificationColor.alpha); invertedColor[3] = 255; - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); @@ -588,6 +655,10 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColor); + scene.primitives.destroyPrimitives = false; + scene.primitives.removeAll(); + scene.primitives.destroyPrimitives = true; + scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; @@ -614,6 +685,10 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColor); + scene.primitives.destroyPrimitives = false; + scene.primitives.removeAll(); + scene.primitives.destroyPrimitives = true; + scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; @@ -688,9 +763,10 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColor); expect(scene).toDrillPickAndCall(function(pickedObjects) { - expect(pickedObjects.length).toEqual(2); + expect(pickedObjects.length).toEqual(3); expect(pickedObjects[0].primitive).toEqual(primitive); - expect(pickedObjects[1].primitive).toEqual(depthPrimitive._primitive); + expect(pickedObjects[1].primitive).toEqual(globePrimitive._primitive); + expect(pickedObjects[2].primitive).toEqual(tilesetPrimitive._primitive); }); }); @@ -970,7 +1046,7 @@ defineSuite([ }); scene.camera.setView({ destination : rectangle }); - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); expect(scene).toRenderAndCall(function(rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); diff --git a/Specs/Scene/Geometry3DTileContentSpec.js b/Specs/Scene/Geometry3DTileContentSpec.js index dae01c33c3cc..d0467d993867 100644 --- a/Specs/Scene/Geometry3DTileContentSpec.js +++ b/Specs/Scene/Geometry3DTileContentSpec.js @@ -14,12 +14,14 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/Cesium3DTileBatchTable', 'Scene/Cesium3DTileset', 'Scene/Cesium3DTileStyle', 'Scene/ClassificationType', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/Cesium3DTilesTester', 'Specs/createScene' ], function( @@ -38,12 +40,14 @@ defineSuite([ RectangleGeometry, Transforms, Pass, + RenderState, Cesium3DTileBatchTable, Cesium3DTileset, Cesium3DTileStyle, ClassificationType, PerInstanceColorAppearance, Primitive, + StencilConstants, Cesium3DTilesTester, createScene) { 'use strict'; @@ -82,73 +86,101 @@ defineSuite([ var scene; var rectangle; - var depthPrimitive; var tileset; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + var depthColor; var ellipsoid = Ellipsoid.WGS84; - beforeAll(function() { - scene = createScene(); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); + depthColor = depthColorAttribute.value; + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { + beforeAll(function() { + scene = createScene(); + rectangle = Rectangle.fromDegrees(-40.0, -40.0, 40.0, 40.0); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(primitive); + beforeEach(function() { + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); }); afterEach(function() { scene.primitives.removeAll(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); tileset = tileset && !tileset.isDestroyed() && tileset.destroy(); }); @@ -236,8 +268,69 @@ defineSuite([ expectRender(scene, [0, 0, 255, 255]); } + it('renders on 3D Tiles', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + tileset = scene.primitives.add(new Cesium3DTileset({ + url : geometryBoxes, + classificationType : ClassificationType.CESIUM_3D_TILE + })); + return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender(depthColor); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + + it('renders on globe', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + tileset = scene.primitives.add(new Cesium3DTileset({ + url : geometryBoxes, + classificationType : ClassificationType.TERRAIN + })); + return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender(depthColor); + globePrimitive.show = true; + tilesetPrimitive.show = false; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + + it('renders on 3D Tiles and globe', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + tileset = scene.primitives.add(new Cesium3DTileset({ + url : geometryBoxes, + classificationType : ClassificationType.BOTH + })); + return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = false; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + it('renders boxes', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryBoxes })); @@ -248,7 +341,7 @@ defineSuite([ }); it('renders batched boxes', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryBoxesBatchedChildren })); @@ -259,7 +352,7 @@ defineSuite([ }); it('renders boxes with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryBoxesWithBatchTable })); @@ -270,7 +363,7 @@ defineSuite([ }); it('renders batched boxes with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryBoxesBatchedChildrenWithBatchTable })); @@ -281,7 +374,7 @@ defineSuite([ }); it('renders boxes with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryBoxesWithBatchIds })); @@ -292,7 +385,7 @@ defineSuite([ }); it('renders cylinders', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryCylinders })); @@ -303,7 +396,7 @@ defineSuite([ }); it('renders batched cylinders', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryCylindersBatchedChildren })); @@ -314,7 +407,7 @@ defineSuite([ }); it('renders cylinders with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryCylindersWithBatchTable })); @@ -325,7 +418,7 @@ defineSuite([ }); it('renders batched cylinders with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryCylindersBatchedChildrenWithBatchTable })); @@ -336,7 +429,7 @@ defineSuite([ }); it('renders cylinders with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryCylindersWithBatchIds })); @@ -347,7 +440,7 @@ defineSuite([ }); it('renders ellipsoids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryEllipsoids })); @@ -358,7 +451,7 @@ defineSuite([ }); it('renders batched ellipsoids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryEllipsoidsBatchedChildren })); @@ -369,7 +462,7 @@ defineSuite([ }); it('renders ellipsoids with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryEllipsoidsWithBatchTable })); @@ -380,7 +473,7 @@ defineSuite([ }); it('renders batched ellipsoids with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryEllipsoidsBatchedChildrenWithBatchTable })); @@ -391,7 +484,7 @@ defineSuite([ }); it('renders ellipsoids with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryEllipsoidsWithBatchIds })); @@ -402,7 +495,7 @@ defineSuite([ }); it('renders spheres', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometrySpheres })); @@ -413,7 +506,7 @@ defineSuite([ }); it('renders batched spheres', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometrySpheresBatchedChildren })); @@ -424,7 +517,7 @@ defineSuite([ }); it('renders spheres with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometrySpheresWithBatchTable })); @@ -435,7 +528,7 @@ defineSuite([ }); it('renders batched spheres with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometrySpheresBatchedChildrenWithBatchTable })); @@ -446,7 +539,7 @@ defineSuite([ }); it('renders spheres with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometrySpheresWithBatchIds })); @@ -457,7 +550,7 @@ defineSuite([ }); it('renders all geometries', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAll })); @@ -468,7 +561,7 @@ defineSuite([ }); it('renders batched all geometries', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAllBatchedChildren })); @@ -479,7 +572,7 @@ defineSuite([ }); it('renders all geometries with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAllWithBatchTable })); @@ -490,7 +583,7 @@ defineSuite([ }); it('renders batched all geometries with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAllBatchedChildrenWithBatchTable })); @@ -501,7 +594,7 @@ defineSuite([ }); it('renders all geometries with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAllWithBatchIds })); @@ -512,7 +605,7 @@ defineSuite([ }); it('renders all geometries with debug color', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : geometryAllWithBatchTable, debugColorizeTiles : true diff --git a/Specs/Scene/GroundPrimitiveSpec.js b/Specs/Scene/GroundPrimitiveSpec.js index 9b6b81543666..65e592793db1 100644 --- a/Specs/Scene/GroundPrimitiveSpec.js +++ b/Specs/Scene/GroundPrimitiveSpec.js @@ -15,12 +15,14 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/ShowGeometryInstanceAttribute', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/ClassificationType', 'Scene/EllipsoidSurfaceAppearance', 'Scene/InvertClassification', 'Scene/Material', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/createCanvas', 'Specs/createScene', 'Specs/pollToPromise' @@ -41,12 +43,14 @@ defineSuite([ RectangleGeometry, ShowGeometryInstanceAttribute, Pass, + RenderState, ClassificationType, EllipsoidSurfaceAppearance, InvertClassification, Material, PerInstanceColorAppearance, Primitive, + StencilConstants, createCanvas, createScene, pollToPromise) { @@ -63,82 +67,108 @@ defineSuite([ var rectangleInstance; var primitive; - var depthPrimitive; - var reusablePrimitive; - - beforeAll(function() { - scene = createScene(); - - scene.postProcessStages.fxaa.enabled = false; - - context = scene.context; - - ellipsoid = Ellipsoid.WGS84; - return GroundPrimitive.initializeTerrainHeights().then(function(){ - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); - depthColor = depthColorAttribute.value; - var bigRectangle = Rectangle.fromDegrees(-180 + CesiumMath.EPSILON4, -90 + CesiumMath.EPSILON4, 180 - CesiumMath.EPSILON4, 90 - CesiumMath.EPSILON4); - - reusablePrimitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : bigRectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); + depthColor = depthColorAttribute.value; + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false }); - }); - - afterAll(function() { - reusablePrimitive.destroy(); - scene.destroyForSpecs(); - // Leave ground primitive uninitialized - ApproximateTerrainHeights._initPromise = undefined; - ApproximateTerrainHeights._terrainHeights = undefined; - }); + } - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.GLOBE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; + beforeAll(function() { + scene = createScene(); + + scene.postProcessStages.fxaa.enabled = false; + + context = scene.context; + + ellipsoid = Ellipsoid.WGS84; + + var bigRectangle = Rectangle.fromDegrees(-180 + CesiumMath.EPSILON4, -90 + CesiumMath.EPSILON4, 180 - CesiumMath.EPSILON4, 90 - CesiumMath.EPSILON4); + reusableGlobePrimitive = createPrimitive(bigRectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(bigRectangle, Pass.CESIUM_3D_TILE); + + return GroundPrimitive.initializeTerrainHeights(); + + }); + + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + // Leave ground primitive uninitialized + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; + }); + beforeEach(function() { scene.morphTo3D(0); scene.render(); // clear any afterRender commands rectangle = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(reusablePrimitive); + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 1.0, 0.0, 1.0)); rectColor = rectColorAttribute.value; @@ -155,9 +185,11 @@ defineSuite([ }); afterEach(function() { + scene.primitives.removeAll(); scene.groundPrimitives.removeAll(); primitive = primitive && !primitive.isDestroyed() && primitive.destroy(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); }); it('default constructs', function() { @@ -342,17 +374,53 @@ defineSuite([ expect(frameState.commandList.length).toEqual(0); }); - function verifyGroundPrimitiveRender(primitive, color) { - scene.camera.setView({ destination : rectangle }); + function expectRender(color) { + expect(scene).toRender(color); + } - scene.groundPrimitives.add(depthPrimitive); + function expectRenderBlank() { expect(scene).toRenderAndCall(function(rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); }); + } + + function verifyGroundPrimitiveRender(primitive, color) { + scene.camera.setView({ destination : rectangle }); + + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + + expectRenderBlank(); scene.groundPrimitives.add(primitive); - expect(scene).toRender(color); + + primitive.classificationType = ClassificationType.BOTH; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(color); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(color); + + primitive.classificationType = ClassificationType.CESIUM_3D_TILE; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRender(color); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRenderBlank(); + + primitive.classificationType = ClassificationType.TERRAIN; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expectRenderBlank(); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expectRender(color); + + globePrimitive.show = true; + tilesetPrimitive.show = true; } it('renders in 3D', function() { @@ -393,7 +461,7 @@ defineSuite([ scene.camera.setView({ destination : bigIdlRectangle }); - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); expect(scene).toRenderAndCall(function(rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); @@ -428,7 +496,7 @@ defineSuite([ scene.camera.setView({ destination : smallIdlRectangle }); - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); expect(scene).toRenderAndCall(function(rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); @@ -469,39 +537,24 @@ defineSuite([ describe('larger scene', function() { // Screen space techniques may produce unexpected results with 1x1 canvasses var largeScene; - var largeSceneReusablePrimitive; + var largeSceneReusableGlobePrimitive; + var largeSceneReusableTilesetPrimitive; beforeAll(function() { largeScene = createScene({ canvas : createCanvas(2, 2) }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); - depthColor = depthColorAttribute.value; var bigRectangle = Rectangle.fromDegrees(-180 + CesiumMath.EPSILON4, -90 + CesiumMath.EPSILON4, 180 - CesiumMath.EPSILON4, 90 - CesiumMath.EPSILON4); - - largeSceneReusablePrimitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : bigRectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + largeSceneReusableGlobePrimitive = createPrimitive(bigRectangle, Pass.GLOBE); + largeSceneReusableTilesetPrimitive = createPrimitive(bigRectangle, Pass.CESIUM_3D_TILE); }); afterAll(function() { - largeSceneReusablePrimitive.destroy(); + largeSceneReusableGlobePrimitive.destroy(); + largeSceneReusableTilesetPrimitive.destroy(); largeScene.destroyForSpecs(); }); afterEach(function(){ + largeScene.primitives.removeAll(); largeScene.groundPrimitives.removeAll(); }); @@ -511,9 +564,9 @@ defineSuite([ largeScene.postProcessStages.fxaa.enabled = false; largeScene.camera.setView({destination : destination}); - var largeSceneDepthPrimitive = new MockGlobePrimitive(largeSceneReusablePrimitive); + var largeSceneGlobePrimitive = new MockPrimitive(largeSceneReusableGlobePrimitive, Pass.GLOBE); - largeScene.groundPrimitives.add(largeSceneDepthPrimitive); + largeScene.primitives.add(largeSceneGlobePrimitive); expect(largeScene).toRenderAndCall(function(rgba) { expect(arraySlice(rgba, 0, 4)).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); @@ -696,7 +749,6 @@ defineSuite([ 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({ @@ -713,7 +765,7 @@ defineSuite([ invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue); invertedColor[3] = 255; - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); @@ -737,7 +789,6 @@ defineSuite([ 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({ @@ -754,7 +805,7 @@ defineSuite([ invertedColor[2] = Color.floatToByte(Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue * scene.invertClassificationColor.alpha); invertedColor[3] = 255; - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); @@ -836,6 +887,10 @@ defineSuite([ verifyGroundPrimitiveRender(primitive, rectColor); + scene.primitives.destroyPrimitives = false; + scene.primitives.removeAll(); + scene.primitives.destroyPrimitives = true; + scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; @@ -862,6 +917,10 @@ defineSuite([ verifyGroundPrimitiveRender(primitive, rectColor); + scene.primitives.destroyPrimitives = false; + scene.primitives.removeAll(); + scene.primitives.destroyPrimitives = true; + scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; @@ -904,7 +963,7 @@ defineSuite([ asynchronous : false }); - scene.groundPrimitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); scene.groundPrimitives.add(primitive); scene.camera.setView({ destination : rect }); scene.renderForSpecs(); diff --git a/Specs/Scene/Vector3DTileContentSpec.js b/Specs/Scene/Vector3DTileContentSpec.js index e4f2cbc69257..079c543159c5 100644 --- a/Specs/Scene/Vector3DTileContentSpec.js +++ b/Specs/Scene/Vector3DTileContentSpec.js @@ -14,12 +14,14 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/Cesium3DTileBatchTable', 'Scene/Cesium3DTileset', 'Scene/Cesium3DTileStyle', 'Scene/ClassificationType', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/Cesium3DTilesTester', 'Specs/createScene' ], function( @@ -38,12 +40,14 @@ defineSuite([ RectangleGeometry, Transforms, Pass, + RenderState, Cesium3DTileBatchTable, Cesium3DTileset, Cesium3DTileStyle, ClassificationType, PerInstanceColorAppearance, Primitive, + StencilConstants, Cesium3DTilesTester, createScene) { 'use strict'; @@ -74,73 +78,101 @@ defineSuite([ var scene; var rectangle; - var depthPrimitive; var tileset; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; + var depthColor; var ellipsoid = Ellipsoid.WGS84; - beforeAll(function() { - scene = createScene(); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); + depthColor = depthColorAttribute.value; + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } - function MockGlobePrimitive(primitive) { + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { + beforeAll(function() { + scene = createScene(); + rectangle = Rectangle.fromDegrees(-40.0, -40.0, 40.0, 40.0); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(primitive); + beforeEach(function() { + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); }); afterEach(function() { scene.primitives.removeAll(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); tileset = tileset && !tileset.isDestroyed() && tileset.destroy(); }); @@ -461,7 +493,7 @@ defineSuite([ }); it('renders polygons', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygons })); @@ -472,7 +504,7 @@ defineSuite([ }); it('renders batched polygons', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygonsBatchedChildren })); @@ -483,7 +515,7 @@ defineSuite([ }); it('renders polygons with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygonsWithBatchTable })); @@ -494,7 +526,7 @@ defineSuite([ }); it('renders batched polygons with a batch table', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygonsBatchedChildrenWithBatchTable })); @@ -505,7 +537,7 @@ defineSuite([ }); it('renders polygons with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygonsWithBatchIds })); @@ -561,7 +593,7 @@ defineSuite([ }); it('renders combined tile', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorCombined })); @@ -571,7 +603,7 @@ defineSuite([ }); it('renders combined tile with batch ids', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorCombinedWithBatchIds })); @@ -581,7 +613,7 @@ defineSuite([ }); it('renders with debug color', function() { - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorCombined, debugColorizeTiles : true @@ -603,41 +635,64 @@ defineSuite([ }); }); - it('renders with different classification types', function() { - scene.primitives.add(depthPrimitive); + it('renders on 3D Tiles', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); tileset = scene.primitives.add(new Cesium3DTileset({ url : vectorPolygonsBatchedChildren, classificationType : ClassificationType.CESIUM_3D_TILE })); return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender(depthColor); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + + it('renders on globe', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + tileset = scene.primitives.add(new Cesium3DTileset({ + url : vectorPolygonsBatchedChildren, + classificationType : ClassificationType.TERRAIN + })); + return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender(depthColor); + globePrimitive.show = true; + tilesetPrimitive.show = false; verifyRender(tileset, scene); verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); - scene.primitives.remove(tileset); - - tileset = scene.primitives.add(new Cesium3DTileset({ - url : vectorPolygonsBatchedChildren, - classificationType : ClassificationType.TERRAIN - })); - return loadTileset(tileset).then(function(tileset) { - depthPrimitive.pass = Pass.GLOBE; - verifyRender(tileset, scene); - verifyPick(scene); - - scene.primitives.remove(tileset); - - tileset = scene.primitives.add(new Cesium3DTileset({ - url : vectorPolygonsBatchedChildren, - classificationType : ClassificationType.BOTH - })); - return loadTileset(tileset).then(function(tileset) { - verifyRender(tileset, scene); - verifyPick(scene); - depthPrimitive.pass = Pass.CESIUM_3D_TILE; - verifyRender(tileset, scene); - verifyPick(scene); - }); - }); + it('renders on 3D Tiles and globe', function() { + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + tileset = scene.primitives.add(new Cesium3DTileset({ + url : vectorPolygonsBatchedChildren, + classificationType : ClassificationType.BOTH + })); + return loadTileset(tileset).then(function(tileset) { + globePrimitive.show = false; + tilesetPrimitive.show = true; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = false; + verifyRender(tileset, scene); + verifyPick(scene); + globePrimitive.show = true; + tilesetPrimitive.show = true; }); }); diff --git a/Specs/Scene/Vector3DTileGeometrySpec.js b/Specs/Scene/Vector3DTileGeometrySpec.js index 233317192880..d28b4e18fbf3 100644 --- a/Specs/Scene/Vector3DTileGeometrySpec.js +++ b/Specs/Scene/Vector3DTileGeometrySpec.js @@ -13,10 +13,13 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/Cesium3DTileBatchTable', + 'Scene/ClassificationType', 'Scene/ColorBlendMode', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/createContext', 'Specs/createScene', 'Specs/pollToPromise' @@ -35,10 +38,13 @@ defineSuite([ RectangleGeometry, Transforms, Pass, + RenderState, Cesium3DTileBatchTable, + ClassificationType, ColorBlendMode, PerInstanceColorAppearance, Primitive, + StencilConstants, createContext, createScene, pollToPromise) { @@ -57,19 +63,14 @@ defineSuite([ var scene; var rectangle; - var depthPrimitive; var geometry; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; var ellipsoid = Ellipsoid.WGS84; - beforeAll(function() { - scene = createScene({ contextOptions : contextOptions }); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); - var mockTileset = { _statistics : { texturesByteLength : 0 @@ -83,61 +84,92 @@ defineSuite([ getFeature : function(id) { return { batchId : id }; } }; - function MockGlobePrimitive(primitive) { + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } + + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { + beforeAll(function() { + scene = createScene({ contextOptions : contextOptions }); + rectangle = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(primitive); + beforeEach(function() { + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); }); afterEach(function() { scene.primitives.removeAll(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); geometry = geometry && !geometry.isDestroyed() && geometry.destroy(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); }); function loadGeometries(geometries) { @@ -217,7 +249,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry(combine(geometryOptions, { center : center, @@ -249,7 +281,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, length); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry(combine(geometryOptions, { center : center, @@ -533,7 +565,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, length); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry({ boxes : boxes, @@ -587,7 +619,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry({ ellipsoids : ellipsoids, @@ -620,7 +652,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry({ ellipsoids : packEllipsoids([{ @@ -648,6 +680,66 @@ defineSuite([ }); }); + it('renders based on classificationType' + webglMessage, function() { + var radii = new Cartesian3(100.0, 100.0, 1000.0); + var ellipsoids = packEllipsoids([{ + modelMatrix : Matrix4.IDENTITY, + radii : radii + }]); + var ellipsoidBatchIds = new Uint16Array([0]); + + var origin = Rectangle.center(rectangle); + var center = ellipsoid.cartographicToCartesian(origin); + var modelMatrix = Transforms.eastNorthUpToFixedFrame(center); + + var bv = new BoundingSphere(center, Cartesian3.maximumComponent(radii)); + + var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); + batchTable.update(mockTileset, scene.frameState); + + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + + geometry = scene.primitives.add(new Vector3DTileGeometry({ + ellipsoids : ellipsoids, + ellipsoidBatchIds : ellipsoidBatchIds, + boundingVolume : bv, + center : center, + modelMatrix : modelMatrix, + batchTable : batchTable + })); + return loadGeometries(geometry).then(function() { + scene.camera.lookAtTransform(modelMatrix, new Cartesian3(0.0, 0.0, 1.0)); + + geometry.classificationType = ClassificationType.CESIUM_3D_TILE; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 255, 255, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 0, 0, 255]); + + geometry.classificationType = ClassificationType.TERRAIN; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 0, 0, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 255, 255, 255]); + + geometry.classificationType = ClassificationType.BOTH; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 255, 255, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 255, 255, 255]); + + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + it('picks geometry' + webglMessage, function() { var origin = Rectangle.center(rectangle); var center = ellipsoid.cartographicToCartesian(origin); @@ -655,7 +747,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); geometry = scene.primitives.add(new Vector3DTileGeometry({ ellipsoids : packEllipsoids([{ diff --git a/Specs/Scene/Vector3DTilePolygonsSpec.js b/Specs/Scene/Vector3DTilePolygonsSpec.js index d7f9d0b28598..767e8642ae82 100644 --- a/Specs/Scene/Vector3DTilePolygonsSpec.js +++ b/Specs/Scene/Vector3DTilePolygonsSpec.js @@ -14,10 +14,13 @@ defineSuite([ 'Core/RectangleGeometry', 'Core/Transforms', 'Renderer/Pass', + 'Renderer/RenderState', 'Scene/Cesium3DTileBatchTable', + 'Scene/ClassificationType', 'Scene/ColorBlendMode', 'Scene/PerInstanceColorAppearance', 'Scene/Primitive', + 'Scene/StencilConstants', 'Specs/createContext', 'Specs/createScene', 'Specs/pollToPromise' @@ -37,10 +40,13 @@ defineSuite([ RectangleGeometry, Transforms, Pass, + RenderState, Cesium3DTileBatchTable, + ClassificationType, ColorBlendMode, PerInstanceColorAppearance, Primitive, + StencilConstants, createContext, createScene, pollToPromise) { @@ -59,19 +65,14 @@ defineSuite([ var scene; var rectangle; - var depthPrimitive; var polygons; + var globePrimitive; + var tilesetPrimitive; + var reusableGlobePrimitive; + var reusableTilesetPrimitive; var ellipsoid = Ellipsoid.WGS84; - beforeAll(function() { - scene = createScene({ contextOptions : contextOptions }); - }); - - afterAll(function() { - scene.destroyForSpecs(); - }); - var mockTileset = { _statistics : { texturesByteLength : 0 @@ -85,61 +86,92 @@ defineSuite([ getFeature : function(id) { return { batchId : id }; } }; - function MockGlobePrimitive(primitive) { + function createPrimitive(rectangle, pass) { + var renderState; + if (pass === Pass.CESIUM_3D_TILE) { + renderState = RenderState.fromCache({ + stencilTest : StencilConstants.setCesium3DTileBit(), + stencilMask : StencilConstants.CESIUM_3D_TILE_MASK, + depthTest : { + enabled : true + } + }); + } + var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); + return new Primitive({ + geometryInstances : new GeometryInstance({ + geometry : new RectangleGeometry({ + ellipsoid : Ellipsoid.WGS84, + rectangle : rectangle + }), + id : 'depth rectangle', + attributes : { + color : depthColorAttribute + } + }), + appearance : new PerInstanceColorAppearance({ + translucent : false, + flat : true, + renderState : renderState + }), + asynchronous : false + }); + } + + function MockPrimitive(primitive, pass) { this._primitive = primitive; - this.pass = Pass.CESIUM_3D_TILE; + this._pass = pass; + this.show = true; } - MockGlobePrimitive.prototype.update = function(frameState) { + MockPrimitive.prototype.update = function(frameState) { + if (!this.show) { + return; + } + var commandList = frameState.commandList; var startLength = commandList.length; this._primitive.update(frameState); for (var i = startLength; i < commandList.length; ++i) { var command = commandList[i]; - command.pass = this.pass; + command.pass = this._pass; } }; - MockGlobePrimitive.prototype.isDestroyed = function() { + MockPrimitive.prototype.isDestroyed = function() { return false; }; - MockGlobePrimitive.prototype.destroy = function() { - this._primitive.destroy(); + MockPrimitive.prototype.destroy = function() { return destroyObject(this); }; - beforeEach(function() { + beforeAll(function() { + scene = createScene({ contextOptions : contextOptions }); + rectangle = Rectangle.fromDegrees(-40.0, -40.0, 40.0, 40.0); + reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE); + reusableTilesetPrimitive = createPrimitive(rectangle, Pass.CESIUM_3D_TILE); + }); - var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 0.0, 1.0)); - var primitive = new Primitive({ - geometryInstances : new GeometryInstance({ - geometry : new RectangleGeometry({ - ellipsoid : ellipsoid, - rectangle : rectangle - }), - id : 'depth rectangle', - attributes : { - color : depthColorAttribute - } - }), - appearance : new PerInstanceColorAppearance({ - translucent : false, - flat : true - }), - asynchronous : false - }); + afterAll(function() { + reusableGlobePrimitive.destroy(); + reusableTilesetPrimitive.destroy(); + scene.destroyForSpecs(); + }); - // wrap rectangle primitive so it gets executed during the globe pass to lay down depth - depthPrimitive = new MockGlobePrimitive(primitive); + beforeEach(function() { + // wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth + globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE); + tilesetPrimitive = new MockPrimitive(reusableTilesetPrimitive, Pass.CESIUM_3D_TILE); }); afterEach(function() { scene.primitives.removeAll(); + globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); + tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); polygons = polygons && !polygons.isDestroyed() && polygons.destroy(); - depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy(); }); function loadPolygons(polygons) { @@ -211,7 +243,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons(combine(polygonOptions, { @@ -256,7 +288,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 2); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons({ @@ -316,7 +348,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 2); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons({ @@ -377,7 +409,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 2); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons({ @@ -424,7 +456,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(tilesetPrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons(combine(polygonOptions, { @@ -465,7 +497,7 @@ defineSuite([ var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons(combine(polygonOptions, { @@ -493,13 +525,68 @@ defineSuite([ }); }); + it('renders based on classificationType' + webglMessage, function() { + var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); + var polygonOptions = createPolygon(rectangle); + + var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); + batchTable.update(mockTileset, scene.frameState); + + scene.primitives.add(globePrimitive); + scene.primitives.add(tilesetPrimitive); + + var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); + polygons = scene.primitives.add(new Vector3DTilePolygons(combine(polygonOptions, { + minimumHeight : -10000.0, + maximumHeight : 10000.0, + center : center, + rectangle : rectangle, + boundingVolume : new BoundingSphere(center, 10000.0), + batchTable : batchTable, + batchIds : new Uint32Array([0]), + isCartographic : true + }))); + return loadPolygons(polygons).then(function() { + scene.camera.setView({ + destination : rectangle + }); + + polygons.classificationType = ClassificationType.CESIUM_3D_TILE; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 255, 255, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 0, 0, 255]); + + polygons.classificationType = ClassificationType.TERRAIN; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 0, 0, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 255, 255, 255]); + + polygons.classificationType = ClassificationType.BOTH; + globePrimitive.show = false; + tilesetPrimitive.show = true; + expect(scene).toRender([255, 255, 255, 255]); + globePrimitive.show = true; + tilesetPrimitive.show = false; + expect(scene).toRender([255, 255, 255, 255]); + + globePrimitive.show = true; + tilesetPrimitive.show = true; + }); + }); + it('picks polygons' + webglMessage, function() { var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var polygonOptions = createPolygon(rectangle); var batchTable = new Cesium3DTileBatchTable(mockTileset, 1); - scene.primitives.add(depthPrimitive); + scene.primitives.add(globePrimitive); var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); polygons = scene.primitives.add(new Vector3DTilePolygons(combine(polygonOptions, {