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