UNPKG

mapbox-gl

Version:
174 lines (135 loc) 6.98 kB
'use strict'; var browser = require('../util/browser'); var mat3 = require('gl-matrix').mat3; module.exports = drawFill; function drawFill(painter, layer, posMatrix, tile) { // No data if (!tile.buffers) return; var elementGroups = tile.elementGroups[layer.ref || layer.id]; if (!elementGroups) return; var gl = painter.gl; var translatedPosMatrix = painter.translateMatrix(posMatrix, tile, layer.paint['fill-translate'], layer.paint['fill-translate-anchor']); var color = layer.paint['fill-color']; var vertex, elements, group, count; // Draw the stencil mask. // We're only drawing to the first seven bits (== support a maximum of // 127 overlapping polygons in one place before we get rendering errors). gl.stencilMask(0x3F); gl.clear(gl.STENCIL_BUFFER_BIT); // Draw front facing triangles. Wherever the 0x80 bit is 1, we are // increasing the lower 7 bits by one if the triangle is a front-facing // triangle. This means that all visible polygons should be in CCW // orientation, while all holes (see below) are in CW orientation. gl.stencilFunc(gl.NOTEQUAL, 0x80, 0x80); // When we do a nonzero fill, we count the number of times a pixel is // covered by a counterclockwise polygon, and subtract the number of // times it is "uncovered" by a clockwise polygon. gl.stencilOpSeparate(gl.FRONT, gl.INCR_WRAP, gl.KEEP, gl.KEEP); gl.stencilOpSeparate(gl.BACK, gl.DECR_WRAP, gl.KEEP, gl.KEEP); // When drawing a shape, we first draw all shapes to the stencil buffer // and incrementing all areas where polygons are gl.colorMask(false, false, false, false); // Draw the actual triangle fan into the stencil buffer. gl.switchShader(painter.fillShader, translatedPosMatrix); // Draw all buffers vertex = tile.buffers.fillVertex; vertex.bind(gl); elements = tile.buffers.fillElement; elements.bind(gl); var offset, elementOffset; gl.disableVertexAttribArray(painter.fillShader.a_color); for (var i = 0; i < elementGroups.groups.length; i++) { group = elementGroups.groups[i]; offset = group.vertexStartIndex * vertex.itemSize; gl.vertexAttribPointer(painter.fillShader.a_pos, 2, gl.SHORT, false, 4, offset + 0); count = group.elementLength * 3; elementOffset = group.elementStartIndex * elements.itemSize; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); } // Now that we have the stencil mask in the stencil buffer, we can start // writing to the color buffer. gl.colorMask(true, true, true, true); // From now on, we don't want to update the stencil buffer anymore. gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.stencilMask(0x0); var strokeColor = layer.paint['fill-outline-color']; // Because we're drawing top-to-bottom, and we update the stencil mask // below, we have to draw the outline first (!) if (layer.paint['fill-antialias'] === true && !(layer.paint['fill-image'] && !strokeColor)) { gl.switchShader(painter.outlineShader, translatedPosMatrix); gl.lineWidth(2 * browser.devicePixelRatio); if (strokeColor) { // If we defined a different color for the fill outline, we are // going to ignore the bits in 0x3F and just care about the global // clipping mask. gl.stencilFunc(gl.EQUAL, 0x80, 0x80); } else { // Otherwise, we only want to draw the antialiased parts that are // *outside* the current shape. This is important in case the fill // or stroke color is translucent. If we wouldn't clip to outside // the current shape, some pixels from the outline stroke overlapped // the (non-antialiased) fill. gl.stencilFunc(gl.EQUAL, 0x80, 0xBF); } gl.uniform2f(painter.outlineShader.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight); // Draw all buffers vertex = tile.buffers.fillVertex; elements = tile.buffers.outlineElement; elements.bind(gl); gl.disableVertexAttribArray(painter.outlineShader.a_color); gl.vertexAttrib4fv(painter.outlineShader.a_color, strokeColor ? strokeColor : color); for (var k = 0; k < elementGroups.groups.length; k++) { group = elementGroups.groups[k]; offset = group.vertexStartIndex * vertex.itemSize; gl.vertexAttribPointer(painter.outlineShader.a_pos, 2, gl.SHORT, false, 4, offset + 0); count = group.secondElementLength * 2; elementOffset = group.secondElementStartIndex * elements.itemSize; gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, elementOffset); } } var image = layer.paint['fill-image']; var opacity = layer.paint['fill-opacity'] || 1; var shader; if (image) { // Draw texture fill var imagePosA = painter.spriteAtlas.getPosition(image.from, true); var imagePosB = painter.spriteAtlas.getPosition(image.to, true); if (!imagePosA || !imagePosB) return; shader = painter.patternShader; gl.switchShader(shader, posMatrix); gl.uniform1i(shader.u_image, 0); gl.uniform2fv(shader.u_pattern_tl_a, imagePosA.tl); gl.uniform2fv(shader.u_pattern_br_a, imagePosA.br); gl.uniform2fv(shader.u_pattern_tl_b, imagePosB.tl); gl.uniform2fv(shader.u_pattern_br_b, imagePosB.br); gl.uniform1f(shader.u_opacity, opacity); gl.uniform1f(shader.u_mix, image.t); var factor = (tile.tileExtent / tile.tileSize) / Math.pow(2, painter.transform.tileZoom - tile.coord.z); var matrixA = mat3.create(); mat3.scale(matrixA, matrixA, [ 1 / (imagePosA.size[0] * factor * image.fromScale), 1 / (imagePosA.size[1] * factor * image.fromScale) ]); var matrixB = mat3.create(); mat3.scale(matrixB, matrixB, [ 1 / (imagePosB.size[0] * factor * image.toScale), 1 / (imagePosB.size[1] * factor * image.toScale) ]); gl.uniformMatrix3fv(shader.u_patternmatrix_a, false, matrixA); gl.uniformMatrix3fv(shader.u_patternmatrix_b, false, matrixB); painter.spriteAtlas.bind(gl, true); } else { // Draw filling rectangle. shader = painter.fillShader; gl.switchShader(shader, posMatrix); gl.disableVertexAttribArray(shader.a_color); gl.vertexAttrib4fv(shader.a_color, color); } // Only draw regions that we marked gl.stencilFunc(gl.NOTEQUAL, 0x0, 0x3F); gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer); gl.vertexAttribPointer(shader.a_pos, painter.tileExtentBuffer.itemSize, gl.SHORT, false, 0, 0); gl.drawArrays(gl.TRIANGLE_STRIP, 0, painter.tileExtentBuffer.itemCount); gl.stencilMask(0x00); gl.stencilFunc(gl.EQUAL, 0x80, 0x80); }