diff --git a/src/ol/render/webgl/webglreplay.js b/src/ol/render/webgl/webglreplay.js index d017868ef3..079d315244 100644 --- a/src/ol/render/webgl/webglreplay.js +++ b/src/ol/render/webgl/webglreplay.js @@ -60,6 +60,12 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { */ this.groupIndices_ = []; + /** + * @type {Array.} + * @private + */ + this.hitDetectionGroupIndices_ = []; + /** * @type {number|undefined} * @private @@ -72,6 +78,12 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { */ this.images_ = []; + /** + * @type {Array.} + * @private + */ + this.hitDetectionImages_ = []; + /** * @type {number|undefined} * @private @@ -168,6 +180,12 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { */ this.textures_ = []; + /** + * @type {Array.} + * @private + */ + this.hitDetectionTextures_ = []; + /** * @type {Array.} * @private @@ -211,6 +229,7 @@ ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction = var verticesBuffer = this.verticesBuffer_; var indicesBuffer = this.indicesBuffer_; var textures = this.textures_; + var hitDetectionTextures = this.hitDetectionTextures_; var gl = context.getGL(); return function() { if (!gl.isContextLost()) { @@ -218,6 +237,9 @@ ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction = for (i = 0, ii = textures.length; i < ii; ++i) { gl.deleteTexture(textures[i]); } + for (i = 0, ii = hitDetectionTextures.length; i < ii; ++i) { + gl.deleteTexture(hitDetectionTextures[i]); + } } context.deleteBuffer(verticesBuffer); context.deleteBuffer(indicesBuffer); @@ -431,7 +453,10 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) { var gl = context.getGL(); this.groupIndices_.push(this.indices_.length); - goog.asserts.assert(this.images_.length == this.groupIndices_.length); + goog.asserts.assert(this.images_.length === this.groupIndices_.length); + this.hitDetectionGroupIndices_.push(this.indices_.length); + goog.asserts.assert(this.hitDetectionImages_.length === + this.hitDetectionGroupIndices_.length); // create, bind, and populate the vertices buffer this.verticesBuffer_ = new ol.webgl.Buffer(this.vertices_); @@ -447,16 +472,53 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) { this.indicesBuffer_ = new ol.webgl.Buffer(indices); context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); - goog.asserts.assert(this.textures_.length === 0); - // create textures - var texture, image, uid; /** @type {Object.} */ var texturePerImage = {}; - var i; - var ii = this.images_.length; + + this.createTextures_(this.textures_, this.images_, texturePerImage, gl); + goog.asserts.assert(this.textures_.length === this.groupIndices_.length); + + this.createTextures_(this.hitDetectionTextures_, this.hitDetectionImages_, + texturePerImage, gl); + goog.asserts.assert(this.hitDetectionTextures_.length === + this.hitDetectionGroupIndices_.length); + + this.anchorX_ = undefined; + this.anchorY_ = undefined; + this.height_ = undefined; + this.images_ = null; + this.hitDetectionImages_ = null; + this.imageHeight_ = undefined; + this.imageWidth_ = undefined; + this.indices_ = null; + this.opacity_ = undefined; + this.originX_ = undefined; + this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; + this.scale_ = undefined; + this.vertices_ = null; + this.width_ = undefined; +}; + + +/** + * @private + * @param {Array.} textures Textures. + * @param {Array.} images + * Images. + * @param {Object.} texturePerImage Texture cache. + * @param {WebGLRenderingContext} gl Gl. + */ +ol.render.webgl.ImageReplay.prototype.createTextures_ = + function(textures, images, texturePerImage, gl) { + goog.asserts.assert(textures.length === 0); + + var texture, image, uid, i; + var ii = images.length; for (i = 0; i < ii; ++i) { - image = this.images_[i]; + image = images[i]; uid = goog.getUid(image).toString(); if (goog.object.containsKey(texturePerImage, uid)) { @@ -476,26 +538,8 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) { goog.webgl.UNSIGNED_BYTE, image); texturePerImage[uid] = texture; } - this.textures_[i] = texture; + textures[i] = texture; } - - goog.asserts.assert(this.textures_.length == this.groupIndices_.length); - - this.anchorX_ = undefined; - this.anchorY_ = undefined; - this.height_ = undefined; - this.images_ = null; - this.imageHeight_ = undefined; - this.imageWidth_ = undefined; - this.indices_ = null; - this.opacity_ = undefined; - this.originX_ = undefined; - this.originY_ = undefined; - this.rotateWithView_ = undefined; - this.rotation_ = undefined; - this.scale_ = undefined; - this.vertices_ = null; - this.width_ = undefined; }; @@ -646,7 +690,7 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context, */ ol.render.webgl.ImageReplay.prototype.drawReplay_ = function(gl, context) { - goog.asserts.assert(this.textures_.length == this.groupIndices_.length); + goog.asserts.assert(this.textures_.length === this.groupIndices_.length); var elementType = context.hasOESElementIndexUint ? goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; var elementSize = context.hasOESElementIndexUint ? 4 : 2; @@ -673,17 +717,18 @@ ol.render.webgl.ImageReplay.prototype.drawReplay_ = */ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ = function(gl, context, featureCallback) { - goog.asserts.assert(this.textures_.length == this.groupIndices_.length); + goog.asserts.assert(this.hitDetectionTextures_.length === + this.hitDetectionGroupIndices_.length); var elementType = context.hasOESElementIndexUint ? goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; var elementSize = context.hasOESElementIndexUint ? 4 : 2; var i, groupStart, groupEnd, numItems, featureInfo, start, end; var featureIndex = this.startIndexForFeature_.length - 1; - for (i = this.textures_.length - 1; i >= 0; --i) { - gl.bindTexture(goog.webgl.TEXTURE_2D, this.textures_[i]); - groupStart = (i > 0) ? this.groupIndices_[i - 1] : 0; - end = this.groupIndices_[i]; + for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]); + groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0; + end = this.hitDetectionGroupIndices_[i]; // draw all features for this texture group while (featureIndex >= 0 && @@ -726,6 +771,10 @@ ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { goog.asserts.assert(!goog.isNull(image)); var imageSize = imageStyle.getImageSize(); goog.asserts.assert(!goog.isNull(imageSize)); + var hitDetectionImage = imageStyle.getHitDetectionImage(1); + goog.asserts.assert(!goog.isNull(hitDetectionImage)); + var hitDetectionImageSize = imageStyle.getHitDetectionImageSize(); + goog.asserts.assert(!goog.isNull(hitDetectionImageSize)); var opacity = imageStyle.getOpacity(); goog.asserts.assert(goog.isDef(opacity)); var origin = imageStyle.getOrigin(); @@ -739,17 +788,31 @@ ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { var scale = imageStyle.getScale(); goog.asserts.assert(goog.isDef(scale)); + var currentImage; if (this.images_.length === 0) { this.images_.push(image); } else { - var currentImage = this.images_[this.images_.length - 1]; + currentImage = this.images_[this.images_.length - 1]; if (goog.getUid(currentImage) != goog.getUid(image)) { this.groupIndices_.push(this.indices_.length); - goog.asserts.assert(this.groupIndices_.length == this.images_.length); + goog.asserts.assert(this.groupIndices_.length === this.images_.length); this.images_.push(image); } } + if (this.hitDetectionImages_.length === 0) { + this.hitDetectionImages_.push(hitDetectionImage); + } else { + currentImage = + this.hitDetectionImages_[this.hitDetectionImages_.length - 1]; + if (goog.getUid(currentImage) != goog.getUid(hitDetectionImage)) { + this.hitDetectionGroupIndices_.push(this.indices_.length); + goog.asserts.assert(this.hitDetectionGroupIndices_.length === + this.hitDetectionImages_.length); + this.hitDetectionImages_.push(hitDetectionImage); + } + } + this.anchorX_ = anchor[0]; this.anchorY_ = anchor[1]; this.height_ = size[1]; @@ -941,7 +1004,7 @@ ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context, * @return {T|undefined} Callback result. * @template T */ -ol.render.webgl.ReplayGroup.prototype.forEachGeometryAtPixel = function( +ol.render.webgl.ReplayGroup.prototype.forEachFeatureAtPixel = function( context, center, resolution, rotation, size, pixelRatio, opacity, brightness, contrast, hue, saturation, skippedFeaturesHash, coordinate, callback) { @@ -950,8 +1013,9 @@ ol.render.webgl.ReplayGroup.prototype.forEachGeometryAtPixel = function( gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); return this.replayHitDetection_(context, - coordinate, resolution, rotation, [1, 1], pixelRatio, - opacity, brightness, contrast, hue, saturation, skippedFeaturesHash, + coordinate, resolution, rotation, ol.render.webgl.HIT_DETECTION_SIZE_, + pixelRatio, opacity, brightness, contrast, hue, saturation, + skippedFeaturesHash, /** * @param {ol.Feature} feature Feature. * @return {?} Callback result. @@ -980,3 +1044,11 @@ ol.render.webgl.ReplayGroup.prototype.forEachGeometryAtPixel = function( ol.render.webgl.BATCH_CONSTRUCTORS_ = { 'Image': ol.render.webgl.ImageReplay }; + + +/** + * @const + * @private + * @type {Array.} + */ +ol.render.webgl.HIT_DETECTION_SIZE_ = [1, 1]; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 1c06f8dda7..1d6efe5372 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -560,7 +560,7 @@ ol.renderer.webgl.Map.prototype.forEachFeatureAtPixel = // use default color values var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_; - result = this.replayGroup.forEachGeometryAtPixel(context, + result = this.replayGroup.forEachFeatureAtPixel(context, viewState.center, viewState.resolution, viewState.rotation, frameState.size, frameState.pixelRatio, d.opacity, d.brightness, d.contrast, d.hue, d.saturation, {}, diff --git a/src/ol/renderer/webgl/webglvectorlayerrenderer.js b/src/ol/renderer/webgl/webglvectorlayerrenderer.js index 5c914ef16a..b53c0b6c8a 100644 --- a/src/ol/renderer/webgl/webglvectorlayerrenderer.js +++ b/src/ol/renderer/webgl/webglvectorlayerrenderer.js @@ -118,7 +118,7 @@ ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtPixel = var layerState = this.layerState_; /** @type {Object.} */ var features = {}; - return this.replayGroup_.forEachGeometryAtPixel(context, + return this.replayGroup_.forEachFeatureAtPixel(context, viewState.center, viewState.resolution, viewState.rotation, frameState.size, frameState.pixelRatio, layerState.opacity, layerState.brightness, layerState.contrast, diff --git a/src/ol/webgl/context.js b/src/ol/webgl/context.js index f0b06a84b1..0baaf6be80 100644 --- a/src/ol/webgl/context.js +++ b/src/ol/webgl/context.js @@ -282,6 +282,8 @@ ol.webgl.Context.prototype.handleWebGLContextLost = function() { goog.object.clear(this.programCache_); this.currentProgram_ = null; this.hitDetectionFramebuffer_ = null; + this.hitDetectionTexture_ = null; + this.hitDetectionRenderbuffer_ = null; }; @@ -304,8 +306,7 @@ ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() { var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); @@ -322,6 +323,8 @@ ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() { gl.bindFramebuffer(gl.FRAMEBUFFER, null); this.hitDetectionFramebuffer_ = framebuffer; + this.hitDetectionTexture_ = texture; + this.hitDetectionRenderbuffer_ = renderbuffer; }; diff --git a/test/spec/ol/render/webglreplay.test.js b/test/spec/ol/render/webglreplay.test.js index 8e600186e7..838004e6d5 100644 --- a/test/spec/ol/render/webglreplay.test.js +++ b/test/spec/ol/render/webglreplay.test.js @@ -16,9 +16,15 @@ describe('ol.render.webgl.ImageReplay', function() { imageStyle.getImage = function() { return image; }; + imageStyle.getHitDetectionImage = function() { + return image; + }; imageStyle.getImageSize = function() { return [512, 512]; }; + imageStyle.getHitDetectionImageSize = function() { + return [512, 512]; + }; imageStyle.getOrigin = function() { return [200, 200]; }; @@ -59,14 +65,20 @@ describe('ol.render.webgl.ImageReplay', function() { expect(replay.width_).to.be(256); expect(replay.images_).to.have.length(1); expect(replay.groupIndices_).to.have.length(0); + expect(replay.hitDetectionImages_).to.have.length(1); + expect(replay.hitDetectionGroupIndices_).to.have.length(0); replay.setImageStyle(imageStyle1); expect(replay.images_).to.have.length(1); expect(replay.groupIndices_).to.have.length(0); + expect(replay.hitDetectionImages_).to.have.length(1); + expect(replay.hitDetectionGroupIndices_).to.have.length(0); replay.setImageStyle(imageStyle2); expect(replay.images_).to.have.length(2); expect(replay.groupIndices_).to.have.length(1); + expect(replay.hitDetectionImages_).to.have.length(2); + expect(replay.hitDetectionGroupIndices_).to.have.length(1); }); });