Add hit-detection support for webgl
This commit is contained in:
@@ -1951,6 +1951,8 @@ ol.render.canvas.ReplayGroup.prototype.replay = function(
|
||||
var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number);
|
||||
goog.array.sort(zs);
|
||||
|
||||
// setup clipping so that the parts of over-simplified geometries are not
|
||||
// visible outside the current extent when panning
|
||||
var maxExtent = this.maxExtent_;
|
||||
var minX = maxExtent[0];
|
||||
var minY = maxExtent[1];
|
||||
|
||||
@@ -180,6 +180,13 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) {
|
||||
*/
|
||||
this.verticesBuffer_ = null;
|
||||
|
||||
/**
|
||||
* Start indices per feature.
|
||||
* @type {Array.<Array.<?>>}
|
||||
* @private
|
||||
*/
|
||||
this.startIndexForFeature_ = [];
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
@@ -377,6 +384,7 @@ ol.render.webgl.ImageReplay.prototype.drawMultiLineStringGeometry =
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawMultiPointGeometry =
|
||||
function(multiPointGeometry, feature) {
|
||||
this.startIndexForFeature_.push([this.indices_.length, feature]);
|
||||
var flatCoordinates = multiPointGeometry.getFlatCoordinates();
|
||||
var stride = multiPointGeometry.getStride();
|
||||
this.drawCoordinates_(
|
||||
@@ -396,6 +404,7 @@ ol.render.webgl.ImageReplay.prototype.drawMultiPolygonGeometry =
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawPointGeometry =
|
||||
function(pointGeometry, feature) {
|
||||
this.startIndexForFeature_.push([this.indices_.length, feature]);
|
||||
var flatCoordinates = pointGeometry.getFlatCoordinates();
|
||||
var stride = pointGeometry.getStride();
|
||||
this.drawCoordinates_(
|
||||
@@ -503,12 +512,14 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) {
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.replay = function(context,
|
||||
center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
|
||||
featureCallback) {
|
||||
var gl = context.getGL();
|
||||
|
||||
// bind the vertices buffer
|
||||
@@ -609,17 +620,12 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context,
|
||||
}
|
||||
|
||||
// draw!
|
||||
goog.asserts.assert(this.textures_.length == this.groupIndices_.length);
|
||||
var i, ii, start;
|
||||
for (i = 0, ii = this.textures_.length, start = 0; i < ii; ++i) {
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, this.textures_[i]);
|
||||
var end = this.groupIndices_[i];
|
||||
var numItems = end - start;
|
||||
var offsetInBytes = start * (context.hasOESElementIndexUint ? 4 : 2);
|
||||
var elementType = context.hasOESElementIndexUint ?
|
||||
goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT;
|
||||
gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
|
||||
start = end;
|
||||
var result;
|
||||
if (!goog.isDef(featureCallback)) {
|
||||
this.drawReplay_(gl, context);
|
||||
} else {
|
||||
// draw feature by feature for the hit-detection
|
||||
result = this.drawHitDetectionReplay_(gl, context, featureCallback);
|
||||
}
|
||||
|
||||
// disable the vertex attrib arrays
|
||||
@@ -628,6 +634,79 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context,
|
||||
gl.disableVertexAttribArray(locations.a_texCoord);
|
||||
gl.disableVertexAttribArray(locations.a_opacity);
|
||||
gl.disableVertexAttribArray(locations.a_rotateWithView);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {WebGLRenderingContext} gl gl.
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawReplay_ =
|
||||
function(gl, context) {
|
||||
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;
|
||||
|
||||
var i, ii, start;
|
||||
for (i = 0, ii = this.textures_.length, start = 0; i < ii; ++i) {
|
||||
gl.bindTexture(goog.webgl.TEXTURE_2D, this.textures_[i]);
|
||||
var end = this.groupIndices_[i];
|
||||
var numItems = end - start;
|
||||
var offsetInBytes = start * elementSize;
|
||||
gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
|
||||
start = end;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {WebGLRenderingContext} gl gl.
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ =
|
||||
function(gl, context, featureCallback) {
|
||||
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;
|
||||
|
||||
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];
|
||||
|
||||
// draw all features for this texture group
|
||||
while (featureIndex >= 0 &&
|
||||
this.startIndexForFeature_[featureIndex][0] >= groupStart) {
|
||||
featureInfo = this.startIndexForFeature_[featureIndex];
|
||||
start = featureInfo[0];
|
||||
numItems = end - start;
|
||||
var offsetInBytes = start * elementSize;
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(
|
||||
goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
|
||||
|
||||
var result = featureCallback(/** @type {ol.Feature} */ (featureInfo[1]));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
end = start;
|
||||
featureIndex--;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -788,19 +867,53 @@ ol.render.webgl.ReplayGroup.prototype.isEmpty = function() {
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.replay = function(context,
|
||||
center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
|
||||
var i, ii, replay, result;
|
||||
for (i = 0, ii = ol.render.REPLAY_ORDER.length; i < ii; ++i) {
|
||||
replay = this.replays_[ol.render.REPLAY_ORDER[i]];
|
||||
if (goog.isDef(replay)) {
|
||||
replay.replay(context,
|
||||
center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
|
||||
undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {ol.Size} size Size.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {number} opacity Global opacity.
|
||||
* @param {number} brightness Global brightness.
|
||||
* @param {number} contrast Global contrast.
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context,
|
||||
center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
|
||||
featureCallback) {
|
||||
var i, replay, result;
|
||||
for (i = ol.render.REPLAY_ORDER.length - 1; i >= 0; --i) {
|
||||
replay = this.replays_[ol.render.REPLAY_ORDER[i]];
|
||||
if (goog.isDef(replay)) {
|
||||
result = replay.replay(context,
|
||||
center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash);
|
||||
opacity, brightness, contrast, hue, saturation,
|
||||
skippedFeaturesHash, featureCallback);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@@ -810,6 +923,53 @@ ol.render.webgl.ReplayGroup.prototype.replay = function(context,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.webgl.Context} context Context.
|
||||
* @param {ol.Coordinate} center Center.
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {ol.Size} size Size.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {number} opacity Global opacity.
|
||||
* @param {number} brightness Global brightness.
|
||||
* @param {number} contrast Global contrast.
|
||||
* @param {number} hue Global hue.
|
||||
* @param {number} saturation Global saturation.
|
||||
* @param {Object} skippedFeaturesHash Ids of features to skip.
|
||||
* @param {ol.Coordinate} coordinate Coordinate.
|
||||
* @param {function(ol.Feature): T|undefined} callback Feature callback.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
ol.render.webgl.ReplayGroup.prototype.forEachGeometryAtPixel = function(
|
||||
context, center, resolution, rotation, size, pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
|
||||
coordinate, callback) {
|
||||
var gl = context.getGL();
|
||||
gl.bindFramebuffer(
|
||||
gl.FRAMEBUFFER, context.getHitDetectionFramebuffer());
|
||||
|
||||
return this.replayHitDetection_(context,
|
||||
coordinate, resolution, rotation, [1, 1], pixelRatio,
|
||||
opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @return {?} Callback result.
|
||||
*/
|
||||
function(feature) {
|
||||
var imageData = new Uint8Array(4);
|
||||
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
|
||||
|
||||
if (imageData[3] > 0) {
|
||||
var result = callback(feature);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
|
||||
@@ -290,13 +290,10 @@ ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ =
|
||||
replayGroup.finish(context);
|
||||
if (!replayGroup.isEmpty()) {
|
||||
// use default color values
|
||||
var opacity = 1;
|
||||
var brightness = 0;
|
||||
var contrast = 1;
|
||||
var hue = 0;
|
||||
var saturation = 1;
|
||||
var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
|
||||
replayGroup.replay(context, center, resolution, rotation, size,
|
||||
pixelRatio, opacity, brightness, contrast, hue, saturation, {});
|
||||
pixelRatio, d.opacity, d.brightness, d.contrast,
|
||||
d.hue, d.saturation, {});
|
||||
}
|
||||
replayGroup.getDeleteResourcesFunction(context)();
|
||||
|
||||
@@ -538,3 +535,79 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
|
||||
this.scheduleExpireIconCache(frameState);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.renderer.webgl.Map.prototype.forEachFeatureAtPixel =
|
||||
function(coordinate, frameState, callback, thisArg,
|
||||
layerFilter, thisArg2) {
|
||||
var result;
|
||||
|
||||
if (this.getGL().isContextLost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var context = this.getContext();
|
||||
var viewState = frameState.viewState;
|
||||
|
||||
// do the hit-detection for the overlays first
|
||||
if (!goog.isNull(this.replayGroup)) {
|
||||
/** @type {Object.<string, boolean>} */
|
||||
var features = {};
|
||||
|
||||
// use default color values
|
||||
var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
|
||||
|
||||
result = this.replayGroup.forEachGeometryAtPixel(context,
|
||||
viewState.center, viewState.resolution, viewState.rotation,
|
||||
frameState.size, frameState.pixelRatio,
|
||||
d.opacity, d.brightness, d.contrast, d.hue, d.saturation, {},
|
||||
coordinate,
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @return {?} Callback result.
|
||||
*/
|
||||
function(feature) {
|
||||
goog.asserts.assert(goog.isDef(feature));
|
||||
var key = goog.getUid(feature).toString();
|
||||
if (!(key in features)) {
|
||||
features[key] = true;
|
||||
return callback.call(thisArg, feature, null);
|
||||
}
|
||||
});
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
var layerStates = this.getMap().getLayerGroup().getLayerStatesArray();
|
||||
var numLayers = layerStates.length;
|
||||
var i;
|
||||
for (i = numLayers - 1; i >= 0; --i) {
|
||||
var layerState = layerStates[i];
|
||||
var layer = layerState.layer;
|
||||
if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
|
||||
layerFilter.call(thisArg2, layer)) {
|
||||
var layerRenderer = this.getLayerRenderer(layer);
|
||||
result = layerRenderer.forEachFeatureAtPixel(
|
||||
coordinate, frameState, callback, thisArg);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_ = {
|
||||
opacity: 1,
|
||||
brightness: 0,
|
||||
contrast: 1,
|
||||
hue: 0,
|
||||
saturation: 1
|
||||
};
|
||||
|
||||
@@ -58,6 +58,13 @@ ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) {
|
||||
*/
|
||||
this.replayGroup_ = null;
|
||||
|
||||
/**
|
||||
* The last layer state.
|
||||
* @private
|
||||
* @type {?ol.layer.LayerState}
|
||||
*/
|
||||
this.layerState_ = null;
|
||||
|
||||
};
|
||||
goog.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer);
|
||||
|
||||
@@ -67,6 +74,7 @@ goog.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer);
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.composeFrame =
|
||||
function(frameState, layerState, context) {
|
||||
this.layerState_ = layerState;
|
||||
var viewState = frameState.viewState;
|
||||
var replayGroup = this.replayGroup_;
|
||||
if (!goog.isNull(replayGroup) && !replayGroup.isEmpty()) {
|
||||
@@ -100,6 +108,35 @@ ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() {
|
||||
*/
|
||||
ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtPixel =
|
||||
function(coordinate, frameState, callback, thisArg) {
|
||||
if (goog.isNull(this.replayGroup_) || goog.isNull(this.layerState_)) {
|
||||
return undefined;
|
||||
} else {
|
||||
var mapRenderer = this.getWebGLMapRenderer();
|
||||
var context = mapRenderer.getContext();
|
||||
var viewState = frameState.viewState;
|
||||
var layer = this.getLayer();
|
||||
var layerState = this.layerState_;
|
||||
/** @type {Object.<string, boolean>} */
|
||||
var features = {};
|
||||
return this.replayGroup_.forEachGeometryAtPixel(context,
|
||||
viewState.center, viewState.resolution, viewState.rotation,
|
||||
frameState.size, frameState.pixelRatio,
|
||||
layerState.opacity, layerState.brightness, layerState.contrast,
|
||||
layerState.hue, layerState.saturation, frameState.skippedFeatureUids,
|
||||
coordinate,
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @return {?} Callback result.
|
||||
*/
|
||||
function(feature) {
|
||||
goog.asserts.assert(goog.isDef(feature));
|
||||
var key = goog.getUid(feature).toString();
|
||||
if (!(key in features)) {
|
||||
features[key] = true;
|
||||
return callback.call(thisArg, feature, layer);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user