diff --git a/CHANGES.md b/CHANGES.md index e67cb71e0711..7e22c39a0be0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Change Log * Fixed glTF support to handle meshes with and without tangent vectors, and with/without morph targets, sharing one material. [#6421](https://github.com/AnalyticalGraphicsInc/cesium/pull/6421) * Fixed glTF support to handle skinned meshes when no skin is supplied. [#6061](https://github.com/AnalyticalGraphicsInc/cesium/issues/6061) * Allow loadWithXhr to work with string URLs in a web worker. +* Fix Firefox WebGL console warnings. [#5912](https://github.com/AnalyticalGraphicsInc/cesium/issues/5912) ### 1.44 - 2018-04-02 diff --git a/Source/Core/PixelFormat.js b/Source/Core/PixelFormat.js index 5c01b6999909..d5225aee883b 100644 --- a/Source/Core/PixelFormat.js +++ b/Source/Core/PixelFormat.js @@ -147,9 +147,8 @@ define([ */ componentsLength : function(pixelFormat) { switch (pixelFormat) { - // Many GPUs store RGB as RGBA internally - // https://devtalk.nvidia.com/default/topic/699479/general-graphics-programming/rgb-auto-converted-to-rgba/post/4142379/#4142379 case PixelFormat.RGB: + return 3; case PixelFormat.RGBA: return 4; case PixelFormat.LUMINANCE_ALPHA: @@ -281,6 +280,46 @@ define([ componentsLength = 1; } return componentsLength * PixelDatatype.sizeInBytes(pixelDatatype) * width * height; + }, + + /** + * @private + */ + createTypedArray : function(pixelFormat, pixelDatatype, width, height) { + var constructor; + var sizeInBytes = PixelDatatype.sizeInBytes(pixelDatatype); + if (sizeInBytes === Uint8Array.BYTES_PER_ELEMENT) { + constructor = Uint8Array; + } else if (sizeInBytes === Uint16Array.BYTES_PER_ELEMENT) { + constructor = Uint16Array; + } else if (sizeInBytes === Float32Array.BYTES_PER_ELEMENT && pixelDatatype === PixelDatatype.FLOAT) { + constructor = Float32Array; + } else { + constructor = Uint32Array; + } + + var size = PixelFormat.componentsLength(pixelFormat) * width * height; + return new constructor(size); + }, + + /** + * @private + */ + flipY : function(bufferView, pixelFormat, pixelDatatype, width, height) { + if (height === 1) { + return bufferView; + } + var flipped = PixelFormat.createTypedArray(pixelFormat, pixelDatatype, width, height); + var numberOfComponents = PixelFormat.componentsLength(pixelFormat); + var textureWidth = width * numberOfComponents; + for (var i = 0; i < height; ++i) { + var row = i * height * numberOfComponents; + var flippedRow = (height - i - 1) * height * numberOfComponents; + for (var j = 0; j < textureWidth; ++j) { + flipped[flippedRow + j] = bufferView[row + j]; + } + } + return flipped; } }; diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index f3a1902328dd..367c90a246cc 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -720,7 +720,8 @@ define([ width : 1, height : 1, arrayBufferView : new Uint8Array([255, 255, 255, 255]) - } + }, + flipY : false }); } @@ -753,7 +754,8 @@ define([ negativeY : face, positiveZ : face, negativeZ : face - } + }, + flipY : false }); } diff --git a/Source/Renderer/CubeMap.js b/Source/Renderer/CubeMap.js index e08eccd1c718..b5523bc9f803 100644 --- a/Source/Renderer/CubeMap.js +++ b/Source/Renderer/CubeMap.js @@ -33,7 +33,6 @@ define([ 'use strict'; function CubeMap(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); @@ -121,25 +120,34 @@ define([ gl.activeTexture(gl.TEXTURE0); gl.bindTexture(textureTarget, texture); - function createFace(target, sourceFace) { - if (sourceFace.arrayBufferView) { - gl.texImage2D(target, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, sourceFace.arrayBufferView); + function createFace(target, sourceFace, preMultiplyAlpha, flipY) { + // TODO: gl.pixelStorei(gl._UNPACK_ALIGNMENT, 4); + var arrayBufferView = sourceFace.arrayBufferView; + if (arrayBufferView) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, size, size); + } + gl.texImage2D(target, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, arrayBufferView); } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + + // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement gl.texImage2D(target, 0, pixelFormat, pixelFormat, pixelDatatype, sourceFace); } } if (defined(source)) { - // TODO: _gl.pixelStorei(_gl._UNPACK_ALIGNMENT, 4); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); - - createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_X, source.positiveX); - createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, source.negativeX); - createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, source.positiveY); - createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, source.negativeY); - createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, source.positiveZ); - createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, source.negativeZ); + createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_X, source.positiveX, preMultiplyAlpha, flipY); + createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, source.negativeX, preMultiplyAlpha, flipY); + createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, source.positiveY, preMultiplyAlpha, flipY); + createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, source.negativeY, preMultiplyAlpha, flipY); + createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, source.positiveZ, preMultiplyAlpha, flipY); + createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, source.negativeZ, preMultiplyAlpha, flipY); } else { gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); @@ -163,12 +171,13 @@ define([ this._flipY = flipY; this._sampler = undefined; - this._positiveX = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_X, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); - this._negativeX = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); - this._positiveY = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); - this._negativeY = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); - this._positiveZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); - this._negativeZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); + var initialized = defined(source); + this._positiveX = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_X, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); + this._negativeX = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); + this._positiveY = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); + this._negativeY = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); + this._positiveZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); + this._negativeZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized); this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); } diff --git a/Source/Renderer/CubeMapFace.js b/Source/Renderer/CubeMapFace.js index 83e9a165cd73..ba5dbf317278 100644 --- a/Source/Renderer/CubeMapFace.js +++ b/Source/Renderer/CubeMapFace.js @@ -1,21 +1,25 @@ define([ '../Core/Check', '../Core/defaultValue', + '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', + '../Core/PixelFormat', './PixelDatatype' ], function( Check, defaultValue, + defined, defineProperties, DeveloperError, + PixelFormat, PixelDatatype) { 'use strict'; /** * @private */ - function CubeMapFace(gl, texture, textureTarget, targetFace, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY) { + function CubeMapFace(gl, texture, textureTarget, targetFace, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized) { this._gl = gl; this._texture = texture; this._textureTarget = textureTarget; @@ -25,6 +29,7 @@ define([ this._size = size; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; + this._initialized = initialized; } defineProperties(CubeMapFace.prototype, { @@ -89,17 +94,72 @@ define([ var gl = this._gl; var target = this._textureTarget; + var targetFace = this._targetFace; // TODO: gl.pixelStorei(gl._UNPACK_ALIGNMENT, 4); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this._flipY); + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); - if (source.arrayBufferView) { - gl.texSubImage2D(this._targetFace, 0, xOffset, yOffset, source.width, source.height, this._pixelFormat, this._pixelDatatype, source.arrayBufferView); - } else { - gl.texSubImage2D(this._targetFace, 0, xOffset, yOffset, this._pixelFormat, this._pixelDatatype, source); + var width = source.width; + var height = source.height; + var arrayBufferView = source.arrayBufferView; + + var size = this._size; + var pixelFormat = this._pixelFormat; + var pixelDatatype = this._pixelDatatype; + + var preMultiplyAlpha = this._preMultiplyAlpha; + var flipY = this._flipY; + + var uploaded = false; + if (!this._initialized) { + if (xOffset === 0 && yOffset === 0 && width === size && height === size) { + // initialize the entire texture + if (defined(arrayBufferView)) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, size, size); + } + gl.texImage2D(targetFace, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, arrayBufferView); + } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + + gl.texImage2D(targetFace, 0, pixelFormat, pixelFormat, pixelDatatype, source); + } + uploaded = true; + } else { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + // initialize the entire texture to zero + var bufferView = PixelFormat.createTypedArray(pixelFormat, pixelDatatype, size, size); + gl.texImage2D(targetFace, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, bufferView); + } + this._initialized = true; + } + + if (!uploaded) { + if (arrayBufferView) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, width, height); + } + gl.texSubImage2D(targetFace, 0, xOffset, yOffset, width, height, pixelFormat, pixelDatatype, arrayBufferView); + } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + + // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement + gl.texSubImage2D(targetFace, 0, xOffset, yOffset, pixelFormat, pixelDatatype, source); + } } gl.bindTexture(target, null); @@ -160,6 +220,7 @@ define([ gl.bindTexture(target, this._texture); gl.copyTexSubImage2D(this._targetFace, 0, xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height); gl.bindTexture(target, null); + this._initialized = true; }; return CubeMapFace; diff --git a/Source/Renderer/Texture.js b/Source/Renderer/Texture.js index 2ba7284e05c7..f3d700b59ade 100644 --- a/Source/Renderer/Texture.js +++ b/Source/Renderer/Texture.js @@ -164,26 +164,36 @@ define([ var preMultiplyAlpha = options.preMultiplyAlpha || pixelFormat === PixelFormat.RGB || pixelFormat === PixelFormat.LUMINANCE; var flipY = defaultValue(options.flipY, true); + var initialized = true; + var gl = context._gl; var textureTarget = gl.TEXTURE_2D; var texture = gl.createTexture(); + // TODO: gl.pixelStorei(gl._UNPACK_ALIGNMENT, 4); + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(textureTarget, texture); if (defined(source)) { - // TODO: _gl.pixelStorei(_gl._UNPACK_ALIGNMENT, 4); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); - if (defined(source.arrayBufferView)) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + // Source: typed array + var arrayBufferView = source.arrayBufferView; if (isCompressed) { - gl.compressedTexImage2D(textureTarget, 0, internalFormat, width, height, 0, source.arrayBufferView); + gl.compressedTexImage2D(textureTarget, 0, internalFormat, width, height, 0, arrayBufferView); } else { - gl.texImage2D(textureTarget, 0, internalFormat, width, height, 0, pixelFormat, pixelDatatype, source.arrayBufferView); + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, width, height); + } + gl.texImage2D(textureTarget, 0, internalFormat, width, height, 0, pixelFormat, pixelDatatype, arrayBufferView); } } else if (defined(source.framebuffer)) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + // Source: framebuffer if (source.framebuffer !== context.defaultFramebuffer) { source.framebuffer._bind(); @@ -195,11 +205,16 @@ define([ source.framebuffer._unBind(); } } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement gl.texImage2D(textureTarget, 0, internalFormat, pixelFormat, pixelDatatype, source); } } else { gl.texImage2D(textureTarget, 0, internalFormat, width, height, 0, pixelFormat, pixelDatatype, null); + initialized = false; } gl.bindTexture(textureTarget, null); @@ -224,6 +239,7 @@ define([ this._sizeInBytes = sizeInBytes; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; + this._initialized = initialized; this._sampler = undefined; this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); @@ -469,15 +485,69 @@ define([ var target = this._textureTarget; // TODO: gl.pixelStorei(gl._UNPACK_ALIGNMENT, 4); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this._flipY); + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); - if (source.arrayBufferView) { - gl.texSubImage2D(target, 0, xOffset, yOffset, source.width, source.height, this._pixelFormat, this._pixelDatatype, source.arrayBufferView); - } else { - gl.texSubImage2D(target, 0, xOffset, yOffset, this._pixelFormat, this._pixelDatatype, source); + var width = source.width; + var height = source.height; + var arrayBufferView = source.arrayBufferView; + + var textureWidth = this._width; + var textureHeight = this._height; + var pixelFormat = this._pixelFormat; + var pixelDatatype = this._pixelDatatype; + + var preMultiplyAlpha = this._preMultiplyAlpha; + var flipY = this._flipY; + + var uploaded = false; + if (!this._initialized) { + if (xOffset === 0 && yOffset === 0 && width === textureWidth && height === textureHeight) { + // initialize the entire texture + if (defined(arrayBufferView)) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, textureWidth, textureHeight); + } + gl.texImage2D(target, 0, pixelFormat, textureWidth, textureHeight, 0, pixelFormat, pixelDatatype, arrayBufferView); + } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + + gl.texImage2D(target, 0, pixelFormat, pixelFormat, pixelDatatype, source); + } + uploaded = true; + } else { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + // initialize the entire texture to zero + var bufferView = PixelFormat.createTypedArray(pixelFormat, pixelDatatype, textureWidth, textureHeight); + gl.texImage2D(target, 0, pixelFormat, textureWidth, textureHeight, 0, pixelFormat, pixelDatatype, bufferView); + } + this._initialized = true; + } + + if (!uploaded) { + if (arrayBufferView) { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (flipY) { + arrayBufferView = PixelFormat.flipY(arrayBufferView, pixelFormat, pixelDatatype, width, height); + } + gl.texSubImage2D(target, 0, xOffset, yOffset, width, height, pixelFormat, pixelDatatype, arrayBufferView); + } else { + // Only valid for DOM-Element uploads + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + + gl.texSubImage2D(target, 0, xOffset, yOffset, pixelFormat, pixelDatatype, source); + } } gl.bindTexture(target, null); @@ -536,6 +606,7 @@ define([ gl.bindTexture(target, this._texture); gl.copyTexSubImage2D(target, 0, xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height); gl.bindTexture(target, null); + this._initialized = true; }; /** diff --git a/Source/Scene/BatchTable.js b/Source/Scene/BatchTable.js index be1b9d5a45c2..c86067b95f12 100644 --- a/Source/Scene/BatchTable.js +++ b/Source/Scene/BatchTable.js @@ -371,7 +371,8 @@ define([ sampler : new Sampler({ minificationFilter : TextureMinificationFilter.NEAREST, magnificationFilter : TextureMagnificationFilter.NEAREST - }) + }), + flipY : false }); } @@ -456,7 +457,7 @@ define([ ' float numberOfAttributes = float('+ stride + '); \n' + ' float xId = mod(batchId * numberOfAttributes, batchTextureDimensions.x); \n' + ' float yId = floor(batchId * numberOfAttributes / batchTextureDimensions.x); \n' + - ' return vec2(centerX + (xId * stepX), 1.0 - (centerY + (yId * stepY))); \n' + + ' return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n' + '} \n'; } diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index 42c32407f0c9..d1a834b58091 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -848,7 +848,7 @@ define([ ' float centerY = tile_textureStep.w; \n' + ' float xId = mod(batchId, tile_textureDimensions.x); \n' + ' float yId = floor(batchId / tile_textureDimensions.x); \n' + - ' return vec2(centerX + (xId * stepX), 1.0 - (centerY + (yId * stepY))); \n' + + ' return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n' + '} \n'; } @@ -1505,6 +1505,7 @@ define([ height : dimensions.y, arrayBufferView : bytes }, + flipY : false, sampler : new Sampler({ minificationFilter : TextureMinificationFilter.NEAREST, magnificationFilter : TextureMagnificationFilter.NEAREST diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 1026832675b8..46bba1be5100 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -668,7 +668,8 @@ define([ height : textureSize, arrayBufferView : waterMask }, - sampler : waterMaskData.sampler + sampler : waterMaskData.sampler, + flipY : false }); texture.referenceCount = 0; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index c7a38d68700d..1e90699d313d 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -111,20 +111,17 @@ define([ function updateTextures(oit, context, width, height) { destroyTextures(oit); - // Use zeroed arraybuffer instead of null to initialize texture - // to workaround Firefox 50. https://github.com/AnalyticalGraphicsInc/cesium/pull/4762 - var source = new Float32Array(width * height * 4); - oit._accumulationTexture = new Texture({ context : context, + width : width, + height : height, pixelFormat : PixelFormat.RGBA, - pixelDatatype : PixelDatatype.FLOAT, - source : { - arrayBufferView : source, - width : width, - height : height - } + pixelDatatype : PixelDatatype.FLOAT }); + + // Use zeroed arraybuffer instead of null to initialize texture + // to workaround Firefox. Only needed for the second color attachment. + var source = new Float32Array(width * height * 4); oit._revealageTexture = new Texture({ context : context, pixelFormat : PixelFormat.RGBA, @@ -133,7 +130,8 @@ define([ arrayBufferView : source, width : width, height : height - } + }, + flipY : false }); } diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl index 6186d322c9b0..9b3b21a640a4 100644 --- a/Source/Shaders/GlobeFS.glsl +++ b/Source/Shaders/GlobeFS.glsl @@ -183,6 +183,7 @@ void main() vec2 waterMaskTranslation = u_waterMaskTranslationAndScale.xy; vec2 waterMaskScale = u_waterMaskTranslationAndScale.zw; vec2 waterMaskTextureCoordinates = v_textureCoordinates.xy * waterMaskScale + waterMaskTranslation; + waterMaskTextureCoordinates.y = 1.0 - waterMaskTextureCoordinates.y; float mask = texture2D(u_waterMask, waterMaskTextureCoordinates).r; diff --git a/Specs/Renderer/CubeMapSpec.js b/Specs/Renderer/CubeMapSpec.js index 6615a2ad47f9..306add5432d7 100644 --- a/Specs/Renderer/CubeMapSpec.js +++ b/Specs/Renderer/CubeMapSpec.js @@ -2,6 +2,7 @@ defineSuite([ 'Renderer/CubeMap', 'Core/Cartesian3', 'Core/Color', + 'Core/defined', 'Core/PixelFormat', 'Core/Resource', 'Renderer/ClearCommand', @@ -18,6 +19,7 @@ defineSuite([ CubeMap, Cartesian3, Color, + defined, PixelFormat, Resource, ClearCommand, @@ -44,14 +46,17 @@ defineSuite([ 'uniform mediump vec3 u_direction;' + 'void main() { gl_FragColor = textureCube(u_texture, normalize(u_direction)); }'; - var faceDirections = [ - new Cartesian3(1.0, 0.0, 0.0), // +X - new Cartesian3(-1.0, 0.0, 0.0), // -X - new Cartesian3(0.0, 1.0, 0.0), // +Y - new Cartesian3(0.0, -1.0, 0.0), // -Y - new Cartesian3(0.0, 0.0, 1.0), // +Z - new Cartesian3(0.0, 0.0, -1.0) // -Z - ]; + var faceDirections = options.faceDirections; + if (!defined(faceDirections)) { + faceDirections = [ + new Cartesian3(1.0, 0.0, 0.0), // +X + new Cartesian3(-1.0, 0.0, 0.0), // -X + new Cartesian3(0.0, 1.0, 0.0), // +Y + new Cartesian3(0.0, -1.0, 0.0), // -Y + new Cartesian3(0.0, 0.0, 1.0), // +Z + new Cartesian3(0.0, 0.0, -1.0) // -Z + ]; + } var uniformMap = { direction : undefined, @@ -497,6 +502,104 @@ defineSuite([ }); }); + it('sub copies images to a cube map', function() { + cubeMap = new CubeMap({ + context : context, + width : 2, + height : 2 + }); + cubeMap.positiveX.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([0, 255, 255, 255]) + }); + cubeMap.negativeX.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([0, 0, 255, 255]) + }); + cubeMap.positiveY.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([0, 255, 0, 255]) + }); + cubeMap.negativeY.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([255, 0, 0, 255]) + }); + cubeMap.positiveZ.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([255, 0, 255, 255]) + }); + cubeMap.negativeZ.copyFrom({ + width : 1, + height : 1, + arrayBufferView : new Uint8Array([0, 255, 0, 255]) + }, 1, 0); + + var negativeZDirection = new Cartesian3(0.25, 0.0, -1.0); + Cartesian3.normalize(negativeZDirection, negativeZDirection); + + expectCubeMapFaces({ + cubeMap : cubeMap, + expectedColors : [ + [0, 64, 64, 255], // +X + [0, 0, 64, 255], // -X + [0, 64, 0, 255], // +Y + [64, 0, 0, 255], // -Y + [64, 0, 64, 255], // +Z + [0, 32, 0, 255] // -Z + ], + faceDirections : [ + new Cartesian3(1.0, 0.0, 0.0), // +X + new Cartesian3(-1.0, 0.0, 0.0), // -X + new Cartesian3(0.0, 1.0, 0.0), // +Y + new Cartesian3(0.0, -1.0, 0.0), // -Y + new Cartesian3(0.0, 0.0, 1.0), // +Z + negativeZDirection // -Z + ] + }); + }); + + it('sub copies array buffers to a cube map', function() { + cubeMap = new CubeMap({ + context : context, + width : 2, + height : 2 + }); + cubeMap.positiveX.copyFrom(blueImage); + cubeMap.negativeX.copyFrom(greenImage); + cubeMap.positiveY.copyFrom(blueImage); + cubeMap.negativeY.copyFrom(greenImage); + cubeMap.positiveZ.copyFrom(blueImage); + cubeMap.negativeZ.copyFrom(greenImage, 1, 0); + + var negativeZDirection = new Cartesian3(0.25, 0.0, -1.0); + Cartesian3.normalize(negativeZDirection, negativeZDirection); + + expectCubeMapFaces({ + cubeMap : cubeMap, + expectedColors : [ + [0, 0, 64, 255], // +X + [0, 64, 0, 255], // -X + [0, 0, 64, 255], // +Y + [0, 64, 0, 255], // -Y + [0, 0, 64, 255], // +Z + [0, 32, 0, 255] // -Z + ], + faceDirections : [ + new Cartesian3(1.0, 0.0, 0.0), // +X + new Cartesian3(-1.0, 0.0, 0.0), // -X + new Cartesian3(0.0, 1.0, 0.0), // +Y + new Cartesian3(0.0, -1.0, 0.0), // -Y + new Cartesian3(0.0, 0.0, 1.0), // +Z + negativeZDirection // -Z + ] + }); + }); + it('copies from the framebuffer', function() { var cxt = createContext({ webgl : { diff --git a/Specs/Renderer/TextureSpec.js b/Specs/Renderer/TextureSpec.js index e26a3cc8afc8..d51602e5d6ee 100644 --- a/Specs/Renderer/TextureSpec.js +++ b/Specs/Renderer/TextureSpec.js @@ -118,7 +118,7 @@ defineSuite([ var expectedHeight = context.canvas.clientHeight; expect(texture.width).toEqual(expectedWidth); expect(texture.height).toEqual(expectedHeight); - expect(texture.sizeInBytes).toEqual(expectedWidth * expectedHeight * 4); + expect(texture.sizeInBytes).toEqual(expectedWidth * expectedHeight * PixelFormat.componentsLength(texture.pixelFormat)); command.color = Color.WHITE; command.execute(context); @@ -158,7 +158,7 @@ defineSuite([ var expectedHeight = context.canvas.clientHeight; expect(texture.width).toEqual(expectedWidth); expect(texture.height).toEqual(expectedHeight); - expect(texture.sizeInBytes).toEqual(expectedWidth * expectedHeight * 4); + expect(texture.sizeInBytes).toEqual(expectedWidth * expectedHeight * PixelFormat.componentsLength(texture.pixelFormat)); // Clear to white command.color = Color.WHITE; @@ -406,6 +406,25 @@ defineSuite([ }).contextToRender(Color.NAVY.toBytes()); }); + it('can copy from a DOM element', function() { + texture = new Texture({ + context : context, + pixelFormat : PixelFormat.RGB, + pixelDatatype : PixelDatatype.UNSIGNED_BYTE, + width : blueImage.width, + height : blueImage.height + }); + + texture.copyFrom(blueImage); + + expect({ + context : context, + fragmentShader : fs, + uniformMap : uniformMap, + epsilon : 1 + }).contextToRender([0, 0, 255, 255]); + }); + it('can replace a subset of a texture', function() { texture = new Texture({ context : context, @@ -583,7 +602,7 @@ defineSuite([ // Uncompressed formats expectTextureByteSize(16, 16, PixelFormat.ALPHA, PixelDatatype.UNSIGNED_BYTE, 256); - expectTextureByteSize(16, 16, PixelFormat.RGB, PixelDatatype.UNSIGNED_BYTE, 256 * 4); + expectTextureByteSize(16, 16, PixelFormat.RGB, PixelDatatype.UNSIGNED_BYTE, 256 * 3); expectTextureByteSize(16, 16, PixelFormat.RGBA, PixelDatatype.UNSIGNED_BYTE, 256 * 4); expectTextureByteSize(16, 16, PixelFormat.LUMINANCE, PixelDatatype.UNSIGNED_BYTE, 256); expectTextureByteSize(16, 16, PixelFormat.LUMINANCE_ALPHA, PixelDatatype.UNSIGNED_BYTE, 256 * 2); diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index ce9f61a122cb..8f6985c0870b 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -740,8 +740,8 @@ defineSuite([ var b3dmGeometryMemory = 840; // Only one box in the tile, unlike most other test tiles var i3dmGeometryMemory = 840; - // Texture is 211x211 RGBA bytes, but upsampled to 256x256 because the wrap mode is REPEAT - var texturesByteLength = 262144; + // Texture is 211x211 RGB bytes, but upsampled to 256x256 because the wrap mode is REPEAT + var texturesByteLength = 196608; var expectedGeometryMemory = b3dmGeometryMemory * 2 + i3dmGeometryMemory * 3; var expectedTextureMemory = texturesByteLength * 5; diff --git a/Specs/Scene/ClippingPlaneCollectionSpec.js b/Specs/Scene/ClippingPlaneCollectionSpec.js index f8e4c0d2434b..1973cd9da42e 100644 --- a/Specs/Scene/ClippingPlaneCollectionSpec.js +++ b/Specs/Scene/ClippingPlaneCollectionSpec.js @@ -186,7 +186,7 @@ defineSuite([ var rgba; var gl = scene.frameState.context._gl; - spyOn(gl, 'texSubImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { + spyOn(gl, 'texImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { rgba = arrayBufferView; }); @@ -317,7 +317,7 @@ defineSuite([ var rgba; var gl = scene.frameState.context._gl; - spyOn(gl, 'texSubImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { + spyOn(gl, 'texImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { rgba = arrayBufferView; }); @@ -399,7 +399,7 @@ defineSuite([ var scene = createScene(); var gl = scene.frameState.context._gl; - spyOn(gl, 'texSubImage2D').and.callThrough(); + spyOn(gl, 'texImage2D').and.callThrough(); clippingPlanes = new ClippingPlaneCollection({ planes : planes, @@ -407,13 +407,13 @@ defineSuite([ edgeColor : Color.RED, modelMatrix : transform }); - expect(gl.texSubImage2D.calls.count()).toEqual(0); + expect(gl.texImage2D.calls.count()).toEqual(0); clippingPlanes.update(scene.frameState); - expect(gl.texSubImage2D.calls.count()).toEqual(1); + expect(gl.texImage2D.calls.count()).toEqual(2); clippingPlanes.update(scene.frameState); - expect(gl.texSubImage2D.calls.count()).toEqual(1); + expect(gl.texImage2D.calls.count()).toEqual(2); clippingPlanes.destroy(); scene.destroyForSpecs(); @@ -423,6 +423,7 @@ defineSuite([ var scene = createScene(); var gl = scene.frameState.context._gl; + spyOn(gl, 'texImage2D').and.callThrough(); spyOn(gl, 'texSubImage2D').and.callThrough(); clippingPlanes = new ClippingPlaneCollection({ @@ -431,13 +432,13 @@ defineSuite([ edgeColor : Color.RED, modelMatrix : transform }); - expect(gl.texSubImage2D.calls.count()).toEqual(0); + expect(gl.texImage2D.calls.count()).toEqual(0); clippingPlanes.update(scene.frameState); - expect(gl.texSubImage2D.calls.count()).toEqual(1); + expect(gl.texImage2D.calls.count()).toEqual(2); clippingPlanes.update(scene.frameState); - expect(gl.texSubImage2D.calls.count()).toEqual(2); + expect(gl.texSubImage2D.calls.count()).toEqual(1); clippingPlanes.destroy(); scene.destroyForSpecs(); diff --git a/Specs/Scene/Instanced3DModel3DTileContentSpec.js b/Specs/Scene/Instanced3DModel3DTileContentSpec.js index c5e409bdc083..c3626c773e57 100644 --- a/Specs/Scene/Instanced3DModel3DTileContentSpec.js +++ b/Specs/Scene/Instanced3DModel3DTileContentSpec.js @@ -280,8 +280,8 @@ defineSuite([ // (24 * 8 * 4) + (36 * 2) = 840 var geometryByteLength = 840; - // Texture is 211x211 RGBA bytes, but upsampled to 256x256 because the wrap mode is REPEAT - var texturesByteLength = 262144; + // Texture is 211x211 RGB bytes, but upsampled to 256x256 because the wrap mode is REPEAT + var texturesByteLength = 196608; // One RGBA byte pixel per feature var batchTexturesByteLength = content.featuresLength * 4; diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index fa5dd7b1f87e..63f45050380d 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -2765,10 +2765,10 @@ defineSuite([ model.zoomTo(); var gl = scene.frameState.context._gl; - spyOn(gl, 'texSubImage2D').and.callThrough(); + spyOn(gl, 'texImage2D').and.callThrough(); scene.renderForSpecs(); - var callsBeforeClipping = gl.texSubImage2D.calls.count(); + var callsBeforeClipping = gl.texImage2D.calls.count(); expect(model._modelViewMatrix).toEqual(Matrix4.IDENTITY); @@ -2780,7 +2780,7 @@ defineSuite([ model.update(scene.frameState); scene.renderForSpecs(); - expect(gl.texSubImage2D.calls.count() - callsBeforeClipping * 2).toEqual(1); + expect(gl.texImage2D.calls.count() - callsBeforeClipping * 2).toEqual(2); primitives.remove(model); });