diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 8a02ca44d78e..3b982b06af77 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: install node 18 + - name: install node 20 uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '20' - name: npm install run: npm install - name: lint *.js @@ -27,8 +27,8 @@ jobs: coverage: runs-on: ubuntu-latest env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_ACCESS_KEY_ID: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-1 BRANCH: ${{ github.ref_name }} steps: @@ -45,7 +45,7 @@ jobs: run: npm run coverage -- --browsers FirefoxHeadless --webgl-stub --failTaskOnError --suppressPassed - name: upload coverage artifacts if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: aws s3 sync ./Build/Coverage s3://cesium-dev/cesium/$BRANCH/Build/Coverage --delete --color on + run: aws s3 sync ./Build/Coverage s3://cesium-public-builds/cesium/$BRANCH/Build/Coverage --delete --color on release-tests: runs-on: ubuntu-latest steps: @@ -69,8 +69,8 @@ jobs: contents: read env: BUILD_VERSION: ${{ github.ref_name }}.${{ github.run_number }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_ACCESS_KEY_ID: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-1 BRANCH: ${{ github.ref_name }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -97,7 +97,22 @@ jobs: - uses: ./.github/actions/verify-package - name: deploy to s3 if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: npm run deploy-s3 -- -b "cesium-dev" -d cesium/$BRANCH -c 'no-cache' --confirm + run: | + aws s3 sync . s3://cesium-public-builds/cesium/$BRANCH/ \ + --cache-control "no-cache" \ + --exclude ".git/*" \ + --exclude ".concierge/*" \ + --exclude ".github/*" \ + --exclude ".husky/*" \ + --exclude ".vscode/*" \ + --exclude "Build/Coverage/*" \ + --exclude "Build/CesiumDev/*" \ + --exclude "Build/Specs/e2e" \ + --exclude "Documentation/*" \ + --exclude "node_modules/*" \ + --exclude "scripts/*" \ + --exclude "Tools/*" \ + --delete - name: set status if: ${{ env.AWS_ACCESS_KEY_ID != '' }} run: npm run deploy-status -- --status success --message Deployed @@ -105,10 +120,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: install node 16 + - name: install node 18 uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' - name: npm install run: npm install - name: release build diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 775d2db212d9..2ddc67d198ef 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ on: jobs: if_error_or_failure: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion != 'success' }} + if: ${{ github.event.workflow_run.conclusion == 'failure' }} steps: - name: message result in slack id: slack diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index cdd897e6c41e..63fc926c4b12 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: install node 18 + - name: install node 20 uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '20' - name: npm install run: npm install - name: lint *.js @@ -22,13 +22,10 @@ jobs: run: npm run prettier-check deploy: runs-on: ubuntu-latest - permissions: - statuses: write env: PROD: true - BUILD_VERSION: ${{ github.ref_name }}.${{ github.run_number }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_ACCESS_KEY_ID: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-1 BRANCH: ${{ github.ref_name }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -36,10 +33,10 @@ jobs: GITHUB_SHA: ${{ github.sha }} steps: - uses: actions/checkout@v3 - - name: install node 18 + - name: install node 20 uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '20' - name: npm install run: npm install - name: build website release @@ -48,7 +45,10 @@ jobs: run: npm run build-apps - name: deploy to cesium.com if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: npm run deploy-s3 -- -b "cesium.com-next" -c 'public, max-age=1800'--skip --confirm - - name: set status - if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - run: npm run deploy-status -- --status success --message Deployed \ No newline at end of file + run: | + curl -LO $(curl https://api.github.com/repos/CesiumGS/cesium/releases/latest -H "Authorization: ${GITHUB_TOKEN}" | jq -r '.assets[0].browser_download_url') + unzip Cesium-$(cat package.json | jq -r '.version' | sed 's/\.0$//').zip -d Build/release/ -x "Apps" + aws s3 sync Build/release/ s3://cesium-website/cesiumjs/releases/$(cat package.json | jq -r '.version' | sed 's/\.0$//')/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/Documentation/ s3://cesium-website/cesiumjs/ref-doc/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/CesiumViewer/ s3://cesium-website/cesiumjs/cesium-viewer/ --cache-control "public, max-age=1800" --delete + aws s3 sync Build/Sandcastle/ s3://cesium-sandcastle-website/ --cache-control "public, max-age=1800" --delete diff --git a/.slackbot.yml b/.slackbot.yml index f1b54f977c8d..ca0d87fc8160 100644 --- a/.slackbot.yml +++ b/.slackbot.yml @@ -4,5 +4,11 @@ releaseSchedule: - ggetz, 3/1/2023 - jjhembd, 4/3/2023 - ggetz, 5/1/2023 - -jjhembd, 6/1/2023 + - jjhembd, 6/1/2023 - ggetz, 7/1/2023 + - jjhembd, 8/1/2023 + - ggetz, 9/1/2023 + - jjhembd, 10/1/2023 + - ggetz, 11/1/2023 + - jjhembd, 12/1/2023 + - ggetz, 1/2/2024 diff --git a/Apps/Sandcastle/gallery/3D Tiles 1.1 Photogrammetry.html b/Apps/Sandcastle/gallery/3D Tiles 1.1 Photogrammetry.html new file mode 100644 index 000000000000..f49f14fa1df8 --- /dev/null +++ b/Apps/Sandcastle/gallery/3D Tiles 1.1 Photogrammetry.html @@ -0,0 +1,422 @@ + + +
+ + + + + + +maximumRequestsPerServer
.
@@ -78,13 +78,7 @@ RequestScheduler.maximumRequestsPerServer = 6;
* "assets.cesium.com:443": 18,
* };
*/
-RequestScheduler.requestsByServer = {
- "api.cesium.com:443": 18,
- "assets.ion.cesium.com:443": 18,
- "ibasemaps-api.arcgis.com:443": 18,
- "tile.googleapis.com:443": 18,
- "tile.openstreetmap.org:443": 18,
-};
+RequestScheduler.requestsByServer = {};
/**
* Specifies if the request scheduler should throttle incoming requests, or let the browser queue requests under its control.
diff --git a/packages/engine/Source/Scene/ArcGisMapService.js b/packages/engine/Source/Scene/ArcGisMapService.js
index 6cd673976ac2..1920062edfe5 100644
--- a/packages/engine/Source/Scene/ArcGisMapService.js
+++ b/packages/engine/Source/Scene/ArcGisMapService.js
@@ -4,7 +4,7 @@ import Resource from "../Core/Resource.js";
let defaultTokenCredit;
const defaultAccessToken =
- "AAPK4db83df15a06415e9b7d45d826eec992u0REAmuLmykhOFOk_4e61HgsO_e3C_qGUgiWNGElpjpiMfO9_Qg5ZRyy8POvB5EF";
+ "AAPK2b93071721df4cc78be0d8b3d79b1fd54YMocOcx2NxlbYTDkyO5gPk8XsDnguQgeMdFKepFwLwTgb8vHfPvSTdjy_KlMHlS";
/**
* Default options for accessing the ArcGIS image tile service.
*
diff --git a/packages/engine/Source/Scene/Cesium3DTileContent.js b/packages/engine/Source/Scene/Cesium3DTileContent.js
index 76dcdd255b47..945242881b42 100644
--- a/packages/engine/Source/Scene/Cesium3DTileContent.js
+++ b/packages/engine/Source/Scene/Cesium3DTileContent.js
@@ -355,18 +355,12 @@ Cesium3DTileContent.prototype.update = function (tileset, frameState) {
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Cesium3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Cesium3DTileContent.prototype.pick = function (ray, frameState, result) {
DeveloperError.throwInstantiationError();
};
diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js
index 50fcf0be44ed..a682d4c93c86 100644
--- a/packages/engine/Source/Scene/Cesium3DTileset.js
+++ b/packages/engine/Source/Scene/Cesium3DTileset.js
@@ -110,11 +110,13 @@ import Ray from "../Core/Ray.js";
* @property {string|number} [instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @property {boolean} [showCreditsOnScreen=false] Whether to display the credits of this tileset on screen.
* @property {SplitDirection} [splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this tileset.
- * @property {boolean} [projectTo2D=false] Whether to accurately project the tileset to 2D. If this is true, the tileset will be projected accurately to 2D, but it will use more memory to do so. If this is false, the tileset will use less memory and will still render in 2D / CV mode, but its projected positions may be inaccurate. This cannot be set after the tileset has loaded.
+ * @property {boolean} [enableCameraCollision=false] When {@link ScreenSpaceCameraController#enableCollisionDetection} is true, prevents the camera from going below the tileset surface.
+ * @property {boolean} [projectTo2D=false] Whether to accurately project the tileset to 2D. If this is true, the tileset will be projected accurately to 2D, but it will use more memory to do so. If this is false, the tileset will use less memory and will still render in 2D / CV mode, but its projected positions may be inaccurate. This cannot be set after the tileset has been created.
+ * @property {boolean} [enablePick=false] Whether to allow with CPU picking with pick
when not using WebGL 2 or above. If using WebGL 2 or above, this option will be ignored. If using WebGL 1 and this is true, the pick
operation will work correctly, but it will use more memory to do so. If running with WebGL 1 and this is false, the model will use less memory, but pick
will always return undefined
. This cannot be set after the tileset has loaded.
* @property {string} [debugHeatmapTilePropertyName] The tile variable to colorize as a heatmap. All rendered tiles will be colorized relative to each other's specified variable value.
* @property {boolean} [debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering.
* @property {boolean} [debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile.
- * @property {boolean} [enableDebugWireframe] For debugging only. This must be true for debugWireframe to work in WebGL1. This cannot be set after the tileset has loaded.
+ * @property {boolean} [enableDebugWireframe=false] For debugging only. This must be true for debugWireframe to work in WebGL1. This cannot be set after the tileset has been created.
* @property {boolean} [debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe.
* @property {boolean} [debugShowBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile.
* @property {boolean} [debugShowContentBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile's content.
@@ -151,6 +153,18 @@ import Ray from "../Core/Ray.js";
* }
*
* @example
+ * // Keep camera from going under 3D tileset
+ * try {
+ * const tileset = await Cesium.Cesium3DTileset.fromUrl(
+ * "http://localhost:8002/tilesets/Seattle/tileset.json",
+ * { enableCameraCollision: true }
+ * );
+ * scene.primitives.add(tileset);
+ * } catch (error) {
+ * console.error(`Error creating tileset: ${error}`);
+ * }
+ *
+ * @example
* // Common setting for the skipLevelOfDetail optimization
* const tileset = await Cesium.Cesium3DTileset.fromUrl(
* "http://localhost:8002/tilesets/Seattle/tileset.json", {
@@ -827,7 +841,23 @@ function Cesium3DTileset(options) {
SplitDirection.NONE
);
+ /**
+ * When {@link ScreenSpaceCameraController#enableCollisionDetection} is true, prevents the camera from going below the tileset surface.
+ * If using WebGL 1, {@link Cesium3DTileset#ConstructorOptions} enablePick
must be true for this behavior to work.
+ *
+ * @type {boolean}
+ * @default false
+ */
+ this.enableCameraCollision = defaultValue(
+ options.enableCameraCollision,
+ false
+ );
+
this._projectTo2D = defaultValue(options.projectTo2D, false);
+ this._enablePick = defaultValue(
+ options.enablePick,
+ this.enableCameraCollision
+ );
/**
* This property is for debugging only; it is not optimized for production use.
@@ -1907,7 +1937,7 @@ Object.defineProperties(Cesium3DTileset.prototype, {
* used for streaming massive heterogeneous 3D geospatial datasets, from a Cesium ion asset ID.
*
* @param {number} assetId The Cesium ion asset id.
- * @param {Cesium3DTileset.ConstructorOptions} options An object describing initialization options
+ * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options
* @returns {Promiseundefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Cesium3DTileset.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Cesium3DTileset.prototype.pick = function (ray, frameState, result) {
const selectedTiles = this._selectedTiles;
const selectedLength = selectedTiles.length;
@@ -3561,7 +3595,11 @@ Cesium3DTileset.prototype.pick = function (
continue;
}
- const candidate = tile.content.pick(ray, frameState, cullBackFaces, result);
+ const candidate = tile.content.pick(
+ ray,
+ frameState,
+ scratchPickIntersection
+ );
if (!defined(candidate)) {
continue;
@@ -3569,7 +3607,7 @@ Cesium3DTileset.prototype.pick = function (
const distance = Cartesian3.distance(ray.origin, candidate);
if (distance < minDistance) {
- intersection = candidate;
+ intersection = Cartesian3.clone(candidate, result);
minDistance = distance;
}
}
@@ -3578,8 +3616,7 @@ Cesium3DTileset.prototype.pick = function (
return undefined;
}
- Cartesian3.clone(intersection, result);
- return result;
+ return intersection;
};
/**
diff --git a/packages/engine/Source/Scene/Composite3DTileContent.js b/packages/engine/Source/Scene/Composite3DTileContent.js
index 900a901cc75b..02be6c08ac6c 100644
--- a/packages/engine/Source/Scene/Composite3DTileContent.js
+++ b/packages/engine/Source/Scene/Composite3DTileContent.js
@@ -1,3 +1,4 @@
+import Cartesian3 from "../Core/Cartesian3.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
@@ -347,6 +348,47 @@ Composite3DTileContent.prototype.update = function (tileset, frameState) {
}
};
+/**
+ * Find an intersection between a ray and the tile content surface that was rendered. The ray must be given in world coordinates.
+ *
+ * @param {Ray} ray The ray to test for intersection.
+ * @param {FrameState} frameState The frame state.
+ * @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
+ * @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
+ *
+ * @private
+ */
+Composite3DTileContent.prototype.pick = function (ray, frameState, result) {
+ if (!this._ready) {
+ return undefined;
+ }
+
+ let intersection;
+ let minDistance = Number.POSITIVE_INFINITY;
+ const contents = this._contents;
+ const length = contents.length;
+
+ for (let i = 0; i < length; ++i) {
+ const candidate = contents[i].pick(ray, frameState, result);
+
+ if (!defined(candidate)) {
+ continue;
+ }
+
+ const distance = Cartesian3.distance(ray.origin, candidate);
+ if (distance < minDistance) {
+ intersection = candidate;
+ minDistance = distance;
+ }
+ }
+
+ if (!defined(intersection)) {
+ return undefined;
+ }
+
+ return result;
+};
+
Composite3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/Empty3DTileContent.js b/packages/engine/Source/Scene/Empty3DTileContent.js
index 2f4f16f57ce3..0b5c4459da35 100644
--- a/packages/engine/Source/Scene/Empty3DTileContent.js
+++ b/packages/engine/Source/Scene/Empty3DTileContent.js
@@ -150,7 +150,7 @@ Empty3DTileContent.prototype.applyStyle = function (style) {};
Empty3DTileContent.prototype.update = function (tileset, frameState) {};
-Empty3DTileContent.prototype.pick = function (ray, frameState) {
+Empty3DTileContent.prototype.pick = function (ray, frameState, result) {
return undefined;
};
diff --git a/packages/engine/Source/Scene/Geometry3DTileContent.js b/packages/engine/Source/Scene/Geometry3DTileContent.js
index 93007bd46a1f..d89a278eb4b1 100644
--- a/packages/engine/Source/Scene/Geometry3DTileContent.js
+++ b/packages/engine/Source/Scene/Geometry3DTileContent.js
@@ -525,6 +525,10 @@ Geometry3DTileContent.prototype.update = function (tileset, frameState) {
}
};
+Geometry3DTileContent.prototype.pick = function (ray, frameState, result) {
+ return undefined;
+};
+
Geometry3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js
index 42077c0234d9..68989c3fe5c7 100644
--- a/packages/engine/Source/Scene/GltfLoader.js
+++ b/packages/engine/Source/Scene/GltfLoader.js
@@ -179,6 +179,7 @@ const GltfLoaderState = {
* @param {Axis} [options.forwardAxis=Axis.Z] The forward-axis of the glTF model.
* @param {boolean} [options.loadAttributesAsTypedArray=false] Load all attributes and indices as typed arrays instead of GPU buffers. If the attributes are interleaved in the glTF they will be de-interleaved in the typed array.
* @param {boolean} [options.loadAttributesFor2D=false] If true
, load the positions buffer and any instanced attribute buffers as typed arrays for accurately projecting models to 2D.
+ * @param {boolean} [options.enablePick=false] If true
, load the positions buffer, any instanced attribute buffers, and index buffer as typed arrays for CPU-enabled picking in WebGL1.
* @param {boolean} [options.loadIndicesForWireframe=false] If true
, load the index buffer as both a buffer and typed array. The latter is useful for creating wireframe indices in WebGL1.
* @param {boolean} [options.loadPrimitiveOutline=true] If true
, load outlines from the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time.
* @param {boolean} [options.loadForClassification=false] If true
and if the model has feature IDs, load the feature IDs and indices as typed arrays. This is useful for batching features for classification.
@@ -203,6 +204,7 @@ function GltfLoader(options) {
false
);
const loadAttributesFor2D = defaultValue(options.loadAttributesFor2D, false);
+ const enablePick = defaultValue(options.enablePick);
const loadIndicesForWireframe = defaultValue(
options.loadIndicesForWireframe,
false
@@ -234,6 +236,7 @@ function GltfLoader(options) {
this._forwardAxis = forwardAxis;
this._loadAttributesAsTypedArray = loadAttributesAsTypedArray;
this._loadAttributesFor2D = loadAttributesFor2D;
+ this._enablePick = enablePick;
this._loadIndicesForWireframe = loadIndicesForWireframe;
this._loadPrimitiveOutline = loadPrimitiveOutline;
this._loadForClassification = loadForClassification;
@@ -1241,6 +1244,8 @@ function loadVertexAttribute(
!hasInstances &&
loader._loadAttributesFor2D &&
!frameState.scene3DOnly;
+ const loadTypedArrayForPicking =
+ isPositionAttribute && loader._enablePick && !frameState.context.webgl2;
const loadTypedArrayForClassification =
loader._loadForClassification && isFeatureIdAttribute;
@@ -1252,6 +1257,7 @@ function loadVertexAttribute(
const outputTypedArray =
outputTypedArrayOnly ||
loadTypedArrayFor2D ||
+ loadTypedArrayForPicking ||
loadTypedArrayForClassification;
// Determine what to load right now:
@@ -1319,6 +1325,8 @@ function loadInstancedAttribute(
loader._loadAttributesAsTypedArray ||
(hasRotation && isTransformAttribute) ||
!frameState.context.instancedArrays;
+ const loadTypedArrayForPicking =
+ loader._enablePick && !frameState.context.webgl2;
const loadBuffer = !loadAsTypedArrayOnly;
@@ -1328,7 +1336,8 @@ function loadInstancedAttribute(
// - the model will be projected to 2D.
const loadFor2D = loader._loadAttributesFor2D && !frameState.scene3DOnly;
const loadTranslationAsTypedArray =
- isTranslationAttribute && (!hasTranslationMinMax || loadFor2D);
+ isTranslationAttribute &&
+ (!hasTranslationMinMax || loadFor2D || loadTypedArrayForPicking);
const loadTypedArray = loadAsTypedArrayOnly || loadTranslationAsTypedArray;
@@ -1365,9 +1374,10 @@ function loadIndices(
indices.count = accessor.count;
const loadAttributesAsTypedArray = loader._loadAttributesAsTypedArray;
- // Load the index buffer as a typed array to generate wireframes in WebGL1.
- const loadForWireframe =
- loader._loadIndicesForWireframe && !frameState.context.webgl2;
+ // Load the index buffer as a typed array to generate wireframes or pick in WebGL1.
+ const loadForCpuOperations =
+ (loader._loadIndicesForWireframe || loader._enablePick) &&
+ !frameState.context.webgl2;
// Load the index buffer as a typed array to batch features together for classification.
const loadForClassification = loader._loadForClassification && hasFeatureIds;
@@ -1377,7 +1387,7 @@ function loadIndices(
const outputTypedArrayOnly = loadAttributesAsTypedArray;
const outputBuffer = !outputTypedArrayOnly;
const outputTypedArray =
- loadAttributesAsTypedArray || loadForWireframe || loadForClassification;
+ loadAttributesAsTypedArray || loadForCpuOperations || loadForClassification;
// Determine what to load right now:
//
diff --git a/packages/engine/Source/Scene/ImageryLayer.js b/packages/engine/Source/Scene/ImageryLayer.js
index a038ef799997..8e1ca78ffba9 100644
--- a/packages/engine/Source/Scene/ImageryLayer.js
+++ b/packages/engine/Source/Scene/ImageryLayer.js
@@ -132,8 +132,8 @@ import TileImagery from "./TileImagery.js";
* @alias ImageryLayer
* @constructor
*
- * @param {ImageryProvider} imageryProvider The imagery provider to use.
- * @param {ImageryLayer.ConstructorOptions} options An object describing initialization options
+ * @param {ImageryProvider} [imageryProvider] The imagery provider to use.
+ * @param {ImageryLayer.ConstructorOptions} [options] An object describing initialization options
*
* @see ImageryLayer.fromProviderAsync
* @see ImageryLayer.fromWorldImagery
diff --git a/packages/engine/Source/Scene/Implicit3DTileContent.js b/packages/engine/Source/Scene/Implicit3DTileContent.js
index 96af9c099ad5..96eebae51c1d 100644
--- a/packages/engine/Source/Scene/Implicit3DTileContent.js
+++ b/packages/engine/Source/Scene/Implicit3DTileContent.js
@@ -1177,6 +1177,10 @@ Implicit3DTileContent.prototype.applyStyle = function (style) {};
Implicit3DTileContent.prototype.update = function (tileset, frameState) {};
+Implicit3DTileContent.prototype.pick = function (ray, frameState, result) {
+ return undefined;
+};
+
Implicit3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/IonImageryProvider.js b/packages/engine/Source/Scene/IonImageryProvider.js
index 17ef1d580094..5b1361bb734e 100644
--- a/packages/engine/Source/Scene/IonImageryProvider.js
+++ b/packages/engine/Source/Scene/IonImageryProvider.js
@@ -251,7 +251,7 @@ Object.defineProperties(IonImageryProvider.prototype, {
* Creates a provider for tiled imagery using the Cesium ion REST API.
*
* @param {Number} assetId An ion imagery asset ID.
- * @param {IonImageryProvider.ConstructorOptions} options Object describing initialization options.
+ * @param {IonImageryProvider.ConstructorOptions} [options] Object describing initialization options.
* @returns {Promisetrue
, load all attributes as typed arrays instead of GPU buffers. If the attributes are interleaved in the glTF they will be de-interleaved in the typed array.
* @param {boolean} [options.loadAttributesFor2D=false] If true
, load the positions buffer and any instanced attribute buffers as typed arrays for accurately projecting models to 2D.
+ * @param {boolean} [options.enablePick=false] If true
, load the positions buffer, any instanced attribute buffers, and index buffer as typed arrays for CPU-enabled picking in WebGL1.
* @param {boolean} [options.loadIndicesForWireframe=false] If true
, load the index buffer as a typed array. This is useful for creating wireframe indices in WebGL1.
* @param {boolean} [options.loadPrimitiveOutline=true] If true
, load outlines from the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time.
* @param {boolean} [options.loadForClassification=false] If true
and if the model has feature IDs, load the feature IDs and indices as typed arrays. This is useful for batching features for classification.
@@ -74,6 +75,7 @@ function B3dmLoader(options) {
false
);
const loadAttributesFor2D = defaultValue(options.loadAttributesFor2D, false);
+ const enablePick = defaultValue(options.enablePick, false);
const loadIndicesForWireframe = defaultValue(
options.loadIndicesForWireframe,
false
@@ -102,6 +104,7 @@ function B3dmLoader(options) {
this._forwardAxis = forwardAxis;
this._loadAttributesAsTypedArray = loadAttributesAsTypedArray;
this._loadAttributesFor2D = loadAttributesFor2D;
+ this._enablePick = enablePick;
this._loadIndicesForWireframe = loadIndicesForWireframe;
this._loadPrimitiveOutline = loadPrimitiveOutline;
this._loadForClassification = loadForClassification;
@@ -223,6 +226,7 @@ B3dmLoader.prototype.load = function () {
incrementallyLoadTextures: this._incrementallyLoadTextures,
loadAttributesAsTypedArray: this._loadAttributesAsTypedArray,
loadAttributesFor2D: this._loadAttributesFor2D,
+ enablePick: this._enablePick,
loadIndicesForWireframe: this._loadIndicesForWireframe,
loadPrimitiveOutline: this._loadPrimitiveOutline,
loadForClassification: this._loadForClassification,
diff --git a/packages/engine/Source/Scene/Model/I3dmLoader.js b/packages/engine/Source/Scene/Model/I3dmLoader.js
index 2a1aff969bd2..0f9457624419 100644
--- a/packages/engine/Source/Scene/Model/I3dmLoader.js
+++ b/packages/engine/Source/Scene/Model/I3dmLoader.js
@@ -64,6 +64,7 @@ const Instances = ModelComponents.Instances;
* @param {Axis} [options.upAxis=Axis.Y] The up-axis of the glTF model.
* @param {Axis} [options.forwardAxis=Axis.X] The forward-axis of the glTF model.
* @param {boolean} [options.loadAttributesAsTypedArray=false] Load all attributes as typed arrays instead of GPU buffers. If the attributes are interleaved in the glTF they will be de-interleaved in the typed array.
+ * @param {boolean} [options.enablePick=false] If true
, load the positions buffer, any instanced attribute buffers, and index buffer as typed arrays for CPU-enabled picking in WebGL1.
* @param {boolean} [options.loadIndicesForWireframe=false] Load the index buffer as a typed array so wireframe indices can be created for WebGL1.
* @param {boolean} [options.loadPrimitiveOutline=true] If true, load outlines from the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time.
*/
@@ -91,6 +92,7 @@ function I3dmLoader(options) {
false
);
const loadPrimitiveOutline = defaultValue(options.loadPrimitiveOutline, true);
+ const enablePick = defaultValue(options.enablePick, false);
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("options.i3dmResource", i3dmResource);
@@ -111,6 +113,7 @@ function I3dmLoader(options) {
this._loadAttributesAsTypedArray = loadAttributesAsTypedArray;
this._loadIndicesForWireframe = loadIndicesForWireframe;
this._loadPrimitiveOutline = loadPrimitiveOutline;
+ this._enablePick = enablePick;
this._state = I3dmLoaderState.NOT_LOADED;
this._promise = undefined;
@@ -240,6 +243,7 @@ I3dmLoader.prototype.load = function () {
releaseGltfJson: this._releaseGltfJson,
incrementallyLoadTextures: this._incrementallyLoadTextures,
loadAttributesAsTypedArray: this._loadAttributesAsTypedArray,
+ enablePick: this._enablePick,
loadIndicesForWireframe: this._loadIndicesForWireframe,
loadPrimitiveOutline: this._loadPrimitiveOutline,
};
diff --git a/packages/engine/Source/Scene/Model/InstancingPipelineStage.js b/packages/engine/Source/Scene/Model/InstancingPipelineStage.js
index 850410c2fd2b..809a1dd06d32 100644
--- a/packages/engine/Source/Scene/Model/InstancingPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/InstancingPipelineStage.js
@@ -73,6 +73,7 @@ InstancingPipelineStage.process = function (renderResources, node, frameState) {
frameState.mode !== SceneMode.SCENE3D &&
!frameState.scene3DOnly &&
model._projectTo2D;
+ const keepTypedArray = model._enablePick && !frameState.context.webgl2;
const instancingVertexAttributes = [];
@@ -81,7 +82,8 @@ InstancingPipelineStage.process = function (renderResources, node, frameState) {
frameState,
instances,
instancingVertexAttributes,
- use2D
+ use2D,
+ keepTypedArray
);
processFeatureIdAttributes(
@@ -697,7 +699,8 @@ function processTransformAttributes(
frameState,
instances,
instancingVertexAttributes,
- use2D
+ use2D,
+ keepTypedArray
) {
const rotationAttribute = ModelUtility.getAttributeBySemantic(
instances,
@@ -711,7 +714,8 @@ function processTransformAttributes(
instances,
instancingVertexAttributes,
frameState,
- use2D
+ use2D,
+ keepTypedArray
);
} else {
processTransformVec3Attributes(
@@ -729,7 +733,8 @@ function processTransformMatrixAttributes(
instances,
instancingVertexAttributes,
frameState,
- use2D
+ use2D,
+ keepTypedArray
) {
const shaderBuilder = renderResources.shaderBuilder;
const count = instances.attributes[0].count;
@@ -755,6 +760,10 @@ function processTransformMatrixAttributes(
buffer = createVertexBuffer(transformsTypedArray, frameState);
model._modelResources.push(buffer);
+ if (keepTypedArray) {
+ runtimeNode.transformsTypedArray = transformsTypedArray;
+ }
+
runtimeNode.instancingTransformsBuffer = buffer;
}
@@ -812,7 +821,8 @@ function processTransformVec3Attributes(
instances,
instancingVertexAttributes,
frameState,
- use2D
+ use2D,
+ keepTypedArray
) {
const shaderBuilder = renderResources.shaderBuilder;
const runtimeNode = renderResources.runtimeNode;
@@ -872,7 +882,7 @@ function processTransformVec3Attributes(
attributeString
);
- if (!use2D) {
+ if (!use2D && !keepTypedArray) {
return;
}
@@ -898,6 +908,10 @@ function processTransformVec3Attributes(
);
const projectedTypedArray = translationsToTypedArray(projectedTranslations);
+ if (keepTypedArray) {
+ runtimeNode.transformsTypedArray = projectedTypedArray;
+ }
+
// This memory is counted during the statistics stage at the end
// of the pipeline.
buffer2D = createVertexBuffer(projectedTypedArray, frameState);
@@ -906,6 +920,10 @@ function processTransformVec3Attributes(
runtimeNode.instancingTranslationBuffer2D = buffer2D;
}
+ if (!use2D) {
+ return;
+ }
+
const byteOffset = 0;
const byteStride = undefined;
diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js
index 26be311fc8b9..389c7aa9d8ad 100644
--- a/packages/engine/Source/Scene/Model/Model.js
+++ b/packages/engine/Source/Scene/Model/Model.js
@@ -1,23 +1,17 @@
-import AttributeType from "../AttributeType.js";
-import AttributeCompression from "../../Core/AttributeCompression.js";
import BoundingSphere from "../../Core/BoundingSphere.js";
import Cartesian3 from "../../Core/Cartesian3.js";
import Cartographic from "../../Core/Cartographic.js";
import Check from "../../Core/Check.js";
import Credit from "../../Core/Credit.js";
import Color from "../../Core/Color.js";
-import ComponentDatatype from "../../Core/ComponentDatatype.js";
import defined from "../../Core/defined.js";
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 Event from "../../Core/Event.js";
-import IndexDatatype from "../../Core/IndexDatatype.js";
-import IntersectionTests from "../../Core/IntersectionTests.js";
import Matrix3 from "../../Core/Matrix3.js";
import Matrix4 from "../../Core/Matrix4.js";
-import Ray from "../../Core/Ray.js";
import Resource from "../../Core/Resource.js";
import RuntimeError from "../../Core/RuntimeError.js";
import Pass from "../../Renderer/Pass.js";
@@ -45,8 +39,7 @@ import ModelUtility from "./ModelUtility.js";
import oneTimeWarning from "../../Core/oneTimeWarning.js";
import PntsLoader from "./PntsLoader.js";
import StyleCommandsNeeded from "./StyleCommandsNeeded.js";
-import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
-import Transforms from "../../Core/Transforms.js";
+import pickModel from "./pickModel.js";
/**
* pick
when not using WebGL 2 or above. If using WebGL 2 or above, this option will be ignored. If using WebGL 1 and this is true, the pick
operation will work correctly, but it will use more memory to do so. If running with WebGL 1 and this is false, the model will use less memory, but pick
will always return undefined
. This cannot be set after the model has loaded.
* @privateParam {string|number} [options.featureIdLabel="featureId_0"] Label of the feature ID set to use for picking and styling. For EXT_mesh_features, this is the feature ID's label property, or "featureId_N" (where N is the index in the featureIds array) when not specified. EXT_feature_metadata did not have a label field, so such feature ID sets are always labeled "featureId_N" where N is the index in the list of all feature Ids, where feature ID attributes are listed before feature ID textures. If featureIdLabel is an integer N, it is converted to the string "featureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @privateParam {string|number} [options.instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @privateParam {object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
@@ -463,6 +457,7 @@ function Model(options) {
this._sceneMode = undefined;
this._projectTo2D = defaultValue(options.projectTo2D, false);
+ this._enablePick = defaultValue(options.enablePick, false);
this._skipLevelOfDetail = false;
this._ignoreCommands = defaultValue(options.ignoreCommands, false);
@@ -2487,201 +2482,18 @@ Model.prototype.isClippingEnabled = function () {
);
};
-const scratchV0 = new Cartesian3();
-const scratchV1 = new Cartesian3();
-const scratchV2 = new Cartesian3();
-const scratchModelMatrix = new Matrix4();
-const scratchPickCartographic = new Cartographic();
-
/**
* Find an intersection between a ray and the model surface that was rendered. The ray must be given in world coordinates.
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Model.prototype.pick = function (ray, frameState, cullBackFaces, result) {
- if (frameState.mode === SceneMode.MORPHING) {
- return;
- }
-
- let minT = Number.MAX_VALUE;
- const modelMatrix = this.sceneGraph.computedModelMatrix;
-
- // Check all the primitive positions
- const nodes = this._sceneGraph._runtimeNodes;
- for (let i = 0; i < nodes.length; i++) {
- const node = nodes[i];
- const instances = node.node.instances;
- if (defined(instances)) {
- // TODO: Instances
- return;
- }
-
- const nodeComputedTransform = node.computedTransform;
- let computedModelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- nodeComputedTransform,
- scratchModelMatrix
- );
- if (frameState.mode !== SceneMode.SCENE3D) {
- computedModelMatrix = Transforms.basisTo2D(
- frameState.mapProjection,
- computedModelMatrix,
- computedModelMatrix
- );
- }
-
- for (let j = 0; j < node.node.primitives.length; j++) {
- const primitive = node.node.primitives[j];
- const positionAttribute = ModelUtility.getAttributeBySemantic(
- primitive,
- VertexAttributeSemantic.POSITION
- );
- const vertexCount = positionAttribute.count;
-
- let indices = primitive.indices.typedArray;
- if (!defined(indices)) {
- const indicesBuffer = primitive.indices.buffer;
- const indicesCount = primitive.indices.count;
- if (defined(indicesBuffer) && frameState.context.webgl2) {
- const useUint8Array = indicesBuffer.sizeInBytes === indicesCount;
- indices = useUint8Array
- ? new Uint8Array(indicesCount)
- : IndexDatatype.createTypedArray(vertexCount, indicesCount);
- indicesBuffer.getBufferData(indices, 0, 0, indicesCount);
- }
- primitive.indices.typedArray = indices;
- }
-
- let vertices = positionAttribute.typedArray;
- let componentDatatype = positionAttribute.componentDatatype;
- let attributeType = positionAttribute.type;
-
- const quantization = positionAttribute.quantization;
- if (defined(quantization)) {
- componentDatatype = positionAttribute.quantization.componentDatatype;
- attributeType = positionAttribute.quantization.type;
- }
-
- const numComponents = AttributeType.getNumberOfComponents(attributeType);
- const elementCount = vertexCount * numComponents;
-
- if (!defined(vertices)) {
- const verticesBuffer = positionAttribute.buffer;
-
- if (defined(verticesBuffer) && frameState.context.webgl2) {
- vertices = ComponentDatatype.createTypedArray(
- componentDatatype,
- elementCount
- );
- verticesBuffer.getBufferData(
- vertices,
- positionAttribute.byteOffset,
- 0,
- elementCount
- );
- }
-
- if (quantization && positionAttribute.normalized) {
- vertices = AttributeCompression.dequantize(
- vertices,
- componentDatatype,
- attributeType,
- vertexCount
- );
- }
-
- positionAttribute.typedArray = vertices;
- }
-
- const indicesLength = indices.length;
- for (let i = 0; i < indicesLength; i += 3) {
- const i0 = indices[i];
- const i1 = indices[i + 1];
- const i2 = indices[i + 2];
-
- const getPosition = (vertices, index, numComponents, result) => {
- const i = index * numComponents;
- result.x = vertices[i];
- result.y = vertices[i + 1];
- result.z = vertices[i + 2];
-
- if (defined(quantization)) {
- if (quantization.octEncoded) {
- result = AttributeCompression.octDecodeInRange(
- result,
- quantization.normalizationRange,
- result
- );
-
- if (quantization.octEncodedZXY) {
- const x = result.x;
- result.x = result.z;
- result.z = result.y;
- result.y = x;
- }
- } else {
- result = Cartesian3.multiplyComponents(
- result,
- quantization.quantizedVolumeStepSize,
- result
- );
-
- result = Cartesian3.add(
- result,
- quantization.quantizedVolumeOffset,
- result
- );
- }
- }
-
- result = Matrix4.multiplyByPoint(computedModelMatrix, result, result);
-
- return result;
- };
-
- const v0 = getPosition(vertices, i0, numComponents, scratchV0);
- const v1 = getPosition(vertices, i1, numComponents, scratchV1);
- const v2 = getPosition(vertices, i2, numComponents, scratchV2);
-
- const t = IntersectionTests.rayTriangleParametric(
- ray,
- v0,
- v1,
- v2,
- cullBackFaces
- );
-
- if (defined(t)) {
- if (t < minT && t >= 0.0) {
- minT = t;
- }
- }
- }
- }
- }
-
- if (minT === Number.MAX_VALUE) {
- return undefined;
- }
-
- result = Ray.getPoint(ray, minT, result);
- if (frameState.mode !== SceneMode.SCENE3D) {
- Cartesian3.fromElements(result.y, result.z, result.x, result);
-
- const projection = frameState.mapProjection;
- const ellipsoid = projection.ellipsoid;
-
- const cart = projection.unproject(result, scratchPickCartographic);
- ellipsoid.cartographicToCartesian(cart, result);
- }
-
- return result;
+Model.prototype.pick = function (ray, frameState, result) {
+ return pickModel(this, ray, frameState, result);
};
/**
@@ -2843,6 +2655,7 @@ Model.prototype.destroyModelResources = function () {
* @param {boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
* @param {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model.
* @param {boolean} [options.projectTo2D=false] Whether to accurately project the model's positions in 2D. If this is true, the model will be projected accurately to 2D, but it will use more memory to do so. If this is false, the model will use less memory and will still render in 2D / CV mode, but its positions may be inaccurate. This disables minimumPixelSize and prevents future modification to the model matrix. This also cannot be set after the model has loaded.
+ * @param {boolean} [options.enablePick=false] Whether to allow with CPU picking with pick
when not using WebGL 2 or above. If using WebGL 2 or above, this option will be ignored. If using WebGL 1 and this is true, the pick
operation will work correctly, but it will use more memory to do so. If running with WebGL 1 and this is false, the model will use less memory, but pick
will always return undefined
. This cannot be set after the model has loaded.
* @param {string|number} [options.featureIdLabel="featureId_0"] Label of the feature ID set to use for picking and styling. For EXT_mesh_features, this is the feature ID's label property, or "featureId_N" (where N is the index in the featureIds array) when not specified. EXT_feature_metadata did not have a label field, so such feature ID sets are always labeled "featureId_N" where N is the index in the list of all feature Ids, where feature ID attributes are listed before feature ID textures. If featureIdLabel is an integer N, it is converted to the string "featureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @param {string|number} [options.instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @param {object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation and lighting.
@@ -2937,6 +2750,7 @@ Model.fromGltfAsync = async function (options) {
upAxis: options.upAxis,
forwardAxis: options.forwardAxis,
loadAttributesFor2D: options.projectTo2D,
+ enablePick: options.enablePick,
loadIndicesForWireframe: options.enableDebugWireframe,
loadPrimitiveOutline: options.enableShowOutline,
loadForClassification: defined(options.classificationType),
@@ -3013,6 +2827,7 @@ Model.fromB3dm = async function (options) {
upAxis: options.upAxis,
forwardAxis: options.forwardAxis,
loadAttributesFor2D: options.projectTo2D,
+ enablePick: options.enablePick,
loadIndicesForWireframe: options.enableDebugWireframe,
loadPrimitiveOutline: options.enableShowOutline,
loadForClassification: defined(options.classificationType),
@@ -3068,6 +2883,7 @@ Model.fromI3dm = async function (options) {
upAxis: options.upAxis,
forwardAxis: options.forwardAxis,
loadAttributesFor2D: options.projectTo2D,
+ enablePick: options.enablePick,
loadIndicesForWireframe: options.enableDebugWireframe,
loadPrimitiveOutline: options.enableShowOutline,
};
@@ -3201,6 +3017,7 @@ function makeModelOptions(loader, modelType, options) {
showCreditsOnScreen: options.showCreditsOnScreen,
splitDirection: options.splitDirection,
projectTo2D: options.projectTo2D,
+ enablePick: options.enablePick,
featureIdLabel: options.featureIdLabel,
instanceFeatureIdLabel: options.instanceFeatureIdLabel,
pointCloudShading: options.pointCloudShading,
diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js
index c0e71069a2c4..3273c81146d6 100644
--- a/packages/engine/Source/Scene/Model/Model3DTileContent.js
+++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js
@@ -421,23 +421,17 @@ Model3DTileContent.fromGeoJson = async function (
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Model3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Model3DTileContent.prototype.pick = function (ray, frameState, result) {
if (!defined(this._model) || !this._ready) {
return undefined;
}
- return this._model.pick(ray, frameState, cullBackFaces, result);
+ return this._model.pick(ray, frameState, result);
};
function makeModelOptions(tileset, tile, content, additionalOptions) {
@@ -466,6 +460,7 @@ function makeModelOptions(tileset, tile, content, additionalOptions) {
enableDebugWireframe: tileset._enableDebugWireframe,
debugWireframe: tileset.debugWireframe,
projectTo2D: tileset._projectTo2D,
+ enablePick: tileset._enablePick,
enableShowOutline: tileset._enableShowOutline,
showOutline: tileset.showOutline,
outlineColor: tileset.outlineColor,
diff --git a/packages/engine/Source/Scene/Model/pickModel.js b/packages/engine/Source/Scene/Model/pickModel.js
new file mode 100644
index 000000000000..16a2bb02f94f
--- /dev/null
+++ b/packages/engine/Source/Scene/Model/pickModel.js
@@ -0,0 +1,369 @@
+import AttributeCompression from "../../Core/AttributeCompression.js";
+import BoundingSphere from "../../Core/BoundingSphere.js";
+import Cartesian3 from "../../Core/Cartesian3.js";
+import Cartographic from "../../Core/Cartographic.js";
+import Check from "../../Core/Check.js";
+import ComponentDatatype from "../../Core/ComponentDatatype.js";
+import defaultValue from "../../Core/defaultValue.js";
+import defined from "../../Core/defined.js";
+import IndexDatatype from "../../Core/IndexDatatype.js";
+import IntersectionTests from "../../Core/IntersectionTests.js";
+import Ray from "../../Core/Ray.js";
+import Matrix4 from "../../Core/Matrix4.js";
+import Transforms from "../../Core/Transforms.js";
+import AttributeType from "../AttributeType.js";
+import SceneMode from "../SceneMode.js";
+import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
+import ModelUtility from "./ModelUtility.js";
+
+const scratchV0 = new Cartesian3();
+const scratchV1 = new Cartesian3();
+const scratchV2 = new Cartesian3();
+const scratchNodeComputedTransform = new Matrix4();
+const scratchModelMatrix = new Matrix4();
+const scratchcomputedModelMatrix = new Matrix4();
+const scratchPickCartographic = new Cartographic();
+const scratchBoundingSphere = new BoundingSphere();
+
+/**
+ * Find an intersection between a ray and the model surface that was rendered. The ray must be given in world coordinates.
+ *
+ * @param {Model} model The model to pick.
+ * @param {Ray} ray The ray to test for intersection.
+ * @param {FrameState} frameState The frame state.
+ * @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
+ * @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
+ *
+ * @private
+ */
+export default function pickModel(model, ray, frameState, result) {
+ //>>includeStart('debug', pragmas.debug);
+ Check.typeOf.object("model", model);
+ Check.typeOf.object("ray", ray);
+ Check.typeOf.object("frameState", frameState);
+ //>>includeEnd('debug');
+
+ if (!model._ready || frameState.mode === SceneMode.MORPHING) {
+ return;
+ }
+
+ let minT = Number.MAX_VALUE;
+ const sceneGraph = model.sceneGraph;
+
+ const nodes = sceneGraph._runtimeNodes;
+ for (let i = 0; i < nodes.length; i++) {
+ const runtimeNode = nodes[i];
+ const node = runtimeNode.node;
+
+ let nodeComputedTransform = Matrix4.clone(
+ runtimeNode.computedTransform,
+ scratchNodeComputedTransform
+ );
+ let modelMatrix = Matrix4.clone(
+ sceneGraph.computedModelMatrix,
+ scratchModelMatrix
+ );
+
+ const instances = node.instances;
+ if (defined(instances)) {
+ if (instances.transformInWorldSpace) {
+ // Replicate the multiplication order in LegacyInstancingStageVS.
+ modelMatrix = Matrix4.multiplyTransformation(
+ model.modelMatrix,
+ sceneGraph.components.transform,
+ modelMatrix
+ );
+
+ nodeComputedTransform = Matrix4.multiplyTransformation(
+ sceneGraph.axisCorrectionMatrix,
+ runtimeNode.computedTransform,
+ nodeComputedTransform
+ );
+ }
+ }
+
+ if (frameState.mode !== SceneMode.SCENE3D) {
+ modelMatrix = Transforms.basisTo2D(
+ frameState.mapProjection,
+ modelMatrix,
+ modelMatrix
+ );
+ }
+
+ const computedModelMatrix = Matrix4.multiplyTransformation(
+ modelMatrix,
+ nodeComputedTransform,
+ scratchcomputedModelMatrix
+ );
+
+ const transforms = [];
+ if (defined(instances)) {
+ const transformsCount = instances.attributes[0].count;
+ const instanceComponentDatatype =
+ instances.attributes[0].componentDatatype;
+
+ const transformElements = 12;
+ let transformsTypedArray = runtimeNode.transformsTypedArray;
+ if (!defined(transformsTypedArray)) {
+ const instanceTransformsBuffer = runtimeNode.instancingTransformsBuffer;
+ if (defined(instanceTransformsBuffer) && frameState.context.webgl2) {
+ transformsTypedArray = ComponentDatatype.createTypedArray(
+ instanceComponentDatatype,
+ transformsCount * transformElements
+ );
+ instanceTransformsBuffer.getBufferData(transformsTypedArray);
+ }
+ }
+
+ if (defined(transformsTypedArray)) {
+ for (let i = 0; i < transformsCount; i++) {
+ const index = i * transformElements;
+
+ const transform = new Matrix4(
+ transformsTypedArray[index],
+ transformsTypedArray[index + 1],
+ transformsTypedArray[index + 2],
+ transformsTypedArray[index + 3],
+ transformsTypedArray[index + 4],
+ transformsTypedArray[index + 5],
+ transformsTypedArray[index + 6],
+ transformsTypedArray[index + 7],
+ transformsTypedArray[index + 8],
+ transformsTypedArray[index + 9],
+ transformsTypedArray[index + 10],
+ transformsTypedArray[index + 11],
+ 0,
+ 0,
+ 0,
+ 1
+ );
+
+ if (instances.transformInWorldSpace) {
+ Matrix4.multiplyTransformation(
+ transform,
+ nodeComputedTransform,
+ transform
+ );
+ Matrix4.multiplyTransformation(modelMatrix, transform, transform);
+ } else {
+ Matrix4.multiplyTransformation(
+ transform,
+ computedModelMatrix,
+ transform
+ );
+ }
+ transforms.push(transform);
+ }
+ }
+ }
+
+ if (transforms.length === 0) {
+ transforms.push(computedModelMatrix);
+ }
+
+ const primitivesLength = runtimeNode.runtimePrimitives.length;
+ for (let j = 0; j < primitivesLength; j++) {
+ const runtimePrimitive = runtimeNode.runtimePrimitives[j];
+ const primitive = runtimePrimitive.primitive;
+
+ if (defined(runtimePrimitive.boundingSphere) && !defined(instances)) {
+ const boundingSphere = BoundingSphere.transform(
+ runtimePrimitive.boundingSphere,
+ computedModelMatrix,
+ scratchBoundingSphere
+ );
+ const boundsIntersection = IntersectionTests.raySphere(
+ ray,
+ boundingSphere
+ );
+ if (!defined(boundsIntersection)) {
+ continue;
+ }
+ }
+
+ const positionAttribute = ModelUtility.getAttributeBySemantic(
+ primitive,
+ VertexAttributeSemantic.POSITION
+ );
+ const vertexCount = positionAttribute.count;
+
+ if (!defined(primitive.indices)) {
+ // Point clouds
+ continue;
+ }
+
+ let indices = primitive.indices.typedArray;
+ if (!defined(indices)) {
+ const indicesBuffer = primitive.indices.buffer;
+ const indicesCount = primitive.indices.count;
+ const indexDatatype = primitive.indices.indexDatatype;
+ if (defined(indicesBuffer) && frameState.context.webgl2) {
+ if (indexDatatype === IndexDatatype.UNSIGNED_BYTE) {
+ indices = new Uint8Array(indicesCount);
+ } else if (indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
+ indices = new Uint16Array(indicesCount);
+ } else if (indexDatatype === IndexDatatype.UNSIGNED_INT) {
+ indices = new Uint32Array(indicesCount);
+ }
+
+ indicesBuffer.getBufferData(indices);
+ }
+ }
+
+ let vertices = positionAttribute.typedArray;
+ let componentDatatype = positionAttribute.componentDatatype;
+ let attributeType = positionAttribute.type;
+
+ const quantization = positionAttribute.quantization;
+ if (defined(quantization)) {
+ componentDatatype = positionAttribute.quantization.componentDatatype;
+ attributeType = positionAttribute.quantization.type;
+ }
+
+ const numComponents = AttributeType.getNumberOfComponents(attributeType);
+ const elementCount = vertexCount * numComponents;
+
+ if (!defined(vertices)) {
+ const verticesBuffer = positionAttribute.buffer;
+
+ if (defined(verticesBuffer) && frameState.context.webgl2) {
+ vertices = ComponentDatatype.createTypedArray(
+ componentDatatype,
+ elementCount
+ );
+ verticesBuffer.getBufferData(
+ vertices,
+ positionAttribute.byteOffset,
+ 0,
+ elementCount
+ );
+ }
+
+ if (quantization && positionAttribute.normalized) {
+ vertices = AttributeCompression.dequantize(
+ vertices,
+ componentDatatype,
+ attributeType,
+ vertexCount
+ );
+ }
+ }
+
+ if (!defined(indices) || !defined(vertices)) {
+ return;
+ }
+
+ const indicesLength = indices.length;
+ for (let i = 0; i < indicesLength; i += 3) {
+ const i0 = indices[i];
+ const i1 = indices[i + 1];
+ const i2 = indices[i + 2];
+
+ for (const instanceTransform of transforms) {
+ const v0 = getVertexPosition(
+ vertices,
+ i0,
+ numComponents,
+ quantization,
+ instanceTransform,
+ scratchV0
+ );
+ const v1 = getVertexPosition(
+ vertices,
+ i1,
+ numComponents,
+ quantization,
+ instanceTransform,
+ scratchV1
+ );
+ const v2 = getVertexPosition(
+ vertices,
+ i2,
+ numComponents,
+ quantization,
+ instanceTransform,
+ scratchV2
+ );
+
+ const t = IntersectionTests.rayTriangleParametric(
+ ray,
+ v0,
+ v1,
+ v2,
+ defaultValue(model.backFaceCulling, true)
+ );
+
+ if (defined(t)) {
+ if (t < minT && t >= 0.0) {
+ minT = t;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (minT === Number.MAX_VALUE) {
+ return undefined;
+ }
+
+ result = Ray.getPoint(ray, minT, result);
+ if (frameState.mode !== SceneMode.SCENE3D) {
+ Cartesian3.fromElements(result.y, result.z, result.x, result);
+
+ const projection = frameState.mapProjection;
+ const ellipsoid = projection.ellipsoid;
+
+ const cartographic = projection.unproject(result, scratchPickCartographic);
+ ellipsoid.cartographicToCartesian(cartographic, result);
+ }
+
+ return result;
+}
+
+function getVertexPosition(
+ vertices,
+ index,
+ numComponents,
+ quantization,
+ instanceTransform,
+ result
+) {
+ const i = index * numComponents;
+ result.x = vertices[i];
+ result.y = vertices[i + 1];
+ result.z = vertices[i + 2];
+
+ if (defined(quantization)) {
+ if (quantization.octEncoded) {
+ result = AttributeCompression.octDecodeInRange(
+ result,
+ quantization.normalizationRange,
+ result
+ );
+
+ if (quantization.octEncodedZXY) {
+ const x = result.x;
+ result.x = result.z;
+ result.z = result.y;
+ result.y = x;
+ }
+ } else {
+ result = Cartesian3.multiplyComponents(
+ result,
+ quantization.quantizedVolumeStepSize,
+ result
+ );
+
+ result = Cartesian3.add(
+ result,
+ quantization.quantizedVolumeOffset,
+ result
+ );
+ }
+ }
+
+ result = Matrix4.multiplyByPoint(instanceTransform, result, result);
+
+ return result;
+}
diff --git a/packages/engine/Source/Scene/Multiple3DTileContent.js b/packages/engine/Source/Scene/Multiple3DTileContent.js
index cd62641172b4..42ede698a349 100644
--- a/packages/engine/Source/Scene/Multiple3DTileContent.js
+++ b/packages/engine/Source/Scene/Multiple3DTileContent.js
@@ -1,3 +1,4 @@
+import Cartesian3 from "../Core/Cartesian3.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
@@ -650,6 +651,47 @@ Multiple3DTileContent.prototype.update = function (tileset, frameState) {
}
};
+/**
+ * Find an intersection between a ray and the tile content surface that was rendered. The ray must be given in world coordinates.
+ *
+ * @param {Ray} ray The ray to test for intersection.
+ * @param {FrameState} frameState The frame state.
+ * @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
+ * @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
+ *
+ * @private
+ */
+Multiple3DTileContent.prototype.pick = function (ray, frameState, result) {
+ if (!this._ready) {
+ return undefined;
+ }
+
+ let intersection;
+ let minDistance = Number.POSITIVE_INFINITY;
+ const contents = this._contents;
+ const length = contents.length;
+
+ for (let i = 0; i < length; ++i) {
+ const candidate = contents[i].pick(ray, frameState, result);
+
+ if (!defined(candidate)) {
+ continue;
+ }
+
+ const distance = Cartesian3.distance(ray.origin, candidate);
+ if (distance < minDistance) {
+ intersection = candidate;
+ minDistance = distance;
+ }
+ }
+
+ if (!defined(intersection)) {
+ return undefined;
+ }
+
+ return result;
+};
+
Multiple3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index 59239949c591..40a6e81bea95 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -163,7 +163,6 @@ function Scene(options) {
this._globeTranslucencyState = new GlobeTranslucencyState();
this._primitives = new PrimitiveCollection();
this._groundPrimitives = new PrimitiveCollection();
- this._terrainTilesets = [];
this._globeHeight = undefined;
this._cameraUnderground = false;
@@ -3568,35 +3567,6 @@ function callAfterRenderFunctions(scene) {
functions.length = 0;
}
-/**
- * Allow camera collisions, if enabled for the camera, on a tileset surface
- * @param {Cesium3DTileset} tileset Tileset fo which to enable collision.
- */
-Scene.prototype.enableCollisionDetectionForTileset = function (tileset) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("tileset", tileset);
- //>>includeEnd('debug');
-
- this._terrainTilesets.push(tileset);
-};
-
-/**
- * Disallow camera collisions on a tileset surface
- * @param {Cesium3DTileset} tileset Tileset for which to disable collision.
- */
-Scene.prototype.disableCollisionDetectionForTileset = function (tileset) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("tileset", tileset);
- //>>includeEnd('debug');
-
- const i = this._terrainTilesets.indexOf(tileset);
- if (i === -1) {
- return;
- }
-
- this._terrainTilesets.splice(i, 1);
-};
-
function getGlobeHeight(scene) {
const camera = scene.camera;
const cartographic = camera.positionCartographic;
@@ -3623,14 +3593,17 @@ Scene.prototype.getHeight = function (cartographic, heightReference) {
heightReference === HeightReference.RELATIVE_TO_3D_TILE;
let maxHeight = Number.NEGATIVE_INFINITY;
+
if (!ignore3dTiles) {
- for (const tileset of this._terrainTilesets) {
- if (!tileset.show) {
+ const length = this.primitives.length;
+ for (let i = 0; i < length; ++i) {
+ const primitive = this.primitives.get(i);
+ if (!primitive.isCesium3DTileset || !primitive.enableCameraCollision) {
continue;
}
- const result = tileset.getHeight(cartographic, this);
- if (result > maxHeight) {
+ const result = primitive.getHeight(cartographic, this);
+ if (defined(result) && result > maxHeight) {
maxHeight = result;
}
}
diff --git a/packages/engine/Source/Scene/Tileset3DTileContent.js b/packages/engine/Source/Scene/Tileset3DTileContent.js
index 655f0e675ef1..41d1652453c1 100644
--- a/packages/engine/Source/Scene/Tileset3DTileContent.js
+++ b/packages/engine/Source/Scene/Tileset3DTileContent.js
@@ -168,6 +168,10 @@ Tileset3DTileContent.prototype.applyStyle = function (style) {};
Tileset3DTileContent.prototype.update = function (tileset, frameState) {};
+Tileset3DTileContent.prototype.pick = function (ray, frameState, result) {
+ return undefined;
+};
+
Tileset3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/Vector3DTileContent.js b/packages/engine/Source/Scene/Vector3DTileContent.js
index e191ede706a9..120a22db5801 100644
--- a/packages/engine/Source/Scene/Vector3DTileContent.js
+++ b/packages/engine/Source/Scene/Vector3DTileContent.js
@@ -727,6 +727,10 @@ Vector3DTileContent.prototype.update = function (tileset, frameState) {
}
};
+Vector3DTileContent.prototype.pick = function (ray, frameState, result) {
+ return undefined;
+};
+
Vector3DTileContent.prototype.getPolylinePositions = function (batchId) {
const polylines = this._polylines;
if (!defined(polylines)) {
diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
index d6c708a9d12d..7877b3ac852b 100644
--- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
+++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
@@ -41,12 +41,15 @@ import Resource from "../Core/Resource.js";
*/
async function createGooglePhotorealistic3DTileset(key, options) {
options = defaultValue(options, {});
- options.showCreditsOnScreen = true;
options.cacheBytes = defaultValue(options.cacheBytes, 1536 * 1024 * 1024);
options.maximumCacheOverflowBytes = defaultValue(
options.maximumCacheOverflowBytes,
1024 * 1024 * 1024
);
+ options.enableCameraCollision = defaultValue(
+ options.enableCameraCollision,
+ true
+ );
key = defaultValue(key, GoogleMaps.defaultApiKey);
if (!defined(key)) {
diff --git a/packages/engine/Specs/Core/EllipsoidRhumbLineSpec.js b/packages/engine/Specs/Core/EllipsoidRhumbLineSpec.js
index f56803e3f411..87157adea73d 100644
--- a/packages/engine/Specs/Core/EllipsoidRhumbLineSpec.js
+++ b/packages/engine/Specs/Core/EllipsoidRhumbLineSpec.js
@@ -13,6 +13,7 @@ describe("Core/EllipsoidRhumbLine", function () {
const fifteenDegrees = Math.PI / 12;
const thirtyDegrees = Math.PI / 6;
const fortyfiveDegrees = Math.PI / 4;
+ const threeHundredDegrees = (5 * Math.PI) / 6;
it("throws without start", function () {
expect(function () {
@@ -749,6 +750,46 @@ describe("Core/EllipsoidRhumbLine", function () {
);
});
+ it("interpolates when heading is near 90 degrees", function () {
+ const start = new Cartographic(0.0, 0.0);
+ const end = new Cartographic(Math.PI / 2, 0.0);
+ const expectedMid = new Cartographic(fortyfiveDegrees, 0.0);
+
+ const rhumb = new EllipsoidRhumbLine(start, end);
+ expect(rhumb.heading).toEqualEpsilon(Math.PI / 2, CesiumMath.EPSILON12);
+
+ const midpoint = rhumb.interpolateUsingFraction(0.5);
+
+ expect(expectedMid.longitude).toEqualEpsilon(
+ midpoint.longitude,
+ CesiumMath.EPSILON12
+ );
+ expect(expectedMid.latitude).toEqualEpsilon(
+ midpoint.latitude,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("interpolates when heading is near 0 degrees", function () {
+ const start = new Cartographic(-threeHundredDegrees, fifteenDegrees);
+ const end = new Cartographic(-threeHundredDegrees, fortyfiveDegrees);
+ const expectedMid = new Cartographic(-threeHundredDegrees, thirtyDegrees);
+
+ const rhumb = new EllipsoidRhumbLine(start, end);
+ expect(rhumb.heading).toEqualEpsilon(0, CesiumMath.EPSILON12);
+
+ const midpoint = rhumb.interpolateUsingFraction(0.5);
+
+ expect(expectedMid.longitude).toEqualEpsilon(
+ midpoint.longitude,
+ CesiumMath.EPSILON12
+ );
+ expect(expectedMid.latitude).toEqualEpsilon(
+ midpoint.latitude,
+ CesiumMath.EPSILON3
+ );
+ });
+
it("interpolates midpoint fraction using result parameter", function () {
const start = new Cartographic(fifteenDegrees, 0.0);
const end = new Cartographic(fortyfiveDegrees, 0.0);
diff --git a/packages/engine/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js b/packages/engine/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js
index 7847fe668050..8927fca15e42 100644
--- a/packages/engine/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js
+++ b/packages/engine/Specs/Core/GoogleEarthEnterpriseTerrainProviderSpec.js
@@ -314,7 +314,11 @@ describe("Core/GoogleEarthEnterpriseTerrainProvider", function () {
const promises = [];
return pollToPromise(function () {
let b = true;
- for (let i = 0; i < 10; ++i) {
+ for (
+ let i = 0;
+ i < RequestScheduler.maximumRequestsPerServer + 2;
+ ++i
+ ) {
b = b && terrainProvider.getTileDataAvailable(i, i, i);
}
return b && terrainProvider.getTileDataAvailable(1, 2, 3);
diff --git a/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js b/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js
index 3a55df6250dc..77635449b0d9 100644
--- a/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js
+++ b/packages/engine/Specs/Core/sampleTerrainMostDetailedSpec.js
@@ -28,9 +28,9 @@ describe("Core/sampleTerrainMostDetailed", function () {
expect(positions[1].height).toBeLessThan(10000);
});
- it("should throw querying heights from Small Terrain", async function () {
+ it("should throw querying heights from terrain without availability", async function () {
const terrainProvider = await CesiumTerrainProvider.fromUrl(
- "https://s3.amazonaws.com/cesiumjs/smallTerrain"
+ "Data/CesiumTerrainTileJson/StandardHeightmap.tile.json"
);
const positions = [
diff --git a/packages/engine/Specs/Core/sampleTerrainSpec.js b/packages/engine/Specs/Core/sampleTerrainSpec.js
index 3369481f068d..c88c1a5ddff3 100644
--- a/packages/engine/Specs/Core/sampleTerrainSpec.js
+++ b/packages/engine/Specs/Core/sampleTerrainSpec.js
@@ -15,6 +15,11 @@ describe("Core/sampleTerrain", function () {
worldTerrain = await createWorldTerrainAsync();
});
+ afterEach(function () {
+ Resource._Implementations.loadWithXhr =
+ Resource._DefaultImplementations.loadWithXhr;
+ });
+
it("queries heights", function () {
const positions = [
Cartographic.fromDegrees(86.925145, 27.988257),
@@ -32,9 +37,42 @@ describe("Core/sampleTerrain", function () {
});
});
- it("queries heights from Small Terrain", async function () {
+ it("queries heights from terrain without availability", async function () {
+ // Mock terrain tile loading
+ Resource._Implementations.loadWithXhr = function (
+ url,
+ responseType,
+ method,
+ data,
+ headers,
+ deferred,
+ overrideMimeType
+ ) {
+ if (defined(url.match(/\/\d+\/\d+\/\d+\.terrain/))) {
+ Resource._DefaultImplementations.loadWithXhr(
+ "Data/CesiumTerrainTileJson/11_3027_1342.terrain",
+ responseType,
+ method,
+ data,
+ headers,
+ deferred
+ );
+ return;
+ }
+
+ Resource._DefaultImplementations.loadWithXhr(
+ url,
+ responseType,
+ method,
+ data,
+ headers,
+ deferred,
+ overrideMimeType
+ );
+ };
+
const terrainProvider = await CesiumTerrainProvider.fromUrl(
- "https://s3.amazonaws.com/cesiumjs/smallTerrain"
+ "Data/CesiumTerrainTileJson/StandardHeightmap.tile.json"
);
const positions = [
diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
index 2e3381f89d49..341c8e365a1d 100644
--- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
+++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
@@ -46,6 +46,7 @@ import Cesium3DTilesTester from "../../../../Specs/Cesium3DTilesTester.js";
import createScene from "../../../../Specs/createScene.js";
import generateJsonBuffer from "../../../../Specs/generateJsonBuffer.js";
import pollToPromise from "../../../../Specs/pollToPromise.js";
+import Ellipsoid from "../../Source/Core/Ellipsoid.js";
describe(
"Scene/Cesium3DTileset",
@@ -2414,6 +2415,183 @@ describe(
});
});
+ it("picks", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(scene, tilesetUrl, {
+ enablePick: !scene.frameState.context.webgl2,
+ });
+ viewRootOnly();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215026.8094312553,
+ -4736367.339076743,
+ 4081652.238842398
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks tileset of tilesets", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ tilesetOfTilesetsUrl,
+ {
+ enablePick: !scene.frameState.context.webgl2,
+ }
+ );
+ viewRootOnly();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215026.8094312553,
+ -4736367.339076743,
+ 4081652.238842398
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks instanced tileset", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ instancedUrl,
+ {
+ enablePick: !scene.frameState.context.webgl2,
+ }
+ );
+ viewInstances();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215015.7820120894,
+ -4736324.352446682,
+ 4081615.004915994
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks translucent tileset", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ translucentUrl,
+ {
+ enablePick: !scene.frameState.context.webgl2,
+ }
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215013.1035421563,
+ -4736313.911345786,
+ 4081605.96109977
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks tileset with transforms", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ tilesetWithTransformsUrl,
+ {
+ enablePick: !scene.frameState.context.webgl2,
+ }
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215013.8353220497,
+ -4736316.763939952,
+ 4081608.4319443353
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picking point cloud tileset returns undefined", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ pointCloudUrl,
+ {
+ enablePick: !scene.frameState.context.webgl2,
+ }
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ expect(tileset.pick(ray, scene.frameState)).toBeUndefined();
+ });
+
+ it("getHeight samples height at a cartographic position", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(scene, tilesetUrl, {
+ enablePick: !scene.frameState.context.webgl2,
+ });
+ viewRootOnly();
+ await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset);
+ scene.renderForSpecs();
+
+ const center = Ellipsoid.WGS84.cartesianToCartographic(
+ tileset.boundingSphere.center
+ );
+ const height = tileset.getHeight(center, scene);
+ expect(height).toEqualEpsilon(78.1558019795064, CesiumMath.EPSILON12);
+ });
+
it("destroys", function () {
return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function (
tileset
diff --git a/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js b/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js
index 129bb57b8cf0..9818f2534b2b 100644
--- a/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js
+++ b/packages/engine/Specs/Scene/GlobeSurfaceTileProviderSpec.js
@@ -12,6 +12,7 @@ import {
GeographicProjection,
HeadingPitchRoll,
Rectangle,
+ Resource,
WebMercatorProjection,
ContextLimits,
RenderState,
@@ -104,6 +105,8 @@ describe(
afterEach(function () {
scene.imageryLayers.removeAll();
scene.primitives.removeAll();
+ Resource._Implementations.loadWithXhr =
+ Resource._DefaultImplementations.loadWithXhr;
});
it("conforms to QuadtreeTileProvider interface", function () {
@@ -902,8 +905,41 @@ describe(
scene.imageryLayers.addImageryProvider(provider);
const terrainCredit = new Credit("terrain credit");
+
+ // Mock terrain tile loading
+ Resource._Implementations.loadWithXhr = function (
+ url,
+ responseType,
+ method,
+ data,
+ headers,
+ deferred,
+ overrideMimeType
+ ) {
+ if (defined(url.match(/\/\d+\/\d+\/\d+\.terrain/))) {
+ Resource._DefaultImplementations.loadWithXhr(
+ "Data/CesiumTerrainTileJson/tile.32bitIndices.terrain",
+ responseType,
+ method,
+ data,
+ headers,
+ deferred
+ );
+ return;
+ }
+
+ Resource._DefaultImplementations.loadWithXhr(
+ url,
+ responseType,
+ method,
+ data,
+ headers,
+ deferred,
+ overrideMimeType
+ );
+ };
scene.terrainProvider = await CesiumTerrainProvider.fromUrl(
- "https://s3.amazonaws.com/cesiumjs/smallTerrain",
+ "Data/CesiumTerrainTileJson/QuantizedMesh.tile.json",
{
credit: terrainCredit,
}
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index df62257eb949..a93f00370367 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -4392,6 +4392,28 @@ describe(
});
});
+ it("pick returns position of intersection between ray and model surface", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ expect(model.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
it("destroy works", function () {
spyOn(ShaderProgram.prototype, "destroy").and.callThrough();
return loadAndZoomToModelAsync({ gltf: boxTexturedGlbUrl }, scene).then(
diff --git a/packages/engine/Specs/Scene/Model/pickModelSpec.js b/packages/engine/Specs/Scene/Model/pickModelSpec.js
new file mode 100644
index 000000000000..af89fd06907d
--- /dev/null
+++ b/packages/engine/Specs/Scene/Model/pickModelSpec.js
@@ -0,0 +1,365 @@
+import {
+ pickModel,
+ Cartesian2,
+ Cartesian3,
+ HeadingPitchRange,
+ Math as CesiumMath,
+ Model,
+ Ray,
+ SceneMode,
+} from "../../../index.js";
+
+import loadAndZoomToModelAsync from "./loadAndZoomToModelAsync.js";
+import createScene from "../../../../../Specs/createScene.js";
+
+describe("Scene/Model/pickModel", function () {
+ const boxTexturedGltfUrl =
+ "./Data/Models/glTF-2.0/BoxTextured/glTF/BoxTextured.gltf";
+ const boxInstanced =
+ "./Data/Models/glTF-2.0/BoxInstancedNoNormals/glTF/BoxInstancedNoNormals.gltf";
+ const boxWithOffsetUrl =
+ "./Data/Models/glTF-2.0/BoxWithOffset/glTF/BoxWithOffset.gltf";
+ const pointCloudUrl =
+ "./Data/Models/glTF-2.0/PointCloudWithRGBColors/glTF-Binary/PointCloudWithRGBColors.glb";
+ const boxWithMixedCompression =
+ "./Data/Models/glTF-2.0/BoxMixedCompression/glTF/BoxMixedCompression.gltf";
+ const boxWithQuantizedAttributes =
+ "./Data/Models/glTF-2.0/BoxWeb3dQuantizedAttributes/glTF/BoxWeb3dQuantizedAttributes.gltf";
+ const boxCesiumRtcUrl =
+ "./Data/Models/glTF-2.0/BoxCesiumRtc/glTF/BoxCesiumRtc.gltf";
+ const boxBackFaceCullingUrl =
+ "./Data/Models/glTF-2.0/BoxBackFaceCulling/glTF/BoxBackFaceCulling.gltf";
+
+ let scene;
+ beforeAll(function () {
+ scene = createScene();
+ });
+
+ afterAll(function () {
+ scene.destroyForSpecs();
+ });
+
+ afterEach(function () {
+ scene.frameState.mode = SceneMode.SCENE3D;
+ scene.primitives.removeAll();
+ });
+
+ it("throws without model", function () {
+ expect(() => pickModel()).toThrowDeveloperError();
+ });
+
+ it("throws without ray", async function () {
+ const model = await Model.fromGltfAsync({
+ url: boxTexturedGltfUrl,
+ });
+ expect(() => pickModel(model)).toThrowDeveloperError();
+ });
+
+ it("throws without frameState", async function () {
+ const model = await Model.fromGltfAsync({
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ });
+ const ray = new Ray();
+ expect(() => pickModel(model, ray)).toThrowDeveloperError();
+ });
+
+ it("returns undefined if model is not ready", async function () {
+ const model = await Model.fromGltfAsync({
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ });
+ const ray = new Ray();
+ expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
+ });
+
+ it("returns undefined if ray does not intersect model surface", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = new Ray();
+ expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
+ });
+
+ it("returns position of intersection between ray and model surface", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("returns position of intersection between ray and model surface with enablePick in WebGL 1", async function () {
+ const sceneWithWebgl1 = createScene({
+ contextOptions: {
+ requestWebgl1: true,
+ },
+ });
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: true,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+
+ sceneWithWebgl1.destroyForSpecs();
+ });
+
+ it("returns position of intersection accounting for node transforms", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxWithOffsetUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.0, 5.5, -0.5);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("returns position of intersection with RTC model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxCesiumRtcUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(6378137.5, 0.0, -0.499999996649);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON8
+ );
+ });
+
+ it("returns position of intersection with quantzed model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxWithQuantizedAttributes,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("returns position of intersection with mixed compression model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxWithMixedCompression,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(1.0, 0, 1.0);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("returns position of intersection with instanced model", async function () {
+ // None of the 4 instanced cubes are in the center of the model's bounding
+ // sphere, so set up a camera view that focuses in on one of them.
+ const offset = new HeadingPitchRange(
+ CesiumMath.PI_OVER_TWO,
+ -CesiumMath.PI_OVER_FOUR,
+ 1
+ );
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxInstanced,
+ enablePick: !scene.frameState.context.webgl2,
+ offset,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0, -0.5, 0.5);
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("returns undefined for point cloud", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: pointCloudUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
+ });
+
+ it("cullsBackFaces by default", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+ ray.origin = model.boundingSphere.center;
+
+ expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
+ });
+
+ it("includes back faces results when model disbales backface culling", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxBackFaceCullingUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ backFaceCulling: false,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ ray.origin = model.boundingSphere.center;
+
+ const expected = new Cartesian3(
+ -0.9999998807907355,
+ 0,
+ -0.9999998807907104
+ );
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("uses result parameter if specified", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const result = new Cartesian3();
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ const returned = pickModel(model, ray, scene.frameState, result);
+ expect(result).toEqualEpsilon(expected, CesiumMath.EPSILON12);
+ expect(returned).toBe(result);
+ });
+
+ it("returns undefined when morphing", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ scene.frameState.mode = SceneMode.MORPHING;
+ expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
+ });
+});
diff --git a/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js b/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js
index 4d888da1355d..2d13eba58fb1 100644
--- a/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js
+++ b/packages/engine/Specs/Scene/OctahedralProjectedCubeMapSpec.js
@@ -208,7 +208,7 @@ describe(
});
});
- it("raises error event when environment map fails to load.", function () {
+ it("raises error event when environment map fails to load.", async function () {
if (!OctahedralProjectedCubeMap.isSupported(context)) {
return;
}
@@ -217,17 +217,22 @@ describe(
const frameState = createFrameState(context);
let error;
- const removeListener = projection.errorEvent.addEventListener((e) => {
- error = e;
- expect(error).toBeDefined();
- expect(projection.ready).toEqual(false);
- removeListener();
+ const promise = new Promise((resolve, reject) => {
+ const removeListener = projection.errorEvent.addEventListener((e) => {
+ error = e;
+ expect(error).toBeDefined();
+ expect(projection.ready).toEqual(false);
+ removeListener();
+ resolve();
+ });
});
- return pollToPromise(function () {
+ await pollToPromise(function () {
projection.update(frameState);
return defined(error);
});
+
+ return promise;
});
},
"WebGL"
diff --git a/packages/engine/Specs/test.mjs b/packages/engine/Specs/test.mjs
index 23f6bbf07cc3..1479b34b2dd2 100644
--- a/packages/engine/Specs/test.mjs
+++ b/packages/engine/Specs/test.mjs
@@ -1,9 +1,9 @@
-import { Cartographic, CesiumTerrainProvider, sampleTerrain } from "@cesium/engine";
+import { Cartographic, createWorldTerrainAsync, sampleTerrain } from "@cesium/engine";
import assert from "node:assert";
// NodeJS smoke screen test
async function test() {
- const provider = await CesiumTerrainProvider.fromUrl("https://s3.amazonaws.com/cesiumjs/smallTerrain");
+ const provider = await createWorldTerrainAsync();
const results = await sampleTerrain(provider, 11, [
Cartographic.fromDegrees(86.925145, 27.988257),
Cartographic.fromDegrees(87.0, 28.0),
diff --git a/packages/engine/package.json b/packages/engine/package.json
index 32a25763dcf6..ee1c7b7a0681 100644
--- a/packages/engine/package.json
+++ b/packages/engine/package.json
@@ -1,6 +1,6 @@
{
"name": "@cesium/engine",
- "version": "6.0.0",
+ "version": "6.1.1",
"description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.",
"keywords": [
"3D",
@@ -45,7 +45,7 @@
"ktx-parse": "^0.6.0",
"lerc": "^2.0.0",
"mersenne-twister": "^1.1.0",
- "meshoptimizer": "^0.19.0",
+ "meshoptimizer": "^0.20.0",
"pako": "^2.0.4",
"protobufjs": "^7.1.0",
"rbush": "^3.0.1",
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index 6357fe6992d6..5b121d5c1689 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -1,6 +1,6 @@
{
"name": "@cesium/widgets",
- "version": "4.2.0",
+ "version": "4.3.1",
"description": "A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.",
"keywords": [
"3D",
@@ -28,7 +28,7 @@
"node": ">=14.0.0"
},
"dependencies": {
- "@cesium/engine": "^6.0.0",
+ "@cesium/engine": "^6.1.1",
"nosleep.js": "^0.12.0"
},
"type": "module",