diff --git a/examples/webgl-points-layer.js b/examples/webgl-points-layer.js index efcde2a0d2..47bba2a1e1 100644 --- a/examples/webgl-points-layer.js +++ b/examples/webgl-points-layer.js @@ -9,6 +9,7 @@ import WebGLPointsLayer from '../src/ol/layer/WebGLPoints.js'; const vectorSource = new Vector({ url: 'data/geojson/world-cities.geojson', format: new GeoJSON(), + wrapX: true, }); const predefinedStyles = { @@ -79,7 +80,7 @@ const predefinedStyles = { 2000000, 28, ], - color: '#006688', + color: ['match', ['get', 'hover'], 1, '#ff3f3f', '#006688'], rotateWithView: false, offset: [0, 0], opacity: [ @@ -97,7 +98,7 @@ const predefinedStyles = { symbol: { symbolType: 'circle', size: ['interpolate', ['exponential', 2.5], ['zoom'], 2, 1, 14, 32], - color: '#240572', + color: ['match', ['get', 'hover'], 1, '#ff3f3f', '#006688'], offset: [0, 0], opacity: 0.95, }, @@ -160,12 +161,27 @@ const map = new Map({ let literalStyle; let pointsLayer; + +let selected = null; + +map.on('pointermove', function (ev) { + if (selected !== null) { + selected.set('hover', 0); + selected = null; + } + + map.forEachFeatureAtPixel(ev.pixel, function (feature) { + feature.set('hover', 1); + selected = feature; + return true; + }); +}); + function refreshLayer(newStyle) { const previousLayer = pointsLayer; pointsLayer = new WebGLPointsLayer({ source: vectorSource, style: newStyle, - disableHitDetection: true, }); map.addLayer(pointsLayer); diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index a94e778a2c..33c0358151 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -19,9 +19,10 @@ import { create as createTransform, makeInverse as makeInverseTransform, multiply as multiplyTransform, + translate as translateTransform, } from '../../transform.js'; import {assert} from '../../asserts.js'; -import {buffer, createEmpty, equals} from '../../extent.js'; +import {buffer, createEmpty, equals, getWidth} from '../../extent.js'; import {create as createWebGLWorker} from '../../worker/webgl.js'; import {getUid} from '../../util.js'; import {listen, unlistenByKey} from '../../events.js'; @@ -461,8 +462,36 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const gl = this.helper.getGL(); this.preRender(gl, frameState); + const projection = frameState.viewState.projection; + const layer = this.getLayer(); + const vectorSource = layer.getSource(); + // FIXME fix hit detection isn't reliable when rendering multiple worlds + const multiWorld = vectorSource.getWrapX() && projection.canWrapX(); + const projectionExtent = projection.getExtent(); + + const extent = frameState.extent; + const worldWidth = multiWorld ? getWidth(projectionExtent) : null; + const endWorld = multiWorld + ? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1 + : 1; + + const startWorld = multiWorld + ? Math.floor((extent[0] - projectionExtent[0]) / worldWidth) + : 0; + + let world = startWorld; const renderCount = this.indicesBuffer_.getSize(); - this.helper.drawElements(0, renderCount); + + do { + // apply the current projection transform with the invert of the one used to fill buffers + this.helper.makeProjectionTransform(frameState, this.currentTransform_); + translateTransform(this.currentTransform_, world * worldWidth, 0); + multiplyTransform(this.currentTransform_, this.invertRenderTransform_); + this.helper.applyUniforms(frameState); + + this.helper.drawElements(0, renderCount); + } while (++world < endWorld); + this.helper.finalizeDraw( frameState, this.dispatchPreComposeEvent, @@ -471,7 +500,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { const canvas = this.helper.getCanvas(); if (this.hitDetectionEnabled_) { - this.renderHitDetection(frameState); + this.renderHitDetection(frameState, startWorld, endWorld, worldWidth); this.hitRenderTarget_.clearCachedData(); } @@ -512,17 +541,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.previousExtent_ = frameState.extent.slice(); } - // 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.useProgram(this.program_); this.helper.prepareDraw(frameState); // write new data this.helper.bindBuffer(this.verticesBuffer_); this.helper.bindBuffer(this.indicesBuffer_); - this.helper.enableAttributes(this.attributes); return true; @@ -686,13 +710,18 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { /** * Render the hit detection data to the corresponding render target * @param {import("../../PluggableMap.js").FrameState} frameState current frame state + * @param {number} startWorld the world to render in the first iteration + * @param {number} endWorld the last world to render + * @param {number} worldWidth the width of the worlds being rendered */ - renderHitDetection(frameState) { + renderHitDetection(frameState, startWorld, endWorld, worldWidth) { // skip render entirely if vertex buffers not ready/generated yet if (!this.hitVerticesBuffer_.getSize()) { return; } + let world = startWorld; + this.hitRenderTarget_.setSize([ Math.floor(frameState.size[0] / 2), Math.floor(frameState.size[1] / 2), @@ -707,11 +736,17 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.helper.bindBuffer(this.hitVerticesBuffer_); this.helper.bindBuffer(this.indicesBuffer_); - this.helper.enableAttributes(this.hitDetectionAttributes); - const renderCount = this.indicesBuffer_.getSize(); - this.helper.drawElements(0, renderCount); + do { + this.helper.makeProjectionTransform(frameState, this.currentTransform_); + translateTransform(this.currentTransform_, world * worldWidth, 0); + multiplyTransform(this.currentTransform_, this.invertRenderTransform_); + this.helper.applyUniforms(frameState); + + const renderCount = this.indicesBuffer_.getSize(); + this.helper.drawElements(0, renderCount); + } while (++world < endWorld); } /** diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 4b6d645722..383b06e848 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -678,7 +678,6 @@ class WebGLHelper extends Disposable { /** * Sets the default matrix uniforms for a given frame state. This is called internally in `prepareDraw`. * @param {import("../PluggableMap.js").FrameState} frameState Frame state. - * @private */ applyFrameState(frameState) { const size = frameState.size; @@ -715,7 +714,6 @@ class WebGLHelper extends Disposable { /** * Sets the custom uniforms based on what was given in the constructor. This is called internally in `prepareDraw`. * @param {import("../PluggableMap.js").FrameState} frameState Frame state. - * @private */ applyUniforms(frameState) { const gl = this.getGL(); diff --git a/test/rendering/cases/webgl-points/expected.png b/test/rendering/cases/webgl-points/expected.png index a30c672ed0..abd9724da2 100644 Binary files a/test/rendering/cases/webgl-points/expected.png and b/test/rendering/cases/webgl-points/expected.png differ