You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1399 lines
45 KiB
JavaScript

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"
);