diff --git a/CHANGES.md b/CHANGES.md index aa47624113fa..30656ca51f19 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Change Log * Added `tileset.uri`, `tileset.show`, and `tileset.maximumScreenSpaceError` properties to CZML processing for loading 3D Tiles. * Added `Color.lerp` for linearly interpolating between two RGB colors. [#8607](https://github.com/CesiumGS/cesium/pull/8607) * `CesiumTerrainProvider` now supports terrain tiles using a `WebMercatorTilingScheme` by specifying `"projection": "EPSG:3857"` in `layer.json`. It also now supports numbering tiles from the North instead of the South by specifying `"scheme": "slippyMap"` in `layer.json`. [#8563](https://github.com/CesiumGS/cesium/pull/8563) +* Added basic support for `isNaN`, `isFinite`, `null`, and `undefined` in the 3D Tiles styling GLSL backend for point clouds. [#8621](https://github.com/CesiumGS/cesium/pull/8621) ##### Fixes :wrench: diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js index a4bf2cab2dac..16dc89873443 100644 --- a/Source/Scene/Expression.js +++ b/Source/Scene/Expression.js @@ -1559,6 +1559,8 @@ import ExpressionNodeType from './ExpressionNodeType.js'; return expressions; } + var nullSentinel = 'czm_infinity'; // null just needs to be some sentinel value that will cause "[expression] === null" to be false in nearly all cases. GLSL doesn't have a NaN constant so use czm_infinity. + Node.prototype.getShaderExpression = function(attributePrefix, shaderState, parent) { var color; var left; @@ -1603,7 +1605,13 @@ import ExpressionNodeType from './ExpressionNodeType.js'; return 'floor(' + left + ' + 0.5)'; } else if (defined(unaryFunctions[value])) { return value + '(' + left + ')'; - } else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) { + } else if (value === 'isNaN') { + // In GLSL 2.0 use isnan instead + return '(' + left + ' != ' + left + ')'; + } else if (value === 'isFinite') { + // In GLSL 2.0 use isinf instead. GLSL doesn't have an infinity constant so use czm_infinity which is an arbitrarily big enough number. + return '(abs(' + left + ') < czm_infinity)'; + } else if ((value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) { throw new RuntimeError('Error generating style shader: "' + value + '" is not supported.'); } else if (defined(unaryFunctions[value])) { return value + '(' + left + ')'; @@ -1659,7 +1667,7 @@ import ExpressionNodeType from './ExpressionNodeType.js'; case ExpressionNodeType.VARIABLE_IN_STRING: throw new RuntimeError('Error generating style shader: Converting a variable to a string is not supported.'); case ExpressionNodeType.LITERAL_NULL: - throw new RuntimeError('Error generating style shader: null is not supported.'); + return nullSentinel; case ExpressionNodeType.LITERAL_BOOLEAN: return value ? 'true' : 'false'; case ExpressionNodeType.LITERAL_NUMBER: @@ -1745,7 +1753,7 @@ import ExpressionNodeType from './ExpressionNodeType.js'; case ExpressionNodeType.LITERAL_REGEX: throw new RuntimeError('Error generating style shader: Regular expressions are not supported.'); case ExpressionNodeType.LITERAL_UNDEFINED: - throw new RuntimeError('Error generating style shader: undefined is not supported.'); + return nullSentinel; case ExpressionNodeType.BUILTIN_VARIABLE: if (value === 'tiles3d_tileset_time') { return 'u_time'; diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index 32bd03f59abc..b5474f81b1c9 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -3546,6 +3546,34 @@ describe('Scene/Expression', function() { expect(shaderExpression).toEqual(expected); }); + it('gets shader expression for isNaN', function() { + var expression = new Expression('isNaN(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(1.0 != 1.0)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for isFinite', function() { + var expression = new Expression('isFinite(1.0)'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(abs(1.0) < czm_infinity)'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for null', function() { + var expression = new Expression('null'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'czm_infinity'; + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for undefined', function() { + var expression = new Expression('undefined'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = 'czm_infinity'; + expect(shaderExpression).toEqual(expected); + }); + it('throws when getting shader expression for regex', function() { var expression = new Expression('regExp("a").test("abc")'); expect(function() { @@ -3610,34 +3638,6 @@ describe('Scene/Expression', function() { }).toThrowRuntimeError(); }); - it('throws when getting shader expression for literal undefined', function() { - var expression = new Expression('undefined'); - expect(function() { - return expression.getShaderExpression('', {}); - }).toThrowRuntimeError(); - }); - - it('throws when getting shader expression for literal null', function() { - var expression = new Expression('null'); - expect(function() { - return expression.getShaderExpression('', {}); - }).toThrowRuntimeError(); - }); - - it('throws when getting shader expression for isNaN', function() { - var expression = new Expression('isNaN(1.0)'); - expect(function() { - return expression.getShaderExpression('', {}); - }).toThrowRuntimeError(); - }); - - it('throws when getting shader expression for isFinite', function() { - var expression = new Expression('isFinite(1.0)'); - expect(function() { - return expression.getShaderExpression('', {}); - }).toThrowRuntimeError(); - }); - it('throws when getting shader expression for isExactClass', function() { var expression = new Expression('isExactClass("door")'); expect(function() {