diff --git a/CHANGES.md b/CHANGES.md
index 690a61d5423c..821cd55bcd1a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,27 +1,40 @@
# Change Log
+### 1.72 - 2020-08-03
+
+##### Fixes :wrench:
+
+- Fixed 3D Tileset replacement refinement when leaf is empty. [#8996](https://github.com/CesiumGS/cesium/8996)
+
### 1.71 - 2020-07-01
##### Breaking Changes :mega:
-- Updated `WallGeometry` to respect the order of positions passed in, instead of making the positions respect a counter clockwise winding order. This will only effect the look of walls with an image material. If this changed the way your wall is drawing, reverse the order of the positions.
+- Updated `WallGeometry` to respect the order of positions passed in, instead of making the positions respect a counter clockwise winding order. This will only affect the look of walls with an image material. If this changed the way your wall is drawing, reverse the order of the positions. [#8955](https://github.com/CesiumGS/cesium/pull/8955/)
##### Additions :tada:
+- Added `backFaceCulling` property to `Cesium3DTileset` and `Model` to support viewing the underside or interior of a tileset or model. [#8981](https://github.com/CesiumGS/cesium/pull/8981)
- Added `Ellipsoid.surfaceArea` for computing the approximate surface area of a rectangle on the surface of an ellipsoid. [#8986](https://github.com/CesiumGS/cesium/pull/8986)
- Added support for PolylineVolume in CZML. [#8841](https://github.com/CesiumGS/cesium/pull/8841)
- Added `Color.toCssHexString` for getting the CSS hex string equivalent for a color. [#8987](https://github.com/CesiumGS/cesium/pull/8987)
##### Fixes :wrench:
-- Fixed issue where tileset was not playing glTF animations [#8962](https://github.com/CesiumGS/cesium/issues/8962)
+- Fixed issue where tileset was not playing glTF animations. [#8962](https://github.com/CesiumGS/cesium/issues/8962)
- Fixed a divide-by-zero bug in `Ellipsoid.geodeticSurfaceNormal` when given the origin as input. `undefined` is returned instead. [#8986](https://github.com/CesiumGS/cesium/pull/8986)
-- Fixed error with `WallGeoemtry` when there were adjacent positions with very close values [#8952](https://github.com/CesiumGS/cesium/pull/8952)
+- Fixed error with `WallGeometry` when there were adjacent positions with very close values. [#8952](https://github.com/CesiumGS/cesium/pull/8952)
- Fixed artifact for skinned model when log depth is enabled. [#6447](https://github.com/CesiumGS/cesium/issues/6447)
- Fixed a bug where certain rhumb arc polylines would lead to a crash. [#8787](https://github.com/CesiumGS/cesium/pull/8787)
-- Fixed handling of Label's backgroundColor and backgroundPadding option [#8949](https://github.com/CesiumGS/cesium/8949)
-- Fixed several bugs when rendering CesiumJS in a WebG 2 context. [#797](https://github.com/CesiumGS/cesium/issues/797)
-- Fixed 3D Tileset replacement refinement when leaf is empty. [#8996](https://github.com/CesiumGS/cesium/8996)
+- Fixed handling of Label's backgroundColor and backgroundPadding option [#8949](https://github.com/CesiumGS/cesium/pull/8949)
+- Fixed several bugs when rendering CesiumJS in a WebGL 2 context. [#797](https://github.com/CesiumGS/cesium/issues/797)
+- Fixed a bug where switching from perspective to orthographic caused triangles to overlap each other incorrectly. [#8346](https://github.com/CesiumGS/cesium/issues/8346)
+- Fixed a bug where switching to orthographic camera on the first frame caused the zoom level to be incorrect. [#8853](https://github.com/CesiumGS/cesium/pull/8853)
+- Fixed `scene.pickFromRay` intersection inaccuracies. [#8439](https://github.com/CesiumGS/cesium/issues/8439)
+- Fixed a bug where a null or undefined name property passed to the `Entity` constructor would throw an exception.[#8832](https://github.com/CesiumGS/cesium/pull/8832)
+- Fixed JSDoc and TypeScript type definitions for `ScreenSpaceEventHandler.getInputAction` which listed incorrect return type. [#9002](https://github.com/CesiumGS/cesium/pull/9002)
+- Improved the style of the error panel. [#8739](https://github.com/CesiumGS/cesium/issues/8739)
+- Fixed animation widget SVG icons not appearing in iOS 13.5.1. [#8993](https://github.com/CesiumGS/cesium/pull/8993)
### 1.70.1 - 2020-06-10
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 3324c0623208..1fa6b409270f 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -117,6 +117,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
- [Orbit Logic](http://www.orbitlogic.com)
- [Roderick Green](https://github.com/roderickgreen/)
- [Sam Parrish](https://github.com/sgparrish/)
+ - [Sang Han](https://github.com/seoular/)
- [Hexastack](https://www.hexastack.com)
- [Mohamed Marrouchi](https://github.com/marrouchi/)
- [PropellerAero](https://www.propelleraero.com/)
@@ -268,3 +269,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
- [Wang Bao](https://github.com/xiaobaogeit)
- [John Remsberg](https://github.com/easternmotors)
- [Bao Thien Tran](https://github.com/baothientran)
+- [Yonatan Kra](https://github.com/yonatankra)
diff --git a/Source/Core/Heap.js b/Source/Core/Heap.js
index 8d7d6ebece75..89aa78bda324 100644
--- a/Source/Core/Heap.js
+++ b/Source/Core/Heap.js
@@ -65,11 +65,20 @@ Object.defineProperties(Heap.prototype, {
return this._maximumLength;
},
set: function (value) {
- this._maximumLength = value;
- if (this._length > value && value > 0) {
+ //>>includeStart('debug', pragmas.debug);
+ Check.typeOf.number.greaterThanOrEquals("maximumLength", value, 0);
+ //>>includeEnd('debug');
+ var originalLength = this._length;
+ if (value < originalLength) {
+ var array = this._array;
+ // Remove trailing references
+ for (var i = value; i < originalLength; ++i) {
+ array[i] = undefined;
+ }
this._length = value;
- this._array.length = value;
+ array.length = value;
}
+ this._maximumLength = value;
},
},
@@ -211,6 +220,7 @@ Heap.prototype.pop = function (index) {
var root = array[index];
swap(array, index, --this._length);
this.heapify(index);
+ array[this._length] = undefined; // Remove trailing reference
return root;
};
diff --git a/Source/Core/Ion.js b/Source/Core/Ion.js
index 1bb1005a2b0a..3610b783b8ea 100644
--- a/Source/Core/Ion.js
+++ b/Source/Core/Ion.js
@@ -4,7 +4,7 @@ import Resource from "./Resource.js";
var defaultTokenCredit;
var defaultAccessToken =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwNjk5ODcyYS00MWMyLTQ1NjctYTRhYS0zMmM3ZjYzMGM2ZGEiLCJpZCI6MjU5LCJzY29wZXMiOlsiYXNyIiwiZ2MiXSwiaWF0IjoxNTkxMDI3NDUwfQ.xUBBQH34cd86pfNMSQ6tBBelRx3g_RS51-nSUFlZq24";
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4NzdlNDVhMy1mNDAxLTQ5MWMtODhkYS00MTc1MTU4NzFmNzciLCJpZCI6MjU5LCJzY29wZXMiOlsiYXNyIiwiZ2MiXSwiaWF0IjoxNTkzNjI4MDI3fQ.sqnKP2DNn0soCyh1t9taAa2xkbZ6EIn0Z7_VwujTCtQ";
/**
* Default settings for accessing the Cesium ion API.
*
diff --git a/Source/Core/ManagedArray.js b/Source/Core/ManagedArray.js
index fb4119da9a93..0c404a0a2ff3 100644
--- a/Source/Core/ManagedArray.js
+++ b/Source/Core/ManagedArray.js
@@ -29,10 +29,20 @@ Object.defineProperties(ManagedArray.prototype, {
return this._length;
},
set: function (length) {
- this._length = length;
- if (length > this._array.length) {
- this._array.length = length;
+ //>>includeStart('debug', pragmas.debug);
+ Check.typeOf.number.greaterThanOrEquals("length", length, 0);
+ //>>includeEnd('debug');
+ var array = this._array;
+ var originalLength = this._length;
+ if (length < originalLength) {
+ // Remove trailing references
+ for (var i = length; i < originalLength; ++i) {
+ array[i] = undefined;
+ }
+ } else if (length > array.length) {
+ array.length = length;
}
+ this._length = length;
},
},
@@ -74,7 +84,7 @@ ManagedArray.prototype.set = function (index, element) {
Check.typeOf.number("index", index);
//>>includeEnd('debug');
- if (index >= this.length) {
+ if (index >= this._length) {
this.length = index + 1;
}
this._array[index] = element;
@@ -105,7 +115,12 @@ ManagedArray.prototype.push = function (element) {
* @returns {*} The last element in the array.
*/
ManagedArray.prototype.pop = function () {
- return this._array[--this.length];
+ if (this._length === 0) {
+ return undefined;
+ }
+ var element = this._array[this._length - 1];
+ --this.length;
+ return element;
};
/**
@@ -142,7 +157,7 @@ ManagedArray.prototype.resize = function (length) {
* @param {Number} [length] The length.
*/
ManagedArray.prototype.trim = function (length) {
- length = defaultValue(length, this.length);
+ length = defaultValue(length, this._length);
this._array.length = length;
};
export default ManagedArray;
diff --git a/Source/Core/ScreenSpaceEventHandler.js b/Source/Core/ScreenSpaceEventHandler.js
index db52177dc029..ce209cef9732 100644
--- a/Source/Core/ScreenSpaceEventHandler.js
+++ b/Source/Core/ScreenSpaceEventHandler.js
@@ -969,6 +969,8 @@ ScreenSpaceEventHandler.prototype.setInputAction = function (
* @param {Number} [modifier] A KeyboardEventModifier key that is held when a type
* event occurs.
*
+ * @returns {Function} The function to be executed on an input event.
+ *
* @see ScreenSpaceEventHandler#setInputAction
* @see ScreenSpaceEventHandler#removeInputAction
*/
diff --git a/Source/DataSources/Entity.js b/Source/DataSources/Entity.js
index 091ca47492df..067effcd9170 100644
--- a/Source/DataSources/Entity.js
+++ b/Source/DataSources/Entity.js
@@ -594,8 +594,10 @@ Entity.prototype.merge = function (source) {
for (var i = 0; i < propertyNamesLength; i++) {
var name = sourcePropertyNames[i];
- //Ignore parent when merging, this only happens at construction time.
- if (name === "parent") {
+ //While source is required by the API to be an Entity, we internally call this method from the
+ //constructor with an options object to configure initial custom properties.
+ //So we need to ignore reserved-non-property.
+ if (name === "parent" || name === "name" || name === "availability") {
continue;
}
diff --git a/Source/DataSources/SampledProperty.js b/Source/DataSources/SampledProperty.js
index a019b61a9741..52c514cd917c 100644
--- a/Source/DataSources/SampledProperty.js
+++ b/Source/DataSources/SampledProperty.js
@@ -817,6 +817,9 @@ SampledProperty.prototype.equals = function (other) {
var values = this._values;
var otherValues = other._values;
+ length = values.length;
+
+ //Since time lengths are equal, values length and other length are guaranteed to be equal.
for (i = 0; i < length; i++) {
if (values[i] !== otherValues[i]) {
return false;
diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js
index 2a9860c27e1a..5e38ecf48cce 100644
--- a/Source/Scene/Batched3DModel3DTileContent.js
+++ b/Source/Scene/Batched3DModel3DTileContent.js
@@ -432,6 +432,7 @@ function initialize(content, arrayBuffer, byteOffset) {
luminanceAtZenith: tileset.luminanceAtZenith,
sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients,
specularEnvironmentMaps: tileset.specularEnvironmentMaps,
+ backFaceCulling: tileset.backFaceCulling,
});
content._model.readyPromise.then(function (model) {
model.activeAnimations.addAll({
@@ -539,6 +540,7 @@ Batched3DModel3DTileContent.prototype.update = function (tileset, frameState) {
this._model.luminanceAtZenith = this._tileset.luminanceAtZenith;
this._model.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients;
this._model.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps;
+ this._model.backFaceCulling = this._tileset.backFaceCulling;
this._model.debugWireframe = this._tileset.debugWireframe;
// Update clipping planes
diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js
index d5927f7bf6d9..820b0e29496e 100644
--- a/Source/Scene/Camera.js
+++ b/Source/Scene/Camera.js
@@ -1112,78 +1112,68 @@ Camera.prototype._setTransform = function (transform) {
updateMembers(this);
};
-var scratchAdjustOrtghographicFrustumMousePosition = new Cartesian2();
-var pickGlobeScratchRay = new Ray();
+var scratchAdjustOrthographicFrustumMousePosition = new Cartesian2();
+var scratchPickRay = new Ray();
var scratchRayIntersection = new Cartesian3();
var scratchDepthIntersection = new Cartesian3();
-Camera.prototype._adjustOrthographicFrustum = function (zooming) {
- if (!(this.frustum instanceof OrthographicFrustum)) {
- return;
+function calculateOrthographicFrustumWidth(camera) {
+ // Camera is fixed to an object, so keep frustum width constant.
+ if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) {
+ return Cartesian3.magnitude(camera.position);
}
- if (!zooming && this._positionCartographic.height < 150000.0) {
- return;
- }
+ var scene = camera._scene;
+ var globe = scene.globe;
- if (!Matrix4.equals(Matrix4.IDENTITY, this.transform)) {
- this.frustum.width = Cartesian3.magnitude(this.position);
- return;
- }
+ var mousePosition = scratchAdjustOrthographicFrustumMousePosition;
+ mousePosition.x = scene.drawingBufferWidth / 2.0;
+ mousePosition.y = scene.drawingBufferHeight / 2.0;
- var scene = this._scene;
- var globe = scene.globe;
var rayIntersection;
- var depthIntersection;
-
if (defined(globe)) {
- var mousePosition = scratchAdjustOrtghographicFrustumMousePosition;
- mousePosition.x = scene.drawingBufferWidth / 2.0;
- mousePosition.y = scene.drawingBufferHeight / 2.0;
-
- var ray = this.getPickRay(mousePosition, pickGlobeScratchRay);
+ var ray = camera.getPickRay(mousePosition, scratchPickRay);
rayIntersection = globe.pickWorldCoordinates(
ray,
scene,
true,
scratchRayIntersection
);
+ }
- if (scene.pickPositionSupported) {
- depthIntersection = scene.pickPositionWorldCoordinates(
- mousePosition,
- scratchDepthIntersection
- );
- }
+ var depthIntersection;
+ if (scene.pickPositionSupported) {
+ depthIntersection = scene.pickPositionWorldCoordinates(
+ mousePosition,
+ scratchDepthIntersection
+ );
+ }
- if (defined(rayIntersection) && defined(depthIntersection)) {
- var depthDistance = defined(depthIntersection)
- ? Cartesian3.distance(depthIntersection, this.positionWC)
- : Number.POSITIVE_INFINITY;
- var rayDistance = defined(rayIntersection)
- ? Cartesian3.distance(rayIntersection, this.positionWC)
- : Number.POSITIVE_INFINITY;
- this.frustum.width = Math.min(depthDistance, rayDistance);
- } else if (defined(depthIntersection)) {
- this.frustum.width = Cartesian3.distance(
- depthIntersection,
- this.positionWC
- );
- } else if (defined(rayIntersection)) {
- this.frustum.width = Cartesian3.distance(
- rayIntersection,
- this.positionWC
- );
- }
+ var distance;
+ if (defined(rayIntersection) || defined(depthIntersection)) {
+ var depthDistance = defined(depthIntersection)
+ ? Cartesian3.distance(depthIntersection, camera.positionWC)
+ : Number.POSITIVE_INFINITY;
+ var rayDistance = defined(rayIntersection)
+ ? Cartesian3.distance(rayIntersection, camera.positionWC)
+ : Number.POSITIVE_INFINITY;
+ distance = Math.min(depthDistance, rayDistance);
+ } else {
+ distance = Math.max(camera.positionCartographic.height, 0.0);
}
+ return distance;
+}
- if (
- !defined(globe) ||
- (!defined(rayIntersection) && !defined(depthIntersection))
- ) {
- var distance = Math.max(this.positionCartographic.height, 0.0);
- this.frustum.width = distance;
+Camera.prototype._adjustOrthographicFrustum = function (zooming) {
+ if (!(this.frustum instanceof OrthographicFrustum)) {
+ return;
}
+
+ if (!zooming && this._positionCartographic.height < 150000.0) {
+ return;
+ }
+
+ this.frustum.width = calculateOrthographicFrustumWidth(this);
};
var scratchSetViewCartesian = new Cartesian3();
@@ -3850,19 +3840,15 @@ Camera.prototype.switchToOrthographicFrustum = function () {
return;
}
+ // This must be called before changing the frustum because it uses the previous
+ // frustum to reconstruct the world space position from the depth buffer.
+ var frustumWidth = calculateOrthographicFrustumWidth(this);
+
var scene = this._scene;
this.frustum = new OrthographicFrustum();
this.frustum.aspectRatio =
scene.drawingBufferWidth / scene.drawingBufferHeight;
-
- // It doesn't matter what we set this to. The adjust below will correct the width based on the camera position.
- this.frustum.width = Cartesian3.magnitude(this.position);
-
- // Check the projection matrix. It will always be defined, but we need to force an off-center update.
- var projectionMatrix = this.frustum.projectionMatrix;
- if (defined(projectionMatrix)) {
- this._adjustOrthographicFrustum(true);
- }
+ this.frustum.width = frustumWidth;
};
/**
diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js
index 163790562211..b03ca969c1fd 100644
--- a/Source/Scene/Cesium3DTileset.js
+++ b/Source/Scene/Cesium3DTileset.js
@@ -89,6 +89,7 @@ import TileOrientedBoundingBox from "./TileOrientedBoundingBox.js";
* @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
* @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
* @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
+ * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the glTF material's doubleSided property; when false, back face culling is disabled.
* @param {String} [options.debugHeatmapTilePropertyName] The tile variable to colorize as a heatmap. All rendered tiles will be colorized relative to each other's specified variable value.
* @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering.
* @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile.
@@ -750,6 +751,15 @@ function Cesium3DTileset(options) {
*/
this.specularEnvironmentMaps = options.specularEnvironmentMaps;
+ /**
+ * Whether to cull back-facing geometry. When true, back face culling is determined
+ * by the glTF material's doubleSided property; when false, back face culling is disabled.
+ *
+ * @type {Boolean}
+ * @default true
+ */
+ this.backFaceCulling = defaultValue(options.backFaceCulling, true);
+
/**
* This property is for debugging only; it is not optimized for production use.
*
diff --git a/Source/Scene/DerivedCommand.js b/Source/Scene/DerivedCommand.js
index 42d06e6fe74d..707b745f6220 100644
--- a/Source/Scene/DerivedCommand.js
+++ b/Source/Scene/DerivedCommand.js
@@ -189,14 +189,22 @@ function getLogDepthShaderProgram(context, shaderProgram) {
sources.push(logMain);
}
- var addExtension = true;
- writesLogDepth = false;
sources = fs.sources;
length = sources.length;
+
+ writesLogDepth = false;
for (i = 0; i < length; ++i) {
if (writeLogDepthRegex.test(sources[i])) {
writesLogDepth = true;
}
+ }
+ // This define indicates that a log depth value is written by the shader but doesn't use czm_writeLogDepth.
+ if (fs.defines.indexOf("LOG_DEPTH_WRITE") !== -1) {
+ writesLogDepth = true;
+ }
+
+ var addExtension = true;
+ for (i = 0; i < length; ++i) {
if (extensionRegex.test(sources[i])) {
addExtension = false;
}
diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js
index 2031bca87669..bc4cf26acdd5 100644
--- a/Source/Scene/Instanced3DModel3DTileContent.js
+++ b/Source/Scene/Instanced3DModel3DTileContent.js
@@ -304,6 +304,7 @@ function initialize(content, arrayBuffer, byteOffset) {
luminanceAtZenith: tileset.luminanceAtZenith,
sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients,
specularEnvironmentMaps: tileset.specularEnvironmentMaps,
+ backFaceCulling: tileset.backFaceCulling,
};
if (gltfFormat === 0) {
@@ -615,6 +616,7 @@ Instanced3DModel3DTileContent.prototype.update = function (
this._modelInstanceCollection.luminanceAtZenith = this._tileset.luminanceAtZenith;
this._modelInstanceCollection.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients;
this._modelInstanceCollection.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps;
+ this._modelInstanceCollection.backFaceCulling = this._tileset.backFaceCulling;
this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe;
var model = this._modelInstanceCollection._model;
diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js
index b3a2f3772ff5..b89fd91a944a 100644
--- a/Source/Scene/Model.js
+++ b/Source/Scene/Model.js
@@ -217,6 +217,7 @@ var uriToGuid = {};
* @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
* @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
* @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
+ * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0.
*
* @see Model.fromGltf
*
@@ -493,6 +494,18 @@ function Model(options) {
// to the root tile.
this.clippingPlanesOriginMatrix = undefined;
+ /**
+ * Whether to cull back-facing geometry. When true, back face culling is
+ * determined by the material's doubleSided property; when false, back face
+ * culling is disabled. Back faces are not culled if {@link Model#color} is
+ * translucent or {@link Model#silhouetteSize} is greater than 0.0.
+ *
+ * @type {Boolean}
+ *
+ * @default true
+ */
+ this.backFaceCulling = defaultValue(options.backFaceCulling, true);
+
/**
* This property is for debugging only; it is not for production use nor is it optimized.
*
@@ -1371,6 +1384,7 @@ function containsGltfMagic(uint8Array) {
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
* @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models.
* @param {Credit|String} [options.credit] A credit for the model, which is displayed on the canvas.
+ * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0.
*
* @returns {Model} The newly created model.
*
@@ -3947,6 +3961,9 @@ function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) {
// Generated on demand when color alpha is less than 1.0
translucentCommand: undefined,
translucentCommand2D: undefined,
+ // Generated on demand when back face culling is false
+ disableCullingCommand: undefined,
+ disableCullingCommand2D: undefined,
// For updating node commands on shader reconstruction
programId: programId,
};
@@ -4516,6 +4533,44 @@ function updateColor(model, frameState, forceDerive) {
}
}
+function getDisableCullingRenderState(renderState) {
+ var rs = clone(renderState, true);
+ rs.cull.enabled = false;
+ return RenderState.fromCache(rs);
+}
+
+function deriveDisableCullingCommand(command) {
+ var disableCullingCommand = DrawCommand.shallowClone(command);
+ disableCullingCommand.renderState = getDisableCullingRenderState(
+ command.renderState
+ );
+ return disableCullingCommand;
+}
+
+function updateBackFaceCulling(model, frameState, forceDerive) {
+ var scene3DOnly = frameState.scene3DOnly;
+ var backFaceCulling = model.backFaceCulling;
+ if (!backFaceCulling) {
+ var nodeCommands = model._nodeCommands;
+ var length = nodeCommands.length;
+ if (!defined(nodeCommands[0].disableCullingCommand) || forceDerive) {
+ for (var i = 0; i < length; ++i) {
+ var nodeCommand = nodeCommands[i];
+ var command = nodeCommand.command;
+ nodeCommand.disableCullingCommand = deriveDisableCullingCommand(
+ command
+ );
+ if (!scene3DOnly) {
+ var command2D = nodeCommand.command2D;
+ nodeCommand.disableCullingCommand2D = deriveDisableCullingCommand(
+ command2D
+ );
+ }
+ }
+ }
+ }
+}
+
function getProgramId(model, program) {
var programs = model._rendererResources.programs;
for (var id in programs) {
@@ -5347,6 +5402,7 @@ Model.prototype.update = function (frameState) {
var silhouette = hasSilhouette(this, frameState);
var translucent = isTranslucent(this);
var invisible = isInvisible(this);
+ var backFaceCulling = this.backFaceCulling;
var displayConditionPassed = defined(this.distanceDisplayCondition)
? distanceDisplayConditionVisible(this, frameState)
: true;
@@ -5491,6 +5547,7 @@ Model.prototype.update = function (frameState) {
regenerateShaders(this, frameState);
} else {
updateColor(this, frameState, false);
+ updateBackFaceCulling(this, frameState, false);
updateSilhouette(this, frameState, false);
}
}
@@ -5525,8 +5582,14 @@ Model.prototype.update = function (frameState) {
for (i = 0; i < length; ++i) {
nc = nodeCommands[i];
if (nc.show) {
- var command = translucent ? nc.translucentCommand : nc.command;
- command = silhouette ? nc.silhouetteModelCommand : command;
+ var command = nc.command;
+ if (silhouette) {
+ command = nc.silhouetteModelCommand;
+ } else if (translucent) {
+ command = nc.translucentCommand;
+ } else if (!backFaceCulling) {
+ command = nc.disableCullingCommand;
+ }
commandList.push(command);
boundingVolume = nc.command.boundingVolume;
if (
@@ -5534,10 +5597,14 @@ Model.prototype.update = function (frameState) {
(boundingVolume.center.y + boundingVolume.radius > idl2D ||
boundingVolume.center.y - boundingVolume.radius < idl2D)
) {
- var command2D = translucent
- ? nc.translucentCommand2D
- : nc.command2D;
- command2D = silhouette ? nc.silhouetteModelCommand2D : command2D;
+ var command2D = nc.command2D;
+ if (silhouette) {
+ command2D = nc.silhouetteModelCommand2D;
+ } else if (translucent) {
+ command2D = nc.translucentCommand2D;
+ } else if (!backFaceCulling) {
+ command2D = nc.disableCullingCommand2D;
+ }
commandList.push(command2D);
}
}
@@ -5664,6 +5731,7 @@ function regenerateShaders(model, frameState) {
// Force update silhouette commands/shaders
updateColor(model, frameState, true);
+ updateBackFaceCulling(model, frameState, true);
updateSilhouette(model, frameState, true);
}
diff --git a/Source/Scene/ModelInstanceCollection.js b/Source/Scene/ModelInstanceCollection.js
index 120f77cb4fed..a94956523945 100644
--- a/Source/Scene/ModelInstanceCollection.js
+++ b/Source/Scene/ModelInstanceCollection.js
@@ -18,6 +18,7 @@ import Buffer from "../Renderer/Buffer.js";
import BufferUsage from "../Renderer/BufferUsage.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import Pass from "../Renderer/Pass.js";
+import RenderState from "../Renderer/RenderState.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import ForEach from "../ThirdParty/GltfPipeline/ForEach.js";
import when from "../ThirdParty/when.js";
@@ -62,6 +63,7 @@ var LoadState = {
* @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
* @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
* @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
+ * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the glTF material's doubleSided property; when false, back face culling is disabled.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for the collection.
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the instances in wireframe.
*
@@ -114,6 +116,9 @@ function ModelInstanceCollection(options) {
this._drawCommands = [];
this._modelCommands = undefined;
+ this._renderStates = undefined;
+ this._disableCullingRenderStates = undefined;
+
this._boundingSphere = createBoundingSphere(this);
this._center = Cartesian3.clone(this._boundingSphere.center);
this._rtcTransform = new Matrix4();
@@ -157,6 +162,8 @@ function ModelInstanceCollection(options) {
this.luminanceAtZenith = options.luminanceAtZenith;
this.sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients;
this.specularEnvironmentMaps = options.specularEnvironmentMaps;
+ this.backFaceCulling = defaultValue(options.backFaceCulling, true);
+ this._backFaceCulling = this.backFaceCulling;
}
Object.defineProperties(ModelInstanceCollection.prototype, {
@@ -787,8 +794,8 @@ function createModel(collection, context) {
}
}
-function updateWireframe(collection) {
- if (collection._debugWireframe !== collection.debugWireframe) {
+function updateWireframe(collection, force) {
+ if (collection._debugWireframe !== collection.debugWireframe || force) {
collection._debugWireframe = collection.debugWireframe;
// This assumes the original primitive was TRIANGLES and that the triangles
@@ -803,9 +810,45 @@ function updateWireframe(collection) {
}
}
}
-function updateShowBoundingVolume(collection) {
+
+function getDisableCullingRenderState(renderState) {
+ var rs = clone(renderState, true);
+ rs.cull.enabled = false;
+ return RenderState.fromCache(rs);
+}
+
+function updateBackFaceCulling(collection, force) {
+ if (collection._backFaceCulling !== collection.backFaceCulling || force) {
+ collection._backFaceCulling = collection.backFaceCulling;
+
+ var commands = collection._drawCommands;
+ var length = commands.length;
+ var i;
+
+ if (!defined(collection._disableCullingRenderStates)) {
+ collection._disableCullingRenderStates = new Array(length);
+ collection._renderStates = new Array(length);
+ for (i = 0; i < length; ++i) {
+ var renderState = commands[i].renderState;
+ var derivedRenderState = getDisableCullingRenderState(renderState);
+ collection._disableCullingRenderStates[i] = derivedRenderState;
+ collection._renderStates[i] = renderState;
+ }
+ }
+
+ for (i = 0; i < length; ++i) {
+ commands[i].renderState = collection._backFaceCulling
+ ? collection._renderStates[i]
+ : collection._disableCullingRenderStates[i];
+ }
+ }
+}
+
+function updateShowBoundingVolume(collection, force) {
if (
- collection.debugShowBoundingVolume !== collection._debugShowBoundingVolume
+ collection.debugShowBoundingVolume !==
+ collection._debugShowBoundingVolume ||
+ force
) {
collection._debugShowBoundingVolume = collection.debugShowBoundingVolume;
@@ -939,13 +982,16 @@ function commandsDirty(model) {
var nodeCommands = model._nodeCommands;
var length = nodeCommands.length;
+ var commandsDirty = false;
+
for (var i = 0; i < length; i++) {
var nc = nodeCommands[i];
if (nc.command.dirty) {
- return true;
+ nc.command.dirty = false;
+ commandsDirty = true;
}
}
- return false;
+ return commandsDirty;
}
function generateModelCommands(modelInstanceCollection, instancingSupported) {
@@ -960,8 +1006,8 @@ function generateModelCommands(modelInstanceCollection, instancingSupported) {
}
}
-function updateShadows(collection) {
- if (collection.shadows !== collection._shadows) {
+function updateShadows(collection, force) {
+ if (collection.shadows !== collection._shadows || force) {
collection._shadows = collection.shadows;
var castShadows = ShadowMode.castShadows(collection.shadows);
@@ -1067,7 +1113,8 @@ ModelInstanceCollection.prototype.update = function (frameState) {
}
// If the model was set to rebuild shaders during update, rebuild instanced commands.
- if (commandsDirty(model)) {
+ var modelCommandsDirty = commandsDirty(model);
+ if (modelCommandsDirty) {
generateModelCommands(this, instancingSupported);
}
@@ -1081,9 +1128,10 @@ ModelInstanceCollection.prototype.update = function (frameState) {
updateCommandsNonInstanced(this);
}
- updateShadows(this);
- updateWireframe(this);
- updateShowBoundingVolume(this);
+ updateShadows(this, modelCommandsDirty);
+ updateWireframe(this, modelCommandsDirty);
+ updateBackFaceCulling(this, modelCommandsDirty);
+ updateShowBoundingVolume(this, modelCommandsDirty);
var passes = frameState.passes;
if (!passes.render && !passes.pick) {
diff --git a/Source/Scene/PickDepth.js b/Source/Scene/PickDepth.js
index 79ca59bbc8ad..b9256c278561 100644
--- a/Source/Scene/PickDepth.js
+++ b/Source/Scene/PickDepth.js
@@ -159,6 +159,11 @@ var packedDepthScale = new Cartesian4(
);
PickDepth.prototype.getDepth = function (context, x, y) {
+ // If this function is called before the framebuffer is created, the depth is undefined.
+ if (!defined(this._framebuffer)) {
+ return undefined;
+ }
+
var pixels = context.readPixels({
x: x,
y: y,
@@ -184,9 +189,11 @@ PickDepth.prototype.destroy = function () {
destroyTextures(this);
destroyFramebuffers(this);
- this._copyDepthCommand.shaderProgram =
- defined(this._copyDepthCommand.shaderProgram) &&
- this._copyDepthCommand.shaderProgram.destroy();
+ if (defined(this._copyDepthCommand)) {
+ this._copyDepthCommand.shaderProgram =
+ defined(this._copyDepthCommand.shaderProgram) &&
+ this._copyDepthCommand.shaderProgram.destroy();
+ }
return destroyObject(this);
};
diff --git a/Source/Scene/Picking.js b/Source/Scene/Picking.js
index fa6b17c46aa9..f4826b05c310 100644
--- a/Source/Scene/Picking.js
+++ b/Source/Scene/Picking.js
@@ -425,6 +425,9 @@ Picking.prototype.pickPositionWorldCoordinates = function (
drawingBufferPosition.x,
drawingBufferPosition.y
);
+ if (!defined(depth)) {
+ continue;
+ }
if (depth > 0.0 && depth < 1.0) {
var renderedFrustum = frustumCommandsList[i];
var height2D;
@@ -772,6 +775,9 @@ function getRayIntersection(
for (var i = 0; i < numFrustums; ++i) {
var pickDepth = picking.getPickDepth(scene, i);
var depth = pickDepth.getDepth(context, 0, 0);
+ if (!defined(depth)) {
+ continue;
+ }
if (depth > 0.0 && depth < 1.0) {
var renderedFrustum = view.frustumCommandsList[i];
var near =
diff --git a/Source/Scene/PointCloudEyeDomeLighting.js b/Source/Scene/PointCloudEyeDomeLighting.js
index 96351a1613df..14dac107f8e4 100644
--- a/Source/Scene/PointCloudEyeDomeLighting.js
+++ b/Source/Scene/PointCloudEyeDomeLighting.js
@@ -99,7 +99,10 @@ function createFramebuffer(processor, context) {
var distanceAndEdlStrengthScratch = new Cartesian2();
function createCommands(processor, context) {
- var blendFS = PointCloudEyeDomeLightingShader;
+ var blendFS = new ShaderSource({
+ defines: ["LOG_DEPTH_WRITE"],
+ sources: [PointCloudEyeDomeLightingShader],
+ });
var blendUniformMap = {
u_pointCloud_colorGBuffer: function () {
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index 4ff0eccf2203..ab461a975579 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -1555,7 +1555,6 @@ Object.defineProperties(Scene.prototype, {
if (this._logDepthBuffer !== value) {
this._logDepthBuffer = value;
this._logDepthBufferDirty = true;
- this._defaultView.updateFrustums = true;
}
},
},
@@ -4372,7 +4371,7 @@ Scene.prototype.clampToHeight = function (
* @param {Cartographic[]} positions The cartographic positions to update with sampled heights.
* @param {Object[]} [objectsToExclude] A list of primitives, entities, or 3D Tiles features to not sample height from.
* @param {Number} [width=0.1] Width of the intersection volume in meters.
- * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed.
+ * @returns {Promise.} A promise that resolves to the provided list of positions when the query has completed.
*
* @example
* var positions = [
diff --git a/Source/Scene/View.js b/Source/Scene/View.js
index b0c89cda68d2..28e38a188e61 100644
--- a/Source/Scene/View.js
+++ b/Source/Scene/View.js
@@ -20,33 +20,18 @@ import SceneFramebuffer from "./SceneFramebuffer.js";
import SceneMode from "./SceneMode.js";
import ShadowMap from "./ShadowMap.js";
+function CommandExtent() {
+ this.command = undefined;
+ this.near = undefined;
+ this.far = undefined;
+}
+
/**
* @private
*/
function View(scene, camera, viewport) {
var context = scene.context;
- var frustumCommandsList = [];
-
- // Initial guess at frustums.
- var near = camera.frustum.near;
- var far = camera.frustum.far;
- var farToNearRatio = scene.logarithmicDepthBuffer
- ? scene.logarithmicDepthFarToNearRatio
- : scene.farToNearRatio;
-
- var numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));
- updateFrustums(
- near,
- far,
- farToNearRatio,
- numFrustums,
- scene.logarithmicDepthBuffer,
- frustumCommandsList,
- false,
- undefined
- );
-
var globeDepth;
if (context.depthTexture) {
globeDepth = new GlobeDepth();
@@ -75,9 +60,12 @@ function View(scene, camera, viewport) {
this.oit = oit;
this.pickDepths = [];
this.debugGlobeDepths = [];
- this.frustumCommandsList = frustumCommandsList;
+ this.frustumCommandsList = [];
this.debugFrustumStatistics = undefined;
- this.updateFrustums = false;
+
+ // Array of all commands that get rendered into frustums along with their near / far values.
+ // Acts similar to a ManagedArray.
+ this._commandExtents = [];
}
var scratchPosition0 = new Cartesian3();
@@ -129,16 +117,37 @@ View.prototype.checkForCameraUpdates = function (scene) {
return false;
};
-function updateFrustums(
- near,
- far,
- farToNearRatio,
- numFrustums,
- logDepth,
- frustumCommandsList,
- is2D,
- nearToFarDistance2D
-) {
+function updateFrustums(view, scene, near, far) {
+ var frameState = scene.frameState;
+ var camera = frameState.camera;
+ var farToNearRatio = frameState.useLogDepth
+ ? scene.logarithmicDepthFarToNearRatio
+ : scene.farToNearRatio;
+ var is2D = scene.mode === SceneMode.SCENE2D;
+ var nearToFarDistance2D = scene.nearToFarDistance2D;
+
+ // The computed near plane must be between the user defined near and far planes.
+ // The computed far plane must between the user defined far and computed near.
+ // This will handle the case where the computed near plane is further than the user defined far plane.
+ near = Math.min(Math.max(near, camera.frustum.near), camera.frustum.far);
+ far = Math.max(Math.min(far, camera.frustum.far), near);
+
+ var numFrustums;
+ if (is2D) {
+ // The multifrustum for 2D is uniformly distributed. To avoid z-fighting in 2D,
+ // the camera is moved to just before the frustum and the frustum depth is scaled
+ // to be in [1.0, nearToFarDistance2D].
+ far = Math.min(far, camera.position.z + scene.nearToFarDistance2D);
+ near = Math.min(near, far);
+ numFrustums = Math.ceil(
+ Math.max(1.0, far - near) / scene.nearToFarDistance2D
+ );
+ } else {
+ // The multifrustum for 3D/CV is non-uniformly distributed.
+ numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));
+ }
+
+ var frustumCommandsList = view.frustumCommandsList;
frustumCommandsList.length = numFrustums;
for (var m = 0; m < numFrustums; ++m) {
var curNear;
@@ -152,12 +161,8 @@ function updateFrustums(
curFar = Math.min(far, curNear + nearToFarDistance2D);
} else {
curNear = Math.max(near, Math.pow(farToNearRatio, m) * near);
- curFar = farToNearRatio * curNear;
- if (!logDepth) {
- curFar = Math.min(far, curFar);
- }
+ curFar = Math.min(far, farToNearRatio * curNear);
}
-
var frustumCommands = frustumCommandsList[m];
if (!defined(frustumCommands)) {
frustumCommands = frustumCommandsList[m] = new FrustumCommands(
@@ -171,7 +176,7 @@ function updateFrustums(
}
}
-function insertIntoBin(scene, view, command, distance) {
+function insertIntoBin(view, scene, command, commandNear, commandFar) {
if (scene.debugShowFrustums) {
command.debugOverlappingFrustums = 0;
}
@@ -184,11 +189,11 @@ function insertIntoBin(scene, view, command, distance) {
var curNear = frustumCommands.near;
var curFar = frustumCommands.far;
- if (distance.start > curFar) {
+ if (commandNear > curFar) {
continue;
}
- if (distance.stop < curNear) {
+ if (commandFar < curNear) {
break;
}
@@ -219,7 +224,7 @@ function insertIntoBin(scene, view, command, distance) {
}
var scratchCullingVolume = new CullingVolume();
-var distances = new Interval();
+var scratchNearFarInterval = new Interval();
View.prototype.createPotentiallyVisibleSet = function (scene) {
var frameState = scene.frameState;
@@ -250,12 +255,15 @@ View.prototype.createPotentiallyVisibleSet = function (scene) {
computeList.length = 0;
overlayList.length = 0;
- var near = Number.MAX_VALUE;
+ var commandExtents = this._commandExtents;
+ var commandExtentCapacity = commandExtents.length;
+ var commandExtentCount = 0;
+
+ var near = +Number.MAX_VALUE;
var far = -Number.MAX_VALUE;
- var undefBV = false;
var shadowsEnabled = frameState.shadowState.shadowsEnabled;
- var shadowNear = Number.MAX_VALUE;
+ var shadowNear = +Number.MAX_VALUE;
var shadowFar = -Number.MAX_VALUE;
var shadowClosestObjectSize = Number.MAX_VALUE;
@@ -280,19 +288,24 @@ View.prototype.createPotentiallyVisibleSet = function (scene) {
} else if (pass === Pass.OVERLAY) {
overlayList.push(command);
} else {
+ var commandNear;
+ var commandFar;
+
var boundingVolume = command.boundingVolume;
if (defined(boundingVolume)) {
if (!scene.isVisible(command, cullingVolume, occluder)) {
continue;
}
- distances = boundingVolume.computePlaneDistances(
+ var nearFarInterval = boundingVolume.computePlaneDistances(
position,
direction,
- distances
+ scratchNearFarInterval
);
- near = Math.min(near, distances.start);
- far = Math.max(far, distances.stop);
+ commandNear = nearFarInterval.start;
+ commandFar = nearFarInterval.stop;
+ near = Math.min(near, commandNear);
+ far = Math.max(far, commandFar);
// Compute a tight near and far plane for commands that receive shadows. This helps compute
// good splits for cascaded shadow maps. Ignore commands that exceed the maximum distance.
@@ -302,51 +315,47 @@ View.prototype.createPotentiallyVisibleSet = function (scene) {
if (
shadowsEnabled &&
command.receiveShadows &&
- distances.start < ShadowMap.MAXIMUM_DISTANCE &&
- !(
- pass === Pass.GLOBE &&
- distances.start < -100.0 &&
- distances.stop > 100.0
- )
+ commandNear < ShadowMap.MAXIMUM_DISTANCE &&
+ !(pass === Pass.GLOBE && commandNear < -100.0 && commandFar > 100.0)
) {
// Get the smallest bounding volume the camera is near. This is used to place more shadow detail near the object.
- var size = distances.stop - distances.start;
- if (pass !== Pass.GLOBE && distances.start < 100.0) {
+ var size = commandFar - commandNear;
+ if (pass !== Pass.GLOBE && commandNear < 100.0) {
shadowClosestObjectSize = Math.min(shadowClosestObjectSize, size);
}
- shadowNear = Math.min(shadowNear, distances.start);
- shadowFar = Math.max(shadowFar, distances.stop);
+ shadowNear = Math.min(shadowNear, commandNear);
+ shadowFar = Math.max(shadowFar, commandFar);
}
- } else {
+ } else if (command instanceof ClearCommand) {
// Clear commands don't need a bounding volume - just add the clear to all frustums.
- // If another command has no bounding volume, though, we need to use the camera's
+ commandNear = camera.frustum.near;
+ commandFar = camera.frustum.far;
+ } else {
+ // If command has no bounding volume we need to use the camera's
// worst-case near and far planes to avoid clipping something important.
- distances.start = camera.frustum.near;
- distances.stop = camera.frustum.far;
- undefBV = undefBV || !(command instanceof ClearCommand);
+ commandNear = camera.frustum.near;
+ commandFar = camera.frustum.far;
+ near = Math.min(near, commandNear);
+ far = Math.max(far, commandFar);
}
- insertIntoBin(scene, this, command, distances);
+ var extent = commandExtents[commandExtentCount];
+ if (!defined(extent)) {
+ extent = commandExtents[commandExtentCount] = new CommandExtent();
+ }
+ extent.command = command;
+ extent.near = commandNear;
+ extent.far = commandFar;
+ commandExtentCount++;
}
}
- if (undefBV) {
- near = camera.frustum.near;
- far = camera.frustum.far;
- } else {
- // The computed near plane must be between the user defined near and far planes.
- // The computed far plane must between the user defined far and computed near.
- // This will handle the case where the computed near plane is further than the user defined far plane.
- near = Math.min(Math.max(near, camera.frustum.near), camera.frustum.far);
- far = Math.max(Math.min(far, camera.frustum.far), near);
-
- if (shadowsEnabled) {
- shadowNear = Math.min(
- Math.max(shadowNear, camera.frustum.near),
- camera.frustum.far
- );
- shadowFar = Math.max(Math.min(shadowFar, camera.frustum.far), shadowNear);
- }
+ if (shadowsEnabled) {
+ shadowNear = Math.min(
+ Math.max(shadowNear, camera.frustum.near),
+ camera.frustum.far
+ );
+ shadowFar = Math.max(Math.min(shadowFar, camera.frustum.far), shadowNear);
}
// Use the computed near and far for shadows
@@ -356,57 +365,31 @@ View.prototype.createPotentiallyVisibleSet = function (scene) {
frameState.shadowState.closestObjectSize = shadowClosestObjectSize;
}
- // Exploit temporal coherence. If the frustums haven't changed much, use the frustums computed
- // last frame, else compute the new frustums and sort them by frustum again.
- var is2D = scene.mode === SceneMode.SCENE2D;
- var logDepth = frameState.useLogDepth;
- var farToNearRatio = logDepth
- ? scene.logarithmicDepthFarToNearRatio
- : scene.farToNearRatio;
- var numFrustums;
+ updateFrustums(this, scene, near, far);
- if (is2D) {
- // The multifrustum for 2D is uniformly distributed. To avoid z-fighting in 2D,
- // the camera is moved to just before the frustum and the frustum depth is scaled
- // to be in [1.0, nearToFarDistance2D].
- far = Math.min(far, camera.position.z + scene.nearToFarDistance2D);
- near = Math.min(near, far);
- numFrustums = Math.ceil(
- Math.max(1.0, far - near) / scene.nearToFarDistance2D
- );
- } else {
- // The multifrustum for 3D/CV is non-uniformly distributed.
- numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));
+ var c;
+ var ce;
+
+ for (c = 0; c < commandExtentCount; c++) {
+ ce = commandExtents[c];
+ insertIntoBin(this, scene, ce.command, ce.near, ce.far);
}
- if (
- this.updateFrustums ||
- (near !== Number.MAX_VALUE &&
- (numFrustums !== numberOfFrustums ||
- (frustumCommandsList.length !== 0 &&
- (near < frustumCommandsList[0].near ||
- (far > frustumCommandsList[numberOfFrustums - 1].far &&
- (logDepth ||
- !CesiumMath.equalsEpsilon(
- far,
- frustumCommandsList[numberOfFrustums - 1].far,
- CesiumMath.EPSILON8
- )))))))
- ) {
- this.updateFrustums = false;
- updateFrustums(
- near,
- far,
- farToNearRatio,
- numFrustums,
- logDepth,
- frustumCommandsList,
- is2D,
- scene.nearToFarDistance2D
- );
- this.createPotentiallyVisibleSet(scene);
+ // Dereference old commands
+ if (commandExtentCount < commandExtentCapacity) {
+ for (c = commandExtentCount; c < commandExtentCapacity; c++) {
+ ce = commandExtents[c];
+ if (!defined(ce.command)) {
+ // If the command is undefined, it's assumed that all
+ // subsequent commmands were set to undefined as well,
+ // so no need to loop over them all
+ break;
+ }
+ ce.command = undefined;
+ }
}
+ var numFrustums = frustumCommandsList.length;
var frustumSplits = frameState.frustumSplits;
frustumSplits.length = numFrustums + 1;
for (var j = 0; j < numFrustums; ++j) {
diff --git a/Source/Shaders/PostProcessStages/PointCloudEyeDomeLighting.glsl b/Source/Shaders/PostProcessStages/PointCloudEyeDomeLighting.glsl
index 3f6447a7a5bf..c82cad4ff47f 100644
--- a/Source/Shaders/PostProcessStages/PointCloudEyeDomeLighting.glsl
+++ b/Source/Shaders/PostProcessStages/PointCloudEyeDomeLighting.glsl
@@ -58,9 +58,6 @@ void main()
color.rgb *= shade;
gl_FragColor = vec4(color);
-#ifdef LOG_DEPTH
- czm_writeLogDepth(1.0 + (czm_projection * vec4(eyeCoordinate.xyz, 1.0)).w);
-#else
- gl_FragDepthEXT = czm_eyeToWindowCoordinates(vec4(eyeCoordinate.xyz, 1.0)).z;
-#endif
+ // Input and output depth are the same.
+ gl_FragDepthEXT = depthOrLogDepth;
}
diff --git a/Source/Widgets/Animation/Animation.js b/Source/Widgets/Animation/Animation.js
index f22b65ea67dd..cb5b5fcb3f61 100644
--- a/Source/Widgets/Animation/Animation.js
+++ b/Source/Widgets/Animation/Animation.js
@@ -29,6 +29,56 @@ function getElementColor(element) {
);
}
+var svgIconsById = {
+ animation_pathReset: {
+ tagName: "path",
+ transform: "translate(16,16) scale(0.85) translate(-16,-16)",
+ d:
+ "M24.316,5.318,9.833,13.682,9.833,5.5,5.5,5.5,5.5,25.5,9.833,25.5,9.833,17.318,24.316,25.682z",
+ },
+ animation_pathPause: {
+ tagName: "path",
+ transform: "translate(16,16) scale(0.85) translate(-16,-16)",
+ d: "M13,5.5,7.5,5.5,7.5,25.5,13,25.5zM24.5,5.5,19,5.5,19,25.5,24.5,25.5z",
+ },
+ animation_pathPlay: {
+ tagName: "path",
+ transform: "translate(16,16) scale(0.85) translate(-16,-16)",
+ d: "M6.684,25.682L24.316,15.5L6.684,5.318V25.682z",
+ },
+ animation_pathPlayReverse: {
+ tagName: "path",
+ transform: "translate(16,16) scale(-0.85,0.85) translate(-16,-16)",
+ d: "M6.684,25.682L24.316,15.5L6.684,5.318V25.682z",
+ },
+ animation_pathLoop: {
+ tagName: "path",
+ transform: "translate(16,16) scale(0.85) translate(-16,-16)",
+ d:
+ "M24.249,15.499c-0.009,4.832-3.918,8.741-8.75,8.75c-2.515,0-4.768-1.064-6.365-2.763l2.068-1.442l-7.901-3.703l0.744,8.694l2.193-1.529c2.244,2.594,5.562,4.242,9.26,4.242c6.767,0,12.249-5.482,12.249-12.249H24.249zM15.499,6.75c2.516,0,4.769,1.065,6.367,2.764l-2.068,1.443l7.901,3.701l-0.746-8.693l-2.192,1.529c-2.245-2.594-5.562-4.245-9.262-4.245C8.734,3.25,3.25,8.734,3.249,15.499H6.75C6.758,10.668,10.668,6.758,15.499,6.75z",
+ },
+ animation_pathClock: {
+ tagName: "path",
+ transform: "translate(16,16) scale(0.85) translate(-16,-15.5)",
+ d:
+ "M15.5,2.374C8.251,2.375,2.376,8.251,2.374,15.5C2.376,22.748,8.251,28.623,15.5,28.627c7.249-0.004,13.124-5.879,13.125-13.127C28.624,8.251,22.749,2.375,15.5,2.374zM15.5,25.623C9.909,25.615,5.385,21.09,5.375,15.5C5.385,9.909,9.909,5.384,15.5,5.374c5.59,0.01,10.115,4.535,10.124,10.125C25.615,21.09,21.091,25.615,15.5,25.623zM8.625,15.5c-0.001-0.552-0.448-0.999-1.001-1c-0.553,0-1,0.448-1,1c0,0.553,0.449,1,1,1C8.176,16.5,8.624,16.053,8.625,15.5zM8.179,18.572c-0.478,0.277-0.642,0.889-0.365,1.367c0.275,0.479,0.889,0.641,1.365,0.365c0.479-0.275,0.643-0.887,0.367-1.367C9.27,18.461,8.658,18.297,8.179,18.572zM9.18,10.696c-0.479-0.276-1.09-0.112-1.366,0.366s-0.111,1.09,0.365,1.366c0.479,0.276,1.09,0.113,1.367-0.366C9.821,11.584,9.657,10.973,9.18,10.696zM22.822,12.428c0.478-0.275,0.643-0.888,0.366-1.366c-0.275-0.478-0.89-0.642-1.366-0.366c-0.479,0.278-0.642,0.89-0.366,1.367C21.732,12.54,22.344,12.705,22.822,12.428zM12.062,21.455c-0.478-0.275-1.089-0.111-1.366,0.367c-0.275,0.479-0.111,1.09,0.366,1.365c0.478,0.277,1.091,0.111,1.365-0.365C12.704,22.344,12.54,21.732,12.062,21.455zM12.062,9.545c0.479-0.276,0.642-0.888,0.366-1.366c-0.276-0.478-0.888-0.642-1.366-0.366s-0.642,0.888-0.366,1.366C10.973,9.658,11.584,9.822,12.062,9.545zM22.823,18.572c-0.48-0.275-1.092-0.111-1.367,0.365c-0.275,0.479-0.112,1.092,0.367,1.367c0.477,0.275,1.089,0.113,1.365-0.365C23.464,19.461,23.3,18.848,22.823,18.572zM19.938,7.813c-0.477-0.276-1.091-0.111-1.365,0.366c-0.275,0.48-0.111,1.091,0.366,1.367s1.089,0.112,1.366-0.366C20.581,8.702,20.418,8.089,19.938,7.813zM23.378,14.5c-0.554,0.002-1.001,0.45-1.001,1c0.001,0.552,0.448,1,1.001,1c0.551,0,1-0.447,1-1C24.378,14.949,23.929,14.5,23.378,14.5zM15.501,6.624c-0.552,0-1,0.448-1,1l-0.466,7.343l-3.004,1.96c-0.478,0.277-0.642,0.889-0.365,1.365c0.275,0.479,0.889,0.643,1.365,0.367l3.305-1.676C15.39,16.99,15.444,17,15.501,17c0.828,0,1.5-0.671,1.5-1.5l-0.5-7.876C16.501,7.072,16.053,6.624,15.501,6.624zM15.501,22.377c-0.552,0-1,0.447-1,1s0.448,1,1,1s1-0.447,1-1S16.053,22.377,15.501,22.377zM18.939,21.455c-0.479,0.277-0.643,0.889-0.366,1.367c0.275,0.477,0.888,0.643,1.366,0.365c0.478-0.275,0.642-0.889,0.366-1.365C20.028,21.344,19.417,21.18,18.939,21.455z",
+ },
+ animation_pathWingButton: {
+ tagName: "path",
+ d:
+ "m 4.5,0.5 c -2.216,0 -4,1.784 -4,4 l 0,24 c 0,2.216 1.784,4 4,4 l 13.71875,0 C 22.478584,27.272785 27.273681,22.511272 32.5,18.25 l 0,-13.75 c 0,-2.216 -1.784,-4 -4,-4 l -24,0 z",
+ },
+ animation_pathPointer: {
+ tagName: "path",
+ d: "M-15,-65,-15,-55,15,-55,15,-65,0,-95z",
+ },
+ animation_pathSwooshFX: {
+ tagName: "path",
+ d:
+ "m 85,0 c 0,16.617 -4.813944,35.356 -13.131081,48.4508 h 6.099803 c 8.317138,-13.0948 13.13322,-28.5955 13.13322,-45.2124 0,-46.94483 -38.402714,-85.00262 -85.7743869,-85.00262 -1.0218522,0 -2.0373001,0.0241 -3.0506131,0.0589 45.958443,1.59437 82.723058,35.77285 82.723058,81.70532 z",
+ },
+};
+
//Dynamically builds an SVG element from a JSON object.
function svgFromObject(obj) {
var ele = document.createElementNS(svgNS, obj.tagName);
@@ -86,6 +136,8 @@ function makeColorString(background, gradient) {
}
function rectButton(x, y, path) {
+ var iconInfo = svgIconsById[path];
+
var button = {
tagName: "g",
class: "cesium-animation-rectButton",
@@ -108,9 +160,11 @@ function rectButton(x, y, path) {
ry: 4,
},
{
- tagName: "use",
class: "cesium-animation-buttonPath",
- "xlink:href": path,
+ id: path,
+ tagName: iconInfo.tagName,
+ transform: iconInfo.transform,
+ d: iconInfo.d,
},
{
tagName: "title",
@@ -122,25 +176,32 @@ function rectButton(x, y, path) {
}
function wingButton(x, y, path) {
+ var buttonIconInfo = svgIconsById[path];
+ var wingIconInfo = svgIconsById["animation_pathWingButton"];
+
var button = {
tagName: "g",
class: "cesium-animation-rectButton",
transform: "translate(" + x + "," + y + ")",
children: [
{
- tagName: "use",
class: "cesium-animation-buttonGlow",
- "xlink:href": "#animation_pathWingButton",
+ id: "animation_pathWingButton",
+ tagName: wingIconInfo.tagName,
+ d: wingIconInfo.d,
},
{
- tagName: "use",
class: "cesium-animation-buttonMain",
- "xlink:href": "#animation_pathWingButton",
+ id: "animation_pathWingButton",
+ tagName: wingIconInfo.tagName,
+ d: wingIconInfo.d,
},
{
- tagName: "use",
class: "cesium-animation-buttonPath",
- "xlink:href": path,
+ id: path,
+ tagName: buttonIconInfo.tagName,
+ transform: buttonIconInfo.transform,
+ d: buttonIconInfo.d,
},
{
tagName: "title",
@@ -436,19 +497,19 @@ function Animation(container, viewModel) {
this._topG = topG;
this._realtimeSVG = new SvgButton(
- wingButton(3, 4, "#animation_pathClock"),
+ wingButton(3, 4, "animation_pathClock"),
viewModel.playRealtimeViewModel
);
this._playReverseSVG = new SvgButton(
- rectButton(44, 99, "#animation_pathPlayReverse"),
+ rectButton(44, 99, "animation_pathPlayReverse"),
viewModel.playReverseViewModel
);
this._playForwardSVG = new SvgButton(
- rectButton(124, 99, "#animation_pathPlay"),
+ rectButton(124, 99, "animation_pathPlay"),
viewModel.playForwardViewModel
);
this._pauseSVG = new SvgButton(
- rectButton(84, 99, "#animation_pathPause"),
+ rectButton(84, 99, "animation_pathPause"),
viewModel.pauseViewModel
);
@@ -467,19 +528,24 @@ function Animation(container, viewModel) {
});
this._shuttleRingBackPanel = shuttleRingBackPanel;
+ var swooshIconInfo = svgIconsById["animation_pathSwooshFX"];
+ var shuttleRingPointerIconInfo = svgIconsById["animation_pathPointer"];
+
var shuttleRingSwooshG = svgFromObject({
tagName: "g",
class: "cesium-animation-shuttleRingSwoosh",
children: [
{
- tagName: "use",
+ tagName: swooshIconInfo.tagName,
transform: "translate(100,97) scale(-1,1)",
- "xlink:href": "#animation_pathSwooshFX",
+ id: "animation_pathSwooshFX",
+ d: swooshIconInfo.d,
},
{
- tagName: "use",
+ tagName: swooshIconInfo.tagName,
transform: "translate(100,97)",
- "xlink:href": "#animation_pathSwooshFX",
+ id: "animation_pathSwooshFX",
+ d: swooshIconInfo.d,
},
{
tagName: "line",
@@ -493,9 +559,10 @@ function Animation(container, viewModel) {
this._shuttleRingSwooshG = shuttleRingSwooshG;
this._shuttleRingPointer = svgFromObject({
- tagName: "use",
class: "cesium-animation-shuttleRingPointer",
- "xlink:href": "#animation_pathPointer",
+ id: "animation_pathPointer",
+ tagName: shuttleRingPointerIconInfo.tagName,
+ d: shuttleRingPointerIconInfo.d,
});
var knobG = svgFromObject({
@@ -1164,63 +1231,6 @@ Animation.prototype.applyThemeChanges = function () {
},
],
},
- {
- id: "animation_pathReset",
- tagName: "path",
- transform: "translate(16,16) scale(0.85) translate(-16,-16)",
- d:
- "M24.316,5.318,9.833,13.682,9.833,5.5,5.5,5.5,5.5,25.5,9.833,25.5,9.833,17.318,24.316,25.682z",
- },
- {
- id: "animation_pathPause",
- tagName: "path",
- transform: "translate(16,16) scale(0.85) translate(-16,-16)",
- d:
- "M13,5.5,7.5,5.5,7.5,25.5,13,25.5zM24.5,5.5,19,5.5,19,25.5,24.5,25.5z",
- },
- {
- id: "animation_pathPlay",
- tagName: "path",
- transform: "translate(16,16) scale(0.85) translate(-16,-16)",
- d: "M6.684,25.682L24.316,15.5L6.684,5.318V25.682z",
- },
- {
- id: "animation_pathPlayReverse",
- tagName: "path",
- transform: "translate(16,16) scale(-0.85,0.85) translate(-16,-16)",
- d: "M6.684,25.682L24.316,15.5L6.684,5.318V25.682z",
- },
- {
- id: "animation_pathLoop",
- tagName: "path",
- transform: "translate(16,16) scale(0.85) translate(-16,-16)",
- d:
- "M24.249,15.499c-0.009,4.832-3.918,8.741-8.75,8.75c-2.515,0-4.768-1.064-6.365-2.763l2.068-1.442l-7.901-3.703l0.744,8.694l2.193-1.529c2.244,2.594,5.562,4.242,9.26,4.242c6.767,0,12.249-5.482,12.249-12.249H24.249zM15.499,6.75c2.516,0,4.769,1.065,6.367,2.764l-2.068,1.443l7.901,3.701l-0.746-8.693l-2.192,1.529c-2.245-2.594-5.562-4.245-9.262-4.245C8.734,3.25,3.25,8.734,3.249,15.499H6.75C6.758,10.668,10.668,6.758,15.499,6.75z",
- },
- {
- id: "animation_pathClock",
- tagName: "path",
- transform: "translate(16,16) scale(0.85) translate(-16,-15.5)",
- d:
- "M15.5,2.374C8.251,2.375,2.376,8.251,2.374,15.5C2.376,22.748,8.251,28.623,15.5,28.627c7.249-0.004,13.124-5.879,13.125-13.127C28.624,8.251,22.749,2.375,15.5,2.374zM15.5,25.623C9.909,25.615,5.385,21.09,5.375,15.5C5.385,9.909,9.909,5.384,15.5,5.374c5.59,0.01,10.115,4.535,10.124,10.125C25.615,21.09,21.091,25.615,15.5,25.623zM8.625,15.5c-0.001-0.552-0.448-0.999-1.001-1c-0.553,0-1,0.448-1,1c0,0.553,0.449,1,1,1C8.176,16.5,8.624,16.053,8.625,15.5zM8.179,18.572c-0.478,0.277-0.642,0.889-0.365,1.367c0.275,0.479,0.889,0.641,1.365,0.365c0.479-0.275,0.643-0.887,0.367-1.367C9.27,18.461,8.658,18.297,8.179,18.572zM9.18,10.696c-0.479-0.276-1.09-0.112-1.366,0.366s-0.111,1.09,0.365,1.366c0.479,0.276,1.09,0.113,1.367-0.366C9.821,11.584,9.657,10.973,9.18,10.696zM22.822,12.428c0.478-0.275,0.643-0.888,0.366-1.366c-0.275-0.478-0.89-0.642-1.366-0.366c-0.479,0.278-0.642,0.89-0.366,1.367C21.732,12.54,22.344,12.705,22.822,12.428zM12.062,21.455c-0.478-0.275-1.089-0.111-1.366,0.367c-0.275,0.479-0.111,1.09,0.366,1.365c0.478,0.277,1.091,0.111,1.365-0.365C12.704,22.344,12.54,21.732,12.062,21.455zM12.062,9.545c0.479-0.276,0.642-0.888,0.366-1.366c-0.276-0.478-0.888-0.642-1.366-0.366s-0.642,0.888-0.366,1.366C10.973,9.658,11.584,9.822,12.062,9.545zM22.823,18.572c-0.48-0.275-1.092-0.111-1.367,0.365c-0.275,0.479-0.112,1.092,0.367,1.367c0.477,0.275,1.089,0.113,1.365-0.365C23.464,19.461,23.3,18.848,22.823,18.572zM19.938,7.813c-0.477-0.276-1.091-0.111-1.365,0.366c-0.275,0.48-0.111,1.091,0.366,1.367s1.089,0.112,1.366-0.366C20.581,8.702,20.418,8.089,19.938,7.813zM23.378,14.5c-0.554,0.002-1.001,0.45-1.001,1c0.001,0.552,0.448,1,1.001,1c0.551,0,1-0.447,1-1C24.378,14.949,23.929,14.5,23.378,14.5zM15.501,6.624c-0.552,0-1,0.448-1,1l-0.466,7.343l-3.004,1.96c-0.478,0.277-0.642,0.889-0.365,1.365c0.275,0.479,0.889,0.643,1.365,0.367l3.305-1.676C15.39,16.99,15.444,17,15.501,17c0.828,0,1.5-0.671,1.5-1.5l-0.5-7.876C16.501,7.072,16.053,6.624,15.501,6.624zM15.501,22.377c-0.552,0-1,0.447-1,1s0.448,1,1,1s1-0.447,1-1S16.053,22.377,15.501,22.377zM18.939,21.455c-0.479,0.277-0.643,0.889-0.366,1.367c0.275,0.477,0.888,0.643,1.366,0.365c0.478-0.275,0.642-0.889,0.366-1.365C20.028,21.344,19.417,21.18,18.939,21.455z",
- },
- {
- id: "animation_pathWingButton",
- tagName: "path",
- d:
- "m 4.5,0.5 c -2.216,0 -4,1.784 -4,4 l 0,24 c 0,2.216 1.784,4 4,4 l 13.71875,0 C 22.478584,27.272785 27.273681,22.511272 32.5,18.25 l 0,-13.75 c 0,-2.216 -1.784,-4 -4,-4 l -24,0 z",
- },
- {
- id: "animation_pathPointer",
- tagName: "path",
- d: "M-15,-65,-15,-55,15,-55,15,-65,0,-95z",
- },
- {
- id: "animation_pathSwooshFX",
- tagName: "path",
- d:
- "m 85,0 c 0,16.617 -4.813944,35.356 -13.131081,48.4508 h 6.099803 c 8.317138,-13.0948 13.13322,-28.5955 13.13322,-45.2124 0,-46.94483 -38.402714,-85.00262 -85.7743869,-85.00262 -1.0218522,0 -2.0373001,0.0241 -3.0506131,0.0589 45.958443,1.59437 82.723058,35.77285 82.723058,81.70532 z",
- },
],
});
diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.css b/Source/Widgets/CesiumWidget/CesiumWidget.css
index 85b0f1753f43..44a92b78a461 100644
--- a/Source/Widgets/CesiumWidget/CesiumWidget.css
+++ b/Source/Widgets/CesiumWidget/CesiumWidget.css
@@ -45,30 +45,72 @@
.cesium-widget-errorPanel-content {
width: 75%;
+ max-width: 500px;
display: inline-block;
text-align: left;
vertical-align: middle;
- border: 1px solid #526f82;
+ border: 1px solid #510c00;
border-radius: 7px;
- background-color: black;
- color: white;
- font-size: 10pt;
- padding: 1em;
+ background-color: #f0d9d5;
+ font-size: 14px;
+ color: #510c00;
+}
+
+.cesium-widget-errorPanel-content.expanded {
+ max-width: 75%;
}
.cesium-widget-errorPanel-header {
- font-size: 120%;
- color: #fe4;
+ font-size: 18px;
+ font-family: "Open Sans", Verdana, Geneva, sans-serif;
+ background: #d69d93;
+ border-bottom: 2px solid #510c00;
+ padding-bottom: 10px;
+ border-radius: 3px 3px 0 0;
+ padding: 15px;
}
.cesium-widget-errorPanel-scroll {
overflow: auto;
- font-family: monospace;
+ font-family: "Open Sans", Verdana, Geneva, sans-serif;
white-space: pre-wrap;
- padding: 0;
- margin: 10px 0;
+ padding: 0 15px;
+ margin: 10px 0 20px 0;
}
.cesium-widget-errorPanel-buttonPanel {
- text-align: center;
+ padding: 0 15px;
+ margin: 10px 0 20px 0;
+ text-align: right;
+}
+
+.cesium-widget-errorPanel-buttonPanel button {
+ border-color: #510c00;
+ background: #d69d93;
+ color: #202020;
+ margin: 0;
+}
+.cesium-widget-errorPanel-buttonPanel button:focus {
+ border-color: #510c00;
+ background: #f0d9d5;
+ color: #510c00;
+}
+.cesium-widget-errorPanel-buttonPanel button:hover {
+ border-color: #510c00;
+ background: #f0d9d5;
+ color: #510c00;
+}
+.cesium-widget-errorPanel-buttonPanel button:active {
+ border-color: #510c00;
+ background: #b17b72;
+ color: #510c00;
+}
+
+.cesium-widget-errorPanel-more-details {
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.cesium-widget-errorPanel-more-details:hover {
+ color: #2b0700;
}
diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js
index 22dd5ff3ad3f..e0237d06e1d0 100644
--- a/Source/Widgets/CesiumWidget/CesiumWidget.js
+++ b/Source/Widgets/CesiumWidget/CesiumWidget.js
@@ -634,7 +634,7 @@ Object.defineProperties(CesiumWidget.prototype, {
* widget was constructed.
*
* @param {String} title The title to be displayed on the error panel. This string is interpreted as text.
- * @param {String} message A helpful, user-facing message to display prior to the detailed error information. This string is interpreted as HTML.
+ * @param {String} [message] A helpful, user-facing message to display prior to the detailed error information. This string is interpreted as HTML.
* @param {String} [error] The error to be displayed on the error panel. This string is formatted using {@link formatError} and then displayed as text.
*/
CesiumWidget.prototype.showErrorPanel = function (title, message, error) {
@@ -663,22 +663,56 @@ CesiumWidget.prototype.showErrorPanel = function (title, message, error) {
window.addEventListener("resize", resizeCallback, false);
}
- if (defined(message)) {
+ var hasMessage = defined(message);
+ var hasError = defined(error);
+
+ if (hasMessage || hasError) {
var errorMessage = document.createElement("div");
errorMessage.className = "cesium-widget-errorPanel-message";
- errorMessage.innerHTML = "" + message + "
";
errorPanelScroller.appendChild(errorMessage);
- }
- var errorDetails = "(no error details available)";
- if (defined(error)) {
- errorDetails = formatError(error);
- }
+ if (hasError) {
+ var errorDetails = formatError(error);
+ if (!hasMessage) {
+ if (typeof error === "string") {
+ error = new Error(error);
+ }
+
+ message = formatError({
+ name: error.name,
+ message: error.message,
+ });
+ errorDetails = error.stack;
+ }
- var errorMessageDetails = document.createElement("div");
- errorMessageDetails.className = "cesium-widget-errorPanel-message";
- errorMessageDetails.appendChild(document.createTextNode(errorDetails));
- errorPanelScroller.appendChild(errorMessageDetails);
+ //IE8 does not have a console object unless the dev tools are open.
+ if (typeof console !== "undefined") {
+ console.error(title + "\n" + message + "\n" + errorDetails);
+ }
+
+ var errorMessageDetails = document.createElement("div");
+ errorMessageDetails.className =
+ "cesium-widget-errorPanel-message-details collapsed";
+
+ var moreDetails = document.createElement("span");
+ moreDetails.className = "cesium-widget-errorPanel-more-details";
+ moreDetails.appendChild(document.createTextNode("See more..."));
+ errorMessageDetails.appendChild(moreDetails);
+
+ errorMessageDetails.onclick = function (e) {
+ errorMessageDetails.removeChild(moreDetails);
+ errorMessageDetails.appendChild(document.createTextNode(errorDetails));
+ errorMessageDetails.className =
+ "cesium-widget-errorPanel-message-details";
+ content.className = "cesium-widget-errorPanel-content expanded";
+ errorMessageDetails.onclick = undefined;
+ };
+
+ errorPanelScroller.appendChild(errorMessageDetails);
+ }
+
+ errorMessage.innerHTML = "" + message + "
";
+ }
var buttonPanel = document.createElement("div");
buttonPanel.className = "cesium-widget-errorPanel-buttonPanel";
@@ -698,11 +732,6 @@ CesiumWidget.prototype.showErrorPanel = function (title, message, error) {
buttonPanel.appendChild(okButton);
element.appendChild(overlay);
-
- //IE8 does not have a console object unless the dev tools are open.
- if (typeof console !== "undefined") {
- console.error(title + "\n" + message + "\n" + errorDetails);
- }
};
/**
diff --git a/Specs/Core/HeapSpec.js b/Specs/Core/HeapSpec.js
index ce38924089cf..e89c7ec754ac 100644
--- a/Specs/Core/HeapSpec.js
+++ b/Specs/Core/HeapSpec.js
@@ -3,6 +3,15 @@ import { Heap } from "../../Source/Cesium.js";
describe("Core/Heap", function () {
var length = 100;
+ function expectTrailingReferenceToBeRemoved(heap) {
+ var array = heap._array;
+ var length = heap._length;
+ var reservedLength = array.length;
+ for (var i = length; i < reservedLength; ++i) {
+ expect(array[i]).toBeUndefined();
+ }
+ }
+
function checkHeap(heap, comparator) {
var array = heap.internalArray;
var pass = true;
@@ -90,6 +99,34 @@ describe("Core/Heap", function () {
expect(pass).toBe(true);
});
+ it("pop removes trailing references", function () {
+ var heap = new Heap({
+ comparator: comparator,
+ });
+
+ for (var i = 0; i < 10; ++i) {
+ heap.insert(Math.random());
+ }
+
+ heap.pop();
+ heap.pop();
+
+ expectTrailingReferenceToBeRemoved(heap);
+ });
+
+ it("setting maximum length less than current length removes trailing references", function () {
+ var heap = new Heap({
+ comparator: comparator,
+ });
+
+ for (var i = 0; i < 10; ++i) {
+ heap.insert(Math.random());
+ }
+
+ heap.maximumLength = 5;
+ expectTrailingReferenceToBeRemoved(heap);
+ });
+
it("insert returns the removed element when maximumLength is set", function () {
var heap = new Heap({
comparator: comparator,
@@ -170,4 +207,13 @@ describe("Core/Heap", function () {
currentId = element.id;
}
});
+
+ it("maximumLength setter throws if length is less than 0", function () {
+ var heap = new Heap({
+ comparator: comparator,
+ });
+ expect(function () {
+ heap.maximumLength = -1;
+ }).toThrowDeveloperError();
+ });
});
diff --git a/Specs/Core/ManagedArraySpec.js b/Specs/Core/ManagedArraySpec.js
index d54219146ebf..df1ee7446c7d 100644
--- a/Specs/Core/ManagedArraySpec.js
+++ b/Specs/Core/ManagedArraySpec.js
@@ -1,6 +1,15 @@
import { ManagedArray } from "../../Source/Cesium.js";
describe("Core/ManagedArray", function () {
+ function expectTrailingReferenceToBeRemoved(managedArray) {
+ var array = managedArray._array;
+ var length = managedArray._length;
+ var reservedLength = array.length;
+ for (var i = length; i < reservedLength; ++i) {
+ expect(array[i]).toBeUndefined();
+ }
+ }
+
it("constructor has expected default values", function () {
var array = new ManagedArray();
expect(array.length).toEqual(0);
@@ -42,6 +51,13 @@ describe("Core/ManagedArray", function () {
}).toThrowDeveloperError();
});
+ it("length setter throws if length is less than 0", function () {
+ var array = new ManagedArray();
+ expect(function () {
+ array.length = -1;
+ }).toThrowDeveloperError();
+ });
+
it("set resizes array", function () {
var array = new ManagedArray();
array.set(0, "a");
@@ -90,6 +106,24 @@ describe("Core/ManagedArray", function () {
}
});
+ it("pop removes trailing references", function () {
+ var length = 10;
+ var array = new ManagedArray(length);
+ array.set(0, Math.random());
+ array.set(1, Math.random());
+ array.set(2, Math.random());
+ array.pop();
+ array.pop();
+ expectTrailingReferenceToBeRemoved(array);
+ });
+
+ it("pop returns undefined if array is empty", function () {
+ var array = new ManagedArray();
+ array.push(1);
+ expect(array.pop()).toBe(1);
+ expect(array.pop()).toBeUndefined();
+ });
+
it("reserve throws if length is less than 0", function () {
var array = new ManagedArray();
expect(function () {
@@ -130,6 +164,16 @@ describe("Core/ManagedArray", function () {
expect(array.length).toEqual(5);
});
+ it("resize removes trailing references", function () {
+ var length = 10;
+ var array = new ManagedArray(length);
+ array.set(0, Math.random());
+ array.set(1, Math.random());
+ array.set(2, Math.random());
+ array.resize(1);
+ expectTrailingReferenceToBeRemoved(array);
+ });
+
it("trim", function () {
var array = new ManagedArray(2);
array.reserve(10);
diff --git a/Specs/Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf b/Specs/Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf
new file mode 100644
index 000000000000..5c278ddb2141
--- /dev/null
+++ b/Specs/Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf
@@ -0,0 +1,100 @@
+{
+ "asset" : {
+ "generator" : "Khronos glTF Blender I/O v1.1.46",
+ "version" : "2.0"
+ },
+ "scene" : 0,
+ "scenes" : [
+ {
+ "name" : "Scene",
+ "nodes" : [
+ 0
+ ]
+ }
+ ],
+ "nodes" : [
+ {
+ "mesh" : 0,
+ "name" : "Cube"
+ }
+ ],
+ "meshes" : [
+ {
+ "name" : "Cube.001",
+ "primitives" : [
+ {
+ "attributes" : {
+ "POSITION" : 0,
+ "NORMAL" : 1,
+ "TEXCOORD_0" : 2
+ },
+ "indices" : 3
+ }
+ ]
+ }
+ ],
+ "accessors" : [
+ {
+ "bufferView" : 0,
+ "componentType" : 5126,
+ "count" : 20,
+ "max" : [
+ 1,
+ 0.9999998807907104,
+ 1
+ ],
+ "min" : [
+ -1,
+ -0.9999998807907104,
+ -1
+ ],
+ "type" : "VEC3"
+ },
+ {
+ "bufferView" : 1,
+ "componentType" : 5126,
+ "count" : 20,
+ "type" : "VEC3"
+ },
+ {
+ "bufferView" : 2,
+ "componentType" : 5126,
+ "count" : 20,
+ "type" : "VEC2"
+ },
+ {
+ "bufferView" : 3,
+ "componentType" : 5123,
+ "count" : 30,
+ "type" : "SCALAR"
+ }
+ ],
+ "bufferViews" : [
+ {
+ "buffer" : 0,
+ "byteLength" : 240,
+ "byteOffset" : 0
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 240,
+ "byteOffset" : 240
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 160,
+ "byteOffset" : 480
+ },
+ {
+ "buffer" : 0,
+ "byteLength" : 60,
+ "byteOffset" : 640
+ }
+ ],
+ "buffers" : [
+ {
+ "byteLength" : 700,
+ "uri" : "data:application/octet-stream;base64,/v9/P/7/fz8AAIA//v9/P/7/f78AAIA/AACAP/7/f7/+/3+/AACAP/7/fz/+/3+/AACAv/7/fz/+/38/AACAv/7/f7/+/38//v9/P/7/f78AAIA//v9/P/7/fz8AAIA/AACAP/7/fz/+/3+/AACAP/7/f7/+/3+//v9/v/7/f78AAIC//v9/v/7/fz8AAIC/AACAv/7/f7/+/38//v9/v/7/f78AAIC/AACAP/7/f7/+/3+//v9/P/7/f78AAIA/AACAP/7/fz/+/3+//v9/v/7/fz8AAIC/AACAv/7/fz/+/38//v9/P/7/fz8AAIA/AACAPwAAAAABAIAzAACAPwAAAAABAIAzAACAPwAAAAABAIAzAACAPwAAAAABAIAzAQCAswAAAAAAAIA/AQCAswAAAAAAAIA/AQCAswAAAAAAAIA/AQCAswAAAAAAAIA/AQCAMwAAAAAAAIC/AQCAMwAAAAAAAIC/AQCAMwAAAAAAAIC/AQCAMwAAAAAAAIC/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAgPwAAQD8AAMA+AABAPwAAwD4AAIA/AAAgPwAAgD8AACA/AAAAPwAAwD4AAAA/AADAPgAAQD8AACA/AABAPwAAID8AAAAAAADAPgAAAAAAAMA+AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AACAPgAAAD4AAIA+AAAAPgAAAD8AAGA/AACAPgAAID8AAIA+AAAgPwAAAD8AAGA/AAAAPwAAAQACAAAAAgADAAQABQAGAAQABgAHAAgACQAKAAgACgALAAwADQAOAAwADgAPABAAEQASABMAEAASAA=="
+ }
+ ]
+}
diff --git a/Specs/DataSources/EntitySpec.js b/Specs/DataSources/EntitySpec.js
index 1c8ae3a5a0fe..b29c11ad8638 100644
--- a/Specs/DataSources/EntitySpec.js
+++ b/Specs/DataSources/EntitySpec.js
@@ -179,6 +179,21 @@ describe("DataSources/Entity", function () {
}
});
+ it("merge ignores reserved property names when called with a plain object.", function () {
+ var entity = new Entity();
+
+ //Technically merge requires passing an Entity instance, but we call it internally
+ //with a plain object during construction to set up custom properties.
+ entity.merge({
+ name: undefined,
+ availability: undefined,
+ parent: undefined,
+ });
+ expect(entity.name).toBeUndefined();
+ expect(entity.availability).toBeUndefined();
+ expect(entity.parent).toBeUndefined();
+ });
+
it("merge does not overwrite availability", function () {
var entity = new Entity();
var interval = TimeInterval.fromIso8601({
diff --git a/Specs/DataSources/SampledPropertySpec.js b/Specs/DataSources/SampledPropertySpec.js
index 19546d30fdd0..06d31fee6aa9 100644
--- a/Specs/DataSources/SampledPropertySpec.js
+++ b/Specs/DataSources/SampledPropertySpec.js
@@ -720,6 +720,17 @@ describe("DataSources/SampledProperty", function () {
expect(left.equals(right)).toEqual(true);
});
+ it("equals works when samples differ with quaternion", function () {
+ var left = new SampledProperty(Quaternion);
+ var right = new SampledProperty(Quaternion);
+ expect(left.equals(right)).toEqual(true);
+
+ var time = JulianDate.now();
+ left.addSample(time, new Quaternion(1, 2, 3, 4));
+ right.addSample(time, new Quaternion(1, 2, 3, 5));
+ expect(left.equals(right)).toEqual(false);
+ });
+
it("equals works when derivatives differ", function () {
var left = new SampledProperty(Number, [Number]);
var right = new SampledProperty(Number);
diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js
index 63e0395c286c..9586e39c8165 100644
--- a/Specs/Scene/Cesium3DTilesetSpec.js
+++ b/Specs/Scene/Cesium3DTilesetSpec.js
@@ -7,6 +7,7 @@ import { defined } from "../../Source/Cesium.js";
import { getAbsoluteUri } from "../../Source/Cesium.js";
import { getStringFromTypedArray } from "../../Source/Cesium.js";
import { HeadingPitchRange } from "../../Source/Cesium.js";
+import { HeadingPitchRoll } from "../../Source/Cesium.js";
import { Intersect } from "../../Source/Cesium.js";
import { JulianDate } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
@@ -2493,6 +2494,61 @@ describe(
);
});
+ function testBackFaceCulling(url, setViewOptions) {
+ var renderOptions = {
+ scene: scene,
+ time: new JulianDate(2457522.154792),
+ };
+ return Cesium3DTilesTester.loadTileset(scene, url).then(function (
+ tileset
+ ) {
+ scene.camera.setView(setViewOptions);
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).toEqual([0, 0, 0, 255]);
+ tileset.backFaceCulling = false;
+ expect(renderOptions).toRenderAndCall(function (rgba2) {
+ expect(rgba2).not.toEqual(rgba);
+ });
+ });
+ });
+ }
+
+ it("renders b3dm tileset when back face culling is disabled", function () {
+ var setViewOptions = {
+ destination: new Cartesian3(
+ 1215012.6853779217,
+ -4736313.101374343,
+ 4081603.4657718465
+ ),
+ orientation: new HeadingPitchRoll(
+ 6.283185307179584,
+ -0.49999825387267993,
+ 6.283185307179586
+ ),
+ endTransform: Matrix4.IDENTITY,
+ };
+
+ return testBackFaceCulling(withoutBatchTableUrl, setViewOptions);
+ });
+
+ it("renders i3dm tileset when back face culling is disabled", function () {
+ var setViewOptions = {
+ destination: new Cartesian3(
+ 1215015.8599828142,
+ -4736324.65638894,
+ 4081609.967056947
+ ),
+ orientation: new HeadingPitchRoll(
+ 6.283185307179585,
+ -0.5000006393986758,
+ 6.283185307179586
+ ),
+ endTransform: Matrix4.IDENTITY,
+ };
+
+ return testBackFaceCulling(instancedUrl, setViewOptions);
+ });
+
///////////////////////////////////////////////////////////////////////////
// Styling tests
diff --git a/Specs/Scene/GroundPolylinePrimitiveSpec.js b/Specs/Scene/GroundPolylinePrimitiveSpec.js
index f832be677c3d..cc7c74308f43 100644
--- a/Specs/Scene/GroundPolylinePrimitiveSpec.js
+++ b/Specs/Scene/GroundPolylinePrimitiveSpec.js
@@ -1,5 +1,7 @@
import { ApproximateTerrainHeights } from "../../Source/Cesium.js";
+import { Cartesian2 } from "../../Source/Cesium.js";
import { Cartesian3 } from "../../Source/Cesium.js";
+import { Math as CesiumMath } from "../../Source/Cesium.js";
import { Color } from "../../Source/Cesium.js";
import { ColorGeometryInstanceAttribute } from "../../Source/Cesium.js";
import { destroyObject } from "../../Source/Cesium.js";
@@ -7,6 +9,7 @@ import { DistanceDisplayConditionGeometryInstanceAttribute } from "../../Source/
import { Ellipsoid } from "../../Source/Cesium.js";
import { GeometryInstance } from "../../Source/Cesium.js";
import { GroundPolylineGeometry } from "../../Source/Cesium.js";
+import { HeadingPitchRange } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { RectangleGeometry } from "../../Source/Cesium.js";
import { ShowGeometryInstanceAttribute } from "../../Source/Cesium.js";
@@ -16,6 +19,7 @@ import { PerInstanceColorAppearance } from "../../Source/Cesium.js";
import { PolylineColorAppearance } from "../../Source/Cesium.js";
import { PolylineMaterialAppearance } from "../../Source/Cesium.js";
import { Primitive } from "../../Source/Cesium.js";
+import createCanvas from "../createCanvas.js";
import createScene from "../createScene.js";
import pollToPromise from "../pollToPromise.js";
@@ -40,8 +44,14 @@ describe(
var lookPosition = Cartesian3.fromDegrees(0.02, 0.0);
var lookPositionOffset = Cartesian3.fromDegrees(0.02, 0.0001);
+ var canvasWidth = 4;
+ var canvasHeight = 4;
+
beforeAll(function () {
- scene = createScene();
+ var canvas = createCanvas(canvasWidth, canvasHeight);
+ scene = createScene({
+ canvas: canvas,
+ });
scene.postProcessStages.fxaa.enabled = false;
context = scene.context;
@@ -86,6 +96,11 @@ describe(
beforeEach(function () {
scene.morphTo3D(0);
+ // Other specs can mess with camera near/far, which can interfere with the
+ // algorithm used to draw ground polylines.
+ scene.camera.frustum.near = 0.1;
+ scene.camera.frustum.far = 10000000000.0;
+
var depthpolylineColorAttribute = ColorGeometryInstanceAttribute.fromColor(
new Color(0.0, 0.0, 1.0, 1.0)
);
@@ -313,6 +328,22 @@ describe(
expect(frameState.commandList.length).toEqual(0);
});
+ function coordinateOfPixelColor(rgba, color) {
+ for (var y = 0; y < canvasHeight; y++) {
+ for (var x = 0; x < canvasWidth; x++) {
+ var i = (y * canvasWidth + x) * 4;
+ if (
+ color[0] === rgba[i] &&
+ color[1] === rgba[i + 1] &&
+ color[2] === rgba[i + 2] &&
+ color[3] === rgba[i + 3]
+ ) {
+ return new Cartesian2(x, canvasHeight - y);
+ }
+ }
+ }
+ }
+
function verifyGroundPolylinePrimitiveRender(
lookPosition,
primitive,
@@ -322,12 +353,16 @@ describe(
scene.groundPrimitives.add(depthRectanglePrimitive);
expect(scene).toRenderAndCall(function (rgba) {
- expect(rgba).not.toEqual([0, 0, 0, 255]);
- expect(rgba[0]).toEqual(0);
+ expect(coordinateOfPixelColor(rgba, depthColor)).toBeDefined();
});
scene.groundPrimitives.add(primitive);
- expect(scene).toRender(color);
+ var coordinate;
+ expect(scene).toRenderAndCall(function (rgba) {
+ coordinate = coordinateOfPixelColor(rgba, color);
+ expect(coordinate).toBeDefined();
+ });
+ return coordinate;
}
it("renders in 3D", function () {
@@ -408,16 +443,23 @@ describe(
appearance: new PolylineColorAppearance(),
});
- // Morph to 2D first because 3D -> 2D/CV morph is difficult in single-pixel
+ scene.groundPrimitives.add(depthRectanglePrimitive);
+ scene.groundPrimitives.add(groundPolylinePrimitive);
+
+ // Morph to 2D first
scene.morphTo2D(0);
scene.renderForSpecs();
scene.morphToColumbusView(1);
- verifyGroundPolylinePrimitiveRender(
- lookPosition,
- groundPolylinePrimitive,
- polylineColor
- );
+ // Morph changes the view distance to be very far, so:
+ // * the mock globe may not be visible due to small canvas size
+ // * GroundPolylinePrimitive renders its volume instead of using the
+ // volume as a stencil for sampling the depth texture
+ // So just check that the ground polyline primitive rendered.
+ expect(scene).toRenderAndCall(function (rgba) {
+ expect(coordinateOfPixelColor(rgba, polylineColor)).toBeDefined();
+ });
+
scene.completeMorph();
});
@@ -657,14 +699,14 @@ describe(
return;
}
- var near = 10000.0;
- var far = 1000000.0;
+ var near = 10.0;
+ var far = 1000.0;
var geometryInstance = new GeometryInstance({
geometry: new GroundPolylineGeometry({
positions: positions,
granularity: 0.0,
- width: 1.0,
+ width: 1000.0,
loop: false,
ellipsoid: ellipsoid,
}),
@@ -674,6 +716,7 @@ describe(
near,
far
),
+ color: polylineColorAttribute,
},
});
@@ -693,17 +736,29 @@ describe(
var center = boundingSphere.center;
var radius = boundingSphere.radius;
- scene.camera.lookAt(center, new Cartesian3(0.0, 0.0, radius));
- expect(scene).toRender(depthColor);
+ scene.camera.lookAt(
+ center,
+ new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius)
+ );
+ expect(scene).toRenderAndCall(function (rgba) {
+ expect(coordinateOfPixelColor(rgba, depthColor)).toBeDefined();
+ });
scene.camera.lookAt(
center,
- new Cartesian3(0.0, 0.0, radius + near + 1.0)
+ new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + near + 1.0)
);
- expect(scene).not.toRender(depthColor);
+ expect(scene).toRenderAndCall(function (rgba) {
+ expect(coordinateOfPixelColor(rgba, depthColor)).toBeUndefined();
+ });
- scene.camera.lookAt(center, new Cartesian3(0.0, 0.0, radius + far + 1.0));
- expect(scene).toRender(depthColor);
+ scene.camera.lookAt(
+ center,
+ new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + far + 1.0)
+ );
+ expect(scene).toRenderAndCall(function (rgba) {
+ expect(coordinateOfPixelColor(rgba, depthColor)).toBeDefined();
+ });
});
it("getGeometryInstanceAttributes returns same object each time", function () {
@@ -747,16 +802,15 @@ describe(
appearance: new PolylineColorAppearance(),
});
- verifyGroundPolylinePrimitiveRender(
+ var polylineColorCoordinate = verifyGroundPolylinePrimitiveRender(
lookPosition,
groundPolylinePrimitive,
polylineColor
);
expect(scene).toPickAndCall(function (result) {
- expect(result.primitive).toEqual(groundPolylinePrimitive);
expect(result.id).toEqual("polyline on terrain");
- });
+ }, polylineColorCoordinate);
});
it("picking in 2D", function () {
@@ -771,16 +825,15 @@ describe(
});
scene.morphTo2D(0);
- verifyGroundPolylinePrimitiveRender(
+ var polylineColorCoordinate = verifyGroundPolylinePrimitiveRender(
lookPosition,
groundPolylinePrimitive,
polylineColor
);
expect(scene).toPickAndCall(function (result) {
- expect(result.primitive).toEqual(groundPolylinePrimitive);
expect(result.id).toEqual("polyline on terrain");
- });
+ }, polylineColorCoordinate);
});
it("picking in Columbus View", function () {
@@ -795,16 +848,16 @@ describe(
});
scene.morphToColumbusView(0);
- verifyGroundPolylinePrimitiveRender(
+ var polylineColorCoordinate = verifyGroundPolylinePrimitiveRender(
lookPosition,
groundPolylinePrimitive,
polylineColor
);
+ scene.camera.lookAt(lookPosition, Cartesian3.UNIT_Z);
expect(scene).toPickAndCall(function (result) {
- expect(result.primitive).toEqual(groundPolylinePrimitive);
expect(result.id).toEqual("polyline on terrain");
- });
+ }, polylineColorCoordinate);
});
it("picking in Morph", function () {
@@ -830,21 +883,29 @@ describe(
appearance: new PolylineColorAppearance(),
});
- // Morph to 2D first because 3D -> 2D/CV morph is difficult in single-pixel
+ // Morph to 2D first
scene.morphTo2D(0);
scene.renderForSpecs();
scene.morphToColumbusView(1);
- verifyGroundPolylinePrimitiveRender(
- lookPosition,
- groundPolylinePrimitive,
- polylineColor
- );
+ // Morph changes the view distance to be very far, so:
+ // * the mock globe may not be visible due to small canvas size
+ // * GroundPolylinePrimitive renders its volume instead of using the
+ // volume as a stencil for sampling the depth texture
+ // So just check that the ground polyline primitive rendered.
+ scene.groundPrimitives.add(depthRectanglePrimitive);
+ scene.groundPrimitives.add(groundPolylinePrimitive);
+ var polylineColorCoordinate;
+ expect(scene).toRenderAndCall(function (rgba) {
+ polylineColorCoordinate = coordinateOfPixelColor(rgba, polylineColor);
+ });
+
+ scene.renderForSpecs();
expect(scene).toPickAndCall(function (result) {
- expect(result.primitive).toEqual(groundPolylinePrimitive);
expect(result.id).toEqual("big polyline on terrain");
- });
+ }, polylineColorCoordinate);
+
scene.completeMorph();
});
diff --git a/Specs/Scene/ModelInstanceCollectionSpec.js b/Specs/Scene/ModelInstanceCollectionSpec.js
index b01122e0dd1d..88b2798efcc1 100644
--- a/Specs/Scene/ModelInstanceCollectionSpec.js
+++ b/Specs/Scene/ModelInstanceCollectionSpec.js
@@ -5,6 +5,7 @@ import { HeadingPitchRange } from "../../Source/Cesium.js";
import { HeadingPitchRoll } from "../../Source/Cesium.js";
import { JulianDate } from "../../Source/Cesium.js";
import { Matrix4 } from "../../Source/Cesium.js";
+import { Axis } from "../../Source/Cesium.js";
import { PrimitiveType } from "../../Source/Cesium.js";
import { Resource } from "../../Source/Cesium.js";
import { Transforms } from "../../Source/Cesium.js";
@@ -340,6 +341,16 @@ describe(
url: riggedFigureUrl,
instances: createInstances(4),
}).then(function (collection) {
+ var instances = collection._instances;
+ // Rotate instances to account for empty space between legs of rigged model.
+ for (var i = 0; i < instances.length; ++i) {
+ instances[i].modelMatrix = Matrix4.multiply(
+ instances[i].modelMatrix,
+ Axis.Y_UP_TO_X_UP,
+ new Matrix4()
+ );
+ }
+
expectRender(collection);
});
});
diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js
index b2db67d54604..01b20f31acf6 100644
--- a/Specs/Scene/ModelSpec.js
+++ b/Specs/Scene/ModelSpec.js
@@ -148,6 +148,9 @@ describe(
var boxGltf2WithTechniquesUrl =
"./Data/Models/Box-Gltf-2-Techniques/Box.gltf";
+ var boxBackFaceCullingUrl =
+ "./Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf";
+
var texturedBoxModel;
var cesiumAirModel;
var animBoxesModel;
@@ -3857,6 +3860,34 @@ describe(
});
});
+ it("renders box when back face culling is disabled", function () {
+ return loadModel(boxBackFaceCullingUrl).then(function (model) {
+ expect(model.ready).toBe(true);
+ model.show = true;
+
+ // Look at the model
+ model.zoomTo();
+
+ expect({
+ scene: scene,
+ time: JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")),
+ }).toRenderAndCall(function (rgba) {
+ expect(rgba).toEqual([0, 0, 0, 255]);
+ });
+
+ model.backFaceCulling = false;
+
+ expect({
+ scene: scene,
+ time: JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")),
+ }).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ });
+
+ primitives.remove(model);
+ });
+ });
+
describe("height referenced model", function () {
function createMockGlobe() {
var globe = {
diff --git a/Specs/Scene/PickingSpec.js b/Specs/Scene/PickingSpec.js
index af839d2806e7..9cd925ade554 100644
--- a/Specs/Scene/PickingSpec.js
+++ b/Specs/Scene/PickingSpec.js
@@ -208,7 +208,7 @@ describe(
scene.morphTo2D(0.0);
camera.setView({ destination: largeRectangle });
var rectangle = createLargeRectangle(0.0);
- scene.initializeFrame();
+ scene.renderForSpecs();
expect(scene).toPickPrimitive(rectangle);
});
@@ -223,7 +223,7 @@ describe(
camera.setView({ destination: largeRectangle });
var rectangle = createLargeRectangle(0.0);
- scene.initializeFrame();
+ scene.renderForSpecs();
expect(scene).toPickPrimitive(rectangle);
});
});
diff --git a/Specs/Scene/SceneSpec.js b/Specs/Scene/SceneSpec.js
index 65ba5488ea1d..0756d03b56f7 100644
--- a/Specs/Scene/SceneSpec.js
+++ b/Specs/Scene/SceneSpec.js
@@ -2267,6 +2267,7 @@ describe(
expect(scene).toRenderAndCall(function (rgba) {
expect(rgba).not.toEqual(opaqueColor);
+ scene.destroyForSpecs();
});
});
});
@@ -2317,6 +2318,7 @@ describe(
return updateGlobeUntilDone(scene).then(function () {
expect(scene).toRenderAndCall(function (rgba) {
expect(rgba[0]).toBeGreaterThan(0);
+ scene.destroyForSpecs();
});
});
});
@@ -2366,6 +2368,7 @@ describe(
return updateGlobeUntilDone(scene).then(function () {
expect(scene).toPickPrimitive(primitive);
+ scene.destroyForSpecs();
});
});
},
diff --git a/Specs/Widgets/CesiumWidget/CesiumWidgetSpec.js b/Specs/Widgets/CesiumWidget/CesiumWidgetSpec.js
index 6bc6891dbc42..a9106afdf6ed 100644
--- a/Specs/Widgets/CesiumWidget/CesiumWidgetSpec.js
+++ b/Specs/Widgets/CesiumWidget/CesiumWidgetSpec.js
@@ -370,7 +370,7 @@ describe(
var found = false;
for (var i = 0; i < messages.length; ++i) {
- if (messages[i].textContent === error) {
+ if (messages[i].textContent.indexOf(error) !== -1) {
found = true;
}
}
diff --git a/Specs/Widgets/Viewer/ViewerSpec.js b/Specs/Widgets/Viewer/ViewerSpec.js
index 9b1f89191829..8ac581bc3ad0 100644
--- a/Specs/Widgets/Viewer/ViewerSpec.js
+++ b/Specs/Widgets/Viewer/ViewerSpec.js
@@ -874,7 +874,7 @@ describe(
var found = false;
for (var i = 0; i < messages.length; ++i) {
- if (messages[i].textContent === error) {
+ if (messages[i].textContent.indexOf(error) !== -1) {
found = true;
}
}
diff --git a/Specs/addDefaultMatchers.js b/Specs/addDefaultMatchers.js
index dd0de5bf3fcc..d574ae6a19f1 100644
--- a/Specs/addDefaultMatchers.js
+++ b/Specs/addDefaultMatchers.js
@@ -363,9 +363,9 @@ function createDefaultMatchers(debug) {
toPickAndCall: function (util, customEqualityTesters) {
return {
- compare: function (actual, expected) {
+ compare: function (actual, expected, args) {
var scene = actual;
- var result = scene.pick(new Cartesian2(0, 0));
+ var result = scene.pick(defaultValue(args, new Cartesian2(0, 0)));
var webglStub = !!window.webglStub;
if (!webglStub) {
diff --git a/package.json b/package.json
index da71fe951649..f00180ed18f8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cesium",
- "version": "1.70.1",
+ "version": "1.71.0",
"description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.",
"homepage": "http://cesium.com/cesiumjs/",
"license": "Apache-2.0",