From a2d83f5358936096d82e5d4e4d66923a47484395 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 10:42:54 +0200 Subject: [PATCH 01/13] WebGL points / add minimal tests --- .../ol/renderer/webgl/pointslayer.test.js | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test/spec/ol/renderer/webgl/pointslayer.test.js diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js new file mode 100644 index 0000000000..4afa629605 --- /dev/null +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -0,0 +1,108 @@ +import Feature from '../../../../../src/ol/Feature.js'; +import Point from '../../../../../src/ol/geom/Point.js'; +import LineString from '../../../../../src/ol/geom/LineString.js'; +import VectorLayer from '../../../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../../../src/ol/source/Vector.js'; +import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer'; +import {get as getProjection} from '../../../../../src/ol/proj'; +import Polygon from '../../../../../src/ol/geom/Polygon'; + + +describe('ol.renderer.webgl.PointsLayer', function() { + + describe('constructor', function() { + + let target; + + beforeEach(function () { + target = document.createElement('div'); + target.style.width = '256px'; + target.style.height = '256px'; + document.body.appendChild(target); + }); + + afterEach(function () { + document.body.removeChild(target); + }); + + it('creates a new instance', function () { + const layer = new VectorLayer({ + source: new VectorSource() + }); + const renderer = new WebGLPointsLayerRenderer(layer); + expect(renderer).to.be.a(WebGLPointsLayerRenderer); + }); + + }); + + describe('#prepareFrame', function() { + let layer, renderer, frameState; + + beforeEach(function() { + layer = new VectorLayer({ + source: new VectorSource() + }); + renderer = new WebGLPointsLayerRenderer(layer); + const projection = getProjection('EPSG:3857'); + frameState = { + skippedFeatureUids: {}, + viewHints: [], + viewState: { + projection: projection, + resolution: 1, + rotation: 0, + center: [10, 10] + }, + size: [256, 256] + }; + }); + + it('calls WebGlHelper#prepareDraw', function() { + const spy = sinon.spy(renderer.helper_, 'prepareDraw'); + renderer.prepareFrame(frameState); + expect(spy.called).to.be(true); + }); + + it('fills up a buffer with 2 triangles per point', function() { + layer.getSource().addFeature(new Feature({ + geometry: new Point([10, 20]) + })); + renderer.prepareFrame(frameState); + + const attributePerVertex = 12; + expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex); + expect(renderer.indicesBuffer_.getArray().length).to.eql(6); + }); + + it('ignores geometries other than points', function() { + layer.getSource().addFeature(new Feature({ + geometry: new LineString([[10, 20], [30, 20]]) + })); + layer.getSource().addFeature(new Feature({ + geometry: new Polygon([[10, 20], [30, 20], [30, 10], [10, 20]]) + })); + renderer.prepareFrame(frameState); + + expect(renderer.verticesBuffer_.getArray().length).to.eql(0); + expect(renderer.indicesBuffer_.getArray().length).to.eql(0); + }); + + it('clears the buffers when the features are gone', function() { + const source = layer.getSource(); + source.addFeature(new Feature({ + geometry: new Point([10, 20]) + })); + source.removeFeature(source.getFeatures()[0]); + source.addFeature(new Feature({ + geometry: new Point([10, 20]) + })); + renderer.prepareFrame(frameState); + + const attributePerVertex = 12; + expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex); + expect(renderer.indicesBuffer_.getArray().length).to.eql(6); + }); + + }); + +}); From 5e36468245a1b29fe56465038774b3befa79a8f8 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 11:09:39 +0200 Subject: [PATCH 02/13] Webgl / add a simple Layer renderer for common logic This will eventually provides utilities for manipulating & rendering data --- src/ol/renderer/webgl/Layer.js | 74 +++++++++++++++++++++++ src/ol/renderer/webgl/PointsLayer.js | 50 +++------------ test/spec/ol/renderer/webgl/layer.test.js | 45 ++++++++++++++ 3 files changed, 129 insertions(+), 40 deletions(-) create mode 100644 src/ol/renderer/webgl/Layer.js create mode 100644 test/spec/ol/renderer/webgl/layer.test.js diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js new file mode 100644 index 0000000000..e18733b7af --- /dev/null +++ b/src/ol/renderer/webgl/Layer.js @@ -0,0 +1,74 @@ +/** + * @module ol/renderer/webgl/Layer + */ +import LayerRenderer from '../Layer.js'; +import WebGLHelper from '../../webgl/Helper'; + + +/** + * @typedef {Object} PostProcessesOptions + * @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than + * the main canvas that will then be sampled up (useful for saving resource on blur steps). + * @property {string} [vertexShader] Vertex shader source + * @property {string} [fragmentShader] Fragment shader source + * @property {Object.} [uniforms] Uniform definitions for the post process step + */ + +/** + * @typedef {Object} Options + * @property {Object.} [uniforms] Uniform definitions for the post process steps + * @property {Array} [postProcesses] Post-processes definitions + */ + +/** + * @classdesc + * Base WebGL renderer class. + * Holds all logic related to data manipulation & some common rendering logic + */ +class WebGLLayerRenderer extends LayerRenderer { + + /** + * @param {import("../../layer/Layer.js").default} layer Layer. + * @param {Options=} [opt_options] Options. + */ + constructor(layer, opt_options) { + super(layer); + + const options = opt_options || {}; + + this.helper_ = new WebGLHelper({ + postProcesses: options.postProcesses, + uniforms: options.uniforms + }); + } + + /** + * @inheritDoc + */ + disposeInternal() { + super.disposeInternal(); + } + + /** + * Will return the last shader compilation errors. If no error happened, will return null; + * @return {string|null} Errors, or null if last compilation was successful + * @api + */ + getShaderCompileErrors() { + return this.helper_.getShaderCompileErrors(); + } +} + +/** + * Returns a texture of 1x1 pixel, white + * @private + * @return {ImageData} Image data. + */ +export function getBlankTexture() { + const canvas = document.createElement('canvas'); + const image = canvas.getContext('2d').createImageData(1, 1); + image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255; + return image; +} + +export default WebGLLayerRenderer; diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 37dbc27830..6e5552af38 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -1,11 +1,11 @@ /** * @module ol/renderer/webgl/PointsLayer */ -import LayerRenderer from '../Layer'; import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; -import WebGLHelper, {DefaultAttrib} from '../../webgl/Helper'; +import {DefaultAttrib} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType'; +import WebGLLayerRenderer, {getBlankTexture} from './Layer'; const VERTEX_SHADER = ` precision mediump float; @@ -55,15 +55,6 @@ const FRAGMENT_SHADER = ` gl_FragColor.rgb *= gl_FragColor.a; }`; -/** - * @typedef {Object} PostProcessesOptions - * @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than - * the main canvas that will then be sampled up (useful for saving resource on blur steps). - * @property {string} [vertexShader] Vertex shader source - * @property {string} [fragmentShader] Fragment shader source - * @property {Object.} [uniforms] Uniform definitions for the post process step - */ - /** * @typedef {Object} Options * @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the @@ -91,7 +82,7 @@ const FRAGMENT_SHADER = ` * @property {string} [fragmentShader] Fragment shader source * @property {Object.} [uniforms] Uniform definitions for the post process steps * Please note that `u_texture` is reserved for the main texture slot. - * @property {Array} [postProcesses] Post-processes definitions + * @property {Array} [postProcesses] Post-processes definitions */ /** @@ -186,22 +177,22 @@ const FRAGMENT_SHADER = ` * * @api */ -class WebGLPointsLayerRenderer extends LayerRenderer { +class WebGLPointsLayerRenderer extends WebGLLayerRenderer { /** * @param {import("../../layer/Vector.js").default} vectorLayer Vector layer. * @param {Options=} [opt_options] Options. */ constructor(vectorLayer, opt_options) { - super(vectorLayer); - const options = opt_options || {}; + // assign the `texture` uniform if not specified in the options const uniforms = options.uniforms || {}; - uniforms.u_texture = options.texture || this.getDefaultTexture(); - this.helper_ = new WebGLHelper({ - postProcesses: options.postProcesses, - uniforms: uniforms + uniforms.u_texture = options.texture || getBlankTexture(); + + super(vectorLayer, { + uniforms: uniforms, + postProcesses: options.postProcesses }); this.sourceRevision_ = -1; @@ -335,27 +326,6 @@ class WebGLPointsLayerRenderer extends LayerRenderer { return true; } - - /** - * Will return the last shader compilation errors. If no error happened, will return null; - * @return {string|null} Errors, or null if last compilation was successful - * @api - */ - getShaderCompileErrors() { - return this.helper_.getShaderCompileErrors(); - } - - /** - * Returns a texture of 1x1 pixel, white - * @private - * @return {ImageData} Image data. - */ - getDefaultTexture() { - const canvas = document.createElement('canvas'); - const image = canvas.getContext('2d').createImageData(1, 1); - image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255; - return image; - } } export default WebGLPointsLayerRenderer; diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js new file mode 100644 index 0000000000..06787c6fcd --- /dev/null +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -0,0 +1,45 @@ +import VectorLayer from '../../../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../../../src/ol/source/Vector.js'; +import WebGLLayerRenderer, {getBlankTexture} from '../../../../../src/ol/renderer/webgl/Layer'; + + +describe('ol.renderer.webgl.Layer', function() { + + describe('constructor', function() { + + let target; + + beforeEach(function () { + target = document.createElement('div'); + target.style.width = '256px'; + target.style.height = '256px'; + document.body.appendChild(target); + }); + + afterEach(function () { + document.body.removeChild(target); + }); + + it('creates a new instance', function () { + const layer = new VectorLayer({ + source: new VectorSource() + }); + const renderer = new WebGLLayerRenderer(layer); + expect(renderer).to.be.a(WebGLLayerRenderer); + }); + + }); + + describe('getBlankTexture', function() { + it('creates a 1x1 white texture', function() { + const texture = getBlankTexture(); + expect(texture.height).to.eql(1); + expect(texture.width).to.eql(1); + expect(texture.data[0]).to.eql(255); + expect(texture.data[1]).to.eql(255); + expect(texture.data[2]).to.eql(255); + expect(texture.data[3]).to.eql(255); + }); + }); + +}); From 3a429d3f6c581c12eeb71f859d4e1569873fa421 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 12:37:21 +0200 Subject: [PATCH 03/13] Webgl / add utils for pushing geojson geometries in webgl buffers For now only point geometries are handled. --- src/ol/renderer/webgl/Layer.js | 69 +++++++++++++++++ test/spec/ol/renderer/webgl/layer.test.js | 91 +++++++++++++++++++++-- 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index e18733b7af..3e06d9a977 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -59,6 +59,75 @@ class WebGLLayerRenderer extends LayerRenderer { } } + +/** + * Pushes vertices and indices in the given buffers using the geometry coordinates and the following properties + * from the feature: + * - `color` + * - `opacity` + * - `size` (for points) + * - `u0`, `v0`, `u1`, `v1` (for points) + * - `rotateWithView` (for points) + * - `width` (for lines) + * Custom attributes can be designated using the `opt_attributes` argument, otherwise other properties on the + * feature will be ignored. + * @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer in which new vertices will be pushed. + * @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer in which new indices will be pushed. + * @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature in geojson format, coordinates + * expressed in EPSG:4326. + * @param {Array} [opt_attributes] Custom attributes. An array of properties which will be read from the + * feature and pushed in the buffer in the given order. + */ +export function pushFeatureInBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { + if (!geojsonFeature.geometry) { + return; + } + switch(geojsonFeature.geometry.type) { + case "Point": + pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) + return; + } +} + +/** + * Pushes a quad (two triangles) based on a point geometry + * @param vertexBuffer + * @param indexBuffer + * @param geojsonFeature + * @param opt_attributes + * @private + */ +function pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { + const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0); + + const x = geojsonFeature.geometry.coordinates[0]; + const y = geojsonFeature.geometry.coordinates[1]; + const u0 = geojsonFeature.properties.u0; + const v0 = geojsonFeature.properties.v0; + const u1 = geojsonFeature.properties.u1; + const v1 = geojsonFeature.properties.v1; + const size = geojsonFeature.properties.size; + const opacity = geojsonFeature.properties.opacity; + const rotateWithView = geojsonFeature.properties.rotateWithView; + const color = geojsonFeature.properties.color; + const red = color[0]; + const green = color[1]; + const blue = color[2]; + const alpha = color[3]; + const baseIndex = vertexBuffer.getArray().length / stride; + + vertexBuffer.getArray().push( + x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha, + x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, + x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, + x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha + ); + indexBuffer.getArray().push( + baseIndex, baseIndex + 1, baseIndex + 3, + baseIndex + 1, baseIndex + 2, baseIndex + 3 + ); +} + /** * Returns a texture of 1x1 pixel, white * @private diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index 06787c6fcd..d87f83a56d 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -1,6 +1,6 @@ -import VectorLayer from '../../../../../src/ol/layer/Vector.js'; -import VectorSource from '../../../../../src/ol/source/Vector.js'; -import WebGLLayerRenderer, {getBlankTexture} from '../../../../../src/ol/renderer/webgl/Layer'; +import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from '../../../../../src/ol/renderer/webgl/Layer'; +import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer'; +import Layer from '../../../../../src/ol/layer/Layer'; describe('ol.renderer.webgl.Layer', function() { @@ -21,15 +21,94 @@ describe('ol.renderer.webgl.Layer', function() { }); it('creates a new instance', function () { - const layer = new VectorLayer({ - source: new VectorSource() - }); + const layer = new Layer({}); const renderer = new WebGLLayerRenderer(layer); expect(renderer).to.be.a(WebGLLayerRenderer); }); }); + describe('pushFeatureInBuffer', function() { + let vertexBuffer, indexBuffer; + + beforeEach(function () { + vertexBuffer = new WebGLArrayBuffer(); + indexBuffer = new WebGLArrayBuffer(); + }); + + it('does nothing if the feature has no geometry', function() { + const feature = { + type: "Feature", + id: "AFG", + properties: { + color:[0.5, 1, 0.2, 0.7], + size: 3 + }, + geometry: null + }; + pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + expect(vertexBuffer.getArray().length).to.eql(0); + expect(indexBuffer.getArray().length).to.eql(0); + }); + + it('adds two triangles with the correct attributes for a point geometry', function() { + const feature = { + type: "Feature", + id: "AFG", + properties: { + color:[0.5, 1, 0.2, 0.7], + size: 3 + }, + geometry: { + type: "Point", + coordinates: [ -75, 47 ] + } + }; + const attributePerVertex = 12; + pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); + expect(indexBuffer.getArray().length).to.eql(6); + }); + + it('correctly sets indices & coordinates for several features', function() { + const feature = { + type: "Feature", + id: "AFG", + properties: { + color:[0.5, 1, 0.2, 0.7], + size: 3 + }, + geometry: { + type: "Point", + coordinates: [ -75, 47 ] + } + }; + const attributePerVertex = 12; + pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + expect(vertexBuffer.getArray()[0]).to.eql(-75); + expect(vertexBuffer.getArray()[1]).to.eql(47); + expect(vertexBuffer.getArray()[0 + attributePerVertex]).to.eql(-75); + expect(vertexBuffer.getArray()[1 + attributePerVertex]).to.eql(47); + + // first point + expect(indexBuffer.getArray()[0]).to.eql(0); + expect(indexBuffer.getArray()[1]).to.eql(1); + expect(indexBuffer.getArray()[2]).to.eql(3); + expect(indexBuffer.getArray()[3]).to.eql(1); + expect(indexBuffer.getArray()[4]).to.eql(2); + expect(indexBuffer.getArray()[5]).to.eql(3); + + // second point + expect(indexBuffer.getArray()[6]).to.eql(4); + expect(indexBuffer.getArray()[7]).to.eql(5); + expect(indexBuffer.getArray()[8]).to.eql(7); + expect(indexBuffer.getArray()[9]).to.eql(5); + expect(indexBuffer.getArray()[10]).to.eql(6); + expect(indexBuffer.getArray()[11]).to.eql(7); + }); + }); + describe('getBlankTexture', function() { it('creates a 1x1 white texture', function() { const texture = getBlankTexture(); From 27e520add464ccaed65482dca6b90512fab1691f Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 14:04:25 +0200 Subject: [PATCH 04/13] WebGL points / use pushFeatureInBuffer util Feature objects are converted to geoJSON with the properties set for rendering. --- src/ol/renderer/webgl/PointsLayer.js | 46 ++++++++++++---------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 6e5552af38..89dd68f8d0 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -5,7 +5,8 @@ import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; import {DefaultAttrib} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType'; -import WebGLLayerRenderer, {getBlankTexture} from './Layer'; +import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer'; +import GeoJSON from '../../format/GeoJSON'; const VERTEX_SHADER = ` precision mediump float; @@ -229,6 +230,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.rotateWithViewCallback_ = options.rotateWithViewCallback || function() { return false; }; + + this.geojsonFormat_ = new GeoJSON(); } /** @@ -261,6 +264,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorSource = vectorLayer.getSource(); + // TODO: get this from somewhere... const stride = 12; this.helper_.prepareDraw(frameState); @@ -280,32 +284,22 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) { return; } - const x = this.coordCallback_(feature, 0); - const y = this.coordCallback_(feature, 1); - const u0 = this.texCoordCallback_(feature, 0); - const v0 = this.texCoordCallback_(feature, 1); - const u1 = this.texCoordCallback_(feature, 2); - const v1 = this.texCoordCallback_(feature, 3); - const size = this.sizeCallback_(feature); - const opacity = this.opacityCallback_(feature); - const rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0; - const color = this.colorCallback_(feature, this.colorArray_); - const red = color[0]; - const green = color[1]; - const blue = color[2]; - const alpha = color[3]; - const baseIndex = this.verticesBuffer_.getArray().length / stride; - this.verticesBuffer_.getArray().push( - x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha, - x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, - x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, - x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha - ); - this.indicesBuffer_.getArray().push( - baseIndex, baseIndex + 1, baseIndex + 3, - baseIndex + 1, baseIndex + 2, baseIndex + 3 - ); + const geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature); + + geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0); + geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1); + geojsonFeature.properties = geojsonFeature.properties || {}; + geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_); + geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0); + geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1); + geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2); + geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3); + geojsonFeature.properties.size = this.sizeCallback_(feature); + geojsonFeature.properties.opacity = this.opacityCallback_(feature); + geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0; + + pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature); }); this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_); From c6d214b585a41eaec701b735ece28bad767f4c84 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 14:17:55 +0200 Subject: [PATCH 05/13] Webgl / implement support for custom attributes in webgl buffers Only numerical attributes can be pushed in webgl buffers. --- src/ol/renderer/webgl/Layer.js | 31 ++++++++++++++++++----- test/spec/ol/renderer/webgl/layer.test.js | 26 +++++++++++++++++++ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 3e06d9a977..67792ccd7e 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -76,7 +76,8 @@ class WebGLLayerRenderer extends LayerRenderer { * @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature in geojson format, coordinates * expressed in EPSG:4326. * @param {Array} [opt_attributes] Custom attributes. An array of properties which will be read from the - * feature and pushed in the buffer in the given order. + * feature and pushed in the buffer in the given order. Note: attributes can only be numerical! Any other type or + * NaN will result in `0` being pushed in the buffer. */ export function pushFeatureInBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { if (!geojsonFeature.geometry) { @@ -89,6 +90,8 @@ export function pushFeatureInBuffer(vertexBuffer, indexBuffer, geojsonFeature, o } } +const tmpArray_ = []; + /** * Pushes a quad (two triangles) based on a point geometry * @param vertexBuffer @@ -116,12 +119,26 @@ function pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_a const alpha = color[3]; const baseIndex = vertexBuffer.getArray().length / stride; - vertexBuffer.getArray().push( - x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha, - x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, - x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, - x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha - ); + // read custom numerical attributes on the feature + const customAttributeValues = tmpArray_; + customAttributeValues.length = opt_attributes ? opt_attributes.length : 0; + for (let i = 0; i < customAttributeValues.length; i++) { + customAttributeValues[i] = parseFloat(geojsonFeature.properties[opt_attributes[i]]) || 0; + } + + // push vertices for each of the four quad corners (first standard then custom attributes) + vertexBuffer.getArray().push(x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha); + Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + + vertexBuffer.getArray().push(x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha); + Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + + vertexBuffer.getArray().push(x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha); + Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + + vertexBuffer.getArray().push(x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha); + Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + indexBuffer.getArray().push( baseIndex, baseIndex + 1, baseIndex + 3, baseIndex + 1, baseIndex + 2, baseIndex + 3 diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index d87f83a56d..59e81ae1db 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -107,6 +107,32 @@ describe('ol.renderer.webgl.Layer', function() { expect(indexBuffer.getArray()[10]).to.eql(6); expect(indexBuffer.getArray()[11]).to.eql(7); }); + + it('correctly adds custom attributes', function() { + const feature = { + type: "Feature", + id: "AFG", + properties: { + color: [0.5, 1, 0.2, 0.7], + custom: 4, + customString: '5', + custom2: 12.4, + customString2: 'abc' + }, + geometry: { + type: "Point", + coordinates: [ -75, 47 ] + } + }; + const attributePerVertex = 16; + pushFeatureInBuffer(vertexBuffer, indexBuffer, feature, ['custom', 'custom2', 'customString', 'customString2']); + expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); + expect(indexBuffer.getArray().length).to.eql(6); + expect(vertexBuffer.getArray()[12]).to.eql(4); + expect(vertexBuffer.getArray()[13]).to.eql(12.4); + expect(vertexBuffer.getArray()[14]).to.eql(5); + expect(vertexBuffer.getArray()[15]).to.eql(0); + }); }); describe('getBlankTexture', function() { From 523097903a7810fc965dca6d1ec250c0674ed770 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 15:07:46 +0200 Subject: [PATCH 06/13] WebGL points / rebuild buffers on every non animation frame --- examples/filter-points-webgl.js | 1 - src/ol/renderer/webgl/PointsLayer.js | 88 +++++++++++++------ .../ol/renderer/webgl/pointslayer.test.js | 22 ++++- 3 files changed, 81 insertions(+), 30 deletions(-) diff --git a/examples/filter-points-webgl.js b/examples/filter-points-webgl.js index 75bfe406d1..76e2deab0a 100644 --- a/examples/filter-points-webgl.js +++ b/examples/filter-points-webgl.js @@ -11,7 +11,6 @@ import {clamp, lerp} from '../src/ol/math'; import Stamen from '../src/ol/source/Stamen'; const vectorSource = new Vector({ - features: [], attributions: 'NASA' }); diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 89dd68f8d0..d4525cd9f3 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -7,6 +7,8 @@ import {DefaultAttrib} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType'; import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer'; import GeoJSON from '../../format/GeoJSON'; +import {getUid} from '../../util'; +import ViewHint from '../../ViewHint'; const VERTEX_SHADER = ` precision mediump float; @@ -232,6 +234,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { }; this.geojsonFormat_ = new GeoJSON(); + + /** + * @type {Object} + * @private + */ + this.geojsonFeatureCache_ = {}; } /** @@ -263,47 +271,25 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { prepareFrame(frameState) { const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorSource = vectorLayer.getSource(); + const viewState = frameState.viewState; // TODO: get this from somewhere... const stride = 12; this.helper_.prepareDraw(frameState); + // the source has changed: clear the feature cache & reload features if (this.sourceRevision_ < vectorSource.getRevision()) { this.sourceRevision_ = vectorSource.getRevision(); - this.verticesBuffer_.getArray().length = 0; - this.indicesBuffer_.getArray().length = 0; + this.geojsonFeatureCache_ = {}; - const viewState = frameState.viewState; const projection = viewState.projection; const resolution = viewState.resolution; - - // loop on features to fill the buffer vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection); - vectorSource.forEachFeature((feature) => { - if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) { - return; - } + } - const geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature); - - geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0); - geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1); - geojsonFeature.properties = geojsonFeature.properties || {}; - geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_); - geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0); - geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1); - geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2); - geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3); - geojsonFeature.properties.size = this.sizeCallback_(feature); - geojsonFeature.properties.opacity = this.opacityCallback_(feature); - geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0; - - pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature); - }); - - this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_); - this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); + if (!frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING]) { + this.rebuildBuffers_(frameState); } // write new data @@ -320,6 +306,52 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { return true; } + + /** + * Rebuild internal webgl buffers based on current view extent; costly, should not be called too much + * @param {import("../../PluggableMap.js").FrameState} frameState + * @private + */ + rebuildBuffers_(frameState) { + const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); + const vectorSource = vectorLayer.getSource(); + + this.verticesBuffer_.getArray().length = 0; + this.indicesBuffer_.getArray().length = 0; + + // loop on features to fill the buffer + const features = vectorSource.getFeatures(); + let feature; + for (let i = 0; i < features.length; i++) { + feature = features[i]; + if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) { + return; + } + + let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)]; + if (!geojsonFeature) { + geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature); + this.geojsonFeatureCache_[getUid(feature)] = geojsonFeature; + } + + geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0); + geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1); + geojsonFeature.properties = geojsonFeature.properties || {}; + geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_); + geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0); + geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1); + geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2); + geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3); + geojsonFeature.properties.size = this.sizeCallback_(feature); + geojsonFeature.properties.opacity = this.opacityCallback_(feature); + geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0; + + pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature); + } + + this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_); + this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); + } } export default WebGLPointsLayerRenderer; diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index 4afa629605..a54339d228 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -6,6 +6,7 @@ import VectorSource from '../../../../../src/ol/source/Vector.js'; import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer'; import {get as getProjection} from '../../../../../src/ol/proj'; import Polygon from '../../../../../src/ol/geom/Polygon'; +import ViewHint from '../../../../../src/ol/ViewHint'; describe('ol.renderer.webgl.PointsLayer', function() { @@ -53,7 +54,8 @@ describe('ol.renderer.webgl.PointsLayer', function() { rotation: 0, center: [10, 10] }, - size: [256, 256] + size: [256, 256], + extent: [-100, -100, 100, 100] }; }); @@ -103,6 +105,24 @@ describe('ol.renderer.webgl.PointsLayer', function() { expect(renderer.indicesBuffer_.getArray().length).to.eql(6); }); + it('rebuilds the buffers only when not interacting or animating', function() { + const spy = sinon.spy(renderer, 'rebuildBuffers_'); + frameState.viewHints[ViewHint.INTERACTING] = 1; + frameState.viewHints[ViewHint.ANIMATING] = 0; + renderer.prepareFrame(frameState); + expect(spy.called).to.be(false); + + frameState.viewHints[ViewHint.INTERACTING] = 0; + frameState.viewHints[ViewHint.ANIMATING] = 1; + renderer.prepareFrame(frameState); + expect(spy.called).to.be(false); + + frameState.viewHints[ViewHint.INTERACTING] = 0; + frameState.viewHints[ViewHint.ANIMATING] = 0; + renderer.prepareFrame(frameState); + expect(spy.called).to.be(true); + }); + }); }); From 75eb62363ab11df6a43696c9eeb26ae917db8486 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 15:18:42 +0200 Subject: [PATCH 07/13] WebGL points / rebuild buffers only when extent changed --- src/ol/renderer/webgl/PointsLayer.js | 7 ++++++- test/spec/ol/renderer/webgl/pointslayer.test.js | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index d4525cd9f3..02fb4eb1dc 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -9,6 +9,7 @@ import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer' import GeoJSON from '../../format/GeoJSON'; import {getUid} from '../../util'; import ViewHint from '../../ViewHint'; +import {createEmpty, equals} from '../../extent'; const VERTEX_SHADER = ` precision mediump float; @@ -240,6 +241,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { * @private */ this.geojsonFeatureCache_ = {}; + + this.previousExtent_ = createEmpty(); } /** @@ -288,8 +291,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection); } - if (!frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING]) { + if (!frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING] && + !equals(this.previousExtent_, frameState.extent)) { this.rebuildBuffers_(frameState); + this.previousExtent_ = frameState.extent.slice(); } // write new data diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index a54339d228..367fd2696f 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -107,6 +107,7 @@ describe('ol.renderer.webgl.PointsLayer', function() { it('rebuilds the buffers only when not interacting or animating', function() { const spy = sinon.spy(renderer, 'rebuildBuffers_'); + frameState.viewHints[ViewHint.INTERACTING] = 1; frameState.viewHints[ViewHint.ANIMATING] = 0; renderer.prepareFrame(frameState); @@ -123,6 +124,19 @@ describe('ol.renderer.webgl.PointsLayer', function() { expect(spy.called).to.be(true); }); + it('rebuilds the buffers only when the frame extent changed', function() { + const spy = sinon.spy(renderer, 'rebuildBuffers_'); + + renderer.prepareFrame(frameState); + expect(spy.callCount).to.be(1); + + renderer.prepareFrame(frameState); + expect(spy.callCount).to.be(1); + + frameState.extent = [10, 20, 30, 40]; + renderer.prepareFrame(frameState); + expect(spy.callCount).to.be(2); + }); }); }); From 3e2e45ce6d772cc648938762d9f384dd7165ba20 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 15:49:51 +0200 Subject: [PATCH 08/13] Webgl helper / add the possibility to use transforms as custom uniforms These will be translated into 4x4 matrices in GLSL. --- src/ol/webgl/Helper.js | 6 ++++-- test/spec/ol/webgl/helper.test.js | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 180654c25f..822c744337 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -62,7 +62,7 @@ export const DefaultAttrib = { }; /** - * @typedef {number|Array|HTMLCanvasElement|HTMLImageElement|ImageData} UniformLiteralValue + * @typedef {number|Array|HTMLCanvasElement|HTMLImageElement|ImageData|import("../transform").Transform} UniformLiteralValue */ /** @@ -565,7 +565,9 @@ class WebGLHelper extends Disposable { // fill texture slots by increasing index gl.uniform1i(this.getUniformLocation(uniform.name), textureSlot++); - } else if (Array.isArray(value)) { + } else if (Array.isArray(value) && value.length === 6) { + this.setUniformMatrixValue(uniform.name, fromTransform(this.tmpMat4_, value)); + } else if (Array.isArray(value) && value.length <= 4) { switch (value.length) { case 2: gl.uniform2f(this.getUniformLocation(uniform.name), value[0], value[1]); diff --git a/test/spec/ol/webgl/helper.test.js b/test/spec/ol/webgl/helper.test.js index 0a48a337d6..11d7469bb1 100644 --- a/test/spec/ol/webgl/helper.test.js +++ b/test/spec/ol/webgl/helper.test.js @@ -1,4 +1,5 @@ import WebGLHelper from '../../../../src/ol/webgl/Helper'; +import {create as createTransform} from '../../../../src/ol/transform'; const VERTEX_SHADER = ` @@ -95,7 +96,8 @@ describe('ol.webgl.WebGLHelper', function() { uniforms: { u_test1: 42, u_test2: [1, 3], - u_test3: document.createElement('canvas') + u_test3: document.createElement('canvas'), + u_test4: createTransform() } }); h.useProgram(h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER)); @@ -116,13 +118,15 @@ describe('ol.webgl.WebGLHelper', function() { }); it('has processed uniforms', function() { - expect(h.uniforms_.length).to.eql(3); + expect(h.uniforms_.length).to.eql(4); expect(h.uniforms_[0].name).to.eql('u_test1'); expect(h.uniforms_[1].name).to.eql('u_test2'); expect(h.uniforms_[2].name).to.eql('u_test3'); + expect(h.uniforms_[3].name).to.eql('u_test4'); expect(h.uniforms_[0].location).to.not.eql(-1); expect(h.uniforms_[1].location).to.not.eql(-1); expect(h.uniforms_[2].location).to.not.eql(-1); + expect(h.uniforms_[3].location).to.not.eql(-1); expect(h.uniforms_[2].texture).to.not.eql(undefined); }); }); From fb455891ce9e60dceb63d7bcbc9732be79e7c2b5 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 17:23:01 +0200 Subject: [PATCH 09/13] Webgl points / make coordinates in view space to avoid precision glitches Fixes #9479 --- src/ol/renderer/webgl/PointsLayer.js | 61 ++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 02fb4eb1dc..e76b936a8a 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -3,13 +3,22 @@ */ import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; -import {DefaultAttrib} from '../../webgl/Helper'; +import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType'; import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer'; import GeoJSON from '../../format/GeoJSON'; import {getUid} from '../../util'; import ViewHint from '../../ViewHint'; import {createEmpty, equals} from '../../extent'; +import { + create as createTransform, + reset as resetTransform, rotate as rotateTransform, + scale as scaleTransform, + translate as translateTransform, + makeInverse as makeInverseTransform, + multiply as multiplyTransform, + apply as applyTransform +} from '../../transform'; const VERTEX_SHADER = ` precision mediump float; @@ -190,9 +199,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { constructor(vectorLayer, opt_options) { const options = opt_options || {}; - // assign the `texture` uniform if not specified in the options const uniforms = options.uniforms || {}; uniforms.u_texture = options.texture || getBlankTexture(); + const projectionMatrixTransform = createTransform(); + uniforms[DefaultUniform.PROJECTION_MATRIX] = projectionMatrixTransform; super(vectorLayer, { uniforms: uniforms, @@ -243,6 +253,22 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.geojsonFeatureCache_ = {}; this.previousExtent_ = createEmpty(); + + /** + * This transform is updated on every frame and is the composition of: + * - invert of the world->screen transform that was used when rebuilding buffers (see `this.renderTransform_`) + * - current world->screen transform + * @type {import("../../transform.js").Transform} + * @private + */ + this.currentTransform_ = projectionMatrixTransform; + + /** + * This transform is updated when buffers are rebuilt and converts world space coordinates to screen space + * @type {import("../../transform.js").Transform} + * @private + */ + this.renderTransform_ = createTransform(); } /** @@ -279,8 +305,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { // TODO: get this from somewhere... const stride = 12; - this.helper_.prepareDraw(frameState); - // the source has changed: clear the feature cache & reload features if (this.sourceRevision_ < vectorSource.getRevision()) { this.sourceRevision_ = vectorSource.getRevision(); @@ -297,6 +321,23 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.previousExtent_ = frameState.extent.slice(); } + // regenerate the transform matrix + const size = frameState.size; + const rotation = frameState.viewState.rotation; + const resolution = frameState.viewState.resolution; + const center = frameState.viewState.center; + + resetTransform(this.currentTransform_); + scaleTransform(this.currentTransform_, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(this.currentTransform_, -rotation); + translateTransform(this.currentTransform_, -center[0], -center[1]); + + // the current transform + const inverseCurrentTransform = makeInverseTransform(createTransform(), this.renderTransform_); + this.currentTransform_ = multiplyTransform(this.currentTransform_, inverseCurrentTransform); + + this.helper_.prepareDraw(frameState); + // write new data this.helper_.bindBuffer(ARRAY_BUFFER, this.verticesBuffer_); this.helper_.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); @@ -324,6 +365,17 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.verticesBuffer_.getArray().length = 0; this.indicesBuffer_.getArray().length = 0; + // regenerate the transform matrix + const size = frameState.size; + const rotation = frameState.viewState.rotation; + const resolution = frameState.viewState.resolution; + const center = frameState.viewState.center; + + resetTransform(this.renderTransform_); + scaleTransform(this.renderTransform_, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(this.renderTransform_, -rotation); + translateTransform(this.renderTransform_, -center[0], -center[1]); + // loop on features to fill the buffer const features = vectorSource.getFeatures(); let feature; @@ -341,6 +393,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0); geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1); + applyTransform(this.renderTransform_, geojsonFeature.geometry.coordinates); geojsonFeature.properties = geojsonFeature.properties || {}; geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_); geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0); From 5d2b7fe4bb2b534de2546cd5b995f7a2f1bb39ab Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 18:59:24 +0200 Subject: [PATCH 10/13] Webgl points / use the helper ton compute the projection transform The `WebGLHelper` class now provides a `makeProjectionTransform` method that updates a transform to match the projection for a given frame state. This also means that the WebGLHelper does not set the projection matrix uniform anymore, this is the responsibility of the renderer as the rendered coordinates will not be in world space from now on. --- src/ol/renderer/webgl/PointsLayer.js | 42 ++++++++++------------------ src/ol/webgl/Helper.js | 37 ++++++++++++++---------- test/spec/ol/webgl/helper.test.js | 35 ++++++++++++++++++++++- 3 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index e76b936a8a..293e84f67d 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -12,9 +12,7 @@ import ViewHint from '../../ViewHint'; import {createEmpty, equals} from '../../extent'; import { create as createTransform, - reset as resetTransform, rotate as rotateTransform, - scale as scaleTransform, - translate as translateTransform, + reset as resetTransform, makeInverse as makeInverseTransform, multiply as multiplyTransform, apply as applyTransform @@ -269,6 +267,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { * @private */ this.renderTransform_ = createTransform(); + + /** + * @type {import("../../transform.js").Transform} + * @private + */ + this.invertRenderTransform_ = createTransform(); } /** @@ -321,20 +325,9 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.previousExtent_ = frameState.extent.slice(); } - // regenerate the transform matrix - const size = frameState.size; - const rotation = frameState.viewState.rotation; - const resolution = frameState.viewState.resolution; - const center = frameState.viewState.center; - - resetTransform(this.currentTransform_); - scaleTransform(this.currentTransform_, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - rotateTransform(this.currentTransform_, -rotation); - translateTransform(this.currentTransform_, -center[0], -center[1]); - - // the current transform - const inverseCurrentTransform = makeInverseTransform(createTransform(), this.renderTransform_); - this.currentTransform_ = multiplyTransform(this.currentTransform_, inverseCurrentTransform); + // apply the current projection transform with the invert of the one used to fill buffers + this.helper_.makeProjectionTransform(frameState, this.currentTransform_); + multiplyTransform(this.currentTransform_, this.invertRenderTransform_); this.helper_.prepareDraw(frameState); @@ -365,16 +358,9 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.verticesBuffer_.getArray().length = 0; this.indicesBuffer_.getArray().length = 0; - // regenerate the transform matrix - const size = frameState.size; - const rotation = frameState.viewState.rotation; - const resolution = frameState.viewState.resolution; - const center = frameState.viewState.center; - - resetTransform(this.renderTransform_); - scaleTransform(this.renderTransform_, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - rotateTransform(this.renderTransform_, -rotation); - translateTransform(this.renderTransform_, -center[0], -center[1]); + // saves the projection transform for the current frame state + this.helper_.makeProjectionTransform(frameState, this.renderTransform_); + makeInverseTransform(this.invertRenderTransform_, this.renderTransform_); // loop on features to fill the buffer const features = vectorSource.getFeatures(); @@ -382,7 +368,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { for (let i = 0; i < features.length; i++) { feature = features[i]; if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) { - return; + continue; } let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)]; diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 822c744337..b5ec037408 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -273,12 +273,6 @@ class WebGLHelper extends Disposable { listen(this.canvas_, ContextEventType.RESTORED, this.handleWebGLContextRestored, this); - /** - * @private - * @type {import("../transform.js").Transform} - */ - this.projectionMatrix_ = createTransform(); - /** * @private * @type {import("../transform.js").Transform} @@ -514,14 +508,6 @@ class WebGLHelper extends Disposable { applyFrameState(frameState) { const size = frameState.size; const rotation = frameState.viewState.rotation; - const resolution = frameState.viewState.resolution; - const center = frameState.viewState.center; - - // set the "uniform" values (coordinates 0,0 are the center of the view) - const projectionMatrix = resetTransform(this.projectionMatrix_); - scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - rotateTransform(projectionMatrix, -rotation); - translateTransform(projectionMatrix, -center[0], -center[1]); const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); @@ -531,7 +517,6 @@ class WebGLHelper extends Disposable { rotateTransform(offsetRotateMatrix, -rotation); } - this.setUniformMatrixValue(DefaultUniform.PROJECTION_MATRIX, fromTransform(this.tmpMat4_, projectionMatrix)); this.setUniformMatrixValue(DefaultUniform.OFFSET_SCALE_MATRIX, fromTransform(this.tmpMat4_, offsetScaleMatrix)); this.setUniformMatrixValue(DefaultUniform.OFFSET_ROTATION_MATRIX, fromTransform(this.tmpMat4_, offsetRotateMatrix)); } @@ -691,6 +676,28 @@ class WebGLHelper extends Disposable { return this.attribLocations_[name]; } + /** + * Modifies the given transform to apply the rotation/translation/scaling of the given frame state. + * The resulting transform can be used to convert world space coordinates to view coordinates. + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../transform").Transform} transform Transform to update. + * @return {import("../transform").Transform} The updated transform object. + * @api + */ + makeProjectionTransform(frameState, transform) { + const size = frameState.size; + const rotation = frameState.viewState.rotation; + const resolution = frameState.viewState.resolution; + const center = frameState.viewState.center; + + resetTransform(transform); + scaleTransform(transform, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(transform, -rotation); + translateTransform(transform, -center[0], -center[1]); + + return transform; + } + /** * Give a value for a standard float uniform * @param {string} uniform Uniform name diff --git a/test/spec/ol/webgl/helper.test.js b/test/spec/ol/webgl/helper.test.js index 11d7469bb1..7fad2ccda4 100644 --- a/test/spec/ol/webgl/helper.test.js +++ b/test/spec/ol/webgl/helper.test.js @@ -1,5 +1,10 @@ import WebGLHelper from '../../../../src/ol/webgl/Helper'; -import {create as createTransform} from '../../../../src/ol/transform'; +import { + create as createTransform, + multiply, + rotate as rotateTransform, + scale as scaleTransform, translate as translateTransform +} from '../../../../src/ol/transform'; const VERTEX_SHADER = ` @@ -185,5 +190,33 @@ describe('ol.webgl.WebGLHelper', function() { }); }); + describe('#makeProjectionTransform', function() { + let h; + let frameState; + beforeEach(function() { + h = new WebGLHelper(); + + frameState = { + size: [100, 150], + viewState: { + rotation: 0.4, + resolution: 2, + center: [10, 20] + } + } + }); + + it('gives out the correct transform', function() { + const scaleX = 2 / frameState.size[0] / frameState.viewState.resolution; + const scaleY = 2 / frameState.size[1] / frameState.viewState.resolution; + const given = createTransform(); + const expected = createTransform(); + scaleTransform(expected, scaleX, scaleY); + rotateTransform(expected, -frameState.viewState.rotation); + translateTransform(expected, -frameState.viewState.center[0], -frameState.viewState.center[1]); + expect(h.makeProjectionTransform(frameState, given)).to.eql(expected); + }); + }); + }); }); From 9ca75e9d430a60fbcab46191d037eaebbb9ed734 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 19:09:08 +0200 Subject: [PATCH 11/13] Webgl points / rebuild buffers when source change --- src/ol/renderer/webgl/PointsLayer.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 293e84f67d..fc08b266d8 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -310,7 +310,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const stride = 12; // the source has changed: clear the feature cache & reload features - if (this.sourceRevision_ < vectorSource.getRevision()) { + const sourceChanged = this.sourceRevision_ < vectorSource.getRevision(); + if (sourceChanged) { this.sourceRevision_ = vectorSource.getRevision(); this.geojsonFeatureCache_ = {}; @@ -319,8 +320,9 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection); } - if (!frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING] && - !equals(this.previousExtent_, frameState.extent)) { + const viewNotMoving = !frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING]; + const extentChanged = !equals(this.previousExtent_, frameState.extent); + if ((sourceChanged || extentChanged) && viewNotMoving) { this.rebuildBuffers_(frameState); this.previousExtent_ = frameState.extent.slice(); } From c705775d753d99f1f0feffee578aa837656a0883 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 14 May 2019 19:14:12 +0200 Subject: [PATCH 12/13] Linting --- src/ol/renderer/webgl/Layer.js | 16 +++---- src/ol/renderer/webgl/PointsLayer.js | 3 +- test/spec/ol/renderer/webgl/layer.test.js | 42 +++++++++---------- .../ol/renderer/webgl/pointslayer.test.js | 6 +-- test/spec/ol/webgl/helper.test.js | 3 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 67792ccd7e..308be68529 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -83,9 +83,11 @@ export function pushFeatureInBuffer(vertexBuffer, indexBuffer, geojsonFeature, o if (!geojsonFeature.geometry) { return; } - switch(geojsonFeature.geometry.type) { - case "Point": - pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) + switch (geojsonFeature.geometry.type) { + case 'Point': + pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes); + return; + default: return; } } @@ -94,10 +96,10 @@ const tmpArray_ = []; /** * Pushes a quad (two triangles) based on a point geometry - * @param vertexBuffer - * @param indexBuffer - * @param geojsonFeature - * @param opt_attributes + * @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer + * @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer + * @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature + * @param {Array} [opt_attributes] Custom attributes * @private */ function pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index fc08b266d8..55c795e6e4 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -12,7 +12,6 @@ import ViewHint from '../../ViewHint'; import {createEmpty, equals} from '../../extent'; import { create as createTransform, - reset as resetTransform, makeInverse as makeInverseTransform, multiply as multiplyTransform, apply as applyTransform @@ -350,7 +349,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { /** * Rebuild internal webgl buffers based on current view extent; costly, should not be called too much - * @param {import("../../PluggableMap.js").FrameState} frameState + * @param {import("../../PluggableMap").FrameState} frameState Frame state. * @private */ rebuildBuffers_(frameState) { diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index 59e81ae1db..427c7d56e1 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -9,18 +9,18 @@ describe('ol.renderer.webgl.Layer', function() { let target; - beforeEach(function () { + beforeEach(function() { target = document.createElement('div'); target.style.width = '256px'; target.style.height = '256px'; document.body.appendChild(target); }); - afterEach(function () { + afterEach(function() { document.body.removeChild(target); }); - it('creates a new instance', function () { + it('creates a new instance', function() { const layer = new Layer({}); const renderer = new WebGLLayerRenderer(layer); expect(renderer).to.be.a(WebGLLayerRenderer); @@ -31,17 +31,17 @@ describe('ol.renderer.webgl.Layer', function() { describe('pushFeatureInBuffer', function() { let vertexBuffer, indexBuffer; - beforeEach(function () { + beforeEach(function() { vertexBuffer = new WebGLArrayBuffer(); indexBuffer = new WebGLArrayBuffer(); }); it('does nothing if the feature has no geometry', function() { const feature = { - type: "Feature", - id: "AFG", + type: 'Feature', + id: 'AFG', properties: { - color:[0.5, 1, 0.2, 0.7], + color: [0.5, 1, 0.2, 0.7], size: 3 }, geometry: null @@ -53,15 +53,15 @@ describe('ol.renderer.webgl.Layer', function() { it('adds two triangles with the correct attributes for a point geometry', function() { const feature = { - type: "Feature", - id: "AFG", + type: 'Feature', + id: 'AFG', properties: { - color:[0.5, 1, 0.2, 0.7], + color: [0.5, 1, 0.2, 0.7], size: 3 }, geometry: { - type: "Point", - coordinates: [ -75, 47 ] + type: 'Point', + coordinates: [-75, 47] } }; const attributePerVertex = 12; @@ -72,15 +72,15 @@ describe('ol.renderer.webgl.Layer', function() { it('correctly sets indices & coordinates for several features', function() { const feature = { - type: "Feature", - id: "AFG", + type: 'Feature', + id: 'AFG', properties: { - color:[0.5, 1, 0.2, 0.7], + color: [0.5, 1, 0.2, 0.7], size: 3 }, geometry: { - type: "Point", - coordinates: [ -75, 47 ] + type: 'Point', + coordinates: [-75, 47] } }; const attributePerVertex = 12; @@ -110,8 +110,8 @@ describe('ol.renderer.webgl.Layer', function() { it('correctly adds custom attributes', function() { const feature = { - type: "Feature", - id: "AFG", + type: 'Feature', + id: 'AFG', properties: { color: [0.5, 1, 0.2, 0.7], custom: 4, @@ -120,8 +120,8 @@ describe('ol.renderer.webgl.Layer', function() { customString2: 'abc' }, geometry: { - type: "Point", - coordinates: [ -75, 47 ] + type: 'Point', + coordinates: [-75, 47] } }; const attributePerVertex = 16; diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index 367fd2696f..b85048b789 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -15,18 +15,18 @@ describe('ol.renderer.webgl.PointsLayer', function() { let target; - beforeEach(function () { + beforeEach(function() { target = document.createElement('div'); target.style.width = '256px'; target.style.height = '256px'; document.body.appendChild(target); }); - afterEach(function () { + afterEach(function() { document.body.removeChild(target); }); - it('creates a new instance', function () { + it('creates a new instance', function() { const layer = new VectorLayer({ source: new VectorSource() }); diff --git a/test/spec/ol/webgl/helper.test.js b/test/spec/ol/webgl/helper.test.js index 7fad2ccda4..b6a75761bd 100644 --- a/test/spec/ol/webgl/helper.test.js +++ b/test/spec/ol/webgl/helper.test.js @@ -1,7 +1,6 @@ import WebGLHelper from '../../../../src/ol/webgl/Helper'; import { create as createTransform, - multiply, rotate as rotateTransform, scale as scaleTransform, translate as translateTransform } from '../../../../src/ol/transform'; @@ -203,7 +202,7 @@ describe('ol.webgl.WebGLHelper', function() { resolution: 2, center: [10, 20] } - } + }; }); it('gives out the correct transform', function() { From e11e3c5f6ebdf6780e4cd484b222cb6fafcb56e6 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Wed, 15 May 2019 11:29:54 +0200 Subject: [PATCH 13/13] Webgl / minor function renaming --- src/ol/renderer/webgl/Layer.js | 8 ++++---- src/ol/renderer/webgl/PointsLayer.js | 4 ++-- test/spec/ol/renderer/webgl/layer.test.js | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 308be68529..c17dfbb56d 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -61,7 +61,7 @@ class WebGLLayerRenderer extends LayerRenderer { /** - * Pushes vertices and indices in the given buffers using the geometry coordinates and the following properties + * Pushes vertices and indices to the given buffers using the geometry coordinates and the following properties * from the feature: * - `color` * - `opacity` @@ -79,13 +79,13 @@ class WebGLLayerRenderer extends LayerRenderer { * feature and pushed in the buffer in the given order. Note: attributes can only be numerical! Any other type or * NaN will result in `0` being pushed in the buffer. */ -export function pushFeatureInBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { +export function pushFeatureToBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { if (!geojsonFeature.geometry) { return; } switch (geojsonFeature.geometry.type) { case 'Point': - pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes); + pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes); return; default: return; @@ -102,7 +102,7 @@ const tmpArray_ = []; * @param {Array} [opt_attributes] Custom attributes * @private */ -function pushPointGeomInBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { +function pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0); const x = geojsonFeature.geometry.coordinates[0]; diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 55c795e6e4..77b4e5f623 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -5,7 +5,7 @@ import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType'; -import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer'; +import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from './Layer'; import GeoJSON from '../../format/GeoJSON'; import {getUid} from '../../util'; import ViewHint from '../../ViewHint'; @@ -391,7 +391,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { geojsonFeature.properties.opacity = this.opacityCallback_(feature); geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0; - pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature); + pushFeatureToBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature); } this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_); diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index 427c7d56e1..83c24a6704 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -1,4 +1,4 @@ -import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from '../../../../../src/ol/renderer/webgl/Layer'; +import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from '../../../../../src/ol/renderer/webgl/Layer'; import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer'; import Layer from '../../../../../src/ol/layer/Layer'; @@ -28,7 +28,7 @@ describe('ol.renderer.webgl.Layer', function() { }); - describe('pushFeatureInBuffer', function() { + describe('pushFeatureToBuffer', function() { let vertexBuffer, indexBuffer; beforeEach(function() { @@ -46,7 +46,7 @@ describe('ol.renderer.webgl.Layer', function() { }, geometry: null }; - pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); expect(vertexBuffer.getArray().length).to.eql(0); expect(indexBuffer.getArray().length).to.eql(0); }); @@ -65,7 +65,7 @@ describe('ol.renderer.webgl.Layer', function() { } }; const attributePerVertex = 12; - pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); expect(indexBuffer.getArray().length).to.eql(6); }); @@ -84,8 +84,8 @@ describe('ol.renderer.webgl.Layer', function() { } }; const attributePerVertex = 12; - pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); - pushFeatureInBuffer(vertexBuffer, indexBuffer, feature); + pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); + pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); expect(vertexBuffer.getArray()[0]).to.eql(-75); expect(vertexBuffer.getArray()[1]).to.eql(47); expect(vertexBuffer.getArray()[0 + attributePerVertex]).to.eql(-75); @@ -125,7 +125,7 @@ describe('ol.renderer.webgl.Layer', function() { } }; const attributePerVertex = 16; - pushFeatureInBuffer(vertexBuffer, indexBuffer, feature, ['custom', 'custom2', 'customString', 'customString2']); + pushFeatureToBuffer(vertexBuffer, indexBuffer, feature, ['custom', 'custom2', 'customString', 'customString2']); expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); expect(indexBuffer.getArray().length).to.eql(6); expect(vertexBuffer.getArray()[12]).to.eql(4);