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
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();
|
|
});
|
|
});
|
|
});
|
|
});
|