diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000000..ec2b08698835 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,64 @@ +name: deploy +on: + push: + branches-ignore: + - 'cesium.com' + - production +concurrency: + group: deploy-${{ github.ref }} + cancel-in-progress: true +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + statuses: write + contents: read + env: + BUILD_VERSION: ${{ github.ref_name }}.${{ github.run_number }} + 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 }} + GITHUB_REPO: ${{ github.repository }} + GITHUB_SHA: ${{ github.sha }} + steps: + - uses: actions/checkout@v3 + - name: install node 20 + uses: actions/setup-node@v3 + with: + node-version: '20' + - name: npm install + run: npm install + - name: set the version in package.json + run: npm run deploy-set-version -- --buildVersion $BUILD_VERSION + - name: create release zip + run: npm run make-zip + - name: package cesium module + run: npm pack &> /dev/null + - name: package workspace modules + run: npm pack --workspaces &> /dev/null + - name: build apps + run: npm run build-apps + - uses: ./.github/actions/verify-package + - name: deploy to s3 + if: ${{ env.AWS_ACCESS_KEY_ID != '' }} + 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 diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 3b982b06af77..0d9368b8080d 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -1,11 +1,11 @@ name: dev on: push: - branches-ignore: - - 'cesium.com' - - production + branches: + - main + pull_request: concurrency: - group: ${{ github.ref }} + group: dev-${{ github.ref }} cancel-in-progress: true jobs: lint: @@ -33,10 +33,10 @@ jobs: BRANCH: ${{ github.ref_name }} 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 @@ -50,73 +50,19 @@ 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: release build - run: npm run build-release + run: npm run make-zip - name: release tests (chrome) run: npm run test -- --browsers ChromeHeadless --failTaskOnError --webgl-stub --release --suppressPassed - name: cloc run: npm run cloc - deploy: - runs-on: ubuntu-latest - permissions: - statuses: write - contents: read - env: - BUILD_VERSION: ${{ github.ref_name }}.${{ github.run_number }} - 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 }} - GITHUB_REPO: ${{ github.repository }} - GITHUB_SHA: ${{ github.sha }} - steps: - - uses: actions/checkout@v3 - - name: install node 18 - uses: actions/setup-node@v3 - with: - node-version: '18' - - name: npm install - run: npm install - - name: set the version in package.json - run: npm run deploy-set-version -- --buildVersion $BUILD_VERSION - - name: create release zip - run: npm run make-zip - - name: package cesium module - run: npm pack &> /dev/null - - name: package workspace modules - run: npm pack --workspaces &> /dev/null - - name: build apps - run: npm run build-apps - - uses: ./.github/actions/verify-package - - name: deploy to s3 - if: ${{ env.AWS_ACCESS_KEY_ID != '' }} - 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 - node-16: + node-18: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2ddc67d198ef..4c89c5aac27a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,7 @@ name: main on: workflow_run: - workflows: [dev] + workflows: [dev, prod] types: [completed] branches: - main diff --git a/CHANGES.md b/CHANGES.md index c02febd28ab7..49ce27726103 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,10 +14,20 @@ - 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.disableCameraCollision` 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) ##### Fixes :wrench: +- Fixed a bug where the `Cesium3DTileset` constructor was ignoring the options `dynamicScreenSpaceError`, `dynamicScreenSpaceErrorDensity`, `dynamicScreenSpaceErrorFactor` and `dynamicScreenSpaceErrorHeightFalloff`. [#11677](https://github.com/CesiumGS/cesium/issues/11677) - Fix globe materials when lighting is false. Slope/Aspect material no longer rely on turning on lighting or shadows. [#11563](https://github.com/CesiumGS/cesium/issues/11563) +- Fixed a bug where `GregorianDate` constructor would not validate the input parameters for valid date. [#10075](https://github.com/CesiumGS/cesium/pull/10075) +- Fixed improper scaling of ellipsoid inner radii in 3D mode. [#11656](https://github.com/CesiumGS/cesium/issues/11656 and [#10245](https://github.com/CesiumGS/cesium/issues/10245) + +#### @cesium/widgets + +##### Fixes :wrench: + +- Fixed a bug where the 3D Tiles Inspector's `dynamicScreenSpaceErrorDensity` slider did not update the tileset [#6143](https://github.com/CesiumGS/cesium/issues/6143) ### 1.113 - 2024-01-02 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ff6613f2465d..0f86551f31e5 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -372,3 +372,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [KOBAYASHI Ittoku](https://github.com/kittoku) - [王康](https://github.com/yieryi) - [孙永政](https://github.com/syzdev) +- [Subhajit Saha](https://github.com/subhajits) +- [Jared Webber](https://github.com/jaredwebber) +- [Anne Gropler](https://github.com/anne-gropler) diff --git a/packages/engine/LICENSE.md b/packages/engine/LICENSE.md index 7c3383354f82..4ace84f3d85d 100644 --- a/packages/engine/LICENSE.md +++ b/packages/engine/LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2011-2022 CesiumJS Contributors +Copyright 2011-2024 CesiumJS Contributors Apache License Version 2.0, January 2004 diff --git a/packages/engine/Source/Core/GregorianDate.js b/packages/engine/Source/Core/GregorianDate.js index 14973a978eca..4fb7446d61db 100644 --- a/packages/engine/Source/Core/GregorianDate.js +++ b/packages/engine/Source/Core/GregorianDate.js @@ -1,3 +1,10 @@ +import Check from "./Check.js"; +import defaultValue from "./defaultValue.js"; +import DeveloperError from "./DeveloperError.js"; +import isLeapYear from "./isLeapYear.js"; + +const daysInYear = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + /** * Represents a Gregorian date in a more precise format than the JavaScript Date object. * In addition to submillisecond precision, this object can also represent leap seconds. @@ -25,6 +32,27 @@ function GregorianDate( millisecond, isLeapSecond ) { + const minimumYear = 1; + const minimumMonth = 1; + const minimumDay = 1; + const minimumHour = 0; + const minimumMinute = 0; + const minimumSecond = 0; + const minimumMillisecond = 0; + + year = defaultValue(year, minimumYear); + month = defaultValue(month, minimumMonth); + day = defaultValue(day, minimumDay); + hour = defaultValue(hour, minimumHour); + minute = defaultValue(minute, minimumMinute); + second = defaultValue(second, minimumSecond); + millisecond = defaultValue(millisecond, minimumMillisecond); + isLeapSecond = defaultValue(isLeapSecond, false); + //>>includeStart('debug', pragmas.debug); + validateRange(); + validateDate(); + //>>includeEnd('debug'); + /** * Gets or sets the year as a whole number. * @type {number} @@ -65,5 +93,62 @@ function GregorianDate( * @type {boolean} */ this.isLeapSecond = isLeapSecond; + + function validateRange() { + const maximumYear = 9999; + const maximumMonth = 12; + const maximumDay = 31; + const maximumHour = 23; + const maximumMinute = 59; + const maximumSecond = 59; + const excludedMaximumMilisecond = 1000; + + Check.typeOf.number.greaterThanOrEquals("Year", year, minimumYear); + Check.typeOf.number.lessThanOrEquals("Year", year, maximumYear); + + Check.typeOf.number.greaterThanOrEquals("Month", month, minimumMonth); + Check.typeOf.number.lessThanOrEquals("Month", month, maximumMonth); + + Check.typeOf.number.greaterThanOrEquals("Day", day, minimumDay); + Check.typeOf.number.lessThanOrEquals("Day", day, maximumDay); + + Check.typeOf.number.greaterThanOrEquals("Hour", hour, minimumHour); + Check.typeOf.number.lessThanOrEquals("Hour", hour, maximumHour); + + Check.typeOf.number.greaterThanOrEquals("Minute", minute, minimumMinute); + Check.typeOf.number.lessThanOrEquals("Minute", minute, maximumMinute); + + Check.typeOf.bool("IsLeapSecond", isLeapSecond); + + Check.typeOf.number.greaterThanOrEquals("Second", second, minimumSecond); + Check.typeOf.number.lessThanOrEquals( + "Second", + second, + isLeapSecond ? maximumSecond + 1 : maximumSecond + ); + + Check.typeOf.number.greaterThanOrEquals( + "Millisecond", + millisecond, + minimumMillisecond + ); + Check.typeOf.number.lessThan( + "Millisecond", + millisecond, + excludedMaximumMilisecond + ); + } + + // Javascript date object supports only dates greater than 1901. Thus validating with custom logic + function validateDate() { + const daysInMonth = + month === 2 && isLeapYear(year) + ? daysInYear[month - 1] + 1 + : daysInYear[month - 1]; + + if (day > daysInMonth) { + throw new DeveloperError("Month and Day represents invalid date"); + } + } } export default GregorianDate; diff --git a/packages/engine/Source/DataSources/EllipsoidGeometryUpdater.js b/packages/engine/Source/DataSources/EllipsoidGeometryUpdater.js index 8984d6ea1d32..877f84a6ac1e 100644 --- a/packages/engine/Source/DataSources/EllipsoidGeometryUpdater.js +++ b/packages/engine/Source/DataSources/EllipsoidGeometryUpdater.js @@ -547,11 +547,10 @@ DynamicEllipsoidGeometryUpdater.prototype.update = function (time) { options.radii = Cartesian3.clone(in3D ? unitSphere : radii, options.radii); if (defined(innerRadii)) { if (in3D) { - const mag = Cartesian3.magnitude(radii); options.innerRadii = Cartesian3.fromElements( - innerRadii.x / mag, - innerRadii.y / mag, - innerRadii.z / mag, + innerRadii.x / radii.x, + innerRadii.y / radii.y, + innerRadii.z / radii.z, options.innerRadii ); } else { diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js index 82320ffe1638..50fbaa6296df 100644 --- a/packages/engine/Source/Scene/Cesium3DTileset.js +++ b/packages/engine/Source/Scene/Cesium3DTileset.js @@ -19,6 +19,7 @@ import IonResource from "../Core/IonResource.js"; import JulianDate from "../Core/JulianDate.js"; import ManagedArray from "../Core/ManagedArray.js"; import CesiumMath from "../Core/Math.js"; +import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import Resource from "../Core/Resource.js"; import RuntimeError from "../Core/RuntimeError.js"; @@ -78,10 +79,10 @@ import Ray from "../Core/Ray.js"; * @property {boolean} [preloadWhenHidden=false] Preload tiles when tileset.show is false. Loads tiles as if the tileset is visible but does not render them. * @property {boolean} [preloadFlightDestinations=true] Optimization option. Preload tiles at the camera's flight destination while the camera is in flight. * @property {boolean} [preferLeaves=false] Optimization option. Prefer loading of leaves first. - * @property {boolean} [dynamicScreenSpaceError=false] Optimization option. Reduce the screen space error for tiles that are further away from the camera. - * @property {number} [dynamicScreenSpaceErrorDensity=0.00278] Density used to adjust the dynamic screen space error, similar to fog density. - * @property {number} [dynamicScreenSpaceErrorFactor=4.0] A factor used to increase the computed dynamic screen space error. - * @property {number} [dynamicScreenSpaceErrorHeightFalloff=0.25] A ratio of the tileset's height at which the density starts to falloff. + * @property {boolean} [dynamicScreenSpaceError=true] Optimization option. For street-level horizon views, use lower resolution tiles far from the camera. This reduces the amount of data loaded and improves tileset loading time with a slight drop in visual quality in the distance. + * @property {number} [dynamicScreenSpaceErrorDensity=2.0e-4] Similar to {@link Fog#density}, this option controls the camera distance at which the {@link Cesium3DTileset#dynamicScreenSpaceError} optimization applies. Larger values will cause tiles closer to the camera to be affected. + * @property {number} [dynamicScreenSpaceErrorFactor=24.0] A parameter that controls the intensity of the {@link Cesium3DTileset#dynamicScreenSpaceError} optimization for tiles on the horizon. Larger values cause lower resolution tiles to load, improving runtime performance at a slight reduction of visual quality. + * @property {number} [dynamicScreenSpaceErrorHeightFalloff=0.25] A ratio of the tileset's height that determines where "street level" camera views occur. When the camera is below this height, the {@link Cesium3DTileset#dynamicScreenSpaceError} optimization will have the maximum effect, and it will roll off above this value. * @property {number} [progressiveResolutionHeightFraction=0.3] Optimization option. If between (0.0, 0.5], tiles at or above the screen space error for the reduced screen resolution of progressiveResolutionHeightFraction*screenHeight will be prioritized first. This can help get a quick layer of tiles down while full resolution tiles continue to load. * @property {boolean} [foveatedScreenSpaceError=true] Optimization option. Prioritize loading tiles in the center of the screen by temporarily raising the screen space error for tiles around the edge of the screen. Screen space error returns to normal once all the tiles in the center of the screen as determined by the {@link Cesium3DTileset#foveatedConeSize} are loaded. * @property {number} [foveatedConeSize=0.1] Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control the cone size that determines which tiles are deferred. Tiles that are inside this cone are loaded immediately. Tiles outside the cone are potentially deferred based on how far outside the cone they are and their screen space error. This is controlled by {@link Cesium3DTileset#foveatedInterpolationCallback} and {@link Cesium3DTileset#foveatedMinimumScreenSpaceErrorRelaxation}. Setting this to 0.0 means the cone will be the line formed by the camera position and its view direction. Setting this to 1.0 means the cone encompasses the entire field of view of the camera, disabling the effect. @@ -182,8 +183,8 @@ import Ray from "../Core/Ray.js"; * const tileset = await Cesium.Cesium3DTileset.fromUrl( * "http://localhost:8002/tilesets/Seattle/tileset.json", { * dynamicScreenSpaceError: true, - * dynamicScreenSpaceErrorDensity: 0.00278, - * dynamicScreenSpaceErrorFactor: 4.0, + * dynamicScreenSpaceErrorDensity: 2.0e-4, + * dynamicScreenSpaceErrorFactor: 24.0, * dynamicScreenSpaceErrorHeightFalloff: 0.25 * }); * scene.primitives.add(tileset); @@ -377,18 +378,18 @@ function Cesium3DTileset(options) { this._pass = undefined; // Cesium3DTilePass /** - * Optimization option. Whether the tileset should refine based on a dynamic screen space error. Tiles that are further - * away will be rendered with lower detail than closer tiles. This improves performance by rendering fewer - * tiles and making less requests, but may result in a slight drop in visual quality for tiles in the distance. - * The algorithm is biased towards "street views" where the camera is close to the ground plane of the tileset and looking - * at the horizon. In addition results are more accurate for tightly fitting bounding volumes like box and region. + * Optimization option. For street-level horizon views, use lower resolution tiles far from the camera. This reduces + * the amount of data loaded and improves tileset loading time with a slight drop in visual quality in the distance. + *

+ * This optimization is strongest when the camera is close to the ground plane of the tileset and looking at the + * horizon. Furthermore, the results are more accurate for tightly fitting bounding volumes like box and region. * * @type {boolean} - * @default false + * @default true */ this.dynamicScreenSpaceError = defaultValue( options.dynamicScreenSpaceError, - false + true ); /** @@ -432,48 +433,68 @@ function Cesium3DTileset(options) { this.foveatedTimeDelay = defaultValue(options.foveatedTimeDelay, 0.2); /** - * A scalar that determines the density used to adjust the dynamic screen space error, similar to {@link Fog}. Increasing this - * value has the effect of increasing the maximum screen space error for all tiles, but in a non-linear fashion. - * The error starts at 0.0 and increases exponentially until a midpoint is reached, and then approaches 1.0 asymptotically. - * This has the effect of keeping high detail in the closer tiles and lower detail in the further tiles, with all tiles - * beyond a certain distance all roughly having an error of 1.0. + * Similar to {@link Fog#density}, this option controls the camera distance at which the {@link Cesium3DTileset#dynamicScreenSpaceError} + * optimization applies. Larger values will cause tiles closer to the camera to be affected. This value must be + * non-negative. *

- * The dynamic error is in the range [0.0, 1.0) and is multiplied by dynamicScreenSpaceErrorFactor to produce the - * final dynamic error. This dynamic error is then subtracted from the tile's actual screen space error. + * This optimization works by rolling off the tile screen space error (SSE) with camera distance like a bell curve. + * This has the effect of selecting lower resolution tiles far from the camera. Near the camera, no adjustment is + * made. For tiles further away, the SSE is reduced by up to {@link Cesium3DTileset#dynamicScreenSpaceErrorFactor} + * (measured in pixels of error). *

*

- * Increasing dynamicScreenSpaceErrorDensity has the effect of moving the error midpoint closer to the camera. - * It is analogous to moving fog closer to the camera. + * Increasing the density makes the bell curve narrower so tiles closer to the camera are affected. This is analagous + * to moving fog closer to the camera. + *

+ *

+ * When the density is 0, the optimization will have no effect on the tileset. *

* * @type {number} - * @default 0.00278 + * @default 2.0e-4 */ - this.dynamicScreenSpaceErrorDensity = 0.00278; + this.dynamicScreenSpaceErrorDensity = defaultValue( + options.dynamicScreenSpaceErrorDensity, + 2.0e-4 + ); /** - * A factor used to increase the screen space error of tiles for dynamic screen space error. As this value increases less tiles - * are requested for rendering and tiles in the distance will have lower detail. If set to zero, the feature will be disabled. + * A parameter that controls the intensity of the {@link Cesium3DTileset#dynamicScreenSpaceError} optimization for + * tiles on the horizon. Larger values cause lower resolution tiles to load, improving runtime performance at a slight + * reduction of visual quality. The value must be non-negative. + *

+ * More specifically, this parameter represents the maximum adjustment to screen space error (SSE) in pixels for tiles + * far away from the camera. See {@link Cesium3DTileset#dynamicScreenSpaceErrorDensity} for more details about how + * this optimization works. + *

+ *

+ * When the SSE factor is set to 0, the optimization will have no effect on the tileset. + *

* * @type {number} - * @default 4.0 + * @default 24.0 */ - this.dynamicScreenSpaceErrorFactor = 4.0; + this.dynamicScreenSpaceErrorFactor = defaultValue( + options.dynamicScreenSpaceErrorFactor, + 24.0 + ); /** - * A ratio of the tileset's height at which the density starts to falloff. If the camera is below this height the - * full computed density is applied, otherwise the density falls off. This has the effect of higher density at - * street level views. + * A ratio of the tileset's height that determines "street level" for the {@link Cesium3DTileset#dynamicScreenSpaceError} + * optimization. When the camera is below this height, the dynamic screen space error optimization will have the maximum + * effect, and it will roll off above this value. Valid values are between 0.0 and 1.0. *

- * Valid values are between 0.0 and 1.0. - *

* * @type {number} * @default 0.25 */ - this.dynamicScreenSpaceErrorHeightFalloff = 0.25; + this.dynamicScreenSpaceErrorHeightFalloff = defaultValue( + options.dynamicScreenSpaceErrorHeightFalloff, + 0.25 + ); - this._dynamicScreenSpaceErrorComputedDensity = 0.0; // Updated based on the camera position and direction + // Updated based on the camera position and direction + this._dynamicScreenSpaceErrorComputedDensity = 0.0; /** * Determines whether the tileset casts or receives shadows from light sources. @@ -1660,6 +1681,7 @@ Object.defineProperties(Cesium3DTileset.prototype, { *
  • The glTF cannot contain morph targets, skins, or animations.
  • *
  • The glTF cannot contain the EXT_mesh_gpu_instancing extension.
  • *
  • Only meshes with TRIANGLES can be used to classify other assets.
  • + *
  • The meshes must be watertight.
  • *
  • The POSITION semantic is required.
  • *
  • If _BATCHIDs and an index buffer are both present, all indices with the same batch id must occupy contiguous sections of the index buffer.
  • *
  • If _BATCHIDs are present with no index buffer, all positions with the same batch id must occupy contiguous sections of the position buffer.
  • @@ -1669,6 +1691,9 @@ Object.defineProperties(Cesium3DTileset.prototype, { * Additionally, classification is not supported for points or instanced 3D * models. *

    + *

    + * The 3D Tiles or terrain receiving the classification must be opaque. + *

    * * @memberof Cesium3DTileset.prototype * @@ -1993,8 +2018,8 @@ Cesium3DTileset.fromIonAssetId = async function (assetId, options) { * const tileset = await Cesium.Cesium3DTileset.fromUrl( * "http://localhost:8002/tilesets/Seattle/tileset.json", { * dynamicScreenSpaceError: true, - * dynamicScreenSpaceErrorDensity: 0.00278, - * dynamicScreenSpaceErrorFactor: 4.0, + * dynamicScreenSpaceErrorDensity: 2.0e-4, + * dynamicScreenSpaceErrorFactor: 24.0, * dynamicScreenSpaceErrorHeightFalloff: 0.25 * }); * scene.primitives.add(tileset); @@ -2291,6 +2316,7 @@ const scratchMatrix = new Matrix4(); const scratchCenter = new Cartesian3(); const scratchPosition = new Cartesian3(); const scratchDirection = new Cartesian3(); +const scratchHalfHeight = new Cartesian3(); /** * @private @@ -2355,10 +2381,16 @@ function updateDynamicScreenSpaceError(tileset, frameState) { direction = Cartesian3.normalize(direction, direction); height = positionLocal.z; if (tileBoundingVolume instanceof TileOrientedBoundingBox) { - // Assuming z-up, the last component stores the half-height of the box - const boxHeight = root._header.boundingVolume.box[11]; - minimumHeight = centerLocal.z - boxHeight; - maximumHeight = centerLocal.z + boxHeight; + // Assuming z-up, the last column is the local z direction and + // represents the height of the bounding box. + const halfHeightVector = Matrix3.getColumn( + boundingVolume.halfAxes, + 2, + scratchHalfHeight + ); + const halfHeight = Cartesian3.magnitude(halfHeightVector); + minimumHeight = centerLocal.z - halfHeight; + maximumHeight = centerLocal.z + halfHeight; } else if (tileBoundingVolume instanceof TileBoundingSphere) { const radius = boundingVolume.radius; minimumHeight = centerLocal.z - radius; diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index f9385ed410ac..1a6a56c5e032 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -113,7 +113,7 @@ import pickModel from "./pickModel.js"; * @internalConstructor * * @privateParam {ResourceLoader} options.loader The loader used to load resources for this model. - * @privateParam {ModelType} options.type Type of this model, to distinguish individual glTF files from 3D Tiles internally. + * @privateParam {ModelType} options.type Type of this model, to distinguish individual glTF files from 3D Tiles internally. * @privateParam {object} options Object with the following properties: * @privateParam {Resource} options.resource The Resource to the 3D model. * @privateParam {boolean} [options.show=true] Whether or not to render the model. @@ -156,7 +156,7 @@ import pickModel from "./pickModel.js"; * @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. * @privateParam {ClassificationType} [options.classificationType] Determines whether terrain, 3D Tiles or both will be classified by this model. This cannot be set after the model has loaded. - + * * @see Model.fromGltfAsync * @@ -194,7 +194,7 @@ function Model(options) { * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's Cartesian WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. - * + * * @type {Matrix4} * @default {@link Matrix4.IDENTITY} @@ -1553,11 +1553,15 @@ Object.defineProperties(Model.prototype, { *
  • The glTF cannot contain morph targets, skins, or animations.
  • *
  • The glTF cannot contain the EXT_mesh_gpu_instancing extension.
  • *
  • Only meshes with TRIANGLES can be used to classify other assets.
  • - *
  • The position attribute is required.
  • + *
  • The meshes must be watertight.
  • + *
  • The POSITION attribute is required.
  • *
  • If feature IDs and an index buffer are both present, all indices with the same feature id must occupy contiguous sections of the index buffer.
  • *
  • If feature IDs are present without an index buffer, all positions with the same feature id must occupy contiguous sections of the position buffer.
  • * *

    + *

    + * The 3D Tiles or terrain receiving the classification must be opaque. + *

    * * @memberof Model.prototype * diff --git a/packages/engine/Specs/Core/GregorianDateSpec.js b/packages/engine/Specs/Core/GregorianDateSpec.js new file mode 100644 index 000000000000..9725fc8c275f --- /dev/null +++ b/packages/engine/Specs/Core/GregorianDateSpec.js @@ -0,0 +1,313 @@ +import { GregorianDate } from "../../index.js"; + +describe("Core/GregorianDate", function () { + describe("With valid parameters", function () { + it("Constructs any valid date", function () { + const validDate = new GregorianDate(2022, 2, 4, 23, 54, 0, 999.9, false); + expect(validDate.year).toEqual(2022); + expect(validDate.month).toEqual(2); + expect(validDate.day).toEqual(4); + expect(validDate.hour).toEqual(23); + expect(validDate.minute).toEqual(54); + expect(validDate.second).toEqual(0); + expect(validDate.millisecond).toEqual(999.9); + expect(validDate.isLeapSecond).toBe(false); + }); + + it("Constructs valid leap year date", function () { + const validDate = new GregorianDate(2024, 2, 29, 23, 54, 0, 999.9, false); + expect(validDate.year).toEqual(2024); + expect(validDate.month).toEqual(2); + expect(validDate.day).toEqual(29); + expect(validDate.hour).toEqual(23); + expect(validDate.minute).toEqual(54); + expect(validDate.second).toEqual(0); + expect(validDate.millisecond).toEqual(999.9); + expect(validDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum date when no parameters are passed", function () { + const minimumDate = new GregorianDate(); + expect(minimumDate.year).toEqual(1); + expect(minimumDate.month).toEqual(1); + expect(minimumDate.day).toEqual(1); + expect(minimumDate.hour).toEqual(0); + expect(minimumDate.minute).toEqual(0); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs valid dates for edge cases of days", function () { + expect(function () { + return new GregorianDate(2022, 1, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2000, 2, 28); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2020, 2, 29); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 3, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 4, 30); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 5, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 6, 30); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 7, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 8, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 9, 30); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 10, 31); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 30); + }).not.toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 12, 31); + }).not.toThrowDeveloperError(); + }); + + it("Constructs the minimum possible date of the year when only year parameter is passed", function () { + const minimumDate = new GregorianDate(2022); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(1); + expect(minimumDate.day).toEqual(1); + expect(minimumDate.hour).toEqual(0); + expect(minimumDate.minute).toEqual(0); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum possible day of the month when only year and month parameters are passed", function () { + const minimumDate = new GregorianDate(2022, 2); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(1); + expect(minimumDate.hour).toEqual(0); + expect(minimumDate.minute).toEqual(0); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum possible time of the day when only year, month and day parameters are passed", function () { + const minimumDate = new GregorianDate(2022, 2, 28); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(28); + expect(minimumDate.hour).toEqual(0); + expect(minimumDate.minute).toEqual(0); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum possible time of the day when only year, month, day and hour parameters are passed", function () { + const minimumDate = new GregorianDate(2022, 2, 28, 10); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(28); + expect(minimumDate.hour).toEqual(10); + expect(minimumDate.minute).toEqual(0); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum possible time of the day when only year, month, day, hour and minutes parameters are passed", function () { + const minimumDate = new GregorianDate(2022, 2, 28, 10, 59); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(28); + expect(minimumDate.hour).toEqual(10); + expect(minimumDate.minute).toEqual(59); + expect(minimumDate.second).toEqual(0); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the minimum possible time of the day when only year, month, day, hour, minutes and seconds parameters are passed", function () { + const minimumDate = new GregorianDate(2022, 2, 28, 10, 59, 59); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(28); + expect(minimumDate.hour).toEqual(10); + expect(minimumDate.minute).toEqual(59); + expect(minimumDate.second).toEqual(59); + expect(minimumDate.millisecond).toEqual(0); + expect(minimumDate.isLeapSecond).toBe(false); + }); + + it("Constructs the date with leap second", function () { + const minimumDate = new GregorianDate(2022, 2, 28, 10, 59, 60, 100, true); + expect(minimumDate.year).toEqual(2022); + expect(minimumDate.month).toEqual(2); + expect(minimumDate.day).toEqual(28); + expect(minimumDate.hour).toEqual(10); + expect(minimumDate.minute).toEqual(59); + expect(minimumDate.second).toEqual(60); + expect(minimumDate.millisecond).toEqual(100); + expect(minimumDate.isLeapSecond).toBe(true); + }); + }); + + describe("With invalid parameters", function () { + it("Should throw error if invalid year is passed", function () { + expect(function () { + return new GregorianDate(-1, 2, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(0, 2, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(10000, 2, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid month is passed", function () { + expect(function () { + return new GregorianDate(2022, -1, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 0, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 13, 4, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid day is passed", function () { + expect(function () { + return new GregorianDate(2022, 12, -10, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 12, 0, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 12, 32, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if day is out of range for month", function () { + expect(function () { + return new GregorianDate(2020, 2, 30, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 31, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 4, 31, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 6, 31, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 9, 31, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if leap day is given for non-leap year", function () { + expect(function () { + return new GregorianDate(2022, 2, 29, 23, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid hours is passed", function () { + expect(function () { + return new GregorianDate(2022, 2, 4, -10, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, -1, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 24, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 4, 100, 54, 0, 999.9, false); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid minute is passed", function () { + expect(function () { + return new GregorianDate(2022, 2, 4, 15, -1, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 60, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 7, 60, 0, 999.9, true); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 4, 0, -1, 0, 999.9, true); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid second is passed", function () { + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 1, -1, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 59, 60, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 7, 59, 61, 999.9, true); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 4, 0, 1, -1, 999.9, true); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid millisecond is passed", function () { + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 1, 0, -1, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 59, 59, 1000, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 7, 59, 60, 1000, true); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 11, 4, 0, 1, 0, -12, true); + }).toThrowDeveloperError(); + }); + + it("Should throw error if invalid type is passed", function () { + expect(function () { + return new GregorianDate("2022A", 2, 4, 15, 1, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, "Two", 4, 15, 1, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, "F0UR", 15, 1, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, "15th", 1, 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, "1st", 0, 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 1, "Zero", 999.9, false); + }).toThrowDeveloperError(); + expect(function () { + return new GregorianDate(2022, 2, 4, 15, 1, 0, "999,9O", false); + }).toThrowDeveloperError(); + }); + }); +}); diff --git a/packages/engine/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js b/packages/engine/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js index 309391961d25..8d3e910ee5b4 100644 --- a/packages/engine/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js +++ b/packages/engine/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js @@ -416,6 +416,33 @@ describe( ); }); + it("Inner radii should be scaled when in 3D mode", function () { + const ellipsoid = new EllipsoidGraphics(); + ellipsoid.radii = createDynamicProperty(new Cartesian3(1, 2, 3)); + ellipsoid.innerRadii = createDynamicProperty(new Cartesian3(0.5, 1, 1.5)); + + const entity = new Entity(); + entity.position = createDynamicProperty(Cartesian3.fromDegrees(0, 0, 0)); + entity.orientation = createDynamicProperty(Quaternion.IDENTITY); + entity.ellipsoid = ellipsoid; + + const updater = new EllipsoidGeometryUpdater(entity, scene); + const primitives = scene.primitives; + + const dynamicUpdater = updater.createDynamicUpdater( + primitives, + new PrimitiveCollection() + ); + dynamicUpdater.update(time); + + scene.initializeFrame(); + scene.render(); + + expect(dynamicUpdater._options.innerRadii.x).toEqual(0.5); + expect(dynamicUpdater._options.innerRadii.y).toEqual(0.5); + expect(dynamicUpdater._options.innerRadii.z).toEqual(0.5); + }); + it("dynamic ellipsoid fast path updates attributes", function () { const ellipsoid = new EllipsoidGraphics(); ellipsoid.show = createDynamicProperty(true); diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index 341c8e365a1d..4d6fe613a4f0 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -1097,30 +1097,27 @@ describe( }); }); - function testDynamicScreenSpaceError(url, distance) { - return Cesium3DTilesTester.loadTileset(scene, url).then(function ( - tileset - ) { - const statistics = tileset._statistics; + async function testDynamicScreenSpaceError(url, distance) { + const tileset = await Cesium3DTilesTester.loadTileset(scene, url); + const statistics = tileset._statistics; - // Horizon view, only root is visible - const center = Cartesian3.fromRadians(centerLongitude, centerLatitude); - scene.camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, distance)); + // Horizon view, only root is visible + const center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, distance)); - // Set dynamic SSE to false (default) - tileset.dynamicScreenSpaceError = false; - scene.renderForSpecs(); - expect(statistics.visited).toEqual(1); - expect(statistics.numberOfCommands).toEqual(1); + // Turn off dynamic SSE + tileset.dynamicScreenSpaceError = false; + scene.renderForSpecs(); + expect(statistics.visited).toEqual(1); + expect(statistics.numberOfCommands).toEqual(1); - // Set dynamic SSE to true, now the root is not rendered - tileset.dynamicScreenSpaceError = true; - tileset.dynamicScreenSpaceErrorDensity = 1.0; - tileset.dynamicScreenSpaceErrorFactor = 10.0; - scene.renderForSpecs(); - expect(statistics.visited).toEqual(0); - expect(statistics.numberOfCommands).toEqual(0); - }); + // Turn on dynamic SSE, now the root is not rendered + tileset.dynamicScreenSpaceError = true; + tileset.dynamicScreenSpaceErrorDensity = 1.0; + tileset.dynamicScreenSpaceErrorFactor = 10.0; + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); } function numberOfChildrenWithoutContent(tile) { @@ -1154,6 +1151,39 @@ describe( return testDynamicScreenSpaceError(withTransformSphereUrl, 144.0); }); + it("dynamic screen space error constructor options work", async function () { + const options = { + dynamicScreenSpaceError: true, + dynamicScreenSpaceErrorDensity: 1.0, + dynamicScreenSpaceErrorFactor: 10.0, + dynamicScreenSpaceErrorHeightFalloff: 0.5, + }; + const distance = 103.0; + const tileset = await Cesium3DTilesTester.loadTileset( + scene, + withTransformBoxUrl, + options + ); + + // Make sure the values match the constructor, not hard-coded defaults + // like in https://github.com/CesiumGS/cesium/issues/11677 + expect(tileset.dynamicScreenSpaceError).toBe(true); + expect(tileset.dynamicScreenSpaceErrorDensity).toBe(1.0); + expect(tileset.dynamicScreenSpaceErrorFactor).toBe(10.0); + expect(tileset.dynamicScreenSpaceErrorHeightFalloff).toBe(0.5); + + const statistics = tileset._statistics; + + // Horizon view, only root is in view, however due to dynamic SSE, + // it will not render. + const center = Cartesian3.fromRadians(centerLongitude, centerLatitude); + scene.camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, distance)); + + scene.renderForSpecs(); + expect(statistics.visited).toEqual(0); + expect(statistics.numberOfCommands).toEqual(0); + }); + it("additive refinement - selects root when sse is met", function () { viewRootOnly(); return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function ( diff --git a/packages/engine/Specs/Scene/PickingSpec.js b/packages/engine/Specs/Scene/PickingSpec.js index 4474051a0adc..19cb2c659667 100644 --- a/packages/engine/Specs/Scene/PickingSpec.js +++ b/packages/engine/Specs/Scene/PickingSpec.js @@ -134,9 +134,16 @@ describe( function createTileset(url) { const options = { maximumScreenSpaceError: 0, + // Dynamic screen space error seems to cause a race condition in + // waitForTilesLoaded. + // See https://github.com/CesiumGS/cesium/issues/11732 + dynamicScreenSpaceError: false, }; return Cesium3DTilesTester.loadTileset(scene, url, options).then( function (tileset) { + // The tilesets used in these tests have transforms that are not + // what we want for our camera setup. Re-position the tileset + // in view of the camera const cartographic = Rectangle.center(largeRectangle); const cartesian = Cartographic.toCartesian(cartographic); tileset.root.transform = Matrix4.IDENTITY; diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index b8297061da79..74a8a66a7efb 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -1187,7 +1187,7 @@ describe( s.destroyForSpecs(); }); - it("alwayas raises preUpdate event prior to updating", function () { + it("always raises preUpdate event prior to updating", function () { const s = createScene(); const spyListener = jasmine.createSpy("listener"); @@ -1207,11 +1207,11 @@ describe( s.destroyForSpecs(); }); - it("always raises preUpdate event after updating", function () { + it("always raises postUpdate event after updating", function () { const s = createScene(); const spyListener = jasmine.createSpy("listener"); - s.preUpdate.addEventListener(spyListener); + s.postUpdate.addEventListener(spyListener); s.render(); diff --git a/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspector.js b/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspector.js index a88bb400ae96..742434dc7361 100644 --- a/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspector.js +++ b/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspector.js @@ -215,7 +215,7 @@ function Cesium3DTilesInspector(container, scene) { "Screen Space Error Factor", "dynamicScreenSpaceErrorFactor", 1, - 10, + 32, 0.1 ) ); diff --git a/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js b/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js index 42ec352db523..093545e1572b 100644 --- a/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js +++ b/packages/widgets/Source/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModel.js @@ -722,9 +722,9 @@ function Cesium3DTilesInspectorViewModel(scene, performanceContainer) { * Gets or sets the dynamic screen space error density. This property is observable. * * @type {number} - * @default 0.00278 + * @default 2.0e-4 */ - this.dynamicScreenSpaceErrorDensity = 0.00278; + this.dynamicScreenSpaceErrorDensity = 2.0e-4; /** * Gets or sets the dynamic screen space error density slider value. @@ -732,7 +732,7 @@ function Cesium3DTilesInspectorViewModel(scene, performanceContainer) { * This property is observable. * * @type {number} - * @default 0.00278 + * @default 2.0e-4 */ this.dynamicScreenSpaceErrorDensitySliderValue = undefined; knockout.defineProperty(this, "dynamicScreenSpaceErrorDensitySliderValue", { @@ -740,7 +740,11 @@ function Cesium3DTilesInspectorViewModel(scene, performanceContainer) { return Math.pow(dynamicScreenSpaceErrorDensity(), 1 / 6); }, set: function (value) { - dynamicScreenSpaceErrorDensity(Math.pow(value, 6)); + const scaledValue = Math.pow(value, 6); + dynamicScreenSpaceErrorDensity(scaledValue); + if (defined(that._tileset)) { + that._tileset.dynamicScreenSpaceErrorDensity = scaledValue; + } }, }); @@ -763,9 +767,9 @@ function Cesium3DTilesInspectorViewModel(scene, performanceContainer) { * Gets or sets the dynamic screen space error factor. This property is observable. * * @type {number} - * @default 4.0 + * @default 24.0 */ - this.dynamicScreenSpaceErrorFactor = 4.0; + this.dynamicScreenSpaceErrorFactor = 24.0; const pickTileset = getPickTileset(this); const pickActive = knockout.observable(); diff --git a/packages/widgets/Specs/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js b/packages/widgets/Specs/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js index d681f7cba70a..51267ff50f61 100644 --- a/packages/widgets/Specs/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js +++ b/packages/widgets/Specs/Cesium3DTilesInspector/Cesium3DTilesInspectorViewModelSpec.js @@ -1,4 +1,9 @@ -import { Cesium3DTileset, Cesium3DTileStyle, Globe } from "@cesium/engine"; +import { + Cesium3DTileset, + Cesium3DTileStyle, + Globe, + Math as CesiumMath, +} from "@cesium/engine"; import { Cesium3DTilesInspectorViewModel } from "../../index.js"; import createScene from "../../../../Specs/createScene.js"; @@ -255,6 +260,23 @@ describe( expect(viewModel.tileset.dynamicScreenSpaceErrorFactor).toBe(2); expect(viewModel.tileset.dynamicScreenSpaceErrorDensity).toBe(0.1); }); + + it("dynamicScreenSpaceErrorDensity slider uses an exponential scale", function () { + // The HTML slider produces a linear range, but the actual density value + // varies exponentially. + const rawSliderValue = 0.2; + const scaledValue = Math.pow(rawSliderValue, 6); + + viewModel.dynamicScreenSpaceErrorDensitySliderValue = rawSliderValue; + expect( + viewModel.dynamicScreenSpaceErrorDensitySliderValue + ).toEqualEpsilon(rawSliderValue, CesiumMath.EPSILON8); + + expect(viewModel.tileset.dynamicScreenSpaceErrorDensity).toEqualEpsilon( + scaledValue, + CesiumMath.EPSILON8 + ); + }); }); describe("style options", function () {