diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js
index b09711b63874..061fd8736c03 100644
--- a/packages/engine/Source/Scene/Cesium3DTileset.js
+++ b/packages/engine/Source/Scene/Cesium3DTileset.js
@@ -98,6 +98,8 @@ import Ray from "../Core/Ray.js";
* @property {boolean} [loadSiblings=false] When skipLevelOfDetail
is true
, determines whether siblings of visible tiles are always downloaded during traversal.
* @property {ClippingPlaneCollection} [clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset.
* @property {ClassificationType} [classificationType] Determines whether terrain, 3D Tiles or both will be classified by this tileset. See {@link Cesium3DTileset#classificationType} for details about restrictions and limitations.
+ * @property {HeightReference} [heightReference] Sets the {@link HeightReference} for point features in vector tilesets.
+ * @property {Scene} [scene] The {@link CesiumWidget#scene}. Must be passed in for tilesets that use {@link HeightReference} to clamp billboards and labels of vector tiles.
* @property {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid determining the size and shape of the globe.
* @property {object} [pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
* @property {Cartesian3} [lightColor] The light color when shading models. When undefined
the scene's light color is used instead.
@@ -344,6 +346,8 @@ function Cesium3DTileset(options) {
this._tileDebugLabels = undefined;
this._classificationType = options.classificationType;
+ this._heightReference = options.heightReference;
+ this._scene = options.scene;
this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
@@ -1712,6 +1716,42 @@ Object.defineProperties(Cesium3DTileset.prototype, {
},
},
+ /**
+ * A Property specifying if the height is relative to terrain, 3D Tiles, or both.
+ *
+ * This option is only applied to point features in tilesets containing vector data. + * This option requires the Viewer's scene to be passed in through options.scene. + *
+ * + * @memberof Cesium3DTileset.prototype + * + * @type {HeightReference} + * @default undefined + * + * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. + * @readonly + */ + heightReference: { + get: function () { + return this._heightReference; + }, + }, + + /** + * The CesiumWidget scene, used with heightReference for clamping vector tile points to terrain or 3D tiles. + * + * @member of Cesium3DTileset.prototype + * + * @type {Scene} + * @readonly + * + */ + scene: { + get: function () { + return this._scene; + }, + }, + /** * Gets an ellipsoid describing the shape of the globe. * diff --git a/packages/engine/Source/Scene/Vector3DTileContent.js b/packages/engine/Source/Scene/Vector3DTileContent.js index 120a22db5801..a94f4921425d 100644 --- a/packages/engine/Source/Scene/Vector3DTileContent.js +++ b/packages/engine/Source/Scene/Vector3DTileContent.js @@ -53,7 +53,7 @@ function Vector3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) { this._ready = false; - initialize(this, arrayBuffer, byteOffset); + initialize(this, arrayBuffer, byteOffset, tileset.scene); } Object.defineProperties(Vector3DTileContent.prototype, { @@ -288,7 +288,7 @@ function createClampedPolylines(options) { return new Vector3DTileClampedPolylines(options); } -function initialize(content, arrayBuffer, byteOffset) { +function initialize(content, arrayBuffer, byteOffset, scene) { byteOffset = defaultValue(byteOffset, 0); const uint8Array = new Uint8Array(arrayBuffer); @@ -528,7 +528,7 @@ function initialize(content, arrayBuffer, byteOffset) { modelMatrix: modelMatrix, }); } - + const tileset = content._tileset; if (numberOfPolylines > 0) { featureTable.featuresLength = numberOfPolylines; @@ -575,7 +575,6 @@ function initialize(content, arrayBuffer, byteOffset) { ); byteOffset += polylinePositionByteLength; - const tileset = content._tileset; const examineVectorLinesFunction = tileset.examineVectorLinesFunction; if (defined(examineVectorLinesFunction)) { const decodedPositions = decodeVectorPolylinePositions( @@ -630,6 +629,8 @@ function initialize(content, arrayBuffer, byteOffset) { maximumHeight: maxHeight, rectangle: rectangle, batchTable: batchTable, + heightReference: tileset.heightReference, + scene: scene, }); } } diff --git a/packages/engine/Source/Scene/Vector3DTilePoints.js b/packages/engine/Source/Scene/Vector3DTilePoints.js index 7bea9c2c9022..ce08d5bc63e9 100644 --- a/packages/engine/Source/Scene/Vector3DTilePoints.js +++ b/packages/engine/Source/Scene/Vector3DTilePoints.js @@ -15,6 +15,7 @@ import LabelCollection from "./LabelCollection.js"; import LabelStyle from "./LabelStyle.js"; import PolylineCollection from "./PolylineCollection.js"; import VerticalOrigin from "./VerticalOrigin.js"; +import HeightReference from "./HeightReference.js"; /** * Creates a batch of points or billboards and labels. @@ -27,8 +28,10 @@ import VerticalOrigin from "./VerticalOrigin.js"; * @param {number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Rectangle} options.rectangle The rectangle containing the tile. + * @param {HeightReference} options.heightReference Determines how billboard and label features are positioned relative to terrain or 3d tiles. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polygons. * @param {Uint16Array} options.batchIds The batch ids for each polygon. + * @param {Scene} options.scene The Cesium Viewer {@link Scene}. This is required for clamping billboards and labels with {@link HeightReference} * * @private */ @@ -42,12 +45,15 @@ function Vector3DTilePoints(options) { this._rectangle = options.rectangle; this._minHeight = options.minimumHeight; this._maxHeight = options.maximumHeight; + this._heightReference = options.heightReference; this._billboardCollection = new BillboardCollection({ batchTable: options.batchTable, + scene: options.scene, }); this._labelCollection = new LabelCollection({ batchTable: options.batchTable, + scene: options.scene, }); this._polylineCollection = new PolylineCollection(); this._polylineCollection._useHighlightColor = true; @@ -176,6 +182,8 @@ function createPoints(points, ellipsoid) { const batchIds = points._batchIds; const numberOfPoints = positions.length / 3; + const heightReference = points._heightReference ?? HeightReference.NONE; + for (let i = 0; i < numberOfPoints; ++i) { const id = batchIds[i]; @@ -184,11 +192,13 @@ function createPoints(points, ellipsoid) { const b = billboardCollection.add(); b.position = position; b._batchIndex = id; + b.heightReference = heightReference; const l = labelCollection.add(); l.text = " "; l.position = position; l._batchIndex = id; + l.heightReference = heightReference; const p = polylineCollection.add(); p.positions = [Cartesian3.clone(position), Cartesian3.clone(position)]; diff --git a/packages/engine/Specs/Scene/Vector3DTileContentSpec.js b/packages/engine/Specs/Scene/Vector3DTileContentSpec.js index 4106b41aa0ed..03bfabc22ec7 100644 --- a/packages/engine/Specs/Scene/Vector3DTileContentSpec.js +++ b/packages/engine/Specs/Scene/Vector3DTileContentSpec.js @@ -7,6 +7,7 @@ import { Cesium3DTileset, Cesium3DTileStyle, ClassificationType, + HeightReference, Color, ColorGeometryInstanceAttribute, destroyObject, @@ -622,6 +623,19 @@ describe( expect(scene).toRender(whitePixel); }); }); + + it("sets the heightReference for Vector3DTilePoints", () => { + const heightReference = HeightReference.CLAMP_TO_3D_TILE; + return Cesium3DTilesTester.loadTileset(scene, vectorTilePointsTileset, { + heightReference: heightReference, + scene, + }).then((tileset) => { + const vectorTile = tileset._root.children[0]; + const vectorTilePoint = vectorTile._content._points; + + expect(vectorTilePoint._heightReference).toEqual(heightReference); + }); + }); }); describe("polygons", () => { diff --git a/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js b/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js index c1a22dfedf69..d583024d564f 100644 --- a/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js +++ b/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js @@ -8,6 +8,7 @@ import { defined, DistanceDisplayCondition, Ellipsoid, + HeightReference, NearFarScalar, Rectangle, Cesium3DTileBatchTable, @@ -579,6 +580,7 @@ describe( maxHeight, cartoPositions ); + const heightReference = HeightReference.CLAMP_TO_TERRAIN; const batchTable = new Cesium3DTileBatchTable(mockTileset, 1); batchTable.update(mockTileset, scene.frameState); @@ -591,6 +593,8 @@ describe( rectangle: rectangle, minimumHeight: minHeight, maximumHeight: maxHeight, + heightReference: heightReference, + scene, }) ); @@ -618,10 +622,64 @@ describe( }).then(function () { expect(billboard.ready).toEqual(true); expect(scene).toRender([0, 0, 255, 255]); + expect(billboard.heightReference).toEqual(heightReference); }); }); }); + it("renders a point with a label", function () { + const minHeight = 0.0; + const maxHeight = 100.0; + const cartoPositions = [Cartographic.fromDegrees(0.0, 0.0, 10.0)]; + const positions = encodePositions( + rectangle, + minHeight, + maxHeight, + cartoPositions + ); + const heightReference = HeightReference.CLAMP_TO_TERRAIN; + + const batchTable = new Cesium3DTileBatchTable(mockTileset, 1); + batchTable.update(mockTileset, scene.frameState); + + points = scene.primitives.add( + new Vector3DTilePoints({ + positions: positions, + batchTable: batchTable, + batchIds: new Uint16Array([0]), + rectangle: rectangle, + minimumHeight: minHeight, + maximumHeight: maxHeight, + heightReference: heightReference, + scene, + }) + ); + + const style = new Cesium3DTileStyle({ + labelText: '"test"', + labelColor: "rgba(100, 255, 0, 255)", + labelHorizontalOrigin: HorizontalOrigin.LEFT, + }); + return loadPoints(points).then(function () { + const features = []; + points.createFeatures(mockTileset, features); + points.applyStyle(style, features); + + const collection = points._labelCollection; + expect(collection.length).toEqual(1); + const label = collection.get(0); + expect(label).toBeDefined(); + + scene.camera.lookAt( + Cartesian3.fromDegrees(0.0, 0.0, 10.0), + new Cartesian3(0.0, 0.0, 50.0) + ); + + expect(scene).toRender([100, 255, 0, 255]); + expect(label.heightReference).toEqual(heightReference); + }); + }); + it("renders multiple points with debug color", function () { const minHeight = 0.0; const maxHeight = 100.0;