move to layer system

This commit is contained in:
Konstantin Käfer
2014-01-23 14:20:22 +01:00
parent b4a95be0b2
commit 50021b730c
14 changed files with 471 additions and 345 deletions

View File

@@ -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;

View 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

View File

@@ -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

View 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

View 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

View File

@@ -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();

View 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

View 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

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,3 @@
#include <llmr/map/layer.hpp>
using namespace llmr;

View File

@@ -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);
// }
// }

View 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);
}

View File

@@ -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));
}