import { BoundingSphere } from "../../Source/Cesium.js"; import { BoxGeometry } from "../../Source/Cesium.js"; import { Cartesian3 } from "../../Source/Cesium.js"; import { ColorGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { ComponentDatatype } from "../../Source/Cesium.js"; import { CylinderGeometry } from "../../Source/Cesium.js"; import { defined } from "../../Source/Cesium.js"; import { DistanceDisplayConditionGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { Geometry } from "../../Source/Cesium.js"; import { GeometryAttribute } from "../../Source/Cesium.js"; import { GeometryInstance } from "../../Source/Cesium.js"; import { GeometryInstanceAttribute } from "../../Source/Cesium.js"; import { HeadingPitchRange } from "../../Source/Cesium.js"; import { Math as CesiumMath } from "../../Source/Cesium.js"; import { Matrix4 } from "../../Source/Cesium.js"; import { PerspectiveFrustum } from "../../Source/Cesium.js"; import { PolygonGeometry } from "../../Source/Cesium.js"; import { PrimitiveType } from "../../Source/Cesium.js"; import { Rectangle } from "../../Source/Cesium.js"; import { RectangleGeometry } from "../../Source/Cesium.js"; import { ShowGeometryInstanceAttribute } from "../../Source/Cesium.js"; import { Transforms } from "../../Source/Cesium.js"; import { Camera } from "../../Source/Cesium.js"; import { MaterialAppearance } from "../../Source/Cesium.js"; import { PerInstanceColorAppearance } from "../../Source/Cesium.js"; import { Primitive } from "../../Source/Cesium.js"; import { SceneMode } from "../../Source/Cesium.js"; import BadGeometry from "../BadGeometry.js"; import createContext from "../createContext.js"; import createFrameState from "../createFrameState.js"; import createScene from "../createScene.js"; import pollToPromise from "../pollToPromise.js"; describe( "Scene/Primitive", function () { var scene; var context; var frameStateContext; var frameState; var ellipsoid; var rectangle1; var rectangle2; var rectangleInstance1; var rectangleInstance2; var primitive; beforeAll(function () { scene = createScene(); scene.primitives.destroyPrimitives = false; context = scene.context; ellipsoid = Ellipsoid.WGS84; frameStateContext = createContext(); }); afterAll(function () { scene.destroyForSpecs(); frameStateContext.destroyForSpecs(); }); beforeEach(function () { scene.morphTo3D(0); var camera = scene.camera; camera.frustum = new PerspectiveFrustum(); camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; camera.frustum.fov = CesiumMath.toRadians(60.0); scene.frameState.passes.render = true; scene.frameState.passes.pick = false; // Mock frameState, separate from scene.frameState, used for test that call primitive.update directly frameState = createFrameState(frameStateContext); rectangle1 = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0); rectangle2 = Rectangle.fromDegrees(70.0, 20.0, 80.0, 30.0); var translation = Cartesian3.multiplyByScalar( Cartesian3.normalize( ellipsoid.cartographicToCartesian(Rectangle.center(rectangle1)), new Cartesian3() ), 2.0, new Cartesian3() ); rectangleInstance1 = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rectangle1, }), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), id: "rectangle1", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), show: new ShowGeometryInstanceAttribute(true), }, }); translation = Cartesian3.multiplyByScalar( Cartesian3.normalize( ellipsoid.cartographicToCartesian(Rectangle.center(rectangle2)), new Cartesian3() ), 3.0, new Cartesian3() ); rectangleInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rectangle2, }), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), id: "rectangle2", attributes: { color: new ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 1.0), show: new ShowGeometryInstanceAttribute(true), }, }); }); afterEach(function () { scene.primitives.removeAll(); primitive = primitive && !primitive.isDestroyed() && primitive.destroy(); }); it("default constructs", function () { primitive = new Primitive(); expect(primitive.geometryInstances).not.toBeDefined(); expect(primitive.appearance).not.toBeDefined(); expect(primitive.depthFailAppearance).not.toBeDefined(); expect(primitive.modelMatrix).toEqual(Matrix4.IDENTITY); 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.cull).toEqual(true); expect(primitive.asynchronous).toEqual(true); expect(primitive.debugShowBoundingVolume).toEqual(false); }); it("Constructs with options", function () { var geometryInstances = {}; var appearance = {}; var depthFailAppearance = {}; var modelMatrix = Matrix4.fromUniformScale(5.0); primitive = new Primitive({ geometryInstances: geometryInstances, appearance: appearance, depthFailAppearance: depthFailAppearance, modelMatrix: modelMatrix, show: false, vertexCacheOptimize: true, interleave: true, compressVertices: false, releaseGeometryInstances: false, allowPicking: false, cull: false, asynchronous: false, debugShowBoundingVolume: true, }); expect(primitive.geometryInstances).toEqual(geometryInstances); expect(primitive.appearance).toEqual(appearance); expect(primitive.depthFailAppearance).toEqual(depthFailAppearance); expect(primitive.modelMatrix).toEqual(modelMatrix); 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.cull).toEqual(false); expect(primitive.asynchronous).toEqual(false); expect(primitive.debugShowBoundingVolume).toEqual(true); }); it("releases geometry instances when releaseGeometryInstances is true", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), releaseGeometryInstances: true, asynchronous: false, }); expect(primitive.geometryInstances).toBeDefined(); scene.primitives.add(primitive); scene.renderForSpecs(); expect(primitive.geometryInstances).not.toBeDefined(); }); it("does not release geometry instances when releaseGeometryInstances is false", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), releaseGeometryInstances: false, asynchronous: false, }); expect(primitive.geometryInstances).toBeDefined(); scene.primitives.add(primitive); scene.renderForSpecs(); expect(primitive.geometryInstances).toBeDefined(); }); it("adds afterRender promise to frame state", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), releaseGeometryInstances: false, asynchronous: false, }); scene.primitives.add(primitive); scene.renderForSpecs(); return primitive.readyPromise.then(function (param) { expect(param.ready).toBe(true); }); }); it("does not render when geometryInstances is an empty array", function () { primitive = new Primitive({ geometryInstances: [], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.commandList.length = 0; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); it("does not render when show is false", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); primitive.update(frameState); expect(frameState.commandList.length).toBeGreaterThan(0); frameState.commandList.length = 0; primitive.show = false; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); it("does not render other than for the color or pick pass", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.passes.render = false; frameState.passes.pick = false; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); it("does not render when scene3DOnly is true and the scene mode is SCENE2D", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.mode = SceneMode.SCENE2D; frameState.scene3DOnly = true; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); it("does not render when scene3DOnly is true and the scene mode is COLUMBUS_VIEW", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.mode = SceneMode.COLUMBUS_VIEW; frameState.scene3DOnly = true; primitive.update(frameState); expect(frameState.commandList.length).toEqual(0); }); it("renders in two passes for closed, translucent geometry", function () { primitive = new Primitive({ geometryInstances: new GeometryInstance({ geometry: BoxGeometry.fromDimensions({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, dimensions: new Cartesian3(500000.0, 500000.0, 500000.0), }), id: "box", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 0.5), }, }), appearance: new PerInstanceColorAppearance({ closed: true, translucent: true, }), asynchronous: false, }); var frameState = scene.frameState; frameState.commandList.length = 0; // set scene3DOnly to true so that the geometry is not split due to the IDL frameState.scene3DOnly = true; scene.primitives.add(primitive); scene.render(); expect(frameState.commandList.length).toEqual(2); }); function verifyPrimitiveRender(primitive, rectangle) { scene.primitives.removeAll(); if (defined(rectangle)) { scene.camera.setView({ destination: rectangle }); } expect(scene).toRender([0, 0, 0, 255]); scene.primitives.add(primitive); expect(scene).notToRender([0, 0, 0, 255]); } it("renders in Columbus view when scene3DOnly is false", function () { scene.frameState.scene3DOnly = false; primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.morphToColumbusView(0); verifyPrimitiveRender(primitive, rectangle1); verifyPrimitiveRender(primitive, rectangle2); }); it("renders in 2D when scene3DOnly is false", function () { scene.frameState.scene3DOnly = false; primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.morphTo2D(0); verifyPrimitiveRender(primitive, rectangle1); verifyPrimitiveRender(primitive, rectangle2); }); it("renders RTC", function () { var dimensions = new Cartesian3(400.0, 300.0, 500.0); var positionOnEllipsoid = Cartesian3.fromDegrees(-105.0, 45.0); var boxModelMatrix = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid), new Cartesian3(0.0, 0.0, dimensions.z * 0.5), new Matrix4() ); var boxGeometry = BoxGeometry.createGeometry( BoxGeometry.fromDimensions({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, dimensions: dimensions, }) ); var positions = boxGeometry.attributes.position.values; var newPositions = new Float32Array(positions.length); for (var i = 0; i < positions.length; ++i) { newPositions[i] = positions[i]; } boxGeometry.attributes.position.values = newPositions; boxGeometry.attributes.position.componentDatatype = ComponentDatatype.FLOAT; BoundingSphere.transform( boxGeometry.boundingSphere, boxModelMatrix, boxGeometry.boundingSphere ); var boxGeometryInstance = new GeometryInstance({ geometry: boxGeometry, attributes: { color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 0.5), }, }); var primitive = new Primitive({ geometryInstances: boxGeometryInstance, appearance: new PerInstanceColorAppearance({ closed: true, }), asynchronous: false, allowPicking: false, rtcCenter: boxGeometry.boundingSphere.center, }); // create test camera var camera = scene.camera; var testCamera = new Camera(scene); testCamera.viewBoundingSphere(boxGeometry.boundingSphere); scene.camera = testCamera; scene.frameState.scene3DOnly = true; verifyPrimitiveRender(primitive); scene.camera = camera; }); it("renders with depth fail appearance", function () { var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var translation = Cartesian3.multiplyByScalar( Cartesian3.normalize( ellipsoid.cartographicToCartesian(Rectangle.center(rect)), new Cartesian3() ), 100.0, new Cartesian3() ); var rectInstance = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rect, }), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), id: "rect", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), }, }); var p0 = new Primitive({ geometryInstances: rectInstance, appearance: new PerInstanceColorAppearance({ translucent: false, }), asynchronous: false, }); var rectInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rect, }), id: "rect2", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), depthFailColor: new ColorGeometryInstanceAttribute( 1.0, 0.0, 1.0, 1.0 ), }, }); var p1 = new Primitive({ geometryInstances: rectInstance2, appearance: new PerInstanceColorAppearance({ translucent: false, }), depthFailAppearance: new PerInstanceColorAppearance({ translucent: false, }), asynchronous: false, }); scene.primitives.add(p0); scene.primitives.add(p1); scene.camera.setView({ destination: rect }); scene.renderForSpecs(); expect(scene).toRender([255, 0, 255, 255]); }); it("pick with depth fail appearance", function () { var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var translation = Cartesian3.multiplyByScalar( Cartesian3.normalize( ellipsoid.cartographicToCartesian(Rectangle.center(rect)), new Cartesian3() ), 100.0, new Cartesian3() ); var rectInstance = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rect, }), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), id: "rect", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), }, }); var p0 = new Primitive({ geometryInstances: rectInstance, appearance: new PerInstanceColorAppearance({ translucent: false, }), asynchronous: false, }); var rectInstance2 = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rect, }), id: "rect2", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), depthFailColor: new ColorGeometryInstanceAttribute( 1.0, 0.0, 1.0, 1.0 ), }, }); var p1 = new Primitive({ geometryInstances: rectInstance2, appearance: new PerInstanceColorAppearance({ translucent: false, }), depthFailAppearance: new PerInstanceColorAppearance({ translucent: false, }), asynchronous: false, }); scene.primitives.add(p0); scene.primitives.add(p1); scene.camera.setView({ destination: rect }); scene.renderForSpecs(); expect(scene).toPickAndCall(function (result) { expect(result.primitive).toEqual(p1); expect(result.id).toEqual("rect2"); }); }); it("RTC throws with more than one instance", function () { expect(function () { return new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance({ closed: true, }), asynchronous: false, allowPicking: false, rtcCenter: Cartesian3.ZERO, }); }).toThrowDeveloperError(); }); it("RTC throws if the scene is not 3D only", function () { scene.frameState.scene3DOnly = false; var primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance({ closed: true, }), asynchronous: false, allowPicking: false, rtcCenter: Cartesian3.ZERO, }); expect(function () { verifyPrimitiveRender(primitive); }).toThrowDeveloperError(); }); it("updates model matrix for one instance in 3D", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); primitive.update(frameState); var commands = frameState.commandList; expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(primitive.modelMatrix); var modelMatrix = Matrix4.fromUniformScale(10.0); primitive.modelMatrix = modelMatrix; commands.length = 0; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(modelMatrix); }); it("updates model matrix for more than one instance in 3D with equal model matrices in 3D only scene", function () { var modelMatrix = Matrix4.fromUniformScale(2.0); rectangleInstance1.modelMatrix = modelMatrix; rectangleInstance2.modelMatrix = modelMatrix; primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.scene3DOnly = true; var commands = frameState.commandList; commands.length = 0; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(modelMatrix); modelMatrix = Matrix4.fromUniformScale(10.0); primitive.modelMatrix = modelMatrix; commands.length = 0; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(modelMatrix); }); it("computes model matrix when given one for a single instance and for the primitive in 3D only", function () { var instanceModelMatrix = Matrix4.fromUniformScale(2.0); var dimensions = new Cartesian3(400000.0, 300000.0, 500000.0); var positionOnEllipsoid = Cartesian3.fromDegrees(-105.0, 45.0); var primitiveModelMatrix = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid), new Cartesian3(0.0, 0.0, dimensions.z * 0.5), new Matrix4() ); var boxGeometry = BoxGeometry.fromDimensions({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, dimensions: dimensions, }); var boxGeometryInstance = new GeometryInstance({ geometry: boxGeometry, modelMatrix: instanceModelMatrix, attributes: { color: new ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 1.0), }, }); primitive = new Primitive({ geometryInstances: boxGeometryInstance, modelMatrix: primitiveModelMatrix, appearance: new PerInstanceColorAppearance({ translucent: false, closed: true, }), asynchronous: false, }); var expectedModelMatrix = Matrix4.multiplyTransformation( primitiveModelMatrix, instanceModelMatrix, new Matrix4() ); frameState.scene3DOnly = true; var commands = frameState.commandList; commands.length = 0; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(expectedModelMatrix); }); it("update model matrix throws in Columbus view", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.mode = SceneMode.COLUMBUS_VIEW; frameState.scene3DOnly = false; var commands = frameState.commandList; commands.length = 0; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(Matrix4.IDENTITY); var modelMatrix = Matrix4.fromUniformScale(10.0); primitive.modelMatrix = modelMatrix; commands.length = 0; expect(function () { primitive.update(frameState); }).toThrowDeveloperError(); }); it("update model matrix throws in 2D", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); frameState.mode = SceneMode.SCENE2D; frameState.scene3DOnly = false; var commands = frameState.commandList; primitive.update(frameState); expect(commands.length).toEqual(1); expect(commands[0].modelMatrix).toEqual(Matrix4.IDENTITY); var modelMatrix = Matrix4.fromUniformScale(10.0); primitive.modelMatrix = modelMatrix; commands.length = 0; expect(function () { primitive.update(frameState); }).toThrowDeveloperError(); }); it("renders bounding volume with debugShowBoundingVolume", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, debugShowBoundingVolume: true, }); scene.primitives.add(primitive); scene.camera.setView({ destination: rectangle1 }); expect(scene).toRenderAndCall(function (rgba) { expect(rgba[0]).not.toEqual(0); expect(rgba[1]).toBeGreaterThanOrEqualTo(0); expect(rgba[2]).toBeGreaterThanOrEqualTo(0); expect(rgba[3]).toEqual(255); }); }); it("transforms to world coordinates", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); verifyPrimitiveRender(primitive, rectangle1); verifyPrimitiveRender(primitive, rectangle2); expect(primitive.modelMatrix).toEqual(Matrix4.IDENTITY); }); it("does not transform to world coordinates", function () { rectangleInstance2.modelMatrix = Matrix4.clone( rectangleInstance1.modelMatrix ); primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.frameState.scene3DOnly = true; verifyPrimitiveRender(primitive, rectangle1); verifyPrimitiveRender(primitive, rectangle2); expect(primitive.modelMatrix).not.toEqual(Matrix4.IDENTITY); scene.frameState.scene3DOnly = true; }); it("get common per instance attributes", function () { rectangleInstance2.attributes.not_used = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype.FLOAT, componentsPerAttribute: 1, value: [0.5], }); primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.primitives.add(primitive); scene.renderForSpecs(); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(attributes.color).toBeDefined(); expect(attributes.show).toBeDefined(); attributes = primitive.getGeometryInstanceAttributes("rectangle2"); expect(attributes.color).toBeDefined(); expect(attributes.show).toBeDefined(); expect(attributes.not_used).not.toBeDefined(); }); it("modify color instance attribute", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.camera.setView({ destination: rectangle1 }); scene.primitives.add(primitive); var pixels; expect(scene).toRenderAndCall(function (rgba) { pixels = rgba; expect(rgba).not.toEqual([0, 0, 0, 255]); }); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(attributes.color).toBeDefined(); attributes.color = [255, 255, 255, 255]; expect(scene).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); expect(rgba).not.toEqual(pixels); }); }); it("modify show instance attribute", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.primitives.add(primitive); scene.camera.setView({ destination: rectangle1 }); expect(scene).notToRender([0, 0, 0, 255]); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(attributes.show).toBeDefined(); attributes.show = [0]; expect(scene).toRender([0, 0, 0, 255]); }); it("get bounding sphere from per instance attribute", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); verifyPrimitiveRender(primitive, rectangle1); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(attributes.boundingSphere).toBeDefined(); }); it("renders with distance display condition per instance attribute", function () { if (!context.floatingPointTexture) { return; } var near = 10000.0; var far = 1000000.0; var rect = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var translation = Cartesian3.multiplyByScalar( Cartesian3.normalize( ellipsoid.cartographicToCartesian(Rectangle.center(rect)), new Cartesian3() ), 2.0, new Cartesian3() ); var rectInstance = new GeometryInstance({ geometry: new RectangleGeometry({ vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, ellipsoid: ellipsoid, rectangle: rect, }), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), id: "rect", attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), distanceDisplayCondition: new DistanceDisplayConditionGeometryInstanceAttribute( near, far ), }, }); primitive = new Primitive({ geometryInstances: rectInstance, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.primitives.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, 0, 255]); scene.camera.lookAt( center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + near + 1.0) ); expect(scene).notToRender([0, 0, 0, 255]); scene.camera.lookAt( center, new HeadingPitchRange(0.0, -CesiumMath.PI_OVER_TWO, radius + far + 1.0) ); expect(scene).toRender([0, 0, 0, 255]); }); it("primitive with display condition properly transforms boundingSphere", function () { var near = 10000.0; var far = 1000000.0; var translation = new Cartesian3(10, 20, 30); var cylinder = new GeometryInstance({ id: "cylinder", vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, geometry: new CylinderGeometry({ length: 10, topRadius: 10, bottomRadius: 10, }), attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), show: new ShowGeometryInstanceAttribute(true), distanceDisplayCondition: new DistanceDisplayConditionGeometryInstanceAttribute( near, far ), }, }); primitive = new Primitive({ geometryInstances: cylinder, appearance: new PerInstanceColorAppearance(), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), asynchronous: false, }); scene.primitives.add(primitive); scene.frameState.scene3DOnly = true; scene.renderForSpecs(); var boundingSphere = primitive.getGeometryInstanceAttributes("cylinder") .boundingSphere; var center = boundingSphere.center; expect(center).toEqual(translation); }); it("primitive without display condition properly transforms boundingSphere", function () { var translation = new Cartesian3(10, 20, 30); var cylinder = new GeometryInstance({ id: "cylinder", vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT, geometry: new CylinderGeometry({ length: 10, topRadius: 10, bottomRadius: 10, }), attributes: { color: new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0), show: new ShowGeometryInstanceAttribute(true), }, }); primitive = new Primitive({ geometryInstances: cylinder, appearance: new PerInstanceColorAppearance(), modelMatrix: Matrix4.fromTranslation(translation, new Matrix4()), asynchronous: false, }); scene.primitives.add(primitive); scene.frameState.scene3DOnly = true; scene.renderForSpecs(); var boundingSphere = primitive.getGeometryInstanceAttributes("cylinder") .boundingSphere; var center = boundingSphere.center; expect(center).toEqual(translation); }); it("getGeometryInstanceAttributes returns same object each time", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); verifyPrimitiveRender(primitive, rectangle1); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); var attributes2 = primitive.getGeometryInstanceAttributes("rectangle1"); expect(attributes).toBe(attributes2); }); it("picking", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); verifyPrimitiveRender(primitive, rectangle1); expect(scene).toPickAndCall(function (result) { expect(result.primitive).toEqual(primitive); expect(result.id).toEqual("rectangle1"); }); verifyPrimitiveRender(primitive, rectangle2); expect(scene).toPickAndCall(function (result) { expect(result.primitive).toEqual(primitive); expect(result.id).toEqual("rectangle2"); }); }); it("does not pick when allowPicking is false", function () { primitive = new Primitive({ geometryInstances: [rectangleInstance1], appearance: new PerInstanceColorAppearance(), allowPicking: false, asynchronous: false, }); verifyPrimitiveRender(primitive, rectangle1); expect(scene).notToPick(); }); it("does not cull when cull is false", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, cull: false, }); frameState.commandList.length = 0; primitive.update(frameState); expect(frameState.commandList[0].cull).toEqual(false); }); it("update throws when geometry primitive types are different", function () { primitive = new Primitive({ geometryInstances: [ new GeometryInstance({ geometry: new Geometry({ attributes: { position: new GeometryAttribute({ componentDatatype: ComponentDatatype.FLOAT, componentsPerAttribute: 3, values: new Float32Array([1.0, 2.0, 3.0, 4.0]), }), }, primitiveType: PrimitiveType.LINES, }), }), new GeometryInstance({ geometry: new Geometry({ attributes: { position: new GeometryAttribute({ componentDatatype: ComponentDatatype.FLOAT, componentsPerAttribute: 3, values: new Float32Array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), }), }, primitiveType: PrimitiveType.TRIANGLES, }), }), ], appearance: new PerInstanceColorAppearance(), asynchronous: false, }); expect(function () { primitive.update(frameState); }).toThrowDeveloperError(); }); it("failed geometry rejects promise and throws on next update", function () { primitive = new Primitive({ geometryInstances: [ new GeometryInstance({ geometry: new BadGeometry(), }), ], appearance: new MaterialAppearance({ materialSupport: MaterialAppearance.MaterialSupport.ALL, }), compressVertices: false, }); scene.frameState.afterRender.length = 0; scene.primitives.add(primitive); return pollToPromise(function () { for (var i = 0; i < frameState.afterRender.length; ++i) { frameState.afterRender[i](); return true; } primitive.update(frameState); return false; }).then(function () { return primitive.readyPromise .then(function () { fail("should not be called"); }) .otherwise(function (e) { expect(e).toBe(primitive._error); // Use toThrow since the error is thrown by RequireJS for the web worker import script expect(function () { scene.render(); }).toThrow(); }); }); }); it("internally invalid asynchronous geometry resolves promise and sets ready", function () { primitive = new Primitive({ geometryInstances: [ new GeometryInstance({ geometry: PolygonGeometry.fromPositions({ positions: [], }), }), ], appearance: new MaterialAppearance({ materialSupport: MaterialAppearance.MaterialSupport.ALL, }), compressVertices: false, }); scene.frameState.afterRender.length = 0; return pollToPromise(function () { for (var i = 0; i < frameState.afterRender.length; ++i) { frameState.afterRender[i](); return true; } primitive.update(frameState); return false; }).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 () { primitive = new Primitive({ geometryInstances: [ new GeometryInstance({ geometry: PolygonGeometry.fromPositions({ positions: [], }), }), ], appearance: new MaterialAppearance({ materialSupport: MaterialAppearance.MaterialSupport.ALL, }), asynchronous: false, compressVertices: false, }); scene.frameState.afterRender.length = 0; return pollToPromise(function () { if (scene.frameState.afterRender.length > 0) { scene.frameState.afterRender[0](); return true; } primitive.update(scene.frameState); return false; }).then(function () { return primitive.readyPromise.then(function (arg) { expect(arg).toBe(primitive); expect(primitive.ready).toBe(true); }); }); }); it("can mix valid and invalid geometry", function () { var instances = []; instances.push(rectangleInstance1); instances.push( new GeometryInstance({ geometry: PolygonGeometry.fromPositions({ positions: [], }), attributes: { color: new ColorGeometryInstanceAttribute(1.0, 0.0, 1.0, 1.0), }, id: "invalid", }) ); instances.push(rectangleInstance2); primitive = new Primitive({ geometryInstances: instances, appearance: new PerInstanceColorAppearance({ flat: true, }), }); return pollToPromise(function () { primitive.update(frameState); if (frameState.afterRender.length > 0) { frameState.afterRender[0](); } return primitive.ready; }).then(function () { expect( primitive.getGeometryInstanceAttributes("rectangle1").boundingSphere ).toBeDefined(); expect( primitive.getGeometryInstanceAttributes("rectangle2").boundingSphere ).toBeDefined(); expect( primitive.getGeometryInstanceAttributes("invalid").boundingSphere ).not.toBeDefined(); }); }); it("shader validation", function () { if (!!window.webglStub) { return; } primitive = new Primitive({ geometryInstances: [rectangleInstance1, rectangleInstance2], appearance: new MaterialAppearance({ materialSupport: MaterialAppearance.MaterialSupport.ALL, }), asynchronous: false, compressVertices: false, }); expect(function () { primitive.update(frameState); }).toThrowDeveloperError(); }); it("setting per instance attribute throws when value is undefined", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); primitive.update(frameState); var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(function () { attributes.color = undefined; }).toThrowDeveloperError(); }); it("can disable picking when asynchronous", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: true, allowPicking: false, }); frameState.afterRender.length = 0; scene.primitives.add(primitive); return pollToPromise(function () { if (frameState.afterRender.length > 0) { frameState.afterRender[0](); } scene.render(); return primitive.ready; }).then(function () { var attributes = primitive.getGeometryInstanceAttributes("rectangle1"); expect(function () { attributes.color = undefined; }).toThrowDeveloperError(); }); }); it("getGeometryInstanceAttributes throws without id", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.primitives.add(primitive); scene.renderForSpecs(); expect(function () { primitive.getGeometryInstanceAttributes(); }).toThrowDeveloperError(); }); it("getGeometryInstanceAttributes throws if update was not called", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); expect(function () { primitive.getGeometryInstanceAttributes("rectangle1"); }).toThrowDeveloperError(); }); it("getGeometryInstanceAttributes returns undefined if id does not exist", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), asynchronous: false, }); scene.primitives.add(primitive); scene.renderForSpecs(); expect( primitive.getGeometryInstanceAttributes("unknown") ).not.toBeDefined(); }); it("isDestroyed", function () { primitive = new Primitive(); expect(primitive.isDestroyed()).toEqual(false); primitive.destroy(); expect(primitive.isDestroyed()).toEqual(true); }); it("renders when using asynchronous pipeline", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance({ flat: true, }), }); var frameState = scene.frameState; return pollToPromise(function () { primitive.update(frameState); for (var i = 0; i < frameState.afterRender.length; ++i) { frameState.afterRender[i](); } return primitive.ready; }).then(function () { verifyPrimitiveRender(primitive, rectangle1); }); }); it("destroy before asynchronous pipeline is complete", function () { primitive = new Primitive({ geometryInstances: rectangleInstance1, appearance: new PerInstanceColorAppearance(), }); primitive.update(frameState); primitive.destroy(); expect(primitive.isDestroyed()).toEqual(true); }); }, "WebGL" );