summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-02-28 12:25:45 +0100
committerKonstantin Käfer <mail@kkaefer.com>2014-02-28 12:25:45 +0100
commit4e73a4d47cd1287f6a5ac3bfc66dc024b0b268e4 (patch)
tree70def4e281be863605a6c93d9ec51be7b6497c69
parentae3bd04171a671a877c3240ed505096186c280cc (diff)
parent675b8eb1fef9e4fc99516b2fa64505107b1fa6bf (diff)
downloadqtlocation-mapboxgl-4e73a4d47cd1287f6a5ac3bfc66dc024b0b268e4.tar.gz
Merge branch 'master' into tessellation
Conflicts: include/llmr/renderer/fill_bucket.hpp src/renderer/fill_bucket.cpp src/renderer/painter.cpp
-rw-r--r--.gitignore18
-rw-r--r--Makefile9
-rwxr-xr-xbin/convert-style.js38
-rw-r--r--bin/style.js75
-rw-r--r--emscripten/main.cpp154
-rw-r--r--emscripten/main.html9
-rw-r--r--enable-emscripten2
-rw-r--r--include/llmr/geometry/point_buffer.hpp19
-rw-r--r--include/llmr/geometry/vertex_buffer.hpp3
-rw-r--r--include/llmr/map/map.hpp12
-rw-r--r--include/llmr/map/tile.hpp3
-rw-r--r--include/llmr/map/transform.hpp17
-rw-r--r--include/llmr/platform/gl.hpp4
-rw-r--r--include/llmr/platform/platform.hpp10
-rw-r--r--include/llmr/renderer/fill_bucket.hpp7
-rw-r--r--include/llmr/renderer/line_bucket.hpp6
-rw-r--r--include/llmr/renderer/painter.hpp4
-rw-r--r--include/llmr/renderer/point_bucket.hpp51
-rw-r--r--include/llmr/renderer/shader-point.hpp41
-rw-r--r--include/llmr/shader/shaders.hpp1
-rw-r--r--include/llmr/style/class_description.hpp1
-rw-r--r--include/llmr/style/properties.hpp20
-rw-r--r--include/llmr/style/sprite.hpp6
-rw-r--r--include/llmr/style/style.hpp4
-rw-r--r--include/llmr/util/animation.hpp44
-rw-r--r--include/llmr/util/pbf.hpp4
-rw-r--r--ios/MBXViewController.mm41
-rw-r--r--linux/llmr-app.gyp32
-rw-r--r--linux/main.cpp294
-rw-r--r--linux/settings.cpp55
-rw-r--r--linux/settings.hpp19
-rw-r--r--llmr.gyp4
-rw-r--r--macosx/llmr-app.gyp2
-rw-r--r--macosx/main.mm51
-rw-r--r--resources/style.pbfbin1050 -> 1702 bytes
-rw-r--r--src/geometry/debug_font_buffer.cpp4
-rw-r--r--src/geometry/point_buffer.cpp12
-rw-r--r--src/geometry/vertex_buffer.cpp2
-rw-r--r--src/map/map.cpp64
-rw-r--r--src/map/tile.cpp24
-rw-r--r--src/map/transform.cpp67
-rw-r--r--src/map/vector_tile.cpp4
-rw-r--r--src/renderer/fill_bucket.cpp1
-rw-r--r--src/renderer/line_bucket.cpp22
-rw-r--r--src/renderer/painter.cpp53
-rw-r--r--src/renderer/point_bucket.cpp52
-rw-r--r--src/renderer/shader-point.cpp74
-rw-r--r--src/renderer/shader.cpp4
-rw-r--r--src/shader/point.fragment.glsl21
-rw-r--r--src/shader/point.vertex.glsl9
-rw-r--r--src/shader/shaders.cpp4
-rw-r--r--src/style/resources.cpp115
-rw-r--r--src/style/sprite.cpp32
-rw-r--r--src/style/style.cpp48
-rw-r--r--src/util/animation.cpp11
55 files changed, 1313 insertions, 370 deletions
diff --git a/.gitignore b/.gitignore
index bbd74195c0..d9eef726c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,9 @@
-*.o
-/emscripten/main.js
-/emscripten/main.js.map
-/build
-*.sublime-*
-config.gypi
-config.mk
-.DS_Store
-out
-mapnik-packaging
*.xcodeproj
+*.o
+/out
+/mapnik-packaging
+/macosx/build
+/ios/build
+/config.gypi
+/config.mk
+/build \ No newline at end of file
diff --git a/Makefile b/Makefile
index 1d16f0c941..854b5fb81f 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ V ?= 1
all: llmr
llmr: config.gypi src llmr.gyp
- $(MAKE) -C out BUILDTYPE=Release V=$(V) llmr-osx
+ $(MAKE) -C out BUILDTYPE=Release V=$(V) llmr-x86
# build OS X app with pure make
app: config.gypi src macosx/llmr-app.gyp
@@ -15,6 +15,11 @@ app: config.gypi src macosx/llmr-app.gyp
make -C build/macosx-make V=$(V)
open build/macosx-make/out/Release/llmr.app
+linux: config.gypi src linux/llmr-app.gyp
+ deps/run_gyp linux/llmr-app.gyp -Goutput_dir=./out/ --depth=. --generator-output=./build/linux-make -f make
+ make -C build/linux-make V=$(V)
+ ./build/linux-make/out/Release/llmr.app
+
# build just xcode project for libllmr
xcode: config.gypi llmr.gyp
deps/run_gyp llmr.gyp -Goutput_dir=./out/ --depth=. --generator-output=./ -f xcode
@@ -63,4 +68,4 @@ distclean:
test: all
echo test
-.PHONY: test
+.PHONY: test linux
diff --git a/bin/convert-style.js b/bin/convert-style.js
index 57aee0136f..a79f46cf7d 100755
--- a/bin/convert-style.js
+++ b/bin/convert-style.js
@@ -6,8 +6,6 @@ var style = require('./style.js');
var Protobuf = require('./protobuf.js');
var fs = require('fs');
-// var fs = require('fs');
-
var pbf = new Protobuf();
// enum
@@ -181,23 +179,47 @@ function createLineClass(layer, name) {
return pbf;
}
+function createMarkerClass(layer, name) {
+ var pbf = new Protobuf();
+ pbf.writeTaggedString(1 /* layer_name */, name);
+
+ if ('color' in layer) {
+ var color = layer.color.match(/^#([0-9a-f]{6})$/i);
+ if (!color) {
+ console.warn('invalid color');
+ } else {
+ pbf.writeTaggedUInt32(3 /* color */, parseInt(color[1] + 'ff', 16));
+ }
+ }
+
+ if ('size' in layer) {
+ pbf.writeMessage(4 /* size */, convertProperty(layer.size));
+ }
+
+ if ('opacity' in layer) {
+ pbf.writeMessage(6 /* opacity */, convertProperty(layer.opacity));
+ }
+
+ if ('image' in layer) {
+ pbf.writeTaggedString(8 /* image */, layer.image);
+ }
+
+ return pbf;
+}
function createClass(klass) {
var pbf = new Protobuf();
pbf.writeTaggedString(1 /* name */, klass.name);
for (var name in klass.layers) {
switch (klass.layers[name].type) {
- case 'fill': pbf.writeMessage(2 /* fill */, createFillClass(klass.layers[name], name)); break;
- case 'line': pbf.writeMessage(3 /* line */, createLineClass(klass.layers[name], name)); break;
+ case 'fill': pbf.writeMessage(2 /* fill */, createFillClass(klass.layers[name], name)); break;
+ case 'line': pbf.writeMessage(3 /* line */, createLineClass(klass.layers[name], name)); break;
+ case 'marker': pbf.writeMessage(4 /* marker */, createMarkerClass(klass.layers[name], name)); break;
}
}
return pbf;
}
-
-
-
-
for (var name in style.buckets) {
var bucket = style.buckets[name];
pbf.writeMessage(1 /* bucket */, createBucket(bucket, name));
diff --git a/bin/style.js b/bin/style.js
index 1596e30a67..50b365c1d4 100644
--- a/bin/style.js
+++ b/bin/style.js
@@ -52,13 +52,41 @@ module.exports = {
"layer": "building",
"type": "fill"
},
- "alcohol": {
+ "alcohol_poi": {
"datasource": "streets",
"layer": "poi_label",
"field": "type",
"value": ["Alcohol"],
"type": "point"
- }
+ },
+ "cafe_poi": {
+ "datasource": "streets",
+ "layer": "poi_label",
+ "field": "type",
+ "value": ["Cafe"],
+ "type": "point"
+ },
+ "embassy_poi": {
+ "datasource": "streets",
+ "layer": "poi_label",
+ "field": "type",
+ "value": ["Embassy"],
+ "type": "point"
+ },
+ "park_poi": {
+ "datasource": "streets",
+ "layer": "poi_label",
+ "field": "type",
+ "value": ["Park"],
+ "type": "point"
+ },
+ "restaurant_poi": {
+ "datasource": "streets",
+ "layer": "poi_label",
+ "field": "type",
+ "value": ["Restaurant"],
+ "type": "point"
+ },
},
"sprite": "img/maki-sprite",
"structure": [
@@ -69,7 +97,11 @@ module.exports = {
{ "name": "road_limited", "bucket": "road_limited" },
{ "name": "road_regular", "bucket": "road_regular" },
{ "name": "road_large", "bucket": "road_large" },
- { "name": "alcohol", "bucket": "alcohol" }
+ { "name": "alcohol_poi", "bucket": "alcohol_poi" },
+ { "name": "cafe_poi", "bucket": "cafe_poi" },
+ { "name": "embassy_poi", "bucket": "embassy_poi" },
+ { "name": "park_poi", "bucket": "park_poi" },
+ { "name": "restaurant_poi", "bucket": "restaurant_poi" },
],
"classes": [
{
@@ -138,10 +170,41 @@ module.exports = {
{ z: 30, val: 64 }
],
},
- "alcohol": {
+ "alcohol_poi": {
+ "type": "marker",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "alcohol-shop",
+ "opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
+ },
+ "cafe_poi": {
"type": "marker",
- "image": "alcohol-shop"
- }
+ "color": "#cccccc",
+ "size": 18,
+ "image": "cafe",
+ "opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
+ },
+ "embassy_poi": {
+ "type": "marker",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "embassy",
+ "opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
+ },
+ "park_poi": {
+ "type": "marker",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "park",
+ "opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
+ },
+ "restaurant_poi": {
+ "type": "marker",
+ "color": "#cccccc",
+ "size": 18,
+ "image": "restaurant",
+ "opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
+ },
}
}
]
diff --git a/emscripten/main.cpp b/emscripten/main.cpp
deleted file mode 100644
index ffdef046b3..0000000000
--- a/emscripten/main.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-#include <emscripten/emscripten.h>
-#include <GL/glfw.h>
-#include <cstdio>
-#include <cstdlib>
-#include <cmath>
- #include <unistd.h>
-
-#include <llmr/llmr.hpp>
-#include <llmr/map/tile.hpp>
-
-bool dirty = true;
-
-class MapView;
-
-static MapView *view;
-
-class MapView {
-public:
- MapView() :
- dirty(true),
- platform(new llmr::platform(this)),
- map(new llmr::map(platform)) {
-
- // Initialize GLFW
- if (!glfwInit()) {
- fprintf(stderr, "Failed to initialize GLFW\n");
- exit(1);
- }
-
- int width, height;
- emscripten_get_canvas_size(&width, &height, nullptr);
-
- glfwSetMousePosCallback(mousemove);
- glfwSetMouseButtonCallback(mouseclick);
- glfwSetMouseWheelCallback(scroll);
-
- // Open a window and create its OpenGL context
- if (!glfwOpenWindow(width, height, 8, 8, 8, 8, 16, 8, GLFW_WINDOW)) {
- fprintf(stderr, "Failed to open GLFW window\n");
-
- glfwTerminate();
- exit(1);
- }
-
- map->setup();
- map->resize(width, height);
-
- }
-
- ~MapView() {
- delete map;
- delete platform;
- glfwTerminate();
- }
-
- static void mousemove(int x, int y) {
- if (view->tracking) {
- view->map->moveBy(x - view->last_x, y - view->last_y);
- }
- view->last_x = x;
- view->last_y = y;
- }
-
- static void scroll(float pos) {
- double delta = pos;
-
- // bool is_wheel = delta != 0 && fmod(delta, 4.000244140625) == 0;
-
- double absdelta = delta < 0 ? -delta : delta;
- double scale = 2.0 / (1.0 + exp(-absdelta / 100.0));
-
- // Make the scroll wheel a bit slower.
- // if (!is_wheel) {
- // scale = (scale - 1.0) / 2.0 + 1.0;
- // }
-
- // Zooming out.
- if (delta < 0 && scale != 0) {
- scale = 1.0 / scale;
- }
-
- view->map->scaleBy(scale, view->last_x, view->last_y);
- }
-
- static void mouseclick(int button, int action) {
- if (button == GLFW_MOUSE_BUTTON_1) {
- view->tracking = action == GLFW_PRESS;
- } else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
- fprintf(stderr, "right mouse\n");
- }
- }
-
- int run() {
- emscripten_set_main_loop(render, 60, 1);
- return 0;
- }
-
- static void render() {
- if (view->dirty) {
- view->map->render();
- // glClearColor(1, 1, 0, 1);
- // glClear(GL_COLOR_BUFFER_BIT);
-
- view->dirty = false;
- }
- }
-
- bool dirty;
- double last_x, last_y;
- bool tracking;
-
-
- llmr::platform *platform;
- llmr::map *map;
-
-};
-
-
-void llmr::platform::restart() {
- view->dirty = true;
-}
-
-void ontileload(void* custom, void* bytes, int length) {
- fprintf(stderr, "data loaded successfully: length: %d\n", length);
- llmr::tile *tile = (llmr::tile *)custom;
- tile->setData((uint8_t *)bytes, length);
- if (tile->parse()) {
- view->map->tileLoaded(tile);
- return;
- }
-}
-
-void ontileerror(void* custom) {
- fprintf(stderr, "data load error\n");
- llmr::tile *tile = (llmr::tile *)custom;
- view->map->tileFailed(tile);
-}
-
-void llmr::platform::request(tile *tile) {
- const char *urlTemplate = "http://api.tiles.mapbox.com/v3/mapbox.mapbox-streets-v4/%d/%d/%d.vector.pbf";
- char urlString[255];
- snprintf(urlString, 255, urlTemplate, tile->z, tile->x, tile->y);
- fprintf(stderr, "requesting %s\n", urlString);
-
- emscripten_async_wget_data(urlString, tile, ontileload, ontileerror);
-}
-
-
-int main() {
- view = new MapView();
- int ret = view->run();
- delete view;
- return ret;
-}
diff --git a/emscripten/main.html b/emscripten/main.html
deleted file mode 100644
index 9676130527..0000000000
--- a/emscripten/main.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<body>
-<script>
-var canvas = document.createElement('canvas');
-canvas.width = 600;
-canvas.height = 400;
-document.body.appendChild(canvas);
-Module = { canvas: canvas };
-</script>
-<script src="main.js"></script>
diff --git a/enable-emscripten b/enable-emscripten
deleted file mode 100644
index d7feba1eab..0000000000
--- a/enable-emscripten
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-export PATH="`emsdk active_path`:$PATH"
diff --git a/include/llmr/geometry/point_buffer.hpp b/include/llmr/geometry/point_buffer.hpp
new file mode 100644
index 0000000000..8c01a20ee2
--- /dev/null
+++ b/include/llmr/geometry/point_buffer.hpp
@@ -0,0 +1,19 @@
+#ifndef LLMR_GEOMETRY_POINT_BUFFER
+#define LLMR_GEOMETRY_POINT_BUFFER
+
+#include "buffer.hpp"
+
+namespace llmr {
+
+ class PointVertexBuffer : public Buffer<
+ 4 // 2 coordinates per vertex (== 4 bytes)
+ > {
+ public:
+ typedef int16_t vertex_type;
+
+ void add(vertex_type x, vertex_type y);
+ };
+
+}
+
+#endif
diff --git a/include/llmr/geometry/vertex_buffer.hpp b/include/llmr/geometry/vertex_buffer.hpp
index 9d053693c9..9478d7b15f 100644
--- a/include/llmr/geometry/vertex_buffer.hpp
+++ b/include/llmr/geometry/vertex_buffer.hpp
@@ -2,6 +2,7 @@
#define LLMR_GEOMETRY_VERTEX_BUFFER
#include <vector>
+#include <cstddef>
#include <cstdint>
#include <cmath>
@@ -17,7 +18,7 @@ public:
* Returns the number of elements in this buffer. This is not the number of
* bytes, but rather the number of coordinates with associated information.
*/
- uint32_t index() const;
+ size_t index() const;
/*
* Transfers this buffer to the GPU and binds the buffer to the GL context.
diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp
index 84cb4d12eb..768be14100 100644
--- a/include/llmr/map/map.hpp
+++ b/include/llmr/map/map.hpp
@@ -20,7 +20,7 @@ public:
~Map();
/* setup */
- void setup();
+ void setup(float pixelRatio = 1);
void loadStyle(const uint8_t *const data, uint32_t bytes);
void loadSprite(const std::string& url);
void loadSettings();
@@ -29,13 +29,13 @@ public:
/* callback */
bool render();
void cancelAnimations();
- void tileLoaded(std::shared_ptr<Tile> tile);
- void tileFailed(std::shared_ptr<Tile> tile);
/* position */
void moveBy(double dx, double dy, double duration = 0);
void setLonLat(double lon, double lat, double duration = 0);
void getLonLat(double &lon, double &lat) const;
+ void startPanning();
+ void stopPanning();
void resetPosition();
/* scale */
@@ -47,12 +47,16 @@ public:
void setLonLatZoom(double lon, double lat, double zoom, double duration = 0);
void getLonLatZoom(double &lon, double &lat, double &zoom) const;
void resetZoom();
+ void startScaling();
+ void stopScaling();
/* rotation */
void rotateBy(double cx, double cy, double sx, double sy, double ex, double ey, double duration = 0);
void setAngle(double angle, double cx = -1, double cy = -1, double duration = 0);
double getAngle() const;
void resetNorth();
+ void startRotating();
+ void stopRotating();
void toggleDebug();
@@ -75,6 +79,8 @@ private:
int32_t min_zoom;
int32_t max_zoom;
+ float pixel_ratio;
+
std::forward_list<std::shared_ptr<Tile>> tiles;
std::forward_list<std::shared_ptr<Tile>> historic_tiles;
};
diff --git a/include/llmr/map/tile.hpp b/include/llmr/map/tile.hpp
index 6c1c6f521e..5d98e919c6 100644
--- a/include/llmr/map/tile.hpp
+++ b/include/llmr/map/tile.hpp
@@ -23,6 +23,7 @@ class VectorTile;
class VectorTileLayer;
class FillVertexBuffer;
class LineVertexBuffer;
+class PointVertexBuffer;
class TriangleElementsBuffer;
class LineElementsBuffer;
class PointElementsBuffer;
@@ -59,6 +60,7 @@ public:
std::shared_ptr<Bucket> createBucket(const VectorTile& tile, const BucketDescription& bucket_desc);
std::shared_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::shared_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
+ std::shared_ptr<Bucket> createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
void cancel();
@@ -77,6 +79,7 @@ public:
std::shared_ptr<FillVertexBuffer> fillVertexBuffer;
std::shared_ptr<LineVertexBuffer> lineVertexBuffer;
+ std::shared_ptr<PointVertexBuffer> pointVertexBuffer;
std::shared_ptr<TriangleElementsBuffer> triangleElementsBuffer;
std::shared_ptr<LineElementsBuffer> lineElementsBuffer;
diff --git a/include/llmr/map/transform.hpp b/include/llmr/map/transform.hpp
index 56a5a8be9d..a628bf33d2 100644
--- a/include/llmr/map/transform.hpp
+++ b/include/llmr/map/transform.hpp
@@ -42,6 +42,14 @@ public:
void getLonLat(double& lon, double& lat) const;
void getLonLatZoom(double& lon, double& lat, double& zoom) const;
+ // Animations
+ void startPanning();
+ void stopPanning();
+ void startRotating();
+ void stopRotating();
+ void startScaling();
+ void stopScaling();
+
// Temporary
void mapCornersToBox(uint32_t z, box& b) const;
@@ -61,6 +69,10 @@ public:
float pixelRatio = 1;
+ bool rotating = false;
+ bool scaling = false;
+ bool panning = false;
+
private:
double x = 0, y = 0; // pixel values of the map center in the current scale
double angle = 0;
@@ -72,7 +84,10 @@ private:
// cache values for spherical mercator math
double zc, Bc, Cc;
- std::forward_list<util::animation> animations;
+ std::forward_list<std::shared_ptr<util::animation>> animations;
+ std::shared_ptr<util::animation> scale_timeout;
+ std::shared_ptr<util::animation> rotate_timeout;
+ std::shared_ptr<util::animation> pan_timeout;
};
}
diff --git a/include/llmr/platform/gl.hpp b/include/llmr/platform/gl.hpp
index 4074fa081a..a0d059c756 100644
--- a/include/llmr/platform/gl.hpp
+++ b/include/llmr/platform/gl.hpp
@@ -1,9 +1,7 @@
#ifndef LLMR_RENDERER_GL
#define LLMR_RENDERER_GL
-#ifdef EMSCRIPTEN
- #include <GLES2/gl2.h>
-#elif __APPLE__
+#if __APPLE__
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
#include <OpenGLES/ES2/gl.h>
diff --git a/include/llmr/platform/platform.hpp b/include/llmr/platform/platform.hpp
index f56e738ffa..25778c5610 100644
--- a/include/llmr/platform/platform.hpp
+++ b/include/llmr/platform/platform.hpp
@@ -6,7 +6,7 @@
#include <string>
#define kTileURL "http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf"
-#define kSpriteURL "http://mapbox-kkaefer.s3.amazonaws.com/static/sprite"
+#define kSpriteURL "https://dl.dropboxusercontent.com/u/575564/sprite"
namespace llmr {
@@ -14,16 +14,16 @@ class Tile;
namespace platform {
-// Restarts painting. This could for example trigger the event loop of the
-// controlling application.
-void restart(void *obj);
+// Restarts painting. This could for example trigger the event loop of the controlling application.
+void restart();
struct Response {
int16_t code = -1;
std::string body;
};
-void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb);
+// Makes an HTTP request of a URL on a background thread, calls a function with the results on the same thread, and finally calls a callback function on the main thread.
+void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback);
// Returns a relative timestamp in seconds. This value must be monotonic.
double time();
diff --git a/include/llmr/renderer/fill_bucket.hpp b/include/llmr/renderer/fill_bucket.hpp
index d5f36211e5..4c82b7b315 100644
--- a/include/llmr/renderer/fill_bucket.hpp
+++ b/include/llmr/renderer/fill_bucket.hpp
@@ -68,10 +68,11 @@ private:
std::shared_ptr<LineElementsBuffer> lineElementsBuffer;
// hold information on where the vertices are located in the FillBuffer
- const uint32_t vertex_start;
- const uint32_t triangle_elements_start;
- const uint32_t line_elements_start;
+ const size_t vertex_start;
+ const size_t triangle_elements_start;
+ const size_t line_elements_start;
VertexArrayObject array;
+
std::vector<triangle_group_type> triangleGroups;
std::vector<line_group_type> lineGroups;
diff --git a/include/llmr/renderer/line_bucket.hpp b/include/llmr/renderer/line_bucket.hpp
index 993b1d18ed..8b03fc5ad4 100644
--- a/include/llmr/renderer/line_bucket.hpp
+++ b/include/llmr/renderer/line_bucket.hpp
@@ -47,9 +47,9 @@ private:
std::shared_ptr<TriangleElementsBuffer> triangleElementsBuffer;
std::shared_ptr<PointElementsBuffer> pointElementsBuffer;
- const uint32_t vertex_start;
- const uint32_t triangle_elements_start;
- const uint32_t point_elements_start;
+ const size_t vertex_start;
+ const size_t triangle_elements_start;
+ const size_t point_elements_start;
std::vector<triangle_group_type> triangleGroups;
std::vector<point_group_type> pointGroups;
diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp
index b6934131d6..aa3a3d73f7 100644
--- a/include/llmr/renderer/painter.hpp
+++ b/include/llmr/renderer/painter.hpp
@@ -12,6 +12,7 @@
#include <llmr/renderer/shader-pattern.hpp>
#include <llmr/renderer/shader-line.hpp>
#include <llmr/renderer/shader-linejoin.hpp>
+#include <llmr/renderer/shader-point.hpp>
namespace llmr {
@@ -23,6 +24,7 @@ class Tile;
class FillBucket;
class LineBucket;
+class PointBucket;
class Painter : private util::noncopyable {
public:
@@ -36,6 +38,7 @@ public:
void renderBackground();
void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id);
+ void renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id);
private:
void setupShaders();
@@ -63,6 +66,7 @@ private:
std::unique_ptr<LineShader> lineShader;
std::unique_ptr<LinejoinShader> linejoinShader;
std::unique_ptr<PatternShader> patternShader;
+ std::unique_ptr<PointShader> pointShader;
// Set up the stencil quad we're using to generate the stencil mask.
VertexBuffer tileStencilBuffer = {
diff --git a/include/llmr/renderer/point_bucket.hpp b/include/llmr/renderer/point_bucket.hpp
new file mode 100644
index 0000000000..9da7edc30e
--- /dev/null
+++ b/include/llmr/renderer/point_bucket.hpp
@@ -0,0 +1,51 @@
+#ifndef LLMR_RENDERER_POINTBUCKET
+#define LLMR_RENDERER_POINTBUCKET
+
+#include <llmr/renderer/bucket.hpp>
+#include <llmr/style/bucket_description.hpp>
+#include <llmr/geometry/elements_buffer.hpp>
+#include <llmr/geometry/point_buffer.hpp>
+
+#include <vector>
+#include <memory>
+
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
+namespace llmr {
+
+class Style;
+class PointVertexBuffer;
+class BucketDescription;
+class PointShader;
+struct Coordinate;
+struct pbf;
+
+class PointBucket : public Bucket {
+public:
+ PointBucket(const std::shared_ptr<PointVertexBuffer>& vertexBuffer,
+ const BucketDescription& bucket_desc);
+
+ virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
+
+ void addGeometry(pbf& data);
+
+ bool hasPoints() const;
+
+ void drawPoints(PointShader& shader);
+
+public:
+ const BucketGeometryDescription geometry;
+
+private:
+ std::shared_ptr<PointVertexBuffer> vertexBuffer;
+ VertexArrayObject<PointShader> array;
+
+ const size_t vertex_start;
+ size_t vertex_end = 0;
+};
+
+}
+
+#endif
diff --git a/include/llmr/renderer/shader-point.hpp b/include/llmr/renderer/shader-point.hpp
new file mode 100644
index 0000000000..1de38504cf
--- /dev/null
+++ b/include/llmr/renderer/shader-point.hpp
@@ -0,0 +1,41 @@
+#ifndef LLMR_RENDERER_SHADER_POINT
+#define LLMR_RENDERER_SHADER_POINT
+
+#include "shader.hpp"
+
+namespace llmr {
+
+class PointShader : public Shader {
+public:
+ PointShader();
+
+ void bind(char *offset);
+
+ void setImage(int32_t image);
+ void setColor(const std::array<float, 4>& color);
+ void setPointTopLeft(const std::array<float, 2>& point_tl);
+ void setPointBottomRight(const std::array<float, 2>& point_br);
+ void setSize(float size);
+
+private:
+ int32_t a_pos = -1;
+
+ int32_t image = -1;
+ int32_t u_image = -1;
+
+ std::array<float, 4> color = {};
+ int32_t u_color = -1;
+
+ std::array<float, 2> point_tl = {};
+ int32_t u_point_tl = -1;
+
+ std::array<float, 2> point_br = {};
+ int32_t u_point_br = -1;
+
+ float size = 0;
+ int32_t u_size = -1;
+};
+
+}
+
+#endif
diff --git a/include/llmr/shader/shaders.hpp b/include/llmr/shader/shaders.hpp
index c0f02337a0..6312cc6941 100644
--- a/include/llmr/shader/shaders.hpp
+++ b/include/llmr/shader/shaders.hpp
@@ -16,6 +16,7 @@ enum {
OUTLINE_SHADER,
PATTERN_SHADER,
PLAIN_SHADER,
+ POINT_SHADER,
SHADER_COUNT
};
diff --git a/include/llmr/style/class_description.hpp b/include/llmr/style/class_description.hpp
index ce5b172b23..eab7b46a95 100644
--- a/include/llmr/style/class_description.hpp
+++ b/include/llmr/style/class_description.hpp
@@ -12,6 +12,7 @@ class ClassDescription {
public:
std::map<std::string, FillClass> fill;
std::map<std::string, LineClass> line;
+ std::map<std::string, PointClass> point;
};
diff --git a/include/llmr/style/properties.hpp b/include/llmr/style/properties.hpp
index 4d4ce68952..79b906cd53 100644
--- a/include/llmr/style/properties.hpp
+++ b/include/llmr/style/properties.hpp
@@ -60,8 +60,22 @@ struct FunctionProperty {
inline T operator()(float z) const { return function(z, values); }
};
+struct PointClass {
+ FunctionProperty<bool> hidden;
+ FunctionProperty<float> size;
+ Color color = {{ 0, 0, 0, 1 }};
+ FunctionProperty<float> opacity = 1;
+ std::string image;
+};
+
+struct PointProperties {
+ bool hidden = false;
+ float size = 0;
+ Color color = {{ 0, 0, 0, 1 }};
+ float opacity = 1.0;
+ std::string image;
+};
-// LineClass is the information we parse from the stylesheet
struct LineClass {
FunctionProperty<bool> hidden;
FunctionProperty<float> width;
@@ -70,7 +84,6 @@ struct LineClass {
FunctionProperty<float> opacity = 1;
};
-// LineProperties is the one we resolve this to.
struct LineProperties {
bool hidden = false;
float width = 0;
@@ -79,9 +92,6 @@ struct LineProperties {
float opacity = 1.0;
};
-
-
-
struct FillClass {
FunctionProperty<bool> hidden;
Winding winding = Winding::NonZero;
diff --git a/include/llmr/style/sprite.hpp b/include/llmr/style/sprite.hpp
index 0fcd15c054..78ca76f237 100644
--- a/include/llmr/style/sprite.hpp
+++ b/include/llmr/style/sprite.hpp
@@ -34,7 +34,9 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite> {
public:
- void load(const std::string& base_url);
+ ~Sprite();
+
+ void load(const std::string& base_url, float pixelRatio = 1);
void bind(bool linear = false);
ImagePosition getPosition(const std::string& name, bool repeating = false);
@@ -52,7 +54,7 @@ private:
uint32_t texture = 0;
std::map<std::string, SpritePosition> pos;
uint32_t width = 0, height = 0;
- std::string img;
+ char *img = nullptr;
};
}
diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp
index 04b7e22163..7a491d810d 100644
--- a/include/llmr/style/style.hpp
+++ b/include/llmr/style/style.hpp
@@ -22,7 +22,7 @@ public:
Style();
void reset();
- void load(const uint8_t *const data, uint32_t bytes);
+ void load(const uint8_t *const data, size_t bytes);
// This is commented out because it is not fully implemented yet. For now,
// we keep using the protobuf stylesheet format
@@ -36,6 +36,7 @@ private:
static std::pair<std::string, ClassDescription> parseClass(pbf data);
static std::pair<std::string, FillClass> parseFillClass(pbf data);
static std::pair<std::string, LineClass> parseLineClass(pbf data);
+ static std::pair<std::string, PointClass> parsePointClass(pbf data);
template <typename T> static FunctionProperty<T> parseProperty(pbf data);
static Color parseColor(pbf& data);
@@ -53,6 +54,7 @@ public:
struct {
std::map<std::string, FillProperties> fills;
std::map<std::string, LineProperties> lines;
+ std::map<std::string, PointProperties> points;
} computed;
};
diff --git a/include/llmr/util/animation.hpp b/include/llmr/util/animation.hpp
index 4095fcba1e..2024fb8753 100644
--- a/include/llmr/util/animation.hpp
+++ b/include/llmr/util/animation.hpp
@@ -2,6 +2,7 @@
#define LLMR_UTIL_ANIMATION
#include <llmr/util/noncopyable.hpp>
+#include <llmr/platform/platform.hpp>
namespace llmr {
namespace util {
@@ -12,14 +13,51 @@ public:
running,
complete
};
+ animation(double duration)
+ : start(platform::time()),
+ duration(duration) {}
- animation(double from, double to, double &value, double duration);
+ double progress() const {
+ return (platform::time() - start) / duration;
+ }
+
+ virtual state update() const = 0;
+ virtual ~animation();
+
+protected:
+ const double start, duration;
+};
+
+class ease_animation : public animation {
+public:
+ ease_animation(double from, double to, double& value, double duration);
state update() const;
private:
- const double start, duration;
const double from, to;
- double &value;
+ double& value;
+};
+
+template <typename T>
+class timeout : public animation {
+public:
+ timeout(T final_value, T& value, double duration)
+ : animation(duration),
+ final_value(final_value),
+ value(value) {}
+
+ state update() const {
+ if (progress() >= 1) {
+ value = final_value;
+ return complete;
+ } else {
+ return running;
+ }
+ }
+
+private:
+ const T final_value;
+ T& value;
};
}
diff --git a/include/llmr/util/pbf.hpp b/include/llmr/util/pbf.hpp
index 132cc14354..2e99cd91cb 100644
--- a/include/llmr/util/pbf.hpp
+++ b/include/llmr/util/pbf.hpp
@@ -20,7 +20,7 @@ struct pbf {
struct unknown_field_type_exception : exception {};
struct end_of_buffer_exception : exception {};
- inline pbf(const unsigned char *data, uint32_t length);
+ inline pbf(const unsigned char *data, size_t length);
inline pbf();
inline operator bool() const;
@@ -49,7 +49,7 @@ struct pbf {
uint32_t tag = 0;
};
-pbf::pbf(const unsigned char *data, uint32_t length)
+pbf::pbf(const unsigned char *data, size_t length)
: data(data),
end(data + length),
value(0),
diff --git a/ios/MBXViewController.mm b/ios/MBXViewController.mm
index c5d6effa1a..2c998f7af5 100644
--- a/ios/MBXViewController.mm
+++ b/ios/MBXViewController.mm
@@ -49,7 +49,7 @@ class MBXMapView
{
settings.load();
- map.setup();
+ map.setup([[UIScreen mainScreen] scale]);
CGRect frame = [[UIScreen mainScreen] bounds];
map.resize(frame.size.width, frame.size.height, frame.size.width, frame.size.height);
@@ -141,6 +141,8 @@ class MBXMapView
- (void)dealloc
{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
mapView->settings.sync();
}
@@ -236,6 +238,8 @@ class MBXMapView
if (pinch.state == UIGestureRecognizerStateBegan)
{
+ mapView->map.startScaling();
+
self.scale = mapView->map.getScale();
}
else if (pinch.state == UIGestureRecognizerStateChanged)
@@ -258,6 +262,8 @@ class MBXMapView
}
else if (pinch.state == UIGestureRecognizerStateEnded)
{
+ mapView->map.stopScaling();
+
if (fabsf(pinch.velocity) < 20)
return;
@@ -270,6 +276,10 @@ class MBXMapView
mapView->map.scaleBy(new_scale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y, duration);
}
+ else if (pinch.state == UIGestureRecognizerStateCancelled)
+ {
+ mapView->map.stopScaling();
+ }
[self updateRender];
}
@@ -280,12 +290,18 @@ class MBXMapView
if (rotate.state == UIGestureRecognizerStateBegan)
{
+ mapView->map.startRotating();
+
self.angle = mapView->map.getAngle();
}
else if (rotate.state == UIGestureRecognizerStateChanged)
{
mapView->map.setAngle(self.angle + rotate.rotation, [rotate locationInView:rotate.view].x, [rotate locationInView:rotate.view].y);
}
+ else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
+ {
+ mapView->map.stopRotating();
+ }
[self updateRender];
}
@@ -373,27 +389,27 @@ class MBXMapView
return ([validSimultaneousGestures containsObject:[gestureRecognizer class]] && [validSimultaneousGestures containsObject:[otherGestureRecognizer class]]);
}
-CADisplayLink *displayLink;
MBXMapView *mapView;
-NSOperationQueue *queue = NULL;
+CADisplayLink *displayLink;
+NSOperationQueue *queue;
namespace llmr
{
namespace platform
{
- void restart(void *)
+ void restart()
{
+ [[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
}
- void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb)
+ void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback)
{
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@1 forKey:@"count"]];
- if (!queue) {
- queue = [[NSOperationQueue alloc] init];
- }
+ if (!queue)
+ queue = [NSOperationQueue new];
- NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]]];
+ NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@(url.c_str())]];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:queue
@@ -407,10 +423,11 @@ namespace llmr
res.body = { (const char *)[data bytes], [data length] };
}
- func(res);
+ background_function(res);
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- cb();
+ dispatch_async(dispatch_get_main_queue(), ^(void)
+ {
+ foreground_callback();
});
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@(-1) forKey:@"count"]];
diff --git a/linux/llmr-app.gyp b/linux/llmr-app.gyp
new file mode 100644
index 0000000000..06d7ed822e
--- /dev/null
+++ b/linux/llmr-app.gyp
@@ -0,0 +1,32 @@
+{
+ 'includes': [
+ '../common.gypi',
+ '../config.gypi'
+ ],
+ 'targets': [
+ {
+ "target_name": "linuxapp",
+ "product_name": "llmr",
+ "type": "executable",
+ "sources": [
+ "./main.cpp",
+ "./settings.cpp",
+ "./settings.hpp"
+ ],
+ 'product_extension': 'app',
+ 'link_settings': {
+ 'libraries': [
+ '<@(glfw3_libraries)',
+ '-lcurl'
+ ],
+ },
+ 'cflags': [
+ '<@(png_cflags)',
+ '<@(glfw3_cflags)'
+ ],
+ "dependencies": [
+ "../llmr.gyp:llmr-x86"
+ ]
+ }
+ ]
+}
diff --git a/linux/main.cpp b/linux/main.cpp
new file mode 100644
index 0000000000..1e6c409a0f
--- /dev/null
+++ b/linux/main.cpp
@@ -0,0 +1,294 @@
+#include <llmr/llmr.hpp>
+#include <GLFW/glfw3.h>
+#include <curl/curl.h>
+#include <llmr/platform/platform.hpp>
+#include "settings.hpp"
+#include <future>
+#include <list>
+#include <chrono>
+
+#include <thread>
+
+std::list<std::future<void>> futures;
+std::list<std::future<void>>::iterator f_it;
+std::list<std::function<void()>> callbacks;
+std::list<std::function<void()>>::iterator c_it;
+std::chrono::milliseconds zero (0);
+
+class MapView {
+public:
+ MapView() :
+ dirty(true),
+ tracking(false),
+ rotating(false),
+ last_click(-1),
+ settings(),
+ map(settings) {
+ }
+
+ void init() {
+ if (!glfwInit()) {
+ fprintf(stderr, "Failed to initialize glfw\n");
+ exit(1);
+ }
+
+ glfwWindowHint(GLFW_STENCIL_BITS, 8);
+ glfwWindowHint(GLFW_DEPTH_BITS, 16);
+
+ window = glfwCreateWindow(1024, 768, "llmr", NULL, NULL);
+ if (!window) {
+ glfwTerminate();
+ fprintf(stderr, "Failed to initialize window\n");
+ exit(1);
+ }
+
+ glfwSetWindowUserPointer(window, this);
+ glfwMakeContextCurrent(window);
+
+ settings.load();
+ map.setup();
+
+ resize(window, 0, 0);
+
+ glfwSwapInterval(1);
+
+ map.loadSettings();
+
+ glfwSetCursorPosCallback(window, mousemove);
+ glfwSetMouseButtonCallback(window, mouseclick);
+ glfwSetWindowSizeCallback(window, resize);
+ glfwSetFramebufferSizeCallback(window, resize);
+ glfwSetScrollCallback(window, scroll);
+ glfwSetCharCallback(window, character);
+ glfwSetKeyCallback(window, key);
+
+ }
+
+ static void character(GLFWwindow *window, unsigned int codepoint) {
+
+ }
+
+
+ static void key(GLFWwindow *window, int key, int scancode, int action, int mods) {
+ MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
+
+ if (action == GLFW_RELEASE) {
+ switch (key) {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, true);
+ break;
+ case GLFW_KEY_TAB:
+ mapView->map.toggleDebug();
+ break;
+ case GLFW_KEY_R:
+ if (!mods) mapView->map.resetPosition();
+ break;
+ case GLFW_KEY_N:
+ if (!mods) mapView->map.resetNorth();
+ break;
+ }
+ }
+ }
+
+
+ static void scroll(GLFWwindow *window, double xoffset, double yoffset) {
+ MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
+ double delta = yoffset * 40;
+
+ bool is_wheel = delta != 0 && fmod(delta, 4.000244140625) == 0;
+
+ double absdelta = delta < 0 ? -delta : delta;
+ double scale = 2.0 / (1.0 + exp(-absdelta / 100.0));
+
+ // Make the scroll wheel a bit slower.
+ if (!is_wheel) {
+ scale = (scale - 1.0) / 2.0 + 1.0;
+ }
+
+ // Zooming out.
+ if (delta < 0 && scale != 0) {
+ scale = 1.0 / scale;
+ }
+
+ mapView->map.scaleBy(scale, mapView->last_x, mapView->last_y);
+ }
+
+ static void resize(GLFWwindow *window, int, int) {
+ MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
+
+ int width, height;
+ glfwGetWindowSize(window, &width, &height);
+ int fb_width, fb_height;
+ glfwGetFramebufferSize(window, &fb_width, &fb_height);
+
+ mapView->map.resize(width, height, fb_width, fb_height);
+ }
+
+ static void mouseclick(GLFWwindow *window, int button, int action, int modifiers) {
+ MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
+
+ if (button == GLFW_MOUSE_BUTTON_RIGHT || (button == GLFW_MOUSE_BUTTON_LEFT && modifiers & GLFW_MOD_CONTROL)) {
+ mapView->rotating = action == GLFW_PRESS;
+ if (mapView->rotating) {
+ mapView->start_x = mapView->last_x;
+ mapView->start_y = mapView->last_y;
+ }
+ } else if (button == GLFW_MOUSE_BUTTON_LEFT) {
+ mapView->tracking = action == GLFW_PRESS;
+
+ if (action == GLFW_RELEASE) {
+ double now = glfwGetTime();
+ if (now - mapView->last_click < 0.4) {
+ mapView->map.scaleBy(2.0, mapView->last_x, mapView->last_y);
+ }
+ mapView->last_click = now;
+ }
+ }
+ }
+
+ static void mousemove(GLFWwindow *window, double x, double y) {
+ MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
+ if (mapView->tracking) {
+ mapView->map.moveBy(x - mapView->last_x, y - mapView->last_y);
+ } else if (mapView->rotating) {
+ mapView->map.rotateBy(mapView->start_x, mapView->start_y, mapView->last_x, mapView->last_y, x, y);
+ }
+ mapView->last_x = x;
+ mapView->last_y = y;
+ }
+
+ int run() {
+ while (!glfwWindowShouldClose(window)) {
+
+ f_it = futures.begin();
+ c_it = callbacks.begin();
+
+ while (f_it != futures.end()) {
+ if (f_it->wait_for(zero) == std::future_status::ready) {
+ std::function<void()> cb = *c_it;
+ futures.erase(f_it++);
+ callbacks.erase(c_it++);
+ cb();
+ } else {
+ f_it++;
+ c_it++;
+ }
+ }
+
+ if (dirty) {
+ try {
+ dirty = render();
+ } catch (std::exception& ex) {
+ fprintf(stderr, "exception: %s\n", ex.what());
+ }
+ glfwSwapBuffers(window);
+ fps();
+ }
+
+ if (dirty || futures.size()) {
+ glfwPollEvents();
+ } else {
+ glfwWaitEvents();
+ }
+ }
+
+ return 0;
+ }
+
+ bool render() {
+ return map.render();
+ }
+
+ void fps() {
+ static int frames = 0;
+ static double time_elapsed = 0;
+
+ frames++;
+ double current_time = glfwGetTime();
+
+ if (current_time - time_elapsed >= 1) {
+ fprintf(stderr, "FPS: %4.2f\n", frames / (current_time - time_elapsed));
+ time_elapsed = current_time;
+ frames = 0;
+ }
+ }
+
+ ~MapView() {
+ glfwTerminate();
+ }
+
+public:
+ bool dirty;
+ double last_x, last_y;
+ bool tracking;
+
+ double start_x, start_y;
+ bool rotating;
+
+ double last_click;
+
+ GLFWwindow *window;
+ llmr::Settings_MacOSX settings;
+ llmr::Map map;
+};
+
+MapView *mapView = nullptr;
+
+namespace llmr {
+namespace platform {
+
+void restart(void *) {
+ if (mapView) {
+ mapView->dirty = true;
+ }
+}
+
+
+
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
+ ((std::string*)userp)->append((char*)contents, size *nmemb);
+ return size * nmemb;
+}
+
+void request_http_async(std::string url, std::function<void(Response&)> func) {
+
+ Response res;
+
+ CURL *curl;
+ CURLcode code;
+ curl = curl_easy_init();
+
+ if (curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res.body);
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
+ code = curl_easy_perform(curl);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res.code);
+ curl_easy_cleanup(curl);
+ }
+
+ func(res);
+}
+
+void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb) {
+ futures.emplace_back(std::async(std::launch::async, request_http_async, url, func));
+ callbacks.push_back(cb);
+}
+
+
+double time() {
+ return glfwGetTime();
+}
+
+}
+}
+
+int main() {
+ mapView = new MapView();
+ mapView->init();
+ int ret = mapView->run();
+ mapView->settings.sync();
+ delete mapView;
+ return ret;
+}
diff --git a/linux/settings.cpp b/linux/settings.cpp
new file mode 100644
index 0000000000..d058337f60
--- /dev/null
+++ b/linux/settings.cpp
@@ -0,0 +1,55 @@
+#include "settings.hpp"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/filestream.h"
+#include "rapidjson/document.h"
+#include <stdio.h>
+
+using namespace llmr;
+
+Settings_MacOSX::Settings_MacOSX()
+{
+}
+
+void Settings_MacOSX::load()
+{
+ FILE *settingsFile = fopen("/tmp/llmr-native.json", "r");
+ if (settingsFile != NULL) {
+ rapidjson::FileStream is(settingsFile);
+ rapidjson::Document document;
+ document.ParseStream<0>(is);
+ if (document.IsArray()) {
+ latitude = document[rapidjson::SizeType(0)].GetDouble();
+ latitude = document[1].GetDouble();
+ scale = document[2].GetDouble();
+ angle = document[3].GetDouble();
+ debug = document[4].GetBool();
+ }
+ }
+}
+
+void Settings_MacOSX::persist()
+{
+}
+
+void Settings_MacOSX::sync()
+{
+
+ rapidjson::FileStream s(fopen("/tmp/llmr-native.json", "w"));
+ rapidjson::PrettyWriter<rapidjson::FileStream> writer(s);
+ writer.StartArray();
+ writer.Double(longitude);
+ writer.Double(latitude);
+ writer.Double(scale);
+ writer.Double(angle);
+ writer.Bool(debug);
+ writer.EndArray();
+}
+
+void Settings_MacOSX::clear()
+{
+ longitude = 0;
+ latitude = 0;
+ scale = 0;
+ angle = 0;
+ debug = false;
+}
diff --git a/linux/settings.hpp b/linux/settings.hpp
new file mode 100644
index 0000000000..e507c6bd17
--- /dev/null
+++ b/linux/settings.hpp
@@ -0,0 +1,19 @@
+#ifndef LLMR_MACOSX_SETTINGS
+#define LLMR_MACOSX_SETTINGS
+
+#include <llmr/map/settings.hpp>
+
+namespace llmr {
+
+class Settings_MacOSX : public Settings {
+public:
+ Settings_MacOSX();
+ virtual void load();
+ virtual void persist();
+ virtual void sync();
+ virtual void clear();
+};
+
+}
+
+#endif
diff --git a/llmr.gyp b/llmr.gyp
index 9f6df6dc6b..e8cf6b3a0c 100644
--- a/llmr.gyp
+++ b/llmr.gyp
@@ -5,8 +5,8 @@
],
'targets': [
{
- 'target_name': 'llmr-osx',
- 'product_name': 'llmr-osx',
+ 'target_name': 'llmr-x86',
+ 'product_name': 'llmr-x86',
'type': 'static_library',
'sources': [
'<!@(find src -name "*.cpp")',
diff --git a/macosx/llmr-app.gyp b/macosx/llmr-app.gyp
index 9b589c7230..09c2029f60 100644
--- a/macosx/llmr-app.gyp
+++ b/macosx/llmr-app.gyp
@@ -46,7 +46,7 @@
'CLANG_ENABLE_OBJC_ARC': 'YES'
},
"dependencies": [
- "../llmr.gyp:llmr-osx"
+ "../llmr.gyp:llmr-x86"
]
}
]
diff --git a/macosx/main.mm b/macosx/main.mm
index c9d3045c3a..472799d996 100644
--- a/macosx/main.mm
+++ b/macosx/main.mm
@@ -5,9 +5,9 @@
#include <llmr/platform/platform.hpp>
#include "settings.hpp"
-#include <thread>
+#include <cstdio>
-NSString *const MBXNeedsRenderNotification = @"MBXNeedsRenderNotification";
+#include <thread>
class MapView {
public:
@@ -39,8 +39,14 @@ public:
glfwSetWindowUserPointer(window, this);
glfwMakeContextCurrent(window);
+
+ int width, height;
+ glfwGetWindowSize(window, &width, &height);
+ int fb_width, fb_height;
+ glfwGetFramebufferSize(window, &fb_width, &fb_height);
+
settings.load();
- map.setup();
+ map.setup((double)fb_width / width);
resize(window, 0, 0);
@@ -55,15 +61,9 @@ public:
glfwSetScrollCallback(window, scroll);
glfwSetCharCallback(window, character);
glfwSetKeyCallback(window, key);
-
- [[NSNotificationCenter defaultCenter] addObserverForName:MBXNeedsRenderNotification
- object:nil
- queue:[NSOperationQueue mainQueue]
- usingBlock: ^ (NSNotification * notification) {
- dirty = true;
- }];
}
+
static void character(GLFWwindow *window, unsigned int codepoint) {
}
@@ -110,6 +110,7 @@ public:
scale = 1.0 / scale;
}
+ mapView->map.startScaling();
mapView->map.scaleBy(scale, mapView->last_x, mapView->last_y);
}
@@ -132,11 +133,14 @@ public:
if (mapView->rotating) {
mapView->start_x = mapView->last_x;
mapView->start_y = mapView->last_y;
+ } else {
+ mapView->map.stopRotating();
}
} else if (button == GLFW_MOUSE_BUTTON_LEFT) {
mapView->tracking = action == GLFW_PRESS;
if (action == GLFW_RELEASE) {
+ mapView->map.stopPanning();
double now = glfwGetTime();
if (now - mapView->last_click < 0.4) {
mapView->map.scaleBy(2.0, mapView->last_x, mapView->last_y);
@@ -149,8 +153,14 @@ public:
static void mousemove(GLFWwindow *window, double x, double y) {
MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
if (mapView->tracking) {
- mapView->map.moveBy(x - mapView->last_x, y - mapView->last_y);
+ double dx = x - mapView->last_x;
+ double dy = y - mapView->last_y;
+ if (dx || dy) {
+ mapView->map.startPanning();
+ mapView->map.moveBy(dx, dy);
+ }
} else if (mapView->rotating) {
+ mapView->map.startRotating();
mapView->map.rotateBy(mapView->start_x, mapView->start_y, mapView->last_x, mapView->last_y, x, y);
}
mapView->last_x = x;
@@ -217,24 +227,26 @@ public:
};
MapView *mapView;
-NSOperationQueue *queue = NULL;
+NSOperationQueue *queue;
namespace llmr {
namespace platform {
-void restart(void *) {
+void restart() {
mapView->dirty = true;
+ CGEventRef event = CGEventCreate(NULL);
+ CGEventSetType(event, kCGEventMouseMoved);
+ [[NSApplication sharedApplication] postEvent: [NSEvent eventWithCGEvent:event] atStart:NO];
}
-void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb) {
+void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback) {
if (!queue) {
- queue = [[NSOperationQueue alloc] init];
+ queue = [NSOperationQueue new];
}
NSMutableURLRequest *urlRequest = [NSMutableURLRequest
requestWithURL:[NSURL
- URLWithString:[NSString
- stringWithUTF8String:url.c_str()]]];
+ URLWithString:@(url.c_str())]];
[NSURLConnection
sendAsynchronousRequest:urlRequest
@@ -247,11 +259,10 @@ void request_http(std::string url, std::function<void(Response&)> func, std::fun
res.body = { (const char *)[data bytes], [data length] };
}
- func(res);
+ background_function(res);
dispatch_async(dispatch_get_main_queue(), ^(void) {
- cb();
+ foreground_callback();
});
- [[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
}];
}
diff --git a/resources/style.pbf b/resources/style.pbf
index 1b991386c7..0cec5c8925 100644
--- a/resources/style.pbf
+++ b/resources/style.pbf
Binary files differ
diff --git a/src/geometry/debug_font_buffer.cpp b/src/geometry/debug_font_buffer.cpp
index 4db1aa285a..a91865fb81 100644
--- a/src/geometry/debug_font_buffer.cpp
+++ b/src/geometry/debug_font_buffer.cpp
@@ -10,8 +10,8 @@ using namespace llmr;
void DebugFontBuffer::addText(const char *text, double left, double baseline, double scale) {
uint16_t *coords = nullptr;
- int32_t length = strlen(text);
- for (int32_t i = 0; i < length; ++i) {
+ size_t length = strlen(text);
+ for (size_t i = 0; i < length; ++i) {
if (text[i] < 32 || text[i] > 127) {
continue;
}
diff --git a/src/geometry/point_buffer.cpp b/src/geometry/point_buffer.cpp
new file mode 100644
index 0000000000..00137eae38
--- /dev/null
+++ b/src/geometry/point_buffer.cpp
@@ -0,0 +1,12 @@
+#include <llmr/geometry/point_buffer.hpp>
+#include <llmr/platform/gl.hpp>
+
+#include <cmath>
+
+using namespace llmr;
+
+void PointVertexBuffer::add(vertex_type x, vertex_type y) {
+ vertex_type *vertices = static_cast<vertex_type *>(addElement());
+ vertices[0] = x;
+ vertices[1] = y;
+}
diff --git a/src/geometry/vertex_buffer.cpp b/src/geometry/vertex_buffer.cpp
index 47fe73a2aa..0ce21fd51c 100644
--- a/src/geometry/vertex_buffer.cpp
+++ b/src/geometry/vertex_buffer.cpp
@@ -12,7 +12,7 @@ VertexBuffer::~VertexBuffer() {
}
}
-uint32_t VertexBuffer::index() const {
+size_t VertexBuffer::index() const {
// We store 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
return array.size() / 2;
}
diff --git a/src/map/map.cpp b/src/map/map.cpp
index e837ecffb6..d09b505f47 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -15,19 +15,20 @@ Map::Map(Settings& settings)
painter(transform, settings, style),
min_zoom(0),
max_zoom(14) {
-
- // TODO: Extract that information from the stylesheet instead of hard coding
- style.sprite = std::make_shared<Sprite>();
- style.sprite->load(kSpriteURL);
}
Map::~Map() {
settings.sync();
}
-void Map::setup() {
+void Map::setup(float pixelRatio) {
painter.setup();
+ pixel_ratio = pixelRatio;
+
+ style.sprite = std::make_shared<Sprite>();
+ style.sprite->load(kSpriteURL, pixel_ratio);
+
style.load(resources::style, resources::style_size);
// style.loadJSON((const char *)resources::style, resources::style_size);
}
@@ -54,7 +55,7 @@ void Map::resize(uint32_t width, uint32_t height, uint32_t fb_width, uint32_t fb
transform.height = height;
transform.fb_width = fb_width;
transform.fb_height = fb_height;
- transform.pixelRatio = (double)fb_width / (double)width;
+ transform.pixelRatio = pixel_ratio;
update();
}
@@ -66,6 +67,16 @@ void Map::moveBy(double dx, double dy, double duration) {
settings.persist();
}
+void Map::startPanning() {
+ transform.startPanning();
+ platform::restart();
+}
+
+void Map::stopPanning() {
+ transform.stopPanning();
+ platform::restart();
+}
+
void Map::scaleBy(double ds, double cx, double cy, double duration) {
transform.scaleBy(ds, cx, cy, duration);
style.cascade(transform.getZoom());
@@ -76,6 +87,16 @@ void Map::scaleBy(double ds, double cx, double cy, double duration) {
settings.persist();
}
+void Map::startScaling() {
+ transform.startScaling();
+ platform::restart();
+}
+
+void Map::stopScaling() {
+ transform.stopScaling();
+ platform::restart();
+}
+
void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double ey, double duration) {
transform.rotateBy(cx, cy, sx, sy, ex, ey, duration);
update();
@@ -84,6 +105,16 @@ void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double
settings.persist();
}
+void Map::startRotating() {
+ transform.startRotating();
+ platform::restart();
+}
+
+void Map::stopRotating() {
+ transform.stopRotating();
+ platform::restart();
+}
+
void Map::setLonLat(double lon, double lat, double duration) {
transform.setLonLat(lon, lat, duration);
update();
@@ -92,7 +123,7 @@ void Map::setLonLat(double lon, double lat, double duration) {
settings.persist();
}
-void Map::getLonLat(double &lon, double &lat) const {
+void Map::getLonLat(double& lon, double& lat) const {
transform.getLonLat(lon, lat);
}
@@ -106,7 +137,7 @@ void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) {
settings.persist();
}
-void Map::getLonLatZoom(double &lon, double &lat, double &zoom) const {
+void Map::getLonLatZoom(double& lon, double& lat, double& zoom) const {
transform.getLonLatZoom(lon, lat, zoom);
}
@@ -202,7 +233,7 @@ void Map::cancelAnimations() {
void Map::update() {
updateTiles();
- platform::restart(this);
+ platform::restart();
}
@@ -375,12 +406,12 @@ bool Map::updateTiles() {
}
bool Map::render() {
- bool changed = false;
if (transform.needsAnimation()) {
transform.updateAnimations();
- changed = updateTiles();
}
+ bool changed = updateTiles();
+
painter.clear();
for (Tile::Ptr& tile : tiles) {
@@ -395,14 +426,3 @@ bool Map::render() {
return changed || transform.needsAnimation();
}
-
-void Map::tileLoaded(Tile::Ptr tile) {
- // std::cerr << "loaded " << tile->toString() << std::endl;
- update();
-}
-
-void Map::tileFailed(Tile::Ptr tile) {
- // fprintf(stderr, "[%8zx] tile failed to load %d/%d/%d\n",
- // std::hash<std::thread::id>()(std::this_thread::get_id()),
- // tile->z, tile->x, tile->y);
-}
diff --git a/src/map/tile.cpp b/src/map/tile.cpp
index 9c85da946b..3cc4469bdc 100644
--- a/src/map/tile.cpp
+++ b/src/map/tile.cpp
@@ -4,9 +4,11 @@
#include <llmr/map/vector_tile.hpp>
#include <llmr/geometry/fill_buffer.hpp>
#include <llmr/geometry/line_buffer.hpp>
+#include <llmr/geometry/point_buffer.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/renderer/line_bucket.hpp>
+#include <llmr/renderer/point_bucket.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/util/pbf.hpp>
#include <llmr/util/string.hpp>
@@ -46,6 +48,7 @@ Tile::Tile(ID id, const Style& style)
state(initial),
fillVertexBuffer(std::make_shared<FillVertexBuffer>()),
lineVertexBuffer(std::make_shared<LineVertexBuffer>()),
+ pointVertexBuffer(std::make_shared<PointVertexBuffer>()),
triangleElementsBuffer(std::make_shared<TriangleElementsBuffer>()),
lineElementsBuffer(std::make_shared<LineElementsBuffer>()),
pointElementsBuffer(std::make_shared<PointElementsBuffer>()),
@@ -81,8 +84,7 @@ void Tile::request() {
fprintf(stderr, "tile loading failed\n");
}
}, []() {
- // TODO: Make sure this gets passed the correct map ID/pointer.
- platform::restart(nullptr);
+ platform::restart();
});
}
@@ -160,6 +162,8 @@ std::shared_ptr<Bucket> Tile::createBucket(const VectorTile& tile, const BucketD
return createFillBucket(layer, bucket_desc);
} else if (bucket_desc.type == BucketType::Line) {
return createLineBucket(layer, bucket_desc);
+ } else if (bucket_desc.type == BucketType::Point) {
+ return createPointBucket(layer, bucket_desc);
} else {
// TODO: create other bucket types.
}
@@ -214,3 +218,19 @@ std::shared_ptr<Bucket> Tile::createLineBucket(const VectorTileLayer& layer, con
return bucket;
}
+
+std::shared_ptr<Bucket> Tile::createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
+ std::shared_ptr<PointBucket> bucket = std::make_shared<PointBucket>(pointVertexBuffer, bucket_desc);
+
+ FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
+ for (pbf feature : filtered_layer) {
+ while (feature.next(4)) { // geometry
+ pbf geometry_pbf = feature.message();
+ if (geometry_pbf) {
+ bucket->addGeometry(geometry_pbf);
+ }
+ }
+ }
+
+ return bucket;
+}
diff --git a/src/map/transform.cpp b/src/map/transform.cpp
index 446cc23e24..12c55e1f93 100644
--- a/src/map/transform.cpp
+++ b/src/map/transform.cpp
@@ -1,6 +1,7 @@
#include <llmr/map/transform.hpp>
#include <llmr/util/constants.hpp>
#include <llmr/util/mat4.hpp>
+#include <llmr/util/std.hpp>
#include <llmr/util/math.hpp>
#include <cstdio>
@@ -24,8 +25,8 @@ bool Transform::needsAnimation() const {
}
void Transform::updateAnimations() {
- animations.remove_if([](const util::animation& animation) {
- return animation.update() == util::animation::complete;
+ animations.remove_if([](const std::shared_ptr<util::animation>& animation) {
+ return animation->update() == util::animation::complete;
});
}
@@ -40,8 +41,25 @@ void Transform::moveBy(double dx, double dy, double duration) {
x = xn;
y = yn;
} else {
- animations.emplace_front(x, xn, x, duration);
- animations.emplace_front(y, yn, y, duration);
+ animations.emplace_front(std::make_shared<util::ease_animation>(x, xn, x, duration));
+ animations.emplace_front(std::make_shared<util::ease_animation>(y, yn, y, duration));
+ }
+}
+
+void Transform::startPanning() {
+ stopPanning();
+
+ // Add a 200ms timeout for resetting this to false
+ panning = true;
+ pan_timeout = std::make_shared<util::timeout<bool>>(false, panning, 0.2);
+ animations.emplace_front(pan_timeout);
+}
+
+void Transform::stopPanning() {
+ panning = false;
+ if (pan_timeout) {
+ animations.remove(pan_timeout);
+ pan_timeout.reset();
}
}
@@ -59,6 +77,22 @@ void Transform::scaleBy(double ds, double cx, double cy, double duration) {
setScale(new_scale, cx, cy, duration);
}
+void Transform::startScaling() {
+ stopScaling();
+
+ // Add a 200ms timeout for resetting this to false
+ scaling = true;
+ scale_timeout = std::make_shared<util::timeout<bool>>(false, scaling, 0.2);
+ animations.emplace_front(scale_timeout);
+}
+
+void Transform::stopScaling() {
+ scaling = false;
+ if (scale_timeout) {
+ animations.remove(scale_timeout);
+ scale_timeout.reset();
+ }
+}
void Transform::rotateBy(double anchor_x, double anchor_y, double start_x, double start_y, double end_x, double end_y, double duration) {
double center_x = width / 2, center_y = height / 2;
@@ -94,7 +128,24 @@ void Transform::setAngle(double new_angle, double duration) {
if (duration == 0) {
angle = new_angle;
} else {
- animations.emplace_front(angle, new_angle, angle, duration);
+ animations.emplace_front(std::make_shared<util::ease_animation>(angle, new_angle, angle, duration));
+ }
+}
+
+void Transform::startRotating() {
+ stopRotating();
+
+ // Add a 200ms timeout for resetting this to false
+ rotating = true;
+ rotate_timeout = std::make_shared<util::timeout<bool>>(false, rotating, 0.2);
+ animations.emplace_front(rotate_timeout);
+}
+
+void Transform::stopRotating() {
+ rotating = false;
+ if (rotate_timeout) {
+ animations.remove(rotate_timeout);
+ rotate_timeout.reset();
}
}
@@ -104,9 +155,9 @@ void Transform::setScaleXY(double new_scale, double xn, double yn, double durati
x = xn;
y = yn;
} else {
- animations.emplace_front(scale, new_scale, scale, duration);
- animations.emplace_front(x, xn, x, duration);
- animations.emplace_front(y, yn, y, duration);
+ animations.emplace_front(std::make_shared<util::ease_animation>(scale, new_scale, scale, duration));
+ animations.emplace_front(std::make_shared<util::ease_animation>(x, xn, x, duration));
+ animations.emplace_front(std::make_shared<util::ease_animation>(y, yn, y, duration));
}
const double s = scale * util::tileSize;
diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp
index e64d7114b1..129ddd997f 100644
--- a/src/map/vector_tile.cpp
+++ b/src/map/vector_tile.cpp
@@ -52,13 +52,13 @@ FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, c
// Find out what key/value IDs we need.
auto key_it = std::find(layer.keys.begin(), layer.keys.end(), bucket_desc.source_field);
if (key_it != layer.keys.end()) {
- key = key_it - layer.keys.begin();
+ key = (int32_t)(key_it - layer.keys.begin());
}
for (const Value& source_value : bucket_desc.source_value) {
auto value_it = std::find(layer.values.begin(), layer.values.end(), source_value);
if (value_it != layer.values.end()) {
- values.insert(value_it - layer.values.begin());
+ values.insert((uint32_t)(value_it - layer.values.begin()));
}
}
}
diff --git a/src/renderer/fill_bucket.cpp b/src/renderer/fill_bucket.cpp
index 7932329ba1..644a40450b 100644
--- a/src/renderer/fill_bucket.cpp
+++ b/src/renderer/fill_bucket.cpp
@@ -96,7 +96,6 @@ void FillBucket::tessellate() {
}
hasVertices = false;
-
std::vector<std::vector<ClipperLib::IntPoint>> polygons;
clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftPositive);
clipper.Clear();
diff --git a/src/renderer/line_bucket.cpp b/src/renderer/line_bucket.cpp
index f24c42d5bf..1ceb2cb0a5 100644
--- a/src/renderer/line_bucket.cpp
+++ b/src/renderer/line_bucket.cpp
@@ -103,7 +103,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
nextNormal = util::normal<double>(currentVertex, lastVertex);
}
- size_t start_vertex = vertexBuffer->index();
+ int32_t start_vertex = (int32_t)vertexBuffer->index();
std::vector<TriangleElement> triangle_store;
std::vector<PointElement> point_store;
@@ -174,7 +174,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Add offset square begin cap.
if (!prevVertex && beginCap == CapType::Square) {
// Add first vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * (prevNormal.x + prevNormal.y), flip * (-prevNormal.x + prevNormal.y), // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -182,7 +182,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * (prevNormal.x - prevNormal.y), flip * (prevNormal.x + prevNormal.y), // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -193,7 +193,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Add offset square end cap.
else if (!nextVertex && endCap == CapType::Square) {
// Add first vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
nextNormal.x - flip * nextNormal.y, flip * nextNormal.x + nextNormal.y, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -201,7 +201,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
nextNormal.x + flip * nextNormal.y, -flip * nextNormal.x + nextNormal.y, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -227,7 +227,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
// Add first vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * joinNormal.x, flip * joinNormal.y, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -235,7 +235,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * joinNormal.x, -flip * joinNormal.y, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -251,7 +251,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
else {
// Close up the previous line
// Add first vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * prevNormal.y, -flip * prevNormal.x, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -259,7 +259,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex.
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * prevNormal.y, flip * prevNormal.x, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -289,7 +289,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Start the new quad.
// Add first vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * nextNormal.y, flip * nextNormal.x, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -297,7 +297,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
- e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
+ e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * nextNormal.y, -flip * nextNormal.x, // extrude normal
0, 1, distance) - start_vertex; // texture normal
diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp
index cc1157e252..fee7c6115a 100644
--- a/src/renderer/painter.cpp
+++ b/src/renderer/painter.cpp
@@ -7,6 +7,7 @@
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/renderer/line_bucket.hpp>
+#include <llmr/renderer/point_bucket.hpp>
#include <llmr/map/transform.hpp>
#include <llmr/map/settings.hpp>
@@ -29,6 +30,7 @@ Painter::Painter(Transform& transform, Settings& settings, Style& style)
void Painter::setup() {
setupShaders();
+ assert(pointShader);
assert(plainShader);
assert(outlineShader);
assert(lineShader);
@@ -52,6 +54,7 @@ void Painter::setupShaders() {
lineShader = std::make_unique<LineShader>();
linejoinShader = std::make_unique<LinejoinShader>();
patternShader = std::make_unique<PatternShader>();
+ pointShader = std::make_unique<PointShader>();
}
void Painter::useProgram(uint32_t program) {
@@ -114,7 +117,7 @@ void Painter::drawClippingMask() {
// Draw the clipping mask
plainShader->setColor(1.0f, 0.0f, 1.0f, 1.0f);
- glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
// glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
@@ -360,6 +363,44 @@ void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, cons
}
}
+void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
+ const PointProperties& properties = style.computed.points[layer_name];
+
+ // Abort early.
+ if (!bucket.hasPoints()) return;
+ if (properties.hidden) return;
+
+ Color color = properties.color;
+ color[0] *= properties.opacity;
+ color[1] *= properties.opacity;
+ color[2] *= properties.opacity;
+ color[3] *= properties.opacity;
+
+ std::string sized_image = properties.image;
+ sized_image.append("-");
+ sized_image.append(std::to_string(static_cast<int>(std::round(properties.size))));
+
+ ImagePosition imagePos = style.sprite->getPosition(sized_image, false);
+
+ // fprintf(stderr, "%f/%f => %f/%f\n", imagePos.tl.x, imagePos.tl.y, imagePos.br.x, imagePos.br.y);
+
+ useProgram(pointShader->program);
+ pointShader->setMatrix(matrix);
+ pointShader->setImage(0);
+ pointShader->setColor(color);
+ const float pointSize = properties.size * 1.4142135623730951 * transform.pixelRatio;
+ #if defined(GL_ES_VERSION_2_0)
+ pointShader->setSize(pointSize);
+ #else
+ glPointSize(pointSize);
+ glEnable(GL_POINT_SPRITE);
+ #endif
+ pointShader->setPointTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
+ pointShader->setPointBottomRight({{ imagePos.br.x, imagePos.br.y }});
+ style.sprite->bind(transform.rotating || transform.scaling || transform.panning);
+ bucket.drawPoints(*pointShader);
+}
+
void Painter::renderDebug(const Tile::Ptr& tile) {
// Blend to the front, not the back.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@@ -370,16 +411,16 @@ void Painter::renderDebug(const Tile::Ptr& tile) {
tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 0.0f, 0.0f, 1.0f);
lineWidth(4.0f * transform.pixelRatio);
- glDrawArrays(GL_LINE_STRIP, 0, tileBorderBuffer.index());
+ glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index());
// draw debug info
tile->debugFontArray.bind(*plainShader, tile->debugFontBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
lineWidth(4.0f * transform.pixelRatio);
- glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
+ glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
lineWidth(2.0f * transform.pixelRatio);
- glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
+ glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
// Revert blending mode to blend to the back.
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
@@ -396,7 +437,7 @@ void Painter::renderMatte() {
// Draw the clipping mask
matteArray.bind(*plainShader, matteBuffer, BUFFER_OFFSET(0));
plainShader->setColor(white);
- glDrawArrays(GL_TRIANGLES, 0, matteBuffer.index());
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)matteBuffer.index());
glEnable(GL_STENCIL_TEST);
}
@@ -410,5 +451,5 @@ void Painter::renderBackground() {
// Draw the clipping mask
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
plainShader->setColor(white);
- glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
}
diff --git a/src/renderer/point_bucket.cpp b/src/renderer/point_bucket.cpp
new file mode 100644
index 0000000000..04e9fbe9d8
--- /dev/null
+++ b/src/renderer/point_bucket.cpp
@@ -0,0 +1,52 @@
+#include <llmr/renderer/point_bucket.hpp>
+#include <llmr/geometry/point_buffer.hpp>
+#include <llmr/geometry/elements_buffer.hpp>
+#include <llmr/geometry/geometry.hpp>
+
+#include <llmr/renderer/painter.hpp>
+#include <llmr/style/style.hpp>
+#include <llmr/map/vector_tile.hpp>
+
+#include <llmr/platform/gl.hpp>
+
+#include <cassert>
+
+struct geometry_too_long_exception : std::exception {};
+
+using namespace llmr;
+
+PointBucket::PointBucket(const std::shared_ptr<PointVertexBuffer>& vertexBuffer,
+ const BucketDescription& bucket_desc)
+ : geometry(bucket_desc.geometry),
+ vertexBuffer(vertexBuffer),
+ vertex_start(vertexBuffer->index()) {
+}
+
+void PointBucket::addGeometry(pbf& geom) {
+ Geometry::command cmd;
+ Geometry geometry(geom);
+ int32_t x, y;
+ while ((cmd = geometry.next(x, y)) != Geometry::end) {
+ if (cmd == Geometry::move_to) {
+ vertexBuffer->add(x, y);
+ } else {
+ fprintf(stderr, "other command than move_to in point geometry\n");
+ }
+ }
+
+ vertex_end = vertexBuffer->index();
+}
+
+void PointBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
+ painter.renderPoint(*this, layer_name, id);
+}
+
+bool PointBucket::hasPoints() const {
+ return vertex_end > 0;
+}
+
+void PointBucket::drawPoints(PointShader& shader) {
+ char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer->itemSize);
+ array.bind(shader, *vertexBuffer, vertex_index);
+ glDrawArrays(GL_POINTS, 0, (GLsizei)(vertex_end - vertex_start));
+}
diff --git a/src/renderer/shader-point.cpp b/src/renderer/shader-point.cpp
new file mode 100644
index 0000000000..835728417d
--- /dev/null
+++ b/src/renderer/shader-point.cpp
@@ -0,0 +1,74 @@
+#include <llmr/renderer/shader-point.hpp>
+#include <llmr/shader/shaders.hpp>
+#include <llmr/platform/gl.hpp>
+
+#include <cstdio>
+
+using namespace llmr;
+
+PointShader::PointShader()
+ : Shader(
+ shaders[POINT_SHADER].vertex,
+ shaders[POINT_SHADER].fragment
+ ) {
+ if (!valid) {
+ fprintf(stderr, "invalid point shader\n");
+ return;
+ }
+
+ a_pos = glGetAttribLocation(program, "a_pos");
+
+ u_matrix = glGetUniformLocation(program, "u_matrix");
+ u_color = glGetUniformLocation(program, "u_color");
+ u_size = glGetUniformLocation(program, "u_size");
+ u_point_tl = glGetUniformLocation(program, "u_tl");
+ u_point_br = glGetUniformLocation(program, "u_br");
+
+ // fprintf(stderr, "PointShader:\n");
+ // fprintf(stderr, " - u_matrix: %d\n", u_matrix);
+ // fprintf(stderr, " - u_color: %d\n", u_color);
+ // fprintf(stderr, " - u_size: %d\n", u_size);
+ // fprintf(stderr, " - u_point_tl: %d\n", u_point_tl);
+ // fprintf(stderr, " - u_point_br: %d\n", u_point_br);
+ // fprintf(stderr, " - u_image: %d\n", u_image);
+}
+
+void PointShader::bind(char *offset) {
+ glEnableVertexAttribArray(a_pos);
+ glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset);
+}
+
+void PointShader::setImage(int32_t new_image) {
+ if (image != new_image) {
+ glUniform1i(u_image, new_image);
+ image = new_image;
+ }
+}
+
+void PointShader::setColor(const std::array<float, 4>& new_color) {
+ if (color != new_color) {
+ glUniform4fv(u_color, 1, new_color.data());
+ color = new_color;
+ }
+}
+
+void PointShader::setSize(float new_size) {
+ if (size != new_size) {
+ glUniform1f(u_size, new_size);
+ size = new_size;
+ }
+}
+
+void PointShader::setPointTopLeft(const std::array<float, 2>& new_point_tl) {
+ if (point_tl != new_point_tl) {
+ glUniform2fv(u_point_tl, 1, new_point_tl.data());
+ point_tl = new_point_tl;
+ }
+}
+
+void PointShader::setPointBottomRight(const std::array<float, 2>& new_point_br) {
+ if (point_br != new_point_br) {
+ glUniform2fv(u_point_br, 1, new_point_br.data());
+ point_br = new_point_br;
+ }
+}
diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp
index cdb8e9a9f6..679931004f 100644
--- a/src/renderer/shader.cpp
+++ b/src/renderer/shader.cpp
@@ -105,8 +105,8 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) {
*shader = glCreateShader(type);
-#if defined(EMSCRIPTEN) || defined(GL_ES_VERSION_2_0)
- // Add WebGL GLSL / OpenGL ES precision premable
+#if defined(GL_ES_VERSION_2_0)
+ // Add OpenGL ES precision premable
const GLchar *preamble = "precision highp float;\n\n";
#else
// Desktop GLSL
diff --git a/src/shader/point.fragment.glsl b/src/shader/point.fragment.glsl
new file mode 100644
index 0000000000..2f099feb79
--- /dev/null
+++ b/src/shader/point.fragment.glsl
@@ -0,0 +1,21 @@
+#define root2 1.4142135623730951
+
+uniform sampler2D u_image;
+uniform vec2 u_tl;
+uniform vec2 u_br;
+uniform vec4 u_color;
+
+uniform vec2 pos;
+uniform float inbounds;
+uniform vec4 color;
+
+void main() {
+ vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;
+
+ float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *
+ (1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));
+
+ vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;
+
+ gl_FragColor = color * u_color;
+}
diff --git a/src/shader/point.vertex.glsl b/src/shader/point.vertex.glsl
new file mode 100644
index 0000000000..4ccc52663b
--- /dev/null
+++ b/src/shader/point.vertex.glsl
@@ -0,0 +1,9 @@
+attribute vec2 a_pos;
+
+uniform mat4 u_matrix;
+uniform float u_size;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
+ gl_PointSize = u_size;
+}
diff --git a/src/shader/shaders.cpp b/src/shader/shaders.cpp
index 203f143ca1..638c554cf5 100644
--- a/src/shader/shaders.cpp
+++ b/src/shader/shaders.cpp
@@ -23,5 +23,9 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
{
"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
"uniform vec4 u_color;\n\nvoid main() {\n gl_FragColor = u_color;\n}\n",
+ },
+ {
+ "attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
+ "#define root2 1.4142135623730951\n\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\n\nuniform vec2 pos;\nuniform float inbounds;\nuniform vec4 color;\n\nvoid main() {\n vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;\n\n float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *\n (1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));\n\n vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;\n\n gl_FragColor = color * u_color;\n}\n",
}
};
diff --git a/src/style/resources.cpp b/src/style/resources.cpp
index 15b6aa040a..3b26d8c2d2 100644
--- a/src/style/resources.cpp
+++ b/src/style/resources.cpp
@@ -33,42 +33,83 @@ const unsigned char resources::style[] = {
108, 97, 110, 100, 117, 115, 101, 42, 5, 99, 108, 97, 115, 115, 50, 12,
10, 10, 105, 110, 100, 117, 115, 116, 114, 105, 97, 108, 10, 31, 10, 8,
98, 117, 105, 108, 100, 105, 110, 103, 16, 1, 26, 7, 115, 116, 114, 101,
- 101, 116, 115, 34, 8, 98, 117, 105, 108, 100, 105, 110, 103, 10, 48, 10,
- 7, 97, 108, 99, 111, 104, 111, 108, 16, 3, 26, 7, 115, 116, 114, 101,
- 101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98, 101, 108, 42, 4,
- 116, 121, 112, 101, 50, 9, 10, 7, 65, 108, 99, 111, 104, 111, 108, 18,
- 12, 10, 4, 112, 97, 114, 107, 18, 4, 112, 97, 114, 107, 18, 12, 10,
- 4, 119, 111, 111, 100, 18, 4, 119, 111, 111, 100, 18, 14, 10, 5, 119,
- 97, 116, 101, 114, 18, 5, 119, 97, 116, 101, 114, 18, 20, 10, 8, 98,
- 117, 105, 108, 100, 105, 110, 103, 18, 8, 98, 117, 105, 108, 100, 105, 110,
- 103, 18, 28, 10, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105, 116, 101,
- 100, 18, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105, 116, 101, 100, 18,
- 28, 10, 12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 18,
- 12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 18, 24, 10,
- 10, 114, 111, 97, 100, 95, 108, 97, 114, 103, 101, 18, 10, 114, 111, 97,
- 100, 95, 108, 97, 114, 103, 101, 18, 18, 10, 7, 97, 108, 99, 111, 104,
- 111, 108, 18, 7, 97, 108, 99, 111, 104, 111, 108, 26, 220, 2, 10, 7,
- 100, 101, 102, 97, 117, 108, 116, 18, 27, 10, 4, 112, 97, 114, 107, 34,
- 8, 8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 159, 223, 200, 66, 4,
- 112, 97, 114, 107, 18, 31, 10, 4, 119, 111, 111, 100, 34, 8, 8, 2,
- 18, 4, 0, 0, 128, 63, 45, 255, 102, 170, 51, 58, 8, 8, 2, 18,
- 4, 205, 204, 204, 61, 18, 39, 10, 5, 119, 97, 116, 101, 114, 34, 8,
- 8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 233, 224, 186, 53, 255, 230,
- 182, 115, 66, 10, 119, 97, 116, 101, 114, 95, 101, 116, 115, 121, 18, 51,
- 10, 8, 98, 117, 105, 108, 100, 105, 110, 103, 34, 8, 8, 2, 18, 4,
- 0, 0, 128, 63, 45, 255, 0, 0, 0, 58, 24, 8, 4, 18, 20, 0,
- 0, 80, 65, 0, 0, 0, 0, 205, 204, 204, 61, 0, 0, 0, 0, 205,
- 204, 204, 61, 26, 41, 10, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105,
- 116, 101, 100, 29, 255, 187, 187, 187, 34, 20, 8, 3, 18, 16, 0, 0,
- 0, 0, 0, 0, 128, 63, 0, 0, 240, 65, 0, 0, 128, 63, 26, 65,
- 10, 12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 29, 255,
- 153, 153, 153, 34, 44, 8, 3, 18, 40, 0, 0, 0, 0, 0, 0, 0,
- 63, 0, 0, 80, 65, 0, 0, 0, 63, 0, 0, 128, 65, 0, 0, 0,
- 64, 0, 0, 160, 65, 0, 0, 0, 66, 0, 0, 240, 65, 0, 0, 0,
- 66, 26, 71, 10, 10, 114, 111, 97, 100, 95, 108, 97, 114, 103, 101, 29,
- 255, 102, 102, 102, 34, 52, 8, 3, 18, 48, 0, 0, 0, 0, 0, 0,
- 0, 63, 0, 0, 48, 65, 0, 0, 0, 63, 0, 0, 80, 65, 0, 0,
- 128, 63, 0, 0, 128, 65, 0, 0, 128, 64, 0, 0, 160, 65, 0, 0,
- 128, 66, 0, 0, 240, 65, 0, 0, 128, 66
+ 101, 116, 115, 34, 8, 98, 117, 105, 108, 100, 105, 110, 103, 10, 52, 10,
+ 11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111, 105, 16, 3, 26, 7,
+ 115, 116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98,
+ 101, 108, 42, 4, 116, 121, 112, 101, 50, 9, 10, 7, 65, 108, 99, 111,
+ 104, 111, 108, 10, 46, 10, 8, 99, 97, 102, 101, 95, 112, 111, 105, 16,
+ 3, 26, 7, 115, 116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95,
+ 108, 97, 98, 101, 108, 42, 4, 116, 121, 112, 101, 50, 6, 10, 4, 67,
+ 97, 102, 101, 10, 52, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112,
+ 111, 105, 16, 3, 26, 7, 115, 116, 114, 101, 101, 116, 115, 34, 9, 112,
+ 111, 105, 95, 108, 97, 98, 101, 108, 42, 4, 116, 121, 112, 101, 50, 9,
+ 10, 7, 69, 109, 98, 97, 115, 115, 121, 10, 46, 10, 8, 112, 97, 114,
+ 107, 95, 112, 111, 105, 16, 3, 26, 7, 115, 116, 114, 101, 101, 116, 115,
+ 34, 9, 112, 111, 105, 95, 108, 97, 98, 101, 108, 42, 4, 116, 121, 112,
+ 101, 50, 6, 10, 4, 80, 97, 114, 107, 10, 58, 10, 14, 114, 101, 115,
+ 116, 97, 117, 114, 97, 110, 116, 95, 112, 111, 105, 16, 3, 26, 7, 115,
+ 116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98, 101,
+ 108, 42, 4, 116, 121, 112, 101, 50, 12, 10, 10, 82, 101, 115, 116, 97,
+ 117, 114, 97, 110, 116, 18, 12, 10, 4, 112, 97, 114, 107, 18, 4, 112,
+ 97, 114, 107, 18, 12, 10, 4, 119, 111, 111, 100, 18, 4, 119, 111, 111,
+ 100, 18, 14, 10, 5, 119, 97, 116, 101, 114, 18, 5, 119, 97, 116, 101,
+ 114, 18, 20, 10, 8, 98, 117, 105, 108, 100, 105, 110, 103, 18, 8, 98,
+ 117, 105, 108, 100, 105, 110, 103, 18, 28, 10, 12, 114, 111, 97, 100, 95,
+ 108, 105, 109, 105, 116, 101, 100, 18, 12, 114, 111, 97, 100, 95, 108, 105,
+ 109, 105, 116, 101, 100, 18, 28, 10, 12, 114, 111, 97, 100, 95, 114, 101,
+ 103, 117, 108, 97, 114, 18, 12, 114, 111, 97, 100, 95, 114, 101, 103, 117,
+ 108, 97, 114, 18, 24, 10, 10, 114, 111, 97, 100, 95, 108, 97, 114, 103,
+ 101, 18, 10, 114, 111, 97, 100, 95, 108, 97, 114, 103, 101, 18, 26, 10,
+ 11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111, 105, 18, 11, 97, 108,
+ 99, 111, 104, 111, 108, 95, 112, 111, 105, 18, 20, 10, 8, 99, 97, 102,
+ 101, 95, 112, 111, 105, 18, 8, 99, 97, 102, 101, 95, 112, 111, 105, 18,
+ 26, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112, 111, 105, 18, 11,
+ 101, 109, 98, 97, 115, 115, 121, 95, 112, 111, 105, 18, 20, 10, 8, 112,
+ 97, 114, 107, 95, 112, 111, 105, 18, 8, 112, 97, 114, 107, 95, 112, 111,
+ 105, 18, 32, 10, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95,
+ 112, 111, 105, 18, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95,
+ 112, 111, 105, 26, 160, 5, 10, 7, 100, 101, 102, 97, 117, 108, 116, 18,
+ 27, 10, 4, 112, 97, 114, 107, 34, 8, 8, 2, 18, 4, 0, 0, 128,
+ 63, 45, 255, 159, 223, 200, 66, 4, 112, 97, 114, 107, 18, 31, 10, 4,
+ 119, 111, 111, 100, 34, 8, 8, 2, 18, 4, 0, 0, 128, 63, 45, 255,
+ 102, 170, 51, 58, 8, 8, 2, 18, 4, 205, 204, 204, 61, 18, 39, 10,
+ 5, 119, 97, 116, 101, 114, 34, 8, 8, 2, 18, 4, 0, 0, 128, 63,
+ 45, 255, 233, 224, 186, 53, 255, 230, 182, 115, 66, 10, 119, 97, 116, 101,
+ 114, 95, 101, 116, 115, 121, 18, 51, 10, 8, 98, 117, 105, 108, 100, 105,
+ 110, 103, 34, 8, 8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 0, 0,
+ 0, 58, 24, 8, 4, 18, 20, 0, 0, 80, 65, 0, 0, 0, 0, 205,
+ 204, 204, 61, 0, 0, 0, 0, 205, 204, 204, 61, 26, 41, 10, 12, 114,
+ 111, 97, 100, 95, 108, 105, 109, 105, 116, 101, 100, 29, 255, 187, 187, 187,
+ 34, 20, 8, 3, 18, 16, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0,
+ 240, 65, 0, 0, 128, 63, 26, 65, 10, 12, 114, 111, 97, 100, 95, 114,
+ 101, 103, 117, 108, 97, 114, 29, 255, 153, 153, 153, 34, 44, 8, 3, 18,
+ 40, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 80, 65, 0, 0, 0,
+ 63, 0, 0, 128, 65, 0, 0, 0, 64, 0, 0, 160, 65, 0, 0, 0,
+ 66, 0, 0, 240, 65, 0, 0, 0, 66, 26, 71, 10, 10, 114, 111, 97,
+ 100, 95, 108, 97, 114, 103, 101, 29, 255, 102, 102, 102, 34, 52, 8, 3,
+ 18, 48, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 48, 65, 0, 0,
+ 0, 63, 0, 0, 80, 65, 0, 0, 128, 63, 0, 0, 128, 65, 0, 0,
+ 128, 64, 0, 0, 160, 65, 0, 0, 128, 66, 0, 0, 240, 65, 0, 0,
+ 128, 66, 34, 68, 10, 11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111,
+ 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65,
+ 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 12, 97, 108, 99, 111,
+ 104, 111, 108, 45, 115, 104, 111, 112, 34, 57, 10, 8, 99, 97, 102, 101,
+ 95, 112, 111, 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0,
+ 0, 144, 65, 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0,
+ 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 4, 99,
+ 97, 102, 101, 34, 63, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112,
+ 111, 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144,
+ 65, 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0,
+ 0, 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 7, 101, 109, 98,
+ 97, 115, 115, 121, 34, 57, 10, 8, 112, 97, 114, 107, 95, 112, 111, 105,
+ 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65, 50,
+ 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0, 128,
+ 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 4, 112, 97, 114, 107, 34,
+ 69, 10, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95, 112, 111,
+ 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65,
+ 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 10, 114, 101, 115, 116,
+ 97, 117, 114, 97, 110, 116
};
const unsigned long resources::style_size = sizeof(resources::style);
diff --git a/src/style/sprite.cpp b/src/style/sprite.cpp
index ceef267392..70626ef396 100644
--- a/src/style/sprite.cpp
+++ b/src/style/sprite.cpp
@@ -23,25 +23,32 @@ ImagePosition::ImagePosition(const vec2<uint16_t>& size, vec2<float> tl, vec2<fl
br(br) {}
+Sprite::~Sprite() {
+ if (img) {
+ free(img);
+ }
+}
Sprite::operator bool() const {
std::lock_guard<std::mutex> lock(mtx);
return loaded;
}
-void Sprite::load(const std::string& base_url) {
+void Sprite::load(const std::string& base_url, float pixelRatio) {
std::shared_ptr<Sprite> sprite = shared_from_this();
auto complete = [sprite]() {
std::lock_guard<std::mutex> lock(sprite->mtx);
- if (sprite->img.size() && sprite->pos.size()) {
+ if (sprite->img && sprite->pos.size()) {
sprite->loaded = true;
- platform::restart(NULL);
+ platform::restart();
fprintf(stderr, "sprite loaded\n");
}
};
- platform::request_http(base_url + ".json", [sprite](const platform::Response & res) {
+ std::string suffix = (pixelRatio > 1 ? "@2x" : "");
+
+ platform::request_http(base_url + suffix + ".json", [sprite](const platform::Response & res) {
if (res.code == 200) {
sprite->parseJSON(res.body);
} else {
@@ -49,7 +56,7 @@ void Sprite::load(const std::string& base_url) {
}
}, complete);
- platform::request_http(base_url + ".png", [sprite](const platform::Response & res) {
+ platform::request_http(base_url + suffix + ".png", [sprite](const platform::Response & res) {
if (res.code == 200) {
sprite->loadImage(res.body);
} else {
@@ -173,11 +180,11 @@ void Sprite::loadImage(const std::string& data) {
png_read_update_info(png, info);
- unsigned int rowbytes = png_get_rowbytes(png, info);
+ png_size_t rowbytes = png_get_rowbytes(png, info);
assert(width * 4 == rowbytes);
- img.resize(width * height * 4);
- char *surface = const_cast<char *>(img.data());
+ img = (char *)malloc(width * height * 4);
+ char *surface = img;
assert(surface);
png_bytep row_pointers[height];
@@ -192,7 +199,10 @@ void Sprite::loadImage(const std::string& data) {
} catch (std::exception& e) {
fprintf(stderr, "loading PNG failed: %s\n", e.what());
png_destroy_read_struct(&png, &info, nullptr);
- img.clear();
+ if (img) {
+ free(img);
+ img = nullptr;
+ }
width = 0;
height = 0;
}
@@ -234,11 +244,13 @@ void Sprite::bind(bool linear) {
if (!texture) {
glGenTextures(1, &texture);
+ glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data());
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
} else {
+ glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
}
diff --git a/src/style/style.cpp b/src/style/style.cpp
index 0e8e9f3b69..28e740e4cd 100644
--- a/src/style/style.cpp
+++ b/src/style/style.cpp
@@ -12,9 +12,10 @@ Style::Style() {
void Style::reset() {
computed.fills.clear();
computed.lines.clear();
+ computed.points.clear();
}
-void Style::load(const uint8_t *const data, uint32_t bytes) {
+void Style::load(const uint8_t *const data, size_t bytes) {
pbf style(data, bytes);
while (style.next()) {
@@ -89,10 +90,12 @@ std::pair<std::string, ClassDescription> Style::parseClass(pbf data) {
while (data.next()) {
if (data.tag == 1) { // name
name = data.string();
- } else if (data.tag == 2) { // fill_style
+ } else if (data.tag == 2) { // fill style
klass.fill.insert(parseFillClass(data.message()));
- } else if (data.tag == 3) { // stroke_style
+ } else if (data.tag == 3) { // line style
klass.line.insert(parseLineClass(data.message()));
+ } else if (data.tag == 4) { // point style
+ klass.point.insert(parsePointClass(data.message()));
} else {
data.skip();
}
@@ -159,6 +162,30 @@ std::pair<std::string, LineClass> Style::parseLineClass(pbf data) {
return { name, stroke };
}
+std::pair<std::string, PointClass> Style::parsePointClass(pbf data) {
+ PointClass point;
+ std::string name;
+
+ while (data.next()) {
+ if (data.tag == 1) { // name
+ name = data.string();
+ } else if (data.tag == 2) { // hidden
+ point.hidden = parseProperty<bool>(data.message());
+ } else if (data.tag == 3) { // color
+ point.color = parseColor(data);
+ } else if (data.tag == 4) { // size
+ point.size = parseProperty<float>(data.message());
+ } else if (data.tag == 6) { // opacity
+ point.opacity = parseProperty<float>(data.message());
+ } else if (data.tag == 8) { // image
+ point.image = data.string();
+ } else {
+ data.skip();
+ }
+ }
+
+ return { name, point };
+}
Color Style::parseColor(pbf& data) {
uint32_t rgba = data.fixed<uint32_t, 4>();
@@ -240,6 +267,21 @@ void Style::cascade(float z) {
stroke.color = layer.color;
stroke.opacity = layer.opacity(z);
}
+
+ // Cascade point classes
+ for (const auto& point_pair : sheetClass.point) {
+ const std::string& layer_name = point_pair.first;
+ const llmr::PointClass& layer = point_pair.second;
+
+ // TODO: This should be restricted to point styles that have actual
+ // values so as to not override with default values.
+ llmr::PointProperties& point = computed.points[layer_name];
+ point.hidden = layer.hidden(z);
+ point.color = layer.color;
+ point.size = layer.size(z);
+ point.opacity = layer.opacity(z);
+ point.image = layer.image;
+ }
}
}
diff --git a/src/util/animation.cpp b/src/util/animation.cpp
index a2c3e778e8..8624fc22ab 100644
--- a/src/util/animation.cpp
+++ b/src/util/animation.cpp
@@ -6,16 +6,17 @@ using namespace llmr::util;
UnitBezier ease(0.25, 0.1, 0.25, 1);
-animation::animation(double from, double to, double &value, double duration)
- : start(platform::time()),
- duration(duration),
+animation::~animation() {}
+
+ease_animation::ease_animation(double from, double to, double &value, double duration)
+ : animation(duration),
from(from),
to(to),
value(value) {
}
-animation::state animation::update() const {
- double t = (platform::time() - start) / duration;
+animation::state ease_animation::update() const {
+ double t = progress();
if (t >= 1) {
value = to;
return complete;