From 828d8ba26f24a733f5cf50f6e47c489ad9a17a03 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 14 Feb 2014 18:13:12 -0800 Subject: [PATCH] refs #60: first cut of POI rendering * add basic marker style to stylesheet * pass through marker style to PBF conversion * marker/point tile parsing, bucket, buffer, shaders, etc. * new sprite hosting for 1x/2x variants * sprite load accepts screen scale argument --- bin/convert-style.js | 38 +++++++++--- bin/style.js | 2 + include/llmr/geometry/point_buffer.hpp | 19 ++++++ include/llmr/map/map.hpp | 4 +- include/llmr/map/tile.hpp | 3 + include/llmr/platform/platform.hpp | 2 +- include/llmr/renderer/painter.hpp | 4 ++ include/llmr/renderer/point_bucket.hpp | 56 +++++++++++++++++ include/llmr/renderer/shader-point.hpp | 37 +++++++++++ include/llmr/shader/shaders.hpp | 1 + include/llmr/style/class_description.hpp | 1 + include/llmr/style/properties.hpp | 20 ++++-- include/llmr/style/sprite.hpp | 2 +- include/llmr/style/style.hpp | 2 + ios/MBXViewController.mm | 2 +- resources/style.pbf | Bin 1050 -> 1090 bytes src/geometry/point_buffer.cpp | 12 ++++ src/map/map.cpp | 13 ++-- src/map/tile.cpp | 21 +++++++ src/renderer/painter.cpp | 33 ++++++++++ src/renderer/point_bucket.cpp | 75 +++++++++++++++++++++++ src/renderer/shader-point.cpp | 66 ++++++++++++++++++++ src/shader/point.fragment.glsl | 25 ++++++++ src/shader/point.vertex.glsl | 10 +++ src/shader/shaders.cpp | 4 ++ src/style/resources.cpp | 7 ++- src/style/sprite.cpp | 8 ++- src/style/style.cpp | 46 +++++++++++++- 28 files changed, 483 insertions(+), 30 deletions(-) create mode 100644 include/llmr/geometry/point_buffer.hpp create mode 100644 include/llmr/renderer/point_bucket.hpp create mode 100644 include/llmr/renderer/shader-point.hpp create mode 100644 src/geometry/point_buffer.cpp create mode 100644 src/renderer/point_bucket.cpp create mode 100644 src/renderer/shader-point.cpp create mode 100644 src/shader/point.fragment.glsl create mode 100644 src/shader/point.vertex.glsl diff --git a/bin/convert-style.js b/bin/convert-style.js index 57aee0136..a79f46cf7 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 1596e30a6..0383c3210 100644 --- a/bin/style.js +++ b/bin/style.js @@ -140,6 +140,8 @@ module.exports = { }, "alcohol": { "type": "marker", + "color": "#999999", + "size": 24, "image": "alcohol-shop" } } diff --git a/include/llmr/geometry/point_buffer.hpp b/include/llmr/geometry/point_buffer.hpp new file mode 100644 index 000000000..8c01a20ee --- /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/map/map.hpp b/include/llmr/map/map.hpp index 84cb4d12e..ada84f05b 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(); @@ -75,6 +75,8 @@ private: int32_t min_zoom; int32_t max_zoom; + float pixel_ratio; + std::forward_list> tiles; std::forward_list> historic_tiles; }; diff --git a/include/llmr/map/tile.hpp b/include/llmr/map/tile.hpp index 99286a953..dd107eb7b 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 createBucket(const VectorTile& tile, const BucketDescription& bucket_desc); std::shared_ptr createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); std::shared_ptr createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); + std::shared_ptr createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); void cancel(); @@ -77,6 +79,7 @@ public: std::shared_ptr fillVertexBuffer; std::shared_ptr lineVertexBuffer; + std::shared_ptr pointVertexBuffer; std::shared_ptr triangleElementsBuffer; std::shared_ptr lineElementsBuffer; diff --git a/include/llmr/platform/platform.hpp b/include/llmr/platform/platform.hpp index f56e738ff..bf40ae85f 100644 --- a/include/llmr/platform/platform.hpp +++ b/include/llmr/platform/platform.hpp @@ -6,7 +6,7 @@ #include #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 { diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp index 47d6a500c..165da3724 100644 --- a/include/llmr/renderer/painter.hpp +++ b/include/llmr/renderer/painter.hpp @@ -12,6 +12,7 @@ #include #include #include +#include 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; std::unique_ptr linejoinShader; std::unique_ptr patternShader; + std::unique_ptr 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 000000000..4f9a3d353 --- /dev/null +++ b/include/llmr/renderer/point_bucket.hpp @@ -0,0 +1,56 @@ +#ifndef LLMR_RENDERER_POINTBUCKET +#define LLMR_RENDERER_POINTBUCKET + +#include +#include +#include +#include + +#include +#include + +#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 { + typedef ElementGroup group_type; +public: + PointBucket(const std::shared_ptr& vertexBuffer, + const std::shared_ptr& elementsBuffer, + const BucketDescription& bucket_desc); + + virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id); + + void addGeometry(pbf& data); + void addGeometry(Coordinate& point); + + bool hasPoints() const; + + void drawPoints(PointShader& shader); + +public: + const BucketGeometryDescription geometry; + +private: + std::shared_ptr vertexBuffer; + std::shared_ptr elementsBuffer; + + const uint32_t vertex_start; + const uint32_t elements_start; + + std::vector groups; +}; + +} + +#endif diff --git a/include/llmr/renderer/shader-point.hpp b/include/llmr/renderer/shader-point.hpp new file mode 100644 index 000000000..12605fb5a --- /dev/null +++ b/include/llmr/renderer/shader-point.hpp @@ -0,0 +1,37 @@ +#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 setColor(const std::array& color); + void setPointTopLeft(const std::array& point_tl); + void setPointBottomRight(const std::array& point_br); + void setSize(float size); + +private: + int32_t a_pos = -1; + + std::array color = {}; + int32_t u_color = -1; + + std::array point_tl = {}; + int32_t u_point_tl = -1; + + std::array 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 c0f02337a..6312cc694 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 ce5b172b2..eab7b46a9 100644 --- a/include/llmr/style/class_description.hpp +++ b/include/llmr/style/class_description.hpp @@ -12,6 +12,7 @@ class ClassDescription { public: std::map fill; std::map line; + std::map point; }; diff --git a/include/llmr/style/properties.hpp b/include/llmr/style/properties.hpp index 4d4ce6895..79b906cd5 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 hidden; + FunctionProperty size; + Color color = {{ 0, 0, 0, 1 }}; + FunctionProperty 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 hidden; FunctionProperty width; @@ -70,7 +84,6 @@ struct LineClass { FunctionProperty 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 hidden; Winding winding = Winding::NonZero; diff --git a/include/llmr/style/sprite.hpp b/include/llmr/style/sprite.hpp index 0fcd15c05..b8b7b7a0b 100644 --- a/include/llmr/style/sprite.hpp +++ b/include/llmr/style/sprite.hpp @@ -34,7 +34,7 @@ public: class Sprite : public std::enable_shared_from_this { public: - void load(const std::string& base_url); + void load(const std::string& base_url, float pixelRatio = 1); void bind(bool linear = false); ImagePosition getPosition(const std::string& name, bool repeating = false); diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp index 04b7e2216..d1212f9fe 100644 --- a/include/llmr/style/style.hpp +++ b/include/llmr/style/style.hpp @@ -36,6 +36,7 @@ private: static std::pair parseClass(pbf data); static std::pair parseFillClass(pbf data); static std::pair parseLineClass(pbf data); + static std::pair parsePointClass(pbf data); template static FunctionProperty parseProperty(pbf data); static Color parseColor(pbf& data); @@ -53,6 +54,7 @@ public: struct { std::map fills; std::map lines; + std::map points; } computed; }; diff --git a/ios/MBXViewController.mm b/ios/MBXViewController.mm index c5d6effa1..a687350af 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); diff --git a/resources/style.pbf b/resources/style.pbf index 1b991386c7f4713760e9be6c3c5b330daf9acda1..46e117e52dcff7d78ee2fbec60fb4842abc611c5 100644 GIT binary patch delta 56 zcmbQmafoBX9ww$1=FR( HBfkIu{YVku delta 15 XcmX@aF^glv9ww$cOq=&H#V`T@Fkc0W diff --git a/src/geometry/point_buffer.cpp b/src/geometry/point_buffer.cpp new file mode 100644 index 000000000..00137eae3 --- /dev/null +++ b/src/geometry/point_buffer.cpp @@ -0,0 +1,12 @@ +#include +#include + +#include + +using namespace llmr; + +void PointVertexBuffer::add(vertex_type x, vertex_type y) { + vertex_type *vertices = static_cast(addElement()); + vertices[0] = x; + vertices[1] = y; +} diff --git a/src/map/map.cpp b/src/map/map.cpp index e837ecffb..35dcb471f 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(); - 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(); + 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(); } diff --git a/src/map/tile.cpp b/src/map/tile.cpp index 902c2c03b..0d7258b24 100644 --- a/src/map/tile.cpp +++ b/src/map/tile.cpp @@ -4,9 +4,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -45,6 +47,7 @@ Tile::Tile(ID id, const Style& style) state(initial), fillVertexBuffer(std::make_shared()), lineVertexBuffer(std::make_shared()), + pointVertexBuffer(std::make_shared()), triangleElementsBuffer(std::make_shared()), lineElementsBuffer(std::make_shared()), pointElementsBuffer(std::make_shared()), @@ -159,6 +162,8 @@ std::shared_ptr 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. } @@ -200,3 +205,19 @@ std::shared_ptr Tile::createLineBucket(const VectorTileLayer& layer, con return bucket; } + +std::shared_ptr Tile::createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + std::shared_ptr bucket = std::make_shared(pointVertexBuffer, pointElementsBuffer, 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/renderer/painter.cpp b/src/renderer/painter.cpp index 73f96a55f..a53496cb3 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -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(); linejoinShader = std::make_unique(); patternShader = std::make_unique(); + pointShader = std::make_unique(); } void Painter::useProgram(uint32_t program) { @@ -377,6 +380,36 @@ 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(std::round(properties.size)))); + + ImagePosition imagePos = style.sprite->getPosition(sized_image, false); + + useProgram(pointShader->program); + pointShader->setMatrix(matrix); + pointShader->setColor(color); + pointShader->setSize(properties.size * transform.pixelRatio); + pointShader->setPointTopLeft({{ imagePos.tl.x, imagePos.tl.y }}); + pointShader->setPointBottomRight({{ imagePos.br.x, imagePos.br.y }}); + style.sprite->bind(true); + + 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); diff --git a/src/renderer/point_bucket.cpp b/src/renderer/point_bucket.cpp new file mode 100644 index 000000000..fcdcf96e9 --- /dev/null +++ b/src/renderer/point_bucket.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +struct geometry_too_long_exception : std::exception {}; + +using namespace llmr; + +PointBucket::PointBucket(const std::shared_ptr& vertexBuffer, + const std::shared_ptr& elementsBuffer, + const BucketDescription& bucket_desc) + : geometry(bucket_desc.geometry), + vertexBuffer(vertexBuffer), + elementsBuffer(elementsBuffer), + vertex_start(vertexBuffer->index()), + elements_start(elementsBuffer->index()) +{ +} + +void PointBucket::addGeometry(pbf& geom) { + Coordinate point; + + Geometry geometry(geom); + int32_t x, y; + geometry.next(x, y); + point.x = x; + point.y = y; + addGeometry(point); +} + +typedef uint16_t PointElement; + +void PointBucket::addGeometry(Coordinate& point) { + vertexBuffer->add(point.x, point.y); + + { + if (!groups.size() || (groups.back().vertex_length + 1 > 65535)) { + groups.emplace_back(); + } + + group_type& group = groups.back(); + elementsBuffer->add(group.vertex_length + point); + + group.vertex_length++; + group.elements_length++; + } +} + +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 !groups.empty(); +} + +void PointBucket::drawPoints(PointShader& shader) { + char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer->itemSize); + char *elements_index = BUFFER_OFFSET(elements_start * elementsBuffer->itemSize); + for (group_type& group : groups) { + group.array.bind(shader, *vertexBuffer, *elementsBuffer, vertex_index); + glDrawElements(GL_POINTS, group.elements_length, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * vertexBuffer->itemSize; + elements_index += group.elements_length * elementsBuffer->itemSize; + } +} diff --git a/src/renderer/shader-point.cpp b/src/renderer/shader-point.cpp new file mode 100644 index 000000000..250ac7d2f --- /dev/null +++ b/src/renderer/shader-point.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +#include + +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); +} + +void PointShader::bind(char *offset) { + glEnableVertexAttribArray(a_pos); + glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset); +} + +void PointShader::setColor(const std::array& 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& 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& 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/shader/point.fragment.glsl b/src/shader/point.fragment.glsl new file mode 100644 index 000000000..dcd45cc69 --- /dev/null +++ b/src/shader/point.fragment.glsl @@ -0,0 +1,25 @@ +#define root2 1.42 + +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; + + if (u_color.a > 0.0) { + gl_FragColor = u_color * color.a; + } else { + gl_FragColor = vec4(color.rgb * color.a, color.a); + } +} diff --git a/src/shader/point.vertex.glsl b/src/shader/point.vertex.glsl new file mode 100644 index 000000000..e506745e0 --- /dev/null +++ b/src/shader/point.vertex.glsl @@ -0,0 +1,10 @@ +attribute vec2 a_pos; + +uniform mat4 u_matrix; +uniform float u_size; + +void main() { + float z = step(32767.0, a_pos.x) * 2.0; + gl_Position = u_matrix * vec4(a_pos, z, 1); + gl_PointSize = u_size; +} diff --git a/src/shader/shaders.cpp b/src/shader/shaders.cpp index 203f143ca..d56d88dc1 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 float z = step(32767.0, a_pos.x) * 2.0;\n gl_Position = u_matrix * vec4(a_pos, z, 1);\n gl_PointSize = u_size;\n}\n", + "#define root2 1.42\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 if (u_color.a > 0.0) {\n gl_FragColor = u_color * color.a;\n } else {\n gl_FragColor = vec4(color.rgb * color.a, color.a);\n }\n}\n", } }; diff --git a/src/style/resources.cpp b/src/style/resources.cpp index 15b6aa040..bf0eb207e 100644 --- a/src/style/resources.cpp +++ b/src/style/resources.cpp @@ -47,7 +47,7 @@ const unsigned char resources::style[] = { 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, + 111, 108, 18, 7, 97, 108, 99, 111, 104, 111, 108, 26, 132, 3, 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, @@ -69,6 +69,9 @@ const unsigned char resources::style[] = { 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 + 128, 66, 0, 0, 240, 65, 0, 0, 128, 66, 34, 38, 10, 7, 97, 108, + 99, 111, 104, 111, 108, 29, 255, 153, 153, 153, 34, 8, 8, 2, 18, 4, + 0, 0, 192, 65, 66, 12, 97, 108, 99, 111, 104, 111, 108, 45, 115, 104, + 111, 112 }; const unsigned long resources::style_size = sizeof(resources::style); diff --git a/src/style/sprite.cpp b/src/style/sprite.cpp index ceef26739..e07cb89fd 100644 --- a/src/style/sprite.cpp +++ b/src/style/sprite.cpp @@ -29,7 +29,7 @@ Sprite::operator bool() const { return loaded; } -void Sprite::load(const std::string& base_url) { +void Sprite::load(const std::string& base_url, float pixelRatio) { std::shared_ptr sprite = shared_from_this(); auto complete = [sprite]() { @@ -41,7 +41,9 @@ void Sprite::load(const std::string& base_url) { } }; - 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 +51,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 { diff --git a/src/style/style.cpp b/src/style/style.cpp index 0e8e9f3b6..2aac0c384 100644 --- a/src/style/style.cpp +++ b/src/style/style.cpp @@ -12,6 +12,7 @@ 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) { @@ -89,10 +90,12 @@ std::pair 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 Style::parseLineClass(pbf data) { return { name, stroke }; } +std::pair 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(data.message()); + } else if (data.tag == 3) { // color + point.color = parseColor(data); + } else if (data.tag == 4) { // size + point.size = parseProperty(data.message()); + } else if (data.tag == 6) { // opacity + point.opacity = parseProperty(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(); @@ -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; + } } }