cesium/Apps/Sandcastle/gallery/3D Tiles 1.1 Photogrammetry...

404 lines
14 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<!-- Use Chrome Frame in IE -->
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta
name="description"
content="Performance comparison of photogrammetry using 3D Tiles 1.0 vs 3D Tiles 1.1 with KTX2 texture compression. Most notably, KTX2 textures provide significant GPU memory savings"
/>
<meta name="cesium-sandcastle-labels" content="3D Tiles" />
<title>3D Tiles 1.1 Photogrammetry</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script
type="text/javascript"
src="../../../Build/CesiumUnminified/Cesium.js"
nomodule
></script>
<script type="module" src="../load-cesium-es6.js"></script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
#slider {
position: absolute;
left: 50%;
top: 0px;
background-color: #d3d3d3;
width: 5px;
height: 100%;
z-index: 9999;
}
#slider:hover {
cursor: ew-resize;
}
#statsContainer {
position: absolute;
top: 15%;
width: 100%;
}
.panel {
background-color: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
font-size: 14px;
}
.statsPane {
position: relative;
z-index: 9998;
}
.statsTitle {
font-size: 20px;
}
#left {
float: left;
text-align: left;
}
#right {
float: right;
text-align: right;
}
.benchmarkNotice {
color: #ffee00;
}
</style>
<div id="cesiumContainer" class="fullSize">
<div id="slider"></div>
<div id="statsContainer">
<div id="left" class="statsPane panel">
<div id="leftTitle" class="statsTitle">3D Tiles 1.0 (JPEG textures)</div>
<div id="leftStats">
Tiles Loaded: <span id="leftTilesLoaded">---</span> /
<span id="leftTilesTotal">---</span>
<br />
GPU Memory: <span id="leftGpuMemoryMB">---</span> MB
<br />
<span id="leftBenchmarkNotice" class="benchmarkNotice"></span>
<br />
Tile Load Time (s): <span id="leftTileLoadTime">---</span>
<br />
</div>
</div>
<div id="right" class="statsPane panel">
<div id="rightTitle" class="statsTitle">3D Tiles 1.1 (KTX2 textures)</div>
<div id="rightStats">
Tiles Loaded: <span id="rightTilesLoaded">---</span> /
<span id="rightTilesTotal">---</span>
<br />
GPU Memory: <span id="rightGpuMemoryMB">---</span> MB
<br />
<span id="rightBenchmarkNotice" class="benchmarkNotice"></span>
<br />
Tile Load Time (s): <span id="rightTileLoadTime">---</span>
<br />
</div>
</div>
</div>
</div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar" class="panel">
<div id="toolbarSelect"></div>
<div id="maxSSE">
Maximum Screen Space Error
<input
type="range"
min="0.0"
max="64.0"
step="0.1"
data-bind="value: maximumScreenSpaceError, valueUpdate: 'input'"
/>
<input type="text" size="5" data-bind="value: maximumScreenSpaceError" />
</div>
</div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
sceneModePicker: false,
homeButton: false,
navigationHelpButton: false,
baseLayerPicker: false,
});
// Asset Lookup tables ================================================
// Left half of the screen:
// Tilesets produced by the Cesium ion 3D Model Tiler
const leftAssetIds = {
"AGI HQ": 40866,
Melbourne: 69380,
};
// Right half of the screen:
// Tilesets produced by the Cesium ion Reality Tiler
const rightAssetIds = {
"AGI HQ": 2325106,
Melbourne: 2325107,
};
const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider();
// AGI HQ looks better with Cesium World Terrain, but Melbourne looks
// better using the ellipsoid terrain.
const updateTerrainFunc = {
"AGI HQ": (viewer) => {
viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain());
},
Melbourne: (viewer) => {
viewer.terrainProvider = ellipsoidProvider;
},
};
// List of tileset names for creating options and indexing into the
// lookup tables above.
const tilesetNames = ["AGI HQ", "Melbourne"];
// Tileset Loading ====================================================
// Create two primitive collections, one for each half of the screen.
// This way we can clear one half of the screen at a time.
const leftCollection = viewer.scene.primitives.add(
new Cesium.PrimitiveCollection(),
);
const rightCollection = viewer.scene.primitives.add(
new Cesium.PrimitiveCollection(),
);
// Load a tileset to one half of the screen, returning the tileset
async function loadTileset(tilesetName, splitDirection) {
const isLeft = splitDirection === Cesium.SplitDirection.LEFT;
const assetIds = isLeft ? leftAssetIds : rightAssetIds;
const collection = isLeft ? leftCollection : rightCollection;
const assetId = assetIds[tilesetName];
if (!Cesium.defined(assetId)) {
collection.removeAll();
return;
}
const side = splitDirection === Cesium.SplitDirection.LEFT ? "left" : "right";
collection.removeAll();
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId);
tileset.splitDirection = splitDirection;
// Whenever a tile loads/unloads, update the stats about GPU memory
// and tile count. Load time is handled separately for better
// accuracy.
const updateStatsCallback = (tile) => {
updateStatsPanel(side, tileset);
};
tileset.tileLoad.addEventListener(updateStatsCallback);
tileset.tileUnload.addEventListener(updateStatsCallback);
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;
updateLoadTime(side, deltaSeconds);
resolve();
});
});
}
async function benchmarkTilesets(tilesetName) {
// Note: For benchmarking, load tilesets one at a time so the loading
// of one tileset doesn't delay the loading of the other.
await benchmarkTileset(tilesetName, Cesium.SplitDirection.LEFT);
await benchmarkTileset(tilesetName, Cesium.SplitDirection.RIGHT);
}
// UI =================================================================
// Tileset dropdown ---------------------------------------------------
// The first tileset in the dropdown will automatically be selected.
let selectedTilesetName = tilesetNames[0];
function createOption(name) {
return {
text: name,
onselect: function () {
selectedTilesetName = name;
viewTilesets(name).catch(console.error);
updateTerrainFunc[name](viewer);
clearStatsPanel("left");
addBenchmarkNotice("left");
clearStatsPanel("right");
addBenchmarkNotice("right");
},
};
}
function createOptions() {
const options = tilesetNames.map(createOption);
return options;
}
Sandcastle.addToolbarMenu(createOptions(), "toolbarSelect");
// Compute load time -------------------------------------------------
// For better accuracy, this button reloads the tilesets one by one
// so the load time of one tileset doesn't affect the other
Sandcastle.addToolbarButton(
"Compute time to load",
async function () {
benchmarkTilesets(selectedTilesetName);
},
"toolbarSelect",
);
// A note to the user that load time requires a button press
function addBenchmarkNotice(side) {
document.getElementById(`${side}BenchmarkNotice`).innerHTML =
"Press 'Compute time to load' to measure load time";
}
// Stats panels -------------------------------------------------------
function clearStatsPanel(side) {
document.getElementById(`${side}TileLoadTime`).innerHTML = "---";
document.getElementById(`${side}BenchmarkNotice`).innerHTML = "";
}
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;
document.getElementById(`${side}TilesTotal`).innerHTML =
stats.numberOfTilesTotal;
const gpuMemoryBytes = stats.geometryByteLength + stats.texturesByteLength;
const gpuMemoryMB = gpuMemoryBytes / 1024 / 1024;
document.getElementById(`${side}GpuMemoryMB`).innerHTML =
gpuMemoryMB.toPrecision(3);
}
// maximum SSE Slider -------------------------------------------------
const viewModel = {
maximumScreenSpaceError: 16.0,
};
Cesium.knockout.track(viewModel);
const toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);
Cesium.knockout
.getObservable(viewModel, "maximumScreenSpaceError")
.subscribe((value) => {
const valueFloat = parseFloat(value);
if (leftCollection.length > 0) {
const leftTileset = leftCollection.get(0);
leftTileset.maximumScreenSpaceError = valueFloat;
}
if (rightCollection.length > 0) {
const rightTileset = rightCollection.get(0);
rightTileset.maximumScreenSpaceError = valueFloat;
}
});
// Splitter ----------------------------------------------------------
// This code is the same as in the 3D Tiles Compare Sandcastle.
// Sync the position of the slider with the split position
const slider = document.getElementById("slider");
viewer.scene.splitPosition = slider.offsetLeft / slider.parentElement.offsetWidth;
const handler = new Cesium.ScreenSpaceEventHandler(slider);
let moveActive = false;
function move(movement) {
if (!moveActive) {
return;
}
const relativeOffset = movement.endPosition.x;
const splitPosition =
(slider.offsetLeft + relativeOffset) / slider.parentElement.offsetWidth;
slider.style.left = `${100.0 * splitPosition}%`;
viewer.scene.splitPosition = splitPosition;
}
handler.setInputAction(function () {
moveActive = true;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(function () {
moveActive = true;
}, Cesium.ScreenSpaceEventType.PINCH_START);
handler.setInputAction(move, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(move, Cesium.ScreenSpaceEventType.PINCH_MOVE);
handler.setInputAction(function () {
moveActive = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
handler.setInputAction(function () {
moveActive = false;
}, Cesium.ScreenSpaceEventType.PINCH_END);
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
Sandcastle.finishedLoading();
}
</script>
</body>
</html>