diff --git a/src/ol/render/webgl/imagereplay/defaultshader.js b/src/ol/render/webgl/imagereplay/defaultshader.js deleted file mode 100644 index ee5fef2ea4..0000000000 --- a/src/ol/render/webgl/imagereplay/defaultshader.js +++ /dev/null @@ -1,154 +0,0 @@ -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.imagereplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.imagereplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.imagereplay.defaultshader.fragment = new ol.render.webgl.imagereplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.imagereplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.imagereplay.defaultshader.vertex = new ol.render.webgl.imagereplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_image = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_image' : 'l'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'k'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - - /** - * @type {number} - */ - this.a_offsets = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_offsets' : 'e'); - - /** - * @type {number} - */ - this.a_opacity = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_opacity' : 'f'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'c'); - - /** - * @type {number} - */ - this.a_rotateWithView = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); - - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'd'); - }; - -} diff --git a/src/ol/render/webgl/replay.js b/src/ol/render/webgl/replay.js index 2222ffd69c..a6d7ef24db 100644 --- a/src/ol/render/webgl/replay.js +++ b/src/ol/render/webgl/replay.js @@ -140,10 +140,9 @@ if (ol.ENABLE_WEBGL) { * @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| - ol.render.webgl.textreplay.defaultshader.Locations} Locations. + ol.render.webgl.texturereplay.defaultshader.Locations} Locations. */ ol.render.webgl.Replay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {}; @@ -153,10 +152,9 @@ if (ol.ENABLE_WEBGL) { * @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| - ol.render.webgl.textreplay.defaultshader.Locations} locations Locations. + ol.render.webgl.texturereplay.defaultshader.Locations} locations Locations. */ ol.render.webgl.Replay.prototype.shutDownProgram = function(gl, locations) {}; diff --git a/src/ol/render/webgl/textreplay/defaultshader.glsl b/src/ol/render/webgl/textreplay/defaultshader.glsl deleted file mode 100644 index 7f0c1a2138..0000000000 --- a/src/ol/render/webgl/textreplay/defaultshader.glsl +++ /dev/null @@ -1,41 +0,0 @@ -//! NAMESPACE=ol.render.webgl.textreplay.defaultshader -//! CLASS=ol.render.webgl.textreplay.defaultshader - - -//! COMMON -varying vec2 v_texCoord; - -//! VERTEX -attribute vec2 a_position; -attribute vec2 a_texCoord; -attribute vec2 a_offsets; -attribute float a_rotateWithView; - -uniform mat4 u_projectionMatrix; -uniform mat4 u_offsetScaleMatrix; -uniform mat4 u_offsetRotateMatrix; - -void main(void) { - mat4 offsetMatrix = u_offsetScaleMatrix; - if (a_rotateWithView == 1.0) { - offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix; - } - vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0); - gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets; - v_texCoord = a_texCoord; -} - - -//! FRAGMENT -uniform float u_opacity; -uniform sampler2D u_image; - -void main(void) { - vec4 texColor = texture2D(u_image, v_texCoord); - gl_FragColor.rgb = texColor.rgb; - float alpha = texColor.a * u_opacity; - if (alpha == 0.0) { - discard; - } - gl_FragColor.a = alpha; -} diff --git a/src/ol/render/webgl/textreplay/defaultshader.js b/src/ol/render/webgl/textreplay/defaultshader.js deleted file mode 100644 index 105ca7f257..0000000000 --- a/src/ol/render/webgl/textreplay/defaultshader.js +++ /dev/null @@ -1,148 +0,0 @@ -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.textreplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.textreplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.textreplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.textreplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform float i;uniform sampler2D j;void main(void){vec4 texColor=texture2D(j,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*i;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.textreplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.textreplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.textreplay.defaultshader.fragment = new ol.render.webgl.textreplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.textreplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.textreplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.textreplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;\n v_texCoord = a_texCoord;\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;attribute vec2 d;attribute float e;uniform mat4 f;uniform mat4 g;uniform mat4 h;void main(void){mat4 offsetMatrix=g;if(e==1.0){offsetMatrix=g*h;}vec4 offsets=offsetMatrix*vec4(d,0.0,0.0);gl_Position=f*vec4(b,0.0,1.0)+offsets;a=c;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.textreplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.textreplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.textreplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.textreplay.defaultshader.vertex = new ol.render.webgl.textreplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.textreplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_image = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_image' : 'j'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'h'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'g'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'i'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'f'); - - /** - * @type {number} - */ - this.a_offsets = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_offsets' : 'd'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'b'); - - /** - * @type {number} - */ - this.a_rotateWithView = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_rotateWithView' : 'e'); - - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'c'); - }; - -} diff --git a/src/ol/render/webgl/texturereplay.js b/src/ol/render/webgl/texturereplay.js new file mode 100644 index 0000000000..4c97e8b5c2 --- /dev/null +++ b/src/ol/render/webgl/texturereplay.js @@ -0,0 +1,497 @@ +goog.provide('ol.render.webgl.TextureReplay'); + +goog.require('ol'); +goog.require('ol.extent'); +goog.require('ol.obj'); +goog.require('ol.render.webgl.texturereplay.defaultshader'); +goog.require('ol.render.webgl.Replay'); +goog.require('ol.webgl'); +goog.require('ol.webgl.Context'); + +if (ol.ENABLE_WEBGL) { + + /** + * @constructor + * @abstract + * @extends {ol.render.webgl.Replay} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Max extent. + * @struct + */ + ol.render.webgl.TextureReplay = function(tolerance, maxExtent) { + ol.render.webgl.Replay.call(this, tolerance, maxExtent); + + /** + * @type {number|undefined} + * @protected + */ + this.anchorX = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.anchorY = undefined; + + /** + * @type {Array.} + * @protected + */ + this.groupIndices = []; + + /** + * @type {Array.} + * @protected + */ + this.hitDetectionGroupIndices = []; + + /** + * @type {number|undefined} + * @protected + */ + this.height = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.imageHeight = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.imageWidth = undefined; + + /** + * @protected + * @type {ol.render.webgl.texturereplay.defaultshader.Locations} + */ + this.defaultLocations = null; + + /** + * @protected + * @type {number|undefined} + */ + this.opacity = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.originX = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.originY = undefined; + + /** + * @protected + * @type {boolean|undefined} + */ + this.rotateWithView = undefined; + + /** + * @protected + * @type {number|undefined} + */ + this.rotation = undefined; + + /** + * @protected + * @type {number|undefined} + */ + this.scale = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.width = undefined; + }; + ol.inherits(ol.render.webgl.TextureReplay, ol.render.webgl.Replay); + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.getDeleteResourcesFunction = function(context) { + var verticesBuffer = this.verticesBuffer; + var indicesBuffer = this.indicesBuffer; + var textures = this.getTextures(true); + var gl = context.getGL(); + return function() { + if (!gl.isContextLost()) { + var i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.deleteTexture(textures[i]); + } + } + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + }; + }; + + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} My end. + * @protected + */ + ol.render.webgl.TextureReplay.prototype.drawCoordinates = function(flatCoordinates, offset, end, stride) { + var anchorX = /** @type {number} */ (this.anchorX); + var anchorY = /** @type {number} */ (this.anchorY); + var height = /** @type {number} */ (this.height); + var imageHeight = /** @type {number} */ (this.imageHeight); + var imageWidth = /** @type {number} */ (this.imageWidth); + var opacity = /** @type {number} */ (this.opacity); + var originX = /** @type {number} */ (this.originX); + var originY = /** @type {number} */ (this.originY); + var rotateWithView = this.rotateWithView ? 1.0 : 0.0; + // this.rotation_ is anti-clockwise, but rotation is clockwise + var rotation = /** @type {number} */ (-this.rotation); + var scale = /** @type {number} */ (this.scale); + var width = /** @type {number} */ (this.width); + var cos = Math.cos(rotation); + var sin = Math.sin(rotation); + var numIndices = this.indices.length; + var numVertices = this.vertices.length; + var i, n, offsetX, offsetY, x, y; + for (i = offset; i < end; i += stride) { + x = flatCoordinates[i] - this.origin[0]; + y = flatCoordinates[i + 1] - this.origin[1]; + + // There are 4 vertices per [x, y] point, one for each corner of the + // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if + // WebGL supported Geometry Shaders (which can emit new vertices), but that + // is not currently the case. + // + // And each vertex includes 8 values: the x and y coordinates, the x and + // y offsets used to calculate the position of the corner, the u and + // v texture coordinates for the corner, the opacity, and whether the + // the image should be rotated with the view (rotateWithView). + + n = numVertices / 8; + + // bottom-left corner + offsetX = -scale * anchorX; + offsetY = -scale * (height - anchorY); + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = originX / imageWidth; + this.vertices[numVertices++] = (originY + height) / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + // bottom-right corner + offsetX = scale * (width - anchorX); + offsetY = -scale * (height - anchorY); + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = (originX + width) / imageWidth; + this.vertices[numVertices++] = (originY + height) / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + // top-right corner + offsetX = scale * (width - anchorX); + offsetY = scale * anchorY; + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = (originX + width) / imageWidth; + this.vertices[numVertices++] = originY / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + // top-left corner + offsetX = -scale * anchorX; + offsetY = scale * anchorY; + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = originX / imageWidth; + this.vertices[numVertices++] = originY / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n + 3; + } + + return numVertices; + }; + + + /** + * @protected + * @param {Array.} textures Textures. + * @param {Array.} images + * Images. + * @param {Object.} texturePerImage Texture cache. + * @param {WebGLRenderingContext} gl Gl. + */ + ol.render.webgl.TextureReplay.prototype.createTextures = function(textures, images, texturePerImage, gl) { + var texture, image, uid, i; + var ii = images.length; + for (i = 0; i < ii; ++i) { + image = images[i]; + + uid = ol.getUid(image).toString(); + if (uid in texturePerImage) { + texture = texturePerImage[uid]; + } else { + texture = ol.webgl.Context.createTexture( + gl, image, ol.webgl.CLAMP_TO_EDGE, ol.webgl.CLAMP_TO_EDGE); + texturePerImage[uid] = texture; + } + textures[i] = texture; + } + }; + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { + // get the program + var fragmentShader = ol.render.webgl.texturereplay.defaultshader.fragment; + var vertexShader = ol.render.webgl.texturereplay.defaultshader.vertex; + var program = context.getProgram(fragmentShader, vertexShader); + + // get the locations + var locations; + if (!this.defaultLocations) { + // eslint-disable-next-line openlayers-internal/no-missing-requires + locations = new ol.render.webgl.texturereplay.defaultshader.Locations(gl, program); + this.defaultLocations = locations; + } else { + locations = this.defaultLocations; + } + + // use the program (FIXME: use the return value) + context.useProgram(program); + + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT, + false, 32, 0); + + gl.enableVertexAttribArray(locations.a_offsets); + gl.vertexAttribPointer(locations.a_offsets, 2, ol.webgl.FLOAT, + false, 32, 8); + + gl.enableVertexAttribArray(locations.a_texCoord); + gl.vertexAttribPointer(locations.a_texCoord, 2, ol.webgl.FLOAT, + false, 32, 16); + + gl.enableVertexAttribArray(locations.a_opacity); + gl.vertexAttribPointer(locations.a_opacity, 1, ol.webgl.FLOAT, + false, 32, 24); + + gl.enableVertexAttribArray(locations.a_rotateWithView); + gl.vertexAttribPointer(locations.a_rotateWithView, 1, ol.webgl.FLOAT, + false, 32, 28); + + return locations; + }; + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.shutDownProgram = function(gl, locations) { + gl.disableVertexAttribArray(locations.a_position); + gl.disableVertexAttribArray(locations.a_offsets); + gl.disableVertexAttribArray(locations.a_texCoord); + gl.disableVertexAttribArray(locations.a_opacity); + gl.disableVertexAttribArray(locations.a_rotateWithView); + }; + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { + var textures = hitDetection ? this.getHitDetectionTextures() : this.getTextures(); + var groupIndices = hitDetection ? this.hitDetectionGroupIndices : this.groupIndices; + + if (!ol.obj.isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping( + gl, context, skippedFeaturesHash, textures, groupIndices); + } else { + var i, ii, start; + for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { + gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]); + var end = groupIndices[i]; + this.drawElements(gl, context, start, end); + start = end; + } + } + }; + + + /** + * Draw the replay while paying attention to skipped features. + * + * This functions creates groups of features that can be drawn to together, + * so that the number of `drawElements` calls is minimized. + * + * For example given the following texture groups: + * + * Group 1: A B C + * Group 2: D [E] F G + * + * If feature E should be skipped, the following `drawElements` calls will be + * made: + * + * drawElements with feature A, B and C + * drawElements with feature D + * drawElements with feature F and G + * + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {ol.webgl.Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.} textures Textures. + * @param {Array.} groupIndices Texture group indices. + */ + ol.render.webgl.TextureReplay.prototype.drawReplaySkipping = function(gl, context, skippedFeaturesHash, textures, + groupIndices) { + var featureIndex = 0; + + var i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]); + var groupStart = (i > 0) ? groupIndices[i - 1] : 0; + var groupEnd = groupIndices[i]; + + var start = groupStart; + var end = groupStart; + while (featureIndex < this.startIndices.length && + this.startIndices[featureIndex] <= groupEnd) { + var feature = this.startIndicesFeature[featureIndex]; + + var featureUid = ol.getUid(feature).toString(); + if (skippedFeaturesHash[featureUid] !== undefined) { + // feature should be skipped + if (start !== end) { + // draw the features so far + this.drawElements(gl, context, start, end); + } + // continue with the next feature + start = (featureIndex === this.startIndices.length - 1) ? + groupEnd : this.startIndices[featureIndex + 1]; + end = start; + } else { + // the feature is not skipped, augment the end index + end = (featureIndex === this.startIndices.length - 1) ? + groupEnd : this.startIndices[featureIndex + 1]; + } + featureIndex++; + } + + if (start !== end) { + // draw the remaining features (in case there was no skipped feature + // in this texture group, all features of a group are drawn together) + this.drawElements(gl, context, start, end); + } + } + }; + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, + featureCallback, opt_hitExtent) { + var i, groupStart, start, end, feature, featureUid; + var featureIndex = this.startIndices.length - 1; + var hitDetectionTextures = this.getHitDetectionTextures(); + for (i = hitDetectionTextures.length - 1; i >= 0; --i) { + gl.bindTexture(ol.webgl.TEXTURE_2D, hitDetectionTextures[i]); + groupStart = (i > 0) ? this.hitDetectionGroupIndices[i - 1] : 0; + end = this.hitDetectionGroupIndices[i]; + + // draw all features for this texture group + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + start = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = ol.getUid(feature).toString(); + + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || ol.extent.intersects( + /** @type {Array} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawElements(gl, context, start, end); + + var result = featureCallback(feature); + if (result) { + return result; + } + } + + end = start; + featureIndex--; + } + } + return undefined; + }; + + + /** + * @inheritDoc + */ + ol.render.webgl.TextureReplay.prototype.finish = function(context) { + this.anchorX = undefined; + this.anchorY = undefined; + this.height = undefined; + 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; + }; + + + /** + * @abstract + * @protected + * @param {boolean=} opt_all Return hit detection textures with regular ones. + * @returns {Array.} Textures. + */ + ol.render.webgl.TextureReplay.prototype.getTextures = function(opt_all) {}; + + + /** + * @abstract + * @protected + * @returns {Array.} Textures. + */ + ol.render.webgl.TextureReplay.prototype.getHitDetectionTextures = function() {}; +} diff --git a/src/ol/render/webgl/imagereplay/defaultshader.glsl b/src/ol/render/webgl/texturereplay/defaultshader.glsl similarity index 89% rename from src/ol/render/webgl/imagereplay/defaultshader.glsl rename to src/ol/render/webgl/texturereplay/defaultshader.glsl index b1b6168fff..3bf3e86da5 100644 --- a/src/ol/render/webgl/imagereplay/defaultshader.glsl +++ b/src/ol/render/webgl/texturereplay/defaultshader.glsl @@ -1,5 +1,5 @@ -//! NAMESPACE=ol.render.webgl.imagereplay.defaultshader -//! CLASS=ol.render.webgl.imagereplay.defaultshader +//! NAMESPACE=ol.render.webgl.texturereplay.defaultshader +//! CLASS=ol.render.webgl.texturereplay.defaultshader //! COMMON diff --git a/src/ol/render/webgl/texturereplay/defaultshader.js b/src/ol/render/webgl/texturereplay/defaultshader.js new file mode 100644 index 0000000000..016a8c2979 --- /dev/null +++ b/src/ol/render/webgl/texturereplay/defaultshader.js @@ -0,0 +1,154 @@ +// This file is automatically generated, do not edit +/* eslint openlayers-internal/no-missing-requires: 0 */ +goog.provide('ol.render.webgl.texturereplay.defaultshader'); + +goog.require('ol'); +goog.require('ol.webgl.Fragment'); +goog.require('ol.webgl.Vertex'); + +if (ol.ENABLE_WEBGL) { + + /** + * @constructor + * @extends {ol.webgl.Fragment} + * @struct + */ + ol.render.webgl.texturereplay.defaultshader.Fragment = function() { + ol.webgl.Fragment.call(this, ol.render.webgl.texturereplay.defaultshader.Fragment.SOURCE); + }; + ol.inherits(ol.render.webgl.texturereplay.defaultshader.Fragment, ol.webgl.Fragment); + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? + ol.render.webgl.texturereplay.defaultshader.Fragment.DEBUG_SOURCE : + ol.render.webgl.texturereplay.defaultshader.Fragment.OPTIMIZED_SOURCE; + + + ol.render.webgl.texturereplay.defaultshader.fragment = new ol.render.webgl.texturereplay.defaultshader.Fragment(); + + + /** + * @constructor + * @extends {ol.webgl.Vertex} + * @struct + */ + ol.render.webgl.texturereplay.defaultshader.Vertex = function() { + ol.webgl.Vertex.call(this, ol.render.webgl.texturereplay.defaultshader.Vertex.SOURCE); + }; + ol.inherits(ol.render.webgl.texturereplay.defaultshader.Vertex, ol.webgl.Vertex); + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}'; + + + /** + * @const + * @type {string} + */ + ol.render.webgl.texturereplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? + ol.render.webgl.texturereplay.defaultshader.Vertex.DEBUG_SOURCE : + ol.render.webgl.texturereplay.defaultshader.Vertex.OPTIMIZED_SOURCE; + + + ol.render.webgl.texturereplay.defaultshader.vertex = new ol.render.webgl.texturereplay.defaultshader.Vertex(); + + + /** + * @constructor + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + * @struct + */ + ol.render.webgl.texturereplay.defaultshader.Locations = function(gl, program) { + + /** + * @type {WebGLUniformLocation} + */ + this.u_image = gl.getUniformLocation( + program, ol.DEBUG_WEBGL ? 'u_image' : 'l'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, ol.DEBUG_WEBGL ? 'u_opacity' : 'k'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); + + /** + * @type {number} + */ + this.a_offsets = gl.getAttribLocation( + program, ol.DEBUG_WEBGL ? 'a_offsets' : 'e'); + + /** + * @type {number} + */ + this.a_opacity = gl.getAttribLocation( + program, ol.DEBUG_WEBGL ? 'a_opacity' : 'f'); + + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, ol.DEBUG_WEBGL ? 'a_position' : 'c'); + + /** + * @type {number} + */ + this.a_rotateWithView = gl.getAttribLocation( + program, ol.DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'd'); + }; + +} diff --git a/test/spec/ol/render/webgl/texturereplay.test.js b/test/spec/ol/render/webgl/texturereplay.test.js new file mode 100644 index 0000000000..5e85440a6b --- /dev/null +++ b/test/spec/ol/render/webgl/texturereplay.test.js @@ -0,0 +1,89 @@ +goog.provide('ol.test.render.webgl.TextureReplay'); + +goog.require('ol.render.webgl.TextureReplay'); +goog.require('ol.render.webgl.texturereplay.defaultshader'); + +describe('ol.render.webgl.TextureReplay', function() { + var replay; + + beforeEach(function() { + var tolerance = 0.1; + var maxExtent = [-10000, -20000, 10000, 20000]; + replay = new ol.render.webgl.TextureReplay(tolerance, maxExtent); + }); + + describe('#setUpProgram', function() { + var context, gl; + beforeEach(function() { + context = { + getProgram: function() {}, + useProgram: function() {} + }; + gl = { + enableVertexAttribArray: function() {}, + vertexAttribPointer: function() {}, + uniform1f: function() {}, + uniform2fv: function() {}, + getUniformLocation: function() {}, + getAttribLocation: function() {} + }; + }); + + it('returns the locations used by the shaders', function() { + var locations = replay.setUpProgram(gl, context, [2, 2], 1); + expect(locations).to.be.a( + ol.render.webgl.texturereplay.defaultshader.Locations); + }); + + it('gets and compiles the shaders', function() { + sinon.spy(context, 'getProgram'); + sinon.spy(context, 'useProgram'); + + replay.setUpProgram(gl, context, [2, 2], 1); + expect(context.getProgram.calledWithExactly( + ol.render.webgl.texturereplay.defaultshader.fragment, + ol.render.webgl.texturereplay.defaultshader.vertex)).to.be(true); + expect(context.useProgram.calledOnce).to.be(true); + }); + + it('initializes the attrib pointers', function() { + sinon.spy(gl, 'getAttribLocation'); + sinon.spy(gl, 'vertexAttribPointer'); + sinon.spy(gl, 'enableVertexAttribArray'); + + replay.setUpProgram(gl, context, [2, 2], 1); + expect(gl.vertexAttribPointer.callCount).to.be(gl.getAttribLocation.callCount); + expect(gl.enableVertexAttribArray.callCount).to.be( + gl.getAttribLocation.callCount); + }); + }); + + describe('#shutDownProgram', function() { + var context, gl; + beforeEach(function() { + context = { + getProgram: function() {}, + useProgram: function() {} + }; + gl = { + enableVertexAttribArray: function() {}, + disableVertexAttribArray: function() {}, + vertexAttribPointer: function() {}, + uniform1f: function() {}, + uniform2fv: function() {}, + getUniformLocation: function() {}, + getAttribLocation: function() {} + }; + }); + + it('disables the attrib pointers', function() { + sinon.spy(gl, 'getAttribLocation'); + sinon.spy(gl, 'disableVertexAttribArray'); + + var locations = replay.setUpProgram(gl, context, [2, 2], 1); + replay.shutDownProgram(gl, locations); + expect(gl.disableVertexAttribArray.callCount).to.be( + gl.getAttribLocation.callCount); + }); + }); +});