Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] Circles support #1241

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
263d57c
Stub circles support
scothis Jun 2, 2015
65bf3e7
Merge branch 'master' into v8-circles
tmcw Jun 2, 2015
77acaed
Add circle shaders
tmcw Jun 2, 2015
1a8e6c1
Add two triangles in the vertex buffer for each circle
tmcw Jun 3, 2015
5d68936
First steps toward drawing circles
tmcw Jun 3, 2015
21940e7
Add circlevertexbuffer and bind more data
tmcw Jun 3, 2015
f33f46a
Bind element and vertex buffer
tmcw Jun 3, 2015
66b70bc
Increment vertex and element counters
tmcw Jun 3, 2015
39b68fb
Tweak shaders: still whitescreen
tmcw Jun 3, 2015
f1fdc08
Try pointing to a_extrude. #whitescreen
tmcw Jun 4, 2015
6b9edfb
BLACK BROKEN TRIANGLE OF SUCCESS
tmcw Jun 4, 2015
2182d9e
Circle: needs placement
tmcw Jun 4, 2015
7298a89
Induce loud failure
tmcw Jun 4, 2015
cad4153
Prettier geometry access
tmcw Jun 4, 2015
24d7e0c
Fix offset value
tmcw Jun 4, 2015
6c44402
Getting closer: v_centerpoint is not being transferred
tmcw Jun 4, 2015
2692288
Implement circle blurring, hitting style spec in a second.
tmcw Jun 4, 2015
8a4670d
Pack more data into buffers, use axis-aligned boxes
tmcw Jun 4, 2015
81a9ef5
Iterate through geometries
tmcw Jun 4, 2015
b2b74f0
fix circle size
ansis Jun 8, 2015
d333d4c
Simplify derivation of v_extrude
tmcw Jun 13, 2015
498ac8e
Implement faux-antialiasing with blur
tmcw Jun 15, 2015
6d9ddef
Simplify blur calculation
tmcw Jun 15, 2015
8988dbc
Fix tile-edge clipping
tmcw Jun 15, 2015
955b5c6
Add updated debug page
tmcw Jun 15, 2015
1e202c2
Compensate for screen resolution differences when calculating min blur
tmcw Jun 16, 2015
947496e
Revert debugs to master versions
tmcw Jun 16, 2015
902427c
Update test to nan branch to get 0.12 compatibility
tmcw Jun 17, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions debug/blank_v8.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"version": 8,
"name": "rivers",
"constants": { },
"sources": { },
"layers": [ ]
}
1 change: 1 addition & 0 deletions debug/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
<script src='site.js'></script>
</body>
</html>

24 changes: 24 additions & 0 deletions debug/index_circle.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link rel='stylesheet' href='../dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
#size { position:absolute;top:20px;left:20px; }
#blur { position:absolute;top:40px;left:20px; }
</style>
</head>

<body>
<div id='map'></div>
<input type=range id=size min=0 max=200 />
<input type=range id=blur min=0 max=1 step=0.01 />
<script src='mapbox-gl.js'></script>
<script src='site.js'></script>
</body>
</html>
1 change: 1 addition & 0 deletions debug/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ function getAccessToken() {

return accessToken;
}

70 changes: 70 additions & 0 deletions debug/site_circle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

mapboxgl.accessToken = getAccessToken();

var map = new mapboxgl.Map({
container: 'map',
zoom: 12.5,
center: [-77.066104, 38.910203].reverse(),
style: "blank_v8.json",
hash: true
});

map.addControl(new mapboxgl.Navigation());

map.on('style.load', function() {
map.addSource('geojson-point', {
"type": "geojson",
"data": {
type: 'Point',
coordinates: [-77.066104, 38.910203]
}
});
map.addLayer({
"id": "point-example",
"type": "circle",
"source": "geojson-point",
"paint": {
"circle-radius": 100,
"circle-color": '#f00',
"circle-blur": 0
}
}, 'point_circle');
});

map.on('click', function(e) {
(new mapboxgl.Popup())
.setLatLng(map.unproject(e.point))
.setHTML("<h1>Hello World!</h1>")
.addTo(map);
});

document.getElementById('size').onchange = function() {
map.setPaintProperty('point-example', 'circle-radius', this.value);
};

document.getElementById('blur').onchange = function() {
map.setPaintProperty('point-example', 'circle-blur', this.value);
};

// keyboard shortcut for comparing rendering with Mapbox GL native
document.onkeypress = function(e) {
if (e.charCode === 111 && !e.shiftKey && !e.metaKey && !e.altKey) {
var center = map.getCenter();
location.href = "mapboxgl://?center=" + center.lat + "," + center.lng + "&zoom=" + map.getZoom() + "&bearing=" + map.getBearing();
return false;
}
};

function getAccessToken() {
var match = location.search.match(/access_token=([^&\/]*)/);
var accessToken = match && match[1];

if (accessToken) {
localStorage.accessToken = accessToken;
} else {
accessToken = localStorage.accessToken;
}

return accessToken;
}

