diff --git a/scwx-qt/gl/overlay.frag b/scwx-qt/gl/overlay.frag new file mode 100644 index 00000000..b2fd78c6 --- /dev/null +++ b/scwx-qt/gl/overlay.frag @@ -0,0 +1,9 @@ +#version 330 core +uniform vec4 uColor; + +layout (location = 0) out vec4 fragColor; + +void main() +{ + fragColor = uColor; +} diff --git a/scwx-qt/gl/overlay.vert b/scwx-qt/gl/overlay.vert new file mode 100644 index 00000000..042a1d8a --- /dev/null +++ b/scwx-qt/gl/overlay.vert @@ -0,0 +1,9 @@ +#version 330 core +layout (location = 0) in vec2 aVertex; + +uniform mat4 uMVPMatrix; + +void main() +{ + gl_Position = uMVPMatrix * vec4(aVertex, 0.0f, 1.0f); +} diff --git a/scwx-qt/res/fonts/din1451alt.ttf b/scwx-qt/res/fonts/din1451alt.ttf new file mode 100644 index 00000000..4f46c82b Binary files /dev/null and b/scwx-qt/res/fonts/din1451alt.ttf differ diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index c9349a9f..5bb594d3 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -60,10 +60,12 @@ set(SRC_GL source/scwx/qt/gl/shader_program.cpp set(HDR_MANAGER source/scwx/qt/manager/radar_manager.hpp) set(SRC_MANAGER source/scwx/qt/manager/radar_manager.cpp) set(HDR_MAP source/scwx/qt/map/map_widget.hpp + source/scwx/qt/map/overlay_layer.hpp source/scwx/qt/map/radar_layer.hpp source/scwx/qt/map/radar_range_layer.hpp source/scwx/qt/map/triangle_layer.hpp) set(SRC_MAP source/scwx/qt/map/map_widget.cpp + source/scwx/qt/map/overlay_layer.cpp source/scwx/qt/map/radar_layer.cpp source/scwx/qt/map/radar_range_layer.cpp source/scwx/qt/map/triangle_layer.cpp) @@ -76,7 +78,9 @@ set(SRC_VIEW source/scwx/qt/view/radar_view.cpp) set(RESOURCE_FILES scwx-qt.qrc) -set(SHADER_FILES gl/radar.frag +set(SHADER_FILES gl/overlay.frag + gl/overlay.vert + gl/radar.frag gl/radar.vert gl/text.frag gl/text.vert) diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc index 2638956d..a2e2bec9 100644 --- a/scwx-qt/scwx-qt.qrc +++ b/scwx-qt/scwx-qt.qrc @@ -1,8 +1,11 @@ + gl/overlay.frag + gl/overlay.vert gl/radar.frag gl/radar.vert gl/text.frag gl/text.vert + res/fonts/din1451alt.ttf diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 1fa910e0..d15814ab 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -118,6 +119,8 @@ void MapWidget::AddLayers() // QMapboxGL::addCustomLayer will take ownership of the QScopedPointer QScopedPointer pHost( new RadarLayer(radarView, p->gl_)); + QScopedPointer pOverlayHost( + new OverlayLayer(radarView, p->gl_)); QString before = "ferry"; @@ -134,6 +137,7 @@ void MapWidget::AddLayers() p->map_->addCustomLayer("radar", pHost, before); RadarRangeLayer::Add(p->map_, before); + p->map_->addCustomLayer("overlay", pOverlayHost); } void MapWidget::keyPressEvent(QKeyEvent* ev) diff --git a/scwx-qt/source/scwx/qt/map/overlay_layer.cpp b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp new file mode 100644 index 00000000..a20356a8 --- /dev/null +++ b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace map +{ + +static const std::string logPrefix_ = "[scwx::qt::map::overlay_layer] "; + +class OverlayLayerImpl +{ +public: + explicit OverlayLayerImpl(std::shared_ptr radarView, + gl::OpenGLFunctions& gl) : + radarView_(radarView), + gl_(gl), + textShader_(gl), + font_(util::Font::Create(":/res/fonts/din1451alt.ttf")), + shaderProgram_(gl), + uMVPMatrixLocation_(GL_INVALID_INDEX), + uColorLocation_(GL_INVALID_INDEX), + vbo_ {GL_INVALID_INDEX}, + vao_ {GL_INVALID_INDEX}, + texture_ {GL_INVALID_INDEX}, + numVertices_ {0}, + colorTableUpdated_(false), + plotUpdated_(true) + { + // TODO: Manage font at the global level, texture at the view level + } + ~OverlayLayerImpl() = default; + + std::shared_ptr radarView_; + gl::OpenGLFunctions& gl_; + + gl::TextShader textShader_; + std::shared_ptr font_; + gl::ShaderProgram shaderProgram_; + GLint uMVPMatrixLocation_; + GLint uColorLocation_; + GLuint vbo_; + GLuint vao_; + GLuint texture_; + + GLsizeiptr numVertices_; + + bool colorTableUpdated_; + bool plotUpdated_; +}; + +OverlayLayer::OverlayLayer(std::shared_ptr radarView, + gl::OpenGLFunctions& gl) : + p(std::make_unique(radarView, gl)) +{ +} +OverlayLayer::~OverlayLayer() = default; + +void OverlayLayer::initialize() +{ + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "initialize()"; + + gl::OpenGLFunctions& gl = p->gl_; + + p->textShader_.Initialize(); + + // Load and configure overlay shader + p->shaderProgram_.Load(":/gl/overlay.vert", ":/gl/overlay.frag"); + + p->uMVPMatrixLocation_ = + gl.glGetUniformLocation(p->shaderProgram_.id(), "uMVPMatrix"); + if (p->uMVPMatrixLocation_ == -1) + { + BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Could not find uMVPMatrix"; + } + + p->uColorLocation_ = + gl.glGetUniformLocation(p->shaderProgram_.id(), "uColor"); + if (p->uColorLocation_ == -1) + { + BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Could not find uColor"; + } + + if (p->texture_ == GL_INVALID_INDEX) + { + p->texture_ = p->font_->GenerateTexture(gl); + } + + p->shaderProgram_.Use(); + + // Generate a vertex array object + gl.glGenVertexArrays(1, &p->vao_); + + // Generate vertex buffer objects + gl.glGenBuffers(1, &p->vbo_); + + gl.glBindVertexArray(p->vao_); + + // Bottom panel (dynamic sized) + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); + gl.glBufferData( + GL_ARRAY_BUFFER, sizeof(float) * 6 * 2, nullptr, GL_DYNAMIC_DRAW); + + gl.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, static_cast(0)); + gl.glEnableVertexAttribArray(0); + + // Bottom panel color + gl.glUniform4f(p->uColorLocation_, 0.0f, 0.0f, 0.0f, 0.75f); + + connect(p->radarView_.get(), + &view::RadarView::PlotUpdated, + this, + &OverlayLayer::ReceivePlotUpdate); +} + +void OverlayLayer::render(const QMapbox::CustomLayerRenderParameters& params) +{ + gl::OpenGLFunctions& gl = p->gl_; + + static std::string plotTimeString; + + if (p->plotUpdated_) + { + using namespace std::chrono; + auto plotTime = time_point_cast(p->radarView_->PlotTime()); + + if (plotTime.time_since_epoch().count() != 0) + { + zoned_time zt = {current_zone(), plotTime}; + std::ostringstream os; + os << zt; + plotTimeString = os.str(); + } + + p->plotUpdated_ = false; + } + + glm::mat4 projection = glm::ortho(0.0f, + static_cast(params.width), + 0.0f, + static_cast(params.height)); + + p->shaderProgram_.Use(); + + gl.glUniformMatrix4fv( + p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(projection)); + + // Bottom panel vertices + float vertices[6][2] = {{0.0f, 0.0f}, + {0.0f, 24.0f}, + {static_cast(params.width), 0.0f}, // + // + {0.0f, 24.0f}, + {static_cast(params.width), 0.0f}, + {static_cast(params.width), 24.0f}}; + + // Draw vertices + gl.glBindVertexArray(p->vao_); + gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); + gl.glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + gl.glDrawArrays(GL_TRIANGLES, 0, 6); + + // Render time + p->textShader_.RenderText(plotTimeString, + params.width - 7.0f, + 7.0f, + 16.0f, + projection, + boost::gil::rgba8_pixel_t(255, 255, 255, 204), + p->font_, + p->texture_, + gl::TextAlign::Right); + + SCWX_GL_CHECK_ERROR(); +} + +void OverlayLayer::deinitialize() +{ + gl::OpenGLFunctions& gl = p->gl_; + + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "deinitialize()"; + + gl.glDeleteVertexArrays(1, &p->vao_); + gl.glDeleteBuffers(1, &p->vbo_); + gl.glDeleteTextures(1, &p->texture_); + + p->uMVPMatrixLocation_ = GL_INVALID_INDEX; + p->uColorLocation_ = GL_INVALID_INDEX; + p->vao_ = GL_INVALID_INDEX; + p->vbo_ = {GL_INVALID_INDEX}; + p->texture_ = GL_INVALID_INDEX; + + disconnect(p->radarView_.get(), + &view::RadarView::PlotUpdated, + this, + &OverlayLayer::ReceivePlotUpdate); +} + +void OverlayLayer::ReceivePlotUpdate() +{ + p->plotUpdated_ = true; +} + +} // namespace map +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/map/overlay_layer.hpp b/scwx-qt/source/scwx/qt/map/overlay_layer.hpp new file mode 100644 index 00000000..8147fa3f --- /dev/null +++ b/scwx-qt/source/scwx/qt/map/overlay_layer.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace map +{ + +class OverlayLayerImpl; + +class OverlayLayer : public QObject, public QMapbox::CustomLayerHostInterface +{ + Q_OBJECT + +public: + explicit OverlayLayer(std::shared_ptr radarView, + gl::OpenGLFunctions& gl); + ~OverlayLayer(); + + void initialize() override final; + void render(const QMapbox::CustomLayerRenderParameters&) override final; + void deinitialize() override final; + +public slots: + void ReceivePlotUpdate(); + +private: + std::unique_ptr p; +}; + +} // namespace map +} // namespace qt +} // namespace scwx