From 4d7562fca28ec8a0d22a0cd1d2581bb5c9f7e95d Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 24 Sep 2019 17:24:54 +0200 Subject: [PATCH] Add a new WebGLPointsLayer type using the shader builder utilities This required adding a `a_index` attribute in the points layer renderer to be able to make the precomputed shaders to work. --- doc/errors/index.md | 4 ++ src/ol/layer/WebGLPoints.js | 69 ++++++++++++++++++++++++++++ src/ol/renderer/webgl/Layer.js | 13 +++--- src/ol/renderer/webgl/PointsLayer.js | 2 + src/ol/style/LiteralStyle.js | 56 ++++++++++++++++++++++ src/ol/webgl/ShaderBuilder.js | 17 ++----- 6 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/ol/layer/WebGLPoints.js create mode 100644 src/ol/style/LiteralStyle.js diff --git a/doc/errors/index.md b/doc/errors/index.md index 4213a0b4d1..c4f34dc824 100644 --- a/doc/errors/index.md +++ b/doc/errors/index.md @@ -240,3 +240,7 @@ Support for the `OES_element_index_uint` WebGL extension is mandatory for WebGL ### 64 Layer opacity must be a number. + +### 65 + +A symbol literal representation must be defined on the style supplied to a `WebGLPointsLayer` instance. diff --git a/src/ol/layer/WebGLPoints.js b/src/ol/layer/WebGLPoints.js new file mode 100644 index 0000000000..8f6cdc4665 --- /dev/null +++ b/src/ol/layer/WebGLPoints.js @@ -0,0 +1,69 @@ +/** + * @module ol/layer/WebGLPoints + */ +import VectorLayer from './Vector.js'; +import {assign} from '../obj.js'; +import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js'; +import {getSymbolFragmentShader, getSymbolVertexShader} from '../webgl/ShaderBuilder.js'; +import {assert} from '../asserts.js'; + + +/** + * @typedef {Object} Options + * @property {import('../style/LiteralStyle.js').LiteralStyle} literalStyle Literal style to apply to the layer features. + * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. + * @property {number} [opacity=1] Opacity (0, 1). + * @property {boolean} [visible=true] Visibility. + * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be + * rendered outside of this extent. + * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers + * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed + * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` + * method was used. + * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be + * visible. + * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will + * be visible. + * @property {import("../source/Vector.js").default} [source] Source. + */ + + +/** + * @classdesc + * Layer optimized for rendering large point datasets. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @fires import("../render/Event.js").RenderEvent + * @api + */ +class WebGLPointsLayer extends VectorLayer { + /** + * @param {Options} options Options. + */ + constructor(options) { + const baseOptions = assign({}, options); + + super(baseOptions); + + /** + * @type {import('../style/LiteralStyle.js').LiteralStyle} + */ + this.literalStyle = options.literalStyle; + + assert(this.literalStyle.symbol !== undefined, 65); + } + + /** + * @inheritDoc + */ + createRenderer() { + return new WebGLPointsLayerRenderer(this, { + vertexShader: getSymbolVertexShader(this.literalStyle.symbol), + fragmentShader: getSymbolFragmentShader() + }); + } +} + +export default WebGLPointsLayer; diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index c8e6cbbc20..2f0779efba 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -123,9 +123,9 @@ const tmpArray_ = []; const bufferPositions_ = {vertexPosition: 0, indexPosition: 0}; export const POINT_INSTRUCTIONS_COUNT = 13; -export const POINT_VERTEX_STRIDE = 12; +export const POINT_VERTEX_STRIDE = 13; -function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha) { +function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha, index) { buffer[pos + 0] = x; buffer[pos + 1] = y; buffer[pos + 2] = offsetX; @@ -138,6 +138,7 @@ function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, ro buffer[pos + 9] = green; buffer[pos + 10] = blue; buffer[pos + 11] = alpha; + buffer[pos + 12] = index; } function writeCustomAttrs(buffer, pos, customAttrs) { @@ -202,19 +203,19 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf const baseIndex = vPos / stride; // push vertices for each of the four quad corners (first standard then custom attributes) - writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha); + writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha, 0); writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha); + writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, 1); writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha); + writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, 2); writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha); + writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha, 3); writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); vPos += stride; diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 7708d23c4b..4a557a9476 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -408,6 +408,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6); this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7); this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8); + this.helper.enableAttributeArray('a_index', 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 12); return true; } @@ -568,6 +569,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6); this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7); this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8); + this.helper.enableAttributeArray('a_index', 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 12); const renderCount = this.indicesBuffer_.getSize(); this.helper.drawElements(0, renderCount); diff --git a/src/ol/style/LiteralStyle.js b/src/ol/style/LiteralStyle.js new file mode 100644 index 0000000000..61971d67b2 --- /dev/null +++ b/src/ol/style/LiteralStyle.js @@ -0,0 +1,56 @@ +/** + * Literal Style objects differ from standard styles in that they cannot + * be functions and are made up of simple objects instead of classes. + * @module ol/style/LiteralStyle + */ + +/** + * Here are a few samples of literal style objects: + * ```js + * const style = { + * symbol: { + * symbolType: 'circle', + * size: 8, + * color: '#33AAFF', + * opacity: 0.9 + * } + * } + * ``` + * + * ```js + * const style = { + * symbol: { + * symbolType: 'image', + * offset: [0, 12], + * size: [4, 8], + * src: '../static/exclamation-mark.png' + * } + * } + * ``` + * + * @typedef {Object} LiteralStyle + * @property {LiteralSymbolStyle} [symbol] Symbol representation. + */ + +/** + * @enum {string} + */ +export const SymbolType = { + CIRCLE: 'circle', + SQUARE: 'square', + TRIANGLE: 'triangle', + IMAGE: 'image' +}; + + +/** + * @typedef {Object} LiteralSymbolStyle + * @property {number|Array.} size Size, mandatory. + * @property {SymbolType} symbolType Symbol type to use, either a regular shape or an image. + * @property {string} [src] Path to the image to be used for the symbol. Only required with `symbolType: 'image'`. + * @property {import("../color.js").Color|string} [color='#FFFFFF'] Color used for the representation (either fill, line or symbol). + * @property {number} [opacity=0] Opacity. + * @property {Array.} [offset] Offset on X and Y axis for symbols. If not specified, the symbol will be centered. + * @property {Array.} [textureCoord] Texture coordinates. If not specified, the whole texture will be used (range for 0 to 1 on both axes). + * @property {boolean} [rotateWithView=false] Specify whether the symbol must rotate with the view or stay upwards. + */ diff --git a/src/ol/webgl/ShaderBuilder.js b/src/ol/webgl/ShaderBuilder.js index fb14cfff6d..cc0115f4b6 100644 --- a/src/ol/webgl/ShaderBuilder.js +++ b/src/ol/webgl/ShaderBuilder.js @@ -16,17 +16,8 @@ export function formatNumber(v) { } /** - * @typedef {Object} SymbolShaderParameters - * @property {number|Array.} size Size. - * @property {boolean} [rotateWithView] Rotate with view. - * @property {Array.} [offset] Offset. - * @property {Array.} [textureCoord] Texture coordinates: u0, v0, u1, v1. - * @property {number} [opacity] Opacity. - * @property {import("../color.js").Color|string} [color] Color. - */ - -/** - * Generates a symbol vertex shader, i.e. a shader intended to be used on point geometries. + * Generates a symbol vertex shader from a literal style, + * intended to be used on point geometries. * * Expected the following attributes to be present in the attribute array: * `vec2 a_position`, `float a_index` (being the index of the vertex in the quad, 0 to 3). @@ -34,7 +25,7 @@ export function formatNumber(v) { * Transmits the following varyings to the fragment shader: * `vec2 v_texCoord`, `float v_opacity`, `vec4 v_color` * - * @param {SymbolShaderParameters} parameters Parameters for the shader. + * @param {import('../style/LiteralStyle.js').LiteralSymbolStyle} parameters Parameters for the shader. * @returns {string} The full shader as a string. */ export function getSymbolVertexShader(parameters) { @@ -82,7 +73,7 @@ void main(void) { } /** - * Generates a symbol fragment shader, i.e. a shader intended to be used on point geometries. + * Generates a symbol fragment shader intended to be used on point geometries. * * Expected the following varyings to be transmitted by the vertex shader: * `vec2 v_texCoord`, `float v_opacity`, `vec4 v_color`