diff --git a/Apps/Sandcastle/gallery/Billboards.html b/Apps/Sandcastle/gallery/Billboards.html index 6b458e698e51..39ddbc541d23 100644 --- a/Apps/Sandcastle/gallery/Billboards.html +++ b/Apps/Sandcastle/gallery/Billboards.html @@ -162,6 +162,26 @@ image.src = '../images/facility.gif'; } + function billboardTranslucencyByDistance(scene, ellipsoid) { + Sandcastle.declare(billboardTranslucencyByDistance); // For highlighting in Sandcastle. + var image = new Image(); + image.onload = function() { + var billboards = new Cesium.BillboardCollection(); + var textureAtlas = scene.getContext().createTextureAtlas({ + image : image + }); + billboards.setTextureAtlas(textureAtlas); + + billboards.add({ + position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-75.59777, 40.03883)), + imageIndex : 0, + translucencyByDistance : new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.2) + }); + scene.getPrimitives().add(billboards); + }; + image.src = '../images/Cesium_Logo_overlay.png'; + } + function addPointBillboards(scene, ellipsoid) { Sandcastle.declare(addPointBillboards); // For highlighting in Sandcastle. // A white circle is drawn into a 2D canvas. The canvas is used as @@ -430,6 +450,16 @@ }; button.textContent = 'Scale by viewer distance'; toolbar.appendChild(button); + + button = document.createElement('button'); + button.className = 'cesium-button'; + button.onclick = function() { + primitives.removeAll(); + billboardTranslucencyByDistance(scene, ellipsoid); + Sandcastle.highlight(billboardTranslucencyByDistance); + }; + button.textContent = 'Fade by viewer distance'; + toolbar.appendChild(button); } var widget = new Cesium.CesiumWidget('cesiumContainer'); diff --git a/Apps/Sandcastle/gallery/Labels.html b/Apps/Sandcastle/gallery/Labels.html index 012d87530a30..e9c5badf8d64 100644 --- a/Apps/Sandcastle/gallery/Labels.html +++ b/Apps/Sandcastle/gallery/Labels.html @@ -90,6 +90,22 @@ scene.getPrimitives().add(labels); } + function labelTranslucencyByDistance(scene, ellipsoid) { + Sandcastle.declare(labelTranslucencyByDistance); // For highlighting in Sandcastle. + var labels = new Cesium.LabelCollection(); + labels.add({ + position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-73.94, 40.67)), + text : 'New York', + translucencyByDistance : new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e8, 0.0) + }); + labels.add({ + position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-84.39, 33.75)), + text : 'Atlanta', + translucencyByDistance : new Cesium.NearFarScalar(1.5e5, 1.0, 1.5e7, 0.0) + }); + scene.getPrimitives().add(labels); + } + function createButtons(widget) { var ellipsoid = widget.centralBody.getEllipsoid(); var scene = widget.scene; @@ -135,6 +151,16 @@ }; button.textContent = 'Add labels in reference frame'; toolbar.appendChild(button); + + button = document.createElement('button'); + button.className = 'cesium-button'; + button.onclick = function() { + primitives.removeAll(); + labelTranslucencyByDistance(scene, ellipsoid); + Sandcastle.highlight(labelTranslucencyByDistance); + }; + button.textContent = 'Fade label by distance'; + toolbar.appendChild(button); } var widget = new Cesium.CesiumWidget('cesiumContainer'); diff --git a/CHANGES.md b/CHANGES.md index d6619f587eb0..a50b6d51b5fe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ Beta Releases * From `Matrix4`: `getTranslation`, `getRotation`, `inverse`, `inverseTransformation`, `multiplyByTranslation`, `multiplyByUniformScale`, `multiplyByPoint` Code that previously looked like `matrix.toArray();` should now look like `Matrix3.toArray(matrix);`. + * Renamed `DynamicBillboard.nearFarScalar` to `DynamicBillboard.scaleByDistance`. +* Added `Billboard.setTranslucencyByDistance`, `Label.setTranslucencyByDistance`, `DynamicBillboard.translucencyByDistance`, and `DynamicLabel.translucencyByDistance` to control minimum/maximum translucency based on camera distance. * Added `Appearances` tab to Sandcastle with an example for each geometry appearance. * Added `options` argument to the `EllipsoidPrimitive` constructor. diff --git a/Source/DynamicScene/DynamicBillboard.js b/Source/DynamicScene/DynamicBillboard.js index 3cef308b7664..c8a48fdb3ef2 100644 --- a/Source/DynamicScene/DynamicBillboard.js +++ b/Source/DynamicScene/DynamicBillboard.js @@ -33,6 +33,8 @@ define(['../Core/defaultValue', this._eyeOffset = undefined; this._pixelOffset = undefined; this._show = undefined; + this._scaleByDistance = undefined; + this._translucencyByDistance = undefined; this._propertyChanged = new Event(); }; @@ -140,7 +142,15 @@ define(['../Core/defaultValue', * @memberof DynamicBillboard.prototype * @type {Property} */ - nearFarScalar : createDynamicPropertyDescriptor('nearFarScalar', '_nearFarScalar') + scaleByDistance : createDynamicPropertyDescriptor('scaleByDistance', '_scaleByDistance'), + + /** + * Gets or sets the {@link NearFarScalar} {@link Property} used to set translucency based on distance. + * If undefined, a constant size is used. + * @memberof DynamicBillboard.prototype + * @type {Property} + */ + translucencyByDistance : createDynamicPropertyDescriptor('translucencyByDistance', '_translucencyByDistance') }); /** @@ -166,7 +176,8 @@ define(['../Core/defaultValue', result.verticalOrigin = this._verticalOrigin; result.width = this._width; result.height = this._height; - result.nearFarScalar = this._nearFarScalar; + result.scaleByDistance = this._scaleByDistance; + result.translucencyByDistance = this._translucencyByDistance; return result; }; @@ -194,7 +205,8 @@ define(['../Core/defaultValue', this.verticalOrigin = defaultValue(this._verticalOrigin, source._verticalOrigin); this.width = defaultValue(this._width, source._width); this.height = defaultValue(this._height, source._height); - this.nearFarScalar = defaultValue(this._nearFarScalar, source._nearFarScalar); + this.scaleByDistance = defaultValue(this._scaleByDistance, source._scaleByDistance); + this.translucencyByDistance = defaultValue(this._translucencyByDistance, source._translucencyByDistance); }; return DynamicBillboard; diff --git a/Source/DynamicScene/DynamicBillboardVisualizer.js b/Source/DynamicScene/DynamicBillboardVisualizer.js index 7d9dccbf6988..60ac058cc921 100644 --- a/Source/DynamicScene/DynamicBillboardVisualizer.js +++ b/Source/DynamicScene/DynamicBillboardVisualizer.js @@ -352,10 +352,15 @@ define([ billboard.setHeight(property.getValue(time)); } - property = dynamicBillboard._nearFarScalar; + property = dynamicBillboard._scaleByDistance; if (defined(property)) { billboard.setScaleByDistance(property.getValue(time)); } + + property = dynamicBillboard._translucencyByDistance; + if (defined(property)) { + billboard.setTranslucencyByDistance(property.getValue(time)); + } } DynamicBillboardVisualizer.prototype._onObjectsRemoved = function(dynamicObjectCollection, added, dynamicObjects) { diff --git a/Source/DynamicScene/DynamicLabel.js b/Source/DynamicScene/DynamicLabel.js index 5d1d0d2a6cd8..d479d0576d17 100644 --- a/Source/DynamicScene/DynamicLabel.js +++ b/Source/DynamicScene/DynamicLabel.js @@ -32,6 +32,7 @@ define(['../Core/defaultValue', this._pixelOffset = undefined; this._scale = undefined; this._show = undefined; + this._translucencyByDistance = undefined; this._propertyChanged = new Event(); }; @@ -129,7 +130,15 @@ define(['../Core/defaultValue', * @memberof DynamicLabel.prototype * @type {Property} */ - show : createDynamicPropertyDescriptor('show', '_show') + show : createDynamicPropertyDescriptor('show', '_show'), + + /** + * Gets or sets the {@link NearFarScalar} {@link Property} used to set translucency based on distance. + * If undefined, a constant size is used. + * @memberof DynamicLabel.prototype + * @type {Property} + */ + translucencyByDistance : createDynamicPropertyDescriptor('translucencyByDistance', '_translucencyByDistance') }); /** @@ -155,6 +164,7 @@ define(['../Core/defaultValue', result.verticalOrigin = this.verticalOrigin; result.eyeOffset = this.eyeOffset; result.pixelOffset = this.pixelOffset; + result.translucencyByDistance = this._translucencyByDistance; return result; }; @@ -182,6 +192,7 @@ define(['../Core/defaultValue', this.verticalOrigin = defaultValue(this.verticalOrigin, source.verticalOrigin); this.eyeOffset = defaultValue(this.eyeOffset, source.eyeOffset); this.pixelOffset = defaultValue(this.pixelOffset, source.pixelOffset); + this.translucencyByDistance = defaultValue(this._translucencyByDistance, source._translucencyByDistance); }; return DynamicLabel; diff --git a/Source/DynamicScene/DynamicLabelVisualizer.js b/Source/DynamicScene/DynamicLabelVisualizer.js index 63c87b618f39..070365e50b32 100644 --- a/Source/DynamicScene/DynamicLabelVisualizer.js +++ b/Source/DynamicScene/DynamicLabelVisualizer.js @@ -331,6 +331,11 @@ define([ label.setVerticalOrigin(verticalOrigin); } } + + property = dynamicLabel._translucencyByDistance; + if (defined(property)) { + label.setTranslucencyByDistance(property.getValue(time)); + } } DynamicLabelVisualizer.prototype._onObjectsRemoved = function(dynamicObjectCollection, added, dynamicObjects) { diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index bb076956c389..ab912f5ec8cd 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -53,6 +53,7 @@ define([ * and add new billboards instead of modifying each one. * * @exception {DeveloperError} scaleByDistance.far must be greater than scaleByDistance.near + * @exception {DeveloperError} translucencyByDistance.far must be greater than translucencyByDistance.near * * @see BillboardCollection * @see BillboardCollection#add @@ -68,6 +69,10 @@ define([ if (defined(options.scaleByDistance) && options.scaleByDistance.far <= options.scaleByDistance.near) { throw new DeveloperError('scaleByDistance.far must be greater than scaleByDistance.near.'); } + if (defined(options.translucencyByDistance) && + options.translucencyByDistance.far <= options.translucencyByDistance.near) { + throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.'); + } this._show = defaultValue(options.show, true); @@ -86,6 +91,7 @@ define([ this._width = options.width; this._height = options.height; this._scaleByDistance = options.scaleByDistance; + this._translucencyByDistance = options.translucencyByDistance; this._id = options.id; this._pickId = undefined; @@ -107,7 +113,8 @@ define([ var ROTATION_INDEX = Billboard.ROTATION_INDEX = 9; var ALIGNED_AXIS_INDEX = Billboard.ALIGNED_AXIS_INDEX = 10; var SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX = 11; - Billboard.NUMBER_OF_PROPERTIES = 12; + var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX = 12; + Billboard.NUMBER_OF_PROPERTIES = 13; function makeDirty(billboard, propertyChanged) { var billboardCollection = billboard._billboardCollection; @@ -334,6 +341,59 @@ define([ this._scaleByDistance = NearFarScalar.clone(scale, this._scaleByDistance); }; + /** + * Returns the near and far translucency properties of a Billboard based on the billboard's distance from the camera. + * + * @memberof Billboard + * + * @returns {NearFarScalar} The near/far translucency values based on camera distance to the billboard + * + * @see Billboard#setTranslucencyByDistance + */ + Billboard.prototype.getTranslucencyByDistance = function() { + return this._translucencyByDistance; + }; + + /** + * Sets near and far translucency properties of a Billboard based on the billboard's distance from the camera. + * A billboard's translucency will interpolate between the {@link NearFarScalar#nearValue} and + * {@link NearFarScalar#farValue} while the camera distance falls within the upper and lower bounds + * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. + * Outside of these ranges the billboard's translucency remains clamped to the nearest bound. If undefined, + * translucencyByDistance will be disabled. + * + * @memberof Billboard + * + * @param {NearFarScalar} translucency The configuration of near and far distances and their respective translucency values + * + * @exception {DeveloperError} far distance must be greater than near distance. + * + * @see Billboard#getTranslucencyByDistance + * + * @example + * // Example 1. + * // Set a billboard's translucency to 1.0 when the + * // camera is 1500 meters from the billboard and disappear as + * // the camera distance approaches 8.0e6 meters. + * b.setTranslucencyByDistance(new NearFarScalar(1.5e2, 1.0, 8.0e6, 0.0)); + * + * // Example 2. + * // disable translucency by distance + * b.setTranslucencyByDistance(undefined); + */ + Billboard.prototype.setTranslucencyByDistance = function(translucency) { + if (NearFarScalar.equals(this._translucencyByDistance, translucency)) { + return; + } + + if (translucency.far <= translucency.near) { + throw new DeveloperError('far distance must be greater than near distance.'); + } + + makeDirty(this, TRANSLUCENCY_BY_DISTANCE_INDEX); + this._translucencyByDistance = NearFarScalar.clone(translucency, this._translucencyByDistance); + }; + /** * Returns the 3D Cartesian offset applied to this billboard in eye coordinates. * @@ -902,6 +962,7 @@ define([ Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && + NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && this._id === other._id; }; diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index f4aa7dace989..3d5a764d5473 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -63,6 +63,7 @@ define([ var ROTATION_INDEX = Billboard.ROTATION_INDEX; var ALIGNED_AXIS_INDEX = Billboard.ALIGNED_AXIS_INDEX; var SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX; + var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX; var NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES; // PERFORMANCE_IDEA: Use vertex compression so we don't run out of @@ -78,7 +79,8 @@ define([ pickColor : 7, // pickColor and color shared an index because pickColor is only used during color : 7, // the 'pick' pass and 'color' is only used during the 'color' pass. rotationAndAlignedAxis : 8, - scaleByDistance : 9 + scaleByDistance : 9, + translucencyByDistance : 10 }; // Identifies to the VertexArrayFacade the attributes that are used only for the pick @@ -156,6 +158,10 @@ define([ this._compiledShaderScaleByDistance = false; this._compiledShaderScaleByDistancePick = false; + this._shaderTranslucencyByDistance = false; + this._compiledShaderTranslucencyByDistance = false; + this._compiledShaderTranslucencyByDistancePick = false; + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxSize = 0.0; @@ -213,7 +219,8 @@ define([ BufferUsage.STATIC_DRAW, // COLOR_INDEX BufferUsage.STATIC_DRAW, // ROTATION_INDEX BufferUsage.STATIC_DRAW, // ALIGNED_AXIS_INDEX - BufferUsage.STATIC_DRAW // SCALE_BY_DISTANCE_INDEX + BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX + BufferUsage.STATIC_DRAW // TRANSLUCENCY_BY_DISTANCE_INDEX ]; var that = this; @@ -255,7 +262,6 @@ define([ * horizontalOrigin : HorizontalOrigin.CENTER, * verticalOrigin : VerticalOrigin.CENTER, * scale : 1.0, - * scaleByDistance : new NearFarScalar(5e6, 1.0, 2e7, 0.0), * imageIndex : 0, * color : Color.WHITE, * id : undefined @@ -686,6 +692,11 @@ define([ componentsPerAttribute : 4, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[SCALE_BY_DISTANCE_INDEX] + }, { + index : attributeIndices.translucencyByDistance, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + usage : buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX] }], 4 * numberOfBillboards); // 4 vertices per billboard } @@ -870,9 +881,9 @@ define([ var allPurposeWriters = vafWriters[allPassPurpose]; var writer = allPurposeWriters[attributeIndices.scaleByDistance]; var near = 0.0; - var nearValue = 0.0; - var far = 0.0; - var farValue = 0.0; + var nearValue = 1.0; + var far = 1.0; + var farValue = 1.0; var scale = billboard.getScaleByDistance(); if (defined(scale)) { @@ -894,6 +905,35 @@ define([ writer(i + 3, near, nearValue, far, farValue); } + function writeTranslucencyByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + var i = billboard._index * 4; + var allPurposeWriters = vafWriters[allPassPurpose]; + var writer = allPurposeWriters[attributeIndices.translucencyByDistance]; + var near = 0.0; + var nearValue = 1.0; + var far = 1.0; + var farValue = 1.0; + + var translucency = billboard.getTranslucencyByDistance(); + if (defined(translucency)) { + near = translucency.near; + nearValue = translucency.nearValue; + far = translucency.far; + farValue = translucency.farValue; + + if (nearValue !== 1.0 || farValue !== 1.0) { + // translucency by distance calculation in shader need not be enabled + // until a billboard with near and far !== 1.0 is found + billboardCollection._shaderTranslucencyByDistance = true; + } + } + + writer(i + 0, near, nearValue, far, farValue); + writer(i + 1, near, nearValue, far, farValue); + writer(i + 2, near, nearValue, far, farValue); + writer(i + 3, near, nearValue, far, farValue); + } + function writeBillboard(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { writePosition(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writePixelOffset(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); @@ -904,6 +944,7 @@ define([ writeTextureCoordinatesAndImageSize(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeRotationAndAlignedAxis(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeTranslucencyByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); } function recomputeActualPositions(billboardCollection, billboards, length, frameState, modelMatrix, recomputeBoundingVolume) { @@ -1092,6 +1133,10 @@ define([ writers.push(writeScaleByDistance); } + if (properties[TRANSLUCENCY_BY_DISTANCE_INDEX]) { + writers.push(writeTranslucencyByDistance); + } + vafWriters = this._vaf.writers; if ((billboardsToUpdateLength / billboardsLength) > 0.1) { @@ -1166,19 +1211,23 @@ define([ }); } - if (!defined(this._sp) || (this._shaderRotation && !this._compiledShaderRotation) || - (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance)) { + if (!defined(this._sp) || + (this._shaderRotation && !this._compiledShaderRotation) || + (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance) || + (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistance)) { this._sp = context.getShaderCache().replaceShaderProgram( this._sp, createShaderSource({ defines : [this._shaderRotation ? 'ROTATION' : '', - this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : ''], + this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : '', + this._shaderTranslucencyByDistance ? 'EYE_DISTANCE_TRANSLUCENCY' : ''], sources : [BillboardCollectionVS] }), BillboardCollectionFS, attributeIndices); this._compiledShaderRotation = this._shaderRotation; this._compiledShaderScaleByDistance = this._shaderScaleByDistance; + this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; } va = this._vaf.vaByPurpose[colorPassPurpose]; @@ -1208,13 +1257,15 @@ define([ if (!defined(this._spPick) || (this._shaderRotation && !this._compiledShaderRotationPick) || - (this._shaderScaleByDistance && !this._compiledShaderScaleByDistancePick)) { + (this._shaderScaleByDistance && !this._compiledShaderScaleByDistancePick) || + (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistancePick)) { this._spPick = context.getShaderCache().replaceShaderProgram( this._spPick, createShaderSource({ defines : ['RENDER_FOR_PICK', this._shaderRotation ? 'ROTATION' : '', - this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : ''], + this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : '', + this._shaderTranslucencyByDistance ? 'EYE_DISTANCE_TRANSLUCENCY' : ''], sources : [BillboardCollectionVS] }), createShaderSource({ @@ -1224,6 +1275,7 @@ define([ attributeIndices); this._compiledShaderRotationPick = this._shaderRotation; this._compiledShaderScaleByDistancePick = this._shaderScaleByDistance; + this._compiledShaderTranslucencyByDistancePick = this._shaderTranslucencyByDistance; } va = this._vaf.vaByPurpose[pickPassPurpose]; diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index aac83704d4e9..ec2b824c13fd 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -6,6 +6,7 @@ define([ '../Core/Cartesian3', '../Core/Color', '../Core/defined', + '../Core/NearFarScalar', './Billboard', './LabelStyle', './HorizontalOrigin', @@ -17,6 +18,7 @@ define([ Cartesian3, Color, defined, + NearFarScalar, Billboard, LabelStyle, HorizontalOrigin, @@ -46,6 +48,8 @@ define([ * @alias Label * @internalConstructor * + * @exception {DeveloperError} translucencyByDistance.far must be greater than translucencyByDistance.near + * * @see LabelCollection * @see LabelCollection#add * @@ -54,6 +58,11 @@ define([ var Label = function(options, labelCollection) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); + if (defined(options.translucencyByDistance) && + options.translucencyByDistance.far <= options.translucencyByDistance.near) { + throw new DeveloperError('translucencyByDistance.far must be greater than translucencyByDistance.near.'); + } + this._text = defaultValue(options.text, ''); this._show = defaultValue(options.show, true); this._font = defaultValue(options.font, '30px sans-serif'); @@ -68,6 +77,7 @@ define([ this._position = Cartesian3.clone(defaultValue(options.position, Cartesian3.ZERO)); this._scale = defaultValue(options.scale, 1.0); this._id = options.id; + this._translucencyByDistance = options.translucencyByDistance; this._labelCollection = labelCollection; this._glyphs = []; @@ -452,6 +462,59 @@ define([ } }; + /** + * Returns the near and far translucency properties of a Label based on the label's distance from the camera. + * + * @memberof Label + * + * @returns {NearFarScalar} The near/far translucency values based on camera distance to the billboard + * + * @see Label#setTranslucencyByDistance + */ + Label.prototype.getTranslucencyByDistance = function() { + return this._translucencyByDistance; + }; + + /** + * Sets near and far translucency properties of a Label based on the Label's distance from the camera. + * A label's translucency will interpolate between the {@link NearFarScalar#nearValue} and + * {@link NearFarScalar#farValue} while the camera distance falls within the upper and lower bounds + * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. + * Outside of these ranges the label's translucency remains clamped to the nearest bound. If undefined, + * translucencyByDistance will be disabled. + * + * @memberof Label + * + * @param {NearFarScalar} translucency The configuration of near and far distances and their respective translucency values + * + * @exception {DeveloperError} far distance must be greater than near distance. + * + * @see Label#getTranslucencyByDistance + * + * @example + * // Example 1. + * // Set a label's translucencyByDistance to 1.0 when the + * // camera is 1500 meters from the label and disappear as + * // the camera distance approaches 8.0e6 meters. + * text.setTranslucencyByDistance(new NearFarScalar(1.5e2, 1.0, 8.0e6, 0.0)); + * + * // Example 2. + * // disable translucency by distance + * text.setTranslucencyByDistance(undefined); + */ + Label.prototype.setTranslucencyByDistance = function(value) { + if (NearFarScalar.equals(this._translucencyByDistance, value)) { + return; + } + + if (value.far <= value.near) { + throw new DeveloperError('far distance must be greater than near distance.'); + } + + this._translucencyByDistance = NearFarScalar.clone(value, this._translucencyByDistance); + rebindAllGlyphs(this); + }; + /** * Returns the 3D Cartesian offset applied to this label in eye coordinates. * @@ -739,6 +802,7 @@ define([ Color.equals(this._outlineColor, other._outlineColor) && Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && + NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && this._id === other._id; }; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index d6ec24ff25d1..30cd7a524c36 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -184,6 +184,7 @@ define([ } glyph.billboard.setImageIndex(glyphTextureInfo.index); + glyph.billboard.setTranslucencyByDistance(label._translucencyByDistance); } } @@ -378,7 +379,7 @@ define([ * eyeOffset : Cartesian3.ZERO, * horizontalOrigin : HorizontalOrigin.LEFT, * verticalOrigin : VerticalOrigin.BOTTOM, - * scale : 1.0, + * scale : 1.0 * }); * * // Example 2: Specify only the label's cartographic position, diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index b5699cf95d54..09f9165f2071 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -7,6 +7,7 @@ attribute vec2 pixelOffset; attribute vec4 eyeOffsetAndScale; // eye offset in meters attribute vec4 rotationAndAlignedAxis; attribute vec4 scaleByDistance; // near, nearScale, far, farScale +attribute vec4 translucencyByDistance; // near, nearTrans, far, farTrans #ifdef RENDER_FOR_PICK attribute vec4 pickColor; @@ -24,6 +25,23 @@ varying vec4 v_pickColor; varying vec4 v_color; #endif +float getNearFarScalar(vec4 nearFarScalar, float cameraDistSq) +{ + float valueAtMin = nearFarScalar.y; + float valueAtMax = nearFarScalar.w; + float nearDistanceSq = nearFarScalar.x * nearFarScalar.x; + float farDistanceSq = nearFarScalar.z * nearFarScalar.z; + + // ensure that t will fall within the range of [0.0, 1.0] + cameraDistSq = clamp(cameraDistSq, nearDistanceSq, farDistanceSq); + + float t = (cameraDistSq - nearDistanceSq) / (farDistanceSq - nearDistanceSq); + + t = pow(t, 0.15); + + return mix(valueAtMin, valueAtMax, t); +} + void main() { // Modifying this shader may also require modifications to Billboard.computeScreenSpacePosition @@ -44,8 +62,7 @@ void main() positionEC.xyz *= show; /////////////////////////////////////////////////////////////////////////// - -#ifdef EYE_DISTANCE_SCALING // scale based on eye distance + float lengthSq; if (czm_sceneMode == czm_sceneMode2D) { @@ -58,19 +75,23 @@ void main() lengthSq = dot(positionEC.xyz, positionEC.xyz); } - float scaleAtMin = scaleByDistance.y; - float scaleAtMax = scaleByDistance.w; - float nearDistanceSq = scaleByDistance.x * scaleByDistance.x; - float farDistanceSq = scaleByDistance.z * scaleByDistance.z; - - // ensure that t will fall within the range of [0.0, 1.0] - lengthSq = clamp(lengthSq, nearDistanceSq, farDistanceSq); - - float t = (lengthSq - nearDistanceSq) / (farDistanceSq - nearDistanceSq); - - t = pow(t, 0.15); +#ifdef EYE_DISTANCE_SCALING + scale *= getNearFarScalar(scaleByDistance, lengthSq); + // push vertex behind near plane for clipping + if (scale == 0.0) + { + positionEC.xyz = vec3(0.0); + } +#endif - scale *= mix(scaleAtMin, scaleAtMax, t); + float translucency = 1.0; +#ifdef EYE_DISTANCE_TRANSLUCENCY + translucency = getNearFarScalar(translucencyByDistance, lengthSq); + // push vertex behind near plane for clipping + if (translucency == 0.0) + { + positionEC.xyz = vec3(0.0); + } #endif vec4 positionWC = czm_eyeToWindowCoordinates(positionEC); @@ -113,5 +134,6 @@ void main() v_pickColor = pickColor; #else v_color = color; + v_color.a *= translucency; #endif } diff --git a/Specs/DynamicScene/DynamicBillboardSpec.js b/Specs/DynamicScene/DynamicBillboardSpec.js index 4da47686e7e8..cb916c5a507f 100644 --- a/Specs/DynamicScene/DynamicBillboardSpec.js +++ b/Specs/DynamicScene/DynamicBillboardSpec.js @@ -34,7 +34,8 @@ defineSuite([ source.show = new ConstantProperty(false); source.width = new ConstantProperty(24); source.height = new ConstantProperty(36); - source.nearFarScalar = new ConstantProperty(new NearFarScalar()); + source.scaleByDistance = new ConstantProperty(new NearFarScalar()); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var target = new DynamicBillboard(); target.merge(source); @@ -51,7 +52,8 @@ defineSuite([ expect(target.show).toBe(source.show); expect(target.width).toBe(source.width); expect(target.height).toBe(source.height); - expect(target.nearFarScalar).toBe(source.nearFarScalar); + expect(target.scaleByDistance).toBe(source.scaleByDistance); + expect(target.translucencyByDistance).toBe(source.translucencyByDistance); }); it('merge does not assign assigned properties', function() { @@ -68,7 +70,8 @@ defineSuite([ source.show = new ConstantProperty(false); source.width = new ConstantProperty(24); source.height = new ConstantProperty(36); - source.nearFarScalar = new ConstantProperty(new NearFarScalar()); + source.scaleByDistance = new ConstantProperty(new NearFarScalar()); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var image = new ConstantProperty(''); var rotation = new ConstantProperty(5); @@ -82,7 +85,8 @@ defineSuite([ var show = new ConstantProperty(false); var width = new ConstantProperty(2); var height = new ConstantProperty(3); - var nearFarScalar = new ConstantProperty(new NearFarScalar()); + var scaleByDistance = new ConstantProperty(new NearFarScalar()); + var translucencyByDistance = new ConstantProperty(new NearFarScalar()); var target = new DynamicBillboard(); target.image = image; @@ -97,7 +101,8 @@ defineSuite([ target.show = show; target.width = width; target.height = height; - target.nearFarScalar = nearFarScalar; + target.scaleByDistance = scaleByDistance; + target.translucencyByDistance = translucencyByDistance; target.merge(source); @@ -113,7 +118,8 @@ defineSuite([ expect(target.show).toBe(show); expect(target.width).toBe(width); expect(target.height).toBe(height); - expect(target.nearFarScalar).toBe(nearFarScalar); + expect(target.scaleByDistance).toBe(scaleByDistance); + expect(target.translucencyByDistance).toBe(translucencyByDistance); }); it('clone works', function() { @@ -130,7 +136,8 @@ defineSuite([ source.show = new ConstantProperty(false); source.width = new ConstantProperty(24); source.height = new ConstantProperty(36); - source.nearFarScalar = new ConstantProperty(new NearFarScalar()); + source.scaleByDistance = new ConstantProperty(new NearFarScalar()); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var result = source.clone(); expect(result.image).toBe(source.image); @@ -145,7 +152,8 @@ defineSuite([ expect(result.show).toBe(source.show); expect(result.width).toBe(source.width); expect(result.height).toBe(source.height); - expect(result.nearFarScalar).toBe(source.nearFarScalar); + expect(result.scaleByDistance).toBe(source.scaleByDistance); + expect(result.translucencyByDistance).toBe(source.translucencyByDistance); }); it('merge throws if source undefined', function() { diff --git a/Specs/DynamicScene/DynamicBillboardVisualizerSpec.js b/Specs/DynamicScene/DynamicBillboardVisualizerSpec.js index ae493558e6b6..dcc8ebc90f20 100644 --- a/Specs/DynamicScene/DynamicBillboardVisualizerSpec.js +++ b/Specs/DynamicScene/DynamicBillboardVisualizerSpec.js @@ -153,7 +153,8 @@ defineSuite([ billboard.pixelOffset = new ConstantProperty(new Cartesian2(3, 2)); billboard.width = new ConstantProperty(15); billboard.height = new ConstantProperty(5); - billboard.nearFarScalar = new ConstantProperty(new NearFarScalar()); + billboard.scaleByDistance = new ConstantProperty(new NearFarScalar()); + billboard.translucencyByDistance = new ConstantProperty(new NearFarScalar()); visualizer.update(time); @@ -174,7 +175,8 @@ defineSuite([ expect(bb.getVerticalOrigin()).toEqual(testObject.billboard.verticalOrigin.getValue(time)); expect(bb.getWidth()).toEqual(testObject.billboard.width.getValue(time)); expect(bb.getHeight()).toEqual(testObject.billboard.height.getValue(time)); - expect(bb.getScaleByDistance()).toEqual(testObject.billboard.nearFarScalar.getValue(time)); + expect(bb.getScaleByDistance()).toEqual(testObject.billboard.scaleByDistance.getValue(time)); + expect(bb.getTranslucencyByDistance()).toEqual(testObject.billboard.translucencyByDistance.getValue(time)); } return bb.getShow(); //true once the image is loaded. }); @@ -194,7 +196,8 @@ defineSuite([ billboard.pixelOffset = new ConstantProperty(new Cartesian2(2, 3)); billboard.width = new ConstantProperty(17); billboard.height = new ConstantProperty(12); - billboard.nearFarScalar = new ConstantProperty(new NearFarScalar()); + billboard.scaleByDistance = new ConstantProperty(new NearFarScalar()); + billboard.translucencyByDistance = new ConstantProperty(new NearFarScalar()); waitsFor(function() { visualizer.update(time); @@ -211,7 +214,8 @@ defineSuite([ expect(bb.getPixelOffset()).toEqual(testObject.billboard.pixelOffset.getValue(time)); expect(bb.getWidth()).toEqual(testObject.billboard.width.getValue(time)); expect(bb.getHeight()).toEqual(testObject.billboard.height.getValue(time)); - expect(bb.getScaleByDistance()).toEqual(testObject.billboard.nearFarScalar.getValue(time)); + expect(bb.getScaleByDistance()).toEqual(testObject.billboard.scaleByDistance.getValue(time)); + expect(bb.getTranslucencyByDistance()).toEqual(testObject.billboard.translucencyByDistance.getValue(time)); } return imageReady; }); diff --git a/Specs/DynamicScene/DynamicLabelSpec.js b/Specs/DynamicScene/DynamicLabelSpec.js index e8d499c7ef1f..d148583e4461 100644 --- a/Specs/DynamicScene/DynamicLabelSpec.js +++ b/Specs/DynamicScene/DynamicLabelSpec.js @@ -4,6 +4,7 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Color', + 'Core/NearFarScalar', 'Scene/HorizontalOrigin', 'Scene/VerticalOrigin', 'Scene/LabelStyle', @@ -13,6 +14,7 @@ defineSuite([ Cartesian2, Cartesian3, Color, + NearFarScalar, HorizontalOrigin, VerticalOrigin, LabelStyle, @@ -34,6 +36,7 @@ defineSuite([ source.pixelOffset = new ConstantProperty(Cartesian2.UNIT_X); source.scale = new ConstantProperty(1); source.show = new ConstantProperty(false); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var target = new DynamicLabel(); target.merge(source); @@ -50,6 +53,7 @@ defineSuite([ expect(target.pixelOffset).toBe(source.pixelOffset); expect(target.scale).toBe(source.scale); expect(target.show).toBe(source.show); + expect(target.translucencyByDistance).toBe(source.translucencyByDistance); }); it('merge does not assign assigned properties', function() { @@ -66,6 +70,7 @@ defineSuite([ source.pixelOffset = new ConstantProperty(Cartesian2.UNIT_X); source.scale = new ConstantProperty(1); source.show = new ConstantProperty(false); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var text = new ConstantProperty('my text'); var font = new ConstantProperty('10px serif'); @@ -79,6 +84,7 @@ defineSuite([ var pixelOffset = new ConstantProperty(Cartesian2.UNIT_Y); var scale = new ConstantProperty(2); var show = new ConstantProperty(true); + var translucencyByDistance = new ConstantProperty(new NearFarScalar()); var target = new DynamicLabel(); target.text = text; @@ -93,6 +99,7 @@ defineSuite([ target.pixelOffset = pixelOffset; target.scale = scale; target.show = show; + target.translucencyByDistance = translucencyByDistance; target.merge(source); @@ -108,6 +115,7 @@ defineSuite([ expect(target.pixelOffset).toBe(pixelOffset); expect(target.scale).toBe(scale); expect(target.show).toBe(show); + expect(target.translucencyByDistance).toBe(translucencyByDistance); }); it('clone works', function() { @@ -124,6 +132,7 @@ defineSuite([ source.pixelOffset = new ConstantProperty(Cartesian2.UNIT_X); source.scale = new ConstantProperty(1); source.show = new ConstantProperty(false); + source.translucencyByDistance = new ConstantProperty(new NearFarScalar()); var result = source.clone(); expect(result.text).toBe(source.text); @@ -138,6 +147,7 @@ defineSuite([ expect(result.pixelOffset).toBe(source.pixelOffset); expect(result.scale).toBe(source.scale); expect(result.show).toBe(source.show); + expect(result.translucencyByDistance).toBe(source.translucencyByDistance); }); it('merge throws if source undefined', function() { diff --git a/Specs/DynamicScene/DynamicLabelVisualizerSpec.js b/Specs/DynamicScene/DynamicLabelVisualizerSpec.js index 5cbb1e1afbb8..13e4dd844f19 100644 --- a/Specs/DynamicScene/DynamicLabelVisualizerSpec.js +++ b/Specs/DynamicScene/DynamicLabelVisualizerSpec.js @@ -10,6 +10,7 @@ defineSuite([ 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Color', + 'Core/NearFarScalar', 'Scene/LabelCollection', 'Scene/HorizontalOrigin', 'Scene/VerticalOrigin', @@ -25,6 +26,7 @@ defineSuite([ Cartesian2, Cartesian3, Color, + NearFarScalar, LabelCollection, HorizontalOrigin, VerticalOrigin, @@ -148,6 +150,7 @@ defineSuite([ label.pixelOffset = new ConstantProperty(new Cartesian2(3, 2)); label.scale = new ConstantProperty(12.5); label.show = new ConstantProperty(true); + label.translucencyByDistance = new ConstantProperty(new NearFarScalar()); visualizer.update(time); @@ -169,6 +172,7 @@ defineSuite([ expect(l.getPixelOffset()).toEqual(testObject.label.pixelOffset.getValue(time)); expect(l.getScale()).toEqual(testObject.label.scale.getValue(time)); expect(l.getShow()).toEqual(testObject.label.show.getValue(time)); + expect(l.getTranslucencyByDistance()).toEqual(testObject.label.translucencyByDistance.getValue(time)); testObject.position = new ConstantProperty(new Cartesian3(5678, 1234, 1293434)); label.text = new ConstantProperty('b'); @@ -183,6 +187,7 @@ defineSuite([ label.pixelOffset = new ConstantProperty(new Cartesian2(2, 3)); label.scale = new ConstantProperty(2.5); label.show = new ConstantProperty(true); + label.translucencyByDistance = new ConstantProperty(new NearFarScalar()); visualizer.update(time); expect(l.getPosition()).toEqual(testObject.position.getValue(time)); @@ -198,6 +203,7 @@ defineSuite([ expect(l.getPixelOffset()).toEqual(testObject.label.pixelOffset.getValue(time)); expect(l.getScale()).toEqual(testObject.label.scale.getValue(time)); expect(l.getShow()).toEqual(testObject.label.show.getValue(time)); + expect(l.getTranslucencyByDistance()).toEqual(testObject.label.translucencyByDistance.getValue(time)); label.show = new ConstantProperty(false); visualizer.update(time); diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index 529b8534f6ce..397b221fcd85 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -124,6 +124,7 @@ defineSuite([ expect(b.getRotation()).toEqual(0.0); expect(b.getAlignedAxis()).toEqual(Cartesian3.ZERO); expect(b.getScaleByDistance()).not.toBeDefined(); + expect(b.getTranslucencyByDistance()).not.toBeDefined(); expect(b.getWidth()).not.toBeDefined(); expect(b.getHeight()).not.toBeDefined(); expect(b.getId()).not.toBeDefined(); @@ -148,6 +149,7 @@ defineSuite([ rotation : 1.0, alignedAxis : new Cartesian3(1.0, 2.0, 3.0), scaleByDistance : new NearFarScalar(1.0, 3.0, 1.0e6, 0.0), + translucencyByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0), width : 300.0, height : 200.0, id : 'id' @@ -168,6 +170,7 @@ defineSuite([ expect(b.getRotation()).toEqual(1.0); expect(b.getAlignedAxis()).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.getScaleByDistance()).toEqual(new NearFarScalar(1.0, 3.0, 1.0e6, 0.0)); + expect(b.getTranslucencyByDistance()).toEqual(new NearFarScalar(1.0, 1.0, 1.0e6, 0.0)); expect(b.getWidth()).toEqual(300.0); expect(b.getHeight()).toEqual(200.0); expect(b.getId()).toEqual('id'); @@ -194,6 +197,7 @@ defineSuite([ b.setWidth(300.0); b.setHeight(200.0); b.setScaleByDistance(new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)); + b.setTranslucencyByDistance(new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0)); expect(b.getShow()).toEqual(false); expect(b.getPosition()).toEqual(new Cartesian3(1.0, 2.0, 3.0)); @@ -210,6 +214,7 @@ defineSuite([ expect(b.getRotation()).toEqual(1.0); expect(b.getAlignedAxis()).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.getScaleByDistance()).toEqual(new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)); + expect(b.getTranslucencyByDistance()).toEqual(new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0)); expect(b.getWidth()).toEqual(300.0); expect(b.getHeight()).toEqual(200.0); }); @@ -220,7 +225,13 @@ defineSuite([ expect(b.getScaleByDistance()).not.toBeDefined(); }); - it('set billboard scaleByDistance farValue to 0', function() { + it('disable billboard setTranslucencyByDistance', function() { + var b = billboards.add(); + b.setTranslucencyByDistance(undefined); + expect(b.getTranslucencyByDistance()).not.toBeDefined(); + }); + + it('render billboard with scaleByDistance', function() { billboards.setTextureAtlas(createTextureAtlas([greenImage])); billboards.add({ position : { @@ -232,10 +243,9 @@ defineSuite([ imageIndex : 0 }); - // verify basis ClearCommand.ALL.execute(context); expect(context.readPixels()).toEqual([0, 0, 0, 0]); - // camera at 1.0 above billboard, expect green pixel to be rendered, as scale is near 1.0 + var us = context.getUniformState(); var eye = new Cartesian3(0.0, 0.0, 1.0); var target = Cartesian3.ZERO; @@ -243,15 +253,45 @@ defineSuite([ us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); render(context, frameState, billboards); expect(context.readPixels()).toEqual([0, 255, 0, 255]); - // clear screen ClearCommand.ALL.execute(context); expect(context.readPixels()).toEqual([0, 0, 0, 0]); - // camera at 6.0 above billboard, expect no green pixels to be rendered, as scale is 0.0 + + eye = new Cartesian3(0.0, 0.0, 6.0); + us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); + render(context, frameState, billboards); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + us.update(context, createFrameState(createCamera(context))); + }); + + it('render billboard with translucencyByDistance', function() { + billboards.setTextureAtlas(createTextureAtlas([greenImage])); + billboards.add({ + position : { + x : 0.0, + y : 0.0, + z : 0.0 + }, + translucencyByDistance: new NearFarScalar(1.0, 1.0, 3.0, 0.0), + imageIndex : 0 + }); + + ClearCommand.ALL.execute(context); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + + var us = context.getUniformState(); + var eye = new Cartesian3(0.0, 0.0, 1.0); + var target = Cartesian3.ZERO; + var up = Cartesian3.UNIT_Y; + us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); + render(context, frameState, billboards); + expect(context.readPixels()).toEqual([0, 255, 0, 255]); + ClearCommand.ALL.execute(context); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + eye = new Cartesian3(0.0, 0.0, 6.0); us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); render(context, frameState, billboards); expect(context.readPixels()).toEqual([0, 0, 0, 0]); - // revert framestate us.update(context, createFrameState(createCamera(context))); }); @@ -280,6 +320,31 @@ defineSuite([ }).toThrow(); }); + it('throws setTranslucencyByDistance with nearDistance === farDistance', function() { + var b = billboards.add(); + var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + b.setTranslucencyByDistance(translucency); + }).toThrow(); + }); + + it('throws new billboard with invalid translucencyByDistance (nearDistance === farDistance)', function() { + var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + billboards.add({ + translucencyByDistance : translucency + }); + }).toThrow(); + }); + + it('throws setTranslucencyByDistance with nearDistance > farDistance', function() { + var b = billboards.add(); + var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); + expect(function() { + b.setTranslucencyByDistance(translucency); + }).toThrow(); + }); + it('throws with non number Index', function() { var b = billboards.add(); expect(function() { @@ -1120,6 +1185,28 @@ defineSuite([ expect(pickedObject).not.toBeDefined(); }); + it('pick a billboard using translucencyByDistance', function() { + billboards.setTextureAtlas(createTextureAtlas([whiteImage])); + var b = billboards.add({ + position : { + x : 0.0, + y : 0.0, + z : 0.0 + }, + imageIndex : 0 + }); + + var translucency = new NearFarScalar(1.0, 1.0, 3.0e9, 0.9); + b.setTranslucencyByDistance(translucency); + var pickedObject = pick(context, frameState, billboards, 0, 0); + expect(pickedObject.primitive).toEqual(b); + translucency.nearValue = 0.0; + translucency.farValue = 0.0; + b.setTranslucencyByDistance(translucency); + pickedObject = pick(context, frameState, billboards, 0, 0); + expect(pickedObject).toBeUndefined(); + }); + it('computes screen space position (1)', function() { billboards.setTextureAtlas(createTextureAtlas([whiteImage])); var b = billboards.add({ diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 38270140d77e..55fa591755ed 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -14,6 +14,7 @@ defineSuite([ 'Core/Cartographic', 'Core/Color', 'Core/Math', + 'Core/NearFarScalar', 'Renderer/ClearCommand', 'Scene/HorizontalOrigin', 'Scene/VerticalOrigin', @@ -35,6 +36,7 @@ defineSuite([ Cartographic, Color, CesiumMath, + NearFarScalar, ClearCommand, HorizontalOrigin, VerticalOrigin, @@ -84,6 +86,7 @@ defineSuite([ expect(label.getVerticalOrigin()).toEqual(VerticalOrigin.BOTTOM); expect(label.getScale()).toEqual(1.0); expect(label.getId()).not.toBeDefined(); + expect(label.getTranslucencyByDistance()).not.toBeDefined(); }); it('can add a label with specified values', function() { @@ -111,6 +114,7 @@ defineSuite([ var horizontalOrigin = HorizontalOrigin.LEFT; var verticalOrigin = VerticalOrigin.BOTTOM; var scale = 2.0; + var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var label = labels.add({ show : show, position : position, @@ -125,7 +129,8 @@ defineSuite([ horizontalOrigin : horizontalOrigin, verticalOrigin : verticalOrigin, scale : scale, - id : 'id' + id : 'id', + translucencyByDistance : translucency }); expect(label.getShow()).toEqual(show); @@ -142,6 +147,7 @@ defineSuite([ expect(label.getVerticalOrigin()).toEqual(verticalOrigin); expect(label.getScale()).toEqual(scale); expect(label.getId()).toEqual('id'); + expect(label.getTranslucencyByDistance()).toEqual(translucency); }); it('can specify font using units other than pixels', function() { @@ -638,6 +644,38 @@ defineSuite([ expect(context.readPixels()).not.toEqual([0, 0, 0, 0]); }); + it('render label with translucencyByDistance', function() { + labels.add({ + position : { + x : 0.0, + y : 0.0, + z : 0.0 + }, + text : 'x', + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER, + translucencyByDistance: new NearFarScalar(1.0, 1.0, 3.0, 0.0) + }); + + ClearCommand.ALL.execute(context); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + var us = context.getUniformState(); + var eye = new Cartesian3(0.0, 0.0, 1.0); + var target = Cartesian3.ZERO; + var up = Cartesian3.UNIT_Y; + us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); + render(context, frameState, labels); + expect(context.readPixels()).not.toEqual([0, 0, 0, 0]); + ClearCommand.ALL.execute(context); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + + eye = new Cartesian3(0.0, 0.0, 6.0); + us.update(context, createFrameState(createCamera(context, eye, target, up, 0.1, 10.0))); + render(context, frameState, labels); + expect(context.readPixels()).toEqual([0, 0, 0, 0]); + us.update(context, createFrameState(createCamera(context))); + }); + it('can pick a label', function() { var label = labels.add({ position : { @@ -673,6 +711,29 @@ defineSuite([ expect(pickedObject).toBeUndefined(); }); + it('pick a label using translucencyByDistance', function() { + var label = labels.add({ + position : { + x : 0.0, + y : 0.0, + z : 0.0 + }, + text : 'x', + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER + }); + + var translucency = new NearFarScalar(1.0, 1.0, 3.0e9, 0.9); + label.setTranslucencyByDistance(translucency); + var pickedObject = pick(context, frameState, labels, 0, 0); + expect(pickedObject.primitive).toEqual(label); + translucency.nearValue = 0.0; + translucency.farValue = 0.0; + label.setTranslucencyByDistance(translucency); + pickedObject = pick(context, frameState, labels, 0, 0); + expect(pickedObject).toBeUndefined(); + }); + it('throws when calling get without an index', function() { expect(function() { labels.get(); @@ -798,6 +859,7 @@ defineSuite([ var horizontalOrigin = HorizontalOrigin.LEFT; var verticalOrigin = VerticalOrigin.BOTTOM; var scale = 2.0; + var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); label.setShow(show); label.setPosition(position); @@ -812,6 +874,7 @@ defineSuite([ label.setHorizontalOrigin(horizontalOrigin); label.setVerticalOrigin(verticalOrigin); label.setScale(scale); + label.setTranslucencyByDistance(translucency); expect(label.getShow()).toEqual(show); expect(label.getPosition()).toEqual(position); @@ -826,6 +889,7 @@ defineSuite([ expect(label.getHorizontalOrigin()).toEqual(horizontalOrigin); expect(label.getVerticalOrigin()).toEqual(verticalOrigin); expect(label.getScale()).toEqual(scale); + expect(label.getTranslucencyByDistance()).toEqual(translucency); }); it('is destroyed after being removed', function() { @@ -1513,6 +1577,31 @@ defineSuite([ }).toThrow(); }); + it('Label.setTranslucencyByDistance throws with nearDistance === farDistance', function() { + var label = labels.add(); + var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + label.setTranslucencyByDistance(translucency); + }).toThrow(); + }); + + it('new label throws with invalid translucencyByDistance (nearDistance === farDistance)', function() { + var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); + expect(function() { + labels.add({ + translucencyByDistance : translucency + }); + }).toThrow(); + }); + + it('Label.setTranslucencyByDistance throws with nearDistance > farDistance', function() { + var label = labels.add(); + var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); + expect(function() { + label.setTranslucencyByDistance(translucency); + }).toThrow(); + }); + it('destroys texture atlas when destroying', function() { labels.add({ text : 'a'