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.

737 lines
24 KiB
JavaScript

import { ComponentDatatype } from "../../Source/Cesium.js";
import { Geometry } from "../../Source/Cesium.js";
import { GeometryAttribute } from "../../Source/Cesium.js";
import { GeometryPipeline } from "../../Source/Cesium.js";
import { IndexDatatype } from "../../Source/Cesium.js";
import { PrimitiveType } from "../../Source/Cesium.js";
import { BufferUsage } from "../../Source/Cesium.js";
import { ClearCommand } from "../../Source/Cesium.js";
import { DrawCommand } from "../../Source/Cesium.js";
import { ShaderProgram } from "../../Source/Cesium.js";
import { VertexArray } from "../../Source/Cesium.js";
import createContext from "../createContext.js";
describe(
"Renderer/VertexArrayFactory",
function () {
var context;
var va;
var sp;
beforeAll(function () {
context = createContext();
});
afterAll(function () {
context.destroyForSpecs();
});
afterEach(function () {
va = va && va.destroy();
sp = sp && sp.destroy();
});
it("throws when there is no context", function () {
expect(function () {
return VertexArray.fromGeometry();
}).toThrowDeveloperError();
});
it("creates with no optional arguments", function () {
va = VertexArray.fromGeometry({
context: context,
});
expect(va.numberOfAttributes).toEqual(0);
expect(va.indexBuffer).not.toBeDefined();
});
it("creates with no geometry", function () {
va = VertexArray.fromGeometry({
context: context,
interleave: true,
});
expect(va.numberOfAttributes).toEqual(0);
expect(va.indexBuffer).not.toBeDefined();
});
it("creates a single-attribute vertex (non-interleaved)", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
});
expect(va.numberOfAttributes).toEqual(1);
expect(va.indexBuffer).not.toBeDefined();
var position = geometry.attributes.position;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(
position.componentDatatype
);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(
position.componentsPerAttribute
);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(0); // Tightly packed
expect(va.getAttribute(0).vertexBuffer.usage).toEqual(
BufferUsage.DYNAMIC_DRAW
); // Default
});
it("creates a single-attribute vertex (interleaved)", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
interleave: true,
bufferUsage: BufferUsage.STATIC_DRAW,
});
expect(va.numberOfAttributes).toEqual(1);
expect(va.indexBuffer).not.toBeDefined();
var position = geometry.attributes.position;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(
position.componentDatatype
);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(
position.componentsPerAttribute
);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(
ComponentDatatype.getSizeInBytes(position.componentDatatype) *
position.componentsPerAttribute
);
expect(va.getAttribute(0).vertexBuffer.usage).toEqual(
BufferUsage.STATIC_DRAW
);
});
it("creates a homogeneous multiple-attribute vertex (non-interleaved)", function () {
var geometry = new Geometry({
attributes: {
customPosition: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 2.0, 2.0, 2.0],
}),
customNormal: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [1.0, 1.0, 1.0, 3.0, 3.0, 3.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
});
expect(va.numberOfAttributes).toEqual(2);
expect(va.indexBuffer).not.toBeDefined();
var position = geometry.attributes.customPosition;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(
position.componentDatatype
);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(
position.componentsPerAttribute
);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(0); // Tightly packed
var normal = geometry.attributes.customNormal;
expect(va.getAttribute(1).index).toEqual(1);
expect(va.getAttribute(1).componentDatatype).toEqual(
normal.componentDatatype
);
expect(va.getAttribute(1).componentsPerAttribute).toEqual(
normal.componentsPerAttribute
);
expect(va.getAttribute(1).offsetInBytes).toEqual(0);
expect(va.getAttribute(1).strideInBytes).toEqual(0); // Tightly packed
expect(va.getAttribute(0).vertexBuffer).not.toBe(
va.getAttribute(1).vertexBuffer
);
});
it("creates a homogeneous multiple-attribute vertex (interleaved)", function () {
var geometry = new Geometry({
attributes: {
customPosition: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 2.0, 2.0, 2.0],
}),
customNormal: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [1.0, 1.0, 1.0, 3.0, 3.0, 3.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
interleave: true,
});
expect(va.numberOfAttributes).toEqual(2);
expect(va.indexBuffer).not.toBeDefined();
var position = geometry.attributes.customPosition;
var normal = geometry.attributes.customNormal;
var expectedStride =
ComponentDatatype.getSizeInBytes(position.componentDatatype) *
position.componentsPerAttribute +
ComponentDatatype.getSizeInBytes(normal.componentDatatype) *
normal.componentsPerAttribute;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(
position.componentDatatype
);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(
position.componentsPerAttribute
);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(expectedStride);
expect(va.getAttribute(1).index).toEqual(1);
expect(va.getAttribute(1).componentDatatype).toEqual(
normal.componentDatatype
);
expect(va.getAttribute(1).componentsPerAttribute).toEqual(
normal.componentsPerAttribute
);
expect(va.getAttribute(1).offsetInBytes).toEqual(
ComponentDatatype.getSizeInBytes(position.componentDatatype) *
position.componentsPerAttribute
);
expect(va.getAttribute(1).strideInBytes).toEqual(expectedStride);
expect(va.getAttribute(0).vertexBuffer).toBe(
va.getAttribute(1).vertexBuffer
);
});
it("creates a heterogeneous multiple-attribute vertex (interleaved)", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 2.0, 2.0, 2.0],
}),
colors: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 4,
values: [1, 1, 1, 1, 2, 2, 2, 2],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: GeometryPipeline.createAttributeLocations(geometry),
interleave: true,
});
expect(va.numberOfAttributes).toEqual(2);
expect(va.indexBuffer).not.toBeDefined();
var position = geometry.attributes.position;
var colors = geometry.attributes.colors;
var expectedStride =
ComponentDatatype.getSizeInBytes(position.componentDatatype) *
position.componentsPerAttribute +
ComponentDatatype.getSizeInBytes(colors.componentDatatype) *
colors.componentsPerAttribute;
expect(va.getAttribute(0).index).toEqual(0);
expect(va.getAttribute(0).componentDatatype).toEqual(
position.componentDatatype
);
expect(va.getAttribute(0).componentsPerAttribute).toEqual(
position.componentsPerAttribute
);
expect(va.getAttribute(0).offsetInBytes).toEqual(0);
expect(va.getAttribute(0).strideInBytes).toEqual(expectedStride);
expect(va.getAttribute(1).index).toEqual(1);
expect(va.getAttribute(1).componentDatatype).toEqual(
colors.componentDatatype
);
expect(va.getAttribute(1).componentsPerAttribute).toEqual(
colors.componentsPerAttribute
);
expect(va.getAttribute(1).offsetInBytes).toEqual(
ComponentDatatype.getSizeInBytes(position.componentDatatype) *
position.componentsPerAttribute
);
expect(va.getAttribute(1).strideInBytes).toEqual(expectedStride);
expect(va.getAttribute(0).vertexBuffer).toBe(
va.getAttribute(1).vertexBuffer
);
});
it("sorts interleaved attributes from large to small components", function () {
var geometry = new Geometry({
attributes: {
bytes: new GeometryAttribute({
componentDatatype: ComponentDatatype.BYTE,
componentsPerAttribute: 1,
values: [0],
}),
shorts: new GeometryAttribute({
componentDatatype: ComponentDatatype.SHORT,
componentsPerAttribute: 1,
values: [1],
}),
floats: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: [2],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var attributeLocations = GeometryPipeline.createAttributeLocations(
geometry
);
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: attributeLocations,
interleave: true,
});
expect(va.numberOfAttributes).toEqual(3);
var vertexBuffer = va.getAttribute(0).vertexBuffer;
expect(vertexBuffer).toBe(va.getAttribute(1).vertexBuffer);
expect(vertexBuffer).toBe(va.getAttribute(2).vertexBuffer);
expect(vertexBuffer.sizeInBytes).toEqual(8); // Includes 1 byte per-vertex padding
// Validate via rendering
var vs =
"attribute float bytes; " +
"attribute float shorts; " +
"attribute float floats; " +
"varying vec4 fsColor; " +
"void main() { " +
" gl_PointSize = 1.0; " +
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0); " +
" fsColor = vec4((bytes == 0.0) && (shorts == 1.0) && (floats == 2.0)); " +
"}";
var fs =
"varying vec4 fsColor; " +
"void main() { " +
" gl_FragColor = fsColor; " +
"}";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
});
command.execute(context);
expect(context).toReadPixels([255, 255, 255, 255]);
});
it("sorts interleaved attributes from large to small components (2)", function () {
var geometry = new Geometry({
attributes: {
color: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 4,
normalize: true,
values: [255, 0, 0, 255, 0, 255, 0, 255],
}),
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var attributeLocations = GeometryPipeline.createAttributeLocations(
geometry
);
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: attributeLocations,
interleave: true,
});
expect(va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(32); // No per-vertex padding needed
// Validate via rendering
var vs =
"attribute vec3 position; " +
"attribute vec4 color; " +
"varying vec4 fsColor; " +
"void main() { " +
" gl_PointSize = 1.0; " +
" gl_Position = vec4(position, 1.0); " +
" fsColor = color; " +
"}";
var fs =
"varying vec4 fsColor; " +
"void main() { " +
" gl_FragColor = fsColor; " +
"}";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
offset: 0,
count: 1,
});
command.execute(context);
expect(context).toReadPixels([255, 0, 0, 255]);
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
offset: 1,
count: 1,
});
command.execute(context);
expect(context).toReadPixels([0, 255, 0, 255]);
});
it("sorts interleaved attributes from large to small components (3)", function () {
var geometry = new Geometry({
attributes: {
unsignedByteAttribute: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 2,
values: [1, 2],
}),
unsignedShortAttribute: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
componentsPerAttribute: 1,
values: [3],
}),
byteAttribute: new GeometryAttribute({
componentDatatype: ComponentDatatype.BYTE,
componentsPerAttribute: 1,
values: [4],
}),
shortAttribute: new GeometryAttribute({
componentDatatype: ComponentDatatype.SHORT,
componentsPerAttribute: 1,
values: [5],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var attributeLocations = GeometryPipeline.createAttributeLocations(
geometry
);
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: attributeLocations,
interleave: true,
});
expect(va.numberOfAttributes).toEqual(4);
expect(va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(8); // Includes 1 byte per-vertex padding
// Validate via rendering
var vs =
"attribute vec2 unsignedByteAttribute; " +
"attribute float unsignedShortAttribute; " +
"attribute float byteAttribute; " +
"attribute float shortAttribute; " +
"varying vec4 fsColor; " +
"void main() { " +
" gl_PointSize = 1.0; " +
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0); " +
" fsColor = vec4((unsignedByteAttribute.x == 1.0) && (unsignedByteAttribute.y == 2.0) && (unsignedShortAttribute == 3.0) && (byteAttribute == 4.0) && (shortAttribute == 5.0)); " +
"}";
var fs =
"varying vec4 fsColor; " +
"void main() { " +
" gl_FragColor = fsColor; " +
"}";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
});
command.execute(context);
expect(context).toReadPixels([255, 255, 255, 255]);
});
it("creates a custom interleaved vertex", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
}),
color: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 3,
normalize: true,
values: [255, 0, 0, 0, 255, 0],
}),
normal: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
}),
temperature: new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
componentsPerAttribute: 1,
values: [75, 100],
}),
},
primitiveType: PrimitiveType.POINTS,
});
var attributeLocations = GeometryPipeline.createAttributeLocations(
geometry
);
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: attributeLocations,
interleave: true,
});
expect(va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(2 * 32); // Includes 3 byte per-vertex padding
// Validate via rendering
var vs =
"attribute vec3 position; " +
"attribute vec3 color; " +
"attribute vec3 normal; " +
"attribute float temperature; " +
"varying vec4 fsColor; " +
"void main() { " +
" gl_PointSize = 1.0; " +
" gl_Position = vec4(position, 1.0); " +
" if ((normal == vec3(1.0, 0.0, 0.0)) && (temperature == 75.0)) { " +
" fsColor = vec4(color, 1.0); " +
" } " +
" else {" +
" fsColor = vec4(1.0); " +
" }" +
"}";
var fs =
"varying vec4 fsColor; " +
"void main() { " +
" gl_FragColor = fsColor; " +
"}";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
offset: 0,
count: 1,
});
command.execute(context);
expect(context).toReadPixels([255, 0, 0, 255]);
var vs2 =
"attribute vec3 position; " +
"attribute vec3 color; " +
"attribute vec3 normal; " +
"attribute float temperature; " +
"varying vec4 fsColor; " +
"void main() { " +
" gl_PointSize = 1.0; " +
" gl_Position = vec4(position, 1.0); " +
" if ((normal == vec3(0.0, 1.0, 0.0)) && (temperature == 100.0)) { " +
" fsColor = vec4(color, 1.0); " +
" } " +
" else {" +
" fsColor = vec4(1.0); " +
" }" +
"}";
sp = sp.destroy();
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs2,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
offset: 1,
count: 1,
});
command.execute(context);
expect(context).toReadPixels([0, 255, 0, 255]);
});
it("creates an index buffer", function () {
var geometry = new Geometry({
attributes: {},
indices: [0],
primitiveType: PrimitiveType.POINTS,
});
var va = VertexArray.fromGeometry({
context: context,
geometry: geometry,
});
expect(va.numberOfAttributes).toEqual(0);
expect(va.indexBuffer).toBeDefined();
expect(va.indexBuffer.usage).toEqual(BufferUsage.DYNAMIC_DRAW); // Default
expect(va.indexBuffer.indexDatatype).toEqual(
IndexDatatype.UNSIGNED_SHORT
);
expect(va.indexBuffer.numberOfIndices).toEqual(geometry.indices.length);
});
it("throws with different number of interleaved attributes", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: [0.0],
}),
normal: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: [1.0, 2.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
expect(function () {
return VertexArray.fromGeometry({
context: context,
geometry: geometry,
interleave: true,
});
}).toThrowRuntimeError();
});
it("throws with duplicate indices", function () {
var geometry = new Geometry({
attributes: {
position: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: [0.0],
}),
normal: new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 1,
values: [1.0],
}),
},
primitiveType: PrimitiveType.POINTS,
});
expect(function () {
return VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: {
position: 0,
normal: 0,
},
});
}).toThrowDeveloperError();
});
},
"WebGL"
);