Skip to content

Commit

Permalink
Refactor draw_line
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas Wojciechowski committed Aug 18, 2016
1 parent e9ac1e5 commit a6dd2ae
Showing 1 changed file with 124 additions and 152 deletions.
276 changes: 124 additions & 152 deletions js/render/draw_line.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,185 +4,157 @@ var browser = require('../util/browser');
var mat2 = require('gl-matrix').mat2;
var pixelsToTileUnits = require('../source/pixels_to_tile_units');

/**
* Draw a line. Under the hood this will read elements from
* a tile, dash textures from a lineAtlas, and style properties from a layer.
* @param {Object} painter
* @param {Object} layer
* @param {Object} posMatrix
* @param {Tile} tile
* @returns {undefined} draws with the painter
* @private
*/
module.exports = function drawLine(painter, source, layer, coords) {
module.exports = function (painter, source, layer, coords) {
if (painter.isOpaquePass) return;
painter.setDepthSublayer(0);
painter.depthMask(false);

var gl = painter.gl;
gl.enable(gl.STENCIL_TEST);

// don't draw zero-width lines
if (layer.paint['line-width'] <= 0) return;

for (var k = 0; k < coords.length; k++) {
drawLineTile(painter, source, layer, coords[k]);
if (layer.paint['line-dasharray']) {
drawDasharrayLines(painter, source, layer, coords);
} else if (layer.paint['line-pattern']) {
drawPatternLines(painter, source, layer, coords);
} else {
drawSolidLines(painter, source, layer, coords);
}

};

function drawLineTile(painter, source, layer, coord) {
var tile = source.getTile(coord);
var bucket = tile.getBucket(layer);
if (!bucket) return;
var bufferGroups = bucket.bufferGroups.line;
if (!bufferGroups) return;

function drawDasharrayLines(painter, source, layer, coords) {
var gl = painter.gl;

var programOptions = bucket.paintAttributes.line[layer.id];

// the distance over which the line edge fades out.
// Retina devices need a smaller distance to avoid aliasing.
var antialiasing = 1 / browser.devicePixelRatio;

var blur = layer.paint['line-blur'] + antialiasing;
var color = layer.paint['line-color'];

var tr = painter.transform;

var antialiasingMatrix = mat2.create();
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._pitch);
var extra = (topedgelength + x) / topedgelength - 1;
var ratio = 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom);

var dasharray = layer.paint['line-dasharray'];
var image = layer.paint['line-pattern'];

var program;
if (dasharray) {

var posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round');
var posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round');

program = painter.useProgram(
'linesdfpattern',
programOptions.defines,
programOptions.vertexPragmas,
programOptions.fragmentPragmas,
function(program) {
gl.uniform1f(program.u_antialiasing, antialiasing / 2);
gl.uniform1f(program.u_blur, blur);
gl.uniform1f(program.u_extra, extra);
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_mix, dasharray.t);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform1f(program.u_tex_y_a, posA.y);
gl.uniform1f(program.u_tex_y_b, posB.y);
gl.uniform4fv(program.u_color, color);
gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, antialiasingMatrix);

gl.uniform1i(program.u_image, 0);
gl.activeTexture(gl.TEXTURE0);

painter.lineAtlas.bind(gl);
}
);

var posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round');
var posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round');

drawLines(painter, source, layer, coords, 'linesdfpattern', function (program) {
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_mix, dasharray.t);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform1f(program.u_tex_y_a, posA.y);
gl.uniform1f(program.u_tex_y_b, posB.y);
gl.uniform4fv(program.u_color, layer.paint['line-color']);

gl.uniform1i(program.u_image, 0);
gl.activeTexture(gl.TEXTURE0);
painter.lineAtlas.bind(gl);

}, function(program, tile) {
var widthA = posA.width * dasharray.fromScale;
var widthB = posB.width * dasharray.toScale;
var scaleA = [1 / pixelsToTileUnits(tile, widthA, painter.transform.tileZoom), -posA.height / 2];
var scaleB = [1 / pixelsToTileUnits(tile, widthB, painter.transform.tileZoom), -posB.height / 2];
var gamma = painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio) / 2;
var gamma = painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio * 2);

gl.uniform1f(program.u_ratio, ratio);
gl.uniform2fv(program.u_patternscale_a, scaleA);
gl.uniform2fv(program.u_patternscale_b, scaleB);
gl.uniform2fv(program.u_patternscale_a, [
1 / pixelsToTileUnits(tile, widthA, painter.transform.tileZoom),
-posA.height / 2
]);
gl.uniform2fv(program.u_patternscale_b, [
1 / pixelsToTileUnits(tile, widthB, painter.transform.tileZoom),
-posB.height / 2
]);
gl.uniform1f(program.u_sdfgamma, gamma);
});
}

} else if (image) {
var imagePosA = painter.spriteAtlas.getPosition(image.from, true);
var imagePosB = painter.spriteAtlas.getPosition(image.to, true);
if (!imagePosA || !imagePosB) return;

program = painter.useProgram(
'linepattern',
programOptions.defines,
programOptions.vertexPragmas,
programOptions.fragmentPragmas,
function(program) {
gl.uniform1f(program.u_antialiasing, antialiasing / 2);
gl.uniform1f(program.u_blur, blur);
gl.uniform1f(program.u_extra, extra);
gl.uniform1f(program.u_fade, image.t);
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform2fv(program.u_pattern_br_a, imagePosA.br);
gl.uniform2fv(program.u_pattern_br_b, imagePosB.br);
gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl);
gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl);
gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, antialiasingMatrix);

