From f3cace499c82b790efcc37d3dd67935c69f4682c Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 10 Jan 2013 23:19:49 +0100 Subject: [PATCH] Implement frame state and animation architecture --- src/ol/animation.js | 101 ++++++++++++++++ src/ol/easing.js | 14 +++ src/ol/framestate.js | 28 +++++ src/ol/iview2d.js | 21 ++++ src/ol/layer/layer.js | 36 ++++++ src/ol/map.js | 114 +++++++++++++++++- src/ol/renderer/dom/domlayerrenderer.js | 5 +- src/ol/renderer/dom/dommaprenderer.js | 29 ++--- src/ol/renderer/dom/domtilelayerrenderer.js | 58 ++++----- src/ol/renderer/maprenderer.js | 5 +- src/ol/renderer/webgl/webgllayerrenderer.js | 8 +- src/ol/renderer/webgl/webglmaprenderer.js | 76 ++++-------- .../renderer/webgl/webgltilelayerrenderer.js | 37 ++---- src/ol/view2d.js | 45 +++---- 14 files changed, 417 insertions(+), 160 deletions(-) create mode 100644 src/ol/animation.js create mode 100644 src/ol/easing.js create mode 100644 src/ol/framestate.js diff --git a/src/ol/animation.js b/src/ol/animation.js new file mode 100644 index 0000000000..3c40d2ddc6 --- /dev/null +++ b/src/ol/animation.js @@ -0,0 +1,101 @@ +goog.provide('ol.animation'); + +goog.require('goog.fx.easing'); +goog.require('ol.PreRenderFunction'); +goog.require('ol.View2D'); +goog.require('ol.easing'); + + +/** + * @param {number} resolution Resolution. + * @param {number=} opt_duration Duration. + * @param {number=} opt_start Start. + * @param {function(number): number=} opt_easingFunction Easing function. + * @return {ol.PreRenderFunction} Pre-render function. + */ +ol.animation.createBounce = + function(resolution, opt_duration, opt_start, opt_easingFunction) { + var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var duration = goog.isDef(opt_duration) ? opt_duration : 1000; + var easingFunction = goog.isDef(opt_easingFunction) ? + opt_easingFunction : ol.easing.upAndDown; + return function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + return true; + } else if (frameState.time < start + duration) { + var delta = easingFunction((frameState.time - start) / duration); + var deltaResolution = resolution - frameState.view2DState.resolution; + frameState.animate = true; + frameState.view2DState.resolution += delta * deltaResolution; + return true; + } else { + return false; + } + }; +}; + + +/** + * @param {ol.Coordinate} source Source. + * @param {number=} opt_duration Duration. + * @param {number=} opt_start Start. + * @param {function(number): number=} opt_easingFunction Easing function. + * @return {ol.PreRenderFunction} Pre-render function. + */ +ol.animation.createPanFrom = + function(source, opt_duration, opt_start, opt_easingFunction) { + var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var sourceX = source.x; + var sourceY = source.y; + var duration = goog.isDef(opt_duration) ? opt_duration : 1000; + var easingFunction = goog.isDef(opt_easingFunction) ? + opt_easingFunction : goog.fx.easing.inAndOut; + return function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + return true; + } else if (frameState.time < start + duration) { + var delta = 1 - easingFunction((frameState.time - start) / duration); + var deltaX = sourceX - frameState.view2DState.center.x; + var deltaY = sourceY - frameState.view2DState.center.y; + frameState.animate = true; + frameState.view2DState.center.x += delta * deltaX; + frameState.view2DState.center.y += delta * deltaY; + return true; + } else { + return false; + } + }; +}; + + +/** + * @param {number=} opt_duration Duration. + * @param {number=} opt_turns Turns. + * @param {number=} opt_start Start. + * @param {function(number): number=} opt_easingFunction Easing function. + * @return {ol.PreRenderFunction} Pre-render function. + */ +ol.animation.createSpin = + function(opt_duration, opt_turns, opt_start, opt_easingFunction) { + var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var duration = goog.isDef(opt_duration) ? opt_duration : 1000; + var turns = goog.isDef(opt_turns) ? opt_turns : 1; + var deltaTheta = 2 * turns * Math.PI; + var easingFunction = goog.isDef(opt_easingFunction) ? + opt_easingFunction : goog.fx.easing.inAndOut; + return function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + return true; + } else if (frameState.time < start + duration) { + var delta = easingFunction((frameState.time - start) / duration); + frameState.animate = true; + frameState.view2DState.rotation += delta * deltaTheta; + return true; + } else { + return false; + } + }; +}; diff --git a/src/ol/easing.js b/src/ol/easing.js new file mode 100644 index 0000000000..74bd1c5210 --- /dev/null +++ b/src/ol/easing.js @@ -0,0 +1,14 @@ +goog.provide('ol.easing'); + + +/** + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +ol.easing.upAndDown = function(t) { + if (t < 0.5) { + return goog.fx.easing.inAndOut(2 * t); + } else { + return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5)); + } +}; diff --git a/src/ol/framestate.js b/src/ol/framestate.js new file mode 100644 index 0000000000..51a7dfaacb --- /dev/null +++ b/src/ol/framestate.js @@ -0,0 +1,28 @@ +goog.provide('ol.FrameState'); +goog.provide('ol.PreRenderFunction'); + +goog.require('ol.Color'); +goog.require('ol.Coordinate'); +goog.require('ol.Extent'); +goog.require('ol.Size'); +goog.require('ol.View2DState'); +goog.require('ol.layer.LayerState'); + + +/** + * @typedef {{animate: boolean, + * backgroundColor: ol.Color, + * extent: (null|ol.Extent), + * layersArray: Array., + * layerStates: Object., + * size: ol.Size, + * time: number, + * view2DState: ol.View2DState}} + */ +ol.FrameState; + + +/** + * @typedef {function(ol.Map, ?ol.FrameState): boolean} + */ +ol.PreRenderFunction; diff --git a/src/ol/iview2d.js b/src/ol/iview2d.js index d8840a8cba..e8dcb0c4db 100644 --- a/src/ol/iview2d.js +++ b/src/ol/iview2d.js @@ -1,4 +1,18 @@ goog.provide('ol.IView2D'); +goog.provide('ol.View2DState'); + +goog.require('ol.Coordinate'); +goog.require('ol.Extent'); +goog.require('ol.Projection'); + + +/** + * @typedef {{center: ol.Coordinate, + * projection: ol.Projection, + * resolution: number, + * rotation: number}} + */ +ol.View2DState; @@ -36,3 +50,10 @@ ol.IView2D.prototype.getResolution = function() { */ ol.IView2D.prototype.getRotation = function() { }; + + +/** + * @return {ol.View2DState} View2D state. + */ +ol.IView2D.prototype.getView2DState = function() { +}; diff --git a/src/ol/layer/layer.js b/src/ol/layer/layer.js index 89aead6fa9..cb06bed1ba 100644 --- a/src/ol/layer/layer.js +++ b/src/ol/layer/layer.js @@ -1,5 +1,6 @@ goog.provide('ol.layer.Layer'); goog.provide('ol.layer.LayerProperty'); +goog.provide('ol.layer.LayerState'); goog.require('goog.events'); goog.require('goog.events.EventType'); @@ -21,6 +22,18 @@ ol.layer.LayerProperty = { }; +/** + * @typedef {{brightness: number, + * contrast: number, + * hue: number, + * opacity: number, + * ready: boolean, + * saturation: number, + * visible: boolean}} + */ +ol.layer.LayerState; + + /** * @constructor @@ -103,6 +116,29 @@ goog.exportProperty( ol.layer.Layer.prototype.getHue); +/** + * @return {ol.layer.LayerState} Layer state. + */ +ol.layer.Layer.prototype.getLayerState = function() { + var brightness = this.getBrightness(); + var contrast = this.getContrast(); + var hue = this.getHue(); + var opacity = this.getOpacity(); + var ready = this.isReady(); + var saturation = this.getSaturation(); + var visible = this.getVisible(); + return { + brightness: goog.isDef(brightness) ? brightness : 0, + contrast: goog.isDef(contrast) ? contrast : 1, + hue: goog.isDef(hue) ? hue : 0, + opacity: goog.isDef(opacity) ? opacity : 1, + ready: ready, + saturation: goog.isDef(saturation) ? saturation : 1, + visible: goog.isDef(visible) ? visible : true + }; +}; + + /** * @return {number} Opacity. */ diff --git a/src/ol/map.js b/src/ol/map.js index 8cf12961f6..8485f2a221 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -29,6 +29,7 @@ goog.require('ol.Collection'); goog.require('ol.Color'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); +goog.require('ol.FrameState'); goog.require('ol.MapBrowserEvent'); goog.require('ol.Object'); goog.require('ol.Pixel'); @@ -38,6 +39,7 @@ goog.require('ol.Size'); goog.require('ol.TransformFunction'); goog.require('ol.View'); goog.require('ol.View2D'); +goog.require('ol.View2DState'); goog.require('ol.control.Attribution'); goog.require('ol.control.Zoom'); goog.require('ol.interaction.DblClickZoom'); @@ -134,6 +136,12 @@ ol.Map = function(mapOptions) { new goog.async.AnimationDelay(this.renderFrame_, undefined, this); this.registerDisposable(this.animationDelay_); + /** + * @private + * @type {?ol.FrameState} + */ + this.frameState_ = null; + /** * @private * @type {number} @@ -231,6 +239,16 @@ ol.Map = function(mapOptions) { goog.events.listen(this.viewportSizeMonitor_, goog.events.EventType.RESIZE, this.handleBrowserWindowResize, false, this); + /** + * @private + * @type {Array.} + */ + this.preRenderFunctions_ = []; + + this.dispatchPostRenderEvent_ = goog.bind(function() { + this.dispatchEvent(ol.MapEventType.POSTRENDER); + }, this); + this.setValues(mapOptionsInternal.values); this.handleBrowserWindowResize(); @@ -247,6 +265,26 @@ ol.Map = function(mapOptions) { goog.inherits(ol.Map, ol.Object); +/** + * @param {ol.PreRenderFunction} preRenderFunction Pre-render function. + */ +ol.Map.prototype.addPreRenderFunction = function(preRenderFunction) { + this.requestRenderFrame(); + this.preRenderFunctions_.push(preRenderFunction); +}; + + +/** + * @param {Array.} preRenderFunctions + * Pre-render functions. + */ +ol.Map.prototype.addPreRenderFunctions = function(preRenderFunctions) { + this.requestRenderFrame(); + Array.prototype.push.apply( + this.preRenderFunctions_, preRenderFunctions); +}; + + /** * @return {boolean} Can rotate. */ @@ -484,18 +522,90 @@ ol.Map.prototype.requestRenderFrame = function() { * @private */ ol.Map.prototype.renderFrame_ = function(time) { + + var i; + if (this.freezeRenderingCount_ != 0) { return; } + if (goog.DEBUG) { this.logger.info('renderFrame_'); } - this.renderer_.renderFrame(time); + + var size = this.getSize(); + var layers = this.getLayers(); + var layersArray = goog.isDef(layers) ? + /** @type {Array.} */ (layers.getArray()) : undefined; + var view = this.getView(); + var view2D = goog.isDef(view) ? this.getView().getView2D() : undefined; + /** @type {?ol.FrameState} */ + var frameState = null; + if (goog.isDef(layersArray) && goog.isDef(size) && goog.isDef(view2D) && + view2D.isDef()) { + var backgroundColor = this.getBackgroundColor(); + var layerStates = {}; + goog.array.forEach(layersArray, function(layer) { + layerStates[goog.getUid(layer)] = layer.getLayerState(); + }); + var view2DState = view2D.getView2DState(); + frameState = { + animate: false, + backgroundColor: goog.isDef(backgroundColor) ? + backgroundColor : new ol.Color(1, 1, 1, 1), + extent: null, + layersArray: layersArray, + layerStates: layerStates, + size: size, + view2DState: view2DState, + time: time + }; + } + + this.preRenderFunctions_ = goog.array.filter( + this.preRenderFunctions_, + function(preRenderFunction) { + return preRenderFunction(this, frameState); + }, + this); + + if (!goog.isNull(frameState)) { + var center = view2DState.center; + var resolution = view2DState.resolution; + var rotation = view2DState.rotation; + var x = resolution * size.width / 2; + var y = resolution * size.height / 2; + var corners = [ + new ol.Coordinate(-x, -y), + new ol.Coordinate(-x, y), + new ol.Coordinate(x, -y), + new ol.Coordinate(x, y) + ]; + var corner; + for (i = 0; i < 4; ++i) { + corner = corners[i]; + corner.rotate(rotation); + corner.add(center); + } + frameState.extent = ol.Extent.boundingExtent.apply(null, corners); + } + + this.renderer_.renderFrame(frameState); + + if (!goog.isNull(frameState)) { + if (frameState.animate) { + this.requestRenderFrame(); + } + } + this.frameState_ = frameState; this.dirty_ = false; + if (goog.DEBUG) { this.logger.info('postrender'); } - this.dispatchEvent(ol.MapEventType.POSTRENDER); + + goog.global.setTimeout(this.dispatchPostRenderEvent_, 0); + }; diff --git a/src/ol/renderer/dom/domlayerrenderer.js b/src/ol/renderer/dom/domlayerrenderer.js index 58bbb51b2a..d8ff224761 100644 --- a/src/ol/renderer/dom/domlayerrenderer.js +++ b/src/ol/renderer/dom/domlayerrenderer.js @@ -1,7 +1,9 @@ goog.provide('ol.renderer.dom.Layer'); goog.require('ol.Coordinate'); +goog.require('ol.FrameState'); goog.require('ol.layer.Layer'); +goog.require('ol.layer.LayerState'); goog.require('ol.renderer.Layer'); @@ -60,6 +62,7 @@ ol.renderer.dom.Layer.prototype.handleLayerVisibleChange = function() { /** - * @param {number} time Time. + * @param {ol.FrameState} frameState Frame state. + * @param {ol.layer.LayerState} layerState Layer state. */ ol.renderer.dom.Layer.prototype.renderFrame = goog.abstractMethod; diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 8b6044ec32..1d36d36eaa 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -9,6 +9,7 @@ goog.require('goog.events'); goog.require('goog.events.Event'); goog.require('goog.functions'); goog.require('ol.Coordinate'); +goog.require('ol.FrameState'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.dom.TileLayer'); @@ -93,26 +94,20 @@ ol.renderer.dom.Map.prototype.handleViewChanged = function() { /** * @inheritDoc */ -ol.renderer.dom.Map.prototype.renderFrame = function(time) { +ol.renderer.dom.Map.prototype.renderFrame = function(frameState) { - var map = this.getMap(); - if (!map.isDef()) { + if (goog.isNull(frameState)) { + // FIXME remove everything return; } - var requestRenderFrame = false; - var layers = map.getLayers(); - if (goog.isDef(layers)) { - layers.forEach(function(layer) { - var layerRenderer = this.getLayerRenderer(layer); - if (layerRenderer.renderFrame(time)) { - requestRenderFrame = true; - } - }, this); - } - - if (requestRenderFrame) { - map.requestRenderFrame(); - } + goog.array.forEach(frameState.layersArray, function(layer) { + var layerState = frameState.layerStates[goog.getUid(layer)]; + if (!layerState.ready) { + return; + } + var layerRenderer = this.getLayerRenderer(layer); + layerRenderer.renderFrame(frameState, layerState); + }, this); }; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 0686f99db9..88969c284f 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -67,47 +67,36 @@ ol.renderer.dom.TileLayer.prototype.getTileLayer = function() { /** * @inheritDoc */ -ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { +ol.renderer.dom.TileLayer.prototype.renderFrame = + function(frameState, layerState) { - var map = this.getMap(); - goog.asserts.assert(map.isDef()); - - var view = map.getView().getView2D(); - var mapCenter = /** @type {!ol.Coordinate} */ (view.getCenter()); - var mapResolution = /** @type {number} */ (view.getResolution()); - var mapSize = /** @type {!ol.Size} */ (map.getSize()); - var mapRotatedExtent = /** @type {!ol.Extent} */ - (view.getRotatedExtent(mapSize)); - var mapRotation = view.getRotation(); - var mapScale = 1 / mapResolution; + var view2DState = frameState.view2DState; var tileLayer = this.getTileLayer(); - var visible = tileLayer.getVisible(); - if (!visible) { + if (!layerState.visible) { if (this.renderedVisible_) { goog.style.showElement(this.target, false); this.renderedVisible_ = false; } - return; + return false; } - var opacity = tileLayer.getOpacity(); - if (opacity != this.renderedOpacity_) { - goog.style.setOpacity(this.target, opacity); - this.renderedOpacity_ = opacity; + if (layerState.opacity != this.renderedOpacity_) { + goog.style.setOpacity(this.target, layerState.opacity); + this.renderedOpacity_ = layerState.opacity; } var tileSource = tileLayer.getTileSource(); var tileGrid = tileSource.getTileGrid(); - var z = tileGrid.getZForResolution(mapResolution); + var z = tileGrid.getZForResolution(view2DState.resolution); /** @type {Object.>} */ var tilesToDrawByZ = {}; var tileRange = tileGrid.getTileRangeForExtentAndResolution( - mapRotatedExtent, mapResolution); + frameState.extent, view2DState.resolution); var allTilesLoaded = true; @@ -173,7 +162,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { tileLayerZ = this.tileLayerZs_[tileLayerZKey]; } else { tileCoordOrigin = - tileGrid.getTileCoordForCoordAndZ(mapCenter, tileLayerZKey); + tileGrid.getTileCoordForCoordAndZ(view2DState.center, tileLayerZKey); tileLayerZ = new ol.renderer.dom.TileLayerZ_(tileGrid, tileCoordOrigin); newTileLayerZKeys[tileLayerZKey] = true; this.tileLayerZs_[tileLayerZKey] = tileLayerZ; @@ -204,14 +193,15 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { origin = tileLayerZ.getOrigin(); goog.vec.Mat4.makeIdentity(transform); goog.vec.Mat4.translate( - transform, mapSize.width / 2, mapSize.height / 2, 0); - if (goog.isDef(mapRotation)) { - goog.vec.Mat4.rotateZ(transform, mapRotation); - } - goog.vec.Mat4.scale( - transform, resolution / mapResolution, resolution / mapResolution, 1); - goog.vec.Mat4.translate(transform, (origin.x - mapCenter.x) / resolution, - (mapCenter.y - origin.y) / resolution, 0); + transform, frameState.size.width / 2, frameState.size.height / 2, 0); + goog.vec.Mat4.rotateZ(transform, view2DState.rotation); + goog.vec.Mat4.scale(transform, resolution / view2DState.resolution, + resolution / view2DState.resolution, 1); + goog.vec.Mat4.translate( + transform, + (origin.x - view2DState.center.x) / resolution, + (view2DState.center.y - origin.y) / resolution, + 0); tileLayerZ.setTransform(transform); if (tileLayerZKey in newTileLayerZKeys) { for (j = tileLayerZKey - 1; j >= 0; --j) { @@ -225,16 +215,18 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { goog.dom.insertChildAt(this.target, tileLayerZ.target, 0); } } else { - tileLayerZ.removeTilesOutsideExtent(mapRotatedExtent); + tileLayerZ.removeTilesOutsideExtent(frameState.extent); } } - if (visible && !this.renderedVisible_) { + if (layerState.visible && !this.renderedVisible_) { goog.style.showElement(this.target, true); this.renderedVisible_ = true; } - return !allTilesLoaded; + if (!allTilesLoaded) { + frameState.animate = true; + } }; diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 3e67e48cf8..38b4af0b0a 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -7,6 +7,7 @@ goog.require('goog.functions'); goog.require('goog.fx.anim'); goog.require('goog.fx.anim.Animated'); goog.require('goog.vec.Mat4'); +goog.require('ol.FrameState'); goog.require('ol.View2D'); goog.require('ol.View2DProperty'); @@ -294,9 +295,9 @@ ol.renderer.Map.prototype.removeLayerRenderer = function(layer) { /** * Render. - * @param {number} time Time. + * @param {?ol.FrameState} frameState Frame state. */ -ol.renderer.Map.prototype.renderFrame = goog.functions.FALSE; +ol.renderer.Map.prototype.renderFrame = goog.nullFunction; /** diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 5a19a31306..7fce8f5256 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -1,7 +1,11 @@ +// FIXME move colorMatrix_ elsewhere? + goog.provide('ol.renderer.webgl.Layer'); goog.require('goog.vec.Mat4'); +goog.require('ol.FrameState'); goog.require('ol.layer.Layer'); +goog.require('ol.layer.LayerState'); goog.require('ol.renderer.Layer'); goog.require('ol.vec.Mat4'); @@ -179,8 +183,8 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction; /** * Render. - * @param {number} time Time. - * @return {boolean} Request render frame. + * @param {ol.FrameState} frameState Frame state. + * @param {ol.layer.LayerState} layerState Layer state. */ ol.renderer.webgl.Layer.prototype.renderFrame = goog.abstractMethod; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 4bfea15f74..83a75771d3 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -143,12 +143,6 @@ ol.renderer.webgl.Map = function(container, map) { goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, this.handleWebGLContextResourced, false, this); - /** - * @private - * @type {ol.Color} - */ - this.clearColor_ = new ol.Color(1, 1, 1, 1); - /** * @private * @type {{aPosition: number, @@ -303,26 +297,6 @@ ol.renderer.webgl.Map.prototype.disposeInternal = function() { }; -/** - * @param {function(this: T, ol.layer.Layer, ol.renderer.webgl.Layer, number)} f - * Function. - * @param {T=} opt_obj Object. - * @template T - */ -ol.renderer.webgl.Map.prototype.forEachReadyVisibleLayer = - function(f, opt_obj) { - var layers = this.map.getLayers(); - if (goog.isDef(layers)) { - layers.forEach(function(layer, index) { - if (layer.isReady() && layer.getVisible()) { - var layerRenderer = this.getLayerRenderer(layer); - f.call(opt_obj, layer, layerRenderer, index); - } - }, this); - } -}; - - /** * @return {WebGLRenderingContext} GL. */ @@ -394,12 +368,6 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) { * @inheritDoc */ ol.renderer.webgl.Map.prototype.handleBackgroundColorChanged = function() { - var backgroundColor = this.getMap().getBackgroundColor(); - this.clearColor_ = new ol.Color( - backgroundColor.r / 255, - backgroundColor.g / 255, - backgroundColor.b / 255, - backgroundColor.a / 255); this.getMap().render(); }; @@ -522,34 +490,37 @@ ol.renderer.webgl.Map.prototype.removeLayerRenderer = function(layer) { /** * @inheritDoc */ -ol.renderer.webgl.Map.prototype.renderFrame = function(time) { +ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { - var map = this.getMap(); - if (!map.isDef()) { - return; + var gl = this.getGL(); + + if (goog.isNull(frameState)) { + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null); + gl.clearColor(0, 0, 0, 0); + gl.clear(goog.webgl.COLOR_BUFFER_BIT); + return false; } - var requestRenderFrame = false; - - this.forEachReadyVisibleLayer(function(layer, layerRenderer) { - if (layerRenderer.renderFrame(time)) { - requestRenderFrame = true; + goog.array.forEach(frameState.layersArray, function(layer) { + var layerState = frameState.layerStates[goog.getUid(layer)]; + if (!layerState.visible || !layerState.ready) { + return; } - }); + var layerRenderer = this.getLayerRenderer(layer); + layerRenderer.renderFrame(frameState, layerState); + }, this); - var size = /** @type {ol.Size} */ (this.getMap().getSize()); + var size = frameState.size; if (!this.canvasSize_.equals(size)) { this.canvas_.width = size.width; this.canvas_.height = size.height; this.canvasSize_ = size; } - var gl = this.getGL(); - gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null); - gl.clearColor(this.clearColor_.r, this.clearColor_.g, this.clearColor_.b, - this.clearColor_.a); + var clearColor = frameState.backgroundColor; + gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); gl.clear(goog.webgl.COLOR_BUFFER_BIT); gl.enable(goog.webgl.BLEND); gl.viewport(0, 0, size.width, size.height); @@ -589,7 +560,12 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(time) { this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8); gl.uniform1i(this.locations_.uTexture, 0); - this.forEachReadyVisibleLayer(function(layer, layerRenderer) { + goog.array.forEach(frameState.layersArray, function(layer) { + var layerState = frameState.layerStates[goog.getUid(layer)]; + if (!layerState.visible || !layerState.ready) { + return; + } + var layerRenderer = this.getLayerRenderer(layer); gl.uniformMatrix4fv( this.locations_.uMatrix, false, layerRenderer.getMatrix()); gl.uniformMatrix4fv( @@ -599,10 +575,6 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(time) { gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); }, this); - if (requestRenderFrame) { - this.getMap().requestRenderFrame(); - } - }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 2152b6a824..23de60684e 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -287,30 +287,21 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() { /** * @inheritDoc */ -ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) { - - var requestRenderFrame = false; +ol.renderer.webgl.TileLayer.prototype.renderFrame = + function(frameState, layerState) { var mapRenderer = this.getMapRenderer(); - var map = this.getMap(); var gl = mapRenderer.getGL(); - var view = map.getView().getView2D(); - goog.asserts.assert(map.isDef()); - var mapSize = map.getSize(); - var mapCenter = view.getCenter(); - var mapExtent = view.getExtent(mapSize); - var mapResolution = /** @type {number} */ (view.getResolution()); - var mapRotatedExtent = view.getRotatedExtent(mapSize); - var mapRotation = view.getRotation(); + var view2DState = frameState.view2DState; var tileLayer = this.getLayer(); var tileSource = tileLayer.getTileSource(); var tileGrid = tileSource.getTileGrid(); - var z = tileGrid.getZForResolution(mapResolution); + var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); var tileRange = tileGrid.getTileRangeForExtentAndResolution( - mapRotatedExtent, tileResolution); + frameState.extent, tileResolution); var framebufferExtent; @@ -465,7 +456,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) { if (!goog.array.isEmpty(tilesToLoad)) { goog.events.listenOnce( - map, + this.getMap(), ol.MapEventType.POSTRENDER, goog.partial(function(mapRenderer, tilesToLoad) { if (goog.DEBUG) { @@ -488,25 +479,23 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) { } else { this.renderedTileRange_ = null; this.renderedFramebufferExtent_ = null; - requestRenderFrame = true; + frameState.animate = true; } } goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, - (mapCenter.x - framebufferExtent.minX) / + (view2DState.center.x - framebufferExtent.minX) / (framebufferExtent.maxX - framebufferExtent.minX), - (mapCenter.y - framebufferExtent.minY) / + (view2DState.center.y - framebufferExtent.minY) / (framebufferExtent.maxY - framebufferExtent.minY), 0); - if (goog.isDef(mapRotation)) { - goog.vec.Mat4.rotateZ(this.matrix_, mapRotation); - } + goog.vec.Mat4.rotateZ(this.matrix_, view2DState.rotation); goog.vec.Mat4.scale(this.matrix_, - (mapExtent.maxX - mapExtent.minX) / + frameState.size.width * view2DState.resolution / (framebufferExtent.maxX - framebufferExtent.minX), - (mapExtent.maxY - mapExtent.minY) / + frameState.size.height * view2DState.resolution / (framebufferExtent.maxY - framebufferExtent.minY), 1); goog.vec.Mat4.translate(this.matrix_, @@ -514,6 +503,4 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) { -0.5, 0); - return requestRenderFrame; - }; diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 998b226196..f07f560ad6 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -1,4 +1,5 @@ // FIXME getView3D has not return type +// FIXME remove getExtent? goog.provide('ol.View2D'); goog.provide('ol.View2DProperty'); @@ -92,32 +93,6 @@ ol.View2D.prototype.getExtent = function(size) { }; -/** - * @param {ol.Size} size Box pixel size. - * @return {ol.Extent} Rotated extent. - */ -ol.View2D.prototype.getRotatedExtent = function(size) { - goog.asserts.assert(this.isDef()); - // FIXME try w/o casting - var center = /** @type {!ol.Coordinate} */ (this.getCenter()); - var resolution = this.getResolution(); - var rotation = this.getRotation(); - var xScale = resolution * size.width / 2; - var yScale = resolution * size.height / 2; - var corners = [ - new ol.Coordinate(-xScale, -yScale), - new ol.Coordinate(-xScale, yScale), - new ol.Coordinate(xScale, -yScale), - new ol.Coordinate(xScale, yScale) - ]; - goog.array.forEach(corners, function(corner) { - corner.rotate(rotation); - corner.add(center); - }); - return ol.Extent.boundingExtent.apply(null, corners); -}; - - /** * @inheritDoc */ @@ -177,6 +152,24 @@ ol.View2D.prototype.getView2D = function() { }; +/** + * @inheritDoc + */ +ol.View2D.prototype.getView2DState = function() { + goog.asserts.assert(this.isDef()); + var center = /** @type {ol.Coordinate} */ (this.getCenter()); + var projection = /** @type {ol.Projection} */ (this.getProjection()); + var resolution = /** @type {number} */ (this.getResolution()); + var rotation = /** @type {number} */ (this.getRotation()); + return { + center: new ol.Coordinate(center.x, center.y), + projection: projection, + resolution: resolution, + rotation: rotation + }; +}; + + /** * FIXME return type */