diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index a20ab86a3537..37bf2b6af10b 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -395,10 +395,7 @@ define([ * Part of the {@link Cesium3DTileContent} interface. */ Batched3DModel3DTileContent.prototype.update = function(tileset, frameState) { - var oldAddCommand = frameState.addCommand; - if (frameState.passes.render) { - frameState.addCommand = this.batchTable.getAddCommand(); - } + var commandStart = frameState.commandList.length; // In the PROCESSING state we may be calling update() to move forward // the content's resource loading. In the READY state, it will @@ -408,7 +405,12 @@ define([ this._model.shadows = this._tileset.shadows; this._model.debugWireframe = this._tileset.debugWireframe; this._model.update(frameState); - frameState.addCommand = oldAddCommand; + + // If any commands were pushed, add derived commands + var commandEnd = frameState.commandList.length; + if ((commandStart < commandEnd) && frameState.passes.render) { + this.batchTable.addDerivedCommands(frameState, commandStart); + } }; /** diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index fd0f5173531d..46f2f9a7ade9 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -894,8 +894,6 @@ define([ ' vec4 featureProperties = texture2D(tile_batchTexture, st); \n' + ' float show = ceil(featureProperties.a); \n' + // 0 - false, non-zeo - true ' gl_Position *= show; \n'; // Per-feature show/hide -// TODO: Translucent should still write depth for picking? For example, we want to grab highlighted building that became translucent -// TODO: Same TODOs below in getFragmentShaderCallback if (handleTranslucent) { newMain += ' bool isStyleTranslucent = (featureProperties.a != 1.0); \n' + @@ -1231,56 +1229,48 @@ define([ OPAQUE_AND_TRANSLUCENT : 2 }; - function updateDerivedCommands(derivedCommands, command) { - for (var name in derivedCommands) { - if (derivedCommands.hasOwnProperty(name)) { - var derivedCommand = derivedCommands[name]; - derivedCommand.castShadows = command.castShadows; - derivedCommand.receiveShadows = command.receiveShadows; - derivedCommand.primitiveType = command.primitiveType; - } - } - } - - Cesium3DTileBatchTable.prototype.getAddCommand = function() { - var styleCommandsNeeded = getStyleCommandsNeeded(this); + Cesium3DTileBatchTable.prototype.addDerivedCommands = function(frameState, commandStart) { + var commandList = frameState.commandList; + var commandEnd = commandList.length; var tile = this._content._tile; var tileset = tile._tileset; + var bivariateVisibilityTest = tileset.skipLODs && tileset._hasMixedContent && frameState.context.stencilBuffer; + var styleCommandsNeeded = getStyleCommandsNeeded(this); -// TODO: This function most likely will not get optimized. Do something like this later in the render loop. - return function(command) { - var commandList = this.commandList; - + for (var i = commandStart; i < commandEnd; ++i) { + var command = commandList[i]; var derivedCommands = command.derivedCommands.tileset; if (!defined(derivedCommands)) { derivedCommands = {}; command.derivedCommands.tileset = derivedCommands; - derivedCommands.originalCommand = deriveCommand(command); - derivedCommands.back = deriveTranslucentCommand(command, CullFace.FRONT); - derivedCommands.front = deriveTranslucentCommand(command, CullFace.BACK); } - var bivariateVisibilityTest = tileset.skipLODs && tileset._hasMixedContent && this.context.stencilBuffer; + updateDerivedCommand(derivedCommands.originalCommand, command); - if (bivariateVisibilityTest) { - if (!defined(derivedCommands.zback)) { - derivedCommands.zback = deriveZBackfaceCommand(command); + if (styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE) { + if (!defined(derivedCommands.translucent)) { + derivedCommands.translucent = deriveTranslucentCommand(derivedCommands.originalCommand); } + updateDerivedCommand(derivedCommands.translucent, command); + } + + if (bivariateVisibilityTest) { if (command.pass !== Pass.TRANSLUCENT) { + if (!defined(derivedCommands.zback)) { + derivedCommands.zback = deriveZBackfaceCommand(derivedCommands.originalCommand); + } tileset._backfaceCommands.push(derivedCommands.zback); } - if (!defined(derivedCommands.stencil) || tile._selectionDepth !== tile._lastSelectionDepth) { derivedCommands.stencil = deriveStencilCommand(derivedCommands.originalCommand, tile._selectionDepth); tile._lastSelectionDepth = tile._selectionDepth; } + updateDerivedCommand(derivedCommands.stencil, command); } - updateDerivedCommands(derivedCommands, command); - - // replace original commands with stenciled commands var opaqueCommand = bivariateVisibilityTest ? derivedCommands.stencil : derivedCommands.originalCommand; + var translucentCommand = derivedCommands.translucent; // If the command was originally opaque: // * If the styling applied to the tile is all opaque, use the original command @@ -1289,36 +1279,35 @@ define([ // and back faces) with a translucent render state. // * If the styling causes both opaque and translucent features in this tile, // then use both sets of commands. -// TODO: if the tile has multiple commands, we do not know what features are in what -// commands so the third-case may be overkill. Change this to a PERFORMANCE_IDEA? if (command.pass !== Pass.TRANSLUCENT) { if (styleCommandsNeeded === StyleCommandsNeeded.ALL_OPAQUE) { - commandList.push(opaqueCommand); + commandList[i] = opaqueCommand; } - if (styleCommandsNeeded === StyleCommandsNeeded.ALL_TRANSLUCENT) { -// TODO: vector tiles, for example, will not always want two passes for translucency. Some primitives, -// for example, those created from Cesium geometries, will also already return commands for two -// passes if the command is originally translucent. Same TODO below. - commandList.push(derivedCommands.back); - commandList.push(derivedCommands.front); + commandList[i] = translucentCommand; } - if (styleCommandsNeeded === StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT) { - commandList.push(opaqueCommand); - commandList.push(derivedCommands.back); - commandList.push(derivedCommands.front); + // PERFORMANCE_IDEA: if the tile has multiple commands, we do not know what features are in what + // commands so this case may be overkill. + commandList[i] = opaqueCommand; + commandList.push(translucentCommand); } } else { // Command was originally translucent so no need to derive new commands; // 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.push(opaqueCommand); + commandList[i] = opaqueCommand; } - }; + } }; + function updateDerivedCommand(derivedCommand, command) { + derivedCommand.castShadows = command.castShadows; + derivedCommand.receiveShadows = command.receiveShadows; + derivedCommand.primitiveType = command.primitiveType; + } + function getStyleCommandsNeeded(batchTable) { var translucentFeaturesLength = batchTable._translucentFeaturesLength; @@ -1331,13 +1320,6 @@ define([ return StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT; } - function deriveTranslucentCommand(command, cullFace) { - var derivedCommand = deriveCommand(command); - derivedCommand.pass = Pass.TRANSLUCENT; - derivedCommand.renderState = getTranslucentRenderState(command.renderState, cullFace); - return derivedCommand; - } - function deriveCommand(command) { var derivedCommand = DrawCommand.shallowClone(command); @@ -1358,13 +1340,22 @@ define([ return derivedCommand; } - // write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front + function deriveTranslucentCommand(command) { + var derivedCommand = DrawCommand.shallowClone(command); + derivedCommand.pass = Pass.TRANSLUCENT; + derivedCommand.renderState = getTranslucentRenderState(command.renderState); + return derivedCommand; + } + function deriveZBackfaceCommand(command) { + // Write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front var derivedCommand = DrawCommand.shallowClone(command); var rs = clone(derivedCommand.renderState, true); rs.cull.enabled = true; rs.cull.face = CullFace.FRONT; derivedCommand.renderState = RenderState.fromCache(rs); + derivedCommand.castShadows = false; + derivedCommand.receiveShadows = false; return derivedCommand; } @@ -1384,10 +1375,9 @@ define([ return derivedCommand; } - function getTranslucentRenderState(renderState, cullFace) { + function getTranslucentRenderState(renderState) { var rs = clone(renderState, true); - rs.cull.enabled = true; - rs.cull.face = cullFace; + rs.cull.enabled = false; rs.depthTest.enabled = true; rs.depthMask = false; rs.blending = BlendingState.ALPHA_BLEND; diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index a73fabd3f26b..da3a84c4d2bb 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -287,10 +287,6 @@ define([ this.minimumDisableDepthTestDistance = undefined; } - FrameState.prototype.addCommand = function(command) { - this.commandList.push(command); - }; - /** * A function that will be called at the end of the frame. * diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js index 0d9460cd806f..e98e1c2d214f 100644 --- a/Source/Scene/Instanced3DModel3DTileContent.js +++ b/Source/Scene/Instanced3DModel3DTileContent.js @@ -480,10 +480,7 @@ define([ * Part of the {@link Cesium3DTileContent} interface. */ Instanced3DModel3DTileContent.prototype.update = function(tileset, frameState) { - var oldAddCommand = frameState.addCommand; - if (frameState.passes.render) { - frameState.addCommand = this.batchTable.getAddCommand(); - } + var commandStart = frameState.commandList.length; // In the PROCESSING state we may be calling update() to move forward // the content's resource loading. In the READY state, it will @@ -494,7 +491,11 @@ define([ this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe; this._modelInstanceCollection.update(frameState); - frameState.addCommand = oldAddCommand; + // If any commands were pushed, add derived commands + var commandEnd = frameState.commandList.length; + if ((commandStart < commandEnd) && frameState.passes.render) { + this.batchTable.addDerivedCommands(frameState, commandStart); + } }; /** diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index efd6961545d5..60054ae62dab 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -4662,6 +4662,7 @@ define([ // and then have them visible immediately when show is set to true. if (show && !this._ignoreCommands) { // PERFORMANCE_IDEA: This is terrible + var commandList = frameState.commandList; var passes = frameState.passes; var nodeCommands = this._nodeCommands; var length = nodeCommands.length; @@ -4677,13 +4678,13 @@ define([ if (nc.show) { var command = translucent ? nc.translucentCommand : nc.command; command = silhouette ? nc.silhouetteModelCommand : command; - frameState.addCommand(command); + commandList.push(command); boundingVolume = nc.command.boundingVolume; if (frameState.mode === SceneMode.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { var command2D = translucent ? nc.translucentCommand2D : nc.command2D; command2D = silhouette ? nc.silhouetteModelCommand2D : command2D; - frameState.addCommand(command2D); + commandList.push(command2D); } } } @@ -4693,11 +4694,11 @@ define([ for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { - frameState.addCommand(nc.silhouetteColorCommand); + commandList.push(nc.silhouetteColorCommand); boundingVolume = nc.command.boundingVolume; if (frameState.mode === SceneMode.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { - frameState.addCommand(nc.silhouetteColorCommand2D); + commandList.push(nc.silhouetteColorCommand2D); } } } @@ -4709,12 +4710,12 @@ define([ nc = nodeCommands[i]; if (nc.show) { var pickCommand = nc.pickCommand; - frameState.addCommand(pickCommand); + commandList.push(pickCommand); boundingVolume = pickCommand.boundingVolume; if (frameState.mode === SceneMode.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { - frameState.addCommand(nc.pickCommand2D); + commandList.push(nc.pickCommand2D); } } } diff --git a/Source/Scene/ModelInstanceCollection.js b/Source/Scene/ModelInstanceCollection.js index 0a500b1d71c2..bc20e8898a49 100644 --- a/Source/Scene/ModelInstanceCollection.js +++ b/Source/Scene/ModelInstanceCollection.js @@ -975,11 +975,12 @@ define([ updateShowBoundingVolume(this); var passes = frameState.passes; + var commandList = frameState.commandList; var commands = passes.render ? this._drawCommands : this._pickCommands; var commandsLength = commands.length; for (var i = 0; i < commandsLength; ++i) { - frameState.addCommand(commands[i]); + commandList.push(commands[i]); } }; diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index db138636504f..08ea0d6dfb37 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -1217,12 +1217,14 @@ define([ this.batchTable.update(tileset, frameState); } + var commandList = frameState.commandList; + var passes = frameState.passes; if (passes.render) { - frameState.addCommand(this._drawCommand); + commandList.push(this._drawCommand); } if (passes.pick) { - frameState.addCommand(this._pickCommand); + commandList.push(this._pickCommand); } };