import { ApproximateTerrainHeights } from "../../Source/Cesium.js"; import { arraySlice } from "../../Source/Cesium.js"; import { Color } from "../../Source/Cesium.js"; import { ColorGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { destroyObject } from "../../Source/Cesium.js"; import { DistanceDisplayConditionGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { GeometryInstance } from "../../Source/Cesium.js"; import { HeadingPitchRange } from "../../Source/Cesium.js"; import { Math as CesiumMath } from "../../Source/Cesium.js"; import { PolygonGeometry } from "../../Source/Cesium.js"; import { Rectangle } from "../../Source/Cesium.js"; import { RectangleGeometry } from "../../Source/Cesium.js"; import { ShowGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { Pass } from "../../Source/Cesium.js"; import { RenderState } from "../../Source/Cesium.js"; import { ClassificationType } from "../../Source/Cesium.js"; import { EllipsoidSurfaceAppearance } from "../../Source/Cesium.js"; import { GroundPrimitive } from "../../Source/Cesium.js"; import { InvertClassification } from "../../Source/Cesium.js"; import { Material } from "../../Source/Cesium.js"; import { PerInstanceColorAppearance } from "../../Source/Cesium.js"; import { Primitive } from "../../Source/Cesium.js"; import { StencilConstants } from "../../Source/Cesium.js"; import createCanvas from "../createCanvas.js"; import createScene from "../createScene.js"; import pollToPromise from "../pollToPromise.js"; describe( "Scene/GroundPrimitive", function () { var scene; var context; var ellipsoid; var rectangle; var depthColor; var rectColor; var rectangleInstance; var primitive; var globePrimitive; var tilesetPrimitive; var reusableGlobePrimitive; var reusableTilesetPrimitive; 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(0.0, 0.0, 1.0, 1.0) ); depthColor = depthColorAttribute.value; 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(); scene.postProcessStages.fxaa.enabled = false; context = scene.context; ellipsoid = Ellipsoid.WGS84; var bigRectangle = Rectangle.fromDegrees( -180 + CesiumMath.EPSILON4, -90 + CesiumMath.EPSILON4, 180 - CesiumMath.EPSILON4, 90 - CesiumMath.EPSILON4 ); reusableGlobePrimitive = createPrimitive(bigRectangle, Pass.GLOBE); reusableTilesetPrimitive = createPrimitive( bigRectangle, Pass.CESIUM_3D_TILE ); return GroundPrimitive.initializeTerrainHeights(); }); afterAll(function () { reusableGlobePrimitive.destroy(); reusableTilesetPrimitive.destroy(); scene.destroyForSpecs(); // Leave ground primitive uninitialized ApproximateTerrainHeights._initPromise = undefined; ApproximateTerrainHeights._terrainHeights = undefined; }); beforeEach(function () { scene.morphTo3D(0); rectangle = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0); // 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 ); var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(1.0, 1.0, 0.0, 1.0) ); rectColor = rectColorAttribute.value; rectangleInstance = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: rectangle, }), id: "rectangle", attributes: { color: rectColorAttribute, }, }); }); afterEach(function () { scene.primitives.removeAll(); scene.groundPrimitives.removeAll(); primitive = primitive && !primitive.isDestroyed() && primitive.destroy(); globePrimitive = globePrimitive && !globePrimitive.isDestroyed() && globePrimitive.destroy(); tilesetPrimitive = tilesetPrimitive && !tilesetPrimitive.isDestroyed() && tilesetPrimitive.destroy(); }); it("default constructs", function () { primitive = new GroundPrimitive(); expect(primitive.geometryInstances).not.toBeDefined(); expect(primitive.show).toEqual(true); expect(primitive.vertexCacheOptimize).toEqual(false); expect(primitive.interleave).toEqual(false); expect(primitive.compressVertices).toEqual(true); expect(primitive.releaseGeometryInstances).toEqual(true); expect(primitive.allowPicking).toEqual(true); expect(primitive.asynchronous).toEqual(true); expect(primitive.debugShowBoundingVolume).toEqual(false); expect(primitive.debugShowShadowVolume).toEqual(false); }); it("constructs with options", function () { var geometryInstances = []; primitive = new GroundPrimitive({ geometryInstances: geometryInstances, show: false, vertexCacheOptimize: true, interleave: true, compressVertices: false, releaseGeometryInstances: false, allowPicking: false, asynchronous: false, debugShowBoundingVolume: true, debugShowShadowVolume: true, }); expect(primitive.geometryInstances).toEqual(geometryInstances); expect(primitive.show).toEqual(false); expect(primitive.vertexCacheOptimize).toEqual(true); expect(primitive.interleave).toEqual(true); expect(primitive.compressVertices).toEqual(false); expect(primitive.releaseGeometryInstances).toEqual(false); expect(primitive.allowPicking).toEqual(false); expect(primitive.asynchronous).toEqual(false); expect(primitive.debugShowBoundingVolume).toEqual(true); expect(primitive.debugShowShadowVolume).toEqual(true); }); it("releases geometry instances when releaseGeometryInstances is true", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, releaseGeometryInstances: true, asynchronous: false, }); expect(primitive.geometryInstances).toBeDefined(); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); expect(primitive.geometryInstances).not.toBeDefined(); }); it("does not release geometry instances when releaseGeometryInstances is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, releaseGeometryInstances: false, asynchronous: false, }); expect(primitive.geometryInstances).toBeDefined(); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); expect(primitive.geometryInstances).toBeDefined(); }); it("adds afterRender promise to frame state", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, releaseGeometryInstances: false, asynchronous: false, }); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); return primitive.readyPromise.then(function (param) { expect(param.ready).toBe(true); }); }); it("does not render when geometryInstances is undefined", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: undefined, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); expect(scene.frameState.commandList.length).toEqual(0); }); it("does not render when show is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); expect(scene.frameState.commandList.length).toBeGreaterThan(0); primitive.show = false; scene.renderForSpecs(); expect(scene.frameState.commandList.length).toEqual(0); }); it("becomes ready when show is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = scene.groundPrimitives.add( new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, show: false, }) ); var ready = false; primitive.readyPromise.then(function () { ready = true; }); return pollToPromise(function () { scene.renderForSpecs(); return ready; }).then(function () { expect(ready).toEqual(true); }); }); it("does not render other than for the color or pick pass", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); var frameState = scene.frameState; frameState.passes.render = false; frameState.passes.pick = false; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); function expectRender(scene, color) { expect(scene).toRenderAndCall(function (rgba) { expect(arraySlice(rgba, 0, 4)).toEqual(color); }); } function expectRenderBlank(scene) { expect(scene).toRenderAndCall(function (rgba) { expect(arraySlice(rgba)).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); }); } function verifyGroundPrimitiveRender(primitive, color) { scene.camera.setView({ destination: rectangle }); scene.primitives.add(globePrimitive); scene.primitives.add(tilesetPrimitive); expectRenderBlank(scene); scene.groundPrimitives.add(primitive); primitive.classificationType = ClassificationType.BOTH; globePrimitive.show = false; tilesetPrimitive.show = true; expectRender(scene, color); globePrimitive.show = true; tilesetPrimitive.show = false; expectRender(scene, color); primitive.classificationType = ClassificationType.CESIUM_3D_TILE; globePrimitive.show = false; tilesetPrimitive.show = true; expectRender(scene, color); globePrimitive.show = true; tilesetPrimitive.show = false; expectRenderBlank(scene); primitive.classificationType = ClassificationType.TERRAIN; globePrimitive.show = false; tilesetPrimitive.show = true; expectRenderBlank(scene); globePrimitive.show = true; tilesetPrimitive.show = false; expectRender(scene, color); globePrimitive.show = true; tilesetPrimitive.show = true; } it("renders in 3D", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); }); it("renders GroundPrimitives with spherical texture coordinates across the IDL in 3D", function () { if (!GroundPrimitive.isSupported(scene)) { return; } var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(1.0, 1.0, 0.0, 1.0) ); var bigIdlRectangle = Rectangle.fromDegrees(176.0, 30.0, -176.0, 34.0); var bigIdlRectangleInstance = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: bigIdlRectangle, }), id: "rectangle", attributes: { color: rectColorAttribute, }, }); primitive = new GroundPrimitive({ geometryInstances: bigIdlRectangleInstance, asynchronous: false, }); scene.camera.setView({ destination: bigIdlRectangle }); scene.primitives.add(globePrimitive); expect(scene).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); }); scene.groundPrimitives.add(primitive); expect(scene).toRender(rectColor); }); it("renders GroundPrimitives with planar texture coordinates across the IDL in 3D", function () { if (!GroundPrimitive.isSupported(scene)) { return; } var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(1.0, 1.0, 0.0, 1.0) ); var smallIdlRectangle = Rectangle.fromDegrees(179.6, 30.0, -179.6, 30.9); var smallIdlRectangleInstance = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: smallIdlRectangle, }), id: "rectangle", attributes: { color: rectColorAttribute, }, }); primitive = new GroundPrimitive({ geometryInstances: smallIdlRectangleInstance, asynchronous: false, }); scene.camera.setView({ destination: smallIdlRectangle }); scene.primitives.add(globePrimitive); expect(scene).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba[0]).toEqual(0); }); scene.groundPrimitives.add(primitive); expect(scene).toRender(rectColor); }); it("renders in Columbus view when scene3DOnly is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.morphToColumbusView(0); verifyGroundPrimitiveRender(primitive, rectColor); }); it("renders in 2D when scene3DOnly is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.morphTo2D(0); verifyGroundPrimitiveRender(primitive, rectColor); }); describe("larger scene", function () { // Screen space techniques may produce unexpected results with 1x1 canvasses var largeScene; var largeSceneReusableGlobePrimitive; var largeSceneReusableTilesetPrimitive; beforeAll(function () { largeScene = createScene({ canvas: createCanvas(2, 2), }); var bigRectangle = Rectangle.fromDegrees( -180 + CesiumMath.EPSILON4, -90 + CesiumMath.EPSILON4, 180 - CesiumMath.EPSILON4, 90 - CesiumMath.EPSILON4 ); largeSceneReusableGlobePrimitive = createPrimitive( bigRectangle, Pass.GLOBE ); largeSceneReusableTilesetPrimitive = createPrimitive( bigRectangle, Pass.CESIUM_3D_TILE ); }); afterAll(function () { largeSceneReusableGlobePrimitive.destroy(); largeSceneReusableTilesetPrimitive.destroy(); largeScene.destroyForSpecs(); }); afterEach(function () { largeScene.primitives.removeAll(); largeScene.groundPrimitives.removeAll(); }); function verifyLargerScene(groundPrimitive, expectedColor, destination) { largeScene.renderForSpecs(); largeScene.postProcessStages.fxaa.enabled = false; largeScene.camera.setView({ destination: destination }); var largeSceneGlobePrimitive = new MockPrimitive( largeSceneReusableGlobePrimitive, Pass.GLOBE ); var largeSceneTilesetPrimitive = new MockPrimitive( largeSceneReusableTilesetPrimitive, Pass.CESIUM_3D_TILE ); largeScene.primitives.add(largeSceneGlobePrimitive); largeScene.primitives.add(largeSceneTilesetPrimitive); expectRenderBlank(largeScene); largeScene.groundPrimitives.add(groundPrimitive); groundPrimitive.classificationType = ClassificationType.BOTH; largeSceneGlobePrimitive.show = false; largeSceneTilesetPrimitive.show = true; expectRender(largeScene, expectedColor); globePrimitive.show = true; tilesetPrimitive.show = false; expectRender(largeScene, expectedColor); groundPrimitive.classificationType = ClassificationType.CESIUM_3D_TILE; largeSceneGlobePrimitive.show = false; largeSceneTilesetPrimitive.show = true; expectRender(largeScene, expectedColor); globePrimitive.show = true; largeSceneTilesetPrimitive.show = false; expectRenderBlank(largeScene); groundPrimitive.classificationType = ClassificationType.TERRAIN; largeSceneGlobePrimitive.show = false; largeSceneTilesetPrimitive.show = true; expectRenderBlank(largeScene); largeSceneGlobePrimitive.show = true; largeSceneTilesetPrimitive.show = false; expectRender(largeScene, expectedColor); largeSceneGlobePrimitive.show = true; largeSceneTilesetPrimitive.show = true; } it("renders batched instances", function () { if (!GroundPrimitive.isSupported(scene)) { return; } var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(0.0, 1.0, 1.0, 1.0) ); var rectangleInstance1 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, rectangle.south, rectangle.east, (rectangle.north + rectangle.south) * 0.5 ), }), id: "rectangle1", attributes: { color: rectColorAttribute, }, }); var rectangleInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, (rectangle.north + rectangle.south) * 0.5, rectangle.east, rectangle.north ), }), id: "rectangle2", attributes: { color: rectColorAttribute, }, }); var batchedPrimitive = new GroundPrimitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], asynchronous: false, }); verifyLargerScene(batchedPrimitive, [0, 255, 255, 255], rectangle); }); it("renders small GeometryInstances with texture", function () { if ( !GroundPrimitive.isSupported(scene) || !GroundPrimitive.supportsMaterials(scene) ) { return; } var whiteImageMaterial = Material.fromType(Material.DiffuseMapType); whiteImageMaterial.uniforms.image = "./Data/Images/White.png"; var radians = CesiumMath.toRadians(0.1); var west = rectangle.west; var south = rectangle.south; var smallRectangle = new Rectangle( west, south, west + radians, south + radians ); var smallRectanglePrimitive = new GroundPrimitive({ geometryInstances: new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: smallRectangle, }), id: "smallRectangle", }), appearance: new EllipsoidSurfaceAppearance({ aboveGround: false, flat: true, material: whiteImageMaterial, }), asynchronous: false, }); verifyLargerScene( smallRectanglePrimitive, [255, 255, 255, 255], smallRectangle ); }); it("renders large GeometryInstances with texture", function () { if ( !GroundPrimitive.isSupported(scene) || !GroundPrimitive.supportsMaterials(scene) ) { return; } var whiteImageMaterial = Material.fromType(Material.DiffuseMapType); whiteImageMaterial.uniforms.image = "./Data/Images/White.png"; var radians = CesiumMath.toRadians(1.1); var west = rectangle.west; var south = rectangle.south; var largeRectangle = new Rectangle( west, south, west + radians, south + radians ); var largeRectanglePrimitive = new GroundPrimitive({ geometryInstances: new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: largeRectangle, }), }), id: "largeRectangle", appearance: new EllipsoidSurfaceAppearance({ aboveGround: false, flat: true, material: whiteImageMaterial, }), asynchronous: false, }); verifyLargerScene( largeRectanglePrimitive, [255, 255, 255, 255], largeRectangle ); }); it("renders GeometryInstances with texture across the IDL", function () { if ( !GroundPrimitive.isSupported(scene) || !GroundPrimitive.supportsMaterials(scene) ) { return; } var whiteImageMaterial = Material.fromType(Material.DiffuseMapType); whiteImageMaterial.uniforms.image = "./Data/Images/White.png"; var largeRectangle = Rectangle.fromDegrees(179.0, 30.0, -179.0, 31.0); var largeRectanglePrimitive = new GroundPrimitive({ geometryInstances: new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: largeRectangle, }), }), id: "largeRectangle", appearance: new EllipsoidSurfaceAppearance({ aboveGround: false, flat: true, material: whiteImageMaterial, }), asynchronous: false, }); verifyLargerScene( largeRectanglePrimitive, [255, 255, 255, 255], largeRectangle ); }); }); it("renders with invert classification and an opaque color", function () { if (!GroundPrimitive.isSupported(scene)) { return; } scene.invertClassification = true; scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 1.0); rectangleInstance.attributes.show = new ShowGeometryInstanceAttribute( true ); primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.camera.setView({ destination: rectangle }); var invertedColor = new Array(4); invertedColor[0] = Color.floatToByte( Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red ); invertedColor[1] = Color.floatToByte( Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green ); invertedColor[2] = Color.floatToByte( Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue ); invertedColor[3] = 255; scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); expect(scene).toRender(rectColor); primitive.getGeometryInstanceAttributes("rectangle").show = [0]; expect(scene).toRender(depthColor); scene.invertClassification = false; }); it("renders with invert classification and a translucent color", function () { if (!GroundPrimitive.isSupported(scene)) { return; } if (!InvertClassification.isTranslucencySupported(scene.context)) { return; } scene.invertClassification = true; scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 0.25); rectangleInstance.attributes.show = new ShowGeometryInstanceAttribute( true ); primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.camera.setView({ destination: rectangle }); var invertedColor = new Array(4); invertedColor[0] = Color.floatToByte( Color.byteToFloat(depthColor[0]) * scene.invertClassificationColor.red * scene.invertClassificationColor.alpha ); invertedColor[1] = Color.floatToByte( Color.byteToFloat(depthColor[1]) * scene.invertClassificationColor.green * scene.invertClassificationColor.alpha ); invertedColor[2] = Color.floatToByte( Color.byteToFloat(depthColor[2]) * scene.invertClassificationColor.blue * scene.invertClassificationColor.alpha ); invertedColor[3] = 255; scene.primitives.add(tilesetPrimitive); expect(scene).toRender(invertedColor); scene.groundPrimitives.add(primitive); expect(scene).toRender(rectColor); primitive.getGeometryInstanceAttributes("rectangle").show = [0]; expect(scene).toRender(depthColor); scene.invertClassification = false; }); it("renders bounding volume with debugShowBoundingVolume", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, debugShowBoundingVolume: true, }); scene.groundPrimitives.add(primitive); scene.camera.setView({ destination: rectangle }); expect(scene).toRenderAndCall(function (rgba) { expect(rgba[1]).toBeGreaterThanOrEqualTo(0); expect(rgba[1]).toBeGreaterThanOrEqualTo(0); expect(rgba[2]).toBeGreaterThanOrEqualTo(0); expect(rgba[3]).toEqual(255); }); }); it("renders shadow volume with debugShowShadowVolume", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, debugShowShadowVolume: true, }); scene.groundPrimitives.add(primitive); scene.camera.setView({ destination: rectangle }); expect(scene).toRenderAndCall(function (rgba) { expect(rgba[1]).toBeGreaterThanOrEqualTo(0); expect(rgba[1]).toBeGreaterThanOrEqualTo(0); expect(rgba[2]).toBeGreaterThanOrEqualTo(0); expect(rgba[3]).toEqual(255); }); }); it("get per instance attributes", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(attributes.color).toBeDefined(); }); it("modify color instance attribute", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); scene.primitives.destroyPrimitives = false; scene.primitives.removeAll(); scene.primitives.destroyPrimitives = true; scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; var newColor = [255, 255, 255, 255]; var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(attributes.color).toBeDefined(); attributes.color = newColor; verifyGroundPrimitiveRender(primitive, newColor); }); it("modify show instance attribute", function () { if (!GroundPrimitive.isSupported(scene)) { return; } rectangleInstance.attributes.show = new ShowGeometryInstanceAttribute( true ); primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); scene.primitives.destroyPrimitives = false; scene.primitives.removeAll(); scene.primitives.destroyPrimitives = true; scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(attributes.show).toBeDefined(); attributes.show = [0]; verifyGroundPrimitiveRender(primitive, depthColor); }); it("renders with distance display condition per instance attribute", function () { if (!context.floatingPointTexture) { return; } if (!GroundPrimitive.isSupported(scene)) { return; } var near = 10000.0; var far = 1000000.0; var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(1.0, 1.0, 0.0, 1.0) ); var rectInstance = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: rectangle, }), id: "rect", attributes: { color: rectColorAttribute, distanceDisplayCondition: new DistanceDisplayConditionGeometryInstanceAttribute( near, far ), }, }); primitive = new GroundPrimitive({ geometryInstances: rectInstance, asynchronous: false, }); scene.primitives.add(globePrimitive); scene.groundPrimitives.add(primitive); scene.camera.setView({ destination: rect }); scene.renderForSpecs(); var boundingSphere = primitive.getGeometryInstanceAttributes("rect") .boundingSphere; var center = boundingSphere.center; var radius = boundingSphere.radius; scene.camera.lookAt( center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius) ); expect(scene).toRender([0, 0, 255, 255]); scene.camera.lookAt( center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + near + 1.0) ); expect(scene).notToRender([0, 0, 255, 255]); scene.camera.lookAt( center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + far + 1.0) ); expect(scene).toRender([0, 0, 255, 255]); }); it("get bounding sphere from per instance attribute", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(attributes.boundingSphere).toBeDefined(); }); it("getGeometryInstanceAttributes returns same object each time", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); var attributes = primitive.getGeometryInstanceAttributes("rectangle"); var attributes2 = primitive.getGeometryInstanceAttributes("rectangle"); expect(attributes).toBe(attributes2); }); it("picking in 3D", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); expect(scene).toPickAndCall(function (result) { expect(result.id).toEqual("rectangle"); }); }); it("picking in 2D", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.morphTo2D(0); verifyGroundPrimitiveRender(primitive, rectColor); expect(scene).toPickAndCall(function (result) { expect(result.id).toEqual("rectangle"); }); }); it("picking in Columbus View", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.morphToColumbusView(0); verifyGroundPrimitiveRender(primitive, rectColor); expect(scene).toPickAndCall(function (result) { expect(result.id).toEqual("rectangle"); }); }); it("picking without depth texture", function () { if (!GroundPrimitive.isSupported(scene)) { return; } spyOn(GroundPrimitive, "_supportsMaterials").and.callFake(function () { return false; }); primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); expect(scene).toPickAndCall(function (result) { expect(result.id).toEqual("rectangle"); }); }); it("does not pick when allowPicking is false", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, allowPicking: false, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); expect(scene).notToPick(); }); it("internally invalid asynchronous geometry resolves promise and sets ready", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: new GeometryInstance({ geometry: PolygonGeometry.fromPositions({ positions: [], }), attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED), }, }), compressVertices: false, }); scene.groundPrimitives.add(primitive); return pollToPromise(function () { scene.renderForSpecs(); return primitive.ready; }).then(function () { return primitive.readyPromise.then(function (arg) { expect(arg).toBe(primitive); expect(primitive.ready).toBe(true); }); }); }); it("internally invalid synchronous geometry resolves promise and sets ready", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: new GeometryInstance({ geometry: PolygonGeometry.fromPositions({ positions: [], }), attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED), }, }), asynchronous: false, compressVertices: false, }); scene.groundPrimitives.add(primitive); return pollToPromise(function () { scene.renderForSpecs(); return primitive.ready; }).then(function () { return primitive.readyPromise.then(function (arg) { expect(arg).toBe(primitive); expect(primitive.ready).toBe(true); }); }); }); it("update throws when batched instance colors are different and materials on GroundPrimitives are not supported", function () { if (!GroundPrimitive.isSupported(scene)) { return; } spyOn(GroundPrimitive, "_supportsMaterials").and.callFake(function () { return false; }); var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(0.0, 1.0, 1.0, 1.0) ); var rectangleInstance1 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, rectangle.south, rectangle.east, (rectangle.north + rectangle.south) * 0.5 ), }), id: "rectangle1", attributes: { color: rectColorAttribute, }, }); rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(1.0, 1.0, 0.0, 1.0) ); var rectangleInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, (rectangle.north + rectangle.south) * 0.5, rectangle.east, rectangle.north ), }), id: "rectangle2", attributes: { color: rectColorAttribute, }, }); primitive = new GroundPrimitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], asynchronous: false, }); expect(function () { verifyGroundPrimitiveRender(primitive, rectColorAttribute.value); }).toThrowDeveloperError(); }); it("update throws when one batched instance color is undefined", function () { if (!GroundPrimitive.isSupported(scene)) { return; } var rectColorAttribute = ColorGeometryInstanceAttribute.fromColor( new Color(0.0, 1.0, 1.0, 1.0) ); var rectangleInstance1 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, rectangle.south, rectangle.east, (rectangle.north + rectangle.south) * 0.5 ), }), id: "rectangle1", attributes: { color: rectColorAttribute, }, }); var rectangleInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ ellipsoid: ellipsoid, rectangle: new Rectangle( rectangle.west, (rectangle.north + rectangle.south) * 0.5, rectangle.east, rectangle.north ), }), id: "rectangle2", }); primitive = new GroundPrimitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], asynchronous: false, }); expect(function () { verifyGroundPrimitiveRender(primitive, rectColorAttribute.value); }).toThrowDeveloperError(); }); it("setting per instance attribute throws when value is undefined", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(function () { attributes.color = undefined; }).toThrowDeveloperError(); }); it("can disable picking when asynchronous", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: true, allowPicking: false, }); scene.groundPrimitives.add(primitive); return pollToPromise(function () { scene.renderForSpecs(); return primitive.ready; }).then(function () { var attributes = primitive.getGeometryInstanceAttributes("rectangle"); expect(function () { attributes.color = undefined; }).toThrowDeveloperError(); }); }); it("getGeometryInstanceAttributes throws without id", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); expect(function () { primitive.getGeometryInstanceAttributes(); }).toThrowDeveloperError(); }); it("getGeometryInstanceAttributes throws if update was not called", function () { primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); expect(function () { primitive.getGeometryInstanceAttributes("rectangle"); }).toThrowDeveloperError(); }); it("getGeometryInstanceAttributes returns undefined if id does not exist", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); verifyGroundPrimitiveRender(primitive, rectColor); expect( primitive.getGeometryInstanceAttributes("unknown") ).not.toBeDefined(); }); it("isDestroyed", function () { primitive = new GroundPrimitive(); expect(primitive.isDestroyed()).toEqual(false); primitive.destroy(); expect(primitive.isDestroyed()).toEqual(true); }); it("renders when using asynchronous pipeline", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, }); scene.groundPrimitives.add(primitive); return pollToPromise(function () { scene.renderForSpecs(); return primitive.ready; }).then(function () { // verifyGroundPrimitiveRender adds the primitive, so remove it to avoid being added twice. scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; verifyGroundPrimitiveRender(primitive, rectColor); }); }); it("destroy before asynchronous pipeline is complete", function () { if (!GroundPrimitive.isSupported(scene)) { return; } primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, }); scene.groundPrimitives.add(primitive); scene.renderForSpecs(); primitive.destroy(); expect(primitive.isDestroyed()).toEqual(true); // The primitive has already been destroyed, so remove it from the scene so it doesn't get destroyed again. scene.groundPrimitives.destroyPrimitives = false; scene.groundPrimitives.removeAll(); scene.groundPrimitives.destroyPrimitives = true; }); it("creating a synchronous primitive throws if initializeTerrainHeights wasn't called", function () { // Make it seem like initializeTerrainHeights was never called var initPromise = ApproximateTerrainHeights._initPromise; var terrainHeights = ApproximateTerrainHeights._terrainHeights; ApproximateTerrainHeights._initPromise = undefined; ApproximateTerrainHeights._terrainHeights = undefined; primitive = new GroundPrimitive({ geometryInstances: rectangleInstance, asynchronous: false, }); scene.groundPrimitives.add(primitive); if (GroundPrimitive.isSupported(scene)) { expect(function () { scene.renderForSpecs(); }).toThrowDeveloperError(); } // Set back to initialized state ApproximateTerrainHeights._initPromise = initPromise; ApproximateTerrainHeights._terrainHeights = terrainHeights; }); }, "WebGL" );