diff --git a/CHANGES.md b/CHANGES.md
index dae9330e0831..ba9c8e17ff3f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -26,6 +26,7 @@ Change Log
* Fixed labels not showing for individual entities in data sources when clustering is enabled. [#6087](https://github.com/AnalyticalGraphicsInc/cesium/issues/6087)
* Fixed a crash for 3D Tiles that have zero volume. [#7945](https://github.com/AnalyticalGraphicsInc/cesium/pull/7945)
* Fixed a bug where dynamic polylines did not use the given arcType. [#8191](https://github.com/AnalyticalGraphicsInc/cesium/issues/8191)
+* Fixed an issue where polygons, corridors, rectangles, and ellipses on terrain would not render on some mobile devices. [#6739](https://github.com/AnalyticalGraphicsInc/cesium/issues/6739)
* Fixed a bug where GlobeSurfaceTile would not render the tile until all layers completed loading causing globe to appear to hang. [#7974](https://github.com/AnalyticalGraphicsInc/cesium/issues/7974)
### 1.61 - 2019-09-03
diff --git a/Source/Renderer/ComputeCommand.js b/Source/Renderer/ComputeCommand.js
index 0ed531cdd1bb..071fac2a5eaa 100644
--- a/Source/Renderer/ComputeCommand.js
+++ b/Source/Renderer/ComputeCommand.js
@@ -107,7 +107,7 @@ define([
/**
* Executes the compute command.
*
- * @param {Context} computeEngine The context that processes the compute command.
+ * @param {ComputeEngine} computeEngine The context that processes the compute command.
*/
ComputeCommand.prototype.execute = function(computeEngine) {
computeEngine.execute(this);
diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js
index 306d3cc5b60a..9dd5eba7c0ff 100644
--- a/Source/Renderer/Context.js
+++ b/Source/Renderer/Context.js
@@ -18,6 +18,7 @@ define([
'../Core/WebGLConstants',
'../Shaders/ViewportQuadVS',
'./BufferUsage',
+ './checkFloatTexturePrecision',
'./ClearCommand',
'./ContextLimits',
'./CubeMap',
@@ -51,6 +52,7 @@ define([
WebGLConstants,
ViewportQuadVS,
BufferUsage,
+ checkFloatTexturePrecision,
ClearCommand,
ContextLimits,
CubeMap,
@@ -445,6 +447,8 @@ define([
this.cache = {};
RenderState.apply(gl, rs, ps);
+
+ this._floatTexSixPlaces = checkFloatTexturePrecision(this);
}
var defaultFramebufferMarker = {};
@@ -586,6 +590,18 @@ define([
}
},
+ /**
+ * Returns true
if the context's floating point textures support 6 decimal places of precision.
+ * @memberof Context.prototype
+ * @type {Boolean}
+ * @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_float/}
+ */
+ floatTextureSixPlaces : {
+ get : function() {
+ return this._floatTexSixPlaces;
+ }
+ },
+
/**
* true
if OES_texture_half_float is supported. This extension provides
* access to floating point textures that, for example, can be attached to framebuffers for high dynamic range.
diff --git a/Source/Renderer/checkFloatTexturePrecision.js b/Source/Renderer/checkFloatTexturePrecision.js
new file mode 100644
index 000000000000..2e00e1100a7c
--- /dev/null
+++ b/Source/Renderer/checkFloatTexturePrecision.js
@@ -0,0 +1,100 @@
+define([
+ '../Core/PixelFormat',
+ '../Shaders/CheckFloatTexturePrecisionFS',
+ './ComputeCommand',
+ './ComputeEngine',
+ './Framebuffer',
+ './PixelDatatype',
+ './Texture'
+ ], function(
+ PixelFormat,
+ CheckFloatTexturePrecisionFS,
+ ComputeCommand,
+ ComputeEngine,
+ Framebuffer,
+ PixelDatatype,
+ Texture) {
+ 'use strict';
+
+ /**
+ * Checks if the context's floating point textures support 6 decimal places of precision.
+ *
+ * @param {Context} context A context wrapping a gl implementation.
+ * @returns {Boolean} Whether or not the context's floating point textures support 6 decimal places of precision
+ *
+ * @private
+ */
+ function checkFloatTexturePrecision(context) {
+ if (!context.floatingPointTexture) {
+ return false;
+ }
+
+ var computeEngine = new ComputeEngine(context);
+ var outputTexture = new Texture({
+ context : context,
+ width : 1,
+ height : 1,
+ pixelFormat : PixelFormat.RGBA
+ });
+
+ var floatTexture = new Texture({
+ context : context,
+ width : 1,
+ height : 1,
+ pixelFormat : PixelFormat.RGBA,
+ pixelDatatype : checkFloatTexturePrecision._getFloatPixelType(),
+ source : {
+ width : 1,
+ height : 1,
+ arrayBufferView : checkFloatTexturePrecision._getArray([123456, 0, 0, 0])
+ }
+ });
+
+ var framebuffer = new Framebuffer({
+ context : context,
+ colorTextures : [outputTexture],
+ destroyAttachments : false
+ });
+
+ var readState = {
+ framebuffer : framebuffer,
+ x : 0,
+ y : 0,
+ width : 1,
+ height : 1
+ };
+
+ var sixPlaces = false;
+ var computeCommand = new ComputeCommand({
+ fragmentShaderSource : CheckFloatTexturePrecisionFS,
+ outputTexture : outputTexture,
+ uniformMap : {
+ u_floatTexture : function() {
+ return floatTexture;
+ }
+ },
+ persists : false,
+ postExecute : function() {
+ var pixel = context.readPixels(readState);
+ sixPlaces = pixel[0] === 0;
+ }
+ });
+
+ computeCommand.execute(computeEngine);
+
+ computeEngine.destroy();
+ framebuffer.destroy();
+
+ return sixPlaces;
+ }
+
+ checkFloatTexturePrecision._getFloatPixelType = function() {
+ return PixelDatatype.FLOAT;
+ };
+
+ checkFloatTexturePrecision._getArray = function(array) {
+ return new Float32Array(array);
+ };
+
+ return checkFloatTexturePrecision;
+});
diff --git a/Source/Scene/ClassificationPrimitive.js b/Source/Scene/ClassificationPrimitive.js
index 0d9be7b8880a..7218167cade9 100644
--- a/Source/Scene/ClassificationPrimitive.js
+++ b/Source/Scene/ClassificationPrimitive.js
@@ -578,7 +578,7 @@ define([
});
var attributeLocations = classificationPrimitive._primitive._attributeLocations;
- var shadowVolumeAppearance = new ShadowVolumeAppearance(cullFragmentsUsingExtents, planarExtents, classificationPrimitive.appearance);
+ var shadowVolumeAppearance = new ShadowVolumeAppearance(cullFragmentsUsingExtents, planarExtents, classificationPrimitive.appearance, context.floatTextureSixPlaces);
classificationPrimitive._spStencil = ShaderProgram.replaceCache({
context : context,
diff --git a/Source/Scene/GroundPrimitive.js b/Source/Scene/GroundPrimitive.js
index 905ea01005bb..eb09ab4561d1 100644
--- a/Source/Scene/GroundPrimitive.js
+++ b/Source/Scene/GroundPrimitive.js
@@ -730,10 +730,11 @@ define([
var boundingRectangle = getRectangle(frameState, geometry);
var textureCoordinateRotationPoints = geometry.textureCoordinateRotationPoints;
+ var useFloatBatchTable = frameState.context.floatTextureSixPlaces;
if (usePlanarExtents) {
- attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection, this._maxHeight);
+ attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection, useFloatBatchTable, this._maxHeight);
} else {
- attributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection);
+ attributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection, useFloatBatchTable);
}
var instanceAttributes = instance.attributes;
diff --git a/Source/Scene/ShadowVolumeAppearance.js b/Source/Scene/ShadowVolumeAppearance.js
index ea9c6e0ba5f6..3bf2997ec2e7 100644
--- a/Source/Scene/ShadowVolumeAppearance.js
+++ b/Source/Scene/ShadowVolumeAppearance.js
@@ -49,15 +49,19 @@ define([
* @param {Boolean} extentsCulling Discard fragments outside the instance's texture coordinate extents.
* @param {Boolean} planarExtents If true, texture coordinates will be computed using planes instead of spherical coordinates.
* @param {Appearance} appearance An Appearance to be used with a ClassificationPrimitive via GroundPrimitive.
+ * @param {Boolean} useFloatBatchTable Whether or not the ShadowVolumeAppearance should use floating point batch table values.
* @private
*/
- function ShadowVolumeAppearance(extentsCulling, planarExtents, appearance) {
+ function ShadowVolumeAppearance(extentsCulling, planarExtents, appearance, useFloatBatchTable) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.bool('extentsCulling', extentsCulling);
Check.typeOf.bool('planarExtents', planarExtents);
Check.typeOf.object('appearance', appearance);
+ Check.typeOf.bool('useFloatBatchTable', useFloatBatchTable);
//>>includeEnd('debug');
+ this._useFloatBatchTable = useFloatBatchTable;
+
// Compute shader dependencies
var colorShaderDependencies = new ShaderDependencies();
colorShaderDependencies.requiresTextureCoordinates = extentsCulling;
@@ -200,7 +204,7 @@ define([
Check.typeOf.bool('columbusView2D', columbusView2D);
Check.defined('mapProjection', mapProjection);
//>>includeEnd('debug');
- return createShadowVolumeAppearanceVS(this._colorShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, this._appearance, mapProjection);
+ return createShadowVolumeAppearanceVS(this._colorShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, this._appearance, mapProjection, this._useFloatBatchTable);
};
/**
@@ -219,7 +223,7 @@ define([
Check.typeOf.bool('columbusView2D', columbusView2D);
Check.defined('mapProjection', mapProjection);
//>>includeEnd('debug');
- return createShadowVolumeAppearanceVS(this._pickShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, undefined, mapProjection);
+ return createShadowVolumeAppearanceVS(this._pickShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, undefined, mapProjection, this._useFloatBatchTable);
};
var longitudeExtentsCartesianScratch = new Cartesian3();
@@ -228,7 +232,7 @@ define([
high : 0.0,
low : 0.0
};
- function createShadowVolumeAppearanceVS(shaderDependencies, planarExtents, columbusView2D, defines, vertexShaderSource, appearance, mapProjection) {
+ function createShadowVolumeAppearanceVS(shaderDependencies, planarExtents, columbusView2D, defines, vertexShaderSource, appearance, mapProjection, useFloatBatchTable) {
var allDefines = defines.slice();
if (projectionExtentDefines.eastMostYhighDefine === '') {
@@ -271,6 +275,10 @@ define([
}
}
+ if (!useFloatBatchTable) {
+ allDefines.push('UINT8_PACKING');
+ }
+
return new ShaderSource({
defines : allDefines,
sources : [vertexShaderSource]
@@ -403,12 +411,81 @@ define([
});
}
+ function encodeLowLessThan100k(value, valueName, attributes) {
+ // Encode a value like 12,345.678 to 4 uint8 values: 12 34 56 78
+ var fract = Math.abs(value);
+ var d12 = Math.floor(fract / 1000);
+ fract -= d12 * 1000; // 345.678
+ var d34 = Math.floor(fract / 10);
+ fract -= d34 * 10; // 5.678
+ var d56 = Math.floor(fract * 10);
+ fract -= d56 * 0.1; // 0.078
+ var d78 = Math.floor(fract * 1000);
+
+ if (value < 0) {
+ d12 = 255 - d12;
+ }
+
+ attributes[valueName] = new GeometryInstanceAttribute({
+ componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute: 4,
+ normalize: false,
+ value : [d12, d34, d56, d78]
+ });
+ }
+
+ function encodeHighLessThan100Million(value, valueName, attributes) {
+ // Encode a value like -12,345,678 to 4 uint8 values: sign+12 34 56 78
+ var fract = Math.abs(value);
+ var d12 = Math.floor(fract / 1000000);
+ fract -= d12 * 1000000; // 345678
+ var d34 = Math.floor(fract / 10000);
+ fract -= d34 * 10000; // 5678
+ var d56 = Math.floor(fract / 100);
+ fract -= d56 * 100; // 78
+ var d78 = Math.floor(fract);
+
+ if (value < 0) {
+ d12 = 255 - d12;
+ }
+
+ attributes[valueName] = new GeometryInstanceAttribute({
+ componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute: 4,
+ normalize: false,
+ value : [d12, d34, d56, d78]
+ });
+ }
+
+ function encodeLessThan1000k(value, valueName, attributes) {
+ // Encode a value like -123456.78 to 4 uint8 values sign+12 34 56 78
+ var fract = Math.abs(value);
+ var d12 = Math.floor(fract / 10000);
+ fract -= d12 * 10000; // 3456.78
+ var d34 = Math.floor(fract / 100);
+ fract -= d34 * 100; // 56.78
+ var d56 = Math.floor(fract);
+ fract -= d56; // 0.78
+ var d78 = Math.floor(fract / 0.001);
+
+ if (value < 0) {
+ d12 = 255 - d12;
+ }
+
+ attributes[valueName] = new GeometryInstanceAttribute({
+ componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
+ componentsPerAttribute: 4,
+ normalize: false,
+ value : [d12, d34, d56, d78]
+ });
+ }
+
var cartographicScratch = new Cartographic();
var cornerScratch = new Cartesian3();
var northWestScratch = new Cartesian3();
var southEastScratch = new Cartesian3();
var highLowScratch = {high : 0.0, low : 0.0};
- function add2DTextureCoordinateAttributes(rectangle, projection, attributes) {
+ function add2DTextureCoordinateAttributes(rectangle, projection, attributes, useFloatBatchTable) {
// Compute corner positions in double precision
var carto = cartographicScratch;
carto.height = 0.0;
@@ -432,9 +509,30 @@ define([
// y: y value for southWestCorner
// z: y value for northWest
// w: x value for southEast
+
+ var encoded;
+ if (!useFloatBatchTable) {
+ encoded = EncodedCartesian3.encode(southWestCorner.x, highLowScratch);
+ encodeHighLessThan100Million(encoded.high, 'planes2D_HIGH_x', attributes);
+ encodeLowLessThan100k(encoded.low, 'planes2D_LOW_x', attributes);
+
+ encoded = EncodedCartesian3.encode(southWestCorner.y, highLowScratch);
+ encodeHighLessThan100Million(encoded.high, 'planes2D_HIGH_y', attributes);
+ encodeLowLessThan100k(encoded.low, 'planes2D_LOW_y', attributes);
+
+ encoded = EncodedCartesian3.encode(northWest.y, highLowScratch);
+ encodeHighLessThan100Million(encoded.high, 'planes2D_HIGH_z', attributes);
+ encodeLowLessThan100k(encoded.low, 'planes2D_LOW_z', attributes);
+
+ encoded = EncodedCartesian3.encode(southEast.x, highLowScratch);
+ encodeHighLessThan100Million(encoded.high, 'planes2D_HIGH_w', attributes);
+ encodeLowLessThan100k(encoded.low, 'planes2D_LOW_w', attributes);
+ return;
+ }
+
var valuesHigh = [0, 0, 0, 0];
var valuesLow = [0, 0, 0, 0];
- var encoded = EncodedCartesian3.encode(southWestCorner.x, highLowScratch);
+ encoded = EncodedCartesian3.encode(southWestCorner.x, highLowScratch);
valuesHigh[0] = encoded.high;
valuesLow[0] = encoded.low;
@@ -578,15 +676,17 @@ define([
* @param {Number[]} textureCoordinateRotationPoints Points in the computed texture coordinate system for remapping texture coordinates
* @param {Ellipsoid} ellipsoid Ellipsoid for converting Rectangle points to world coordinates
* @param {MapProjection} projection The MapProjection used for 2D and Columbus View.
+ * @param {Boolean} useFloatBatchTable Whether or not the ShadowVolumeAppearance should use floating point batch table values.
* @param {Number} [height=0] The maximum height for the shadow volume.
* @returns {Object} An attributes dictionary containing planar texture coordinate attributes.
*/
- ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes = function(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection, height) {
+ ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes = function(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection, useFloatBatchTable, height) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('boundingRectangle', boundingRectangle);
Check.defined('textureCoordinateRotationPoints', textureCoordinateRotationPoints);
Check.typeOf.object('ellipsoid', ellipsoid);
Check.typeOf.object('projection', projection);
+ Check.typeOf.bool('useFloatBatchTable', useFloatBatchTable);
//>>includeEnd('debug');
var corner = cornerScratch;
@@ -598,6 +698,30 @@ define([
addTextureCoordinateRotationAttributes(attributes, textureCoordinateRotationPoints);
var encoded = EncodedCartesian3.fromCartesian(corner, encodeScratch);
+
+ if (!useFloatBatchTable) {
+ var high = encoded.high;
+ encodeHighLessThan100Million(high.x, 'southWest_HIGH_x', attributes);
+ encodeHighLessThan100Million(high.y, 'southWest_HIGH_y', attributes);
+ encodeHighLessThan100Million(high.z, 'southWest_HIGH_z', attributes);
+
+ var low = encoded.low;
+ encodeLowLessThan100k(low.x, 'southWest_LOW_x', attributes);
+ encodeLowLessThan100k(low.y, 'southWest_LOW_y', attributes);
+ encodeLowLessThan100k(low.z, 'southWest_LOW_z', attributes);
+
+ encodeLessThan1000k(eastward.x, 'eastward_x', attributes);
+ encodeLessThan1000k(eastward.y, 'eastward_y', attributes);
+ encodeLessThan1000k(eastward.z, 'eastward_z', attributes);
+
+ encodeLessThan1000k(northward.x, 'northward_x', attributes);
+ encodeLessThan1000k(northward.y, 'northward_y', attributes);
+ encodeLessThan1000k(northward.z, 'northward_z', attributes);
+
+ add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes, false);
+ return attributes;
+ }
+
attributes.southWest_HIGH = new GeometryInstanceAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
@@ -623,7 +747,7 @@ define([
value : Cartesian3.pack(northward, [0, 0, 0])
});
- add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes);
+ add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes, true);
return attributes;
};
@@ -667,14 +791,16 @@ define([
* @param {Number[]} textureCoordinateRotationPoints Points in the computed texture coordinate system for remapping texture coordinates
* @param {Ellipsoid} ellipsoid Ellipsoid for converting Rectangle points to world coordinates
* @param {MapProjection} projection The MapProjection used for 2D and Columbus View.
+ * @param {Boolean} useFloatBatchTable Whether or not the ShadowVolumeAppearance should use floating point batch table values.
* @returns {Object} An attributes dictionary containing spherical texture coordinate attributes.
*/
- ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes = function(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection) {
+ ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes = function(boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection, useFloatBatchTable) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('boundingRectangle', boundingRectangle);
Check.defined('textureCoordinateRotationPoints', textureCoordinateRotationPoints);
Check.typeOf.object('ellipsoid', ellipsoid);
Check.typeOf.object('projection', projection);
+ Check.typeOf.bool('useFloatBatchTable', useFloatBatchTable);
//>>includeEnd('debug');
// rectangle cartographic coords !== spherical because it's on an ellipsoid
@@ -721,21 +847,48 @@ define([
};
addTextureCoordinateRotationAttributes(attributes, textureCoordinateRotationPoints);
- add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes);
+ add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes, useFloatBatchTable);
return attributes;
};
ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes = function(attributes) {
- return defined(attributes.southWest_HIGH) && defined(attributes.southWest_LOW) &&
+ var hasFloatAttributes =
+ defined(attributes.southWest_HIGH) && defined(attributes.southWest_LOW) &&
defined(attributes.northward) && defined(attributes.eastward) &&
defined(attributes.planes2D_HIGH) && defined(attributes.planes2D_LOW) &&
defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents);
+
+ var hasUint8Attributes =
+ defined(attributes.southWest_HIGH_x) && defined(attributes.southWest_LOW_x) &&
+ defined(attributes.southWest_HIGH_y) && defined(attributes.southWest_LOW_y) &&
+ defined(attributes.southWest_HIGH_z) && defined(attributes.southWest_LOW_z) &&
+ defined(attributes.northward_x) && defined(attributes.eastward_x) &&
+ defined(attributes.northward_y) && defined(attributes.eastward_y) &&
+ defined(attributes.northward_z) && defined(attributes.eastward_z) &&
+ defined(attributes.planes2D_HIGH_x) && defined(attributes.planes2D_LOW_x) &&
+ defined(attributes.planes2D_HIGH_y) && defined(attributes.planes2D_LOW_y) &&
+ defined(attributes.planes2D_HIGH_z) && defined(attributes.planes2D_LOW_z) &&
+ defined(attributes.planes2D_HIGH_w) && defined(attributes.planes2D_LOW_w) &&
+ defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents);
+
+ return hasFloatAttributes || hasUint8Attributes;
};
ShadowVolumeAppearance.hasAttributesForSphericalExtents = function(attributes) {
- return defined(attributes.sphericalExtents) && defined(attributes.longitudeRotation) &&
- defined(attributes.planes2D_HIGH) && defined(attributes.planes2D_LOW) &&
- defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents);
+ var hasFloatAttributes =
+ defined(attributes.sphericalExtents) && defined(attributes.longitudeRotation) &&
+ defined(attributes.planes2D_HIGH) && defined(attributes.planes2D_LOW) &&
+ defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents);
+
+ var hasUint8Attributes =
+ defined(attributes.sphericalExtents) && defined(attributes.longitudeRotation) &&
+ defined(attributes.planes2D_HIGH_x) && defined(attributes.planes2D_LOW_x) &&
+ defined(attributes.planes2D_HIGH_y) && defined(attributes.planes2D_LOW_y) &&
+ defined(attributes.planes2D_HIGH_z) && defined(attributes.planes2D_LOW_z) &&
+ defined(attributes.planes2D_HIGH_w) && defined(attributes.planes2D_LOW_w) &&
+ defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents);
+
+ return hasFloatAttributes || hasUint8Attributes;
};
function shouldUseSpherical(rectangle) {
diff --git a/Source/Shaders/CheckFloatTexturePrecisionFS.glsl b/Source/Shaders/CheckFloatTexturePrecisionFS.glsl
new file mode 100644
index 000000000000..8f77d167cef5
--- /dev/null
+++ b/Source/Shaders/CheckFloatTexturePrecisionFS.glsl
@@ -0,0 +1,8 @@
+uniform sampler2D u_floatTexture;
+
+void main()
+{
+ float actual = texture2D(u_floatTexture, vec2(0.5, 0.5)).r;
+ float expected = 123456.0;
+ gl_FragColor = vec4(abs(actual - expected), 0.0, 0.0, 1.0);
+}
diff --git a/Source/Shaders/ShadowVolumeAppearanceFS.glsl b/Source/Shaders/ShadowVolumeAppearanceFS.glsl
index ab8903295863..d42c278e5111 100644
--- a/Source/Shaders/ShadowVolumeAppearanceFS.glsl
+++ b/Source/Shaders/ShadowVolumeAppearanceFS.glsl
@@ -20,12 +20,14 @@ varying vec4 v_color;
#endif
#ifdef NORMAL_EC
-vec3 getEyeCoordinate3FromWindowCoordinate(vec2 fragCoord, float logDepthOrDepth) {
+vec3 getEyeCoordinate3FromWindowCoordinate(vec2 fragCoord, float logDepthOrDepth)
+{
vec4 eyeCoordinate = czm_windowToEyeCoordinates(fragCoord, logDepthOrDepth);
return eyeCoordinate.xyz / eyeCoordinate.w;
}
-vec3 vectorFromOffset(vec4 eyeCoordinate, vec2 positiveOffset) {
+vec3 vectorFromOffset(vec4 eyeCoordinate, vec2 positiveOffset)
+{
vec2 glFragCoordXY = gl_FragCoord.xy;
// Sample depths at both offset and negative offset
float upOrRightLogDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, (glFragCoordXY + positiveOffset) / czm_viewport.zw));
@@ -71,7 +73,8 @@ void main(void)
#ifdef PICK
#ifdef CULL_FRAGMENTS
- if (0.0 <= uv.x && uv.x <= 1.0 && 0.0 <= uv.y && uv.y <= 1.0) {
+ if (0.0 <= uv.x && uv.x <= 1.0 && 0.0 <= uv.y && uv.y <= 1.0)
+ {
gl_FragColor.a = 1.0; // 0.0 alpha leads to discard from ShaderSource.createPickFragmentShaderSource
czm_writeDepthClampedToFarPlane();
}
@@ -81,7 +84,8 @@ void main(void)
#else // PICK
#ifdef CULL_FRAGMENTS
- if (uv.x <= 0.0 || 1.0 <= uv.x || uv.y <= 0.0 || 1.0 <= uv.y) {
+ if (uv.x <= 0.0 || 1.0 <= uv.x || uv.y <= 0.0 || 1.0 <= uv.y)
+ {
discard;
}
#endif
diff --git a/Source/Shaders/ShadowVolumeAppearanceVS.glsl b/Source/Shaders/ShadowVolumeAppearanceVS.glsl
index 0f2b1c456a3b..b5d486c7de68 100644
--- a/Source/Shaders/ShadowVolumeAppearanceVS.glsl
+++ b/Source/Shaders/ShadowVolumeAppearanceVS.glsl
@@ -25,6 +25,88 @@ varying vec3 v_uMaxAndInverseDistance;
varying vec3 v_vMaxAndInverseDistance;
#endif // TEXTURE_COORDINATES
+#if defined(TEXTURE_COORDINATES) && !defined(SPHERICAL) && defined(UINT8_PACKING)
+vec4 clampAndMagnitude(vec4 sd)
+{
+ vec4 d = sd;
+ d.x = czm_branchFreeTernary(sd.x < 128.0, d.x, (255.0 - sd.x));
+ d.x = floor(0.5 + d.x);
+ d.y = floor(0.5 + d.y);
+ d.z = floor(0.5 + d.z);
+ d.w = floor(0.5 + d.w);
+ return d;
+}
+
+float unpackLowLessThan100k(vec4 sd)
+{
+ vec4 d = clampAndMagnitude(sd);
+ return (1000.0 * d.x + 10.0 * d.y + 0.1 * d.z + 0.001 * d.w) * czm_branchFreeTernary(sd.x < 128.0, 1.0, -1.0);
+}
+
+vec3 southwest_LOW(vec4 x, vec4 y, vec4 z)
+{
+ vec3 value;
+ value.x = unpackLowLessThan100k(x);
+ value.y = unpackLowLessThan100k(y);
+ value.z = unpackLowLessThan100k(z);
+ return value;
+}
+
+float unpackHighMagLessThan100Million(vec4 sd)
+{
+ vec4 d = clampAndMagnitude(sd);
+ return (1000000.0 * d.x + 10000.0 * d.y + 100.0 * d.z + d.w) * czm_branchFreeTernary(sd.x < 128.0, 1.0, -1.0);
+}
+
+vec3 southwest_HIGH(vec4 x, vec4 y, vec4 z)
+{
+ vec3 value;
+ value.x = unpackHighMagLessThan100Million(x);
+ value.y = unpackHighMagLessThan100Million(y);
+ value.z = unpackHighMagLessThan100Million(z);
+ return value;
+}
+
+#ifdef COLUMBUS_VIEW_2D
+vec4 unpackPlanes2D_HIGH(vec4 x, vec4 y, vec4 z, vec4 w)
+{
+ vec4 value;
+ value.x = unpackHighMagLessThan100Million(x);
+ value.y = unpackHighMagLessThan100Million(y);
+ value.z = unpackHighMagLessThan100Million(z);
+ value.w = unpackHighMagLessThan100Million(w);
+ return value;
+}
+
+vec4 unpackPlanes2D_LOW(vec4 x, vec4 y, vec4 z, vec4 w)
+{
+ vec4 value;
+ value.x = unpackLowLessThan100k(x);
+ value.y = unpackLowLessThan100k(y);
+ value.z = unpackLowLessThan100k(z);
+ value.w = unpackLowLessThan100k(w);
+ return value;
+}
+
+#else
+float unpackLowLessThan1000k(vec4 sd)
+{
+ vec4 d = clampAndMagnitude(sd);
+ return (10000.0 * d.x + 100.0 * d.y + d.z + 0.01 * d.w) * czm_branchFreeTernary(sd.x < 128.0, 1.0, -1.0);
+}
+
+vec3 unpackExtent(vec4 x, vec4 y, vec4 z)
+{
+ vec3 value;
+ value.x = unpackLowLessThan1000k(x);
+ value.y = unpackLowLessThan1000k(y);
+ value.z = unpackLowLessThan1000k(z);
+ return value;
+}
+
+#endif
+#endif
+
void main()
{
vec4 position = czm_computePosition();
@@ -43,8 +125,19 @@ void main()
v_uvMinAndSphericalLongitudeRotation.z = czm_batchTable_longitudeRotation(batchId);
#else // SPHERICAL
#ifdef COLUMBUS_VIEW_2D
+#ifdef UINT8_PACKING
+ vec4 planes2D_high = unpackPlanes2D_HIGH(czm_batchTable_planes2D_HIGH_x(batchId),
+ czm_batchTable_planes2D_HIGH_y(batchId),
+ czm_batchTable_planes2D_HIGH_z(batchId),
+ czm_batchTable_planes2D_HIGH_w(batchId));
+ vec4 planes2D_low = unpackPlanes2D_LOW(czm_batchTable_planes2D_LOW_x(batchId),
+ czm_batchTable_planes2D_LOW_y(batchId),
+ czm_batchTable_planes2D_LOW_z(batchId),
+ czm_batchTable_planes2D_LOW_w(batchId));
+#else // UINT8_PACKING
vec4 planes2D_high = czm_batchTable_planes2D_HIGH(batchId);
vec4 planes2D_low = czm_batchTable_planes2D_LOW(batchId);
+#endif // UINT8_PACKING
// If the primitive is split across the IDL (planes2D_high.x > planes2D_high.w):
// - If this vertex is on the east side of the IDL (position3DLow.y > 0.0, comparison with position3DHigh may produce artifacts)
@@ -68,9 +161,24 @@ void main()
vec3 southEastCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(vec3(0.0, planes2D_high.w, planes2D_high.y), vec3(0.0, planes2D_low.w, planes2D_low.y))).xyz;
#else // COLUMBUS_VIEW_2D
// 3D case has smaller "plane extents," so planes encoded as a 64 bit position and 2 vec3s for distances/direction
+#ifdef UINT8_PACKING
+ vec3 low = southwest_LOW(czm_batchTable_southWest_LOW_x(batchId), czm_batchTable_southWest_LOW_y(batchId), czm_batchTable_southWest_LOW_z(batchId));
+ vec3 high = southwest_HIGH(czm_batchTable_southWest_HIGH_x(batchId), czm_batchTable_southWest_HIGH_y(batchId), czm_batchTable_southWest_HIGH_z(batchId));
+ vec3 southWestCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(high, low)).xyz;
+ vec3 northWestCorner = czm_normal * unpackExtent(
+ czm_batchTable_northward_x(batchId),
+ czm_batchTable_northward_y(batchId),
+ czm_batchTable_northward_z(batchId)) + southWestCorner;
+
+ vec3 southEastCorner = czm_normal * unpackExtent(
+ czm_batchTable_eastward_x(batchId),
+ czm_batchTable_eastward_y(batchId),
+ czm_batchTable_eastward_z(batchId)) + southWestCorner;
+#else // UINT8_PACKING
vec3 southWestCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(czm_batchTable_southWest_HIGH(batchId), czm_batchTable_southWest_LOW(batchId))).xyz;
vec3 northWestCorner = czm_normal * czm_batchTable_northward(batchId) + southWestCorner;
vec3 southEastCorner = czm_normal * czm_batchTable_eastward(batchId) + southWestCorner;
+#endif // UINT8_PACKING
#endif // COLUMBUS_VIEW_2D
vec3 eastWard = southEastCorner - southWestCorner;
diff --git a/Specs/Renderer/ContextSpec.js b/Specs/Renderer/ContextSpec.js
index d395ca7a7daf..273967a07865 100644
--- a/Specs/Renderer/ContextSpec.js
+++ b/Specs/Renderer/ContextSpec.js
@@ -173,6 +173,10 @@ describe('Renderer/Context', function() {
expect(context.floatingPointTexture).toBeDefined();
});
+ it('gets whether the texture float has 6 places of precision', function() {
+ expect(context.floatTextureSixPlaces).toBeDefined();
+ });
+
it('gets texture filter anisotropic extension', function() {
expect(context.textureFilterAnisotropic).toBeDefined();
});
diff --git a/Specs/Renderer/checkFloatTexturePrecisionSpec.js b/Specs/Renderer/checkFloatTexturePrecisionSpec.js
new file mode 100644
index 000000000000..c9224ed87f99
--- /dev/null
+++ b/Specs/Renderer/checkFloatTexturePrecisionSpec.js
@@ -0,0 +1,42 @@
+define([
+ 'Renderer/checkFloatTexturePrecision',
+ 'Renderer/PixelDatatype',
+ 'Specs/createContext'
+ ], function(
+ checkFloatTexturePrecision,
+ PixelDatatype,
+ createContext) {
+ 'use strict';
+
+describe('Renderer/checkFloatTexturePrecision', function() {
+
+ var context;
+
+ beforeAll(function() {
+ context = createContext();
+ });
+
+ afterAll(function() {
+ context.destroyForSpecs();
+ });
+
+ it('returns false when float textures are not available', function() {
+ expect(checkFloatTexturePrecision({ floatingPointTexture : false })).toBe(false);
+ });
+
+ it('returns false when float textures are of insufficient precision', function() {
+ if (!context.floatingPointTexture) {
+ return;
+ }
+
+ spyOn(checkFloatTexturePrecision, '_getFloatPixelType').and.callFake(function() {
+ return PixelDatatype.HALF_FLOAT;
+ });
+ spyOn(checkFloatTexturePrecision, '_getArray').and.callFake(function(array) {
+ return new Uint16Array(array);
+ });
+
+ expect(checkFloatTexturePrecision(context)).toBe(false);
+ });
+}, 'WebGL');
+});
diff --git a/Specs/Scene/ShadowVolumeAppearanceSpec.js b/Specs/Scene/ShadowVolumeAppearanceSpec.js
index 85936024fa8e..3b261cf41e2c 100644
--- a/Specs/Scene/ShadowVolumeAppearanceSpec.js
+++ b/Specs/Scene/ShadowVolumeAppearanceSpec.js
@@ -47,8 +47,10 @@ describe('Scene/ShadowVolumeAppearance', function() {
var largeTestRectangle = Rectangle.fromDegrees(-45.0, -45.0, 45.0, 45.0);
var smallTestRectangle = Rectangle.fromDegrees(-0.1, -0.1, 0.1, 0.1);
- var largeRectangleAttributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(largeTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection);
- var smallRectangleAttributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(smallTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection);
+ var largeRectangleAttributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(largeTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection, true);
+ var smallRectangleAttributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(smallTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection, true);
+ var largeRectangleAttributesBadFloats = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(largeTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection, false);
+ var smallRectangleAttributesBadFloats = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(smallTestRectangle, [0, 0, 0, 1, 1, 0], unitSphereEllipsoid, projection, false);
var perInstanceColorMaterialAppearance = new PerInstanceColorAppearance();
var flatPerInstanceColorMaterialAppearance = new PerInstanceColorAppearance({
@@ -123,88 +125,219 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(attribute.normalize).toEqual(false);
}
- it('provides attributes for computing texture coordinates using planes in 3D', function() {
- var attributes = smallRectangleAttributes;
-
- var southWest_LOW = attributes.southWest_LOW;
- var southWest_HIGH = attributes.southWest_HIGH;
- var eastward = attributes.eastward;
- var northward = attributes.northward;
-
- checkGeometryInstanceAttributeVec3(southWest_LOW);
- checkGeometryInstanceAttributeVec3(southWest_HIGH);
- checkGeometryInstanceAttributeVec3(eastward);
- checkGeometryInstanceAttributeVec3(northward);
-
- // We're using a unit sphere, so expect all HIGH values to be basically 0
- // and LOW value to be within a small cone around UNIT_X
- expect(southWest_HIGH.value[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(southWest_HIGH.value[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(southWest_HIGH.value[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ function clampAndMagnitude(signedVec4Attribute) {
+ var signedVec4 = signedVec4Attribute.value;
+ var unsigned = signedVec4.slice();
+ unsigned[0] = signedVec4[0] < 128.0 ? signedVec4[0] : (255.0 - signedVec4[0]);
+ unsigned[0] = Math.floor(0.5 + unsigned[0]);
+ unsigned[1] = Math.floor(0.5 + unsigned[1]);
+ unsigned[2] = Math.floor(0.5 + unsigned[2]);
+ unsigned[3] = Math.floor(0.5 + unsigned[3]);
+ return unsigned;
+ }
- expect(southWest_LOW.value[0]).toBeGreaterThan(Math.cos(CesiumMath.toRadians(0.2)));
+ function unpackLowLessThan100k(signedVec4Attribute) {
+ var signed = signedVec4Attribute.value;
+ var unsigned = clampAndMagnitude(signedVec4Attribute);
+ return (1000.0 * unsigned[0] + 10.0 * unsigned[1] + 0.1 * unsigned[2] + 0.001 * unsigned[3]) * (signed[0] < 128.0 ? 1.0 : -1.0);
+ }
- // Expect eastward and northward to be unit-direction vectors in the ENU coordinate system at the rectangle center
- var smallRectangleCenter = Cartographic.toCartesian(Rectangle.center(smallTestRectangle), unitSphereEllipsoid);
- var enuMatrix = Transforms.eastNorthUpToFixedFrame(smallRectangleCenter, unitSphereEllipsoid);
- var inverseEnu = Matrix4.inverse(enuMatrix, new Matrix4());
+ function unpackHighMagLessThan100Million(signedVec4Attribute) {
+ var signed = signedVec4Attribute.value;
+ var unsigned = clampAndMagnitude(signedVec4Attribute);
+ return (1000000.0 * unsigned[0] + 10000.0 * unsigned[1] + 100.0 * unsigned[2] + unsigned[3]) * (signed[0] < 128.0 ? 1.0 : -1.0);
+ }
- var eastwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(eastward.value), new Cartesian3());
- eastwardENU = Cartesian3.normalize(eastwardENU, eastwardENU);
- expect(Cartesian3.equalsEpsilon(eastwardENU, Cartesian3.UNIT_X, CesiumMath.EPSILON7)).toBe(true);
+ function unpackLowLessThan1000k(signedVec4Attribute) {
+ var signed = signedVec4Attribute.value;
+ var unsigned = clampAndMagnitude(signedVec4Attribute);
+ return (10000.0 * unsigned[0] + 100.0 * unsigned[1] + unsigned[2] + 0.01 * unsigned[3]) * (signed[0] < 128.0 ? 1.0 : -1.0);
+ }
- var northwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(northward.value), new Cartesian3());
- northwardENU = Cartesian3.normalize(northwardENU, northwardENU);
- expect(Cartesian3.equalsEpsilon(northwardENU, Cartesian3.UNIT_Y, CesiumMath.EPSILON7)).toBe(true);
+ describe('floating point textures reliable', function () {
+ it('provides attributes for computing texture coordinates using planes in 3D', function() {
+ var attributes = smallRectangleAttributes;
+
+ var southWest_LOW = attributes.southWest_LOW;
+ var southWest_HIGH = attributes.southWest_HIGH;
+ var eastward = attributes.eastward;
+ var northward = attributes.northward;
+
+ checkGeometryInstanceAttributeVec3(southWest_LOW);
+ checkGeometryInstanceAttributeVec3(southWest_HIGH);
+ checkGeometryInstanceAttributeVec3(eastward);
+ checkGeometryInstanceAttributeVec3(northward);
+
+ // We're using a unit sphere, so expect all HIGH values to be basically 0
+ // and LOW value to be within a small cone around UNIT_X
+ expect(southWest_HIGH.value[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(southWest_HIGH.value[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(southWest_HIGH.value[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+
+ expect(southWest_LOW.value[0]).toBeGreaterThan(Math.cos(CesiumMath.toRadians(0.2)));
+
+ // Expect eastward and northward to be unit-direction vectors in the ENU coordinate system at the rectangle center
+ var smallRectangleCenter = Cartographic.toCartesian(Rectangle.center(smallTestRectangle), unitSphereEllipsoid);
+ var enuMatrix = Transforms.eastNorthUpToFixedFrame(smallRectangleCenter, unitSphereEllipsoid);
+ var inverseEnu = Matrix4.inverse(enuMatrix, new Matrix4());
+
+ var eastwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(eastward.value), new Cartesian3());
+ eastwardENU = Cartesian3.normalize(eastwardENU, eastwardENU);
+ expect(Cartesian3.equalsEpsilon(eastwardENU, Cartesian3.UNIT_X, CesiumMath.EPSILON7)).toBe(true);
+
+ var northwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(northward.value), new Cartesian3());
+ northwardENU = Cartesian3.normalize(northwardENU, northwardENU);
+ expect(Cartesian3.equalsEpsilon(northwardENU, Cartesian3.UNIT_Y, CesiumMath.EPSILON7)).toBe(true);
+ });
+
+ it('provides attributes for computing planes in 2D and Columbus View', function() {
+ var planes2D_HIGH = largeRectangleAttributes.planes2D_HIGH;
+ var planes2D_LOW = largeRectangleAttributes.planes2D_LOW;
+
+ expect(planes2D_HIGH.componentDatatype).toEqual(ComponentDatatype.FLOAT);
+ expect(planes2D_HIGH.componentsPerAttribute).toEqual(4);
+ expect(planes2D_HIGH.normalize).toEqual(false);
+
+ // Because using a unit sphere expect all HIGH values to be basically 0
+ var highValue = planes2D_HIGH.value;
+ expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+
+ expect(planes2D_LOW.componentDatatype).toEqual(ComponentDatatype.FLOAT);
+ expect(planes2D_LOW.componentsPerAttribute).toEqual(4);
+ expect(planes2D_LOW.normalize).toEqual(false);
+
+ var cartographic = Cartographic.fromDegrees(-45, -45, 0.0); // southwest corner
+ var southwestCartesian = projection.project(cartographic);
+ var lowValue = planes2D_LOW.value;
+ expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON7);
+ expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON7);
+ expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON7);
+ expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON7);
+
+ // Small case
+ // Because using a unit sphere expect all HIGH values to be basically 0
+ highValue = smallRectangleAttributes.planes2D_HIGH.value;
+ expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+ expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
+
+ cartographic = Cartographic.fromDegrees(-0.1, -0.1, 0.0); // southwest corner
+ southwestCartesian = projection.project(cartographic);
+ lowValue = smallRectangleAttributes.planes2D_LOW.value;
+ expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON7);
+ expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON7);
+ expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON7);
+ expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON7);
+ });
});
- it('provides attributes for computing planes in 2D and Columbus View', function() {
- var planes2D_HIGH = largeRectangleAttributes.planes2D_HIGH;
- var planes2D_LOW = largeRectangleAttributes.planes2D_LOW;
-
- expect(planes2D_HIGH.componentDatatype).toEqual(ComponentDatatype.FLOAT);
- expect(planes2D_HIGH.componentsPerAttribute).toEqual(4);
- expect(planes2D_HIGH.normalize).toEqual(false);
-
- // Because using a unit sphere expect all HIGH values to be basically 0
- var highValue = planes2D_HIGH.value;
- expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
-
- expect(planes2D_LOW.componentDatatype).toEqual(ComponentDatatype.FLOAT);
- expect(planes2D_LOW.componentsPerAttribute).toEqual(4);
- expect(planes2D_LOW.normalize).toEqual(false);
-
- var cartographic = Cartographic.fromDegrees(-45, -45, 0.0); // southwest corner
- var southwestCartesian = projection.project(cartographic);
- var lowValue = planes2D_LOW.value;
- expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON7);
- expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON7);
- expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON7);
- expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON7);
-
- // Small case
- // Because using a unit sphere expect all HIGH values to be basically 0
- highValue = smallRectangleAttributes.planes2D_HIGH.value;
- expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
- expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON7);
-
- cartographic = Cartographic.fromDegrees(-0.1, -0.1, 0.0); // southwest corner
- southwestCartesian = projection.project(cartographic);
- lowValue = smallRectangleAttributes.planes2D_LOW.value;
- expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON7);
- expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON7);
- expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON7);
- expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON7);
+ describe('floating point textures unreliable', function () {
+ it('provides attributes for computing texture coordinates using planes in 3D', function() {
+ var attributes = smallRectangleAttributesBadFloats;
+
+ var southWest_LOW = [0, 0, 0];
+ southWest_LOW[0] = unpackLowLessThan100k(attributes.southWest_LOW_x);
+ southWest_LOW[1] = unpackLowLessThan100k(attributes.southWest_LOW_y);
+ southWest_LOW[2] = unpackLowLessThan100k(attributes.southWest_LOW_z);
+
+ var southWest_HIGH = [0, 0, 0];
+ southWest_HIGH[0] = unpackHighMagLessThan100Million(attributes.southWest_HIGH_x);
+ southWest_HIGH[1] = unpackHighMagLessThan100Million(attributes.southWest_HIGH_y);
+ southWest_HIGH[2] = unpackHighMagLessThan100Million(attributes.southWest_HIGH_z);
+
+ var eastward = [0, 0, 0];
+ eastward[0] = unpackLowLessThan1000k(attributes.eastward_x);
+ eastward[1] = unpackLowLessThan1000k(attributes.eastward_y);
+ eastward[2] = unpackLowLessThan1000k(attributes.eastward_z);
+
+ var northward = [0, 0, 0];
+ northward[0] = unpackLowLessThan1000k(attributes.northward_x);
+ northward[1] = unpackLowLessThan1000k(attributes.northward_y);
+ northward[2] = unpackLowLessThan1000k(attributes.northward_z);
+
+ // We're using a unit sphere, so expect all HIGH values to be basically 0
+ // and LOW value to be within a small cone around UNIT_X
+ expect(southWest_HIGH[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(southWest_HIGH[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(southWest_HIGH[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+
+ expect(southWest_LOW[0]).toBeGreaterThan(Math.cos(CesiumMath.toRadians(0.2)));
+
+ // Expect eastward and northward to be unit-direction vectors in the ENU coordinate system at the rectangle center
+ var smallRectangleCenter = Cartographic.toCartesian(Rectangle.center(smallTestRectangle), unitSphereEllipsoid);
+ var enuMatrix = Transforms.eastNorthUpToFixedFrame(smallRectangleCenter, unitSphereEllipsoid);
+ var inverseEnu = Matrix4.inverse(enuMatrix, new Matrix4());
+
+ var eastwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(eastward), new Cartesian3());
+ eastwardENU = Cartesian3.normalize(eastwardENU, eastwardENU);
+ expect(Cartesian3.equalsEpsilon(eastwardENU, Cartesian3.UNIT_X, CesiumMath.EPSILON2)).toBe(true);
+
+ var northwardENU = Matrix4.multiplyByPointAsVector(inverseEnu, Cartesian3.fromArray(northward), new Cartesian3());
+ northwardENU = Cartesian3.normalize(northwardENU, northwardENU);
+ expect(Cartesian3.equalsEpsilon(northwardENU, Cartesian3.UNIT_Y, CesiumMath.EPSILON2)).toBe(true);
+ });
+
+ it('provides attributes for computing planes in 2D and Columbus View', function() {
+ var attributes = largeRectangleAttributesBadFloats;
+ var highValue = [0, 0, 0, 0];
+ highValue[0] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_x);
+ highValue[1] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_y);
+ highValue[2] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_z);
+ highValue[3] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_w);
+
+ var lowValue = [0, 0, 0, 0];
+ lowValue[0] = unpackLowLessThan100k(attributes.planes2D_LOW_x);
+ lowValue[1] = unpackLowLessThan100k(attributes.planes2D_LOW_y);
+ lowValue[2] = unpackLowLessThan100k(attributes.planes2D_LOW_z);
+ lowValue[3] = unpackLowLessThan100k(attributes.planes2D_LOW_w);
+
+ // Because using a unit sphere expect all HIGH values to be basically 0
+ expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+
+ var cartographic = Cartographic.fromDegrees(-45, -45, 0.0); // southwest corner
+ var southwestCartesian = projection.project(cartographic);
+ expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON2);
+ expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON2);
+ expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON2);
+ expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON2);
+
+ // Small case
+ attributes = smallRectangleAttributesBadFloats;
+ // Because using a unit sphere expect all HIGH values to be basically 0
+ highValue[0] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_x);
+ highValue[1] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_y);
+ highValue[2] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_z);
+ highValue[3] = unpackHighMagLessThan100Million(attributes.planes2D_HIGH_w);
+
+ expect(highValue[0]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[1]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[2]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+ expect(highValue[3]).toEqualEpsilon(0.0, CesiumMath.EPSILON2);
+
+ cartographic = Cartographic.fromDegrees(-0.1, -0.1, 0.0); // southwest corner
+ southwestCartesian = projection.project(cartographic);
+ lowValue[0] = unpackLowLessThan100k(attributes.planes2D_LOW_x);
+ lowValue[1] = unpackLowLessThan100k(attributes.planes2D_LOW_y);
+ lowValue[2] = unpackLowLessThan100k(attributes.planes2D_LOW_z);
+ lowValue[3] = unpackLowLessThan100k(attributes.planes2D_LOW_w);
+
+ expect(lowValue[0]).toEqualEpsilon(southwestCartesian.x, CesiumMath.EPSILON2);
+ expect(lowValue[1]).toEqualEpsilon(southwestCartesian.y, CesiumMath.EPSILON2);
+ expect(lowValue[2]).toEqualEpsilon(-southwestCartesian.y, CesiumMath.EPSILON2);
+ expect(lowValue[3]).toEqualEpsilon(-southwestCartesian.x, CesiumMath.EPSILON2);
+ });
});
it('provides attributes for rotating texture coordinates', function() {
// 90 degree rotation of a square, so "max" in Y direction is (0,0), "max" in X direction is (1,1)
- var attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(smallTestRectangle, [1, 0, 0, 0, 1, 1], unitSphereEllipsoid, projection, 0.0);
+ var attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(smallTestRectangle, [1, 0, 0, 0, 1, 1], unitSphereEllipsoid, projection, false, 0.0);
var uMaxVmax = attributes.uMaxVmax;
var uvMinAndExtents = attributes.uvMinAndExtents;
@@ -233,11 +366,15 @@ describe('Scene/ShadowVolumeAppearance', function() {
it('checks for spherical extent attributes', function() {
expect(ShadowVolumeAppearance.hasAttributesForSphericalExtents(smallRectangleAttributes)).toBe(false);
expect(ShadowVolumeAppearance.hasAttributesForSphericalExtents(largeRectangleAttributes)).toBe(true);
+ expect(ShadowVolumeAppearance.hasAttributesForSphericalExtents(smallRectangleAttributesBadFloats)).toBe(false);
+ expect(ShadowVolumeAppearance.hasAttributesForSphericalExtents(largeRectangleAttributesBadFloats)).toBe(true);
});
it('checks for planar texture coordinate attributes', function() {
expect(ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(smallRectangleAttributes)).toBe(true);
expect(ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(largeRectangleAttributes)).toBe(false);
+ expect(ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(smallRectangleAttributesBadFloats)).toBe(true);
+ expect(ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes(largeRectangleAttributesBadFloats)).toBe(false);
});
it('checks if a rectangle should use spherical texture coordinates', function() {
@@ -245,9 +382,36 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(ShadowVolumeAppearance.shouldUseSphericalCoordinates(largeTestRectangle)).toBe(true);
});
+ it('creates vertex shaders based on whether or not float textures are used for high/low positions', function() {
+ // Check defines
+ var sphericalTexturedAppearanceUnsafe = new ShadowVolumeAppearance(true, false, textureMaterialAppearance, false);
+ var shaderSource = sphericalTexturedAppearanceUnsafe.createVertexShader([], testVs, false, projection);
+ var defines = shaderSource.defines;
+ expect(defines.length).toEqual(3);
+ expect(defines.indexOf('UINT8_PACKING')).not.toEqual(-1);
+
+ // 2D variant
+ shaderSource = sphericalTexturedAppearanceUnsafe.createVertexShader([], testVs, true, projection);
+ defines = shaderSource.defines;
+ expect(defines.length).toEqual(7);
+ expect(defines.indexOf('UINT8_PACKING')).not.toEqual(-1);
+
+ var sphericalTexturedAppearanceSafe = new ShadowVolumeAppearance(true, false, textureMaterialAppearance, true);
+ shaderSource = sphericalTexturedAppearanceSafe.createVertexShader([], testVs, false, projection);
+ defines = shaderSource.defines;
+ expect(defines.length).toEqual(2);
+ expect(defines.indexOf('UINT8_PACKING')).toEqual(-1);
+
+ // 2D variant
+ shaderSource = sphericalTexturedAppearanceSafe.createVertexShader([], testVs, true, projection);
+ defines = shaderSource.defines;
+ expect(defines.length).toEqual(6);
+ expect(defines.indexOf('UINT8_PACKING')).toEqual(-1);
+ });
+
it('creates vertex shaders for color', function() {
// Check defines
- var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance);
+ var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance, true);
var shaderSource = sphericalTexturedAppearance.createVertexShader([], testVs, false, projection);
var defines = shaderSource.defines;
expect(defines.length).toEqual(2);
@@ -267,7 +431,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.indexOf(westMostYlowDefine)).not.toEqual(-1);
// Unculled color appearance - no texcoords at all
- var sphericalUnculledColorAppearance = new ShadowVolumeAppearance(false, false, perInstanceColorMaterialAppearance);
+ var sphericalUnculledColorAppearance = new ShadowVolumeAppearance(false, false, perInstanceColorMaterialAppearance, true);
shaderSource = sphericalUnculledColorAppearance.createVertexShader([], testVs, false, projection);
defines = shaderSource.defines;
expect(defines.length).toEqual(1);
@@ -286,7 +450,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.indexOf('PER_INSTANCE_COLOR')).not.toEqual(-1);
// Planar textured, without culling
- var planarTexturedAppearance = new ShadowVolumeAppearance(false, true, textureMaterialAppearance);
+ var planarTexturedAppearance = new ShadowVolumeAppearance(false, true, textureMaterialAppearance, true);
shaderSource = planarTexturedAppearance.createVertexShader([], testVs, false, projection);
defines = shaderSource.defines;
expect(defines.indexOf('TEXTURE_COORDINATES')).not.toEqual(-1);
@@ -307,7 +471,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
it('creates vertex shaders for pick', function() {
// Check defines
- var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance);
+ var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance, true);
var shaderSource = sphericalTexturedAppearance.createPickVertexShader([], testVs, false, projection);
var defines = shaderSource.defines;
expect(defines.length).toEqual(2);
@@ -327,7 +491,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.indexOf(westMostYlowDefine)).not.toEqual(-1);
// Unculled color appearance - no texcoords at all
- var sphericalUnculledColorAppearance = new ShadowVolumeAppearance(false, false, perInstanceColorMaterialAppearance);
+ var sphericalUnculledColorAppearance = new ShadowVolumeAppearance(false, false, perInstanceColorMaterialAppearance, true);
shaderSource = sphericalUnculledColorAppearance.createPickVertexShader([], testVs, false, projection);
defines = shaderSource.defines;
expect(defines.length).toEqual(0);
@@ -344,7 +508,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.length).toEqual(4);
// Planar textured, without culling
- var planarTexturedAppearance = new ShadowVolumeAppearance(false, true, textureMaterialAppearance);
+ var planarTexturedAppearance = new ShadowVolumeAppearance(false, true, textureMaterialAppearance, true);
shaderSource = planarTexturedAppearance.createPickVertexShader([], testVs, false, projection);
defines = shaderSource.defines;
expect(defines.length).toEqual(0);
@@ -362,7 +526,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
it('creates fragment shaders for color and pick', function() {
// Check defines
- var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance);
+ var sphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, textureMaterialAppearance, true);
var shaderSource = sphericalTexturedAppearance.createFragmentShader(false);
var defines = shaderSource.defines;
@@ -395,7 +559,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.length).toEqual(9);
// Culling with planar texture coordinates on a per-color material
- var planarColorAppearance = new ShadowVolumeAppearance(true, true, perInstanceColorMaterialAppearance);
+ var planarColorAppearance = new ShadowVolumeAppearance(true, true, perInstanceColorMaterialAppearance, true);
shaderSource = planarColorAppearance.createFragmentShader(false);
defines = shaderSource.defines;
@@ -419,7 +583,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.length).toEqual(5);
// Flat
- var flatSphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, flatTextureMaterialAppearance);
+ var flatSphericalTexturedAppearance = new ShadowVolumeAppearance(true, false, flatTextureMaterialAppearance, true);
shaderSource = flatSphericalTexturedAppearance.createFragmentShader(false);
defines = shaderSource.defines;
expect(defines.indexOf('SPHERICAL')).not.toEqual(-1);
@@ -434,7 +598,7 @@ describe('Scene/ShadowVolumeAppearance', function() {
expect(defines.indexOf('FLAT')).not.toEqual(-1);
expect(defines.length).toEqual(10);
- var flatSphericalColorAppearance = new ShadowVolumeAppearance(false, false, flatPerInstanceColorMaterialAppearance);
+ var flatSphericalColorAppearance = new ShadowVolumeAppearance(false, false, flatPerInstanceColorMaterialAppearance, true);
shaderSource = flatSphericalColorAppearance.createFragmentShader(false);
defines = shaderSource.defines;
expect(defines.indexOf('SPHERICAL')).not.toEqual(-1);