move to layer system
This commit is contained in:
@@ -5,13 +5,13 @@
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class fill_buffer {
|
||||
class FillBuffer {
|
||||
public:
|
||||
typedef int16_t vertex_type;
|
||||
const uint8_t components = 2;
|
||||
|
||||
fill_buffer();
|
||||
~fill_buffer();
|
||||
FillBuffer();
|
||||
~FillBuffer();
|
||||
|
||||
uint32_t vertex_length() const;
|
||||
uint32_t elements_length() const;
|
||||
|
||||
31
include/llmr/map/layer.hpp
Normal file
31
include/llmr/map/layer.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef LLMR_MAP_LAYER
|
||||
#define LLMR_MAP_LAYER
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Bucket;
|
||||
class Tile;
|
||||
|
||||
class Layer {
|
||||
public:
|
||||
Layer(const std::string& name, const std::shared_ptr<Bucket>& bucket)
|
||||
: name(name),
|
||||
bucket(bucket) {}
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
std::shared_ptr<Bucket> bucket;
|
||||
|
||||
private:
|
||||
// Make noncopyable
|
||||
Layer(const Layer&) = delete;
|
||||
Layer(const Layer&&) = delete;
|
||||
Layer& operator=(const Layer&) = delete;
|
||||
Layer& operator=(const Layer && ) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../geometry/linevertexbuffer.hpp"
|
||||
#include "../geometry/fill_buffer.hpp"
|
||||
|
||||
#include "layer.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <forward_list>
|
||||
#include <mutex>
|
||||
@@ -32,76 +34,33 @@ public:
|
||||
obsolete
|
||||
};
|
||||
|
||||
struct fill_index {
|
||||
struct group {
|
||||
uint32_t vertex_length;
|
||||
uint32_t elements_length;
|
||||
|
||||
group() : vertex_length(0), elements_length(0) {}
|
||||
group(uint32_t vertex_length, uint32_t elements_length)
|
||||
: vertex_length(vertex_length),
|
||||
elements_length(elements_length) {
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t vertex_start;
|
||||
uint32_t elements_start;
|
||||
uint32_t length;
|
||||
std::vector<group> groups;
|
||||
|
||||
fill_index(uint32_t vertex_start, uint32_t elements_start)
|
||||
: vertex_start(vertex_start),
|
||||
elements_start(elements_start),
|
||||
length(0),
|
||||
groups(1) {
|
||||
}
|
||||
|
||||
// debug
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
// struct fill_index {
|
||||
// uint32_t vertex_start;
|
||||
// uint32_t vertex_length;
|
||||
// uint32_t elements_start;
|
||||
// uint32_t elements_length;
|
||||
// std::string name;
|
||||
|
||||
// fill_index(uint32_t vertex_start, uint32_t elements_start)
|
||||
// : vertex_start(vertex_start),
|
||||
// vertex_length(0),
|
||||
// elements_start(elements_start),
|
||||
// elements_length(0) {
|
||||
// }
|
||||
// };
|
||||
|
||||
public:
|
||||
Tile(ID id);
|
||||
~Tile();
|
||||
|
||||
// Make noncopyable
|
||||
Tile(const Tile &) = delete;
|
||||
Tile(const Tile &&) = delete;
|
||||
Tile &operator=(const Tile &) = delete;
|
||||
Tile &operator=(const Tile && ) = delete;
|
||||
Tile(const Tile&) = delete;
|
||||
Tile(const Tile&&) = delete;
|
||||
Tile& operator=(const Tile&) = delete;
|
||||
Tile& operator=(const Tile && ) = delete;
|
||||
|
||||
// Other functions
|
||||
void setData(uint8_t *data, uint32_t bytes);
|
||||
bool parse();
|
||||
void parseLayer(const uint8_t *data, uint32_t bytes);
|
||||
void parseFeature(const uint8_t *data, uint32_t bytes);
|
||||
void parseLayer(const pbf layer);
|
||||
// void parseFeature(const uint8_t *data, uint32_t bytes);
|
||||
|
||||
void addLineGeometry(pbf &geom);
|
||||
void addFillGeometry(pbf &geom);
|
||||
// void addLineGeometry(pbf& geom);
|
||||
// void addFillGeometry(pbf& geom);
|
||||
|
||||
std::shared_ptr<Bucket> createFillBucket(const pbf data);
|
||||
|
||||
void cancel();
|
||||
|
||||
const std::string toString() const;
|
||||
|
||||
static ID parent(const ID &id, int32_t z);
|
||||
static std::forward_list<ID> children(const ID &id, int32_t z);
|
||||
static ID parent(const ID& id, int32_t z);
|
||||
static std::forward_list<ID> children(const ID& id, int32_t z);
|
||||
|
||||
public:
|
||||
const ID id;
|
||||
@@ -109,8 +68,11 @@ public:
|
||||
linevertexbuffer lineVertex;
|
||||
debug_font_buffer debugFontVertex;
|
||||
|
||||
fill_buffer fillBuffer;
|
||||
std::vector<fill_index> fillIndices;
|
||||
std::shared_ptr<FillBuffer> fillBuffer;
|
||||
|
||||
std::forward_list<Layer> layers;
|
||||
|
||||
// std::vector<fill_index> fillIndices;
|
||||
|
||||
private:
|
||||
// Source data
|
||||
|
||||
19
include/llmr/renderer/bucket.hpp
Normal file
19
include/llmr/renderer/bucket.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef LLMR_GEOMETRY_BUCKET
|
||||
#define LLMR_GEOMETRY_BUCKET
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Style;
|
||||
class Painter;
|
||||
|
||||
class Bucket {
|
||||
public:
|
||||
virtual void draw(const Style& style, const std::string& layerName) = 0;
|
||||
virtual void render(Painter& painter) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
46
include/llmr/renderer/fill_bucket.hpp
Normal file
46
include/llmr/renderer/fill_bucket.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef LLMR_GEOMETRY_FILLBUCKET
|
||||
#define LLMR_GEOMETRY_FILLBUCKET
|
||||
|
||||
#include "bucket.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Style;
|
||||
class FillBuffer;
|
||||
struct pbf;
|
||||
|
||||
class FillBucket : public Bucket {
|
||||
public:
|
||||
FillBucket(const std::shared_ptr<FillBuffer>& buffer);
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
|
||||
virtual void draw(const Style& style, const std::string& layerName);
|
||||
virtual void render(Painter& painter);
|
||||
|
||||
public:
|
||||
std::shared_ptr<FillBuffer> buffer;
|
||||
|
||||
struct group {
|
||||
uint32_t vertex_length;
|
||||
uint32_t elements_length;
|
||||
|
||||
group() : vertex_length(0), elements_length(0) {}
|
||||
group(uint32_t vertex_length, uint32_t elements_length)
|
||||
: vertex_length(vertex_length),
|
||||
elements_length(elements_length) {
|
||||
}
|
||||
};
|
||||
|
||||
// hold information on where the vertices are located in the FillBuffer
|
||||
uint32_t vertex_start;
|
||||
uint32_t elements_start;
|
||||
uint32_t length;
|
||||
std::vector<group> groups;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,9 @@ namespace llmr {
|
||||
class Settings;
|
||||
class Transform;
|
||||
|
||||
class FillBucket;
|
||||
struct FillStyle;
|
||||
|
||||
class Painter {
|
||||
public:
|
||||
Painter(Transform& transform, Settings& settings);
|
||||
@@ -31,7 +34,7 @@ public:
|
||||
void renderDebug(const std::shared_ptr<Tile>& tile);
|
||||
|
||||
void renderBackground();
|
||||
void renderFill(const std::shared_ptr<Tile>& tile, const Tile::fill_index& index);
|
||||
void renderFill(const FillBucket& bucket, const FillStyle& style);
|
||||
|
||||
|
||||
void drawClippingMask();
|
||||
|
||||
27
include/llmr/renderer/stroke_bucket.hpp
Normal file
27
include/llmr/renderer/stroke_bucket.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef LLMR_GEOMETRY_STROKEBUCKET
|
||||
#define LLMR_GEOMETRY_STROKEBUCKET
|
||||
|
||||
#include "bucket.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Style;
|
||||
class StrokeBuffer;
|
||||
|
||||
class StrokeBucket : public Bucket {
|
||||
public:
|
||||
virtual void draw(const Style& style, const std::string& layerName);
|
||||
|
||||
private:
|
||||
std::shared_ptr<StrokeBuffer> buffer;
|
||||
|
||||
// hold information on where the vertices are located in the StrokeBuffer
|
||||
uint32_t vertex_start;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
46
include/llmr/style/style.hpp
Normal file
46
include/llmr/style/style.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef LLMR_STYLE_STYLE
|
||||
#define LLMR_STYLE_STYLE
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
typedef std::array<float, 4> Color;
|
||||
|
||||
enum Winding {
|
||||
EvenOdd = 1,
|
||||
NonZero = 2
|
||||
};
|
||||
|
||||
enum LineCap {
|
||||
Round = 1
|
||||
};
|
||||
|
||||
enum LineJoin {
|
||||
Butt = 1,
|
||||
Bevel = 2
|
||||
};
|
||||
|
||||
struct StrokeStyle {
|
||||
Color line_color = {{ 0, 0, 0, 1 }};
|
||||
float line_width = 1;
|
||||
};
|
||||
|
||||
struct FillStyle {
|
||||
Winding winding = NonZero;
|
||||
bool antialiasing = true;
|
||||
Color fill_color = {{ 0, 0, 0, 1 }};
|
||||
Color stroke_color = {{ 0, 0, 0, 1 }};
|
||||
float stroke_width = 1;
|
||||
};
|
||||
|
||||
|
||||
class Style {
|
||||
public:
|
||||
StrokeStyle strokeStyle(const std::string& layer_name) const;
|
||||
FillStyle fillStyle(const std::string& layer_name) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -10,6 +10,7 @@ SET(llmr_SOURCES
|
||||
renderer/shader-line.cpp
|
||||
renderer/shader-outline.cpp
|
||||
renderer/shader.cpp
|
||||
renderer/fill_bucket.cpp
|
||||
shader/shaders.cpp
|
||||
util/animation.cpp
|
||||
util/mat4.cpp
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
fill_buffer::fill_buffer()
|
||||
FillBuffer::FillBuffer()
|
||||
: vertex_buffer(0),
|
||||
element_buffer(0),
|
||||
dirty(true) {
|
||||
|
||||
}
|
||||
|
||||
fill_buffer::~fill_buffer() {
|
||||
FillBuffer::~FillBuffer() {
|
||||
if (vertex_buffer != 0) {
|
||||
glDeleteBuffers(1, &vertex_buffer);
|
||||
vertex_buffer = 0;
|
||||
@@ -22,33 +22,33 @@ fill_buffer::~fill_buffer() {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t fill_buffer::vertex_length() const {
|
||||
uint32_t FillBuffer::vertex_length() const {
|
||||
// We store 2 coordinates per vertex
|
||||
return vertices.size() / 2;
|
||||
}
|
||||
|
||||
uint32_t fill_buffer::elements_length() const {
|
||||
uint32_t FillBuffer::elements_length() const {
|
||||
// A triangle has 3 indices
|
||||
return elements.size() / 3;
|
||||
}
|
||||
|
||||
void fill_buffer::addDegenerate() {
|
||||
void FillBuffer::addDegenerate() {
|
||||
vertices.push_back(32767);
|
||||
vertices.push_back(0);
|
||||
}
|
||||
|
||||
void fill_buffer::addCoordinate(int16_t x, int16_t y) {
|
||||
void FillBuffer::addCoordinate(int16_t x, int16_t y) {
|
||||
vertices.push_back(x);
|
||||
vertices.push_back(y);
|
||||
}
|
||||
|
||||
void fill_buffer::addElements(uint16_t a, uint16_t b, uint16_t c) {
|
||||
void FillBuffer::addElements(uint16_t a, uint16_t b, uint16_t c) {
|
||||
elements.push_back(a);
|
||||
elements.push_back(b);
|
||||
elements.push_back(c);
|
||||
}
|
||||
|
||||
void fill_buffer::bind() {
|
||||
void FillBuffer::bind() {
|
||||
if (vertex_buffer == 0) {
|
||||
glGenBuffers(1, &vertex_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
|
||||
|
||||
3
src/map/layer.cpp
Normal file
3
src/map/layer.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <llmr/map/layer.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
190
src/map/tile.cpp
190
src/map/tile.cpp
@@ -8,6 +8,7 @@
|
||||
#include <llmr/util/pbf.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
#include <llmr/geometry/geometry.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <cmath>
|
||||
|
||||
using namespace llmr;
|
||||
@@ -41,6 +42,7 @@ std::forward_list<Tile::ID> Tile::children(const ID& id, int32_t z) {
|
||||
Tile::Tile(ID id)
|
||||
: id(id),
|
||||
state(initial),
|
||||
fillBuffer(std::make_shared<FillBuffer>()),
|
||||
data(0),
|
||||
bytes(0) {
|
||||
|
||||
@@ -92,23 +94,13 @@ bool Tile::parse() {
|
||||
try {
|
||||
while (tile.next()) {
|
||||
if (tile.tag == 3) { // layer
|
||||
uint32_t bytes = (uint32_t)tile.varint();
|
||||
parseLayer(tile.data, bytes);
|
||||
tile.skipBytes(bytes);
|
||||
parseLayer(tile.message());
|
||||
} else {
|
||||
tile.skip();
|
||||
}
|
||||
}
|
||||
} catch (const pbf::exception& ex) {
|
||||
fprintf(stderr, "[%p] parsing tile [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
cancel();
|
||||
return false;
|
||||
} catch (const Tile::exception& ex) {
|
||||
fprintf(stderr, "[%p] parsing tile [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
cancel();
|
||||
return false;
|
||||
} catch (const std::exception& ex) {
|
||||
fprintf(stderr, "[%p] general exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
@@ -119,39 +111,23 @@ bool Tile::parse() {
|
||||
state = ready;
|
||||
}
|
||||
|
||||
// int i = 0;
|
||||
// for (const fill_index& index : fillIndices) {
|
||||
// fprintf(stderr, "[%p] %d: vertex % 8d / % 8d\n", this, i, index.vertex_start, index.vertex_length);
|
||||
// fprintf(stderr, "[%p] %d: elements % 8d / % 8d\n", this, i, index.elements_start, index.elements_length);
|
||||
// i++;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tile::parseLayer(const uint8_t *data, uint32_t bytes) {
|
||||
pbf layer(data, bytes);
|
||||
std::string name;
|
||||
|
||||
uint32_t vertex_start = fillBuffer.vertex_length();
|
||||
uint32_t elements_start = fillBuffer.elements_length();
|
||||
fillIndices.emplace_back(vertex_start, elements_start);
|
||||
|
||||
void Tile::parseLayer(const pbf data) {
|
||||
pbf layer(data);
|
||||
while (layer.next()) {
|
||||
if (layer.tag == 1) {
|
||||
name = layer.string();
|
||||
fillIndices.back().name = name;
|
||||
} else if (layer.tag == 2) {
|
||||
uint32_t bytes = (uint32_t)layer.varint();
|
||||
parseFeature(layer.data, bytes);
|
||||
layer.skipBytes(bytes);
|
||||
std::string name = layer.string();
|
||||
if (name == "water") {
|
||||
layers.emplace_front(name, createFillBucket(data));
|
||||
}
|
||||
} else {
|
||||
layer.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum geom_type {
|
||||
Unknown = 0,
|
||||
Point = 1,
|
||||
@@ -159,126 +135,46 @@ enum geom_type {
|
||||
Polygon = 3
|
||||
};
|
||||
|
||||
std::shared_ptr<Bucket> Tile::createFillBucket(const pbf data) {
|
||||
pbf layer(data);
|
||||
std::shared_ptr<FillBucket> bucket = std::make_shared<FillBucket>(fillBuffer);
|
||||
|
||||
void Tile::parseFeature(const uint8_t *data, uint32_t bytes) {
|
||||
pbf feature(data, bytes);
|
||||
while (layer.next()) {
|
||||
if (layer.tag == 2) { // feature
|
||||
pbf feature = layer.message();
|
||||
pbf geometry;
|
||||
geom_type type = Unknown;
|
||||
|
||||
geom_type type = Unknown;
|
||||
pbf geom;
|
||||
|
||||
while (feature.next()) {
|
||||
if (feature.tag == 1) {
|
||||
/*uint32_t id =*/ feature.varint();
|
||||
} else if (feature.tag == 2) {
|
||||
const uint8_t *tag_end = feature.data + feature.varint();
|
||||
while (feature.data < tag_end) {
|
||||
/*uint32_t key =*/ feature.varint();
|
||||
/*uint32_t value =*/ feature.varint();
|
||||
}
|
||||
} else if (feature.tag == 3) {
|
||||
type = (geom_type)feature.varint();
|
||||
} else if (feature.tag == 4) {
|
||||
geom = feature.message();
|
||||
} else {
|
||||
feature.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (geom) {
|
||||
if (type == LineString) {
|
||||
addLineGeometry(geom);
|
||||
} else if (type == Polygon) {
|
||||
addFillGeometry(geom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tile::addFillGeometry(pbf& geom) {
|
||||
|
||||
std::vector<std::vector<std::pair<int16_t, int16_t>>> lines;
|
||||
|
||||
{
|
||||
std::vector<std::pair<int16_t, int16_t>> line;
|
||||
Geometry::command cmd;
|
||||
int32_t x, y;
|
||||
Geometry geometry(geom);
|
||||
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
if (cmd == Geometry::move_to) {
|
||||
if (line.size()) {
|
||||
lines.push_back(line);
|
||||
line.clear();
|
||||
while (feature.next()) {
|
||||
if (feature.tag == 3) { // geometry type
|
||||
type = (geom_type)feature.varint();
|
||||
} else if (feature.tag == 4) { // geometry
|
||||
geometry = feature.message();
|
||||
} else {
|
||||
feature.skip();
|
||||
}
|
||||
}
|
||||
line.emplace_back(x, y);
|
||||
}
|
||||
if (line.size()) {
|
||||
lines.push_back(line);
|
||||
|
||||
if (type == Polygon && geometry) {
|
||||
bucket->addGeometry(geometry);
|
||||
}
|
||||
} else {
|
||||
layer.skip();
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::vector<std::pair<int16_t, int16_t>>& line : lines) {
|
||||
uint32_t vertex_start = fillBuffer.vertex_length();
|
||||
|
||||
fillBuffer.addDegenerate();
|
||||
for (const std::pair<int16_t, int16_t>& coord : line) {
|
||||
fillBuffer.addCoordinate(coord.first, coord.second);
|
||||
}
|
||||
|
||||
uint32_t vertex_end = fillBuffer.vertex_length();
|
||||
|
||||
if (vertex_end - vertex_start > 65535) {
|
||||
throw geometry_too_long_exception();
|
||||
}
|
||||
|
||||
if (!fillIndices.size()) {
|
||||
// Create a new index because there is none yet.
|
||||
throw std::runtime_error("no index yet");
|
||||
}
|
||||
|
||||
fill_index& index = fillIndices.back();
|
||||
if (!index.groups.size()) {
|
||||
throw std::runtime_error("no group yet");
|
||||
}
|
||||
|
||||
uint32_t vertex_count = vertex_end - vertex_start;
|
||||
index.length += vertex_count;
|
||||
|
||||
if (index.groups.back().vertex_length + vertex_count > 65535) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
index.groups.emplace_back();
|
||||
}
|
||||
|
||||
fill_index::group& group = index.groups.back();
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
// coordinate in this polygon.
|
||||
// The first valid index that is not a degenerate.
|
||||
uint16_t firstIndex = group.vertex_length + 1;
|
||||
|
||||
assert(firstIndex + vertex_count - 1 < 65536);
|
||||
|
||||
uint32_t elements_start = fillBuffer.elements_length();
|
||||
|
||||
for (uint32_t i = 2; i < vertex_count; i++) {
|
||||
fillBuffer.addElements(firstIndex, firstIndex + i - 1, firstIndex + i);
|
||||
}
|
||||
|
||||
uint32_t elements_end = fillBuffer.elements_length();
|
||||
uint32_t elements_count = elements_end - elements_start;
|
||||
group.vertex_length += vertex_count;
|
||||
group.elements_length += elements_count;
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
void Tile::addLineGeometry(pbf& geom) {
|
||||
Geometry geometry(geom);
|
||||
// void Tile::addLineGeometry(pbf& geom) {
|
||||
// Geometry geometry(geom);
|
||||
|
||||
Geometry::command cmd;
|
||||
int32_t x, y;
|
||||
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
if (cmd == Geometry::move_to) {
|
||||
lineVertex.addDegenerate();
|
||||
}
|
||||
lineVertex.addCoordinate(x, y);
|
||||
}
|
||||
}
|
||||
// Geometry::command cmd;
|
||||
// int32_t x, y;
|
||||
// while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
// if (cmd == Geometry::move_to) {
|
||||
// lineVertex.addDegenerate();
|
||||
// }
|
||||
// lineVertex.addCoordinate(x, y);
|
||||
// }
|
||||
// }
|
||||
|
||||
103
src/renderer/fill_bucket.cpp
Normal file
103
src/renderer/fill_bucket.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/geometry.hpp>
|
||||
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/style/style.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
struct geometry_too_long_exception : std::exception {};
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
FillBucket::FillBucket(const std::shared_ptr<FillBuffer>& buffer)
|
||||
: buffer(buffer),
|
||||
vertex_start(buffer->vertex_length()),
|
||||
elements_start(buffer->elements_length()),
|
||||
length(0) {
|
||||
}
|
||||
|
||||
void FillBucket::addGeometry(pbf& geom) {
|
||||
std::vector<std::vector<std::pair<int16_t, int16_t>>> lines;
|
||||
|
||||
{
|
||||
std::vector<std::pair<int16_t, int16_t>> line;
|
||||
Geometry::command cmd;
|
||||
int32_t x, y;
|
||||
Geometry geometry(geom);
|
||||
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
if (cmd == Geometry::move_to) {
|
||||
if (line.size()) {
|
||||
lines.push_back(line);
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
line.emplace_back(x, y);
|
||||
}
|
||||
if (line.size()) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::vector<std::pair<int16_t, int16_t>>& line : lines) {
|
||||
uint32_t vertex_start = buffer->vertex_length();
|
||||
|
||||
buffer->addDegenerate();
|
||||
for (const std::pair<int16_t, int16_t>& coord : line) {
|
||||
buffer->addCoordinate(coord.first, coord.second);
|
||||
}
|
||||
|
||||
uint32_t vertex_end = buffer->vertex_length();
|
||||
|
||||
if (vertex_end - vertex_start > 65535) {
|
||||
throw geometry_too_long_exception();
|
||||
}
|
||||
|
||||
FillBucket& index = *this;
|
||||
if (!index.groups.size()) {
|
||||
index.groups.emplace_back();
|
||||
}
|
||||
|
||||
uint32_t vertex_count = vertex_end - vertex_start;
|
||||
index.length += vertex_count;
|
||||
|
||||
if (index.groups.back().vertex_length + vertex_count > 65535) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
index.groups.emplace_back();
|
||||
}
|
||||
|
||||
group& group = index.groups.back();
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
// coordinate in this polygon.
|
||||
// The first valid index that is not a degenerate.
|
||||
uint16_t firstIndex = group.vertex_length + 1;
|
||||
|
||||
assert(firstIndex + vertex_count - 1 < 65536);
|
||||
|
||||
uint32_t elements_start = buffer->elements_length();
|
||||
|
||||
for (uint32_t i = 2; i < vertex_count; i++) {
|
||||
buffer->addElements(firstIndex, firstIndex + i - 1, firstIndex + i);
|
||||
}
|
||||
|
||||
uint32_t elements_end = buffer->elements_length();
|
||||
uint32_t elements_count = elements_end - elements_start;
|
||||
group.vertex_length += vertex_count;
|
||||
group.elements_length += elements_count;
|
||||
}
|
||||
}
|
||||
|
||||
void FillBucket::draw(const Style& style, const std::string& layerName) {
|
||||
|
||||
}
|
||||
|
||||
void FillBucket::render(Painter& painter) {
|
||||
// TODO: obtain the correct style information for this layer
|
||||
FillStyle style;
|
||||
style.fill_color = {{ 0, 0, 1, 0.5 }};
|
||||
style.stroke_color = style.fill_color;
|
||||
|
||||
painter.renderFill(*this, style);
|
||||
}
|
||||
@@ -8,8 +8,11 @@
|
||||
#include <llmr/platform/gl.hpp>
|
||||
#include <llmr/map/settings.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/style/style.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
|
||||
using namespace llmr;
|
||||
@@ -17,9 +20,14 @@ using namespace llmr;
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
GLshort tile_stencil_vertices[] = {
|
||||
// top left triangle
|
||||
0, 0,
|
||||
4096, 0,
|
||||
0, 4096,
|
||||
|
||||
// bottom right triangle
|
||||
4096, 0,
|
||||
0, 4096,
|
||||
4096, 4096
|
||||
};
|
||||
|
||||
@@ -124,7 +132,7 @@ void Painter::drawClippingMask() {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
|
||||
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
|
||||
glUniform4f(lineShader->u_color, 1.0f, 0.0f, 1.0f, 1.0f);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(tile_stencil_vertices));
|
||||
glDrawArrays(GL_TRIANGLES, 0, sizeof(tile_stencil_vertices));
|
||||
|
||||
// glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
@@ -152,9 +160,8 @@ void Painter::render(const Tile::Ptr& tile) {
|
||||
switchShader(outlineShader);
|
||||
glUniformMatrix4fv(outlineShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
// glDisable(GL_STENCIL_TEST);
|
||||
// glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
// glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
// draw lines:
|
||||
tile->lineVertex.bind();
|
||||
@@ -164,12 +171,8 @@ void Painter::render(const Tile::Ptr& tile) {
|
||||
glLineWidth(2.0f);
|
||||
glDrawArrays(GL_LINE_STRIP, 0, tile->lineVertex.length());
|
||||
|
||||
// draw fills:
|
||||
|
||||
// TODO: expose this to the stylesheet
|
||||
|
||||
for (const Tile::fill_index& index : tile->fillIndices) {
|
||||
renderFill(tile, index);
|
||||
for (Layer& layer : tile->layers) {
|
||||
layer.bucket->render(*this);
|
||||
}
|
||||
|
||||
if (settings.debug) {
|
||||
@@ -179,145 +182,131 @@ void Painter::render(const Tile::Ptr& tile) {
|
||||
renderBackground();
|
||||
}
|
||||
|
||||
void Painter::renderFill(const std::shared_ptr<Tile>& tile, const Tile::fill_index& index) {
|
||||
float color[4] = { 1.0, 0.0, 0.0, 1.0 };
|
||||
float alpha = color[3];
|
||||
void Painter::renderFill(const FillBucket& bucket, const FillStyle& style) {
|
||||
// Draw the stencil mask.
|
||||
{
|
||||
// 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);
|
||||
|
||||
// TODO: expose this to the stylesheet.
|
||||
bool evenodd = false;
|
||||
bool background = false;
|
||||
bool antialiasing = true;
|
||||
bool stroke = false;
|
||||
// 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_NOTEQUAL, 0x80, 0x80);
|
||||
|
||||
if (!background) {
|
||||
// Draw the stencil mask.
|
||||
{
|
||||
// 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_NOTEQUAL, 0x80, 0x80);
|
||||
|
||||
if (evenodd) {
|
||||
// When we draw an even/odd winding fill, we just invert all the bits.
|
||||
glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP);
|
||||
} else {
|
||||
// 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.
|
||||
glStencilOpSeparate(GL_FRONT, GL_INCR_WRAP, GL_KEEP, GL_KEEP);
|
||||
glStencilOpSeparate(GL_BACK, GL_DECR_WRAP, GL_KEEP, GL_KEEP);
|
||||
}
|
||||
|
||||
// When drawing a shape, we first draw all shapes to the stencil buffer
|
||||
// and incrementing all areas where polygons are
|
||||
glColorMask(false, false, false, false);
|
||||
|
||||
// Draw the actual triangle fan into the stencil buffer.
|
||||
switchShader(fillShader);
|
||||
glUniformMatrix4fv(fillShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
|
||||
// Draw all groups
|
||||
char *vertex_index = BUFFER_OFFSET(index.vertex_start * 2 * sizeof(uint16_t));
|
||||
char *elements_index = BUFFER_OFFSET(index.elements_start * 3 * sizeof(uint16_t));
|
||||
tile->fillBuffer.bind();
|
||||
for (const Tile::fill_index::group& group : index.groups) {
|
||||
glVertexAttribPointer(fillShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, vertex_index);
|
||||
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
|
||||
vertex_index += group.vertex_length * 2 * sizeof(uint16_t);
|
||||
elements_index += group.elements_length * 3 * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
// Now that we have the stencil mask in the stencil buffer, we can start
|
||||
// writing to the color buffer.
|
||||
glColorMask(true, true, true, true);
|
||||
if (style.winding == EvenOdd) {
|
||||
// When we draw an even/odd winding fill, we just invert all the bits.
|
||||
glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP);
|
||||
} else {
|
||||
// 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.
|
||||
glStencilOpSeparate(GL_FRONT, GL_INCR_WRAP, GL_KEEP, GL_KEEP);
|
||||
glStencilOpSeparate(GL_BACK, GL_DECR_WRAP, GL_KEEP, GL_KEEP);
|
||||
}
|
||||
|
||||
// From now on, we don't want to update the stencil buffer anymore.
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glStencilMask(0x0);
|
||||
// When drawing a shape, we first draw all shapes to the stencil buffer
|
||||
// and incrementing all areas where polygons are
|
||||
glColorMask(false, false, false, false);
|
||||
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (antialiasing) {
|
||||
switchShader(outlineShader);
|
||||
glUniformMatrix4fv(outlineShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
glLineWidth(2);
|
||||
// Draw the actual triangle fan into the stencil buffer.
|
||||
switchShader(fillShader);
|
||||
glUniformMatrix4fv(fillShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
|
||||
if (stroke) {
|
||||
// If we defined a different color for the fill outline, we are
|
||||
// going to ignore the bits in 0x3F and just care about the global
|
||||
// clipping mask.
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
glUniform2f(outlineShader->u_world, transform.fb_width, transform.fb_height);
|
||||
glUniform4f(outlineShader->u_color, color[0], color[1], color[2], color[3]);
|
||||
|
||||
// Draw the entire line
|
||||
char *vertex_index = BUFFER_OFFSET(index.vertex_start * 2 * sizeof(uint16_t));
|
||||
glVertexAttribPointer(outlineShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, vertex_index);
|
||||
glLineWidth(2.0f);
|
||||
glDrawArrays(GL_LINE_STRIP, 0, index.length);
|
||||
// Draw all groups
|
||||
char *vertex_index = BUFFER_OFFSET(bucket.vertex_start * 2 * sizeof(uint16_t));
|
||||
char *elements_index = BUFFER_OFFSET(bucket.elements_start * 3 * sizeof(uint16_t));
|
||||
bucket.buffer->bind();
|
||||
for (const auto& group : bucket.groups) {
|
||||
glVertexAttribPointer(fillShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, vertex_index);
|
||||
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
|
||||
vertex_index += group.vertex_length * 2 * sizeof(uint16_t);
|
||||
elements_index += group.elements_length * 3 * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
// Now that we have the stencil mask in the stencil buffer, we can start
|
||||
// writing to the color buffer.
|
||||
glColorMask(true, true, true, true);
|
||||
}
|
||||
|
||||
// From now on, we don't want to update the stencil buffer anymore.
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glStencilMask(0x0);
|
||||
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (style.antialiasing) {
|
||||
switchShader(outlineShader);
|
||||
glUniformMatrix4fv(outlineShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
glLineWidth(2);
|
||||
|
||||
if (style.stroke_color != style.fill_color) {
|
||||
// If we defined a different color for the fill outline, we are
|
||||
// going to ignore the bits in 0x3F and just care about the global
|
||||
// clipping mask.
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
const Color& color = style.stroke_color;
|
||||
glUniform4f(outlineShader->u_color, color[0], color[1], color[2], color[3]);
|
||||
} else {
|
||||
// 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);
|
||||
const Color& color = style.fill_color;
|
||||
glUniform4f(outlineShader->u_color, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
glUniform2f(outlineShader->u_world, transform.fb_width, transform.fb_height);
|
||||
|
||||
// Draw the entire line
|
||||
char *vertex_index = BUFFER_OFFSET(bucket.vertex_start * 2 * sizeof(uint16_t));
|
||||
glVertexAttribPointer(outlineShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, vertex_index);
|
||||
glLineWidth(2.0f);
|
||||
glDrawArrays(GL_LINE_STRIP, 0, bucket.length);
|
||||
}
|
||||
|
||||
// var imagePos = layerStyle.image && imageSprite.getPosition(layerStyle.image, true);
|
||||
bool imagePos = false;
|
||||
if (imagePos) {
|
||||
// // Draw texture fill
|
||||
|
||||
// if (imagePos) {
|
||||
// // Draw texture fill
|
||||
// var factor = 8 / Math.pow(2, painter.transform.zoom - params.z);
|
||||
// var mix = painter.transform.z % 1.0;
|
||||
// var imageSize = [imagePos.size[0] * factor, imagePos.size[1] * factor];
|
||||
|
||||
// var factor = 8 / Math.pow(2, painter.transform.zoom - params.z);
|
||||
// var mix = painter.transform.z % 1.0;
|
||||
// var imageSize = [imagePos.size[0] * factor, imagePos.size[1] * factor];
|
||||
// var offset = [
|
||||
// (params.x * 4096) % imageSize[0],
|
||||
// (params.y * 4096) % imageSize[1]
|
||||
// ];
|
||||
|
||||
// var offset = [
|
||||
// (params.x * 4096) % imageSize[0],
|
||||
// (params.y * 4096) % imageSize[1]
|
||||
// ];
|
||||
|
||||
// glSwitchShader(painter.patternShader, painter.posMatrix, painter.exMatrix);
|
||||
// glUniform1i(painter.patternShader.u_image, 0);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_size, imageSize);
|
||||
// glUniform2fv(painter.patternShader.u_offset, offset);
|
||||
// glUniform2fv(painter.patternShader.u_rotate, [1, 1]);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_tl, imagePos.tl);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_br, imagePos.br);
|
||||
// glUniform4fv(painter.patternShader.u_color, color);
|
||||
// glUniform1f(painter.patternShader.u_mix, mix);
|
||||
// imageSprite.bind(gl, true);
|
||||
|
||||
// } else {
|
||||
// glSwitchShader(painter.patternShader, painter.posMatrix, painter.exMatrix);
|
||||
// glUniform1i(painter.patternShader.u_image, 0);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_size, imageSize);
|
||||
// glUniform2fv(painter.patternShader.u_offset, offset);
|
||||
// glUniform2fv(painter.patternShader.u_rotate, [1, 1]);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_tl, imagePos.tl);
|
||||
// glUniform2fv(painter.patternShader.u_pattern_br, imagePos.br);
|
||||
// glUniform4fv(painter.patternShader.u_color, color);
|
||||
// glUniform1f(painter.patternShader.u_mix, mix);
|
||||
// imageSprite.bind(gl, true);
|
||||
} else {
|
||||
// Draw filling rectangle.
|
||||
switchShader(fillShader);
|
||||
glUniformMatrix4fv(fillShader->u_matrix, 1, GL_FALSE, matrix);
|
||||
glUniform4f(fillShader->u_color, color[0], color[1], color[2], color[3]);
|
||||
// }
|
||||
|
||||
if (background) {
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
} else {
|
||||
// Only draw regions that we marked
|
||||
glStencilFunc(GL_NOTEQUAL, 0x0, 0x3F);
|
||||
glUniform4f(fillShader->u_color, style.fill_color[0], style.fill_color[1], style.fill_color[2], style.fill_color[3]);
|
||||
}
|
||||
|
||||
// Only draw regions that we marked
|
||||
glStencilFunc(GL_NOTEQUAL, 0x0, 0x3F);
|
||||
|
||||
// Draw a rectangle that covers the entire viewport.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
|
||||
glVertexAttribPointer(fillShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(tile_stencil_vertices));
|
||||
glDrawArrays(GL_TRIANGLES, 0, sizeof(tile_stencil_vertices));
|
||||
|
||||
glStencilMask(0x00);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
@@ -359,8 +348,8 @@ void Painter::renderBackground() {
|
||||
// Draw the clipping mask
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
|
||||
glVertexAttribPointer(fillShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
|
||||
glUniform4f(fillShader->u_color, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(tile_stencil_vertices));
|
||||
glUniform4f(fillShader->u_color, 0.5f, 0.5f, 0.5f, 1.0f);
|
||||
glDrawArrays(GL_TRIANGLES, 0, sizeof(tile_stencil_vertices));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user