diff --git a/bin/style.js b/bin/style.js index 47cdcc1f9..9bbe86631 100644 --- a/bin/style.js +++ b/bin/style.js @@ -2067,6 +2067,19 @@ module.exports = { "opacity": 0.25 }, } + }, { + "name": "satellite", + "layers": { + "building": { + "enabled": false, + }, + "building_wall": { + "enabled": false, + }, + "building_shadow": { + "enabled": false, + }, + } }], "sprite": "/img/maki-sprite" }; diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp index a9bdf53e5..e82ef42b5 100644 --- a/include/llmr/map/map.hpp +++ b/include/llmr/map/map.hpp @@ -115,6 +115,8 @@ private: // Unconditionally performs a render with the current map state. void render(); + void renderLayers(const std::vector& layers); + void renderLayer(const LayerDescription& layer_desc); private: // If cleared, the next time the render thread attempts to render the map, it will *actually* diff --git a/include/llmr/map/source.hpp b/include/llmr/map/source.hpp index 4da8e6149..7d47d61b7 100644 --- a/include/llmr/map/source.hpp +++ b/include/llmr/map/source.hpp @@ -36,7 +36,8 @@ public: bool update(); size_t prepareRender(const TransformState &transform); - void render(); + void render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc); + void finishRender(); std::forward_list getIDs() const; void updateClipIDs(const std::map &mapping); diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp index a5455ba62..d236ead44 100644 --- a/include/llmr/renderer/painter.hpp +++ b/include/llmr/renderer/painter.hpp @@ -43,11 +43,23 @@ public: void setup(); void clear(); + + // Updates the default matrices to the current viewport dimensions. void changeMatrix(); - void render(const Tile& tile); + + // Renders a particular layer from a tile. + void renderTileLayer(const Tile& tile, const LayerDescription &layer_desc); + + // Renders debug information for a tile. + void renderTileDebug(const Tile& tile); + + // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any + // tiles whatsoever. void renderMatte(); + // Renders the red debug frame around a tile, visualizing its perimeter. void renderDebugFrame(); + void renderDebugText(DebugBucket& bucket); void renderDebugText(const std::vector &strings); void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id); @@ -61,19 +73,24 @@ public: // Changes whether debug information is drawn onto the map void setDebug(bool enabled); + // Opaque/Translucent pass setting + void startOpaquePass(); + void startTranslucentPass(); + void endPass(); + + // Configures the painter strata that is used for early z-culling of fragments. + void setStrata(float strata); + void prepareClippingMask(); void drawClippingMask(const mat4& matrix, const ClipID& clip); void finishClippingMask(); - void setCurrentSourceName(const std::string &name); - bool needsAnimation() const; private: void setupShaders(); - void renderLayers(const std::shared_ptr& tile, const std::vector& layers); - void renderLayer(const std::shared_ptr& tile_data, const LayerDescription& layer_desc); void translateLayer(std::array translation, bool reverse = false); + void prepareTile(const Tile& tile); void useProgram(uint32_t program); void lineWidth(float lineWidth); void depthMask(bool value); @@ -96,9 +113,8 @@ private: bool gl_depthMask = true; std::array gl_viewport = {{ 0, 0 }}; float strata = 0; - const float strata_epsilon = 1.0f / (1 << 16); enum { Opaque, Translucent } pass = Opaque; - std::string currentSourceName; + const float strata_epsilon = 1.0f / (1 << 16); std::unique_ptr plainShader; std::unique_ptr outlineShader; diff --git a/src/map/map.cpp b/src/map/map.cpp index dc707ee8f..0155874ac 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -423,14 +423,16 @@ void Map::render() { painter.finishClippingMask(); + // Render per layer in the stylesheet. + renderLayers(style.layers); - // Then, render each source's tiles. TODO: handle more than one - // vector source. - + // Finalize the rendering, e.g. by calling debug render calls per tile. + // This guarantees that we have at least one function per tile called. + // When only rendering layers via the stylesheet, it's possible that we don't + // ever visit a tile during rendering. for (auto &pair : sources) { Source &source = *pair.second; - painter.setCurrentSourceName(pair.first); - source.render(); + source.finishRender(); } painter.renderMatte(); @@ -442,3 +444,55 @@ void Map::render() { update(); } } + +void Map::renderLayers(const std::vector& layers) { + // TODO: Correctly compute the number of layers recursively beforehand. + float strata_thickness = 1.0f / (layers.size() + 1); + + // - FIRST PASS ------------------------------------------------------------ + // Render everything top-to-bottom by using reverse iterators. Render opaque + // objects first. + painter.startOpaquePass(); + typedef std::vector::const_reverse_iterator riterator; + int i = 0; + for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++i) { + painter.setStrata(i * strata_thickness); + renderLayer(*it); + } + painter.endPass(); + + // - SECOND PASS ----------------------------------------------------------- + // Make a second pass, rendering translucent objects. This time, we render + // bottom-to-top. + painter.startTranslucentPass(); + typedef std::vector::const_iterator iterator; + --i; + for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --i) { + painter.setStrata(i * strata_thickness); + renderLayer(*it); + } + painter.endPass(); +} + +void Map::renderLayer(const LayerDescription& layer_desc) { + if (layer_desc.child_layer.size()) { + // This is a layer group. + // TODO: create framebuffer + renderLayers(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 = style.buckets.find(layer_desc.bucket_name); + if (bucket_it != style.buckets.end()) { + const BucketDescription &bucket_desc = bucket_it->second; + + // Find the source and render all tiles in it. + auto source_it = sources.find(bucket_desc.source_name); + if (source_it != sources.end()) { + const std::unique_ptr &source = source_it->second; + source->render(layer_desc, bucket_desc); + } + } + } +} diff --git a/src/map/source.cpp b/src/map/source.cpp index 91e9d7cb5..c6f3f63e1 100644 --- a/src/map/source.cpp +++ b/src/map/source.cpp @@ -57,16 +57,25 @@ size_t Source::prepareRender(const TransformState &transform) { return masks; } -void Source::render() { +void Source::render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc) { if (!enabled) return; for (const Tile& tile : tiles) { if (tile.data && tile.data->state == TileData::State::parsed) { - painter.render(tile); + painter.renderTileLayer(tile, layer_desc); } } } +void Source::finishRender() { + if (!enabled) return; + + for (const Tile& tile : tiles) { + painter.renderTileDebug(tile); + } +} + + std::forward_list Source::getIDs() const { std::forward_list ptrs; std::transform(tiles.begin(), tiles.end(), std::front_inserter(ptrs), [](const Tile &tile) { diff --git a/src/map/vector_tile_data.cpp b/src/map/vector_tile_data.cpp index c5d3a23ec..6b06b74b4 100644 --- a/src/map/vector_tile_data.cpp +++ b/src/map/vector_tile_data.cpp @@ -40,4 +40,4 @@ void VectorTileData::render(Painter &painter, const LayerDescription& layer_desc assert(databucket_it->second); databucket_it->second->render(painter, layer_desc.name, id); } -} \ No newline at end of file +} diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index 5ab9c91d3..6deb884b4 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -167,11 +167,6 @@ void Painter::finishClippingMask() { gl::end_group(); } -void Painter::setCurrentSourceName(const std::string &name) { - currentSourceName = name; -} - - void Painter::clear() { gl::group group("clear"); glStencilMask(0xFF); @@ -185,86 +180,59 @@ void Painter::clear() { #endif } -void Painter::render(const Tile& tile) { +void Painter::startOpaquePass() { + gl::start_group("opaque pass"); + pass = Opaque; + glDisable(GL_BLEND); + depthMask(true); +} + +void Painter::startTranslucentPass() { + gl::start_group("translucent pass"); + pass = Translucent; + glEnable(GL_BLEND); + depthMask(false); +} + +void Painter::endPass() { + gl::end_group(); +} + +void Painter::setStrata(float value) { + strata = value; +} + +void Painter::prepareTile(const Tile& tile) { + matrix = tile.matrix; + + GLint id = tile.clip.mask.to_ulong(); + GLuint mask = clipMask[tile.clip.length]; + glStencilFunc(GL_EQUAL, id, mask); +} + +void Painter::renderTileLayer(const Tile& tile, const LayerDescription &layer_desc) { gl::group group(util::sprintf<32>("render %d/%d/%d", tile.id.z, tile.id.y, tile.id.z)); assert(tile.data); if (tile.data->state == TileData::State::parsed) { - matrix = tile.matrix; - GLint id = tile.clip.mask.to_ulong(); - GLuint mask = clipMask[tile.clip.length]; - - glStencilFunc(GL_EQUAL, id, mask); - - renderLayers(tile.data, map.getStyle().layers); + prepareTile(tile); + tile.data->render(*this, layer_desc); } else { fprintf(stderr, "tile not parsed yet\n"); } frameHistory.record(map.getAnimationTime(), map.getState().getNormalizedZoom()); +} +void Painter::renderTileDebug(const Tile& tile) { + assert(tile.data); if (debug) { + prepareTile(tile); renderDebugText(tile.data->debugBucket); renderDebugFrame(); } } -void Painter::renderLayers(const std::shared_ptr& tile_data, const std::vector& layers) { - float strata_thickness = 1.0f / (layers.size() + 1); - - // - FIRST PASS ------------------------------------------------------------ - // Render everything top-to-bottom by using reverse iterators. Render opaque - // objects first. - gl::start_group("opaque pass"); - pass = Opaque; - glDisable(GL_BLEND); - depthMask(true); - - typedef std::vector::const_reverse_iterator riterator; - int i = 0; - for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++i) { - strata = i * strata_thickness; - renderLayer(tile_data, *it); - } - gl::end_group(); - - // - SECOND PASS ----------------------------------------------------------- - // Make a second pass, rendering translucent objects. This time, we render - // bottom-to-top. - gl::start_group("translucent pass"); - pass = Translucent; - glEnable(GL_BLEND); - depthMask(false); - - typedef std::vector::const_iterator iterator; - --i; - for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --i) { - strata = i * strata_thickness; - renderLayer(tile_data, *it); - } - gl::end_group(); -} - -void Painter::renderLayer(const std::shared_ptr& tile_data, const LayerDescription& layer_desc) { - if (layer_desc.child_layer.size()) { - // This is a layer group. - // TODO: create framebuffer - 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 = map.getStyle().buckets.find(layer_desc.bucket_name); - if (bucket_it != map.getStyle().buckets.end()) { - const BucketDescription &bucket = bucket_it->second; - // Only try to render the bucket if it draws data from the current source. - if (bucket.source_name == currentSourceName) { - tile_data->render(*this, layer_desc); - } - } - } -} - void Painter::translateLayer(std::array translation, bool reverse) { if (translation[0] || translation[1]) { if (reverse) {