diff --git a/CHANGES.md b/CHANGES.md index 83cba9819fc3..cf12d0c7272c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ Change Log * Fixed billboard rotation when sized in meters. [#3979](https://github.com/AnalyticalGraphicsInc/cesium/issues/3979) * Fixed timeline touch events. [#4305](https://github.com/AnalyticalGraphicsInc/cesium/pull/4305) * Fixed a bug that could lead to incorrect terrain heights when using `HeightmapTerrainData` with an encoding in which actual heights were equal to the minimum representable height. +* Fixed a bug in `AttributeCompression.compressTextureCoordinates` and `decompressTextureCoordinates` that could cause a small inaccuracy in the encoded texture coordinates. * Added `DebugCameraPrimitive` to visualize the view frustum of a camera. ### 1.25 - 2016-09-01 diff --git a/Source/Core/AttributeCompression.js b/Source/Core/AttributeCompression.js index f4a41c5da943..d5e6973d29cf 100644 --- a/Source/Core/AttributeCompression.js +++ b/Source/Core/AttributeCompression.js @@ -75,9 +75,9 @@ define([ * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding. * @param {Cartesian2} result The 2 byte oct-encoded unit length vector. * @returns {Cartesian2} The 2 byte oct-encoded unit length vector. - * + * * @exception {DeveloperError} vector must be normalized. - * + * * @see AttributeCompression.octEncodeInRange * @see AttributeCompression.octDecode */ @@ -124,14 +124,14 @@ define([ /** * Decodes a unit-length vector in 2 byte 'oct' encoding to a normalized 3-component vector. - * + * * @param {Number} x The x component of the oct-encoded unit length vector. * @param {Number} y The y component of the oct-encoded unit length vector. * @param {Cartesian3} result The decoded and normalized vector. * @returns {Cartesian3} The decoded and normalized vector. - * + * * @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and 255. - * + * * @see AttributeCompression.octDecodeInRange */ AttributeCompression.octDecode = function(x, y, result) { @@ -268,7 +268,7 @@ define([ /** * Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision. * - * @param {Cartesian2} textureCoordinates The texture coordinates to compress + * @param {Cartesian2} textureCoordinates The texture coordinates to compress. Both coordinates must be in the range 0.0-1.0. * @returns {Number} The packed texture coordinates. * */ @@ -279,8 +279,9 @@ define([ } //>>includeEnd('debug'); - var x = textureCoordinates.x === 1.0 ? 4095.0 : (textureCoordinates.x * 4096.0) | 0; - var y = textureCoordinates.y === 1.0 ? 4095.0 : (textureCoordinates.y * 4096.0) | 0; + // Move x and y to the range 0-4095; + var x = (textureCoordinates.x * 4095.0) | 0; + var y = (textureCoordinates.y * 4095.0) | 0; return 4096.0 * x + y; }; @@ -303,8 +304,9 @@ define([ //>>includeEnd('debug'); var temp = compressed / 4096.0; - result.x = Math.floor(temp) / 4096.0; - result.y = temp - Math.floor(temp); + var xZeroTo4095 = Math.floor(temp); + result.x = xZeroTo4095 / 4095.0; + result.y = (compressed - xZeroTo4095 * 4096) / 4095; return result; }; diff --git a/Source/Shaders/Builtin/Functions/decompressTextureCoordinates.glsl b/Source/Shaders/Builtin/Functions/decompressTextureCoordinates.glsl index c91a5e87473c..fa2a6814382c 100644 --- a/Source/Shaders/Builtin/Functions/decompressTextureCoordinates.glsl +++ b/Source/Shaders/Builtin/Functions/decompressTextureCoordinates.glsl @@ -10,7 +10,8 @@ vec2 czm_decompressTextureCoordinates(float encoded) { float temp = encoded / 4096.0; - float stx = floor(temp) / 4096.0; - float sty = temp - floor(temp); + float xZeroTo4095 = floor(temp); + float stx = xZeroTo4095 / 4095.0; + float sty = (encoded - xZeroTo4095 * 4096.0) / 4095.0; return vec2(stx, sty); } diff --git a/Specs/Core/AttributeCompressionSpec.js b/Specs/Core/AttributeCompressionSpec.js index 1a37151f3380..574bda8995bf 100644 --- a/Specs/Core/AttributeCompressionSpec.js +++ b/Specs/Core/AttributeCompressionSpec.js @@ -490,24 +490,49 @@ defineSuite([ it('compresses texture coordinates', function() { var coords = new Cartesian2(0.5, 0.5); - expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqual(coords); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqualEpsilon(coords, 1.0 / 4096.0); }); - + it('compress texture coordinates throws without texture coordinates', function() { expect(function() { AttributeCompression.compressTextureCoordinates(undefined); }).toThrowDeveloperError(); }); - + it('decompress texture coordinates throws without encoded texture coordinates', function() { expect(function() { AttributeCompression.decompressTextureCoordinates(undefined, new Cartesian2()); }).toThrowDeveloperError(); }); - + it('decompress texture coordinates throws without result', function() { expect(function() { AttributeCompression.decompressTextureCoordinates(0.0, undefined); }).toThrowDeveloperError(); }); + + it('compresses/decompresses 1.0', function() { + var coords = new Cartesian2(1.0, 1.0); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqual(coords); + }); + + it('compresses/decompresses 0.0', function() { + var coords = new Cartesian2(1.0, 1.0); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqual(coords); + }); + + it('compresses/decompresses 0.5 / 1.0', function() { + var coords = new Cartesian2(0.5, 1.0); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqualEpsilon(coords, 1.0 / 4095.0); + }); + + it('compresses/decompresses 1.0 / 0.5', function() { + var coords = new Cartesian2(1.0, 0.5); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqualEpsilon(coords, 1.0 / 4095.0); + }); + + it('compresses/decompresses values very close but not equal to 1.0', function() { + var coords = new Cartesian2(0.99999999999999, 0.99999999999999); + expect(AttributeCompression.decompressTextureCoordinates(AttributeCompression.compressTextureCoordinates(coords), new Cartesian2())).toEqualEpsilon(coords, 1.0 / 4095.0); + }); }); diff --git a/Specs/Core/TerrainEncodingSpec.js b/Specs/Core/TerrainEncodingSpec.js index e2f4b3195bf0..9928217ae848 100644 --- a/Specs/Core/TerrainEncodingSpec.js +++ b/Specs/Core/TerrainEncodingSpec.js @@ -183,7 +183,7 @@ defineSuite([ expect(encoding.getStride()).toEqual(3); expect(buffer.length).toEqual(encoding.getStride()); - expect(encoding.decodeTextureCoordinates(buffer, 0)).toEqualEpsilon(texCoords, CesiumMath.EPSILON14); + expect(encoding.decodeTextureCoordinates(buffer, 0)).toEqualEpsilon(texCoords, 1.0 / 4095.0); }); it('encodes textureCoordinates with quantization and normals', function() { @@ -198,7 +198,7 @@ defineSuite([ expect(encoding.getStride()).toEqual(4); expect(buffer.length).toEqual(encoding.getStride()); - expect(encoding.decodeTextureCoordinates(buffer, 0)).toEqualEpsilon(texCoords, CesiumMath.EPSILON14); + expect(encoding.decodeTextureCoordinates(buffer, 0)).toEqualEpsilon(texCoords, 1.0 / 4095.0); }); it('encodes height with quantization and without normals', function() { @@ -214,7 +214,7 @@ defineSuite([ expect(encoding.getStride()).toEqual(3); expect(buffer.length).toEqual(encoding.getStride()); - expect(encoding.decodeHeight(buffer, 0)).toEqualEpsilon(height, CesiumMath.EPSILON14); + expect(encoding.decodeHeight(buffer, 0)).toEqualEpsilon(height, 200.0 / 4095.0); }); it('encodes height with quantization and normals', function() { @@ -230,7 +230,7 @@ defineSuite([ expect(encoding.getStride()).toEqual(4); expect(buffer.length).toEqual(encoding.getStride()); - expect(encoding.decodeHeight(buffer, 0)).toEqualEpsilon(height, CesiumMath.EPSILON14); + expect(encoding.decodeHeight(buffer, 0)).toEqualEpsilon(height, 200.0 / 4095.0); }); it('gets oct-encoded normal', function() { diff --git a/Specs/Renderer/BuiltinFunctionsSpec.js b/Specs/Renderer/BuiltinFunctionsSpec.js index 9ff5c55e04d3..24387af25124 100644 --- a/Specs/Renderer/BuiltinFunctionsSpec.js +++ b/Specs/Renderer/BuiltinFunctionsSpec.js @@ -219,7 +219,7 @@ defineSuite([ it('has czm_decompressTextureCoordinates', function() { var fs = 'void main() { ' + - ' gl_FragColor = vec4(czm_decompressTextureCoordinates(8390656.0) == vec2(0.5, 0.5)); ' + + ' gl_FragColor = vec4(czm_decompressTextureCoordinates(8386559.0) == vec2(0.4998779, 0.4998779)); ' + '}'; context.verifyDrawForSpecs(fs); });