diff --git a/include/llmr/map/tile.hpp b/include/llmr/map/tile.hpp index 5d98e919c..bde0af91b 100644 --- a/include/llmr/map/tile.hpp +++ b/include/llmr/map/tile.hpp @@ -2,6 +2,7 @@ #define LLMR_MAP_TILE #include +#include #include #include #include @@ -71,6 +72,7 @@ public: public: const ID id; + uint8_t clip_id; state state; // Holds the actual geometries in this tile. @@ -89,6 +91,7 @@ public: // They contain the location offsets in the buffers stored above std::map> buckets; + mat4 matrix; private: // Source data std::string data; diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp index aa3a3d73f..10ab4f7c1 100644 --- a/include/llmr/renderer/painter.hpp +++ b/include/llmr/renderer/painter.hpp @@ -32,7 +32,7 @@ public: void setup(); void clear(); - void changeMatrix(const Tile::ID& id); + void changeMatrix(); void render(const std::shared_ptr& tile); void renderMatte(); void renderBackground(); @@ -40,24 +40,28 @@ public: 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); + void prepareClippingMask(); + void drawClippingMask(const mat4& matrix, uint8_t clip_id); + void finishClippingMask(); private: void setupShaders(); - void drawClippingMask(); void renderLayers(const std::shared_ptr& tile, const std::vector& layers); void renderDebug(const std::shared_ptr& tile); void useProgram(uint32_t program); void lineWidth(float lineWidth); +public: + mat4 matrix; + mat4 projMatrix; + mat4 nativeMatrix; + mat4 extrudeMatrix; + private: Transform& transform; Settings& settings; Style& style; - mat4 nativeMatrix; - mat4 matrix; - mat4 exMatrix; - uint32_t gl_program = 0; float gl_lineWidth = 0; diff --git a/macosx/main.mm b/macosx/main.mm index 472799d99..3f35b875b 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -227,7 +227,6 @@ public: }; MapView *mapView; -NSOperationQueue *queue; namespace llmr { namespace platform { @@ -240,10 +239,6 @@ void restart() { } void request_http(std::string url, std::function background_function, std::function foreground_callback) { - if (!queue) { - queue = [NSOperationQueue new]; - } - NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@(url.c_str())]]; diff --git a/src/map/map.cpp b/src/map/map.cpp index d09b505f4..23d04f015 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -414,10 +414,26 @@ bool Map::render() { painter.clear(); + painter.changeMatrix(); + + // First, update all tile matrices with the new transform and render into + // the stencil buffer. + uint8_t i = 1; + painter.prepareClippingMask(); for (Tile::Ptr& tile : tiles) { assert(tile); if (tile->state == Tile::ready) { - painter.changeMatrix(tile->id); + // The position matrix. + transform.matrixFor(tile->matrix, tile->id); + matrix::multiply(tile->matrix, painter.projMatrix, tile->matrix); + tile->clip_id = i++; + painter.drawClippingMask(tile->matrix, tile->clip_id); + } + } + painter.finishClippingMask(); + + for (Tile::Ptr& tile : tiles) { + if (tile->state == Tile::ready) { painter.render(tile); } } diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index fee7c6115..c57cff0e6 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -71,19 +71,14 @@ void Painter::lineWidth(float lineWidth) { } } -void Painter::changeMatrix(const Tile::ID& id) { +void Painter::changeMatrix() { // Initialize projection matrix - mat4 projMatrix; matrix::ortho(projMatrix, 0, transform.width, transform.height, 0, 1, 10); - // The position matrix. - transform.matrixFor(matrix, id); - matrix::multiply(matrix, projMatrix, matrix); - // The extrusion matrix. - matrix::identity(exMatrix); - matrix::multiply(exMatrix, projMatrix, exMatrix); - matrix::rotate_z(exMatrix, exMatrix, transform.getAngle()); + matrix::identity(extrudeMatrix); + matrix::multiply(extrudeMatrix, projMatrix, extrudeMatrix); + matrix::rotate_z(extrudeMatrix, extrudeMatrix, transform.getAngle()); // The native matrix is a 1:1 matrix that paints the coordinates at the // same screen position as the vertex specifies. @@ -92,38 +87,26 @@ void Painter::changeMatrix(const Tile::ID& id) { matrix::multiply(nativeMatrix, projMatrix, nativeMatrix); } -void Painter::drawClippingMask() { +void Painter::prepareClippingMask() { useProgram(plainShader->program); - plainShader->setMatrix(matrix); glColorMask(false, false, false, false); - - // Clear the entire stencil buffer, except for the 7th bit, which stores - // the global clipping mask that allows us to avoid drawing in regions of - // tiles we've already painted in. - glStencilMask(0xBF); - glClear(GL_STENCIL_BUFFER_BIT); - - // The stencil test will fail always, meaning we set all pixels covered - // by this geometry to 0x80. We use the highest bit 0x80 to mark the regions - // we want to draw in. All pixels that have this bit *not* set will never be - // drawn in. - glStencilFunc(GL_EQUAL, 0xC0, 0x40); - glStencilMask(0xC0); - glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); - + glStencilMask(0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0)); +} - // Draw the clipping mask - plainShader->setColor(1.0f, 0.0f, 1.0f, 1.0f); +void Painter::drawClippingMask(const mat4& matrix, uint8_t clip_id) { + plainShader->setMatrix(matrix); + glStencilFunc(GL_ALWAYS, clip_id, 0xFF); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index()); +} - // glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 0x80, 0x80); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0x00); +void Painter::finishClippingMask() { glColorMask(true, true, true, true); + glEnable(GL_STENCIL_TEST); + glStencilMask(0x0); } void Painter::clear() { @@ -136,7 +119,8 @@ void Painter::render(const Tile::Ptr& tile) { return; } - drawClippingMask(); + matrix = tile->matrix; + glStencilFunc(GL_EQUAL, tile->clip_id, 0xFF); renderLayers(tile, style.layers); @@ -183,8 +167,18 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons fill_color[2] *= properties.opacity; fill_color[3] *= properties.opacity; + Color stroke_color = properties.stroke_color; + stroke_color[0] *= properties.opacity; + stroke_color[1] *= properties.opacity; + stroke_color[2] *= properties.opacity; + stroke_color[3] *= properties.opacity; + bool outline = properties.antialias && properties.stroke_color != properties.fill_color; bool fringeline = properties.antialias && properties.stroke_color == properties.fill_color; + if (fringeline) { + outline = true; + stroke_color = fill_color; + } // Because we're drawing top-to-bottom, and we update the stencil mask // below, we have to draw the outline first (!) @@ -193,27 +187,27 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons outlineShader->setMatrix(matrix); lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio! - outlineShader->setColor(properties.stroke_color); + outlineShader->setColor(stroke_color); // Draw the entire line outlineShader->setWorld({{ transform.fb_width, transform.fb_height }}); bucket.drawVertices(*outlineShader); } else if (fringeline) { - // We're only drawing to the first seven bits (== support a maximum of - // 127 overlapping polygons in one place before we get rendering errors). - glStencilMask(0x3F); - glClear(GL_STENCIL_BUFFER_BIT); + // // We're only drawing to the first seven bits (== support a maximum of + // // 127 overlapping polygons in one place before we get rendering errors). + // glStencilMask(0x3F); + // glClear(GL_STENCIL_BUFFER_BIT); - // Draw front facing triangles. Wherever the 0x80 bit is 1, we are - // increasing the lower 7 bits by one if the triangle is a front-facing - // triangle. This means that all visible polygons should be in CCW - // orientation, while all holes (see below) are in CW orientation. - glStencilFunc(GL_EQUAL, 0x80, 0x80); + // // Draw front facing triangles. Wherever the 0x80 bit is 1, we are + // // increasing the lower 7 bits by one if the triangle is a front-facing + // // triangle. This means that all visible polygons should be in CCW + // // orientation, while all holes (see below) are in CW orientation. + // glStencilFunc(GL_EQUAL, 0x80, 0x80); - // When we do a nonzero fill, we count the number of times a pixel is - // covered by a counterclockwise polygon, and subtract the number of - // times it is "uncovered" by a clockwise polygon. - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP); + // // When we do a nonzero fill, we count the number of times a pixel is + // // covered by a counterclockwise polygon, and subtract the number of + // // times it is "uncovered" by a clockwise polygon. + // glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP); } if (properties.image.size() && *style.sprite) { @@ -260,27 +254,27 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons // Because we're drawing top-to-bottom, and we update the stencil mask // below, we have to draw the outline first (!) if (fringeline) { - useProgram(outlineShader->program); - outlineShader->setMatrix(matrix); - lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio! + // useProgram(outlineShader->program); + // outlineShader->setMatrix(matrix); + // lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio! - // From now on, we don't want to update the stencil buffer anymore. - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0x0); + // // From now on, we don't want to update the stencil buffer anymore. + // glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + // glStencilMask(0x0); - // Otherwise, we only want to draw the antialiased parts that are - // *outside* the current shape. This is important in case the fill - // or stroke color is translucent. If we wouldn't clip to outside - // the current shape, some pixels from the outline stroke overlapped - // the (non-antialiased) fill. - glStencilFunc(GL_EQUAL, 0x80, 0xBF); - outlineShader->setColor(fill_color); + // // Otherwise, we only want to draw the antialiased parts that are + // // *outside* the current shape. This is important in case the fill + // // or stroke color is translucent. If we wouldn't clip to outside + // // the current shape, some pixels from the outline stroke overlapped + // // the (non-antialiased) fill. + // glStencilFunc(GL_EQUAL, 0x80, 0xBF); + // outlineShader->setColor(fill_color); - // Draw the entire line - outlineShader->setWorld({{ transform.fb_width, transform.fb_height }}); - bucket.drawVertices(*outlineShader); + // // Draw the entire line + // outlineShader->setWorld({{ transform.fb_width, transform.fb_height }}); + // bucket.drawVertices(*outlineShader); - glStencilFunc(GL_EQUAL, 0x80, 0x80); + // glStencilFunc(GL_EQUAL, 0x80, 0x80); } } @@ -339,7 +333,7 @@ void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, cons // imageSprite.bind(gl, true); // //factor = Math.pow(2, 4 - painter.transform.zoom + params.z); - // gl.switchShader(painter.linepatternShader, painter.translatedMatrix || painter.posMatrix, painter.exMatrix); + // gl.switchShader(painter.linepatternShader, painter.translatedMatrix || painter.posMatrix, painter.extrudeMatrix); // shader = painter.linepatternShader; // glUniform2fv(painter.linepatternShader.u_pattern_size, [imagePos.size[0] * factor, imagePos.size[1] ]); // glUniform2fv(painter.linepatternShader.u_pattern_tl, imagePos.tl); @@ -349,7 +343,7 @@ void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, cons } else { useProgram(lineShader->program); lineShader->setMatrix(matrix); - lineShader->setExtrudeMatrix(exMatrix); + lineShader->setExtrudeMatrix(extrudeMatrix); // TODO: Move this to transform? const float tilePixelRatio = transform.getScale() / (1 << transform.getIntegerZoom()) / 8;