366 lines
12 KiB
JavaScript
366 lines
12 KiB
JavaScript
goog.provide('ol.render.webgl.Replay');
|
|
|
|
goog.require('ol');
|
|
goog.require('ol.extent');
|
|
goog.require('ol.render.VectorContext');
|
|
goog.require('ol.transform');
|
|
goog.require('ol.vec.Mat4');
|
|
goog.require('ol.webgl');
|
|
|
|
|
|
if (ol.ENABLE_WEBGL) {
|
|
|
|
/**
|
|
* @constructor
|
|
* @abstract
|
|
* @extends {ol.render.VectorContext}
|
|
* @param {number} tolerance Tolerance.
|
|
* @param {ol.Extent} maxExtent Max extent.
|
|
* @struct
|
|
*/
|
|
ol.render.webgl.Replay = function(tolerance, maxExtent) {
|
|
ol.render.VectorContext.call(this);
|
|
|
|
/**
|
|
* @protected
|
|
* @type {number}
|
|
*/
|
|
this.tolerance = tolerance;
|
|
|
|
/**
|
|
* @protected
|
|
* @const
|
|
* @type {ol.Extent}
|
|
*/
|
|
this.maxExtent = maxExtent;
|
|
|
|
/**
|
|
* The origin of the coordinate system for the point coordinates sent to
|
|
* the GPU. To eliminate jitter caused by precision problems in the GPU
|
|
* we use the "Rendering Relative to Eye" technique described in the "3D
|
|
* Engine Design for Virtual Globes" book.
|
|
* @protected
|
|
* @type {ol.Coordinate}
|
|
*/
|
|
this.origin = ol.extent.getCenter(maxExtent);
|
|
|
|
/**
|
|
* @private
|
|
* @type {ol.Transform}
|
|
*/
|
|
this.projectionMatrix_ = ol.transform.create();
|
|
|
|
/**
|
|
* @private
|
|
* @type {ol.Transform}
|
|
*/
|
|
this.offsetRotateMatrix_ = ol.transform.create();
|
|
|
|
/**
|
|
* @private
|
|
* @type {ol.Transform}
|
|
*/
|
|
this.offsetScaleMatrix_ = ol.transform.create();
|
|
|
|
/**
|
|
* @private
|
|
* @type {Array.<number>}
|
|
*/
|
|
this.tmpMat4_ = ol.vec.Mat4.create();
|
|
|
|
/**
|
|
* @protected
|
|
* @type {Array.<number>}
|
|
*/
|
|
this.indices = [];
|
|
|
|
/**
|
|
* @protected
|
|
* @type {?ol.webgl.Buffer}
|
|
*/
|
|
this.indicesBuffer = null;
|
|
|
|
/**
|
|
* Start index per feature (the index).
|
|
* @protected
|
|
* @type {Array.<number>}
|
|
*/
|
|
this.startIndices = [];
|
|
|
|
/**
|
|
* Start index per feature (the feature).
|
|
* @protected
|
|
* @type {Array.<ol.Feature|ol.render.Feature>}
|
|
*/
|
|
this.startIndicesFeature = [];
|
|
|
|
/**
|
|
* @protected
|
|
* @type {Array.<number>}
|
|
*/
|
|
this.vertices = [];
|
|
|
|
/**
|
|
* @protected
|
|
* @type {?ol.webgl.Buffer}
|
|
*/
|
|
this.verticesBuffer = null;
|
|
|
|
/**
|
|
* Optional parameter for PolygonReplay instances.
|
|
* @protected
|
|
* @type {ol.render.webgl.LineStringReplay|undefined}
|
|
*/
|
|
this.lineStringReplay = undefined;
|
|
|
|
};
|
|
ol.inherits(ol.render.webgl.Replay, ol.render.VectorContext);
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @param {ol.webgl.Context} context WebGL context.
|
|
* @return {function()} Delete resources function.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.getDeleteResourcesFunction = function(context) {};
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @param {ol.webgl.Context} context Context.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.finish = function(context) {};
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {ol.Size} size Size.
|
|
* @param {number} pixelRatio Pixel ratio.
|
|
* @return {ol.render.webgl.circlereplay.defaultshader.Locations|
|
|
ol.render.webgl.imagereplay.defaultshader.Locations|
|
|
ol.render.webgl.linestringreplay.defaultshader.Locations|
|
|
ol.render.webgl.polygonreplay.defaultshader.Locations} Locations.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {};
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.render.webgl.circlereplay.defaultshader.Locations|
|
|
ol.render.webgl.imagereplay.defaultshader.Locations|
|
|
ol.render.webgl.linestringreplay.defaultshader.Locations|
|
|
ol.render.webgl.polygonreplay.defaultshader.Locations} locations Locations.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.shutDownProgram = function(gl, locations) {};
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
|
|
* to skip.
|
|
* @param {boolean} hitDetection Hit detection mode.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {};
|
|
|
|
|
|
/**
|
|
* @abstract
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
|
|
* to skip.
|
|
* @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
|
|
* @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
|
|
* this extent are checked.
|
|
* @return {T|undefined} Callback result.
|
|
* @template T
|
|
*/
|
|
ol.render.webgl.Replay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {};
|
|
|
|
|
|
/**
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
|
|
* to skip.
|
|
* @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
|
|
* @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
|
|
* @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
|
|
* this extent are checked.
|
|
* @return {T|undefined} Callback result.
|
|
* @template T
|
|
*/
|
|
ol.render.webgl.Replay.prototype.drawHitDetectionReplay = function(gl, context, skippedFeaturesHash,
|
|
featureCallback, oneByOne, opt_hitExtent) {
|
|
if (!oneByOne) {
|
|
// draw all hit-detection features in "once" (by texture group)
|
|
return this.drawHitDetectionReplayAll(gl, context,
|
|
skippedFeaturesHash, featureCallback);
|
|
} else {
|
|
// draw hit-detection features one by one
|
|
return this.drawHitDetectionReplayOneByOne(gl, context,
|
|
skippedFeaturesHash, featureCallback, opt_hitExtent);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features
|
|
* to skip.
|
|
* @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
|
|
* @return {T|undefined} Callback result.
|
|
* @template T
|
|
*/
|
|
ol.render.webgl.Replay.prototype.drawHitDetectionReplayAll = function(gl, context, skippedFeaturesHash,
|
|
featureCallback) {
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
this.drawReplay(gl, context, skippedFeaturesHash, true);
|
|
|
|
var result = featureCallback(null);
|
|
if (result) {
|
|
return result;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @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 {Object.<string, boolean>} skippedFeaturesHash Ids of features
|
|
* to skip.
|
|
* @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback.
|
|
* @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
|
|
* @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
|
|
* this extent are checked.
|
|
* @return {T|undefined} Callback result.
|
|
* @template T
|
|
*/
|
|
ol.render.webgl.Replay.prototype.replay = function(context,
|
|
center, resolution, rotation, size, pixelRatio,
|
|
opacity, skippedFeaturesHash,
|
|
featureCallback, oneByOne, opt_hitExtent) {
|
|
var gl = context.getGL();
|
|
var tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask,
|
|
tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail;
|
|
|
|
if (this.lineStringReplay) {
|
|
tmpStencil = gl.isEnabled(gl.STENCIL_TEST);
|
|
tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
|
|
tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK);
|
|
tmpStencilRef = gl.getParameter(gl.STENCIL_REF);
|
|
tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK);
|
|
tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL);
|
|
tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
|
|
tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
|
|
|
|
gl.enable(gl.STENCIL_TEST);
|
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
gl.stencilMask(255);
|
|
gl.stencilFunc(gl.ALWAYS, 1, 255);
|
|
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
|
|
|
|
this.lineStringReplay.replay(context,
|
|
center, resolution, rotation, size, pixelRatio,
|
|
opacity, skippedFeaturesHash,
|
|
featureCallback, oneByOne, opt_hitExtent);
|
|
|
|
gl.stencilMask(0);
|
|
gl.stencilFunc(gl.NOTEQUAL, 1, 255);
|
|
}
|
|
|
|
context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.verticesBuffer);
|
|
|
|
context.bindBuffer(ol.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
|
|
|
|
var locations = this.setUpProgram(gl, context, size, pixelRatio);
|
|
|
|
// set the "uniform" values
|
|
var projectionMatrix = ol.transform.reset(this.projectionMatrix_);
|
|
ol.transform.scale(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1]));
|
|
ol.transform.rotate(projectionMatrix, -rotation);
|
|
ol.transform.translate(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1]));
|
|
|
|
var offsetScaleMatrix = ol.transform.reset(this.offsetScaleMatrix_);
|
|
ol.transform.scale(offsetScaleMatrix, 2 / size[0], 2 / size[1]);
|
|
|
|
var offsetRotateMatrix = ol.transform.reset(this.offsetRotateMatrix_);
|
|
if (rotation !== 0) {
|
|
ol.transform.rotate(offsetRotateMatrix, -rotation);
|
|
}
|
|
|
|
gl.uniformMatrix4fv(locations.u_projectionMatrix, false,
|
|
ol.vec.Mat4.fromTransform(this.tmpMat4_, projectionMatrix));
|
|
gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false,
|
|
ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetScaleMatrix));
|
|
gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false,
|
|
ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetRotateMatrix));
|
|
gl.uniform1f(locations.u_opacity, opacity);
|
|
|
|
// draw!
|
|
var result;
|
|
if (featureCallback === undefined) {
|
|
this.drawReplay(gl, context, skippedFeaturesHash, false);
|
|
} else {
|
|
// draw feature by feature for the hit-detection
|
|
result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash,
|
|
featureCallback, oneByOne, opt_hitExtent);
|
|
}
|
|
|
|
// disable the vertex attrib arrays
|
|
this.shutDownProgram(gl, locations);
|
|
|
|
if (this.lineStringReplay) {
|
|
if (!tmpStencil) {
|
|
gl.disable(gl.STENCIL_TEST);
|
|
}
|
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
gl.stencilFunc(/** @type {number} */ (tmpStencilFunc),
|
|
/** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal));
|
|
gl.stencilMask(/** @type {number} */ (tmpStencilMask));
|
|
gl.stencilOp(/** @type {number} */ (tmpStencilOpFail),
|
|
/** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass));
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* @protected
|
|
* @param {WebGLRenderingContext} gl gl.
|
|
* @param {ol.webgl.Context} context Context.
|
|
* @param {number} start Start index.
|
|
* @param {number} end End index.
|
|
*/
|
|
ol.render.webgl.Replay.prototype.drawElements = function(
|
|
gl, context, start, end) {
|
|
var elementType = context.hasOESElementIndexUint ?
|
|
ol.webgl.UNSIGNED_INT : ol.webgl.UNSIGNED_SHORT;
|
|
var elementSize = context.hasOESElementIndexUint ? 4 : 2;
|
|
|
|
var numItems = end - start;
|
|
var offsetInBytes = start * elementSize;
|
|
gl.drawElements(ol.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
|
|
};
|
|
|
|
}
|