4 changes: 4 additions & 0 deletions js/data/buffer/buffer_set.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ var GlyphVertexBuffer = require('./glyph_vertex_buffer');
var GlyphElementBuffer = require('./triangle_element_buffer');
var IconVertexBuffer = require('./icon_vertex_buffer');
var IconElementBuffer = require('./triangle_element_buffer');
var CircleVertexBuffer = require('./circle_vertex_buffer');
var CircleElementBuffer = require('./triangle_element_buffer');
var CollisionBoxVertexBuffer = require('./collision_box_vertex_buffer');

module.exports = function(bufferset) {
Expand All @@ -18,6 +20,8 @@ module.exports = function(bufferset) {
glyphElement: new GlyphElementBuffer(bufferset.glyphElement),
iconVertex: new IconVertexBuffer(bufferset.iconVertex),
iconElement: new IconElementBuffer(bufferset.iconElement),
circleVertex: new CircleVertexBuffer(bufferset.circleVertex),
circleElement: new CircleElementBuffer(bufferset.circleElement),
fillVertex: new FillVertexBuffer(bufferset.fillVertex),
fillElement: new FillElementBuffer(bufferset.fillElement),
outlineElement: new OutlineElementBuffer(bufferset.outlineElement),
Expand Down
41 changes: 41 additions & 0 deletions js/data/buffer/circle_vertex_buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';

var util = require('../../util/util');
var Buffer = require('./buffer');

module.exports = CircleVertexBuffer;

/**
* This contains the data that displays circle markers on the map,
* including their centerpoint
*/
function CircleVertexBuffer(buffer) {
Buffer.call(this, buffer);
}

CircleVertexBuffer.prototype = util.inherit(Buffer, {
defaultLength: 2048 * 16,

itemSize: 4, // 2 bytes per short * 4 of them

add: function(x, y, extrudeX, extrudeY) {
var pos = this.pos,
pos2 = pos / 2;

this.resize();

// pack the extrusion of -1 or 1 into one short
this.shorts[pos2 + 0] = (x * 2) + ((extrudeX + 1) / 2);
this.shorts[pos2 + 1] = (y * 2) + ((extrudeY + 1) / 2);

this.pos += this.itemSize;
},

bind: function(gl, shader, offset) {
Buffer.prototype.bind.call(this, gl);

gl.vertexAttribPointer(shader.a_pos, 2,
gl.SHORT, false,
this.itemSize, offset + 0);
}
});
56 changes: 56 additions & 0 deletions js/data/circle_bucket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

var ElementGroups = require('./element_groups');

module.exports = CircleBucket;

/**
* A container for all circle data
*
* Circles are represented by two triangles.
*
* Each corner has a pos that is the center of the circle and an extrusion
* vector that is where it points.
*/
function CircleBucket(buffers) {
this.buffers = buffers;
this.elementGroups = new ElementGroups(
buffers.circleVertex,
buffers.circleElement);
}

CircleBucket.prototype.addFeatures = function() {
for (var i = 0; i < this.features.length; i++) {
var geometries = this.features[i].loadGeometry()[0];
for (var j = 0; j < geometries.length; j++) {
this.elementGroups.makeRoomFor(6);
var x = geometries[j].x,
y = geometries[j].y;

var idx = this.buffers.circleVertex.index -
this.elementGroups.current.vertexStartIndex;

// this geometry will be of the Point type, and we'll derive
// two triangles from it.
//
// ┌─────────┐
// │ 4 3 │
// │ │
// │ 1 2 │
// └─────────┘
//
this.buffers.circleVertex.add(x, y, -1, -1); // 1
this.buffers.circleVertex.add(x, y, 1, -1); // 2
this.buffers.circleVertex.add(x, y, 1, 1); // 3
this.buffers.circleVertex.add(x, y, -1, 1); // 4

// 1, 2, 3
// 1, 4, 3
this.elementGroups.elementBuffer.add(idx, idx + 1, idx + 2);
this.elementGroups.elementBuffer.add(idx, idx + 3, idx + 2);

this.elementGroups.current.vertexLength += 4;
this.elementGroups.current.elementLength += 2;
}
}
};
4 changes: 3 additions & 1 deletion js/data/create_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = createBucket;
var LineBucket = require('./line_bucket');
var FillBucket = require('./fill_bucket');
var SymbolBucket = require('./symbol_bucket');
var CircleBucket = require('./circle_bucket');
var LayoutProperties = require('../style/layout_properties');
var featureFilter = require('feature-filter');
var StyleDeclarationSet = require('../style/style_declaration_set');
Expand All @@ -21,7 +22,8 @@ function createBucket(layer, buffers, collision, z, overscaling, collisionDebug)
var BucketClass =
layer.type === 'line' ? LineBucket :
layer.type === 'fill' ? FillBucket :
layer.type === 'symbol' ? SymbolBucket : null;
layer.type === 'symbol' ? SymbolBucket :
layer.type === 'circle' ? CircleBucket : null;

var bucket = new BucketClass(buffers, new LayoutProperties[layer.type](layout), collision, overscaling, collisionDebug);

Expand Down
49 changes: 49 additions & 0 deletions js/render/draw_circle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

var browser = require('../util/browser.js');

module.exports = drawCircles;

function drawCircles(painter, layer, posMatrix, tile) {
// short-circuit if tile is empty
if (!tile.buffers) return;

var elementGroups = tile.elementGroups[layer.ref || layer.id];
if (!elementGroups) return;

var gl = painter.gl;

// Allow circles to be drawn across boundaries, so that
// large circles are not clipped to tiles
gl.disable(gl.STENCIL_TEST);

gl.switchShader(painter.circleShader, tile.posMatrix, tile.exMatrix);

var vertex = tile.buffers.circleVertex;
var shader = painter.circleShader;
var elements = tile.buffers.circleElement;

// antialiasing factor: this is a minimum blur distance that serves as
// a faux-antialiasing for the circle. since blur is a ratio of the circle's
// size and the intent is to keep the blur at roughly 1px, the two
// are inversely related.
var antialias = 1 / browser.devicePixelRatio / layer.paint['circle-radius'];

gl.uniform4fv(shader.u_color, layer.paint['circle-color']);
gl.uniform1f(shader.u_blur, Math.max(layer.paint['circle-blur'], antialias));
gl.uniform1f(shader.u_size, layer.paint['circle-radius']);

for (var k = 0; k < elementGroups.groups.length; k++) {
var group = elementGroups.groups[k];
var offset = group.vertexStartIndex * vertex.itemSize;

vertex.bind(gl, shader, offset);
elements.bind(gl, shader, offset);

var count = group.elementLength * 3;
var elementOffset = group.elementStartIndex * elements.itemSize;
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset);
}

gl.enable(gl.STENCIL_TEST);
}
7 changes: 6 additions & 1 deletion js/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,13 @@ Painter.prototype.setup = function() {
['a_pos', 'a_data'],
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_blur', 'u_patternscale_a', 'u_tex_y_a', 'u_patternscale_b', 'u_tex_y_b', 'u_image', 'u_sdfgamma', 'u_mix']);

this.circleShader = gl.initializeShader('circle',
['a_pos'],
['u_matrix', 'u_exmatrix', 'u_blur', 'u_size', 'u_color']);

this.dotShader = gl.initializeShader('dot',
['a_pos'],
['u_matrix', 'u_size', 'u_color', 'u_blur']);
['u_matrix', 'u_size', 'u_color']);

this.sdfShader = gl.initializeShader('sdf',
['a_pos', 'a_offset', 'a_data1', 'a_data2'],
Expand Down Expand Up @@ -225,6 +229,7 @@ Painter.prototype.bindDefaultFramebuffer = function() {

var draw = {
symbol: require('./draw_symbol'),
circle: require('./draw_circle'),
line: require('./draw_line'),
fill: require('./draw_fill'),
raster: require('./draw_raster'),
Expand Down
1 change: 1 addition & 0 deletions js/render/shaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
"debug": glify('../../shaders/debug.*.glsl'),
"dot": glify('../../shaders/dot.*.glsl'),
"fill": glify('../../shaders/fill.*.glsl'),
"circle": glify('../../shaders/circle.*.glsl'),
"gaussian": glify('../../shaders/gaussian.*.glsl'),
"line": glify('../../shaders/line.*.glsl'),
"linepattern": glify('../../shaders/linepattern.*.glsl'),
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"gl-matrix": "https://github.com/toji/gl-matrix/archive/v2.2.1.tar.gz",
"glify": "0.5.0",
"mapbox-gl-function": "^1.0.0",
"mapbox-gl-style-spec": "^7.4.0",
"mapbox-gl-style-spec": "https://github.com/mapbox/mapbox-gl-style-spec/archive/v8.tar.gz",
"minifyify": "^6.1.0",
"pbf": "^1.2.0",
"pngjs": "^0.4.0",
Expand Down Expand Up @@ -46,7 +46,7 @@
"zuul": "1.10.0"
},
"optionalDependencies": {
"gl": "git+https://github.com/kkaefer/headless-gl.git"
"gl": "https://github.com/mapbox/headless-gl/archive/cce881612f24b60843da6db8b3e4a2f108793298.tar.gz"
},
"browserify": {
"transform": [
Expand Down
10 changes: 10 additions & 0 deletions shaders/circle.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
uniform vec4 u_color;
uniform float u_blur;
uniform float u_size;

varying vec2 v_extrude;

void main() {
float t = smoothstep(1.0 - u_blur, 1.0, length(v_extrude));
gl_FragColor = u_color * (1.0 - t);
}
23 changes: 23 additions & 0 deletions shaders/circle.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// set by gl_util
uniform float u_size;

attribute vec2 a_pos;

uniform mat4 u_matrix;
uniform mat4 u_exmatrix;

varying vec2 v_extrude;

void main(void) {
// unencode the extrusion vector that we snuck into the a_pos vector
v_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);

vec4 extrude = u_exmatrix * vec4(v_extrude * u_size, 0, 0);
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
gl_Position = u_matrix * vec4(a_pos * 0.5, 0, 1);

// gl_Position is divided by gl_Position.w after this shader runs.
// Multiply the extrude by it so that it isn't affected by it.
gl_Position += extrude * gl_Position.w;
}