diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index a87932fffe..f3a4b3afe8 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -9,6 +9,15 @@ import {listen, unlistenAll} from '../events.js'; import {clear} from '../obj.js'; import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TEXTURE_2D, TEXTURE_WRAP_S, TEXTURE_WRAP_T} from '../webgl.js'; import ContextEventType from '../webgl/ContextEventType.js'; +import {TRIANGLES, UNSIGNED_INT, UNSIGNED_SHORT} from "../webgl"; +import { + create as createTransform, + reset as resetTransform, + rotate as rotateTransform, + scale as scaleTransform, + translate as translateTransform +} from "../transform"; +import {create, fromTransform} from "../vec/mat4"; /** @@ -17,6 +26,13 @@ import ContextEventType from '../webgl/ContextEventType.js'; * @property {WebGLBuffer} buffer */ +export const DefaultUniform = { + PROJECTION_MATRIX: 'u_projectionMatrix', + SCALE_MATRIX: 'u_offsetScaleMatrix', + OFFSET_ROTATION_MATRIX: 'u_offsetRotateMatrix', + OPACITY: 'u_opacity' +}; + /** * @classdesc @@ -26,9 +42,8 @@ class WebGLContext extends Disposable { /** * @param {HTMLCanvasElement} canvas Canvas. - * @param {WebGLRenderingContext} gl GL. */ - constructor(canvas, gl) { + constructor(canvas) { super(); /** @@ -41,7 +56,7 @@ class WebGLContext extends Disposable { * @private * @type {WebGLRenderingContext} */ - this.gl_ = gl; + this.gl_ = canvas.getContext('webgl'); /** * @private @@ -67,24 +82,6 @@ class WebGLContext extends Disposable { */ this.currentProgram_ = null; - /** - * @private - * @type {WebGLFramebuffer} - */ - this.hitDetectionFramebuffer_ = null; - - /** - * @private - * @type {WebGLTexture} - */ - this.hitDetectionTexture_ = null; - - /** - * @private - * @type {WebGLRenderbuffer} - */ - this.hitDetectionRenderbuffer_ = null; - /** * @type {boolean} */ @@ -92,7 +89,7 @@ class WebGLContext extends Disposable { // use the OES_element_index_uint extension if available if (this.hasOESElementIndexUint) { - gl.getExtension('OES_element_index_uint'); + this.gl_.getExtension('OES_element_index_uint'); } listen(this.canvas_, ContextEventType.LOST, @@ -100,12 +97,34 @@ class WebGLContext extends Disposable { listen(this.canvas_, ContextEventType.RESTORED, this.handleWebGLContextRestored, this); + /** + * @private + * @type {import("../transform.js").Transform} + */ + this.projectionMatrix_ = createTransform(); + + /** + * @private + * @type {import("../transform.js").Transform} + */ + this.offsetRotateMatrix_ = createTransform(); + + /** + * @private + * @type {import("../transform.js").Transform} + */ + this.offsetScaleMatrix_ = createTransform(); + + this.tmpMat4_ = create(); + + this.locations_ = {}; } /** * Just bind the buffer if it's in the cache. Otherwise create * the WebGL buffer, bind it, populate it, and add an entry to * the cache. + * TODO: improve this, the logic is unclear: we want A/ to bind a buffer and B/ to flush data in it * @param {number} target Target. * @param {import("./Buffer.js").default} buf Buffer. */ @@ -163,13 +182,24 @@ class WebGLContext extends Disposable { for (const key in this.shaderCache_) { gl.deleteShader(this.shaderCache_[key]); } - // delete objects for hit-detection - gl.deleteFramebuffer(this.hitDetectionFramebuffer_); - gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_); - gl.deleteTexture(this.hitDetectionTexture_); } } + /** + * @protected + * @param {number} start Start index. + * @param {number} end End index. + */ + drawElements(start, end) { + const elementType = this.hasOESElementIndexUint ? + UNSIGNED_INT : UNSIGNED_SHORT; + const elementSize = this.hasOESElementIndexUint ? 4 : 2; + + const numItems = end - start; + const offsetInBytes = start * elementSize; + this.getGL().drawElements(TRIANGLES, numItems, elementType, offsetInBytes); + } + /** * @return {HTMLCanvasElement} Canvas. */ @@ -186,17 +216,6 @@ class WebGLContext extends Disposable { return this.gl_; } - /** - * Get the frame buffer for hit detection. - * @return {WebGLFramebuffer} The hit detection frame buffer. - */ - getHitDetectionFramebuffer() { - if (!this.hitDetectionFramebuffer_) { - this.initHitDetectionFramebuffer_(); - } - return this.hitDetectionFramebuffer_; - } - /** * Get shader from the cache if it's in the cache. Otherwise, create * the WebGL shader, compile it, and add entry to cache. @@ -240,6 +259,48 @@ class WebGLContext extends Disposable { } } + + /** + * Sets the matrices uniforms for a given frame state + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. + */ + applyFrameState(frameState) { + const size = frameState.size; + const rotation = frameState.viewState.rotation; + const resolution = frameState.viewState.resolution; + const center = frameState.viewState.center; + + // set the "uniform" values (coordinates 0,0 are the center of the view + const projectionMatrix = resetTransform(this.projectionMatrix_); + scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(projectionMatrix, -rotation); + translateTransform(projectionMatrix, -center[0], -center[1]); + + const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); + scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); + + const offsetRotateMatrix = resetTransform(this.offsetRotateMatrix_); + if (rotation !== 0) { + rotateTransform(offsetRotateMatrix, -rotation); + } + + this.getGL().uniformMatrix4fv(locations.u_projectionMatrix, false, + fromTransform(this.tmpMat4_, projectionMatrix)); + this.getGL().uniformMatrix4fv(locations.u_offsetScaleMatrix, false, + fromTransform(this.tmpMat4_, offsetScaleMatrix)); + this.getGL().uniformMatrix4fv(locations.u_offsetRotateMatrix, false, + fromTransform(this.tmpMat4_, offsetRotateMatrix)); + } + + /** + * Give a value for a standard float uniform + * @param {string} uniform Uniform name + * @param {number} value Value + */ + setUniformFloatValue(uniform, value) { + this.getGL().uniformMatrix4fv(this.locations_[uniform], false, value); + } + /** * FIXME empty description for jsdoc */ @@ -248,9 +309,6 @@ class WebGLContext extends Disposable { clear(this.shaderCache_); clear(this.programCache_); this.currentProgram_ = null; - this.hitDetectionFramebuffer_ = null; - this.hitDetectionTexture_ = null; - this.hitDetectionRenderbuffer_ = null; } /** @@ -259,33 +317,6 @@ class WebGLContext extends Disposable { handleWebGLContextRestored() { } - /** - * Creates a 1x1 pixel framebuffer for the hit-detection. - * @private - */ - initHitDetectionFramebuffer_() { - const gl = this.gl_; - const framebuffer = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - - const texture = createEmptyTexture(gl, 1, 1); - const renderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); - gl.framebufferTexture2D( - gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, - gl.RENDERBUFFER, renderbuffer); - - gl.bindTexture(gl.TEXTURE_2D, null); - gl.bindRenderbuffer(gl.RENDERBUFFER, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - this.hitDetectionFramebuffer_ = framebuffer; - this.hitDetectionTexture_ = texture; - this.hitDetectionRenderbuffer_ = renderbuffer; - } - /** * Use a program. If the program is already in use, this will return `false`. * @param {WebGLProgram} program Program.