Merge branch 'feature/geo-line' into develop

This commit is contained in:
Dan Paulat 2022-10-06 07:50:46 -05:00
commit fa78861a76
38 changed files with 1471 additions and 227 deletions

3
.gitmodules vendored
View file

@ -13,3 +13,6 @@
[submodule "external/freetype-gl"]
path = external/freetype-gl
url = https://github.com/rougier/freetype-gl.git
[submodule "external/stb"]
path = external/stb
url = https://github.com/nothings/stb.git

View file

@ -6,8 +6,10 @@ set_property(DIRECTORY
PROPERTY CMAKE_CONFIGURE_DEPENDS
freetype-gl.cmake
hsluv-c.cmake
mapbox-gl-native.cmake)
mapbox-gl-native.cmake
stb.cmake)
include(freetype-gl.cmake)
include(hsluv-c.cmake)
include(mapbox-gl-native.cmake)
include(stb.cmake)

1
external/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55

4
external/stb.cmake vendored Normal file
View file

@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.19)
set(PROJECT_NAME scwx-stb)
set(STB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stb PARENT_SCOPE)

41
scwx-qt/gl/geo_line.vert Normal file
View file

@ -0,0 +1,41 @@
#version 330 core
#define DEGREES_MAX 360.0f
#define LATITUDE_MAX 85.051128779806604f
#define LONGITUDE_MAX 180.0f
#define PI 3.1415926535897932384626433f
#define RAD2DEG 57.295779513082320876798156332941f
layout (location = 0) in vec2 aLatLong;
layout (location = 1) in vec2 aXYOffset;
layout (location = 2) in vec2 aTexCoord;
layout (location = 3) in vec4 aModulate;
uniform mat4 uMVPMatrix;
uniform mat4 uMapMatrix;
uniform vec2 uMapScreenCoord;
smooth out vec2 texCoord;
flat out vec4 modulate;
vec2 latLngToScreenCoordinate(in vec2 latLng)
{
vec2 p;
latLng.x = clamp(latLng.x, -LATITUDE_MAX, LATITUDE_MAX);
p.xy = vec2(LONGITUDE_MAX + latLng.y,
-(LONGITUDE_MAX - RAD2DEG * log(tan(PI / 4 + latLng.x * PI / DEGREES_MAX))));
return p;
}
void main()
{
// Pass the texture coordinate and color modulate to the fragment shader
texCoord = aTexCoord;
modulate = aModulate;
vec2 p = latLngToScreenCoordinate(aLatLong) - uMapScreenCoord;
// Transform the position to screen coordinates
gl_Position = uMapMatrix * vec4(p, 0.0f, 1.0f) -
uMVPMatrix * vec4(aXYOffset, 0.0f, 0.0f);
}

16
scwx-qt/gl/texture2d.frag Normal file
View file

@ -0,0 +1,16 @@
#version 330 core
// Lower the default precision to medium
precision mediump float;
uniform sampler2D uTexture;
smooth in vec2 texCoord;
flat in vec4 modulate;
layout (location = 0) out vec4 fragColor;
void main()
{
fragColor = texture(uTexture, texCoord) * modulate;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

View file

@ -40,14 +40,19 @@ set(SRC_MAIN source/scwx/qt/main/main_window.cpp)
set(UI_MAIN source/scwx/qt/main/main_window.ui)
set(HDR_CONFIG source/scwx/qt/config/radar_site.hpp)
set(SRC_CONFIG source/scwx/qt/config/radar_site.cpp)
set(SRC_EXTERNAL source/scwx/qt/external/stb_rect_pack.cpp)
set(HDR_GL source/scwx/qt/gl/gl.hpp
source/scwx/qt/gl/gl_context.hpp
source/scwx/qt/gl/shader_program.hpp
source/scwx/qt/gl/text_shader.hpp)
set(SRC_GL source/scwx/qt/gl/shader_program.cpp
set(SRC_GL source/scwx/qt/gl/gl_context.cpp
source/scwx/qt/gl/shader_program.cpp
source/scwx/qt/gl/text_shader.cpp)
set(HDR_GL_DRAW source/scwx/qt/gl/draw/draw_item.hpp
source/scwx/qt/gl/draw/geo_line.hpp
source/scwx/qt/gl/draw/rectangle.hpp)
set(SRC_GL_DRAW source/scwx/qt/gl/draw/draw_item.cpp
source/scwx/qt/gl/draw/geo_line.cpp
source/scwx/qt/gl/draw/rectangle.cpp)
set(HDR_MANAGER source/scwx/qt/manager/radar_product_manager.hpp
source/scwx/qt/manager/radar_product_manager_notifier.hpp
@ -71,6 +76,7 @@ set(SRC_MAP source/scwx/qt/map/color_table_layer.cpp
source/scwx/qt/map/draw_layer.cpp
source/scwx/qt/map/generic_layer.cpp
source/scwx/qt/map/layer_wrapper.cpp
source/scwx/qt/map/map_context.cpp
source/scwx/qt/map/map_widget.cpp
source/scwx/qt/map/overlay_layer.cpp
source/scwx/qt/map/radar_product_layer.cpp
@ -101,10 +107,13 @@ set(SRC_UI source/scwx/qt/ui/flow_layout.cpp
source/scwx/qt/ui/level3_products_widget.cpp)
set(HDR_UTIL source/scwx/qt/util/font.hpp
source/scwx/qt/util/font_buffer.hpp
source/scwx/qt/util/json.hpp)
source/scwx/qt/util/json.hpp
source/scwx/qt/util/streams.hpp
source/scwx/qt/util/texture_atlas.hpp)
set(SRC_UTIL source/scwx/qt/util/font.cpp
source/scwx/qt/util/font_buffer.cpp
source/scwx/qt/util/json.cpp)
source/scwx/qt/util/json.cpp
source/scwx/qt/util/texture_atlas.cpp)
set(HDR_VIEW source/scwx/qt/view/level2_product_view.hpp
source/scwx/qt/view/level3_product_view.hpp
source/scwx/qt/view/level3_radial_view.hpp
@ -122,12 +131,14 @@ set(RESOURCE_FILES scwx-qt.qrc)
set(SHADER_FILES gl/color.frag
gl/color.vert
gl/geo_line.vert
gl/radar.frag
gl/radar.vert
gl/text.frag
gl/text.vert
gl/texture1d.frag
gl/texture1d.vert)
gl/texture1d.vert
gl/texture2d.frag)
set(CMAKE_FILES scwx-qt.cmake)
@ -139,6 +150,7 @@ set(PROJECT_SOURCES ${HDR_MAIN}
${SRC_MAIN}
${HDR_CONFIG}
${SRC_CONFIG}
${SRC_EXTERNAL}
${HDR_GL}
${SRC_GL}
${HDR_GL_DRAW}
@ -173,6 +185,7 @@ source_group("Header Files\\main" FILES ${HDR_MAIN})
source_group("Source Files\\main" FILES ${SRC_MAIN})
source_group("Header Files\\config" FILES ${HDR_CONFIG})
source_group("Source Files\\config" FILES ${SRC_CONFIG})
source_group("Source Files\\external" FILES ${SRC_EXTERNAL})
source_group("Header Files\\gl" FILES ${HDR_GL})
source_group("Source Files\\gl" FILES ${SRC_GL})
source_group("Header Files\\gl\\draw" FILES ${HDR_GL_DRAW})
@ -223,7 +236,8 @@ endif()
target_include_directories(scwx-qt PUBLIC ${scwx-qt_SOURCE_DIR}/source
${FTGL_INCLUDE_DIR}
${MBGL_INCLUDE_DIR})
${MBGL_INCLUDE_DIR}
${STB_INCLUDE_DIR})
target_include_directories(supercell-wx PUBLIC ${scwx-qt_SOURCE_DIR}/source)

View file

@ -2,16 +2,20 @@
<qresource prefix="/">
<file>gl/color.frag</file>
<file>gl/color.vert</file>
<file>gl/geo_line.vert</file>
<file>gl/radar.frag</file>
<file>gl/radar.vert</file>
<file>gl/text.frag</file>
<file>gl/text.vert</file>
<file>gl/texture1d.frag</file>
<file>gl/texture1d.vert</file>
<file>gl/texture2d.frag</file>
<file>res/config/radar_sites.json</file>
<file>res/fonts/din1451alt.ttf</file>
<file>res/fonts/din1451alt_g.ttf</file>
<file>res/icons/font-awesome-6/square-minus-regular.svg</file>
<file>res/icons/font-awesome-6/square-plus-regular.svg</file>
<file>res/textures/lines/default-1x7.png</file>
<file>res/textures/lines/test-pattern.png</file>
</qresource>
</RCC>

View file

@ -0,0 +1,2 @@
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb_rect_pack.h>

View file

@ -2,6 +2,13 @@
#include <string>
#pragma warning(push, 0)
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <mbgl/util/constants.hpp>
#pragma warning(pop)
namespace scwx
{
namespace qt
@ -13,20 +20,76 @@ namespace draw
static const std::string logPrefix_ = "scwx::qt::gl::draw::draw_item";
class DrawItemImpl
class DrawItem::Impl
{
public:
explicit DrawItemImpl() {}
explicit Impl(OpenGLFunctions& gl) : gl_ {gl} {}
~Impl() {}
~DrawItemImpl() {}
OpenGLFunctions& gl_;
};
DrawItem::DrawItem() : p(std::make_unique<DrawItemImpl>()) {}
DrawItem::DrawItem(OpenGLFunctions& gl) : p(std::make_unique<Impl>(gl)) {}
DrawItem::~DrawItem() = default;
DrawItem::DrawItem(DrawItem&&) noexcept = default;
DrawItem::DrawItem(DrawItem&&) noexcept = default;
DrawItem& DrawItem::operator=(DrawItem&&) noexcept = default;
void DrawItem::UseDefaultProjection(
const QMapbox::CustomLayerRenderParameters& params, GLint uMVPMatrixLocation)
{
glm::mat4 projection = glm::ortho(0.0f,
static_cast<float>(params.width),
0.0f,
static_cast<float>(params.height));
p->gl_.glUniformMatrix4fv(
uMVPMatrixLocation, 1, GL_FALSE, glm::value_ptr(projection));
}
// TODO: Refactor to utility class
static glm::vec2
LatLongToScreenCoordinate(const QMapbox::Coordinate& coordinate)
{
double latitude = std::clamp(
coordinate.first, -mbgl::util::LATITUDE_MAX, mbgl::util::LATITUDE_MAX);
glm::vec2 screen {
mbgl::util::LONGITUDE_MAX + coordinate.second,
-(mbgl::util::LONGITUDE_MAX -
mbgl::util::RAD2DEG *
std::log(std::tan(M_PI / 4.0 +
latitude * M_PI / mbgl::util::DEGREES_MAX)))};
return screen;
}
void DrawItem::UseMapProjection(
const QMapbox::CustomLayerRenderParameters& params,
GLint uMVPMatrixLocation,
GLint uMapScreenCoordLocation)
{
OpenGLFunctions& gl = p->gl_;
// TODO: Refactor to utility class
const float scale = std::pow(2.0, params.zoom) * 2.0f *
mbgl::util::tileSize / mbgl::util::DEGREES_MAX;
const float xScale = scale / params.width;
const float yScale = scale / params.height;
glm::mat4 uMVPMatrix(1.0f);
uMVPMatrix = glm::scale(uMVPMatrix, glm::vec3(xScale, yScale, 1.0f));
uMVPMatrix = glm::rotate(uMVPMatrix,
glm::radians<float>(params.bearing),
glm::vec3(0.0f, 0.0f, 1.0f));
gl.glUniform2fv(uMapScreenCoordLocation,
1,
glm::value_ptr(LatLongToScreenCoordinate(
{params.latitude, params.longitude})));
gl.glUniformMatrix4fv(
uMVPMatrixLocation, 1, GL_FALSE, glm::value_ptr(uMVPMatrix));
}
} // namespace draw
} // namespace gl
} // namespace qt

