From 12cbbfe11d8186edce33aeb9334cd65485439ba3 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 14 Dec 2016 16:27:39 -0500 Subject: [PATCH 1/3] Support vectors in the styling language --- .../gallery/3D Tiles Point Cloud Styling.html | 15 +- Source/Scene/Expression.js | 222 ++++++++++---- Source/Scene/ExpressionNodeType.js | 7 +- Source/Scene/PointCloud3DTileContent.js | 45 ++- Specs/Scene/ExpressionSpec.js | 272 +++++++++++++++++- 5 files changed, 485 insertions(+), 76 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html index 7d2a15bae834..6362b6edfcf4 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html @@ -130,18 +130,18 @@ }); addStyle('Min and Max', { - color : "rgb(min(${POSITION}[0], 0.75) * 255, max(${POSITION}[2], 0.25) * 255, 255)", + color : "rgb(min(${POSITION}.x, 0.75) * 255, max(${POSITION}.z, 0.25) * 255, 255)", pointSize : "5" }); +// secondaryColor is originally a vec3 - so add vec4(0.0, 0.0, 0.0, 1.0) to make the point cloud opaque addStyle('Secondary Color', { color : { - expression : "[${secondaryColor}[0], ${secondaryColor}[1], ${secondaryColor}[2], 1.0]", conditions : [ - ["${id} < 250", "${expression}"], - ["${id} < 500", "${expression} * ${expression}"], - ["${id} < 750", "${expression} / 5.0"], - ["${id} < 1000", "rgb(0, 0, Number(${expression}[0] < 0.5) * 255)"] + ["${id} < 250", "${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], + ["${id} < 500", "${secondaryColor} * ${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], + ["${id} < 750", "${secondaryColor} / 5.0 + vec4(0.0, 0.0, 0.0, 1.0)"], + ["${id} < 1000", "rgb(0, 0, Number(${secondaryColor}.x < 0.5) * 255)"] ] } }); @@ -154,8 +154,9 @@ show : "${POSITION}[0] > 0.5 || ${POSITION}[1] > 0.5 || ${POSITION}[2] > 0.5" }); +// POSITION contains 0 as its last component, so add 1.0 to make the point cloud opaque addStyle('Color based on position', { - color : "rgb(${POSITION}[0] * 255, ${POSITION}[1] * 255, ${POSITION}[2] * 255)" + color : "${POSITION} + vec4(0.0, 0.0, 0.0, 1.0)" }); addStyle('Style point size', { diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js index 7edb2d791ee2..b9d3de154ff9 100644 --- a/Source/Scene/Expression.js +++ b/Source/Scene/Expression.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../Core/Cartesian4', '../Core/Color', '../Core/defined', '../Core/defineProperties', @@ -9,6 +10,7 @@ define([ '../ThirdParty/jsep', './ExpressionNodeType' ], function( + Cartesian4, Color, defined, defineProperties, @@ -32,8 +34,11 @@ define([ var ScratchStorage = { scratchColorIndex : 0, scratchColors : [new Color()], + scratchCartesianIndex : 0, + scratchCartesians : [new Cartesian4()], reset : function() { this.scratchColorIndex = 0; + this.scratchCartesianIndex = 0; }, getColor : function() { if (this.scratchColorIndex >= this.scratchColors.length) { @@ -42,6 +47,14 @@ define([ var scratchColor = this.scratchColors[this.scratchColorIndex]; ++this.scratchColorIndex; return scratchColor; + }, + getCartesian : function() { + if (this.scratchCartesianIndex >= this.scratchCartesians.length) { + this.scratchCartesians.push(new Cartesian4()); + } + var scratchCartesian = this.scratchCartesians[this.scratchCartesianIndex]; + ++this.scratchCartesianIndex; + return scratchCartesian; } }; @@ -138,16 +151,19 @@ define([ * is of type Boolean, Number, or String, the corresponding JavaScript * primitive type will be returned. If the result is a RegExp, a Javascript RegExp * object will be returned. If the result is a Color, a {@link Color} object will be returned. + * If the result is a Cartesian4, a {@link Cartesian4} object will be returned. * * @param {FrameState} frameState The frame state. * @param {Cesium3DTileFeature} feature The feature who's properties may be used as variables in the expression. - * @returns {Boolean|Number|String|Color|RegExp} The result of evaluating the expression. + * @returns {Boolean|Number|String|Color|Cartesian4|RegExp} The result of evaluating the expression. */ Expression.prototype.evaluate = function(frameState, feature) { ScratchStorage.reset(); var result = this._runtimeAst.evaluate(frameState, feature); if (result instanceof Color) { return Color.clone(result); + } else if (result instanceof Cartesian4) { + return Cartesian4.clone(result); } return result; }; @@ -352,6 +368,14 @@ define([ createRuntimeAst(expression, args[3]) ]; return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); + } else if (call === 'vec2' || call === 'vec3' || call === 'vec4') { + val = [ + defined(args[0]) ? createRuntimeAst(expression, args[0]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), + defined(args[1]) ? createRuntimeAst(expression, args[1]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), + defined(args[2]) ? createRuntimeAst(expression, args[2]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), + defined(args[3]) ? createRuntimeAst(expression, args[3]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0) + ]; + return new Node(ExpressionNodeType.LITERAL_VECTOR, call, val); } else if (call === 'isNaN' || call === 'isFinite') { if (args.length === 0) { if (call === 'isNaN') { @@ -484,12 +508,14 @@ define([ } function parseMemberExpression(expression, ast) { + var val; var obj = createRuntimeAst(expression, ast.object); if (ast.computed) { - var val = createRuntimeAst(expression, ast.property); + val = createRuntimeAst(expression, ast.property); return new Node(ExpressionNodeType.MEMBER, 'brackets', obj, val); } else { - return new Node(ExpressionNodeType.MEMBER, 'dot', obj, ast.property.name); + val = new Node(ExpressionNodeType.LITERAL_STRING, ast.property.name); + return new Node(ExpressionNodeType.MEMBER, 'dot', obj, val); } } @@ -660,6 +686,8 @@ define([ node.evaluate = node._evaluateVariableString; } else if (node._type === ExpressionNodeType.LITERAL_COLOR) { node.evaluate = node._evaluateLiteralColor; + } else if (node._type === ExpressionNodeType.LITERAL_VECTOR) { + node.evaluate = node._evaluateLiteralVector; } else if (node._type === ExpressionNodeType.LITERAL_STRING) { node.evaluate = node._evaluateLiteralString; } else if (node._type === ExpressionNodeType.REGEX) { @@ -702,42 +730,53 @@ define([ if (!defined(args)) { return Color.fromBytes(255, 255, 255, 255, result); } else if (args.length > 1) { - Color.fromCssColorString(args[0].evaluate(frameState, feature, result), result); - result.alpha = args[1].evaluate(frameState, feature, result); + Color.fromCssColorString(args[0].evaluate(frameState, feature), result); + result.alpha = args[1].evaluate(frameState, feature); } else { - Color.fromCssColorString(args[0].evaluate(frameState, feature, result), result); + Color.fromCssColorString(args[0].evaluate(frameState, feature), result); } } else if (this._value === 'rgb') { Color.fromBytes( - args[0].evaluate(frameState, feature, result), - args[1].evaluate(frameState, feature, result), - args[2].evaluate(frameState, feature, result), + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), 255, result); } else if (this._value === 'rgba') { // convert between css alpha (0 to 1) and cesium alpha (0 to 255) - var a = args[3].evaluate(frameState, feature, result) * 255; + var a = args[3].evaluate(frameState, feature) * 255; Color.fromBytes( - args[0].evaluate(frameState, feature, result), - args[1].evaluate(frameState, feature, result), - args[2].evaluate(frameState, feature, result), + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), a, result); } else if (this._value === 'hsl') { Color.fromHsl( - args[0].evaluate(frameState, feature, result), - args[1].evaluate(frameState, feature, result), - args[2].evaluate(frameState, feature, result), + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), 1.0, result); } else if (this._value === 'hsla') { Color.fromHsl( - args[0].evaluate(frameState, feature, result), - args[1].evaluate(frameState, feature, result), - args[2].evaluate(frameState, feature, result), - args[3].evaluate(frameState, feature, result), + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + args[3].evaluate(frameState, feature), result); } return result; }; + Node.prototype._evaluateLiteralVector = function(frameState, feature) { + var result = ScratchStorage.getCartesian(); + var args = this._left; + return Cartesian4.fromElements( + args[0].evaluate(frameState, feature), + args[1].evaluate(frameState, feature), + args[2].evaluate(frameState, feature), + args[3].evaluate(frameState, feature), + result); + }; + Node.prototype._evaluateLiteralString = function(frameState, feature) { return this._value; }; @@ -769,25 +808,65 @@ define([ // PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime Node.prototype._evaluateMemberDot = function(frameState, feature) { - if(checkFeature(this._left)) { - return feature.getProperty(this._right); + if (checkFeature(this._left)) { + return feature.getProperty(this._right.evaluate(frameState, feature)); } var property = this._left.evaluate(frameState, feature); if (!defined(property)) { return undefined; } - return property[this._right]; + + var member = this._right.evaluate(frameState, feature); + if (property instanceof Color) { + // Color components may be accessed with .x, .y, .z, .w and implicitly with .red, .green, .blue, .alpha + if (member === 'x') { + return property.red; + } else if (member === 'y') { + return property.green; + } else if (member === 'z') { + return property.blue; + } else if (member === 'w') { + return property.alpha; + } + } + + return property[member]; }; Node.prototype._evaluateMemberBrackets = function(frameState, feature) { - if(checkFeature(this._left)) { + if (checkFeature(this._left)) { return feature.getProperty(this._right.evaluate(frameState, feature)); } var property = this._left.evaluate(frameState, feature); if (!defined(property)) { return undefined; } - return property[this._right.evaluate(frameState, feature)]; + + var member = this._right.evaluate(frameState, feature); + if (property instanceof Color) { + // Color components may be accessed with [0][1][2][3], ['x']['y']['z']['w'], and implicitly with ['red']['green']['blue']['alpha'] + if (member === 0 || member === 'x') { + return property.red; + } else if (member === 1 || member === 'y') { + return property.green; + } else if (member === 2 || member === 'z') { + return property.blue; + } else if (member === 3 || member === 'w') { + return property.alpha; + } + } else if (property instanceof Cartesian4) { + // Vector components may be accessed with [0][1][2][3] and implicitly with ['x']['y']['z']['w'] + if (member === 0) { + return property.x; + } else if (member === 1) { + return property.y; + } else if (member === 2) { + return property.z; + } else if (member === 3) { + return property.w; + } + } + return property[member]; }; Node.prototype._evaluateArray = function(frameState, feature) { @@ -806,11 +885,19 @@ define([ }; Node.prototype._evaluateNegative = function(frameState, feature) { - return -(this._left.evaluate(frameState, feature)); + var left = this._left.evaluate(frameState, feature); + if (left instanceof Cartesian4) { + return Cartesian4.negate(left, ScratchStorage.getCartesian()); + } + return -left; }; Node.prototype._evaluatePositive = function(frameState, feature) { - return +(this._left.evaluate(frameState, feature)); + var left = this._left.evaluate(frameState, feature); + if ((left instanceof Color) || (left instanceof Cartesian4)) { + return left; + } + return +left; }; Node.prototype._evaluateLessThan = function(frameState, feature) { @@ -886,6 +973,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.add(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.add(left, right, ScratchStorage.getCartesian()); } return left + right; }; @@ -895,6 +984,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.subtract(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.subtract(left, right, ScratchStorage.getCartesian()); } return left - right; }; @@ -908,6 +999,12 @@ define([ return Color.multiplyByScalar(right, left, ScratchStorage.getColor()); } else if ((left instanceof Color) && (typeof(right) === 'number')) { return Color.multiplyByScalar(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.multiplyComponents(left, right, ScratchStorage.getCartesian()); + } else if ((right instanceof Cartesian4) && (typeof(left) === 'number')) { + return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian()); + } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { + return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian()); } return left * right; }; @@ -919,6 +1016,10 @@ define([ return Color.divide(left, right, ScratchStorage.getColor()); } else if ((left instanceof Color) && (typeof(right) === 'number')) { return Color.divideByScalar(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.divideComponents(left, right, ScratchStorage.getCartesian()); + } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { + return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian()); } return left / right; }; @@ -928,6 +1029,10 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.mod(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + // TODO : make mod a built-in Cartesian function? + // TODO : modByScalar? + return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian()); } return left % right; }; @@ -937,6 +1042,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.equals(left, right); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.equals(left, right); } return left === right; }; @@ -946,6 +1053,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.equals(left, right); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return Cartesian4.equals(left, right); } // Specifically want to do an abstract equality comparison (==) instead of a strict equality comparison (===) @@ -958,6 +1067,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return !Color.equals(left, right); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return !Cartesian4.equals(left, right); } return left !== right; }; @@ -967,6 +1078,8 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return !Color.equals(left, right); + } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return !Cartesian4.equals(left, right); } // Specifically want to do an abstract inequality comparison (!=) instead of a strict inequality comparison (!==) // so that cases like "5 != '5'" return false. Tell jsHint to ignore this line. @@ -1069,7 +1182,7 @@ define([ Node.prototype._evaluateToString = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if ((left instanceof RegExp) || (left instanceof Color)) { + if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian4)) { return String(left); } //>>includeStart('debug', pragmas.debug); @@ -1152,7 +1265,7 @@ define([ return expressions; } - Node.prototype.getShaderExpression = function(attributePrefix, shaderState) { + Node.prototype.getShaderExpression = function(attributePrefix, shaderState, parent) { var color; var left; var right; @@ -1161,21 +1274,12 @@ define([ var type = this._type; var value = this._value; - // Right may be a string if it's a member variable: e.g. "${property.name}" - if (typeof(this._right) === 'string') { - //>>includeStart('debug', pragmas.debug); - throw new DeveloperError('Error generating style shader: string members are not supported.'); - //>>includeEnd('debug'); - // Return undefined when not in debug. Tell jsHint to ignore this line. - return; // jshint ignore:line - } - if (defined(this._left)) { if (isArray(this._left)) { - // Left can be an array if the type is LITERAL_COLOR + // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR left = getExpressionArray(this._left, attributePrefix, shaderState); } else { - left = this._left.getShaderExpression(attributePrefix, shaderState); + left = this._left.getShaderExpression(attributePrefix, shaderState, this); } if (!defined(left)) { // If the left side is not valid shader code, then the expression is not valid @@ -1184,7 +1288,7 @@ define([ } if (defined(this._right)) { - right = this._right.getShaderExpression(attributePrefix, shaderState); + right = this._right.getShaderExpression(attributePrefix, shaderState, this); if (!defined(right)) { // If the right side is not valid shader code, then the expression is not valid return undefined; @@ -1192,7 +1296,7 @@ define([ } if (defined(this._test)) { - test = this._test.getShaderExpression(attributePrefix, shaderState); + test = this._test.getShaderExpression(attributePrefix, shaderState, this); if (!defined(test)) { // If the test is not valid shader code, then the expression is not valid return undefined; @@ -1225,12 +1329,13 @@ define([ return 'cos(' + left + ')'; } else if (value === 'sqrt') { return 'sqrt(' + left + ')'; - } - //>>includeStart('debug', pragmas.debug); - else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) { + } else if ((value === 'isNaN') || (value === 'isFinite') || (value === 'String') || (value === 'isExactClass') || (value === 'isClass') || (value === 'getExactClassName')) { + //>>includeStart('debug', pragmas.debug); throw new DeveloperError('Error generating style shader: "' + value + '" is not supported.'); + //>>includeEnd('debug'); + // Return undefined when not in debug. Tell jsHint to ignore this line. + return undefined; // jshint ignore:line } - //>>includeEnd('debug'); return value + left; case ExpressionNodeType.BINARY: // Supported types: ||, &&, ===, ==, !==, !=, <, >, <=, >=, +, -, *, /, % @@ -1249,7 +1354,18 @@ define([ case ExpressionNodeType.CONDITIONAL: return '(' + test + ' ? ' + left + ' : ' + right + ')'; case ExpressionNodeType.MEMBER: - // This is intended for accessing the components of vec2, vec3, and vec4 properties. String members aren't supported. + // This is intended for accessing the components of vec4 properties. String members aren't supported. + // Check for 0.0 rather than 0 because all numbers are previously converted to decimals. + // In this shader there is not much distinction between colors and vectors so allow .red to access the 0th component for both. + if (right === 'red' || right === 'x' || right === '0.0') { + return left + '[0]'; + } else if (right === 'green' || right === 'y' || right === '1.0') { + return left + '[1]'; + } else if (right === 'blue' || right === 'z' || right === '2.0') { + return left + '[2]'; + } else if (right === 'alpha' || right === 'w' || right === '3.0') { + return left + '[3]'; + } return left + '[int(' + right + ')]'; case ExpressionNodeType.FUNCTION_CALL: //>>includeStart('debug', pragmas.debug); @@ -1286,7 +1402,15 @@ define([ case ExpressionNodeType.LITERAL_NUMBER: return numberToString(value); case ExpressionNodeType.LITERAL_STRING: - // The only supported strings are css color strings + // Check if parent is of type MEMBER. Otherwise it is not possible to know whether 'red', 'green', and 'blue' + // refer to CSS strings or component accessors. + if (defined(parent) && (parent._type === ExpressionNodeType.MEMBER)) { + if (value === 'red' || value === 'green' || value === 'blue' || value === 'alpha' || + value === 'x' || value === 'y' || value === 'z' || value === 'w') { + return value; + } + } + // Check for css color strings color = Color.fromCssColorString(value, scratchColor); if (defined(color)) { return colorToVec3(color); @@ -1348,6 +1472,8 @@ define([ } } break; + case ExpressionNodeType.LITERAL_VECTOR: + return 'vec4(' + left[0] + ', ' + left[1] + ', ' + left[2] + ', ' + left[3] + ')'; case ExpressionNodeType.LITERAL_REGEX: //>>includeStart('debug', pragmas.debug); throw new DeveloperError('Error generating style shader: Regular expressions are not supported.'); diff --git a/Source/Scene/ExpressionNodeType.js b/Source/Scene/ExpressionNodeType.js index 072a7aa34707..e1f3701d187b 100644 --- a/Source/Scene/ExpressionNodeType.js +++ b/Source/Scene/ExpressionNodeType.js @@ -23,9 +23,10 @@ define([ LITERAL_NUMBER : 11, LITERAL_STRING : 12, LITERAL_COLOR : 13, - LITERAL_REGEX : 14, - LITERAL_UNDEFINED : 15, - LITERAL_GLOBAL : 16 + LITERAL_VECTOR : 14, + LITERAL_REGEX : 15, + LITERAL_UNDEFINED : 16, + LITERAL_GLOBAL : 17 }; return freezeObject(ExpressionNodeType); diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index 6b4e3f954d7c..7f3665554e3c 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -800,7 +800,8 @@ define([ } // Edit the function header to accept the point position, color, and normal - return source.replace('()', '(vec3 position, vec4 color, vec3 normal)'); + // The styling language expects all vectors to be vec4 + return source.replace('()', '(vec4 position, vec4 color, vec4 normal)'); } function createShaders(content, frameState, style) { @@ -908,12 +909,9 @@ define([ attributeLocations.a_batchId = batchIdLocation; } - var vs = 'attribute vec3 a_position; \n' + - 'varying vec4 v_color; \n' + - 'uniform float u_pointSize; \n' + - 'uniform vec4 u_constantColor; \n' + - 'uniform vec4 u_highlightColor; \n' + - 'uniform float u_tilesetTime; \n'; + var attributeDeclarations = ''; + var attributeGlobals = ''; + var attributeDefaults = ''; var length = styleableProperties.length; for (i = 0; i < length; ++i) { @@ -930,14 +928,35 @@ define([ var attributeType; if (componentCount === 1) { attributeType = 'float'; + } else if (componentCount === 4) { + attributeType = 'vec4'; } else { - attributeType = 'vec' + componentCount; + // The styling language expects all vectors to be vec4. GLSL can cast vertex attributes to vec4 but sets + // the w component to 1.0, while the styling language expects 0.0. Since vertex attributes are read-only, + // create global variables and set their w component to 0.0 in main(). + attributeType = 'vec4'; + if (componentCount < 4) { + var globalName = 'czm_tiles3d_style_' + name; + attributeName = 'czm_tiles3d_attribute_' + name; + attributeGlobals = 'vec4 ' + globalName + '; \n'; + attributeDefaults += ' ' + globalName + ' = vec4(' + attributeName + '.xyz, 0.0); \n'; + } } - vs += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; + attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; attributeLocations[attributeName] = attribute.location; } + var vs = 'attribute vec3 a_position; \n' + + 'varying vec4 v_color; \n' + + 'uniform float u_pointSize; \n' + + 'uniform vec4 u_constantColor; \n' + + 'uniform vec4 u_highlightColor; \n' + + 'uniform float u_tilesetTime; \n'; + + vs += attributeDeclarations; + vs += attributeGlobals; + if (usesColors) { if (isTranslucent) { vs += 'attribute vec4 a_color; \n'; @@ -984,6 +1003,8 @@ define([ vs += 'void main() \n' + '{ \n'; + vs += attributeDefaults; + if (usesColors) { if (isTranslucent) { vs += ' vec4 color = a_color; \n'; @@ -1020,15 +1041,15 @@ define([ } if (hasColorStyle) { - vs += ' color = getColorFromStyle(position, color, normal); \n'; + vs += ' color = getColorFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; } if (hasShowStyle) { - vs += ' float show = float(getShowFromStyle(position, color, normal)); \n'; + vs += ' float show = float(getShowFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0))); \n'; } if (hasPointSizeStyle) { - vs += ' gl_PointSize = getPointSizeFromStyle(position, color, normal); \n'; + vs += ' gl_PointSize = getPointSizeFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; } else { vs += ' gl_PointSize = u_pointSize; \n'; } diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index 19c546e74840..006f0e05cd8b 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -1,11 +1,13 @@ /*global defineSuite*/ defineSuite([ 'Scene/Expression', + 'Core/Cartesian4', 'Core/Color', 'Core/Math', 'Scene/ExpressionNodeType' ], function( Expression, + Cartesian4, Color, CesiumMath, ExpressionNodeType) { @@ -485,7 +487,7 @@ defineSuite([ }).toThrowDeveloperError(); }); - it('evaluates color properties', function() { + it('evaluates color properties (reg, green, blue, alpha)', function() { var expression = new Expression('color(\'#ffffff\').red'); expect(expression.evaluate(frameState, undefined)).toEqual(1); @@ -499,6 +501,140 @@ defineSuite([ expect(expression.evaluate(frameState, undefined)).toEqual(0.5); }); + it('evaluates color properties (x, y, z, w)', function() { + var expression = new Expression('color(\'#ffffff\').x'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0).y'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan").z'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5).w'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties ([0], [1], [2]. [3])', function() { + var expression = new Expression('color(\'#ffffff\')[0]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)[1]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")[2]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)[3]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties (["red"], ["green"], ["blue"], ["alpha"])', function() { + var expression = new Expression('color(\'#ffffff\')["red"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)["green"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")["blue"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)["alpha"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates color properties (["x"], ["y"], ["z"], ["w"])', function() { + var expression = new Expression('color(\'#ffffff\')["x"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgb(255, 255, 0)["y"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('color("cyan")["z"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1); + + expression = new Expression('rgba(255, 255, 0, 0.5)["w"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(0.5); + }); + + it('evaluates vector', function() { + var expression = new Expression('vec2()'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + + expression = new Expression('vec3()'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + + expression = new Expression('vec4()'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + + expression = new Expression('vec2(1.0, 2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 0.0, 0.0)); + + expression = new Expression('vec3(1.0, 2.0, 3.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 0.0)); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 4.0)); + }); + + it('evaluates vector with expressions as arguments', function() { + var feature = new MockFeature(); + feature.addProperty('height', 2); + feature.addProperty('width', 4); + feature.addProperty('depth', 3); + feature.addProperty('scale', 1); + + var expression = new Expression('vec4(${height}, ${width}, ${depth}, ${scale})'); + expect(expression.evaluate(frameState, feature)).toEqual(new Cartesian4(2.0, 4.0, 3.0, 1.0)); + }); + + it('evaluates expression with multiple nested vectors', function() { + var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4().w, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 0.0, 5.0)); + }); + + it('evaluates vector properties (x, y, z, w)', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).x'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).y'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).z'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0).w'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties ([0], [1], [2]. [3])', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[0]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[1]'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[2]'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[3]'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + + it('evaluates vector properties (["x"], ["y"], ["z"]. ["w"])', function() { + var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["x"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(1.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["y"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(2.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["z"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(3.0); + + expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)["w"]'); + expect(expression.evaluate(frameState, undefined)).toEqual(4.0); + }); + it('evaluates unary not', function() { var expression = new Expression('!true'); expect(expression.evaluate(frameState, undefined)).toEqual(false); @@ -748,7 +884,10 @@ defineSuite([ }); it('evaluates color operations', function() { - var expression = new Expression('rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)'); + var expression = new Expression('+rgba(255, 0, 0, 1.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(Color.RED); + + expression = new Expression('rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)'); expect(expression.evaluate(frameState, undefined)).toEqual(Color.MAGENTA); expression = new Expression('rgba(0, 255, 255, 1.0) - rgba(0, 255, 0, 0)'); @@ -783,6 +922,56 @@ defineSuite([ expression = new Expression('color(\'green\') != color(\'green\')'); expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('color(\'green\') !== color(\'green\')'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + }); + + it('evaluates vector operations', function() { + var expression = new Expression('+vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 0)); + + expression = new Expression('-vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, 0)); + + expression = new Expression('vec2(1, 2) + vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 5, 6)); + + expression = new Expression('vec2(1, 2) - vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -5, -6)); + + expression = new Expression('vec4(1, 2, 3, 4) * vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 8, 15, 24)); + + expression = new Expression('vec3(1, 2, 3) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 0)); + + expression = new Expression('3.0 * vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 0)); + + expression = new Expression('vec3(1, 2, 3) / vec4(2, 5, 3, 1)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 0.0)); + + expression = new Expression('vec3(1, 2, 3) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 1.0, 1.5, 0.0)); + + expression = new Expression('vec4(2, 3, 4, 5) % vec3(3, 3, 3, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2, 0, 1, 1)); + + expression = new Expression('vec2(1, 3) == vec4(1, 3, 0, 0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec2(1, 3) === vec4(1, 3, 0, 0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('!!vec4() == true'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec3(1, 2, 3) != vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('vec3(1, 2, 3) !== vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); }); it('evaluates color toString function', function() { @@ -799,6 +988,17 @@ defineSuite([ expect(expression.evaluate(frameState, feature)).toEqual('(0, 0, 1, 1)'); }); + it('evaluates vector toString function', function() { + var feature = new MockFeature(); + feature.addProperty('property', new Cartesian4(1, 2, 3, 4)); + + var expression = new Expression('vec4(1, 2, 3, 4).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3, 4)'); + + expression = new Expression('${property}.toString()'); + expect(expression.evaluate(frameState, feature)).toEqual('(1, 2, 3, 4)'); + }); + it('evaluates isNaN function', function() { var expression = new Expression('isNaN()'); expect(expression.evaluate(frameState, undefined)).toEqual(true); @@ -1472,7 +1672,7 @@ defineSuite([ expect(expression.evaluate(frameState, feature)).toEqual(false); }); - it('throws if test is not call with a RegExp', function() { + it('throws if test is not called with a RegExp', function() { expect(function() { return new Expression('color("blue").test()'); }).toThrowDeveloperError(); @@ -1728,12 +1928,12 @@ defineSuite([ it('gets shader expression for array indexing', function() { var expression = new Expression('${property[0]}'); var shaderExpression = expression.getShaderExpression('', {}); - var expected = 'property[int(0.0)]'; + var expected = 'property[0]'; expect(shaderExpression).toEqual(expected); - expression = new Expression('rgb(0,0,0)[1]'); + expression = new Expression('${property[4 / 2]}'); shaderExpression = expression.getShaderExpression('', {}); - expected = 'vec4(0.0, 0.0, 0.0, 1.0)[int(1.0)]'; + expected = 'property[int((4.0 / 2.0))]'; expect(shaderExpression).toEqual(expected); }); @@ -1892,6 +2092,66 @@ defineSuite([ expect(shaderState.translucent).toBe(true); }); + it('gets shader expression for color components', function() { + // .red, .green, .blue, .alpha + var expression = new Expression('color().red + color().green + color().blue + color().alpha'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])'; + expect(shaderExpression).toEqual(expected); + + // .x, .y, .z, .w + expression = new Expression('color().x + color().y + color().z + color().w'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + + // [0], [1], [2], [3] + expression = new Expression('color()[0] + color()[1] + color()[2] + color()[3]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + + // ['red'], ['green'], ['blue'], ['alpha'] + expression = new Expression('color()["red"] + color()["green"] + color()["blue"] + color()["alpha"]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + + // ['x'], ['y'], ['z'], ['w'] + expression = new Expression('color()["x"] + color()["y"] + color()["z"] + color()["w"]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + }); + + it('gets shader expression for vector', function() { + var expression = new Expression('vec4(1, 2, 3, 4)'); + var shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('vec4(1.0, 2.0, 3.0, 4.0)'); + + expression = new Expression('vec2() + vec3() + vec4()'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('((vec4(0.0, 0.0, 0.0, 0.0) + vec4(0.0, 0.0, 0.0, 0.0)) + vec4(0.0, 0.0, 0.0, 0.0))'); + + expression = new Expression('vec4(1, ${property}, vec2(1, 2).x)'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('vec4(1.0, property, vec4(1.0, 2.0, 0.0, 0.0)[0], 0.0)'); + }); + + it('gets shader expression for color components', function() { + // .x, .y, .z, .w + var expression = new Expression('vec4().x + vec4().y + vec4().z + vec4().w'); + var shaderExpression = expression.getShaderExpression('', {}); + var expected = '(((vec4(0.0, 0.0, 0.0, 0.0)[0] + vec4(0.0, 0.0, 0.0, 0.0)[1]) + vec4(0.0, 0.0, 0.0, 0.0)[2]) + vec4(0.0, 0.0, 0.0, 0.0)[3])'; + expect(shaderExpression).toEqual(expected); + + // [0], [1], [2], [3] + expression = new Expression('vec4()[0] + vec4()[1] + vec4()[2] + vec4()[3]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + + // ['x'], ['y'], ['z'], ['w'] + expression = new Expression('vec4()["x"] + vec4()["y"] + vec4()["z"] + vec4()["w"]'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual(expected); + }); + it('gets shader expression for TILES3D_TILESET_TIME', function() { var expression = new Expression('TILES3D_TILESET_TIME'); var shaderExpression = expression.getShaderExpression('', {}); From a5d9c37f56a10629baf885438fdce3368473f0bb Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 15 Dec 2016 10:57:27 -0500 Subject: [PATCH 2/3] Typo --- Specs/Scene/ExpressionSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index 006f0e05cd8b..e90349408d1f 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -2134,7 +2134,7 @@ defineSuite([ expect(shaderExpression).toEqual('vec4(1.0, property, vec4(1.0, 2.0, 0.0, 0.0)[0], 0.0)'); }); - it('gets shader expression for color components', function() { + it('gets shader expression for vector components', function() { // .x, .y, .z, .w var expression = new Expression('vec4().x + vec4().y + vec4().z + vec4().w'); var shaderExpression = expression.getShaderExpression('', {}); From d76dadef0b84a97d785af2c78bae93ee9beb1cb7 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 6 Jan 2017 10:43:08 -0500 Subject: [PATCH 3/3] Support vec2, vec3, and vec4 as distinct types --- .../gallery/3D Tiles Point Cloud Styling.html | 9 +- Source/Scene/Expression.js | 292 ++++++++++++----- Source/Scene/PointCloud3DTileContent.js | 27 +- Specs/Scene/ExpressionSpec.js | 309 ++++++++++++++---- 4 files changed, 470 insertions(+), 167 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html index 777721bc269f..733b9d6edc37 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Styling.html @@ -134,7 +134,6 @@ pointSize : "5" }); -// secondaryColor is originally a vec3 - so add vec4(0.0, 0.0, 0.0, 1.0) to make the point cloud opaque addStyle('Clamp and Mix', { color : "color() * clamp(${temperature}, 0.1, 0.2)", pointSize : "mix(${temperature}, 2.0, 0.5) * 0.2" @@ -143,9 +142,9 @@ addStyle('Secondary Color', { color : { conditions : [ - ["${id} < 250", "${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], - ["${id} < 500", "${secondaryColor} * ${secondaryColor} + vec4(0.0, 0.0, 0.0, 1.0)"], - ["${id} < 750", "${secondaryColor} / 5.0 + vec4(0.0, 0.0, 0.0, 1.0)"], + ["${id} < 250", "vec4(${secondaryColor}, 1.0)"], + ["${id} < 500", "vec4(${secondaryColor} * ${secondaryColor}, 1.0)"], + ["${id} < 750", "vec4(${secondaryColor} / 5.0, 1.0)"], ["${id} < 1000", "rgb(0, 0, Number(${secondaryColor}.x < 0.5) * 255)"] ] } @@ -161,7 +160,7 @@ // POSITION contains 0 as its last component, so add 1.0 to make the point cloud opaque addStyle('Color based on position', { - color : "${POSITION} + vec4(0.0, 0.0, 0.0, 1.0)" + color : "vec4(${POSITION}, 1.0)" }); addStyle('Style point size', { diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js index 47b024b292f0..17ae36798635 100644 --- a/Source/Scene/Expression.js +++ b/Source/Scene/Expression.js @@ -1,5 +1,7 @@ /*global define*/ define([ + '../Core/Cartesian2', + '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Color', '../Core/defined', @@ -10,6 +12,8 @@ define([ '../ThirdParty/jsep', './ExpressionNodeType' ], function( + Cartesian2, + Cartesian3, Cartesian4, Color, defined, @@ -33,28 +37,53 @@ define([ var ScratchStorage = { scratchColorIndex : 0, - scratchColors : [new Color()], - scratchCartesianIndex : 0, - scratchCartesians : [new Cartesian4()], + scratchColorArray : [new Color()], + scratchArrayIndex : 0, + scratchArrayArray : [[]], + scratchCartesian2Index : 0, + scratchCartesian3Index : 0, + scratchCartesian4Index : 0, + scratchCartesian2Array : [new Cartesian2()], + scratchCartesian3Array : [new Cartesian3()], + scratchCartesian4Array : [new Cartesian4()], reset : function() { this.scratchColorIndex = 0; - this.scratchCartesianIndex = 0; + this.scratchArrayIndex = 0; + this.scratchCartesian2Index = 0; + this.scratchCartesian3Index = 0; + this.scratchCartesian4Index = 0; }, getColor : function() { - if (this.scratchColorIndex >= this.scratchColors.length) { - this.scratchColors.push(new Color()); + if (this.scratchColorIndex >= this.scratchColorArray.length) { + this.scratchColorArray.push(new Color()); } - var scratchColor = this.scratchColors[this.scratchColorIndex]; - ++this.scratchColorIndex; - return scratchColor; + return this.scratchColorArray[this.scratchColorIndex++]; }, - getCartesian : function() { - if (this.scratchCartesianIndex >= this.scratchCartesians.length) { - this.scratchCartesians.push(new Cartesian4()); + getArray : function() { + if (this.scratchArrayIndex >= this.scratchArrayArray.length) { + this.scratchArrayArray.push([]); } - var scratchCartesian = this.scratchCartesians[this.scratchCartesianIndex]; - ++this.scratchCartesianIndex; - return scratchCartesian; + var scratchArray = this.scratchArrayArray[this.scratchArrayIndex++]; + scratchArray.length = 0; + return scratchArray; + }, + getCartesian2 : function() { + if (this.scratchCartesian2Index >= this.scratchCartesian2Array.length) { + this.scratchCartesian2Array.push(new Cartesian2()); + } + return this.scratchCartesian2Array[this.scratchCartesian2Index++]; + }, + getCartesian3 : function() { + if (this.scratchCartesian3Index >= this.scratchCartesian3Array.length) { + this.scratchCartesian3Array.push(new Cartesian3()); + } + return this.scratchCartesian3Array[this.scratchCartesian3Index++]; + }, + getCartesian4 : function() { + if (this.scratchCartesian4Index >= this.scratchCartesian4Array.length) { + this.scratchCartesian4Array.push(new Cartesian4()); + } + return this.scratchCartesian4Array[this.scratchCartesian4Index++]; } }; @@ -156,19 +185,18 @@ define([ * is of type Boolean, Number, or String, the corresponding JavaScript * primitive type will be returned. If the result is a RegExp, a Javascript RegExp * object will be returned. If the result is a Color, a {@link Color} object will be returned. - * If the result is a Cartesian4, a {@link Cartesian4} object will be returned. + * If the result is a Cartesian2, Cartesian3, or Cartesian4, + * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. * * @param {FrameState} frameState The frame state. * @param {Cesium3DTileFeature} feature The feature who's properties may be used as variables in the expression. - * @returns {Boolean|Number|String|Color|Cartesian4|RegExp} The result of evaluating the expression. + * @returns {Boolean|Number|String|Color|Cartesian2|Cartesian3|Cartesian4|RegExp} The result of evaluating the expression. */ Expression.prototype.evaluate = function(frameState, feature) { ScratchStorage.reset(); var result = this._runtimeAst.evaluate(frameState, feature); - if (result instanceof Color) { - return Color.clone(result); - } else if (result instanceof Cartesian4) { - return Cartesian4.clone(result); + if ((result instanceof Color) || (result instanceof Cartesian2) || (result instanceof Cartesian3) || (result instanceof Cartesian4)) { + return result.clone(); } return result; }; @@ -302,6 +330,7 @@ define([ function parseCall(expression, ast) { var args = ast.arguments; + var argsLength = args.length; var call; var val, left, right; @@ -316,7 +345,7 @@ define([ throw new DeveloperError('Error: ' + call + ' is not a function.'); } //>>includeEnd('debug'); - if (args.length === 0) { + if (argsLength === 0) { if (call === 'test') { return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false); } else { @@ -339,7 +368,7 @@ define([ // Non-member function calls call = ast.callee.name; if (call === 'color') { - if (args.length === 0) { + if (argsLength === 0) { return new Node(ExpressionNodeType.LITERAL_COLOR, call); } val = createRuntimeAst(expression, args[0]); @@ -350,7 +379,7 @@ define([ return new Node(ExpressionNodeType.LITERAL_COLOR, call, [val]); } else if (call === 'rgb' || call === 'hsl') { //>>includeStart('debug', pragmas.debug); - if (args.length < 3) { + if (argsLength < 3) { throw new DeveloperError('Error: ' + call + ' requires three arguments.'); } //>>includeEnd('debug'); @@ -362,7 +391,7 @@ define([ return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); } else if (call === 'rgba' || call === 'hsla') { //>>includeStart('debug', pragmas.debug); - if (args.length < 4) { + if (argsLength < 4) { throw new DeveloperError('Error: ' + call + ' requires four arguments.'); } //>>includeEnd('debug'); @@ -374,15 +403,14 @@ define([ ]; return new Node(ExpressionNodeType.LITERAL_COLOR, call, val); } else if (call === 'vec2' || call === 'vec3' || call === 'vec4') { - val = [ - defined(args[0]) ? createRuntimeAst(expression, args[0]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[1]) ? createRuntimeAst(expression, args[1]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[2]) ? createRuntimeAst(expression, args[2]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0), - defined(args[3]) ? createRuntimeAst(expression, args[3]) : new Node(ExpressionNodeType.LITERAL_NUMBER, 0) - ]; + // Check for invalid constructors at evaluation time + val = new Array(argsLength); + for (var i = 0; i < argsLength; ++i) { + val[i] = createRuntimeAst(expression, args[i]); + } return new Node(ExpressionNodeType.LITERAL_VECTOR, call, val); } else if (call === 'isNaN' || call === 'isFinite') { - if (args.length === 0) { + if (argsLength === 0) { if (call === 'isNaN') { return new Node(ExpressionNodeType.LITERAL_BOOLEAN, true); } else { @@ -393,7 +421,7 @@ define([ return new Node(ExpressionNodeType.UNARY, call, val); } else if (call === 'isExactClass' || call === 'isClass') { //>>includeStart('debug', pragmas.debug); - if (args.length < 1 || args.length > 1) { + if (argsLength < 1 || argsLength > 1) { throw new DeveloperError('Error: ' + call + ' requires exactly one argument.'); } //>>includeEnd('debug'); @@ -401,14 +429,14 @@ define([ return new Node(ExpressionNodeType.UNARY, call, val); } else if (call === 'getExactClassName') { //>>includeStart('debug', pragmas.debug); - if (args.length > 0) { + if (argsLength > 0) { throw new DeveloperError('Error: ' + call + ' does not take any argument.'); } //>>includeEnd('debug'); return new Node(ExpressionNodeType.UNARY, call); } else if (defined(unaryFunctions[call])) { //>>includeStart('debug', pragmas.debug); - if (args.length !== 1) { + if (argsLength !== 1) { throw new DeveloperError('Error: ' + call + ' requires exactly one argument.'); } //>>includeEnd('debug'); @@ -416,7 +444,7 @@ define([ return new Node(ExpressionNodeType.UNARY, call, val); } else if (defined(binaryFunctions[call])) { //>>includeStart('debug', pragmas.debug); - if (args.length !== 2) { + if (argsLength !== 2) { throw new DeveloperError('Error: ' + call + ' requires exactly two arguments.'); } //>>includeEnd('debug'); @@ -425,7 +453,7 @@ define([ return new Node(ExpressionNodeType.BINARY, call, left, right); } else if (defined(ternaryFunctions[call])) { //>>includeStart('debug', pragmas.debug); - if (args.length !== 3) { + if (argsLength !== 3) { throw new DeveloperError('Error: ' + call + ' requires exactly three arguments.'); } //>>includeEnd('debug'); @@ -434,19 +462,19 @@ define([ var test = createRuntimeAst(expression, args[2]); return new Node(ExpressionNodeType.TERNARY, call, left, right, test); } else if (call === 'Boolean') { - if (args.length === 0) { + if (argsLength === 0) { return new Node(ExpressionNodeType.LITERAL_BOOLEAN, false); } val = createRuntimeAst(expression, args[0]); return new Node(ExpressionNodeType.UNARY, call, val); } else if (call === 'Number') { - if (args.length === 0) { + if (argsLength === 0) { return new Node(ExpressionNodeType.LITERAL_NUMBER, 0); } val = createRuntimeAst(expression, args[0]); return new Node(ExpressionNodeType.UNARY, call, val); } else if (call === 'String') { - if (args.length === 0) { + if (argsLength === 0) { return new Node(ExpressionNodeType.LITERAL_STRING, ''); } val = createRuntimeAst(expression, args[0]); @@ -791,14 +819,62 @@ define([ }; Node.prototype._evaluateLiteralVector = function(frameState, feature) { - var result = ScratchStorage.getCartesian(); + // Gather the components that make up the vector, which includes components from interior vectors. + // For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid. + // + // If the number of components does not equal the vector's size, then a DeveloperError is thrown - with two exceptions: + // 1. A vector may be constructed from a larger vector and drop the extra components. + // 2. A vector may be constructed from a single component - vec3(1) will become vec3(1, 1, 1). + // + // Examples of invalid constructors include: + // vec4(1, 2) // not enough components + // vec3(vec2(1, 2)) // not enough components + // vec3(1, 2, 3, 4) // too many components + // vec2(vec4(1), 1) // too many components + + var components = ScratchStorage.getArray(); var args = this._left; - return Cartesian4.fromElements( - args[0].evaluate(frameState, feature), - args[1].evaluate(frameState, feature), - args[2].evaluate(frameState, feature), - args[3].evaluate(frameState, feature), - result); + var argsLength = args.length; + for (var i = 0; i < argsLength; ++i) { + var value = args[i].evaluate(frameState, feature); + if (typeof(value) === 'number') { + components.push(value); + } else if (value instanceof Cartesian2) { + components.push(value.x, value.y); + } else if (value instanceof Cartesian3) { + components.push(value.x, value.y, value.z); + } else if (value instanceof Cartesian4) { + components.push(value.x, value.y, value.z, value.w); + } + } + + var componentsLength = components.length; + var call = this._value; + var vectorLength = parseInt(call.charAt(3)); + + //>>includeStart('debug', pragmas.debug); + if (componentsLength === 0) { + throw new DeveloperError('Error: Invalid ' + call + ' constructor. No valid arguments.'); + } else if ((componentsLength < vectorLength) && (componentsLength > 1)) { + throw new DeveloperError('Error: Invalid ' + call + ' constructor. Not enough arguments.'); + } else if ((componentsLength > vectorLength) && (argsLength > 1)) { + throw new DeveloperError('Error: Invalid ' + call + ' constructor. Too many arguments.'); + } + //>>includeEnd('debug'); + + if (componentsLength === 1) { + // Add the same component 3 more times + var component = components[0]; + components.push(component, component, component); + } + + if (call === 'vec2') { + return Cartesian2.fromArray(components, 0, ScratchStorage.getCartesian2()); + } else if (call === 'vec3') { + return Cartesian3.fromArray(components, 0, ScratchStorage.getCartesian3()); + } else if (call === 'vec4') { + return Cartesian4.fromArray(components, 0, ScratchStorage.getCartesian4()); + } }; Node.prototype._evaluateLiteralString = function(frameState, feature) { @@ -878,8 +954,9 @@ define([ } else if (member === 3 || member === 'w') { return property.alpha; } - } else if (property instanceof Cartesian4) { + } else if ((property instanceof Cartesian2) || (property instanceof Cartesian3) || (property instanceof Cartesian4)) { // Vector components may be accessed with [0][1][2][3] and implicitly with ['x']['y']['z']['w'] + // For Cartesian2 and Cartesian3 out-of-range components will just return undefined if (member === 0) { return property.x; } else if (member === 1) { @@ -910,15 +987,19 @@ define([ Node.prototype._evaluateNegative = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if (left instanceof Cartesian4) { - return Cartesian4.negate(left, ScratchStorage.getCartesian()); + if (left instanceof Cartesian2) { + return Cartesian2.negate(left, ScratchStorage.getCartesian2()); + } else if (left instanceof Cartesian3) { + return Cartesian3.negate(left, ScratchStorage.getCartesian3()); + } else if (left instanceof Cartesian4) { + return Cartesian4.negate(left, ScratchStorage.getCartesian4()); } return -left; }; Node.prototype._evaluatePositive = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if ((left instanceof Color) || (left instanceof Cartesian4)) { + if ((left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) { return left; } return +left; @@ -997,8 +1078,12 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.add(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.add(left, right, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.add(left, right, ScratchStorage.getCartesian3()); } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.add(left, right, ScratchStorage.getCartesian()); + return Cartesian4.add(left, right, ScratchStorage.getCartesian4()); } return left + right; }; @@ -1008,8 +1093,12 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.subtract(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.subtract(left, right, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.subtract(left, right, ScratchStorage.getCartesian3()); } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.subtract(left, right, ScratchStorage.getCartesian()); + return Cartesian4.subtract(left, right, ScratchStorage.getCartesian4()); } return left - right; }; @@ -1023,12 +1112,24 @@ define([ return Color.multiplyByScalar(right, left, ScratchStorage.getColor()); } else if ((left instanceof Color) && (typeof(right) === 'number')) { return Color.multiplyByScalar(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.multiplyComponents(left, right, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian2) && (typeof(left) === 'number')) { + return Cartesian2.multiplyByScalar(right, left, ScratchStorage.getCartesian2()); + } else if ((left instanceof Cartesian2) && (typeof(right) === 'number')) { + return Cartesian2.multiplyByScalar(left, right, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.multiplyComponents(left, right, ScratchStorage.getCartesian3()); + } else if ((right instanceof Cartesian3) && (typeof(left) === 'number')) { + return Cartesian3.multiplyByScalar(right, left, ScratchStorage.getCartesian3()); + } else if ((left instanceof Cartesian3) && (typeof(right) === 'number')) { + return Cartesian3.multiplyByScalar(left, right, ScratchStorage.getCartesian3()); } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.multiplyComponents(left, right, ScratchStorage.getCartesian()); + return Cartesian4.multiplyComponents(left, right, ScratchStorage.getCartesian4()); } else if ((right instanceof Cartesian4) && (typeof(left) === 'number')) { - return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian()); + return Cartesian4.multiplyByScalar(right, left, ScratchStorage.getCartesian4()); } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { - return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian()); + return Cartesian4.multiplyByScalar(left, right, ScratchStorage.getCartesian4()); } return left * right; }; @@ -1040,10 +1141,18 @@ define([ return Color.divide(left, right, ScratchStorage.getColor()); } else if ((left instanceof Color) && (typeof(right) === 'number')) { return Color.divideByScalar(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.divideComponents(left, right, ScratchStorage.getCartesian2()); + } else if ((left instanceof Cartesian2) && (typeof(right) === 'number')) { + return Cartesian2.divideByScalar(left, right, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.divideComponents(left, right, ScratchStorage.getCartesian3()); + } else if ((left instanceof Cartesian3) && (typeof(right) === 'number')) { + return Cartesian3.divideByScalar(left, right, ScratchStorage.getCartesian3()); } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.divideComponents(left, right, ScratchStorage.getCartesian()); + return Cartesian4.divideComponents(left, right, ScratchStorage.getCartesian4()); } else if ((left instanceof Cartesian4) && (typeof(right) === 'number')) { - return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian()); + return Cartesian4.divideByScalar(left, right, ScratchStorage.getCartesian4()); } return left / right; }; @@ -1053,10 +1162,12 @@ define([ var right = this._right.evaluate(frameState, feature); if ((right instanceof Color) && (left instanceof Color)) { return Color.mod(left, right, ScratchStorage.getColor()); + } else if ((right instanceof Cartesian2) && (left instanceof Cartesian2)) { + return Cartesian2.fromElements(left.x % right.x, left.y % right.y, ScratchStorage.getCartesian2()); + } else if ((right instanceof Cartesian3) && (left instanceof Cartesian3)) { + return Cartesian3.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, ScratchStorage.getCartesian3()); } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - // TODO : make mod a built-in Cartesian function? - // TODO : modByScalar? - return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian()); + return Cartesian4.fromElements(left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, ScratchStorage.getCartesian4()); } return left % right; }; @@ -1064,10 +1175,11 @@ define([ Node.prototype._evaluateEqualsStrict = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); var right = this._right.evaluate(frameState, feature); - if ((right instanceof Color) && (left instanceof Color)) { - return Color.equals(left, right); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.equals(left, right); + if ((right instanceof Color) && (left instanceof Color) || + (right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return left.equals(right); } return left === right; }; @@ -1075,10 +1187,11 @@ define([ Node.prototype._evaluateEquals = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); var right = this._right.evaluate(frameState, feature); - if ((right instanceof Color) && (left instanceof Color)) { - return Color.equals(left, right); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return Cartesian4.equals(left, right); + if ((right instanceof Color) && (left instanceof Color) || + (right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return left.equals(right); } // Specifically want to do an abstract equality comparison (==) instead of a strict equality comparison (===) @@ -1089,10 +1202,11 @@ define([ Node.prototype._evaluateNotEqualsStrict = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); var right = this._right.evaluate(frameState, feature); - if ((right instanceof Color) && (left instanceof Color)) { - return !Color.equals(left, right); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return !Cartesian4.equals(left, right); + if ((right instanceof Color) && (left instanceof Color) || + (right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return !left.equals(right); } return left !== right; }; @@ -1100,10 +1214,11 @@ define([ Node.prototype._evaluateNotEquals = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); var right = this._right.evaluate(frameState, feature); - if ((right instanceof Color) && (left instanceof Color)) { - return !Color.equals(left, right); - } else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) { - return !Cartesian4.equals(left, right); + if ((right instanceof Color) && (left instanceof Color) || + (right instanceof Cartesian2) && (left instanceof Cartesian2) || + (right instanceof Cartesian3) && (left instanceof Cartesian3) || + (right instanceof Cartesian4) && (left instanceof Cartesian4)) { + return !left.equals(right); } // Specifically want to do an abstract inequality comparison (!=) instead of a strict inequality comparison (!==) // so that cases like "5 != '5'" return false. Tell jsHint to ignore this line. @@ -1206,7 +1321,7 @@ define([ Node.prototype._evaluateToString = function(frameState, feature) { var left = this._left.evaluate(frameState, feature); - if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian4)) { + if ((left instanceof RegExp) || (left instanceof Color) || (left instanceof Cartesian2) || (left instanceof Cartesian3) || (left instanceof Cartesian4)) { return String(left); } //>>includeStart('debug', pragmas.debug); @@ -1275,11 +1390,11 @@ define([ return 'vec4(' + r + ', ' + g + ', ' + b + ', ' + a + ')'; } - function getExpressionArray(array, attributePrefix, shaderState) { + function getExpressionArray(array, attributePrefix, shaderState, parent) { var length = array.length; var expressions = new Array(length); for (var i = 0; i < length; ++i) { - var shader = array[i].getShaderExpression(attributePrefix, shaderState); + var shader = array[i].getShaderExpression(attributePrefix, shaderState, parent); if (!defined(shader)) { // If any of the expressions are not valid, the array is not valid return undefined; @@ -1301,7 +1416,7 @@ define([ if (defined(this._left)) { if (isArray(this._left)) { // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR - left = getExpressionArray(this._left, attributePrefix, shaderState); + left = getExpressionArray(this._left, attributePrefix, shaderState, this); } else { left = this._left.getShaderExpression(attributePrefix, shaderState, this); } @@ -1329,7 +1444,7 @@ define([ if (isArray(this._value)) { // For ARRAY type - value = getExpressionArray(this._value, attributePrefix, shaderState); + value = getExpressionArray(this._value, attributePrefix, shaderState, this); if (!defined(value)) { // If the values are not valid shader code, then the expression is not valid return undefined; @@ -1383,7 +1498,7 @@ define([ case ExpressionNodeType.CONDITIONAL: return '(' + test + ' ? ' + left + ' : ' + right + ')'; case ExpressionNodeType.MEMBER: - // This is intended for accessing the components of vec4 properties. String members aren't supported. + // This is intended for accessing the components of vector properties. String members aren't supported. // Check for 0.0 rather than 0 because all numbers are previously converted to decimals. // In this shader there is not much distinction between colors and vectors so allow .red to access the 0th component for both. if (right === 'red' || right === 'x' || right === '0.0') { @@ -1502,7 +1617,16 @@ define([ } break; case ExpressionNodeType.LITERAL_VECTOR: - return 'vec4(' + left[0] + ', ' + left[1] + ', ' + left[2] + ', ' + left[3] + ')'; + var length = left.length; + var vectorExpression = value + '('; + for (var i = 0; i < length; ++i) { + vectorExpression += left[i]; + if (i < (length - 1)) { + vectorExpression += ', '; + } + } + vectorExpression += ')'; + return vectorExpression; case ExpressionNodeType.LITERAL_REGEX: //>>includeStart('debug', pragmas.debug); throw new DeveloperError('Error generating style shader: Regular expressions are not supported.'); diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index bfb377eeb02f..e00cea225aec 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -800,8 +800,7 @@ define([ } // Edit the function header to accept the point position, color, and normal - // The styling language expects all vectors to be vec4 - return source.replace('()', '(vec4 position, vec4 color, vec4 normal)'); + return source.replace('()', '(vec3 position, vec4 color, vec3 normal)'); } function createShaders(content, frameState, style) { @@ -910,8 +909,6 @@ define([ } var attributeDeclarations = ''; - var attributeGlobals = ''; - var attributeDefaults = ''; var length = styleableProperties.length; for (i = 0; i < length; ++i) { @@ -928,19 +925,8 @@ define([ var attributeType; if (componentCount === 1) { attributeType = 'float'; - } else if (componentCount === 4) { - attributeType = 'vec4'; } else { - // The styling language expects all vectors to be vec4. GLSL can cast vertex attributes to vec4 but sets - // the w component to 1.0, while the styling language expects 0.0. Since vertex attributes are read-only, - // create global variables and set their w component to 0.0 in main(). - attributeType = 'vec4'; - if (componentCount < 4) { - var globalName = 'czm_tiles3d_style_' + name; - attributeName = 'czm_tiles3d_attribute_' + name; - attributeGlobals = 'vec4 ' + globalName + '; \n'; - attributeDefaults += ' ' + globalName + ' = vec4(' + attributeName + '.xyz, 0.0); \n'; - } + attributeType = 'vec' + componentCount; } attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; @@ -955,7 +941,6 @@ define([ 'uniform float u_tilesetTime; \n'; vs += attributeDeclarations; - vs += attributeGlobals; if (usesColors) { if (isTranslucent) { @@ -1003,8 +988,6 @@ define([ vs += 'void main() \n' + '{ \n'; - vs += attributeDefaults; - if (usesColors) { if (isTranslucent) { vs += ' vec4 color = a_color; \n'; @@ -1041,15 +1024,15 @@ define([ } if (hasColorStyle) { - vs += ' color = getColorFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; + vs += ' color = getColorFromStyle(position, color, normal); \n'; } if (hasShowStyle) { - vs += ' float show = float(getShowFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0))); \n'; + vs += ' float show = float(getShowFromStyle(position, color, normal)); \n'; } if (hasPointSizeStyle) { - vs += ' gl_PointSize = getPointSizeFromStyle(vec4(position, 0.0), color, vec4(normal, 0.0)); \n'; + vs += ' gl_PointSize = getPointSizeFromStyle(position, color, normal); \n'; } else { vs += ' gl_PointSize = u_pointSize; \n'; } diff --git a/Specs/Scene/ExpressionSpec.js b/Specs/Scene/ExpressionSpec.js index a39a449f1341..078761b40a2e 100644 --- a/Specs/Scene/ExpressionSpec.js +++ b/Specs/Scene/ExpressionSpec.js @@ -1,12 +1,16 @@ /*global defineSuite*/ defineSuite([ 'Scene/Expression', + 'Core/Cartesian2', + 'Core/Cartesian3', 'Core/Cartesian4', 'Core/Color', 'Core/Math', 'Scene/ExpressionNodeType' ], function( Expression, + Cartesian2, + Cartesian3, Cartesian4, Color, CesiumMath, @@ -557,24 +561,138 @@ defineSuite([ expect(expression.evaluate(frameState, undefined)).toEqual(0.5); }); - it('evaluates vector', function() { + it('evaluates vec2', function() { + var expression = new Expression('vec2(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2.0, 2.0)); + + expression = new Expression('vec2(3.0, 4.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec2(3.0, 4.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec3(3.0, 4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + + expression = new Expression('vec2(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3.0, 4.0)); + }); + + it('throws if vec2 has invalid number of arguments', function() { var expression = new Expression('vec2()'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec2(3.0, 4.0, 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec2(vec2(3.0, 4.0), 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + }); + + it('evaluates vec3', function() { + var expression = new Expression('vec3(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2.0, 2.0, 2.0)); + + expression = new Expression('vec3(3.0, 4.0, 5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec2(3.0, 4.0), 5.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(3.0, vec2(4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec3(3.0, 4.0, 5.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + + expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3.0, 4.0, 5.0)); + }); + + it ('throws if vec3 has invalid number of arguments', function() { + var expression = new Expression('vec3()'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec3(3.0, 4.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec3(3.0, 4.0, 5.0, 6.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec3(vec2(3.0, 4.0), vec2(5.0, 6.0))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec3(vec4(3.0, 4.0, 5.0, 6.0), 1.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + }); + + it('evaluates vec4', function() { + var expression = new Expression('vec4(2.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 2.0, 2.0)); - expression = new Expression('vec3()'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); - expression = new Expression('vec4()'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + expression = new Expression('vec4(vec2(3.0, 4.0), 5.0, 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); - expression = new Expression('vec2(1.0, 2.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 0.0, 0.0)); + expression = new Expression('vec4(3.0, vec2(4.0, 5.0), 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(3.0, 4.0, vec2(5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(vec3(3.0, 4.0, 5.0), 6.0)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(3.0, vec3(4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + + expression = new Expression('vec4(vec4(3.0, 4.0, 5.0, 6.0))'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3.0, 4.0, 5.0, 6.0)); + }); + + it ('throws if vec4 has invalid number of arguments', function() { + var expression = new Expression('vec4()'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec4(3.0, 4.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); + + expression = new Expression('vec4(3.0, 4.0, 5.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); - expression = new Expression('vec3(1.0, 2.0, 3.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 0.0)); + expression = new Expression('vec4(3.0, 4.0, 5.0, 6.0, 7.0)'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); - expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1.0, 2.0, 3.0, 4.0)); + expression = new Expression('vec4(vec3(3.0, 4.0, 5.0))'); + expect(function() { + expression.evaluate(frameState, undefined); + }).toThrowDeveloperError(); }); it('evaluates vector with expressions as arguments', function() { @@ -589,8 +707,8 @@ defineSuite([ }); it('evaluates expression with multiple nested vectors', function() { - var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4().w, 5)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 0.0, 5.0)); + var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4(1.0).w, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2.0, 2.0, 1.0, 5.0)); }); it('evaluates vector properties (x, y, z, w)', function() { @@ -607,7 +725,7 @@ defineSuite([ expect(expression.evaluate(frameState, undefined)).toEqual(4.0); }); - it('evaluates vector properties ([0], [1], [2]. [3])', function() { + it('evaluates vector properties ([0], [1], [2], [3])', function() { var expression = new Expression('vec4(1.0, 2.0, 3.0, 4.0)[0]'); expect(expression.evaluate(frameState, undefined)).toEqual(1.0); @@ -928,50 +1046,134 @@ defineSuite([ }); it('evaluates vector operations', function() { - var expression = new Expression('+vec3(1, 2, 3)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 0)); + var expression = new Expression('+vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(1, 2)); + + expression = new Expression('+vec3(1, 2, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(1, 2, 3)); + + expression = new Expression('+vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(1, 2, 3, 4)); + + expression = new Expression('-vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-1, -2)); expression = new Expression('-vec3(1, 2, 3)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-1, -2, -3)); - expression = new Expression('vec2(1, 2) + vec4(3, 4, 5, 6)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 5, 6)); + expression = new Expression('-vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, -4)); - expression = new Expression('vec2(1, 2) - vec4(3, 4, 5, 6)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -5, -6)); + expression = new Expression('vec2(1, 2) + vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(4, 6)); - expression = new Expression('vec4(1, 2, 3, 4) * vec4(3, 4, 5, 6)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 8, 15, 24)); + expression = new Expression('vec3(1, 2, 3) + vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(4, 6, 8)); + + expression = new Expression('vec4(1, 2, 3, 4) + vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(4, 6, 8, 10)); + + expression = new Expression('vec2(1, 2) - vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(-2, -2)); + + expression = new Expression('vec3(1, 2, 3) - vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(-2, -2, -2)); + + expression = new Expression('vec4(1, 2, 3, 4) - vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-2, -2, -2, -2)); + + expression = new Expression('vec2(1, 2) * vec2(3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 8)); + + expression = new Expression('vec2(1, 2) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6)); + + expression = new Expression('3.0 * vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(3, 6)); + + expression = new Expression('vec3(1, 2, 3) * vec3(3, 4, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 8, 15)); expression = new Expression('vec3(1, 2, 3) * 3.0'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9)); expression = new Expression('3.0 * vec3(1, 2, 3)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(3, 6, 9)); + + expression = new Expression('vec4(1, 2, 3, 4) * vec4(3, 4, 5, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 8, 15, 24)); - expression = new Expression('vec3(1, 2, 3) / vec4(2, 5, 3, 1)'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 0.0)); + expression = new Expression('vec4(1, 2, 3, 4) * 3.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12)); + + expression = new Expression('3.0 * vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(3, 6, 9, 12)); + + expression = new Expression('vec2(1, 2) / vec2(2, 5)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 0.4)); + + expression = new Expression('vec2(1, 2) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(0.5, 1.0)); + + expression = new Expression('vec3(1, 2, 3) / vec3(2, 5, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 0.4, 1.0)); expression = new Expression('vec3(1, 2, 3) / 2.0'); - expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 1.0, 1.5, 0.0)); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(0.5, 1.0, 1.5)); + + expression = new Expression('vec4(1, 2, 3, 4) / vec4(2, 5, 3, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 0.4, 1.0, 2.0)); + + expression = new Expression('vec4(1, 2, 3, 4) / 2.0'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(0.5, 1.0, 1.5, 2.0)); + + expression = new Expression('vec2(2, 3) % vec2(3, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian2(2, 0)); - expression = new Expression('vec4(2, 3, 4, 5) % vec3(3, 3, 3, 2)'); + expression = new Expression('vec3(2, 3, 4) % vec3(3, 3, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian3(2, 0, 1)); + + expression = new Expression('vec4(2, 3, 4, 5) % vec4(3, 3, 3, 2)'); expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(2, 0, 1, 1)); - expression = new Expression('vec2(1, 3) == vec4(1, 3, 0, 0)'); + expression = new Expression('vec2(1, 3) == vec2(1, 3)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec3(1, 3, 4) == vec3(1, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec4(1, 3, 4, 6) == vec4(1, 3, 4, 6)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec2(1, 2) === vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(true); + + expression = new Expression('vec3(1, 2, 3) === vec3(1, 2, 3)'); expect(expression.evaluate(frameState, undefined)).toEqual(true); - expression = new Expression('vec2(1, 3) === vec4(1, 3, 0, 0)'); + expression = new Expression('vec4(1, 2, 3, 4) === vec4(1, 2, 3, 4)'); expect(expression.evaluate(frameState, undefined)).toEqual(true); - expression = new Expression('!!vec4() == true'); + expression = new Expression('!!vec4(1.0) == true'); expect(expression.evaluate(frameState, undefined)).toEqual(true); + expression = new Expression('vec2(1, 2) != vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + expression = new Expression('vec3(1, 2, 3) != vec3(1, 2, 3)'); expect(expression.evaluate(frameState, undefined)).toEqual(false); + expression = new Expression('vec4(1, 2, 3, 4) != vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('vec2(1, 2) !== vec2(1, 2)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); + expression = new Expression('vec3(1, 2, 3) !== vec3(1, 2, 3)'); expect(expression.evaluate(frameState, undefined)).toEqual(false); + + expression = new Expression('vec4(1, 2, 3, 4) !== vec4(1, 2, 3, 4)'); + expect(expression.evaluate(frameState, undefined)).toEqual(false); }); it('evaluates color toString function', function() { @@ -992,7 +1194,13 @@ defineSuite([ var feature = new MockFeature(); feature.addProperty('property', new Cartesian4(1, 2, 3, 4)); - var expression = new Expression('vec4(1, 2, 3, 4).toString()'); + var expression = new Expression('vec2(1, 2).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2)'); + + expression = new Expression('vec3(1, 2, 3).toString()'); + expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3)'); + + expression = new Expression('vec4(1, 2, 3, 4).toString()'); expect(expression.evaluate(frameState, undefined)).toEqual('(1, 2, 3, 4)'); expression = new Expression('${property}.toString()'); @@ -2160,16 +2368,6 @@ defineSuite([ expression = new Expression('color()[0] + color()[1] + color()[2] + color()[3]'); shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual(expected); - - // ['red'], ['green'], ['blue'], ['alpha'] - expression = new Expression('color()["red"] + color()["green"] + color()["blue"] + color()["alpha"]'); - shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual(expected); - - // ['x'], ['y'], ['z'], ['w'] - expression = new Expression('color()["x"] + color()["y"] + color()["z"] + color()["w"]'); - shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual(expected); }); it('gets shader expression for vector', function() { @@ -2177,29 +2375,28 @@ defineSuite([ var shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual('vec4(1.0, 2.0, 3.0, 4.0)'); - expression = new Expression('vec2() + vec3() + vec4()'); + expression = new Expression('vec4(1) + vec4(2)'); + shaderExpression = expression.getShaderExpression('', {}); + expect(shaderExpression).toEqual('(vec4(1.0) + vec4(2.0))'); + + expression = new Expression('vec4(1, ${property}, vec2(1, 2).x, 0)'); shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual('((vec4(0.0, 0.0, 0.0, 0.0) + vec4(0.0, 0.0, 0.0, 0.0)) + vec4(0.0, 0.0, 0.0, 0.0))'); + expect(shaderExpression).toEqual('vec4(1.0, property, vec2(1.0, 2.0)[0], 0.0)'); - expression = new Expression('vec4(1, ${property}, vec2(1, 2).x)'); + expression = new Expression('vec4(vec3(2), 1.0)'); shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual('vec4(1.0, property, vec4(1.0, 2.0, 0.0, 0.0)[0], 0.0)'); + expect(shaderExpression).toEqual('vec4(vec3(2.0), 1.0)'); }); it('gets shader expression for vector components', function() { // .x, .y, .z, .w - var expression = new Expression('vec4().x + vec4().y + vec4().z + vec4().w'); + var expression = new Expression('vec4(1).x + vec4(1).y + vec4(1).z + vec4(1).w'); var shaderExpression = expression.getShaderExpression('', {}); - var expected = '(((vec4(0.0, 0.0, 0.0, 0.0)[0] + vec4(0.0, 0.0, 0.0, 0.0)[1]) + vec4(0.0, 0.0, 0.0, 0.0)[2]) + vec4(0.0, 0.0, 0.0, 0.0)[3])'; + var expected = '(((vec4(1.0)[0] + vec4(1.0)[1]) + vec4(1.0)[2]) + vec4(1.0)[3])'; expect(shaderExpression).toEqual(expected); // [0], [1], [2], [3] - expression = new Expression('vec4()[0] + vec4()[1] + vec4()[2] + vec4()[3]'); - shaderExpression = expression.getShaderExpression('', {}); - expect(shaderExpression).toEqual(expected); - - // ['x'], ['y'], ['z'], ['w'] - expression = new Expression('vec4()["x"] + vec4()["y"] + vec4()["z"] + vec4()["w"]'); + expression = new Expression('vec4(1)[0] + vec4(1)[1] + vec4(1)[2] + vec4(1)[3]'); shaderExpression = expression.getShaderExpression('', {}); expect(shaderExpression).toEqual(expected); });