gl.uniform1i(program.u_image, 0);
gl.activeTexture(gl.TEXTURE0);

painter.spriteAtlas.bind(gl, true);
}
);

gl.uniform1f(program.u_ratio, ratio);
function drawPatternLines(painter, source, layer, coords) {
var gl = painter.gl;
var imagePosA = painter.spriteAtlas.getPosition(layer.paint['line-pattern'].from, true);
var imagePosB = painter.spriteAtlas.getPosition(layer.paint['line-pattern'].to, true);
if (!imagePosA || !imagePosB) return;

drawLines(painter, source, layer, coords, 'linepattern', function(program) {
gl.uniform1f(program.u_fade, layer.paint['line-pattern'].t);
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform2fv(program.u_pattern_br_a, imagePosA.br);
gl.uniform2fv(program.u_pattern_br_b, imagePosB.br);
gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl);
gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl);

gl.uniform1i(program.u_image, 0);
gl.activeTexture(gl.TEXTURE0);

painter.spriteAtlas.bind(gl, true);

}, function(program, tile) {
gl.uniform2fv(program.u_pattern_size_a, [
pixelsToTileUnits(tile, imagePosA.size[0] * image.fromScale, painter.transform.tileZoom),
pixelsToTileUnits(tile, imagePosA.size[0] * layer.paint['line-pattern'].fromScale, painter.transform.tileZoom),
imagePosB.size[1]
]);
gl.uniform2fv(program.u_pattern_size_b, [
pixelsToTileUnits(tile, imagePosB.size[0] * image.toScale, painter.transform.tileZoom),
pixelsToTileUnits(tile, imagePosB.size[0] * layer.paint['line-pattern'].toScale, painter.transform.tileZoom),
imagePosB.size[1]
]);
});
}

} else {
program = painter.useProgram(
'line',
function drawSolidLines(painter, source, layer, coords) {
var gl = painter.gl;

drawLines(painter, source, layer, coords, 'line', function onChange(program) {
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform4fv(program.u_color, layer.paint['line-color']);
});
}

function drawLines(painter, source, layer, coords, programName, onProgramChange, onTileChange) {
var gl = painter.gl;

gl.enable(gl.STENCIL_TEST);
painter.setDepthSublayer(0);
painter.depthMask(false);

var antialiasingMatrix = mat2.create();
mat2.scale(antialiasingMatrix, antialiasingMatrix, [1, Math.cos(painter.transform._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(painter.transform.height * painter.transform.height / 4 * (1 + painter.transform.altitude * painter.transform.altitude));
var extra = (topedgelength + (painter.transform.height / 2 * Math.tan(painter.transform._pitch))) / topedgelength - 1;

function _onProgramChange(program) {
gl.uniform1f(program.u_extra, extra);
gl.uniform1f(program.u_antialiasing, 1 / browser.devicePixelRatio / 2);
gl.uniform1f(program.u_blur, layer.paint['line-blur'] + 1 / browser.devicePixelRatio);
gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, antialiasingMatrix);
onProgramChange(program);
}

for (var j = 0; j < coords.length; j++) {
var coord = coords[j];
var tile = source.getTile(coord);
var bucket = tile.getBucket(layer);
if (!bucket) return;
var groups = bucket.bufferGroups.line;
if (!groups) return;

var programOptions = bucket.paintAttributes.line[layer.id];
var program = painter.useProgram(
programName,
programOptions.defines,
programOptions.vertexPragmas,
programOptions.fragmentPragmas,
function(program) {
gl.uniform1f(program.u_antialiasing, antialiasing / 2);
gl.uniform1f(program.u_blur, blur);
gl.uniform1f(program.u_extra, extra);
gl.uniform1f(program.u_gapwidth, layer.paint['line-gap-width'] / 2);
gl.uniform1f(program.u_linewidth, layer.paint['line-width'] / 2);
gl.uniform1f(program.u_offset, -layer.paint['line-offset']);
gl.uniform1f(program.u_opacity, layer.paint['line-opacity']);
gl.uniform4fv(program.u_color, color);
gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, antialiasingMatrix);
}
_onProgramChange
);

gl.uniform1f(program.u_ratio, ratio);
}
var posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, layer.paint['line-translate'], layer.paint['line-translate-anchor']);
gl.uniformMatrix4fv(program.u_matrix, false, posMatrix);
gl.uniform1f(program.u_ratio, 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom));
painter.enableTileClippingMask(coord);
bucket.setUniforms(gl, 'line', program, layer, {zoom: painter.transform.zoom});

painter.enableTileClippingMask(coord);
if (onTileChange) onTileChange(program, tile);

// set uniforms that are different for each tile
var posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, layer.paint['line-translate'], layer.paint['line-translate-anchor']);
gl.uniformMatrix4fv(program.u_matrix, false, posMatrix);

bucket.setUniforms(gl, 'line', program, layer, {zoom: painter.transform.zoom});

for (var i = 0; i < bufferGroups.length; i++) {
var group = bufferGroups[i];
group.vaos[layer.id].bind(gl, program, group.layoutVertexBuffer, group.elementBuffer, group.paintVertexBuffers[layer.id]);
gl.drawElements(gl.TRIANGLES, group.elementBuffer.length * 3, gl.UNSIGNED_SHORT, 0);
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
group.vaos[layer.id].bind(gl, program, group.layoutVertexBuffer, group.elementBuffer, group.paintVertexBuffers[layer.id]);
gl.drawElements(gl.TRIANGLES, group.elementBuffer.length * 3, gl.UNSIGNED_SHORT, 0);
}
}

}

0 comments on commit a6dd2ae

Please sign in to comment.