mirror of https://github.com/CesiumGS/cesium.git
404 lines
14 KiB
HTML
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>
|