diff --git a/.travis.yml b/.travis.yml index 024b8097b877..42139a1752fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ script: - echo 'makeZipFile' && echo -en 'travis_fold:start:script.makeZipFile\\r' - npm run clean - - npm run makeZipFile + - npm run makeZipFile -- --concurrency 2 - npm pack - echo -en 'travis_fold:end:script.makeZipFile\\r' diff --git a/Apps/Sandcastle/gallery/Classification.html b/Apps/Sandcastle/gallery/Classification.html new file mode 100644 index 000000000000..bafb8cf32ffa --- /dev/null +++ b/Apps/Sandcastle/gallery/Classification.html @@ -0,0 +1,232 @@ + + +
+ + + + + +true if the EXT_blend_minmax extension is supported. This
+ * extension extends blending capabilities by adding two new blend equations:
+ * the minimum or maximum color components of the source and destination colors.
+ * @memberof Context.prototype
+ * @type {Boolean}
+ * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_blend_minmax/}
+ */
+ blendMinmax : {
+ get : function() {
+ return this._blendMinmax || this._webgl2;
}
},
@@ -510,7 +527,7 @@ define([
*/
depthTexture : {
get : function() {
- return this._depthTexture;
+ return this._depthTexture || this._webgl2;
}
},
@@ -523,7 +540,7 @@ define([
*/
floatingPointTexture : {
get : function() {
- return this._textureFloat;
+ return this._textureFloat || this._colorBufferFloat;
}
},
@@ -597,7 +614,7 @@ define([
*/
fragmentDepth : {
get : function() {
- return this._fragDepth;
+ return this._fragDepth || this._webgl2;
}
},
@@ -614,6 +631,20 @@ define([
}
},
+ /**
+ * true if the EXT_color_buffer_float extension is supported. This
+ * extension makes the formats gl.R16F, gl.RG16F, gl.RGBA16F, gl.R32F, gl.RG32F,
+ * gl.RGBA32F, gl.R11F_G11F_B10F color renderable.
+ * @memberof Context.prototype
+ * @type {Boolean}
+ * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_float/}
+ */
+ colorBufferFloat : {
+ get : function() {
+ return this._colorBufferFloat;
+ }
+ },
+
/**
* true if the WEBGL_draw_buffers extension is supported. This
* extensions provides support for multiple render targets. The framebuffer object can have mutiple
@@ -1076,7 +1107,8 @@ define([
}),
uniformMap : overrides.uniformMap,
owner : overrides.owner,
- framebuffer : overrides.framebuffer
+ framebuffer : overrides.framebuffer,
+ pass : overrides.pass
});
};
diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js
index 3b30bebbd290..07ab7633fe12 100644
--- a/Source/Renderer/RenderState.js
+++ b/Source/Renderer/RenderState.js
@@ -21,7 +21,9 @@ define([
function validateBlendEquation(blendEquation) {
return ((blendEquation === WebGLConstants.FUNC_ADD) ||
(blendEquation === WebGLConstants.FUNC_SUBTRACT) ||
- (blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT));
+ (blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT) ||
+ (blendEquation === WebGLConstants.MIN) ||
+ (blendEquation === WebGLConstants.MAX));
}
function validateBlendFunction(blendFunction) {
diff --git a/Source/Renderer/ShaderCache.js b/Source/Renderer/ShaderCache.js
index d8635213de6d..a2a9e86d7358 100644
--- a/Source/Renderer/ShaderCache.js
+++ b/Source/Renderer/ShaderCache.js
@@ -96,8 +96,8 @@ define([
});
}
- var vertexShaderText = vertexShaderSource.createCombinedVertexShader();
- var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader();
+ var vertexShaderText = vertexShaderSource.createCombinedVertexShader(this._context);
+ var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(this._context);
var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations);
var cachedShader;
@@ -169,10 +169,11 @@ define([
});
}
- var vertexShaderText = vertexShaderSource.createCombinedVertexShader();
- var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader();
-
var context = this._context;
+
+ var vertexShaderText = vertexShaderSource.createCombinedVertexShader(context);
+ var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(context);
+
var derivedShaderProgram = new ShaderProgram({
gl : context._gl,
logShaderCompilation : context.logShaderCompilation,
diff --git a/Source/Renderer/ShaderSource.js b/Source/Renderer/ShaderSource.js
index 36bb4637145b..b1f4684b93df 100644
--- a/Source/Renderer/ShaderSource.js
+++ b/Source/Renderer/ShaderSource.js
@@ -2,12 +2,14 @@ define([
'../Core/defaultValue',
'../Core/defined',
'../Core/DeveloperError',
+ '../Renderer/modernizeShader',
'../Shaders/Builtin/CzmBuiltins',
'./AutomaticUniforms'
], function(
defaultValue,
defined,
DeveloperError,
+ modernizeShader,
CzmBuiltins,
AutomaticUniforms) {
'use strict';
@@ -151,7 +153,7 @@ define([
return builtinsSource.replace(root.glslSource, '');
}
- function combineShader(shaderSource, isFragmentShader) {
+ function combineShader(shaderSource, isFragmentShader, context) {
var i;
var length;
@@ -186,6 +188,17 @@ define([
return '\n';
});
+ // Extract shader extensions from sources
+ var extensions = [];
+ combinedSources = combinedSources.replace(/#extension.*\n/gm, function(match) {
+ // Extract extension to put at the top
+ extensions.push(match);
+
+ // Replace original #extension directive with a new line so the line numbers
+ // are not off by one.
+ return '\n';
+ });
+
// Remove precision qualifier
combinedSources = combinedSources.replace(/precision\s(lowp|mediump|highp)\s(float|int);/, '');
@@ -204,6 +217,11 @@ define([
result = '#version ' + version + '\n';
}
+ var extensionsLength = extensions.length;
+ for (i = 0; i < extensionsLength; i++) {
+ result += extensions[i];
+ }
+
if (isFragmentShader) {
result += '\
#ifdef GL_FRAGMENT_PRECISION_HIGH\n\
@@ -224,6 +242,12 @@ define([
}
}
+ // GLSLModernizer inserts its own layout qualifiers
+ // at this position in the source
+ if (context.webgl2) {
+ result += '#define OUTPUT_DECLARATION\n\n';
+ }
+
// append built-ins
if (shaderSource.includeBuiltIns) {
result += getBuiltinsAndAutomaticUniforms(combinedSources);
@@ -235,6 +259,11 @@ define([
// append actual source
result += combinedSources;
+ // modernize the source
+ if (context.webgl2) {
+ result = modernizeShader(result, isFragmentShader, true);
+ }
+
return result;
}
@@ -297,19 +326,23 @@ define([
/**
* Create a single string containing the full, combined vertex shader with all dependencies and defines.
*
+ * @param {Context} context The current rendering context
+ *
* @returns {String} The combined shader string.
*/
- ShaderSource.prototype.createCombinedVertexShader = function() {
- return combineShader(this, false);
+ ShaderSource.prototype.createCombinedVertexShader = function(context) {
+ return combineShader(this, false, context);
};
/**
* Create a single string containing the full, combined fragment shader with all dependencies and defines.
*
+ * @param {Context} context The current rendering context
+ *
* @returns {String} The combined shader string.
*/
- ShaderSource.prototype.createCombinedFragmentShader = function() {
- return combineShader(this, true);
+ ShaderSource.prototype.createCombinedFragmentShader = function(context) {
+ return combineShader(this, true, context);
};
/**
diff --git a/Source/Renderer/Texture.js b/Source/Renderer/Texture.js
index 2de068ea749e..34d71a6d56a6 100644
--- a/Source/Renderer/Texture.js
+++ b/Source/Renderer/Texture.js
@@ -71,6 +71,23 @@ define([
internalFormat = WebGLConstants.DEPTH_COMPONENT24;
}
}
+
+ if (pixelDatatype === PixelDatatype.FLOAT) {
+ switch (pixelFormat) {
+ case PixelFormat.RGBA:
+ internalFormat = WebGLConstants.RGBA32F;
+ break;
+ case PixelFormat.RGB:
+ internalFormat = WebGLConstants.RGB32F;
+ break;
+ case PixelFormat.RG:
+ internalFormat = WebGLConstants.RG32F;
+ break;
+ case PixelFormat.R:
+ internalFormat = WebGLConstants.R32F;
+ break;
+ }
+ }
}
//>>includeStart('debug', pragmas.debug);
diff --git a/Source/Renderer/modernizeShader.js b/Source/Renderer/modernizeShader.js
new file mode 100644
index 000000000000..b2accdf7e62a
--- /dev/null
+++ b/Source/Renderer/modernizeShader.js
@@ -0,0 +1,227 @@
+define([
+ '../Core/defined',
+ '../Core/DeveloperError',
+ ], function(
+ defined,
+ DeveloperError) {
+ 'use strict';
+
+ /**
+ * A function to port GLSL shaders from GLSL ES 1.00 to GLSL ES 3.00
+ *
+ * This function is nowhere near comprehensive or complete. It just
+ * handles some common cases.
+ *
+ * Note that this function requires the presence of the
+ * "#define OUTPUT_DECLARATION" line that is appended
+ * by ShaderSource.
+ *
+ * @private
+ */
+ function modernizeShader(source, isFragmentShader) {
+ var outputDeclarationRegex = /#define OUTPUT_DECLARATION/;
+ var splitSource = source.split('\n');
+
+ if (/#version 300 es/g.test(source)) {
+ return source;
+ }
+
+ var outputDeclarationLine = -1;
+ var i, line;
+ for (i = 0; i < splitSource.length; ++i) {
+ line = splitSource[i];
+ if (outputDeclarationRegex.test(line)) {
+ outputDeclarationLine = i;
+ break;
+ }
+ }
+
+ if (outputDeclarationLine === -1) {
+ throw new DeveloperError('Could not find a #define OUTPUT_DECLARATION!');
+ }
+
+ var outputVariables = [];
+
+ for (i = 0; i < 10; i++) {
+ var fragDataString = 'gl_FragData\\[' + i + '\\]';
+ var newOutput = 'czm_out' + i;
+ var regex = new RegExp(fragDataString, 'g');
+ if (regex.test(source)) {
+ setAdd(newOutput, outputVariables);
+ replaceInSourceString(fragDataString, newOutput, splitSource);
+ splitSource.splice(outputDeclarationLine, 0, 'layout(location = ' + i + ') out vec4 ' + newOutput + ';');
+ outputDeclarationLine += 1;
+ }
+ }
+
+ var czmFragColor = 'czm_fragColor';
+ if (findInSource('gl_FragColor', splitSource)) {
+ setAdd(czmFragColor, outputVariables);
+ replaceInSourceString('gl_FragColor', czmFragColor, splitSource);
+ splitSource.splice(outputDeclarationLine, 0, 'layout(location = 0) out vec4 czm_fragColor;');
+ outputDeclarationLine += 1;
+ }
+
+ var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource);
+ var lineAdds = {};
+ for (i = 0; i < splitSource.length; i++) {
+ line = splitSource[i];
+ for (var variable in variableMap) {
+ if (variableMap.hasOwnProperty(variable)) {
+ var matchVar = new RegExp('(layout)[^]+(out)[^]+(' + variable + ')[^]+', 'g');
+ if (matchVar.test(line)) {
+ lineAdds[line] = variable;
+ }
+ }
+ }
+ }
+
+ for (var layoutDeclaration in lineAdds) {
+ if (lineAdds.hasOwnProperty(layoutDeclaration)) {
+ var variableName = lineAdds[layoutDeclaration];
+ var lineNumber = splitSource.indexOf(layoutDeclaration);
+ var entry = variableMap[variableName];
+ var depth = entry.length;
+ var d;
+ for (d = 0; d < depth; d++) {
+ splitSource.splice(lineNumber, 0, entry[d]);
+ }
+ lineNumber += depth + 1;
+ for (d = depth - 1; d >= 0; d--) {
+ splitSource.splice(lineNumber, 0, '#endif //' + entry[d]);
+ }
+ }
+ }
+
+ var versionThree = '#version 300 es';
+ var foundVersion = false;
+ for (i = 0; i < splitSource.length; i++) {
+ if (/#version/.test(splitSource[i])) {
+ splitSource[i] = versionThree;
+ foundVersion = true;
+ }
+ }
+
+ if (!foundVersion) {
+ splitSource.splice(0, 0, versionThree);
+ }
+
+ removeExtension('EXT_draw_buffers', splitSource);
+ removeExtension('EXT_frag_depth', splitSource);
+
+ replaceInSourceString('texture2D', 'texture', splitSource);
+ replaceInSourceString('texture3D', 'texture', splitSource);
+ replaceInSourceString('textureCube', 'texture', splitSource);
+ replaceInSourceString('gl_FragDepthEXT', 'gl_FragDepth', splitSource);
+
+ if (isFragmentShader) {
+ replaceInSourceString('varying', 'in', splitSource);
+ } else {
+ replaceInSourceString('attribute', 'in', splitSource);
+ replaceInSourceString('varying', 'out', splitSource);
+ }
+
+ return compileSource(splitSource);
+ }
+
+ // Note that this fails if your string looks like
+ // searchString[singleCharacter]searchString
+ function replaceInSourceString(str, replacement, splitSource) {
+ var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
+ var regex = new RegExp(regexStr, 'g');
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ splitSource[i] = line.replace(regex, '$1' + replacement + '$3');
+ }
+ }
+
+ function replaceInSourceRegex(regex, replacement, splitSource) {
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ splitSource[i] = line.replace(regex, replacement);
+ }
+ }
+
+ function findInSource(str, splitSource) {
+ var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
+ var regex = new RegExp(regexStr, 'g');
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ if (regex.test(line)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function compileSource(splitSource) {
+ var wholeSource = '';
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ wholeSource += splitSource[i] + '\n';
+ }
+ return wholeSource;
+ }
+
+ function setAdd(variable, set) {
+ if (set.indexOf(variable) === -1) {
+ set.push(variable);
+ }
+ }
+
+ function getVariablePreprocessorBranch(layoutVariables, splitSource) {
+ var variableMap = {};
+
+ var numLayoutVariables = layoutVariables.length;
+
+ var stack = [];
+ for (var i = 0; i < splitSource.length; ++i) {
+ var line = splitSource[i];
+ var hasIF = /(#ifdef|#if)/g.test(line);
+ var hasELSE = /#else/g.test(line);
+ var hasENDIF = /#endif/g.test(line);
+
+ if (hasIF) {
+ stack.push(line);
+ } else if (hasELSE) {
+ var top = stack[stack.length - 1];
+ var op = top.replace('ifdef', 'ifndef');
+ if (/if/g.test(op)) {
+ op = op.replace(/(#if\s+)(\S*)([^]*)/, '$1!($2)$3');
+ }
+ stack.pop();
+ stack.push(op);
+ } else if (hasENDIF) {
+ stack.pop();
+ } else if (!/layout/g.test(line)) {
+ for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) {
+ var varName = layoutVariables[varIndex];
+ if (line.indexOf(varName) !== -1) {
+ if (!defined(variableMap[varName])) {
+ variableMap[varName] = stack.slice();
+ } else {
+ variableMap[varName] = variableMap[varName].filter(function(x) {
+ return stack.indexOf(x) >= 0;
+ });
+ }
+ }
+ }
+ }
+ }
+
+ return variableMap;
+ }
+
+ function removeExtension(name, splitSource) {
+ var regex = '#extension\\s+GL_' + name + '\\s+:\\s+[a-zA-Z0-9]+\\s*$';
+ replaceInSourceRegex(new RegExp(regex, 'g'), '', splitSource);
+ }
+
+ return modernizeShader;
+});
diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js
index fd2a50198a5f..0bcd5f7e3181 100644
--- a/Source/Scene/Batched3DModel3DTileContent.js
+++ b/Source/Scene/Batched3DModel3DTileContent.js
@@ -13,6 +13,7 @@ define([
'../Core/getStringFromTypedArray',
'../Core/RequestType',
'../Core/RuntimeError',
+ '../Renderer/Pass',
'./Cesium3DTileBatchTable',
'./Cesium3DTileFeature',
'./Cesium3DTileFeatureTable',
@@ -33,6 +34,7 @@ define([
getStringFromTypedArray,
RequestType,
RuntimeError,
+ Pass,
Cesium3DTileBatchTable,
Cesium3DTileFeature,
Cesium3DTileFeatureTable,
@@ -352,6 +354,7 @@ define([
gltf : gltfView,
cull : false, // The model is already culled by 3D Tiles
releaseGltfJson : true, // Models are unique and will not benefit from caching so save memory
+ opaquePass : Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass
basePath : basePath,
requestType : RequestType.TILES3D,
modelMatrix : tile.computedTransform,
diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js
index 88e8d1870597..56d0fef92103 100644
--- a/Source/Scene/Billboard.js
+++ b/Source/Scene/Billboard.js
@@ -76,22 +76,47 @@ define([
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
- if (defined(options.scaleByDistance) && options.scaleByDistance.far <= options.scaleByDistance.near) {
- throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
+ if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) {
+ throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.');
}
- if (defined(options.translucencyByDistance) && options.translucencyByDistance.far <= options.translucencyByDistance.near) {
- throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
+ //>>includeEnd('debug');
+
+ var translucencyByDistance = options.translucencyByDistance;
+ var pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance;
+ var scaleByDistance = options.scaleByDistance;
+ var distanceDisplayCondition = options.distanceDisplayCondition;
+ if (defined(translucencyByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (translucencyByDistance.far <= translucencyByDistance.near) {
+ throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ translucencyByDistance = NearFarScalar.clone(translucencyByDistance);
}
- if (defined(options.pixelOffsetScaleByDistance) && options.pixelOffsetScaleByDistance.far <= options.pixelOffsetScaleByDistance.near) {
- throw new DeveloperError('pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near.');
+ if (defined(pixelOffsetScaleByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (pixelOffsetScaleByDistance.far <= pixelOffsetScaleByDistance.near) {
+ throw new DeveloperError('pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ pixelOffsetScaleByDistance = NearFarScalar.clone(pixelOffsetScaleByDistance);
}
- if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) {
- throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near');
+ if (defined(scaleByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (scaleByDistance.far <= scaleByDistance.near) {
+ throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ scaleByDistance = NearFarScalar.clone(scaleByDistance);
}
- if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) {
- throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.');
+ if (defined(distanceDisplayCondition)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (distanceDisplayCondition.far <= distanceDisplayCondition.near) {
+ throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near.');
+ }
+ //>>includeEnd('debug');
+ distanceDisplayCondition = DistanceDisplayCondition.clone(distanceDisplayCondition);
}
- //>>includeEnd('debug');
this._show = defaultValue(options.show, true);
this._position = Cartesian3.clone(defaultValue(options.position, Cartesian3.ZERO));
@@ -108,11 +133,11 @@ define([
this._alignedAxis = Cartesian3.clone(defaultValue(options.alignedAxis, Cartesian3.ZERO));
this._width = options.width;
this._height = options.height;
- this._scaleByDistance = options.scaleByDistance;
- this._translucencyByDistance = options.translucencyByDistance;
- this._pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance;
+ this._scaleByDistance = scaleByDistance;
+ this._translucencyByDistance = translucencyByDistance;
+ this._pixelOffsetScaleByDistance = pixelOffsetScaleByDistance;
this._sizeInMeters = defaultValue(options.sizeInMeters, false);
- this._distanceDisplayCondition = options.distanceDisplayCondition;
+ this._distanceDisplayCondition = distanceDisplayCondition;
this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0);
this._id = options.id;
this._collection = defaultValue(options.collection, billboardCollection);
diff --git a/Source/Scene/BlendEquation.js b/Source/Scene/BlendEquation.js
index 27bf019d21eb..e589beeae418 100644
--- a/Source/Scene/BlendEquation.js
+++ b/Source/Scene/BlendEquation.js
@@ -34,9 +34,27 @@ define([
* @type {Number}
* @constant
*/
- REVERSE_SUBTRACT : WebGLConstants.FUNC_REVERSE_SUBTRACT
+ REVERSE_SUBTRACT : WebGLConstants.FUNC_REVERSE_SUBTRACT,
- // No min and max like in ColladaFX GLES2 profile
+ /**
+ * Pixel values are given to the minimum function (min(source, destination)).
+ *
+ * This equation operates on each pixel color component.
+ *
+ * @type {Number}
+ * @constant
+ */
+ MIN : WebGLConstants.MIN,
+
+ /**
+ * Pixel values are given to the maximum function (max(source, destination)).
+ *
+ * This equation operates on each pixel color component.
+ *
+ * @type {Number}
+ * @constant
+ */
+ MAX : WebGLConstants.MAX
};
return freezeObject(BlendEquation);
diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js
index 79401ae40314..f2b10bc06534 100644
--- a/Source/Scene/Cesium3DTileBatchTable.js
+++ b/Source/Scene/Cesium3DTileBatchTable.js
@@ -68,6 +68,9 @@ define([
StencilOperation) {
'use strict';
+ var DEFAULT_COLOR_VALUE = Color.WHITE;
+ var DEFAULT_SHOW_VALUE = true;
+
/**
* @private
*/
@@ -414,7 +417,7 @@ define([
Check.typeOf.object('color', color);
//>>includeEnd('debug');
- if (Color.equals(color, Color.WHITE) && !defined(this._batchValues)) {
+ if (Color.equals(color, DEFAULT_COLOR_VALUE) && !defined(this._batchValues)) {
// Avoid allocating since the default is white
return;
}
@@ -475,7 +478,7 @@ define([
//>>includeEnd('debug');
if (!defined(this._batchValues)) {
- return Color.clone(Color.WHITE, result);
+ return Color.clone(DEFAULT_COLOR_VALUE, result);
}
var batchValues = this._batchValues;
@@ -495,7 +498,7 @@ define([
Cesium3DTileBatchTable.prototype.applyStyle = function(frameState, style) {
if (!defined(style)) {
- this.setAllColor(Color.WHITE);
+ this.setAllColor(DEFAULT_COLOR_VALUE);
this.setAllShow(true);
return;
}
@@ -504,8 +507,8 @@ define([
var length = this.featuresLength;
for (var i = 0; i < length; ++i) {
var feature = content.getFeature(i);
- var color = style.color.evaluateColor(frameState, feature, scratchColor);
- var show = style.show.evaluate(frameState, feature);
+ var color = defined(style.color) ? style.color.evaluateColor(frameState, feature, scratchColor) : DEFAULT_COLOR_VALUE;
+ var show = defined(style.show) ? style.show.evaluate(frameState, feature) : DEFAULT_SHOW_VALUE;
this.setColor(i, color);
this.setShow(i, show);
}
@@ -1305,10 +1308,6 @@ define([
// even though their style is opaque.
var translucentCommand = (derivedCommand.pass === Pass.TRANSLUCENT);
- if (!translucentCommand) {
- derivedCommand.pass = Pass.CESIUM_3D_TILE;
- }
-
derivedCommand.uniformMap = defined(derivedCommand.uniformMap) ? derivedCommand.uniformMap : {};
derivedCommand.uniformMap.tile_translucentCommand = function() {
return translucentCommand;
diff --git a/Source/Scene/Cesium3DTileStyle.js b/Source/Scene/Cesium3DTileStyle.js
index 02b4d55947ae..a21770cf06ea 100644
--- a/Source/Scene/Cesium3DTileStyle.js
+++ b/Source/Scene/Cesium3DTileStyle.js
@@ -22,10 +22,6 @@ define([
Expression) {
'use strict';
- var DEFAULT_JSON_COLOR_EXPRESSION = 'color("#ffffff")';
- var DEFAULT_JSON_BOOLEAN_EXPRESSION = true;
- var DEFAULT_JSON_NUMBER_EXPRESSION = 1.0;
-
/**
* A style that is applied to a {@link Cesium3DTileset}.
*
@@ -95,49 +91,13 @@ define([
styleJson = defaultValue(styleJson, defaultValue.EMPTY_OBJECT);
- that._colorShaderFunctionReady = !defined(styleJson.color);
- that._showShaderFunctionReady = !defined(styleJson.show);
- that._pointSizeShaderFunctionReady = !defined(styleJson.pointSize);
-
- var colorExpression = defaultValue(styleJson.color, DEFAULT_JSON_COLOR_EXPRESSION);
- var showExpression = defaultValue(styleJson.show, DEFAULT_JSON_BOOLEAN_EXPRESSION);
- var pointSizeExpression = defaultValue(styleJson.pointSize, DEFAULT_JSON_NUMBER_EXPRESSION);
-
- var defines = styleJson.defines;
-
- var color;
- if (typeof colorExpression === 'string') {
- color = new Expression(colorExpression, defines);
- } else if (defined(colorExpression.conditions)) {
- color = new ConditionsExpression(colorExpression, defines);
- }
-
- that._color = color;
-
- var show;
- if (typeof showExpression === 'boolean') {
- show = new Expression(String(showExpression), defines);
- } else if (typeof showExpression === 'string') {
- show = new Expression(showExpression, defines);
- } else if (defined(showExpression.conditions)) {
- show = new ConditionsExpression(showExpression, defines);
- }
-
- that._show = show;
-
- var pointSize;
- if (typeof pointSizeExpression === 'number') {
- pointSize = new Expression(String(pointSizeExpression), defines);
- } else if (typeof pointSizeExpression === 'string') {
- pointSize = new Expression(pointSizeExpression, defines);
- } else if (defined(pointSizeExpression.conditions)) {
- pointSize = new ConditionsExpression(pointSizeExpression, defines);
- }
-
- that._pointSize = pointSize;
+ that.color = styleJson.color;
+ that.show = styleJson.show;
+ that.pointSize = styleJson.pointSize;
var meta = {};
if (defined(styleJson.meta)) {
+ var defines = styleJson.defines;
var metaJson = defaultValue(styleJson.meta, defaultValue.EMPTY_OBJECT);
for (var property in metaJson) {
if (metaJson.hasOwnProperty(property)) {
@@ -209,7 +169,8 @@ define([
},
/**
- * Gets or sets the {@link StyleExpression} object used to evaluate the style's show property.
+ * Gets or sets the {@link StyleExpression} object used to evaluate the style's show property. Alternatively a boolean, string, or object defining a show style can be used.
+ * The getter will return the internal {@link Expression} or {@link ConditionsExpression}, which may differ from the value provided to the setter.
*
* The expression must return or convert to a Boolean.
*
color property.
+ * Gets or sets the {@link StyleExpression} object used to evaluate the style's color property. Alternatively a string or object defining a color style can be used.
+ * The getter will return the internal {@link Expression} or {@link ConditionsExpression}, which may differ from the value provided to the setter.
*
* The expression must return a Color.
*
pointSize property.
+ * Gets or sets the {@link StyleExpression} object used to evaluate the style's pointSize property. Alternatively a number, string, or object defining a pointSize style can be used.
+ * The getter will return the internal {@link Expression} or {@link ConditionsExpression}, which may differ from the value provided to the setter.
*
* The expression must return or convert to a Number.
*
+ * A primitive combines the geometry instance with an {@link Appearance} that describes the full shading, including + * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement, + * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix + * and match most of them and add a new geometry or appearance independently of each other. Only the {@link PerInstanceColorAppearance} + * is supported at this time. + *
+ *+ * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there + * will be rendering artifacts for some viewing angles. + *
+ *+ * Valid geometries are {@link BoxGeometry}, {@link CylinderGeometry}, {@link EllipsoidGeometry}, {@link PolylineVolumeGeometry}, and {@link SphereGeometry}. + *
+ *+ * Geometries that follow the surface of the ellipsoid, such as {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}, + * are also valid if they are extruded volumes; otherwise, they will not be rendered. + *
+ * + * @alias ClassificationPrimitive + * @constructor + * + * @param {Object} [options] Object with the following properties: + * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. This can either be a single instance or an array of length one. + * @param {Boolean} [options.show=true] Determines if this primitive will be shown. + * @param {Boolean} [options.vertexCacheOptimize=false] Whentrue, geometry vertices are optimized for the pre and post-vertex-shader caches.
+ * @param {Boolean} [options.interleave=false] When true, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
+ * @param {Boolean} [options.compressVertices=true] When true, the geometry vertices are compressed, which will save memory.
+ * @param {Boolean} [options.releaseGeometryInstances=true] When true, the primitive does not keep a reference to the input geometryInstances to save memory.
+ * @param {Boolean} [options.allowPicking=true] When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved.
+ * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first.
+ * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
+ * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be true on
+ * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be false.
+ *
+ * @see Primitive
+ * @see GroundPrimitive
+ * @see GeometryInstance
+ * @see Appearance
+ */
+ function ClassificationPrimitive(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ /**
+ * The geometry instance rendered with this primitive. This may
+ * be undefined if options.releaseGeometryInstances
+ * is true when the primitive is constructed.
+ * + * Changing this property after the primitive is rendered has no effect. + *
+ *
+ * Because of the rendering technique used, all geometry instances must be the same color.
+ * If there is an instance with a differing color, a DeveloperError will be thrown
+ * on the first attempt to render.
+ *
+ * Draws the bounding sphere for each draw command in the primitive. + *
+ * + * @type {Boolean} + * + * @default false + */ + this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false); + /** + * This property is for debugging only; it is not for production use nor is it optimized. + *+ * Draws the shadow volume for each geometry in the primitive. + *
+ * + * @type {Boolean} + * + * @default false + */ + this.debugShowShadowVolume = defaultValue(options.debugShowShadowVolume, false); + this._debugShowShadowVolume = false; + + // These are used by GroundPrimitive to augment the shader and uniform map. + this._extruded = defaultValue(options._extruded, false); + this._uniformMap = options._uniformMap; + + this._sp = undefined; + this._spPick = undefined; + + this._rsStencilPreloadPass = undefined; + this._rsStencilDepthPass = undefined; + this._rsColorPass = undefined; + this._rsPickPass = undefined; + + this._ready = false; + this._readyPromise = when.defer(); + + this._primitive = undefined; + this._pickPrimitive = options._pickPrimitive; + + var appearance = new PerInstanceColorAppearance({ + flat : true + }); + + var readOnlyAttributes; + if (defined(this.geometryInstances) && isArray(this.geometryInstances) && this.geometryInstances.length > 1) { + readOnlyAttributes = ClassificationPrimitiveReadOnlyInstanceAttributes; + } + + this._createBoundingVolumeFunction = options._createBoundingVolumeFunction; + this._updateAndQueueCommandsFunction = options._updateAndQueueCommandsFunction; + + this._primitiveOptions = { + geometryInstances : undefined, + appearance : appearance, + vertexCacheOptimize : defaultValue(options.vertexCacheOptimize, false), + interleave : defaultValue(options.interleave, false), + releaseGeometryInstances : defaultValue(options.releaseGeometryInstances, true), + allowPicking : defaultValue(options.allowPicking, true), + asynchronous : defaultValue(options.asynchronous, true), + compressVertices : defaultValue(options.compressVertices, true), + _readOnlyInstanceAttributes : readOnlyAttributes, + _createBoundingVolumeFunction : undefined, + _createRenderStatesFunction : undefined, + _createShaderProgramFunction : undefined, + _createCommandsFunction : undefined, + _updateAndQueueCommandsFunction : undefined, + _createPickOffsets : true + }; + } + + defineProperties(ClassificationPrimitive.prototype, { + /** + * Whentrue, geometry vertices are optimized for the pre and post-vertex-shader caches.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default true
+ */
+ vertexCacheOptimize : {
+ get : function() {
+ return this._primitiveOptions.vertexCacheOptimize;
+ }
+ },
+
+ /**
+ * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default false
+ */
+ interleave : {
+ get : function() {
+ return this._primitiveOptions.interleave;
+ }
+ },
+
+ /**
+ * When true, the primitive does not keep a reference to the input geometryInstances to save memory.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default true
+ */
+ releaseGeometryInstances : {
+ get : function() {
+ return this._primitiveOptions.releaseGeometryInstances;
+ }
+ },
+
+ /**
+ * When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default true
+ */
+ allowPicking : {
+ get : function() {
+ return this._primitiveOptions.allowPicking;
+ }
+ },
+
+ /**
+ * Determines if the geometry instances will be created and batched on a web worker.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default true
+ */
+ asynchronous : {
+ get : function() {
+ return this._primitiveOptions.asynchronous;
+ }
+ },
+
+ /**
+ * When true, geometry vertices are compressed, which will save memory.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ *
+ * @default true
+ */
+ compressVertices : {
+ get : function() {
+ return this._primitiveOptions.compressVertices;
+ }
+ },
+
+ /**
+ * Determines if the primitive is complete and ready to render. If this property is
+ * true, the primitive will be rendered the next time that {@link ClassificationPrimitive#update}
+ * is called.
+ *
+ * @memberof ClassificationPrimitive.prototype
+ *
+ * @type {Boolean}
+ * @readonly
+ */
+ ready : {
+ get : function() {
+ return this._ready;
+ }
+ },
+
+ /**
+ * Gets a promise that resolves when the primitive is ready to render.
+ * @memberof ClassificationPrimitive.prototype
+ * @type {Promise.true if ClassificationPrimitives are supported; otherwise, returns false
+ */
+ ClassificationPrimitive.isSupported = function(scene) {
+ return scene.context.stencilBuffer;
+ };
+
+ function getStencilPreloadRenderState(enableStencil) {
+ return {
+ colorMask : {
+ red : false,
+ green : false,
+ blue : false,
+ alpha : false
+ },
+ stencilTest : {
+ enabled : enableStencil,
+ frontFunction : StencilFunction.ALWAYS,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.DECREMENT_WRAP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ backFunction : StencilFunction.ALWAYS,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.INCREMENT_WRAP,
+ zPass : StencilOperation.INCREMENT_WRAP
+ },
+ reference : 0,
+ mask : ~0
+ },
+ depthTest : {
+ enabled : false
+ },
+ depthMask : false
+ };
+ }
+
+ function getStencilDepthRenderState(enableStencil) {
+ return {
+ colorMask : {
+ red : false,
+ green : false,
+ blue : false,
+ alpha : false
+ },
+ stencilTest : {
+ enabled : enableStencil,
+ frontFunction : StencilFunction.ALWAYS,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.INCREMENT_WRAP
+ },
+ backFunction : StencilFunction.ALWAYS,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ reference : 0,
+ mask : ~0
+ },
+ depthTest : {
+ enabled : true,
+ func : DepthFunction.LESS_OR_EQUAL
+ },
+ depthMask : false
+ };
+ }
+
+
+ function getColorRenderState(enableStencil) {
+ return {
+ stencilTest : {
+ enabled : enableStencil,
+ frontFunction : StencilFunction.NOT_EQUAL,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ backFunction : StencilFunction.NOT_EQUAL,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ reference : 0,
+ mask : ~0
+ },
+ depthTest : {
+ enabled : false
+ },
+ depthMask : false,
+ blending : BlendingState.ALPHA_BLEND
+ };
+ }
+
+ var pickRenderState = {
+ stencilTest : {
+ enabled : true,
+ frontFunction : StencilFunction.NOT_EQUAL,
+ frontOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ backFunction : StencilFunction.NOT_EQUAL,
+ backOperation : {
+ fail : StencilOperation.KEEP,
+ zFail : StencilOperation.KEEP,
+ zPass : StencilOperation.DECREMENT_WRAP
+ },
+ reference : 0,
+ mask : ~0
+ },
+ depthTest : {
+ enabled : false
+ },
+ depthMask : false
+ };
+
+ function createRenderStates(classificationPrimitive, context, appearance, twoPasses) {
+ if (defined(classificationPrimitive._rsStencilPreloadPass)) {
+ return;
+ }
+ var stencilEnabled = !classificationPrimitive.debugShowShadowVolume;
+
+ classificationPrimitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled));
+ classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled));
+ classificationPrimitive._rsColorPass = RenderState.fromCache(getColorRenderState(stencilEnabled));
+ classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState);
+ }
+
+ function modifyForEncodedNormals(primitive, vertexShaderSource) {
+ if (!primitive.compressVertices) {
+ return vertexShaderSource;
+ }
+
+ if (vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1) {
+ var attributeName = 'compressedAttributes';
+
+ //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes
+ var attributeDecl = 'attribute vec2 ' + attributeName + ';';
+
+ var globalDecl = 'vec3 extrudeDirection;\n';
+ var decode = ' extrudeDirection = czm_octDecode(' + attributeName + ', 65535.0);\n';
+
+ var modifiedVS = vertexShaderSource;
+ modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+extrudeDirection;/g, '');
+ modifiedVS = ShaderSource.replaceMain(modifiedVS, 'czm_non_compressed_main');
+ var compressedMain =
+ 'void main() \n' +
+ '{ \n' +
+ decode +
+ ' czm_non_compressed_main(); \n' +
+ '}';
+
+ return [attributeDecl, globalDecl, modifiedVS, compressedMain].join('\n');
+ }
+ }
+
+ function createShaderProgram(classificationPrimitive, frameState, appearance) {
+ if (defined(classificationPrimitive._sp)) {
+ return;
+ }
+
+ var context = frameState.context;
+ var primitive = classificationPrimitive._primitive;
+ var vs = ShadowVolumeVS;
+ vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(vs);
+ vs = Primitive._appendShowToShader(primitive, vs);
+ vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
+ vs = Primitive._modifyShaderPosition(classificationPrimitive, vs, frameState.scene3DOnly);
+ vs = Primitive._updateColorAttribute(primitive, vs);
+
+ if (classificationPrimitive._extruded) {
+ vs = modifyForEncodedNormals(primitive, vs);
+ }
+
+ var extrudedDefine = classificationPrimitive._extruded ? 'EXTRUDED_GEOMETRY' : '';
+
+ var vsSource = new ShaderSource({
+ defines : [extrudedDefine],
+ sources : [vs]
+ });
+ var fsSource = new ShaderSource({
+ sources : [ShadowVolumeFS]
+ });
+ var attributeLocations = classificationPrimitive._primitive._attributeLocations;
+
+ classificationPrimitive._sp = ShaderProgram.replaceCache({
+ context : context,
+ shaderProgram : classificationPrimitive._sp,
+ vertexShaderSource : vsSource,
+ fragmentShaderSource : fsSource,
+ attributeLocations : attributeLocations
+ });
+
+ if (classificationPrimitive._primitive.allowPicking) {
+ var vsPick = ShaderSource.createPickVertexShaderSource(vs);
+ vsPick = Primitive._updatePickColorAttribute(vsPick);
+
+ var pickVS = new ShaderSource({
+ defines : [extrudedDefine],
+ sources : [vsPick]
+ });
+
+ var pickFS = new ShaderSource({
+ sources : [ShadowVolumeFS],
+ pickColorQualifier : 'varying'
+ });
+
+ classificationPrimitive._spPick = ShaderProgram.replaceCache({
+ context : context,
+ shaderProgram : classificationPrimitive._spPick,
+ vertexShaderSource : pickVS,
+ fragmentShaderSource : pickFS,
+ attributeLocations : attributeLocations
+ });
+ } else {
+ classificationPrimitive._spPick = ShaderProgram.fromCache({
+ context : context,
+ vertexShaderSource : vsSource,
+ fragmentShaderSource : fsSource,
+ attributeLocations : attributeLocations
+ });
+ }
+ }
+
+ function createColorCommands(classificationPrimitive, colorCommands) {
+ var primitive = classificationPrimitive._primitive;
+ var length = primitive._va.length * 3;
+ colorCommands.length = length;
+
+ var vaIndex = 0;
+ var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap);
+
+ for (var i = 0; i < length; i += 3) {
+ var vertexArray = primitive._va[vaIndex++];
+
+ // stencil preload command
+ var command = colorCommands[i];
+ if (!defined(command)) {
+ command = colorCommands[i] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.renderState = classificationPrimitive._rsStencilPreloadPass;
+ command.shaderProgram = classificationPrimitive._sp;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+
+ // stencil depth command
+ command = colorCommands[i + 1];
+ if (!defined(command)) {
+ command = colorCommands[i + 1] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.renderState = classificationPrimitive._rsStencilDepthPass;
+ command.shaderProgram = classificationPrimitive._sp;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+
+ // color command
+ command = colorCommands[i + 2];
+ if (!defined(command)) {
+ command = colorCommands[i + 2] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.renderState = classificationPrimitive._rsColorPass;
+ command.shaderProgram = classificationPrimitive._sp;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+ }
+ }
+
+ function createPickCommands(classificationPrimitive, pickCommands) {
+ var primitive = classificationPrimitive._primitive;
+ var pickOffsets = primitive._pickOffsets;
+ var length = pickOffsets.length * 3;
+ pickCommands.length = length;
+
+ var pickIndex = 0;
+ var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap);
+
+ for (var j = 0; j < length; j += 3) {
+ var pickOffset = pickOffsets[pickIndex++];
+
+ var offset = pickOffset.offset;
+ var count = pickOffset.count;
+ var vertexArray = primitive._va[pickOffset.index];
+
+ // stencil preload command
+ var command = pickCommands[j];
+ if (!defined(command)) {
+ command = pickCommands[j] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.offset = offset;
+ command.count = count;
+ command.renderState = classificationPrimitive._rsStencilPreloadPass;
+ command.shaderProgram = classificationPrimitive._sp;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+
+ // stencil depth command
+ command = pickCommands[j + 1];
+ if (!defined(command)) {
+ command = pickCommands[j + 1] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.offset = offset;
+ command.count = count;
+ command.renderState = classificationPrimitive._rsStencilDepthPass;
+ command.shaderProgram = classificationPrimitive._sp;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+
+ // color command
+ command = pickCommands[j + 2];
+ if (!defined(command)) {
+ command = pickCommands[j + 2] = new DrawCommand({
+ owner : classificationPrimitive,
+ primitiveType : primitive._primitiveType
+ });
+ }
+
+ command.vertexArray = vertexArray;
+ command.offset = offset;
+ command.count = count;
+ command.renderState = classificationPrimitive._rsPickPass;
+ command.shaderProgram = classificationPrimitive._spPick;
+ command.uniformMap = uniformMap;
+ command.pass = Pass.GROUND;
+ }
+ }
+
+ function createCommands(classificationPrimitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) {
+ createColorCommands(classificationPrimitive, colorCommands);
+ createPickCommands(classificationPrimitive, pickCommands);
+ }
+
+ function updateAndQueueCommands(classificationPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) {
+ var primitive = classificationPrimitive._primitive;
+ Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);
+
+ var boundingVolumes;
+ if (frameState.mode === SceneMode.SCENE3D) {
+ boundingVolumes = primitive._boundingSphereWC;
+ } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
+ boundingVolumes = primitive._boundingSphereCV;
+ } else if (frameState.mode === SceneMode.SCENE2D && defined(primitive._boundingSphere2D)) {
+ boundingVolumes = primitive._boundingSphere2D;
+ } else if (defined(primitive._boundingSphereMorph)) {
+ boundingVolumes = primitive._boundingSphereMorph;
+ }
+
+ var commandList = frameState.commandList;
+ var passes = frameState.passes;
+ if (passes.render) {
+ var colorLength = colorCommands.length;
+ for (var i = 0; i < colorLength; ++i) {
+ var colorCommand = colorCommands[i];
+ colorCommand.modelMatrix = modelMatrix;
+ colorCommand.boundingVolume = boundingVolumes[Math.floor(i / 3)];
+ colorCommand.cull = cull;
+ colorCommand.debugShowBoundingVolume = debugShowBoundingVolume;
+
+ commandList.push(colorCommand);
+ }
+ }
+
+ if (passes.pick) {
+ var pickOffsets = primitive._pickOffsets;
+ var length = pickOffsets.length * 3;
+ pickCommands.length = length;
+
+ for (var j = 0; j < length; ++j) {
+ var pickOffset = pickOffsets[Math.floor(j / 3)];
+ var pickCommand = pickCommands[j];
+ pickCommand.modelMatrix = modelMatrix;
+ pickCommand.boundingVolume = boundingVolumes[pickOffset.index];
+ pickCommand.cull = cull;
+
+ commandList.push(pickCommand);
+ }
+ }
+ }
+
+ /**
+ * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
+ * get the draw commands needed to render this primitive.
+ * + * Do not call this function directly. This is documented just to + * list the exceptions that may be propagated when the scene is rendered: + *
+ * + * @exception {DeveloperError} All instance geometries must have the same primitiveType. + * @exception {DeveloperError} Appearance and material have a uniform with the same name. + * @exception {DeveloperError} Not all of the geometry instances have the same color attribute. + */ + ClassificationPrimitive.prototype.update = function(frameState) { + if (!this.show || (!defined(this._primitive) && !defined(this.geometryInstances))) { + return; + } + + var that = this; + var primitiveOptions = this._primitiveOptions; + + if (!defined(this._primitive)) { + var instances = isArray(this.geometryInstances) ? this.geometryInstances : [this.geometryInstances]; + var length = instances.length; + + var i; + var instance; + //>>includeStart('debug', pragmas.debug); + var color; + for (i = 0; i < length; ++i) { + instance = instances[i]; + var attributes = instance.attributes; + if (!defined(attributes) || !defined(attributes.color)) { + throw new DeveloperError('Not all of the geometry instances have the same color attribute.'); + } else if (defined(color) && !ColorGeometryInstanceAttribute.equals(color, attributes.color)) { + throw new DeveloperError('Not all of the geometry instances have the same color attribute.'); + } else if (!defined(color)) { + color = attributes.color; + } + } + //>>includeEnd('debug'); + + var geometryInstances = new Array(length); + for (i = 0; i < length; ++i) { + instance = instances[i]; + geometryInstances[i] = new GeometryInstance({ + geometry : instance.geometry, + attributes : instance.attributes, + modelMatrix : instance.modelMatrix, + id : instance.id, + pickPrimitive : defaultValue(this._pickPrimitive, that) + }); + } + + primitiveOptions.geometryInstances = geometryInstances; + + if (defined(this._createBoundingVolumeFunction)) { + primitiveOptions._createBoundingVolumeFunction = function(frameState, geometry) { + that._createBoundingVolumeFunction(frameState, geometry); + }; + } + + primitiveOptions._createRenderStatesFunction = function(primitive, context, appearance, twoPasses) { + createRenderStates(that, context); + }; + primitiveOptions._createShaderProgramFunction = function(primitive, frameState, appearance) { + createShaderProgram(that, frameState); + }; + primitiveOptions._createCommandsFunction = function(primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) { + createCommands(that, undefined, undefined, true, false, colorCommands, pickCommands); + }; + + if (defined(this._updateAndQueueCommandsFunction)) { + primitiveOptions._updateAndQueueCommandsFunction = function(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { + that._updateAndQueueCommandsFunction(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses); + }; + } else { + primitiveOptions._updateAndQueueCommandsFunction = function(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { + updateAndQueueCommands(that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses); + }; + } + + this._primitive = new Primitive(primitiveOptions); + this._primitive.readyPromise.then(function(primitive) { + that._ready = true; + + if (that.releaseGeometryInstances) { + that.geometryInstances = undefined; + } + + var error = primitive._error; + if (!defined(error)) { + that._readyPromise.resolve(that); + } else { + that._readyPromise.reject(error); + } + }); + } + + if (this.debugShowShadowVolume && !this._debugShowShadowVolume && this._ready) { + this._debugShowShadowVolume = true; + this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false)); + this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false)); + this._rsColorPass = RenderState.fromCache(getColorRenderState(false)); + } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) { + this._debugShowShadowVolume = false; + this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(true)); + this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(true)); + this._rsColorPass = RenderState.fromCache(getColorRenderState(true)); + } + + this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; + this._primitive.update(frameState); + }; + + /** + * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. + * + * @param {Object} id The id of the {@link GeometryInstance}. + * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. + * + * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. + * + * @example + * var attributes = primitive.getGeometryInstanceAttributes('an id'); + * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); + * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); + */ + ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function(id) { + //>>includeStart('debug', pragmas.debug); + if (!defined(this._primitive)) { + throw new DeveloperError('must call update before calling getGeometryInstanceAttributes'); + } + //>>includeEnd('debug'); + return this._primitive.getGeometryInstanceAttributes(id); + }; + + /** + * Returns true if this object was destroyed; otherwise, false. + *
+ * If this object was destroyed, it should not be used; calling any function other than
+ * isDestroyed will result in a {@link DeveloperError} exception.
+ *
true if this object was destroyed; otherwise, false.
+ *
+ * @see ClassificationPrimitive#destroy
+ */
+ ClassificationPrimitive.prototype.isDestroyed = function() {
+ return false;
+ };
+
+ /**
+ * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
+ * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
+ *
+ * Once an object is destroyed, it should not be used; calling any function other than
+ * isDestroyed will result in a {@link DeveloperError} exception. Therefore,
+ * assign the return value (undefined) to the object as done in the example.
+ *
true if GroundPrimitives are supported; otherwise, returns false
*/
- GroundPrimitive.isSupported = function(scene) {
- return scene.context.stencilBuffer;
- };
+ GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;
GroundPrimitive._defaultMaxTerrainHeight = 9000.0;
GroundPrimitive._defaultMinTerrainHeight = -100000.0;
@@ -417,123 +371,6 @@ define([
};
}
- function getStencilPreloadRenderState(enableStencil) {
- return {
- colorMask : {
- red : false,
- green : false,
- blue : false,
- alpha : false
- },
- stencilTest : {
- enabled : enableStencil,
- frontFunction : StencilFunction.ALWAYS,
- frontOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.DECREMENT_WRAP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- backFunction : StencilFunction.ALWAYS,
- backOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.INCREMENT_WRAP,
- zPass : StencilOperation.INCREMENT_WRAP
- },
- reference : 0,
- mask : ~0
- },
- depthTest : {
- enabled : false
- },
- depthMask : false
- };
- }
-
- function getStencilDepthRenderState(enableStencil) {
- return {
- colorMask : {
- red : false,
- green : false,
- blue : false,
- alpha : false
- },
- stencilTest : {
- enabled : enableStencil,
- frontFunction : StencilFunction.ALWAYS,
- frontOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.INCREMENT_WRAP
- },
- backFunction : StencilFunction.ALWAYS,
- backOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- reference : 0,
- mask : ~0
- },
- depthTest : {
- enabled : true,
- func : DepthFunction.LESS_OR_EQUAL
- },
- depthMask : false
- };
- }
-
-
- function getColorRenderState(enableStencil) {
- return {
- stencilTest : {
- enabled : enableStencil,
- frontFunction : StencilFunction.NOT_EQUAL,
- frontOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- backFunction : StencilFunction.NOT_EQUAL,
- backOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- reference : 0,
- mask : ~0
- },
- depthTest : {
- enabled : false
- },
- depthMask : false,
- blending : BlendingState.ALPHA_BLEND
- };
- }
-
- var pickRenderState = {
- stencilTest : {
- enabled : true,
- frontFunction : StencilFunction.NOT_EQUAL,
- frontOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- backFunction : StencilFunction.NOT_EQUAL,
- backOperation : {
- fail : StencilOperation.KEEP,
- zFail : StencilOperation.KEEP,
- zPass : StencilOperation.DECREMENT_WRAP
- },
- reference : 0,
- mask : ~0
- },
- depthTest : {
- enabled : false
- },
- depthMask : false
- };
-
var scratchBVCartesianHigh = new Cartesian3();
var scratchBVCartesianLow = new Cartesian3();
var scratchBVCartesian = new Cartesian3();
@@ -717,229 +554,6 @@ define([
}
}
- function createRenderStates(groundPrimitive, context, appearance, twoPasses) {
- if (defined(groundPrimitive._rsStencilPreloadPass)) {
- return;
- }
- var stencilEnabled = !groundPrimitive.debugShowShadowVolume;
-
- groundPrimitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled));
- groundPrimitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled));
- groundPrimitive._rsColorPass = RenderState.fromCache(getColorRenderState(stencilEnabled));
- groundPrimitive._rsPickPass = RenderState.fromCache(pickRenderState);
- }
-
- function modifyForEncodedNormals(primitive, vertexShaderSource) {
- if (!primitive.compressVertices) {
- return vertexShaderSource;
- }
-
- if (vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1) {
- var attributeName = 'compressedAttributes';
-
- //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes
- var attributeDecl = 'attribute vec2 ' + attributeName + ';';
-
- var globalDecl = 'vec3 extrudeDirection;\n';
- var decode = ' extrudeDirection = czm_octDecode(' + attributeName + ', 65535.0);\n';
-
- var modifiedVS = vertexShaderSource;
- modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+extrudeDirection;/g, '');
- modifiedVS = ShaderSource.replaceMain(modifiedVS, 'czm_non_compressed_main');
- var compressedMain =
- 'void main() \n' +
- '{ \n' +
- decode +
- ' czm_non_compressed_main(); \n' +
- '}';
-
- return [attributeDecl, globalDecl, modifiedVS, compressedMain].join('\n');
- }
- }
-
- function createShaderProgram(groundPrimitive, frameState, appearance) {
- if (defined(groundPrimitive._sp)) {
- return;
- }
-
- var context = frameState.context;
- var primitive = groundPrimitive._primitive;
- var vs = ShadowVolumeVS;
- vs = groundPrimitive._primitive._batchTable.getVertexShaderCallback()(vs);
- vs = Primitive._appendShowToShader(primitive, vs);
- vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs);
- vs = Primitive._modifyShaderPosition(groundPrimitive, vs, frameState.scene3DOnly);
- vs = Primitive._updateColorAttribute(primitive, vs);
- vs = modifyForEncodedNormals(primitive, vs);
-
- var fs = ShadowVolumeFS;
- var attributeLocations = groundPrimitive._primitive._attributeLocations;
-
- groundPrimitive._sp = ShaderProgram.replaceCache({
- context : context,
- shaderProgram : groundPrimitive._sp,
- vertexShaderSource : vs,
- fragmentShaderSource : fs,
- attributeLocations : attributeLocations
- });
-
- if (groundPrimitive._primitive.allowPicking) {
- var vsPick = ShaderSource.createPickVertexShaderSource(vs);
- vsPick = Primitive._updatePickColorAttribute(vsPick);
-
- var pickFS = new ShaderSource({
- sources : [fs],
- pickColorQualifier : 'varying'
- });
- groundPrimitive._spPick = ShaderProgram.replaceCache({
- context : context,
- shaderProgram : groundPrimitive._spPick,
- vertexShaderSource : vsPick,
- fragmentShaderSource : pickFS,
- attributeLocations : attributeLocations
- });
- } else {
- groundPrimitive._spPick = ShaderProgram.fromCache({
- context : context,
- vertexShaderSource : vs,
- fragmentShaderSource : fs,
- attributeLocations : attributeLocations
- });
- }
- }
-
- function createColorCommands(groundPrimitive, colorCommands) {
- var primitive = groundPrimitive._primitive;
- var length = primitive._va.length * 3;
- colorCommands.length = length;
-
- var vaIndex = 0;
- var uniformMap = primitive._batchTable.getUniformMapCallback()(groundPrimitive._uniformMap);
-
- for (var i = 0; i < length; i += 3) {
- var vertexArray = primitive._va[vaIndex++];
-
- // stencil preload command
- var command = colorCommands[i];
- if (!defined(command)) {
- command = colorCommands[i] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.renderState = groundPrimitive._rsStencilPreloadPass;
- command.shaderProgram = groundPrimitive._sp;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
-
- // stencil depth command
- command = colorCommands[i + 1];
- if (!defined(command)) {
- command = colorCommands[i + 1] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.renderState = groundPrimitive._rsStencilDepthPass;
- command.shaderProgram = groundPrimitive._sp;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
-
- // color command
- command = colorCommands[i + 2];
- if (!defined(command)) {
- command = colorCommands[i + 2] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.renderState = groundPrimitive._rsColorPass;
- command.shaderProgram = groundPrimitive._sp;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
- }
- }
-
- function createPickCommands(groundPrimitive, pickCommands) {
- var primitive = groundPrimitive._primitive;
- var pickOffsets = primitive._pickOffsets;
- var length = pickOffsets.length * 3;
- pickCommands.length = length;
-
- var pickIndex = 0;
- var uniformMap = primitive._batchTable.getUniformMapCallback()(groundPrimitive._uniformMap);
-
- for (var j = 0; j < length; j += 3) {
- var pickOffset = pickOffsets[pickIndex++];
-
- var offset = pickOffset.offset;
- var count = pickOffset.count;
- var vertexArray = primitive._va[pickOffset.index];
-
- // stencil preload command
- var command = pickCommands[j];
- if (!defined(command)) {
- command = pickCommands[j] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.offset = offset;
- command.count = count;
- command.renderState = groundPrimitive._rsStencilPreloadPass;
- command.shaderProgram = groundPrimitive._sp;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
-
- // stencil depth command
- command = pickCommands[j + 1];
- if (!defined(command)) {
- command = pickCommands[j + 1] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.offset = offset;
- command.count = count;
- command.renderState = groundPrimitive._rsStencilDepthPass;
- command.shaderProgram = groundPrimitive._sp;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
-
- // color command
- command = pickCommands[j + 2];
- if (!defined(command)) {
- command = pickCommands[j + 2] = new DrawCommand({
- owner : groundPrimitive,
- primitiveType : primitive._primitiveType
- });
- }
-
- command.vertexArray = vertexArray;
- command.offset = offset;
- command.count = count;
- command.renderState = groundPrimitive._rsPickPass;
- command.shaderProgram = groundPrimitive._spPick;
- command.uniformMap = uniformMap;
- command.pass = Pass.GROUND;
- }
- }
-
- function createCommands(groundPrimitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) {
- createColorCommands(groundPrimitive, colorCommands);
- createPickCommands(groundPrimitive, pickCommands);
- }
-
function updateAndQueueCommands(groundPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) {
var boundingVolumes;
if (frameState.mode === SceneMode.SCENE3D) {
@@ -952,40 +566,35 @@ define([
var passes = frameState.passes;
if (passes.render) {
var colorLength = colorCommands.length;
- for (var j = 0; j < colorLength; ++j) {
- colorCommands[j].modelMatrix = modelMatrix;
- colorCommands[j].boundingVolume = boundingVolumes[Math.floor(j / 3)];
- colorCommands[j].cull = cull;
- colorCommands[j].debugShowBoundingVolume = debugShowBoundingVolume;
-
- commandList.push(colorCommands[j]);
+ for (var i = 0; i < colorLength; ++i) {
+ var colorCommand = colorCommands[i];
+ colorCommand.owner = groundPrimitive;
+ colorCommand.modelMatrix = modelMatrix;
+ colorCommand.boundingVolume = boundingVolumes[Math.floor(i / 3)];
+ colorCommand.cull = cull;
+ colorCommand.debugShowBoundingVolume = debugShowBoundingVolume;
+
+ commandList.push(colorCommand);
}
}
if (passes.pick) {
- var primitive = groundPrimitive._primitive;
+ var primitive = groundPrimitive._primitive._primitive;
var pickOffsets = primitive._pickOffsets;
var length = pickOffsets.length * 3;
pickCommands.length = length;
- var pickIndex = 0;
- for (var k = 0; k < length; k += 3) {
- var pickOffset = pickOffsets[pickIndex++];
+ for (var j = 0; j < length; ++j) {
+ var pickOffset = pickOffsets[Math.floor(j / 3)];
var bv = boundingVolumes[pickOffset.index];
- pickCommands[k].modelMatrix = modelMatrix;
- pickCommands[k].boundingVolume = bv;
- pickCommands[k].cull = cull;
-
- pickCommands[k + 1].modelMatrix = modelMatrix;
- pickCommands[k + 1].boundingVolume = bv;
- pickCommands[k + 1].cull = cull;
-
- pickCommands[k + 2].modelMatrix = modelMatrix;
- pickCommands[k + 2].boundingVolume = bv;
- pickCommands[k + 2].cull = cull;
+ var pickCommand = pickCommands[j];
+ pickCommand.owner = groundPrimitive;
+ pickCommand.modelMatrix = modelMatrix;
+ pickCommand.boundingVolume = bv;
+ pickCommand.cull = cull;
- commandList.push(pickCommands[k], pickCommands[k + 1], pickCommands[k + 2]);
+ commandList.push(pickCommand);
}
}
}
@@ -1057,7 +666,6 @@ define([
var groundInstances = new Array(length);
var i;
- var color;
var rectangle;
for (i = 0; i < length; ++i) {
instance = instances[i];
@@ -1077,19 +685,7 @@ define([
}
instanceType = geometry.constructor;
- if (defined(instanceType) && defined(instanceType.createShadowVolume)) {
- var attributes = instance.attributes;
-
- //>>includeStart('debug', pragmas.debug);
- if (!defined(attributes) || !defined(attributes.color)) {
- throw new DeveloperError('Not all of the geometry instances have the same color attribute.');
- } else if (defined(color) && !ColorGeometryInstanceAttribute.equals(color, attributes.color)) {
- throw new DeveloperError('Not all of the geometry instances have the same color attribute.');
- } else if (!defined(color)) {
- color = attributes.color;
- }
- //>>includeEnd('debug');
- } else {
+ if (!defined(instanceType) || !defined(instanceType.createShadowVolume)) {
//>>includeStart('debug', pragmas.debug);
throw new DeveloperError('Not all of the geometry instances have GroundPrimitive support.');
//>>includeEnd('debug');
@@ -1110,8 +706,7 @@ define([
geometry : instanceType.createShadowVolume(geometry, getComputeMinimumHeightFunction(this),
getComputeMaximumHeightFunction(this)),
attributes : instance.attributes,
- id : instance.id,
- pickPrimitive : this
+ id : instance.id
});
}
@@ -1120,20 +715,11 @@ define([
primitiveOptions._createBoundingVolumeFunction = function(frameState, geometry) {
createBoundingVolume(that, frameState, geometry);
};
- primitiveOptions._createRenderStatesFunction = function(primitive, context, appearance, twoPasses) {
- createRenderStates(that, context);
- };
- primitiveOptions._createShaderProgramFunction = function(primitive, frameState, appearance) {
- createShaderProgram(that, frameState);
- };
- primitiveOptions._createCommandsFunction = function(primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) {
- createCommands(that, undefined, undefined, true, false, colorCommands, pickCommands);
- };
primitiveOptions._updateAndQueueCommandsFunction = function(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) {
updateAndQueueCommands(that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses);
};
- this._primitive = new Primitive(primitiveOptions);
+ this._primitive = new ClassificationPrimitive(primitiveOptions);
this._primitive.readyPromise.then(function(primitive) {
that._ready = true;
@@ -1150,18 +736,7 @@ define([
});
}
- if (this.debugShowShadowVolume && !this._debugShowShadowVolume && this._ready) {
- this._debugShowShadowVolume = true;
- this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false));
- this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false));
- this._rsColorPass = RenderState.fromCache(getColorRenderState(false));
- } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) {
- this._debugShowShadowVolume = false;
- this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(true));
- this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(true));
- this._rsColorPass = RenderState.fromCache(getColorRenderState(true));
- }
-
+ this._primitive.debugShowShadowVolume = this.debugShowShadowVolume;
this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
this._primitive.update(frameState);
};
@@ -1235,8 +810,6 @@ define([
*/
GroundPrimitive.prototype.destroy = function() {
this._primitive = this._primitive && this._primitive.destroy();
- this._sp = this._sp && this._sp.destroy();
- this._spPick = this._spPick && this._spPick.destroy();
return destroyObject(this);
};
diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js
index df1de7ca0593..429f51d35200 100644
--- a/Source/Scene/Instanced3DModel3DTileContent.js
+++ b/Source/Scene/Instanced3DModel3DTileContent.js
@@ -21,6 +21,7 @@ define([
'../Core/RuntimeError',
'../Core/Transforms',
'../Core/TranslationRotationScale',
+ '../Renderer/Pass',
'./Cesium3DTileBatchTable',
'./Cesium3DTileFeature',
'./Cesium3DTileFeatureTable',
@@ -48,6 +49,7 @@ define([
RuntimeError,
Transforms,
TranslationRotationScale,
+ Pass,
Cesium3DTileBatchTable,
Cesium3DTileFeature,
Cesium3DTileFeatureTable,
@@ -303,11 +305,16 @@ define([
gltf : undefined,
basePath : undefined,
incrementallyLoadTextures : false,
- upAxis : content._tileset._gltfUpAxis
+ upAxis : content._tileset._gltfUpAxis,
+ opaquePass : Pass.CESIUM_3D_TILE // Draw opaque portions during the 3D Tiles pass
};
if (gltfFormat === 0) {
var gltfUrl = getStringFromTypedArray(gltfView);
+
+ // We need to remove padding from the end of the model URL in case this tile was part of a composite tile.
+ // This removes all white space and null characters from the end of the string.
+ gltfUrl = gltfUrl.replace(/[\s\0]+$/, '');
collectionOptions.url = getAbsoluteUri(joinUrls(getBaseUri(content._url, true), gltfUrl));
} else {
collectionOptions.gltf = gltfView;
diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js
index 54865f47fde8..813522182a0e 100644
--- a/Source/Scene/Label.js
+++ b/Source/Scene/Label.js
@@ -68,22 +68,47 @@ define([
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
- if (defined(options.translucencyByDistance) && options.translucencyByDistance.far <= options.translucencyByDistance.near) {
- throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
+ if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) {
+ throw new DeveloperError('disableDepthTestDistance must be greater than 0.0.');
}
- if (defined(options.pixelOffsetScaleByDistance) && options.pixelOffsetScaleByDistance.far <= options.pixelOffsetScaleByDistance.near) {
- throw new DeveloperError('pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near.');
+ //>>includeEnd('debug');
+
+ var translucencyByDistance = options.translucencyByDistance;
+ var pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance;
+ var scaleByDistance = options.scaleByDistance;
+ var distanceDisplayCondition = options.distanceDisplayCondition;
+ if (defined(translucencyByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (translucencyByDistance.far <= translucencyByDistance.near) {
+ throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ translucencyByDistance = NearFarScalar.clone(translucencyByDistance);
}
- if (defined(options.scaleByDistance) && options.scaleByDistance.far <= options.scaleByDistance.near) {
- throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
+ if (defined(pixelOffsetScaleByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (pixelOffsetScaleByDistance.far <= pixelOffsetScaleByDistance.near) {
+ throw new DeveloperError('pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ pixelOffsetScaleByDistance = NearFarScalar.clone(pixelOffsetScaleByDistance);
}
- if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) {
- throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near');
+ if (defined(scaleByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (scaleByDistance.far <= scaleByDistance.near) {
+ throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ scaleByDistance = NearFarScalar.clone(scaleByDistance);
}
- if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) {
- throw new DeveloperError('disableDepthTestDistance must be greater than 0.0.');
+ if (defined(distanceDisplayCondition)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (distanceDisplayCondition.far <= distanceDisplayCondition.near) {
+ throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near.');
+ }
+ //>>includeEnd('debug');
+ distanceDisplayCondition = DistanceDisplayCondition.clone(distanceDisplayCondition);
}
- //>>includeEnd('debug');
this._text = defaultValue(options.text, '');
this._show = defaultValue(options.show, true);
@@ -102,11 +127,11 @@ define([
this._position = Cartesian3.clone(defaultValue(options.position, Cartesian3.ZERO));
this._scale = defaultValue(options.scale, 1.0);
this._id = options.id;
- this._translucencyByDistance = options.translucencyByDistance;
- this._pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance;
- this._scaleByDistance = options.scaleByDistance;
+ this._translucencyByDistance = translucencyByDistance;
+ this._pixelOffsetScaleByDistance = pixelOffsetScaleByDistance;
+ this._scaleByDistance = scaleByDistance;
this._heightReference = defaultValue(options.heightReference, HeightReference.NONE);
- this._distanceDisplayCondition = options.distanceDisplayCondition;
+ this._distanceDisplayCondition = distanceDisplayCondition;
this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0);
this._labelCollection = labelCollection;
@@ -194,9 +219,7 @@ define([
backgroundBillboard.position = value;
}
- if (this._heightReference !== HeightReference.NONE) {
- this._updateClamping();
- }
+ this._updateClamping();
}
}
},
@@ -964,22 +987,17 @@ define([
this._actualClampedPosition = Cartesian3.clone(value, this._actualClampedPosition);
var glyphs = this._glyphs;
- value = defaultValue(value, this._position);
for (var i = 0, len = glyphs.length; i < len; i++) {
var glyph = glyphs[i];
if (defined(glyph.billboard)) {
// Set all the private values here, because we already clamped to ground
// so we don't want to do it again for every glyph
glyph.billboard._clampedPosition = value;
- Cartesian3.clone(value, glyph.billboard._position);
- Cartesian3.clone(value, glyph.billboard._actualPosition);
}
}
var backgroundBillboard = this._backgroundBillboard;
if (defined(backgroundBillboard)) {
backgroundBillboard._clampedPosition = value;
- Cartesian3.clone(value, backgroundBillboard._position);
- Cartesian3.clone(value, backgroundBillboard._actualPosition);
}
}
},
diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js
index c93803a6c255..efb9f4fd11b0 100644
--- a/Source/Scene/LabelCollection.js
+++ b/Source/Scene/LabelCollection.js
@@ -106,6 +106,10 @@ define([
if (defined(billboard)) {
billboard.show = false;
billboard.image = undefined;
+ if (defined(billboard._removeCallbackFunc)) {
+ billboard._removeCallbackFunc();
+ billboard._removeCallbackFunc = undefined;
+ }
labelCollection._spareBillboards.push(billboard);
glyph.billboard = undefined;
}
diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js
index 06425037b1f8..0bd2a4d638bf 100644
--- a/Source/Scene/Model.js
+++ b/Source/Scene/Model.js
@@ -644,6 +644,12 @@ define([
*/
this.cull = defaultValue(options.cull, true);
+ /**
+ * @private
+ * @readonly
+ */
+ this.opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
+
this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale
this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
this._boundingSphere = undefined;
@@ -3483,7 +3489,7 @@ define([
uniformMap : uniformMap,
renderState : rs,
owner : owner,
- pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
+ pass : isTranslucent ? Pass.TRANSLUCENT : model.opaquePass
});
var pickCommand;
@@ -3522,7 +3528,7 @@ define([
uniformMap : pickUniformMap,
renderState : rs,
owner : owner,
- pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
+ pass : isTranslucent ? Pass.TRANSLUCENT : model.opaquePass
});
}
@@ -4764,6 +4770,11 @@ define([
destroy(this._rendererResources.vertexArrays);
}
+ if (defined(this._removeUpdateHeightCallback)) {
+ this._removeUpdateHeightCallback();
+ this._removeUpdateHeightCallback = undefined;
+ }
+
this._rendererResources = undefined;
this._cachedRendererResources = this._cachedRendererResources && this._cachedRendererResources.release();
diff --git a/Source/Scene/ModelInstanceCollection.js b/Source/Scene/ModelInstanceCollection.js
index 86f7586b6b50..514e34323921 100644
--- a/Source/Scene/ModelInstanceCollection.js
+++ b/Source/Scene/ModelInstanceCollection.js
@@ -16,6 +16,7 @@ define([
'../Renderer/Buffer',
'../Renderer/BufferUsage',
'../Renderer/DrawCommand',
+ '../Renderer/Pass',
'../Renderer/ShaderSource',
'../ThirdParty/when',
'./getAttributeOrUniformBySemantic',
@@ -41,6 +42,7 @@ define([
Buffer,
BufferUsage,
DrawCommand,
+ Pass,
ShaderSource,
when,
getAttributeOrUniformBySemantic,
@@ -107,12 +109,15 @@ define([
this._instancingSupported = false;
this._dynamic = defaultValue(options.dynamic, false);
this._allowPicking = defaultValue(options.allowPicking, true);
- this._cull = defaultValue(options.cull, true); // Undocumented option
this._ready = false;
this._readyPromise = when.defer();
this._state = LoadState.NEEDS_LOAD;
this._dirty = false;
+ // Undocumented options
+ this._cull = defaultValue(options.cull, true);
+ this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
+
this._instances = createInstances(this, options.instances);
// When the model instance collection is backed by an i3dm tile,
@@ -603,7 +608,8 @@ define([
pickVertexShaderLoaded : undefined,
pickFragmentShaderLoaded : undefined,
pickUniformMapLoaded : undefined,
- ignoreCommands : true
+ ignoreCommands : true,
+ opaquePass : collection._opaquePass
};
if (allowPicking && !usesBatchTable) {
diff --git a/Source/Scene/OrthographicFrustum.js b/Source/Scene/OrthographicFrustum.js
index 057b9d80fa11..bbb36ec006ac 100644
--- a/Source/Scene/OrthographicFrustum.js
+++ b/Source/Scene/OrthographicFrustum.js
@@ -6,9 +6,9 @@ define([
OrthographicFrustum) {
'use strict';
- function DeprecatedOrthographicFrustum() {
+ function DeprecatedOrthographicFrustum(options) {
deprecationWarning('OrthographicFrustum', 'Scene/OrthographicFrustum is deprecated. It has moved to Core/OrthographicFrustum in 1.36. Scene/OrthographicFrustum will be removed in 1.38.');
- return new OrthographicFrustum();
+ return new OrthographicFrustum(options);
}
return DeprecatedOrthographicFrustum;
diff --git a/Source/Scene/OrthographicOffCenterFrustum.js b/Source/Scene/OrthographicOffCenterFrustum.js
index 93a9f14edf45..54d53cf14321 100644
--- a/Source/Scene/OrthographicOffCenterFrustum.js
+++ b/Source/Scene/OrthographicOffCenterFrustum.js
@@ -6,9 +6,9 @@ define([
OrthographicOffCenterFrustum) {
'use strict';
- function DeprecatedOrthographicOffCenterFrustum() {
+ function DeprecatedOrthographicOffCenterFrustum(options) {
deprecationWarning('OrthographicOffCenterFrustum', 'Scene/OrthographicOffCenterFrustum is deprecated. It has moved to Core/OrthographicOffCenterFrustum in 1.36. Scene/OrthographicOffCenterFrustum will be removed in 1.38.');
- return new OrthographicOffCenterFrustum();
+ return new OrthographicOffCenterFrustum(options);
}
return DeprecatedOrthographicOffCenterFrustum;
diff --git a/Source/Scene/PerspectiveFrustum.js b/Source/Scene/PerspectiveFrustum.js
index c2ae3b511fa5..5030940f5c5c 100644
--- a/Source/Scene/PerspectiveFrustum.js
+++ b/Source/Scene/PerspectiveFrustum.js
@@ -6,9 +6,9 @@ define([
PerspectiveFrustum) {
'use strict';
- function DeprecatedPerspectiveFrustum() {
+ function DeprecatedPerspectiveFrustum(options) {
deprecationWarning('PerspectiveFrustum', 'Scene/PerspectiveFrustum is deprecated. It has moved to Core/PerspectiveFrustum in 1.36. Scene/PerspectiveFrustum will be removed in 1.38.');
- return new PerspectiveFrustum();
+ return new PerspectiveFrustum(options);
}
return DeprecatedPerspectiveFrustum;
diff --git a/Source/Scene/PerspectiveOffCenterFrustum.js b/Source/Scene/PerspectiveOffCenterFrustum.js
index 4953638ef85d..e8496b4ef925 100644
--- a/Source/Scene/PerspectiveOffCenterFrustum.js
+++ b/Source/Scene/PerspectiveOffCenterFrustum.js
@@ -6,9 +6,9 @@ define([
PerspectiveOffCenterFrustum) {
'use strict';
- function DeprecatedPerspectiveOffCenterFrustum() {
+ function DeprecatedPerspectiveOffCenterFrustum(options) {
deprecationWarning('PerspectiveOffCenterFrustum', 'Scene/PerspectiveOffCenterFrustum is deprecated. It has moved to Core/PerspectiveOffCenterFrustum in 1.36. Scene/PerspectiveOffCenterFrustum will be removed in 1.38.');
- return new PerspectiveOffCenterFrustum();
+ return new PerspectiveOffCenterFrustum(options);
}
return DeprecatedPerspectiveOffCenterFrustum;
diff --git a/Source/Scene/PointPrimitive.js b/Source/Scene/PointPrimitive.js
index 088f03b61dfa..b4e18e8eed9a 100644
--- a/Source/Scene/PointPrimitive.js
+++ b/Source/Scene/PointPrimitive.js
@@ -59,20 +59,39 @@ define([
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
- if (defined(options.scaleByDistance) && options.scaleByDistance.far <= options.scaleByDistance.near) {
- throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
- }
- if (defined(options.translucencyByDistance) && options.translucencyByDistance.far <= options.translucencyByDistance.near) {
- throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
- }
- if (defined(options.distanceDisplayCondition) && options.distanceDisplayCondition.far <= options.distanceDisplayCondition.near) {
- throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near');
- }
if (defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0) {
throw new DeveloperError('disableDepthTestDistance must be greater than or equal to 0.0.');
}
//>>includeEnd('debug');
+ var translucencyByDistance = options.translucencyByDistance;
+ var scaleByDistance = options.scaleByDistance;
+ var distanceDisplayCondition = options.distanceDisplayCondition;
+ if (defined(translucencyByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (translucencyByDistance.far <= translucencyByDistance.near) {
+ throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ translucencyByDistance = NearFarScalar.clone(translucencyByDistance);
+ }
+ if (defined(scaleByDistance)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (scaleByDistance.far <= scaleByDistance.near) {
+ throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.');
+ }
+ //>>includeEnd('debug');
+ scaleByDistance = NearFarScalar.clone(scaleByDistance);
+ }
+ if (defined(distanceDisplayCondition)) {
+ //>>includeStart('debug', pragmas.debug);
+ if (distanceDisplayCondition.far <= distanceDisplayCondition.near) {
+ throw new DeveloperError('distanceDisplayCondition.far must be greater than distanceDisplayCondition.near.');
+ }
+ //>>includeEnd('debug');
+ distanceDisplayCondition = DistanceDisplayCondition.clone(distanceDisplayCondition);
+ }
+
this._show = defaultValue(options.show, true);
this._position = Cartesian3.clone(defaultValue(options.position, Cartesian3.ZERO));
this._actualPosition = Cartesian3.clone(this._position); // For columbus view and 2D
@@ -80,9 +99,9 @@ define([
this._outlineColor = Color.clone(defaultValue(options.outlineColor, Color.TRANSPARENT));
this._outlineWidth = defaultValue(options.outlineWidth, 0.0);
this._pixelSize = defaultValue(options.pixelSize, 10.0);
- this._scaleByDistance = options.scaleByDistance;
- this._translucencyByDistance = options.translucencyByDistance;
- this._distanceDisplayCondition = options.distanceDisplayCondition;
+ this._scaleByDistance = scaleByDistance;
+ this._translucencyByDistance = translucencyByDistance;
+ this._distanceDisplayCondition = distanceDisplayCondition;
this._disableDepthTestDistance = defaultValue(options.disableDepthTestDistance, 0.0);
this._id = options.id;
this._collection = defaultValue(options.collection, pointPrimitiveCollection);
diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js
index a872bef41470..0484e51bff28 100644
--- a/Source/Scene/Primitive.js
+++ b/Source/Scene/Primitive.js
@@ -189,6 +189,8 @@ define([
*
* @see GeometryInstance
* @see Appearance
+ * @see ClassificationPrimitive
+ * @see GroundPrimitive
*/
function Primitive(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
@@ -201,6 +203,7 @@ define([
* Changing this property after the primitive is rendered has no effect.
*
*
+ * @readonly
* @type GeometryInstance[]|GeometryInstance
*
* @default undefined
@@ -1622,36 +1625,30 @@ define([
}
}
- function updateBoundingVolumes(primitive, frameState) {
+ Primitive._updateBoundingVolumes = function(primitive, frameState, modelMatrix) {
+ var i;
+ var length;
+ var boundingSphere;
+
// Update bounding volumes for primitives that are sized in pixels.
// The pixel size in meters varies based on the distance from the camera.
var pixelSize = primitive.appearance.pixelSize;
if (defined(pixelSize)) {
- var length = primitive._boundingSpheres.length;
- for (var i = 0; i < length; ++i) {
- var boundingSphere = primitive._boundingSpheres[i];
+ length = primitive._boundingSpheres.length;
+ for (i = 0; i < length; ++i) {
+ boundingSphere = primitive._boundingSpheres[i];
var boundingSphereWC = primitive._boundingSphereWC[i];
var pixelSizeInMeters = frameState.camera.getPixelSize(boundingSphere, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight);
var sizeInMeters = pixelSizeInMeters * pixelSize;
boundingSphereWC.radius = boundingSphere.radius + sizeInMeters;
}
}
- }
-
- function updateAndQueueCommands(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) {
- //>>includeStart('debug', pragmas.debug);
- if (frameState.mode !== SceneMode.SCENE3D && !Matrix4.equals(modelMatrix, Matrix4.IDENTITY)) {
- throw new DeveloperError('Primitive.modelMatrix is only supported in 3D mode.');
- }
- //>>includeEnd('debug');
-
- updateBoundingVolumes(primitive, frameState);
if (!Matrix4.equals(modelMatrix, primitive._modelMatrix)) {
Matrix4.clone(modelMatrix, primitive._modelMatrix);
- var length = primitive._boundingSpheres.length;
- for (var i = 0; i < length; ++i) {
- var boundingSphere = primitive._boundingSpheres[i];
+ length = primitive._boundingSpheres.length;
+ for (i = 0; i < length; ++i) {
+ boundingSphere = primitive._boundingSpheres[i];
if (defined(boundingSphere)) {
primitive._boundingSphereWC[i] = BoundingSphere.transform(boundingSphere, modelMatrix, primitive._boundingSphereWC[i]);
if (!frameState.scene3DOnly) {
@@ -1662,6 +1659,16 @@ define([
}
}
}
+ };
+
+ function updateAndQueueCommands(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) {
+ //>>includeStart('debug', pragmas.debug);
+ if (frameState.mode !== SceneMode.SCENE3D && !Matrix4.equals(modelMatrix, Matrix4.IDENTITY)) {
+ throw new DeveloperError('Primitive.modelMatrix is only supported in 3D mode.');
+ }
+ //>>includeEnd('debug');
+
+ Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);
var boundingSpheres;
if (frameState.mode === SceneMode.SCENE3D) {
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index 67193b1ca034..f99853f1ac4e 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -1901,6 +1901,17 @@ define([
executeCommand(commands[j], scene, context, passState);
}
+ us.updatePass(Pass.CESIUM_3D_TILE);
+ commands = frustumCommands.commands[Pass.CESIUM_3D_TILE];
+ length = frustumCommands.indices[Pass.CESIUM_3D_TILE];
+ for (j = 0; j < length; ++j) {
+ executeCommand(commands[j], scene, context, passState);
+ }
+
+ if (length > 0 && context.stencilBuffer) {
+ scene._stencilClearCommand.execute(context, passState);
+ }
+
if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer && (scene.copyGlobeDepth || scene.debugShowGlobeDepth)) {
globeDepth.update(context, passState);
globeDepth.executeCopyDepth(context, passState);
@@ -1929,13 +1940,6 @@ define([
}
}
- us.updatePass(Pass.CESIUM_3D_TILE);
- commands = frustumCommands.commands[Pass.CESIUM_3D_TILE];
- length = frustumCommands.indices[Pass.CESIUM_3D_TILE];
- for (j = 0; j < length; ++j) {
- executeCommand(commands[j], scene, context, passState);
- }
-
// Execute commands in order by pass up to the translucent pass.
// Translucent geometry needs special handling (sorting/OIT).
var startPass = Pass.GROUND + 1;
diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl
index c36fc9737286..34f8bc02c4ca 100644
--- a/Source/Shaders/GlobeFS.glsl
+++ b/Source/Shaders/GlobeFS.glsl
@@ -66,7 +66,7 @@ varying vec3 v_mieColor;
vec4 sampleAndBlend(
vec4 previousColor,
- sampler2D texture,
+ sampler2D textureToSample,
vec2 tileTextureCoordinates,
vec4 textureCoordinateRectangle,
vec4 textureCoordinateTranslationAndScale,
@@ -94,7 +94,7 @@ vec4 sampleAndBlend(
vec2 translation = textureCoordinateTranslationAndScale.xy;
vec2 scale = textureCoordinateTranslationAndScale.zw;
vec2 textureCoordinates = tileTextureCoordinates * scale + translation;
- vec4 value = texture2D(texture, textureCoordinates);
+ vec4 value = texture2D(textureToSample, textureCoordinates);
vec3 color = value.rgb;
float alpha = value.a;
diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl
index 809cf0be80a3..a85fc04a548e 100644
--- a/Source/Shaders/ShadowVolumeVS.glsl
+++ b/Source/Shaders/ShadowVolumeVS.glsl
@@ -1,10 +1,13 @@
attribute vec3 position3DHigh;
attribute vec3 position3DLow;
-attribute vec3 extrudeDirection;
attribute vec4 color;
attribute float batchId;
+#ifdef EXTRUDED_GEOMETRY
+attribute vec3 extrudeDirection;
+
uniform float u_globeMinimumAltitude;
+#endif
// emulated noperspective
varying float v_WindowZ;
@@ -22,10 +25,14 @@ void main()
v_color = color;
vec4 position = czm_computePosition();
+
+#ifdef EXTRUDED_GEOMETRY
float delta = min(u_globeMinimumAltitude, czm_geometricToleranceOverMeter * length(position.xyz));
delta *= czm_sceneMode == czm_sceneMode3D ? 1.0 : 0.0;
//extrudeDirection is zero for the top layer
position = position + vec4(extrudeDirection * delta, 0.0);
+#endif
+
gl_Position = depthClampFarPlane(czm_modelViewProjectionRelativeToEye * position);
}
diff --git a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js
index b58d82ec377d..6fff82c67a4a 100644
--- a/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js
+++ b/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js
@@ -1014,7 +1014,7 @@ define([
// Restore original color to feature that is no longer selected
var frameState = this._scene.frameState;
if (!this.colorize && defined(this._style)) {
- currentFeature.color = this._style.color.evaluateColor(frameState, currentFeature, scratchColor);
+ currentFeature.color = defined(this._style.color) ? this._style.color.evaluateColor(frameState, currentFeature, scratchColor) : Color.WHITE;
} else {
currentFeature.color = oldColor;
}
diff --git a/Source/Widgets/InfoBox/InfoBox.js b/Source/Widgets/InfoBox/InfoBox.js
index da8d533b05c8..10eed5e586cb 100644
--- a/Source/Widgets/InfoBox/InfoBox.js
+++ b/Source/Widgets/InfoBox/InfoBox.js
@@ -1,21 +1,21 @@
define([
'../../Core/buildModuleUrl',
+ '../../Core/Check',
'../../Core/Color',
'../../Core/defined',
'../../Core/defineProperties',
'../../Core/destroyObject',
- '../../Core/DeveloperError',
'../../ThirdParty/knockout',
'../getElement',
'../subscribeAndEvaluate',
'./InfoBoxViewModel'
], function(
buildModuleUrl,
+ Check,
Color,
defined,
defineProperties,
destroyObject,
- DeveloperError,
knockout,
getElement,
subscribeAndEvaluate,
@@ -34,9 +34,7 @@ define([
*/
function InfoBox(container) {
//>>includeStart('debug', pragmas.debug);
- if (!defined(container)) {
- throw new DeveloperError('container is required.');
- }
+ Check.defined('container', container);
//>>includeEnd('debug')
container = getElement(container);
diff --git a/Source/Workers/createFrustumGeometry.js b/Source/Workers/createFrustumGeometry.js
new file mode 100644
index 000000000000..3f1c141e6896
--- /dev/null
+++ b/Source/Workers/createFrustumGeometry.js
@@ -0,0 +1,17 @@
+define([
+ '../Core/defined',
+ '../Core/FrustumGeometry'
+ ], function(
+ defined,
+ FrustumGeometry) {
+ 'use strict';
+
+ function createFrustumGeometry(frustumGeometry, offset) {
+ if (defined(offset)) {
+ frustumGeometry = FrustumGeometry.unpack(frustumGeometry, offset);
+ }
+ return FrustumGeometry.createGeometry(frustumGeometry);
+ }
+
+ return createFrustumGeometry;
+});
diff --git a/Source/Workers/createFrustumOutlineGeometry.js b/Source/Workers/createFrustumOutlineGeometry.js
new file mode 100644
index 000000000000..4e6a84370bcf
--- /dev/null
+++ b/Source/Workers/createFrustumOutlineGeometry.js
@@ -0,0 +1,17 @@
+define([
+ '../Core/defined',
+ '../Core/FrustumOutlineGeometry'
+ ], function(
+ defined,
+ FrustumOutlineGeometry) {
+ 'use strict';
+
+ function createFrustumOutlineGeometry(frustumGeometry, offset) {
+ if (defined(offset)) {
+ frustumGeometry = FrustumOutlineGeometry.unpack(frustumGeometry, offset);
+ }
+ return FrustumOutlineGeometry.createGeometry(frustumGeometry);
+ }
+
+ return createFrustumOutlineGeometry;
+});
diff --git a/Source/Workers/transcodeCRNToDXT.js b/Source/Workers/transcodeCRNToDXT.js
index e5a28e1297cd..2d058b700e30 100644
--- a/Source/Workers/transcodeCRNToDXT.js
+++ b/Source/Workers/transcodeCRNToDXT.js
@@ -132,8 +132,12 @@ define([
// Mipmaps are unsupported, so copy the level 0 texture
// When mipmaps are supported, a copy will still be necessary as dxtData is a view on the heap.
var length = PixelFormat.compressedTextureSizeInBytes(format, width, height);
+
+ // Get a copy of the 0th mip level. dxtData will exceed length when there are more mip levels.
+ // Equivalent to dxtData.slice(0, length), which is not supported in IE11
+ var level0DXTDataView = dxtData.subarray(0, length);
var level0DXTData = new Uint8Array(length);
- level0DXTData.set(dxtData, 0);
+ level0DXTData.set(level0DXTDataView, 0);
transferableObjects.push(level0DXTData.buffer);
return new CompressedTextureBuffer(format, width, height, level0DXTData);
diff --git a/Specs/Core/CorridorGeometrySpec.js b/Specs/Core/CorridorGeometrySpec.js
index 129575e85781..4d4f0ac14780 100644
--- a/Specs/Core/CorridorGeometrySpec.js
+++ b/Specs/Core/CorridorGeometrySpec.js
@@ -41,6 +41,13 @@ defineSuite([
width: 10000
}));
expect(geometry).toBeUndefined();
+
+ geometry = CorridorGeometry.createGeometry(new CorridorGeometry({
+ positions : [new Cartesian3(-1349511.388149118, -5063973.22857992, 3623141.6372688496), //same lon/lat, different height
+ new Cartesian3(-1349046.4811926484, -5062228.688739784, 3621885.0521561056)],
+ width: 10000
+ }));
+ expect(geometry).toBeUndefined();
});
it('computes positions', function() {
diff --git a/Specs/Core/CorridorOutlineGeometrySpec.js b/Specs/Core/CorridorOutlineGeometrySpec.js
index f3317faaf0c4..4392d7b89468 100644
--- a/Specs/Core/CorridorOutlineGeometrySpec.js
+++ b/Specs/Core/CorridorOutlineGeometrySpec.js
@@ -35,6 +35,13 @@ defineSuite([
width: 10000
}));
expect(geometry).toBeUndefined();
+
+ geometry = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({
+ positions : [new Cartesian3(-1349511.388149118, -5063973.22857992, 3623141.6372688496), //same lon/lat, different height
+ new Cartesian3(-1349046.4811926484, -5062228.688739784, 3621885.0521561056)],
+ width: 10000
+ }));
+ expect(geometry).toBeUndefined();
});
it('computes positions', function() {
diff --git a/Specs/Core/FrustumGeometrySpec.js b/Specs/Core/FrustumGeometrySpec.js
new file mode 100644
index 000000000000..0b9f5669fc6d
--- /dev/null
+++ b/Specs/Core/FrustumGeometrySpec.js
@@ -0,0 +1,93 @@
+defineSuite([
+ 'Core/FrustumGeometry',
+ 'Core/Cartesian3',
+ 'Core/Math',
+ 'Core/PerspectiveFrustum',
+ 'Core/Quaternion',
+ 'Core/VertexFormat',
+ 'Specs/createPackableSpecs'
+ ], function(
+ FrustumGeometry,
+ Cartesian3,
+ CesiumMath,
+ PerspectiveFrustum,
+ Quaternion,
+ VertexFormat,
+ createPackableSpecs) {
+ 'use strict';
+
+ it('constructor throws without options', function() {
+ expect(function() {
+ return new FrustumGeometry();
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without frustum', function() {
+ expect(function() {
+ return new FrustumGeometry({
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without position', function() {
+ expect(function() {
+ return new FrustumGeometry({
+ frustum : new PerspectiveFrustum(),
+ orientation : Quaternion.IDENTITY
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without orientation', function() {
+ expect(function() {
+ return new FrustumGeometry({
+ frustum : new PerspectiveFrustum(),
+ origin : Cartesian3.ZERO
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor computes all vertex attributes', function() {
+ var frustum = new PerspectiveFrustum();
+ frustum.fov = CesiumMath.toRadians(30.0);
+ frustum.aspectRatio = 1920.0 / 1080.0;
+ frustum.near = 1.0;
+ frustum.far = 3.0;
+
+ var m = FrustumGeometry.createGeometry(new FrustumGeometry({
+ frustum : frustum,
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY,
+ vertexFormat : VertexFormat.ALL
+ }));
+
+ var numVertices = 24; //3 components x 8 corners
+ var numTriangles = 12; //6 sides x 2 triangles per side
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.attributes.normal.values.length).toEqual(numVertices * 3);
+ expect(m.attributes.tangent.values.length).toEqual(numVertices * 3);
+ expect(m.attributes.bitangent.values.length).toEqual(numVertices * 3);
+ expect(m.attributes.st.values.length).toEqual(numVertices * 2);
+
+ expect(m.indices.length).toEqual(numTriangles * 3);
+
+ expect(m.boundingSphere.center).toEqual(new Cartesian3(0.0, 0.0, 2.0));
+ expect(m.boundingSphere.radius).toBeGreaterThan(1.0);
+ expect(m.boundingSphere.radius).toBeLessThan(2.0);
+ });
+
+ var packableFrustum = new PerspectiveFrustum();
+ packableFrustum.fov = 1.0;
+ packableFrustum.aspectRatio = 2.0;
+ packableFrustum.near = 3.0;
+ packableFrustum.far = 4.0;
+
+ createPackableSpecs(FrustumGeometry, new FrustumGeometry({
+ frustum : packableFrustum,
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY,
+ vertexFormat : VertexFormat.POSITION_ONLY
+ }), [0.0, 1.0, 2.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
+});
diff --git a/Specs/Core/FrustumOutlineGeometrySpec.js b/Specs/Core/FrustumOutlineGeometrySpec.js
new file mode 100644
index 000000000000..71a46b75da84
--- /dev/null
+++ b/Specs/Core/FrustumOutlineGeometrySpec.js
@@ -0,0 +1,87 @@
+defineSuite([
+ 'Core/FrustumOutlineGeometry',
+ 'Core/Cartesian3',
+ 'Core/Math',
+ 'Core/PerspectiveFrustum',
+ 'Core/Quaternion',
+ 'Core/VertexFormat',
+ 'Specs/createPackableSpecs'
+ ], function(
+ FrustumOutlineGeometry,
+ Cartesian3,
+ CesiumMath,
+ PerspectiveFrustum,
+ Quaternion,
+ VertexFormat,
+ createPackableSpecs) {
+ 'use strict';
+
+ it('constructor throws without options', function() {
+ expect(function() {
+ return new FrustumOutlineGeometry();
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without frustum', function() {
+ expect(function() {
+ return new FrustumOutlineGeometry({
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without position', function() {
+ expect(function() {
+ return new FrustumOutlineGeometry({
+ frustum : new PerspectiveFrustum(),
+ orientation : Quaternion.IDENTITY
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor throws without orientation', function() {
+ expect(function() {
+ return new FrustumOutlineGeometry({
+ frustum : new PerspectiveFrustum(),
+ origin : Cartesian3.ZERO
+ });
+ }).toThrowDeveloperError();
+ });
+
+ it('constructor computes all vertex attributes', function() {
+ var frustum = new PerspectiveFrustum();
+ frustum.fov = CesiumMath.toRadians(30.0);
+ frustum.aspectRatio = 1920.0 / 1080.0;
+ frustum.near = 1.0;
+ frustum.far = 3.0;
+
+ var m = FrustumOutlineGeometry.createGeometry(new FrustumOutlineGeometry({
+ frustum : frustum,
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY
+ }));
+
+ var numVertices = 8;
+ var numLines = 12;
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.indices.length).toEqual(numLines * 2);
+
+ expect(m.boundingSphere.center).toEqual(new Cartesian3(0.0, 0.0, 2.0));
+ expect(m.boundingSphere.radius).toBeGreaterThan(1.0);
+ expect(m.boundingSphere.radius).toBeLessThan(2.0);
+ });
+
+ var packableFrustum = new PerspectiveFrustum();
+ packableFrustum.fov = 1.0;
+ packableFrustum.aspectRatio = 2.0;
+ packableFrustum.near = 3.0;
+ packableFrustum.far = 4.0;
+
+ createPackableSpecs(FrustumOutlineGeometry, new FrustumOutlineGeometry({
+ frustum : packableFrustum,
+ origin : Cartesian3.ZERO,
+ orientation : Quaternion.IDENTITY,
+ vertexFormat : VertexFormat.POSITION_ONLY
+ }), [0.0, 1.0, 2.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0]);
+});
diff --git a/Specs/Core/OrthographicFrustumSpec.js b/Specs/Core/OrthographicFrustumSpec.js
index f40c2ca60fe0..4ac99bd4e0ec 100644
--- a/Specs/Core/OrthographicFrustumSpec.js
+++ b/Specs/Core/OrthographicFrustumSpec.js
@@ -1,17 +1,19 @@
defineSuite([
- 'Core/OrthographicFrustum',
- 'Core/Cartesian2',
- 'Core/Cartesian3',
- 'Core/Cartesian4',
- 'Core/Math',
- 'Core/Matrix4'
-], function(
- OrthographicFrustum,
- Cartesian2,
- Cartesian3,
- Cartesian4,
- CesiumMath,
- Matrix4) {
+ 'Core/OrthographicFrustum',
+ 'Core/Cartesian2',
+ 'Core/Cartesian3',
+ 'Core/Cartesian4',
+ 'Core/Math',
+ 'Core/Matrix4',
+ 'Specs/createPackableSpecs'
+ ], function(
+ OrthographicFrustum,
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
+ CesiumMath,
+ Matrix4,
+ createPackableSpecs) {
'use strict';
var frustum, planes;
@@ -25,6 +27,30 @@ defineSuite([
planes = frustum.computeCullingVolume(new Cartesian3(), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Y).planes;
});
+ it('constructs', function() {
+ var options = {
+ width : 1.0,
+ aspectRatio : 2.0,
+ near : 3.0,
+ far : 4.0,
+ xOffset : 5.0,
+ yOffset : 6.0
+ };
+ var f = new OrthographicFrustum(options);
+ expect(f.width).toEqual(options.width);
+ expect(f.aspectRatio).toEqual(options.aspectRatio);
+ expect(f.near).toEqual(options.near);
+ expect(f.far).toEqual(options.far);
+ });
+
+ it('default constructs', function() {
+ var f = new OrthographicFrustum();
+ expect(f.width).toBeUndefined();
+ expect(f.aspectRatio).toBeUndefined();
+ expect(f.near).toEqual(1.0);
+ expect(f.far).toEqual(500000000.0);
+ });
+
it('undefined width causes an exception', function() {
frustum.width = undefined;
expect(function() {
@@ -167,4 +193,11 @@ defineSuite([
expect(frustum2).toBe(result);
expect(frustum).toEqual(frustum2);
});
+
+ createPackableSpecs(OrthographicFrustum, new OrthographicFrustum({
+ width : 1.0,
+ aspectRatio : 2.0,
+ near : 3.0,
+ far : 4.0
+ }), [1.0, 2.0, 3.0, 4.0]);
});
diff --git a/Specs/Core/OrthographicOffCenterFrustumSpec.js b/Specs/Core/OrthographicOffCenterFrustumSpec.js
index 176024388cba..a81c88f087dc 100644
--- a/Specs/Core/OrthographicOffCenterFrustumSpec.js
+++ b/Specs/Core/OrthographicOffCenterFrustumSpec.js
@@ -27,6 +27,32 @@ defineSuite([
planes = frustum.computeCullingVolume(new Cartesian3(), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Y).planes;
});
+ it('constructs', function() {
+ var options = {
+ left : -1.0,
+ right : 2.0,
+ top : 5.0,
+ bottom : -1.0,
+ near : 3.0,
+ far : 4.0
+ };
+ var f = new OrthographicOffCenterFrustum(options);
+ expect(f.width).toEqual(options.width);
+ expect(f.aspectRatio).toEqual(options.aspectRatio);
+ expect(f.near).toEqual(options.near);
+ expect(f.far).toEqual(options.far);
+ });
+
+ it('default constructs', function() {
+ var f = new OrthographicOffCenterFrustum();
+ expect(f.left).toBeUndefined();
+ expect(f.right).toBeUndefined();
+ expect(f.top).toBeUndefined();
+ expect(f.bottom).toBeUndefined();
+ expect(f.near).toEqual(1.0);
+ expect(f.far).toEqual(500000000.0);
+ });
+
it('left greater than right causes an exception', function() {
frustum.left = frustum.right + 1.0;
expect(function() {
diff --git a/Specs/Core/PerspectiveFrustumSpec.js b/Specs/Core/PerspectiveFrustumSpec.js
index 4a3f37c80085..7682b09d6ffc 100644
--- a/Specs/Core/PerspectiveFrustumSpec.js
+++ b/Specs/Core/PerspectiveFrustumSpec.js
@@ -4,14 +4,16 @@ defineSuite([
'Core/Cartesian3',
'Core/Cartesian4',
'Core/Math',
- 'Core/Matrix4'
+ 'Core/Matrix4',
+ 'Specs/createPackableSpecs'
], function(
PerspectiveFrustum,
Cartesian2,
Cartesian3,
Cartesian4,
CesiumMath,
- Matrix4) {
+ Matrix4,
+ createPackableSpecs) {
'use strict';
var frustum, planes;
@@ -25,6 +27,34 @@ defineSuite([
planes = frustum.computeCullingVolume(new Cartesian3(), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Y).planes;
});
+ it('constructs', function() {
+ var options = {
+ fov : 1.0,
+ aspectRatio : 2.0,
+ near : 3.0,
+ far : 4.0,
+ xOffset : 5.0,
+ yOffset : 6.0
+ };
+ var f = new PerspectiveFrustum(options);
+ expect(f.fov).toEqual(options.fov);
+ expect(f.aspectRatio).toEqual(options.aspectRatio);
+ expect(f.near).toEqual(options.near);
+ expect(f.far).toEqual(options.far);
+ expect(f.xOffset).toEqual(options.xOffset);
+ expect(f.yOffset).toEqual(options.yOffset);
+ });
+
+ it('default constructs', function() {
+ var f = new PerspectiveFrustum();
+ expect(f.fov).toBeUndefined();
+ expect(f.aspectRatio).toBeUndefined();
+ expect(f.near).toEqual(1.0);
+ expect(f.far).toEqual(500000000.0);
+ expect(f.xOffset).toEqual(0.0);
+ expect(f.yOffset).toEqual(0.0);
+ });
+
it('out of range fov causes an exception', function() {
frustum.fov = -1.0;
expect(function() {
@@ -172,4 +202,13 @@ defineSuite([
expect(frustum2).toBe(result);
expect(frustum).toEqual(frustum2);
});
+
+ createPackableSpecs(PerspectiveFrustum, new PerspectiveFrustum({
+ fov : 1.0,
+ aspectRatio : 2.0,
+ near : 3.0,
+ far : 4.0,
+ xOffset : 5.0,
+ yOffset : 6.0
+ }), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
});
diff --git a/Specs/Core/PerspectiveOffCenterFrustumSpec.js b/Specs/Core/PerspectiveOffCenterFrustumSpec.js
index 01df32c6eec0..937f0d2a499f 100644
--- a/Specs/Core/PerspectiveOffCenterFrustumSpec.js
+++ b/Specs/Core/PerspectiveOffCenterFrustumSpec.js
@@ -27,6 +27,32 @@ defineSuite([
planes = frustum.computeCullingVolume(new Cartesian3(), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Y).planes;
});
+ it('constructs', function() {
+ var options = {
+ left : -1.0,
+ right : 2.0,
+ top : 5.0,
+ bottom : -1.0,
+ near : 3.0,
+ far : 4.0
+ };
+ var f = new PerspectiveOffCenterFrustum(options);
+ expect(f.width).toEqual(options.width);
+ expect(f.aspectRatio).toEqual(options.aspectRatio);
+ expect(f.near).toEqual(options.near);
+ expect(f.far).toEqual(options.far);
+ });
+
+ it('default constructs', function() {
+ var f = new PerspectiveOffCenterFrustum();
+ expect(f.left).toBeUndefined();
+ expect(f.right).toBeUndefined();
+ expect(f.top).toBeUndefined();
+ expect(f.bottom).toBeUndefined();
+ expect(f.near).toEqual(1.0);
+ expect(f.far).toEqual(500000000.0);
+ });
+
it('out of range near plane throws an exception', function() {
frustum.near = -1.0;
expect(function() {
diff --git a/Specs/Core/loadCRNSpec.js b/Specs/Core/loadCRNSpec.js
index ce187d90187f..b5d53cf2d066 100644
--- a/Specs/Core/loadCRNSpec.js
+++ b/Specs/Core/loadCRNSpec.js
@@ -13,6 +13,7 @@ defineSuite([
'use strict';
var validCompressed = new Uint8Array([72, 120, 0, 74, 227, 123, 0, 0, 0, 138, 92, 167, 0, 4, 0, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 22, 0, 1, 0, 0, 96, 0, 0, 12, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 108, 0, 0, 0, 137, 0, 10, 96, 0, 0, 0, 0, 0, 0, 16, 4, 9, 130, 0, 0, 0, 0, 0, 0, 109, 4, 0, 0, 198, 96, 128, 0, 0, 0, 0, 0, 26, 80, 0, 0, 6, 96, 0, 0, 0, 0, 0, 0, 16, 0, 51, 0, 0, 0, 0, 0, 0, 0, 128, 1, 152, 0, 0, 0, 0, 0, 0, 4, 0]);
+ var validCompressedMipmap = new Uint8Array([72, 120, 0, 82, 183, 141, 0, 0, 0, 148, 151, 24, 0, 4, 0, 4, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 22, 0, 1, 0, 0, 104, 0, 0, 12, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 116, 0, 0, 0, 145, 0, 0, 0, 146, 0, 0, 0, 147, 0, 130, 97, 0, 0, 0, 0, 0, 4, 35, 37, 0, 3, 48, 0, 0, 0, 0, 0, 0, 8, 200, 0, 198, 96, 128, 0, 0, 0, 0, 0, 26, 80, 0, 0, 6, 96, 0, 0, 0, 0, 0, 0, 16, 0, 51, 0, 0, 0, 0, 0, 0, 0, 128, 1, 152, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]);
var fakeXHR;
beforeEach(function() {
@@ -119,7 +120,7 @@ defineSuite([
expect(rejectedError.response).toEqual(error);
});
- it('returns a promise that resolves to an compressed texture when the request loads', function() {
+ it('returns a promise that resolves to a compressed texture when the request loads', function() {
var testUrl = 'http://example.invalid/testuri';
var promise = loadCRN(testUrl);
@@ -149,6 +150,36 @@ defineSuite([
});
});
+ it('returns a promise that resolves to a compressed texture containing the first mip level of the original texture', function() {
+ var testUrl = 'http://example.invalid/testuri';
+ var promise = loadCRN(testUrl);
+
+ expect(promise).toBeDefined();
+
+ var resolvedValue;
+ var rejectedError;
+ var newPromise = promise.then(function(value) {
+ resolvedValue = value;
+ }, function(error) {
+ rejectedError = error;
+ });
+
+ expect(resolvedValue).toBeUndefined();
+ expect(rejectedError).toBeUndefined();
+
+ var response = validCompressedMipmap.buffer;
+ fakeXHR.simulateLoad(response);
+
+ return newPromise.then(function() {
+ expect(resolvedValue).toBeDefined();
+ expect(resolvedValue.width).toEqual(4);
+ expect(resolvedValue.height).toEqual(4);
+ expect(PixelFormat.isCompressedFormat(resolvedValue.internalFormat)).toEqual(true);
+ expect(resolvedValue.bufferView).toBeDefined();
+ expect(rejectedError).toBeUndefined();
+ });
+ });
+
it('returns undefined if the request is throttled', function() {
var oldMaximumRequests = RequestScheduler.maximumRequests;
RequestScheduler.maximumRequests = 0;
diff --git a/Specs/Core/loadKTXSpec.js b/Specs/Core/loadKTXSpec.js
index 745c22c1a488..0f2ebcd3fe30 100644
--- a/Specs/Core/loadKTXSpec.js
+++ b/Specs/Core/loadKTXSpec.js
@@ -15,7 +15,9 @@ defineSuite([
'use strict';
var validCompressed = new Uint8Array([171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10, 1, 2, 3, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 241, 131, 0, 0, 8, 25, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 224, 7, 224, 7, 0, 0, 0, 0]);
+ var validCompressedMipmap = new Uint8Array([171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10, 1, 2, 3, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 100, 141, 0, 0, 7, 25, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 52, 0, 0, 0, 19, 0, 0, 0, 67, 82, 78, 76, 73, 66, 95, 70, 79, 85, 82, 67, 67, 0, 69, 84, 67, 49, 0, 0, 23, 0, 0, 0, 75, 84, 88, 111, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 0, 83, 61, 114, 44, 84, 61, 100, 0, 0, 8, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 0, 8, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 0, 8, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 0]);
var validUncompressed = new Uint8Array([171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10, 1, 2, 3, 4, 1, 20, 0, 0, 1, 0, 0, 0, 8, 25, 0, 0, 88, 128, 0, 0, 8, 25, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 32, 0, 0, 0, 27, 0, 0, 0, 75, 84, 88, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 0, 83, 61, 114, 44, 84, 61, 100, 44, 82, 61, 105, 0, 0, 64, 0, 0, 0, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255]);
+ var validUncompressedMipmap = new Uint8Array([171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10, 1, 2, 3, 4, 1, 20, 0, 0, 1, 0, 0, 0, 7, 25, 0, 0, 81, 128, 0, 0, 7, 25, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 52, 0, 0, 0, 19, 0, 0, 0, 67, 82, 78, 76, 73, 66, 95, 70, 79, 85, 82, 67, 67, 0, 82, 71, 66, 120, 0, 0, 23, 0, 0, 0, 75, 84, 88, 111, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 0, 83, 61, 114, 44, 84, 61, 100, 0, 0, 48, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 12, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 3, 0, 0, 0, 255, 0, 0, 0]);
var fakeXHR;
@@ -150,7 +152,34 @@ defineSuite([
expect(rejectedError).toBeUndefined();
});
- it('returns a promise that resolves to an compressed texture when the request loads', function() {
+ it('returns a promise that resolves to an uncompressed texture containing the first mip level of the original texture', function() {
+ var testUrl = 'http://example.invalid/testuri';
+ var promise = loadKTX(testUrl);
+
+ expect(promise).toBeDefined();
+
+ var resolvedValue;
+ var rejectedError;
+ promise.then(function(value) {
+ resolvedValue = value;
+ }, function(error) {
+ rejectedError = error;
+ });
+
+ expect(resolvedValue).toBeUndefined();
+ expect(rejectedError).toBeUndefined();
+
+ var response = validUncompressedMipmap.buffer;
+ fakeXHR.simulateLoad(response);
+ expect(resolvedValue).toBeDefined();
+ expect(resolvedValue.width).toEqual(4);
+ expect(resolvedValue.height).toEqual(4);
+ expect(PixelFormat.isCompressedFormat(resolvedValue.internalFormat)).toEqual(false);
+ expect(resolvedValue.bufferView).toBeDefined();
+ expect(rejectedError).toBeUndefined();
+ });
+
+ it('returns a promise that resolves to a compressed texture when the request loads', function() {
var testUrl = 'http://example.invalid/testuri';
var promise = loadKTX(testUrl);
@@ -177,6 +206,34 @@ defineSuite([
expect(rejectedError).toBeUndefined();
});
+ it('returns a promise that resolves to a compressed texture containing the first mip level of the original texture', function() {
+ var testUrl = 'http://example.invalid/testuri';
+ var promise = loadKTX(testUrl);
+
+ expect(promise).toBeDefined();
+
+ var resolvedValue;
+ var rejectedError;
+ promise.then(function(value) {
+ resolvedValue = value;
+ }, function(error) {
+ rejectedError = error;
+ });
+
+ expect(resolvedValue).toBeUndefined();
+ expect(rejectedError).toBeUndefined();
+
+ var response = validCompressedMipmap.buffer;
+ fakeXHR.simulateLoad(response);
+ expect(resolvedValue).toBeDefined();
+ expect(resolvedValue.width).toEqual(4);
+ expect(resolvedValue.height).toEqual(4);
+ expect(PixelFormat.isCompressedFormat(resolvedValue.internalFormat)).toEqual(true);
+ expect(resolvedValue.bufferView).toBeDefined();
+ expect(rejectedError).toBeUndefined();
+ });
+
+
it('cannot parse invalid KTX buffer', function() {
var invalidKTX = new Uint8Array(validCompressed);
invalidKTX[0] = 0;
@@ -276,26 +333,6 @@ defineSuite([
expect(rejectedError.message).toEqual('The type size for compressed textures must be 1.');
});
- it('cannot parse KTX buffer with compressed texture and needs to generate mipmaps', function() {
- var reinterprestBuffer = new Uint32Array(validCompressed.buffer);
- var invalidKTX = new Uint32Array(reinterprestBuffer);
- invalidKTX[14] = 0;
-
- var promise = loadKTX(invalidKTX.buffer);
-
- var resolvedValue;
- var rejectedError;
- promise.then(function(value) {
- resolvedValue = value;
- }, function(error) {
- rejectedError = error;
- });
-
- expect(resolvedValue).toBeUndefined();
- expect(rejectedError instanceof RuntimeError).toEqual(true);
- expect(rejectedError.message).toEqual('Generating mipmaps for a compressed texture is unsupported.');
- });
-
it('cannot parse KTX buffer with uncompressed texture and base format is not the same as format', function() {
var reinterprestBuffer = new Uint32Array(validUncompressed.buffer);
var invalidKTX = new Uint32Array(reinterprestBuffer);
diff --git a/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm
index d5636e9602d7..b21138f99ab9 100644
Binary files a/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm and b/Specs/Data/Cesium3DTiles/Batched/BatchedTextured/batchedTextured.b3dm differ
diff --git a/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/box.glb b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/box.glb
new file mode 100644
index 000000000000..2bde3c4ee98e
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/box.glb differ
diff --git a/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/compositeOfInstanced.cmpt b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/compositeOfInstanced.cmpt
new file mode 100644
index 000000000000..99f84f85f092
Binary files /dev/null and b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/compositeOfInstanced.cmpt differ
diff --git a/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/tileset.json b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/tileset.json
new file mode 100644
index 000000000000..9609546a000c
--- /dev/null
+++ b/Specs/Data/Cesium3DTiles/Composite/CompositeOfInstanced/tileset.json
@@ -0,0 +1,29 @@
+{
+ "asset": {
+ "version": "0.0"
+ },
+ "properties": {
+ "Height": {
+ "minimum": 20,
+ "maximum": 20
+ }
+ },
+ "geometricError": 70,
+ "root": {
+ "refine": "ADD",
+ "boundingVolume": {
+ "region": [
+ -1.3197004795898053,
+ 0.6988582109,
+ -1.3196595204101946,
+ 0.6988897891,
+ 0,
+ 30
+ ]
+ },
+ "geometricError": 0,
+ "content": {
+ "url": "compositeOfInstanced.cmpt"
+ }
+ }
+}
diff --git a/Specs/DataSources/EntitySpec.js b/Specs/DataSources/EntitySpec.js
index 85b1444e2915..5a4a4eb4738a 100644
--- a/Specs/DataSources/EntitySpec.js
+++ b/Specs/DataSources/EntitySpec.js
@@ -241,13 +241,20 @@ defineSuite([
}).toThrowDeveloperError();
});
- it('_getModelMatrix returns undefined when position is undefined.', function() {
+ it('computeModelMatrix throws if no time specified.', function() {
+ var entity = new Entity();
+ expect(function() {
+ entity.computeModelMatrix();
+ }).toThrowDeveloperError();
+ });
+
+ it('computeModelMatrix returns undefined when position is undefined.', function() {
var entity = new Entity();
entity.orientation = new ConstantProperty(Quaternion.IDENTITY);
- expect(entity._getModelMatrix(new JulianDate())).toBeUndefined();
+ expect(entity.computeModelMatrix(new JulianDate())).toBeUndefined();
});
- it('_getModelMatrix returns correct value.', function() {
+ it('computeModelMatrix returns correct value.', function() {
var entity = new Entity();
var position = new Cartesian3(123456, 654321, 123456);
@@ -257,28 +264,28 @@ defineSuite([
entity.position = new ConstantProperty(position);
entity.orientation = new ConstantProperty(orientation);
- var modelMatrix = entity._getModelMatrix(new JulianDate());
+ var modelMatrix = entity.computeModelMatrix(new JulianDate());
var expected = Matrix4.fromRotationTranslation(Matrix3.fromQuaternion(orientation), position);
expect(modelMatrix).toEqual(expected);
});
- it('_getModelMatrix returns ENU when quaternion is undefined.', function() {
+ it('computeModelMatrix returns ENU when quaternion is undefined.', function() {
var entity = new Entity();
var position = new Cartesian3(123456, 654321, 123456);
entity.position = new ConstantProperty(position);
- var modelMatrix = entity._getModelMatrix(new JulianDate());
+ var modelMatrix = entity.computeModelMatrix(new JulianDate());
var expected = Transforms.eastNorthUpToFixedFrame(position);
expect(modelMatrix).toEqual(expected);
});
- it('_getModelMatrix works with result parameter.', function() {
+ it('computeModelMatrix works with result parameter.', function() {
var entity = new Entity();
var position = new Cartesian3(123456, 654321, 123456);
entity.position = new ConstantProperty(position);
var result = new Matrix4();
- var modelMatrix = entity._getModelMatrix(new JulianDate(), result);
+ var modelMatrix = entity.computeModelMatrix(new JulianDate(), result);
var expected = Transforms.eastNorthUpToFixedFrame(position);
expect(modelMatrix).toBe(result);
expect(modelMatrix).toEqual(expected);
diff --git a/Specs/DataSources/GeoJsonDataSourceSpec.js b/Specs/DataSources/GeoJsonDataSourceSpec.js
index a19097e85205..18fd511998bd 100644
--- a/Specs/DataSources/GeoJsonDataSourceSpec.js
+++ b/Specs/DataSources/GeoJsonDataSourceSpec.js
@@ -279,6 +279,19 @@ defineSuite([
expect(dataSource.show).toBe(true);
});
+ it('setting name raises changed event', function() {
+ var dataSource = new GeoJsonDataSource();
+
+ var spy = jasmine.createSpy('changedEvent');
+ dataSource.changedEvent.addEventListener(spy);
+
+ var newName = 'chester';
+ dataSource.name = newName;
+ expect(dataSource.name).toEqual(newName);
+ expect(spy.calls.count()).toEqual(1);
+ expect(spy).toHaveBeenCalledWith(dataSource);
+ });
+
it('show sets underlying entity collection show.', function() {
var dataSource = new GeoJsonDataSource();
diff --git a/Specs/DataSources/KmlDataSourceSpec.js b/Specs/DataSources/KmlDataSourceSpec.js
index 17198cd83648..4d1a3636d5d9 100644
--- a/Specs/DataSources/KmlDataSourceSpec.js
+++ b/Specs/DataSources/KmlDataSourceSpec.js
@@ -150,6 +150,19 @@ defineSuite([
expect(dataSource.show).toBe(true);
});
+ it('setting name raises changed event', function() {
+ var dataSource = new KmlDataSource(options);
+
+ var spy = jasmine.createSpy('changedEvent');
+ dataSource.changedEvent.addEventListener(spy);
+
+ var newName = 'garfield';
+ dataSource.name = newName;
+ expect(dataSource.name).toEqual(newName);
+ expect(spy.calls.count()).toEqual(1);
+ expect(spy).toHaveBeenCalledWith(dataSource);
+ });
+
it('show sets underlying entity collection show.', function() {
var dataSource = new KmlDataSource(options);
diff --git a/Specs/Renderer/ShaderProgramSpec.js b/Specs/Renderer/ShaderProgramSpec.js
index e2934ed94b66..0fe33e0acc0b 100644
--- a/Specs/Renderer/ShaderProgramSpec.js
+++ b/Specs/Renderer/ShaderProgramSpec.js
@@ -65,13 +65,13 @@ defineSuite([
var expectedVSText = new ShaderSource({
sources : [vs]
- }).createCombinedVertexShader();
+ }).createCombinedVertexShader(context);
expect(sp._vertexShaderText).toEqual(expectedVSText);
var expectedFSText = new ShaderSource({
sources : [fs]
- }).createCombinedFragmentShader();
+ }).createCombinedFragmentShader(context);
expect(sp._fragmentShaderText).toEqual(expectedFSText);
});
diff --git a/Specs/Renderer/ShaderSourceSpec.js b/Specs/Renderer/ShaderSourceSpec.js
index b9105e4b28ed..80b4f4fbe9c4 100644
--- a/Specs/Renderer/ShaderSourceSpec.js
+++ b/Specs/Renderer/ShaderSourceSpec.js
@@ -4,12 +4,16 @@ defineSuite([
ShaderSource) {
'use strict';
+ var mockContext = {
+ webgl2 : false
+ };
+
it('combines #defines', function() {
var source = new ShaderSource({
defines : ['A', 'B', '']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#define A');
expect(shaderText).toContain('#define B');
expect(shaderText.match(/#define/g).length).toEqual(2);
@@ -19,7 +23,7 @@ defineSuite([
var source = new ShaderSource({
sources : ['void func() {}', 'void main() {}']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#line 0\nvoid func() {}');
expect(shaderText).toContain('#line 0\nvoid main() {}');
});
@@ -29,7 +33,7 @@ defineSuite([
defines : ['A', 'B', ''],
sources : ['void func() {}', 'void main() {}']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#define A');
expect(shaderText).toContain('#define B');
expect(shaderText.match(/#define/g).length).toEqual(2);
@@ -42,7 +46,7 @@ defineSuite([
sources : ['void main() { gl_FragColor = vec4(1.0); }'],
pickColorQualifier : 'uniform'
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('uniform vec4 czm_pickColor;');
expect(shaderText).toContain('gl_FragColor = czm_pickColor;');
});
@@ -52,7 +56,7 @@ defineSuite([
sources : ['void main() { gl_FragColor = vec4(1.0); }'],
pickColorQualifier : 'varying'
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('varying vec4 czm_pickColor;');
expect(shaderText).toContain('gl_FragColor = czm_pickColor;');
});
@@ -69,7 +73,7 @@ defineSuite([
var source = new ShaderSource({
sources : ['#version 300 es\nvoid main() {gl_FragColor = vec4(1.0); }']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toStartWith('#version 300 es\n');
});
});
diff --git a/Specs/Renderer/modernizeShaderSpec.js b/Specs/Renderer/modernizeShaderSpec.js
new file mode 100644
index 000000000000..b915fd6461e7
--- /dev/null
+++ b/Specs/Renderer/modernizeShaderSpec.js
@@ -0,0 +1,317 @@
+defineSuite([
+ 'Renderer/modernizeShader'
+ ], function(
+ modernizeShader) {
+ 'use strict';
+
+ it('adds version string', function() {
+ var simple =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var output = modernizeShader(simple, true);
+ var expected = '#version 300 es';
+ expect(output.split('\n')[0]).toEqual(expected);
+ });
+
+ it('removes extensions', function() {
+ var extensions =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var output = modernizeShader(extensions, true);
+ var notExpected = '#extension GL_EXT_draw_buffers : enable \n';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('throws exception if no output declaration', function() {
+ var noOutputDeclaration =
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var runFunc = function() {
+ modernizeShader(noOutputDeclaration, true);
+ };
+ expect(runFunc).toThrowDeveloperError();
+ });
+
+ it('creates layout qualifier for gl_FragColor', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragColor = vec4(0.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = 'layout(location = 0) out vec4 czm_fragColor;';
+ expect(output).toContain(expected);
+ });
+
+ it('creates layout qualifier for gl_FragData', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = 'layout(location = 0) out vec4 czm_out0;';
+ expect(output).toContain(expected);
+ });
+
+ it('creates layout qualifier for MRT', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = 'layout(location = 0) out vec4 czm_out0;';
+ var expected1 = 'layout(location = 1) out vec4 czm_out1;';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('does not create layout qualifier for reserved word lookalike variables', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'uniform sampler2D example; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' vec4 gl_FragData_ = vec4(0.0); \n' +
+ ' vec4 a_gl_FragData = vec4(0.0); \n' +
+ ' vec2 thisIsNotAGoodNameForAtexture2D = vec2(0.0); \n' +
+ ' vec4 gl_FragColor = texture2D(example, thisIsNotAGoodNameForAtexture2D); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expectedBadName = 'vec2 thisIsNotAGoodNameForAtexture2D';
+ var expectedVariable = 'vec4 a_gl_FragData = vec4(0.0);';
+ var expectedTextureCall = 'texture(example, thisIsNotAGoodNameForAtexture2D)';
+ var notExpectedLayout = 'layout(location = 0) out czm_out';
+ expect(output).toContain(expectedBadName);
+ expect(output).toContain(expectedVariable);
+ expect(output).toContain(expectedTextureCall);
+ expect(output).not.toContain(notExpectedLayout);
+ });
+
+ it('creates layout qualifier with swizzle', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1].xyz = vec3(1.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = 'layout(location = 0) out vec4 czm_out0;';
+ var expected1 = 'layout(location = 1) out vec4 czm_out1;';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('removes old functions/variables from fragment shader', function() {
+ var old_fragment =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'uniform sampler2D example; \n' +
+ 'uniform sampler2D exampleCube; \n' +
+ 'uniform sampler2D example3D; \n' +
+ 'varying vec2 v_textureCoordinates; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = texture2D(example, v_textureCoordinates); \n' +
+ ' gl_FragData[1] = textureCube(exampleCube, v_textureCoordinates); \n' +
+ ' gl_FragData[2] = texture3D(example3D, v_textureCoordinates); \n' +
+ ' gl_FragDepthEXT = 0.0; \n' +
+ '} \n';
+
+ var output = modernizeShader(old_fragment, true);
+
+ var expectedDepth = 'gl_FragDepth = 0.0;';
+ var expectedTexture2D = 'texture(example, v_textureCoordinates);';
+ var expectedTextureCube = 'texture(exampleCube, v_textureCoordinates);';
+ var expectedTexture3D = 'texture(example3D, v_textureCoordinates);';
+ var expectedIn = 'in vec2 v_textureCoordinates;';
+
+ var notExpectedDepth = 'gl_FragDepthEXT = 0.0;';
+ var notExpectedTexture2D = 'texture2D(example, v_textureCoordinates);';
+ var notExpectedTextureCube = 'textureCube(exampleCube, v_textureCoordinates);';
+ var notExpectedTexture3D = 'texture3D(example3D, v_textureCoordinates);';
+ var notExpectedVarying = 'varying vec2 v_textureCoordinates;';
+
+ expect(output).toContain(expectedDepth);
+ expect(output).toContain(expectedTexture2D);
+ expect(output).toContain(expectedTextureCube);
+ expect(output).toContain(expectedTexture3D);
+ expect(output).toContain(expectedIn);
+
+ expect(output).not.toContain(notExpectedDepth);
+ expect(output).not.toContain(notExpectedTexture2D);
+ expect(output).not.toContain(notExpectedTextureCube);
+ expect(output).not.toContain(notExpectedTexture3D);
+ expect(output).not.toContain(notExpectedVarying);
+ });
+
+ it('removes old functions/variables from vertex shader', function() {
+ var old_vertex =
+ '#define OUTPUT_DECLARATION \n' +
+ 'attribute vec4 position; \n' +
+ 'varying vec4 varyingVariable; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_Position = position; \n' +
+ ' varyingVariable = position.wzyx; \n' +
+ '} \n';
+
+ var output = modernizeShader(old_vertex, false);
+
+ var expectedOut = 'out vec4 varyingVariable;';
+ var expectedIn = 'in vec4 position;';
+
+ var notExpectedAttribute = 'attribute vec4 varyingVariable;';
+ var notExpectedVarying = 'varying vec2 varyingVariable;';
+
+ expect(output).toContain(expectedOut);
+ expect(output).toContain(expectedIn);
+
+ expect(output).not.toContain(notExpectedAttribute);
+ expect(output).not.toContain(notExpectedVarying);
+ });
+
+ it('creates single layout qualifier under single branch, single condition', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).toContain(expected);
+ });
+
+ it('creates multiple layout qualifiers under single branch, single condition', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 1) out vec4 czm_out1;\n#endif';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('creates multiple layout qualifiers under multiple branches, single condition (cancels)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ ' #ifndef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //!EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates single layout qualifier under multiple branches, multiple conditions (cancels)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ '#define EXAMPLE_BRANCH1 \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ ' #ifdef EXAMPLE_BRANCH1 \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH1 \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates multiple layout qualifiers under multiple branches, multiple conditions (cascades)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ '#define EXAMPLE_BRANCH1 \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #ifdef EXAMPLE_BRANCH1 \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH1 \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = /#ifdef (EXAMPLE_BRANCH|EXAMPLE_BRANCH1)\s*\n\s*#ifdef (EXAMPLE_BRANCH1|EXAMPLE_BRANCH)\s*\n\s*layout\(location = 1\) out vec4 czm_out1;/g;
+ var containsExpected0 = expected1.test(output);
+ expect(output).toContain(expected0);
+ expect(containsExpected0).toBe(true);
+ });
+
+ it('creates single layout qualifier under multiple branches, single condition (else)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #else \n' +
+ ' gl_FragData[0] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates branched layout qualifiers for gl_FragColor and gl_FragData', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #else \n' +
+ ' gl_FragColor = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = '#ifndef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_fragColor;\n#endif';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+});
diff --git a/Specs/Scene/Cesium3DTileStyleSpec.js b/Specs/Scene/Cesium3DTileStyleSpec.js
index e1dccae26621..43fab6e103b1 100644
--- a/Specs/Scene/Cesium3DTileStyleSpec.js
+++ b/Specs/Scene/Cesium3DTileStyleSpec.js
@@ -82,28 +82,28 @@ defineSuite([
});
});
- it('sets show value to default expression', function() {
+ it('sets show value to undefined if value not present', function() {
var style = new Cesium3DTileStyle({});
- expect(style.show).toEqual(new Expression('true'));
+ expect(style.show).toBeUndefined();
style = new Cesium3DTileStyle();
- expect(style.show).toEqual(new Expression('true'));
+ expect(style.show).toBeUndefined();
});
- it('sets color value to default expression', function() {
+ it('sets color value to undefined if value not present', function() {
var style = new Cesium3DTileStyle({});
- expect(style.color).toEqual(new Expression('color("#ffffff")'));
+ expect(style.color).toBeUndefined();
style = new Cesium3DTileStyle();
- expect(style.color).toEqual(new Expression('color("#ffffff")'));
+ expect(style.color).toBeUndefined();
});
- it('sets pointSize value to default expression', function() {
+ it('sets pointSize value to undefined if value not present', function() {
var style = new Cesium3DTileStyle({});
- expect(style.pointSize).toEqual(new Expression('1'));
+ expect(style.pointSize).toBeUndefined();
style = new Cesium3DTileStyle();
- expect(style.pointSize).toEqual(new Expression('1'));
+ expect(style.pointSize).toBeUndefined();
});
it('sets show value to expression', function() {
@@ -147,11 +147,48 @@ defineSuite([
expect(style.show).toEqual(new ConditionsExpression(jsonExp));
});
- it('sets show to undefined if not a string, boolean, or conditional', function() {
- var style = new Cesium3DTileStyle({
- show : 1
+ it('sets show expressions in setter', function() {
+ var style = new Cesium3DTileStyle();
+
+ var condExp = new ConditionsExpression({
+ conditions : [
+ ['${height} > 2', 'false'],
+ ['true', 'true']
+ ]
});
- expect(style.show).toEqual(undefined);
+
+ style.show = condExp;
+ expect(style.show).toEqual(condExp);
+
+ var exp = new Expression('false');
+ style.show = exp;
+ expect(style.show).toEqual(exp);
+ });
+
+ it('sets show values in setter', function() {
+ var defines = {
+ 'showFactor': 10
+ };
+ var style = new Cesium3DTileStyle({ 'defines': defines });
+
+ style.show = '${height} * ${showFactor} >= 1000';
+ expect(style.show).toEqual(new Expression('${height} * ${showFactor} >= 1000', defines));
+
+ style.show = false;
+ expect(style.show).toEqual(new Expression('false'));
+
+ var jsonExp = {
+ conditions : [
+ ['${height} > ${showFactor}', 'false'],
+ ['true', 'true']
+ ]
+ };
+
+ style.show = jsonExp;
+ expect(style.show).toEqual(new ConditionsExpression(jsonExp, defines));
+
+ style.show = undefined;
+ expect(style.show).toBeUndefined();
});
it('sets color value to expression', function() {
@@ -185,11 +222,45 @@ defineSuite([
expect(style.color).toEqual(new ConditionsExpression(jsonExp));
});
- it('sets color to undefined if not a string or conditional', function() {
- var style = new Cesium3DTileStyle({
- color : 1
+ it('sets color expressions in setter', function() {
+ var style = new Cesium3DTileStyle();
+
+ var exp = new Expression('color("red")');
+ style.color = exp;
+ expect(style.color).toEqual(exp);
+
+ var condExp = new ConditionsExpression({
+ conditions : [
+ ['${height} > 2', 'color("cyan")'],
+ ['true', 'color("blue")']
+ ]
});
- expect(style.color).toEqual(undefined);
+
+ style.color = condExp;
+ expect(style.color).toEqual(condExp);
+
+ style.color = undefined;
+ expect(style.color).toBeUndefined();
+ });
+
+ it('sets color values in setter', function() {
+ var defines = {
+ 'targetColor': 'red'
+ };
+ var style = new Cesium3DTileStyle({ 'defines': defines });
+
+ style.color = 'color("${targetColor}")';
+ expect(style.color).toEqual(new Expression('color("${targetColor}")', defines));
+
+ var jsonExp = {
+ conditions : [
+ ['${height} > 2', 'color("cyan")'],
+ ['true', 'color("${targetColor}")']
+ ]
+ };
+
+ style.color = jsonExp;
+ expect(style.color).toEqual(new ConditionsExpression(jsonExp, defines));
});
it('sets pointSize value to expression', function() {
@@ -223,11 +294,51 @@ defineSuite([
expect(style.pointSize).toEqual(new ConditionsExpression(jsonExp));
});
- it('sets pointSize to undefined if not a number, string, or conditional', function() {
- var style = new Cesium3DTileStyle({
- pointSize : true
+ it('sets pointSize expressions in setter', function() {
+ var style = new Cesium3DTileStyle();
+
+ style.pointSize = 2;
+ expect(style.pointSize).toEqual(new Expression('2'));
+
+ var exp = new Expression('2');
+ style.pointSize = exp;
+ expect(style.pointSize).toEqual(exp);
+
+ var condExp = new ConditionsExpression({
+ conditions : [
+ ['${height} > 2', '1.0'],
+ ['true', '2.0']
+ ]
});
- expect(style.pointSize).toEqual(undefined);
+
+ style.pointSize = condExp;
+ expect(style.pointSize).toEqual(condExp);
+
+ style.pointSize = undefined;
+ expect(style.pointSize).toBeUndefined();
+ });
+
+ it('sets pointSize values in setter', function() {
+ var defines = {
+ 'targetPointSize': '2.0'
+ };
+ var style = new Cesium3DTileStyle({ 'defines': defines });
+
+ style.pointSize = 2;
+ expect(style.pointSize).toEqual(new Expression('2'));
+
+ style.pointSize = '${targetPointSize} + 1.0';
+ expect(style.pointSize).toEqual(new Expression('${targetPointSize} + 1.0', defines));
+
+ var jsonExp = {
+ conditions : [
+ ['${height} > 2', '1.0'],
+ ['true', '${targetPointSize}']
+ ]
+ };
+
+ style.pointSize = jsonExp;
+ expect(style.pointSize).toEqual(new ConditionsExpression(jsonExp, defines));
});
it('throws on accessing style if not ready', function() {
@@ -334,7 +445,6 @@ defineSuite([
expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.show.evaluate(frameState, feature2)).toEqual(false);
- expect(style.color.evaluateColor(frameState, undefined)).toEqual(Color.WHITE);
});
it('applies show style with regexp and variables', function() {
@@ -344,7 +454,6 @@ defineSuite([
expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.show.evaluate(frameState, feature2)).toEqual(false);
- expect(style.color.evaluateColor(frameState, undefined)).toEqual(Color.WHITE);
});
it('applies show style with conditional', function() {
@@ -368,7 +477,6 @@ defineSuite([
var style = new Cesium3DTileStyle({
"color" : "(${Temperature} > 90) ? color('red') : color('white')"
});
- expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.WHITE);
expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.RED);
});
@@ -377,7 +485,6 @@ defineSuite([
var style = new Cesium3DTileStyle({
"color" : "rgba(${red}, ${green}, ${blue}, (${volume} > 100 ? 0.5 : 1.0))"
});
- expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.color.evaluateColor(frameState, feature1)).toEqual(new Color(38/255, 255/255, 82/255, 0.5));
expect(style.color.evaluateColor(frameState, feature2)).toEqual(new Color(255/255, 30/255, 30/255, 1.0));
});
@@ -395,7 +502,6 @@ defineSuite([
]
}
});
- expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.RED);
expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.LIME);
});
@@ -413,7 +519,6 @@ defineSuite([
]
}
});
- expect(style.show.evaluate(frameState, feature1)).toEqual(true);
expect(style.color.evaluateColor(frameState, feature1)).toEqual(Color.BLUE);
expect(style.color.evaluateColor(frameState, feature2)).toEqual(Color.YELLOW);
});
diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js
index 307a07e4c7dd..d791f597de56 100644
--- a/Specs/Scene/Cesium3DTilesetSpec.js
+++ b/Specs/Scene/Cesium3DTilesetSpec.js
@@ -1880,11 +1880,15 @@ defineSuite([
return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) {
// Check that the feature is red
var sourceRed;
- expect(scene).toRenderAndCall(function(rgba) {
+ var renderOptions = {
+ scene : scene,
+ time : new JulianDate(2457522.154792)
+ };
+ expect(renderOptions).toRenderAndCall(function(rgba) {
sourceRed = rgba[0];
});
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[1]).toEqual(0);
expect(rgba[2]).toEqual(0);
@@ -1898,7 +1902,7 @@ defineSuite([
tileset.style = new Cesium3DTileStyle({
color : 'rgb(128, 128, 0)'
});
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[0]).toBeLessThan(sourceRed);
expect(rgba[1]).toEqual(0);
@@ -1910,7 +1914,7 @@ defineSuite([
tileset.style = new Cesium3DTileStyle({
color : 'rgba(255, 255, 0, 0.5)'
});
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[0]).toBeLessThan(sourceRed);
expect(rgba[1]).toEqual(0);
@@ -1927,7 +1931,7 @@ defineSuite([
});
var replaceRed;
var replaceGreen;
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
replaceRed = rgba[0];
replaceGreen = rgba[1];
expect(rgba[0]).toBeGreaterThan(0);
@@ -1942,7 +1946,7 @@ defineSuite([
tileset.style = new Cesium3DTileStyle({
color : 'rgba(255, 255, 0, 0.5)'
});
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[0]).toBeLessThan(255);
expect(rgba[1]).toBeGreaterThan(0);
@@ -1961,7 +1965,7 @@ defineSuite([
});
var mixRed;
var mixGreen;
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
mixRed = rgba[0];
mixGreen = rgba[1];
expect(rgba[0]).toBeGreaterThan(replaceRed);
@@ -1974,7 +1978,7 @@ defineSuite([
// Set colorBlendAmount to 0.25. Expect color to be closer to the source color.
tileset.colorBlendAmount = 0.25;
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(mixRed);
expect(rgba[0]).toBeLessThan(sourceRed);
expect(rgba[1]).toBeGreaterThan(0);
@@ -1985,7 +1989,7 @@ defineSuite([
// Set colorBlendAmount to 0.0. Expect color to equal the source color
tileset.colorBlendAmount = 0.0;
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toEqual(sourceRed);
expect(rgba[1]).toEqual(0);
expect(rgba[2]).toEqual(0);
@@ -1994,7 +1998,7 @@ defineSuite([
// Set colorBlendAmount to 1.0. Expect color to equal the style color
tileset.colorBlendAmount = 1.0;
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toEqual(replaceRed);
expect(rgba[1]).toEqual(replaceGreen);
expect(rgba[2]).toEqual(0);
@@ -2006,7 +2010,7 @@ defineSuite([
tileset.style = new Cesium3DTileStyle({
color : 'rgba(255, 255, 0, 0.5)'
});
- expect(scene).toRenderAndCall(function(rgba) {
+ expect(renderOptions).toRenderAndCall(function(rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[1]).toBeGreaterThan(0);
expect(rgba[2]).toEqual(0);
diff --git a/Specs/Scene/ClassificationPrimitiveSpec.js b/Specs/Scene/ClassificationPrimitiveSpec.js
new file mode 100644
index 000000000000..0bbbba8058ad
--- /dev/null
+++ b/Specs/Scene/ClassificationPrimitiveSpec.js
@@ -0,0 +1,909 @@
+defineSuite([
+ 'Scene/ClassificationPrimitive',
+ 'Core/BoxGeometry',
+ 'Core/Cartesian3',
+ 'Core/Color',
+ 'Core/ColorGeometryInstanceAttribute',
+ 'Core/destroyObject',
+ 'Core/DistanceDisplayConditionGeometryInstanceAttribute',
+ 'Core/Ellipsoid',
+ 'Core/GeometryInstance',
+ 'Core/HeadingPitchRange',
+ 'Core/Math',
+ 'Core/Matrix4',
+ 'Core/PolygonGeometry',
+ 'Core/Rectangle',
+ 'Core/RectangleGeometry',
+ 'Core/ShowGeometryInstanceAttribute',
+ 'Core/Transforms',
+ 'Renderer/Pass',
+ 'Scene/PerInstanceColorAppearance',
+ 'Scene/Primitive',
+ 'Specs/createScene',
+ 'Specs/pollToPromise'
+ ], function(
+ ClassificationPrimitive,
+ BoxGeometry,
+ Cartesian3,
+ Color,
+ ColorGeometryInstanceAttribute,
+ destroyObject,
+ DistanceDisplayConditionGeometryInstanceAttribute,
+ Ellipsoid,
+ GeometryInstance,
+ HeadingPitchRange,
+ CesiumMath,
+ Matrix4,
+ PolygonGeometry,
+ Rectangle,
+ RectangleGeometry,
+ ShowGeometryInstanceAttribute,
+ Transforms,
+ Pass,
+ PerInstanceColorAppearance,
+ Primitive,
+ createScene,
+ pollToPromise) {
+ 'use strict';
+
+ var scene;
+
+ var ellipsoid;
+ var rectangle;
+
+ var depthColor;
+ var boxColor;
+
+ var boxInstance;
+ var primitive;
+ var depthPrimitive;
+
+ beforeAll(function() {
+ scene = createScene();
+ scene.fxaa = false;
+
+ ellipsoid = Ellipsoid.WGS84;
+ });
+
+ afterAll(function() {
+ scene.destroyForSpecs();
+ });
+
+ function MockGlobePrimitive(primitive) {
+ this._primitive = primitive;
+ }
+ MockGlobePrimitive.prototype.update = function(frameState) {
+ var commandList = frameState.commandList;
+ var startLength = commandList.length;
+ this._primitive.update(frameState);
+
+ for (var i = startLength; i < commandList.length; ++i) {
+ var command = commandList[i];
+ command.pass = Pass.GLOBE;
+ }
+ };
+
+ MockGlobePrimitive.prototype.isDestroyed = function() {
+ return false;
+ };
+
+ MockGlobePrimitive.prototype.destroy = function() {
+ this._primitive.destroy();
+ return destroyObject(this);
+ };
+
+ beforeEach(function() {
+ scene.morphTo3D(0);
+
+ rectangle = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0);
+
+ var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0));
+ depthColor = depthColorAttribute.value;
+ var primitive = new Primitive({
+ geometryInstances : new GeometryInstance({
+ geometry : new RectangleGeometry({
+ ellipsoid : ellipsoid,
+ rectangle : rectangle
+ }),
+ id : 'depth rectangle',
+ attributes : {
+ color : depthColorAttribute
+ }
+ }),
+ appearance : new PerInstanceColorAppearance({
+ translucent : false,
+ flat : true
+ }),
+ asynchronous : false
+ });
+
+ // wrap rectangle primitive so it gets executed during the globe pass to lay down depth
+ depthPrimitive = new MockGlobePrimitive(primitive);
+
+ var center = Rectangle.center(rectangle);
+ var origin = ellipsoid.cartographicToCartesian(center);
+ var modelMatrix = Transforms.eastNorthUpToFixedFrame(origin);
+
+ var dimensions = new Cartesian3(1000000.0, 1000000.0, 1000000.0);
+
+ var boxColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 1.0, 0.0, 1.0));
+ boxColor = boxColorAttribute.value;
+ boxInstance = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box',
+ attributes : {
+ color : boxColorAttribute
+ }
+ });
+ });
+
+ afterEach(function() {
+ scene.groundPrimitives.removeAll();
+ primitive = primitive && !primitive.isDestroyed() && primitive.destroy();
+ depthPrimitive = depthPrimitive && !depthPrimitive.isDestroyed() && depthPrimitive.destroy();
+ });
+
+ it('default constructs', function() {
+ primitive = new ClassificationPrimitive();
+ expect(primitive.geometryInstances).not.toBeDefined();
+ expect(primitive.show).toEqual(true);
+ expect(primitive.vertexCacheOptimize).toEqual(false);
+ expect(primitive.interleave).toEqual(false);
+ expect(primitive.compressVertices).toEqual(true);
+ expect(primitive.releaseGeometryInstances).toEqual(true);
+ expect(primitive.allowPicking).toEqual(true);
+ expect(primitive.asynchronous).toEqual(true);
+ expect(primitive.debugShowBoundingVolume).toEqual(false);
+ expect(primitive.debugShowShadowVolume).toEqual(false);
+ });
+
+ it('constructs with options', function() {
+ var geometryInstances = [];
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : geometryInstances,
+ show : false,
+ vertexCacheOptimize : true,
+ interleave : true,
+ compressVertices : false,
+ releaseGeometryInstances : false,
+ allowPicking : false,
+ asynchronous : false,
+ debugShowBoundingVolume : true,
+ debugShowShadowVolume : true
+ });
+
+ expect(primitive.geometryInstances).toEqual(geometryInstances);
+ expect(primitive.show).toEqual(false);
+ expect(primitive.vertexCacheOptimize).toEqual(true);
+ expect(primitive.interleave).toEqual(true);
+ expect(primitive.compressVertices).toEqual(false);
+ expect(primitive.releaseGeometryInstances).toEqual(false);
+ expect(primitive.allowPicking).toEqual(false);
+ expect(primitive.asynchronous).toEqual(false);
+ expect(primitive.debugShowBoundingVolume).toEqual(true);
+ expect(primitive.debugShowShadowVolume).toEqual(true);
+ });
+
+ it('releases geometry instances when releaseGeometryInstances is true', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ releaseGeometryInstances : true,
+ asynchronous : false
+ });
+
+ expect(primitive.geometryInstances).toBeDefined();
+ scene.groundPrimitives.add(primitive);
+ scene.renderForSpecs();
+ expect(primitive.geometryInstances).not.toBeDefined();
+ });
+
+ it('does not release geometry instances when releaseGeometryInstances is false', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ releaseGeometryInstances : false,
+ asynchronous : false
+ });
+
+ expect(primitive.geometryInstances).toBeDefined();
+ scene.groundPrimitives.add(primitive);
+ scene.renderForSpecs();
+ expect(primitive.geometryInstances).toBeDefined();
+ });
+
+ it('adds afterRender promise to frame state', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ releaseGeometryInstances : false,
+ asynchronous : false
+ });
+
+ scene.groundPrimitives.add(primitive);
+ scene.renderForSpecs();
+
+ return primitive.readyPromise.then(function(param) {
+ expect(param.ready).toBe(true);
+ });
+ });
+
+ it('does not render when geometryInstances is undefined', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : undefined,
+ appearance : new PerInstanceColorAppearance(),
+ asynchronous : false
+ });
+
+ var frameState = scene.frameState;
+ frameState.commandList.length = 0;
+
+ primitive.update(frameState);
+ expect(frameState.commandList.length).toEqual(0);
+ });
+
+ it('does not render when show is false', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ var frameState = scene.frameState;
+
+ frameState.commandList.length = 0;
+ primitive.update(frameState);
+ expect(frameState.afterRender.length).toEqual(1);
+
+ frameState.afterRender[0]();
+ frameState.commandList.length = 0;
+ primitive.update(frameState);
+ expect(frameState.commandList.length).toBeGreaterThan(0);
+
+ frameState.commandList.length = 0;
+ primitive.show = false;
+ primitive.update(frameState);
+ expect(frameState.commandList.length).toEqual(0);
+ });
+
+ it('does not render other than for the color or pick pass', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ var frameState = scene.frameState;
+ frameState.passes.render = false;
+ frameState.passes.pick = false;
+
+ primitive.update(frameState);
+ expect(frameState.commandList.length).toEqual(0);
+ });
+
+ function verifyClassificationPrimitiveRender(primitive, color) {
+ scene.camera.setView({ destination : rectangle });
+
+ scene.groundPrimitives.add(depthPrimitive);
+ expect(scene).toRenderAndCall(function(rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba[0]).toEqual(0);
+ });
+
+ scene.groundPrimitives.add(primitive);
+ expect(scene).toRender(color);
+ }
+
+ it('renders in 3D', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+ });
+
+ it('renders in Columbus view when scene3DOnly is false', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ scene.morphToColumbusView(0);
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+ });
+
+ it('renders in 2D when scene3DOnly is false', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ scene.morphTo2D(0);
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+ });
+
+ it('renders batched instances', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ var neCarto = Rectangle.northeast(rectangle);
+ var nwCarto = Rectangle.northwest(rectangle);
+
+ var ne = ellipsoid.cartographicToCartesian(neCarto);
+ var nw = ellipsoid.cartographicToCartesian(nwCarto);
+
+ var direction = Cartesian3.subtract(ne, nw, new Cartesian3());
+ var distance = Cartesian3.magnitude(direction) * 0.25;
+ Cartesian3.normalize(direction, direction);
+ Cartesian3.multiplyByScalar(direction, distance, direction);
+
+ var center = Rectangle.center(rectangle);
+ var origin = ellipsoid.cartographicToCartesian(center);
+
+ var origin1 = Cartesian3.add(origin, direction, new Cartesian3());
+ var modelMatrix = Transforms.eastNorthUpToFixedFrame(origin1);
+
+ var dimensions = new Cartesian3(500000.0, 1000000.0, 1000000.0);
+
+ var boxColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 1.0, 1.0, 1.0));
+ var boxInstance1 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box1',
+ attributes : {
+ color : boxColorAttribute
+ }
+ });
+
+ Cartesian3.negate(direction, direction);
+ var origin2 = Cartesian3.add(origin, direction, new Cartesian3());
+ modelMatrix = Transforms.eastNorthUpToFixedFrame(origin2);
+
+ var boxInstance2 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box2',
+ attributes : {
+ color : boxColorAttribute
+ }
+ });
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : [boxInstance1, boxInstance2],
+ asynchronous : false
+ });
+ verifyClassificationPrimitiveRender(primitive, boxColorAttribute.value);
+ });
+
+ it('renders bounding volume with debugShowBoundingVolume', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false,
+ debugShowBoundingVolume : true
+ });
+
+ scene.groundPrimitives.add(primitive);
+ scene.camera.setView({ destination : rectangle });
+ expect(scene).toRenderAndCall(function(rgba) {
+ expect(rgba[1]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[1]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[2]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[3]).toEqual(255);
+ });
+ });
+
+ it('renders shadow volume with debugShowShadowVolume', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false,
+ debugShowShadowVolume : true
+ });
+
+ scene.groundPrimitives.add(primitive);
+ scene.camera.setView({ destination : rectangle });
+ expect(scene).toRenderAndCall(function(rgba) {
+ expect(rgba[1]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[1]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[2]).toBeGreaterThanOrEqualTo(0);
+ expect(rgba[3]).toEqual(255);
+ });
+ });
+
+ it('get per instance attributes', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ expect(attributes.color).toBeDefined();
+ });
+
+ it('modify color instance attribute', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ scene.groundPrimitives.destroyPrimitives = false;
+ scene.groundPrimitives.removeAll();
+ scene.groundPrimitives.destroyPrimitives = true;
+
+ var newColor = [255, 255, 255, 255];
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ expect(attributes.color).toBeDefined();
+ attributes.color = newColor;
+
+ verifyClassificationPrimitiveRender(primitive, newColor);
+ });
+
+ it('modify show instance attribute', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ boxInstance.attributes.show = new ShowGeometryInstanceAttribute(true);
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ scene.groundPrimitives.destroyPrimitives = false;
+ scene.groundPrimitives.removeAll();
+ scene.groundPrimitives.destroyPrimitives = true;
+
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ expect(attributes.show).toBeDefined();
+ attributes.show = [0];
+
+ verifyClassificationPrimitiveRender(primitive, depthColor);
+ });
+
+ it('get bounding sphere from per instance attribute', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ expect(attributes.boundingSphere).toBeDefined();
+ });
+
+ it('getGeometryInstanceAttributes returns same object each time', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ var attributes2 = primitive.getGeometryInstanceAttributes('box');
+ expect(attributes).toBe(attributes2);
+ });
+
+ it('picking', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ expect(scene).toPickAndCall(function(result) {
+ expect(result.id).toEqual('box');
+ });
+ });
+
+ it('does not pick when allowPicking is false', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ allowPicking : false,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ expect(scene).notToPick();
+ });
+
+ it('internally invalid asynchronous geometry resolves promise and sets ready', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : new GeometryInstance({
+ geometry : PolygonGeometry.fromPositions({
+ positions : []
+ }),
+ attributes: {
+ color: ColorGeometryInstanceAttribute.fromColor(Color.RED)
+ }
+ }),
+ compressVertices : false
+ });
+
+ var frameState = scene.frameState;
+ frameState.afterRender.length = 0;
+ return pollToPromise(function() {
+ if (frameState.afterRender.length > 0) {
+ frameState.afterRender[0]();
+ return true;
+ }
+ primitive.update(frameState);
+ return false;
+ }).then(function() {
+ return primitive.readyPromise.then(function(arg) {
+ expect(arg).toBe(primitive);
+ expect(primitive.ready).toBe(true);
+ });
+ });
+ });
+
+ it('internally invalid synchronous geometry resolves promise and sets ready', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : new GeometryInstance({
+ geometry : PolygonGeometry.fromPositions({
+ positions : []
+ }),
+ attributes: {
+ color: ColorGeometryInstanceAttribute.fromColor(Color.RED)
+ }
+ }),
+ asynchronous : false,
+ compressVertices : false
+ });
+
+ var frameState = scene.frameState;
+ frameState.afterRender.length = 0;
+ return pollToPromise(function() {
+ if (frameState.afterRender.length > 0) {
+ frameState.afterRender[0]();
+ return true;
+ }
+ primitive.update(frameState);
+ return false;
+ }).then(function() {
+ return primitive.readyPromise.then(function(arg) {
+ expect(arg).toBe(primitive);
+ expect(primitive.ready).toBe(true);
+ });
+ });
+ });
+
+ it('update throws when batched instance colors are different', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ var neCarto = Rectangle.northeast(rectangle);
+ var nwCarto = Rectangle.northwest(rectangle);
+
+ var ne = ellipsoid.cartographicToCartesian(neCarto);
+ var nw = ellipsoid.cartographicToCartesian(nwCarto);
+
+ var direction = Cartesian3.subtract(ne, nw, new Cartesian3());
+ var distance = Cartesian3.magnitude(direction) * 0.25;
+ Cartesian3.normalize(direction, direction);
+ Cartesian3.multiplyByScalar(direction, distance, direction);
+
+ var center = Rectangle.center(rectangle);
+ var origin = ellipsoid.cartographicToCartesian(center);
+
+ var origin1 = Cartesian3.add(origin, direction, new Cartesian3());
+ var modelMatrix = Transforms.eastNorthUpToFixedFrame(origin1);
+
+ var dimensions = new Cartesian3(500000.0, 1000000.0, 1000000.0);
+
+ var boxColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 1.0, 1.0, 1.0));
+ var boxInstance1 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box1',
+ attributes : {
+ color : ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 1.0, 1.0, 1.0))
+ }
+ });
+
+ Cartesian3.negate(direction, direction);
+ var origin2 = Cartesian3.add(origin, direction, new Cartesian3());
+ modelMatrix = Transforms.eastNorthUpToFixedFrame(origin2);
+
+ var boxInstance2 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box2',
+ attributes : {
+ color : ColorGeometryInstanceAttribute.fromColor(new Color(1.0, 0.0, 1.0, 1.0))
+ }
+ });
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : [boxInstance1, boxInstance2],
+ asynchronous : false
+ });
+
+ expect(function() {
+ verifyClassificationPrimitiveRender(primitive, boxColorAttribute.value);
+ }).toThrowDeveloperError();
+ });
+
+ it('update throws when one batched instance color is undefined', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ var neCarto = Rectangle.northeast(rectangle);
+ var nwCarto = Rectangle.northwest(rectangle);
+
+ var ne = ellipsoid.cartographicToCartesian(neCarto);
+ var nw = ellipsoid.cartographicToCartesian(nwCarto);
+
+ var direction = Cartesian3.subtract(ne, nw, new Cartesian3());
+ var distance = Cartesian3.magnitude(direction) * 0.25;
+ Cartesian3.normalize(direction, direction);
+ Cartesian3.multiplyByScalar(direction, distance, direction);
+
+ var center = Rectangle.center(rectangle);
+ var origin = ellipsoid.cartographicToCartesian(center);
+
+ var origin1 = Cartesian3.add(origin, direction, new Cartesian3());
+ var modelMatrix = Transforms.eastNorthUpToFixedFrame(origin1);
+
+ var dimensions = new Cartesian3(500000.0, 1000000.0, 1000000.0);
+
+ var boxColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 1.0, 1.0, 1.0));
+ var boxInstance1 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box1',
+ attributes : {
+ color : ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 1.0, 1.0, 1.0))
+ }
+ });
+
+ Cartesian3.negate(direction, direction);
+ var origin2 = Cartesian3.add(origin, direction, new Cartesian3());
+ modelMatrix = Transforms.eastNorthUpToFixedFrame(origin2);
+
+ var boxInstance2 = new GeometryInstance({
+ geometry : BoxGeometry.fromDimensions({
+ dimensions : dimensions
+ }),
+ modelMatrix : modelMatrix,
+ id : 'box2'
+ });
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : [boxInstance1, boxInstance2],
+ asynchronous : false
+ });
+
+ expect(function() {
+ verifyClassificationPrimitiveRender(primitive, boxColorAttribute.value);
+ }).toThrowDeveloperError();
+ });
+
+ it('setting per instance attribute throws when value is undefined', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+
+ expect(function() {
+ attributes.color = undefined;
+ }).toThrowDeveloperError();
+ });
+
+ it('can disable picking when asynchronous', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : true,
+ allowPicking : false
+ });
+
+ var frameState = scene.frameState;
+
+ return pollToPromise(function() {
+ primitive.update(frameState);
+ if (frameState.afterRender.length > 0) {
+ frameState.afterRender[0]();
+ }
+ return primitive.ready;
+ }).then(function() {
+ var attributes = primitive.getGeometryInstanceAttributes('box');
+ expect(function() {
+ attributes.color = undefined;
+ }).toThrowDeveloperError();
+ });
+ });
+
+ it('getGeometryInstanceAttributes throws without id', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ expect(function() {
+ primitive.getGeometryInstanceAttributes();
+ }).toThrowDeveloperError();
+ });
+
+ it('getGeometryInstanceAttributes throws if update was not called', function() {
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ expect(function() {
+ primitive.getGeometryInstanceAttributes('box');
+ }).toThrowDeveloperError();
+ });
+
+ it('getGeometryInstanceAttributes returns undefined if id does not exist', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance,
+ asynchronous : false
+ });
+
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+
+ expect(primitive.getGeometryInstanceAttributes('unknown')).not.toBeDefined();
+ });
+
+ it('isDestroyed', function() {
+ primitive = new ClassificationPrimitive();
+ expect(primitive.isDestroyed()).toEqual(false);
+ primitive.destroy();
+ expect(primitive.isDestroyed()).toEqual(true);
+ });
+
+ it('renders when using asynchronous pipeline', function() {
+ if (!ClassificationPrimitive.isSupported(scene)) {
+ return;
+ }
+
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance
+ });
+
+ var frameState = scene.frameState;
+
+ return pollToPromise(function() {
+ primitive.update(frameState);
+ if (frameState.afterRender.length > 0) {
+ frameState.afterRender[0]();
+ }
+ return primitive.ready;
+ }).then(function() {
+ verifyClassificationPrimitiveRender(primitive, boxColor);
+ });
+ });
+
+ it('destroy before asynchonous pipeline is complete', function() {
+ primitive = new ClassificationPrimitive({
+ geometryInstances : boxInstance
+ });
+
+ var frameState = scene.frameState;
+ primitive.update(frameState);
+
+ primitive.destroy();
+ expect(primitive.isDestroyed()).toEqual(true);
+ });
+}, 'WebGL');
diff --git a/Specs/Scene/Composite3DTileContentSpec.js b/Specs/Scene/Composite3DTileContentSpec.js
index 103ad89ac774..6eb79d187840 100644
--- a/Specs/Scene/Composite3DTileContentSpec.js
+++ b/Specs/Scene/Composite3DTileContentSpec.js
@@ -20,12 +20,13 @@ defineSuite([
var compositeUrl = './Data/Cesium3DTiles/Composite/Composite/';
var compositeOfComposite = './Data/Cesium3DTiles/Composite/CompositeOfComposite/';
+ var compositeOfInstanced = './Data/Cesium3DTiles/Composite/CompositeOfInstanced/';
beforeAll(function() {
scene = createScene();
// One item in each data set is always located in the center, so point the camera there
var center = Cartesian3.fromRadians(centerLongitude, centerLatitude);
- scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 30.0));
+ scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 26.0));
});
afterAll(function() {
@@ -121,6 +122,10 @@ defineSuite([
return Cesium3DTilesTester.loadTileset(scene, compositeOfComposite).then(expectRenderComposite);
});
+ it('renders multiple instanced tilesets', function() {
+ return Cesium3DTilesTester.loadTileset(scene, compositeOfInstanced).then(expectRenderComposite);
+ });
+
it('destroys', function() {
return Cesium3DTilesTester.tileDestroys(scene, compositeUrl);
});
diff --git a/Specs/Scene/DebugCameraPrimitiveSpec.js b/Specs/Scene/DebugCameraPrimitiveSpec.js
index 9634f622b567..90fe211053bb 100644
--- a/Specs/Scene/DebugCameraPrimitiveSpec.js
+++ b/Specs/Scene/DebugCameraPrimitiveSpec.js
@@ -16,16 +16,18 @@ defineSuite([
var camera;
beforeAll(function() {
- scene = createScene();
-
- camera = new Camera(scene);
- camera.position = new Cartesian3(0.0, 0.0, 0.0);
- camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3());
- camera.up = Cartesian3.clone(Cartesian3.UNIT_Z);
+ scene = createScene({
+ scene3DOnly : true
+ });
scene.camera.position = new Cartesian3(0.0, 0.0, 0.0);
scene.camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3());
scene.camera.up = Cartesian3.clone(Cartesian3.UNIT_Z);
+ scene.camera.frustum.near = 1.0;
+ scene.camera.frustum.far = 500.0;
+
+ camera = Camera.clone(scene.camera);
+
scene.camera.zoomOut(1.0);
});
@@ -85,9 +87,9 @@ defineSuite([
camera : camera
}));
scene.renderForSpecs();
- var primitive = p._outlinePrimitive;
+ var primitive = p._outlinePrimitives[0];
scene.renderForSpecs();
- expect(p._outlinePrimitive).not.toBe(primitive);
+ expect(p._outlinePrimitives[0]).not.toBe(primitive);
});
it('does not update when updateOnChange is false', function() {
diff --git a/Specs/Scene/GroundPrimitiveSpec.js b/Specs/Scene/GroundPrimitiveSpec.js
index 54c22f83c35a..f80c0fcdb7fa 100644
--- a/Specs/Scene/GroundPrimitiveSpec.js
+++ b/Specs/Scene/GroundPrimitiveSpec.js
@@ -152,6 +152,7 @@ defineSuite([
expect(primitive.allowPicking).toEqual(true);
expect(primitive.asynchronous).toEqual(true);
expect(primitive.debugShowBoundingVolume).toEqual(false);
+ expect(primitive.debugShowShadowVolume).toEqual(false);
});
it('constructs with options', function() {
@@ -166,7 +167,8 @@ defineSuite([
releaseGeometryInstances : false,
allowPicking : false,
asynchronous : false,
- debugShowBoundingVolume : true
+ debugShowBoundingVolume : true,
+ debugShowShadowVolume : true
});
expect(primitive.geometryInstances).toEqual(geometryInstances);
@@ -178,6 +180,7 @@ defineSuite([
expect(primitive.allowPicking).toEqual(false);
expect(primitive.asynchronous).toEqual(false);
expect(primitive.debugShowBoundingVolume).toEqual(true);
+ expect(primitive.debugShowShadowVolume).toEqual(true);
});
it('releases geometry instances when releaseGeometryInstances is true', function() {
diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js
index 1b10f96f4c5f..2f999ca1ea9d 100644
--- a/Specs/Scene/LabelCollectionSpec.js
+++ b/Specs/Scene/LabelCollectionSpec.js
@@ -2210,6 +2210,40 @@ defineSuite([
cartographic = scene.globe.ellipsoid.cartesianToCartographic(l._clampedPosition);
expect(cartographic.height).toEqualEpsilon(100.0, CesiumMath.EPSILON9);
});
+
+ it('resets the clamped position when HeightReference.NONE', function() {
+ scene.globe = createGlobe();
+ spyOn(scene.camera, 'update');
+ var l = labelsWithHeight.add({
+ heightReference : HeightReference.CLAMP_TO_GROUND,
+ text: 't',
+ position : Cartesian3.fromDegrees(-72.0, 40.0)
+ });
+ scene.renderForSpecs();
+ expect(l._clampedPosition).toBeDefined();
+ expect(l._glyphs[0].billboard._clampedPosition).toBeDefined();
+
+ l.heightReference = HeightReference.NONE;
+ expect(l._clampedPosition).toBeUndefined();
+ expect(l._glyphs[0].billboard._clampedPosition).toBeUndefined();
+ });
+
+ it('clears the billboard height reference callback when the label is removed', function() {
+ scene.globe = createGlobe();
+ spyOn(scene.camera, 'update');
+ var l = labelsWithHeight.add({
+ heightReference : HeightReference.CLAMP_TO_GROUND,
+ text: 't',
+ position : Cartesian3.fromDegrees(-72.0, 40.0)
+ });
+ scene.renderForSpecs();
+ var billboard = l._glyphs[0].billboard;
+ expect(billboard._removeCallbackFunc).toBeDefined();
+ var spy = spyOn(billboard, '_removeCallbackFunc');
+ labelsWithHeight.remove(l);
+ expect(spy).toHaveBeenCalled();
+ expect(labelsWithHeight._spareBillboards[0]._removeCallbackFunc).not.toBeDefined();
+ });
});
}, 'WebGL');
diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js
index 87f7a76c5346..c7dd2dfec270 100644
--- a/Specs/Scene/ModelSpec.js
+++ b/Specs/Scene/ModelSpec.js
@@ -2521,6 +2521,23 @@ defineSuite([
});
});
+ it('removes the callback on destroy', function() {
+ scene.globe = createMockGlobe();
+ return loadModelJson(texturedBoxModel.gltf, {
+ heightReference : HeightReference.CLAMP_TO_GROUND,
+ position : Cartesian3.fromDegrees(-72.0, 40.0),
+ scene : scene,
+ show : true
+ }).then(function(model) {
+ expect(scene.globe.callback).toBeDefined();
+ scene.renderForSpecs();
+
+ primitives.remove(model);
+ scene.renderForSpecs();
+ expect(scene.globe.callback).toBeUndefined();
+ });
+ });
+
it('changing the terrain provider', function() {
scene.globe = createMockGlobe();
return loadModelJson(texturedBoxModel.gltf, {
@@ -2566,5 +2583,4 @@ defineSuite([
});
});
});
-
}, 'WebGL');
diff --git a/Specs/createGlobe.js b/Specs/createGlobe.js
index aaa05130dca2..4ad23aec8b51 100644
--- a/Specs/createGlobe.js
+++ b/Specs/createGlobe.js
@@ -17,6 +17,8 @@ define([
callback : undefined,
removedCallback : false,
ellipsoid : ellipsoid,
+ beginFrame: function() {},
+ endFrame: function() {},
update : function() {},
getHeight : function() {
return 0.0;
diff --git a/Specs/karma.conf.js b/Specs/karma.conf.js
index 374f114dded8..a697e0767b27 100644
--- a/Specs/karma.conf.js
+++ b/Specs/karma.conf.js
@@ -6,6 +6,9 @@ module.exports = function(config) {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath : '..',
+ // Disable module load timeout
+ waitSeconds : 0,
+
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks : ['jasmine', 'requirejs', 'detectBrowsers'],
diff --git a/gulpfile.js b/gulpfile.js
index eb0a95c34a6a..6be3722bd14b 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -46,8 +46,10 @@ var noDevelopmentGallery = taskName === 'release' || taskName === 'makeZipFile';
var buildingRelease = noDevelopmentGallery;
var minifyShaders = taskName === 'minify' || taskName === 'minifyRelease' || taskName === 'release' || taskName === 'makeZipFile' || taskName === 'buildApps';
-//travis reports 32 cores but only has 3GB of memory, which causes the VM to run out. Limit to 8 cores instead.
-var concurrency = Math.min(os.cpus().length, 8);
+var concurrency = yargs.argv.concurrency;
+if (!concurrency) {
+ concurrency = os.cpus().length;
+}
//Since combine and minify run in parallel already, split concurrency in half when building both.
//This can go away when gulp 4 comes out because it allows for synchronous tasks.
@@ -123,6 +125,10 @@ gulp.task('clean', function(done) {
gulp.task('requirejs', function(done) {
var config = JSON.parse(new Buffer(process.argv[3].substring(2), 'base64').toString('utf8'));
+
+ // Disable module load timeout
+ config.waitSeconds = 0;
+
requirejs.optimize(config, function() {
done();
}, done);