View file

@ -1,7 +1,11 @@
#pragma once
#include <scwx/qt/gl/gl.hpp>
#include <memory>
#include <QMapbox>
namespace scwx
{
namespace qt
@ -11,26 +15,33 @@ namespace gl
namespace draw
{
class DrawItemImpl;
class DrawItem
{
public:
explicit DrawItem();
explicit DrawItem(OpenGLFunctions& gl);
~DrawItem();
DrawItem(const DrawItem&) = delete;
DrawItem(const DrawItem&) = delete;
DrawItem& operator=(const DrawItem&) = delete;
DrawItem(DrawItem&&) noexcept;
DrawItem& operator=(DrawItem&&) noexcept;
virtual void Initialize() = 0;
virtual void Render() = 0;
virtual void Deinitialize() = 0;
virtual void Initialize() = 0;
virtual void Render(const QMapbox::CustomLayerRenderParameters& params) = 0;
virtual void Deinitialize() = 0;
protected:
void UseDefaultProjection(const QMapbox::CustomLayerRenderParameters& params,
GLint uMVPMatrixLocation);
void UseMapProjection(const QMapbox::CustomLayerRenderParameters& params,
GLint uMVPMatrixLocation,
GLint uMapScreenCoordLocation);
private:
std::unique_ptr<DrawItemImpl> p;
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace draw

View file

@ -0,0 +1,292 @@
#include <scwx/qt/gl/draw/geo_line.hpp>
#include <scwx/qt/util/texture_atlas.hpp>
#include <scwx/common/geographic.hpp>
#include <scwx/util/logger.hpp>
#include <optional>
namespace scwx
{
namespace qt
{
namespace gl
{
namespace draw
{
static const std::string logPrefix_ = "scwx::qt::gl::draw::geo_line";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static constexpr size_t kNumRectangles = 1;
static constexpr size_t kNumTriangles = kNumRectangles * 2;
static constexpr size_t kVerticesPerTriangle = 3;
static constexpr size_t kVerticesPerRectangle = kVerticesPerTriangle * 2;
static constexpr size_t kPointsPerVertex = 10;
static constexpr size_t kBufferLength =
kNumTriangles * kVerticesPerTriangle * kPointsPerVertex;
class GeoLine::Impl
{
public:
explicit Impl(std::shared_ptr<GlContext> context) :
context_ {context},
dirty_ {false},
visible_ {true},
points_ {},
width_ {7.0f},
modulateColor_ {std::nullopt},
shaderProgram_ {nullptr},
uMVPMatrixLocation_(GL_INVALID_INDEX),
uMapMatrixLocation_(GL_INVALID_INDEX),
uMapScreenCoordLocation_(GL_INVALID_INDEX),
texture_ {},
vao_ {GL_INVALID_INDEX},
vbo_ {GL_INVALID_INDEX}
{
}
~Impl() {}
std::shared_ptr<GlContext> context_;
bool dirty_;
bool visible_;
std::array<common::Coordinate, 2> points_;
float width_;
std::optional<boost::gil::rgba8_pixel_t> modulateColor_;
std::shared_ptr<ShaderProgram> shaderProgram_;
GLint uMVPMatrixLocation_;
GLint uMapMatrixLocation_;
GLint uMapScreenCoordLocation_;
util::TextureAttributes texture_;
GLuint vao_;
GLuint vbo_;
void Update();
};
GeoLine::GeoLine(std::shared_ptr<GlContext> context) :
DrawItem(context->gl()), p(std::make_unique<Impl>(context))
{
}
GeoLine::~GeoLine() = default;
GeoLine::GeoLine(GeoLine&&) noexcept = default;
GeoLine& GeoLine::operator=(GeoLine&&) noexcept = default;
void GeoLine::Initialize()
{
gl::OpenGLFunctions& gl = p->context_->gl();
p->shaderProgram_ = p->context_->GetShaderProgram(":/gl/geo_line.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->uMapScreenCoordLocation_ =
gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapScreenCoord");
if (p->uMapScreenCoordLocation_ == -1)
{
logger_->warn("Could not find uMapScreenCoord");
}
p->texture_ =
util::TextureAtlas::Instance().GetTextureAttributes("lines/default-1x7");
gl.glGenVertexArrays(1, &p->vao_);
gl.glGenBuffers(1, &p->vbo_);
gl.glBindVertexArray(p->vao_);
gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
gl.glBufferData(
GL_ARRAY_BUFFER, sizeof(float) * kBufferLength, nullptr, GL_DYNAMIC_DRAW);
// aLatLong
gl.glVertexAttribPointer(0,
2,
GL_FLOAT,
GL_FALSE,
kPointsPerVertex * sizeof(float),
static_cast<void*>(0));
gl.glEnableVertexAttribArray(0);
// aXYOffset
gl.glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
kPointsPerVertex * sizeof(float),
reinterpret_cast<void*>(2 * sizeof(float)));
gl.glEnableVertexAttribArray(1);
// aTexCoord
gl.glVertexAttribPointer(2,
2,
GL_FLOAT,
GL_FALSE,
kPointsPerVertex * sizeof(float),
reinterpret_cast<void*>(4 * sizeof(float)));
gl.glEnableVertexAttribArray(2);
// aModulate
gl.glVertexAttribPointer(3,
4,
GL_FLOAT,
GL_FALSE,
kPointsPerVertex * sizeof(float),
reinterpret_cast<void*>(6 * sizeof(float)));
gl.glEnableVertexAttribArray(3);
p->dirty_ = true;
}
void GeoLine::Render(const QMapbox::CustomLayerRenderParameters& params)
{
if (p->visible_)
{
gl::OpenGLFunctions& gl = p->context_->gl();
gl.glBindVertexArray(p->vao_);
gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
p->Update();
p->shaderProgram_->Use();
UseDefaultProjection(params, p->uMVPMatrixLocation_);
UseMapProjection(
params, p->uMapMatrixLocation_, p->uMapScreenCoordLocation_);
// Draw line
gl.glDrawArrays(GL_TRIANGLES, 0, 6);
}
}
void GeoLine::Deinitialize()
{
gl::OpenGLFunctions& gl = p->context_->gl();
gl.glDeleteVertexArrays(1, &p->vao_);
gl.glDeleteBuffers(1, &p->vbo_);
}
void GeoLine::SetPoints(float latitude1,
float longitude1,
float latitude2,
float longitude2)
{
if (p->points_[0].latitude_ != latitude1 ||
p->points_[0].longitude_ != longitude1 ||
p->points_[1].latitude_ != latitude2 ||
p->points_[1].longitude_ != longitude2)
{
p->points_[0] = {latitude1, longitude1};
p->points_[1] = {latitude2, longitude2};
p->dirty_ = true;
}
}
void GeoLine::SetModulateColor(boost::gil::rgba8_pixel_t color)
{
if (p->modulateColor_ != color)
{
p->modulateColor_ = color;
p->dirty_ = true;
}
}
void GeoLine::SetWidth(float width)
{
if (p->width_ != width)
{
p->width_ = width;
p->dirty_ = true;
}
}
void GeoLine::SetVisible(bool visible)
{
p->visible_ = visible;
}
void GeoLine::Impl::Update()
{
if (dirty_)
{
gl::OpenGLFunctions& gl = context_->gl();
// Latitude and longitude coordinates in degrees
const float lx = points_[0].latitude_;
const float rx = points_[1].latitude_;
const float by = points_[0].longitude_;
const float ty = points_[1].longitude_;
// Offset x/y in pixels
const double i = points_[1].longitude_ - points_[0].longitude_;
const double j = points_[1].latitude_ - points_[0].latitude_;
const double angle = std::atan2(i, j) * 180.0 / M_PI;
const float ox = width_ * 0.5f * std::cosf(angle);
const float oy = width_ * 0.5f * std::sinf(angle);
// Texture coordinates
const float ls = texture_.sLeft_;
const float rs = texture_.sRight_;
const float tt = texture_.tTop_;
const float bt = texture_.tBottom_;
float mc0 = 1.0f;
float mc1 = 1.0f;
float mc2 = 1.0f;
float mc3 = 1.0f;
if (modulateColor_.has_value())
{
boost::gil::rgba8_pixel_t& mc = modulateColor_.value();
mc0 = mc[0] / 255.0f;
mc1 = mc[1] / 255.0f;
mc2 = mc[2] / 255.0f;
mc3 = mc[3] / 255.0f;
}
const float buffer[kNumRectangles][kVerticesPerRectangle]
[kPointsPerVertex] = //
{ //
// Line
{
{lx, by, -ox, -oy, ls, bt, mc0, mc1, mc2, mc3}, // BL
{lx, by, +ox, +oy, ls, tt, mc0, mc1, mc2, mc3}, // TL
{rx, ty, -ox, -oy, rs, bt, mc0, mc1, mc2, mc3}, // BR
{rx, ty, -ox, -oy, rs, bt, mc0, mc1, mc2, mc3}, // BR
{rx, ty, +ox, +oy, rs, tt, mc0, mc1, mc2, mc3}, // TR
{lx, by, +ox, +oy, ls, tt, mc0, mc1, mc2, mc3} // TL
}};
gl.glBufferData(GL_ARRAY_BUFFER,
sizeof(float) * kBufferLength,
buffer,
GL_DYNAMIC_DRAW);
dirty_ = false;
}
}
} // namespace draw
} // namespace gl
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,77 @@
#pragma once
#include <scwx/qt/gl/gl_context.hpp>
#include <scwx/qt/gl/draw/draw_item.hpp>
#include <boost/gil.hpp>
namespace scwx
{
namespace qt
{
namespace gl
{
namespace draw
{
class GeoLine : public DrawItem
{
public:
explicit GeoLine(std::shared_ptr<GlContext> context);
~GeoLine();
GeoLine(const GeoLine&) = delete;
GeoLine& operator=(const GeoLine&) = delete;
GeoLine(GeoLine&&) noexcept;
GeoLine& operator=(GeoLine&&) noexcept;
void Initialize() override;
void Render(const QMapbox::CustomLayerRenderParameters& params) override;
void Deinitialize() override;
/**
* Sets the geographic coordinate endpoints associated with the line.
*
* @param latitude1 Latitude of the first endpoint in degrees
* @param longitude1 Longitude of the first endpoint in degrees
* @param latitude2 Latitude of the second endpoint in degrees
* @param longitude2 Longitude of the second endpoint in degrees
*/
void SetPoints(float latitude1,
float longitude1,
float latitude2,
float longitude2);
/**
* Sets the modulate color of the line. If specified, the texture color will
* be multiplied by the modulate color to produce the result.
*
* @param color Modulate color (RGBA)
*/
void SetModulateColor(boost::gil::rgba8_pixel_t color);
/**
* Sets the width of the line.
*
* @param width Width in pixels
*/
void SetWidth(float width);
/**
* Sets the visibility of the line.
*
* @param visible
*/
void SetVisible(bool visible);
private:
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace draw
} // namespace gl
} // namespace qt
} // namespace scwx

View file

@ -1,4 +1,5 @@
#include <scwx/qt/gl/draw/rectangle.hpp>
#include <scwx/util/logger.hpp>
#include <optional>
@ -12,6 +13,7 @@ namespace draw
{
static const std::string logPrefix_ = "scwx::qt::gl::draw::rectangle";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static constexpr size_t NUM_RECTANGLES = 5;
static constexpr size_t NUM_TRIANGLES = NUM_RECTANGLES * 2;
@ -21,11 +23,11 @@ static constexpr size_t POINTS_PER_VERTEX = 7;
static constexpr size_t BUFFER_LENGTH =
NUM_TRIANGLES * VERTICES_PER_TRIANGLE * POINTS_PER_VERTEX;
class RectangleImpl
class Rectangle::Impl
{
public:
explicit RectangleImpl(OpenGLFunctions& gl) :
gl_ {gl},
explicit Impl(std::shared_ptr<GlContext> context) :
context_ {context},
dirty_ {false},
visible_ {true},
x_ {0.0f},
@ -36,14 +38,16 @@ public:
borderColor_ {0, 0, 0, 0},
borderWidth_ {0.0f},
fillColor_ {std::nullopt},
shaderProgram_ {nullptr},
uMVPMatrixLocation_(GL_INVALID_INDEX),
vao_ {GL_INVALID_INDEX},
vbo_ {GL_INVALID_INDEX}
{
}
~RectangleImpl() {}
~Impl() {}
OpenGLFunctions& gl_;
std::shared_ptr<GlContext> context_;
bool dirty_;
@ -59,25 +63,37 @@ public:
std::optional<boost::gil::rgba8_pixel_t> fillColor_;
std::shared_ptr<ShaderProgram> shaderProgram_;
GLint uMVPMatrixLocation_;
GLuint vao_;
GLuint vbo_;
void Update();
};
// TODO: OpenGL context with shaders
Rectangle::Rectangle(OpenGLFunctions& gl) :
DrawItem(), p(std::make_unique<RectangleImpl>(gl))
Rectangle::Rectangle(std::shared_ptr<GlContext> context) :
DrawItem(context->gl()), p(std::make_unique<Impl>(context))
{
}
Rectangle::~Rectangle() = default;
Rectangle::Rectangle(Rectangle&&) noexcept = default;
Rectangle::Rectangle(Rectangle&&) noexcept = default;
Rectangle& Rectangle::operator=(Rectangle&&) noexcept = default;
void Rectangle::Initialize()
{
gl::OpenGLFunctions& gl = p->gl_;
gl::OpenGLFunctions& gl = p->context_->gl();
p->shaderProgram_ =
p->context_->GetShaderProgram(":/gl/color.vert", ":/gl/color.frag");
p->uMVPMatrixLocation_ =
gl.glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix");
if (p->uMVPMatrixLocation_ == -1)
{
logger_->warn("Could not find uMVPMatrix");
}
gl.glGenVertexArrays(1, &p->vao_);
gl.glGenBuffers(1, &p->vbo_);
@ -106,16 +122,18 @@ void Rectangle::Initialize()
p->dirty_ = true;
}
void Rectangle::Render()
void Rectangle::Render(const QMapbox::CustomLayerRenderParameters& params)
{
if (p->visible_)
{
gl::OpenGLFunctions& gl = p->gl_;
gl::OpenGLFunctions& gl = p->context_->gl();
gl.glBindVertexArray(p->vao_);
gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
p->Update();
p->shaderProgram_->Use();
UseDefaultProjection(params, p->uMVPMatrixLocation_);
if (p->fillColor_.has_value())
{
@ -133,7 +151,7 @@ void Rectangle::Render()
void Rectangle::Deinitialize()
{
gl::OpenGLFunctions& gl = p->gl_;
gl::OpenGLFunctions& gl = p->context_->gl();
gl.glDeleteVertexArrays(1, &p->vao_);
gl.glDeleteBuffers(1, &p->vbo_);
@ -184,11 +202,11 @@ void Rectangle::SetVisible(bool visible)
p->visible_ = visible;
}
void RectangleImpl::Update()
void Rectangle::Impl::Update()
{
if (dirty_)
{
gl::OpenGLFunctions& gl = gl_;
gl::OpenGLFunctions& gl = context_->gl();
const float lox = x_;
const float rox = x_ + width_;

View file

@ -1,6 +1,6 @@
#pragma once
#include <scwx/qt/gl/gl.hpp>
#include <scwx/qt/gl/gl_context.hpp>
#include <scwx/qt/gl/draw/draw_item.hpp>
#include <boost/gil.hpp>
@ -14,22 +14,20 @@ namespace gl
namespace draw
{
class RectangleImpl;
class Rectangle : public DrawItem
{
public:
explicit Rectangle(OpenGLFunctions& gl);
explicit Rectangle(std::shared_ptr<GlContext> context);
~Rectangle();
Rectangle(const Rectangle&) = delete;
Rectangle(const Rectangle&) = delete;
Rectangle& operator=(const Rectangle&) = delete;
Rectangle(Rectangle&&) noexcept;
Rectangle& operator=(Rectangle&&) noexcept;
void Initialize() override;
void Render() override;
void Render(const QMapbox::CustomLayerRenderParameters& params) override;
void Deinitialize() override;
void SetBorder(float width, boost::gil::rgba8_pixel_t color);
@ -39,7 +37,9 @@ public:
void SetVisible(bool visible);
private:
std::unique_ptr<RectangleImpl> p;
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace draw

View file

@ -0,0 +1,90 @@
#include <scwx/qt/gl/gl_context.hpp>
#include <scwx/qt/util/texture_atlas.hpp>
#include <scwx/util/hash.hpp>
#include <scwx/util/logger.hpp>
namespace scwx
{
namespace qt
{
namespace gl
{
static const std::string logPrefix_ = "scwx::qt::gl::gl_context";
class GlContext::Impl
{
public:
explicit Impl() :
gl_ {},
shaderProgramMap_ {},
shaderProgramMutex_ {},
textureAtlas_ {GL_INVALID_INDEX},
textureMutex_ {}
{
}
~Impl() {}
gl::OpenGLFunctions gl_;
std::unordered_map<std::pair<std::string, std::string>,
std::shared_ptr<gl::ShaderProgram>,
scwx::util::hash<std::pair<std::string, std::string>>>
shaderProgramMap_;
std::mutex shaderProgramMutex_;
GLuint textureAtlas_;
std::mutex textureMutex_;
};
GlContext::GlContext() : p(std::make_unique<Impl>()) {}
GlContext::~GlContext() = default;
GlContext::GlContext(GlContext&&) noexcept = default;
GlContext& GlContext::operator=(GlContext&&) noexcept = default;
gl::OpenGLFunctions& GlContext::gl()
{
return p->gl_;
}
std::shared_ptr<gl::ShaderProgram>
GlContext::GetShaderProgram(const std::string& vertexPath,
const std::string& fragmentPath)
{
const std::pair<std::string, std::string> key {vertexPath, fragmentPath};
std::shared_ptr<gl::ShaderProgram> shaderProgram;
std::unique_lock lock(p->shaderProgramMutex_);
auto it = p->shaderProgramMap_.find(key);
if (it == p->shaderProgramMap_.end())
{
shaderProgram = std::make_shared<gl::ShaderProgram>(p->gl_);
shaderProgram->Load(vertexPath, fragmentPath);
p->shaderProgramMap_[key] = shaderProgram;
}
else
{
shaderProgram = it->second;
}
return shaderProgram;
}
GLuint GlContext::GetTextureAtlas()
{
std::unique_lock lock(p->textureMutex_);
if (p->textureAtlas_ == GL_INVALID_INDEX)
{
p->textureAtlas_ = util::TextureAtlas::Instance().BufferAtlas(p->gl_);
}
return p->textureAtlas_;
}
} // namespace gl
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,41 @@
#pragma once
#include <scwx/qt/gl/gl.hpp>
#include <scwx/qt/gl/shader_program.hpp>
namespace scwx
{
namespace qt
{
namespace gl
{
class GlContext
{
public:
explicit GlContext();
virtual ~GlContext();
GlContext(const GlContext&) = delete;
GlContext& operator=(const GlContext&) = delete;
GlContext(GlContext&&) noexcept;
GlContext& operator=(GlContext&&) noexcept;
gl::OpenGLFunctions& gl();
std::shared_ptr<gl::ShaderProgram>
GetShaderProgram(const std::string& vertexPath,
const std::string& fragmentPath);
GLuint GetTextureAtlas();
private:
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace gl
} // namespace qt
} // namespace scwx

View file

@ -13,19 +13,18 @@ namespace gl
static const std::string logPrefix_ = "scwx::qt::gl::shader_program";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static constexpr GLsizei INFO_LOG_BUF_SIZE = 512;
static constexpr GLsizei kInfoLogBufSize = 512;
class ShaderProgramImpl
class ShaderProgram::Impl
{
public:
explicit ShaderProgramImpl(OpenGLFunctions& gl) :
gl_(gl), id_ {GL_INVALID_INDEX}
explicit Impl(OpenGLFunctions& gl) : gl_(gl), id_ {GL_INVALID_INDEX}
{
// Create shader program
id_ = gl_.glCreateProgram();
}
~ShaderProgramImpl()
~Impl()
{
// Delete shader program
gl_.glDeleteProgram(id_);
@ -37,12 +36,12 @@ public:
};
ShaderProgram::ShaderProgram(OpenGLFunctions& gl) :
p(std::make_unique<ShaderProgramImpl>(gl))
p(std::make_unique<Impl>(gl))
{
}
ShaderProgram::~ShaderProgram() = default;
ShaderProgram::ShaderProgram(ShaderProgram&&) noexcept = default;
ShaderProgram::ShaderProgram(ShaderProgram&&) noexcept = default;
ShaderProgram& ShaderProgram::operator=(ShaderProgram&&) noexcept = default;
GLuint ShaderProgram::id() const
@ -59,7 +58,7 @@ bool ShaderProgram::Load(const std::string& vertexPath,
GLint glSuccess;
bool success = true;
char infoLog[INFO_LOG_BUF_SIZE];
char infoLog[kInfoLogBufSize];
GLsizei logLength;
QFile vertexFile(vertexPath.c_str());
@ -102,7 +101,7 @@ bool ShaderProgram::Load(const std::string& vertexPath,
// Check for errors
gl.glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &glSuccess);
gl.glGetShaderInfoLog(vertexShader, INFO_LOG_BUF_SIZE, &logLength, infoLog);
gl.glGetShaderInfoLog(vertexShader, kInfoLogBufSize, &logLength, infoLog);
if (!glSuccess)
{
logger_->error("Vertex shader compilation failed: {}", infoLog);
@ -122,8 +121,7 @@ bool ShaderProgram::Load(const std::string& vertexPath,
// Check for errors
gl.glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &glSuccess);
gl.glGetShaderInfoLog(
fragmentShader, INFO_LOG_BUF_SIZE, &logLength, infoLog);
gl.glGetShaderInfoLog(fragmentShader, kInfoLogBufSize, &logLength, infoLog);
if (!glSuccess)
{
logger_->error("Fragment shader compilation failed: {}", infoLog);
@ -142,7 +140,7 @@ bool ShaderProgram::Load(const std::string& vertexPath,
// Check for errors
gl.glGetProgramiv(p->id_, GL_LINK_STATUS, &glSuccess);
gl.glGetProgramInfoLog(p->id_, INFO_LOG_BUF_SIZE, &logLength, infoLog);
gl.glGetProgramInfoLog(p->id_, kInfoLogBufSize, &logLength, infoLog);
if (!glSuccess)
{
logger_->error("Shader program link failed: {}", infoLog);

View file

@ -16,15 +16,13 @@ namespace qt
namespace gl
{
class ShaderProgramImpl;
class ShaderProgram
{
public:
explicit ShaderProgram(OpenGLFunctions& gl);
virtual ~ShaderProgram();
ShaderProgram(const ShaderProgram&) = delete;
ShaderProgram(const ShaderProgram&) = delete;
ShaderProgram& operator=(const ShaderProgram&) = delete;
ShaderProgram(ShaderProgram&&) noexcept;
@ -37,7 +35,9 @@ public:
void Use() const;
private:
std::unique_ptr<ShaderProgramImpl> p;
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace gl

View file

@ -1,4 +1,5 @@
#include <scwx/qt/gl/text_shader.hpp>
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/util/logger.hpp>
#pragma warning(push, 0)
@ -18,41 +19,46 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class TextShaderImpl
{
public:
explicit TextShaderImpl(OpenGLFunctions& gl) :
gl_ {gl}, projectionLocation_(GL_INVALID_INDEX)
explicit TextShaderImpl(std::shared_ptr<GlContext> context) :
context_ {context},
shaderProgram_ {nullptr},
projectionLocation_(GL_INVALID_INDEX)
{
}
~TextShaderImpl() {}
OpenGLFunctions& gl_;
std::shared_ptr<GlContext> context_;
std::shared_ptr<ShaderProgram> shaderProgram_;
GLint projectionLocation_;
};
TextShader::TextShader(OpenGLFunctions& gl) :
ShaderProgram(gl), p(std::make_unique<TextShaderImpl>(gl))
TextShader::TextShader(std::shared_ptr<GlContext> context) :
p(std::make_unique<TextShaderImpl>(context))
{
}
TextShader::~TextShader() = default;
TextShader::TextShader(TextShader&&) noexcept = default;
TextShader::TextShader(TextShader&&) noexcept = default;
TextShader& TextShader::operator=(TextShader&&) noexcept = default;
bool TextShader::Initialize()
{
OpenGLFunctions& gl = p->gl_;
OpenGLFunctions& gl = p->context_->gl();
// Load and configure shader
bool success = Load(":/gl/text.vert", ":/gl/text.frag");
p->shaderProgram_ =
p->context_->GetShaderProgram(":/gl/text.vert", ":/gl/text.frag");
p->projectionLocation_ = gl.glGetUniformLocation(id(), "projection");
p->projectionLocation_ =
gl.glGetUniformLocation(p->shaderProgram_->id(), "projection");
if (p->projectionLocation_ == -1)
{
logger_->warn("Could not find projection");
}
return success;
return true;
}
void TextShader::RenderText(const std::string& text,
@ -65,9 +71,9 @@ void TextShader::RenderText(const std::string& text,
GLuint textureId,
TextAlign align)
{
OpenGLFunctions& gl = p->gl_;
OpenGLFunctions& gl = p->context_->gl();
Use();
p->shaderProgram_->Use();
gl.glEnable(GL_BLEND);
gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -103,7 +109,7 @@ void TextShader::RenderText(const std::string& text,
void TextShader::SetProjection(const glm::mat4& projection)
{
p->gl_.glUniformMatrix4fv(
p->context_->gl().glUniformMatrix4fv(
p->projectionLocation_, 1, GL_FALSE, glm::value_ptr(projection));
}

View file

@ -1,6 +1,6 @@
#pragma once
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/qt/gl/gl_context.hpp>
#include <scwx/qt/util/font.hpp>
#include <memory>
@ -24,13 +24,13 @@ enum class TextAlign
class TextShaderImpl;
class TextShader : public ShaderProgram
class TextShader
{
public:
explicit TextShader(OpenGLFunctions& gl);
explicit TextShader(std::shared_ptr<GlContext> context);
~TextShader();
TextShader(const TextShader&) = delete;
TextShader(const TextShader&) = delete;
TextShader& operator=(const TextShader&) = delete;
TextShader(TextShader&&) noexcept;

View file

@ -1,5 +1,6 @@
#include <scwx/qt/manager/resource_manager.hpp>
#include <scwx/qt/util/font.hpp>
#include <scwx/qt/util/texture_atlas.hpp>
namespace scwx
{
@ -20,6 +21,13 @@ static void LoadFonts()
{
util::Font::Create(":/res/fonts/din1451alt.ttf");
util::Font::Create(":/res/fonts/din1451alt_g.ttf");
util::TextureAtlas& textureAtlas = util::TextureAtlas::Instance();
textureAtlas.RegisterTexture("lines/default-1x7",
":/res/textures/lines/default-1x7.png");
textureAtlas.RegisterTexture("lines/test-pattern",
":/res/textures/lines/test-pattern.png");
textureAtlas.BuildAtlas(8, 8);
}
} // namespace ResourceManager

View file

@ -21,7 +21,7 @@ class ColorTableLayerImpl
{
public:
explicit ColorTableLayerImpl(std::shared_ptr<MapContext> context) :
shaderProgram_(context->gl_),
shaderProgram_(nullptr),
uMVPMatrixLocation_(GL_INVALID_INDEX),
vbo_ {GL_INVALID_INDEX},
vao_ {GL_INVALID_INDEX},
@ -31,7 +31,8 @@ public:
}
~ColorTableLayerImpl() = default;
gl::ShaderProgram shaderProgram_;
std::shared_ptr<gl::ShaderProgram> shaderProgram_;
GLint uMVPMatrixLocation_;
std::array<GLuint, 2> vbo_;
GLuint vao_;
@ -52,13 +53,14 @@ void ColorTableLayer::Initialize()
{
logger_->debug("Initialize()");
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
// Load and configure overlay shader
p->shaderProgram_.Load(":/gl/texture1d.vert", ":/gl/texture1d.frag");
p->shaderProgram_ =
context()->GetShaderProgram(":/gl/texture1d.vert", ":/gl/texture1d.frag");
p->uMVPMatrixLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uMVPMatrix");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix");
if (p->uMVPMatrixLocation_ == -1)
{
logger_->warn("Could not find uMVPMatrix");
@ -66,7 +68,7 @@ void ColorTableLayer::Initialize()
gl.glGenTextures(1, &p->texture_);
p->shaderProgram_.Use();
p->shaderProgram_->Use();
// Generate a vertex array object
gl.glGenVertexArrays(1, &p->vao_);
@ -99,7 +101,7 @@ void ColorTableLayer::Initialize()
gl.glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, static_cast<void*>(0));
gl.glEnableVertexAttribArray(1);
connect(context()->radarProductView_.get(),
connect(context()->radar_product_view().get(),
&view::RadarProductView::ColorTableUpdated,
this,
[=]() { p->colorTableNeedsUpdate_ = true; });
@ -107,10 +109,11 @@ void ColorTableLayer::Initialize()
void ColorTableLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
{
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
auto radarProductView = context()->radar_product_view();
if (context()->radarProductView_ == nullptr ||
!context()->radarProductView_->IsInitialized())
if (context()->radar_product_view() == nullptr ||
!context()->radar_product_view()->IsInitialized())
{
// Defer rendering until view is initialized
return;
@ -121,14 +124,14 @@ void ColorTableLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
0.0f,
static_cast<float>(params.height));
p->shaderProgram_.Use();
p->shaderProgram_->Use();
gl.glUniformMatrix4fv(
p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(projection));
if (p->colorTableNeedsUpdate_)
{
p->colorTable_ = context()->radarProductView_->color_table();
p->colorTable_ = radarProductView->color_table();
gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_1D, p->texture_);
@ -145,9 +148,8 @@ void ColorTableLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
gl.glGenerateMipmap(GL_TEXTURE_1D);
}
if (p->colorTable_.size() > 0 &&
context()->radarProductView_->sweep_time() !=
std::chrono::system_clock::time_point())
if (p->colorTable_.size() > 0 && radarProductView->sweep_time() !=
std::chrono::system_clock::time_point())
{
// Color table panel vertices
const float vertexLX = 0.0f;
@ -176,7 +178,7 @@ void ColorTableLayer::Deinitialize()
{
logger_->debug("Deinitialize()");
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
gl.glDeleteVertexArrays(1, &p->vao_);
gl.glDeleteBuffers(2, p->vbo_.data());

View file

@ -2,12 +2,6 @@
#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/util/logger.hpp>
#pragma warning(push, 0)
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#pragma warning(pop)
namespace scwx
{
namespace qt
@ -22,16 +16,14 @@ class DrawLayerImpl
{
public:
explicit DrawLayerImpl(std::shared_ptr<MapContext> context) :
shaderProgram_ {context->gl_}, uMVPMatrixLocation_(GL_INVALID_INDEX)
context_ {context}, drawList_ {}, textureAtlas_ {GL_INVALID_INDEX}
{
}
~DrawLayerImpl() {}
gl::ShaderProgram shaderProgram_;
GLint uMVPMatrixLocation_;
std::shared_ptr<MapContext> context_;
std::vector<std::shared_ptr<gl::draw::DrawItem>> drawList_;
GLuint textureAtlas_;
};
DrawLayer::DrawLayer(std::shared_ptr<MapContext> context) :
@ -42,20 +34,9 @@ DrawLayer::~DrawLayer() = default;
void DrawLayer::Initialize()
{
gl::OpenGLFunctions& gl = context()->gl_;
p->textureAtlas_ = p->context_->GetTextureAtlas();
p->shaderProgram_.Load(":/gl/color.vert", ":/gl/color.frag");
p->uMVPMatrixLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uMVPMatrix");
if (p->uMVPMatrixLocation_ == -1)
{
logger_->warn("Could not find uMVPMatrix");
}
p->shaderProgram_.Use();
for (auto item : p->drawList_)
for (auto& item : p->drawList_)
{
item->Initialize();
}
@ -63,27 +44,22 @@ void DrawLayer::Initialize()
void DrawLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
{
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = p->context_->gl();
p->shaderProgram_.Use();
gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, p->textureAtlas_);
glm::mat4 projection = glm::ortho(0.0f,
static_cast<float>(params.width),
0.0f,
static_cast<float>(params.height));
gl.glUniformMatrix4fv(
p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(projection));
for (auto item : p->drawList_)
for (auto& item : p->drawList_)
{
item->Render();
item->Render(params);
}
}
void DrawLayer::Deinitialize()
{
for (auto item : p->drawList_)
p->textureAtlas_ = GL_INVALID_INDEX;
for (auto& item : p->drawList_)
{
item->Deinitialize();
}

View file

@ -0,0 +1,90 @@
#include <scwx/qt/map/map_context.hpp>
namespace scwx
{
namespace qt
{
namespace map
{
class MapContext::Impl
{
public:
explicit Impl(std::shared_ptr<view::RadarProductView> radarProductView) :
settings_ {},
radarProductView_ {radarProductView},
radarProductGroup_ {common::RadarProductGroup::Unknown},
radarProduct_ {"???"},
radarProductCode_ {0}
{
}
~Impl() {}
MapSettings settings_;
std::shared_ptr<view::RadarProductView> radarProductView_;
common::RadarProductGroup radarProductGroup_;
std::string radarProduct_;
int16_t radarProductCode_;
};
MapContext::MapContext(
std::shared_ptr<view::RadarProductView> radarProductView) :
p(std::make_unique<Impl>(radarProductView))
{
}
MapContext::~MapContext() = default;
MapContext::MapContext(MapContext&&) noexcept = default;
MapContext& MapContext::operator=(MapContext&&) noexcept = default;
MapSettings& MapContext::settings()
{
return p->settings_;
}
std::shared_ptr<view::RadarProductView> MapContext::radar_product_view() const
{
return p->radarProductView_;
}
common::RadarProductGroup MapContext::radar_product_group() const
{
return p->radarProductGroup_;
}
std::string MapContext::radar_product() const
{
return p->radarProduct_;
}
int16_t MapContext::radar_product_code() const
{
return p->radarProductCode_;
}
void MapContext::set_radar_product_view(
std::shared_ptr<view::RadarProductView> radarProductView)
{
p->radarProductView_ = radarProductView;
}
void MapContext::set_radar_product_group(
common::RadarProductGroup radarProductGroup)
{
p->radarProductGroup_ = radarProductGroup;
}
void MapContext::set_radar_product(const std::string& radarProduct)
{
p->radarProduct_ = radarProduct;
}
void MapContext::set_radar_product_code(int16_t radarProductCode)
{
p->radarProductCode_ = radarProductCode;
}
} // namespace map
} // namespace qt
} // namespace scwx

View file

@ -1,6 +1,6 @@
#pragma once
#include <scwx/qt/gl/gl.hpp>
#include <scwx/qt/gl/gl_context.hpp>
#include <scwx/qt/map/map_settings.hpp>
#include <scwx/qt/view/radar_product_view.hpp>
@ -11,32 +11,35 @@ namespace qt
namespace map
{
struct MapContext
class MapContext : public gl::GlContext
{
public:
explicit MapContext(
std::shared_ptr<view::RadarProductView> radarProductView = nullptr) :
gl_ {},
settings_ {},
radarProductView_ {radarProductView},
radarProductGroup_ {common::RadarProductGroup::Unknown},
radarProduct_ {"???"},
radarProductCode_ {0}
{
}
~MapContext() = default;
std::shared_ptr<view::RadarProductView> radarProductView = nullptr);
~MapContext();
MapContext(const MapContext&) = delete;
MapContext(const MapContext&) = delete;
MapContext& operator=(const MapContext&) = delete;
MapContext(MapContext&&) noexcept = default;
MapContext& operator=(MapContext&&) noexcept = default;
MapContext(MapContext&&) noexcept;
MapContext& operator=(MapContext&&) noexcept;
gl::OpenGLFunctions gl_;
MapSettings settings_;
std::shared_ptr<view::RadarProductView> radarProductView_;
common::RadarProductGroup radarProductGroup_;
std::string radarProduct_;
int16_t radarProductCode_;
MapSettings& settings();
std::shared_ptr<view::RadarProductView> radar_product_view() const;
common::RadarProductGroup radar_product_group() const;
std::string radar_product() const;
int16_t radar_product_code() const;
void set_radar_product_view(
std::shared_ptr<view::RadarProductView> radarProductView);
void set_radar_product_group(common::RadarProductGroup radarProductGroup);
void set_radar_product(const std::string& radarProduct);
void set_radar_product_code(int16_t radarProductCode);
private:
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace map

View file

@ -153,9 +153,11 @@ common::Level3ProductCategoryMap MapWidget::GetAvailableLevel3Categories()
float MapWidget::GetElevation() const
{
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
return p->context_->radarProductView_->elevation();
return radarProductView->elevation();
}
else
{
@ -165,9 +167,11 @@ float MapWidget::GetElevation() const
std::vector<float> MapWidget::GetElevationCuts() const
{
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
return p->context_->radarProductView_->GetElevationCuts();
return radarProductView->GetElevationCuts();
}
else
{
@ -182,10 +186,12 @@ MapWidgetImpl::GetLevel2ProductOrDefault(const std::string& productName) const
if (level2Product == common::Level2Product::Unknown)
{
if (context_->radarProductView_ != nullptr)
auto radarProductView = context_->radar_product_view();
if (radarProductView != nullptr)
{
level2Product = common::GetLevel2Product(
context_->radarProductView_->GetRadarProductName());
level2Product =
common::GetLevel2Product(radarProductView->GetRadarProductName());
}
}
@ -218,9 +224,11 @@ std::vector<std::string> MapWidget::GetLevel3Products()
common::RadarProductGroup MapWidget::GetRadarProductGroup() const
{
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
return p->context_->radarProductView_->GetRadarProductGroup();
return radarProductView->GetRadarProductGroup();
}
else
{
@ -230,10 +238,11 @@ common::RadarProductGroup MapWidget::GetRadarProductGroup() const
std::string MapWidget::GetRadarProductName() const
{
auto radarProductView = p->context_->radar_product_view();
if (p->context_->radarProductView_ != nullptr)
if (radarProductView != nullptr)
{
return p->context_->radarProductView_->GetRadarProductName();
return radarProductView->GetRadarProductName();
}
else
{
@ -255,9 +264,11 @@ std::shared_ptr<config::RadarSite> MapWidget::GetRadarSite() const
uint16_t MapWidget::GetVcp() const
{
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
return p->context_->radarProductView_->vcp();
return radarProductView->vcp();
}
else
{
@ -267,10 +278,12 @@ uint16_t MapWidget::GetVcp() const
void MapWidget::SelectElevation(float elevation)
{
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
p->context_->radarProductView_->SelectElevation(elevation);
p->context_->radarProductView_->Update();
radarProductView->SelectElevation(elevation);
radarProductView->Update();
}
}
@ -280,8 +293,7 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
{
bool radarProductViewCreated = false;
std::shared_ptr<view::RadarProductView>& radarProductView =
p->context_->radarProductView_;
auto radarProductView = p->context_->radar_product_view();
std::string productName {product};
@ -304,12 +316,13 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
(radarProductView->GetRadarProductGroup() ==
common::RadarProductGroup::Level2 &&
radarProductView->GetRadarProductName() != productName) ||
p->context_->radarProductCode_ != productCode)
p->context_->radar_product_code() != productCode)
{
p->RadarProductViewDisconnect();
radarProductView = view::RadarProductViewFactory::Create(
group, productName, productCode, p->radarProductManager_);
p->context_->set_radar_product_view(radarProductView);
p->RadarProductViewConnect();
@ -320,9 +333,9 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
radarProductView->SelectProduct(productName);
}
p->context_->radarProductGroup_ = group;
p->context_->radarProduct_ = productName;
p->context_->radarProductCode_ = productCode;
p->context_->set_radar_product_group(group);
p->context_->set_radar_product(productName);
p->context_->set_radar_product_code(productCode);
if (radarProductView != nullptr)
{
@ -372,7 +385,7 @@ void MapWidget::SelectRadarProduct(
void MapWidget::SetActive(bool isActive)
{
p->context_->settings_.isActive_ = isActive;
p->context_->settings().isActive_ = isActive;
update();
}
@ -382,11 +395,13 @@ void MapWidget::SetAutoRefresh(bool enabled)
{
p->autoRefreshEnabled_ = enabled;
if (p->autoRefreshEnabled_ && p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (p->autoRefreshEnabled_ && radarProductView != nullptr)
{
p->radarProductManager_->EnableRefresh(
p->context_->radarProductView_->GetRadarProductGroup(),
p->context_->radarProductView_->GetRadarProductName(),
radarProductView->GetRadarProductGroup(),
radarProductView->GetRadarProductName(),
true);
}
}
@ -430,7 +445,9 @@ void MapWidget::AddLayers()
}
p->layerList_.clear();
if (p->context_->radarProductView_ != nullptr)
auto radarProductView = p->context_->radar_product_view();
if (radarProductView != nullptr)
{
p->radarProductLayer_ = std::make_shared<RadarProductLayer>(p->context_);
p->colorTableLayer_ = std::make_shared<ColorTableLayer>(p->context_);
@ -453,7 +470,7 @@ void MapWidget::AddLayers()
p->AddLayer("radar", p->radarProductLayer_, before);
RadarRangeLayer::Add(p->map_,
p->context_->radarProductView_->range(),
radarProductView->range(),
{radarSite->latitude(), radarSite->longitude()});
p->AddLayer("colorTable", p->colorTableLayer_);
}
@ -572,7 +589,7 @@ void MapWidget::initializeGL()
logger_->debug("initializeGL()");
makeCurrent();
p->context_->gl_.initializeOpenGLFunctions();
p->context_->gl().initializeOpenGLFunctions();
p->map_.reset(new QMapboxGL(nullptr, p->settings_, size(), pixelRatio()));
connect(p->map_.get(),
@ -637,9 +654,10 @@ void MapWidgetImpl::RadarProductManagerConnect()
const std::string& product,
std::chrono::system_clock::time_point latestTime)
{
if (autoRefreshEnabled_ && context_->radarProductGroup_ == group &&
if (autoRefreshEnabled_ &&
context_->radar_product_group() == group &&
(group == common::RadarProductGroup::Level2 ||
context_->radarProduct_ == product))
context_->radar_product() == product))
{
// Create file request
std::shared_ptr<request::NexradFileRequest> request =
@ -698,16 +716,18 @@ void MapWidgetImpl::InitializeNewRadarProductView(
util::async(
[=]()
{
auto radarProductView = context_->radar_product_view();
std::string colorTableFile =
manager::SettingsManager::palette_settings()->palette(colorPalette);
if (!colorTableFile.empty())
{
std::shared_ptr<common::ColorTable> colorTable =
common::ColorTable::Load(colorTableFile);
context_->radarProductView_->LoadColorTable(colorTable);
radarProductView->LoadColorTable(colorTable);
}
context_->radarProductView_->Initialize();
radarProductView->Initialize();
});
if (map_ != nullptr)
@ -718,26 +738,28 @@ void MapWidgetImpl::InitializeNewRadarProductView(
void MapWidgetImpl::RadarProductViewConnect()
{
if (context_->radarProductView_ != nullptr)
auto radarProductView = context_->radar_product_view();
if (radarProductView != nullptr)
{
connect(
context_->radarProductView_.get(),
radarProductView.get(),
&view::RadarProductView::ColorTableUpdated,
this,
[&]() { widget_->update(); },
Qt::QueuedConnection);
connect(
context_->radarProductView_.get(),
radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
[&]()
[=]()
{
std::shared_ptr<config::RadarSite> radarSite =
radarProductManager_->radar_site();
RadarRangeLayer::Update(
map_,
context_->radarProductView_->range(),
radarProductView->range(),
{radarSite->latitude(), radarSite->longitude()});
widget_->update();
emit widget_->RadarSweepUpdated();
@ -748,13 +770,15 @@ void MapWidgetImpl::RadarProductViewConnect()
void MapWidgetImpl::RadarProductViewDisconnect()
{
if (context_->radarProductView_ != nullptr)
auto radarProductView = context_->radar_product_view();
if (radarProductView != nullptr)
{
disconnect(context_->radarProductView_.get(),
disconnect(radarProductView.get(),
&view::RadarProductView::ColorTableUpdated,
this,
nullptr);
disconnect(context_->radarProductView_.get(),
disconnect(radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
nullptr);

View file

@ -34,12 +34,12 @@ class OverlayLayerImpl
{
public:
explicit OverlayLayerImpl(std::shared_ptr<MapContext> context) :
textShader_(context->gl_),
textShader_(context),
font_(util::Font::Create(":/res/fonts/din1451alt.ttf")),
texture_ {GL_INVALID_INDEX},
activeBoxOuter_ {std::make_shared<gl::draw::Rectangle>(context->gl_)},
activeBoxInner_ {std::make_shared<gl::draw::Rectangle>(context->gl_)},
timeBox_ {std::make_shared<gl::draw::Rectangle>(context->gl_)},
activeBoxOuter_ {std::make_shared<gl::draw::Rectangle>(context)},
activeBoxInner_ {std::make_shared<gl::draw::Rectangle>(context)},
timeBox_ {std::make_shared<gl::draw::Rectangle>(context)},
sweepTimeString_ {},
sweepTimeNeedsUpdate_ {true}
{
@ -81,7 +81,8 @@ void OverlayLayer::Initialize()
DrawLayer::Initialize();
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
auto radarProductView = context()->radar_product_view();
p->textShader_.Initialize();
@ -90,9 +91,9 @@ void OverlayLayer::Initialize()
p->texture_ = p->font_->GenerateTexture(gl);
}
if (context()->radarProductView_ != nullptr)
if (radarProductView != nullptr)
{
connect(context()->radarProductView_.get(),
connect(radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
&OverlayLayer::UpdateSweepTimeNextFrame);
@ -103,14 +104,14 @@ void OverlayLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
{
constexpr float fontSize = 16.0f;
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
auto radarProductView = context()->radar_product_view();
auto& settings = context()->settings();
if (p->sweepTimeNeedsUpdate_ && context()->radarProductView_ != nullptr)
if (p->sweepTimeNeedsUpdate_ && radarProductView != nullptr)
{
p->sweepTimeString_ =
scwx::util::TimeString(context()->radarProductView_->sweep_time(),
std::chrono::current_zone(),
false);
p->sweepTimeString_ = scwx::util::TimeString(
radarProductView->sweep_time(), std::chrono::current_zone(), false);
p->sweepTimeNeedsUpdate_ = false;
}
@ -120,9 +121,9 @@ void OverlayLayer::Render(const QMapbox::CustomLayerRenderParameters& params)
static_cast<float>(params.height));
// Active Box
p->activeBoxOuter_->SetVisible(context()->settings_.isActive_);
p->activeBoxInner_->SetVisible(context()->settings_.isActive_);
if (context()->settings_.isActive_)
p->activeBoxOuter_->SetVisible(settings.isActive_);
p->activeBoxInner_->SetVisible(settings.isActive_);
if (settings.isActive_)
{
p->activeBoxOuter_->SetSize(params.width, params.height);
p->activeBoxInner_->SetSize(params.width - 2.0f, params.height - 2.0f);
@ -164,15 +165,16 @@ void OverlayLayer::Deinitialize()
DrawLayer::Deinitialize();
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
auto radarProductView = context()->radar_product_view();
gl.glDeleteTextures(1, &p->texture_);
p->texture_ = GL_INVALID_INDEX;
if (context()->radarProductView_ != nullptr)
if (radarProductView != nullptr)
{
disconnect(context()->radarProductView_.get(),
disconnect(radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
&OverlayLayer::UpdateSweepTimeNextFrame);

View file

@ -33,7 +33,7 @@ class RadarProductLayerImpl
{
public:
explicit RadarProductLayerImpl(std::shared_ptr<MapContext> context) :
shaderProgram_(context->gl_),
shaderProgram_(nullptr),
uMVPMatrixLocation_(GL_INVALID_INDEX),
uMapScreenCoordLocation_(GL_INVALID_INDEX),
uDataMomentOffsetLocation_(GL_INVALID_INDEX),
@ -50,7 +50,8 @@ public:
}
~RadarProductLayerImpl() = default;
gl::ShaderProgram shaderProgram_;
std::shared_ptr<gl::ShaderProgram> shaderProgram_;
GLint uMVPMatrixLocation_;
GLint uMapScreenCoordLocation_;
GLint uDataMomentOffsetLocation_;
@ -78,47 +79,48 @@ void RadarProductLayer::Initialize()
{
logger_->debug("Initialize()");
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
// Load and configure radar shader
p->shaderProgram_.Load(":/gl/radar.vert", ":/gl/radar.frag");
p->shaderProgram_ =
context()->GetShaderProgram(":/gl/radar.vert", ":/gl/radar.frag");
p->uMVPMatrixLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uMVPMatrix");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix");
if (p->uMVPMatrixLocation_ == -1)
{
logger_->warn("Could not find uMVPMatrix");
}
p->uMapScreenCoordLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uMapScreenCoord");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uMapScreenCoord");
if (p->uMapScreenCoordLocation_ == -1)
{
logger_->warn("Could not find uMapScreenCoord");
}
p->uDataMomentOffsetLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uDataMomentOffset");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uDataMomentOffset");
if (p->uDataMomentOffsetLocation_ == -1)
{
logger_->warn("Could not find uDataMomentOffset");
}
p->uDataMomentScaleLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uDataMomentScale");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uDataMomentScale");
if (p->uDataMomentScaleLocation_ == -1)
{
logger_->warn("Could not find uDataMomentScale");
}
p->uCFPEnabledLocation_ =
gl.glGetUniformLocation(p->shaderProgram_.id(), "uCFPEnabled");
gl.glGetUniformLocation(p->shaderProgram_->id(), "uCFPEnabled");
if (p->uCFPEnabledLocation_ == -1)
{
logger_->warn("Could not find uCFPEnabled");
}
p->shaderProgram_.Use();
p->shaderProgram_->Use();
// Generate a vertex array object
gl.glGenVertexArrays(1, &p->vao_);
@ -138,11 +140,12 @@ void RadarProductLayer::Initialize()
gl.glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
connect(context()->radarProductView_.get(),
auto radarProductView = context()->radar_product_view();
connect(radarProductView.get(),
&view::RadarProductView::ColorTableUpdated,
this,
[=]() { p->colorTableNeedsUpdate_ = true; });
connect(context()->radarProductView_.get(),
connect(radarProductView.get(),
&view::RadarProductView::SweepComputed,
this,
[=]() { p->sweepNeedsUpdate_ = true; });
@ -152,12 +155,12 @@ void RadarProductLayer::UpdateSweep()
{
logger_->debug("UpdateSweep()");
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
boost::timer::cpu_timer timer;
std::shared_ptr<view::RadarProductView> radarProductView =
context()->radarProductView_;
context()->radar_product_view();
std::unique_lock sweepLock(radarProductView->sweep_mutex(),
std::try_to_lock);
@ -253,9 +256,9 @@ void RadarProductLayer::UpdateSweep()
void RadarProductLayer::Render(
const QMapbox::CustomLayerRenderParameters& params)
{
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
p->shaderProgram_.Use();
p->shaderProgram_->Use();
if (p->colorTableNeedsUpdate_)
{
@ -300,7 +303,7 @@ void RadarProductLayer::Deinitialize()
{
logger_->debug("Deinitialize()");
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
gl.glDeleteVertexArrays(1, &p->vao_);
gl.glDeleteBuffers(3, p->vbo_.data());
@ -321,9 +324,9 @@ void RadarProductLayer::UpdateColorTable()
p->colorTableNeedsUpdate_ = false;
gl::OpenGLFunctions& gl = context()->gl_;
gl::OpenGLFunctions& gl = context()->gl();
std::shared_ptr<view::RadarProductView> radarProductView =
context()->radarProductView_;
context()->radar_product_view();
const std::vector<boost::gil::rgba8_pixel_t>& colorTable =
radarProductView->color_table();

View file

@ -0,0 +1,33 @@
#pragma once
#include <boost/iostreams/categories.hpp>
#include <QIODevice>
namespace scwx
{
namespace qt
{
namespace util
{
class IoDeviceSource
{
public:
typedef char char_type;
typedef boost::iostreams::source_tag category;
IoDeviceSource(QIODevice& source) : source_ {source} {}
~IoDeviceSource() {}
std::streamsize read(char* buffer, std::streamsize n)
{
return source_.read(buffer, n);
}
private:
QIODevice& source_;
};
} // namespace util
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,292 @@
#include <scwx/qt/util/texture_atlas.hpp>
#include <scwx/qt/util/streams.hpp>
#include <scwx/util/logger.hpp>
#include <shared_mutex>
#include <unordered_map>
#pragma warning(push, 0)
#pragma warning(disable : 4714)
#include <boost/gil/extension/io/png.hpp>
#include <boost/iostreams/stream.hpp>
#include <stb_rect_pack.h>
#include <QFile>
#pragma warning(pop)
namespace scwx
{
namespace qt
{
namespace util
{
static const std::string logPrefix_ = "scwx::qt::util::texture_atlas";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
class TextureAtlas::Impl
{
public:
explicit Impl() :
texturePathMap_ {},
texturePathMutex_ {},
atlas_ {},
atlasMap_ {},
atlasMutex_ {}
{
}
~Impl() {}
static boost::gil::rgba8_image_t LoadImage(const std::string& imagePath);
std::unordered_map<std::string, std::string> texturePathMap_;
std::shared_mutex texturePathMutex_;
boost::gil::rgba8_image_t atlas_;
std::unordered_map<std::string, TextureAttributes> atlasMap_;
std::shared_mutex atlasMutex_;
};
TextureAtlas::TextureAtlas() : p(std::make_unique<Impl>()) {}
TextureAtlas::~TextureAtlas() = default;
TextureAtlas::TextureAtlas(TextureAtlas&&) noexcept = default;
TextureAtlas& TextureAtlas::operator=(TextureAtlas&&) noexcept = default;
void TextureAtlas::RegisterTexture(const std::string& name,
const std::string& path)
{
std::unique_lock lock(p->texturePathMutex_);
p->texturePathMap_.insert_or_assign(name, path);
}
void TextureAtlas::BuildAtlas(size_t width, size_t height)
{
logger_->debug("Building {}x{} texture atlas", width, height);
if (width > INT_MAX || height > INT_MAX)
{
logger_->error("Cannot build texture atlas of size {}x{}", width, height);
return;
}
std::vector<std::pair<std::string, boost::gil::rgba8_image_t>> images;
std::vector<stbrp_rect> stbrpRects;
// Load images
{
// Take a read lock on the texture path map
std::shared_lock lock(p->texturePathMutex_);
// For each registered texture
std::for_each(p->texturePathMap_.cbegin(),
p->texturePathMap_.cend(),
[&](const auto& pair)
{
// Load texture image
boost::gil::rgba8_image_t image =
Impl::LoadImage(pair.second);
if (image.width() > 0u && image.height() > 0u)
{
// Store STB rectangle pack data in a vector
stbrpRects.push_back(stbrp_rect {
0,
static_cast<stbrp_coord>(image.width()),
static_cast<stbrp_coord>(image.height()),
0,
0,
0});
// Store image data in a vector
images.emplace_back(pair.first, std::move(image));
}
});
}
// Pack images
{
logger_->trace("Packing {} images", images.size());
// Optimal number of nodes = width
stbrp_context stbrpContext;
std::vector<stbrp_node> stbrpNodes(width);
stbrp_init_target(&stbrpContext,
static_cast<int>(width),
static_cast<int>(height),
stbrpNodes.data(),
static_cast<int>(stbrpNodes.size()));
// Pack loaded textures
stbrp_pack_rects(
&stbrpContext, stbrpRects.data(), static_cast<int>(stbrpRects.size()));
}
// Lock atlas
std::unique_lock lock(p->atlasMutex_);
// Clear index
p->atlasMap_.clear();
// Clear atlas
p->atlas_.recreate(width, height);
boost::gil::rgba8_view_t atlasView = boost::gil::view(p->atlas_);
boost::gil::fill_pixels(atlasView,
boost::gil::rgba8_pixel_t {255, 0, 255, 255});
// Populate atlas
logger_->trace("Populating atlas");
const float xStep = 1.0f / width;
const float yStep = 1.0f / height;
const float xMin = xStep * 0.5f;
const float yMin = yStep * 0.5f;
for (size_t i = 0; i < images.size(); i++)
{
// If the image was packed successfully
if (stbrpRects[i].was_packed != 0)
{
// Populate the atlas
boost::gil::rgba8c_view_t imageView =
boost::gil::const_view(images[i].second);
boost::gil::rgba8_view_t atlasSubView =
boost::gil::subimage_view(atlasView,
stbrpRects[i].x,
stbrpRects[i].y,
imageView.width(),
imageView.height());
boost::gil::copy_pixels(imageView, atlasSubView);
// Add texture image to the index
const stbrp_coord x = stbrpRects[i].x;
const stbrp_coord y = stbrpRects[i].y;
const float sLeft = x * xStep + xMin;
const float sRight =
sLeft + static_cast<float>(imageView.width() - 1) / width;
const float tTop = y * yStep + yMin;
const float tBottom =
tTop + static_cast<float>(imageView.height() - 1) / height;
p->atlasMap_.emplace(
std::piecewise_construct,
std::forward_as_tuple(images[i].first),
std::forward_as_tuple(
boost::gil::point_t {x, y},
boost::gil::point_t {imageView.width(), imageView.height()},
sLeft,
sRight,
tTop,
tBottom));
}
else
{
logger_->warn("Unable to pack texture: {}", images[i].first);
}
}
}
GLuint TextureAtlas::BufferAtlas(gl::OpenGLFunctions& gl)
{
GLuint texture = GL_INVALID_INDEX;
std::shared_lock lock(p->atlasMutex_);
if (p->atlas_.width() > 0u && p->atlas_.height() > 0u)
{
boost::gil::rgba8_view_t view = boost::gil::view(p->atlas_);
std::vector<boost::gil::rgba8_pixel_t> pixelData(view.width() *
view.height());
boost::gil::copy_pixels(
view,
boost::gil::interleaved_view(view.width(),
view.height(),
pixelData.data(),
view.width() *
sizeof(boost::gil::rgba8_pixel_t)));
lock.unlock();
gl.glGenTextures(1, &texture);
gl.glBindTexture(GL_TEXTURE_2D, texture);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
view.width(),
view.height(),
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixelData.data());
}
return texture;
}
TextureAttributes TextureAtlas::GetTextureAttributes(const std::string& name)
{
TextureAttributes attr {};
std::shared_lock lock(p->atlasMutex_);
const auto& it = p->atlasMap_.find(name);
if (it != p->atlasMap_.cend())
{
attr = it->second;
}
return attr;
}
boost::gil::rgba8_image_t
TextureAtlas::Impl::LoadImage(const std::string& imagePath)
{
logger_->debug("Loading image: {}", imagePath);
boost::gil::rgba8_image_t image;
QFile imageFile(imagePath.c_str());
imageFile.open(QIODevice::ReadOnly);
if (!imageFile.isOpen())
{
logger_->error("Could not open image: {}", imagePath);
return std::move(image);
}
boost::iostreams::stream<util::IoDeviceSource> dataStream(imageFile);
boost::gil::image<boost::gil::rgba8_pixel_t, false> x;
try
{
boost::gil::read_and_convert_image(
dataStream, image, boost::gil::png_tag());
}
catch (const std::exception& ex)
{
logger_->error("Error reading image: {}", ex.what());
return std::move(image);
}
return std::move(image);
}
TextureAtlas& TextureAtlas::Instance()
{
static TextureAtlas instance_ {};
return instance_;
}
} // namespace util
} // namespace qt
} // namespace scwx

View file

@ -0,0 +1,83 @@
#pragma once
#include <scwx/qt/gl/gl.hpp>
#include <memory>
#include <string>
#include <boost/gil/point.hpp>
namespace scwx
{
namespace qt
{
namespace util
{
struct TextureAttributes
{
TextureAttributes() :
valid_ {false},
position_ {},
size_ {},
sLeft_ {},
sRight_ {},
tTop_ {},
tBottom_ {}
{
}
TextureAttributes(boost::gil::point_t position,
boost::gil::point_t size,
float sLeft,
float sRight,
float tTop,
float tBottom) :
valid_ {true},
position_ {position},
size_ {size},
sLeft_ {sLeft},
sRight_ {sRight},
tTop_ {tTop},
tBottom_ {tBottom}
{
}
bool valid_;
boost::gil::point_t position_;
boost::gil::point_t size_;
float sLeft_;
float sRight_;
float tTop_;
float tBottom_;
};
class TextureAtlas
{
public:
explicit TextureAtlas();
~TextureAtlas();
TextureAtlas(const TextureAtlas&) = delete;
TextureAtlas& operator=(const TextureAtlas&) = delete;
TextureAtlas(TextureAtlas&&) noexcept;
TextureAtlas& operator=(TextureAtlas&&) noexcept;
static TextureAtlas& Instance();
void RegisterTexture(const std::string& name, const std::string& path);
void BuildAtlas(size_t width, size_t height);
GLuint BufferAtlas(gl::OpenGLFunctions& gl);
TextureAttributes GetTextureAttributes(const std::string& name);
private:
class Impl;
std::unique_ptr<Impl> p;
};
} // namespace util
} // namespace qt
} // namespace scwx

View file

@ -13,6 +13,8 @@ struct Coordinate
double latitude_; ///< Latitude in degrees
double longitude_; ///< Longitude in degrees
Coordinate() : Coordinate(0.0, 0.0) {}
Coordinate(double latitude, double longitude) :
latitude_ {latitude}, longitude_ {longitude}
{

View file

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <utility>
namespace scwx
{
namespace util
{
template<class Key>
struct hash;
template<>
struct hash<std::pair<std::string, std::string>>
{
size_t operator()(const std::pair<std::string, std::string>& x) const;
};
} // namespace util
} // namespace scwx

View file

@ -0,0 +1,20 @@
#include <scwx/util/hash.hpp>
#include <boost/container_hash/hash.hpp>
namespace scwx
{
namespace util
{
size_t hash<std::pair<std::string, std::string>>::operator()(
const std::pair<std::string, std::string>& x) const
{
size_t seed = 0;
boost::hash_combine(seed, x.first);
boost::hash_combine(seed, x.second);
return seed;
}
} // namespace util
} // namespace scwx

View file

@ -46,6 +46,7 @@ set(SRC_PROVIDER source/scwx/provider/aws_level2_data_provider.cpp
source/scwx/provider/nexrad_data_provider_factory.cpp)
set(HDR_UTIL include/scwx/util/environment.hpp
include/scwx/util/float.hpp
include/scwx/util/hash.hpp
include/scwx/util/iterator.hpp
include/scwx/util/logger.hpp
include/scwx/util/map.hpp
@ -56,6 +57,7 @@ set(HDR_UTIL include/scwx/util/environment.hpp
include/scwx/util/vectorbuf.hpp)
set(SRC_UTIL source/scwx/util/environment.cpp
source/scwx/util/float.cpp
source/scwx/util/hash.cpp
source/scwx/util/logger.cpp
source/scwx/util/rangebuf.cpp
source/scwx/util/streams.cpp