cesium/Apps/Sandcastle/gallery/Voxels.html

341 lines
13 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta
name="description"
content="Render voxel primitives with different shape types."
/>
<meta name="cesium-sandcastle-labels" content="Showcases, Voxels" />
<title>Cesium Demo</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);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.TileMapServiceImageryProvider.fromUrl(
Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"),
),
),
baseLayerPicker: false,
geocoder: false,
animation: false,
timeline: false,
projectionPicker: true,
sceneModePicker: false,
});
viewer.extend(Cesium.viewerVoxelInspectorMixin);
viewer.scene.debugShowFramesPerSecond = true;
const globalTransform = Cesium.Matrix4.fromScale(
Cesium.Cartesian3.fromElements(
Cesium.Ellipsoid.WGS84.maximumRadius,
Cesium.Ellipsoid.WGS84.maximumRadius,
Cesium.Ellipsoid.WGS84.maximumRadius,
),
);
function ProceduralSingleTileVoxelProvider(shape) {
this.shape = shape;
this.minBounds = Cesium.VoxelShapeType.getMinBounds(shape).clone();
this.maxBounds = Cesium.VoxelShapeType.getMaxBounds(shape).clone();
this.dimensions = new Cesium.Cartesian3(8, 8, 8);
this.names = ["color"];
this.types = [Cesium.MetadataType.VEC4];
this.componentTypes = [Cesium.MetadataComponentType.FLOAT32];
this.globalTransform = globalTransform;
}
const scratchColor = new Cesium.Color();
ProceduralSingleTileVoxelProvider.prototype.requestData = function (options) {
if (options.tileLevel >= 1) {
return Promise.reject(`No tiles available beyond level 0`);
}
const dimensions = this.dimensions;
const voxelCount = dimensions.x * dimensions.y * dimensions.z;
const type = this.types[0];
const channelCount = Cesium.MetadataType.getComponentCount(type);
const dataColor = new Float32Array(voxelCount * channelCount);
const randomSeed = dimensions.y * dimensions.x + dimensions.x;
Cesium.Math.setRandomNumberSeed(randomSeed);
const hue = Cesium.Math.nextRandomNumber();
for (let z = 0; z < dimensions.z; z++) {
for (let y = 0; y < dimensions.y; y++) {
const indexZY = z * dimensions.y * dimensions.x + y * dimensions.x;
for (let x = 0; x < dimensions.x; x++) {
const lerperX = x / (dimensions.x - 1);
const lerperY = y / (dimensions.y - 1);
const lerperZ = z / (dimensions.z - 1);
const h = hue + lerperX * 0.5 - lerperY * 0.3 + lerperZ * 0.2;
const s = 1.0 - lerperY * 0.2;
const v = 0.5 + 2.0 * (lerperZ - 0.5) * 0.2;
const color = Cesium.Color.fromHsl(h, s, v, 1.0, scratchColor);
const index = (indexZY + x) * channelCount;
dataColor[index + 0] = color.red;
dataColor[index + 1] = color.green;
dataColor[index + 2] = color.blue;
dataColor[index + 3] = 0.75;
}
}
}
const content = Cesium.VoxelContent.fromMetadataArray([dataColor]);
return Promise.resolve(content);
};
function ProceduralMultiTileVoxelProvider(shape) {
this.shape = shape;
this.minBounds = Cesium.VoxelShapeType.getMinBounds(shape).clone();
this.maxBounds = Cesium.VoxelShapeType.getMaxBounds(shape).clone();
this.dimensions = new Cesium.Cartesian3(4, 4, 4);
this.paddingBefore = new Cesium.Cartesian3(1, 1, 1);
this.paddingAfter = new Cesium.Cartesian3(1, 1, 1);
this.names = ["color"];
this.types = [Cesium.MetadataType.VEC4];
this.componentTypes = [Cesium.MetadataComponentType.FLOAT32];
this.globalTransform = globalTransform;
this.availableLevels = 2;
this._allVoxelData = new Array(this.availableLevels);
const allVoxelData = this._allVoxelData;
const channelCount = Cesium.MetadataType.getComponentCount(this.types[0]);
const { dimensions } = this;
for (let level = 0; level < this.availableLevels; level++) {
const dimAtLevel = Math.pow(2, level);
const voxelCountX = dimensions.x * dimAtLevel;
const voxelCountY = dimensions.y * dimAtLevel;
const voxelCountZ = dimensions.z * dimAtLevel;
const voxelsPerLevel = voxelCountX * voxelCountY * voxelCountZ;
const levelData = (allVoxelData[level] = new Array(
voxelsPerLevel * channelCount,
));
for (let z = 0; z < voxelCountX; z++) {
for (let y = 0; y < voxelCountY; y++) {
const indexZY = z * voxelCountY * voxelCountX + y * voxelCountX;
for (let x = 0; x < voxelCountZ; x++) {
const index = (indexZY + x) * channelCount;
levelData[index + 0] = x / (voxelCountX - 1);
levelData[index + 1] = y / (voxelCountY - 1);
levelData[index + 2] = z / (voxelCountZ - 1);
levelData[index + 3] = 0.5;
}
}
}
}
}
ProceduralMultiTileVoxelProvider.prototype.requestData = function (options) {
const { tileLevel, tileX, tileY, tileZ } = options;
if (tileLevel >= this.availableLevels) {
return Promise.reject(
`No tiles available beyond level ${this.availableLevels - 1}`,
);
}
const type = this.types[0];
const channelCount = Cesium.MetadataType.getComponentCount(type);
const { dimensions, paddingBefore, paddingAfter } = this;
const paddedDimensions = Cesium.Cartesian3.fromElements(
dimensions.x + paddingBefore.x + paddingAfter.x,
dimensions.y + paddingBefore.y + paddingAfter.y,
dimensions.z + paddingBefore.z + paddingAfter.z,
);
const dimAtLevel = Math.pow(2, tileLevel);
const dimensionsGlobal = Cesium.Cartesian3.fromElements(
dimensions.x * dimAtLevel,
dimensions.y * dimAtLevel,
dimensions.z * dimAtLevel,
);
const minimumGlobalCoord = Cesium.Cartesian3.ZERO;
const maximumGlobalCoord = new Cesium.Cartesian3(
dimensionsGlobal.x - 1,
dimensionsGlobal.y - 1,
dimensionsGlobal.z - 1,
);
let coordGlobal = new Cesium.Cartesian3();
const dataGlobal = this._allVoxelData;
const dataTile = new Float32Array(
paddedDimensions.x * paddedDimensions.y * paddedDimensions.z * channelCount,
);
for (let z = 0; z < paddedDimensions.z; z++) {
const indexZ = z * paddedDimensions.y * paddedDimensions.x;
for (let y = 0; y < paddedDimensions.y; y++) {
const indexZY = indexZ + y * paddedDimensions.x;
for (let x = 0; x < paddedDimensions.x; x++) {
const indexTile = indexZY + x;
coordGlobal = Cesium.Cartesian3.clamp(
Cesium.Cartesian3.fromElements(
tileX * dimensions.x + (x - paddingBefore.x),
tileY * dimensions.y + (y - paddingBefore.y),
tileZ * dimensions.z + (z - paddingBefore.z),
coordGlobal,
),
minimumGlobalCoord,
maximumGlobalCoord,
coordGlobal,
);
const indexGlobal =
coordGlobal.z * dimensionsGlobal.y * dimensionsGlobal.x +
coordGlobal.y * dimensionsGlobal.x +
coordGlobal.x;
for (let c = 0; c < channelCount; c++) {
dataTile[indexTile * channelCount + c] =
dataGlobal[tileLevel][indexGlobal * channelCount + c];
}
}
}
}
const content = Cesium.VoxelContent.fromMetadataArray([dataTile]);
return Promise.resolve(content);
};
function createPrimitive(provider, customShader) {
viewer.scene.primitives.removeAll();
const voxelPrimitive = viewer.scene.primitives.add(
new Cesium.VoxelPrimitive({
provider: provider,
customShader: customShader,
}),
);
viewer.voxelInspector.viewModel.voxelPrimitive = voxelPrimitive;
viewer.camera.flyToBoundingSphere(voxelPrimitive.boundingSphere, {
duration: 0.0,
});
return voxelPrimitive;
}
const customShaderColor = new Cesium.CustomShader({
fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
{
material.diffuse = fsInput.metadata.color.rgb;
float transparency = 1.0 - fsInput.metadata.color.a;
// To mimic light scattering, use exponential decay
float thickness = fsInput.voxel.travelDistance * 16.0;
material.alpha = 1.0 - pow(transparency, thickness);
}`,
});
Sandcastle.addToolbarMenu([
{
text: "Ellipsoid - Procedural Tile",
onselect: function () {
const provider = new ProceduralSingleTileVoxelProvider(
Cesium.VoxelShapeType.ELLIPSOID,
);
provider.minBounds.z = 0.0;
provider.maxBounds.z = 1000000.0;
const primitive = createPrimitive(provider, customShaderColor);
},
},
{
text: "Cylinder - Procedural Tile",
onselect: function () {
const provider = new ProceduralSingleTileVoxelProvider(
Cesium.VoxelShapeType.CYLINDER,
);
const primitive = createPrimitive(provider, customShaderColor);
},
},
{
text: "Box - Procedural Tile",
onselect: function () {
const provider = new ProceduralSingleTileVoxelProvider(
Cesium.VoxelShapeType.BOX,
);
const primitive = createPrimitive(provider, customShaderColor);
},
},
{
text: "Box - Procedural Tileset",
onselect: function () {
const provider = new ProceduralMultiTileVoxelProvider(
Cesium.VoxelShapeType.BOX,
);
const primitive = createPrimitive(provider, customShaderColor);
},
},
{
text: "Ellipsoid - Procedural Tileset",
onselect: function () {
const provider = new ProceduralMultiTileVoxelProvider(
Cesium.VoxelShapeType.ELLIPSOID,
);
provider.minBounds.z = 0.0;
provider.maxBounds.z = 1000000.0;
const primitive = createPrimitive(provider, customShaderColor);
},
},
{
text: "Cylinder - Procedural Tileset",
onselect: function () {
const provider = new ProceduralMultiTileVoxelProvider(
Cesium.VoxelShapeType.CYLINDER,
);
const primitive = createPrimitive(provider, customShaderColor);
},
},
]);
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
const scene = viewer.scene;
const camera = scene.camera;
const mousePosition = movement.position;
const pickedPrimitive = scene.pick(mousePosition);
console.log(pickedPrimitive);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
Sandcastle.finishedLoading();
}
</script>
</body>
</html>