implement tile wrapping

fixes #14
This commit is contained in:
Konstantin Käfer
2014-03-05 09:40:32 +01:00
parent b0ddabd6ac
commit 027c6f33b3
9 changed files with 106 additions and 69 deletions

View File

@@ -65,8 +65,8 @@ private:
bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
bool updateTiles();
std::shared_ptr<TileData> addTile(const Tile::ID& id);
std::shared_ptr<TileData> hasTile(const Tile::ID& id);
const Tile *addTile(const Tile::ID& id);
const Tile *hasTile(const Tile::ID& id);
void update();
@@ -82,8 +82,8 @@ private:
float pixel_ratio;
std::forward_list<std::shared_ptr<TileData>> tiles;
std::forward_list<std::shared_ptr<TileData>> historic_tiles;
std::forward_list<Tile> tiles;
std::forward_list<std::weak_ptr<TileData>> tile_data;
};
}

View File

@@ -2,19 +2,34 @@
#define LLMR_MAP_TILE
#include <llmr/util/vec.hpp>
#include <llmr/util/mat4.hpp>
#include <llmr/util/noncopyable.hpp>
#include <cstdint>
#include <forward_list>
namespace llmr {
class Tile {
class TileData;
class Tile : private util::noncopyable {
public:
typedef vec3<int32_t> ID;
public:
Tile(const ID& id);
static ID parent(const ID& id, int32_t z);
static ID normalize(const ID& id);
static std::forward_list<ID> children(const ID& id, int32_t z);
public:
const Tile::ID id;
uint8_t clip_id;
mat4 matrix;
std::shared_ptr<TileData> data;
};
}

View File

@@ -71,7 +71,6 @@ public:
public:
const Tile::ID id;
uint8_t clip_id;
std::atomic<state> state;
// Holds the actual geometries in this tile.
@@ -89,8 +88,6 @@ public:
// Holds the buckets of this tile.
// They contain the location offsets in the buffers stored above
std::map<std::string, std::shared_ptr<Bucket>> buckets;
mat4 matrix;
private:
// Source data
std::string data;

View File

@@ -33,7 +33,7 @@ public:
void setup();
void clear();
void changeMatrix();
void render(const std::shared_ptr<TileData>& tile);
void render(const Tile& tile);
void renderMatte();
void renderBackground();
void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id);

View File

