cesium/Specs/Cesium3DTilesTester.js

503 lines
16 KiB
JavaScript

import {
Cartesian3,
Color,
Frozen,
defined,
JulianDate,
ImageBasedLighting,
Resource,
Cesium3DTileContentFactory,
Cesium3DTileset,
TileBoundingSphere,
} from "@cesium/engine";
import pollToPromise from "./pollToPromise.js";
const mockTile = {
contentBoundingVolume: new TileBoundingSphere(),
_contentBoundingVolume: new TileBoundingSphere(),
_header: {
content: {
boundingVolume: {
sphere: [0.0, 0.0, 0.0, 1.0],
},
},
},
};
function Cesium3DTilesTester() {}
function padStringToByteAlignment(string, byteAlignment) {
const length = string.length;
const paddedLength = Math.ceil(length / byteAlignment) * byteAlignment; // Round up to the required alignment
const padding = paddedLength - length;
let whitespace = "";
for (let i = 0; i < padding; ++i) {
whitespace += " ";
}
return string + whitespace;
}
const time = new JulianDate(2457522.0);
Cesium3DTilesTester.expectRender = function (scene, tileset, callback) {
const renderOptions = {
scene: scene,
time: time,
};
tileset.show = false;
expect(renderOptions).toRender([0, 0, 0, 255]);
tileset.show = true;
expect(renderOptions).toRenderAndCall(function (rgba) {
expect(rgba).not.toEqual([0, 0, 0, 255]);
if (defined(callback)) {
callback(rgba);
}
});
};
Cesium3DTilesTester.expectRenderBlank = function (scene, tileset) {
const renderOptions = {
scene: scene,
time: time,
};
tileset.show = false;
expect(renderOptions).toRender([0, 0, 0, 255]);
tileset.show = true;
expect(renderOptions).toRender([0, 0, 0, 255]);
};
Cesium3DTilesTester.expectRenderTileset = function (scene, tileset) {
// Verify render before being picked
Cesium3DTilesTester.expectRender(scene, tileset);
// Pick a feature
expect(scene).toPickAndCall(function (result) {
expect(result).toBeDefined();
// Change the color of the picked feature to yellow
result.color = Color.clone(Color.YELLOW, result.color);
// Expect the pixel color to be some shade of yellow
Cesium3DTilesTester.expectRender(scene, tileset, function (rgba) {
expect(rgba[0]).toBeGreaterThan(0);
expect(rgba[1]).toBeGreaterThan(0);
expect(rgba[2]).toEqual(0);
expect(rgba[3]).toEqual(255);
});
// Turn show off and on
result.show = false;
Cesium3DTilesTester.expectRenderBlank(scene, tileset);
result.show = true;
Cesium3DTilesTester.expectRender(scene, tileset);
});
};
Cesium3DTilesTester.waitForTilesLoaded = function (scene, tileset) {
return pollToPromise(function () {
scene.renderForSpecs();
return tileset.tilesLoaded;
}).then(function () {
scene.renderForSpecs();
return tileset;
});
};
Cesium3DTilesTester.waitForTileContent = function (scene, tile) {
return pollToPromise(function () {
scene.renderForSpecs();
return !tile.contentUnloaded;
}).then(function () {
scene.renderForSpecs();
return tile;
});
};
Cesium3DTilesTester.waitForTileContentReady = function (scene, tile) {
return pollToPromise(function () {
scene.renderForSpecs();
return tile.contentReady;
}).then(function () {
scene.renderForSpecs();
return tile;
});
};
// A white ambient light with low intensity
const defaultIbl = new ImageBasedLighting({
sphericalHarmonicCoefficients: [
new Cartesian3(0.4, 0.4, 0.4),
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
],
});
Cesium3DTilesTester.loadTileset = async function (scene, url, options) {
options = options ?? {};
options.cullRequestsWhileMoving = options.cullRequestsWhileMoving ?? false;
options.imageBasedLighting = options.imageBasedLighting ?? defaultIbl;
options.environmentMapOptions = {
enabled: false, // disable other diffuse lighting by default
...options.environmentMapOptions,
};
const tileset = await Cesium3DTileset.fromUrl(url, options);
// Load all visible tiles
scene.primitives.add(tileset);
await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset);
return tileset;
};
Cesium3DTilesTester.createContentForMockTile = async function (
arrayBuffer,
type,
) {
const tileset = {};
const url = Resource.createIfNeeded("");
return Cesium3DTileContentFactory[type](
tileset,
mockTile,
url,
arrayBuffer,
0,
);
};
Cesium3DTilesTester.tileDestroys = function (scene, url, options) {
return Cesium3DTilesTester.loadTileset(scene, url, options).then(
function (tileset) {
const content = tileset.root.content;
expect(content.isDestroyed()).toEqual(false);
scene.primitives.remove(tileset);
expect(content.isDestroyed()).toEqual(true);
},
);
};
Cesium3DTilesTester.generateBatchedTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [98, 51, 100, 109];
const version = options.version ?? 1;
const featuresLength = options.featuresLength ?? 1;
const featureTableJson = {
BATCH_LENGTH: featuresLength,
};
const featureTableJsonString = JSON.stringify(featureTableJson);
const featureTableJsonByteLength = featureTableJsonString.length;
const headerByteLength = 28;
const byteLength = headerByteLength + featureTableJsonByteLength;
const buffer = new ArrayBuffer(byteLength);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true); // version
view.setUint32(8, byteLength, true); // byteLength
view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength
view.setUint32(16, 0, true); // featureTableBinaryByteLength
view.setUint32(20, 0, true); // batchTableJsonByteLength
view.setUint32(24, 0, true); // batchTableBinaryByteLength
let i;
let byteOffset = headerByteLength;
for (i = 0; i < featureTableJsonByteLength; i++) {
view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i));
byteOffset++;
}
return buffer;
};
Cesium3DTilesTester.generateInstancedTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [105, 51, 100, 109];
const version = options.version ?? 1;
const gltfFormat = options.gltfFormat ?? 1;
const gltfUri = options.gltfUri ?? "model.gltf";
const gltfUriByteLength = gltfUri.length;
const featureTableJson = options.featureTableJson;
let featureTableJsonString = "";
if (defined(featureTableJson)) {
if (Object.keys(featureTableJson).length > 0) {
featureTableJsonString = JSON.stringify(featureTableJson);
}
} else {
const featuresLength = options.featuresLength ?? 1;
featureTableJsonString = JSON.stringify({
INSTANCES_LENGTH: featuresLength,
POSITION: new Array(featuresLength * 3).fill(0),
});
}
featureTableJsonString = padStringToByteAlignment(featureTableJsonString, 8);
const featureTableJsonByteLength = featureTableJsonString.length;
const featureTableBinary = options.featureTableBinary ?? new Uint8Array(0);
const featureTableBinaryByteLength = featureTableBinary.length;
const batchTableJson = options.batchTableJson;
let batchTableJsonString = "";
if (defined(batchTableJson) && Object.keys(batchTableJson).length > 0) {
batchTableJsonString = JSON.stringify(batchTableJson);
}
batchTableJsonString = padStringToByteAlignment(batchTableJsonString, 8);
const batchTableJsonByteLength = batchTableJsonString.length;
const batchTableBinary = options.batchTableBinary ?? new Uint8Array(0);
const batchTableBinaryByteLength = batchTableBinary.length;
const headerByteLength = 32;
const byteLength =
headerByteLength +
featureTableJsonByteLength +
featureTableBinaryByteLength +
batchTableJsonByteLength +
batchTableBinaryByteLength +
gltfUriByteLength;
const buffer = new ArrayBuffer(byteLength);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true);
view.setUint32(8, byteLength, true);
view.setUint32(12, featureTableJsonByteLength, true);
view.setUint32(16, featureTableBinaryByteLength, true);
view.setUint32(20, batchTableJsonByteLength, true);
view.setUint32(24, batchTableBinaryByteLength, true);
view.setUint32(28, gltfFormat, true);
let i;
let byteOffset = headerByteLength;
for (i = 0; i < featureTableJsonByteLength; i++) {
view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i));
byteOffset++;
}
for (i = 0; i < featureTableBinaryByteLength; i++) {
view.setUint8(byteOffset, featureTableBinary[i]);
byteOffset++;
}
for (i = 0; i < batchTableJsonByteLength; i++) {
view.setUint8(byteOffset, batchTableJsonString.charCodeAt(i));
byteOffset++;
}
for (i = 0; i < batchTableBinaryByteLength; i++) {
view.setUint8(byteOffset, batchTableBinary[i]);
byteOffset++;
}
for (i = 0; i < gltfUriByteLength; i++) {
view.setUint8(byteOffset, gltfUri.charCodeAt(i));
byteOffset++;
}
return buffer;
};
Cesium3DTilesTester.generatePointCloudTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [112, 110, 116, 115];
const version = options.version ?? 1;
let featureTableJson = options.featureTableJson;
if (!defined(featureTableJson)) {
featureTableJson = {
POINTS_LENGTH: 1,
POSITIONS: {
byteOffset: 0,
},
};
}
let featureTableJsonString = JSON.stringify(featureTableJson);
featureTableJsonString = padStringToByteAlignment(featureTableJsonString, 4);
const featureTableJsonByteLength =
options.featureTableJsonByteLength ?? featureTableJsonString.length;
const featureTableBinary = new ArrayBuffer(12); // Enough space to hold 3 floats
const featureTableBinaryByteLength = featureTableBinary.byteLength;
const headerByteLength = 28;
const byteLength =
headerByteLength +
featureTableJsonByteLength +
featureTableBinaryByteLength;
const buffer = new ArrayBuffer(byteLength);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true); // version
view.setUint32(8, byteLength, true); // byteLength
view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength
view.setUint32(16, featureTableBinaryByteLength, true); // featureTableBinaryByteLength
view.setUint32(20, 0, true); // batchTableJsonByteLength
view.setUint32(24, 0, true); // batchTableBinaryByteLength
let i;
let byteOffset = headerByteLength;
for (i = 0; i < featureTableJsonByteLength; i++) {
view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i));
byteOffset++;
}
for (i = 0; i < featureTableBinaryByteLength; i++) {
view.setUint8(byteOffset, featureTableBinary[i]);
byteOffset++;
}
return buffer;
};
Cesium3DTilesTester.generateCompositeTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [99, 109, 112, 116];
const version = options.version ?? 1;
const tiles = options.tiles ?? Frozen.EMPTY_ARRAY;
const tilesLength = tiles.length;
let i;
let tilesByteLength = 0;
for (i = 0; i < tilesLength; ++i) {
tilesByteLength += tiles[i].byteLength;
}
const headerByteLength = 16;
const byteLength = headerByteLength + tilesByteLength;
const buffer = new ArrayBuffer(byteLength);
const uint8Array = new Uint8Array(buffer);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true); // version
view.setUint32(8, byteLength, true); // byteLength
view.setUint32(12, tilesLength, true); // tilesLength
let byteOffset = headerByteLength;
for (i = 0; i < tilesLength; ++i) {
const tile = new Uint8Array(tiles[i]);
uint8Array.set(tile, byteOffset);
byteOffset += tile.byteLength;
}
return buffer;
};
Cesium3DTilesTester.generateVectorTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [118, 99, 116, 114];
const version = options.version ?? 1;
let featureTableJsonString;
let featureTableJsonByteLength = 0;
const defineFeatureTable = options.defineFeatureTable ?? true;
if (defineFeatureTable) {
const defineRegion = options.defineRegion ?? true;
const featureTableJson = {
REGION: defineRegion ? [-1.0, -1.0, 1.0, 1.0, -1.0, 1.0] : undefined,
POLYGONS_LENGTH: options.polygonsLength ?? 0,
POLYLINES_LENGTH: options.polylinesLength ?? 0,
POINTS_LENGTH: options.pointsLength ?? 0,
POLYGON_BATCH_IDS: options.polygonBatchIds,
POLYLINE_BATCH_IDS: options.polylineBatchIds,
POINT_BATCH_IDS: options.pointBatchIds,
};
featureTableJsonString = JSON.stringify(featureTableJson);
featureTableJsonByteLength = featureTableJsonString.length;
}
const headerByteLength = 44;
const byteLength = headerByteLength + featureTableJsonByteLength;
const buffer = new ArrayBuffer(byteLength);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true); // version
view.setUint32(8, byteLength, true); // byteLength
view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength
view.setUint32(16, 0, true); // featureTableBinaryByteLength
view.setUint32(20, 0, true); // batchTableJsonByteLength
view.setUint32(24, 0, true); // batchTableBinaryByteLength
view.setUint32(28, 0, true); // indicesByteLength
view.setUint32(32, 0, true); // polygonPositionByteLength
view.setUint32(36, 0, true); // polylinePositionByteLength
view.setUint32(40, 0, true); // pointsPositionByteLength
let i;
let byteOffset = headerByteLength;
for (i = 0; i < featureTableJsonByteLength; i++) {
view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i));
byteOffset++;
}
return buffer;
};
Cesium3DTilesTester.generateGeometryTileBuffer = function (options) {
// Procedurally generate the tile array buffer for testing purposes
options = options ?? Frozen.EMPTY_OBJECT;
const magic = options.magic ?? [103, 101, 111, 109];
const version = options.version ?? 1;
let featureTableJsonString;
let featureTableJsonByteLength = 0;
const defineFeatureTable = options.defineFeatureTable ?? true;
if (defineFeatureTable) {
const featureTableJson = {
BOXES_LENGTH: options.boxesLength ?? 0,
CYLINDERS_LENGTH: options.cylindersLength ?? 0,
ELLIPSOIDS_LENGTH: options.ellipsoidsLength ?? 0,
SPHERES_LENGTH: options.spheresLength ?? 0,
BOX_BATCH_IDS: options.boxBatchIds,
CYLINDER_BATCH_IDS: options.cylinderBatchIds,
ELLIPSOID_BATCH_IDS: options.ellipsoidBatchIds,
SPHERE_BATCH_IDS: options.sphereBatchIds,
};
featureTableJsonString = JSON.stringify(featureTableJson);
featureTableJsonByteLength = featureTableJsonString.length;
}
const headerByteLength = 28;
const byteLength = headerByteLength + featureTableJsonByteLength;
const buffer = new ArrayBuffer(byteLength);
const view = new DataView(buffer);
view.setUint8(0, magic[0]);
view.setUint8(1, magic[1]);
view.setUint8(2, magic[2]);
view.setUint8(3, magic[3]);
view.setUint32(4, version, true); // version
view.setUint32(8, byteLength, true); // byteLength
view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength
view.setUint32(16, 0, true); // featureTableBinaryByteLength
view.setUint32(20, 0, true); // batchTableJsonByteLength
view.setUint32(24, 0, true); // batchTableBinaryByteLength
let i;
let byteOffset = headerByteLength;
for (i = 0; i < featureTableJsonByteLength; i++) {
view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i));
byteOffset++;
}
return buffer;
};
export default Cesium3DTilesTester;