diff --git a/examples/icon-sprite-webgl.js b/examples/icon-sprite-webgl.js index 03886ffec4..fbddd00490 100644 --- a/examples/icon-sprite-webgl.js +++ b/examples/icon-sprite-webgl.js @@ -8,11 +8,28 @@ goog.require('ol.style.Icon'); goog.require('ol.style.Style'); -var iconInfo = [ - {size: [55, 55], offset: [0, 0], opacity: 1.0, scale: 1.0}, - {size: [55, 55], offset: [110, 86], opacity: 0.75, scale: 1.25}, - {size: [55, 86], offset: [55, 0], opacity: 0.5, scale: 1.5} -]; +var iconInfo = [{ + offset: [0, 0], + opacity: 1.0, + rotateWithView: true, + rotation: 0.0, + scale: 1.0, + size: [55, 55] +}, { + offset: [110, 86], + opacity: 0.75, + rotateWithView: false, + rotation: Math.PI / 2.0, + scale: 1.25, + size: [55, 55] +}, { + offset: [55, 0], + opacity: 0.5, + rotateWithView: true, + rotation: Math.PI / 3.0, + scale: 1.5, + size: [55, 86] +}]; var i; @@ -21,15 +38,17 @@ var icons = new Array(iconCount); for (i = 0; i < iconCount; ++i) { var info = iconInfo[i]; icons[i] = new ol.style.Icon({ - src: 'data/Butterfly.png', - size: info.size, offset: info.offset, opacity: info.opacity, - scale: info.scale + rotateWithView: info.rotateWithView, + rotation: info.rotation, + scale: info.scale, + size: info.size, + src: 'data/Butterfly.png' }); } -var featureCount = 10000; +var featureCount = 30000; var features = new Array(featureCount); var feature, geometry; var e = 25000000; @@ -52,8 +71,14 @@ var vector = new ol.layer.Vector({ source: vectorSource }); +// Use the "webgl" renderer by default. +var renderer = exampleNS.getRendererFromQueryString(); +if (!renderer) { + renderer = 'webgl'; +} + var map = new ol.Map({ - renderer: 'webgl', + renderer: renderer, layers: [vector], target: document.getElementById('map'), view: new ol.View({ diff --git a/src/ol/render/webgl/webglimage.glsl b/src/ol/render/webgl/webglimage.glsl index 5098ac2a3f..d5f5db9191 100644 --- a/src/ol/render/webgl/webglimage.glsl +++ b/src/ol/render/webgl/webglimage.glsl @@ -11,13 +11,19 @@ attribute vec2 a_position; attribute vec2 a_texCoord; attribute vec2 a_offsets; attribute float a_opacity; +attribute float a_rotateWithView; uniform mat4 u_projectionMatrix; -uniform mat2 u_sizeMatrix; +uniform mat4 u_offsetScaleMatrix; +uniform mat4 u_offsetRotateMatrix; void main(void) { - vec2 offsets = u_sizeMatrix * a_offsets; - gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + vec4(offsets, 0., 0.); + mat4 offsetMatrix = u_offsetScaleMatrix; + if (a_rotateWithView == 1.0) { + offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix; + } + vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.); + gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets; v_texCoord = a_texCoord; v_opacity = a_opacity; } diff --git a/src/ol/render/webgl/webglimageshader.js b/src/ol/render/webgl/webglimageshader.js index f50e358d84..69d59a09ce 100644 --- a/src/ol/render/webgl/webglimageshader.js +++ b/src/ol/render/webgl/webglimageshader.js @@ -28,7 +28,7 @@ ol.render.webgl.imagereplay.shader.Fragment.DEBUG_SOURCE = 'precision mediump fl * @const * @type {string} */ -ol.render.webgl.imagereplay.shader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform sampler2D i;void main(void){vec4 texColor=texture2D(i,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*b;}'; +ol.render.webgl.imagereplay.shader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform sampler2D k;void main(void){vec4 texColor=texture2D(k,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*b;}'; /** @@ -57,14 +57,14 @@ goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.Vertex); * @const * @type {string} */ -ol.render.webgl.imagereplay.shader.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;\n\nuniform mat4 u_projectionMatrix;\nuniform mat2 u_sizeMatrix;\n\nvoid main(void) {\n vec2 offsets = u_sizeMatrix * a_offsets;\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + vec4(offsets, 0., 0.);\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; +ol.render.webgl.imagereplay.shader.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.);\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; /** * @const * @type {string} */ -ol.render.webgl.imagereplay.shader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;uniform mat4 g;uniform mat2 h;void main(void){vec2 offsets=h*e;gl_Position=g*vec4(c,0.,1.)+vec4(offsets,0.,0.);a=d;b=f;}'; +ol.render.webgl.imagereplay.shader.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.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}'; /** @@ -89,19 +89,25 @@ ol.render.webgl.imagereplay.shader.Locations = function(gl, program) { * @type {WebGLUniformLocation} */ this.u_image = gl.getUniformLocation( - program, goog.DEBUG ? 'u_image' : 'i'); + program, goog.DEBUG ? 'u_image' : 'k'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i'); /** * @type {WebGLUniformLocation} */ this.u_projectionMatrix = gl.getUniformLocation( - program, goog.DEBUG ? 'u_projectionMatrix' : 'g'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_sizeMatrix = gl.getUniformLocation( - program, goog.DEBUG ? 'u_sizeMatrix' : 'h'); + program, goog.DEBUG ? 'u_projectionMatrix' : 'h'); /** * @type {number} @@ -121,6 +127,12 @@ ol.render.webgl.imagereplay.shader.Locations = function(gl, program) { this.a_position = gl.getAttribLocation( program, goog.DEBUG ? 'a_position' : 'c'); + /** + * @type {number} + */ + this.a_rotateWithView = gl.getAttribLocation( + program, goog.DEBUG ? 'a_rotateWithView' : 'g'); + /** * @type {number} */ diff --git a/src/ol/render/webgl/webglreplay.js b/src/ol/render/webgl/webglreplay.js index 8725e1cbe5..a00f40c27e 100644 --- a/src/ol/render/webgl/webglreplay.js +++ b/src/ol/render/webgl/webglreplay.js @@ -110,6 +110,18 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { */ this.opacity_ = undefined; + /** + * @type {!goog.vec.Mat4.Number} + * @private + */ + this.offsetRotateMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @type {!goog.vec.Mat4.Number} + * @private + */ + this.offsetScaleMatrix_ = goog.vec.Mat4.createNumberIdentity(); + /** * @type {number|undefined} * @private @@ -128,6 +140,18 @@ ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { */ this.projectionMatrix_ = goog.vec.Mat4.createNumberIdentity(); + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; + /** * @private * @type {number|undefined} @@ -216,6 +240,8 @@ ol.render.webgl.ImageReplay.prototype.drawCoordinates_ = goog.asserts.assert(goog.isDef(this.opacity_)); goog.asserts.assert(goog.isDef(this.originX_)); goog.asserts.assert(goog.isDef(this.originY_)); + goog.asserts.assert(goog.isDef(this.rotateWithView_)); + goog.asserts.assert(goog.isDef(this.rotation_)); goog.asserts.assert(goog.isDef(this.scale_)); goog.asserts.assert(goog.isDef(this.width_)); var anchorX = this.anchorX_; @@ -226,50 +252,74 @@ ol.render.webgl.ImageReplay.prototype.drawCoordinates_ = var opacity = this.opacity_; var originX = this.originX_; var originY = this.originY_; + var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0; + var rotation = this.rotation_; var scale = this.scale_; var width = this.width_; + var cos = Math.cos(rotation); + var sin = Math.sin(rotation); var numIndices = this.indices_.length; var numVertices = this.vertices_.length; - var i, x, y, n; + 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]; - n = numVertices / 7; + // 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). - // 4 vertices per coordinate, with 7 values per vertex + n = numVertices / 8; + offsetX = -scale * anchorX; + offsetY = -scale * (height - anchorY); this.vertices_[numVertices++] = x; this.vertices_[numVertices++] = y; - this.vertices_[numVertices++] = -2 * scale * anchorX; - this.vertices_[numVertices++] = -2 * scale * (height - anchorY); + 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; + offsetX = scale * (width - anchorX); + offsetY = -scale * (height - anchorY); this.vertices_[numVertices++] = x; this.vertices_[numVertices++] = y; - this.vertices_[numVertices++] = 2 * scale * (width - anchorX); - this.vertices_[numVertices++] = -2 * scale * (height - anchorY); + 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; + offsetX = scale * (width - anchorX); + offsetY = scale * anchorY; this.vertices_[numVertices++] = x; this.vertices_[numVertices++] = y; - this.vertices_[numVertices++] = 2 * scale * (width - anchorX); - this.vertices_[numVertices++] = 2 * scale * anchorY; + 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; + offsetX = -scale * anchorX; + offsetY = scale * anchorY; this.vertices_[numVertices++] = x; this.vertices_[numVertices++] = y; - this.vertices_[numVertices++] = -2 * scale * anchorX; - this.vertices_[numVertices++] = 2 * scale * anchorY; + 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; this.indices_[numIndices++] = n; this.indices_[numIndices++] = n + 1; @@ -393,9 +443,9 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) { gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, goog.webgl.CLAMP_TO_EDGE); gl.texParameteri(goog.webgl.TEXTURE_2D, - goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.NEAREST); + goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR); gl.texParameteri(goog.webgl.TEXTURE_2D, - goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.NEAREST); + goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR); gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, image); this.textures_[i] = texture; @@ -413,6 +463,8 @@ ol.render.webgl.ImageReplay.prototype.finish = function(context) { this.opacity_ = undefined; this.originX_ = undefined; this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; this.scale_ = undefined; this.vertices_ = null; this.width_ = undefined; @@ -462,29 +514,43 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context, -rotation, -(center[0] - this.origin_[0]), -(center[1] - this.origin_[1])); + var offsetScaleMatrix = this.offsetScaleMatrix_; + goog.vec.Mat4.makeScale(offsetScaleMatrix, 2 / size[0], 2 / size[1], 1); + + var offsetRotateMatrix = this.offsetRotateMatrix_; + goog.vec.Mat4.makeIdentity(offsetRotateMatrix); + if (rotation !== 0) { + goog.vec.Mat4.rotateZ(offsetRotateMatrix, -rotation); + } + var locations = this.locations_; gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_); gl.enableVertexAttribArray(locations.a_position); gl.vertexAttribPointer(locations.a_position, 2, goog.webgl.FLOAT, - false, 28, 0); + false, 32, 0); gl.enableVertexAttribArray(locations.a_offsets); gl.vertexAttribPointer(locations.a_offsets, 2, goog.webgl.FLOAT, - false, 28, 8); + false, 32, 8); gl.enableVertexAttribArray(locations.a_texCoord); gl.vertexAttribPointer(locations.a_texCoord, 2, goog.webgl.FLOAT, - false, 28, 16); + false, 32, 16); gl.enableVertexAttribArray(locations.a_opacity); gl.vertexAttribPointer(locations.a_opacity, 1, goog.webgl.FLOAT, - false, 28, 24); + false, 32, 24); + + gl.enableVertexAttribArray(locations.a_rotateWithView); + gl.vertexAttribPointer(locations.a_rotateWithView, 1, goog.webgl.FLOAT, + false, 32, 28); gl.uniformMatrix4fv(locations.u_projectionMatrix, false, projectionMatrix); - gl.uniformMatrix2fv(locations.u_sizeMatrix, false, - new Float32Array([1 / size[0], 0.0, 0.0, 1 / size[1]])); + gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, offsetScaleMatrix); + gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, + offsetRotateMatrix); gl.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); @@ -521,6 +587,10 @@ ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { goog.asserts.assert(goog.isDef(opacity)); var origin = imageStyle.getOrigin(); goog.asserts.assert(!goog.isNull(origin)); + var rotateWithView = imageStyle.getRotateWithView(); + goog.asserts.assert(goog.isDef(rotateWithView)); + var rotation = imageStyle.getRotation(); + goog.asserts.assert(goog.isDef(rotation)); var size = imageStyle.getSize(); goog.asserts.assert(!goog.isNull(size)); var scale = imageStyle.getScale(); @@ -545,6 +615,8 @@ ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { this.opacity_ = opacity; this.originX_ = origin[0]; this.originY_ = origin[1]; + this.rotation_ = rotation; + this.rotateWithView_ = rotateWithView; this.scale_ = scale; this.width_ = size[0]; };