diff --git a/Apps/SampleData/ClampToGround.czml b/Apps/SampleData/ClampToGround.czml
index 9d5abaccd264..44734a3de77e 100644
--- a/Apps/SampleData/ClampToGround.czml
+++ b/Apps/SampleData/ClampToGround.czml
@@ -5,7 +5,7 @@
"clock": {
"interval": "2018-07-19T15:18:00Z/2018-07-19T15:18:30Z",
"currentTime": "2018-07-19T15:18:00Z",
- "multiplier": 5,
+ "multiplier": 2,
"range": "LOOP_STOP",
"step": "SYSTEM_CLOCK_MULTIPLIER"
}
@@ -19,14 +19,22 @@
"interpolationAlgorithm": "LINEAR",
"forwardExtrapolationType": "HOLD",
"cartesian": [
- "2018-07-19T15:18:00Z",
- 1216348.1632364073,
- -4736348.958775471,
- 4081284.5528982095,
+ "2018-07-19T15:18:00Z",
+ 1216327.3893347275,
+ -4736164.778028102,
+ 4081507.5209477833,
+ "2018-07-19T15:18:10Z",
+ 1216369.543258349,
+ -4736201.237448179,
+ 4081447.3732212726,
+ "2018-07-19T15:18:20Z",
+ 1216434.7507773656,
+ -4736241.372142024,
+ 4081386.1802605274,
"2018-07-19T15:18:30Z",
- 1216369.1229444197,
- -4736377.467107148,
- 4081240.888485707
+ 1216525.7792628652,
+ -4736271.927759278,
+ 4081319.744558958
]
},
"orientation": {
@@ -43,26 +51,29 @@
"polyline": {
"positions": {
"cartesian": [
- 1216348.1632364073,
- -4736348.958775471,
- 4081284.5528982095,
- 1216369.1229444197,
- -4736377.467107148,
- 4081240.888485707
+ 1216327.3893347275,
+ -4736164.778028102,
+ 4081507.5209477833,
+ 1216369.543258349,
+ -4736201.237448179,
+ 4081447.3732212726,
+ 1216434.7507773656,
+ -4736241.372142024,
+ 4081386.1802605274,
+ 1216525.7792628652,
+ -4736271.927759278,
+ 4081319.744558958
]
},
"material": {
"polylineOutline": {
"color": {
- "rgba": [255, 255, 0, 255]
+ "rgba": [100, 149, 237, 140]
},
- "outlineColor": {
- "rgba": [0, 0, 0, 255]
- },
- "outlineWidth": 2
+ "outlineWidth": 0
}
},
- "width": 10,
+ "width": 12,
"clampToGround": true
}
}
diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html
new file mode 100644
index 000000000000..cdda0b4e48a6
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.jpg b/Apps/Sandcastle/gallery/Clamp Entities to Ground.jpg
new file mode 100644
index 000000000000..dfdf6efe707c
Binary files /dev/null and b/Apps/Sandcastle/gallery/Clamp Entities to Ground.jpg differ
diff --git a/Apps/Sandcastle/gallery/Clamp Model to Ground.html b/Apps/Sandcastle/gallery/Clamp Model to Ground.html
new file mode 100644
index 000000000000..1d10dd1121bf
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Clamp Model to Ground.html
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Clamp Model to Ground.jpg b/Apps/Sandcastle/gallery/Clamp Model to Ground.jpg
new file mode 100644
index 000000000000..174b994d5b5d
Binary files /dev/null and b/Apps/Sandcastle/gallery/Clamp Model to Ground.jpg differ
diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles.html b/Apps/Sandcastle/gallery/Clamp to 3D Tiles.html
deleted file mode 100644
index dadb13795bf0..000000000000
--- a/Apps/Sandcastle/gallery/Clamp to 3D Tiles.html
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
- Cesium Demo
-
-
-
-
-
-
-
- Loading...
-
-
-
-
diff --git a/Apps/Sandcastle/gallery/Clamp to 3D Tiles.jpg b/Apps/Sandcastle/gallery/Clamp to 3D Tiles.jpg
deleted file mode 100644
index 8164f073a8e6..000000000000
Binary files a/Apps/Sandcastle/gallery/Clamp to 3D Tiles.jpg and /dev/null differ
diff --git a/Apps/Sandcastle/gallery/Clamp to Terrain.html b/Apps/Sandcastle/gallery/Clamp to Terrain.html
deleted file mode 100644
index f01888c3ee85..000000000000
--- a/Apps/Sandcastle/gallery/Clamp to Terrain.html
+++ /dev/null
@@ -1,384 +0,0 @@
-
-
-
-
-
-
-
-
- Cesium Demo
-
-
-
-
-
-
-
- Loading...
-
-
-
-
diff --git a/Apps/Sandcastle/gallery/Clamp to Terrain.jpg b/Apps/Sandcastle/gallery/Clamp to Terrain.jpg
deleted file mode 100644
index 9841e9883479..000000000000
Binary files a/Apps/Sandcastle/gallery/Clamp to Terrain.jpg and /dev/null differ
diff --git a/Apps/Sandcastle/gallery/development/Clamp to Ground Modes.html b/Apps/Sandcastle/gallery/development/Clamp to Ground Modes.html
new file mode 100644
index 000000000000..db8662736f2b
--- /dev/null
+++ b/Apps/Sandcastle/gallery/development/Clamp to Ground Modes.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/CHANGES.md b/CHANGES.md
index 2a35b7b448b6..db922b50013f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,12 +9,15 @@
- By default, the screen space camera controller will no longer go inside or under instances of `Cesium3DTileset`. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- This behavior can be disabled by setting `Cesium3DTileset.disableCollision` to true.
- This feature is enabled by default only for WebGL 2 and above, but can be enabled for WebGL 1 by setting the `enablePick` option to true when creating the `Cesium3DTileset`.
+- Clamping to ground, `HeightReference.CLAMP_TO_GROUND`, and `HeightReference.RELATIVE_TO_GROUND` now take into account 3D Tilesets. These opions will clamp to either 3D Tilesets or Terrain, whichever has a greater height. [#11604](https://github.com/CesiumGS/cesium/pull/11604)
+ - To restore previous behavior where an entity is clamped only to terrain or relative only to terrain, set `heightReference` to `HeightReference.CLAMP_TO_TERRAIN` or `HeightReference.RELATIVE_TO_TERRAIN` respectively.
- Remove the need for node internal packages `http`, `https`, `url` and `zlib` in the `Resource` class. This means they do not need to be marked external by build tools anymore. [#11773](https://github.com/CesiumGS/cesium/pull/11773)
- This slightly changed the contents of the `RequestErrorEvent` error that is thrown in node environments when a request fails. The `response` property is now a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object instead of an [`http.IncomingMessage`](https://nodejs.org/docs/latest-v20.x/api/http.html#class-httpincomingmessage)
- `PolygonGeometry.computeRectangle` has been removed. Use `PolygonGeometry.computeRectangleFromPositions` instead.
##### Additions :tada:
+- Added `HeightReference.CLAMP_TO_TERRAIN`, `HeightReference.RELATIVE_TO_TERRAIN`, `HeightReference.CLAMP_TO_3D_TILE`, and `HeightReference.RELATIVE_TO_3D_TILE` to position relatve to terrain or 3D tilesets exclusively.[#11604](https://github.com/CesiumGS/cesium/pull/11604)
- Added `Cesium3DTileset.getHeight` to sample height values of the loaded tiles. If using WebGL 1, the `enablePick` option must be set to true to use this function. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- Added `Cesium3DTileset.disableCollision` to allow the camera from to go inside or below a 3D tileset, for instance, to be used with 3D Tiles interiors. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- The `Cesium3DTileset.dynamicScreenSpaceError` optimization is now enabled by default, as this improves performance for street-level horizon views. Furthermore, the default settings of this feature were tuned for improved performance. `Cesium3DTileset.dynamicScreenSpaceErrorDensity` was changed from 0.00278 to 0.0002. `Cesium3DTileset.dynamicScreenSpaceErrorFactor` was changed from 4 to 24. [#11718](https://github.com/CesiumGS/cesium/pull/11718)
diff --git a/Specs/createGlobe.js b/Specs/createGlobe.js
index 200419f42880..fdd17e3d2e77 100644
--- a/Specs/createGlobe.js
+++ b/Specs/createGlobe.js
@@ -4,8 +4,8 @@ function createGlobe(ellipsoid) {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
const globe = {
- callback: undefined,
- removedCallback: false,
+ _callback: undefined,
+ _removedCallback: false,
ellipsoid: ellipsoid,
beginFrame: function () {},
endFrame: function () {},
@@ -22,10 +22,10 @@ function createGlobe(ellipsoid) {
};
globe._surface.updateHeight = function (position, callback) {
- globe.callback = callback;
+ globe._callback = callback;
return function () {
- globe.removedCallback = true;
- globe.callback = undefined;
+ globe._removedCallback = true;
+ globe._callback = undefined;
};
};
diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js
index 934ba64f49c9..c7d20e8c0a3f 100644
--- a/packages/engine/Source/DataSources/Entity.js
+++ b/packages/engine/Source/DataSources/Entity.js
@@ -13,7 +13,9 @@ import Quaternion from "../Core/Quaternion.js";
import Transforms from "../Core/Transforms.js";
import GroundPolylinePrimitive from "../Scene/GroundPolylinePrimitive.js";
import GroundPrimitive from "../Scene/GroundPrimitive.js";
-import HeightReference from "../Scene/HeightReference.js";
+import HeightReference, {
+ isHeightReferenceClamp,
+} from "../Scene/HeightReference.js";
import BillboardGraphics from "./BillboardGraphics.js";
import BoxGraphics from "./BoxGraphics.js";
import ConstantPositionProperty from "./ConstantPositionProperty.js";
@@ -707,7 +709,7 @@ Entity.prototype.computeModelMatrixForHeightReference = function (
}
const carto = ellipsoid.cartesianToCartographic(position, cartoScratch);
- if (heightReference === HeightReference.CLAMP_TO_GROUND) {
+ if (isHeightReferenceClamp(heightReference)) {
carto.height = heightOffset;
} else {
carto.height += heightOffset;
diff --git a/packages/engine/Source/DataSources/GroundGeometryUpdater.js b/packages/engine/Source/DataSources/GroundGeometryUpdater.js
index 5373bb70d37b..438b483dd0ae 100644
--- a/packages/engine/Source/DataSources/GroundGeometryUpdater.js
+++ b/packages/engine/Source/DataSources/GroundGeometryUpdater.js
@@ -5,7 +5,9 @@ import DeveloperError from "../Core/DeveloperError.js";
import GeometryOffsetAttribute from "../Core/GeometryOffsetAttribute.js";
import oneTimeWarning from "../Core/oneTimeWarning.js";
import GroundPrimitive from "../Scene/GroundPrimitive.js";
-import HeightReference from "../Scene/HeightReference.js";
+import HeightReference, {
+ isHeightReferenceClamp,
+} from "../Scene/HeightReference.js";
import CallbackProperty from "./CallbackProperty.js";
import ConstantProperty from "./ConstantProperty.js";
import GeometryUpdater from "./GeometryUpdater.js";
@@ -164,9 +166,10 @@ GroundGeometryUpdater.getGeometryHeight = function (height, heightReference) {
return;
}
- if (heightReference !== HeightReference.CLAMP_TO_GROUND) {
+ if (!isHeightReferenceClamp(heightReference)) {
return height;
}
+
return 0.0;
};
@@ -186,7 +189,7 @@ GroundGeometryUpdater.getGeometryExtrudedHeight = function (
}
return;
}
- if (extrudedHeightReference !== HeightReference.CLAMP_TO_GROUND) {
+ if (!isHeightReferenceClamp(extrudedHeightReference)) {
return extrudedHeight;
}
diff --git a/packages/engine/Source/DataSources/ModelVisualizer.js b/packages/engine/Source/DataSources/ModelVisualizer.js
index 5665c9dc5a7b..1e2b7a0a2c6c 100644
--- a/packages/engine/Source/DataSources/ModelVisualizer.js
+++ b/packages/engine/Source/DataSources/ModelVisualizer.js
@@ -4,19 +4,22 @@ import Cartesian2 from "../Core/Cartesian2.js";
import Cartesian3 from "../Core/Cartesian3.js";
import Check from "../Core/Check.js";
import Color from "../Core/Color.js";
+import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
+import Ellipsoid from "../Core/Ellipsoid.js";
import Matrix4 from "../Core/Matrix4.js";
import Resource from "../Core/Resource.js";
import ColorBlendMode from "../Scene/ColorBlendMode.js";
-import HeightReference from "../Scene/HeightReference.js";
+import HeightReference, {
+ isHeightReferenceClamp,
+} from "../Scene/HeightReference.js";
import Model from "../Scene/Model/Model.js";
import ModelAnimationLoop from "../Scene/ModelAnimationLoop.js";
import ShadowMode from "../Scene/ShadowMode.js";
import BoundingSphereState from "./BoundingSphereState.js";
import Property from "./Property.js";
-import sampleTerrainMostDetailed from "../Core/sampleTerrainMostDetailed.js";
import Cartographic from "../Core/Cartographic.js";
const defaultScale = 1.0;
@@ -172,9 +175,6 @@ ModelVisualizer.prototype.update = function (time) {
articulationsScratch: {},
loadFailed: false,
modelUpdated: false,
- awaitingSampleTerrain: false,
- clampedBoundingSphere: undefined,
- sampleTerrainFailed: false,
};
modelHash[entity.id] = modelData;
@@ -399,9 +399,6 @@ ModelVisualizer.prototype.destroy = function () {
return destroyObject(this);
};
-// Used for testing.
-ModelVisualizer._sampleTerrainMostDetailed = sampleTerrainMostDetailed;
-
const scratchPosition = new Cartesian3();
const scratchCartographic = new Cartographic();
/**
@@ -445,106 +442,30 @@ ModelVisualizer.prototype.getBoundingSphere = function (entity, result) {
const scene = this._scene;
const globe = scene.globe;
+ const ellipsoid = defaultValue(globe?.ellipsoid, Ellipsoid.WGS84);
- // cannot access a terrain provider if there is no globe; formally set to undefined
- const terrainProvider = defined(globe) ? globe.terrainProvider : undefined;
const hasHeightReference = model.heightReference !== HeightReference.NONE;
- if (defined(globe) && hasHeightReference) {
- const ellipsoid = globe.ellipsoid;
+ if (hasHeightReference) {
const modelMatrix = model.modelMatrix;
scratchPosition.x = modelMatrix[12];
scratchPosition.y = modelMatrix[13];
scratchPosition.z = modelMatrix[14];
- const cartoPosition = ellipsoid.cartesianToCartographic(scratchPosition);
-
- // For a terrain provider that does not have availability, like the EllipsoidTerrainProvider,
- // we can directly assign the bounding sphere's center from model matrix's translation.
- if (!defined(terrainProvider.availability)) {
- // Regardless of what the original model's position is set to, for CLAMP_TO_GROUND, we reset it to 0
- // when computing the position to zoom/fly to.
- if (model.heightReference === HeightReference.CLAMP_TO_GROUND) {
- cartoPosition.height = 0;
- }
-
- const scratchPosition = ellipsoid.cartographicToCartesian(cartoPosition);
- BoundingSphere.clone(model.boundingSphere, result);
- result.center = scratchPosition;
-
- return BoundingSphereState.DONE;
- }
-
- // Otherwise, in the case of terrain providers with availability,
- // since the model's bounding sphere may be clamped to a lower LOD tile if
- // the camera is initially far away, we use sampleTerrainMostDetailed to estimate
- // where the bounding sphere should be and set that as the target bounding sphere
- // for the camera.
- let clampedBoundingSphere = this._modelHash[entity.id]
- .clampedBoundingSphere;
-
- // Check if the sample terrain function has failed.
- const sampleTerrainFailed = this._modelHash[entity.id].sampleTerrainFailed;
- if (sampleTerrainFailed) {
- this._modelHash[entity.id].sampleTerrainFailed = false;
- return BoundingSphereState.FAILED;
- }
+ const cartoPosition = ellipsoid.cartesianToCartographic(
+ scratchPosition,
+ scratchCartographic
+ );
- if (!defined(clampedBoundingSphere)) {
- clampedBoundingSphere = new BoundingSphere();
-
- // Since this function is called per-frame, we set a flag when sampleTerrainMostDetailed
- // is called and check for it to avoid calling it again.
- const awaitingSampleTerrain = this._modelHash[entity.id]
- .awaitingSampleTerrain;
- if (!awaitingSampleTerrain) {
- Cartographic.clone(cartoPosition, scratchCartographic);
- this._modelHash[entity.id].awaitingSampleTerrain = true;
- ModelVisualizer._sampleTerrainMostDetailed(terrainProvider, [
- scratchCartographic,
- ])
- .then((result) => {
- if (this.isDestroyed()) {
- return;
- }
-
- this._modelHash[entity.id].awaitingSampleTerrain = false;
-
- const updatedCartographic = result[0];
- if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {
- updatedCartographic.height += cartoPosition.height;
- }
- ellipsoid.cartographicToCartesian(
- updatedCartographic,
- scratchPosition
- );
-
- // Update the bounding sphere with the updated position.
- BoundingSphere.clone(model.boundingSphere, clampedBoundingSphere);
- clampedBoundingSphere.center = scratchPosition;
-
- this._modelHash[
- entity.id
- ].clampedBoundingSphere = BoundingSphere.clone(
- clampedBoundingSphere
- );
- })
- .catch((e) => {
- if (this.isDestroyed()) {
- return;
- }
-
- this._modelHash[entity.id].sampleTerrainFailed = true;
- this._modelHash[entity.id].awaitingSampleTerrain = false;
- });
+ const height = scene.getHeight(cartoPosition, model.heightReference);
+ if (defined(height)) {
+ if (isHeightReferenceClamp(model.heightReference)) {
+ cartoPosition.height = height;
+ } else {
+ cartoPosition.height += height;
}
-
- // We will return the state as pending until the clamped bounding sphere is defined,
- // which happens when the sampleTerrainMostDetailed promise returns.
- return BoundingSphereState.PENDING;
}
- BoundingSphere.clone(clampedBoundingSphere, result);
- // Reset the clamped bounding sphere.
- this._modelHash[entity.id].clampedBoundingSphere = undefined;
+ BoundingSphere.clone(model.boundingSphere, result);
+ result.center = ellipsoid.cartographicToCartesian(cartoPosition);
return BoundingSphereState.DONE;
}
diff --git a/packages/engine/Source/DataSources/TerrainOffsetProperty.js b/packages/engine/Source/DataSources/TerrainOffsetProperty.js
index 17eab9c26406..662d108eed26 100644
--- a/packages/engine/Source/DataSources/TerrainOffsetProperty.js
+++ b/packages/engine/Source/DataSources/TerrainOffsetProperty.js
@@ -6,12 +6,12 @@ import destroyObject from "../Core/destroyObject.js";
import Event from "../Core/Event.js";
import Iso8601 from "../Core/Iso8601.js";
import CesiumMath from "../Core/Math.js";
-import HeightReference from "../Scene/HeightReference.js";
-import SceneMode from "../Scene/SceneMode.js";
+import HeightReference, {
+ isHeightReferenceRelative,
+} from "../Scene/HeightReference.js";
import Property from "./Property.js";
const scratchPosition = new Cartesian3();
-const scratchCarto = new Cartographic();
/**
* @private
@@ -118,40 +118,32 @@ TerrainOffsetProperty.prototype._updateClamping = function () {
const globe = scene.globe;
const position = this._position;
- if (!defined(globe) || Cartesian3.equals(position, Cartesian3.ZERO)) {
+ if (Cartesian3.equals(position, Cartesian3.ZERO)) {
this._terrainHeight = 0;
return;
}
const ellipsoid = globe.ellipsoid;
- const surface = globe._surface;
-
- const that = this;
const cartographicPosition = ellipsoid.cartesianToCartographic(
position,
this._cartographicPosition
);
- const height = globe.getHeight(cartographicPosition);
+
+ const height = scene.getHeight(cartographicPosition, this._heightReference);
if (defined(height)) {
this._terrainHeight = height;
} else {
this._terrainHeight = 0;
}
- function updateFunction(clampedPosition) {
- if (scene.mode === SceneMode.SCENE3D) {
- const carto = ellipsoid.cartesianToCartographic(
- clampedPosition,
- scratchCarto
- );
- that._terrainHeight = carto.height;
- } else {
- that._terrainHeight = clampedPosition.x;
- }
- that.definitionChanged.raiseEvent();
- }
- this._removeCallbackFunc = surface.updateHeight(
+ const updateFunction = (clampedPosition) => {
+ this._terrainHeight = clampedPosition.height;
+ this.definitionChanged.raiseEvent();
+ };
+
+ this._removeCallbackFunc = scene.updateHeight(
cartographicPosition,
- updateFunction
+ updateFunction,
+ this._heightReference
);
};
@@ -174,7 +166,7 @@ TerrainOffsetProperty.prototype.getValue = function (time, result) {
if (
heightReference === HeightReference.NONE &&
- extrudedHeightReference !== HeightReference.RELATIVE_TO_GROUND
+ !isHeightReferenceRelative(extrudedHeightReference)
) {
this._position = Cartesian3.clone(Cartesian3.ZERO, this._position);
return Cartesian3.clone(Cartesian3.ZERO, result);
diff --git a/packages/engine/Source/Scene/Billboard.js b/packages/engine/Source/Scene/Billboard.js
index b10b42d40aa8..878bd41dc25e 100644
--- a/packages/engine/Source/Scene/Billboard.js
+++ b/packages/engine/Source/Scene/Billboard.js
@@ -10,10 +10,13 @@ import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import DeveloperError from "../Core/DeveloperError.js";
import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";
+import Ellipsoid from "../Core/Ellipsoid.js";
import Matrix4 from "../Core/Matrix4.js";
import NearFarScalar from "../Core/NearFarScalar.js";
import Resource from "../Core/Resource.js";
-import HeightReference from "./HeightReference.js";
+import HeightReference, {
+ isHeightReferenceRelative,
+} from "./HeightReference.js";
import HorizontalOrigin from "./HorizontalOrigin.js";
import SceneMode from "./SceneMode.js";
import SceneTransforms from "./SceneTransforms.js";
@@ -1056,15 +1059,13 @@ Billboard.prototype._updateClamping = function () {
};
const scratchCartographic = new Cartographic();
-const scratchPosition = new Cartesian3();
-
Billboard._updateClamping = function (collection, owner) {
const scene = collection._scene;
- if (!defined(scene) || !defined(scene.globe)) {
+ if (!defined(scene)) {
//>>includeStart('debug', pragmas.debug);
if (owner._heightReference !== HeightReference.NONE) {
throw new DeveloperError(
- "Height reference is not supported without a scene and globe."
+ "Height reference is not supported without a scene."
);
}
//>>includeEnd('debug');
@@ -1072,8 +1073,7 @@ Billboard._updateClamping = function (collection, owner) {
}
const globe = scene.globe;
- const ellipsoid = globe.ellipsoid;
- const surface = globe._surface;
+ const ellipsoid = defaultValue(globe?.ellipsoid, Ellipsoid.WGS84);
const mode = scene.frameState.mode;
@@ -1096,45 +1096,48 @@ Billboard._updateClamping = function (collection, owner) {
return;
}
+ if (defined(owner._removeCallbackFunc)) {
+ owner._removeCallbackFunc();
+ }
+
const position = ellipsoid.cartesianToCartographic(owner._position);
if (!defined(position)) {
owner._actualClampedPosition = undefined;
return;
}
- if (defined(owner._removeCallbackFunc)) {
- owner._removeCallbackFunc();
- }
-
function updateFunction(clampedPosition) {
- if (owner._heightReference === HeightReference.RELATIVE_TO_GROUND) {
+ owner._clampedPosition = ellipsoid.cartographicToCartesian(
+ clampedPosition,
+ owner._clampedPosition
+ );
+
+ if (isHeightReferenceRelative(owner._heightReference)) {
if (owner._mode === SceneMode.SCENE3D) {
- const clampedCart = ellipsoid.cartesianToCartographic(
+ clampedPosition.height += position.height;
+ ellipsoid.cartographicToCartesian(
clampedPosition,
- scratchCartographic
+ owner._clampedPosition
);
- clampedCart.height += position.height;
- ellipsoid.cartographicToCartesian(clampedCart, clampedPosition);
} else {
- clampedPosition.x += position.height;
+ owner._clampedPosition.x += position.height;
}
}
- owner._clampedPosition = Cartesian3.clone(
- clampedPosition,
- owner._clampedPosition
- );
}
- owner._removeCallbackFunc = surface.updateHeight(position, updateFunction);
+
+ owner._removeCallbackFunc = scene.updateHeight(
+ position,
+ updateFunction,
+ owner._heightReference
+ );
Cartographic.clone(position, scratchCartographic);
- const height = globe.getHeight(position);
+ const height = scene.getHeight(position, owner._heightReference);
if (defined(height)) {
scratchCartographic.height = height;
}
- ellipsoid.cartographicToCartesian(scratchCartographic, scratchPosition);
-
- updateFunction(scratchPosition);
+ updateFunction(scratchCartographic);
};
Billboard.prototype._loadImage = function () {
diff --git a/packages/engine/Source/Scene/BillboardCollection.js b/packages/engine/Source/Scene/BillboardCollection.js
index c0116eaabc6b..9bd1d3be1f9b 100644
--- a/packages/engine/Source/Scene/BillboardCollection.js
+++ b/packages/engine/Source/Scene/BillboardCollection.js
@@ -28,7 +28,7 @@ import BillboardCollectionVS from "../Shaders/BillboardCollectionVS.js";
import Billboard from "./Billboard.js";
import BlendingState from "./BlendingState.js";
import BlendOption from "./BlendOption.js";
-import HeightReference from "./HeightReference.js";
+import HeightReference, { isHeightReferenceClamp } from "./HeightReference.js";
import HorizontalOrigin from "./HorizontalOrigin.js";
import SceneMode from "./SceneMode.js";
import SDFSettings from "./SDFSettings.js";
@@ -1375,7 +1375,7 @@ function writeCompressedAttribute3(
let disableDepthTestDistance = billboard.disableDepthTestDistance;
const clampToGround =
- billboard.heightReference === HeightReference.CLAMP_TO_GROUND &&
+ isHeightReferenceClamp(billboard.heightReference) &&
frameState.context.depthTexture;
if (!defined(disableDepthTestDistance)) {
disableDepthTestDistance = clampToGround ? 5000.0 : 0.0;
@@ -1448,7 +1448,7 @@ function writeTextureCoordinateBoundsOrLabelTranslate(
vafWriters,
billboard
) {
- if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) {
+ if (isHeightReferenceClamp(billboard.heightReference)) {
const scene = billboardCollection._scene;
const context = frameState.context;
const globeTranslucent = frameState.globeTranslucencyState.translucent;
diff --git a/packages/engine/Source/Scene/Cesium3DTile.js b/packages/engine/Source/Scene/Cesium3DTile.js
index 7edd883817d8..8dd01a1f77a3 100644
--- a/packages/engine/Source/Scene/Cesium3DTile.js
+++ b/packages/engine/Source/Scene/Cesium3DTile.js
@@ -474,6 +474,7 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
this._touchedFrame = 0;
this._visitedFrame = 0;
this._selectedFrame = 0;
+ this._wasSelectedLastFrame = false;
this._requestedFrame = 0;
this._ancestorWithContent = undefined;
this._ancestorWithContentAvailable = undefined;
diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js
index 330527ead252..df733b9d8d84 100644
--- a/packages/engine/Source/Scene/Cesium3DTileset.js
+++ b/packages/engine/Source/Scene/Cesium3DTileset.js
@@ -1,4 +1,5 @@
import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
+import BoundingSphere from "../Core/BoundingSphere.js";
import Cartesian2 from "../Core/Cartesian2.js";
import Cartesian3 from "../Core/Cartesian3.js";
import Cartographic from "../Core/Cartographic.js";
@@ -266,6 +267,8 @@ function Cesium3DTileset(options) {
? Matrix4.clone(options.modelMatrix)
: Matrix4.clone(Matrix4.IDENTITY);
+ this._addHeightCallbacks = [];
+
this._statistics = new Cesium3DTilesetStatistics();
this._statisticsLast = new Cesium3DTilesetStatistics();
this._statisticsPerPass = new Array(Cesium3DTilePass.NUMBER_OF_PASSES);
@@ -2632,6 +2635,55 @@ function filterProcessingQueue(tileset) {
tiles.length -= removeCount;
}
+const scratchUpdateHeightCartographic = new Cartographic();
+const scratchUpdateHeightCartographic2 = new Cartographic();
+const scratchUpdateHeightCartesian = new Cartesian3();
+function processUpdateHeight(tileset, tile, frameState) {
+ const heightCallbackData = tileset._addHeightCallbacks;
+ const boundingSphere = tile.boundingSphere;
+
+ for (const callbackData of heightCallbackData) {
+ // No need to upadate if the tile was already visible last frame
+ if (callbackData.invoked || tile._wasSelectedLastFrame) {
+ continue;
+ }
+
+ const ellipsoid = callbackData.ellipsoid;
+ const positionCartographic = Cartographic.clone(
+ callbackData.positionCartographic,
+ scratchUpdateHeightCartographic
+ );
+ const centerCartographic = Cartographic.fromCartesian(
+ boundingSphere.center,
+ ellipsoid,
+ scratchUpdateHeightCartographic2
+ );
+
+ // This can be undefined when the bounding sphere is at the origin
+ if (defined(centerCartographic)) {
+ positionCartographic.height = centerCartographic.height;
+ }
+
+ const position = Cartographic.toCartesian(
+ positionCartographic,
+ ellipsoid,
+ scratchUpdateHeightCartesian
+ );
+ if (
+ Cartesian3.distance(position, boundingSphere.center) <=
+ boundingSphere.radius
+ ) {
+ frameState.afterRender.push(() => {
+ // Callback can be removed before it actually invoked at the end of the frame
+ if (defined(callbackData.callback)) {
+ callbackData.callback(positionCartographic);
+ }
+ callbackData.invoked = false;
+ });
+ }
+ }
+}
+
/**
* Process tiles in the PROCESSING state so they will eventually move to the READY state.
* @private
@@ -2899,6 +2951,7 @@ function updateTiles(tileset, frameState, passOptions) {
if (isRender) {
tileVisible.raiseEvent(tile);
}
+ processUpdateHeight(tileset, tile, frameState);
tile.update(tileset, frameState, passOptions);
statistics.incrementSelectionCounts(tile.content);
++statistics.selected;
@@ -3475,6 +3528,7 @@ Cesium3DTileset.prototype.getHeight = function (cartographic, scene) {
cartographic,
ray.direction
);
+ Cartesian3.normalize(ray.direction, ray.direction);
ray.direction = Cartesian3.normalize(position, ray.direction);
ray.direction = Cartesian3.negate(position, ray.direction);
@@ -3495,6 +3549,51 @@ Cesium3DTileset.prototype.getHeight = function (cartographic, scene) {
)?.height;
};
+/**
+ * Calls the callback when a new tile is rendered that contains the given cartographic. The only parameter
+ * is the cartographic position on the tile.
+ *
+ * @private
+ *
+ * @param {Scene} scene The scene where visualization is taking place.
+ * @param {Cartographic} cartographic The cartographic position.
+ * @param {Function} callback The function to be called when a new tile is loaded.
+ * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
+ * @returns {Function} The function to remove this callback from the quadtree.
+ */
+Cesium3DTileset.prototype.updateHeight = function (
+ cartographic,
+ callback,
+ ellipsoid
+) {
+ ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
+
+ const object = {
+ positionCartographic: cartographic,
+ ellipsoid: ellipsoid,
+ callback: callback,
+ invoked: false,
+ };
+
+ const removeCallback = () => {
+ const addedCallbacks = this._addHeightCallbacks;
+ const length = addedCallbacks.length;
+ for (let i = 0; i < length; ++i) {
+ if (addedCallbacks[i] === object) {
+ addedCallbacks.splice(i, 1);
+ break;
+ }
+ }
+
+ if (object.callback) {
+ object.callback = undefined;
+ }
+ };
+
+ this._addHeightCallbacks.push(object);
+ return removeCallback;
+};
+
const scratchSphereIntersection = new Interval();
const scratchPickIntersection = new Cartesian3();
@@ -3515,42 +3614,50 @@ Cesium3DTileset.prototype.pick = function (ray, frameState, result) {
const selectedTiles = this._selectedTiles;
const selectedLength = selectedTiles.length;
+ const candidates = [];
- let intersection;
- let minDistance = Number.POSITIVE_INFINITY;
for (let i = 0; i < selectedLength; ++i) {
const tile = selectedTiles[i];
const boundsIntersection = IntersectionTests.raySphere(
ray,
- tile.boundingSphere,
+ tile.contentBoundingVolume.boundingSphere,
scratchSphereIntersection
);
- if (!defined(boundsIntersection)) {
+ if (!defined(boundsIntersection) || !defined(tile.content)) {
continue;
}
- const candidate = tile.content?.pick(
+ candidates.push(tile);
+ }
+
+ const length = candidates.length;
+ candidates.sort((a, b) => {
+ const aDist = BoundingSphere.distanceSquaredTo(
+ a.contentBoundingVolume.boundingSphere,
+ ray.origin
+ );
+ const bDist = BoundingSphere.distanceSquaredTo(
+ b.contentBoundingVolume.boundingSphere,
+ ray.origin
+ );
+
+ return aDist - bDist;
+ });
+
+ let intersection;
+ for (let i = 0; i < length; ++i) {
+ const tile = candidates[i];
+ const candidate = tile.content.pick(
ray,
frameState,
scratchPickIntersection
);
- if (!defined(candidate)) {
- continue;
- }
-
- const distance = Cartesian3.distance(ray.origin, candidate);
- if (distance < minDistance) {
+ if (defined(candidate)) {
intersection = Cartesian3.clone(candidate, result);
- minDistance = distance;
+ return intersection;
}
}
-
- if (!defined(intersection)) {
- return undefined;
- }
-
- return intersection;
};
/**
diff --git a/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js b/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js
index 12561ba541eb..57dea371ce94 100644
--- a/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js
+++ b/packages/engine/Source/Scene/Cesium3DTilesetTraversal.js
@@ -79,6 +79,8 @@ Cesium3DTilesetTraversal.selectTile = function (tile, frameState) {
return;
}
+ tile._wasSelectedLastFrame = true;
+
const { content, tileset } = tile;
if (content.featurePropertiesDirty) {
// A feature's property in this tile changed, the tile needs to be re-styled.
@@ -88,7 +90,9 @@ Cesium3DTilesetTraversal.selectTile = function (tile, frameState) {
} else if (tile._selectedFrame < frameState.frameNumber - 1) {
// Tile is newly selected; it is selected this frame, but was not selected last frame.
tileset._selectedTilesToStyle.push(tile);
+ tile._wasSelectedLastFrame = false;
}
+
tile._selectedFrame = frameState.frameNumber;
tileset._selectedTiles.push(tile);
};
diff --git a/packages/engine/Source/Scene/HeightReference.js b/packages/engine/Source/Scene/HeightReference.js
index 809fe4c04943..e81a671690de 100644
--- a/packages/engine/Source/Scene/HeightReference.js
+++ b/packages/engine/Source/Scene/HeightReference.js
@@ -12,17 +12,74 @@ const HeightReference = {
NONE: 0,
/**
- * The position is clamped to the terrain.
+ * The position is clamped to the terrain and 3D Tiles.
* @type {number}
* @constant
*/
CLAMP_TO_GROUND: 1,
/**
- * The position height is the height above the terrain.
+ * The position height is the height above the terrain and 3D Tiles.
* @type {number}
* @constant
*/
RELATIVE_TO_GROUND: 2,
+
+ /**
+ * The position is clamped to terain.
+ * @type {number}
+ * @constant
+ */
+ CLAMP_TO_TERRAIN: 3,
+
+ /**
+ * The position height is the height above terrain.
+ * @type {number}
+ * @constant
+ */
+ RELATIVE_TO_TERRAIN: 4,
+
+ /**
+ * The position is clamped to 3D Tiles.
+ * @type {number}
+ * @constant
+ */
+ CLAMP_TO_3D_TILE: 5,
+
+ /**
+ * The position height is the height above 3D Tiles.
+ * @type {number}
+ * @constant
+ */
+ RELATIVE_TO_3D_TILE: 6,
};
+
export default Object.freeze(HeightReference);
+
+/**
+ * Returns true if the height should be clamped to the surface
+ * @param {HeightReference} heightReference
+ * @returns true if the height should be clamped to the surface
+ * @private
+ */
+export function isHeightReferenceClamp(heightReference) {
+ return (
+ heightReference === HeightReference.CLAMP_TO_GROUND ||
+ heightReference === HeightReference.CLAMP_TO_3D_TILE ||
+ heightReference === HeightReference.CLAMP_TO_TERRAIN
+ );
+}
+
+/**
+ * Returns true if the height should be offset relative to the surface
+ * @param {HeightReference} heightReference
+ * @returns true if the height should be offset relative to the surface
+ * @private
+ */
+export function isHeightReferenceRelative(heightReference) {
+ return (
+ heightReference === HeightReference.RELATIVE_TO_GROUND ||
+ heightReference === HeightReference.RELATIVE_TO_3D_TILE ||
+ heightReference === HeightReference.RELATIVE_TO_TERRAIN
+ );
+}
diff --git a/packages/engine/Source/Scene/LabelCollection.js b/packages/engine/Source/Scene/LabelCollection.js
index 314db367fba3..627ba87862cd 100644
--- a/packages/engine/Source/Scene/LabelCollection.js
+++ b/packages/engine/Source/Scene/LabelCollection.js
@@ -10,7 +10,7 @@ import writeTextToCanvas from "../Core/writeTextToCanvas.js";
import bitmapSDF from "bitmap-sdf";
import BillboardCollection from "./BillboardCollection.js";
import BlendOption from "./BlendOption.js";
-import HeightReference from "./HeightReference.js";
+import { isHeightReferenceClamp } from "./HeightReference.js";
import HorizontalOrigin from "./HorizontalOrigin.js";
import Label from "./Label.js";
import LabelStyle from "./LabelStyle.js";
@@ -510,7 +510,7 @@ function repositionAllGlyphs(label) {
);
}
- if (label.heightReference === HeightReference.CLAMP_TO_GROUND) {
+ if (isHeightReferenceClamp(label.heightReference)) {
for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) {
glyph = glyphs[glyphIndex];
const billboard = glyph.billboard;
diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js
index 33378231ccda..548e25f3478c 100644
--- a/packages/engine/Source/Scene/Model/Model.js
+++ b/packages/engine/Source/Scene/Model/Model.js
@@ -9,6 +9,7 @@ import defaultValue from "../../Core/defaultValue.js";
import DeveloperError from "../../Core/DeveloperError.js";
import destroyObject from "../../Core/destroyObject.js";
import DistanceDisplayCondition from "../../Core/DistanceDisplayCondition.js";
+import Ellipsoid from "../../Core/Ellipsoid.js";
import Event from "../../Core/Event.js";
import Matrix3 from "../../Core/Matrix3.js";
import Matrix4 from "../../Core/Matrix4.js";
@@ -18,7 +19,9 @@ import Pass from "../../Renderer/Pass.js";
import ClippingPlaneCollection from "../ClippingPlaneCollection.js";
import ColorBlendMode from "../ColorBlendMode.js";
import GltfLoader from "../GltfLoader.js";
-import HeightReference from "../HeightReference.js";
+import HeightReference, {
+ isHeightReferenceRelative,
+} from "../HeightReference.js";
import ImageBasedLighting from "../ImageBasedLighting.js";
import PointCloudShading from "../PointCloudShading.js";
import SceneMode from "../SceneMode.js";
@@ -342,10 +345,9 @@ function Model(options) {
const scene = options.scene;
if (defined(scene) && defined(scene.terrainProviderChanged)) {
this._terrainProviderChangedCallback = scene.terrainProviderChanged.addEventListener(
- function () {
+ () => {
this._heightDirty = true;
- },
- this
+ }
);
}
this._scene = scene;
@@ -2054,15 +2056,11 @@ function updateClamping(model) {
}
const scene = model._scene;
- if (
- !defined(scene) ||
- !defined(scene.globe) ||
- model.heightReference === HeightReference.NONE
- ) {
+ if (!defined(scene) || model.heightReference === HeightReference.NONE) {
//>>includeStart('debug', pragmas.debug);
if (model.heightReference !== HeightReference.NONE) {
throw new DeveloperError(
- "Height reference is not supported without a scene and globe."
+ "Height reference is not supported without a scene."
);
}
//>>includeEnd('debug');
@@ -2071,7 +2069,7 @@ function updateClamping(model) {
}
const globe = scene.globe;
- const ellipsoid = globe.ellipsoid;
+ const ellipsoid = defaultValue(globe?.ellipsoid, Ellipsoid.WGS84);
// Compute cartographic position so we don't recompute every update
const modelMatrix = model.modelMatrix;
@@ -2085,14 +2083,14 @@ function updateClamping(model) {
}
// Install callback to handle updating of terrain tiles
- const surface = globe._surface;
- model._removeUpdateHeightCallback = surface.updateHeight(
+ model._removeUpdateHeightCallback = scene.updateHeight(
cartoPosition,
- getUpdateHeightCallback(model, ellipsoid, cartoPosition)
+ getUpdateHeightCallback(model, ellipsoid, cartoPosition),
+ model.heightReference
);
// Set the correct height now
- const height = globe.getHeight(cartoPosition);
+ const height = scene.getHeight(cartoPosition, model.heightReference);
if (defined(height)) {
// Get callback with cartoPosition being the non-clamped position
const callback = getUpdateHeightCallback(model, ellipsoid, cartoPosition);
@@ -2100,8 +2098,7 @@ function updateClamping(model) {
// Compute the clamped cartesian and call updateHeight callback
Cartographic.clone(cartoPosition, scratchCartographic);
scratchCartographic.height = height;
- ellipsoid.cartographicToCartesian(scratchCartographic, scratchPosition);
- callback(scratchPosition);
+ callback(scratchCartographic);
}
model._heightDirty = false;
@@ -2358,24 +2355,25 @@ function scaleInPixels(positionWC, radius, frameState) {
);
}
-function getUpdateHeightCallback(model, ellipsoid, cartoPosition) {
+const scratchUpdateHeightCartesian = new Cartesian3();
+function getUpdateHeightCallback(model, ellipsoid, originalPostition) {
return function (clampedPosition) {
- if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {
- const clampedCart = ellipsoid.cartesianToCartographic(
- clampedPosition,
- scratchCartographic
- );
- clampedCart.height += cartoPosition.height;
- ellipsoid.cartographicToCartesian(clampedCart, clampedPosition);
+ if (isHeightReferenceRelative(model.heightReference)) {
+ clampedPosition.height += originalPostition.height;
}
+ ellipsoid.cartographicToCartesian(
+ clampedPosition,
+ scratchUpdateHeightCartesian
+ );
+
const clampedModelMatrix = model._clampedModelMatrix;
// Modify clamped model matrix to use new height
Matrix4.clone(model.modelMatrix, clampedModelMatrix);
- clampedModelMatrix[12] = clampedPosition.x;
- clampedModelMatrix[13] = clampedPosition.y;
- clampedModelMatrix[14] = clampedPosition.z;
+ clampedModelMatrix[12] = scratchUpdateHeightCartesian.x;
+ clampedModelMatrix[13] = scratchUpdateHeightCartesian.y;
+ clampedModelMatrix[14] = scratchUpdateHeightCartesian.z;
model._heightDirty = true;
};
diff --git a/packages/engine/Source/Scene/QuadtreePrimitive.js b/packages/engine/Source/Scene/QuadtreePrimitive.js
index 5b93f2cfad25..66ed2109ca30 100644
--- a/packages/engine/Source/Scene/QuadtreePrimitive.js
+++ b/packages/engine/Source/Scene/QuadtreePrimitive.js
@@ -274,7 +274,7 @@ QuadtreePrimitive.prototype.forEachRenderedTile = function (tileFunction) {
* is the cartesian position on the tile.
*
* @param {Cartographic} cartographic The cartographic position.
- * @param {Function} callback The function to be called when a new tile is loaded containing cartographic.
+ * @param {Function} callback The function to be called when a new tile is loaded containing the updated cartographic.
* @returns {Function} The function to remove this callback from the quadtree.
*/
QuadtreePrimitive.prototype.updateHeight = function (cartographic, callback) {
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index 1d56d15d9333..17ed1cefaa5b 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -18,6 +18,7 @@ import Event from "../Core/Event.js";
import GeographicProjection from "../Core/GeographicProjection.js";
import GeometryInstance from "../Core/GeometryInstance.js";
import GeometryPipeline from "../Core/GeometryPipeline.js";
+import HeightReference from "./HeightReference.js";
import Intersect from "../Core/Intersect.js";
import JulianDate from "../Core/JulianDate.js";
import CesiumMath from "../Core/Math.js";
@@ -3614,25 +3615,57 @@ function callAfterRenderFunctions(scene) {
}
function getGlobeHeight(scene) {
- const globe = scene._globe;
+ if (scene.mode === SceneMode.MORPHING) {
+ return;
+ }
+
const camera = scene.camera;
const cartographic = camera.positionCartographic;
+ return scene.getHeight(cartographic);
+}
+
+/**
+ * Gets the height of the loaded surface at the cartographic position.
+ * @param {Cartographic} cartographic The cartographic position.
+ * @param {HeightReference} [heightReference=CLAMP_TO_GROUND] Based on the height reference value, determines whether to ignore heights from 3D Tiles or terrain.
+ * @private
+ */
+Scene.prototype.getHeight = function (cartographic, heightReference) {
+ if (!defined(cartographic)) {
+ return undefined;
+ }
+
+ const ignore3dTiles =
+ heightReference === HeightReference.CLAMP_TO_TERRAIN ||
+ heightReference === HeightReference.RELATIVE_TO_TERRAIN;
+
+ const ignoreTerrain =
+ heightReference === HeightReference.CLAMP_TO_3D_TILE ||
+ heightReference === HeightReference.RELATIVE_TO_3D_TILE;
let maxHeight = Number.NEGATIVE_INFINITY;
- const length = scene.primitives.length;
- for (let i = 0; i < length; ++i) {
- const primitive = scene.primitives.get(i);
- if (!primitive.isCesium3DTileset || primitive.disableCollision) {
- continue;
- }
- const result = primitive.getHeight(cartographic, scene);
- if (defined(result) && result > maxHeight) {
- maxHeight = result;
+ if (!ignore3dTiles) {
+ const length = this.primitives.length;
+ for (let i = 0; i < length; ++i) {
+ const primitive = this.primitives.get(i);
+ if (
+ !primitive.isCesium3DTileset ||
+ !primitive.show ||
+ primitive.disableCollision
+ ) {
+ continue;
+ }
+
+ const result = primitive.getHeight(cartographic, this);
+ if (defined(result) && result > maxHeight) {
+ maxHeight = result;
+ }
}
}
- if (defined(globe) && globe.show && defined(cartographic)) {
+ const globe = this._globe;
+ if (!ignoreTerrain && defined(globe) && globe.show) {
const result = globe.getHeight(cartographic);
if (result > maxHeight) {
maxHeight = result;
@@ -3644,7 +3677,108 @@ function getGlobeHeight(scene) {
}
return undefined;
-}
+};
+
+const updateHeightScratchCartographic = new Cartographic();
+/**
+ * Calls the callback when a new tile is rendered that contains the given cartographic. The only parameter
+ * is the cartesian position on the tile.
+ *
+ * @private
+ *
+ * @param {Cartographic} cartographic The cartographic position.
+ * @param {Function} callback The function to be called when a new tile is loaded containing the updated cartographic.
+ * @param {HeightReference} [heightReference=CLAMP_TO_GROUND] Based on the height reference value, determines whether to ignore heights from 3D Tiles or terrain.
+ * @returns {Function} The function to remove this callback from the quadtree.
+ */
+Scene.prototype.updateHeight = function (
+ cartographic,
+ callback,
+ heightReference
+) {
+ //>>includeStart('debug', pragmas.debug);
+ Check.typeOf.func("callback", callback);
+ //>>includeEnd('debug');
+
+ const callbackWrapper = () => {
+ Cartographic.clone(cartographic, updateHeightScratchCartographic);
+
+ const height = this.getHeight(cartographic, heightReference);
+ if (defined(height)) {
+ updateHeightScratchCartographic.height = height;
+ callback(updateHeightScratchCartographic);
+ }
+ };
+
+ const ignore3dTiles =
+ heightReference === HeightReference.CLAMP_TO_TERRAIN ||
+ heightReference === HeightReference.RELATIVE_TO_TERRAIN;
+
+ const ignoreTerrain =
+ heightReference === HeightReference.CLAMP_TO_3D_TILE ||
+ heightReference === HeightReference.RELATIVE_TO_3D_TILE;
+
+ let terrainRemoveCallback;
+ if (!ignoreTerrain && defined(this.globe)) {
+ terrainRemoveCallback = this.globe._surface.updateHeight(
+ cartographic,
+ callbackWrapper
+ );
+ }
+
+ let tilesetRemoveCallbacks = {};
+ const ellipsoid = this.globe?.ellipsoid;
+ const createPrimitiveEventListener = (primitive) => {
+ if (
+ ignore3dTiles ||
+ !primitive.isCesium3DTileset ||
+ primitive.disableCollision
+ ) {
+ return;
+ }
+
+ const tilesetRemoveCallback = primitive.updateHeight(
+ cartographic,
+ callbackWrapper,
+ ellipsoid
+ );
+ tilesetRemoveCallbacks[primitive.id] = tilesetRemoveCallback;
+ };
+
+ if (!ignore3dTiles) {
+ const length = this.primitives.length;
+ for (let i = 0; i < length; ++i) {
+ const primitive = this.primitives.get(i);
+ createPrimitiveEventListener(primitive);
+ }
+ }
+
+ const removeAddedListener = this.primitives.primitiveAdded.addEventListener(
+ createPrimitiveEventListener
+ );
+ const removeRemovedListener = this.primitives.primitiveRemoved.addEventListener(
+ (primitive) => {
+ if (!primitive.isCesium3DTileset) {
+ return;
+ }
+
+ tilesetRemoveCallbacks[primitive.id]();
+ delete tilesetRemoveCallbacks[primitive.id];
+ }
+ );
+
+ const removeCallback = () => {
+ terrainRemoveCallback = terrainRemoveCallback && terrainRemoveCallback();
+ Object.values(tilesetRemoveCallbacks).forEach((tilesetRemoveCallback) =>
+ tilesetRemoveCallback()
+ );
+ tilesetRemoveCallbacks = {};
+ removeAddedListener();
+ removeRemovedListener();
+ };
+
+ return removeCallback;
+};
function isCameraUnderground(scene) {
const camera = scene.camera;
diff --git a/packages/engine/Source/Scene/TileBoundingS2Cell.js b/packages/engine/Source/Scene/TileBoundingS2Cell.js
index 872529473f43..307fa36c8e39 100644
--- a/packages/engine/Source/Scene/TileBoundingS2Cell.js
+++ b/packages/engine/Source/Scene/TileBoundingS2Cell.js
@@ -330,7 +330,7 @@ Object.defineProperties(TileBoundingS2Cell.prototype, {
/**
* The underlying bounding volume.
*
- * @memberof TileOrientedBoundingBox.prototype
+ * @memberof TileBoundingS2Cell.prototype
*
* @type {object}
* @readonly
@@ -343,7 +343,7 @@ Object.defineProperties(TileBoundingS2Cell.prototype, {
/**
* The underlying bounding sphere.
*
- * @memberof TileOrientedBoundingBox.prototype
+ * @memberof TileBoundingS2Cell.prototype
*
* @type {BoundingSphere}
* @readonly
diff --git a/packages/engine/Specs/DataSources/ModelVisualizerSpec.js b/packages/engine/Specs/DataSources/ModelVisualizerSpec.js
index 4c12504069d0..7232087d6cd0 100644
--- a/packages/engine/Specs/DataSources/ModelVisualizerSpec.js
+++ b/packages/engine/Specs/DataSources/ModelVisualizerSpec.js
@@ -13,6 +13,7 @@ import {
Resource,
Transforms,
BoundingSphereState,
+ Cesium3DTileset,
ConstantPositionProperty,
ConstantProperty,
EntityCollection,
@@ -24,7 +25,6 @@ import {
CustomShader,
Globe,
Cartographic,
- createWorldTerrainAsync,
} from "../../index.js";
import createScene from "../../../../Specs/createScene.js";
import pollToPromise from "../../../../Specs/pollToPromise.js";
@@ -53,6 +53,7 @@ describe(
afterEach(function () {
visualizer = visualizer && visualizer.destroy();
entityCollection.removeAll();
+ scene.primitives.removeAll();
});
afterAll(function () {
@@ -404,16 +405,18 @@ describe(
expect(result).toEqual(expected);
});
- it("computes bounding sphere with height reference clamp to ground", async function () {
+ it("computes bounding sphere with height reference clamp to terrain", async function () {
// Setup a position for the model.
const position = Cartesian3.fromDegrees(149.515332, -34.984799);
- const positionCartographic = Cartographic.fromCartesian(position);
+
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
// Initialize the Entity and the ModelGraphics.
const time = JulianDate.now();
const testObject = entityCollection.getOrCreateEntity("test");
const model = new ModelGraphics({
- heightReference: HeightReference.CLAMP_TO_GROUND,
+ heightReference: HeightReference.CLAMP_TO_TERRAIN,
});
testObject.model = model;
testObject.position = new ConstantProperty(position);
@@ -426,61 +429,96 @@ describe(
let state = visualizer.getBoundingSphere(testObject, result);
expect(state).toBe(BoundingSphereState.PENDING);
- // Assign a tiled terrain provider to the globe.
- const globe = scene.globe;
- globe.terrainProvider = await createWorldTerrainAsync();
+ spyOn(scene.globe, "getHeight").and.returnValue(10.0);
+ spyOn(tileset, "getHeight").and.returnValue(20.0);
+
+ // Repeatedly request the bounding sphere until it's ready.
+ await pollToPromise(function () {
+ scene.renderForSpecs();
+ visualizer.update(time);
+ state = visualizer.getBoundingSphere(testObject, result);
+ return state !== BoundingSphereState.PENDING;
+ });
+
+ expect(state).toBe(BoundingSphereState.DONE);
+
+ // Ensure that flags and results computed for this model are reset.
+ const modelData = visualizer._modelHash[testObject.id];
+ expect(modelData.clampedBoundingSphere).toBeUndefined();
- const updatedCartographics = await ModelVisualizer._sampleTerrainMostDetailed(
- globe.terrainProvider,
- [positionCartographic]
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = 10.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
);
- const sampledResultCartographic = updatedCartographics[0];
- const sampledResult = globe.ellipsoid.cartographicToCartesian(
- sampledResultCartographic
+ });
+
+ it("computes bounding sphere with height reference relative to terrain", async function () {
+ // Setup a position for the model.
+ const heightOffset = 1000.0;
+ const position = Cartesian3.fromDegrees(
+ 149.515332,
+ -34.984799,
+ heightOffset
);
- const sampleTerrainSpy = spyOn(
- ModelVisualizer,
- "_sampleTerrainMostDetailed"
- ).and.callThrough();
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
+
+ // Initialize the Entity and the ModelGraphics.
+ const time = JulianDate.now();
+ const testObject = entityCollection.getOrCreateEntity("test");
+ const model = new ModelGraphics({
+ heightReference: HeightReference.RELATIVE_TO_TERRAIN,
+ });
+ testObject.model = model;
+ testObject.position = new ConstantProperty(position);
+ model.uri = new ConstantProperty(boxUrl);
+
+ visualizer.update(time);
+
+ // Request the bounding sphere once.
+ const result = new BoundingSphere();
+ let state = visualizer.getBoundingSphere(testObject, result);
+ expect(state).toBe(BoundingSphereState.PENDING);
+
+ spyOn(scene.globe, "getHeight").and.returnValue(10.0);
+ spyOn(tileset, "getHeight").and.returnValue(20.0);
// Repeatedly request the bounding sphere until it's ready.
await pollToPromise(function () {
- scene.render();
+ scene.renderForSpecs();
visualizer.update(time);
state = visualizer.getBoundingSphere(testObject, result);
return state !== BoundingSphereState.PENDING;
});
-
expect(state).toBe(BoundingSphereState.DONE);
// Ensure that flags and results computed for this model are reset.
const modelData = visualizer._modelHash[testObject.id];
- expect(modelData.awaitingSampleTerrain).toBe(false);
expect(modelData.clampedBoundingSphere).toBeUndefined();
- // Ensure that we only sample the terrain once from the visualizer.
- expect(sampleTerrainSpy).toHaveBeenCalledTimes(1);
-
- // Calculate the distance of the bounding sphere returned from the position returned from sample terrain.
- // Since sampleTerrainMostDetailed isn't always precise, we account for some error.
- const distance = Cartesian3.distance(result.center, sampledResult);
- const errorMargin = 100.0;
- expect(distance).toBeLessThan(errorMargin);
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = heightOffset + 10.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
+ );
});
- it("computes bounding sphere with height reference clamp to ground on terrain provider without availability", function () {
+ it("computes bounding sphere with height reference clamp to 3D Tiles", async function () {
// Setup a position for the model.
- const longitude = CesiumMath.toRadians(149.515332);
- const latitude = CesiumMath.toRadians(-34.984799);
- const height = 1000;
- const position = Cartesian3.fromRadians(longitude, latitude, height);
+ const position = Cartesian3.fromDegrees(149.515332, -34.984799);
+
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
// Initialize the Entity and the ModelGraphics.
const time = JulianDate.now();
const testObject = entityCollection.getOrCreateEntity("test");
const model = new ModelGraphics({
- heightReference: HeightReference.CLAMP_TO_GROUND,
+ heightReference: HeightReference.CLAMP_TO_3D_TILE,
});
testObject.model = model;
testObject.position = new ConstantProperty(position);
@@ -493,36 +531,32 @@ describe(
let state = visualizer.getBoundingSphere(testObject, result);
expect(state).toBe(BoundingSphereState.PENDING);
- // Ensure that the terrain provider does not have availability.
- const globe = scene.globe;
- const terrainProvider = globe.terrainProvider;
- expect(terrainProvider.availability).toBe(undefined);
+ spyOn(scene.globe, "getHeight").and.returnValue(20.0);
+ spyOn(tileset, "getHeight").and.returnValue(10.0);
// Repeatedly request the bounding sphere until it's ready.
- return pollToPromise(function () {
- scene.render();
+ await pollToPromise(function () {
+ scene.renderForSpecs();
visualizer.update(time);
state = visualizer.getBoundingSphere(testObject, result);
return state !== BoundingSphereState.PENDING;
- }).then(() => {
- expect(state).toBe(BoundingSphereState.DONE);
- // Ensure that the clamped position has height set to 0.
- const cartographic = globe.ellipsoid.cartesianToCartographic(
- result.center
- );
- expect(cartographic.height).toEqualEpsilon(0, CesiumMath.EPSILON6);
- expect(cartographic.latitude).toEqualEpsilon(
- latitude,
- CesiumMath.EPSILON6
- );
- expect(cartographic.longitude).toEqualEpsilon(
- longitude,
- CesiumMath.EPSILON6
- );
});
+
+ expect(state).toBe(BoundingSphereState.DONE);
+
+ // Ensure that flags and results computed for this model are reset.
+ const modelData = visualizer._modelHash[testObject.id];
+ expect(modelData.clampedBoundingSphere).toBeUndefined();
+
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = 10.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
+ );
});
- it("computes bounding sphere with height reference relative to ground", async function () {
+ it("computes bounding sphere with height reference relative to 3D Tiles", async function () {
// Setup a position for the model.
const heightOffset = 1000.0;
const position = Cartesian3.fromDegrees(
@@ -530,19 +564,15 @@ describe(
-34.984799,
heightOffset
);
- const positionCartographic = Cartographic.fromCartesian(position);
- // Setup a spy so we can track how often sampleTerrain is called.
- const sampleTerrainSpy = spyOn(
- ModelVisualizer,
- "_sampleTerrainMostDetailed"
- ).and.callThrough();
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
// Initialize the Entity and the ModelGraphics.
const time = JulianDate.now();
const testObject = entityCollection.getOrCreateEntity("test");
const model = new ModelGraphics({
- heightReference: HeightReference.RELATIVE_TO_GROUND,
+ heightReference: HeightReference.RELATIVE_TO_3D_TILE,
});
testObject.model = model;
testObject.position = new ConstantProperty(position);
@@ -555,51 +585,90 @@ describe(
let state = visualizer.getBoundingSphere(testObject, result);
expect(state).toBe(BoundingSphereState.PENDING);
- // Assign a tiled terrain provider to the globe.
- const globe = scene.globe;
- globe.terrainProvider = await createWorldTerrainAsync();
+ spyOn(scene.globe, "getHeight").and.returnValue(20.0);
+ spyOn(tileset, "getHeight").and.returnValue(10.0);
- const updatedCartographics = await ModelVisualizer._sampleTerrainMostDetailed(
- globe.terrainProvider,
- [positionCartographic]
- );
- const sampledResultCartographic = updatedCartographics[0];
- const sampledResult = globe.ellipsoid.cartographicToCartesian(
- sampledResultCartographic
+ // Repeatedly request the bounding sphere until it's ready.
+ await pollToPromise(function () {
+ scene.renderForSpecs();
+ visualizer.update(time);
+ state = visualizer.getBoundingSphere(testObject, result);
+ return state !== BoundingSphereState.PENDING;
+ });
+ expect(state).toBe(BoundingSphereState.DONE);
+
+ // Ensure that flags and results computed for this model are reset.
+ const modelData = visualizer._modelHash[testObject.id];
+ expect(modelData.clampedBoundingSphere).toBeUndefined();
+
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = heightOffset + 10.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
);
+ });
+
+ it("computes bounding sphere with height reference clamp to ground", async function () {
+ // Setup a position for the model.
+ const position = Cartesian3.fromDegrees(149.515332, -34.984799);
+
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
+
+ // Initialize the Entity and the ModelGraphics.
+ const time = JulianDate.now();
+ const testObject = entityCollection.getOrCreateEntity("test");
+ const model = new ModelGraphics({
+ heightReference: HeightReference.CLAMP_TO_GROUND,
+ });
+ testObject.model = model;
+ testObject.position = new ConstantProperty(position);
+ model.uri = new ConstantProperty(boxUrl);
+
+ visualizer.update(time);
+
+ // Request the bounding sphere once.
+ const result = new BoundingSphere();
+ let state = visualizer.getBoundingSphere(testObject, result);
+ expect(state).toBe(BoundingSphereState.PENDING);
+
+ spyOn(scene.globe, "getHeight").and.returnValue(10.0);
+ spyOn(tileset, "getHeight").and.returnValue(20.0);
// Repeatedly request the bounding sphere until it's ready.
await pollToPromise(function () {
- scene.render();
+ scene.renderForSpecs();
visualizer.update(time);
state = visualizer.getBoundingSphere(testObject, result);
return state !== BoundingSphereState.PENDING;
});
+
expect(state).toBe(BoundingSphereState.DONE);
// Ensure that flags and results computed for this model are reset.
const modelData = visualizer._modelHash[testObject.id];
- expect(modelData.awaitingSampleTerrain).toBe(false);
expect(modelData.clampedBoundingSphere).toBeUndefined();
- // Ensure that we only sample the terrain once from the visualizer.
- // We check for 2 calls here because we call it once in the test.
- expect(sampleTerrainSpy).toHaveBeenCalledTimes(2);
-
- // Calculate the distance of the bounding sphere returned from the position returned from sample terrain.
- // Since sampleTerrainMostDetailed isn't always precise, we account for some error.
- const distance =
- Cartesian3.distance(result.center, sampledResult) - heightOffset;
- const errorMargin = 100.0;
- expect(distance).toBeLessThan(errorMargin);
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = 20.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
+ );
});
- it("computes bounding sphere with height reference relative to ground on terrain provider without availability", function () {
+ it("computes bounding sphere with height reference relative to ground", async function () {
// Setup a position for the model.
- const longitude = CesiumMath.toRadians(149.515332);
- const latitude = CesiumMath.toRadians(-34.984799);
- const height = 1000;
- const position = Cartesian3.fromRadians(longitude, latitude, height);
+ const heightOffset = 1000.0;
+ const position = Cartesian3.fromDegrees(
+ 149.515332,
+ -34.984799,
+ heightOffset
+ );
+
+ const tileset = new Cesium3DTileset();
+ scene.primitives.add(tileset);
// Initialize the Entity and the ModelGraphics.
const time = JulianDate.now();
@@ -618,31 +687,28 @@ describe(
let state = visualizer.getBoundingSphere(testObject, result);
expect(state).toBe(BoundingSphereState.PENDING);
- // Ensure that the terrain provider does not have availability.
- const globe = scene.globe;
- const terrainProvider = globe.terrainProvider;
- expect(terrainProvider.availability).toBe(undefined);
+ spyOn(scene.globe, "getHeight").and.returnValue(10.0);
+ spyOn(tileset, "getHeight").and.returnValue(20.0);
// Repeatedly request the bounding sphere until it's ready.
- return pollToPromise(function () {
- scene.render();
+ await pollToPromise(function () {
+ scene.renderForSpecs();
visualizer.update(time);
state = visualizer.getBoundingSphere(testObject, result);
return state !== BoundingSphereState.PENDING;
- }).then(() => {
- const cartographic = globe.ellipsoid.cartesianToCartographic(
- result.center
- );
- expect(cartographic.height).toEqualEpsilon(height, CesiumMath.EPSILON6);
- expect(cartographic.latitude).toEqualEpsilon(
- latitude,
- CesiumMath.EPSILON6
- );
- expect(cartographic.longitude).toEqualEpsilon(
- longitude,
- CesiumMath.EPSILON6
- );
});
+ expect(state).toBe(BoundingSphereState.DONE);
+
+ // Ensure that flags and results computed for this model are reset.
+ const modelData = visualizer._modelHash[testObject.id];
+ expect(modelData.clampedBoundingSphere).toBeUndefined();
+
+ const expectedCenter = Cartographic.fromCartesian(position);
+ expectedCenter.height = heightOffset + 20.0;
+ expect(result.center).toEqualEpsilon(
+ Cartographic.toCartesian(expectedCenter),
+ CesiumMath.EPSILON8
+ );
});
it("computes bounding sphere where globe is undefined", async function () {
@@ -715,61 +781,6 @@ describe(
expect(state).toBe(BoundingSphereState.FAILED);
});
- it("fails bounding sphere when sampleTerrainMostDetailed fails", async function () {
- // Setup a position for the model.
- const heightOffset = 1000.0;
- const position = Cartesian3.fromDegrees(
- 149.515332,
- -34.984799,
- heightOffset
- );
-
- // Setup a spy so we can track how often sampleTerrain is called.
- const sampleTerrainSpy = spyOn(
- ModelVisualizer,
- "_sampleTerrainMostDetailed"
- ).and.callFake(() => {
- return Promise.reject(404);
- });
-
- // Initialize the Entity and the ModelGraphics.
- const time = JulianDate.now();
- const testObject = entityCollection.getOrCreateEntity("test");
- const model = new ModelGraphics({
- heightReference: HeightReference.RELATIVE_TO_GROUND,
- });
- testObject.model = model;
- testObject.position = new ConstantProperty(position);
- model.uri = new ConstantProperty(boxUrl);
-
- visualizer.update(time);
-
- // Assign a tiled terrain provider to the globe.
- const globe = scene.globe;
- globe.terrainProvider = await createWorldTerrainAsync();
-
- // Request the bounding sphere once.
- const result = new BoundingSphere();
- let state;
-
- // Repeatedly request the bounding sphere until it's ready.
- return pollToPromise(function () {
- scene.render();
- visualizer.update(time);
- state = visualizer.getBoundingSphere(testObject, result);
- return state !== BoundingSphereState.PENDING;
- }).then(() => {
- expect(state).toBe(BoundingSphereState.FAILED);
-
- // Ensure that flags and results computed for this model are reset.
- const modelData = visualizer._modelHash[testObject.id];
- expect(modelData.sampleTerrainFailed).toBe(false);
-
- // Ensure that we only sample the terrain once from the visualizer.
- expect(sampleTerrainSpy).toHaveBeenCalledTimes(1);
- });
- });
-
it("compute bounding sphere throws without entity", function () {
const result = new BoundingSphere();
expect(function () {
diff --git a/packages/engine/Specs/Scene/BillboardCollectionSpec.js b/packages/engine/Specs/Scene/BillboardCollectionSpec.js
index 529ff9049530..58b341e8fc7f 100644
--- a/packages/engine/Specs/Scene/BillboardCollectionSpec.js
+++ b/packages/engine/Specs/Scene/BillboardCollectionSpec.js
@@ -3,10 +3,12 @@ import {
BoundingSphere,
Cartesian2,
Cartesian3,
+ Cartographic,
CesiumTerrainProvider,
Color,
createGuid,
DistanceDisplayCondition,
+ Globe,
NearFarScalar,
OrthographicOffCenterFrustum,
PerspectiveFrustum,
@@ -23,7 +25,6 @@ import {
import { Math as CesiumMath } from "../../index.js";
-import createGlobe from "../../../../Specs/createGlobe.js";
import createScene from "../../../../Specs/createScene.js";
import pollToPromise from "../../../../Specs/pollToPromise.js";
@@ -2394,7 +2395,7 @@ describe(
describe("height referenced billboards", function () {
let billboardsWithHeight;
beforeEach(function () {
- scene.globe = createGlobe();
+ scene.globe = new Globe();
billboardsWithHeight = new BillboardCollection({
scene: scene,
});
@@ -2417,63 +2418,127 @@ describe(
});
it("creating with a height reference creates a height update callback", function () {
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("set height reference property creates a height update callback", function () {
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const b = billboardsWithHeight.add({
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
b.heightReference = HeightReference.CLAMP_TO_GROUND;
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("updates the callback when the height reference changes", function () {
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const b = billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
b.heightReference = HeightReference.RELATIVE_TO_GROUND;
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeDefined();
- scene.globe.removedCallback = false;
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.RELATIVE_TO_GROUND
+ );
+ });
+
+ it("removes the callback when the height reference changes", function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const b = billboardsWithHeight.add({
+ heightReference: HeightReference.CLAMP_TO_GROUND,
+ position: position,
+ });
+
b.heightReference = HeightReference.NONE;
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeUndefined();
+ expect(removeCallback).toHaveBeenCalled();
});
it("changing the position updates the callback", function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ let position = Cartesian3.fromDegrees(-72.0, 40.0);
const b = billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
- b.position = Cartesian3.fromDegrees(-73.0, 40.0);
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeDefined();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
+
+ position = b.position = Cartesian3.fromDegrees(-73.0, 40.0);
+
+ expect(removeCallback).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("callback updates the position", function () {
+ let invokeCallback;
+ spyOn(scene, "updateHeight").and.callFake(
+ (cartographic, updateCallback) => {
+ invokeCallback = (height) => {
+ cartographic.height = height;
+ updateCallback(cartographic);
+ };
+ }
+ );
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const b = billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalled();
let cartographic = scene.globe.ellipsoid.cartesianToCartographic(
b._clampedPosition
);
expect(cartographic.height).toEqual(0.0);
- scene.globe.callback(Cartesian3.fromDegrees(-72.0, 40.0, 100.0));
+ invokeCallback(100.0);
+
cartographic = scene.globe.ellipsoid.cartesianToCartographic(
b._clampedPosition
);
@@ -2484,14 +2549,16 @@ describe(
expect(b._clampedPosition).toBeUndefined();
});
- it("disableDepthTest after another function", function () {
+ it("removes callback after disableDepthTest", function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
const b = billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
position: Cartesian3.fromDegrees(-122, 46.0),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
});
scene.renderForSpecs();
- expect(scene.globe.callback).toBeDefined();
expect(b._clampedPosition).toBeDefined();
//After changing disableDepthTestDistance and heightReference, the callback should be undefined
@@ -2499,18 +2566,24 @@ describe(
b.heightReference = HeightReference.NONE;
scene.renderForSpecs();
- expect(scene.globe.callback).toBeUndefined();
expect(b._clampedPosition).toBeUndefined();
+ expect(removeCallback).toHaveBeenCalled();
});
- it("changing the terrain provider", async function () {
- const b = billboardsWithHeight.add({
+ it("updates the callback when the terrain provider is changed", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+ billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
-
- spyOn(b, "_updateClamping").and.callThrough();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
const terrainProvider = await CesiumTerrainProvider.fromUrl(
"made/up/url",
@@ -2521,7 +2594,12 @@ describe(
scene.terrainProvider = terrainProvider;
- expect(b._updateClamping).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
+ expect(removeCallback).toHaveBeenCalled();
});
it("height reference without a scene rejects", function () {
@@ -2543,15 +2621,17 @@ describe(
}).toThrowDeveloperError();
});
- it("height reference without a globe rejects", function () {
+ it("height reference without a globe works", function () {
scene.globe = undefined;
expect(function () {
- return billboardsWithHeight.add({
+ billboardsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
position: Cartesian3.fromDegrees(-72.0, 40.0),
});
- }).toThrowDeveloperError();
+
+ scene.renderForSpecs();
+ }).not.toThrowError();
});
it("changing height reference without a globe throws DeveloperError", function () {
@@ -2563,7 +2643,8 @@ describe(
expect(function () {
b.heightReference = HeightReference.CLAMP_TO_GROUND;
- }).toThrowDeveloperError();
+ scene.renderForSpecs();
+ }).not.toThrowDeveloperError();
});
});
},
diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
index 20849c2cd004..2fddd5b7a6cb 100644
--- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
+++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
@@ -2577,9 +2577,9 @@ describe(
);
const expected = new Cartesian3(
- 1215013.8353220497,
- -4736316.763939952,
- 4081608.4319443353
+ 1215013.1035421563,
+ -4736313.911345786,
+ 4081605.96109977
);
expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
expected,
diff --git a/packages/engine/Specs/Scene/LabelCollectionSpec.js b/packages/engine/Specs/Scene/LabelCollectionSpec.js
index a4af501bbd44..70d96c24404e 100644
--- a/packages/engine/Specs/Scene/LabelCollectionSpec.js
+++ b/packages/engine/Specs/Scene/LabelCollectionSpec.js
@@ -3,6 +3,7 @@ import {
BoundingSphere,
Cartesian2,
Cartesian3,
+ Cartographic,
Color,
defined,
DistanceDisplayCondition,
@@ -19,16 +20,12 @@ import {
} from "../../index.js";
import { Math as CesiumMath } from "../../index.js";
-
-import createGlobe from "../../../../Specs/createGlobe.js";
import createScene from "../../../../Specs/createScene.js";
import pollToPromise from "../../../../Specs/pollToPromise.js";
describe(
"Scene/LabelCollection",
function () {
- // TODO: rendering tests for pixel offset, eye offset, horizontal origin, vertical origin, font, style, outlineColor, outlineWidth, and fillColor properties
-
let scene;
let camera;
let labels;
@@ -2565,8 +2562,7 @@ describe(
describe("height referenced labels", function () {
beforeEach(function () {
- scene.globe = createGlobe();
-
+ scene.globe = new Globe();
labelsWithHeight = new LabelCollection({
scene: scene,
});
@@ -2574,7 +2570,6 @@ describe(
});
it("explicitly constructs a label with height reference", function () {
- scene.globe = createGlobe();
const l = labelsWithHeight.add({
text: "test",
heightReference: HeightReference.CLAMP_TO_GROUND,
@@ -2584,7 +2579,6 @@ describe(
});
it("set label height reference property", function () {
- scene.globe = createGlobe();
const l = labelsWithHeight.add({
text: "test",
});
@@ -2594,68 +2588,127 @@ describe(
});
it("creating with a height reference creates a height update callback", function () {
- scene.globe = createGlobe();
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("set height reference property creates a height update callback", function () {
- scene.globe = createGlobe();
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const l = labelsWithHeight.add({
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
l.heightReference = HeightReference.CLAMP_TO_GROUND;
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("updates the callback when the height reference changes", function () {
- scene.globe = createGlobe();
+ spyOn(scene, "updateHeight");
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const l = labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
l.heightReference = HeightReference.RELATIVE_TO_GROUND;
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeDefined();
- scene.globe.removedCallback = false;
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.RELATIVE_TO_GROUND
+ );
+ });
+
+ it("removes the callback when the height reference changes", function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const l = labelsWithHeight.add({
+ heightReference: HeightReference.CLAMP_TO_GROUND,
+ position: position,
+ });
+
l.heightReference = HeightReference.NONE;
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeUndefined();
+
+ expect(removeCallback).toHaveBeenCalled();
});
it("changing the position updates the callback", function () {
- scene.globe = createGlobe();
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ let position = Cartesian3.fromDegrees(-72.0, 40.0);
const l = labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
- l.position = Cartesian3.fromDegrees(-73.0, 40.0);
- expect(scene.globe.removedCallback).toEqual(true);
- expect(scene.globe.callback).toBeDefined();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
+
+ position = l.position = Cartesian3.fromDegrees(-73.0, 40.0);
+
+ expect(removeCallback).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Cartographic.fromCartesian(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
it("callback updates the position", function () {
- scene.globe = createGlobe();
+ let invokeCallback;
+ spyOn(scene, "updateHeight").and.callFake(
+ (cartographic, updateCallback) => {
+ invokeCallback = (height) => {
+ cartographic.height = height;
+ updateCallback(cartographic);
+ };
+ }
+ );
+
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
const l = labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
- position: Cartesian3.fromDegrees(-72.0, 40.0),
+ position: position,
});
- expect(scene.globe.callback).toBeDefined();
+ expect(scene.updateHeight).toHaveBeenCalled();
let cartographic = scene.globe.ellipsoid.cartesianToCartographic(
l._clampedPosition
);
expect(cartographic.height).toEqual(0.0);
- scene.globe.callback(Cartesian3.fromDegrees(-72.0, 40.0, 100.0));
+ invokeCallback(100.0);
+
cartographic = scene.globe.ellipsoid.cartesianToCartographic(
l._clampedPosition
);
@@ -2663,7 +2716,6 @@ describe(
});
it("resets the clamped position when HeightReference.NONE", function () {
- scene.globe = createGlobe();
spyOn(scene.camera, "update");
const l = labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
@@ -2681,7 +2733,6 @@ describe(
});
it("clears the billboard height reference callback when the label is removed", function () {
- scene.globe = createGlobe();
spyOn(scene.camera, "update");
const l = labelsWithHeight.add({
heightReference: HeightReference.CLAMP_TO_GROUND,
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index 983c037b155e..b86bbd1df89e 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -19,8 +19,8 @@ import {
DynamicAtmosphereLightingType,
DracoLoader,
Ellipsoid,
- Event,
FeatureDetection,
+ Globe,
Fog,
HeadingPitchRange,
HeadingPitchRoll,
@@ -2252,304 +2252,258 @@ describe(
});
describe("height reference", function () {
- let sceneWithMockGlobe;
-
- function createMockGlobe() {
- const globe = {
- callback: undefined,
- removedCallback: false,
- ellipsoid: Ellipsoid.WGS84,
- update: function () {},
- render: function () {},
- getHeight: function () {
- return 0.0;
- },
- _surface: {
- tileProvider: {},
- _tileLoadQueueHigh: [],
- _tileLoadQueueMedium: [],
- _tileLoadQueueLow: [],
- _debug: {
- tilesWaitingForChildren: 0,
- },
- },
- imageryLayersUpdatedEvent: new Event(),
- destroy: function () {},
- beginFrame: function () {},
- endFrame: function () {},
- terrainProviderChanged: new Event(),
- };
-
- Object.defineProperties(globe, {
- terrainProvider: {
- set: function (value) {
- this.terrainProviderChanged.raiseEvent(value);
- },
- },
- });
-
- globe._surface.updateHeight = function (position, callback) {
- globe.callback = callback;
- return function () {
- globe.removedCallback = true;
- globe.callback = undefined;
- };
- };
-
- return globe;
- }
-
- beforeAll(function () {
- sceneWithMockGlobe = createScene();
- });
-
- beforeEach(function () {
- sceneWithMockGlobe.globe = createMockGlobe();
- });
-
- afterEach(function () {
- sceneWithMockGlobe.primitives.removeAll();
+ beforeEach(() => {
+ scene.globe = new Globe();
});
- afterAll(function () {
- sceneWithMockGlobe.destroyForSpecs();
+ afterEach(() => {
+ scene.globe = undefined;
});
- it("initializes with height reference", function () {
- return loadAndZoomToModelAsync(
+ it("initializes with height reference", async function () {
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(model.heightReference).toEqual(
- HeightReference.CLAMP_TO_GROUND
- );
- expect(model._scene).toBe(sceneWithMockGlobe);
- expect(model._clampedModelMatrix).toBeDefined();
- });
+ scene
+ );
+ expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
+ expect(model._scene).toBe(scene);
+ expect(model._clampedModelMatrix).toBeDefined();
});
- it("changing height reference works", function () {
- return loadAndZoomToModelAsync(
+ it("changing height reference works", async function () {
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
heightReference: HeightReference.NONE,
- scene: sceneWithMockGlobe,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(model.heightReference).toEqual(HeightReference.NONE);
- expect(model._clampedModelMatrix).toBeUndefined();
+ scene
+ );
+ expect(model.heightReference).toEqual(HeightReference.NONE);
+ expect(model._clampedModelMatrix).toBeUndefined();
- model.heightReference = HeightReference.CLAMP_TO_GROUND;
- expect(model._heightDirty).toBe(true);
+ model.heightReference = HeightReference.CLAMP_TO_GROUND;
+ expect(model._heightDirty).toBe(true);
- sceneWithMockGlobe.renderForSpecs();
- expect(model._heightDirty).toBe(false);
- expect(model.heightReference).toEqual(
- HeightReference.CLAMP_TO_GROUND
- );
- expect(model._clampedModelMatrix).toBeDefined();
- });
+ scene.renderForSpecs();
+ expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
+ expect(model._clampedModelMatrix).toBeDefined();
});
- it("creates height update callback when initializing with height reference", function () {
- return loadAndZoomToModelAsync(
+ it("creates height update callback when initializing with height reference", async function () {
+ spyOn(scene, "updateHeight");
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(model.heightReference).toEqual(
- HeightReference.CLAMP_TO_GROUND
- );
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
- });
+ scene
+ );
+
+ expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
- it("creates height update callback after setting height reference", function () {
- return loadAndZoomToModelAsync(
+ it("creates height update callback after setting height reference", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
heightReference: HeightReference.NONE,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(model.heightReference).toEqual(HeightReference.NONE);
- expect(sceneWithMockGlobe.globe.callback).toBeUndefined();
+ scene
+ );
- model.heightReference = HeightReference.CLAMP_TO_GROUND;
- expect(model.heightReference).toEqual(
- HeightReference.CLAMP_TO_GROUND
- );
- sceneWithMockGlobe.renderForSpecs();
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
- });
+ model.heightReference = HeightReference.CLAMP_TO_GROUND;
+ expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND);
+
+ scene.renderForSpecs();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
- it("updates height reference callback when the height reference changes", function () {
- return loadAndZoomToModelAsync(
+ it("removes height update callback after changing height reference", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
+ scene
+ );
- model.heightReference = HeightReference.RELATIVE_TO_GROUND;
- sceneWithMockGlobe.renderForSpecs();
- expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true);
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
+ model.heightReference = HeightReference.NONE;
+ expect(model.heightReference).toEqual(HeightReference.NONE);
- sceneWithMockGlobe.globe.removedCallback = false;
- model.heightReference = HeightReference.NONE;
- sceneWithMockGlobe.renderForSpecs();
- expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true);
- expect(sceneWithMockGlobe.globe.callback).toBeUndefined();
- });
+ scene.renderForSpecs();
+ expect(removeCallback).toHaveBeenCalled();
});
- it("updates height reference callback when the model matrix changes", function () {
- const modelMatrix = Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- );
- return loadAndZoomToModelAsync(
+ it("updates height reference callback when the height reference changes", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+ const position = Cartesian3.fromDegrees(-72.0, 40.0);
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Matrix4.clone(modelMatrix),
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(position),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
-
- // Modify the model matrix in place
- const position = Cartesian3.fromDegrees(-73.0, 40.0);
- model.modelMatrix[12] = position.x;
- model.modelMatrix[13] = position.y;
- model.modelMatrix[14] = position.z;
-
- sceneWithMockGlobe.renderForSpecs();
- expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true);
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
+ scene
+ );
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
- // Replace the model matrix entirely
- model.modelMatrix = modelMatrix;
+ model.heightReference = HeightReference.RELATIVE_TO_GROUND;
+ scene.renderForSpecs();
- sceneWithMockGlobe.renderForSpecs();
- expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true);
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
- });
+ expect(removeCallback).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.RELATIVE_TO_GROUND
+ );
});
- it("height reference callback updates the position", function () {
- return loadAndZoomToModelAsync(
+ it("updates height reference callback when the model matrix changes", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ let position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const modelMatrix = Transforms.eastNorthUpToFixedFrame(position);
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
+ modelMatrix: Matrix4.clone(modelMatrix),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
+ scene
+ );
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
- sceneWithMockGlobe.globe.callback(
- Cartesian3.fromDegrees(-72.0, 40.0, 100.0)
- );
- const matrix = model._clampedModelMatrix;
- const position = new Cartesian3(matrix[12], matrix[13], matrix[14]);
- const ellipsoid = sceneWithMockGlobe.globe.ellipsoid;
- const cartographic = ellipsoid.cartesianToCartographic(position);
- expect(cartographic.height).toEqualEpsilon(
- 100.0,
- CesiumMath.EPSILON9
- );
- });
+ // Modify the model matrix in place
+ position = Cartesian3.fromDegrees(-73.0, 40.0);
+ model.modelMatrix[12] = position.x;
+ model.modelMatrix[13] = position.y;
+ model.modelMatrix[14] = position.z;
+
+ scene.renderForSpecs();
+ expect(removeCallback).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
- it("height reference accounts for change in terrain provider", function () {
- return loadAndZoomToModelAsync(
+ it("updates height reference callback when the model matrix is set", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ let position = Cartesian3.fromDegrees(-72.0, 40.0);
+ const modelMatrix = Transforms.eastNorthUpToFixedFrame(position);
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
+ modelMatrix: Matrix4.clone(modelMatrix),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(model._heightDirty).toBe(false);
- const terrainProvider = new CesiumTerrainProvider({
- url: "made/up/url",
- requestVertexNormals: true,
- });
- sceneWithMockGlobe.terrainProvider = terrainProvider;
+ scene
+ );
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
- expect(model._heightDirty).toBe(true);
- sceneWithMockGlobe.terrainProvider = undefined;
- });
+ position = Cartesian3.fromDegrees(-73.0, 40.0);
+ modelMatrix[12] = position.x;
+ modelMatrix[13] = position.y;
+ modelMatrix[14] = position.z;
+ model.modelMatrix = modelMatrix;
+
+ scene.renderForSpecs();
+ expect(removeCallback).toHaveBeenCalled();
+ expect(scene.updateHeight).toHaveBeenCalledWith(
+ Ellipsoid.WGS84.cartesianToCartographic(position),
+ jasmine.any(Function),
+ HeightReference.CLAMP_TO_GROUND
+ );
});
- it("throws when initializing height reference with no scene", function () {
- return loadAndZoomToModelAsync(
+ it("height reference callback updates the position", async function () {
+ let invokeCallback;
+ spyOn(scene, "updateHeight").and.callFake(
+ (cartographic, updateCallback) => {
+ invokeCallback = (height) => {
+ cartographic.height = height;
+ updateCallback(cartographic);
+ };
+ }
+ );
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
modelMatrix: Transforms.eastNorthUpToFixedFrame(
Cartesian3.fromDegrees(-72.0, 40.0)
),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: undefined,
+ scene: scene,
},
- sceneWithMockGlobe
- ).catch(function (error) {
- expect(error.message).toEqual(
- "Height reference is not supported without a scene and globe."
- );
- });
- });
+ scene
+ );
- it("throws when changing height reference with no scene", function () {
- return loadAndZoomToModelAsync(
- {
- gltf: boxTexturedGltfUrl,
- modelMatrix: Transforms.eastNorthUpToFixedFrame(
- Cartesian3.fromDegrees(-72.0, 40.0)
- ),
- heightReference: HeightReference.NONE,
- },
- sceneWithMockGlobe
- ).then(function (model) {
- expect(function () {
- model.heightReference = HeightReference.CLAMP_TO_GROUND;
- sceneWithMockGlobe.renderForSpecs();
- }).toThrowDeveloperError();
- });
+ invokeCallback(100.0);
+
+ const matrix = model._clampedModelMatrix;
+ const position = new Cartesian3(matrix[12], matrix[13], matrix[14]);
+ const cartographic = Ellipsoid.WGS84.cartesianToCartographic(position);
+ expect(cartographic.height).toEqualEpsilon(100.0, CesiumMath.EPSILON9);
});
- it("throws when initializing height reference with no globe", function () {
- return loadAndZoomToModelAsync(
+ it("height reference accounts for change in terrain provider", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
modelMatrix: Transforms.eastNorthUpToFixedFrame(
@@ -2559,49 +2513,89 @@ describe(
scene: scene,
},
scene
- ).catch(function (error) {
- expect(error.message).toEqual(
- "Height reference is not supported without a scene and globe."
- );
+ );
+ expect(model._heightDirty).toBe(false);
+ const terrainProvider = new CesiumTerrainProvider({
+ url: "made/up/url",
+ requestVertexNormals: true,
});
+ scene.terrainProvider = terrainProvider;
+
+ expect(model._heightDirty).toBe(true);
+ scene.terrainProvider = undefined;
});
- it("throws when changing height reference with no globe", function () {
- return loadAndZoomToModelAsync(
+ it("throws when initializing height reference with no scene", async function () {
+ await expectAsync(
+ loadAndZoomToModelAsync(
+ {
+ gltf: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(-72.0, 40.0)
+ ),
+ heightReference: HeightReference.CLAMP_TO_GROUND,
+ scene: undefined,
+ },
+ scene
+ )
+ ).toBeRejectedWithDeveloperError(
+ "Height reference is not supported without a scene."
+ );
+ });
+
+ it("throws when changing height reference with no scene", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGltfUrl,
modelMatrix: Transforms.eastNorthUpToFixedFrame(
Cartesian3.fromDegrees(-72.0, 40.0)
),
- scene: scene,
+ heightReference: HeightReference.NONE,
},
scene
- ).then(function (model) {
- expect(function () {
- model.heightReference = HeightReference.CLAMP_TO_GROUND;
- scene.renderForSpecs();
- }).toThrowDeveloperError();
- });
+ );
+
+ expect(function () {
+ model.heightReference = HeightReference.CLAMP_TO_GROUND;
+ scene.renderForSpecs();
+ }).toThrowDeveloperError();
});
- it("destroys height reference callback", function () {
- return loadAndZoomToModelAsync(
+ it("works when initializing height reference with no globe", function () {
+ return expectAsync(
+ loadAndZoomToModelAsync(
+ {
+ gltf: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(-72.0, 40.0)
+ ),
+ heightReference: HeightReference.CLAMP_TO_GROUND,
+ scene: scene,
+ },
+ scene
+ )
+ ).toBeResolved();
+ });
+
+ it("destroys height reference callback", async function () {
+ const removeCallback = jasmine.createSpy();
+ spyOn(scene, "updateHeight").and.returnValue(removeCallback);
+
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxTexturedGlbUrl,
modelMatrix: Transforms.eastNorthUpToFixedFrame(
Cartesian3.fromDegrees(-72.0, 40.0)
),
heightReference: HeightReference.CLAMP_TO_GROUND,
- scene: sceneWithMockGlobe,
+ scene: scene,
},
- sceneWithMockGlobe
- ).then(function (model) {
- expect(sceneWithMockGlobe.globe.callback).toBeDefined();
+ scene
+ );
- sceneWithMockGlobe.primitives.remove(model);
- expect(model.isDestroyed()).toBe(true);
- expect(sceneWithMockGlobe.globe.callback).toBeUndefined();
- });
+ scene.primitives.remove(model);
+ expect(model.isDestroyed()).toBe(true);
+ expect(removeCallback).toHaveBeenCalled();
});
});