From e002798350b608c3688a6dd36121a58b20b61e24 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 5 Jan 2015 20:48:14 -0500 Subject: [PATCH 01/20] switch to perspective matrix --- js/geo/transform.js | 16 +++++++++++++++- js/render/painter.js | 6 ++++-- js/source/tile.js | 7 ++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 3a34d94ed1a..0897b9dadca 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -2,7 +2,8 @@ var LatLng = require('./lat_lng'), Point = require('point-geometry'), - wrap = require('../util/util').wrap; + wrap = require('../util/util').wrap, + mat4 = require('gl-matrix').mat4; module.exports = Transform; @@ -21,6 +22,8 @@ function Transform(minZoom, maxZoom) { this.zoom = 0; this.center = new LatLng(0, 0); this.angle = 0; + this.tilt = 0; + this.altitude = 200; } Transform.prototype = { @@ -206,5 +209,16 @@ Transform.prototype = { x2 !== undefined ? x2 : this.x, y2 !== undefined ? y2 : this.y)); } + }, + + getProjMatrix: function() { + var m = new Float64Array(16); + mat4.perspective(m, 2 * Math.atan((this.height / 2) / this.altitude), this.width/this.height, 0, this.altitude + 1); + mat4.translate(m, m, [0, 0, -this.altitude]); + mat4.scale(m, m, [1, -1, 1 / this.height]); + mat4.rotateX(m, m, Math.PI / 180 * this.tilt); + mat4.rotateZ(m, m, this.angle); + mat4.translate(m, m, [-this.centerPoint.x, -this.centerPoint.y, 0]); + return m; } }; diff --git a/js/render/painter.js b/js/render/painter.js index 18b67cc5c8d..27e9da5a476 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -40,11 +40,13 @@ GLPainter.prototype.resize = function(width, height) { var gl = this.gl; // Initialize projection matrix this.projectionMatrix = mat4.create(); - mat4.ortho(this.projectionMatrix, 0, width, height, 0, 0, -1); + + this.projectionMatrix = this.transform.getProjMatrix(); + gl.viewport(0, 0, this.transform.width * window.devicePixelRatio, + this.transform.height * window.devicePixelRatio); this.width = width * browser.devicePixelRatio; this.height = height * browser.devicePixelRatio; - gl.viewport(0, 0, this.width, this.height); }; diff --git a/js/source/tile.js b/js/source/tile.js index 81d82dca357..3bbc0a21903 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -25,9 +25,6 @@ Tile.prototype = { // The position matrix this.posMatrix = mat4.create(); - mat4.translate(this.posMatrix, this.posMatrix, [transform.centerPoint.x, transform.centerPoint.y, 0]); - mat4.rotateZ(this.posMatrix, this.posMatrix, transform.angle); - mat4.translate(this.posMatrix, this.posMatrix, [-transform.centerPoint.x, -transform.centerPoint.y, 0]); var pixelX = transform.width / 2 - transform.x, pixelY = transform.height / 2 - transform.y; @@ -43,6 +40,10 @@ Tile.prototype = { // The extrusion matrix. this.exMatrix = mat4.clone(painter.projectionMatrix); + /* + this.exMatrix = mat4.create(); + mat4.ortho(this.exMatrix, 0, transform.width, transform.height, 0, 0, -1); + */ mat4.rotateZ(this.exMatrix, this.exMatrix, transform.angle); // 2x2 matrix for rotating points From 8f5731190b2829f93291cb4cf87164e3a80df08f Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 6 Jan 2015 12:37:23 -0500 Subject: [PATCH 02/20] fix extrudes, rotation, and unskew labels --- js/geo/transform.js | 2 ++ js/render/draw_symbol.js | 4 ++++ js/render/painter.js | 2 +- js/source/tile.js | 7 +++---- shaders/line.vertex.glsl | 3 +-- shaders/sdf.vertex.glsl | 9 ++++++++- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 0897b9dadca..a30f83f750b 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -23,7 +23,9 @@ function Transform(minZoom, maxZoom) { this.center = new LatLng(0, 0); this.angle = 0; this.tilt = 0; + this.tilt = 75; this.altitude = 200; + this.altitude = 1.5; } Transform.prototype = { diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 7e41d9a571d..11f2eb5525d 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -33,8 +33,12 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr if (angleOffset) { mat4.rotateZ(exMatrix, exMatrix, angleOffset); + return; // todo remove } + // todo, only for horizontal labels? + exMatrix = mat4.clone(painter.tile.exMatrix); + // If layerStyle.size > layoutProperties[prefix + '-max-size'] then labels may collide var fontSize = layerStyle[prefix + '-size'] || layoutProperties[prefix + '-max-size']; var fontScale = fontSize / defaultSizes[prefix]; diff --git a/js/render/painter.js b/js/render/painter.js index 27e9da5a476..d62299377db 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -78,7 +78,7 @@ GLPainter.prototype.setup = function() { this.lineShader = gl.initializeShader('line', ['a_pos', 'a_extrude', 'a_linesofar'], - ['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_dasharray', 'u_blur']); + ['u_matrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_dasharray', 'u_blur']); this.linepatternShader = gl.initializeShader('linepattern', ['a_pos', 'a_extrude', 'a_linesofar'], diff --git a/js/source/tile.js b/js/source/tile.js index 3bbc0a21903..e547f569ceb 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -15,6 +15,8 @@ Tile.prototype = { calculateMatrices(z, x, y, transform, painter) { + if (painter) painter.resize(); + // Initialize model-view matrix that converts from the tile coordinates // to screen coordinates. var tileScale = Math.pow(2, z); @@ -39,12 +41,9 @@ Tile.prototype = { mat4.multiply(this.posMatrix, painter.projectionMatrix, this.posMatrix); // The extrusion matrix. - this.exMatrix = mat4.clone(painter.projectionMatrix); - /* this.exMatrix = mat4.create(); mat4.ortho(this.exMatrix, 0, transform.width, transform.height, 0, 0, -1); - */ - mat4.rotateZ(this.exMatrix, this.exMatrix, transform.angle); + //mat4.rotateZ(this.exMatrix, this.exMatrix, -transform.angle); // 2x2 matrix for rotating points this.rotationMatrix = mat2.create(); diff --git a/shaders/line.vertex.glsl b/shaders/line.vertex.glsl index 1d8e687c957..abb23cc4868 100644 --- a/shaders/line.vertex.glsl +++ b/shaders/line.vertex.glsl @@ -13,7 +13,6 @@ attribute float a_linesofar; // matrix is for the vertex position, exmatrix is for rotating and projecting // the extrusion vector. uniform mat4 u_matrix; -uniform mat4 u_exmatrix; // shared uniform float u_ratio; @@ -40,6 +39,6 @@ void main() { // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist; + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist.xy / u_ratio, 0.0, 1.0); v_linesofar = a_linesofar * u_ratio; } diff --git a/shaders/sdf.vertex.glsl b/shaders/sdf.vertex.glsl index c5166ae46f8..4ff194186ea 100644 --- a/shaders/sdf.vertex.glsl +++ b/shaders/sdf.vertex.glsl @@ -64,6 +64,13 @@ void main() { // hide if (angle >= a_rangeend && angle < rangestart) z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle)); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0); + gl_Position = u_matrix * vec4(a_pos, 0, 1); + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0); + + //if (u_flip == 0.0) { + //extrude *= gl_Position.w; + //} + + gl_Position += extrude; v_tex = a_tex / u_texsize; } From 1599a01878627001a6c5ba985f0892461a943b0a Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 6 Jan 2015 14:44:12 -0500 Subject: [PATCH 03/20] fill entire screen with tiles --- js/geo/transform.js | 83 +++++++++++++++++++++++++++++++++++++++++++-- js/source/tile.js | 5 +-- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index a30f83f750b..7e55c6334df 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -23,7 +23,7 @@ function Transform(minZoom, maxZoom) { this.center = new LatLng(0, 0); this.angle = 0; this.tilt = 0; - this.tilt = 75; + this.tilt = 55; this.altitude = 200; this.altitude = 1.5; } @@ -148,7 +148,84 @@ Transform.prototype = { }; }, - pointCoordinate(tileCenter, p) { + pointCoordinate: function(_, p) { + var m = this.coordinatePointMatrix(this.tileZoom); + + // This could definitely be more elegant + // or at least understandable + + // We know: + // the matrix, unprojected z, y (0, 1), and projected x, y (point) + // We don't know: + // the unprojected x, y (which we want), and the projected z, y + // + // Solve 3 equations with three unknowns + + // Terrible temporary hack to avoid division by 0 + if (p.x === 0) p.x = -1; + if (p.y === 0) p.y = -1; + + var f1 = m[0] / m[1]; + var g1 = p.x - f1 * p.y; + // 0 = a1 * x + b1 * y + c1 + var a1 = m[3]; + var b1 = m[7] - (m[4] - f1 * m[5]) / g1; + var c1 = m[15] - (m[12] - f1 * m[13]) / g1; + + if (m[1] === 0) { + a1 = m[3]; + b1 = m[7] - m[5] / p.y; + c1 = m[15] - m[13] / p.y; + } + + var f2 = m[4] / m[5]; + var g2 = p.x - f2 * p.y; + // 0 = a2 * x + b2 * y + c2 + var a2 = m[3] - (m[0] - f2 * m[1]) / g2; + var b2 = m[7]; + var c2 = m[15] - (m[12] - f2 * m[13]) / g2; + + if (m[5] === 0) { + a2 = m[3] - m[1] / p.y; + b2 = m[7]; + c2 = m[15] - m[13] / p.y; + } + + var f3 = a1 / a2; + var b3 = b1 - f3 * b2; + var c3 = c1 - f3 * c2; + var y = -c3 / b3; + + var x = a1 !== 0 ? + -(b1 * y + c1) / a1 : + -(b2 * y + c2) / a2; + + return { + column: x, + row: y, + zoom: this.tileZoom + }; + }, + + coordinatePointMatrix: function(z) { + var proj = this.getProjMatrix(); + var tileScale = Math.pow(2, z); // number of tiles along an edge at that z level + var scale = this.worldSize / tileScale; + mat4.scale(proj, proj, [scale, scale, 1]); + mat4.multiply(proj, this.getPixelMatrix(), proj); + return proj; + }, + + // converts pixel points to gl coords + getPixelMatrix: function() { + // gl coords to screen coords + var m = mat4.create(); + mat4.scale(m, m, [this.width / 2, -this.height / 2, 1]); + mat4.translate(m, m, [1, -1, 0]); + return m; + }, + + pointCoordinateOld(tileCenter, p) { var zoomFactor = this.zoomScale(this.zoomFraction), kt = this.zoomScale(this.tileZoom - tileCenter.zoom), p2 = this.centerPoint._sub(p)._rotate(-this.angle)._div(this.tileSize * zoomFactor); @@ -220,7 +297,7 @@ Transform.prototype = { mat4.scale(m, m, [1, -1, 1 / this.height]); mat4.rotateX(m, m, Math.PI / 180 * this.tilt); mat4.rotateZ(m, m, this.angle); - mat4.translate(m, m, [-this.centerPoint.x, -this.centerPoint.y, 0]); + mat4.translate(m, m, [-this.x, -this.y, 0]); return m; } }; diff --git a/js/source/tile.js b/js/source/tile.js index e547f569ceb..44fb3dab406 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -28,10 +28,7 @@ Tile.prototype = { // The position matrix this.posMatrix = mat4.create(); - var pixelX = transform.width / 2 - transform.x, - pixelY = transform.height / 2 - transform.y; - - mat4.translate(this.posMatrix, this.posMatrix, [pixelX + x * scale, pixelY + y * scale, 1]); + mat4.translate(this.posMatrix, this.posMatrix, [x * scale, y * scale, 1]); // Create inverted matrix for interaction this.invPosMatrix = mat4.create(); From cb0dba9e9812d724fa4d89e977837f46e105bc50 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 6 Jan 2015 14:44:41 -0500 Subject: [PATCH 04/20] fix text size --- js/render/draw_symbol.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 11f2eb5525d..a115e81db7b 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -38,6 +38,8 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr // todo, only for horizontal labels? exMatrix = mat4.clone(painter.tile.exMatrix); + var s = painter.transform.altitude; + mat4.scale(exMatrix, exMatrix, [s, s, 1, 1]); // If layerStyle.size > layoutProperties[prefix + '-max-size'] then labels may collide var fontSize = layerStyle[prefix + '-size'] || layoutProperties[prefix + '-max-size']; From 9ab29209a6017c4bc0f967afcea5e8d661fb307b Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 6 Jan 2015 17:03:27 -0500 Subject: [PATCH 05/20] add easing for tilt --- js/ui/easings.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/js/ui/easings.js b/js/ui/easings.js index 759466bd8e8..0d5c352f168 100644 --- a/js/ui/easings.js +++ b/js/ui/easings.js @@ -193,7 +193,7 @@ util.extend(exports, { this.flyTo(center, zoom, 0, options); }, - easeTo(latlng, zoom, bearing, options) { + easeTo(latlng, zoom, bearing, tilt, options) { this.stop(); options = util.extend({ @@ -205,7 +205,8 @@ util.extend(exports, { var tr = this.transform, offset = Point.convert(options.offset).rotate(-tr.angle), startZoom = this.getZoom(), - startBearing = this.getBearing(); + startBearing = this.getBearing(), + startTilt = this.transform.tilt; latlng = LatLng.convert(latlng); zoom = zoom === undefined ? startZoom : zoom; @@ -235,6 +236,10 @@ util.extend(exports, { tr.bearing = util.interp(startBearing, bearing, k); } + if (tilt !== startTilt) { + tr.tilt = util.interp(startTilt, tilt, k); + } + this.animationLoop.set(300); // text fading this._move(zoom !== startZoom, bearing !== startBearing); }, function() { From 25a38b0cd08269c6c1a76c50657c81f2be2b0096 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 6 Jan 2015 17:03:48 -0500 Subject: [PATCH 06/20] adapt style for tilted view --- debug/style.json | 20 ++++++++++++++------ js/style/style.js | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/debug/style.json b/debug/style.json index 3b505ca43a2..5025b058f78 100644 --- a/debug/style.json +++ b/debug/style.json @@ -1174,7 +1174,10 @@ "text-font": "@sans", "text-field": "@name", "text-max-size": 13, - "symbol-placement": "line" + "symbol-min-distance": 300, + "text-padding": 40, + "symbol-placement": "line", + "text-rotation-alignment": "viewport" }, "paint": { "text-color": "#765", @@ -1221,8 +1224,9 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, - "text-padding": 2, "text-offset": [0, 0.6], + "text-padding": 20, + "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1245,8 +1249,9 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, - "text-padding": 2, "text-offset": [0, 0.6], + "text-padding": 20, + "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1269,8 +1274,9 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, - "text-padding": 2, "text-offset": [0, 0.6], + "text-padding": 20, + "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1293,8 +1299,9 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, - "text-padding": 2, "text-offset": [0, 0.6], + "text-padding": 20, + "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1317,8 +1324,9 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, - "text-padding": 2, "text-offset": [0, 0.6], + "text-padding": 20, + "icon-padding": 20, "text-anchor": "top" }, "paint": { diff --git a/js/style/style.js b/js/style/style.js index b7fae74619c..0edfa8b04d4 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -169,7 +169,7 @@ Style.prototype = util.inherit(Evented, { var source = bucket && bucket.source; // mark source as used so that tiles are downloaded - if (source) this.sources[source].used = true; + if (source && this.sources[source]) this.sources[source].used = true; } if (appliedLayer['raster-fade-duration']) { From f4448b337a2462b7798ba83e242b85517c8060a2 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 6 Feb 2015 19:35:57 -0800 Subject: [PATCH 07/20] fix posMatrix precision issues --- js/source/tile.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/source/tile.js b/js/source/tile.js index 44fb3dab406..a954c1fad72 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -26,8 +26,8 @@ Tile.prototype = { this.scale = scale; // The position matrix - this.posMatrix = mat4.create(); - + this.posMatrix = new Float64Array(16); + mat4.identity(this.posMatrix); mat4.translate(this.posMatrix, this.posMatrix, [x * scale, y * scale, 1]); // Create inverted matrix for interaction @@ -45,6 +45,8 @@ Tile.prototype = { // 2x2 matrix for rotating points this.rotationMatrix = mat2.create(); mat2.rotate(this.rotationMatrix, this.rotationMatrix, transform.angle); + + this.posMatrix = new Float32Array(this.posMatrix); }, positionAt(id, point) { From 8afc15a1f7eb2b45d3d1954f822a48ff78975d70 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 6 Feb 2015 19:46:04 -0800 Subject: [PATCH 08/20] use linear interpolate for icons when tilted --- js/render/draw_symbol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index a115e81db7b..38b6dda50a8 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -66,7 +66,7 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr buffer = bucket.buffers.glyphVertex; texsize = [painter.glyphAtlas.width / 4, painter.glyphAtlas.height / 4]; } else { - imageSprite.bind(gl, alignedWithMap || params.rotating || params.zooming || fontScale != 1 || sdf); + imageSprite.bind(gl, alignedWithMap || params.rotating || params.zooming || fontScale != 1 || sdf || painter.transform.tilt); buffer = bucket.buffers.iconVertex; texsize = [imageSprite.img.width, imageSprite.img.height]; } From 9bbe1a59f5fd62b75451f0265505f8804f72ddf7 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 6 Feb 2015 19:50:21 -0800 Subject: [PATCH 09/20] fix fill antialiasing for tilt --- shaders/outline.vertex.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/outline.vertex.glsl b/shaders/outline.vertex.glsl index 29c16e3ded2..535063b5964 100644 --- a/shaders/outline.vertex.glsl +++ b/shaders/outline.vertex.glsl @@ -6,5 +6,5 @@ varying vec2 v_pos; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); - v_pos = (gl_Position.xy + 1.0) / 2.0 * u_world; + v_pos = (gl_Position.xy/gl_Position.w + 1.0) / 2.0 * u_world; } From 49c279ce83ff0e393febfe52110aada2f7d3fba0 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 6 Feb 2015 20:40:13 -0800 Subject: [PATCH 10/20] fix line antialiasing in perspective view --- js/render/draw_line.js | 16 ++++++++++++++++ js/render/painter.js | 2 +- shaders/line.fragment.glsl | 4 +++- shaders/line.vertex.glsl | 16 ++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 3fbb0594e07..75a5ebae934 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -1,6 +1,7 @@ 'use strict'; var browser = require('../util/browser'); +var mat2 = require('gl-matrix').mat2; module.exports = function drawLine(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite) { // don't draw zero-width lines @@ -18,6 +19,19 @@ module.exports = function drawLine(gl, painter, bucket, layerStyle, posMatrix, p var ratio = painter.transform.scale / (1 << params.z) / 8; var vtxMatrix = painter.translateMatrix(posMatrix, params.z, layerStyle['line-translate'], layerStyle['line-translate-anchor']); + var tr = painter.transform; + + + var antialiasingMatrix = mat2.create(); + mat2.scale(antialiasingMatrix, antialiasingMatrix, [1, Math.cos(tr.tilt / 180 * Math.PI)]); + mat2.rotate(antialiasingMatrix, antialiasingMatrix, painter.transform.angle); + + // calculate how much longer the real world distance is at the top of the screen + // than at the middle of the screen. + var topedgelength = Math.sqrt(tr.height * tr.height / 4 * (1 + tr.altitude * tr.altitude)); + var x = tr.height / 2 * Math.tan(tr.tilt / 180 * Math.PI); + var extra = (topedgelength + x) / topedgelength - 1; + var shader; var imagePos = layerStyle['line-image'] && imageSprite.getPosition(layerStyle['line-image']); @@ -45,6 +59,8 @@ module.exports = function drawLine(gl, painter, bucket, layerStyle, posMatrix, p gl.uniform2fv(shader.u_linewidth, [ outset, inset ]); gl.uniform1f(shader.u_ratio, ratio); gl.uniform1f(shader.u_blur, blur); + gl.uniform1f(shader.u_extra, extra); + gl.uniformMatrix2fv(shader.u_antialiasingmatrix, false, antialiasingMatrix); gl.uniform4fv(shader.u_color, color); gl.uniform2fv(shader.u_dasharray, layerStyle['line-dasharray']); diff --git a/js/render/painter.js b/js/render/painter.js index d62299377db..baeba57266f 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -78,7 +78,7 @@ GLPainter.prototype.setup = function() { this.lineShader = gl.initializeShader('line', ['a_pos', 'a_extrude', 'a_linesofar'], - ['u_matrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_dasharray', 'u_blur']); + ['u_matrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_dasharray', 'u_blur', 'u_extra', 'u_antialiasingmatrix']); this.linepatternShader = gl.initializeShader('linepattern', ['a_pos', 'a_extrude', 'a_linesofar'], diff --git a/shaders/line.fragment.glsl b/shaders/line.fragment.glsl index f4ac1458b33..27e9ea5072e 100644 --- a/shaders/line.fragment.glsl +++ b/shaders/line.fragment.glsl @@ -6,6 +6,7 @@ uniform vec2 u_dasharray; varying vec2 v_normal; varying float v_linesofar; +varying float gamma_scale; void main() { // Calculate the distance of the pixel from the line in pixels. @@ -14,7 +15,8 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_linewidth.t) or when fading out // (v_linewidth.s) - float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0); + float blur = u_blur * gamma_scale; + float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0); // Calculate the antialiasing fade factor based on distance to the dash. // Only affects alpha when line is dashed diff --git a/shaders/line.vertex.glsl b/shaders/line.vertex.glsl index abb23cc4868..ca6004eb2f5 100644 --- a/shaders/line.vertex.glsl +++ b/shaders/line.vertex.glsl @@ -19,8 +19,12 @@ uniform float u_ratio; uniform vec2 u_linewidth; uniform vec4 u_color; +uniform float u_extra; +uniform mat2 u_antialiasingmatrix; + varying vec2 v_normal; varying float v_linesofar; +varying float gamma_scale; void main() { // We store the texture normals in the most insignificant bit @@ -41,4 +45,16 @@ void main() { // tile's zoom level. gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist.xy / u_ratio, 0.0, 1.0); v_linesofar = a_linesofar * u_ratio; + + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + + // how much features are squished in the y direction by the tilt + float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - y * u_extra); + + // the 0.9 adds a tiny bit of extra blurriness to account for the inexactness of these calculations + gamma_scale = perspective_scale * squish_scale * 0.9; } From cc52ae3a1d90ee4f7acaea56825198532f2cede0 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 15:15:18 -0800 Subject: [PATCH 11/20] support line labels in perspective view --- debug/style.json | 2 +- js/geo/transform.js | 9 +++++++-- js/render/draw_symbol.js | 34 ++++++++++++++++++++++------------ js/render/painter.js | 2 +- shaders/sdf.fragment.glsl | 4 +++- shaders/sdf.vertex.glsl | 22 ++++++++++++++++------ 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/debug/style.json b/debug/style.json index 5025b058f78..3bde0ee81db 100644 --- a/debug/style.json +++ b/debug/style.json @@ -1177,7 +1177,7 @@ "symbol-min-distance": 300, "text-padding": 40, "symbol-placement": "line", - "text-rotation-alignment": "viewport" + "text-rotation-alignment": "map" }, "paint": { "text-color": "#765", diff --git a/js/geo/transform.js b/js/geo/transform.js index 7e55c6334df..18ecf52593e 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -151,8 +151,6 @@ Transform.prototype = { pointCoordinate: function(_, p) { var m = this.coordinatePointMatrix(this.tileZoom); - // This could definitely be more elegant - // or at least understandable // We know: // the matrix, unprojected z, y (0, 1), and projected x, y (point) @@ -160,6 +158,9 @@ Transform.prototype = { // the unprojected x, y (which we want), and the projected z, y // // Solve 3 equations with three unknowns + // + // We could invert the matrix and use that to unproject, but then we + // need to know the projected z value. We only know x, y. // Terrible temporary hack to avoid division by 0 if (p.x === 0) p.x = -1; @@ -294,7 +295,11 @@ Transform.prototype = { var m = new Float64Array(16); mat4.perspective(m, 2 * Math.atan((this.height / 2) / this.altitude), this.width/this.height, 0, this.altitude + 1); mat4.translate(m, m, [0, 0, -this.altitude]); + + // After the rotateX, z values are in pixel units. Convert them to + // altitude unites. 1 altitude unit = the screen height. mat4.scale(m, m, [1, -1, 1 / this.height]); + mat4.rotateX(m, m, Math.PI / 180 * this.tilt); mat4.rotateZ(m, m, this.angle); mat4.translate(m, m, [-this.x, -this.y, 0]); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 38b6dda50a8..efd7b772134 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -26,26 +26,34 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr posMatrix = painter.translateMatrix(posMatrix, params.z, layerStyle[prefix + '-translate'], layerStyle[prefix + '-translate-anchor']); var layoutProperties = bucket.layoutProperties; + var tr = painter.transform; - var exMatrix = mat4.clone(painter.projectionMatrix); var alignedWithMap = layoutProperties[prefix + '-rotation-alignment'] === 'map'; - var angleOffset = (alignedWithMap ? painter.transform.angle : 0); + var skewed = alignedWithMap; + var exMatrix, s, gamma_scale; - if (angleOffset) { - mat4.rotateZ(exMatrix, exMatrix, angleOffset); - return; // todo remove + if (skewed) { + exMatrix = mat4.create(); + s = 8 / Math.pow(2, painter.transform.zoom - params.z); + gamma_scale = 1 / Math.cos(tr.tilt / 180 * Math.PI); + } else { + exMatrix = mat4.clone(painter.tile.exMatrix); + s = painter.transform.altitude; + gamma_scale = 1; } - - // todo, only for horizontal labels? - exMatrix = mat4.clone(painter.tile.exMatrix); - var s = painter.transform.altitude; - mat4.scale(exMatrix, exMatrix, [s, s, 1, 1]); + mat4.scale(exMatrix, exMatrix, [s, s, 1]); // If layerStyle.size > layoutProperties[prefix + '-max-size'] then labels may collide var fontSize = layerStyle[prefix + '-size'] || layoutProperties[prefix + '-max-size']; var fontScale = fontSize / defaultSizes[prefix]; mat4.scale(exMatrix, exMatrix, [ fontScale, fontScale, 1 ]); + // calculate how much longer the real world distance is at the top of the screen + // than at the middle of the screen. + var topedgelength = Math.sqrt(tr.height * tr.height / 4 * (1 + tr.altitude * tr.altitude)); + var x = tr.height / 2 * Math.tan(tr.tilt / 180 * Math.PI); + var extra = (topedgelength + x) / topedgelength - 1; + var text = prefix === 'text'; var sdf = text || bucket.elementGroups.sdfIcons; var shader, buffer, texsize; @@ -74,6 +82,8 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr gl.switchShader(shader, posMatrix, exMatrix); gl.uniform1i(shader.u_texture, 0); gl.uniform2fv(shader.u_texsize, texsize); + gl.uniform1i(shader.u_skewed, skewed); + gl.uniform1f(shader.u_extra, extra); buffer.bind(gl, shader); @@ -103,14 +113,14 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr var haloOffset = 6; var gamma = 0.105 * defaultSizes[prefix] / fontSize / browser.devicePixelRatio; - gl.uniform1f(shader.u_gamma, gamma); + gl.uniform1f(shader.u_gamma, gamma * gamma_scale); gl.uniform4fv(shader.u_color, layerStyle[prefix + '-color']); gl.uniform1f(shader.u_buffer, (256 - 64) / 256); gl.drawArrays(gl.TRIANGLES, begin, len); if (layerStyle[prefix + '-halo-color']) { // Draw halo underneath the text. - gl.uniform1f(shader.u_gamma, layerStyle[prefix + '-halo-blur'] * blurOffset / fontScale / sdfPx + gamma); + gl.uniform1f(shader.u_gamma, (layerStyle[prefix + '-halo-blur'] * blurOffset / fontScale / sdfPx + gamma) * gamma_scale); gl.uniform4fv(shader.u_color, layerStyle[prefix + '-halo-color']); gl.uniform1f(shader.u_buffer, (haloOffset - layerStyle[prefix + '-halo-width'] / fontScale) / sdfPx); gl.drawArrays(gl.TRIANGLES, begin, len); diff --git a/js/render/painter.js b/js/render/painter.js index baeba57266f..9f8c8cad247 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -90,7 +90,7 @@ GLPainter.prototype.setup = function() { this.sdfShader = gl.initializeShader('sdf', ['a_pos', 'a_tex', 'a_offset', 'a_angle', 'a_minzoom', 'a_maxzoom', 'a_rangeend', 'a_rangestart', 'a_labelminzoom'], - ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_color', 'u_gamma', 'u_buffer', 'u_angle', 'u_zoom', 'u_flip', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom']); + ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_color', 'u_gamma', 'u_buffer', 'u_angle', 'u_zoom', 'u_flip', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom', 'u_skewed', 'u_extra']); this.iconShader = gl.initializeShader('icon', ['a_pos', 'a_tex', 'a_offset', 'a_angle', 'a_minzoom', 'a_maxzoom', 'a_rangeend', 'a_rangestart', 'a_labelminzoom'], diff --git a/shaders/sdf.fragment.glsl b/shaders/sdf.fragment.glsl index d72d61dab19..a7ae7f821f6 100644 --- a/shaders/sdf.fragment.glsl +++ b/shaders/sdf.fragment.glsl @@ -5,9 +5,11 @@ uniform float u_gamma; varying vec2 v_tex; varying float v_alpha; +varying float v_gamma_scale; void main() { + float gamma = u_gamma * v_gamma_scale; float dist = texture2D(u_texture, v_tex).a; - float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist) * v_alpha; + float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * v_alpha; gl_FragColor = u_color * alpha; } diff --git a/shaders/sdf.vertex.glsl b/shaders/sdf.vertex.glsl index 4ff194186ea..69148c92225 100644 --- a/shaders/sdf.vertex.glsl +++ b/shaders/sdf.vertex.glsl @@ -20,11 +20,14 @@ uniform float u_fadedist; uniform float u_minfadezoom; uniform float u_maxfadezoom; uniform float u_fadezoom; +uniform bool u_skewed; +uniform float u_extra; uniform vec2 u_texsize; varying vec2 v_tex; varying float v_alpha; +varying float v_gamma_scale; void main() { @@ -64,13 +67,20 @@ void main() { // hide if (angle >= a_rangeend && angle < rangestart) z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle)); - gl_Position = u_matrix * vec4(a_pos, 0, 1); - vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0); + if (u_skewed) { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0); + gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1); + gl_Position.z += z * gl_Position.w; + } else { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude; + } - //if (u_flip == 0.0) { - //extrude *= gl_Position.w; - //} + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - y * u_extra); + v_gamma_scale = perspective_scale; - gl_Position += extrude; v_tex = a_tex / u_texsize; } From 15cb421c49900240d2bef443fb937d73ab153d1c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 15:16:35 -0800 Subject: [PATCH 12/20] fix line antialiasing adjustment --- shaders/line.vertex.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shaders/line.vertex.glsl b/shaders/line.vertex.glsl index ca6004eb2f5..4ab568e2e86 100644 --- a/shaders/line.vertex.glsl +++ b/shaders/line.vertex.glsl @@ -55,6 +55,6 @@ void main() { // how much features are squished in all directions by the perspectiveness float perspective_scale = 1.0 / (1.0 - y * u_extra); - // the 0.9 adds a tiny bit of extra blurriness to account for the inexactness of these calculations - gamma_scale = perspective_scale * squish_scale * 0.9; + // the 1.1 adds a tiny bit of extra blurriness to account for the inexactness of these calculations + gamma_scale = perspective_scale * squish_scale * 1.1; } From 9b0029b0ef6f2a6058d249221b70f1a322075005 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 15:53:02 -0800 Subject: [PATCH 13/20] rename tilt to pitch, and clamp it --- js/geo/transform.js | 24 +++++++++++++++++++----- js/render/draw_line.js | 4 ++-- js/render/draw_symbol.js | 6 +++--- js/ui/easings.js | 8 ++++---- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 18ecf52593e..9b294337954 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -22,10 +22,10 @@ function Transform(minZoom, maxZoom) { this.zoom = 0; this.center = new LatLng(0, 0); this.angle = 0; - this.tilt = 0; - this.tilt = 55; - this.altitude = 200; - this.altitude = 1.5; + this._altitude = 1.5; + this._pitch = 0; + + this.pitch = 55; } Transform.prototype = { @@ -60,6 +60,20 @@ Transform.prototype = { this.angle = -wrap(bearing, -180, 180) * Math.PI / 180; }, + get pitch() { + return this._pitch / Math.PI * 180; + }, + set pitch(pitch) { + this._pitch = Math.min(60, pitch) / 180 * Math.PI; + }, + + get altitude() { + return this._altitude; + }, + set altitude(altitude) { + this._altitude = Math.max(0.75, altitude); + }, + get zoom() { return this._zoom; }, set zoom(zoom) { zoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom); @@ -300,7 +314,7 @@ Transform.prototype = { // altitude unites. 1 altitude unit = the screen height. mat4.scale(m, m, [1, -1, 1 / this.height]); - mat4.rotateX(m, m, Math.PI / 180 * this.tilt); + mat4.rotateX(m, m, this._pitch); mat4.rotateZ(m, m, this.angle); mat4.translate(m, m, [-this.x, -this.y, 0]); return m; diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 75a5ebae934..52e9d59092d 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -23,13 +23,13 @@ module.exports = function drawLine(gl, painter, bucket, layerStyle, posMatrix, p var antialiasingMatrix = mat2.create(); - mat2.scale(antialiasingMatrix, antialiasingMatrix, [1, Math.cos(tr.tilt / 180 * Math.PI)]); + mat2.scale(antialiasingMatrix, antialiasingMatrix, [1, Math.cos(tr._pitch)]); mat2.rotate(antialiasingMatrix, antialiasingMatrix, painter.transform.angle); // calculate how much longer the real world distance is at the top of the screen // than at the middle of the screen. var topedgelength = Math.sqrt(tr.height * tr.height / 4 * (1 + tr.altitude * tr.altitude)); - var x = tr.height / 2 * Math.tan(tr.tilt / 180 * Math.PI); + var x = tr.height / 2 * Math.tan(tr._pitch); var extra = (topedgelength + x) / topedgelength - 1; var shader; diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index efd7b772134..dfad72608ed 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -35,7 +35,7 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr if (skewed) { exMatrix = mat4.create(); s = 8 / Math.pow(2, painter.transform.zoom - params.z); - gamma_scale = 1 / Math.cos(tr.tilt / 180 * Math.PI); + gamma_scale = 1 / Math.cos(tr._pitch); } else { exMatrix = mat4.clone(painter.tile.exMatrix); s = painter.transform.altitude; @@ -51,7 +51,7 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr // calculate how much longer the real world distance is at the top of the screen // than at the middle of the screen. var topedgelength = Math.sqrt(tr.height * tr.height / 4 * (1 + tr.altitude * tr.altitude)); - var x = tr.height / 2 * Math.tan(tr.tilt / 180 * Math.PI); + var x = tr.height / 2 * Math.tan(tr._pitch); var extra = (topedgelength + x) / topedgelength - 1; var text = prefix === 'text'; @@ -74,7 +74,7 @@ function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSpr buffer = bucket.buffers.glyphVertex; texsize = [painter.glyphAtlas.width / 4, painter.glyphAtlas.height / 4]; } else { - imageSprite.bind(gl, alignedWithMap || params.rotating || params.zooming || fontScale != 1 || sdf || painter.transform.tilt); + imageSprite.bind(gl, alignedWithMap || params.rotating || params.zooming || fontScale != 1 || sdf || painter.transform.pitch); buffer = bucket.buffers.iconVertex; texsize = [imageSprite.img.width, imageSprite.img.height]; } diff --git a/js/ui/easings.js b/js/ui/easings.js index 0d5c352f168..4358ea520dd 100644 --- a/js/ui/easings.js +++ b/js/ui/easings.js @@ -193,7 +193,7 @@ util.extend(exports, { this.flyTo(center, zoom, 0, options); }, - easeTo(latlng, zoom, bearing, tilt, options) { + easeTo(latlng, zoom, bearing, pitch, options) { this.stop(); options = util.extend({ @@ -206,7 +206,7 @@ util.extend(exports, { offset = Point.convert(options.offset).rotate(-tr.angle), startZoom = this.getZoom(), startBearing = this.getBearing(), - startTilt = this.transform.tilt; + startPitch = this.transform.pitch; latlng = LatLng.convert(latlng); zoom = zoom === undefined ? startZoom : zoom; @@ -236,8 +236,8 @@ util.extend(exports, { tr.bearing = util.interp(startBearing, bearing, k); } - if (tilt !== startTilt) { - tr.tilt = util.interp(startTilt, tilt, k); + if (pitch !== startPitch) { + tr.pitch = util.interp(startPitch, pitch, k); } this.animationLoop.set(300); // text fading From fca31bc66db943266870ac6288e888fd551f3cc1 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 16:00:11 -0800 Subject: [PATCH 14/20] fix line icons aligned with map --- js/render/painter.js | 2 +- shaders/icon.vertex.glsl | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/js/render/painter.js b/js/render/painter.js index 9f8c8cad247..bbb23802b04 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -94,7 +94,7 @@ GLPainter.prototype.setup = function() { this.iconShader = gl.initializeShader('icon', ['a_pos', 'a_tex', 'a_offset', 'a_angle', 'a_minzoom', 'a_maxzoom', 'a_rangeend', 'a_rangestart', 'a_labelminzoom'], - ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_angle', 'u_zoom', 'u_flip', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom', 'u_opacity']); + ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_angle', 'u_zoom', 'u_flip', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom', 'u_opacity', 'u_skewed', 'u_extra']); this.outlineShader = gl.initializeShader('outline', ['a_pos'], diff --git a/shaders/icon.vertex.glsl b/shaders/icon.vertex.glsl index 8c69c404100..6c0350371bc 100644 --- a/shaders/icon.vertex.glsl +++ b/shaders/icon.vertex.glsl @@ -21,6 +21,8 @@ uniform float u_minfadezoom; uniform float u_maxfadezoom; uniform float u_fadezoom; uniform float u_opacity; +uniform bool u_skewed; +uniform float u_extra; uniform vec2 u_texsize; @@ -66,7 +68,15 @@ void main() { // hide if (angle >= a_rangeend && angle < rangestart) z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle)); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0); + if (u_skewed) { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0); + gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1); + gl_Position.z += z * gl_Position.w; + } else { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude; + } + v_tex = a_tex / u_texsize; v_alpha *= u_opacity; From d036aa8ea1f4eae34a08e847aecee12d3eba82ab Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 16:12:46 -0800 Subject: [PATCH 15/20] don't constantly resize painter --- js/render/painter.js | 3 --- js/source/tile.js | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/js/render/painter.js b/js/render/painter.js index bbb23802b04..6650c774136 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -38,10 +38,7 @@ function GLPainter(gl, transform) { */ GLPainter.prototype.resize = function(width, height) { var gl = this.gl; - // Initialize projection matrix - this.projectionMatrix = mat4.create(); - this.projectionMatrix = this.transform.getProjMatrix(); gl.viewport(0, 0, this.transform.width * window.devicePixelRatio, this.transform.height * window.devicePixelRatio); diff --git a/js/source/tile.js b/js/source/tile.js index a954c1fad72..607d4727323 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -15,8 +15,6 @@ Tile.prototype = { calculateMatrices(z, x, y, transform, painter) { - if (painter) painter.resize(); - // Initialize model-view matrix that converts from the tile coordinates // to screen coordinates. var tileScale = Math.pow(2, z); @@ -35,7 +33,7 @@ Tile.prototype = { mat4.invert(this.invPosMatrix, this.posMatrix); mat4.scale(this.posMatrix, this.posMatrix, [ scale / this.tileExtent, scale / this.tileExtent, 1 ]); - mat4.multiply(this.posMatrix, painter.projectionMatrix, this.posMatrix); + mat4.multiply(this.posMatrix, transform.getProjMatrix(), this.posMatrix); // The extrusion matrix. this.exMatrix = mat4.create(); From 32929f3e9aedfed78f02f05511c0dcc9c3448640 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 16:14:31 -0800 Subject: [PATCH 16/20] revert style changes --- debug/style.json | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/debug/style.json b/debug/style.json index 3bde0ee81db..3b505ca43a2 100644 --- a/debug/style.json +++ b/debug/style.json @@ -1174,10 +1174,7 @@ "text-font": "@sans", "text-field": "@name", "text-max-size": 13, - "symbol-min-distance": 300, - "text-padding": 40, - "symbol-placement": "line", - "text-rotation-alignment": "map" + "symbol-placement": "line" }, "paint": { "text-color": "#765", @@ -1224,9 +1221,8 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, + "text-padding": 2, "text-offset": [0, 0.6], - "text-padding": 20, - "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1249,9 +1245,8 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, + "text-padding": 2, "text-offset": [0, 0.6], - "text-padding": 20, - "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1274,9 +1269,8 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, + "text-padding": 2, "text-offset": [0, 0.6], - "text-padding": 20, - "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1299,9 +1293,8 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, + "text-padding": 2, "text-offset": [0, 0.6], - "text-padding": 20, - "icon-padding": 20, "text-anchor": "top" }, "paint": { @@ -1324,9 +1317,8 @@ "text-field": "@name", "text-max-size": 12, "text-max-width": 9, + "text-padding": 2, "text-offset": [0, 0.6], - "text-padding": 20, - "icon-padding": 20, "text-anchor": "top" }, "paint": { From d3975e5d95262f735dc23da647af295cb83c29f1 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 17:21:45 -0800 Subject: [PATCH 17/20] add get/setPitch() and fix related tests --- js/render/painter.js | 4 +--- js/source/tile.js | 2 +- js/ui/easings.js | 7 ++++--- js/ui/hash.js | 2 +- js/ui/map.js | 22 +++++++++++++++------- test/js/ui/easings.test.js | 28 ++++++++++++++-------------- 6 files changed, 36 insertions(+), 29 deletions(-) diff --git a/js/render/painter.js b/js/render/painter.js index b6ceaae2734..6314aa15c6d 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -31,11 +31,9 @@ function GLPainter(gl, transform) { GLPainter.prototype.resize = function(width, height) { var gl = this.gl; - gl.viewport(0, 0, this.transform.width * window.devicePixelRatio, - this.transform.height * window.devicePixelRatio); - this.width = width * browser.devicePixelRatio; this.height = height * browser.devicePixelRatio; + gl.viewport(0, 0, this.width, this.height); }; diff --git a/js/source/tile.js b/js/source/tile.js index 4c956707b9f..b972e4ab4ad 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -23,7 +23,7 @@ Tile.prototype = { // todo unhardcode tileExtent: 4096, - calculateMatrices: function(z, x, y, transform, painter) { + calculateMatrices: function(z, x, y, transform) { // Initialize model-view matrix that converts from the tile coordinates // to screen coordinates. diff --git a/js/ui/easings.js b/js/ui/easings.js index f6f0e357a08..fec6e3c4891 100644 --- a/js/ui/easings.js +++ b/js/ui/easings.js @@ -207,11 +207,12 @@ util.extend(exports, { offset = Point.convert(options.offset).rotate(-tr.angle), startZoom = this.getZoom(), startBearing = this.getBearing(), - startPitch = this.transform.pitch; + startPitch = this.getPitch(); latlng = LatLng.convert(latlng); zoom = zoom === undefined ? startZoom : zoom; bearing = bearing === undefined ? startBearing : this._normalizeBearing(bearing, startBearing); + pitch = pitch === undefined ? startPitch : pitch; var scale = tr.zoomScale(zoom - startZoom), from = tr.point, @@ -238,7 +239,7 @@ util.extend(exports, { } if (pitch !== startPitch) { - tr.pitch = util.interp(startPitch, pitch, k); + tr.pitch = interpolate(startPitch, pitch, k); } this.animationLoop.set(300); // text fading @@ -277,7 +278,7 @@ util.extend(exports, { to = tr.project(latlng).sub(offset.div(scale)); if (options.animate === false) { - return this.setView(latlng, zoom, bearing); + return this.setView(latlng, zoom, bearing, this.getPitch()); } var startWorldSize = tr.worldSize, diff --git a/js/ui/hash.js b/js/ui/hash.js index 28e537fbd78..ff35e02b7d4 100644 --- a/js/ui/hash.js +++ b/js/ui/hash.js @@ -29,7 +29,7 @@ Hash.prototype = { _onHashChange: function() { var loc = location.hash.replace('#', '').split('/'); if (loc.length >= 3) { - this._map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0)); + this._map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0), this._map.getPitch()); return true; } return false; diff --git a/js/ui/map.js b/js/ui/map.js index 8f0295dc731..9b83c73975a 100644 --- a/js/ui/map.js +++ b/js/ui/map.js @@ -53,7 +53,7 @@ var Map = module.exports = function(options) { this._hash = options.hash && (new Hash()).addTo(this); // don't set position from options if set through hash if (!this._hash || !this._hash._onHashChange()) { - this.setView(options.center, options.zoom, options.bearing); + this.setView(options.center, options.zoom, options.bearing, options.pitch); } this.sources = {}; @@ -75,6 +75,7 @@ util.extend(Map.prototype, { center: [0, 0], zoom: 0, bearing: 0, + pitch: 0, minZoom: 0, maxZoom: 20, @@ -91,38 +92,45 @@ util.extend(Map.prototype, { }, // Set the map's center, zoom, and bearing - setView: function(center, zoom, bearing) { + setView: function(center, zoom, bearing, pitch) { this.stop(); var tr = this.transform, zoomChanged = tr.zoom !== +zoom, - bearingChanged = tr.bearing !== +bearing; + bearingChanged = tr.bearing !== +bearing, + pitchChanged = tr.pitch !== +pitch; tr.center = LatLng.convert(center); tr.zoom = +zoom; tr.bearing = +bearing; + tr.pitch = +pitch; return this .fire('movestart') - ._move(zoomChanged, bearingChanged) + ._move(zoomChanged, bearingChanged, pitchChanged) .fire('moveend'); }, setCenter: function(center) { - this.setView(center, this.getZoom(), this.getBearing()); + this.setView(center, this.getZoom(), this.getBearing(), this.getPitch()); }, setZoom: function(zoom) { - this.setView(this.getCenter(), zoom, this.getBearing()); + this.setView(this.getCenter(), zoom, this.getBearing(), this.getPitch()); }, setBearing: function(bearing) { - this.setView(this.getCenter(), this.getZoom(), bearing); + this.setView(this.getCenter(), this.getZoom(), bearing, this.getPitch()); + }, + + setPitch: function(pitch) { + this.setView(this.getCenter(), this.getZoom(), this.getBearing(), pitch); }, getCenter: function() { return this.transform.center; }, getZoom: function() { return this.transform.zoom; }, getBearing: function() { return this.transform.bearing; }, + getPitch: function() { return this.transform.pitch; }, addClass: function(klass, options) { if (this._classes[klass]) return; diff --git a/test/js/ui/easings.test.js b/test/js/ui/easings.test.js index 6fbe022b7c3..1cce7c50b80 100644 --- a/test/js/ui/easings.test.js +++ b/test/js/ui/easings.test.js @@ -260,28 +260,28 @@ test('Map', function(t) { t.test('#easeTo', function(t) { t.test('pans to specified location', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, undefined, { duration: 0 }); + map.easeTo([0, 100], undefined, undefined, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.end(); }); t.test('zooms to specified level', function(t) { var map = createMap(); - map.easeTo(undefined, 3.2, undefined, { duration: 0 }); + map.easeTo(undefined, 3.2, undefined, undefined, { duration: 0 }); t.equal(map.getZoom(), 3.2); t.end(); }); t.test('rotates to specified bearing', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, 90, { duration: 0 }); + map.easeTo(undefined, undefined, 90, undefined, { duration: 0 }); t.equal(map.getBearing(), 90); t.end(); }); t.test('pans and zooms', function(t) { var map = createMap(); - map.easeTo([0, 100], 3.2, undefined, { duration: 0 }); + map.easeTo([0, 100], 3.2, undefined, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.equal(map.getZoom(), 3.2); t.end(); @@ -289,7 +289,7 @@ test('Map', function(t) { t.test('pans and rotates', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, 90, { duration: 0 }); + map.easeTo([0, 100], undefined, 90, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.equal(map.getBearing(), 90); t.end(); @@ -297,7 +297,7 @@ test('Map', function(t) { t.test('zooms and rotates', function(t) { var map = createMap(); - map.easeTo(undefined, 3.2, 90, { duration: 0 }); + map.easeTo(undefined, 3.2, 90, undefined, { duration: 0 }); t.equal(map.getZoom(), 3.2); t.equal(map.getBearing(), 90); t.end(); @@ -305,7 +305,7 @@ test('Map', function(t) { t.test('pans, zooms, and rotates', function(t) { var map = createMap(); - map.easeTo([0, 100], 3.2, 90, { duration: 0 }); + map.easeTo([0, 100], 3.2, 90, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.equal(map.getZoom(), 3.2); t.equal(map.getBearing(), 90); @@ -314,7 +314,7 @@ test('Map', function(t) { t.test('noop', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, undefined, { duration: 0 }); + map.easeTo(undefined, undefined, undefined, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 0 }); t.equal(map.getZoom(), 0); t.equal(map.getBearing(), 0); @@ -323,7 +323,7 @@ test('Map', function(t) { t.test('noop with offset', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo(undefined, undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 0 }); t.equal(map.getZoom(), 0); t.equal(map.getBearing(), 0); @@ -332,14 +332,14 @@ test('Map', function(t) { t.test('pans with specified offset', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo([0, 100], undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 29.6875 }); t.end(); }); t.test('pans with specified offset relative to viewport on a rotated map', function(t) { var map = createMap({bearing: 180}); - map.easeTo([0, 100], undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo([0, 100], undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 170.3125 }); t.end(); }); @@ -375,13 +375,13 @@ test('Map', function(t) { t.end(); }); - map.easeTo([0, 100], 3.2, 90, { duration: 0 }); + map.easeTo([0, 100], 3.2, 90, undefined, { duration: 0 }); }); t.test('stops existing ease', function(t) { var map = createMap(); - map.easeTo([0, 200], undefined, undefined, { duration: 100 }); - map.easeTo([0, 100], undefined, undefined, { duration: 0 }); + map.easeTo([0, 200], undefined, undefined, undefined, { duration: 100 }); + map.easeTo([0, 100], undefined, undefined, undefined, { duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.end(); }); From e5ee155af3154aacbbb8f54dd700ff5767b9719d Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Mar 2015 17:46:23 -0800 Subject: [PATCH 18/20] fix tile pyramid tests Since projection is more complex with support for perspective views, pointCoordinate is less precise. The tests were failing because it expected exactly `row: 1` and got `row: 1.0000000000000069`. This loaded an extra tile in the tests, which is acceptable for cases the tile edge is right at the edge of the screen. --- js/geo/transform.js | 6 ++---- test/js/source/tile_pyramid.test.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 6beae784739..871f8a42d22 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -24,8 +24,6 @@ function Transform(minZoom, maxZoom) { this.angle = 0; this._altitude = 1.5; this._pitch = 0; - - this.pitch = 55; } Transform.prototype = { @@ -177,8 +175,8 @@ Transform.prototype = { // need to know the projected z value. We only know x, y. // Terrible temporary hack to avoid division by 0 - if (p.x === 0) p.x = -1; - if (p.y === 0) p.y = -1; + if (p.x === 0) p.x = 1; + if (p.y === 0) p.y = 1; var f1 = m[0] / m[1]; var g1 = p.x - f1 * p.y; diff --git a/test/js/source/tile_pyramid.test.js b/test/js/source/tile_pyramid.test.js index 089cf836318..1cbfc200172 100644 --- a/test/js/source/tile_pyramid.test.js +++ b/test/js/source/tile_pyramid.test.js @@ -245,8 +245,8 @@ test('TilePyramid#update', function(t) { t.test('loads covering tiles', function(t) { var transform = new Transform(); - transform.width = 512; - transform.height = 512; + transform.width = 511; + transform.height = 511; transform.zoom = 0; var pyramid = createPyramid({}); @@ -258,8 +258,8 @@ test('TilePyramid#update', function(t) { t.test('removes unused tiles', function(t) { var transform = new Transform(); - transform.width = 512; - transform.height = 512; + transform.width = 511; + transform.height = 511; transform.zoom = 0; var pyramid = createPyramid({ @@ -285,8 +285,8 @@ test('TilePyramid#update', function(t) { t.test('retains parent tiles for pending children', function(t) { var transform = new Transform(); - transform.width = 512; - transform.height = 512; + transform.width = 511; + transform.height = 511; transform.zoom = 0; var pyramid = createPyramid({ @@ -313,8 +313,8 @@ test('TilePyramid#update', function(t) { t.test('retains parent tiles for pending children (wrapped)', function(t) { var transform = new Transform(); - transform.width = 512; - transform.height = 512; + transform.width = 511; + transform.height = 511; transform.zoom = 0; transform.center = new LatLng(0, 360); From 9d3efcd7bf1d798c3be5a97779d54510c9d6328c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 4 Mar 2015 12:19:24 -0800 Subject: [PATCH 19/20] fix tiny rendering differences caused by altitude --- js/geo/transform.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 871f8a42d22..56f0feb174d 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -294,7 +294,14 @@ Transform.prototype = { getProjMatrix: function() { var m = new Float64Array(16); mat4.perspective(m, 2 * Math.atan((this.height / 2) / this.altitude), this.width / this.height, 0, this.altitude + 1); - mat4.translate(m, m, [0, 0, -this.altitude]); + + // Subtracting by one z pixel here is weird. I'm not sure exactly what is going on, + // but this fixes tiny rendering differences between top-down (pitch=0) images rendered + // rendered with different altitude values. Without this, images with smaller altitude appear + // a tiny bit more zoomed. The difference is almost imperceptible, but it affects rendering tests. + var onePixelZ = 1 / this.height; + + mat4.translate(m, m, [0, 0, -this.altitude - onePixelZ]); // After the rotateX, z values are in pixel units. Convert them to // altitude unites. 1 altitude unit = the screen height. From 98d14077ce08b393744dbb96878ae0874e98fd46 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 4 Mar 2015 12:27:29 -0800 Subject: [PATCH 20/20] fix dashed and pattern line rendering --- shaders/line.vertex.glsl | 3 +-- shaders/linepattern.vertex.glsl | 2 +- shaders/linesdfpattern.vertex.glsl | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/shaders/line.vertex.glsl b/shaders/line.vertex.glsl index d4369c478cb..fc7fc7f771e 100644 --- a/shaders/line.vertex.glsl +++ b/shaders/line.vertex.glsl @@ -55,6 +55,5 @@ void main() { // how much features are squished in all directions by the perspectiveness float perspective_scale = 1.0 / (1.0 - y * u_extra); - // the 1.1 adds a tiny bit of extra blurriness to account for the inexactness of these calculations - gamma_scale = perspective_scale * squish_scale * 1.1; + gamma_scale = perspective_scale * squish_scale; } diff --git a/shaders/linepattern.vertex.glsl b/shaders/linepattern.vertex.glsl index 987f2e8ce6c..65aed9f26c2 100644 --- a/shaders/linepattern.vertex.glsl +++ b/shaders/linepattern.vertex.glsl @@ -43,7 +43,7 @@ void main() { // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0) + u_exmatrix * vec4(dist, 0.0, 0.0); + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist.xy / u_ratio, 0.0, 1.0); v_linesofar = a_linesofar;// * u_ratio; } diff --git a/shaders/linesdfpattern.vertex.glsl b/shaders/linesdfpattern.vertex.glsl index 3c1155f61d8..9da42801b4e 100644 --- a/shaders/linesdfpattern.vertex.glsl +++ b/shaders/linesdfpattern.vertex.glsl @@ -45,7 +45,7 @@ void main() { // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist; + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist.xy / u_ratio, 0.0, 1.0); v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b);