diff options
-rw-r--r-- | include/mbgl/style/types.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/renderer/line_bucket.cpp | 67 | ||||
-rw-r--r-- | src/mbgl/renderer/line_bucket.hpp | 3 |
3 files changed, 64 insertions, 9 deletions
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index f6ffcd6865..e46ece6f4a 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -92,6 +92,8 @@ enum class JoinType : uint8_t { Miter, Bevel, Round, + // the following two types are for internal use only + FakeRound, FlipBevel }; @@ -99,6 +101,7 @@ MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, { { JoinType::Miter, "miter" }, { JoinType::Bevel, "bevel" }, { JoinType::Round, "round" }, + { JoinType::FakeRound, "fakeround" }, { JoinType::FlipBevel, "flipbevel" }, }); diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index 0ba91af34d..446c75de76 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -146,8 +146,12 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { const CapType currentCap = nextVertex ? beginCap : endCap; if (middleVertex) { - if (currentJoin == JoinType::Round && miterLength < layout.round_limit) { - currentJoin = JoinType::Miter; + if (currentJoin == JoinType::Round) { + if (miterLength < layout.round_limit) { + currentJoin = JoinType::Miter; + } else if (miterLength <= 2) { + currentJoin = JoinType::FakeRound; + } } if (currentJoin == JoinType::Miter && miterLength > miterLimit) { @@ -191,13 +195,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { triangleStore); flip = -flip; - } else if (middleVertex && currentJoin == JoinType::Bevel) { - const float dir = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x; + } else if (middleVertex && (currentJoin == JoinType::Bevel || currentJoin == JoinType::FakeRound)) { + const bool lineTurnsLeft = flip * (prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x) > 0; const float offset = -std::sqrt(miterLength * miterLength - 1); float offsetA; float offsetB; - if (flip * dir > 0) { + if (lineTurnsLeft) { offsetB = 0; offsetA = offset; } else { @@ -211,6 +215,29 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { startVertex, triangleStore); } + if (currentJoin == JoinType::FakeRound) { + // The join angle is sharp enough that a round join would be visible. + // Bevel joins fill the gap between segments with a single pie slice triangle. + // Create a round join by adding multiple pie slices. The join isn't actually round, but + // it looks like it is at the sizes we render lines at. + + // Add more triangles for sharper angles. + // This math is just a good enough approximation. It isn't "correct". + const int n = std::floor((0.5 - (cosHalfAngle - 0.5)) * 8); + + for (int m = 0; m < n; m++) { + auto approxFractionalJoinNormal = util::unit(nextNormal * ((m + 1.0f) / (n + 1.0f)) + prevNormal); + addPieSliceVertex(currentVertex, flip, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + } + + addPieSliceVertex(currentVertex, flip, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore); + + for (int k = n - 1; k >= 0; k--) { + auto approxFractionalJoinNormal = util::unit(prevNormal * ((k + 1.0f) / (n + 1.0f)) + nextNormal); + addPieSliceVertex(currentVertex, flip, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + } + } + // Start next segment if (nextVertex) { addCurrentVertex(currentVertex, flip, distance, nextNormal, -offsetA, -offsetB, @@ -260,15 +287,14 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { // The segment is done. Unset vertices to disconnect segments. e1 = e2 = -1; flip = 1; + } - } else if (beginCap == CapType::Round) { + // Start next segment with a butt + if (nextVertex) { // Add round cap before first segment addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, true, startVertex, triangleStore); - } - // Start next segment with a butt - if (nextVertex) { addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false, startVertex, triangleStore); } @@ -337,6 +363,29 @@ void LineBucket::addCurrentVertex(const Coordinate& currentVertex, e2 = e3; } +void LineBucket::addPieSliceVertex(const Coordinate& currentVertex, + float flip, + double distance, + const vec2<double>& extrude, + bool lineTurnsLeft, + int32_t startVertex, + std::vector<TriangleElement>& triangleStore) { + int8_t ty = lineTurnsLeft; + + auto flippedExtrude = extrude * (flip * (lineTurnsLeft ? -1 : 1)); + e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, flippedExtrude.x, flippedExtrude.y, 0, ty, + distance) - startVertex; + if (e1 >= 0 && e2 >= 0) { + triangleStore.emplace_back(e1, e2, e3); + } + + if (lineTurnsLeft) { + e2 = e3; + } else { + e1 = e3; + } +} + void LineBucket::upload() { vertexBuffer.upload(); triangleElementsBuffer.upload(); diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp index 6f13e3c819..cd5fce3cb9 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/line_bucket.hpp @@ -47,6 +47,9 @@ private: void addCurrentVertex(const Coordinate& currentVertex, float flip, double distance, const vec2<double>& normal, float endLeft, float endRight, bool round, int32_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore); + void addPieSliceVertex(const Coordinate& currentVertex, float flip, double distance, + const vec2<double>& extrude, bool lineTurnsLeft, int32_t startVertex, + std::vector<TriangleElement>& triangleStore); public: StyleLayoutLine layout; |