import { Cartesian3 } from "../../Source/Cesium.js"; import { Cartesian4 } from "../../Source/Cesium.js"; import { CesiumTerrainProvider } from "../../Source/Cesium.js"; import { Color } from "../../Source/Cesium.js"; import { Credit } from "../../Source/Cesium.js"; import { defined } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { EllipsoidTerrainProvider } from "../../Source/Cesium.js"; import { GeographicProjection } from "../../Source/Cesium.js"; import { HeadingPitchRoll } from "../../Source/Cesium.js"; import { Rectangle } from "../../Source/Cesium.js"; import { WebMercatorProjection } from "../../Source/Cesium.js"; import { ContextLimits } from "../../Source/Cesium.js"; import { RenderState } from "../../Source/Cesium.js"; import { BlendingState } from "../../Source/Cesium.js"; import { ClippingPlane } from "../../Source/Cesium.js"; import { ClippingPlaneCollection } from "../../Source/Cesium.js"; import { Fog } from "../../Source/Cesium.js"; import { Globe } from "../../Source/Cesium.js"; import { GlobeSurfaceShaderSet } from "../../Source/Cesium.js"; import { GlobeSurfaceTileProvider } from "../../Source/Cesium.js"; import { ImageryLayerCollection } from "../../Source/Cesium.js"; import { ImagerySplitDirection } from "../../Source/Cesium.js"; import { Model } from "../../Source/Cesium.js"; import { QuadtreeTile } from "../../Source/Cesium.js"; import { QuadtreeTileProvider } from "../../Source/Cesium.js"; import { SceneMode } from "../../Source/Cesium.js"; import { SingleTileImageryProvider } from "../../Source/Cesium.js"; import { WebMapServiceImageryProvider } from "../../Source/Cesium.js"; import createScene from "../createScene.js"; import pollToPromise from "../pollToPromise.js"; describe( "Scene/GlobeSurfaceTileProvider", function () { var scene; function forEachRenderedTile( quadtreePrimitive, minimumTiles, maximumTiles, callback ) { var tileCount = 0; quadtreePrimitive.forEachRenderedTile(function (tile) { ++tileCount; callback(tile); }); if (defined(minimumTiles)) { expect(tileCount).not.toBeLessThan(minimumTiles); } if (defined(maximumTiles)) { expect(tileCount).not.toBeGreaterThan(maximumTiles); } } /** * Repeatedly calls update until the load queue is empty. You must wrap any code to follow * this in a "runs" function. */ function updateUntilDone(globe) { // update until the load queue is empty. return pollToPromise(function () { scene.renderForSpecs(); return ( globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0 ); }); } var cameraDestination = new Rectangle(0.0001, 0.0001, 0.003, 0.003); function switchViewMode(mode, projection) { scene.mode = mode; scene.frameState.mapProjection = projection; scene.camera.update(scene.mode); scene.camera.setView({ destination: cameraDestination, }); } beforeAll(function () { scene = createScene(); scene.frameState.scene3DOnly = false; }); afterAll(function () { scene.destroyForSpecs(); }); beforeEach(function () { scene.globe = new Globe(); }); afterEach(function () { scene.imageryLayers.removeAll(); scene.primitives.removeAll(); }); it("conforms to QuadtreeTileProvider interface", function () { expect(GlobeSurfaceTileProvider).toConformToInterface( QuadtreeTileProvider ); }); describe( "construction", function () { it("throws if a terrainProvider is not provided", function () { function constructWithoutTerrainProvider() { return new GlobeSurfaceTileProvider({ imageryLayers: new ImageryLayerCollection(), surfaceShaderSet: new GlobeSurfaceShaderSet(), }); } expect(constructWithoutTerrainProvider).toThrowDeveloperError(); }); it("throws if a imageryLayers is not provided", function () { function constructWithoutImageryLayerCollection() { return new GlobeSurfaceTileProvider({ terrainProvider: new EllipsoidTerrainProvider(), surfaceShaderSet: new GlobeSurfaceShaderSet(), }); } expect( constructWithoutImageryLayerCollection ).toThrowDeveloperError(); }); it("throws if a surfaceShaderSet is not provided", function () { function constructWithoutImageryLayerCollection() { return new GlobeSurfaceTileProvider({ terrainProvider: new EllipsoidTerrainProvider(), imageryLayers: new ImageryLayerCollection(), }); } expect( constructWithoutImageryLayerCollection ).toThrowDeveloperError(); }); }, "WebGL" ); describe( "layer updating", function () { it("removing a layer removes it from all tiles", function () { var layer = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); return updateUntilDone(scene.globe).then(function () { // All tiles should have one or more associated images. forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBeGreaterThan(0); for (var i = 0; i < tile.data.imagery.length; ++i) { expect(tile.data.imagery[i].readyImagery.imageryLayer).toEqual( layer ); } }); scene.imageryLayers.remove(layer); // All associated images should be gone. forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toEqual(0); }); }); }); it("adding a layer adds it to all tiles after update", function () { scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); return updateUntilDone(scene.globe).then(function () { // Add another layer var layer2 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Green4x4.png", }) ); return updateUntilDone(scene.globe).then(function () { // All tiles should have one or more associated images. forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBeGreaterThan(0); var hasImageFromLayer2 = false; for (var i = 0; i < tile.data.imagery.length; ++i) { var imageryTile = tile.data.imagery[i].readyImagery; if (!defined(imageryTile)) { imageryTile = tile.data.imagery[i].loadingImagery; } if (imageryTile.imageryLayer === layer2) { hasImageFromLayer2 = true; } } expect(hasImageFromLayer2).toEqual(true); }); }); }); }); it("moving a layer moves the corresponding TileImagery instances on every tile", function () { var layer1 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); var layer2 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Green4x4.png", }) ); return updateUntilDone(scene.globe).then(function () { forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBeGreaterThan(0); var indexOfFirstLayer1 = tile.data.imagery.length; var indexOfLastLayer1 = -1; var indexOfFirstLayer2 = tile.data.imagery.length; for (var i = 0; i < tile.data.imagery.length; ++i) { if (tile.data.imagery[i].readyImagery.imageryLayer === layer1) { indexOfFirstLayer1 = Math.min(indexOfFirstLayer1, i); indexOfLastLayer1 = i; } else { expect( tile.data.imagery[i].readyImagery.imageryLayer ).toEqual(layer2); indexOfFirstLayer2 = Math.min(indexOfFirstLayer2, i); } } expect(indexOfFirstLayer1).toBeLessThan(indexOfFirstLayer2); expect(indexOfLastLayer1).toBeLessThan(indexOfFirstLayer2); }); scene.imageryLayers.raiseToTop(layer1); return updateUntilDone(scene.globe).then(function () { forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBeGreaterThan(0); var indexOfFirstLayer2 = tile.data.imagery.length; var indexOfLastLayer2 = -1; var indexOfFirstLayer1 = tile.data.imagery.length; for (var i = 0; i < tile.data.imagery.length; ++i) { if ( tile.data.imagery[i].readyImagery.imageryLayer === layer2 ) { indexOfFirstLayer2 = Math.min(indexOfFirstLayer2, i); indexOfLastLayer2 = i; } else { expect( tile.data.imagery[i].readyImagery.imageryLayer ).toEqual(layer1); indexOfFirstLayer1 = Math.min(indexOfFirstLayer1, i); } } expect(indexOfFirstLayer2).toBeLessThan(indexOfFirstLayer1); expect(indexOfLastLayer2).toBeLessThan(indexOfFirstLayer1); }); }); }); }); it("adding a layer creates its skeletons only once", function () { scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); return updateUntilDone(scene.globe).then(function () { // Add another layer var layer2 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Green4x4.png", }) ); return updateUntilDone(scene.globe).then(function () { // All tiles should have one or more associated images. forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBeGreaterThan(0); var tilesFromLayer2 = 0; for (var i = 0; i < tile.data.imagery.length; ++i) { var imageryTile = tile.data.imagery[i].readyImagery; if (!defined(imageryTile)) { imageryTile = tile.data.imagery[i].loadingImagery; } if (imageryTile.imageryLayer === layer2) { ++tilesFromLayer2; } } expect(tilesFromLayer2).toBe(1); }); }); }); }); it("calling _reload adds a callback per layer per tile", function () { var layer1 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); var layer2 = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Green4x4.png", }) ); return updateUntilDone(scene.globe).then(function () { // Verify that each tile has 2 imagery objects and no loaded callbacks forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBe(2); expect(Object.keys(tile._loadedCallbacks).length).toBe(0); }); // Reload each layer layer1._imageryProvider._reload(); layer2._imageryProvider._reload(); // These should be ignored layer1._imageryProvider._reload(); layer2._imageryProvider._reload(); // Verify that each tile has 4 imagery objects (the old imagery and the reloaded imagery for each layer) // and also has 2 callbacks so the old imagery will be removed once loaded. forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBe(4); expect(Object.keys(tile._loadedCallbacks).length).toBe(2); }); return updateUntilDone(scene.globe).then(function () { // Verify the old imagery was removed and the callbacks are no longer there forEachRenderedTile(scene.globe._surface, 1, undefined, function ( tile ) { expect(tile.data.imagery.length).toBe(2); expect(Object.keys(tile._loadedCallbacks).length).toBe(0); }); }); }); }); }, "WebGL" ); it("renders in 2D geographic", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE2D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); it("renders in 2D web mercator", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE2D, new WebMercatorProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); it("renders in Columbus View geographic", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); it("renders in Columbus View web mercator", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.COLUMBUS_VIEW, new WebMercatorProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 128, 255]); }); }); it("renders in 3D", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); it("renders in 3D (2)", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); describe("fog", function () { it("culls tiles in full fog", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); var oldFog = scene.fog; scene.fog = new Fog(); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); scene.camera.lookUp(1.2); // Horizon-view return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); scene.fog.enabled = true; scene.fog.density = 1.0; scene.fog.screenSpaceErrorFactor = 0.0; expect(scene).toRender([0, 0, 0, 255]); scene.fog = oldFog; }); }); it("culls tiles because of increased SSE", function () { expect(scene).toRender([0, 0, 0, 255]); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); var oldFog = scene.fog; scene.fog = new Fog(); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); scene.camera.lookUp(1.2); // Horizon-view return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); scene.fog.enabled = true; scene.fog.density = 0.001; scene.fog.screenSpaceErrorFactor = 0.0; var result; expect(scene).toRenderAndCall(function (rgba) { result = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); scene.fog.screenSpaceErrorFactor = 10000.0; expect(scene).notToRender(result); scene.fog = oldFog; }); }); }); it("can change baseColor", function () { expect(scene).toRender([0, 0, 0, 255]); scene.globe.baseColor = Color.RED; scene.fog.enabled = false; switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).toRender([255, 0, 0, 255]); }); }); it("renders in 3D and then Columbus View", function () { scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); switchViewMode( SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); }); it("renders even if imagery root tiles fail to load", function () { expect(scene).toRender([0, 0, 0, 255]); var providerWithInvalidRootTiles = new WebMapServiceImageryProvider({ url: "/invalid", layers: "invalid", }); scene.imageryLayers.addImageryProvider(providerWithInvalidRootTiles); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); }); }); it("passes layer adjustment values as uniforms", function () { expect(scene).toRender([0, 0, 0, 255]); var layer = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); layer.alpha = 0.123; layer.nightAlpha = 0.658; layer.dayAlpha = 0.356; layer.brightness = 0.456; layer.contrast = 0.654; layer.gamma = 0.321; layer.saturation = 0.123; layer.hue = 0.456; layer.splitDirection = ImagerySplitDirection.LEFT; switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var tileCommandCount = 0; var commandList = scene.frameState.commandList; for (var i = 0; i < commandList.length; ++i) { var command = commandList[i]; var uniforms = command.uniformMap; if (!defined(uniforms) || !defined(uniforms.u_dayTextureAlpha)) { continue; } ++tileCommandCount; expect(uniforms.u_dayTextureAlpha()).toEqual([0.123]); expect(uniforms.u_dayTextureNightAlpha()).toEqual([0.658]); expect(uniforms.u_dayTextureDayAlpha()).toEqual([0.356]); expect(uniforms.u_dayTextureBrightness()).toEqual([0.456]); expect(uniforms.u_dayTextureContrast()).toEqual([0.654]); expect(uniforms.u_dayTextureOneOverGamma()).toEqual([1.0 / 0.321]); expect(uniforms.u_dayTextureSaturation()).toEqual([0.123]); expect(uniforms.u_dayTextureHue()).toEqual([0.456]); expect(uniforms.u_dayTextureSplit()).toEqual([ ImagerySplitDirection.LEFT, ]); } expect(tileCommandCount).toBeGreaterThan(0); }); }); it("renders imagery cutout", function () { expect(scene).toRender([0, 0, 0, 255]); var layer = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); layer.cutoutRectangle = cameraDestination; switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); var baseColor; return updateUntilDone(scene.globe) .then(function () { expect(scene).toRenderAndCall(function (rgba) { baseColor = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); layer.cutoutRectangle = undefined; return updateUntilDone(scene.globe); }) .then(function () { expect(scene).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual(baseColor); expect(rgba).not.toEqual([0, 0, 0, 255]); }); }); }); it("renders imagery with color-to-alpha", function () { expect(scene).toRender([0, 0, 0, 255]); var layer = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); var layerColor; return updateUntilDone(scene.globe) .then(function () { expect(scene).toRenderAndCall(function (rgba) { layerColor = rgba; // Expect the layer color to be mostly red expect(layerColor[0]).toBeGreaterThan(layerColor[1]); expect(layerColor[0]).toBeGreaterThan(layerColor[2]); }); layer.colorToAlpha = new Color(1.0, 0.0, 0.0); layer.colorToAlphaThreshold = 0.1; return updateUntilDone(scene.globe); }) .then(function () { var commandList = scene.frameState.commandList; for (var i = 0; i < commandList.length; ++i) { var command = commandList[i]; var uniforms = command.uniformMap; if (!defined(uniforms) || !defined(uniforms.u_dayTextureAlpha)) { continue; } expect(uniforms.u_colorsToAlpha()).toEqual([ new Cartesian4(1.0, 0.0, 0.0, 0.1), ]); } expect(scene).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual(layerColor); }); }); }); it("skips layer with uniform alpha value of zero", function () { var layer = scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); layer.alpha = 0.0; switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var tileCommandCount = 0; var commandList = scene.frameState.commandList; for (var i = 0; i < commandList.length; ++i) { var command = commandList[i]; var uniforms = command.uniformMap; if (!defined(uniforms) || !defined(uniforms.u_dayTextureAlpha)) { continue; } ++tileCommandCount; expect(uniforms.u_dayTextureAlpha()).toEqual([]); } expect(tileCommandCount).toBeGreaterThan(0); }); }); it("can render more imagery layers than the available texture units", function () { for (var i = 0; i < ContextLimits.maximumTextureImageUnits + 1; ++i) { scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", }) ); } switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var renderStateWithAlphaBlending = RenderState.fromCache({ blending: BlendingState.ALPHA_BLEND, }); var drawCommandsPerTile = {}; var commandList = scene.frameState.commandList; for (var i = 0; i < commandList.length; ++i) { var command = commandList[i]; if (command.owner instanceof QuadtreeTile) { var tile = command.owner; var key = "L" + tile.level + "X" + tile.x + "Y" + tile.y; if (!defined(drawCommandsPerTile[key])) { drawCommandsPerTile[key] = 0; // The first draw command for each tile should use a non-alpha-blending render state. expect(command.renderState.blending).not.toEqual( renderStateWithAlphaBlending.blending ); } else { // Successive draw commands per tile should alpha blend. expect(command.renderState.blending).toEqual( renderStateWithAlphaBlending.blending ); expect(command.uniformMap.u_initialColor().w).toEqual(0.0); } ++drawCommandsPerTile[key]; } } var tileCount = 0; for (var tileID in drawCommandsPerTile) { if (drawCommandsPerTile.hasOwnProperty(tileID)) { ++tileCount; expect(drawCommandsPerTile[tileID]).toBeGreaterThanOrEqualTo(2); } } expect(tileCount).toBeGreaterThanOrEqualTo(1); }); }); it("adds terrain and imagery credits to the CreditDisplay", function () { var imageryCredit = new Credit("imagery credit"); scene.imageryLayers.addImageryProvider( new SingleTileImageryProvider({ url: "Data/Images/Red16x16.png", credit: imageryCredit, }) ); var terrainCredit = new Credit("terrain credit"); scene.terrainProvider = new CesiumTerrainProvider({ url: "https://s3.amazonaws.com/cesiumjs/smallTerrain", credit: terrainCredit, }); return updateUntilDone(scene.globe).then(function () { var creditDisplay = scene.frameState.creditDisplay; creditDisplay.showLightbox(); expect( creditDisplay._currentFrameCredits.lightboxCredits.values ).toContain(imageryCredit); expect( creditDisplay._currentFrameCredits.lightboxCredits.values ).toContain(terrainCredit); creditDisplay.hideLightbox(); }); }); describe( "switching terrain providers", function () { it("clears the replacement queue", function () { return updateUntilDone(scene.globe).then(function () { var surface = scene.globe._surface; var replacementQueue = surface._tileReplacementQueue; expect(replacementQueue.count).toBeGreaterThan(0); var oldTile = replacementQueue.head; surface.tileProvider.terrainProvider = new EllipsoidTerrainProvider(); scene.renderForSpecs(); expect(replacementQueue.count).toBeGreaterThan(0); expect(replacementQueue.head).not.toBe(oldTile); }); }); it("recreates the level zero tiles", function () { var surface = scene.globe._surface; scene.renderForSpecs(); var levelZeroTiles = surface._levelZeroTiles; expect(levelZeroTiles.length).toBe(2); var levelZero0 = levelZeroTiles[0]; var levelZero1 = levelZeroTiles[1]; surface.tileProvider.terrainProvider = new EllipsoidTerrainProvider(); scene.renderForSpecs(); scene.renderForSpecs(); levelZeroTiles = surface._levelZeroTiles; expect(levelZeroTiles[0]).not.toBe(levelZero0); expect(levelZeroTiles[1]).not.toBe(levelZero1); }); it("does nothing if the new provider is the same as the old", function () { var surface = scene.globe._surface; var provider = surface.tileProvider.terrainProvider; scene.renderForSpecs(); var levelZeroTiles = surface._levelZeroTiles; expect(levelZeroTiles.length).toBe(2); var levelZero0 = levelZeroTiles[0]; var levelZero1 = levelZeroTiles[1]; surface.tileProvider.terrainProvider = provider; scene.renderForSpecs(); levelZeroTiles = surface._levelZeroTiles; expect(levelZeroTiles[0]).toBe(levelZero0); expect(levelZeroTiles[1]).toBe(levelZero1); }); }, "WebGL" ); it("renders back side of globe when camera is near the poles", function () { var camera = scene.camera; camera.position = new Cartesian3( 2909078.1077849553, -38935053.40234136, -63252400.94628872 ); camera.direction = new Cartesian3( -0.03928753135806185, 0.44884096070717633, 0.8927476025569903 ); camera.up = new Cartesian3( 0.00002847975895320034, -0.8934368803055558, 0.4491887577613425 ); camera.right = new Cartesian3( 0.99922794650124, 0.017672942642764363, 0.03508814656908402 ); scene.cullingVolume = camera.frustum.computeCullingVolume( camera.position, camera.direction, camera.up ); return updateUntilDone(scene.globe).then(function () { // Both level zero tiles should be rendered. forEachRenderedTile(scene.globe._surface, 2, 2, function (tile) {}); }); }); it("throws if baseColor is assigned undefined", function () { expect(function () { scene.globe._surface.tileProvider.baseColor = undefined; }).toThrowDeveloperError(); }); it("clipping planes selectively disable rendering globe surface", function () { expect(scene).toRender([0, 0, 0, 255]); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var result; expect(scene).toRenderAndCall(function (rgba) { result = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); var clipPlane = new ClippingPlane(Cartesian3.UNIT_Z, -10000.0); scene.globe.clippingPlanes = new ClippingPlaneCollection({ planes: [clipPlane], }); expect(scene).notToRender(result); clipPlane.distance = 0.0; expect(scene).toRender(result); scene.globe.clippingPlanes = undefined; }); }); it("renders with clipping planes edge styling on globe surface", function () { expect(scene).toRender([0, 0, 0, 255]); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var result; expect(scene).toRenderAndCall(function (rgba) { result = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); var clipPlane = new ClippingPlane(Cartesian3.UNIT_Z, -1000.0); scene.globe.clippingPlanes = new ClippingPlaneCollection({ planes: [clipPlane], edgeWidth: 20.0, edgeColor: Color.RED, }); expect(scene).notToRender(result); clipPlane.distance = 0.0; expect(scene).toRender([255, 0, 0, 255]); scene.globe.clippingPlanes = undefined; }); }); it("renders with multiple clipping planes clipping regions according to the value of unionClippingPlane", function () { expect(scene).toRender([0, 0, 0, 255]); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe).then(function () { expect(scene).notToRender([0, 0, 0, 255]); var result; expect(scene).toRenderAndCall(function (rgba) { result = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); scene.globe.clippingPlanes = new ClippingPlaneCollection({ planes: [ new ClippingPlane(Cartesian3.UNIT_Z, -10000.0), new ClippingPlane(Cartesian3.UNIT_X, -1000.0), ], unionClippingRegions: true, }); expect(scene).notToRender(result); scene.globe.clippingPlanes.unionClippingRegions = false; expect(scene).toRender(result); scene.globe.clippingPlanes = undefined; }); }); it("No extra tiles culled with no clipping planes", function () { var globe = scene.globe; switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(globe).then(function () { expect(scene.frameState.commandList.length).toBe(4); }); }); it("Culls tiles when completely inside clipping region", function () { var globe = scene.globe; globe.clippingPlanes = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_Z, -1000000.0)], }); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(globe).then(function () { var surface = globe._surface; var tile = surface._levelZeroTiles[0]; expect(tile.isClipped).toBe(true); expect(scene.frameState.commandList.length).toBe(2); }); }); it("Doesn't cull, but clips tiles when intersecting clipping plane", function () { var globe = scene.globe; globe.clippingPlanes = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_Z, 0.0)], }); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(globe).then(function () { var surface = globe._surface; var tile = surface._levelZeroTiles[0]; expect(tile.isClipped).toBe(true); expect(scene.frameState.commandList.length).toBe(4); }); }); it("Doesn't cull or clip tiles when completely outside clipping region", function () { var globe = scene.globe; globe.clippingPlanes = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_Z, 10000000.0)], }); switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(globe).then(function () { var surface = globe._surface; var tile = surface._levelZeroTiles[0]; expect(tile.isClipped).toBe(false); expect(scene.frameState.commandList.length).toBe(4); }); }); it("destroys attached ClippingPlaneCollections that have been detached", function () { var clippingPlanes = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_Z, 10000000.0)], }); var globe = scene.globe; globe.clippingPlanes = clippingPlanes; expect(clippingPlanes.isDestroyed()).toBe(false); globe.clippingPlanes = undefined; expect(clippingPlanes.isDestroyed()).toBe(true); }); it("throws a DeveloperError when given a ClippingPlaneCollection attached to a Model", function () { var clippingPlanes = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_Z, 10000000.0)], }); var model = scene.primitives.add( Model.fromGltf({ url: "./Data/Models/Box/CesiumBoxTest.gltf", }) ); model.clippingPlanes = clippingPlanes; var globe = scene.globe; expect(function () { globe.clippingPlanes = clippingPlanes; }).toThrowDeveloperError(); }); it("cartographicLimitRectangle selectively enables rendering globe surface", function () { expect(scene).toRender([0, 0, 0, 255]); switchViewMode( SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84) ); var result; return updateUntilDone(scene.globe) .then(function () { expect(scene).notToRender([0, 0, 0, 255]); expect(scene).toRenderAndCall(function (rgba) { result = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); scene.globe.cartographicLimitRectangle = Rectangle.fromDegrees( -2, -2, -1, -1 ); expect(scene).notToRender(result); scene.camera.setView({ destination: scene.globe.cartographicLimitRectangle, }); return updateUntilDone(scene.globe); }) .then(function () { expect(scene).toRender(result); }); }); it("cartographicLimitRectangle defaults to Rectangle.MAX_VALUE", function () { scene.globe.cartographicLimitRectangle = undefined; expect( scene.globe.cartographicLimitRectangle.equals(Rectangle.MAX_VALUE) ).toBe(true); }); it("cartographicLimitRectangle culls tiles outside the region", function () { switchViewMode( SceneMode.COLUMBUS_VIEW, new GeographicProjection(Ellipsoid.WGS84) ); var unculledCommandCount; return updateUntilDone(scene.globe) .then(function () { unculledCommandCount = scene.frameState.commandList.length; scene.globe.cartographicLimitRectangle = Rectangle.fromDegrees( -2, -2, -1, -1 ); return updateUntilDone(scene.globe); }) .then(function () { expect(unculledCommandCount).toBeGreaterThan( scene.frameState.commandList.length ); }); }); it("cartographicLimitRectangle may cross the antimeridian", function () { switchViewMode( SceneMode.SCENE2D, new GeographicProjection(Ellipsoid.WGS84) ); var unculledCommandCount; return updateUntilDone(scene.globe) .then(function () { unculledCommandCount = scene.frameState.commandList.length; scene.globe.cartographicLimitRectangle = Rectangle.fromDegrees( 179, -2, -179, -1 ); return updateUntilDone(scene.globe); }) .then(function () { expect(unculledCommandCount).toBeGreaterThan( scene.frameState.commandList.length ); }); }); it("disables skirts and enables back face culling when camera is underground", function () { switchViewMode( SceneMode.SCENE3D, new GeographicProjection(Ellipsoid.WGS84) ); return updateUntilDone(scene.globe) .then(function () { var command = scene.frameState.commandList[0]; expect(command.count).toBe( command.owner.data.renderedMesh.indices.length ); // Has skirts expect(command.renderState.cull.enabled).toBe(true); // Has back face culling // Look underground scene.camera.setView({ destination: new Cartesian3( -746658.0557573901, -5644191.0002196245, 2863585.099969967 ), orientation: new HeadingPitchRoll( 0.3019699121236403, 0.07316306869231592, 0.0007089903642230055 ), }); return updateUntilDone(scene.globe); }) .then(function () { var command = scene.frameState.commandList[0]; expect(command.count).toBe( command.owner.data.renderedMesh.indexCountWithoutSkirts ); // No skirts expect(command.renderState.cull.enabled).toBe(false); // No back face culling }); }); }, "WebGL" );