You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

682 lines
23 KiB
JavaScript

import { BoundingSphere } from "../../Source/Cesium.js";
import { Color } from "../../Source/Cesium.js";
import { ColorGeometryInstanceAttribute } from "../../Source/Cesium.js";
import { combine } from "../../Source/Cesium.js";
import { destroyObject } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { GeometryInstance } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { RectangleGeometry } from "../../Source/Cesium.js";
import { Pass } from "../../Source/Cesium.js";
import { RenderState } from "../../Source/Cesium.js";
import { Cesium3DTileBatchTable } from "../../Source/Cesium.js";
import { ClassificationType } from "../../Source/Cesium.js";
import { ColorBlendMode } from "../../Source/Cesium.js";
import { PerInstanceColorAppearance } from "../../Source/Cesium.js";
import { Primitive } from "../../Source/Cesium.js";
import { StencilConstants } from "../../Source/Cesium.js";
import { Vector3DTilePolygons } from "../../Source/Cesium.js";
import createContext from "../createContext.js";
import createScene from "../createScene.js";
import pollToPromise from "../pollToPromise.js";
// Testing of this feature in WebGL is currently disabled due to test
// failures that started in https://github.com/CesiumGS/cesium/pull/8600.
// Classification does NOT work reliably in WebGL2 anyway, see
// https://github.com/CesiumGS/cesium/issues/8629
var testInWebGL2 = false;
describe(
"Scene/Vector3DTilePolygons",
function () {
createPolygonSpecs({});
if (testInWebGL2) {
var c = createContext({ requestWebgl2: true });
// Don't repeat WebGL 1 tests when WebGL 2 is not supported
if (c.webgl2) {
createPolygonSpecs({ requestWebgl2: true });
}
c.destroyForSpecs();
}
function createPolygonSpecs(contextOptions) {
var webglMessage = contextOptions.requestWebgl2 ? ": WebGL 2" : "";
var scene;
var rectangle;
var polygons;
var globePrimitive;
var tilesetPrimitive;
var reusableGlobePrimitive;
var reusableTilesetPrimitive;
var ellipsoid = Ellipsoid.WGS84;
var mockTileset = {
_statistics: {
texturesByteLength: 0,
},
tileset: {
_statistics: {
batchTableByteLength: 0,
},
colorBlendMode: ColorBlendMode.HIGHLIGHT,
},
getFeature: function (id) {
return { batchId: id };
},
};
function createPrimitive(rectangle, pass) {
var renderState;
if (pass === Pass.CESIUM_3D_TILE) {
renderState = RenderState.fromCache({
stencilTest: StencilConstants.setCesium3DTileBit(),
stencilMask: StencilConstants.CESIUM_3D_TILE_MASK,
depthTest: {
enabled: true,
},
});
}
var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(
new Color(1.0, 0.0, 0.0, 1.0)
);
return new Primitive({
geometryInstances: new GeometryInstance({
geometry: new RectangleGeometry({
ellipsoid: Ellipsoid.WGS84,
rectangle: rectangle,
}),
id: "depth rectangle",
attributes: {
color: depthColorAttribute,
},
}),
appearance: new PerInstanceColorAppearance({
translucent: false,
flat: true,
renderState: renderState,
}),
asynchronous: false,
});
}
function MockPrimitive(primitive, pass) {
this._primitive = primitive;
this._pass = pass;
this.show = true;
}
MockPrimitive.prototype.update = function (frameState) {
if (!this.show) {
return;
}
var commandList = frameState.commandList;
var startLength = commandList.length;
this._primitive.update(frameState);
for (var i = startLength; i < commandList.length; ++i) {
var command = commandList[i];
command.pass = this._pass;
}
};
MockPrimitive.prototype.isDestroyed = function () {
return false;
};
MockPrimitive.prototype.destroy = function () {
return destroyObject(this);
};
beforeAll(function () {
scene = createScene({ contextOptions: contextOptions });
rectangle = Rectangle.fromDegrees(-40.0, -40.0, 40.0, 40.0);
reusableGlobePrimitive = createPrimitive(rectangle, Pass.GLOBE);
reusableTilesetPrimitive = createPrimitive(
rectangle,
Pass.CESIUM_3D_TILE
);
});
afterAll(function () {
reusableGlobePrimitive.destroy();
reusableTilesetPrimitive.destroy();
scene.destroyForSpecs();
});
beforeEach(function () {
// wrap rectangle primitive so it gets executed during the globe pass and 3D Tiles pass to lay down depth
globePrimitive = new MockPrimitive(reusableGlobePrimitive, Pass.GLOBE);
tilesetPrimitive = new MockPrimitive(
reusableTilesetPrimitive,
Pass.CESIUM_3D_TILE
);
});
afterEach(function () {
scene.primitives.removeAll();
globePrimitive =
globePrimitive &&
!globePrimitive.isDestroyed() &&
globePrimitive.destroy();
tilesetPrimitive =
tilesetPrimitive &&
!tilesetPrimitive.isDestroyed() &&
tilesetPrimitive.destroy();
polygons = polygons && !polygons.isDestroyed() && polygons.destroy();
});
function loadPolygons(polygons) {
var ready = false;
polygons.readyPromise.then(function () {
ready = true;
});
return pollToPromise(function () {
polygons.update(scene.frameState);
scene.frameState.commandList.length = 0;
return ready;
});
}
function zigZag(value) {
return ((value << 1) ^ (value >> 15)) & 0xffff;
}
var maxShort = 32767;
function encodePositions(rectangle, positions) {
var length = positions.length;
var buffer = new Uint16Array(length * 2);
var lastU = 0;
var lastV = 0;
for (var i = 0; i < length; ++i) {
var position = positions[i];
var u = (position.longitude - rectangle.west) / rectangle.width;
var v = (position.latitude - rectangle.south) / rectangle.height;
u = CesiumMath.clamp(u, 0.0, 1.0);
v = CesiumMath.clamp(v, 0.0, 1.0);
u = Math.floor(u * maxShort);
v = Math.floor(v * maxShort);
buffer[i] = zigZag(u - lastU);
buffer[i + length] = zigZag(v - lastV);
lastU = u;
lastV = v;
}
return buffer;
}
function createPolygon(rectangle) {
var cartographicPositions = [
Rectangle.northwest(rectangle),
Rectangle.southwest(rectangle),
Rectangle.southeast(rectangle),
Rectangle.northeast(rectangle),
];
return {
positions: encodePositions(rectangle, cartographicPositions),
indices: new Uint16Array([0, 1, 2, 0, 2, 3]),
counts: new Uint32Array([4]),
indexCounts: new Uint32Array([6]),
};
}
it("renders a single polygon" + webglMessage, function () {
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var polygonOptions = createPolygon(rectangle);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 1);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons(
combine(polygonOptions, {
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0]),
isCartographic: true,
})
)
);
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(0, Color.BLUE);
polygons.updateCommands(0, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
});
});
it("renders multiple polygons" + webglMessage, function () {
var rectangle1 = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var rectangle2 = Rectangle.fromDegrees(1.0, -1.0, 2.0, 1.0);
var cartographicPositions = [
Rectangle.northwest(rectangle1),
Rectangle.southwest(rectangle1),
Rectangle.southeast(rectangle1),
Rectangle.northeast(rectangle1),
Rectangle.northwest(rectangle2),
Rectangle.southwest(rectangle2),
Rectangle.southeast(rectangle2),
Rectangle.northeast(rectangle2),
];
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 2.0, 1.0);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 2);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons({
positions: encodePositions(rectangle, cartographicPositions),
indices: new Uint16Array([0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]),
counts: new Uint32Array([4, 4]),
indexCounts: new Uint32Array([6, 6]),
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0, 1]),
isCartographic: true,
})
);
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle1,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(0, Color.BLUE);
polygons.updateCommands(0, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
scene.camera.setView({
destination: rectangle2,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(1, Color.BLUE);
polygons.updateCommands(1, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
});
});
it(
"renders multiple polygons after re-batch" + webglMessage,
function () {
var rectangle1 = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var rectangle2 = Rectangle.fromDegrees(1.0, -1.0, 2.0, 1.0);
var cartographicPositions = [
Rectangle.northwest(rectangle1),
Rectangle.southwest(rectangle1),
Rectangle.southeast(rectangle1),
Rectangle.northeast(rectangle1),
Rectangle.northwest(rectangle2),
Rectangle.southwest(rectangle2),
Rectangle.southeast(rectangle2),
Rectangle.northeast(rectangle2),
];
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 2.0, 1.0);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 2);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons({
positions: encodePositions(rectangle, cartographicPositions),
indices: new Uint16Array([0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]),
counts: new Uint32Array([4, 4]),
indexCounts: new Uint32Array([6, 6]),
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0, 1]),
isCartographic: true,
})
);
polygons.forceRebatch = true;
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle1,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(0, Color.BLUE);
polygons.updateCommands(0, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
scene.camera.setView({
destination: rectangle2,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(1, Color.BLUE);
polygons.updateCommands(1, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
});
}
);
it(
"renders multiple polygons with different minimum and maximum heights" +
webglMessage,
function () {
var rectangle1 = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var rectangle2 = Rectangle.fromDegrees(1.0, -1.0, 2.0, 1.0);
var cartographicPositions = [
Rectangle.northwest(rectangle1),
Rectangle.southwest(rectangle1),
Rectangle.southeast(rectangle1),
Rectangle.northeast(rectangle1),
Rectangle.northwest(rectangle2),
Rectangle.southwest(rectangle2),
Rectangle.southeast(rectangle2),
Rectangle.northeast(rectangle2),
];
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 2.0, 1.0);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 2);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons({
positions: encodePositions(rectangle, cartographicPositions),
indices: new Uint16Array([0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]),
counts: new Uint32Array([4, 4]),
indexCounts: new Uint32Array([6, 6]),
minimumHeight: -10000.0,
maximumHeight: 10000.0,
polygonMinimumHeights: new Float32Array([-10000.0, 10.0]),
polygonMaximumHeights: new Float32Array([10000.0, 100.0]),
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0, 1]),
isCartographic: true,
})
);
polygons.forceRebatch = true;
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle1,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(0, Color.BLUE);
polygons.updateCommands(0, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
scene.camera.setView({
destination: rectangle2,
});
expect(scene).toRender([255, 0, 0, 255]);
});
}
);
it("renders with inverted classification" + webglMessage, function () {
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var polygonOptions = createPolygon(rectangle);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 1);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(tilesetPrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons(
combine(polygonOptions, {
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0]),
isCartographic: true,
})
)
);
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: Rectangle.fromDegrees(-2.0, -1.0, -1.0, 1.0),
});
expect(scene).toRender([255, 0, 0, 255]);
scene.invertClassification = true;
scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 1.0);
expect(scene).toRender([64, 0, 0, 255]);
scene.camera.setView({
destination: rectangle,
});
expect(scene).toRender([255, 255, 255, 255]);
scene.invertClassification = false;
});
});
it("renders wireframe" + webglMessage, function () {
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var polygonOptions = createPolygon(rectangle);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 1);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons(
combine(polygonOptions, {
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0]),
isCartographic: true,
})
)
);
polygons.debugWireframe = true;
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle,
});
expect(scene).toRender([255, 255, 255, 255]);
batchTable.setColor(0, Color.BLUE);
polygons.updateCommands(0, Color.BLUE);
batchTable.update(mockTileset, scene.frameState);
expect(scene).toRender([0, 0, 255, 255]);
});
});
it("renders based on classificationType" + webglMessage, function () {
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var polygonOptions = createPolygon(rectangle);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 1);
batchTable.update(mockTileset, scene.frameState);
scene.primitives.add(globePrimitive);
scene.primitives.add(tilesetPrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons(
combine(polygonOptions, {
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0]),
isCartographic: true,
})
)
);
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle,
});
polygons.classificationType = ClassificationType.CESIUM_3D_TILE;
globePrimitive.show = false;
tilesetPrimitive.show = true;
expect(scene).toRender([255, 255, 255, 255]);
globePrimitive.show = true;
tilesetPrimitive.show = false;
expect(scene).toRender([255, 0, 0, 255]);
polygons.classificationType = ClassificationType.TERRAIN;
globePrimitive.show = false;
tilesetPrimitive.show = true;
expect(scene).toRender([255, 0, 0, 255]);
globePrimitive.show = true;
tilesetPrimitive.show = false;
expect(scene).toRender([255, 255, 255, 255]);
polygons.classificationType = ClassificationType.BOTH;
globePrimitive.show = false;
tilesetPrimitive.show = true;
expect(scene).toRender([255, 255, 255, 255]);
globePrimitive.show = true;
tilesetPrimitive.show = false;
expect(scene).toRender([255, 255, 255, 255]);
globePrimitive.show = true;
tilesetPrimitive.show = true;
});
});
it("picks polygons" + webglMessage, function () {
var rectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0);
var polygonOptions = createPolygon(rectangle);
var batchTable = new Cesium3DTileBatchTable(mockTileset, 1);
scene.primitives.add(globePrimitive);
var center = ellipsoid.cartographicToCartesian(
Rectangle.center(rectangle)
);
polygons = scene.primitives.add(
new Vector3DTilePolygons(
combine(polygonOptions, {
minimumHeight: -10000.0,
maximumHeight: 10000.0,
center: center,
rectangle: rectangle,
boundingVolume: new BoundingSphere(center, 10000.0),
batchTable: batchTable,
batchIds: new Uint32Array([0]),
isCartographic: true,
})
)
);
polygons.debugWireframe = true;
return loadPolygons(polygons).then(function () {
scene.camera.setView({
destination: rectangle,
});
var features = [];
polygons.createFeatures(mockTileset, features);
var getFeature = mockTileset.getFeature;
mockTileset.getFeature = function (index) {
return features[index];
};
scene.frameState.passes.pick = true;
batchTable.update(mockTileset, scene.frameState);
expect(scene).toPickAndCall(function (result) {
expect(result).toBe(features[0]);
});
mockTileset.getFeature = getFeature;
});
});
it("isDestroyed" + webglMessage, function () {
polygons = new Vector3DTilePolygons({});
expect(polygons.isDestroyed()).toEqual(false);
polygons.destroy();
expect(polygons.isDestroyed()).toEqual(true);
});
}
},
"WebGL"
);