diff --git a/Apps/Sandcastle/gallery/Geometry Height Reference.html b/Apps/Sandcastle/gallery/Geometry Height Reference.html new file mode 100644 index 000000000000..affc60edc5a0 --- /dev/null +++ b/Apps/Sandcastle/gallery/Geometry Height Reference.html @@ -0,0 +1,102 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Geometry Height Reference.jpg b/Apps/Sandcastle/gallery/Geometry Height Reference.jpg new file mode 100644 index 000000000000..054d09cbc2e4 Binary files /dev/null and b/Apps/Sandcastle/gallery/Geometry Height Reference.jpg differ diff --git a/CHANGES.md b/CHANGES.md index 04cd837bf343..546089ba1afc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,9 +14,12 @@ Change Log * Requires depth texture support (`WEBGL_depth_texture` or `WEBKIT_WEBGL_depth_texture`), otherwise `clampToGround` will be ignored. Use `Entity.supportsPolylinesOnTerrain` to check for support. * Added `GroundPolylinePrimitive` and `GroundPolylineGeometry`. * `PostProcessStage` has a `selected` property which is an array of primitives used for selectively applying a post-process stage. [#6476](https://github.com/AnalyticalGraphicsInc/cesium/pull/6476) - * The `PostProcessStageLibrary.createBlackAndWhiteStage` and `PostProcessStageLibrary.createSilhouetteStage` have per-feature support. + * The `PostProcessStageLibrary.createBlackAndWhiteStage` and `PostProcessStageLibrary.createSilhouetteStage` have per-feature support. * Added CZML support for `zIndex` for `corridor`, `ellipse`, `polygon`, `polyline` and `rectangle`. [#6708](https://github.com/AnalyticalGraphicsInc/cesium/pull/6708) * Added CZML `clampToGround` option for `polyline`. [#6706](https://github.com/AnalyticalGraphicsInc/cesium/pull/6706) +* Added `heightReference` and `extrudedHeightReference` properties to `CorridorGraphics`, `EllipseGraphics`, `PolygonGraphics` and `RectangleGraphics`. [#6717](https://github.com/AnalyticalGraphicsInc/cesium/pull/6717) + * This can be used in conjunction with the `height` and/or `extrudedHeight` properties to clamp the geometry to terrain or set the height relative to terrain. + * Note, this will not make the geometry conform to terrain. Extruded geoemtry that is clamped to the ground will have a flat top will sinks into the terrain at the base. ##### Fixes :wrench: * Fixed a bug that caused Cesium to be unable to load local resources in Electron. [#6726](https://github.com/AnalyticalGraphicsInc/cesium/pull/6726) diff --git a/Source/DataSources/CorridorGeometryUpdater.js b/Source/DataSources/CorridorGeometryUpdater.js index ab1eef75de9b..69a6a65ac7f3 100644 --- a/Source/DataSources/CorridorGeometryUpdater.js +++ b/Source/DataSources/CorridorGeometryUpdater.js @@ -1,4 +1,6 @@ define([ + '../Core/ApproximateTerrainHeights', + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -8,9 +10,13 @@ define([ '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', + '../Core/Rectangle', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', @@ -19,6 +25,8 @@ define([ './GroundGeometryUpdater', './Property' ], function( + ApproximateTerrainHeights, + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,9 +36,13 @@ define([ DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, + Rectangle, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, @@ -41,6 +53,9 @@ define([ 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchRectangle = new Rectangle(); function CorridorGeometryOptions(entity) { this.id = entity; @@ -51,6 +66,7 @@ define([ this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; + this.offsetAttribute = undefined; } /** @@ -99,11 +115,13 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)), + offset : undefined, + color : undefined + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -112,17 +130,11 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayCondition, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayCondition - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -153,19 +165,34 @@ define([ var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)), + offset : undefined + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new CorridorOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) - } + attributes : attributes }); }; + CorridorGeometryUpdater.prototype._computeCenter = function(time, result) { + var positions = Property.getValueOrUndefined(this._entity.corridor.positions, time); + if (!defined(positions) || positions.length === 0) { + return; + } + return Cartesian3.clone(positions[Math.floor(positions.length / 2.0)], result); + }; + CorridorGeometryUpdater.prototype._isHidden = function(entity, corridor) { - return !defined(corridor.positions) || GeometryUpdater.prototype._isHidden.call(this, entity, corridor); + return !defined(corridor.positions) || !defined(corridor.width) || GeometryUpdater.prototype._isHidden.call(this, entity, corridor); }; CorridorGeometryUpdater.prototype._isOnTerrain = function(entity, corridor) { @@ -193,20 +220,25 @@ define([ CorridorGeometryUpdater.prototype._setStaticOptions = function(entity, corridor) { var height = corridor.height; + var heightReference = corridor.heightReference; var extrudedHeight = corridor.extrudedHeight; - var granularity = corridor.granularity; - var width = corridor.width; - var cornerType = corridor.cornerType; - var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; + var extrudedHeightReference = corridor.extrudedHeightReference; var options = this._options; - options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; + options.vertexFormat = (this._materialProperty instanceof ColorMaterialProperty) ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.positions = corridor.positions.getValue(Iso8601.MINIMUM_VALUE, options.positions); - options.height = defined(height) ? height.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.extrudedHeight = defined(extrudedHeight) ? extrudedHeight.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.width = defined(width) ? width.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.cornerType = defined(cornerType) ? cornerType.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.width = corridor.width.getValue(Iso8601.MINIMUM_VALUE); + options.granularity = Property.getValueOrUndefined(corridor.granularity, Iso8601.MINIMUM_VALUE); + options.cornerType = Property.getValueOrUndefined(corridor.cornerType, Iso8601.MINIMUM_VALUE); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, Iso8601.MINIMUM_VALUE); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(CorridorGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; CorridorGeometryUpdater.DynamicGeometryUpdater = DynamicCorridorGeometryUpdater; @@ -230,12 +262,24 @@ define([ DynamicCorridorGeometryUpdater.prototype._setOptions = function(entity, corridor, time) { var options = this._options; + var height = corridor.height; + var heightReference = corridor.heightReference; + var extrudedHeight = corridor.extrudedHeight; + var extrudedHeightReference = corridor.extrudedHeightReference; + options.positions = Property.getValueOrUndefined(corridor.positions, time); options.width = Property.getValueOrUndefined(corridor.width, time); - options.height = Property.getValueOrUndefined(corridor.height, time); - options.extrudedHeight = Property.getValueOrUndefined(corridor.extrudedHeight, time); options.granularity = Property.getValueOrUndefined(corridor.granularity, time); options.cornerType = Property.getValueOrUndefined(corridor.cornerType, time); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, time); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, time); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(CorridorGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; return CorridorGeometryUpdater; diff --git a/Source/DataSources/CorridorGraphics.js b/Source/DataSources/CorridorGraphics.js index a1a7e3e9c8b5..268f4e7da314 100644 --- a/Source/DataSources/CorridorGraphics.js +++ b/Source/DataSources/CorridorGraphics.js @@ -29,7 +29,9 @@ define([ * @param {Property} [options.width] A numeric Property specifying the distance between the edges of the corridor. * @param {Property} [options.cornerType=CornerType.ROUNDED] A {@link CornerType} Property specifying the style of the corners. * @param {Property} [options.height=0] A numeric Property specifying the altitude of the corridor relative to the ellipsoid surface. + * @param {Property} [options.heightReference] A Property specifying what the height is relative to. * @param {Property} [options.extrudedHeight] A numeric Property specifying the altitude of the corridor's extruded face relative to the ellipsoid surface. + * @param {Property} [options.extrudedHeightReference] A Property specifying what the extrudedHeight is relative to. * @param {Property} [options.show=true] A boolean Property specifying the visibility of the corridor. * @param {Property} [options.fill=true] A boolean Property specifying whether the corridor is filled with the provided material. * @param {MaterialProperty} [options.material=Color.WHITE] A Property specifying the material used to fill the corridor. @@ -53,8 +55,12 @@ define([ this._positionsSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; + this._heightReference = undefined; + this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; + this._extrudedHeightReference = undefined; + this._extrudedHeightReferenceSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._width = undefined; @@ -126,6 +132,14 @@ define([ */ height : createPropertyDescriptor('height'), + /** + * Gets or sets the Property specifying the {@link HeightReference}. + * @memberof CorridorGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + heightReference : createPropertyDescriptor('heightReference'), + /** * Gets or sets the numeric Property specifying the altitude of the corridor extrusion. * Setting this property creates a corridor shaped volume starting at height and ending @@ -135,6 +149,14 @@ define([ */ extrudedHeight : createPropertyDescriptor('extrudedHeight'), + /** + * Gets or sets the Property specifying the extruded {@link HeightReference}. + * @memberof CorridorGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + extrudedHeightReference : createPropertyDescriptor('extrudedHeightReference'), + /** * Gets or sets the numeric Property specifying the sampling distance between each latitude and longitude point. * @memberof CorridorGraphics.prototype @@ -237,7 +259,9 @@ define([ result.material = this.material; result.positions = this.positions; result.height = this.height; + result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; + result.extrudedHeightReference = this.extrudedHeightReference; result.granularity = this.granularity; result.width = this.width; result.fill = this.fill; @@ -269,7 +293,9 @@ define([ this.material = defaultValue(this.material, source.material); this.positions = defaultValue(this.positions, source.positions); this.height = defaultValue(this.height, source.height); + this.heightReference = defaultValue(this.heightReference, source.heightReference); this.extrudedHeight = defaultValue(this.extrudedHeight, source.extrudedHeight); + this.extrudedHeightReference = defaultValue(this.extrudedHeightReference, source.extrudedHeightReference); this.granularity = defaultValue(this.granularity, source.granularity); this.width = defaultValue(this.width, source.width); this.fill = defaultValue(this.fill, source.fill); diff --git a/Source/DataSources/EllipseGeometryUpdater.js b/Source/DataSources/EllipseGeometryUpdater.js index b577e28981ba..30fe5088f920 100644 --- a/Source/DataSources/EllipseGeometryUpdater.js +++ b/Source/DataSources/EllipseGeometryUpdater.js @@ -1,4 +1,6 @@ define([ + '../Core/ApproximateTerrainHeights', + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -8,9 +10,13 @@ define([ '../Core/EllipseGeometry', '../Core/EllipseOutlineGeometry', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', + '../Core/Rectangle', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', @@ -19,6 +25,8 @@ define([ './GroundGeometryUpdater', './Property' ], function( + ApproximateTerrainHeights, + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,9 +36,13 @@ define([ EllipseGeometry, EllipseOutlineGeometry, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, + Rectangle, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, @@ -41,6 +53,9 @@ define([ 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchRectangle = new Rectangle(); function EllipseGeometryOptions(entity) { this.id = entity; @@ -54,6 +69,7 @@ define([ this.granularity = undefined; this.stRotation = undefined; this.numberOfVerticalLines = undefined; + this.offsetAttribute = undefined; } /** @@ -102,12 +118,13 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)), + offset : undefined, + color : undefined + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -116,17 +133,11 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -158,17 +169,28 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition), + offset : undefined + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new EllipseOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + EllipseGeometryUpdater.prototype._computeCenter = function(time, result) { + return Property.getValueOrUndefined(this._entity.position, time, result); + }; + EllipseGeometryUpdater.prototype._isHidden = function(entity, ellipse) { var position = entity.position; @@ -201,25 +223,29 @@ define([ }; EllipseGeometryUpdater.prototype._setStaticOptions = function(entity, ellipse) { - var rotation = ellipse.rotation; var height = ellipse.height; + var heightReference = ellipse.heightReference; var extrudedHeight = ellipse.extrudedHeight; - var granularity = ellipse.granularity; - var stRotation = ellipse.stRotation; - var numberOfVerticalLines = ellipse.numberOfVerticalLines; - var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; + var extrudedHeightReference = ellipse.extrudedHeightReference; var options = this._options; - options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; + options.vertexFormat = (this._materialProperty instanceof ColorMaterialProperty) ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.center = entity.position.getValue(Iso8601.MINIMUM_VALUE, options.center); options.semiMajorAxis = ellipse.semiMajorAxis.getValue(Iso8601.MINIMUM_VALUE, options.semiMajorAxis); options.semiMinorAxis = ellipse.semiMinorAxis.getValue(Iso8601.MINIMUM_VALUE, options.semiMinorAxis); - options.rotation = defined(rotation) ? rotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.height = defined(height) ? height.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.extrudedHeight = defined(extrudedHeight) ? extrudedHeight.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.stRotation = defined(stRotation) ? stRotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.numberOfVerticalLines = defined(numberOfVerticalLines) ? numberOfVerticalLines.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.rotation = Property.getValueOrUndefined(ellipse.rotation, Iso8601.MINIMUM_VALUE); + options.granularity = Property.getValueOrUndefined(ellipse.granularity, Iso8601.MINIMUM_VALUE); + options.stRotation = Property.getValueOrUndefined(ellipse.stRotation, Iso8601.MINIMUM_VALUE); + options.numberOfVerticalLines = Property.getValueOrUndefined(ellipse.numberOfVerticalLines, Iso8601.MINIMUM_VALUE); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, Iso8601.MINIMUM_VALUE); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(EllipseGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; EllipseGeometryUpdater.DynamicGeometryUpdater = DynamicEllipseGeometryUpdater; @@ -243,15 +269,27 @@ define([ DynamicEllipseGeometryUpdater.prototype._setOptions = function(entity, ellipse, time) { var options = this._options; + var height = ellipse.height; + var heightReference = ellipse.heightReference; + var extrudedHeight = ellipse.extrudedHeight; + var extrudedHeightReference = ellipse.extrudedHeightReference; + options.center = Property.getValueOrUndefined(entity.position, time, options.center); options.semiMajorAxis = Property.getValueOrUndefined(ellipse.semiMajorAxis, time); options.semiMinorAxis = Property.getValueOrUndefined(ellipse.semiMinorAxis, time); options.rotation = Property.getValueOrUndefined(ellipse.rotation, time); - options.height = Property.getValueOrUndefined(ellipse.height, time); - options.extrudedHeight = Property.getValueOrUndefined(ellipse.extrudedHeight, time); options.granularity = Property.getValueOrUndefined(ellipse.granularity, time); options.stRotation = Property.getValueOrUndefined(ellipse.stRotation, time); options.numberOfVerticalLines = Property.getValueOrUndefined(ellipse.numberOfVerticalLines, time); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, time); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, time); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(EllipseGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; return EllipseGeometryUpdater; diff --git a/Source/DataSources/EllipseGraphics.js b/Source/DataSources/EllipseGraphics.js index bdb5e5f99fd0..c120ffc691c1 100644 --- a/Source/DataSources/EllipseGraphics.js +++ b/Source/DataSources/EllipseGraphics.js @@ -29,7 +29,9 @@ define([ * @param {Property} [options.semiMajorAxis] The numeric Property specifying the semi-major axis. * @param {Property} [options.semiMinorAxis] The numeric Property specifying the semi-minor axis. * @param {Property} [options.height=0] A numeric Property specifying the altitude of the ellipse relative to the ellipsoid surface. + * @param {Property} [options.heightReference] A Property specifying what the height is relative to. * @param {Property} [options.extrudedHeight] A numeric Property specifying the altitude of the ellipse's extruded face relative to the ellipsoid surface. + * @param {Property} [options.extrudedHeightReference] A Property specifying what the extrudedHeight is relative to. * @param {Property} [options.show=true] A boolean Property specifying the visibility of the ellipse. * @param {Property} [options.fill=true] A boolean Property specifying whether the ellipse is filled with the provided material. * @param {MaterialProperty} [options.material=Color.WHITE] A Property specifying the material used to fill the ellipse. @@ -59,8 +61,12 @@ define([ this._materialSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; + this._heightReference = undefined; + this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; + this._extrudedHeightReference = undefined; + this._extrudedHeightReferenceSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._stRotation = undefined; @@ -148,6 +154,14 @@ define([ */ height : createPropertyDescriptor('height'), + /** + * Gets or sets the Property specifying the {@link HeightReference}. + * @memberof EllipseGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + heightReference : createPropertyDescriptor('heightReference'), + /** * Gets or sets the numeric Property specifying the altitude of the ellipse extrusion. * Setting this property creates volume starting at height and ending at this altitude. @@ -156,6 +170,14 @@ define([ */ extrudedHeight : createPropertyDescriptor('extrudedHeight'), + /** + * Gets or sets the Property specifying the extruded {@link HeightReference}. + * @memberof EllipseGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + extrudedHeightReference : createPropertyDescriptor('extrudedHeightReference'), + /** * Gets or sets the numeric Property specifying the angular distance between points on the ellipse. * @memberof EllipseGraphics.prototype @@ -261,7 +283,9 @@ define([ result.show = this.show; result.material = this.material; result.height = this.height; + result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; + result.extrudedHeightReference = this.extrudedHeightReference; result.granularity = this.granularity; result.stRotation = this.stRotation; result.fill = this.fill; @@ -295,7 +319,9 @@ define([ this.show = defaultValue(this.show, source.show); this.material = defaultValue(this.material, source.material); this.height = defaultValue(this.height, source.height); + this.heightReference = defaultValue(this.heightReference, source.heightReference); this.extrudedHeight = defaultValue(this.extrudedHeight, source.extrudedHeight); + this.extrudedHeightReference = defaultValue(this.extrudedHeightReference, source.extrudedHeightReference); this.granularity = defaultValue(this.granularity, source.granularity); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.fill = defaultValue(this.fill, source.fill); diff --git a/Source/DataSources/GeometryVisualizer.js b/Source/DataSources/GeometryVisualizer.js index 9ea93859248f..2f70c1eb260a 100644 --- a/Source/DataSources/GeometryVisualizer.js +++ b/Source/DataSources/GeometryVisualizer.js @@ -142,23 +142,31 @@ define([ this._changedObjects = new AssociativeArray(); var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; - this._outlineBatches = new Array(numberOfShadowModes); - this._closedColorBatches = new Array(numberOfShadowModes); - this._closedMaterialBatches = new Array(numberOfShadowModes); - this._openColorBatches = new Array(numberOfShadowModes); - this._openMaterialBatches = new Array(numberOfShadowModes); + this._outlineBatches = new Array(numberOfShadowModes*2); + this._closedColorBatches = new Array(numberOfShadowModes*2); + this._closedMaterialBatches = new Array(numberOfShadowModes*2); + this._openColorBatches = new Array(numberOfShadowModes*2); + this._openMaterialBatches = new Array(numberOfShadowModes*2); var supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain(scene); this._supportsMaterialsforEntitiesOnTerrain = supportsMaterialsforEntitiesOnTerrain; var i; for (i = 0; i < numberOfShadowModes; ++i) { - this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i); + this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i, false); + this._outlineBatches[numberOfShadowModes + i] = new StaticOutlineGeometryBatch(primitives, scene, i, true); - this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i); - this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i); - this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i); - this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i); + this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, true); + this._closedColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, false); + + this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, true); + this._closedMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, false); + + this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, true); + this._openColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, false); + + this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, true); + this._openMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, false); } var numberOfClassificationTypes = ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES; @@ -390,8 +398,13 @@ define([ shadows = updater.shadowsProperty.getValue(time); } + var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; if (updater.outlineEnabled) { - this._outlineBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._outlineBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._outlineBatches[shadows].add(time, updater); + } } if (updater.fillEnabled) { @@ -408,12 +421,24 @@ define([ } } else if (updater.isClosed) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - this._closedColorBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._closedColorBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._closedColorBatches[shadows].add(time, updater); + } + } else if (defined(updater.terrainOffsetProperty)) { + this._closedMaterialBatches[numberOfShadowModes + shadows].add(time, updater); } else { this._closedMaterialBatches[shadows].add(time, updater); } } else if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - this._openColorBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._openColorBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._openColorBatches[shadows].add(time, updater); + } + } else if (defined(updater.terrainOffsetProperty)) { + this._openMaterialBatches[numberOfShadowModes + shadows].add(time, updater); } else { this._openMaterialBatches[shadows].add(time, updater); } diff --git a/Source/DataSources/GroundGeometryUpdater.js b/Source/DataSources/GroundGeometryUpdater.js index 43f2a98afc14..76c2c7b1810e 100644 --- a/Source/DataSources/GroundGeometryUpdater.js +++ b/Source/DataSources/GroundGeometryUpdater.js @@ -1,19 +1,37 @@ define([ + '../Core/ApproximateTerrainHeights', + '../Core/Cartesian3', + '../Core/Check', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', + '../Core/DeveloperError', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', '../Core/oneTimeWarning', + '../Scene/HeightReference', + './CallbackProperty', './ConstantProperty', - './GeometryUpdater' + './GeometryUpdater', + './Property', + './TerrainOffsetProperty' ], function( + ApproximateTerrainHeights, + Cartesian3, + Check, defaultValue, defined, defineProperties, + DeveloperError, + GeometryOffsetAttribute, Iso8601, oneTimeWarning, + HeightReference, + CallbackProperty, ConstantProperty, - GeometryUpdater) { + GeometryUpdater, + Property, + TerrainOffsetProperty) { 'use strict'; var defaultZIndex = new ConstantProperty(0); @@ -33,6 +51,7 @@ define([ GeometryUpdater.call(this, options); this._zIndex = 0; + this._terrainOffsetProperty = undefined; } if (defined(Object.create)) { @@ -51,9 +70,17 @@ define([ get: function() { return this._zIndex; } + }, + + terrainOffsetProperty: { + get: function() { + return this._terrainOffsetProperty; + } } }); + GroundGeometryUpdater.prototype._computeCenter = DeveloperError.throwInstantiationError; + GroundGeometryUpdater.prototype._onEntityPropertyChanged = function(entity, propertyName, newValue, oldValue) { GeometryUpdater.prototype._onEntityPropertyChanged.call(this, entity, propertyName, newValue, oldValue); if (this._observedPropertyNames.indexOf(propertyName) === -1) { @@ -69,6 +96,71 @@ define([ } this._zIndex = defaultValue(geometry.zIndex, defaultZIndex); + + if (defined(this._terrainOffsetProperty)) { + this._terrainOffsetProperty.destroy(); + this._terrainOffsetProperty = undefined; + } + + var heightReferenceProperty = geometry.heightReference; + var extrudedHeightReferenceProperty = geometry.extrudedHeightReference; + + if (defined(heightReferenceProperty) || defined(extrudedHeightReferenceProperty)) { + var centerPosition = new CallbackProperty(this._computeCenter.bind(this), !this._dynamic); + this._terrainOffsetProperty = new TerrainOffsetProperty(this._scene, heightReferenceProperty, extrudedHeightReferenceProperty, centerPosition); + } + }; + + /** + * @private + */ + GroundGeometryUpdater.getGeometryHeight = function(heightProperty, heightReferenceProperty, time) { + var heightReference = Property.getValueOrDefault(heightReferenceProperty, time, HeightReference.NONE); + if (heightReference !== HeightReference.CLAMP_TO_GROUND) { + return Property.getValueOrUndefined(heightProperty, time); + } + return 0.0; + }; + + /** + * @private + */ + GroundGeometryUpdater.getGeometryExtrudedHeight = function(extrudedHeightProperty, extrudedHeightReferenceProperty, time) { + var heightReference = Property.getValueOrDefault(extrudedHeightReferenceProperty, time, HeightReference.NONE); + if (heightReference !== HeightReference.CLAMP_TO_GROUND) { + return Property.getValueOrUndefined(extrudedHeightProperty, time); + } + + return GroundGeometryUpdater.CLAMP_TO_GROUND; + }; + + /** + * @private + */ + GroundGeometryUpdater.CLAMP_TO_GROUND = 'clamp'; + + /** + * @private + */ + GroundGeometryUpdater.computeGeometryOffsetAttribute = function(heightReferenceProperty, extrudedHeightReferenceProperty, time) { + var heightReference = Property.getValueOrDefault(heightReferenceProperty, time, HeightReference.NONE); + var extrudedHeightReference = Property.getValueOrDefault(extrudedHeightReferenceProperty, time, HeightReference.NONE); + + var n = 0; + if (heightReference !== HeightReference.NONE) { + n++; + } + if (extrudedHeightReference === HeightReference.RELATIVE_TO_GROUND) { + n++; + } + if (n === 2) { + return GeometryOffsetAttribute.ALL; + } + if (n === 1) { + return GeometryOffsetAttribute.TOP; + } + + return undefined; }; return GroundGeometryUpdater; diff --git a/Source/DataSources/PolygonGeometryUpdater.js b/Source/DataSources/PolygonGeometryUpdater.js index c56094f34b35..a26e346dcccd 100644 --- a/Source/DataSources/PolygonGeometryUpdater.js +++ b/Source/DataSources/PolygonGeometryUpdater.js @@ -1,4 +1,6 @@ define([ + '../Core/ApproximateTerrainHeights', + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -6,13 +8,17 @@ define([ '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/isArray', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', '../Core/PolygonGeometry', '../Core/PolygonHierarchy', '../Core/PolygonOutlineGeometry', + '../Core/Rectangle', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', @@ -21,6 +27,8 @@ define([ './GroundGeometryUpdater', './Property' ], function( + ApproximateTerrainHeights, + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,13 +36,17 @@ define([ DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, GeometryInstance, + GeometryOffsetAttribute, isArray, Iso8601, + OffsetGeometryInstanceAttribute, PolygonGeometry, PolygonHierarchy, PolygonOutlineGeometry, + Rectangle, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, @@ -45,6 +57,9 @@ define([ 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchRectangle = new Rectangle(); function PolygonGeometryOptions(entity) { this.id = entity; @@ -57,6 +72,7 @@ define([ this.extrudedHeight = undefined; this.granularity = undefined; this.stRotation = undefined; + this.offsetAttribute = undefined; } /** @@ -105,12 +121,13 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)), + offset : undefined, + color : undefined + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -119,17 +136,10 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -161,17 +171,45 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition), + offset : undefined + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new PolygonOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + PolygonGeometryUpdater.prototype._computeCenter = function(time, result) { + var positions = Property.getValueOrUndefined(this._entity.polygon.hierarchy, time); + if (defined(positions) && !isArray(positions)) { + positions = positions.positions; + } + if (positions.length === 0) { + return; + } + + var centroid = Cartesian3.clone(Cartesian3.ZERO, result); + var length = positions.length; + for (var i = 0; i < length; i++) { + centroid = Cartesian3.add(positions[i], centroid, centroid); + } + centroid = Cartesian3.multiplyByScalar(centroid, 1 / length, centroid); + if (defined(this._scene.globe)) { + centroid = this._scene.globe.ellipsoid.scaleToGeodeticSurface(centroid, centroid); + } + return centroid; + }; + PolygonGeometryUpdater.prototype._isHidden = function(entity, polygon) { return !defined(polygon.hierarchy) || GeometryUpdater.prototype._isHidden.call(this, entity, polygon); }; @@ -208,10 +246,13 @@ define([ hierarchyValue = new PolygonHierarchy(hierarchyValue); } - var heightValue = Property.getValueOrUndefined(polygon.height, Iso8601.MINIMUM_VALUE); - var closeTopValue = Property.getValueOrDefault(polygon.closeTop, Iso8601.MINIMUM_VALUE, true); - var closeBottomValue = Property.getValueOrDefault(polygon.closeBottom, Iso8601.MINIMUM_VALUE, true); - var extrudedHeightValue = Property.getValueOrUndefined(polygon.extrudedHeight, Iso8601.MINIMUM_VALUE); + var height = polygon.height; + var heightReference = polygon.heightReference; + var extrudedHeight = polygon.extrudedHeight; + var extrudedHeightReference = polygon.extrudedHeightReference; + + var heightValue = GroundGeometryUpdater.getGeometryHeight(height, heightReference, Iso8601.MINIMUM_VALUE); + var extrudedHeightValue = Property.getValueOrUndefined(extrudedHeight, Iso8601.MINIMUM_VALUE); var perPositionHeightValue = Property.getValueOrUndefined(polygon.perPositionHeight, Iso8601.MINIMUM_VALUE); if (defined(extrudedHeightValue) && !defined(heightValue) && !defined(perPositionHeightValue)) { @@ -219,13 +260,20 @@ define([ } options.polygonHierarchy = hierarchyValue; - options.height = heightValue; - options.extrudedHeight = extrudedHeightValue; options.granularity = Property.getValueOrUndefined(polygon.granularity, Iso8601.MINIMUM_VALUE); options.stRotation = Property.getValueOrUndefined(polygon.stRotation, Iso8601.MINIMUM_VALUE); options.perPositionHeight = perPositionHeightValue; - options.closeTop = closeTopValue; - options.closeBottom = closeBottomValue; + options.closeTop = Property.getValueOrDefault(polygon.closeTop, Iso8601.MINIMUM_VALUE, true); + options.closeBottom = Property.getValueOrDefault(polygon.closeBottom, Iso8601.MINIMUM_VALUE, true); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + options.height = heightValue; + + extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(PolygonGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; PolygonGeometryUpdater.prototype._getIsClosed = function(options) { @@ -255,6 +303,11 @@ define([ DyanmicPolygonGeometryUpdater.prototype._setOptions = function(entity, polygon, time) { var options = this._options; + var height = polygon.height; + var heightReference = polygon.heightReference; + var extrudedHeight = polygon.extrudedHeight; + var extrudedHeightReference = polygon.extrudedHeightReference; + var hierarchy = Property.getValueOrUndefined(polygon.hierarchy, time); if (isArray(hierarchy)) { options.polygonHierarchy = new PolygonHierarchy(hierarchy); @@ -262,13 +315,28 @@ define([ options.polygonHierarchy = hierarchy; } - options.height = Property.getValueOrUndefined(polygon.height, time); - options.extrudedHeight = Property.getValueOrUndefined(polygon.extrudedHeight, time); + var heightValue = GroundGeometryUpdater.getGeometryHeight(height, heightReference, time); + var extrudedHeightValue = Property.getValueOrUndefined(extrudedHeight, time); + var perPositionHeightValue = Property.getValueOrUndefined(polygon.perPositionHeight, time); + + if (defined(extrudedHeightValue) && !defined(heightValue) && !defined(perPositionHeightValue)) { + heightValue = 0; + } + options.granularity = Property.getValueOrUndefined(polygon.granularity, time); options.stRotation = Property.getValueOrUndefined(polygon.stRotation, time); options.perPositionHeight = Property.getValueOrUndefined(polygon.perPositionHeight, time); options.closeTop = Property.getValueOrDefault(polygon.closeTop, time, true); options.closeBottom = Property.getValueOrDefault(polygon.closeBottom, time, true); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + options.height = heightValue; + + extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, time); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(PolygonGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; return PolygonGeometryUpdater; diff --git a/Source/DataSources/PolygonGraphics.js b/Source/DataSources/PolygonGraphics.js index 99c5243d86fe..5eb968df4d59 100644 --- a/Source/DataSources/PolygonGraphics.js +++ b/Source/DataSources/PolygonGraphics.js @@ -27,7 +27,9 @@ define([ * @param {Object} [options] Object with the following properties: * @param {Property} [options.hierarchy] A Property specifying the {@link PolygonHierarchy}. * @param {Property} [options.height=0] A numeric Property specifying the altitude of the polygon relative to the ellipsoid surface. + * @param {Property} [options.heightReference] A Property specifying what the height is relative to. * @param {Property} [options.extrudedHeight] A numeric Property specifying the altitude of the polygon's extruded face relative to the ellipsoid surface. + * @param {Property} [options.extrudedHeightReference] A Property specifying what the extrudedHeight is relative to. * @param {Property} [options.show=true] A boolean Property specifying the visibility of the polygon. * @param {Property} [options.fill=true] A boolean Property specifying whether the polygon is filled with the provided material. * @param {MaterialProperty} [options.material=Color.WHITE] A Property specifying the material used to fill the polygon. @@ -55,8 +57,12 @@ define([ this._hierarchySubscription = undefined; this._height = undefined; this._heightSubscription = undefined; + this._heightReference = undefined; + this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; + this._extrudedHeightReference = undefined; + this._extrudedHeightReferenceSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._stRotation = undefined; @@ -133,6 +139,14 @@ define([ */ height : createPropertyDescriptor('height'), + /** + * Gets or sets the Property specifying the {@link HeightReference}. + * @memberof PolygonGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + heightReference : createPropertyDescriptor('heightReference'), + /** * Gets or sets the numeric Property specifying the altitude of the polygon extrusion. * If {@link PolygonGraphics#perPositionHeight} is false, the volume starts at {@link PolygonGraphics#height} and ends at this altitude. @@ -142,6 +156,14 @@ define([ */ extrudedHeight : createPropertyDescriptor('extrudedHeight'), + /** + * Gets or sets the Property specifying the extruded {@link HeightReference}. + * @memberof PolygonGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + extrudedHeightReference : createPropertyDescriptor('extrudedHeightReference'), + /** * Gets or sets the numeric Property specifying the angular distance between points on the polygon. * @memberof PolygonGraphics.prototype @@ -260,7 +282,9 @@ define([ result.material = this.material; result.hierarchy = this.hierarchy; result.height = this.height; + result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; + result.extrudedHeightReference = this.extrudedHeightReference; result.granularity = this.granularity; result.stRotation = this.stRotation; result.fill = this.fill; @@ -295,7 +319,9 @@ define([ this.material = defaultValue(this.material, source.material); this.hierarchy = defaultValue(this.hierarchy, source.hierarchy); this.height = defaultValue(this.height, source.height); + this.heightReference = defaultValue(this.heightReference, source.heightReference); this.extrudedHeight = defaultValue(this.extrudedHeight, source.extrudedHeight); + this.extrudedHeightReference = defaultValue(this.extrudedHeightReference, source.extrudedHeightReference); this.granularity = defaultValue(this.granularity, source.granularity); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.fill = defaultValue(this.fill, source.fill); diff --git a/Source/DataSources/RectangleGeometryUpdater.js b/Source/DataSources/RectangleGeometryUpdater.js index 21e6c35a89a0..9206ba1eb4df 100644 --- a/Source/DataSources/RectangleGeometryUpdater.js +++ b/Source/DataSources/RectangleGeometryUpdater.js @@ -1,16 +1,24 @@ define([ + '../Core/ApproximateTerrainHeights', + '../Core/Cartesian3', + '../Core/Cartographic', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/Ellipsoid', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', + '../Core/Rectangle', '../Core/RectangleGeometry', '../Core/RectangleOutlineGeometry', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', @@ -19,18 +27,26 @@ define([ './GroundGeometryUpdater', './Property' ], function( + ApproximateTerrainHeights, + Cartesian3, + Cartographic, Check, Color, ColorGeometryInstanceAttribute, defined, DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, + Ellipsoid, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, + Rectangle, RectangleGeometry, RectangleOutlineGeometry, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, @@ -41,6 +57,11 @@ define([ 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchRectangle = new Rectangle(); + var scratchCenterRect = new Rectangle(); + var scratchCarto = new Cartographic(); function RectangleGeometryOptions(entity) { this.id = entity; @@ -51,6 +72,7 @@ define([ this.granularity = undefined; this.stRotation = undefined; this.rotation = undefined; + this.offsetAttribute = undefined; } /** @@ -99,12 +121,13 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)), + offset : undefined, + color : undefined + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -113,17 +136,10 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -155,17 +171,33 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition), + offset : undefined + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new RectangleOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + RectangleGeometryUpdater.prototype._computeCenter = function(time, result) { + var rect = Property.getValueOrUndefined(this._entity.rectangle.coordinates, time, scratchCenterRect); + if (!defined(rect)) { + return; + } + var center = Rectangle.center(rect, scratchCarto); + return Cartographic.toCartesian(center, Ellipsoid.WGS84, result); + }; + RectangleGeometryUpdater.prototype._isHidden = function(entity, rectangle) { return !defined(rectangle.coordinates) || GeometryUpdater.prototype._isHidden.call(this, entity, rectangle); }; @@ -190,19 +222,25 @@ define([ var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; var height = rectangle.height; + var heightReference = rectangle.heightReference; var extrudedHeight = rectangle.extrudedHeight; - var granularity = rectangle.granularity; - var stRotation = rectangle.stRotation; - var rotation = rectangle.rotation; + var extrudedHeightReference = rectangle.extrudedHeightReference; var options = this._options; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.rectangle = rectangle.coordinates.getValue(Iso8601.MINIMUM_VALUE, options.rectangle); - options.height = defined(height) ? height.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.extrudedHeight = defined(extrudedHeight) ? extrudedHeight.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.stRotation = defined(stRotation) ? stRotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; - options.rotation = defined(rotation) ? rotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.granularity = Property.getValueOrUndefined(rectangle.granularity, Iso8601.MINIMUM_VALUE); + options.stRotation = Property.getValueOrUndefined(rectangle.stRotation, Iso8601.MINIMUM_VALUE); + options.rotation = Property.getValueOrUndefined(rectangle.rotation, Iso8601.MINIMUM_VALUE); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, Iso8601.MINIMUM_VALUE); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, Iso8601.MINIMUM_VALUE); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(RectangleGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; RectangleGeometryUpdater.prototype._getIsClosed = function(options) { @@ -231,12 +269,24 @@ define([ DynamicRectangleGeometryUpdater.prototype._setOptions = function(entity, rectangle, time) { var options = this._options; + var height = rectangle.height; + var heightReference = rectangle.heightReference; + var extrudedHeight = rectangle.extrudedHeight; + var extrudedHeightReference = rectangle.extrudedHeightReference; + options.rectangle = Property.getValueOrUndefined(rectangle.coordinates, time, options.rectangle); - options.height = Property.getValueOrUndefined(rectangle.height, time); - options.extrudedHeight = Property.getValueOrUndefined(rectangle.extrudedHeight, time); options.granularity = Property.getValueOrUndefined(rectangle.granularity, time); options.stRotation = Property.getValueOrUndefined(rectangle.stRotation, time); options.rotation = Property.getValueOrUndefined(rectangle.rotation, time); + options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + options.height = GroundGeometryUpdater.getGeometryHeight(height, heightReference, time); + + var extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight(extrudedHeight, extrudedHeightReference, time); + if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { + extrudedHeightValue = ApproximateTerrainHeights.getApproximateTerrainHeights(RectangleGeometry.computeRectangle(options, scratchRectangle)).minimumTerrainHeight; + } + + options.extrudedHeight = extrudedHeightValue; }; return RectangleGeometryUpdater; diff --git a/Source/DataSources/RectangleGraphics.js b/Source/DataSources/RectangleGraphics.js index 290de41f8569..9840679d75d0 100644 --- a/Source/DataSources/RectangleGraphics.js +++ b/Source/DataSources/RectangleGraphics.js @@ -27,7 +27,9 @@ define([ * @param {Object} [options] Object with the following properties: * @param {Property} [options.coordinates] The Property specifying the {@link Rectangle}. * @param {Property} [options.height=0] A numeric Property specifying the altitude of the rectangle relative to the ellipsoid surface. + * @param {Property} [options.heightReference] A Property specifying what the height is relative to. * @param {Property} [options.extrudedHeight] A numeric Property specifying the altitude of the rectangle's extruded face relative to the ellipsoid surface. + * @param {Property} [options.extrudedHeightReference] A Property specifying what the extrudedHeight is relative to. * @param {Property} [options.show=true] A boolean Property specifying the visibility of the rectangle. * @param {Property} [options.fill=true] A boolean Property specifying whether the rectangle is filled with the provided material. * @param {MaterialProperty} [options.material=Color.WHITE] A Property specifying the material used to fill the rectangle. @@ -53,8 +55,12 @@ define([ this._coordinatesSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; + this._heightReference = undefined; + this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; + this._extrudedHeightReference = undefined; + this._extrudedHeightReferenceSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._stRotation = undefined; @@ -128,6 +134,14 @@ define([ */ height : createPropertyDescriptor('height'), + /** + * Gets or sets the Property specifying the {@link HeightReference}. + * @memberof RectangleGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + heightReference : createPropertyDescriptor('heightReference'), + /** * Gets or sets the numeric Property specifying the altitude of the rectangle extrusion. * Setting this property creates volume starting at height and ending at this altitude. @@ -136,6 +150,14 @@ define([ */ extrudedHeight : createPropertyDescriptor('extrudedHeight'), + /** + * Gets or sets the Property specifying the extruded {@link HeightReference}. + * @memberof RectangleGraphics.prototype + * @type {Property} + * @default HeightReference.NONE + */ + extrudedHeightReference : createPropertyDescriptor('extrudedHeightReference'), + /** * Gets or sets the numeric Property specifying the angular distance between points on the rectangle. * @memberof RectangleGraphics.prototype @@ -239,7 +261,9 @@ define([ result.coordinates = this.coordinates; result.material = this.material; result.height = this.height; + result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; + result.extrudedHeightReference = this.extrudedHeightReference; result.granularity = this.granularity; result.stRotation = this.stRotation; result.rotation = this.rotation; @@ -272,7 +296,9 @@ define([ this.coordinates = defaultValue(this.coordinates, source.coordinates); this.material = defaultValue(this.material, source.material); this.height = defaultValue(this.height, source.height); + this.heightReference = defaultValue(this.heightReference, source.heightReference); this.extrudedHeight = defaultValue(this.extrudedHeight, source.extrudedHeight); + this.extrudedHeightReference = defaultValue(this.extrudedHeightReference, source.extrudedHeightReference); this.granularity = defaultValue(this.granularity, source.granularity); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.rotation = defaultValue(this.rotation, source.rotation); diff --git a/Source/DataSources/StaticGeometryColorBatch.js b/Source/DataSources/StaticGeometryColorBatch.js index 32168a30c123..7f87f7a1f699 100644 --- a/Source/DataSources/StaticGeometryColorBatch.js +++ b/Source/DataSources/StaticGeometryColorBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', @@ -13,11 +15,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, @@ -29,6 +33,8 @@ define([ var colorScratch = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, translucent, appearanceType, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.translucent = translucent; @@ -80,7 +86,7 @@ define([ this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); - if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -245,6 +251,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/StaticGeometryPerMaterialBatch.js b/Source/DataSources/StaticGeometryPerMaterialBatch.js index 4734c5be14fd..0fd9eed26e2c 100644 --- a/Source/DataSources/StaticGeometryPerMaterialBatch.js +++ b/Source/DataSources/StaticGeometryPerMaterialBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', @@ -13,11 +15,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, @@ -28,6 +32,8 @@ define([ var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, appearanceType, materialProperty, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.primitives = primitives; @@ -74,7 +80,7 @@ define([ var id = updater.id; this.updaters.set(id, updater); this.geometry.set(id, updater.createFillGeometryInstance(time)); - if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -234,6 +240,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/StaticOutlineGeometryBatch.js b/Source/DataSources/StaticOutlineGeometryBatch.js index 848154fa3c51..def326371c71 100644 --- a/Source/DataSources/StaticOutlineGeometryBatch.js +++ b/Source/DataSources/StaticOutlineGeometryBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/PerInstanceColorAppearance', '../Scene/Primitive', @@ -12,11 +14,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, PerInstanceColorAppearance, Primitive, @@ -27,6 +31,8 @@ define([ var colorScratch = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, translucent, width, shadows) { this.translucent = translucent; @@ -50,7 +56,7 @@ define([ this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); - if (!updater.hasConstantOutline || !updater.outlineColorProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantOutline || !updater.outlineColorProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -189,6 +195,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/TerrainOffsetProperty.js b/Source/DataSources/TerrainOffsetProperty.js new file mode 100644 index 000000000000..7f9f740cf7d7 --- /dev/null +++ b/Source/DataSources/TerrainOffsetProperty.js @@ -0,0 +1,208 @@ +define([ + '../Core/Cartesian3', + '../Core/Cartographic', + '../Core/Check', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/Event', + '../Core/Iso8601', + '../Core/Math', + '../Scene/HeightReference', + '../Scene/SceneMode', + './Property' +], function( + Cartesian3, + Cartographic, + Check, + defined, + defineProperties, + destroyObject, + Event, + Iso8601, + CesiumMath, + HeightReference, + SceneMode, + Property) { + 'use strict'; + + var scratchPosition = new Cartesian3(); + var scratchCarto = new Cartographic(); + + /** + * @private + */ + function TerrainOffsetProperty(scene, heightReferenceProperty, extrudedHeightReferenceProperty, positionProperty) { + //>>includeStart('debug', pragmas.debug); + Check.defined('scene', scene); + Check.defined('positionProperty', positionProperty); + //>>includeEnd('debug'); + + this._scene = scene; + this._heightReference = heightReferenceProperty; + this._extrudedHeightReference = extrudedHeightReferenceProperty; + this._positionProperty = positionProperty; + + this._position = new Cartesian3(); + this._cartographicPosition = new Cartographic(); + this._normal = new Cartesian3(); + + this._definitionChanged = new Event(); + this._terrainHeight = 0; + this._removeCallbackFunc = undefined; + this._removeEventListener = undefined; + this._removeModeListener = undefined; + + var that = this; + if (defined(scene.globe)) { + this._removeEventListener = scene.terrainProviderChanged.addEventListener(function() { + that._updateClamping(); + }); + this._removeModeListener = scene.morphComplete.addEventListener(function() { + that._updateClamping(); + }); + } + + if (positionProperty.isConstant) { + var position = positionProperty.getValue(Iso8601.MINIMUM_VALUE, scratchPosition); + if (!defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe)) { + return; + } + this._position = Cartesian3.clone(position, this._position); + + this._updateClamping(); + + this._normal = scene.globe.ellipsoid.geodeticSurfaceNormal(position, this._normal); + } + } + + defineProperties(TerrainOffsetProperty.prototype, { + /** + * Gets a value indicating if this property is constant. + * @memberof TerrainOffsetProperty.prototype + * + * @type {Boolean} + * @readonly + */ + isConstant : { + get : function() { + return false; + } + }, + /** + * Gets the event that is raised whenever the definition of this property changes. + * @memberof TerrainOffsetProperty.prototype + * + * @type {Event} + * @readonly + */ + definitionChanged : { + get : function() { + return this._definitionChanged; + } + } + }); + + /** + * @private + */ + TerrainOffsetProperty.prototype._updateClamping = function() { + if (defined(this._removeCallbackFunc)) { + this._removeCallbackFunc(); + } + + var scene = this._scene; + var globe = scene.globe; + var position = this._position; + + if (!defined(globe) || Cartesian3.equals(position, Cartesian3.ZERO)) { + this._terrainHeight = 0; + return; + } + var ellipsoid = globe.ellipsoid; + var surface = globe._surface; + + var that = this; + var cartographicPosition = ellipsoid.cartesianToCartographic(position, this._cartographicPosition); + var height = globe.getHeight(cartographicPosition); + if (defined(height)) { + this._terrainHeight = height; + } else { + this._terrainHeight = 0; + } + + function updateFunction(clampedPosition) { + if (scene.mode === SceneMode.SCENE3D) { + var carto = ellipsoid.cartesianToCartographic(clampedPosition, scratchCarto); + that._terrainHeight = carto.height; + } else { + that._terrainHeight = clampedPosition.x; + } + that.definitionChanged.raiseEvent(); + } + this._removeCallbackFunc = surface.updateHeight(cartographicPosition, updateFunction); + }; + + /** + * Gets the height relative to the terrain based on the positions. + * + * @returns {Cartesian3} The offset + */ + TerrainOffsetProperty.prototype.getValue = function(time, result) { + var heightReference = Property.getValueOrDefault(this._heightReference, time, HeightReference.NONE); + var extrudedHeightReference = Property.getValueOrDefault(this._extrudedHeightReference, time, HeightReference.NONE); + + if (heightReference === HeightReference.NONE && extrudedHeightReference !== HeightReference.RELATIVE_TO_GROUND) { + this._position = Cartesian3.clone(Cartesian3.ZERO, this._position); + return Cartesian3.clone(Cartesian3.ZERO, result); + } + + if (this._positionProperty.isConstant) { + return Cartesian3.multiplyByScalar(this._normal, this._terrainHeight, result); + } + + var scene = this._scene; + var position = this._positionProperty.getValue(time, scratchPosition); + if (!defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe)) { + return Cartesian3.clone(Cartesian3.ZERO, result); + } + + if (Cartesian3.equalsEpsilon(this._position, position, CesiumMath.EPSILON10)) { + return Cartesian3.multiplyByScalar(this._normal, this._terrainHeight, result); + } + + this._position = Cartesian3.clone(position, this._position); + + this._updateClamping(); + + var normal = scene.globe.ellipsoid.geodeticSurfaceNormal(position, this._normal); + return Cartesian3.multiplyByScalar(normal, this._terrainHeight, result); + }; + + TerrainOffsetProperty.prototype.isDestroyed = function() { + return false; + }; + + TerrainOffsetProperty.prototype.destroy = function() { + if (defined(this._removeEventListener)) { + this._removeEventListener(); + } + if (defined(this._removeModeListener)) { + this._removeModeListener(); + } + if (defined(this._removeCallbackFunc)) { + this._removeCallbackFunc(); + } + return destroyObject(this); + }; + + /** + * A function which creates one or more providers. + * @callback TerrainOffsetProperty~PositionFunction + * @param {JulianDate} time The clock time at which to retrieve the position + * @param {Cartesian3} result The result position + * @returns {Cartesian3} The position at which to do the terrain height check + */ + + return TerrainOffsetProperty; +}); diff --git a/Specs/DataSources/CorridorGeometryUpdaterSpec.js b/Specs/DataSources/CorridorGeometryUpdaterSpec.js index 940a2718d92b..1f4c23f2b3cc 100644 --- a/Specs/DataSources/CorridorGeometryUpdaterSpec.js +++ b/Specs/DataSources/CorridorGeometryUpdaterSpec.js @@ -1,8 +1,11 @@ defineSuite([ 'DataSources/CorridorGeometryUpdater', + 'Core/ApproximateTerrainHeights', 'Core/Cartesian3', 'Core/CornerType', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/TimeInterval', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -20,9 +23,12 @@ defineSuite([ 'Specs/createScene' ], function( CorridorGeometryUpdater, + ApproximateTerrainHeights, Cartesian3, CornerType, + GeometryOffsetAttribute, JulianDate, + CesiumMath, TimeInterval, TimeIntervalCollection, ConstantProperty, @@ -46,15 +52,20 @@ defineSuite([ beforeAll(function() { scene = createScene(); time = JulianDate.now(); + + return ApproximateTerrainHeights.initialize(); }); afterAll(function() { scene.destroyForSpecs(); + + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; }); function createBasicCorridor() { var corridor = new CorridorGraphics(); - corridor.positions = new ConstantProperty(Cartesian3.fromRadiansArray([ + corridor.positions = new ConstantProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -69,13 +80,13 @@ defineSuite([ function createDynamicCorridor() { var entity = createBasicCorridor(); - entity.corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([0, 0, 1, 0, 1, 1, 0, 1])); + entity.corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([0, 0, 1, 0, 1, 1, 0, 1])); return entity; } function createBasicCorridorWithoutHeight() { var corridor = new CorridorGraphics(); - corridor.positions = new ConstantProperty(Cartesian3.fromRadiansArray([ + corridor.positions = new ConstantProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -89,7 +100,7 @@ defineSuite([ function createDynamicCorridorWithoutHeight() { var entity = createBasicCorridorWithoutHeight(); - entity.corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([0, 0, 1, 0, 1, 1, 0, 1])); + entity.corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([0, 0, 1, 0, 1, 1, 0, 1])); return entity; } @@ -193,6 +204,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._cornerType).toEqual(options.cornerType); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -201,11 +213,12 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._cornerType).toEqual(options.cornerType); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { var corridor = new CorridorGraphics(); - corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([ + corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -234,6 +247,7 @@ defineSuite([ expect(options.width).toEqual(corridor.width.getValue()); expect(options.granularity).toEqual(corridor.granularity.getValue()); expect(options.cornerType).toEqual(corridor.cornerType.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -268,6 +282,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(4); }); + it('computes center', function() { + var entity = createBasicCorridor(); + var updater = new CorridorGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqualEpsilon(Cartesian3.fromDegrees(1.0, 1.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/EllipseGeometryUpdaterSpec.js b/Specs/DataSources/EllipseGeometryUpdaterSpec.js index a429a8dd859a..559020da2906 100644 --- a/Specs/DataSources/EllipseGeometryUpdaterSpec.js +++ b/Specs/DataSources/EllipseGeometryUpdaterSpec.js @@ -1,6 +1,8 @@ defineSuite([ 'DataSources/EllipseGeometryUpdater', + 'Core/ApproximateTerrainHeights', 'Core/Cartesian3', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', 'Core/TimeIntervalCollection', 'DataSources/ConstantPositionProperty', @@ -17,7 +19,9 @@ defineSuite([ 'Specs/createScene' ], function( EllipseGeometryUpdater, + ApproximateTerrainHeights, Cartesian3, + GeometryOffsetAttribute, JulianDate, TimeIntervalCollection, ConstantPositionProperty, @@ -40,10 +44,15 @@ defineSuite([ beforeAll(function() { scene = createScene(); time = JulianDate.now(); + + return ApproximateTerrainHeights.initialize(); }); afterAll(function() { scene.destroyForSpecs(); + + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; }); function createBasicEllipse() { @@ -234,6 +243,7 @@ defineSuite([ expect(geometry._height).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -245,6 +255,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._numberOfVerticalLines).toEqual(options.numberOfVerticalLines); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { @@ -265,6 +276,7 @@ defineSuite([ expect(options.semiMajorAxis).toEqual(ellipse.semiMajorAxis.getValue()); expect(options.semiMinorAxis).toEqual(ellipse.semiMinorAxis.getValue()); expect(options.height).toEqual(ellipse.height.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -304,6 +316,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(5); }); + it('computes center', function() { + var entity = createBasicEllipse(); + var updater = new EllipseGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqual(entity.position.getValue(time)); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/GroundGeometryUpdaterSpec.js b/Specs/DataSources/GroundGeometryUpdaterSpec.js new file mode 100644 index 000000000000..6974d335bd18 --- /dev/null +++ b/Specs/DataSources/GroundGeometryUpdaterSpec.js @@ -0,0 +1,115 @@ +defineSuite([ + 'DataSources/GroundGeometryUpdater', + 'Core/ApproximateTerrainHeights', + 'Core/Event', + 'Core/GeometryOffsetAttribute', + 'Core/JulianDate', + 'Core/Rectangle', + 'Scene/HeightReference', + 'DataSources/ConstantProperty' +], function( + GroundGeometryUpdater, + ApproximateTerrainHeights, + Event, + GeometryOffsetAttribute, + JulianDate, + Rectangle, + HeightReference, + ConstantProperty) { + 'use strict'; + + var time = JulianDate.now(); + + beforeAll(function() { + return ApproximateTerrainHeights.initialize(); + }); + + afterAll(function() { + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; + }); + + it('getGeometryHeight works for for height reference NONE and RELATIVE_TO_GROUND', function() { + var expected = 30; + var height = new ConstantProperty(expected); + var heightReference = new ConstantProperty(HeightReference.NONE); + expect(GroundGeometryUpdater.getGeometryHeight(height, heightReference, time)).toEqual(expected); + + heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + expect(GroundGeometryUpdater.getGeometryHeight(height, heightReference, time)).toEqual(expected); + }); + + it('getGeometryHeight works for for height reference CLAMP_TO_GROUND', function() { + var height = new ConstantProperty(50); + var heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + expect(GroundGeometryUpdater.getGeometryHeight(height, heightReference, time)).toEqual(0); + }); + + it('getGeometryExtrudedHeight works for for height reference NONE and RELATIVE_TO_GROUND', function() { + var expected = 30; + var height = new ConstantProperty(expected); + var heightReference = new ConstantProperty(HeightReference.NONE); + expect(GroundGeometryUpdater.getGeometryExtrudedHeight(height, heightReference, time)).toEqual(expected); + + heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + expect(GroundGeometryUpdater.getGeometryExtrudedHeight(height, heightReference, time)).toEqual(expected); + }); + + it('getGeometryExtrudedHeight works for for height reference CLAMP_TO_GROUND', function() { + var height = new ConstantProperty(50); + var heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + expect(GroundGeometryUpdater.getGeometryExtrudedHeight(height, heightReference, time)).toEqual(GroundGeometryUpdater.CLAMP_TO_GROUND); + }); + + it('computeGeometryOffsetAttribute works', function() { + var heightReference; + var extrudedHeightReference; + var result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBeUndefined(); + + heightReference = new ConstantProperty(HeightReference.NONE); + extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBeUndefined(); + + heightReference = new ConstantProperty(HeightReference.NONE); + extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBeUndefined(); + + heightReference = new ConstantProperty(HeightReference.NONE); + extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.TOP); + + heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.TOP); + + heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.TOP); + + heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.ALL); + + heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.TOP); + + heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.TOP); + + heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + result = GroundGeometryUpdater.computeGeometryOffsetAttribute(heightReference, extrudedHeightReference, time); + expect(result).toBe(GeometryOffsetAttribute.ALL); + }); +}); diff --git a/Specs/DataSources/PolygonGeometryUpdaterSpec.js b/Specs/DataSources/PolygonGeometryUpdaterSpec.js index 1e87f87d4d5a..18d40f26e264 100644 --- a/Specs/DataSources/PolygonGeometryUpdaterSpec.js +++ b/Specs/DataSources/PolygonGeometryUpdaterSpec.js @@ -1,7 +1,11 @@ defineSuite([ 'DataSources/PolygonGeometryUpdater', + 'Core/ApproximateTerrainHeights', 'Core/Cartesian3', + 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/PolygonHierarchy', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -19,8 +23,12 @@ defineSuite([ 'Specs/createScene' ], function( PolygonGeometryUpdater, + ApproximateTerrainHeights, Cartesian3, + Ellipsoid, + GeometryOffsetAttribute, JulianDate, + CesiumMath, PolygonHierarchy, TimeIntervalCollection, ConstantProperty, @@ -46,19 +54,23 @@ defineSuite([ scene = createScene(); time = JulianDate.now(); groundPrimitiveSupported = GroundPrimitive.isSupported(scene); + return ApproximateTerrainHeights.initialize(); }); afterAll(function() { scene.destroyForSpecs(); + + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; }); function createBasicPolygon() { var polygon = new PolygonGraphics(); polygon.hierarchy = new ConstantProperty(new PolygonHierarchy(Cartesian3.fromRadiansArray([ - 0, 0, - 1, 0, + -1, -1, + 1, -1, 1, 1, - 0, 1 + -1, 1 ]))); polygon.height = new ConstantProperty(0); var entity = new Entity(); @@ -225,6 +237,7 @@ defineSuite([ expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._closeTop).toEqual(options.closeTop); expect(geometry._closeBottom).toEqual(options.closeBottom); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -232,6 +245,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._perPositionHeight).toEqual(options.perPositionHeight); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('Checks that a polygon with per position heights isn\'t on terrain', function() { @@ -291,6 +305,7 @@ defineSuite([ expect(options.stRotation).toEqual(polygon.stRotation.getValue()); expect(options.closeTop).toEqual(polygon.closeTop.getValue()); expect(options.closeBottom).toEqual(polygon.closeBottom.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -333,6 +348,14 @@ defineSuite([ expect(updater.onTerrain).toBe(false); }); + it('computes center', function() { + var entity = createBasicPolygon(); + var updater = new PolygonGeometryUpdater(entity, scene); + var result = updater._computeCenter(time); + result = Ellipsoid.WGS84.scaleToGeodeticSurface(result, result); + expect(result).toEqualEpsilon(Cartesian3.fromDegrees(0.0, 0.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/PolylineGeometryUpdaterSpec.js b/Specs/DataSources/PolylineGeometryUpdaterSpec.js index cc14250f3aef..67f2e43daff3 100644 --- a/Specs/DataSources/PolylineGeometryUpdaterSpec.js +++ b/Specs/DataSources/PolylineGeometryUpdaterSpec.js @@ -800,5 +800,7 @@ defineSuite([ var instance = updater.createFillGeometryInstance(time); expect(instance.geometry instanceof GroundPolylineGeometry).toBe(false); + + updater.destroy(); }); }, 'WebGL'); diff --git a/Specs/DataSources/RectangleGeometryUpdaterSpec.js b/Specs/DataSources/RectangleGeometryUpdaterSpec.js index 2608b931511f..4f8c74175435 100644 --- a/Specs/DataSources/RectangleGeometryUpdaterSpec.js +++ b/Specs/DataSources/RectangleGeometryUpdaterSpec.js @@ -1,7 +1,10 @@ defineSuite([ 'DataSources/RectangleGeometryUpdater', + 'Core/ApproximateTerrainHeights', 'Core/Cartesian3', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/Rectangle', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -16,8 +19,11 @@ defineSuite([ 'Specs/createScene' ], function( RectangleGeometryUpdater, + ApproximateTerrainHeights, Cartesian3, + GeometryOffsetAttribute, JulianDate, + CesiumMath, Rectangle, TimeIntervalCollection, ConstantProperty, @@ -38,17 +44,22 @@ defineSuite([ beforeAll(function() { scene = createScene(); time = new JulianDate(0, 0); + + return ApproximateTerrainHeights.initialize(); }); afterAll(function() { scene.destroyForSpecs(); + + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; }); function createBasicRectangle() { var rectangle = new RectangleGraphics(); var entity = new Entity(); entity.rectangle = rectangle; - entity.rectangle.coordinates = new ConstantProperty(new Rectangle(0, 0, 1, 1)); + entity.rectangle.coordinates = new ConstantProperty(new Rectangle(-1, -1, 1, 1)); entity.rectangle.height = new ConstantProperty(0); return entity; } @@ -161,12 +172,14 @@ defineSuite([ expect(geometry._surfaceHeight).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; expect(geometry._surfaceHeight).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { @@ -194,6 +207,7 @@ defineSuite([ expect(options.extrudedHeight).toEqual(rectangle.extrudedHeight.getValue()); expect(options.granularity).toEqual(rectangle.granularity.getValue()); expect(options.stRotation).toEqual(rectangle.stRotation.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -224,6 +238,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(3); }); + it('computes center', function() { + var entity = createBasicRectangle(); + var updater = new RectangleGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqualEpsilon(Cartesian3.fromDegrees(0.0, 0.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/TerrainOffsetPropertySpec.js b/Specs/DataSources/TerrainOffsetPropertySpec.js new file mode 100644 index 000000000000..9a0b060c5e4f --- /dev/null +++ b/Specs/DataSources/TerrainOffsetPropertySpec.js @@ -0,0 +1,63 @@ +defineSuite([ + 'DataSources/TerrainOffsetProperty', + 'Core/Cartesian3', + 'Core/Event', + 'Core/JulianDate', + 'Core/Rectangle', + 'Scene/HeightReference', + 'DataSources/CallbackProperty', + 'DataSources/ConstantProperty', + 'Specs/createGlobe', + 'Specs/createScene' +], function( + TerrainOffsetProperty, + Cartesian3, + Event, + JulianDate, + Rectangle, + HeightReference, + CallbackProperty, + ConstantProperty, + createGlobe, + createScene) { + 'use strict'; + + var scene; + var time = JulianDate.now(); + beforeAll(function() { + scene = createScene(); + scene.globe = createGlobe(); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + it('can construct and destroy', function() { + var position = new CallbackProperty(jasmine.createSpy(), false); + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + var property = new TerrainOffsetProperty(scene, height, extrudedHeight, position); + expect(property.isConstant).toBe(false); + expect(property.getValue(time)).toEqual(Cartesian3.ZERO); + property.destroy(); + expect(property.isDestroyed()).toBe(true); + }); + + it('throws without scene', function() { + var position = new CallbackProperty(jasmine.createSpy(), false); + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + expect(function() { + return new TerrainOffsetProperty(undefined, height, extrudedHeight, position); + }).toThrowDeveloperError(); + }); + + it('throws without position', function() { + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + expect(function() { + return new TerrainOffsetProperty(scene, height, extrudedHeight, undefined); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/createGeometryUpdaterGroundGeometrySpecs.js b/Specs/createGeometryUpdaterGroundGeometrySpecs.js index b0ed38ab479c..9a7f3af4efb7 100644 --- a/Specs/createGeometryUpdaterGroundGeometrySpecs.js +++ b/Specs/createGeometryUpdaterGroundGeometrySpecs.js @@ -1,20 +1,24 @@ define([ 'Core/Color', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', 'DataSources/ColorMaterialProperty', 'DataSources/ConstantProperty', 'DataSources/SampledProperty', 'Scene/ClassificationType', 'Scene/GroundPrimitive', + 'Scene/HeightReference', 'Scene/PrimitiveCollection' ], function( Color, + GeometryOffsetAttribute, JulianDate, ColorMaterialProperty, ConstantProperty, SampledProperty, ClassificationType, GroundPrimitive, + HeightReference, PrimitiveCollection) { 'use strict'; @@ -119,6 +123,108 @@ define([ expect(updater.onTerrain).toBe(false); }); + it('Creates geometry with no offsetAttribute when geometry is on terrain', function() { + var entity = createEntity(); + + var updater = new Updater(entity, getScene()); + + var instance; + var geometry; + instance = updater.createFillGeometryInstance(time); + geometry = instance.geometry; + expect(geometry._offsetAttribute).toBeUndefined(); + }); + + it('Creates geometry with expected offsetAttribute based on height and extrudedHeight', function() { + var entity = createEntity(); + var graphics = entity[geometryPropertyName]; + graphics.outline = true; + graphics.outlineColor = Color.BLACK; + graphics.height = new ConstantProperty(20.0); + graphics.extrudedHeight = new ConstantProperty(0.0); + var updater = new Updater(entity, getScene()); + + var instance; + + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.heightReference = new ConstantProperty(HeightReference.NONE); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.heightReference = new ConstantProperty(HeightReference.NONE); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.heightReference = new ConstantProperty(HeightReference.NONE); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + + graphics.heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + }); + it('color material sets onTerrain to true', function() { var entity = createEntity(); var geometry = entity[geometryPropertyName];