@@ -69,11 +69,10 @@ std::forward_list<llmr::vec3<int32_t>> llmr::covering_tiles(int32_t zoom, const
std::forward_list<vec3<int32_t>> tiles;
auto scanLine = [&tiles, zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
int32_t x, wx;
int32_t x;
if (y >= 0 && y <= ymax) {
for (x = x0; x < x1; x++) {
wx = (x + ymax) % ymax;
tiles.emplace_front(wx, y, zoom);
tiles.emplace_front(x, y, zoom);
}
}
};

View File

@@ -238,29 +238,42 @@ void Map::update() {
}
TileData::Ptr Map::hasTile(const Tile::ID& id) {
for (TileData::Ptr& tile : tiles) {
if (tile->id == id) {
return tile;
const Tile *Map::hasTile(const Tile::ID& id) {
for (const Tile& tile : tiles) {
if (tile.id == id) {
return &tile;
}
}
return TileData::Ptr();
return nullptr;
}
TileData::Ptr Map::addTile(const Tile::ID& id) {
TileData::Ptr tile = hasTile(id);
const Tile *Map::addTile(const Tile::ID& id) {
const Tile *tile = hasTile(id);
if (!tile.get()) {
// We couldn't find the tile in the list. Create a new one.
tile = std::make_shared<TileData>(id, style);
assert(tile);
// std::cerr << "init " << id.z << "/" << id.x << "/" << id.y << std::endl;
// std::cerr << "add " << tile->toString() << std::endl;
tiles.push_front(tile);
if (tile) {
return tile;
}
return tile;
tiles.emplace_front(id);
Tile& new_tile = tiles.front();
// We couldn't find the tile in the list. Create a new one.
// Try to find the associated TileData object.
const Tile::ID normalized_id = Tile::normalize(id);
auto it = std::find_if(tile_data.begin(), tile_data.end(), [&normalized_id](const std::weak_ptr<TileData>& tile_data) {
return !tile_data.expired() && tile_data.lock()->id == normalized_id;
});
if (it != tile_data.end() && !it->expired()) {
// There is already a tile data object and it's not yet expired, so we're
// going to reuse it.
new_tile.data = it->lock();
} else {
new_tile.data = std::make_shared<TileData>(normalized_id, style);
tile_data.push_front(new_tile.data);
}
return &new_tile;
}
/**
@@ -278,9 +291,8 @@ bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::f
auto ids = Tile::children(id, z + 1);
for (const Tile::ID& child_id : ids) {
const TileData::Ptr& tile = hasTile(child_id);
if (tile && tile->state == TileData::parsed) {
assert(tile);
const Tile *tile = hasTile(child_id);
if (tile && tile->data && tile->data->state == TileData::parsed) {
retain.emplace_front(tile->id);
} else {
complete = false;
@@ -305,9 +317,8 @@ bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::f
bool Map::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain) {
for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
const Tile::ID parent_id = Tile::parent(id, z);
const TileData::Ptr tile = hasTile(parent_id);
if (tile && tile->state == TileData::parsed) {
assert(tile);
const Tile *tile = hasTile(parent_id);
if (tile && tile->data && tile->data->state == TileData::parsed) {
retain.emplace_front(tile->id);
return true;
}
@@ -344,10 +355,10 @@ bool Map::updateTiles() {
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const Tile::ID& id : required) {
TileData::Ptr tile = addTile(id);
const Tile *tile = addTile(id);
assert(tile);
if (tile->state != TileData::parsed) {
if (tile && tile->data && tile->data->state != TileData::parsed) {
// The tile we require is not yet loaded. Try to find a parent or
// child tile that we already have.
@@ -362,20 +373,18 @@ bool Map::updateTiles() {
}
}
if (tile->state == TileData::initial) {
if (tile && tile->data && tile->data->state == TileData::initial) {
// If the tile is new, we have to make sure to load it.
tile->request();
tile->data->request();
changed = true;
}
}
// Remove tiles that we definitely don't need, i.e. tiles that are not on
// the required list.
tiles.remove_if([&retain, &changed](const TileData::Ptr& tile) {
assert(tile);
bool obsolete = std::find(retain.begin(), retain.end(), tile->id) == retain.end();
tiles.remove_if([&retain, &changed](const Tile & tile) {
bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end();
if (obsolete) {
tile->cancel();
changed = true;
}
return obsolete;
@@ -384,8 +393,13 @@ bool Map::updateTiles() {
// Sort tiles by zoom level, front to back.
// We're painting front-to-back, so we want to draw more detailed tiles first
// before filling in other parts with lower zoom levels.
tiles.sort([](const TileData::Ptr& a, const TileData::Ptr& b) {
return a->id.z > b->id.z;
tiles.sort([](const Tile & a, const Tile & b) {
return a.id.z > b.id.z;
});
// Remove all the expired pointers from the list.
tile_data.remove_if([](const std::weak_ptr<TileData>& tile_data) {
return tile_data.expired();
});
return changed;
@@ -406,20 +420,19 @@ bool Map::render() {
// the stencil buffer.
uint8_t i = 1;
painter.prepareClippingMask();
for (TileData::Ptr& tile : tiles) {
assert(tile);
if (tile->state == TileData::parsed) {
for (Tile& tile : tiles) {
if (tile.data && tile.data->state == TileData::parsed) {
// The position matrix.
transform.matrixFor(tile->matrix, tile->id);
matrix::multiply(tile->matrix, painter.projMatrix, tile->matrix);
tile->clip_id = i++;
painter.drawClippingMask(tile->matrix, tile->clip_id);
transform.matrixFor(tile.matrix, tile.id);
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
tile.clip_id = i++;
painter.drawClippingMask(tile.matrix, tile.clip_id);
}
}
painter.finishClippingMask();
for (TileData::Ptr& tile : tiles) {
if (tile->state == TileData::parsed) {
for (const Tile& tile : tiles) {
if (tile.data && tile.data->state == TileData::parsed) {
painter.render(tile);
}
}

View File

@@ -4,6 +4,10 @@
using namespace llmr;
Tile::Tile(const ID& id)
: id(id) {
}
Tile::ID Tile::parent(const ID& id, int32_t z) {
assert(z < id.z);
ID pos(id);
@@ -29,3 +33,11 @@ std::forward_list<Tile::ID> Tile::children(const ID& id, int32_t z) {
return children;
}
Tile::ID Tile::normalize(const ID& id) {
ID pos(id);
int32_t dim = pow(2, id.z);
while (pos.x < 0) pos.x += dim;
while (pos.x >= dim) pos.x -= dim;
return pos;
}

View File

@@ -83,9 +83,9 @@ bool TileData::parse() {
parseStyleLayers(tile, style.layers);
double parse_time = (platform::time() - parse_time_start) * 1000.0;
if (state == obsolete) {
fprintf(stderr, "[%p] parsing [%d/%d/%d] cancelled after %8.3fms\n", this, id.z, id.x, id.y, parse_time);
// fprintf(stderr, "[%p] parsing [%d/%d/%d] cancelled after %8.3fms\n", this, id.z, id.x, id.y, parse_time);
} else {
fprintf(stderr, "[%p] parsing [%d/%d/%d] took %8.3fms\n", this, id.z, id.x, id.y, parse_time);
// fprintf(stderr, "[%p] parsing [%d/%d/%d] took %8.3fms\n", this, id.z, id.x, id.y, parse_time);
}
} catch (const std::exception& ex) {
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());

View File

@@ -130,24 +130,25 @@ void Painter::clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void Painter::render(const TileData::Ptr& tile) {
if (tile->state != TileData::parsed) {
void Painter::render(const Tile& tile) {
assert(tile.data);
if (tile.data->state != TileData::parsed) {
return;
}
matrix = tile->matrix;
glStencilFunc(GL_EQUAL, tile->clip_id, 0xFF);
matrix = tile.matrix;
glStencilFunc(GL_EQUAL, tile.clip_id, 0xFF);
renderLayers(tile, style.layers);
renderLayers(tile.data, style.layers);
if (settings.debug) {
renderDebug(tile);
renderDebug(tile.data);
}
renderBackground();
}
void Painter::renderLayers(const std::shared_ptr<TileData>& tile, const std::vector<LayerDescription>& layers) {
void Painter::renderLayers(const std::shared_ptr<TileData>& tile_data, const std::vector<LayerDescription>& layers) {
// Render everything top-to-bottom by using reverse iterators
typedef std::vector<LayerDescription>::const_reverse_iterator iterator;
for (iterator it = layers.rbegin(), end = layers.rend(); it != end; ++it) {
@@ -156,15 +157,15 @@ void Painter::renderLayers(const std::shared_ptr<TileData>& tile, const std::vec
if (layer_desc.child_layer.size()) {
// This is a layer group.
// TODO: create framebuffer
renderLayers(tile, layer_desc.child_layer);
renderLayers(tile_data, layer_desc.child_layer);
// TODO: render framebuffer on previous framebuffer
} else {
// This is a singular layer. Try to find the bucket associated with
// this layer and render it.
auto bucket_it = tile->buckets.find(layer_desc.bucket_name);
if (bucket_it != tile->buckets.end()) {
auto bucket_it = tile_data->buckets.find(layer_desc.bucket_name);
if (bucket_it != tile_data->buckets.end()) {
assert(bucket_it->second);
bucket_it->second->render(*this, layer_desc.name, tile->id);
bucket_it->second->render(*this, layer_desc.name, tile_data->id);
}
}
}
@@ -423,7 +424,7 @@ void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, co
bucket.drawPoints(*pointShader);
}
void Painter::renderDebug(const TileData::Ptr& tile) {
void Painter::renderDebug(const TileData::Ptr& tile_data) {
// Disable depth test and don't count this towards the depth buffer,
// but *don't* disable stencil test, as we want to clip the red tile border
// to the tile viewport.
@@ -442,13 +443,13 @@ void Painter::renderDebug(const TileData::Ptr& tile) {
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index());
// draw debug info
tile->debugFontArray.bind(*plainShader, tile->debugFontBuffer, BUFFER_OFFSET(0));
tile_data->debugFontArray.bind(*plainShader, tile_data->debugFontBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
lineWidth(4.0f * transform.pixelRatio);
glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index());
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
lineWidth(2.0f * transform.pixelRatio);
glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index());
// Revert blending mode to blend to the back.
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);