From 4a3678acf325a191967a9cdea96fce55c79b9af4 Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Thu, 19 Oct 2023 12:33:29 -0400 Subject: [PATCH 01/64] Fix rhumb line interpolate for 0 headings --- .../engine/Source/Core/EllipsoidRhumbLine.js | 14 +++++-- .../Specs/Core/EllipsoidRhumbLineSpec.js | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Core/EllipsoidRhumbLine.js b/packages/engine/Source/Core/EllipsoidRhumbLine.js index 047d48e87961..d29623bb3063 100644 --- a/packages/engine/Source/Core/EllipsoidRhumbLine.js +++ b/packages/engine/Source/Core/EllipsoidRhumbLine.js @@ -333,10 +333,16 @@ function interpolateUsingSurfaceDistance( latitude = calculateInverseM(M2, ellipticity, major); //Now find the longitude of the second point - const sigma1 = calculateSigma(ellipticity, start.latitude); - const sigma2 = calculateSigma(ellipticity, latitude); - deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); - longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + + // Check to see if the rhumb line has constant longitude + if (Math.abs(Math.abs(heading)) < CesiumMath.EPSILON10) { + longitude = CesiumMath.negativePiToPi(start.longitude); + } else { + const sigma1 = calculateSigma(ellipticity, start.latitude); + const sigma2 = calculateSigma(ellipticity, latitude); + deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } } else { //If heading is close to 90 degrees latitude = start.latitude; 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); From 82bedffd795fcd5f82928da1170fc1f75e10f843 Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Fri, 20 Oct 2023 14:30:02 -0400 Subject: [PATCH 02/64] Fixes an issue with polygon hole rendering --- packages/engine/Source/Core/PolygonGeometry.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/engine/Source/Core/PolygonGeometry.js b/packages/engine/Source/Core/PolygonGeometry.js index dee6abd84979..1e334f66b30b 100644 --- a/packages/engine/Source/Core/PolygonGeometry.js +++ b/packages/engine/Source/Core/PolygonGeometry.js @@ -636,7 +636,7 @@ function createGeometryFromPositionsExtruded( } let outerRing = hierarchy.outerRing; - let tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); + const tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); let positions2D = tangentPlane.projectPointsOntoPlane( outerRing, createGeometryFromPositionsExtrudedPositions @@ -664,8 +664,6 @@ function createGeometryFromPositionsExtruded( const holes = hierarchy.holes; for (i = 0; i < holes.length; i++) { let hole = holes[i]; - - tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid); positions2D = tangentPlane.projectPointsOntoPlane( hole, createGeometryFromPositionsExtrudedPositions @@ -1331,7 +1329,7 @@ function getTangentPlane(rectangle, positions, ellipsoid) { } const scratchCartographicCyllindrical = new Cartographic(); -function createProjectTo2d(rectangle, ellipsoid) { +function createProjectTo2d(rectangle, outerPositions, ellipsoid) { return (positions, results) => { // If the polygon positions span a large enough extent, use a specialized projection if (rectangle.height >= CesiumMath.PI || rectangle.width >= CesiumMath.PI) { @@ -1360,7 +1358,10 @@ function createProjectTo2d(rectangle, ellipsoid) { } // Use a local tangent plane for smaller extents - const tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); + const tangentPlane = EllipsoidTangentPlane.fromPoints( + outerPositions, + ellipsoid + ); return tangentPlane.projectPointsOntoPlane(positions, results); }; } @@ -1466,7 +1467,7 @@ PolygonGeometry.createGeometry = function (polygonGeometry) { const results = PolygonGeometryLibrary.polygonsFromHierarchy( polygonHierarchy, hasTextureCoordinates, - createProjectTo2d(rectangle, ellipsoid), + createProjectTo2d(rectangle, outerPositions, ellipsoid), !perPositionHeight, ellipsoid, createSplitPolygons(rectangle, ellipsoid, arcType, perPositionHeight) From d9691e954566556a01d541a5c9896461a1f67521 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Mon, 23 Oct 2023 15:54:50 -0400 Subject: [PATCH 03/64] Add Sandcastle for Next-Gen 3D Model Tiling Pipeline --- .../Next-Gen 3D Model Tiling Pipeline.html | 340 ++++++++++++++++++ .../Next-Gen 3D Model Tiling Pipeline.jpg | Bin 0 -> 57553 bytes 2 files changed, 340 insertions(+) create mode 100644 Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html create mode 100644 Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.jpg diff --git a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html new file mode 100644 index 000000000000..a4e42fe3b0b4 --- /dev/null +++ b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html @@ -0,0 +1,340 @@ + + + + + + + + + + Next-Gen 3D Model Tiling Pipeline + + + + + + +
+
+
+
+
---
+
+ Tiles Loaded: --- / + --- +
+ GPU Memory: --- MB +
+
+ +
+
+

Loading...

+
+
+
+ Maximum Screen Space Error + + +
+
+ + + diff --git a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.jpg b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..879dee80dfa008ec521823f31cbe022b933ed180 GIT binary patch literal 57553 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 Dv>GfU literal 0 HcmV?d00001 From 1443acc2ef43cb53e69fc98472fcd21d921b1339 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 24 Oct 2023 09:03:44 -0400 Subject: [PATCH 04/64] Rework sandcastle to allow measuring tile load time --- .../Next-Gen 3D Model Tiling Pipeline.html | 101 +++++++++++++----- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html index a4e42fe3b0b4..3062084e7f31 100644 --- a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html +++ b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html @@ -75,7 +75,8 @@ text-align: right; } - #toolbar { + .benchmarkNotice { + color: #ffee00; }
@@ -84,19 +85,29 @@
---
+ Tile Load Time (s): --- +
Tiles Loaded: --- / ---
GPU Memory: --- MB +
+ Press "Benchmark Current View" to measure stats
@@ -165,7 +176,6 @@ const tilesetNames = getTilesetNames(); let selectedTilesetName = tilesetNames[0]; - let selectedTileset; // Create two primitive collections, one for each half of the screen. This way we // can clear one half of the screen at a time. @@ -178,28 +188,7 @@ // Tileset Loading ================ - async function loadTileset(assetId, splitDirection, collection) { - const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId); - tileset.splitDirection = splitDirection; - - const side = - splitDirection === Cesium.SplitDirection.LEFT ? "left" : "right"; - - collection.removeAll(); - collection.add(tileset); - - viewer.zoomTo(tileset); - - tileset.allTilesLoaded.addEventListener(() => { - updateStatsPanel(side, tileset); - }); - } - - async function viewTileset(tilesetName, splitDirection) { - const side = - splitDirection === Cesium.SplitDirection.LEFT ? "left" : "right"; - clearStatsPanel(side); - + async function loadTileset(tilesetName, splitDirection) { const assetIds = splitDirection === Cesium.SplitDirection.LEFT ? leftAssetIds @@ -215,14 +204,51 @@ return; } - await loadTileset(assetId, splitDirection, collection); + collection.removeAll(); + const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId); + tileset.splitDirection = splitDirection; + + collection.add(tileset); + + return tileset; + } + + async function viewTileset(tilesetName, splitDirection) { + const tileset = await loadTileset(tilesetName, splitDirection); + viewer.zoomTo(tileset); } async function viewTilesets(tilesetName) { + // Load the tilesets simultaneously viewTileset(tilesetName, Cesium.SplitDirection.LEFT); viewTileset(tilesetName, Cesium.SplitDirection.RIGHT); } + async function benchmarkTileset(tilesetName, splitDirection) { + const side = + splitDirection === Cesium.SplitDirection.LEFT ? "left" : "right"; + clearStatsPanel(side); + + const startMilliseconds = performance.now(); + const tileset = await loadTileset(tilesetName, splitDirection); + + return new Promise((resolve) => { + tileset.initialTilesLoaded.addEventListener(() => { + const endMilliseconds = performance.now(); + const deltaSeconds = + (endMilliseconds - startMilliseconds) / 1000.0; + updateStatsPanel(side, tileset, deltaSeconds); + resolve(); + }); + }); + } + + async function benchmarkTilesets(tilesetName) { + // Note: For benchmarking, lo + await benchmarkTileset(tilesetName, Cesium.SplitDirection.LEFT); + await benchmarkTileset(tilesetName, Cesium.SplitDirection.RIGHT); + } + // UI ============================= function createOption(name) { @@ -231,6 +257,11 @@ onselect: function () { selectedTilesetName = name; viewTilesets(name).catch(console.error); + + clearStatsPanel("left"); + addBenchmarkNotice("left"); + clearStatsPanel("right"); + addBenchmarkNotice("right"); }, }; } @@ -241,14 +272,32 @@ } Sandcastle.addToolbarMenu(createOptions(), "toolbarSelect"); + Sandcastle.addToolbarButton( + "Benchmark current view", + async function () { + benchmarkTilesets(selectedTilesetName); + }, + "toolbarSelect" + ); + + function addBenchmarkNotice(side) { + document.getElementById(`${side}BenchmarkNotice`).innerHTML = + "Press 'Benchmark Current View' to measure stats"; + } function clearStatsPanel(side) { + document.getElementById(`${side}TileLoadTime`).innerHTML = "---"; document.getElementById(`${side}TilesLoaded`).innerHTML = "---"; document.getElementById(`${side}TilesTotal`).innerHTML = "---"; document.getElementById(`${side}GpuMemoryMB`).innerHTML = "---"; + document.getElementById(`${side}BenchmarkNotice`).innerHTML = ""; } - function updateStatsPanel(side, tileset) { + function updateStatsPanel(side, tileset, tileLoadTimeSeconds) { + document.getElementById( + `${side}TileLoadTime` + ).innerHTML = tileLoadTimeSeconds.toPrecision(3); + const stats = tileset.statistics; document.getElementById(`${side}TilesLoaded`).innerHTML = stats.numberOfLoadedTilesTotal; From 53d4cf0d8870e713b5ef83f827d4e7de2b698fe1 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 24 Oct 2023 17:11:20 -0400 Subject: [PATCH 05/64] Separate out the tile load time stats updates --- .../Next-Gen 3D Model Tiling Pipeline.html | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html index 3062084e7f31..0cc1ce44b6b5 100644 --- a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html +++ b/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html @@ -85,29 +85,29 @@
---
- Tile Load Time (s): --- -
Tiles Loaded: --- / ---
GPU Memory: --- MB
- Press "Benchmark Current View" to measure stats + +
+ Tile Load Time (s): --- +
@@ -135,16 +135,28 @@ window.startup = async function (Cesium) { "use strict"; //Sandcastle_Begin + + const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider(); + + const updateTerrainFunc = { + "AGI HQ": (viewer) => { + viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain()); + }, + Melbourne: (viewer) => { + viewer.terrainProvider = ellipsoidProvider; + }, + }; + // Tilesets produced by the 3D Model Tiling Pipeline const currentGenAssetIds = { - agiHq: 40866, - melbourne: 69380, + "AGI HQ": 40866, + Melbourne: 69380, }; // Tilesets produced by the Next-Gen 3D Model Tiling Pipeline const nextGenAssetIds = { - agiHq: 2325106, - melbourne: 2325107, + "AGI HQ": 2325106, + Melbourne: 2325107, }; // To make the code more reusable, use "left/right" terminology @@ -166,12 +178,11 @@ } const viewer = new Cesium.Viewer("cesiumContainer", { - globe: false, - skybox: false, - geooder: false, + geocoder: false, sceneModePicker: false, homeButton: false, navigationHelpButton: false, + baseLayerPicker: false, }); const tilesetNames = getTilesetNames(); @@ -204,10 +215,19 @@ return; } + const side = + splitDirection === Cesium.SplitDirection.LEFT ? "left" : "right"; + collection.removeAll(); const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId); tileset.splitDirection = splitDirection; + const updateStatsCallback = (tile) => { + updateStatsPanel(side, tileset); + }; + tileset.tileLoad.addEventListener(updateStatsCallback); + tileset.tileUnload.addEventListener(updateStatsCallback); + collection.add(tileset); return tileset; @@ -237,7 +257,7 @@ const endMilliseconds = performance.now(); const deltaSeconds = (endMilliseconds - startMilliseconds) / 1000.0; - updateStatsPanel(side, tileset, deltaSeconds); + updateLoadTime(side, deltaSeconds); resolve(); }); }); @@ -258,6 +278,8 @@ selectedTilesetName = name; viewTilesets(name).catch(console.error); + updateTerrainFunc[name](viewer); + clearStatsPanel("left"); addBenchmarkNotice("left"); clearStatsPanel("right"); @@ -273,7 +295,7 @@ Sandcastle.addToolbarMenu(createOptions(), "toolbarSelect"); Sandcastle.addToolbarButton( - "Benchmark current view", + "Compute time to load", async function () { benchmarkTilesets(selectedTilesetName); }, @@ -282,22 +304,21 @@ function addBenchmarkNotice(side) { document.getElementById(`${side}BenchmarkNotice`).innerHTML = - "Press 'Benchmark Current View' to measure stats"; + "Press 'Compute time to load' to measure load time"; } function clearStatsPanel(side) { document.getElementById(`${side}TileLoadTime`).innerHTML = "---"; - document.getElementById(`${side}TilesLoaded`).innerHTML = "---"; - document.getElementById(`${side}TilesTotal`).innerHTML = "---"; - document.getElementById(`${side}GpuMemoryMB`).innerHTML = "---"; document.getElementById(`${side}BenchmarkNotice`).innerHTML = ""; } - function updateStatsPanel(side, tileset, tileLoadTimeSeconds) { + function updateLoadTime(side, tileLoadTimeSeconds) { document.getElementById( `${side}TileLoadTime` ).innerHTML = tileLoadTimeSeconds.toPrecision(3); + } + function updateStatsPanel(side, tileset) { const stats = tileset.statistics; document.getElementById(`${side}TilesLoaded`).innerHTML = stats.numberOfLoadedTilesTotal; From 5cb8f0cf851b265a1ee96946d5fcfdd839a81484 Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Thu, 26 Oct 2023 09:55:34 -0400 Subject: [PATCH 06/64] Minimum required attribution for Google 3D Tiles --- CHANGES.md | 1 + .../engine/Source/Scene/createGooglePhotorealistic3DTileset.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 60212357f17d..a5b85292a269 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ - Fixed `czm_normal`, `czm_normal3D`, `czm_inverseNormal`, and `czm_inverseNormal3D` for cases where the model matrix has non-uniform scale. [#11553](https://github.com/CesiumGS/cesium/pull/11553) - Fixed issue with clustered labels when `dataSource.show` was toggled. [#11560](https://github.com/CesiumGS/cesium/pull/11560) - Fixed inconsistant clustering when `dataSource.show` was toggled. [#11560](https://github.com/CesiumGS/cesium/pull/11560) +- By default, `createGooglePhotorealistic3DTileset` no longer shows credits on screen, as this is compliant with the minimum required attribution. To restore this behavior, pass the option `showCreditsOnScreen: true`. ### 1.110.1 - 2023-10-25 diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index d6c708a9d12d..aa678f6a0161 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -41,7 +41,6 @@ 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, From 63bfcdc949df1812f0c658ccd0263ed323d00200 Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Thu, 26 Oct 2023 09:58:24 -0400 Subject: [PATCH 07/64] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a5b85292a269..31893cbc0ede 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,7 +13,7 @@ - Fixed `czm_normal`, `czm_normal3D`, `czm_inverseNormal`, and `czm_inverseNormal3D` for cases where the model matrix has non-uniform scale. [#11553](https://github.com/CesiumGS/cesium/pull/11553) - Fixed issue with clustered labels when `dataSource.show` was toggled. [#11560](https://github.com/CesiumGS/cesium/pull/11560) - Fixed inconsistant clustering when `dataSource.show` was toggled. [#11560](https://github.com/CesiumGS/cesium/pull/11560) -- By default, `createGooglePhotorealistic3DTileset` no longer shows credits on screen, as this is compliant with the minimum required attribution. To restore this behavior, pass the option `showCreditsOnScreen: true`. +- By default, `createGooglePhotorealistic3DTileset` no longer shows credits on screen, as this is compliant with the minimum required attribution. To restore this behavior, pass the option `showCreditsOnScreen: true`. [#11589](https://github.com/CesiumGS/cesium/pull/11589) ### 1.110.1 - 2023-10-25 From 42f68ac0a82794eea4b0d441aea7711c49704f88 Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Fri, 27 Oct 2023 13:58:36 -0400 Subject: [PATCH 08/64] Update S3 CI location and public URLs --- .github/workflows/dev.yml | 12 ++++++------ .github/workflows/prod.yml | 4 ++-- Documentation/Contributors/BuildGuide/README.md | 11 +++++------ gulpfile.js | 3 +-- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 8a02ca44d78e..2280022439d3 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -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,7 @@ 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: npm run deploy-s3 -- -b "cesium-public-builds" -d cesium/$BRANCH -c 'no-cache' --confirm - name: set status if: ${{ env.AWS_ACCESS_KEY_ID != '' }} run: npm run deploy-status -- --status success --message Deployed diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index cdd897e6c41e..41cdeab6dc96 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -27,8 +27,8 @@ jobs: 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 }} diff --git a/Documentation/Contributors/BuildGuide/README.md b/Documentation/Contributors/BuildGuide/README.md index 72518b29a1a2..3502e4899e3e 100644 --- a/Documentation/Contributors/BuildGuide/README.md +++ b/Documentation/Contributors/BuildGuide/README.md @@ -170,23 +170,22 @@ Additional set up is required for deployment if you do not have commit access to ### Configure a Different S3 Bucket -It is possible to configure your development branch of CesiumJS to deploy build artifacts to a different [AWS S3 Bucket](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html). If you are using the cesium-dev bucket and have valid credentials, skip to [Configure S3 Credentials](#configure-s3-credentials) +It is possible to configure your development branch of CesiumJS to deploy build artifacts to a different [AWS S3 Bucket](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html). If you are using the cesium-public-builds bucket and have valid credentials, skip to [Configure S3 Credentials](#configure-s3-credentials) -- In `.gtihub/workflows/dev.yml`, in the following lines, replace "cesium-dev" with the name of your S3 bucket. +- In `.gtihub/workflows/dev.yml`, in the following lines, replace "cesium-public-builds" with the name of your S3 bucket. ```yml -aws s3 sync ./Build/Coverage s3://cesium-dev/cesium/$BRANCH/Build/Coverage --delete --color on +aws s3 sync ./Build/Coverage s3://cesium-public-builds/cesium/$BRANCH/Build/Coverage --delete --color on ``` ```yml -npm run deploy-s3 -- -b "cesium-dev" -d cesium/$BRANCH -c 'no-cache' --confirm +npm run deploy-s3 -- -b "cesium-public-builds" -d cesium/$BRANCH -c 'no-cache' --confirm ``` - In `gulpfile.js`, edit the following line: ```javascript -const devDeployUrl = - "http://cesium-dev.s3-website-us-east-1.amazonaws.com/cesium/"; +const devDeployUrl = "https://ci-builds.cesium.com/cesium/"; ``` - Edit the URL to match the URL of the S3 bucket specified in the previous step. diff --git a/gulpfile.js b/gulpfile.js index bdd55f9d1a66..5cd3d4ea2360 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -61,8 +61,7 @@ if (/\.0$/.test(version)) { } const karmaConfigFile = resolve("./Specs/karma.conf.cjs"); -const devDeployUrl = - "http://cesium-dev.s3-website-us-east-1.amazonaws.com/cesium/"; +const devDeployUrl = "https://ci-builds.cesium.com/cesium/"; const isProduction = process.env.PROD; //Gulp doesn't seem to have a way to get the currently running tasks for setting From 1fff80a29ef7b0e8bbfd52fa25686afbdc6f70ac Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Mon, 30 Oct 2023 09:10:29 -0400 Subject: [PATCH 09/64] Fix deployment url --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 5cd3d4ea2360..a14de9b5b675 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1224,7 +1224,7 @@ export async function deployStatus() { const status = argv.status; const message = argv.message; - const deployUrl = `${devDeployUrl + process.env.BRANCH}/`; + const deployUrl = `${devDeployUrl + process.env.BRANCH}/index.html`; const zipUrl = `${deployUrl}Cesium-${version}.zip`; const npmUrl = `${deployUrl}cesium-${version}.tgz`; const coverageUrl = `${ From 53e6d6a25e971477617dbcc92b957a0af3c18c5d Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Mon, 30 Oct 2023 10:29:53 -0400 Subject: [PATCH 10/64] Node 18/16 --> Node 20/18 --- .github/workflows/dev.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 8a02ca44d78e..ea863b75b0ad 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 @@ -105,10 +105,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 From 48f32b5d7bcad86e4b90257b62e17528ceefa543 Mon Sep 17 00:00:00 2001 From: Gabby Getz Date: Mon, 30 Oct 2023 10:39:34 -0400 Subject: [PATCH 11/64] Update dev dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 70c7e2e9df1c..dd964b8a7e7d 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,8 @@ "@cesium/widgets": "^4.2.0" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.342.0", - "@aws-sdk/lib-storage": "^3.342.0", + "@aws-sdk/client-s3": "^3.438.0", + "@aws-sdk/lib-storage": "^3.438.0", "@playwright/test": "^1.34.3", "chokidar": "^3.5.3", "cloc": "^2.8.0", @@ -105,7 +105,7 @@ "prismjs": "^1.28.0", "request": "^2.79.0", "rimraf": "^5.0.0", - "sinon": "^16.0.0", + "sinon": "^17.0.0", "stream-to-promise": "^3.0.0", "tsd-jsdoc": "^2.5.0", "typescript": "^5.0.2", @@ -167,4 +167,4 @@ "packages/engine", "packages/widgets" ] -} \ No newline at end of file +} From f5f3637254f8b6690e5ac3802a012c0e2bc91ff8 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Mon, 30 Oct 2023 12:28:46 -0400 Subject: [PATCH 12/64] Fix typo --- .../engine/Source/Core/EllipsoidRhumbLine.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/engine/Source/Core/EllipsoidRhumbLine.js b/packages/engine/Source/Core/EllipsoidRhumbLine.js index d29623bb3063..191d6916abe2 100644 --- a/packages/engine/Source/Core/EllipsoidRhumbLine.js +++ b/packages/engine/Source/Core/EllipsoidRhumbLine.js @@ -335,14 +335,14 @@ function interpolateUsingSurfaceDistance( //Now find the longitude of the second point // Check to see if the rhumb line has constant longitude - if (Math.abs(Math.abs(heading)) < CesiumMath.EPSILON10) { - longitude = CesiumMath.negativePiToPi(start.longitude); - } else { - const sigma1 = calculateSigma(ellipticity, start.latitude); - const sigma2 = calculateSigma(ellipticity, latitude); - deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); - longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); - } + //if (Math.abs(heading) < CesiumMath.EPSILON10) { + // longitude = CesiumMath.negativePiToPi(start.longitude); + //} else { + const sigma1 = calculateSigma(ellipticity, start.latitude); + const sigma2 = calculateSigma(ellipticity, latitude); + deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + //} } else { //If heading is close to 90 degrees latitude = start.latitude; From 061c47ec3cc07b8a072677ea92f0b0ebbd801bb5 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Mon, 30 Oct 2023 12:29:50 -0400 Subject: [PATCH 13/64] Fix typo in typo fix --- .../engine/Source/Core/EllipsoidRhumbLine.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/engine/Source/Core/EllipsoidRhumbLine.js b/packages/engine/Source/Core/EllipsoidRhumbLine.js index 191d6916abe2..51b410622677 100644 --- a/packages/engine/Source/Core/EllipsoidRhumbLine.js +++ b/packages/engine/Source/Core/EllipsoidRhumbLine.js @@ -335,14 +335,14 @@ function interpolateUsingSurfaceDistance( //Now find the longitude of the second point // Check to see if the rhumb line has constant longitude - //if (Math.abs(heading) < CesiumMath.EPSILON10) { - // longitude = CesiumMath.negativePiToPi(start.longitude); - //} else { - const sigma1 = calculateSigma(ellipticity, start.latitude); - const sigma2 = calculateSigma(ellipticity, latitude); - deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); - longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); - //} + if (Math.abs(heading) < CesiumMath.EPSILON10) { + longitude = CesiumMath.negativePiToPi(start.longitude); + } else { + const sigma1 = calculateSigma(ellipticity, start.latitude); + const sigma2 = calculateSigma(ellipticity, latitude); + deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } } else { //If heading is close to 90 degrees latitude = start.latitude; From 31865ffcf34d7d612a04b599f2fa95736cf5dea0 Mon Sep 17 00:00:00 2001 From: Peter Gagliardi Date: Tue, 31 Oct 2023 13:43:31 -0400 Subject: [PATCH 14/64] Update naming conventions --- ...eline.html => Cesium ion Reality Tiler.html} | 6 +++--- .../gallery/Cesium ion Reality Tiler.jpg | Bin 0 -> 59647 bytes .../Next-Gen 3D Model Tiling Pipeline.jpg | Bin 57553 -> 0 bytes 3 files changed, 3 insertions(+), 3 deletions(-) rename Apps/Sandcastle/gallery/{Next-Gen 3D Model Tiling Pipeline.html => Cesium ion Reality Tiler.html} (98%) create mode 100644 Apps/Sandcastle/gallery/Cesium ion Reality Tiler.jpg delete mode 100644 Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.jpg diff --git a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html b/Apps/Sandcastle/gallery/Cesium ion Reality Tiler.html similarity index 98% rename from Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html rename to Apps/Sandcastle/gallery/Cesium ion Reality Tiler.html index 0cc1ce44b6b5..b8e576ffc192 100644 --- a/Apps/Sandcastle/gallery/Next-Gen 3D Model Tiling Pipeline.html +++ b/Apps/Sandcastle/gallery/Cesium ion Reality Tiler.html @@ -13,7 +13,7 @@ content="Comparison of tilesets from Cesium ion's current and next-gen 3D model tiling pipelines to showcase GPU memory improvements." /> - Next-Gen 3D Model Tiling Pipeline + Cesium ion Reality Tiler