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.

491 lines
16 KiB
JavaScript

import MockImageryProvider from "../MockImageryProvider.js";
import MockTerrainProvider from "../MockTerrainProvider.js";
import TerrainTileProcessor from "../TerrainTileProcessor.js";
import { Cartesian3 } from "../../Source/Cesium.js";
import { Cartesian4 } from "../../Source/Cesium.js";
import { createWorldTerrain } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { EllipsoidTerrainProvider } from "../../Source/Cesium.js";
import { GeographicTilingScheme } from "../../Source/Cesium.js";
import { Ray } from "../../Source/Cesium.js";
import { GlobeSurfaceTile } from "../../Source/Cesium.js";
import { ImageryLayerCollection } from "../../Source/Cesium.js";
import { QuadtreeTile } from "../../Source/Cesium.js";
import { QuadtreeTileLoadState } from "../../Source/Cesium.js";
import { TerrainState } from "../../Source/Cesium.js";
import createScene from "../createScene.js";
import { when } from "../../Source/Cesium.js";
describe("Scene/GlobeSurfaceTile", function () {
var frameState;
var tilingScheme;
var rootTiles;
var rootTile;
var imageryLayerCollection;
var mockTerrain;
var processor;
beforeEach(function () {
frameState = {
context: {
cache: {},
},
};
tilingScheme = new GeographicTilingScheme();
rootTiles = QuadtreeTile.createLevelZeroTiles(tilingScheme);
rootTile = rootTiles[0];
imageryLayerCollection = new ImageryLayerCollection();
mockTerrain = new MockTerrainProvider();
processor = new TerrainTileProcessor(
frameState,
mockTerrain,
imageryLayerCollection
);
});
afterEach(function () {
for (var i = 0; i < rootTiles.length; ++i) {
rootTiles[i].freeResources();
}
});
describe("processStateMachine", function () {
beforeEach(function () {
processor.mockWebGL();
});
it("starts in the START state", function () {
for (var i = 0; i < rootTiles.length; ++i) {
var tile = rootTiles[i];
expect(tile.state).toBe(QuadtreeTileLoadState.START);
}
});
it("transitions to the LOADING state immediately if this tile is available", function () {
mockTerrain.willBeAvailable(rootTile.southwestChild);
return processor.process([rootTile.southwestChild]).then(function () {
expect(rootTile.southwestChild.state).toBe(
QuadtreeTileLoadState.LOADING
);
expect(rootTile.southwestChild.data.terrainState).toBe(
TerrainState.UNLOADED
);
});
});
it("transitions to the LOADING tile state and FAILED terrain state immediately if this tile is NOT available", function () {
mockTerrain.willBeUnavailable(rootTile.southwestChild);
return processor.process([rootTile.southwestChild]).then(function () {
expect(rootTile.southwestChild.state).toBe(
QuadtreeTileLoadState.LOADING
);
expect(rootTile.southwestChild.data.terrainState).toBe(
TerrainState.FAILED
);
});
});
it("pushes parent along if waiting on it to be able to upsample", function () {
mockTerrain
.willBeAvailable(rootTile)
.requestTileGeometryWillSucceed(rootTile)
.willBeUnavailable(rootTile.southwestChild);
spyOn(mockTerrain, "requestTileGeometry").and.callThrough();
return processor.process([rootTile.southwestChild]).then(function () {
expect(mockTerrain.requestTileGeometry.calls.count()).toBe(1);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[0]).toBe(0);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[1]).toBe(0);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[2]).toBe(0);
});
});
it("does nothing when a root tile is unavailable", function () {
mockTerrain.willBeUnavailable(rootTile);
return processor.process([rootTile]).then(function () {
expect(rootTile.state).toBe(QuadtreeTileLoadState.FAILED);
expect(rootTile.data.terrainState).toBe(TerrainState.FAILED);
});
});
it("does nothing when a root tile fails to load", function () {
mockTerrain.requestTileGeometryWillFail(rootTile);
return processor.process([rootTile]).then(function () {
expect(rootTile.state).toBe(QuadtreeTileLoadState.FAILED);
expect(rootTile.data.terrainState).toBe(TerrainState.FAILED);
});
});
it("upsamples failed tiles from parent TerrainData", function () {
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.createMeshWillSucceed(rootTile)
.willBeUnavailable(rootTile.southwestChild)
.upsampleWillSucceed(rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.data.terrainData.wasCreatedByUpsampling()).toBe(
false
);
expect(
rootTile.southwestChild.data.terrainData.wasCreatedByUpsampling()
).toBe(true);
});
});
it("loads available tiles", function () {
mockTerrain
.willBeAvailable(rootTile.southwestChild)
.requestTileGeometryWillSucceed(rootTile.southwestChild);
spyOn(mockTerrain, "requestTileGeometry").and.callThrough();
return processor.process([rootTile.southwestChild]).then(function () {
expect(mockTerrain.requestTileGeometry.calls.count()).toBe(1);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[0]).toBe(0);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[1]).toBe(1);
expect(mockTerrain.requestTileGeometry.calls.argsFor(0)[2]).toBe(1);
});
});
it("marks an upsampled tile as such", function () {
mockTerrain
.willBeAvailable(rootTile)
.requestTileGeometryWillSucceed(rootTile)
.createMeshWillSucceed(rootTile)
.willBeUnavailable(rootTile.southwestChild)
.upsampleWillSucceed(rootTile.southwestChild)
.createMeshWillSucceed(rootTile.southwestChild);
var mockImagery = new MockImageryProvider();
imageryLayerCollection.addImageryProvider(mockImagery);
mockImagery
.requestImageWillSucceed(rootTile)
.requestImageWillFail(rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.state).toBe(QuadtreeTileLoadState.DONE);
expect(rootTile.upsampledFromParent).toBe(false);
expect(rootTile.southwestChild.state).toBe(
QuadtreeTileLoadState.DONE
);
expect(rootTile.southwestChild.upsampledFromParent).toBe(true);
});
});
it("does not mark a tile as upsampled if it has fresh imagery", function () {
mockTerrain
.willBeAvailable(rootTile)
.requestTileGeometryWillSucceed(rootTile)
.createMeshWillSucceed(rootTile)
.willBeUnavailable(rootTile.southwestChild)
.upsampleWillSucceed(rootTile.southwestChild)
.createMeshWillSucceed(rootTile.southwestChild);
var mockImagery = new MockImageryProvider();
imageryLayerCollection.addImageryProvider(mockImagery);
mockImagery
.requestImageWillSucceed(rootTile)
.requestImageWillSucceed(rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.state).toBe(QuadtreeTileLoadState.DONE);
expect(rootTile.upsampledFromParent).toBe(false);
expect(rootTile.southwestChild.state).toBe(
QuadtreeTileLoadState.DONE
);
expect(rootTile.southwestChild.upsampledFromParent).toBe(false);
});
});
it("does not mark a tile as upsampled if it has fresh terrain", function () {
mockTerrain
.willBeAvailable(rootTile)
.requestTileGeometryWillSucceed(rootTile)
.createMeshWillSucceed(rootTile)
.willBeAvailable(rootTile.southwestChild)
.requestTileGeometryWillSucceed(rootTile.southwestChild)
.createMeshWillSucceed(rootTile.southwestChild);
var mockImagery = new MockImageryProvider();
imageryLayerCollection.addImageryProvider(mockImagery);
mockImagery
.requestImageWillSucceed(rootTile)
.requestImageWillFail(rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.state).toBe(QuadtreeTileLoadState.DONE);
expect(rootTile.upsampledFromParent).toBe(false);
expect(rootTile.southwestChild.state).toBe(
QuadtreeTileLoadState.DONE
);
expect(rootTile.southwestChild.upsampledFromParent).toBe(false);
});
});
it("creates water mask texture from one-byte water mask data, if it exists", function () {
mockTerrain
.willBeAvailable(rootTile)
.requestTileGeometryWillSucceed(rootTile)
.willHaveWaterMask(false, true, rootTile);
return processor.process([rootTile]).then(function () {
expect(rootTile.data.waterMaskTexture).toBeDefined();
});
});
it("uses undefined water mask texture for tiles that are entirely land", function () {
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.willHaveWaterMask(true, false, rootTile);
return processor.process([rootTile]).then(function () {
expect(rootTile.data.waterMaskTexture).toBeUndefined();
});
});
it("uses shared water mask texture for tiles that are entirely water", function () {
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.willHaveWaterMask(false, true, rootTile)
.requestTileGeometryWillSucceed(rootTile.southwestChild)
.willHaveWaterMask(false, true, rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.data.waterMaskTexture).toBe(
rootTile.southwestChild.data.waterMaskTexture
);
});
});
it("creates water mask texture from multi-byte water mask data, if it exists", function () {
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.willHaveWaterMask(true, true, rootTile);
return processor.process([rootTile]).then(function () {
expect(rootTile.data.waterMaskTexture).toBeDefined();
});
});
it("upsamples water mask if data is not available", function () {
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.willHaveWaterMask(false, true, rootTile)
.requestTileGeometryWillSucceed(rootTile.southwestChild);
return processor
.process([rootTile, rootTile.southwestChild])
.then(function () {
expect(rootTile.southwestChild.data.waterMaskTexture).toBeDefined();
expect(
rootTile.southwestChild.data.waterMaskTranslationAndScale
).toEqual(new Cartesian4(0.0, 0.0, 0.5, 0.5));
});
});
});
describe(
"pick",
function () {
var scene;
beforeAll(function () {
scene = createScene();
});
afterAll(function () {
scene.destroyForSpecs();
});
it("gets correct results even when the mesh includes normals", function () {
var terrainProvider = createWorldTerrain({
requestVertexNormals: true,
requestWaterMask: false,
});
var tile = new QuadtreeTile({
tilingScheme: new GeographicTilingScheme(),
level: 11,
x: 3788,
y: 1336,
});
processor.frameState = scene.frameState;
processor.terrainProvider = terrainProvider;
return processor.process([tile]).then(function () {
var ray = new Ray(
new Cartesian3(
-5052039.459789615,
2561172.040315167,
-2936276.999965875
),
new Cartesian3(
0.5036332963145244,
0.6648033332898124,
0.5517155343926082
)
);
var pickResult = tile.data.pick(ray, undefined, undefined, true);
var cartographic = Ellipsoid.WGS84.cartesianToCartographic(
pickResult
);
expect(cartographic.height).toBeGreaterThan(-500.0);
});
});
it("gets correct result when a closer triangle is processed after a farther triangle", function () {
// Pick root tile (level=0, x=0, y=0) from the east side towards the west.
// Based on heightmap triangle processing order the west triangle will be tested first, followed
// by the east triangle. But since the east triangle is closer we expect it to be the pick result.
var terrainProvider = new EllipsoidTerrainProvider();
var tile = new QuadtreeTile({
tilingScheme: new GeographicTilingScheme(),
level: 0,
x: 0,
y: 0,
});
processor.frameState = scene.frameState;
processor.terrainProvider = terrainProvider;
return processor.process([tile]).then(function () {
var origin = new Cartesian3(50000000.0, -1.0, 0.0);
var direction = new Cartesian3(-1.0, 0.0, 0.0);
var ray = new Ray(origin, direction);
var cullBackFaces = false;
var pickResult = tile.data.pick(
ray,
undefined,
undefined,
cullBackFaces
);
expect(pickResult.x).toBeGreaterThan(0.0);
});
});
it("ignores triangles that are behind the ray", function () {
// Pick root tile (level=0, x=0, y=0) from the center towards the east side (+X).
var terrainProvider = new EllipsoidTerrainProvider();
var tile = new QuadtreeTile({
tilingScheme: new GeographicTilingScheme(),
level: 0,
x: 0,
y: 0,
});
processor.frameState = scene.frameState;
processor.terrainProvider = terrainProvider;
return processor.process([tile]).then(function () {
var origin = new Cartesian3(0.0, -1.0, 0.0);
var direction = new Cartesian3(1.0, 0.0, 0.0);
var ray = new Ray(origin, direction);
var cullBackFaces = false;
var pickResult = tile.data.pick(
ray,
undefined,
undefined,
cullBackFaces
);
expect(pickResult.x).toBeGreaterThan(0.0);
});
});
},
"WebGL"
);
describe("eligibleForUnloading", function () {
beforeEach(function () {
processor.mockWebGL();
});
it("returns true when no loading has been done", function () {
rootTile.data = new GlobeSurfaceTile();
expect(rootTile.data.eligibleForUnloading).toBe(true);
});
it("returns true when some loading has been done", function () {
mockTerrain.requestTileGeometryWillSucceed(rootTile);
return processor
.process([rootTile])
.then(function () {
expect(rootTile.data.eligibleForUnloading).toBe(true);
mockTerrain.createMeshWillSucceed(rootTile);
return processor.process([rootTile]);
})
.then(function () {
expect(rootTile.data.eligibleForUnloading).toBe(true);
});
});
it("returns false when RECEIVING", function () {
var deferred = when.defer();
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.requestTileGeometryWillWaitOn(deferred.promise, rootTile);
return processor.process([rootTile], 5).then(function () {
expect(rootTile.data.eligibleForUnloading).toBe(false);
deferred.resolve();
});
});
it("returns false when TRANSFORMING", function () {
var deferred = when.defer();
mockTerrain
.requestTileGeometryWillSucceed(rootTile)
.createMeshWillSucceed(rootTile)
.createMeshWillWaitOn(deferred.promise, rootTile);
return processor.process([rootTile], 5).then(function () {
expect(rootTile.data.eligibleForUnloading).toBe(false);
deferred.resolve();
});
});
it("returns false when imagery is TRANSITIONING", function () {
var deferred = when.defer();
var mockImagery = new MockImageryProvider();
imageryLayerCollection.addImageryProvider(mockImagery);
mockImagery.requestImageWillWaitOn(deferred.promise, rootTile);
mockTerrain.requestTileGeometryWillSucceed(rootTile);
return processor.process([rootTile], 5).then(function () {
expect(rootTile.data.eligibleForUnloading).toBe(false);
deferred.resolve();
});
});
});
});