diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index c78d5143eb07..a2ad587e8f00 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -200,7 +200,6 @@ define([ */ this.oceanNormalMapUrl = buildModuleUrl('Assets/Textures/waterNormalsSmall.jpg'); this._oceanNormalMapUrl = undefined; - this._oceanNormalMapChanged = false; /** * True if primitives such as billboards, polylines, labels, etc. should be depth-tested @@ -273,7 +272,6 @@ define([ this._oceanNormalMap = undefined; this._zoomedOutOceanSpecularIntensity = 0.5; - this._hasWaterMask = false; this._hasVertexNormals = false; this._lightingFadeDistance = new Cartesian2(this.lightingFadeOutDistance, this.lightingFadeInDistance); @@ -845,19 +843,22 @@ define([ var oceanNormalMapUrl = this.oceanNormalMapUrl; this._oceanNormalMapUrl = oceanNormalMapUrl; - var that = this; - when(loadImage(oceanNormalMapUrl), function(image) { - if (oceanNormalMapUrl !== that.oceanNormalMapUrl) { - // url changed while we were loading - return; - } + if (defined(oceanNormalMapUrl)) { + var that = this; + when(loadImage(oceanNormalMapUrl), function(image) { + if (oceanNormalMapUrl !== that.oceanNormalMapUrl) { + // url changed while we were loading + return; + } - that._oceanNormalMap = that._oceanNormalMap && that._oceanNormalMap.destroy(); - that._oceanNormalMap = context.createTexture2D({ - source : image + that._oceanNormalMap = that._oceanNormalMap && that._oceanNormalMap.destroy(); + that._oceanNormalMap = context.createTexture2D({ + source : image + }); }); - that._oceanNormalMapChanged = true; - }); + } else { + this._oceanNormalMap = this._oceanNormalMap && this._oceanNormalMap.destroy(); + } } // Initial compile or re-compile if uber-shader parameters changed @@ -867,8 +868,6 @@ define([ if (!defined(northPoleCommand.shaderProgram) || !defined(southPoleCommand.shaderProgram) || modeChanged || - this._oceanNormalMapChanged || - this._hasWaterMask !== hasWaterMask || this._hasVertexNormals !== hasVertexNormals || this._enableLighting !== enableLighting) { @@ -909,14 +908,6 @@ define([ var shaderDefines = []; - if (hasWaterMask) { - shaderDefines.push('SHOW_REFLECTIVE_OCEAN'); - - if (defined(this._oceanNormalMap)) { - shaderDefines.push('SHOW_OCEAN_WAVES'); - } - } - if (enableLighting) { if (hasVertexNormals) { shaderDefines.push('ENABLE_VERTEX_LIGHTING'); @@ -951,10 +942,8 @@ define([ northPoleCommand.shaderProgram = poleShaderProgram; southPoleCommand.shaderProgram = poleShaderProgram; - this._hasWaterMask = hasWaterMask; this._hasVertexNormals = hasVertexNormals; this._enableLighting = enableLighting; - this._oceanNormalMapChanged = false; } this._occluder.cameraPosition = frameState.camera.positionWC; @@ -988,6 +977,7 @@ define([ tileProvider.lightingFadeOutDistance = this.lightingFadeOutDistance; tileProvider.lightingFadeInDistance = this.lightingFadeInDistance; tileProvider.zoomedOutOceanSpecularIntensity = this._zoomedOutOceanSpecularIntensity; + tileProvider.hasWaterMask = hasWaterMask; tileProvider.oceanNormalMap = this._oceanNormalMap; surface.update(context, frameState, commandList); diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index ca5a91742bb6..58cb3df3424c 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -33,44 +33,17 @@ define([ this._shaders = {}; }; - function getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha) { - var key = ''; - key += textureCount; - - if (applyBrightness) { - key += '_brightness'; - } - - if (applyContrast) { - key += '_contrast'; - } - - if (applyHue) { - key += '_hue'; - } - - if (applySaturation) { - key += '_saturation'; - } - - if (applyGamma) { - key += '_gamma'; - } - - if (applyAlpha) { - key += '_alpha'; - } - - return key; + function getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { + return '' + textureCount + (+applyBrightness) + (+applyContrast) + (+applyHue) + (+applySaturation) + (+applyGamma) + (+applyAlpha) + (+showReflectiveOcean) + (+showOceanWaves); } - GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha) { - var key = getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha); + GlobeSurfaceShaderSet.prototype.getShaderProgram = function(context, textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves) { + var key = getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves); var shader = this._shaders[key]; if (!defined(shader)) { - var vs = this.baseVertexShaderSource; - + var vs = this.baseVertexShaderSource.clone(); var fs = this.baseFragmentShaderSource.clone(); + fs.defines.push('TEXTURE_UNITS ' + textureCount); if (applyBrightness) { @@ -91,6 +64,13 @@ define([ if (applyAlpha) { fs.defines.push('APPLY_ALPHA'); } + if (showReflectiveOcean) { + fs.defines.push('SHOW_REFLECTIVE_OCEAN'); + vs.defines.push('SHOW_REFLECTIVE_OCEAN'); + } + if (showOceanWaves) { + fs.defines.push('SHOW_OCEAN_WAVES'); + } var computeDayColor = '\ vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 0cd864d9bc7a..eebaaf43238a 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -448,20 +448,7 @@ define([ // If there's a water mask included in the terrain data, create a // texture for it. - var waterMask = surfaceTile.terrainData.waterMask; - if (defined(waterMask)) { - if (defined(surfaceTile.waterMaskTexture)) { - --surfaceTile.waterMaskTexture.referenceCount; - if (surfaceTile.waterMaskTexture.referenceCount === 0) { - surfaceTile.waterMaskTexture.destroy(); - } - } - surfaceTile.waterMaskTexture = createWaterMaskTexture(context, waterMask); - surfaceTile.waterMaskTranslationAndScale.x = 0.0; - surfaceTile.waterMaskTranslationAndScale.y = 0.0; - surfaceTile.waterMaskTranslationAndScale.z = 1.0; - surfaceTile.waterMaskTranslationAndScale.w = 1.0; - } + createWaterMaskTextureIfNeeded(context, surfaceTile); propagateNewLoadedDataToChildren(tile); } @@ -622,7 +609,6 @@ define([ } function isDataAvailable(tile, terrainProvider) { - if (defined(terrainProvider.getTileDataAvailable)) { var tileDataAvailable = terrainProvider.getTileDataAvailable(tile.x, tile.y, tile.level); if (defined(tileDataAvailable)) { @@ -647,81 +633,90 @@ define([ return parent.data.terrainData.isChildAvailable(parent.x, parent.y, tile.x, tile.y); } - function createWaterMaskTexture(context, waterMask) { - var result; - - var waterMaskData = context.cache.tile_waterMaskData; - if (!defined(waterMaskData)) { - waterMaskData = context.cache.tile_waterMaskData = { - allWaterTexture : undefined, - allLandTexture : undefined, - sampler : undefined, - destroy : function() { - if (defined(this.allWaterTexture)) { - this.allWaterTexture.destroy(); - } - if (defined(this.allLandTexture)) { - this.allLandTexture.destroy(); - } - } + function getContextWaterMaskData(context) { + var data = context.cache.tile_waterMaskData; + + if (!defined(data)) { + var allWaterTexture = context.createTexture2D({ + pixelFormat : PixelFormat.LUMINANCE, + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + source : { + arrayBufferView : new Uint8Array([255]), + width : 1, + height : 1 + } + }); + allWaterTexture.referenceCount = 1; + + var sampler = context.createSampler({ + wrapS : TextureWrap.CLAMP_TO_EDGE, + wrapT : TextureWrap.CLAMP_TO_EDGE, + minificationFilter : TextureMinificationFilter.LINEAR, + magnificationFilter : TextureMagnificationFilter.LINEAR + }); + + data = { + allWaterTexture : allWaterTexture, + sampler : sampler, + destroy : function() { + this.allWaterTexture.destroy(); + } }; + + context.cache.tile_waterMaskData = data; } - var waterMaskSize = Math.sqrt(waterMask.length); - if (waterMaskSize === 1 && (waterMask[0] === 0 || waterMask[0] === 255)) { - // Tile is entirely land or entirely water. - if (!defined(waterMaskData.allWaterTexture)) { - waterMaskData.allWaterTexture = context.createTexture2D({ - pixelFormat : PixelFormat.LUMINANCE, - pixelDatatype : PixelDatatype.UNSIGNED_BYTE, - source : { - arrayBufferView : new Uint8Array([255]), - width : 1, - height : 1 - } - }); - waterMaskData.allWaterTexture.referenceCount = 1; - - waterMaskData.allLandTexture = context.createTexture2D({ - pixelFormat : PixelFormat.LUMINANCE, - pixelDatatype : PixelDatatype.UNSIGNED_BYTE, - source : { - arrayBufferView : new Uint8Array([0]), - width : 1, - height : 1 - } - }); - waterMaskData.allLandTexture.referenceCount = 1; + return data; + } + + function createWaterMaskTextureIfNeeded(context, surfaceTile) { + var previousTexture = surfaceTile.waterMaskTexture; + if (defined(previousTexture)) { + --previousTexture.referenceCount; + if (previousTexture.referenceCount === 0) { + previousTexture.destroy(); } + surfaceTile.waterMaskTexture = undefined; + } + + var waterMask = surfaceTile.terrainData.waterMask; + if (!defined(waterMask)) { + return; + } + + var waterMaskData = getContextWaterMaskData(context); + var texture; - result = waterMask[0] === 0 ? waterMaskData.allLandTexture : waterMaskData.allWaterTexture; + var waterMaskLength = waterMask.length; + if (waterMaskLength === 1) { + // Length 1 means the tile is entirely land or entirely water. + // A value of 0 indicates entirely land, a value of 1 indicates entirely water. + if (waterMask[0] !== 0) { + texture = waterMaskData.allWaterTexture; + } else { + // Leave the texture undefined if the tile is entirely land. + return; + } } else { - result = context.createTexture2D({ + var textureSize = Math.sqrt(waterMaskLength); + texture = context.createTexture2D({ pixelFormat : PixelFormat.LUMINANCE, pixelDatatype : PixelDatatype.UNSIGNED_BYTE, source : { - width : waterMaskSize, - height : waterMaskSize, + width : textureSize, + height : textureSize, arrayBufferView : waterMask } }); - result.referenceCount = 0; - - if (!defined(waterMaskData.sampler)) { - waterMaskData.sampler = context.createSampler({ - wrapS : TextureWrap.CLAMP_TO_EDGE, - wrapT : TextureWrap.CLAMP_TO_EDGE, - minificationFilter : TextureMinificationFilter.LINEAR, - magnificationFilter : TextureMagnificationFilter.LINEAR - }); - } - - result.sampler = waterMaskData.sampler; + texture.referenceCount = 0; + texture.sampler = waterMaskData.sampler; } - ++result.referenceCount; - return result; + ++texture.referenceCount; + surfaceTile.waterMaskTexture = texture; + + Cartesian4.fromElements(0.0, 0.0, 1.0, 1.0, surfaceTile.waterMaskTranslationAndScale); } function upsampleWaterMask(tile) { diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js index 97b19c48ef08..085ee9d0f4a1 100644 --- a/Source/Scene/GlobeSurfaceTileProvider.js +++ b/Source/Scene/GlobeSurfaceTileProvider.js @@ -92,6 +92,7 @@ define([ this.lightingFadeOutDistance = 6500000.0; this.lightingFadeInDistance = 9000000.0; + this.hasWaterMask = false; this.oceanNormalMap = undefined; this.zoomedOutOceanSpecularIntensity = 0.5; @@ -800,10 +801,16 @@ define([ var viewMatrix = frameState.camera.viewMatrix; var maxTextures = context.maximumTextureImageUnits; - if (defined(tileProvider.oceanNormalMap)) { + + var waterMaskTexture = surfaceTile.waterMaskTexture; + var showReflectiveOcean = tileProvider.hasWaterMask && defined(waterMaskTexture); + var oceanNormalMap = tileProvider.oceanNormalMap; + var showOceanWaves = showReflectiveOcean && defined(oceanNormalMap); + + if (showReflectiveOcean) { --maxTextures; } - if (defined(surfaceTile.waterMaskTexture)) { + if (showOceanWaves) { --maxTextures; } @@ -903,7 +910,7 @@ define([ command.debugShowBoundingVolume = (tile === tileProvider._debug.boundingSphereTile); Cartesian4.clone(initialColor, uniformMap.initialColor); - uniformMap.oceanNormalMap = tileProvider.oceanNormalMap; + uniformMap.oceanNormalMap = oceanNormalMap; uniformMap.lightingFadeDistance.x = tileProvider.lightingFadeOutDistance; uniformMap.lightingFadeDistance.y = tileProvider.lightingFadeInDistance; uniformMap.zoomedOutOceanSpecularIntensity = tileProvider.zoomedOutOceanSpecularIntensity; @@ -976,10 +983,10 @@ define([ // trim texture array to the used length so we don't end up using old textures // which might get destroyed eventually uniformMap.dayTextures.length = numberOfDayTextures; - uniformMap.waterMask = surfaceTile.waterMaskTexture; + uniformMap.waterMask = waterMaskTexture; Cartesian4.clone(surfaceTile.waterMaskTranslationAndScale, uniformMap.waterMaskTranslationAndScale); - command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha); + command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(context, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves); command.renderState = renderState; command.primitiveType = PrimitiveType.TRIANGLES; command.vertexArray = surfaceTile.vertexArray; diff --git a/Specs/Scene/GlobeSurfaceTileSpec.js b/Specs/Scene/GlobeSurfaceTileSpec.js index 3dbef6199d31..7ec781f17e68 100644 --- a/Specs/Scene/GlobeSurfaceTileSpec.js +++ b/Specs/Scene/GlobeSurfaceTileSpec.js @@ -49,31 +49,31 @@ defineSuite([ context = createContext(); alwaysDeferTerrainProvider = { - requestTileGeometry : function(x, y, level) { - return undefined; - }, - tilingScheme : tilingScheme, - hasWaterMask : function() { - return true; - }, - getTileDataAvailable : function(x, y, level) { - return undefined; - } + requestTileGeometry : function(x, y, level) { + return undefined; + }, + tilingScheme : tilingScheme, + hasWaterMask : function() { + return true; + }, + getTileDataAvailable : function(x, y, level) { + return undefined; + } }; alwaysFailTerrainProvider = { - requestTileGeometry : function(x, y, level) { - var deferred = when.defer(); - deferred.reject(); - return deferred.promise; - }, - tilingScheme : tilingScheme, - hasWaterMask : function() { - return true; - }, - getTileDataAvailable : function(x, y, level) { - return undefined; - } + requestTileGeometry : function(x, y, level) { + var deferred = when.defer(); + deferred.reject(); + return deferred.promise; + }, + tilingScheme : tilingScheme, + hasWaterMask : function() { + return true; + }, + getTileDataAvailable : function(x, y, level) { + return undefined; + } }; realTerrainProvider = new CesiumTerrainProvider({ @@ -285,9 +285,9 @@ defineSuite([ var referenceCount; runs(function() { - expect(childTile.data.waterMaskTexture).toBeDefined(); - childWaterMaskTexture = childTile.data.waterMaskTexture; - referenceCount = childWaterMaskTexture.referenceCount; + expect(childTile.data.waterMaskTexture).toBeDefined(); + childWaterMaskTexture = childTile.data.waterMaskTexture; + referenceCount = childWaterMaskTexture.referenceCount; }); waitsFor(function() { @@ -416,24 +416,24 @@ defineSuite([ it('uses shared water mask texture for tiles that are entirely water', function() { var allWaterTerrainProvider = { - requestTileGeometry : function(x, y, level) { - var real = realTerrainProvider.requestTileGeometry(x, y, level); - if (!defined(real)) { - return real; - } - - return when(real, function(terrainData) { - terrainData._waterMask = new Uint8Array([255]); - return terrainData; - }); - }, - tilingScheme : realTerrainProvider.tilingScheme, - hasWaterMask : function() { - return realTerrainProvider.hasWaterMask(); - }, - getTileDataAvailable : function(x, y, level) { - return undefined; + requestTileGeometry : function(x, y, level) { + var real = realTerrainProvider.requestTileGeometry(x, y, level); + if (!defined(real)) { + return real; } + + return when(real, function(terrainData) { + terrainData._waterMask = new Uint8Array([255]); + return terrainData; + }); + }, + tilingScheme : realTerrainProvider.tilingScheme, + hasWaterMask : function() { + return realTerrainProvider.hasWaterMask(); + }, + getTileDataAvailable : function(x, y, level) { + return undefined; + } }; waitsFor(function() { @@ -454,26 +454,26 @@ defineSuite([ }); }); - it('uses shared water mask texture for tiles that are entirely land', function() { + it('uses undefined water mask texture for tiles that are entirely land', function() { var allLandTerrainProvider = { - requestTileGeometry : function(x, y, level) { - var real = realTerrainProvider.requestTileGeometry(x, y, level); - if (!defined(real)) { - return real; - } - - return when(real, function(terrainData) { - terrainData._waterMask = new Uint8Array([0]); - return terrainData; - }); - }, - tilingScheme : realTerrainProvider.tilingScheme, - hasWaterMask : function() { - return realTerrainProvider.hasWaterMask(); - }, - getTileDataAvailable : function(x, y, level) { - return undefined; + requestTileGeometry : function(x, y, level) { + var real = realTerrainProvider.requestTileGeometry(x, y, level); + if (!defined(real)) { + return real; } + + return when(real, function(terrainData) { + terrainData._waterMask = new Uint8Array([0]); + return terrainData; + }); + }, + tilingScheme : realTerrainProvider.tilingScheme, + hasWaterMask : function() { + return realTerrainProvider.hasWaterMask(); + }, + getTileDataAvailable : function(x, y, level) { + return undefined; + } }; waitsFor(function() { @@ -489,8 +489,7 @@ defineSuite([ }, 'child tile to be ready'); runs(function() { - expect(childTile.data.waterMaskTexture).toBeDefined(); - expect(childTile.data.waterMaskTexture).toBe(rootTile.data.waterMaskTexture); + expect(childTile.data.waterMaskTexture).toBeUndefined(); }); }); }, 'WebGL');