diff options
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 @@ -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 @@ -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 Binary files differindex 1b991386c7..0cec5c8925 100644 --- a/resources/style.pbf +++ b/resources/style.pbf 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; |