From 9f58e9c859380927893490e914b284cb00ee6d8a Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Wed, 31 Aug 2016 10:21:30 -0400 Subject: [PATCH 01/37] Working on drawing highlighted models --- Apps/Sandcastle/gallery/3D Models.html | 18 ++- Source/DataSources/ModelGraphics.js | 11 +- Source/DataSources/ModelVisualizer.js | 5 + Source/Scene/Model.js | 147 ++++++++++++++++++++++++- 4 files changed, 176 insertions(+), 5 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index 577880df487c..4fd53b4a5fb2 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -33,6 +33,10 @@ shadows : true }); +var entity = null; + +var highlight = false; + function createModel(url, height) { viewer.entities.removeAll(); @@ -42,7 +46,7 @@ var roll = 0; var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll); - var entity = viewer.entities.add({ + entity = viewer.entities.add({ name : url, position : position, orientation : orientation, @@ -83,6 +87,18 @@ }]; Sandcastle.addToolbarMenu(options); + +Sandcastle.addToolbarButton('Toggle Highlight', function() { + highlight = !highlight; + entity.model.highlight = new Cesium.ConstantProperty(highlight); +}); + +Sandcastle.addToolbarButton('Change Highlight Color', function() { + entity.model.highlightColor = new Cesium.ConstantProperty(new Cesium.Cartesian4(Math.random(), Math.random(), Math.random(), 1.0)); +}); + + + //Sandcastle_End Sandcastle.finishedLoading(); } diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index e0b5ceb10794..c02e509102af 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -80,6 +80,8 @@ define([ this._nodeTransformationsSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; + this._highlight = undefined; + this._highlightColor = undefined; this._definitionChanged = new Event(); this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); @@ -200,7 +202,10 @@ define([ * @type {Property} * @default HeightReference.NONE */ - heightReference : createPropertyDescriptor('heightReference') + heightReference : createPropertyDescriptor('heightReference'), + + highlight: createPropertyDescriptor('highlight'), + highlightColor: createPropertyDescriptor('highlightColor') }); /** @@ -225,6 +230,8 @@ define([ result.runAnimations = this.runAnimations; result.nodeTransformations = this.nodeTransformations; result.heightReference = this._heightReference; + result.highlight = this.highlight; + result.highlightColor = this.highlightColor; return result; }; @@ -253,6 +260,8 @@ define([ this.uri = defaultValue(this.uri, source.uri); this.runAnimations = defaultValue(this.runAnimations, source.runAnimations); this.heightReference = defaultValue(this.heightReference, source.heightReference); + this.highlight = defaultValue(this.highlight, source.highlight); + this.highlightColor = defaultValue(this.highlightColor, source.highlightColor); var sourceNodeTransformations = source.nodeTransformations; if (defined(sourceNodeTransformations)) { diff --git a/Source/DataSources/ModelVisualizer.js b/Source/DataSources/ModelVisualizer.js index fde2fedcb7f8..f4d68a4d8a83 100644 --- a/Source/DataSources/ModelVisualizer.js +++ b/Source/DataSources/ModelVisualizer.js @@ -6,6 +6,7 @@ define([ '../Core/destroyObject', '../Core/DeveloperError', '../Core/Matrix4', + '../Core/Cartesian4', '../Scene/HeightReference', '../Scene/Model', '../Scene/ModelAnimationLoop', @@ -19,6 +20,7 @@ define([ destroyObject, DeveloperError, Matrix4, + Cartesian4, HeightReference, Model, ModelAnimationLoop, @@ -32,6 +34,7 @@ define([ var defaultIncrementallyLoadTextures = true; var defaultShadows = ShadowMode.ENABLED; var defaultHeightReference = HeightReference.NONE; + var defaultHighlightColor = new Cartesian4(1.0, 0.0, 0.0, 1.0); var modelMatrixScratch = new Matrix4(); var nodeMatrixScratch = new Matrix4(); @@ -147,6 +150,8 @@ define([ model.modelMatrix = Matrix4.clone(modelMatrix, model.modelMatrix); model.shadows = shadows; model.heightReference = Property.getValueOrDefault(modelGraphics._heightReference, time, defaultHeightReference); + model.highlight = Property.getValueOrDefault(modelGraphics.highlight, time, false); + model.highlightColor = Property.getValueOrDefault(modelGraphics.highlightColor, time, defaultHighlightColor); if (model.ready) { var runAnimations = Property.getValueOrDefault(modelGraphics._runAnimations, time, true); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 5644c433eb83..39582c3d95cc 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -399,6 +399,25 @@ define([ */ this.show = defaultValue(options.show, true); + /** + * Determines if the model will be highlighted. + * + * @type {Boolean} + * + * @default false + */ + this.highlight = defaultValue(options.highlight, false); + + /** + * The highlight color + * + * @type {Cartesian4} + * + * @default new Cartesian4(1.0, 0.0, 0.0, 1.0) + */ + this.highlightColor = defaultValue(options.highlightColor, new Cartesian4(1.0, 0.0, 0.0, 1.0)); + + /** * The 4x4 transformation matrix that transforms the model from model to world coordinates. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates. @@ -598,6 +617,7 @@ define([ vertexArrays : {}, programs : {}, pickPrograms : {}, + hilightPrograms: {}, textures : {}, samplers : {}, @@ -1801,6 +1821,37 @@ define([ attributeLocations : attributeLocations }); } + + // TODO: This is just the shader for the plane. Need to be more generic. + + var hilightVS = "precision highp float;\n" + + "attribute vec3 a_position;\n" + + "attribute vec3 a_normal;\n" + + "varying vec3 v_normal;\n" + + "uniform mat3 u_normalMatrix;\n" + + "uniform mat4 u_modelViewMatrix;\n" + + "uniform mat4 u_projectionMatrix;\n" + + "attribute vec2 a_texcoord0;\n" + + "varying vec2 v_texcoord0;\n" + + "void main(void) {\n" + + " vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);\n" + + " v_normal = u_normalMatrix * a_normal;\n" + + " pos.xyz += v_normal * 0.2;\n" + + " v_texcoord0 = a_texcoord0;\n" + + " gl_Position = u_projectionMatrix * pos;\n" + + "}\n"; + + var hilightFS = 'uniform vec4 u_highlightColor;\n' + + 'void main() \n' + + '{ \n' + + ' gl_FragColor = u_highlightColor;\n' + + '}'; + model._rendererResources.hilightPrograms[id] = ShaderProgram.fromCache({ + context : context, + vertexShaderSource : hilightVS, + fragmentShaderSource : hilightFS, + attributeLocations : attributeLocations + }); } function createPrograms(model, context) { @@ -2768,6 +2819,7 @@ define([ u.jointMatrixUniformName = jointMatrixUniformName; } } + } function scaleFromMatrix5Array(matrix) { @@ -2877,6 +2929,7 @@ define([ var rendererPrograms = resources.programs; var rendererPickPrograms = resources.pickPrograms; var rendererRenderStates = resources.renderStates; + var rendererHilightPrograms = resources.hilightPrograms; var uniformMaps = model._uniformMaps; var gltf = model.gltf; @@ -2959,6 +3012,27 @@ define([ var castShadows = ShadowMode.castShadows(model._shadows); var receiveShadows = ShadowMode.receiveShadows(model._shadows); + + // Setup the stencil for the first pass + var drawRS = clone(rs); + drawRS.stencilTest = { + enabled : true, + frontFunction : WebGLConstants.ALWAYS, + backFunction : WebGLConstants.ALWAYS, + reference : 1, + mask : ~0, + frontOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + }, + backOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + } + }; + drawRS = RenderState.fromCache(drawRS); var command = new DrawCommand({ boundingVolume : new BoundingSphere(), // updated in update() @@ -2972,11 +3046,59 @@ define([ castShadows : castShadows, receiveShadows : receiveShadows, uniformMap : uniformMap, - renderState : rs, + renderState : drawRS, owner : owner, pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE }); + // Setup the stencil command for the hilight + var hilightRS = clone(rs); + hilightRS.stencilTest = { + enabled : true, + frontFunction : WebGLConstants.NOTEQUAL, + backFunction : WebGLConstants.NOTEQUAL, + reference : 1, + mask : ~0, + frontOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + }, + backOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + } + }; + hilightRS.cull = { + enabled : true, + face : WebGLConstants.FRONT + }; + hilightRS.depthTest = false; + + hilightRS = RenderState.fromCache(hilightRS); + + // Setup the highlight color uniform. + uniformMap.u_highlightColor = function(){ + return model.highlightColor; + }; + + var hilightCommand = new DrawCommand({ + boundingVolume : new BoundingSphere(), // updated in update() + cull : model.cull, + modelMatrix : new Matrix4(), // computed in update() + primitiveType : primitive.mode, + vertexArray : vertexArray, + count : count, + offset : offset, + shaderProgram : rendererHilightPrograms[technique.program], + uniformMap : uniformMap, + renderState : hilightRS, + owner : owner, + pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE + }); + + var pickCommand; if (allowPicking) { @@ -3037,7 +3159,8 @@ define([ command : command, pickCommand : pickCommand, command2D : command2D, - pickCommand2D : pickCommand2D + pickCommand2D : pickCommand2D, + hilightCommand: hilightCommand }; runtimeNode.commands.push(nodeCommand); nodeCommands.push(nodeCommand); @@ -3234,6 +3357,12 @@ define([ BoundingSphere.clone(command.boundingVolume, pickCommand.boundingVolume); } + var hilightCommand = primitiveCommand.hilightCommand; + Matrix4.clone(command.modelMatrix, hilightCommand.modelMatrix); + //Matrix4.multiplyByUniformScale(hilightCommand.modelMatrix, 1.04, hilightCommand.modelMatrix); + BoundingSphere.clone(command.boundingVolume, hilightCommand.boundingVolume); + + // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it // will be clipped by the viewport. We create a second command that translates the model // model matrix to the opposite side of the map so the part that was clipped in one viewport @@ -3805,7 +3934,9 @@ define([ var idl2D = frameState.mapProjection.ellipsoid.maximumRadius * CesiumMath.PI; var boundingVolume; - if (passes.render) { + if (passes.render) { + + // The actual render commands for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { @@ -3819,6 +3950,16 @@ define([ } } } + + if (this.highlight) { + // Hilight commands second. + for (i = 0; i < length; ++i) { + nc = nodeCommands[i]; + if (nc.show) { + commandList.push(nc.hilightCommand); + } + } + } } if (passes.pick && this.allowPicking) { From de364e9daae9e91e976d54da984fbc5ce24387cd Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Wed, 31 Aug 2016 12:51:36 -0400 Subject: [PATCH 02/37] Changed demo to be able to modify the highlight size --- Apps/Sandcastle/gallery/3D Models.html | 47 +++++++++++++++++-- .../gallery/development/3D Models.html | 4 +- Source/DataSources/ModelGraphics.js | 6 ++- Source/DataSources/ModelVisualizer.js | 2 + Source/Scene/Model.js | 22 +++++++-- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index 4fd53b4a5fb2..6603c6f065ab 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -19,10 +19,31 @@

Loading...

-
+
+ + + + + + +
Highlight Size + + +
+
+ + + + + +
+

Loading...

+
+ + + + + + +
Highlight Size + + +
+
+ + + diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.jpg b/Apps/Sandcastle/gallery/development/3D Models Outline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..571d9866731e8ffdcfd69aad0b4944b0a76e964b GIT binary patch literal 11892 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 BD^>si literal 0 HcmV?d00001 From fa237332a9544292d83d772d5c524fb4a6ffa6a6 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Tue, 13 Sep 2016 11:10:04 -0400 Subject: [PATCH 11/37] Fix jshint issues --- Source/Scene/Model.js | 69 +++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 6d486de645a6..7929262265e9 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1853,26 +1853,32 @@ define([ // Get the projection matrix name. There is probably a better way to do this. var projectionMatrixUniformName = null; for (var techniqueName in model.gltf.techniques) { - var projectionMatrixParameterName = ""; - var technique = model.gltf.techniques[techniqueName]; - for (var parameterName in technique.parameters) { - var parameter = technique.parameters[parameterName]; - if (parameter.semantic === "PROJECTION") { - projectionMatrixParameterName = parameterName; - break; + if (model.gltf.techniques.hasOwnProperty(techniqueName)) { + var technique = model.gltf.techniques[techniqueName]; + var projectionMatrixParameterName = ""; + for (var parameterName in technique.parameters) { + if (technique.parameters.hasOwnProperty(parameterName)) { + var parameter = technique.parameters[parameterName]; + if (parameter.semantic === "PROJECTION") { + projectionMatrixParameterName = parameterName; + break; + } + } } - } - for (var uniformName in technique.uniforms) { - var paramName = technique.uniforms[uniformName]; - if (paramName == projectionMatrixParameterName) { - projectionMatrixUniformName = uniformName; - break; + for (var uniformName in technique.uniforms) { + if (technique.uniforms.hasOwnProperty(uniformName)) { + var paramName = technique.uniforms[uniformName]; + if (paramName === projectionMatrixParameterName) { + projectionMatrixUniformName = uniformName; + break; + } + } } - } - if (projectionMatrixUniformName) { - break; + if (projectionMatrixUniformName) { + break; + } } } @@ -2955,6 +2961,18 @@ define([ }; } + function createHighlightColorFunction(model) { + return function() { + return model.highlightColor; + }; + } + + function createHighlightSizeFunction(model) { + return function() { + return model.highlightSize; + }; + } + function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) { var nodeCommands = model._nodeCommands; var pickIds = model._pickIds; @@ -3115,14 +3133,9 @@ define([ highlightRS = RenderState.fromCache(highlightRS); - // Setup the highlight color uniform. - uniformMap.u_highlightColor = function(){ - return model.highlightColor; - }; - - uniformMap.u_highlightSize = function() { - return model.highlightSize; - }; + // Setup the highlight color and size uniforms. + uniformMap.u_highlightColor = createHighlightColorFunction(model); + uniformMap.u_highlightSize = createHighlightSizeFunction(model); var highlightCommand = new DrawCommand({ boundingVolume : new BoundingSphere(), // updated in update() @@ -3991,10 +4004,9 @@ define([ for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { - var command = nc.command; - commandList.push(command); + commandList.push(nc.command); - boundingVolume = command.boundingVolume; + boundingVolume = nc.command.boundingVolume; if (frameState.mode === SceneMode.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { commandList.push(nc.command2D); @@ -4008,8 +4020,7 @@ define([ nc = nodeCommands[i]; if (nc.show) { commandList.push(nc.highlightCommand); - - boundingVolume = command.boundingVolume; + boundingVolume = nc.command.boundingVolume; if (frameState.mode === SceneMode.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { commandList.push(nc.highlightCommand2D); From 751fbda10995f9295cba2139e4869b5b54b7ed12 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Tue, 13 Sep 2016 11:34:09 -0400 Subject: [PATCH 12/37] Added new getUniformNameForSemantic function --- Source/Scene/Model.js | 65 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 7929262265e9..dee58f2b6e10 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1799,6 +1799,40 @@ define([ return renamedVS + '\n' + highlightMain; } + function getUniformNameForSemantic(model, semantic) { + var semanticUniformName = null; + for (var techniqueName in model.gltf.techniques) { + if (model.gltf.techniques.hasOwnProperty(techniqueName)) { + var technique = model.gltf.techniques[techniqueName]; + var semanticParameterName = ""; + for (var parameterName in technique.parameters) { + if (technique.parameters.hasOwnProperty(parameterName)) { + var parameter = technique.parameters[parameterName]; + if (parameter.semantic === semantic) { + semanticParameterName = parameterName; + break; + } + } + } + + for (var uniformName in technique.uniforms) { + if (technique.uniforms.hasOwnProperty(uniformName)) { + var paramName = technique.uniforms[uniformName]; + if (paramName === semanticParameterName) { + semanticUniformName = uniformName; + break; + } + } + } + + if (semanticUniformName) { + break; + } + } + } + return semanticUniformName; + } + function createProgram(id, model, context) { var programs = model.gltf.programs; var shaders = model._loadResources.shaders; @@ -1851,36 +1885,7 @@ define([ } // Get the projection matrix name. There is probably a better way to do this. - var projectionMatrixUniformName = null; - for (var techniqueName in model.gltf.techniques) { - if (model.gltf.techniques.hasOwnProperty(techniqueName)) { - var technique = model.gltf.techniques[techniqueName]; - var projectionMatrixParameterName = ""; - for (var parameterName in technique.parameters) { - if (technique.parameters.hasOwnProperty(parameterName)) { - var parameter = technique.parameters[parameterName]; - if (parameter.semantic === "PROJECTION") { - projectionMatrixParameterName = parameterName; - break; - } - } - } - - for (var uniformName in technique.uniforms) { - if (technique.uniforms.hasOwnProperty(uniformName)) { - var paramName = technique.uniforms[uniformName]; - if (paramName === projectionMatrixParameterName) { - projectionMatrixUniformName = uniformName; - break; - } - } - } - - if (projectionMatrixUniformName) { - break; - } - } - } + var projectionMatrixUniformName = getUniformNameForSemantic(model, "PROJECTION"); var highlightVS = createHighlightVertexShaderSource(vs, projectionMatrixUniformName); From f41bb6e4490ee4bd8b20627c5b47960031e40eef Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Tue, 13 Sep 2016 12:04:49 -0400 Subject: [PATCH 13/37] Changed the highlightSize parameter to be in pixels instead of clip space size --- .../gallery/development/3D Models Outline.html | 4 ++-- Source/Scene/Model.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.html b/Apps/Sandcastle/gallery/development/3D Models Outline.html index 3c7778ade08b..cb9138fa284b 100644 --- a/Apps/Sandcastle/gallery/development/3D Models Outline.html +++ b/Apps/Sandcastle/gallery/development/3D Models Outline.html @@ -37,7 +37,7 @@ Highlight Size - + @@ -59,7 +59,7 @@ // The viewModel tracks the state of our mini application. var viewModel = { highlight: false, - highlightSize: 0.002 + highlightSize: 2 }; // Convert the viewModel members into knockout observables. diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index dee58f2b6e10..386d4c25dd88 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1782,7 +1782,7 @@ define([ return shader; } - function createHighlightVertexShaderSource(vertexShaderSource, projectionMatrixName) { + function createHighlightVertexShaderSource(vertexShaderSource, projectionMatrixName, viewportName) { var renamedVS = ShaderSource.replaceMain(vertexShaderSource, 'czm_old_main'); // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ var highlightMain = 'uniform float u_highlightSize;\n' + @@ -1793,7 +1793,7 @@ define([ ' n.x *= ' + projectionMatrixName + '[0][0];\n' + ' n.y *= ' + projectionMatrixName + '[1][1];\n' + ' vec4 clip = gl_Position;\n' + - ' clip.xy += n.xy * clip.w * u_highlightSize;\n' + + ' clip.xy += n.xy * clip.w * u_highlightSize / ' + viewportName + '.z * 2.0;\n' + ' gl_Position = clip;\n' + '}'; return renamedVS + '\n' + highlightMain; @@ -1886,8 +1886,15 @@ define([ // Get the projection matrix name. There is probably a better way to do this. var projectionMatrixUniformName = getUniformNameForSemantic(model, "PROJECTION"); + if (!projectionMatrixUniformName) { + projectionMatrixUniformName = "czm_projection"; + } + var viewportUniformName = getUniformNameForSemantic(model, "VIEWPORT"); + if (!viewportUniformName) { + viewportUniformName = "czm_viewport"; + } - var highlightVS = createHighlightVertexShaderSource(vs, projectionMatrixUniformName); + var highlightVS = createHighlightVertexShaderSource(vs, projectionMatrixUniformName, viewportUniformName); var highlightFS = 'uniform vec4 u_highlightColor;\n' + 'void main() \n' + From 36f046d76af636209e4f6e54c9e45a1f6d63fa82 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Tue, 13 Sep 2016 13:15:56 -0400 Subject: [PATCH 14/37] Simplified highlight shader just using cesium builtins --- Source/Scene/Model.js | 54 ++++--------------------------------------- 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 386d4c25dd88..88f7d496dd2c 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1782,7 +1782,7 @@ define([ return shader; } - function createHighlightVertexShaderSource(vertexShaderSource, projectionMatrixName, viewportName) { + function createHighlightVertexShaderSource(vertexShaderSource) { var renamedVS = ShaderSource.replaceMain(vertexShaderSource, 'czm_old_main'); // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ var highlightMain = 'uniform float u_highlightSize;\n' + @@ -1790,49 +1790,15 @@ define([ '{ \n' + ' czm_old_main(); \n' + ' vec3 n = normalize(v_normal);\n' + - ' n.x *= ' + projectionMatrixName + '[0][0];\n' + - ' n.y *= ' + projectionMatrixName + '[1][1];\n' + + ' n.x *= czm_projection[0][0];\n' + + ' n.y *= czm_projection[1][1];\n' + ' vec4 clip = gl_Position;\n' + - ' clip.xy += n.xy * clip.w * u_highlightSize / ' + viewportName + '.z * 2.0;\n' + + ' clip.xy += n.xy * clip.w * u_highlightSize / czm_viewport.z * 2.0;\n' + ' gl_Position = clip;\n' + '}'; return renamedVS + '\n' + highlightMain; } - function getUniformNameForSemantic(model, semantic) { - var semanticUniformName = null; - for (var techniqueName in model.gltf.techniques) { - if (model.gltf.techniques.hasOwnProperty(techniqueName)) { - var technique = model.gltf.techniques[techniqueName]; - var semanticParameterName = ""; - for (var parameterName in technique.parameters) { - if (technique.parameters.hasOwnProperty(parameterName)) { - var parameter = technique.parameters[parameterName]; - if (parameter.semantic === semantic) { - semanticParameterName = parameterName; - break; - } - } - } - - for (var uniformName in technique.uniforms) { - if (technique.uniforms.hasOwnProperty(uniformName)) { - var paramName = technique.uniforms[uniformName]; - if (paramName === semanticParameterName) { - semanticUniformName = uniformName; - break; - } - } - } - - if (semanticUniformName) { - break; - } - } - } - return semanticUniformName; - } - function createProgram(id, model, context) { var programs = model.gltf.programs; var shaders = model._loadResources.shaders; @@ -1884,17 +1850,7 @@ define([ }); } - // Get the projection matrix name. There is probably a better way to do this. - var projectionMatrixUniformName = getUniformNameForSemantic(model, "PROJECTION"); - if (!projectionMatrixUniformName) { - projectionMatrixUniformName = "czm_projection"; - } - var viewportUniformName = getUniformNameForSemantic(model, "VIEWPORT"); - if (!viewportUniformName) { - viewportUniformName = "czm_viewport"; - } - - var highlightVS = createHighlightVertexShaderSource(vs, projectionMatrixUniformName, viewportUniformName); + var highlightVS = createHighlightVertexShaderSource(vs); var highlightFS = 'uniform vec4 u_highlightColor;\n' + 'void main() \n' + From af86c9a0d461771b5b7c78a490b26dd08e08437e Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:12:02 -0400 Subject: [PATCH 15/37] Defaulting highlight to true in 3D Models Outline example. Changed highlightSize to 2.0 instead of 2 --- Apps/Sandcastle/gallery/development/3D Models Outline.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.html b/Apps/Sandcastle/gallery/development/3D Models Outline.html index cb9138fa284b..2f1151f2322e 100644 --- a/Apps/Sandcastle/gallery/development/3D Models Outline.html +++ b/Apps/Sandcastle/gallery/development/3D Models Outline.html @@ -58,8 +58,8 @@ // The viewModel tracks the state of our mini application. var viewModel = { - highlight: false, - highlightSize: 2 + highlight: true, + highlightSize: 2.0 }; // Convert the viewModel members into knockout observables. From 817da193e904faf96466b704291e6ebb23249c0a Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:17:42 -0400 Subject: [PATCH 16/37] Changed highlightColor default to default constructed color and highlightSize to 2.0 --- Source/DataSources/ModelGraphics.js | 10 +++++----- Source/DataSources/ModelVisualizer.js | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index 7f1ccd85749d..40c148fe6576 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -50,8 +50,8 @@ define([ * @param {Property} [options.shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from each light source. * @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @param {Property} [options.highlight=false] Whether to highlight the model using an outline - * @param {Property} [options.highlightColor=Color(1.0, 0.0, 0.0, 1.0)] The highlight color for the outline. - * @param {Property} [options.highlightSize=0.002] The highlight color for the outline. + * @param {Property} [options.highlightColor=new Color())] The highlight color for the outline. + * @param {Property} [options.highlightSize=2] The size of the highlight in pixels * @param {Property} [options.distanceDisplayCondition] A Property specifying at what distance from the camera that this model will be displayed. * * @see {@link http://cesiumjs.org/2014/03/03/Cesium-3D-Models-Tutorial/|3D Models Tutorial} @@ -199,15 +199,15 @@ define([ * Gets or sets the Color Property specifying the highlight color of this model. * @memberof ModelGraphics.prototype * @type {Property} - * @default Color(1.0,0.0,1.0,1.0) + * @default Color() */ highlightColor: createPropertyDescriptor('highlightColor'), /** - * Gets or sets the float Property specifying the size of the highlight of this model. + * Gets or sets the float Property specifying the size of the highlight of this model in pixels * @memberof ModelGraphics.prototype * @type {Property} - * @default 0.002 + * @default 2.0 */ highlightSize: createPropertyDescriptor('highlightSize'), diff --git a/Source/DataSources/ModelVisualizer.js b/Source/DataSources/ModelVisualizer.js index c63e91d8ce84..dac71ec9a48a 100644 --- a/Source/DataSources/ModelVisualizer.js +++ b/Source/DataSources/ModelVisualizer.js @@ -34,8 +34,8 @@ define([ var defaultIncrementallyLoadTextures = true; var defaultShadows = ShadowMode.ENABLED; var defaultHeightReference = HeightReference.NONE; - var defaultHighlightColor = new Color(1.0, 0.0, 0.0, 1.0); - var defaultHighlightSize = 0.002; + var defaultHighlightColor = new Color(); + var defaultHighlightSize = 2.0; var defaultHighlight = false; var modelMatrixScratch = new Matrix4(); From a492379eb4b27d60132aef7a0ad0a8c9c890960a Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:18:47 -0400 Subject: [PATCH 17/37] Changed hilightSize default to 2.0 in Model.js and highlightColor to default constructed Color --- Source/Scene/Model.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 32b666d7d8a3..24aef2f5ed34 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -412,21 +412,20 @@ define([ /** * The highlight color * - * @type {Cartesian4} + * @type {Color} * - * @default Color(1.0, 0.0, 0.0, 1.0) + * @default Color() */ - this.highlightColor = defaultValue(options.highlightColor, new Color(1.0, 0.0, 0.0, 1.0)); + this.highlightColor = defaultValue(options.highlightColor, new Color()); /** * The size of the highlight * * @type {Float} * - * @default 0.002 + * @default 2.0 */ - this.highlightSize = 0.002; - + this.highlightSize = 2.0; /** * The 4x4 transformation matrix that transforms the model from model to world coordinates. From 20a71493aca30c37d46249029f83893cbde80be5 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:25:10 -0400 Subject: [PATCH 18/37] Removed extra whitespace --- Source/Scene/Model.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 24aef2f5ed34..5ec798d8aabf 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1823,12 +1823,12 @@ define([ } var highlightVS = createHighlightVertexShaderSource(vs); - var highlightFS = 'uniform vec4 u_highlightColor;\n' + - 'void main() \n' + - '{ \n' + - ' gl_FragColor = u_highlightColor;\n' + - '}'; + 'void main() \n' + + '{ \n' + + ' gl_FragColor = u_highlightColor;\n' + + '}'; + model._rendererResources.highlightPrograms[id] = ShaderProgram.fromCache({ context : context, vertexShaderSource : highlightVS, From d98e0ea90a807fd5335e7ac262a1dd699b466e2b Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:46:55 -0400 Subject: [PATCH 19/37] Not setting face in highlightRS, just enabled: false --- Source/Scene/Model.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 5ec798d8aabf..492772a07d11 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3065,8 +3065,7 @@ define([ } }; highlightRS.cull = { - enabled : false, - face : WebGLConstants.FRONT + enabled : false }; highlightRS.depthTest = false; From aa62d4d61a1b0362c24b264c779a0efb31e13bb8 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:51:00 -0400 Subject: [PATCH 20/37] Whitespace --- Source/Scene/Model.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 492772a07d11..8150fc93218c 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3076,21 +3076,20 @@ define([ uniformMap.u_highlightSize = createHighlightSizeFunction(model); var highlightCommand = new DrawCommand({ - boundingVolume : new BoundingSphere(), // updated in update() - cull : model.cull, - modelMatrix : new Matrix4(), // computed in update() - primitiveType : primitive.mode, - vertexArray : vertexArray, - count : count, - offset : offset, - shaderProgram : rendererhighlightPrograms[technique.program], - uniformMap : uniformMap, - renderState : highlightRS, - owner : owner, - pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE + boundingVolume : new BoundingSphere(), // updated in update() + cull : model.cull, + modelMatrix : new Matrix4(), // computed in update() + primitiveType : primitive.mode, + vertexArray : vertexArray, + count : count, + offset : offset, + shaderProgram : rendererhighlightPrograms[technique.program], + uniformMap : uniformMap, + renderState : highlightRS, + owner : owner, + pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE }); - var pickCommand; if (allowPicking) { From c2a8ed17af4c70f93cecf558357fcaf216bdd4e6 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 12:53:42 -0400 Subject: [PATCH 21/37] Whitespace --- Source/Scene/Model.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 8150fc93218c..f6e2816e7a73 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3158,7 +3158,6 @@ define([ pickCommand2D : pickCommand2D, highlightCommand: highlightCommand, highlightCommand2D: highlightCommand2D - }; runtimeNode.commands.push(nodeCommand); nodeCommands.push(nodeCommand); @@ -3359,7 +3358,6 @@ define([ Matrix4.clone(command.modelMatrix, highlightCommand.modelMatrix); BoundingSphere.clone(command.boundingVolume, highlightCommand.boundingVolume); - // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it // will be clipped by the viewport. We create a second command that translates the model // model matrix to the opposite side of the map so the part that was clipped in one viewport From df36205e518c2dfde27c6568e24c2b06e958b04c Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 13:13:55 -0400 Subject: [PATCH 22/37] Enabling ALPHA_BLEND blending on highlight state --- Apps/Sandcastle/gallery/development/3D Models Outline.html | 2 +- Source/Scene/Model.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.html b/Apps/Sandcastle/gallery/development/3D Models Outline.html index 2f1151f2322e..26658bb3cc08 100644 --- a/Apps/Sandcastle/gallery/development/3D Models Outline.html +++ b/Apps/Sandcastle/gallery/development/3D Models Outline.html @@ -130,7 +130,7 @@ Sandcastle.addToolbarButton('Change Highlight Color', function() { entity.model.highlightColor = new Cesium.ConstantProperty(Cesium.Color.fromRandom({ - alpha : 1.0 + alpha : 0.3 })); }); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index f6e2816e7a73..99b3a9380e25 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -50,6 +50,7 @@ define([ '../ThirdParty/Uri', '../ThirdParty/when', './getBinaryAccessor', + './BlendingState', './HeightReference', './ModelAnimationCache', './ModelAnimationCollection', @@ -111,6 +112,7 @@ define([ Uri, when, getBinaryAccessor, + BlendingState, HeightReference, ModelAnimationCache, ModelAnimationCollection, @@ -3068,6 +3070,7 @@ define([ enabled : false }; highlightRS.depthTest = false; + highlightRS.blending = BlendingState.ALPHA_BLEND; highlightRS = RenderState.fromCache(highlightRS); From fa3c2e9d3b9b50474e6c92e749458766e211a2c3 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 13:31:47 -0400 Subject: [PATCH 23/37] Added opacity slider to 3D Models Outline demo. Outline.html --- .../development/3D Models Outline.html | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.html b/Apps/Sandcastle/gallery/development/3D Models Outline.html index 26658bb3cc08..268ba40d1fc4 100644 --- a/Apps/Sandcastle/gallery/development/3D Models Outline.html +++ b/Apps/Sandcastle/gallery/development/3D Models Outline.html @@ -34,13 +34,21 @@

Loading...

- - + + + + + + +
Highlight Size
Size
Opacity + + +
@@ -59,7 +67,9 @@ // The viewModel tracks the state of our mini application. var viewModel = { highlight: true, - highlightSize: 2.0 + highlightSize: 2.0, + highlightOpacity: 100.0, + highlightColor: new Cesium.Color(1.0, 1.0, 1.0, 1.0) }; // Convert the viewModel members into knockout observables. @@ -88,7 +98,8 @@ minimumPixelSize : 128, maximumScale : 20000, highlight: viewModel.highlight, - highlightSize: viewModel.highlightSize + highlightSize: viewModel.highlightSize, + highlightColor: viewModel.highlightColor } }); viewer.trackedEntity = entity; @@ -129,9 +140,9 @@ }); Sandcastle.addToolbarButton('Change Highlight Color', function() { - entity.model.highlightColor = new Cesium.ConstantProperty(Cesium.Color.fromRandom({ - alpha : 0.3 - })); + viewModel.highlightColor = Cesium.Color.fromRandom({ + alpha : viewModel.highlightOpacity / 100.0 + }); }); Cesium.knockout.getObservable(viewModel, "highlightSize").subscribe( @@ -140,6 +151,18 @@ } ); +Cesium.knockout.getObservable(viewModel, "highlightOpacity").subscribe( + function(newValue) { + viewModel.highlightColor = Cesium.Color.fromAlpha(viewModel.highlightColor, viewModel.highlightOpacity / 100.0); + } +); + +Cesium.knockout.getObservable(viewModel, "highlightColor").subscribe( + function(newValue) { + entity.model.highlightColor = new Cesium.ConstantProperty(viewModel.highlightColor); + } +); + //Sandcastle_End From 1bcce58df666fe1033cce357425b26cc2c7dfdc8 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 14:52:01 -0400 Subject: [PATCH 24/37] Explicitly requesting a stencil buffer in the 3D Models Outline demo since highlighting depends on it --- .../Sandcastle/gallery/development/3D Models Outline.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.html b/Apps/Sandcastle/gallery/development/3D Models Outline.html index 268ba40d1fc4..e5320439f935 100644 --- a/Apps/Sandcastle/gallery/development/3D Models Outline.html +++ b/Apps/Sandcastle/gallery/development/3D Models Outline.html @@ -59,7 +59,13 @@ var viewer = new Cesium.Viewer('cesiumContainer', { infoBox : false, selectionIndicator : false, - shadows : true + shadows : true, + // Explicitly request a stencil buffer so highlighting will work. + contextOptions: { + webgl : { + stencil: true + } + } }); var entity = null; From fee0674d73f4d66c2e78133941a185bc770ddaea Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 15:21:54 -0400 Subject: [PATCH 25/37] Only rendering highlight commands if stencilBits is greater than 0, otherwise logging a warning --- Source/Scene/Model.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 99b3a9380e25..c60c3db735f8 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3983,18 +3983,25 @@ define([ } if (this.highlight) { - // highlight commands second. - for (i = 0; i < length; ++i) { - nc = nodeCommands[i]; - if (nc.show) { - commandList.push(nc.highlightCommand); - boundingVolume = nc.command.boundingVolume; - if (frameState.mode === SceneMode.SCENE2D && - (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { - commandList.push(nc.highlightCommand2D); + + // Only render the highlight commands if we have sufficient stencil bits. + if (context.stencilBits > 0) { + // highlight commands second. + for (i = 0; i < length; ++i) { + nc = nodeCommands[i]; + if (nc.show) { + commandList.push(nc.highlightCommand); + boundingVolume = nc.command.boundingVolume; + if (frameState.mode === SceneMode.SCENE2D && + (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { + commandList.push(nc.highlightCommand2D); + } } } } + else { + console.log("Model highlighting not supported, stencilBits = " + context.stencilBits + ". Request a stencil buffer when initializing the Viewer"); + } } } From 508b14df5068b028d5698254f8158ea93e3fa9c7 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 15:44:45 -0400 Subject: [PATCH 26/37] ModelGraphicSpec tests for model highlighting --- Specs/DataSources/ModelGraphicsSpec.js | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Specs/DataSources/ModelGraphicsSpec.js b/Specs/DataSources/ModelGraphicsSpec.js index c2c18f2965df..4b52a32866ef 100644 --- a/Specs/DataSources/ModelGraphicsSpec.js +++ b/Specs/DataSources/ModelGraphicsSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'DataSources/ModelGraphics', 'Core/Cartesian3', + 'Core/Color', 'Core/DistanceDisplayCondition', 'Core/JulianDate', 'Core/Quaternion', @@ -12,6 +13,7 @@ defineSuite([ ], function( ModelGraphics, Cartesian3, + Color, DistanceDisplayCondition, JulianDate, Quaternion, @@ -32,6 +34,9 @@ defineSuite([ runAnimations : false, shadows : ShadowMode.DISABLED, distanceDisplayCondition : new DistanceDisplayCondition(), + highlight: false, + highlightSize: 3.0, + highlightColor: new Color(1.0, 0.0, 0.0, 1.0), nodeTransformations : { node1 : { translation : Cartesian3.UNIT_Y, @@ -50,6 +55,9 @@ defineSuite([ expect(model.incrementallyLoadTextures).toBeInstanceOf(ConstantProperty); expect(model.shadows).toBeInstanceOf(ConstantProperty); expect(model.runAnimations).toBeInstanceOf(ConstantProperty); + expect(model.highlight).toBeInstanceOf(ConstantProperty); + expect(model.highlightSize).toBeInstanceOf(ConstantProperty); + expect(model.highlightColor).toBeInstanceOf(ConstantProperty); expect(model.nodeTransformations).toBeInstanceOf(PropertyBag); @@ -62,6 +70,9 @@ defineSuite([ expect(model.shadows.getValue()).toEqual(options.shadows); expect(model.distanceDisplayCondition.getValue()).toEqual(options.distanceDisplayCondition); expect(model.runAnimations.getValue()).toEqual(options.runAnimations); + expect(model.highlight.getValue()).toEqual(options.highlight); + expect(model.highlightSize.getValue()).toEqual(options.highlightSize); + expect(model.highlightColor.getValue()).toEqual(options.highlightColor); var actualNodeTransformations = model.nodeTransformations.getValue(new JulianDate()); var expectedNodeTransformations = options.nodeTransformations; @@ -81,6 +92,9 @@ defineSuite([ source.maximumScale = new ConstantProperty(200.0); source.incrementallyLoadTextures = new ConstantProperty(true); source.shadows = new ConstantProperty(ShadowMode.ENABLED); + source.highlight = new ConstantProperty(true); + source.highlightSize = new ConstantProperty(3.0); + source.highlightColor = new ConstantProperty(new Color(1.0, 0.0, 0.0, 1.0)); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); source.runAnimations = new ConstantProperty(true); source.nodeTransformations = { @@ -107,6 +121,9 @@ defineSuite([ expect(target.distanceDisplayCondition).toBe(source.distanceDisplayCondition); expect(target.runAnimations).toBe(source.runAnimations); expect(target.nodeTransformations).toEqual(source.nodeTransformations); + expect(target.highlight).toEqual(source.highlight); + expect(target.highlightSize).toEqual(source.highlightSize); + expect(target.highlightColor).toEqual(source.highlightColor); }); it('merge does not assign assigned properties', function() { @@ -123,6 +140,9 @@ defineSuite([ source.nodeTransformations = { transform : new NodeTransformationProperty() }; + source.highlight = new ConstantProperty(true); + source.highlightSize = new ConstantProperty(1.0); + source.highlightColor = new ConstantProperty(new Color()); var uri = new ConstantProperty(''); var show = new ConstantProperty(true); @@ -136,6 +156,9 @@ defineSuite([ var nodeTransformations = new PropertyBag({ transform : new NodeTransformationProperty() }); + var highlight = new ConstantProperty(true); + var highlightSize = new ConstantProperty(1.0); + var highlightColor = new ConstantProperty(new Color()); var target = new ModelGraphics(); target.uri = uri; @@ -148,6 +171,9 @@ defineSuite([ target.distanceDisplayCondition = distanceDisplayCondition; target.runAnimations = runAnimations; target.nodeTransformations = nodeTransformations; + target.highlight = highlight; + target.highlightSize = highlightSize; + target.highlightColor = highlightColor; target.merge(source); @@ -161,6 +187,9 @@ defineSuite([ expect(target.distanceDisplayCondition).toBe(distanceDisplayCondition); expect(target.runAnimations).toBe(runAnimations); expect(target.nodeTransformations).toBe(nodeTransformations); + expect(target.highlight).toBe(highlight); + expect(target.highlightSize).toBe(highlightSize); + expect(target.highlightColor).toBe(highlightColor); }); it('clone works', function() { @@ -178,6 +207,9 @@ defineSuite([ node1 : new NodeTransformationProperty(), node2 : new NodeTransformationProperty() }; + source.highlight = new ConstantProperty(true); + source.highlightSize = new ConstantProperty(2.0); + source.highlightColor = new ConstantProperty(new Color()); var result = source.clone(); expect(result.uri).toBe(source.uri); @@ -190,6 +222,9 @@ defineSuite([ expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition); expect(result.runAnimations).toBe(source.runAnimations); expect(result.nodeTransformations).toEqual(source.nodeTransformations); + expect(result.highlight).toEqual(source.highlight); + expect(result.highlightSize).toEqual(source.highlightSize); + expect(result.highlightColor).toEqual(source.highlightColor); }); it('merge throws if source undefined', function() { From 1c3337196905d94c5ae677c2e506422c965a0a77 Mon Sep 17 00:00:00 2001 From: Jason Beverage Date: Mon, 17 Oct 2016 15:52:53 -0400 Subject: [PATCH 27/37] Model testing --- Specs/Scene/ModelSpec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 2699f497616c..58886a31d35e 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -10,6 +10,7 @@ defineSuite([ 'Core/defaultValue', 'Core/defined', 'Core/defineProperties', + 'Core/Color', 'Core/DistanceDisplayCondition', 'Core/Ellipsoid', 'Core/Event', @@ -42,6 +43,7 @@ defineSuite([ defaultValue, defined, defineProperties, + Color, DistanceDisplayCondition, Ellipsoid, Event, @@ -233,6 +235,10 @@ defineSuite([ expect(texturedBoxModel.debugShowBoundingVolume).toEqual(false); expect(texturedBoxModel.debugWireframe).toEqual(false); expect(texturedBoxModel.distanceDisplayCondition).toBeUndefined(); + expect(texturedBoxModel.highlight).toEqual(false); + expect(texturedBoxModel.highlightSize).toEqual(2.0); + expect(texturedBoxModel.highlightColor).toEqual(new Color()); + }); it('renders', function() { @@ -1986,7 +1992,7 @@ defineSuite([ scene.renderForSpecs(); expect(scene.globe.removedCallback).toEqual(true); expect(scene.globe.callback).not.toBeDefined(); - + primitives.remove(model); }); }); From 5f93bf13640ce8d781633281bb4a86f5ccd95dcc Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 6 Dec 2016 17:35:55 -0500 Subject: [PATCH 28/37] Reorganized silhouetting --- .../gallery/3D Models Coloring.html | 64 ++- .../development/3D Models Outline.html | 184 -------- .../gallery/development/3D Models Outline.jpg | Bin 11892 -> 0 bytes Source/DataSources/CzmlDataSource.js | 2 + Source/DataSources/ModelGraphics.js | 46 +- Source/DataSources/ModelVisualizer.js | 10 +- Source/Renderer/Context.js | 13 + Source/Scene/ColorBlendMode.js | 3 + Source/Scene/GroundPrimitive.js | 2 +- Source/Scene/Model.js | 420 +++++++++++------- Specs/DataSources/CzmlDataSourceSpec.js | 16 +- Specs/DataSources/ModelGraphicsSpec.js | 55 +-- Specs/Scene/ModelSpec.js | 2 - 13 files changed, 388 insertions(+), 429 deletions(-) delete mode 100644 Apps/Sandcastle/gallery/development/3D Models Outline.html delete mode 100644 Apps/Sandcastle/gallery/development/3D Models Outline.jpg diff --git a/Apps/Sandcastle/gallery/3D Models Coloring.html b/Apps/Sandcastle/gallery/3D Models Coloring.html index e67783ba0e80..979c9a6f955a 100644 --- a/Apps/Sandcastle/gallery/3D Models Coloring.html +++ b/Apps/Sandcastle/gallery/3D Models Coloring.html @@ -29,11 +29,15 @@ padding-top: 2px; padding-bottom: 2px; } + #toolbar .header { + font-weight: bold; + }

Loading...

+ @@ -56,6 +60,25 @@ + + + + + + + + + + + + +
Model Color
Mode
Model Silhouette
Color
Alpha + + +
Size + + +
- - - - - -
-

Loading...

-
- - - - - - - - - - - -
Size - - -
Opacity - - -
-
- - - diff --git a/Apps/Sandcastle/gallery/development/3D Models Outline.jpg b/Apps/Sandcastle/gallery/development/3D Models Outline.jpg deleted file mode 100644 index 571d9866731e8ffdcfd69aad0b4944b0a76e964b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11892 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 BD^>si diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index 02b3b7ca4bd5..db63ab5830ef 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -1417,6 +1417,8 @@ define([ processPacketData(Boolean, model, 'runAnimations', modelData.runAnimations, interval, sourceUri, entityCollection); processPacketData(ShadowMode, model, 'shadows', modelData.shadows, interval, sourceUri, entityCollection); processPacketData(HeightReference, model, 'heightReference', modelData.heightReference, interval, sourceUri, entityCollection); + processPacketData(Color, model, 'silhouetteColor', modelData.silhouetteColor, interval, sourceUri, entityCollection); + processPacketData(Number, model, 'silhouetteSize', modelData.silhouetteSize, interval, sourceUri, entityCollection); processPacketData(Color, model, 'color', modelData.color, interval, sourceUri, entityCollection); processPacketData(ColorBlendMode, model, 'colorBlendMode', modelData.colorBlendMode, interval, sourceUri, entityCollection); processPacketData(Number, model, 'colorBlendAmount', modelData.colorBlendAmount, interval, sourceUri, entityCollection); diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index 4a520173fdff..766d60064855 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -49,10 +49,9 @@ define([ * @param {Property} [options.nodeTransformations] An object, where keys are names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node. * @param {Property} [options.shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from each light source. * @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to. - * @param {Property} [options.highlight=false] Whether to highlight the model using an outline - * @param {Property} [options.highlightColor=new Color())] The highlight color for the outline. - * @param {Property} [options.highlightSize=2] The size of the highlight in pixels * @param {Property} [options.distanceDisplayCondition] A Property specifying at what distance from the camera that this model will be displayed. + * @param {Property} [options.silhouetteColor=Color.RED] A Property specifying the {@link Color} of the silhouette. + * @param {Property} [options.silhouetteSize=0.0] A numeric Property specifying the size of the silhouette in pixels. * @param {Property} [options.color=Color.WHITE] A Property specifying the {@link Color} that blends with the model's rendered color. * @param {Property} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] An enum Property specifying how the color blends with the model. * @param {Property} [options.colorBlendAmount=0.5] A numeric Property specifying the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. @@ -81,11 +80,12 @@ define([ this._nodeTransformationsSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; - this._highlight = undefined; - this._highlightColor = undefined; - this._highlightSize = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; + this._silhouetteColor = undefined; + this._silhouetteColorSubscription = undefined; + this._silhouetteSize = undefined; + this._silhouetteSizeSubscription = undefined; this._color = undefined; this._colorSubscription = undefined; this._colorBlendMode = undefined; @@ -197,35 +197,27 @@ define([ heightReference : createPropertyDescriptor('heightReference'), /** - * Gets or sets the boolean Property specifying whether this model should be highlighted or not. + * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this model will be displayed. * @memberof ModelGraphics.prototype * @type {Property} - * @default false */ - highlight: createPropertyDescriptor('highlight'), + distanceDisplayCondition : createPropertyDescriptor('distanceDisplayCondition'), /** - * Gets or sets the Color Property specifying the highlight color of this model. + * Gets or sets the Property specifying the {@link Color} of the silhouette. * @memberof ModelGraphics.prototype * @type {Property} - * @default Color() + * @default Color.RED */ - highlightColor: createPropertyDescriptor('highlightColor'), + silhouetteColor: createPropertyDescriptor('silhouetteColor'), /** - * Gets or sets the float Property specifying the size of the highlight of this model in pixels - * @memberof ModelGraphics.prototype - * @type {Property} - * @default 2.0 - */ - highlightSize: createPropertyDescriptor('highlightSize'), - - /* - * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this model will be displayed. + * Gets or sets the numeric Property specifying the size of the silhouette in pixels. * @memberof ModelGraphics.prototype * @type {Property} + * @default 0.0 */ - distanceDisplayCondition : createPropertyDescriptor('distanceDisplayCondition'), + silhouetteSize : createPropertyDescriptor('silhouetteSize'), /** * Gets or sets the Property specifying the {@link Color} that blends with the model's rendered color. @@ -274,10 +266,9 @@ define([ result.runAnimations = this.runAnimations; result.nodeTransformations = this.nodeTransformations; result.heightReference = this._heightReference; - result.highlight = this.highlight; - result.highlightColor = this.highlightColor; - result.highlightSize = this.highlightSize; result.distanceDisplayCondition = this.distanceDisplayCondition; + result.silhouetteColor = this.silhouetteColor; + result.silhouetteSize = this.silhouetteSize; result.color = this.color; result.colorBlendMode = this.colorBlendMode; result.colorBlendAmount = this.colorBlendAmount; @@ -307,10 +298,9 @@ define([ this.uri = defaultValue(this.uri, source.uri); this.runAnimations = defaultValue(this.runAnimations, source.runAnimations); this.heightReference = defaultValue(this.heightReference, source.heightReference); - this.highlight = defaultValue(this.highlight, source.highlight); - this.highlightColor = defaultValue(this.highlightColor, source.highlightColor); - this.highlightSize = defaultValue(this.highlightSize, source.highlightSize); this.distanceDisplayCondition = defaultValue(this.distanceDisplayCondition, source.distanceDisplayCondition); + this.silhouetteColor = defaultValue(this.silhouetteColor, source.silhouetteColor); + this.silhouetteSize = defaultValue(this.silhouetteSize, source.silhouetteSize); this.color = defaultValue(this.color, source.color); this.colorBlendMode = defaultValue(this.colorBlendMode, source.colorBlendMode); this.colorBlendAmount = defaultValue(this.colorBlendAmount, source.colorBlendAmount); diff --git a/Source/DataSources/ModelVisualizer.js b/Source/DataSources/ModelVisualizer.js index fae1b8086542..bd00c0ae9829 100644 --- a/Source/DataSources/ModelVisualizer.js +++ b/Source/DataSources/ModelVisualizer.js @@ -36,9 +36,8 @@ define([ var defaultIncrementallyLoadTextures = true; var defaultShadows = ShadowMode.ENABLED; var defaultHeightReference = HeightReference.NONE; - var defaultHighlightColor = new Color(); - var defaultHighlightSize = 2.0; - var defaultHighlight = false; + var defaultSilhouetteColor = Color.RED; + var defaultSilhouetteSize = 0.0; var defaultColor = Color.WHITE; var defaultColorBlendMode = ColorBlendMode.HIGHLIGHT; var defaultColorBlendAmount = 0.5; @@ -149,10 +148,9 @@ define([ model.modelMatrix = Matrix4.clone(modelMatrix, model.modelMatrix); model.shadows = Property.getValueOrDefault(modelGraphics._shadows, time, defaultShadows); model.heightReference = Property.getValueOrDefault(modelGraphics._heightReference, time, defaultHeightReference); - model.highlight = Property.getValueOrDefault(modelGraphics.highlight, time, defaultHighlight); - model.highlightColor = Property.getValueOrDefault(modelGraphics.highlightColor, time, defaultHighlightColor); - model.highlightSize = Property.getValueOrDefault(modelGraphics.highlightSize, time, defaultHighlightSize); model.distanceDisplayCondition = Property.getValueOrUndefined(modelGraphics._distanceDisplayCondition, time); + model.silhouetteColor = Property.getValueOrDefault(modelGraphics.silhouetteColor, time, defaultSilhouetteColor); + model.silhouetteSize = Property.getValueOrDefault(modelGraphics.silhouetteSize, time, defaultSilhouetteSize); model.color = Property.getValueOrDefault(modelGraphics._color, time, defaultColor, color); model.colorBlendMode = Property.getValueOrDefault(modelGraphics._colorBlendMode, time, defaultColorBlendMode); model.colorBlendAmount = Property.getValueOrDefault(modelGraphics._colorBlendAmount, time, defaultColorBlendAmount); diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index ca41b53f505f..2d889140979f 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -193,6 +193,7 @@ define([ // Override select WebGL defaults webglOptions.alpha = defaultValue(webglOptions.alpha, false); // WebGL default is true + webglOptions.stencil = defaultValue(webglOptions.stencil, true); // WebGL default is false var defaultToWebgl2 = false; var webgl2Supported = (typeof WebGL2RenderingContext !== 'undefined'); @@ -507,6 +508,18 @@ define([ } }, + /** + * true if the WebGL context supports stencil buffers. + * Stencil buffers are not supported by all systems. + * @memberof Context.prototype + * @type {Boolean} + */ + stencilBuffer : { + get : function() { + return this._stencilBits >= 8; + } + }, + /** * true if the WebGL context supports antialiasing. By default * antialiasing is requested, but it is not supported by all systems. diff --git a/Source/Scene/ColorBlendMode.js b/Source/Scene/ColorBlendMode.js index 224acbb5284e..c020c1dd1248 100644 --- a/Source/Scene/ColorBlendMode.js +++ b/Source/Scene/ColorBlendMode.js @@ -24,6 +24,9 @@ define([ MIX : 2 }; + /** + * @private + */ ColorBlendMode.getColorBlend = function(colorBlendMode, colorBlendAmount) { if (colorBlendMode === ColorBlendMode.HIGHLIGHT) { return 0.0; diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js index c35c4b1828c5..cfcea5ec94e3 100644 --- a/Source/Scene/GroundPrimitive.js +++ b/Source/Scene/GroundPrimitive.js @@ -396,7 +396,7 @@ define([ * @returns {Boolean} true if GroundPrimitives are supported; otherwise, returns false */ GroundPrimitive.isSupported = function(scene) { - return scene.context.fragmentDepth; + return scene.context.fragmentDepth && scene.context.stencilBuffer; }; GroundPrimitive._defaultMaxTerrainHeight = 9000.0; diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 2db592db4b6b..9966118b2ba1 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -324,10 +324,12 @@ define([ * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. - * @param {DistanceDisplayCondition} [options.istanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. + * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. + * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. + * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * * @exception {DeveloperError} bgltf is not a valid Binary glTF file. * @exception {DeveloperError} Only glTF Binary version 1 is supported. @@ -406,31 +408,23 @@ define([ this.show = defaultValue(options.show, true); /** - * Determines if the model will be highlighted. - * - * @type {Boolean} - * - * @default false - */ - this.highlight = defaultValue(options.highlight, false); - - /** - * The highlight color + * The silhouette color. * * @type {Color} * - * @default Color() + * @default Color.RED */ - this.highlightColor = defaultValue(options.highlightColor, new Color()); + this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED); + this._silhouetteColor = new Color(); /** - * The size of the highlight + * The size of the silhouette in pixels. * - * @type {Float} + * @type {Number} * - * @default 2.0 + * @default 0.0 */ - this.highlightSize = 2.0; + this.silhouetteSize = defaultValue(options.silhouetteSize, 0.0); /** * The 4x4 transformation matrix that transforms the model from model to world coordinates. @@ -558,6 +552,7 @@ define([ * @default Color.WHITE */ this.color = defaultValue(options.color, Color.WHITE); + this._color = new Color(); /** * Defines how the color blends with the model. @@ -658,7 +653,7 @@ define([ vertexArrays : {}, programs : {}, pickPrograms : {}, - highlightPrograms: {}, + silhouettePrograms: {}, textures : {}, samplers : {}, @@ -953,6 +948,20 @@ define([ var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + function silhouetteSupported(context) { + return context.stencilBuffer; + } + + /** + * Determines if silhouettes are supported. + * + * @param {Scene} scene The scene. + * @returns {Boolean} true if silhouettes are supported; otherwise, returns false + */ + Model.silhouetteSupported = function(scene) { + return silhouetteSupported(scene.context); + }; + /** * This function differs from the normal subarray function * because it takes offset and length, rather than begin and end. @@ -1819,23 +1828,6 @@ define([ return shader; } - function createHighlightVertexShaderSource(vertexShaderSource) { - var renamedVS = ShaderSource.replaceMain(vertexShaderSource, 'czm_old_main'); - // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ - var highlightMain = 'uniform float u_highlightSize;\n' + - 'void main() \n' + - '{ \n' + - ' czm_old_main(); \n' + - ' vec3 n = normalize(v_normal);\n' + - ' n.x *= czm_projection[0][0];\n' + - ' n.y *= czm_projection[1][1];\n' + - ' vec4 clip = gl_Position;\n' + - ' clip.xy += n.xy * clip.w * u_highlightSize / czm_viewport.z * 2.0;\n' + - ' gl_Position = clip;\n' + - '}'; - return renamedVS + '\n' + highlightMain; - } - function createProgram(id, model, context) { var programs = model.gltf.programs; var shaders = model._loadResources.shaders; @@ -1889,20 +1881,6 @@ define([ attributeLocations : attributeLocations }); } - - var highlightVS = createHighlightVertexShaderSource(vs); - var highlightFS = 'uniform vec4 u_highlightColor;\n' + - 'void main() \n' + - '{ \n' + - ' gl_FragColor = u_highlightColor;\n' + - '}'; - - model._rendererResources.highlightPrograms[id] = ShaderProgram.fromCache({ - context : context, - vertexShaderSource : highlightVS, - fragmentShaderSource : highlightFS, - attributeLocations : attributeLocations - }); } function createPrograms(model, context) { @@ -2880,7 +2858,6 @@ define([ u.jointMatrixUniformName = jointMatrixUniformName; } } - } function scaleFromMatrix5Array(matrix) { @@ -2979,15 +2956,15 @@ define([ }; } - function createHighlightColorFunction(model) { + function createSilhouetteColorFunction(model) { return function() { - return model.highlightColor; + return model.silhouetteColor; }; } - function createHighlightSizeFunction(model) { + function createSilhouetteSizeFunction(model) { return function() { - return model.highlightSize; + return model.silhouetteSize; }; } @@ -3014,7 +2991,6 @@ define([ var rendererPrograms = resources.programs; var rendererPickPrograms = resources.pickPrograms; var rendererRenderStates = resources.renderStates; - var rendererhighlightPrograms = resources.highlightPrograms; var uniformMaps = model._uniformMaps; var gltf = model.gltf; @@ -3102,27 +3078,6 @@ define([ var castShadows = ShadowMode.castShadows(model._shadows); var receiveShadows = ShadowMode.receiveShadows(model._shadows); - // Setup the stencil for the first pass - var drawRS = clone(rs); - drawRS.stencilTest = { - enabled : true, - frontFunction : WebGLConstants.ALWAYS, - backFunction : WebGLConstants.ALWAYS, - reference : 1, - mask : ~0, - frontOperation : { - fail : WebGLConstants.KEEP, - zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE - }, - backOperation : { - fail : WebGLConstants.KEEP, - zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE - } - }; - drawRS = RenderState.fromCache(drawRS); - var command = new DrawCommand({ boundingVolume : new BoundingSphere(), // updated in update() cull : model.cull, @@ -3135,53 +3090,7 @@ define([ castShadows : castShadows, receiveShadows : receiveShadows, uniformMap : uniformMap, - renderState : drawRS, - owner : owner, - pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE - }); - - // Setup the stencil command for the highlight - var highlightRS = clone(rs); - highlightRS.stencilTest = { - enabled : true, - frontFunction : WebGLConstants.NOTEQUAL, - backFunction : WebGLConstants.NOTEQUAL, - reference : 1, - mask : ~0, - frontOperation : { - fail : WebGLConstants.KEEP, - zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE - }, - backOperation : { - fail : WebGLConstants.KEEP, - zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE - } - }; - highlightRS.cull = { - enabled : false - }; - highlightRS.depthTest = false; - highlightRS.blending = BlendingState.ALPHA_BLEND; - - highlightRS = RenderState.fromCache(highlightRS); - - // Setup the highlight color and size uniforms. - uniformMap.u_highlightColor = createHighlightColorFunction(model); - uniformMap.u_highlightSize = createHighlightSizeFunction(model); - - var highlightCommand = new DrawCommand({ - boundingVolume : new BoundingSphere(), // updated in update() - cull : model.cull, - modelMatrix : new Matrix4(), // computed in update() - primitiveType : primitive.mode, - vertexArray : vertexArray, - count : count, - offset : offset, - shaderProgram : rendererhighlightPrograms[technique.program], - uniformMap : uniformMap, - renderState : highlightRS, + renderState : rs, owner : owner, pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE }); @@ -3228,21 +3137,16 @@ define([ var command2D; var pickCommand2D; - var highlightCommand2D; if (!scene3DOnly) { command2D = DrawCommand.shallowClone(command); - command2D.boundingVolume = new BoundingSphere(); - command2D.modelMatrix = new Matrix4(); + command2D.boundingVolume = new BoundingSphere(); // updated in update() + command2D.modelMatrix = new Matrix4(); // updated in update() if (allowPicking) { pickCommand2D = DrawCommand.shallowClone(pickCommand); - pickCommand2D.boundingVolume = new BoundingSphere(); - pickCommand2D.modelMatrix = new Matrix4(); + pickCommand2D.boundingVolume = new BoundingSphere(); // updated in update() + pickCommand2D.modelMatrix = new Matrix4(); // updated in update() } - - highlightCommand2D = DrawCommand.shallowClone(highlightCommand); - highlightCommand2D.boundingVolume = new BoundingSphere(); - highlightCommand2D.modelMatrix = new Matrix4(); } var nodeCommand = { @@ -3252,9 +3156,12 @@ define([ pickCommand : pickCommand, command2D : command2D, pickCommand2D : pickCommand2D, - highlightCommand: highlightCommand, - highlightCommand2D: highlightCommand2D, - // Generate on demand when color alpha is less than 1.0 + // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0 + silhouetteModelCommand : undefined, + silhouetteModelCommand2D : undefined, + silhouetteColorCommand : undefined, + silhouetteColorCommand2D : undefined, + // Generated on demand when color alpha is less than 1.0 translucentCommand : undefined, translucentCommand2D : undefined }; @@ -3353,6 +3260,7 @@ define([ resources.vertexArrays = cachedResources.vertexArrays; resources.programs = cachedResources.programs; resources.pickPrograms = cachedResources.pickPrograms; + resources.silhouettePrograms = cachedResources.silhouettePrograms; resources.textures = cachedResources.textures; resources.samplers = cachedResources.samplers; resources.renderStates = cachedResources.renderStates; @@ -3453,10 +3361,6 @@ define([ BoundingSphere.clone(command.boundingVolume, pickCommand.boundingVolume); } - var highlightCommand = primitiveCommand.highlightCommand; - Matrix4.clone(command.modelMatrix, highlightCommand.modelMatrix); - BoundingSphere.clone(command.boundingVolume, highlightCommand.boundingVolume); - // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it // will be clipped by the viewport. We create a second command that translates the model // model matrix to the opposite side of the map so the part that was clipped in one viewport @@ -3472,10 +3376,6 @@ define([ Matrix4.clone(command.modelMatrix, pickCommand2D.modelMatrix); BoundingSphere.clone(command.boundingVolume, pickCommand2D.boundingVolume); } - - var highlightCommand2D = primitiveCommand.highlightCommand2D; - Matrix4.clone(command.modelMatrix, highlightCommand2D.modelMatrix); - BoundingSphere.clone(command.boundingVolume, highlightCommand2D.boundingVolume); } } } @@ -3659,12 +3559,12 @@ define([ } function updateColor(model, frameState) { + // Generate translucent commands when the blend color has an alpha in the range (0.0, 1.0) exclusive var scene3DOnly = frameState.scene3DOnly; var alpha = model.color.alpha; if ((alpha > 0.0) && (alpha < 1.0)) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; - // Generate translucent commands when the blend color has an alpha less than 1.0 if (!defined(nodeCommands[0].translucentCommand)) { for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; @@ -3679,6 +3579,197 @@ define([ } } + function getProgramId(model, program) { + var programs = model._rendererResources.programs; + for (var id in programs) { + if (programs.hasOwnProperty(id)) { + if (programs[id] === program) { + return id; + } + } + } + } + + function createSilhouetteProgram(program, frameState) { + var vs = program.vertexShaderSource.sources[0]; + var attributeLocations = program._attributeLocations; + + // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ + vs = ShaderSource.replaceMain(vs, 'gltf_silhouette_main'); + vs += + 'uniform float gltf_silhouetteSize;\n' + + 'void main() \n' + + '{ \n' + + ' gltf_silhouette_main(); \n' + + ' vec3 n = normalize(v_normal);\n' + + ' n.x *= czm_projection[0][0];\n' + + ' n.y *= czm_projection[1][1];\n' + + ' vec4 clip = gl_Position;\n' + + ' clip.xy += n.xy * clip.w * gltf_silhouetteSize / czm_viewport.z * 2.0;\n' + + ' gl_Position = clip;\n' + + '}'; + + var fs = + 'uniform vec4 gltf_silhouetteColor;\n' + + 'void main() \n' + + '{ \n' + + ' gl_FragColor = gltf_silhouetteColor;\n' + + '}'; + + return ShaderProgram.fromCache({ + context : frameState.context, + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : attributeLocations + }); + } + + function hasSilhouette(model, frameState) { + return silhouetteSupported(frameState.context) && (model.silhouetteSize > 0.0) && (model.silhouetteColor.alpha > 0.0); + } + + function isTranslucent(model) { + return (model.color.alpha > 0.0) && (model.color.alpha < 1.0); + } + + function isInvisible(model) { + return (model.color.alpha === 0.0); + } + + function alphaDirty(currAlpha, prevAlpha) { + // Returns whether the alpha state has changed between invisible, translucent, or opaque + return (Math.floor(currAlpha) !== Math.floor(prevAlpha)) || (Math.ceil(currAlpha) !== Math.ceil(prevAlpha)); + } + + function updateSilhouette(model, frameState) { + // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0 + // There are two silhouette commands: + // 1. silhouetteModelCommand : render model normally while enabling stencil mask + // 2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests + if (!hasSilhouette(model, frameState)) { + return; + } + + var nodeCommands = model._nodeCommands; + var dirty = alphaDirty(model.color.alpha, model._color.alpha) || + alphaDirty(model.silhouetteColor.alpha, model._silhouetteColor.alpha) || + !defined(nodeCommands[0].silhouetteModelCommand); + + Color.clone(model.color, model._color); + Color.clone(model.silhouetteColor, model._silhouetteColor); + + if (!dirty) { + return; + } + + var silhouettePrograms = model._rendererResources.silhouettePrograms; + var scene3DOnly = frameState.scene3DOnly; + var length = nodeCommands.length; + for (var i = 0; i < length; ++i) { + var nodeCommand = nodeCommands[i]; + var command = nodeCommand.command; + + // Create model command + var modelCommand = isTranslucent(model) ? nodeCommand.translucentCommand : command; + var silhouetteModelCommand = DrawCommand.shallowClone(modelCommand); + var renderState = clone(modelCommand.renderState); + renderState.stencilTest = { + enabled : true, + frontFunction : WebGLConstants.ALWAYS, + backFunction : WebGLConstants.ALWAYS, + reference : 1, + mask : ~0, + frontOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + }, + backOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + } + }; + + if (isInvisible(model)) { + // Disable color and depth writes but still write into the stencil buffer + renderState.colorMask = { + red : false, + green : false, + blue : false, + alpha : false + }; + renderState.depthMask = false; + } + renderState = RenderState.fromCache(renderState); + silhouetteModelCommand.renderState = renderState; + nodeCommand.silhouetteModelCommand = silhouetteModelCommand; + + // Create color command + var silhouetteColorCommand = DrawCommand.shallowClone(command); + renderState = clone(command.renderState, true); + if (model.silhouetteColor.alpha < 1.0) { + silhouetteColorCommand.pass = Pass.TRANSLUCENT; + renderState.depthMask = false; + renderState.blending = BlendingState.ALPHA_BLEND; + } + renderState.depthTest.enabled = true; + renderState.cull.enabled = false; + renderState.stencilTest = { + enabled : true, + frontFunction : WebGLConstants.NOTEQUAL, + backFunction : WebGLConstants.NOTEQUAL, + reference : 1, + mask : ~0, + frontOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + }, + backOperation : { + fail : WebGLConstants.KEEP, + zFail : WebGLConstants.KEEP, + zPass : WebGLConstants.REPLACE + } + }; + renderState = RenderState.fromCache(renderState); + + // If the silhouette program has already been cached use it + var program = command.shaderProgram; + var id = getProgramId(model, program); + var silhouetteProgram = silhouettePrograms[id]; + if (!defined(silhouetteProgram)) { + silhouetteProgram = createSilhouetteProgram(program, frameState); + silhouettePrograms[id] = silhouetteProgram; + } + + var silhouetteUniformMap = combine(command.uniformMap, { + gltf_silhouetteColor : createSilhouetteColorFunction(model), + gltf_silhouetteSize : createSilhouetteSizeFunction(model) + }); + + silhouetteColorCommand.renderState = renderState; + silhouetteColorCommand.shaderProgram = silhouetteProgram; + silhouetteColorCommand.uniformMap = silhouetteUniformMap; + silhouetteColorCommand.castShadows = false; + silhouetteColorCommand.receiveShadows = false; + nodeCommand.silhouetteColorCommand = silhouetteColorCommand; + + if (!scene3DOnly) { + var command2D = nodeCommand.command2D; + var silhouetteModelCommand2D = DrawCommand.shallowClone(silhouetteModelCommand); + silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume; + silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix; + nodeCommand.silhouetteModelCommand2D = silhouetteModelCommand2D; + + var silhouetteColorCommand2D = DrawCommand.shallowClone(silhouetteColorCommand); + silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume; + silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix; + nodeCommand.silhouetteColorCommand2D = silhouetteColorCommand2D; + } + } + } + var scratchBoundingSphere = new BoundingSphere(); function scaleInPixels(positionWC, radius, frameState) { @@ -3758,6 +3849,7 @@ define([ this.vertexArrays = undefined; this.programs = undefined; this.pickPrograms = undefined; + this.silhouettePrograms = undefined; this.textures = undefined; this.samplers = undefined; this.renderStates = undefined; @@ -3781,6 +3873,7 @@ define([ destroy(resources.vertexArrays); destroy(resources.programs); destroy(resources.pickPrograms); + destroy(resources.silhouettePrograms); destroy(resources.textures); } @@ -4013,9 +4106,11 @@ define([ } } - var alpha = this.color.alpha; + var silhouette = hasSilhouette(this, frameState); + var translucent = isTranslucent(this); + var invisible = isInvisible(this); var displayConditionPassed = defined(this.distanceDisplayCondition) ? distanceDisplayConditionVisible(this, frameState) : true; - var show = this.show && displayConditionPassed && (this.scale !== 0.0) && (alpha > 0.0); + var show = this.show && displayConditionPassed && (this.scale !== 0.0) && (!invisible || silhouette); if ((show && this._state === ModelState.LOADED) || justLoaded) { var animated = this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty; @@ -4075,6 +4170,7 @@ define([ updateShowBoundingVolume(this); updateShadows(this); updateColor(this, frameState); + updateSilhouette(this, frameState); } if (justLoaded) { @@ -4103,43 +4199,35 @@ define([ var boundingVolume; if (passes.render) { - - // The actual render commands for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { - var translucent = (alpha < 1.0); var command = translucent ? nc.translucentCommand : nc.command; + command = silhouette ? nc.silhouetteModelCommand : 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; commandList.push(command2D); } } } - if (this.highlight) { - - // Only render the highlight commands if we have sufficient stencil bits. - if (context.stencilBits > 0) { - // highlight commands second. - for (i = 0; i < length; ++i) { - nc = nodeCommands[i]; - if (nc.show) { - commandList.push(nc.highlightCommand); - boundingVolume = nc.command.boundingVolume; - if (frameState.mode === SceneMode.SCENE2D && - (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) { - commandList.push(nc.highlightCommand2D); - } + if (silhouette) { + // Render second silhouette pass + for (i = 0; i < length; ++i) { + nc = nodeCommands[i]; + if (nc.show) { + 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)) { + commandList.push(nc.silhouetteColorCommand2D); } } } - else { - console.log("Model highlighting not supported, stencilBits = " + context.stencilBits + ". Request a stencil buffer when initializing the Viewer"); - } } } diff --git a/Specs/DataSources/CzmlDataSourceSpec.js b/Specs/DataSources/CzmlDataSourceSpec.js index b3be324ede7e..0c19ed092c3e 100644 --- a/Specs/DataSources/CzmlDataSourceSpec.js +++ b/Specs/DataSources/CzmlDataSourceSpec.js @@ -1839,7 +1839,11 @@ defineSuite([ gltf : './Data/Models/Box/CesiumBoxTest.gltf', incrementallyLoadTextures : true, shadows : 'ENABLED', - heightReference: 'CLAMP_TO_GROUND', + heightReference : 'CLAMP_TO_GROUND', + silhouetteColor : { + rgbaf : [1.0, 0.0, 0.0, 1.0] + }, + silhouetteSize : 2.0, color : { rgbaf : [0.0, 1.0, 0.0, 0.2] }, @@ -1874,6 +1878,8 @@ defineSuite([ expect(entity.model.incrementallyLoadTextures.getValue(Iso8601.MINIMUM_VALUE)).toEqual(true); expect(entity.model.shadows.getValue(Iso8601.MINIMUM_VALUE)).toEqual(ShadowMode.ENABLED); expect(entity.model.heightReference.getValue(Iso8601.MINIMUM_VALUE)).toEqual(HeightReference.CLAMP_TO_GROUND); + expect(entity.model.silhouetteColor.getValue(Iso8601.MINIMUM_VALUE)).toEqual(new Color(1.0, 0.0, 0.0, 1.0)); + expect(entity.model.silhouetteSize.getValue(Iso8601.MINIMUM_VALUE)).toEqual(2.0); expect(entity.model.color.getValue(Iso8601.MINIMUM_VALUE)).toEqual(new Color(0.0, 1.0, 0.0, 0.2)); expect(entity.model.colorBlendMode.getValue(Iso8601.MINIMUM_VALUE)).toEqual(ColorBlendMode.HIGHLIGHT); expect(entity.model.colorBlendAmount.getValue(Iso8601.MINIMUM_VALUE)).toEqual(0.5); @@ -1903,6 +1909,10 @@ defineSuite([ incrementallyLoadTextures : true, shadows : 'ENABLED', heightReference: 'CLAMP_TO_GROUND', + silhouetteColor : { + rgbaf : [1.0, 0.0, 0.0, 1.0] + }, + silhouetteSize : 2.0, color : { rgbaf : [0.0, 1.0, 0.0, 0.2] }, @@ -1941,6 +1951,8 @@ defineSuite([ expect(entity.model.incrementallyLoadTextures.getValue(validTime)).toEqual(true); expect(entity.model.shadows.getValue(validTime)).toEqual(ShadowMode.ENABLED); expect(entity.model.heightReference.getValue(validTime)).toEqual(HeightReference.CLAMP_TO_GROUND); + expect(entity.model.silhouetteColor.getValue(validTime)).toEqual(new Color(1.0, 0.0, 0.0, 1.0)); + expect(entity.model.silhouetteSize.getValue(validTime)).toEqual(2.0); expect(entity.model.color.getValue(validTime)).toEqual(new Color(0.0, 1.0, 0.0, 0.2)); expect(entity.model.colorBlendMode.getValue(validTime)).toEqual(ColorBlendMode.HIGHLIGHT); expect(entity.model.colorBlendAmount.getValue(validTime)).toEqual(0.5); @@ -1966,6 +1978,8 @@ defineSuite([ expect(entity.model.shadows.getValue(invalidTime)).toBeUndefined(); expect(entity.model.heightReference.getValue(invalidTime)).toBeUndefined(); expect(entity.model.color.getValue(invalidTime)).toBeUndefined(); + expect(entity.model.silhouetteColor.getValue(invalidTime)).toBeUndefined(); + expect(entity.model.silhouetteSize.getValue(invalidTime)).toBeUndefined(); expect(entity.model.colorBlendMode.getValue(invalidTime)).toBeUndefined(); expect(entity.model.colorBlendAmount.getValue(invalidTime)).toBeUndefined(); diff --git a/Specs/DataSources/ModelGraphicsSpec.js b/Specs/DataSources/ModelGraphicsSpec.js index a6d7bb074761..34fe22724987 100644 --- a/Specs/DataSources/ModelGraphicsSpec.js +++ b/Specs/DataSources/ModelGraphicsSpec.js @@ -39,9 +39,8 @@ defineSuite([ shadows : ShadowMode.DISABLED, heightReference : HeightReference.CLAMP_TO_GROUND, distanceDisplayCondition : new DistanceDisplayCondition(), - highlight: false, - highlightSize: 3.0, - highlightColor: new Color(1.0, 0.0, 0.0, 1.0), + silhouetteColor : new Color(1.0, 0.0, 0.0, 1.0), + silhouetteSize : 3.0, color : new Color(0.0, 1.0, 0.0, 0.2), colorBlendMode : ColorBlendMode.HIGHLIGHT, colorBlendAmount : 0.5, @@ -64,13 +63,12 @@ defineSuite([ expect(model.shadows).toBeInstanceOf(ConstantProperty); expect(model.heightReference).toBeInstanceOf(ConstantProperty); expect(model.distanceDisplayCondition).toBeInstanceOf(ConstantProperty); + expect(model.silhouetteColor).toBeInstanceOf(ConstantProperty); + expect(model.silhouetteSize).toBeInstanceOf(ConstantProperty); expect(model.color).toBeInstanceOf(ConstantProperty); expect(model.colorBlendMode).toBeInstanceOf(ConstantProperty); expect(model.colorBlendAmount).toBeInstanceOf(ConstantProperty); expect(model.runAnimations).toBeInstanceOf(ConstantProperty); - expect(model.highlight).toBeInstanceOf(ConstantProperty); - expect(model.highlightSize).toBeInstanceOf(ConstantProperty); - expect(model.highlightColor).toBeInstanceOf(ConstantProperty); expect(model.nodeTransformations).toBeInstanceOf(PropertyBag); @@ -83,13 +81,12 @@ defineSuite([ expect(model.shadows.getValue()).toEqual(options.shadows); expect(model.heightReference.getValue()).toEqual(options.heightReference); expect(model.distanceDisplayCondition.getValue()).toEqual(options.distanceDisplayCondition); + expect(model.silhouetteColor.getValue()).toEqual(options.silhouetteColor); + expect(model.silhouetteSize.getValue()).toEqual(options.silhouetteSize); expect(model.color.getValue()).toEqual(options.color); expect(model.colorBlendMode.getValue()).toEqual(options.colorBlendMode); expect(model.colorBlendAmount.getValue()).toEqual(options.colorBlendAmount); expect(model.runAnimations.getValue()).toEqual(options.runAnimations); - expect(model.highlight.getValue()).toEqual(options.highlight); - expect(model.highlightSize.getValue()).toEqual(options.highlightSize); - expect(model.highlightColor.getValue()).toEqual(options.highlightColor); var actualNodeTransformations = model.nodeTransformations.getValue(new JulianDate()); var expectedNodeTransformations = options.nodeTransformations; @@ -109,11 +106,10 @@ defineSuite([ source.maximumScale = new ConstantProperty(200.0); source.incrementallyLoadTextures = new ConstantProperty(true); source.shadows = new ConstantProperty(ShadowMode.ENABLED); - source.highlight = new ConstantProperty(true); - source.highlightSize = new ConstantProperty(3.0); - source.highlightColor = new ConstantProperty(new Color(1.0, 0.0, 0.0, 1.0)); source.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); + source.silhouetteColor = new ConstantProperty(new Color(1.0, 0.0, 0.0, 1.0)); + source.silhouetteSize = new ConstantProperty(3.0); source.color = new ConstantProperty(new Color(0.0, 1.0, 0.0, 0.2)); source.colorBlendMode = new ConstantProperty(ColorBlendMode.HIGHLIGHT); source.colorBlendAmount = new ConstantProperty(0.5); @@ -141,14 +137,13 @@ defineSuite([ expect(target.shadows).toBe(source.shadows); expect(target.heightReference).toBe(source.heightReference); expect(target.distanceDisplayCondition).toBe(source.distanceDisplayCondition); + expect(target.silhouetteColor).toEqual(source.silhouetteColor); + expect(target.silhouetteSize).toEqual(source.silhouetteSize); expect(target.color).toBe(source.color); expect(target.colorBlendMode).toBe(source.colorBlendMode); expect(target.colorBlendAmount).toBe(source.colorBlendAmount); expect(target.runAnimations).toBe(source.runAnimations); expect(target.nodeTransformations).toEqual(source.nodeTransformations); - expect(target.highlight).toEqual(source.highlight); - expect(target.highlightSize).toEqual(source.highlightSize); - expect(target.highlightColor).toEqual(source.highlightColor); }); it('merge does not assign assigned properties', function() { @@ -162,6 +157,8 @@ defineSuite([ source.shadows = new ConstantProperty(ShadowMode.ENABLED); source.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); + source.silhouetteColor = new ConstantProperty(new Color()); + source.silhouetteSize = new ConstantProperty(1.0); source.color = new ConstantProperty(new Color(0.0, 1.0, 0.0, 0.2)); source.colorBlendMode = new ConstantProperty(ColorBlendMode.HIGHLIGHT); source.colorBlendAmount = new ConstantProperty(0.5); @@ -169,9 +166,6 @@ defineSuite([ source.nodeTransformations = { transform : new NodeTransformationProperty() }; - source.highlight = new ConstantProperty(true); - source.highlightSize = new ConstantProperty(1.0); - source.highlightColor = new ConstantProperty(new Color()); var uri = new ConstantProperty(''); var show = new ConstantProperty(true); @@ -182,6 +176,8 @@ defineSuite([ var shadows = new ConstantProperty(ShadowMode.ENABLED); var heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); var distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); + var silhouetteColor = new ConstantProperty(new Color()); + var silhouetteSize = new ConstantProperty(1.0); var color = new ConstantProperty(new Color(0.0, 1.0, 0.0, 0.2)); var colorBlendMode = new ConstantProperty(ColorBlendMode.HIGHLIGHT); var colorBlendAmount = new ConstantProperty(0.5); @@ -189,9 +185,6 @@ defineSuite([ var nodeTransformations = new PropertyBag({ transform : new NodeTransformationProperty() }); - var highlight = new ConstantProperty(true); - var highlightSize = new ConstantProperty(1.0); - var highlightColor = new ConstantProperty(new Color()); var target = new ModelGraphics(); target.uri = uri; @@ -203,14 +196,13 @@ defineSuite([ target.shadows = shadows; target.heightReference = heightReference; target.distanceDisplayCondition = distanceDisplayCondition; + target.silhouetteColor = silhouetteColor; + target.silhouetteSize = silhouetteSize; target.color = color; target.colorBlendMode = colorBlendMode; target.colorBlendAmount = colorBlendAmount; target.runAnimations = runAnimations; target.nodeTransformations = nodeTransformations; - target.highlight = highlight; - target.highlightSize = highlightSize; - target.highlightColor = highlightColor; target.merge(source); @@ -223,14 +215,13 @@ defineSuite([ expect(target.shadows).toBe(shadows); expect(target.heightReference).toBe(heightReference); expect(target.distanceDisplayCondition).toBe(distanceDisplayCondition); + expect(target.silhouetteColor).toBe(silhouetteColor); + expect(target.silhouetteSize).toBe(silhouetteSize); expect(target.color).toBe(color); expect(target.colorBlendMode).toBe(colorBlendMode); expect(target.colorBlendAmount).toBe(colorBlendAmount); expect(target.runAnimations).toBe(runAnimations); expect(target.nodeTransformations).toBe(nodeTransformations); - expect(target.highlight).toBe(highlight); - expect(target.highlightSize).toBe(highlightSize); - expect(target.highlightColor).toBe(highlightColor); }); it('clone works', function() { @@ -244,6 +235,8 @@ defineSuite([ source.shadows = new ConstantProperty(ShadowMode.ENABLED); source.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); source.distanceDisplayCondition = new ConstantProperty(new DistanceDisplayCondition()); + source.silhouetteColor = new ConstantProperty(new Color()); + source.silhouetteSize = new ConstantProperty(2.0); source.color = new ConstantProperty(new Color(0.0, 1.0, 0.0, 0.2)); source.colorBlendMode = new ConstantProperty(ColorBlendMode.HIGHLIGHT); source.colorBlendAmount = new ConstantProperty(0.5); @@ -252,9 +245,6 @@ defineSuite([ node1 : new NodeTransformationProperty(), node2 : new NodeTransformationProperty() }; - source.highlight = new ConstantProperty(true); - source.highlightSize = new ConstantProperty(2.0); - source.highlightColor = new ConstantProperty(new Color()); var result = source.clone(); expect(result.uri).toBe(source.uri); @@ -266,14 +256,13 @@ defineSuite([ expect(result.shadows).toBe(source.shadows); expect(result.heightReference).toBe(source.heightReference); expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition); + expect(result.silhouetteColor).toEqual(source.silhouetteColor); + expect(result.silhouetteSize).toEqual(source.silhouetteSize); expect(result.color).toBe(source.color); expect(result.colorBlendMode).toBe(source.colorBlendMode); expect(result.colorBlendAmount).toBe(source.colorBlendAmount); expect(result.runAnimations).toBe(source.runAnimations); expect(result.nodeTransformations).toEqual(source.nodeTransformations); - expect(result.highlight).toEqual(source.highlight); - expect(result.highlightSize).toEqual(source.highlightSize); - expect(result.highlightColor).toEqual(source.highlightColor); }); it('merge throws if source undefined', function() { diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index fcff616909c0..b23a5699a2e3 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -10,7 +10,6 @@ defineSuite([ 'Core/combine', 'Core/defined', 'Core/defineProperties', - 'Core/Color', 'Core/DistanceDisplayCondition', 'Core/Ellipsoid', 'Core/Event', @@ -44,7 +43,6 @@ defineSuite([ combine, defined, defineProperties, - Color, DistanceDisplayCondition, Ellipsoid, Event, From 93d4e684b1a63c85e6e1f88a249e2813a3895669 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 7 Dec 2016 13:48:22 -0500 Subject: [PATCH 29/37] Prevent divide-by-zero during unpremultiply --- Source/Scene/Model.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 9966118b2ba1..5cd6e1be4730 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1807,8 +1807,17 @@ define([ ' gltf_blend_main(); \n'; // Un-premultiply the alpha so that blending is correct. + + // Avoid divide-by-zero. The code below is equivalent to: + // if (gl_FragColor.a > 0.0) + // { + // gl_FragColor.rgb /= gl_FragColor.a; + // } + if (premultipliedAlpha) { - shader += ' gl_FragColor.rgb /= gl_FragColor.a; \n'; + shader += + ' float alpha = 1.0 - ceil(gl_FragColor.a) + gl_FragColor.a; \n' + + ' gl_FragColor.rgb /= alpha; \n'; } shader += @@ -3601,7 +3610,7 @@ define([ 'void main() \n' + '{ \n' + ' gltf_silhouette_main(); \n' + - ' vec3 n = normalize(v_normal);\n' + + ' vec3 n = v_normal;\n' + ' n.x *= czm_projection[0][0];\n' + ' n.y *= czm_projection[1][1];\n' + ' vec4 clip = gl_Position;\n' + @@ -3692,7 +3701,7 @@ define([ }; if (isInvisible(model)) { - // Disable color and depth writes but still write into the stencil buffer + // When the model is invisible disable color and depth writes but still write into the stencil buffer renderState.colorMask = { red : false, green : false, From d7ae242b45b4b7a01dbf34916a5c8ebbd83e0955 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 7 Dec 2016 15:46:50 -0500 Subject: [PATCH 30/37] Issue clear command --- Source/Renderer/RenderState.js | 2 +- Source/Scene/Model.js | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js index 92aea5c35a45..9966cc98daff 100644 --- a/Source/Renderer/RenderState.js +++ b/Source/Renderer/RenderState.js @@ -589,7 +589,7 @@ define([ // Section 6.8 of the WebGL spec requires the reference and masks to be the same for // front- and back-face tests. This call prevents invalid operation errors when calling // stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this. - gl.stencilFunc(stencilTest.frontFunction, stencilTest.reference, stencilTest.mask); + gl.stencilFunc(frontFunction, reference, mask); gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask); gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 5cd6e1be4730..f54d44689ebb 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -35,6 +35,7 @@ define([ '../Core/Transforms', '../Renderer/Buffer', '../Renderer/BufferUsage', + '../Renderer/ClearCommand', '../Renderer/DrawCommand', '../Renderer/RenderState', '../Renderer/Sampler', @@ -97,6 +98,7 @@ define([ Transforms, Buffer, BufferUsage, + ClearCommand, DrawCommand, RenderState, Sampler, @@ -3606,23 +3608,23 @@ define([ // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ vs = ShaderSource.replaceMain(vs, 'gltf_silhouette_main'); vs += - 'uniform float gltf_silhouetteSize;\n' + + 'uniform float gltf_silhouetteSize; \n' + 'void main() \n' + '{ \n' + ' gltf_silhouette_main(); \n' + - ' vec3 n = v_normal;\n' + - ' n.x *= czm_projection[0][0];\n' + - ' n.y *= czm_projection[1][1];\n' + - ' vec4 clip = gl_Position;\n' + - ' clip.xy += n.xy * clip.w * gltf_silhouetteSize / czm_viewport.z * 2.0;\n' + - ' gl_Position = clip;\n' + + ' vec3 n = v_normal; \n' + + ' n.x *= czm_projection[0][0]; \n' + + ' n.y *= czm_projection[1][1]; \n' + + ' vec4 clip = gl_Position; \n' + + ' clip.xy += n.xy * clip.w * gltf_silhouetteSize / czm_viewport.z * 2.0; \n' + + ' gl_Position = clip; \n' + '}'; var fs = - 'uniform vec4 gltf_silhouetteColor;\n' + + 'uniform vec4 gltf_silhouetteColor; \n' + 'void main() \n' + '{ \n' + - ' gl_FragColor = gltf_silhouetteColor;\n' + + ' gl_FragColor = gltf_silhouetteColor; \n' + '}'; return ShaderProgram.fromCache({ @@ -3682,6 +3684,8 @@ define([ var modelCommand = isTranslucent(model) ? nodeCommand.translucentCommand : command; var silhouetteModelCommand = DrawCommand.shallowClone(modelCommand); var renderState = clone(modelCommand.renderState); + + // Write the value 1 into the stencil buffer renderState.stencilTest = { enabled : true, frontFunction : WebGLConstants.ALWAYS, @@ -3724,6 +3728,8 @@ define([ } renderState.depthTest.enabled = true; renderState.cull.enabled = false; + + // Only render if value of the stencil buffer is not 1. renderState.stencilTest = { enabled : true, frontFunction : WebGLConstants.NOTEQUAL, @@ -3733,12 +3739,12 @@ define([ frontOperation : { fail : WebGLConstants.KEEP, zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE + zPass : WebGLConstants.KEEP }, backOperation : { fail : WebGLConstants.KEEP, zFail : WebGLConstants.KEEP, - zPass : WebGLConstants.REPLACE + zPass : WebGLConstants.KEEP } }; renderState = RenderState.fromCache(renderState); @@ -4099,6 +4105,7 @@ define([ cachedResources.vertexArrays = resources.vertexArrays; cachedResources.programs = resources.programs; cachedResources.pickPrograms = resources.pickPrograms; + cachedResources.silhouettePrograms = resources.silhouettePrograms; cachedResources.textures = resources.textures; cachedResources.samplers = resources.samplers; cachedResources.renderStates = resources.renderStates; @@ -4238,6 +4245,10 @@ define([ } } } + + commandList.push(new ClearCommand({ + stencil : 0 + })); } if (passes.pick && this.allowPicking) { From 5e1e9cff682260033eddeae2a074e219e80097c8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 8 Dec 2016 14:14:50 -0500 Subject: [PATCH 31/37] More accurate translucencly check --- Source/Scene/Model.js | 88 +++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index f54d44689ebb..7af8f2d1995b 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3612,11 +3612,11 @@ define([ 'void main() \n' + '{ \n' + ' gltf_silhouette_main(); \n' + - ' vec3 n = v_normal; \n' + + ' vec3 n = normalize(v_normal); \n' + ' n.x *= czm_projection[0][0]; \n' + ' n.y *= czm_projection[1][1]; \n' + ' vec4 clip = gl_Position; \n' + - ' clip.xy += n.xy * clip.w * gltf_silhouetteSize / czm_viewport.z * 2.0; \n' + + ' clip.xy += n.xy * clip.w * gltf_silhouetteSize / czm_viewport.z; \n' + ' gl_Position = clip; \n' + '}'; @@ -3639,6 +3639,19 @@ define([ return silhouetteSupported(frameState.context) && (model.silhouetteSize > 0.0) && (model.silhouetteColor.alpha > 0.0); } + function hasTranslucentCommands(model) { + var nodeCommands = model._nodeCommands; + var length = nodeCommands.length; + for (var i = 0; i < length; ++i) { + var nodeCommand = nodeCommands[i]; + var command = nodeCommand.command; + if (command.renderState.blending.enabled) { + return true; + } + } + return false; + } + function isTranslucent(model) { return (model.color.alpha > 0.0) && (model.color.alpha < 1.0); } @@ -3652,29 +3665,16 @@ define([ return (Math.floor(currAlpha) !== Math.floor(prevAlpha)) || (Math.ceil(currAlpha) !== Math.ceil(prevAlpha)); } - function updateSilhouette(model, frameState) { - // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0 - // There are two silhouette commands: - // 1. silhouetteModelCommand : render model normally while enabling stencil mask - // 2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests - if (!hasSilhouette(model, frameState)) { - return; - } - - var nodeCommands = model._nodeCommands; - var dirty = alphaDirty(model.color.alpha, model._color.alpha) || - alphaDirty(model.silhouetteColor.alpha, model._silhouetteColor.alpha) || - !defined(nodeCommands[0].silhouetteModelCommand); - - Color.clone(model.color, model._color); - Color.clone(model.silhouetteColor, model._silhouetteColor); - - if (!dirty) { - return; - } + // TODO : alternatively increment silhouette length as part of frame state and edit the model render states on the fly + var silhouettesLength = 0; + function createSilhouetteCommands(model, frameState) { + // If the model is translucent the silhouette needs to be in the translucent pass. + // Otherwise the silhouette would be rendered before the model. + var silhouetteTranslucent = hasTranslucentCommands(model) || isTranslucent(model) || (model.silhouetteColor.alpha < 1.0); var silhouettePrograms = model._rendererResources.silhouettePrograms; var scene3DOnly = frameState.scene3DOnly; + var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; @@ -3685,12 +3685,16 @@ define([ var silhouetteModelCommand = DrawCommand.shallowClone(modelCommand); var renderState = clone(modelCommand.renderState); - // Write the value 1 into the stencil buffer + // Wrap around after exceeding the 8-bit stencil limit. + // The reference is unique to each model until this point. + var stencilReference = (++silhouettesLength) % 255; + + // Write the reference value into the stencil buffer. renderState.stencilTest = { enabled : true, frontFunction : WebGLConstants.ALWAYS, backFunction : WebGLConstants.ALWAYS, - reference : 1, + reference : stencilReference, mask : ~0, frontOperation : { fail : WebGLConstants.KEEP, @@ -3721,20 +3725,20 @@ define([ // Create color command var silhouetteColorCommand = DrawCommand.shallowClone(command); renderState = clone(command.renderState, true); - if (model.silhouetteColor.alpha < 1.0) { + renderState.depthTest.enabled = true; + renderState.cull.enabled = false; + if (silhouetteTranslucent) { silhouetteColorCommand.pass = Pass.TRANSLUCENT; renderState.depthMask = false; renderState.blending = BlendingState.ALPHA_BLEND; } - renderState.depthTest.enabled = true; - renderState.cull.enabled = false; - // Only render if value of the stencil buffer is not 1. + // Only render silhouette if the value in the stencil buffer equals the reference renderState.stencilTest = { enabled : true, frontFunction : WebGLConstants.NOTEQUAL, backFunction : WebGLConstants.NOTEQUAL, - reference : 1, + reference : stencilReference, mask : ~0, frontOperation : { fail : WebGLConstants.KEEP, @@ -3785,6 +3789,28 @@ define([ } } + function updateSilhouette(model, frameState) { + // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0 + // There are two silhouette commands: + // 1. silhouetteModelCommand : render model normally while enabling stencil mask + // 2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests + if (!hasSilhouette(model, frameState)) { + return; + } + + var nodeCommands = model._nodeCommands; + var dirty = alphaDirty(model.color.alpha, model._color.alpha) || + alphaDirty(model.silhouetteColor.alpha, model._silhouetteColor.alpha) || + !defined(nodeCommands[0].silhouetteModelCommand); + + Color.clone(model.color, model._color); + Color.clone(model.silhouetteColor, model._silhouetteColor); + + if (dirty) { + createSilhouetteCommands(model, frameState); + } + } + var scratchBoundingSphere = new BoundingSphere(); function scaleInPixels(positionWC, radius, frameState) { @@ -4245,10 +4271,6 @@ define([ } } } - - commandList.push(new ClearCommand({ - stencil : 0 - })); } if (passes.pick && this.allowPicking) { From 246dc8348a57612d6b94177ab1b13b88ea34e0c6 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 8 Dec 2016 14:33:43 -0500 Subject: [PATCH 32/37] Fix spec --- Specs/Scene/ModelSpec.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index b23a5699a2e3..5c484eee2b8d 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -235,9 +235,8 @@ defineSuite([ expect(texturedBoxModel.debugShowBoundingVolume).toEqual(false); expect(texturedBoxModel.debugWireframe).toEqual(false); expect(texturedBoxModel.distanceDisplayCondition).toBeUndefined(); - expect(texturedBoxModel.highlight).toEqual(false); - expect(texturedBoxModel.highlightSize).toEqual(2.0); - expect(texturedBoxModel.highlightColor).toEqual(new Color()); + expect(texturedBoxModel.silhouetteColor).toEqual(Color.RED); + expect(texturedBoxModel.silhouetteSize).toEqual(0.0); }); From 60caefac64ddd3e3b20f3fd5fea295d403f1cab9 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 8 Dec 2016 14:39:05 -0500 Subject: [PATCH 33/37] Change order of stencilReference --- Source/Scene/Model.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 7af8f2d1995b..d7cd0e4f73f5 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -35,7 +35,6 @@ define([ '../Core/Transforms', '../Renderer/Buffer', '../Renderer/BufferUsage', - '../Renderer/ClearCommand', '../Renderer/DrawCommand', '../Renderer/RenderState', '../Renderer/Sampler', @@ -98,7 +97,6 @@ define([ Transforms, Buffer, BufferUsage, - ClearCommand, DrawCommand, RenderState, Sampler, @@ -3669,6 +3667,10 @@ define([ var silhouettesLength = 0; function createSilhouetteCommands(model, frameState) { + // Wrap around after exceeding the 8-bit stencil limit. + // The reference is unique to each model until this point. + var stencilReference = (++silhouettesLength) % 255; + // If the model is translucent the silhouette needs to be in the translucent pass. // Otherwise the silhouette would be rendered before the model. var silhouetteTranslucent = hasTranslucentCommands(model) || isTranslucent(model) || (model.silhouetteColor.alpha < 1.0); @@ -3685,10 +3687,6 @@ define([ var silhouetteModelCommand = DrawCommand.shallowClone(modelCommand); var renderState = clone(modelCommand.renderState); - // Wrap around after exceeding the 8-bit stencil limit. - // The reference is unique to each model until this point. - var stencilReference = (++silhouettesLength) % 255; - // Write the reference value into the stencil buffer. renderState.stencilTest = { enabled : true, From 11fc0da3d6e73761e7e6c830097bcc14a6d2b9e2 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 12 Dec 2016 13:22:07 -0500 Subject: [PATCH 34/37] Doc and stencil clear after ground pass --- Source/Scene/Model.js | 3 +-- Source/Scene/Scene.js | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index d7cd0e4f73f5..a94e59ff3957 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -328,7 +328,7 @@ define([ * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. - * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. + * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * * @exception {DeveloperError} bgltf is not a valid Binary glTF file. @@ -3663,7 +3663,6 @@ define([ return (Math.floor(currAlpha) !== Math.floor(prevAlpha)) || (Math.ceil(currAlpha) !== Math.ceil(prevAlpha)); } - // TODO : alternatively increment silhouette length as part of frame state and edit the model render states on the fly var silhouettesLength = 0; function createSilhouetteCommands(model, frameState) { diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 439e70b1baef..e8e5840d0464 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -285,6 +285,9 @@ define([ depth : 1.0, owner : this }); + this._stencilClearCommand = new ClearCommand({ + stencil : 0 + }); this._pickDepths = []; this._debugGlobeDepths = []; @@ -1755,6 +1758,11 @@ define([ executeCommand(commands[j], scene, context, passState); } + // Clear the stencil after the ground pass + if (length > 0 && context.stencilBuffer) { + scene._stencilClearCommand.execute(context, passState); + } + if (clearGlobeDepth) { clearDepth.execute(context, passState); if (useDepthPlane) { From cb35414d9fc0f3cd6460acc3e6c1b25dd6032780 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 12 Dec 2016 14:09:24 -0500 Subject: [PATCH 35/37] No longer hardcoding v_normal --- Source/Scene/Model.js | 15 ++++++-- .../Scene/getAttributeOrUniformBySemantic.js | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 Source/Scene/getAttributeOrUniformBySemantic.js diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index a94e59ff3957..7fe5ccc1dd75 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -50,6 +50,7 @@ define([ '../ThirdParty/when', './BlendingState', './ColorBlendMode', + './getAttributeOrUniformBySemantic', './getBinaryAccessor', './HeightReference', './ModelAnimationCache', @@ -112,6 +113,7 @@ define([ when, BlendingState, ColorBlendMode, + getAttributeOrUniformBySemantic, getBinaryAccessor, HeightReference, ModelAnimationCache, @@ -416,6 +418,7 @@ define([ */ this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED); this._silhouetteColor = new Color(); + this._normalAttributeName = undefined; /** * The size of the silhouette in pixels. @@ -3599,9 +3602,10 @@ define([ } } - function createSilhouetteProgram(program, frameState) { + function createSilhouetteProgram(model, program, frameState) { var vs = program.vertexShaderSource.sources[0]; var attributeLocations = program._attributeLocations; + var normalAttributeName = model._normalAttributeName; // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ vs = ShaderSource.replaceMain(vs, 'gltf_silhouette_main'); @@ -3610,7 +3614,7 @@ define([ 'void main() \n' + '{ \n' + ' gltf_silhouette_main(); \n' + - ' vec3 n = normalize(v_normal); \n' + + ' vec3 n = normalize(czm_normal * ' + normalAttributeName + '); \n' + ' n.x *= czm_projection[0][0]; \n' + ' n.y *= czm_projection[1][1]; \n' + ' vec4 clip = gl_Position; \n' + @@ -3634,7 +3638,7 @@ define([ } function hasSilhouette(model, frameState) { - return silhouetteSupported(frameState.context) && (model.silhouetteSize > 0.0) && (model.silhouetteColor.alpha > 0.0); + return silhouetteSupported(frameState.context) && (model.silhouetteSize > 0.0) && (model.silhouetteColor.alpha > 0.0) && defined(model._normalAttributeName); } function hasTranslucentCommands(model) { @@ -3755,7 +3759,7 @@ define([ var id = getProgramId(model, program); var silhouetteProgram = silhouettePrograms[id]; if (!defined(silhouetteProgram)) { - silhouetteProgram = createSilhouetteProgram(program, frameState); + silhouetteProgram = createSilhouetteProgram(model, program, frameState); silhouettePrograms[id] = silhouetteProgram; } @@ -4134,6 +4138,9 @@ define([ cachedResources.renderStates = resources.renderStates; cachedResources.ready = true; + // The normal attribute name is required for silhouettes, so get it before the gltf JSON is released + this._normalAttributeName = getAttributeOrUniformBySemantic(this.gltf, 'NORMAL'); + // Vertex arrays are unique to this model, do not store in cache. if (defined(this._precreatedAttributes)) { cachedResources.vertexArrays = {}; diff --git a/Source/Scene/getAttributeOrUniformBySemantic.js b/Source/Scene/getAttributeOrUniformBySemantic.js new file mode 100644 index 000000000000..41520014e123 --- /dev/null +++ b/Source/Scene/getAttributeOrUniformBySemantic.js @@ -0,0 +1,38 @@ +/*global define*/ +define([], function() { + 'use strict'; + + /** + * Return the uniform or attribute that has the given semantic. + * + * @private + */ + function getAttributeOrUniformBySemantic(gltf, semantic) { + var techniques = gltf.techniques; + for (var techniqueName in techniques) { + if (techniques.hasOwnProperty(techniqueName)) { + var technique = techniques[techniqueName]; + var parameters = technique.parameters; + var attributes = technique.attributes; + var uniforms = technique.uniforms; + for (var attributeName in attributes) { + if (attributes.hasOwnProperty(attributeName)) { + if (parameters[attributes[attributeName]].semantic === semantic) { + return attributeName; + } + } + } + for (var uniformName in uniforms) { + if (uniforms.hasOwnProperty(uniformName)) { + if (parameters[uniforms[uniformName]].semantic === semantic) { + return uniformName; + } + } + } + } + } + return undefined; + } + + return getAttributeOrUniformBySemantic; +}); From 95d024e30c4ef08cec01be3db915aa91e6ae2680 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 12 Dec 2016 14:11:58 -0500 Subject: [PATCH 36/37] Updated CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index f9eeb43dd66d..57a5b2f871f5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ Change Log ### 1.29 - 2017-01-02 * Added the ability to blend a `Model` with a color/translucency. Added `color`, `colorBlendMode`, and `colorBlendAmount` properties to `Model`, `ModelGraphics`, and CZML. Added `ColorBlendMode` enum. [#4547](https://github.com/AnalyticalGraphicsInc/cesium/pull/4547) +* Added the ability to render a `Model` with a silhouette. Added `silhouetteColor` and `silhouetteSize` properties to `Model`, `ModelGraphics`, and CZML. [#4314](https://github.com/AnalyticalGraphicsInc/cesium/pull/4314) ### 1.28 - 2016-12-01 From 077e225ff1f9e50c1874fcbf1ab69bc7d2f4c095 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 12 Dec 2016 14:23:41 -0500 Subject: [PATCH 37/37] Added tests --- Source/Scene/Model.js | 4 +- Specs/Scene/ModelSpec.js | 130 ++++++++++++++++++++++++++++++++++++++- Specs/createScene.js | 1 + 3 files changed, 132 insertions(+), 3 deletions(-) diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 7fe5ccc1dd75..e620037690f2 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -3614,7 +3614,7 @@ define([ 'void main() \n' + '{ \n' + ' gltf_silhouette_main(); \n' + - ' vec3 n = normalize(czm_normal * ' + normalAttributeName + '); \n' + + ' vec3 n = normalize(czm_normal3D * ' + normalAttributeName + '); \n' + ' n.x *= czm_projection[0][0]; \n' + ' n.y *= czm_projection[1][1]; \n' + ' vec4 clip = gl_Position; \n' + @@ -3647,7 +3647,7 @@ define([ for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; var command = nodeCommand.command; - if (command.renderState.blending.enabled) { + if (command.pass === Pass.TRANSLUCENT) { return true; } } diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 5c484eee2b8d..5c56d9c4c468 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -28,6 +28,7 @@ defineSuite([ 'Renderer/WebGLConstants', 'Scene/ColorBlendMode', 'Scene/HeightReference', + 'Scene/Pass', 'Scene/ModelAnimationLoop', 'Specs/createScene', 'Specs/pollToPromise', @@ -61,6 +62,7 @@ defineSuite([ WebGLConstants, ColorBlendMode, HeightReference, + Pass, ModelAnimationLoop, createScene, pollToPromise, @@ -237,7 +239,9 @@ defineSuite([ expect(texturedBoxModel.distanceDisplayCondition).toBeUndefined(); expect(texturedBoxModel.silhouetteColor).toEqual(Color.RED); expect(texturedBoxModel.silhouetteSize).toEqual(0.0); - + expect(texturedBoxModel.color).toEqual(Color.WHITE); + expect(texturedBoxModel.colorBlendMode).toEqual(ColorBlendMode.HIGHLIGHT); + expect(texturedBoxModel.colorBlendAmount).toEqual(0.5); }); it('renders', function() { @@ -1934,6 +1938,130 @@ defineSuite([ }); }); + it('silhouetteSupported', function() { + expect(Model.silhouetteSupported(scene)).toBe(true); + scene.context._stencilBits = 0; + expect(Model.silhouetteSupported(scene)).toBe(false); + scene.context._stencilBits = 8; + }); + + it('renders with a silhouette', function() { + return loadModel(boxUrl).then(function(model) { + model.show = true; + model.zoomTo(); + + var commands = scene.frameState.commandList; + + // No silhouette + model.silhouetteSize = 0.0; + scene.renderForSpecs(); + expect(commands.length).toBe(1); + expect(commands[0].renderState.stencilTest.enabled).toBe(false); + expect(commands[0].pass).toBe(Pass.OPAQUE); + + // Opaque silhouette + model.silhouetteSize = 1.0; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); + + // Translucent silhouette + model.silhouetteColor = Color.fromAlpha(Color.GREEN, 0.5); + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + + // Invisible silhouette. The model is rendered normally. + model.silhouetteColor = Color.fromAlpha(Color.GREEN, 0.0); + scene.renderForSpecs(); + expect(commands.length).toBe(1); + expect(commands[0].renderState.stencilTest.enabled).toBe(false); + expect(commands[0].pass).toBe(Pass.OPAQUE); + + // Invisible model with no silhouette. No commands. + model.color = Color.fromAlpha(Color.WHITE, 0.0); + model.silhouetteColor = Color.GREEN; + model.silhouetteSize = 0.0; + scene.renderForSpecs(); + expect(commands.length).toBe(0); + + // Invisible model with silhouette. Model command is stencil-only. + model.silhouetteSize = 1.0; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.colorMask).toEqual({ + red : false, + green : false, + blue : false, + alpha : false + }); + expect(commands[0].renderState.depthMask).toEqual(false); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); + + // Translucent model with opaque silhouette. Silhouette is placed in the translucent pass. + model.color = Color.fromAlpha(Color.WHITE, 0.5); + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + + // Model with translucent commands with silhouette + model.color = Color.WHITE; + model._nodeCommands[0].command.pass = Pass.TRANSLUCENT; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + model._nodeCommands[0].command.pass = Pass.OPAQUE; // Revert change + + // Translucent model with translucent silhouette. + model.color = Color.fromAlpha(Color.WHITE, 0.5); + model.silhouetteColor = Color.fromAlpha(Color.GREEN, 0.5); + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + + model.color = Color.WHITE; + model.silhouetteColor = Color.GREEN; + + // Load a second model + return loadModel(boxUrl).then(function(model) { + model.show = true; + model.silhouetteSize = 1.0; + scene.renderForSpecs(); + expect(commands.length).toBe(4); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); + expect(commands[2].renderState.stencilTest.enabled).toBe(true); + expect(commands[2].pass).toBe(Pass.OPAQUE); + expect(commands[3].renderState.stencilTest.enabled).toBe(true); + expect(commands[3].pass).toBe(Pass.OPAQUE); + + var reference1 = commands[0].renderState.stencilTest.reference; + var reference2 = commands[2].renderState.stencilTest.reference; + expect(reference2).toEqual(reference1 + 1); + }); + }); + }); + describe('height referenced model', function() { function createMockGlobe() { var globe = { diff --git a/Specs/createScene.js b/Specs/createScene.js index bbb1d7e7009f..6b070ae6772a 100644 --- a/Specs/createScene.js +++ b/Specs/createScene.js @@ -32,6 +32,7 @@ define([ var contextOptions = options.contextOptions; contextOptions.webgl = defaultValue(contextOptions.webgl, {}); contextOptions.webgl.antialias = defaultValue(contextOptions.webgl.antialias, false); + contextOptions.webgl.stencil = defaultValue(contextOptions.webgl.stencil, true); var scene = new Scene(options);