From 12833202b7a829af3825e04fe823b6e4aa51b913 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Fri, 18 Aug 2023 00:42:55 -0500 Subject: [PATCH] Use geometry shaders for icon and polygon thresholds, fix threshold method to map distance for placefile text --- .../scwx/qt/gl/draw/placefile_icons.cpp | 99 ++++++++++++++----- .../scwx/qt/gl/draw/placefile_icons.hpp | 2 + .../scwx/qt/gl/draw/placefile_polygons.cpp | 99 +++++++++++++------ .../scwx/qt/gl/draw/placefile_polygons.hpp | 2 + scwx-qt/source/scwx/qt/gl/shader_program.cpp | 10 ++ scwx-qt/source/scwx/qt/gl/shader_program.hpp | 2 + .../source/scwx/qt/map/placefile_layer.cpp | 60 +++-------- 7 files changed, 174 insertions(+), 100 deletions(-) diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp index ff63792b..dbbc2308 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -84,6 +85,7 @@ public: uMVPMatrixLocation_(GL_INVALID_INDEX), uMapMatrixLocation_(GL_INVALID_INDEX), uMapScreenCoordLocation_(GL_INVALID_INDEX), + uMapDistanceLocation_(GL_INVALID_INDEX), vao_ {GL_INVALID_INDEX}, vbo_ {GL_INVALID_INDEX}, numVertices_ {0} @@ -95,6 +97,7 @@ public: std::shared_ptr context_; bool dirty_ {false}; + bool thresholded_ {false}; boost::unordered_flat_map iconFiles_ {}; @@ -105,9 +108,10 @@ public: GLint uMVPMatrixLocation_; GLint uMapMatrixLocation_; GLint uMapScreenCoordLocation_; + GLint uMapDistanceLocation_; - GLuint vao_; - GLuint vbo_; + GLuint vao_; + std::array vbo_; GLsizei numVertices_; @@ -123,39 +127,32 @@ PlacefileIcons::~PlacefileIcons() = default; PlacefileIcons::PlacefileIcons(PlacefileIcons&&) noexcept = default; PlacefileIcons& PlacefileIcons::operator=(PlacefileIcons&&) noexcept = default; +void PlacefileIcons::set_thresholded(bool thresholded) +{ + p->thresholded_ = thresholded; +} + void PlacefileIcons::Initialize() { gl::OpenGLFunctions& gl = p->context_->gl(); - p->shaderProgram_ = p->context_->GetShaderProgram(":/gl/geo_texture2d.vert", - ":/gl/texture2d.frag"); - - p->uMVPMatrixLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix"); - if (p->uMVPMatrixLocation_ == -1) - { - logger_->warn("Could not find uMVPMatrix"); - } - - p->uMapMatrixLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapMatrix"); - if (p->uMapMatrixLocation_ == -1) - { - logger_->warn("Could not find uMapMatrix"); - } + p->shaderProgram_ = p->context_->GetShaderProgram( + {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"}, + {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"}, + {GL_FRAGMENT_SHADER, ":/gl/texture2d.frag"}}); + p->uMVPMatrixLocation_ = p->shaderProgram_->GetUniformLocation("uMVPMatrix"); + p->uMapMatrixLocation_ = p->shaderProgram_->GetUniformLocation("uMapMatrix"); p->uMapScreenCoordLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapScreenCoord"); - if (p->uMapScreenCoordLocation_ == -1) - { - logger_->warn("Could not find uMapScreenCoord"); - } + p->shaderProgram_->GetUniformLocation("uMapScreenCoord"); + p->uMapDistanceLocation_ = + p->shaderProgram_->GetUniformLocation("uMapDistance"); gl.glGenVertexArrays(1, &p->vao_); - gl.glGenBuffers(1, &p->vbo_); + gl.glGenBuffers(2, p->vbo_.data()); gl.glBindVertexArray(p->vao_); - gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]); gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW); // aLatLong @@ -203,6 +200,17 @@ void PlacefileIcons::Initialize() reinterpret_cast(10 * sizeof(float))); gl.glEnableVertexAttribArray(4); + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]); + gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW); + + // aThreshold + gl.glVertexAttribIPointer(5, // + 1, + GL_INT, + 0, + static_cast(0)); + gl.glEnableVertexAttribArray(5); + p->dirty_ = true; } @@ -214,7 +222,6 @@ void PlacefileIcons::Render( gl::OpenGLFunctions& gl = p->context_->gl(); gl.glBindVertexArray(p->vao_); - gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); p->Update(); p->shaderProgram_->Use(); @@ -222,6 +229,21 @@ void PlacefileIcons::Render( UseMapProjection( params, p->uMapMatrixLocation_, p->uMapScreenCoordLocation_); + if (p->thresholded_) + { + // If thresholding is enabled, set the map distance + // TODO: nautical miles + auto mapDistance = + util::maplibre::GetMapDistance(params).value() / 1852.0f; + gl.glUniform1f(p->uMapDistanceLocation_, + static_cast(mapDistance)); + } + else + { + // If thresholding is disabled, set the map distance to 0 + gl.glUniform1f(p->uMapDistanceLocation_, 0.0f); + } + // Don't interpolate texture coordinates gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -236,7 +258,7 @@ void PlacefileIcons::Deinitialize() gl::OpenGLFunctions& gl = p->context_->gl(); gl.glDeleteVertexArrays(1, &p->vao_); - gl.glDeleteBuffers(1, &p->vbo_); + gl.glDeleteBuffers(2, p->vbo_.data()); } void PlacefileIcons::SetIconFiles( @@ -279,8 +301,11 @@ void PlacefileIcons::Impl::Update() if (dirty_) { static std::vector buffer {}; + static std::vector thresholds {}; buffer.clear(); buffer.reserve(iconList_.size() * kBufferLength); + thresholds.clear(); + thresholds.reserve(iconList_.size() * kVerticesPerRectangle); numVertices_ = 0; for (auto& di : iconList_) @@ -303,6 +328,10 @@ void PlacefileIcons::Impl::Update() continue; } + // TODO: nautical miles + GLint threshold = + static_cast(std::roundf(di->threshold_.value() / 1852.0f)); + // Latitude and longitude coordinates in degrees const float lat = static_cast(di->latitude_); const float lon = static_cast(di->longitude_); @@ -357,17 +386,33 @@ void PlacefileIcons::Impl::Update() lat, lon, rx, ty, rs, tt, mc0, mc1, mc2, mc3, a, // TR lat, lon, lx, ty, ls, tt, mc0, mc1, mc2, mc3, a // TL }); + thresholds.insert(thresholds.end(), + {threshold, // + threshold, + threshold, + threshold, + threshold, + threshold}); numVertices_ += 6; } gl::OpenGLFunctions& gl = context_->gl(); + // Buffer vertex data + gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]); gl.glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buffer.size(), buffer.data(), GL_DYNAMIC_DRAW); + // Buffer threshold data + gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]); + gl.glBufferData(GL_ARRAY_BUFFER, + sizeof(GLint) * thresholds.size(), + thresholds.data(), + GL_DYNAMIC_DRAW); + dirty_ = false; } } diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.hpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.hpp index 9f771d8e..9dd1b3e7 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.hpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.hpp @@ -27,6 +27,8 @@ public: PlacefileIcons(PlacefileIcons&&) noexcept; PlacefileIcons& operator=(PlacefileIcons&&) noexcept; + void set_thresholded(bool thresholded); + void Initialize() override; void Render(const QMapLibreGL::CustomLayerRenderParameters& params) override; void Deinitialize() override; diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp index 216b4a9b..202a5c0e 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp @@ -6,6 +6,7 @@ #include #include +#include #if defined(_WIN32) typedef void (*_GLUfuncptr)(void); @@ -48,6 +49,7 @@ public: uMVPMatrixLocation_(GL_INVALID_INDEX), uMapMatrixLocation_(GL_INVALID_INDEX), uMapScreenCoordLocation_(GL_INVALID_INDEX), + uMapDistanceLocation_(GL_INVALID_INDEX), vao_ {GL_INVALID_INDEX}, vbo_ {GL_INVALID_INDEX}, numVertices_ {0} @@ -88,6 +90,7 @@ public: std::shared_ptr context_; bool dirty_ {false}; + bool thresholded_ {false}; std::vector> polygonList_ {}; @@ -96,7 +99,9 @@ public: std::mutex bufferMutex_ {}; std::vector currentBuffer_ {}; + std::vector currentThresholdBuffer_ {}; std::vector newBuffer_ {}; + std::vector newThresholdBuffer_ {}; GLUtesselator* tessellator_; @@ -104,13 +109,14 @@ public: GLint uMVPMatrixLocation_; GLint uMapMatrixLocation_; GLint uMapScreenCoordLocation_; + GLint uMapDistanceLocation_; - GLuint vao_; - GLuint vbo_; + GLuint vao_; + std::array vbo_; GLsizei numVertices_; - boost::gil::rgba8_pixel_t currentColor_ {255, 255, 255, 255}; + GLint currentThreshold_; }; PlacefilePolygons::PlacefilePolygons(std::shared_ptr context) : @@ -123,39 +129,32 @@ PlacefilePolygons::PlacefilePolygons(PlacefilePolygons&&) noexcept = default; PlacefilePolygons& PlacefilePolygons::operator=(PlacefilePolygons&&) noexcept = default; +void PlacefilePolygons::set_thresholded(bool thresholded) +{ + p->thresholded_ = thresholded; +} + void PlacefilePolygons::Initialize() { gl::OpenGLFunctions& gl = p->context_->gl(); - p->shaderProgram_ = - p->context_->GetShaderProgram(":/gl/map_color.vert", ":/gl/color.frag"); - - p->uMVPMatrixLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix"); - if (p->uMVPMatrixLocation_ == -1) - { - logger_->warn("Could not find uMVPMatrix"); - } - - p->uMapMatrixLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapMatrix"); - if (p->uMapMatrixLocation_ == -1) - { - logger_->warn("Could not find uMapMatrix"); - } + p->shaderProgram_ = p->context_->GetShaderProgram( + {{GL_VERTEX_SHADER, ":/gl/map_color.vert"}, + {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"}, + {GL_FRAGMENT_SHADER, ":/gl/color.frag"}}); + p->uMVPMatrixLocation_ = p->shaderProgram_->GetUniformLocation("uMVPMatrix"); + p->uMapMatrixLocation_ = p->shaderProgram_->GetUniformLocation("uMapMatrix"); p->uMapScreenCoordLocation_ = - gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapScreenCoord"); - if (p->uMapScreenCoordLocation_ == -1) - { - logger_->warn("Could not find uMapScreenCoord"); - } + p->shaderProgram_->GetUniformLocation("uMapScreenCoord"); + p->uMapDistanceLocation_ = + p->shaderProgram_->GetUniformLocation("uMapDistance"); gl.glGenVertexArrays(1, &p->vao_); - gl.glGenBuffers(1, &p->vbo_); + gl.glGenBuffers(2, p->vbo_.data()); gl.glBindVertexArray(p->vao_); - gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]); gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW); // aScreenCoord @@ -185,18 +184,28 @@ void PlacefilePolygons::Initialize() reinterpret_cast(4 * sizeof(float))); gl.glEnableVertexAttribArray(2); + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]); + gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW); + + // aThreshold + gl.glVertexAttribIPointer(3, // + 1, + GL_INT, + 0, + static_cast(0)); + gl.glEnableVertexAttribArray(3); + p->dirty_ = true; } void PlacefilePolygons::Render( const QMapLibreGL::CustomLayerRenderParameters& params) { - if (!p->polygonList_.empty()) + if (!p->currentBuffer_.empty()) { gl::OpenGLFunctions& gl = p->context_->gl(); gl.glBindVertexArray(p->vao_); - gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); p->Update(); p->shaderProgram_->Use(); @@ -204,6 +213,21 @@ void PlacefilePolygons::Render( UseMapProjection( params, p->uMapMatrixLocation_, p->uMapScreenCoordLocation_); + if (p->thresholded_) + { + // If thresholding is enabled, set the map distance + // TODO: nautical miles + auto mapDistance = + util::maplibre::GetMapDistance(params).value() / 1852.0f; + gl.glUniform1f(p->uMapDistanceLocation_, + static_cast(mapDistance)); + } + else + { + // If thresholding is disabled, set the map distance to 0 + gl.glUniform1f(p->uMapDistanceLocation_, 0.0f); + } + // Draw icons gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_); } @@ -214,13 +238,14 @@ void PlacefilePolygons::Deinitialize() gl::OpenGLFunctions& gl = p->context_->gl(); gl.glDeleteVertexArrays(1, &p->vao_); - gl.glDeleteBuffers(1, &p->vbo_); + gl.glDeleteBuffers(2, p->vbo_.data()); } void PlacefilePolygons::StartPolygons() { // Clear the new buffer p->newBuffer_.clear(); + p->newThresholdBuffer_.clear(); // Clear the polygon list p->polygonList_.clear(); @@ -242,6 +267,7 @@ void PlacefilePolygons::FinishPolygons() // Swap buffers p->currentBuffer_.swap(p->newBuffer_); + p->currentThresholdBuffer_.swap(p->newThresholdBuffer_); // Mark the draw item dirty p->dirty_ = true; @@ -255,11 +281,20 @@ void PlacefilePolygons::Impl::Update() std::unique_lock lock {bufferMutex_}; + // Buffer vertex data + gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]); gl.glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * currentBuffer_.size(), currentBuffer_.data(), GL_DYNAMIC_DRAW); + // Buffer threshold data + gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]); + gl.glBufferData(GL_ARRAY_BUFFER, + sizeof(GLint) * currentThresholdBuffer_.size(), + currentThresholdBuffer_.data(), + GL_DYNAMIC_DRAW); + numVertices_ = static_cast(currentBuffer_.size() / kPointsPerVertex); @@ -276,6 +311,10 @@ void PlacefilePolygons::Impl::Tessellate( // Default color to "Color" statement boost::gil::rgba8_pixel_t lastColor = di->color_; + // TODO: nautical miles + currentThreshold_ = + static_cast(std::roundf(di->threshold_.value() / 1852.0f)); + gluTessBeginPolygon(tessellator_, this); for (auto& contour : di->contours_) @@ -322,6 +361,7 @@ void PlacefilePolygons::Impl::Tessellate( while (newBuffer_.size() % kVerticesPerTriangle != 0) { newBuffer_.pop_back(); + newThresholdBuffer_.pop_back(); } } @@ -382,6 +422,7 @@ void PlacefilePolygons::Impl::TessellateVertexCallback(void* vertexData, static_cast(data[kTessVertexG_]), static_cast(data[kTessVertexB_]), static_cast(data[kTessVertexA_])}); + self->newThresholdBuffer_.push_back(self->currentThreshold_); } void PlacefilePolygons::Impl::TessellateErrorCallback(GLenum errorCode) diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.hpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.hpp index f4a44ecd..451e007f 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.hpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.hpp @@ -27,6 +27,8 @@ public: PlacefilePolygons(PlacefilePolygons&&) noexcept; PlacefilePolygons& operator=(PlacefilePolygons&&) noexcept; + void set_thresholded(bool thresholded); + void Initialize() override; void Render(const QMapLibreGL::CustomLayerRenderParameters& params) override; void Deinitialize() override; diff --git a/scwx-qt/source/scwx/qt/gl/shader_program.cpp b/scwx-qt/source/scwx/qt/gl/shader_program.cpp index 54db55fc..4da07a32 100644 --- a/scwx-qt/source/scwx/qt/gl/shader_program.cpp +++ b/scwx-qt/source/scwx/qt/gl/shader_program.cpp @@ -56,6 +56,16 @@ GLuint ShaderProgram::id() const return p->id_; } +GLint ShaderProgram::GetUniformLocation(const std::string& name) +{ + GLint location = p->gl_.glGetUniformLocation(p->id_, name.c_str()); + if (location == -1) + { + logger_->warn("Could not find {}", name); + } + return location; +} + std::string ShaderProgram::Impl::ShaderName(GLenum type) { auto it = kShaderNames_.find(type); diff --git a/scwx-qt/source/scwx/qt/gl/shader_program.hpp b/scwx-qt/source/scwx/qt/gl/shader_program.hpp index dcc36b3e..a2b887d8 100644 --- a/scwx-qt/source/scwx/qt/gl/shader_program.hpp +++ b/scwx-qt/source/scwx/qt/gl/shader_program.hpp @@ -30,6 +30,8 @@ public: GLuint id() const; + GLint GetUniformLocation(const std::string& name); + bool Load(const std::string& vertexPath, const std::string& fragmentPath); bool Load(std::initializer_list> shaderPaths); diff --git a/scwx-qt/source/scwx/qt/map/placefile_layer.cpp b/scwx-qt/source/scwx/qt/map/placefile_layer.cpp index a7e258e6..70db64a5 100644 --- a/scwx-qt/source/scwx/qt/map/placefile_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/placefile_layer.cpp @@ -4,11 +4,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -41,12 +41,8 @@ public: void ConnectSignals(); - void - RenderIconDrawItem(const QMapLibreGL::CustomLayerRenderParameters& params, - const std::shared_ptr& di); - void RenderPolygonDrawItem( - const QMapLibreGL::CustomLayerRenderParameters& params, - const std::shared_ptr& di); + void AddIcon(const std::shared_ptr& di); + void AddPolygon(const std::shared_ptr& di); void RenderTextDrawItem(const QMapLibreGL::CustomLayerRenderParameters& params, const std::shared_ptr& di); @@ -73,6 +69,8 @@ public: bool thresholded_ {true}; ImFont* monospaceFont_ {}; + boost::units::quantity mapDistance_ {}; + std::shared_ptr placefileIcons_; std::shared_ptr placefilePolygons_; }; @@ -122,8 +120,7 @@ void PlacefileLayer::Initialize() DrawLayer::Initialize(); } -void PlacefileLayer::Impl::RenderIconDrawItem( - const QMapLibreGL::CustomLayerRenderParameters& params, +void PlacefileLayer::Impl::AddIcon( const std::shared_ptr& di) { if (!dirty_) @@ -131,51 +128,25 @@ void PlacefileLayer::Impl::RenderIconDrawItem( return; } - auto distance = - (thresholded_) ? - util::GeographicLib::GetDistance( - params.latitude, params.longitude, di->latitude_, di->longitude_) : - 0; - - if (distance < di->threshold_) - { - placefileIcons_->AddIcon(di); - } + placefileIcons_->AddIcon(di); } -void PlacefileLayer::Impl::RenderPolygonDrawItem( - const QMapLibreGL::CustomLayerRenderParameters& params, +void PlacefileLayer::Impl::AddPolygon( const std::shared_ptr& di) { if (!dirty_) { return; - } + }; - auto distance = (thresholded_) ? - util::GeographicLib::GetDistance(params.latitude, - params.longitude, - di->center_.latitude_, - di->center_.longitude_) : - 0; - - if (distance < di->threshold_) - { - placefilePolygons_->AddPolygon(di); - } + placefilePolygons_->AddPolygon(di); } void PlacefileLayer::Impl::RenderTextDrawItem( const QMapLibreGL::CustomLayerRenderParameters& params, const std::shared_ptr& di) { - auto distance = - (thresholded_) ? - util::GeographicLib::GetDistance( - params.latitude, params.longitude, di->latitude_, di->longitude_) : - 0; - - if (distance < di->threshold_) + if (!thresholded_ || mapDistance_ <= di->threshold_) { const auto screenCoordinates = (util::maplibre::LatLongToScreenCoordinate( {di->latitude_, di->longitude_}) - @@ -265,6 +236,7 @@ void PlacefileLayer::Render( p->mapBearingSin_ = sinf(params.bearing * common::kDegreesToRadians); p->halfWidth_ = params.width * 0.5f; p->halfHeight_ = params.height * 0.5f; + p->mapDistance_ = util::maplibre::GetMapDistance(params); // Get monospace font pointer std::size_t fontSize = 16; @@ -292,6 +264,8 @@ void PlacefileLayer::Render( { p->thresholded_ = placefileManager->placefile_thresholded(placefile->name()); + p->placefileIcons_->set_thresholded(p->thresholded_); + p->placefilePolygons_->set_thresholded(p->thresholded_); if (p->dirty_) { @@ -315,14 +289,12 @@ void PlacefileLayer::Render( break; case gr::Placefile::ItemType::Icon: - p->RenderIconDrawItem( - params, + p->AddIcon( std::static_pointer_cast(drawItem)); break; case gr::Placefile::ItemType::Polygon: - p->RenderPolygonDrawItem( - params, + p->AddPolygon( std::static_pointer_cast( drawItem)); break;