diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index bd731fb817..555232c179 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -85,67 +85,13 @@ class WebGLLayerRenderer extends LayerRenderer { } - -/** - * @param {Float32Array} instructions Instructons array in which to write. - * @param {number} elementIndex Index from which render instructions will be written. - * @param {number} x Point center X coordinate - * @param {number} y Point center Y coordinate - * @param {number} u0 Left texture coordinate - * @param {number} v0 Bottom texture coordinate - * @param {number} u1 Right texture coordinate - * @param {number} v1 Top texture coordinate - * @param {number} size Radius of the point - * @param {number} opacity Opacity - * @param {boolean} rotateWithView If true, the point will stay aligned with the view - * @param {Array} color Array holding red, green, blue, alpha values - * @return {number} Index from which the next element should be written - * @private - */ -export function writePointFeatureInstructions(instructions, elementIndex, x, y, u0, v0, u1, v1, size, opacity, rotateWithView, color) { - let i = elementIndex; - instructions[i++] = x; - instructions[i++] = y; - instructions[i++] = u0; - instructions[i++] = v0; - instructions[i++] = u1; - instructions[i++] = v1; - instructions[i++] = size; - instructions[i++] = opacity; - instructions[i++] = rotateWithView ? 1 : 0; - instructions[i++] = color[0]; - instructions[i++] = color[1]; - instructions[i++] = color[2]; - instructions[i++] = color[3]; - return i; -} - const tmpArray_ = []; const bufferPositions_ = {vertexPosition: 0, indexPosition: 0}; -export const POINT_INSTRUCTIONS_COUNT = 13; -export const POINT_VERTEX_STRIDE = 13; - -function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha, index) { +function writePointVertex(buffer, pos, x, y, index) { buffer[pos + 0] = x; buffer[pos + 1] = y; - buffer[pos + 2] = offsetX; - buffer[pos + 3] = offsetY; - buffer[pos + 4] = u; - buffer[pos + 5] = v; - buffer[pos + 6] = opacity; - buffer[pos + 7] = rotateWithView; - buffer[pos + 8] = red; - buffer[pos + 9] = green; - buffer[pos + 10] = blue; - buffer[pos + 11] = alpha; - buffer[pos + 12] = index; -} - -function writeCustomAttrs(buffer, pos, customAttrs) { - if (customAttrs.length) { - buffer.set(customAttrs, pos); - } + buffer[pos + 2] = index; } /** @@ -161,42 +107,27 @@ function writeCustomAttrs(buffer, pos, customAttrs) { * @param {number} elementIndex Index from which render instructions will be read. * @param {Float32Array} vertexBuffer Buffer in the form of a typed array. * @param {Uint32Array} indexBuffer Buffer in the form of a typed array. + * @param {number} customAttributesCount Amount of custom attributes for each element. * @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0. - * @param {number} [count] Amount of render instructions that will be read. Default value is POINT_INSTRUCTIONS_COUNT - * but a higher value can be provided; all values beyond the default count will be put in the vertices buffer as - * is, thus allowing specifying custom attributes. Please note: this value should not vary inside the same buffer or - * rendering will break. * @return {BufferPositions} New buffer positions where to write next * @property {number} vertexPosition New position in the vertex buffer where future writes should start. * @property {number} indexPosition New position in the index buffer where future writes should start. * @private */ -export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, bufferPositions, count) { - const count_ = count > POINT_INSTRUCTIONS_COUNT ? count : POINT_INSTRUCTIONS_COUNT; +export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, customAttributesCount, bufferPositions) { + // This is for x, y and index + const baseVertexAttrsCount = 3; + const baseInstructionsCount = 2; + const stride = baseVertexAttrsCount + customAttributesCount; const x = instructions[elementIndex + 0]; const y = instructions[elementIndex + 1]; - const u0 = instructions[elementIndex + 2]; - const v0 = instructions[elementIndex + 3]; - const u1 = instructions[elementIndex + 4]; - const v1 = instructions[elementIndex + 5]; - const size = instructions[elementIndex + 6]; - const opacity = instructions[elementIndex + 7]; - const rotateWithView = instructions[elementIndex + 8]; - const red = instructions[elementIndex + 9]; - const green = instructions[elementIndex + 10]; - const blue = instructions[elementIndex + 11]; - const alpha = instructions[elementIndex + 12]; - - // the default vertex buffer stride is 12, plus additional custom values if any - const baseStride = POINT_VERTEX_STRIDE; - const stride = baseStride + count_ - POINT_INSTRUCTIONS_COUNT; // read custom numerical attributes on the feature const customAttrs = tmpArray_; - customAttrs.length = count_ - POINT_INSTRUCTIONS_COUNT; + customAttrs.length = customAttributesCount; for (let i = 0; i < customAttrs.length; i++) { - customAttrs[i] = instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + i]; + customAttrs[i] = instructions[elementIndex + baseInstructionsCount + i]; } let vPos = bufferPositions ? bufferPositions.vertexPosition : 0; @@ -204,20 +135,20 @@ 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, 0); - writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + writePointVertex(vertexBuffer, vPos, x, y, 0); + customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, 1); - writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + writePointVertex(vertexBuffer, vPos, x, y, 1); + customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, 2); - writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + writePointVertex(vertexBuffer, vPos, x, y, 2); + customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); vPos += stride; - writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha, 3); - writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + writePointVertex(vertexBuffer, vPos, x, y, 3); + customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); vPos += stride; indexBuffer[iPos++] = baseIndex; indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 3; diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 767e4c564d..c07d20c72c 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -2,23 +2,17 @@ * @module ol/renderer/webgl/PointsLayer */ import WebGLArrayBuffer from '../../webgl/Buffer.js'; -import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js'; -import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js'; +import {ARRAY_BUFFER, DYNAMIC_DRAW, ELEMENT_ARRAY_BUFFER} from '../../webgl.js'; +import {AttributeType, DefaultUniform} from '../../webgl/Helper.js'; import GeometryType from '../../geom/GeometryType.js'; -import WebGLLayerRenderer, { - colorDecodeId, - colorEncodeId, - getBlankImageData, - POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE, WebGLWorkerMessageType, - writePointFeatureInstructions -} from './Layer.js'; +import WebGLLayerRenderer, {colorDecodeId, colorEncodeId, getBlankImageData, WebGLWorkerMessageType} from './Layer.js'; import ViewHint from '../../ViewHint.js'; import {createEmpty, equals} from '../../extent.js'; import { + apply as applyTransform, create as createTransform, makeInverse as makeInverseTransform, - multiply as multiplyTransform, - apply as applyTransform + multiply as multiplyTransform } from '../../transform.js'; import {create as createWebGLWorker} from '../../worker/webgl.js'; import {getUid} from '../../util.js'; @@ -92,29 +86,21 @@ const HIT_FRAGMENT_SHADER = ` gl_FragColor = v_color; }`; +/** + * @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different + * for each feature. + * @property {string} name Attribute name. + * @property {function(import("../../Feature").default):number} callback This callback computes the numerical value of the + * attribute for a given feature. + */ + /** * @typedef {Object} Options - * @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the - * source to compute the size of the quad on screen (in pixels). This is only done on source change. - * @property {function(import("../../Feature").default, number):number} [coordCallback] Will be called on every feature in the - * source to compute the coordinate of the quad center on screen (in pixels). This is only done on source change. - * The second argument is 0 for `x` component and 1 for `y`. - * @property {function(import("../../Feature").default, number):number} [texCoordCallback] Will be called on every feature in the - * source to compute the texture coordinates of each corner of the quad (without effect if no `texture` option defined). This is only done on source change. - * The second argument is 0 for `u0` component, 1 for `v0`, 2 for `u1`, and 3 for `v1`. - * @property {function(import("../../Feature").default, Array=):Array} [colorCallback] Will be called on every feature in the - * source to compute the color for use in the fragment shader (available as the `v_color` varying). This is only done on source change. - * The return value should be between an array of R, G, B, A values between 0 and 1. To reduce unnecessary - * allocation, the function is called with a reusable array that can serve as the return value after updating - * the R, G, B, and A values. - * @property {function(import("../../Feature").default):number} [opacityCallback] Will be called on every feature in the - * source to compute the opacity of the quad on screen (from 0 to 1). This is only done on source change. - * Note: this is multiplied with the color of the point which can also have an alpha value < 1. - * @property {function(import("../../Feature").default):boolean} [rotateWithViewCallback] Will be called on every feature in the - * source to compute whether the quad on screen must stay upwards (`false`) or follow the view rotation (`true`). Default is `false`. - * This is only done on source change. - * @property {HTMLCanvasElement|HTMLImageElement|ImageData} [texture] Texture to use on points. `texCoordCallback` and `sizeCallback` - * must be defined for this to have any effect. + * @property {Array} [attributes] These attributes will be read from the features in the source and then + * passed to the GPU. The `name` property of each attribute will serve as its identifier: + * * In the vertex shader as an `attribute` by prefixing it with `a_` + * * In the fragment shader as a `varying` by prefixing it with `v_` + * Please note that these can only be numerical values. * @property {string} [vertexShader] Vertex shader source * @property {string} [fragmentShader] Fragment shader source * @property {Object.} [uniforms] Uniform definitions for the post process steps @@ -224,7 +210,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const options = opt_options || {}; const uniforms = options.uniforms || {}; - uniforms.u_texture = options.texture || getBlankImageData(); + uniforms.u_texture = getBlankImageData(); const projectionMatrixTransform = createTransform(); uniforms[DefaultUniform.PROJECTION_MATRIX] = projectionMatrixTransform; @@ -248,28 +234,53 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { VERTEX_SHADER ); - this.sizeCallback_ = options.sizeCallback || function() { - return 1; - }; - this.coordCallback_ = options.coordCallback || function(feature, index) { - const geom = feature.getGeometry(); - return geom.getCoordinates()[index]; - }; - this.opacityCallback_ = options.opacityCallback || function() { - return 1; - }; - this.texCoordCallback_ = options.texCoordCallback || function(feature, index) { - return index < 2 ? 0 : 1; - }; + const customAttributes = options.attributes ? + options.attributes.map(function(attribute) { + return { + name: 'a_' + attribute.name, + size: 1, + type: AttributeType.FLOAT + }; + }) : []; - this.colorArray_ = [1, 1, 1, 1]; - this.colorCallback_ = options.colorCallback || function(feature, color) { - return this.colorArray_; - }; + /** + * A list of attributes used by the renderer. By default only the position and + * index of the vertex (0 to 3) are required. + * @type {Array} + */ + this.attributes = [{ + name: 'a_position', + size: 2, + type: AttributeType.FLOAT + }, { + name: 'a_index', + size: 1, + type: AttributeType.FLOAT + }].concat(customAttributes); - this.rotateWithViewCallback_ = options.rotateWithViewCallback || function() { - return false; - }; + /** + * A list of attributes used for hit detection. + * @type {Array} + */ + this.hitDetectionAttributes = [{ + name: 'a_position', + size: 2, + type: AttributeType.FLOAT + }, { + name: 'a_index', + size: 1, + type: AttributeType.FLOAT + }, { + name: 'a_color', + size: 4, + type: AttributeType.FLOAT + }, { + name: 'a_featureUid', + size: 1, + type: AttributeType.FLOAT + }].concat(customAttributes); + + this.customAttributes = options.attributes ? options.attributes : []; this.previousExtent_ = createEmpty(); @@ -371,8 +382,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const vectorSource = vectorLayer.getSource(); const viewState = frameState.viewState; - const stride = POINT_VERTEX_STRIDE; - // the source has changed: clear the feature cache & reload features const sourceChanged = this.sourceRevision_ < vectorSource.getRevision(); if (sourceChanged) { @@ -401,14 +410,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.bindBuffer(this.verticesBuffer_); this.helper.bindBuffer(this.indicesBuffer_); - const bytesPerFloat = Float32Array.BYTES_PER_ELEMENT; - this.helper.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * stride, 0); - this.helper.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 2); - this.helper.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 4); - 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); + this.helper.enableAttributes(this.attributes); return true; } @@ -427,76 +429,61 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.makeProjectionTransform(frameState, projectionTransform); const features = vectorSource.getFeatures(); - const totalInstructionsCount = POINT_INSTRUCTIONS_COUNT * features.length; + const totalInstructionsCount = (2 + this.customAttributes.length) * features.length; if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) { this.renderInstructions_ = new Float32Array(totalInstructionsCount); } - if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalInstructionsCount) { - this.hitRenderInstructions_ = new Float32Array(totalInstructionsCount); + const totalHitInstructionsCount = (7 + this.customAttributes.length) * features.length; + if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalHitInstructionsCount) { + this.hitRenderInstructions_ = new Float32Array(totalHitInstructionsCount); } // loop on features to fill the buffer let feature; const tmpCoords = []; const tmpColor = []; - let elementIndex = 0; - let u0, v0, u1, v1, size, opacity, rotateWithView, color; + let renderIndex = 0; + let hitIndex = 0; + let hitColor; for (let i = 0; i < features.length; i++) { feature = features[i]; if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) { continue; } - tmpCoords[0] = this.coordCallback_(feature, 0); - tmpCoords[1] = this.coordCallback_(feature, 1); + tmpCoords[0] = feature.getGeometry().getFlatCoordinates()[0]; + tmpCoords[1] = feature.getGeometry().getFlatCoordinates()[1]; applyTransform(projectionTransform, tmpCoords); - u0 = this.texCoordCallback_(feature, 0); - v0 = this.texCoordCallback_(feature, 1); - u1 = this.texCoordCallback_(feature, 2); - v1 = this.texCoordCallback_(feature, 3); - size = this.sizeCallback_(feature); - opacity = this.opacityCallback_(feature); - rotateWithView = this.rotateWithViewCallback_(feature); - color = this.colorCallback_(feature, this.colorArray_); + hitColor = colorEncodeId(hitIndex + 6, tmpColor); - writePointFeatureInstructions( - this.renderInstructions_, - elementIndex, - tmpCoords[0], - tmpCoords[1], - u0, - v0, - u1, - v1, - size, - opacity, - rotateWithView, - color - ); + this.renderInstructions_[renderIndex++] = tmpCoords[0]; + this.renderInstructions_[renderIndex++] = tmpCoords[1]; // for hit detection, the feature uid is saved in the opacity value // and the index of the opacity value is encoded in the color values - elementIndex = writePointFeatureInstructions( - this.hitRenderInstructions_, - elementIndex, - tmpCoords[0], - tmpCoords[1], - u0, - v0, - u1, - v1, - size, - opacity > 0 ? Number(getUid(feature)) : 0, - rotateWithView, - colorEncodeId(elementIndex + 7, tmpColor) - ); + this.hitRenderInstructions_[hitIndex++] = tmpCoords[0]; + this.hitRenderInstructions_[hitIndex++] = tmpCoords[1]; + this.hitRenderInstructions_[hitIndex++] = hitColor[0]; + this.hitRenderInstructions_[hitIndex++] = hitColor[1]; + this.hitRenderInstructions_[hitIndex++] = hitColor[2]; + this.hitRenderInstructions_[hitIndex++] = hitColor[3]; + this.hitRenderInstructions_[hitIndex++] = Number(getUid(feature)); + + // pushing custom attributes + let value; + for (let j = 0; j < this.customAttributes.length; j++) { + value = this.customAttributes[j].callback(feature); + this.renderInstructions_[renderIndex++] = value; + this.hitRenderInstructions_[hitIndex++] = value; + } } /** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */ const message = { type: WebGLWorkerMessageType.GENERATE_BUFFERS, - renderInstructions: this.renderInstructions_.buffer + renderInstructions: this.renderInstructions_.buffer, + customAttributesCount: this.customAttributes.length }; // additional properties will be sent back as-is by the worker message['projectionTransform'] = projectionTransform; @@ -506,7 +493,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { /** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */ const hitMessage = { type: WebGLWorkerMessageType.GENERATE_BUFFERS, - renderInstructions: this.hitRenderInstructions_.buffer + renderInstructions: this.hitRenderInstructions_.buffer, + customAttributesCount: 5 + this.customAttributes.length }; hitMessage['projectionTransform'] = projectionTransform; hitMessage['hitDetection'] = true; @@ -549,9 +537,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { renderHitDetection(frameState) { // skip render entirely if vertices buffers for display & hit detection have different sizes // this typically means both buffers are temporarily out of sync - if (this.hitVerticesBuffer_.getSize() !== this.verticesBuffer_.getSize()) { - return; - } + // FIXME: adapt this to the new points renderer behaviour + // if (this.hitVerticesBuffer_.getSize() !== this.verticesBuffer_.getSize()) { + // return; + // } this.hitRenderTarget_.setSize(frameState.size); @@ -561,15 +550,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.bindBuffer(this.hitVerticesBuffer_); this.helper.bindBuffer(this.indicesBuffer_); - const stride = POINT_VERTEX_STRIDE; - const bytesPerFloat = Float32Array.BYTES_PER_ELEMENT; - this.helper.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * stride, 0); - this.helper.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 2); - this.helper.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 4); - 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); + this.helper.enableAttributes(this.hitDetectionAttributes); const renderCount = this.indicesBuffer_.getSize(); this.helper.drawElements(0, renderCount); diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 3e4b969168..517edac2b2 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -45,20 +45,6 @@ export const DefaultUniform = { OFFSET_ROTATION_MATRIX: 'u_offsetRotateMatrix' }; -/** - * Attribute names used in the default shaders: `POSITION`, `TEX_COORD`, `OPACITY`, - * `ROTATE_WITH_VIEW`, `OFFSETS` and `COLOR` - * @enum {string} - */ -export const DefaultAttrib = { - POSITION: 'a_position', - TEX_COORD: 'a_texCoord', - OPACITY: 'a_opacity', - ROTATE_WITH_VIEW: 'a_rotateWithView', - OFFSETS: 'a_offsets', - COLOR: 'a_color' -}; - /** * Attribute types, either `UNSIGNED_BYTE`, `UNSIGNED_SHORT`, `UNSIGNED_INT` or `FLOAT` * Note: an attribute stored in a `Float32Array` should be of type `FLOAT`. @@ -860,7 +846,7 @@ class WebGLHelper extends Disposable { } /** - * Compute a stride based on a list of attributes + * Compute a stride in bytes based on a list of attributes * @param {Array} attributes Ordered list of attributes * @returns {number} Stride, ie amount of values for each vertex in the vertex buffer * @api diff --git a/src/ol/worker/webgl.js b/src/ol/worker/webgl.js index 93c446aefb..c88c294dcf 100644 --- a/src/ol/worker/webgl.js +++ b/src/ol/worker/webgl.js @@ -3,8 +3,6 @@ * @module ol/worker/webgl */ import { - POINT_INSTRUCTIONS_COUNT, - POINT_VERTEX_STRIDE, WebGLWorkerMessageType, writePointFeatureToBuffers } from '../renderer/webgl/Layer.js'; @@ -16,13 +14,17 @@ const worker = self; worker.onmessage = event => { const received = event.data; if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) { + // This is specific to point features + const baseVertexAttrsCount = 3; + const baseInstructionsCount = 2; + + const customAttrsCount = received.customAttributesCount; + const instructionsCount = baseInstructionsCount + customAttrsCount; const renderInstructions = new Float32Array(received.renderInstructions); - const customAttributesCount = received.customAttributesCount || 0; - const instructionsCount = POINT_INSTRUCTIONS_COUNT + customAttributesCount; const elementsCount = renderInstructions.length / instructionsCount; const indicesCount = elementsCount * 6; - const verticesCount = elementsCount * 4 * (POINT_VERTEX_STRIDE + customAttributesCount); + const verticesCount = elementsCount * 4 * (customAttrsCount + baseVertexAttrsCount); const indexBuffer = new Uint32Array(indicesCount); const vertexBuffer = new Float32Array(verticesCount); @@ -33,8 +35,8 @@ worker.onmessage = event => { i, vertexBuffer, indexBuffer, - bufferPositions, - instructionsCount); + customAttrsCount, + bufferPositions); } /** @type {import('../renderer/webgl/Layer').WebGLWorkerGenerateBuffersMessage} */ diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index 8bbd676850..62127025ab 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -1,8 +1,8 @@ import WebGLLayerRenderer, { colorDecodeId, colorEncodeId, - getBlankImageData, POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE, - writePointFeatureInstructions, writePointFeatureToBuffers + getBlankImageData, + writePointFeatureToBuffers } from '../../../../../src/ol/renderer/webgl/Layer.js'; import Layer from '../../../../../src/ol/layer/Layer.js'; @@ -32,60 +32,6 @@ describe('ol.renderer.webgl.Layer', function() { }); - describe('writePointFeatureInstructions', function() { - let instructions; - - beforeEach(function() { - instructions = new Float32Array(100); - }); - - it('writes instructions corresponding to the given parameters', function() { - const baseIndex = 17; - writePointFeatureInstructions(instructions, baseIndex, - 1, 2, 3, 4, 5, 6, - 7, 8, true, [10, 11, 12, 13]); - expect(instructions[baseIndex + 0]).to.eql(1); - expect(instructions[baseIndex + 1]).to.eql(2); - expect(instructions[baseIndex + 2]).to.eql(3); - expect(instructions[baseIndex + 3]).to.eql(4); - expect(instructions[baseIndex + 4]).to.eql(5); - expect(instructions[baseIndex + 5]).to.eql(6); - expect(instructions[baseIndex + 6]).to.eql(7); - expect(instructions[baseIndex + 7]).to.eql(8); - expect(instructions[baseIndex + 8]).to.eql(1); - expect(instructions[baseIndex + 9]).to.eql(10); - expect(instructions[baseIndex + 10]).to.eql(11); - expect(instructions[baseIndex + 11]).to.eql(12); - expect(instructions[baseIndex + 12]).to.eql(13); - }); - - it('correctly chains writes', function() { - let baseIndex = 0; - baseIndex = writePointFeatureInstructions(instructions, baseIndex, - 1, 2, 3, 4, 5, 6, - 7, 8, true, [10, 11, 12, 13]); - baseIndex = writePointFeatureInstructions(instructions, baseIndex, - 1, 2, 3, 4, 5, 6, - 7, 8, true, [10, 11, 12, 13]); - writePointFeatureInstructions(instructions, baseIndex, - 1, 2, 3, 4, 5, 6, - 7, 8, true, [10, 11, 12, 13]); - expect(instructions[baseIndex + 0]).to.eql(1); - expect(instructions[baseIndex + 1]).to.eql(2); - expect(instructions[baseIndex + 2]).to.eql(3); - expect(instructions[baseIndex + 3]).to.eql(4); - expect(instructions[baseIndex + 4]).to.eql(5); - expect(instructions[baseIndex + 5]).to.eql(6); - expect(instructions[baseIndex + 6]).to.eql(7); - expect(instructions[baseIndex + 7]).to.eql(8); - expect(instructions[baseIndex + 8]).to.eql(1); - expect(instructions[baseIndex + 9]).to.eql(10); - expect(instructions[baseIndex + 10]).to.eql(11); - expect(instructions[baseIndex + 11]).to.eql(12); - expect(instructions[baseIndex + 12]).to.eql(13); - }); - }); - describe('writePointFeatureToBuffers', function() { let vertexBuffer, indexBuffer, instructions, elementIndex; @@ -93,50 +39,29 @@ describe('ol.renderer.webgl.Layer', function() { vertexBuffer = new Float32Array(100); indexBuffer = new Uint32Array(100); instructions = new Float32Array(100); - elementIndex = 3; - writePointFeatureInstructions(instructions, elementIndex, - 1, 2, 3, 4, 5, 6, - 7, 8, true, [10, 11, 12, 13]); + instructions.set([0, 0, 0, 0, 10, 11]); }); it('writes correctly to the buffers (without custom attributes)', function() { - const stride = POINT_VERTEX_STRIDE; - const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer); + const stride = 3; + const positions = writePointFeatureToBuffers(instructions, 4, vertexBuffer, indexBuffer, 0); - expect(vertexBuffer[0]).to.eql(1); - expect(vertexBuffer[1]).to.eql(2); - expect(vertexBuffer[2]).to.eql(-3.5); - expect(vertexBuffer[3]).to.eql(-3.5); - expect(vertexBuffer[4]).to.eql(3); - expect(vertexBuffer[5]).to.eql(4); - expect(vertexBuffer[6]).to.eql(8); - expect(vertexBuffer[7]).to.eql(1); - expect(vertexBuffer[8]).to.eql(10); - expect(vertexBuffer[9]).to.eql(11); - expect(vertexBuffer[10]).to.eql(12); - expect(vertexBuffer[11]).to.eql(13); + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); - expect(vertexBuffer[stride + 0]).to.eql(1); - expect(vertexBuffer[stride + 1]).to.eql(2); - expect(vertexBuffer[stride + 2]).to.eql(+3.5); - expect(vertexBuffer[stride + 3]).to.eql(-3.5); - expect(vertexBuffer[stride + 4]).to.eql(5); - expect(vertexBuffer[stride + 5]).to.eql(4); + expect(vertexBuffer[stride + 0]).to.eql(10); + expect(vertexBuffer[stride + 1]).to.eql(11); + expect(vertexBuffer[stride + 2]).to.eql(1); - expect(vertexBuffer[stride * 2 + 0]).to.eql(1); - expect(vertexBuffer[stride * 2 + 1]).to.eql(2); - expect(vertexBuffer[stride * 2 + 2]).to.eql(+3.5); - expect(vertexBuffer[stride * 2 + 3]).to.eql(+3.5); - expect(vertexBuffer[stride * 2 + 4]).to.eql(5); - expect(vertexBuffer[stride * 2 + 5]).to.eql(6); + expect(vertexBuffer[stride * 2 + 0]).to.eql(10); + expect(vertexBuffer[stride * 2 + 1]).to.eql(11); + expect(vertexBuffer[stride * 2 + 2]).to.eql(2); - expect(vertexBuffer[stride * 3 + 0]).to.eql(1); - expect(vertexBuffer[stride * 3 + 1]).to.eql(2); - expect(vertexBuffer[stride * 3 + 2]).to.eql(-3.5); - expect(vertexBuffer[stride * 3 + 3]).to.eql(+3.5); - expect(vertexBuffer[stride * 3 + 4]).to.eql(3); - expect(vertexBuffer[stride * 3 + 5]).to.eql(6); + expect(vertexBuffer[stride * 3 + 0]).to.eql(10); + expect(vertexBuffer[stride * 3 + 1]).to.eql(11); + expect(vertexBuffer[stride * 3 + 2]).to.eql(3); expect(indexBuffer[0]).to.eql(0); expect(indexBuffer[1]).to.eql(1); @@ -149,43 +74,34 @@ describe('ol.renderer.webgl.Layer', function() { expect(positions.vertexPosition).to.eql(stride * 4); }); - it('writes correctly to the buffers (with custom attributes)', function() { - instructions[elementIndex + POINT_INSTRUCTIONS_COUNT] = 101; - instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 1] = 102; - instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 2] = 103; + it('writes correctly to the buffers (with 2 custom attributes)', function() { + instructions.set([0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13]); + const stride = 5; + const positions = writePointFeatureToBuffers(instructions, 8, vertexBuffer, indexBuffer, 2); - const stride = POINT_VERTEX_STRIDE + 3; - const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, - undefined, POINT_INSTRUCTIONS_COUNT + 3); + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); + expect(vertexBuffer[3]).to.eql(12); + expect(vertexBuffer[4]).to.eql(13); - expect(vertexBuffer[0]).to.eql(1); - expect(vertexBuffer[1]).to.eql(2); - expect(vertexBuffer[2]).to.eql(-3.5); - expect(vertexBuffer[3]).to.eql(-3.5); - expect(vertexBuffer[4]).to.eql(3); - expect(vertexBuffer[5]).to.eql(4); - expect(vertexBuffer[6]).to.eql(8); - expect(vertexBuffer[7]).to.eql(1); - expect(vertexBuffer[8]).to.eql(10); - expect(vertexBuffer[9]).to.eql(11); - expect(vertexBuffer[10]).to.eql(12); - expect(vertexBuffer[11]).to.eql(13); + expect(vertexBuffer[stride + 0]).to.eql(10); + expect(vertexBuffer[stride + 1]).to.eql(11); + expect(vertexBuffer[stride + 2]).to.eql(1); + expect(vertexBuffer[stride + 3]).to.eql(12); + expect(vertexBuffer[stride + 4]).to.eql(13); - expect(vertexBuffer[12]).to.eql(101); - expect(vertexBuffer[13]).to.eql(102); - expect(vertexBuffer[14]).to.eql(103); + expect(vertexBuffer[stride * 2 + 0]).to.eql(10); + expect(vertexBuffer[stride * 2 + 1]).to.eql(11); + expect(vertexBuffer[stride * 2 + 2]).to.eql(2); + expect(vertexBuffer[stride * 2 + 3]).to.eql(12); + expect(vertexBuffer[stride * 2 + 4]).to.eql(13); - expect(vertexBuffer[stride + 12]).to.eql(101); - expect(vertexBuffer[stride + 13]).to.eql(102); - expect(vertexBuffer[stride + 14]).to.eql(103); - - expect(vertexBuffer[stride * 2 + 12]).to.eql(101); - expect(vertexBuffer[stride * 2 + 13]).to.eql(102); - expect(vertexBuffer[stride * 2 + 14]).to.eql(103); - - expect(vertexBuffer[stride * 3 + 12]).to.eql(101); - expect(vertexBuffer[stride * 3 + 13]).to.eql(102); - expect(vertexBuffer[stride * 3 + 14]).to.eql(103); + expect(vertexBuffer[stride * 3 + 0]).to.eql(10); + expect(vertexBuffer[stride * 3 + 1]).to.eql(11); + expect(vertexBuffer[stride * 3 + 2]).to.eql(3); + expect(vertexBuffer[stride * 3 + 3]).to.eql(12); + expect(vertexBuffer[stride * 3 + 4]).to.eql(13); expect(indexBuffer[0]).to.eql(0); expect(indexBuffer[1]).to.eql(1); @@ -199,25 +115,23 @@ describe('ol.renderer.webgl.Layer', function() { }); it('correctly chains buffer writes', function() { - const stride = POINT_VERTEX_STRIDE; - let positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer); - positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions); - positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions); + instructions.set([10, 11, 20, 21, 30, 31]); + const stride = 3; + let positions = writePointFeatureToBuffers(instructions, 0, vertexBuffer, indexBuffer, 0); + positions = writePointFeatureToBuffers(instructions, 2, vertexBuffer, indexBuffer, 0, positions); + positions = writePointFeatureToBuffers(instructions, 4, vertexBuffer, indexBuffer, 0, positions); - expect(vertexBuffer[0]).to.eql(1); - expect(vertexBuffer[1]).to.eql(2); - expect(vertexBuffer[2]).to.eql(-3.5); - expect(vertexBuffer[3]).to.eql(-3.5); + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); - expect(vertexBuffer[stride * 4 + 0]).to.eql(1); - expect(vertexBuffer[stride * 4 + 1]).to.eql(2); - expect(vertexBuffer[stride * 4 + 2]).to.eql(-3.5); - expect(vertexBuffer[stride * 4 + 3]).to.eql(-3.5); + expect(vertexBuffer[stride * 4 + 0]).to.eql(20); + expect(vertexBuffer[stride * 4 + 1]).to.eql(21); + expect(vertexBuffer[stride * 4 + 2]).to.eql(0); - expect(vertexBuffer[stride * 8 + 0]).to.eql(1); - expect(vertexBuffer[stride * 8 + 1]).to.eql(2); - expect(vertexBuffer[stride * 8 + 2]).to.eql(-3.5); - expect(vertexBuffer[stride * 8 + 3]).to.eql(-3.5); + expect(vertexBuffer[stride * 8 + 0]).to.eql(30); + expect(vertexBuffer[stride * 8 + 1]).to.eql(31); + expect(vertexBuffer[stride * 8 + 2]).to.eql(0); expect(indexBuffer[6 + 0]).to.eql(4); expect(indexBuffer[6 + 1]).to.eql(5); diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index 53b36a8041..e99ede9f34 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -5,8 +5,9 @@ import VectorSource from '../../../../../src/ol/source/Vector.js'; import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer.js'; import {get as getProjection} from '../../../../../src/ol/proj.js'; import ViewHint from '../../../../../src/ol/ViewHint.js'; -import {POINT_VERTEX_STRIDE, WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js'; -import {create as createTransform, compose as composeTransform} from '../../../../../src/ol/transform.js'; +import {WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js'; +import {compose as composeTransform, create as createTransform} from '../../../../../src/ol/transform.js'; +import {getSymbolVertexShader} from '../../../../../src/ol/webgl/ShaderBuilder.js'; const baseFrameState = { viewHints: [], @@ -77,7 +78,7 @@ describe('ol.renderer.webgl.PointsLayer', function() { })); renderer.prepareFrame(frameState); - const attributePerVertex = POINT_VERTEX_STRIDE; + const attributePerVertex = 3; renderer.worker_.addEventListener('message', function(event) { if (event.data.type !== WebGLWorkerMessageType.GENERATE_BUFFERS) { @@ -109,7 +110,7 @@ describe('ol.renderer.webgl.PointsLayer', function() { if (event.data.type !== WebGLWorkerMessageType.GENERATE_BUFFERS) { return; } - const attributePerVertex = 12; + const attributePerVertex = 3; expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex); expect(renderer.indicesBuffer_.getArray().length).to.eql(6); done(); @@ -162,9 +163,9 @@ describe('ol.renderer.webgl.PointsLayer', function() { }) }); renderer = new WebGLPointsLayerRenderer(layer, { - sizeCallback: function() { - return 4; - } + vertexShader: getSymbolVertexShader({ + size: 4 + }) }); });