Merge pull request #1343 from mapbox/get-rid-of-gl-points
match -js line tesselation (get rid of joins drawn with GL_POINTS).
This commit is contained in:
@@ -74,6 +74,11 @@ inline vec2<T> normal(const S1& a, const S2& b) {
|
||||
return { dx / c, dy / c };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T perp(const T& a) {
|
||||
return T(-a.y, a.x);
|
||||
}
|
||||
|
||||
template <typename T, typename S1, typename S2>
|
||||
inline T dist(const S1& a, const S2& b) {
|
||||
T dx = b.x - a.x;
|
||||
@@ -93,6 +98,11 @@ inline T mag(const S& a) {
|
||||
return std::sqrt(a.x * a.x + a.y * a.y);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
inline S unit(const S& a) {
|
||||
return a * (1 / mag(a));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T clamp(T value, T min, T max) {
|
||||
return value < min ? min : (value > max ? max : value);
|
||||
|
||||
@@ -61,7 +61,13 @@ struct vec2 {
|
||||
template <typename O>
|
||||
inline typename std::enable_if<!std::is_arithmetic<O>::value, vec2>::type
|
||||
operator-(const O &o) const {
|
||||
return {x - o.x, y - o.y};
|
||||
return vec2<T>(x - o.x, y - o.y);
|
||||
}
|
||||
|
||||
template <typename O>
|
||||
inline typename std::enable_if<!std::is_arithmetic<O>::value, vec2>::type
|
||||
operator+(const O &o) const {
|
||||
return vec2<T>(x + o.x, y + o.y);
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
|
||||
@@ -14,8 +14,3 @@ void LineElementsBuffer::add(element_type a, element_type b) {
|
||||
elements[0] = a;
|
||||
elements[1] = b;
|
||||
}
|
||||
|
||||
void PointElementsBuffer::add(element_type a) {
|
||||
uint16_t *data = static_cast<element_type *>(addElement());
|
||||
data[0] = a;
|
||||
}
|
||||
|
||||
@@ -44,16 +44,6 @@ public:
|
||||
void add(element_type a, element_type b);
|
||||
};
|
||||
|
||||
class PointElementsBuffer : public Buffer<
|
||||
2, // bytes per point (1 unsigned short)
|
||||
GL_ELEMENT_ARRAY_BUFFER
|
||||
> {
|
||||
public:
|
||||
typedef uint16_t element_type;
|
||||
|
||||
void add(element_type a);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -172,8 +172,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la
|
||||
std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
|
||||
const StyleBucket& bucket_desc) {
|
||||
auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer,
|
||||
tile.triangleElementsBuffer,
|
||||
tile.pointElementsBuffer);
|
||||
tile.triangleElementsBuffer);
|
||||
|
||||
const float z = tile.id.z;
|
||||
auto& layout = bucket->layout;
|
||||
|
||||
@@ -50,7 +50,6 @@ protected:
|
||||
|
||||
TriangleElementsBuffer triangleElementsBuffer;
|
||||
LineElementsBuffer lineElementsBuffer;
|
||||
PointElementsBuffer pointElementsBuffer;
|
||||
|
||||
// Holds the buckets of this tile.
|
||||
// They contain the location offsets in the buffers stored above
|
||||
|
||||
@@ -8,34 +8,25 @@
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
#ifndef BUFFER_OFFSET
|
||||
#define BUFFER_OFFSET(i) ((char*)nullptr + (i))
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
LineBucket::LineBucket(LineVertexBuffer &vertexBuffer_,
|
||||
TriangleElementsBuffer &triangleElementsBuffer_,
|
||||
PointElementsBuffer &pointElementsBuffer_)
|
||||
LineBucket::LineBucket(LineVertexBuffer& vertexBuffer_,
|
||||
TriangleElementsBuffer& triangleElementsBuffer_)
|
||||
: vertexBuffer(vertexBuffer_),
|
||||
triangleElementsBuffer(triangleElementsBuffer_),
|
||||
pointElementsBuffer(pointElementsBuffer_),
|
||||
vertex_start(vertexBuffer_.index()),
|
||||
triangle_elements_start(triangleElementsBuffer_.index()),
|
||||
point_elements_start(pointElementsBuffer_.index()) {
|
||||
}
|
||||
triangle_elements_start(triangleElementsBuffer_.index()){};
|
||||
|
||||
LineBucket::~LineBucket() {
|
||||
// Do not remove. header file only contains forward definitions to unique pointers.
|
||||
}
|
||||
|
||||
struct TriangleElement {
|
||||
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
|
||||
uint16_t a, b, c;
|
||||
};
|
||||
|
||||
typedef uint16_t PointElement;
|
||||
|
||||
void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
|
||||
for (auto& line : geometryCollection) {
|
||||
addGeometry(line);
|
||||
@@ -43,366 +34,365 @@ void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
|
||||
}
|
||||
|
||||
void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
// TODO: use roundLimit
|
||||
// const float roundLimit = geometry.round_limit;
|
||||
const auto len = [&vertices] {
|
||||
auto l = vertices.size();
|
||||
// If the line has duplicate vertices at the end, adjust length to remove them.
|
||||
while (l > 2 && vertices[l - 1] == vertices[l - 2]) {
|
||||
l--;
|
||||
}
|
||||
return l;
|
||||
}();
|
||||
|
||||
if (vertices.size() < 2) {
|
||||
if (len < 2) {
|
||||
// fprintf(stderr, "a line must have at least two vertices\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Coordinate firstVertex = vertices.front();
|
||||
Coordinate lastVertex = vertices.back();
|
||||
bool closed = firstVertex.x == lastVertex.x && firstVertex.y == lastVertex.y;
|
||||
const float miterLimit = layout.join == JoinType::Bevel ? 1.05f : layout.miter_limit;
|
||||
|
||||
if (vertices.size() == 2 && closed) {
|
||||
const Coordinate firstVertex = vertices.front();
|
||||
const Coordinate lastVertex = vertices[len - 1];
|
||||
const bool closed = firstVertex == lastVertex;
|
||||
|
||||
if (len == 2 && closed) {
|
||||
// fprintf(stderr, "a line may not have coincident points\n");
|
||||
return;
|
||||
}
|
||||
|
||||
CapType beginCap = layout.cap;
|
||||
CapType endCap = closed ? CapType::Butt : layout.cap;
|
||||
|
||||
JoinType currentJoin = JoinType::Miter;
|
||||
|
||||
Coordinate currentVertex = Coordinate::null(),
|
||||
prevVertex = Coordinate::null(),
|
||||
nextVertex = Coordinate::null();
|
||||
vec2<double> prevNormal = vec2<double>::null(),
|
||||
nextNormal = vec2<double>::null();
|
||||
|
||||
int32_t e1 = -1, e2 = -1, e3 = -1;
|
||||
const CapType beginCap = layout.cap;
|
||||
const CapType endCap = closed ? CapType::Butt : layout.cap;
|
||||
|
||||
int8_t flip = 1;
|
||||
double distance = 0;
|
||||
bool startOfLine = true;
|
||||
Coordinate currentVertex = Coordinate::null(), prevVertex = Coordinate::null(),
|
||||
nextVertex = Coordinate::null();
|
||||
vec2<double> prevNormal = vec2<double>::null(), nextNormal = vec2<double>::null();
|
||||
|
||||
// the last three vertices added
|
||||
e1 = e2 = e3 = -1;
|
||||
|
||||
if (closed) {
|
||||
currentVertex = vertices[vertices.size() - 2];
|
||||
nextNormal = util::normal<double>(currentVertex, lastVertex);
|
||||
currentVertex = vertices[len - 2];
|
||||
nextNormal = util::perp(util::unit(vec2<double>(firstVertex - currentVertex)));
|
||||
}
|
||||
|
||||
int32_t start_vertex = (int32_t)vertexBuffer.index();
|
||||
const int32_t startVertex = (int32_t)vertexBuffer.index();
|
||||
std::vector<TriangleElement> triangleStore;
|
||||
|
||||
std::vector<TriangleElement> triangle_store;
|
||||
std::vector<PointElement> point_store;
|
||||
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
if (nextNormal) prevNormal = { -nextNormal.x, -nextNormal.y };
|
||||
if (currentVertex) prevVertex = currentVertex;
|
||||
|
||||
currentVertex = vertices[i];
|
||||
currentJoin = layout.join;
|
||||
|
||||
if (prevVertex) distance += util::dist<double>(currentVertex, prevVertex);
|
||||
|
||||
// Find the next vertex.
|
||||
if (i + 1 < vertices.size()) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (closed && i == len - 1) {
|
||||
// if the line is closed, we treat the last vertex like the first
|
||||
nextVertex = vertices[i];
|
||||
} else if (i + 1 < len) {
|
||||
// just the next vertex
|
||||
nextVertex = vertices[i + 1];
|
||||
} else {
|
||||
// there is no next vertex
|
||||
nextVertex = Coordinate::null();
|
||||
}
|
||||
|
||||
// If the line is closed, we treat the last vertex like the first vertex.
|
||||
if (!nextVertex && closed) {
|
||||
nextVertex = vertices[1];
|
||||
// if two consecutive vertices exist, skip the current one
|
||||
if (nextVertex && vertices[i] == nextVertex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextVertex) {
|
||||
// if two consecutive vertices exist, skip one
|
||||
if (currentVertex.x == nextVertex.x && currentVertex.y == nextVertex.y) continue;
|
||||
if (nextNormal) {
|
||||
prevNormal = nextNormal;
|
||||
}
|
||||
if (currentVertex) {
|
||||
prevVertex = currentVertex;
|
||||
}
|
||||
|
||||
currentVertex = vertices[i];
|
||||
|
||||
// Calculate how far along the line the currentVertex is
|
||||
if (prevVertex)
|
||||
distance += util::dist<double>(currentVertex, prevVertex);
|
||||
|
||||
// Calculate the normal towards the next vertex in this line. In case
|
||||
// there is no next vertex, pretend that the line is continuing straight,
|
||||
// meaning that we are just reversing the previous normal
|
||||
if (nextVertex) {
|
||||
nextNormal = util::normal<double>(currentVertex, nextVertex);
|
||||
} else {
|
||||
nextNormal = { -prevNormal.x, -prevNormal.y };
|
||||
}
|
||||
// meaning that we are just using the previous normal.
|
||||
nextNormal = nextVertex ? util::perp(util::unit(vec2<double>(nextVertex - currentVertex)))
|
||||
: prevNormal;
|
||||
|
||||
// If we still don't have a previous normal, this is the beginning of a
|
||||
// non-closed line, so we're doing a straight "join".
|
||||
if (!prevNormal) {
|
||||
prevNormal = { -nextNormal.x, -nextNormal.y };
|
||||
prevNormal = nextNormal;
|
||||
}
|
||||
|
||||
// Determine the normal of the join extrusion. It is the angle bisector
|
||||
// of the segments between the previous line and the next line.
|
||||
vec2<double> joinNormal = {
|
||||
prevNormal.x + nextNormal.x,
|
||||
prevNormal.y + nextNormal.y
|
||||
};
|
||||
vec2<double> joinNormal = util::unit(prevNormal + nextNormal);
|
||||
|
||||
// Cross product yields 0..1 depending on whether they are parallel
|
||||
// or perpendicular.
|
||||
double joinAngularity = nextNormal.x * joinNormal.y - nextNormal.y * joinNormal.x;
|
||||
joinNormal.x /= joinAngularity;
|
||||
joinNormal.y /= joinAngularity;
|
||||
double roundness = std::fmax(std::abs(joinNormal.x), std::abs(joinNormal.y));
|
||||
/* joinNormal prevNormal
|
||||
* ↖ ↑
|
||||
* .________. prevVertex
|
||||
* |
|
||||
* nextNormal ← | currentVertex
|
||||
* |
|
||||
* nextVertex !
|
||||
*
|
||||
*/
|
||||
|
||||
// Calculate the length of the miter (the ratio of the miter to the width).
|
||||
// Find the cosine of the angle between the next and join normals
|
||||
// using dot product. The inverse of that is the miter length.
|
||||
const float cosHalfAngle = joinNormal.x * nextNormal.x + joinNormal.y * nextNormal.y;
|
||||
const float miterLength = 1 / cosHalfAngle;
|
||||
|
||||
// Switch to miter joins if the angle is very low.
|
||||
if (currentJoin != JoinType::Miter) {
|
||||
if (std::fabs(joinAngularity) < 0.5 && roundness < layout.miter_limit) {
|
||||
// The join if a middle vertex, otherwise the cap
|
||||
const bool middleVertex = prevVertex && nextVertex;
|
||||
JoinType currentJoin = layout.join;
|
||||
const CapType currentCap = nextVertex ? beginCap : endCap;
|
||||
|
||||
if (middleVertex) {
|
||||
if (currentJoin == JoinType::Round && miterLength < layout.round_limit) {
|
||||
currentJoin = JoinType::Miter;
|
||||
}
|
||||
}
|
||||
|
||||
// Add offset square begin cap.
|
||||
if (!prevVertex && beginCap == CapType::Square) {
|
||||
// Add first vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
// Add second vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
}
|
||||
|
||||
// Add offset square end cap.
|
||||
else if (!nextVertex && endCap == CapType::Square) {
|
||||
// Add first vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
// Add second vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
}
|
||||
|
||||
else if (currentJoin == JoinType::Miter) {
|
||||
// MITER JOIN
|
||||
if (std::fabs(joinAngularity) < 0.01) {
|
||||
// The two normals are almost parallel.
|
||||
joinNormal.x = -nextNormal.y;
|
||||
joinNormal.y = nextNormal.x;
|
||||
} else if (roundness > layout.miter_limit) {
|
||||
// If the miter grows too large, flip the direction to make a
|
||||
// bevel join.
|
||||
joinNormal.x = (prevNormal.x - nextNormal.x) / joinAngularity;
|
||||
joinNormal.y = (prevNormal.y - nextNormal.y) / joinAngularity;
|
||||
if (currentJoin == JoinType::Miter && miterLength > miterLimit) {
|
||||
currentJoin = JoinType::Bevel;
|
||||
}
|
||||
|
||||
if (roundness > layout.miter_limit) {
|
||||
flip = -flip;
|
||||
}
|
||||
|
||||
// Add first vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
// Add second vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
if ((!prevVertex && beginCap == CapType::Round) ||
|
||||
(!nextVertex && endCap == CapType::Round)) {
|
||||
point_store.emplace_back(e1);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// Close up the previous line
|
||||
// Add first vertex
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
// Add second vertex.
|
||||
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
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
|
||||
prevNormal = { -nextNormal.x, -nextNormal.y };
|
||||
flip = 1;
|
||||
|
||||
|
||||
// begin/end caps
|
||||
if ((!prevVertex && beginCap == CapType::Round) ||
|
||||
(!nextVertex && endCap == CapType::Round)) {
|
||||
point_store.emplace_back(e1);
|
||||
}
|
||||
|
||||
|
||||
if (currentJoin == JoinType::Round) {
|
||||
if (prevVertex && nextVertex && (!closed || i > 0)) {
|
||||
point_store.emplace_back(e1);
|
||||
if (currentJoin == JoinType::Bevel) {
|
||||
// The maximum extrude length is 128 / 63 = 2 times the width of the line
|
||||
// so if miterLength >= 2 we need to draw a different type of bevel where.
|
||||
if (miterLength > 2) {
|
||||
currentJoin = JoinType::FlipBevel;
|
||||
}
|
||||
|
||||
// Reset the previous vertices so that we don't accidentally create
|
||||
// any triangles.
|
||||
e1 = -1; e2 = -1; e3 = -1;
|
||||
// If the miterLength is really small and the line bevel wouldn't be visible,
|
||||
// just draw a miter join to save a triangle.
|
||||
if (miterLength < miterLimit) {
|
||||
currentJoin = JoinType::Miter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (middleVertex && currentJoin == JoinType::Miter) {
|
||||
joinNormal = joinNormal * miterLength;
|
||||
addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
|
||||
triangleStore);
|
||||
|
||||
} else if (middleVertex && currentJoin == JoinType::FlipBevel) {
|
||||
// miter is too big, flip the direction to make a beveled join
|
||||
|
||||
if (miterLength > 100) {
|
||||
// Almost parallel lines
|
||||
joinNormal = nextNormal;
|
||||
} else {
|
||||
const float bevelLength = miterLength * util::mag(prevNormal + nextNormal) /
|
||||
util::mag(prevNormal - nextNormal);
|
||||
joinNormal = util::perp(joinNormal) * bevelLength;
|
||||
}
|
||||
|
||||
// Start the new quad.
|
||||
// Add first vertex
|
||||
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
|
||||
addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
|
||||
triangleStore);
|
||||
flip = -flip;
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
} else if (middleVertex && currentJoin == JoinType::Bevel) {
|
||||
const float dir = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x;
|
||||
const float offset = -std::sqrt(miterLength * miterLength - 1);
|
||||
float offsetA;
|
||||
float offsetB;
|
||||
|
||||
// Add second vertex
|
||||
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
|
||||
if (flip * dir > 0) {
|
||||
offsetB = 0;
|
||||
offsetA = offset;
|
||||
} else {
|
||||
offsetA = 0;
|
||||
offsetB = offset;
|
||||
}
|
||||
|
||||
if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
|
||||
e1 = e2; e2 = e3;
|
||||
// Close previous segement with bevel
|
||||
if (!startOfLine) {
|
||||
addCurrentVertex(currentVertex, flip, distance, prevNormal, offsetA, offsetB, false,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
// Start next segment
|
||||
if (nextVertex) {
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, -offsetA, -offsetB,
|
||||
false, startVertex, triangleStore);
|
||||
}
|
||||
|
||||
} else if (!middleVertex && currentCap == CapType::Butt) {
|
||||
if (!startOfLine) {
|
||||
// Close previous segment with a butt
|
||||
addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
// Start next segment with a butt
|
||||
if (nextVertex) {
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
} else if (!middleVertex && currentCap == CapType::Square) {
|
||||
if (!startOfLine) {
|
||||
// Close previous segment with a square cap
|
||||
addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, false,
|
||||
startVertex, triangleStore);
|
||||
|
||||
// The segment is done. Unset vertices to disconnect segments.
|
||||
e1 = e2 = -1;
|
||||
flip = 1;
|
||||
}
|
||||
|
||||
// Start next segment
|
||||
if (nextVertex) {
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, false,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
} else if (middleVertex ? currentJoin == JoinType::Round : currentCap == CapType::Round) {
|
||||
if (!startOfLine) {
|
||||
// Close previous segment with a butt
|
||||
addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
|
||||
startVertex, triangleStore);
|
||||
|
||||
// Add round cap or linejoin at end of segment
|
||||
addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, true, startVertex,
|
||||
triangleStore);
|
||||
|
||||
// The segment is done. Unset vertices to disconnect segments.
|
||||
e1 = e2 = -1;
|
||||
flip = 1;
|
||||
|
||||
} else if (beginCap == CapType::Round) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
startOfLine = false;
|
||||
}
|
||||
|
||||
size_t end_vertex = vertexBuffer.index();
|
||||
size_t vertex_count = end_vertex - start_vertex;
|
||||
const size_t endVertex = vertexBuffer.index();
|
||||
const size_t vertexCount = endVertex - startVertex;
|
||||
|
||||
// Store the triangle/line groups.
|
||||
{
|
||||
if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertex_count > 65535)) {
|
||||
if (!triangleGroups.size() ||
|
||||
(triangleGroups.back()->vertex_length + vertexCount > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
triangleGroups.emplace_back(util::make_unique<triangle_group_type>());
|
||||
triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
|
||||
}
|
||||
|
||||
assert(triangleGroups.back());
|
||||
triangle_group_type& group = *triangleGroups.back();
|
||||
for (const auto& triangle : triangle_store) {
|
||||
triangleElementsBuffer.add(
|
||||
group.vertex_length + triangle.a,
|
||||
group.vertex_length + triangle.b,
|
||||
group.vertex_length + triangle.c
|
||||
);
|
||||
auto& group = *triangleGroups.back();
|
||||
for (const auto& triangle : triangleStore) {
|
||||
triangleElementsBuffer.add(group.vertex_length + triangle.a,
|
||||
group.vertex_length + triangle.b,
|
||||
group.vertex_length + triangle.c);
|
||||
}
|
||||
|
||||
group.vertex_length += vertex_count;
|
||||
group.elements_length += triangle_store.size();
|
||||
}
|
||||
|
||||
// Store the line join/cap groups.
|
||||
{
|
||||
if (!pointGroups.size() || (pointGroups.back()->vertex_length + vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
pointGroups.emplace_back(util::make_unique<point_group_type>());
|
||||
}
|
||||
|
||||
assert(pointGroups.back());
|
||||
point_group_type& group = *pointGroups.back();
|
||||
for (const auto point : point_store) {
|
||||
pointElementsBuffer.add(group.vertex_length + point);
|
||||
}
|
||||
|
||||
group.vertex_length += vertex_count;
|
||||
group.elements_length += point_store.size();
|
||||
group.vertex_length += vertexCount;
|
||||
group.elements_length += triangleStore.size();
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
|
||||
const mat4 &matrix) {
|
||||
void LineBucket::addCurrentVertex(const Coordinate& currentVertex,
|
||||
float flip,
|
||||
double distance,
|
||||
const vec2<double>& normal,
|
||||
float endLeft,
|
||||
float endRight,
|
||||
bool round,
|
||||
int32_t startVertex,
|
||||
std::vector<TriangleElement>& triangleStore) {
|
||||
int8_t tx = round ? 1 : 0;
|
||||
|
||||
vec2<double> extrude = normal * flip;
|
||||
if (endLeft)
|
||||
extrude = extrude - (util::perp(normal) * endLeft);
|
||||
e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 0,
|
||||
distance) -
|
||||
startVertex;
|
||||
if (e1 >= 0 && e2 >= 0) {
|
||||
triangleStore.emplace_back(e1, e2, e3);
|
||||
}
|
||||
e1 = e2;
|
||||
e2 = e3;
|
||||
|
||||
extrude = normal * (-flip);
|
||||
if (endRight)
|
||||
extrude = extrude - (util::perp(normal) * endRight);
|
||||
e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 1,
|
||||
distance) -
|
||||
startVertex;
|
||||
if (e1 >= 0 && e2 >= 0) {
|
||||
triangleStore.emplace_back(e1, e2, e3);
|
||||
}
|
||||
e1 = e2;
|
||||
e2 = e3;
|
||||
}
|
||||
|
||||
void LineBucket::render(Painter& painter,
|
||||
const StyleLayer& layer_desc,
|
||||
const TileID& id,
|
||||
const mat4& matrix) {
|
||||
painter.renderLine(*this, layer_desc, id, matrix);
|
||||
}
|
||||
|
||||
bool LineBucket::hasData() const {
|
||||
return !triangleGroups.empty() || !pointGroups.empty();
|
||||
}
|
||||
|
||||
bool LineBucket::hasPoints() const {
|
||||
if (!pointGroups.empty()) {
|
||||
for (const auto& group : pointGroups) {
|
||||
assert(group);
|
||||
if (group->elements_length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !triangleGroups.empty();
|
||||
}
|
||||
|
||||
void LineBucket::drawLines(LineShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
|
||||
elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawLineSDF(LineSDFShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
|
||||
elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawLinePatterns(LinepatternShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
|
||||
for (auto& group : triangleGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
|
||||
elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::drawPoints(LinejoinShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize);
|
||||
for (auto& group : pointGroups) {
|
||||
assert(group);
|
||||
if (!group->elements_length) {
|
||||
continue;
|
||||
}
|
||||
group->array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
|
||||
MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group->elements_length, GL_UNSIGNED_SHORT, elements_index));
|
||||
vertex_index += group->vertex_length * vertexBuffer.itemSize;
|
||||
elements_index += group->elements_length * pointElementsBuffer.itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,14 @@ class Style;
|
||||
class LineVertexBuffer;
|
||||
class TriangleElementsBuffer;
|
||||
class LineShader;
|
||||
class LinejoinShader;
|
||||
class LineSDFShader;
|
||||
class LinepatternShader;
|
||||
|
||||
class LineBucket : public Bucket {
|
||||
typedef ElementGroup<3> triangle_group_type;
|
||||
typedef ElementGroup<1> point_group_type;
|
||||
using TriangleGroup = ElementGroup<3>;
|
||||
|
||||
public:
|
||||
LineBucket(LineVertexBuffer &vertexBuffer,
|
||||
TriangleElementsBuffer &triangleElementsBuffer,
|
||||
PointElementsBuffer &pointElementsBuffer);
|
||||
LineBucket(LineVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer);
|
||||
~LineBucket() override;
|
||||
|
||||
void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
|
||||
@@ -39,12 +35,18 @@ public:
|
||||
void addGeometry(const GeometryCollection&);
|
||||
void addGeometry(const std::vector<Coordinate>& line);
|
||||
|
||||
bool hasPoints() const;
|
||||
|
||||
void drawLines(LineShader& shader);
|
||||
void drawLineSDF(LineSDFShader& shader);
|
||||
void drawLinePatterns(LinepatternShader& shader);
|
||||
void drawPoints(LinejoinShader& shader);
|
||||
|
||||
private:
|
||||
struct TriangleElement {
|
||||
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
|
||||
uint16_t a, b, c;
|
||||
};
|
||||
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);
|
||||
|
||||
public:
|
||||
StyleLayoutLine layout;
|
||||
@@ -52,14 +54,15 @@ public:
|
||||
private:
|
||||
LineVertexBuffer& vertexBuffer;
|
||||
TriangleElementsBuffer& triangleElementsBuffer;
|
||||
PointElementsBuffer& pointElementsBuffer;
|
||||
|
||||
const size_t vertex_start;
|
||||
const size_t triangle_elements_start;
|
||||
const size_t point_elements_start;
|
||||
|
||||
std::vector<std::unique_ptr<triangle_group_type>> triangleGroups;
|
||||
std::vector<std::unique_ptr<point_group_type>> pointGroups;
|
||||
int32_t e1;
|
||||
int32_t e2;
|
||||
int32_t e3;
|
||||
|
||||
std::vector<std::unique_ptr<TriangleGroup>> triangleGroups;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ void Painter::setup() {
|
||||
assert(plainShader);
|
||||
assert(outlineShader);
|
||||
assert(lineShader);
|
||||
assert(linejoinShader);
|
||||
assert(linepatternShader);
|
||||
assert(patternShader);
|
||||
assert(rasterShader);
|
||||
@@ -96,7 +95,6 @@ void Painter::setupShaders() {
|
||||
if (!plainShader) plainShader = util::make_unique<PlainShader>();
|
||||
if (!outlineShader) outlineShader = util::make_unique<OutlineShader>();
|
||||
if (!lineShader) lineShader = util::make_unique<LineShader>();
|
||||
if (!linejoinShader) linejoinShader = util::make_unique<LinejoinShader>();
|
||||
if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>();
|
||||
if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>();
|
||||
if (!patternShader) patternShader = util::make_unique<PatternShader>();
|
||||
@@ -112,7 +110,6 @@ void Painter::deleteShaders() {
|
||||
plainShader = nullptr;
|
||||
outlineShader = nullptr;
|
||||
lineShader = nullptr;
|
||||
linejoinShader = nullptr;
|
||||
linepatternShader = nullptr;
|
||||
patternShader = nullptr;
|
||||
iconShader = nullptr;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <mbgl/shader/outline_shader.hpp>
|
||||
#include <mbgl/shader/pattern_shader.hpp>
|
||||
#include <mbgl/shader/line_shader.hpp>
|
||||
#include <mbgl/shader/linejoin_shader.hpp>
|
||||
#include <mbgl/shader/linesdf_shader.hpp>
|
||||
#include <mbgl/shader/linepattern_shader.hpp>
|
||||
#include <mbgl/shader/icon_shader.hpp>
|
||||
@@ -199,7 +198,6 @@ public:
|
||||
std::unique_ptr<PlainShader> plainShader;
|
||||
std::unique_ptr<OutlineShader> outlineShader;
|
||||
std::unique_ptr<LineShader> lineShader;
|
||||
std::unique_ptr<LinejoinShader> linejoinShader;
|
||||
std::unique_ptr<LineSDFShader> linesdfShader;
|
||||
std::unique_ptr<LinepatternShader> linepatternShader;
|
||||
std::unique_ptr<PatternShader> patternShader;
|
||||
|
||||
@@ -51,29 +51,6 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
|
||||
|
||||
depthRange(strata, 1.0f);
|
||||
|
||||
// We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway.
|
||||
if (bucket.hasPoints() && outset > 1.0f) {
|
||||
useProgram(linejoinShader->program);
|
||||
linejoinShader->u_matrix = vtxMatrix;
|
||||
linejoinShader->u_color = color;
|
||||
linejoinShader->u_world = {{
|
||||
state.getFramebufferWidth() * 0.5f,
|
||||
state.getFramebufferHeight() * 0.5f
|
||||
}};
|
||||
linejoinShader->u_linewidth = {{
|
||||
((outset - 0.25f) * state.getPixelRatio()),
|
||||
((inset - 0.25f) * state.getPixelRatio())
|
||||
}};
|
||||
|
||||
float pointSize = std::ceil(state.getPixelRatio() * outset * 2.0);
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
linejoinShader->u_size = pointSize;
|
||||
#else
|
||||
MBGL_CHECK_ERROR(glPointSize(pointSize));
|
||||
#endif
|
||||
bucket.drawPoints(*linejoinShader);
|
||||
}
|
||||
|
||||
if (properties.dash_array.from.size()) {
|
||||
|
||||
useProgram(linesdfShader->program);
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
uniform vec4 u_color;
|
||||
uniform vec2 u_linewidth;
|
||||
|
||||
varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
float dist = length(v_pos - gl_FragCoord.xy);
|
||||
|
||||
// Calculate the antialiasing fade factor. This is either when fading in
|
||||
// the line in case of an offset line (v_linewidth.t) or when fading out
|
||||
// (v_linewidth.s)
|
||||
float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);
|
||||
gl_FragColor = u_color * alpha;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
attribute vec2 a_pos;
|
||||
|
||||
uniform mat4 u_matrix;
|
||||
uniform vec2 u_world;
|
||||
uniform float u_size;
|
||||
|
||||
varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0);
|
||||
v_pos = (gl_Position.xy + 1.0) * u_world;
|
||||
gl_PointSize = u_size;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <mbgl/shader/linejoin_shader.hpp>
|
||||
#include <mbgl/shader/shaders.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
LinejoinShader::LinejoinShader()
|
||||
: Shader(
|
||||
"linejoin",
|
||||
shaders[LINEJOIN_SHADER].vertex,
|
||||
shaders[LINEJOIN_SHADER].fragment
|
||||
) {
|
||||
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
|
||||
}
|
||||
|
||||
void LinejoinShader::bind(char *offset) {
|
||||
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
|
||||
// Note: We're referring to the vertices in a line array, which are 8 bytes long!
|
||||
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset));
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef MBGL_SHADER_SHADER_LINEJOIN
|
||||
#define MBGL_SHADER_SHADER_LINEJOIN
|
||||
|
||||
#include <mbgl/shader/shader.hpp>
|
||||
#include <mbgl/shader/uniform.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class LinejoinShader : public Shader {
|
||||
public:
|
||||
LinejoinShader();
|
||||
|
||||
void bind(char *offset);
|
||||
|
||||
UniformMatrix<4> u_matrix = {"u_matrix", *this};
|
||||
Uniform<std::array<float, 4>> u_color = {"u_color", *this};
|
||||
Uniform<std::array<float, 2>> u_world = {"u_world", *this};
|
||||
Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this};
|
||||
Uniform<float> u_size = {"u_size", *this};
|
||||
|
||||
private:
|
||||
int32_t a_pos = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -91,13 +91,15 @@ MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, {
|
||||
enum class JoinType : uint8_t {
|
||||
Miter,
|
||||
Bevel,
|
||||
Round
|
||||
Round,
|
||||
FlipBevel
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
|
||||
{ JoinType::Miter, "miter" },
|
||||
{ JoinType::Bevel, "bevel" },
|
||||
{ JoinType::Round, "round" },
|
||||
{ JoinType::FlipBevel, "flipbevel" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user