#include #include #include namespace scwx::qt::gl::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; static constexpr size_t VERTICES_PER_TRIANGLE = 3; static constexpr size_t VERTICES_PER_RECTANGLE = VERTICES_PER_TRIANGLE * 2; static constexpr size_t POINTS_PER_VERTEX = 7; static constexpr size_t BUFFER_LENGTH = NUM_TRIANGLES * VERTICES_PER_TRIANGLE * POINTS_PER_VERTEX; class Rectangle::Impl { public: explicit Impl(std::shared_ptr context) : context_ {std::move(context)}, dirty_ {false}, visible_ {true}, x_ {0.0f}, y_ {0.0f}, z_ {0.0f}, width_ {0.0f}, height_ {0.0f}, borderWidth_ {0.0f}, borderColor_ {0, 0, 0, 0}, fillColor_ {std::nullopt}, shaderProgram_ {nullptr}, uMVPMatrixLocation_(GL_INVALID_INDEX), vao_ {GL_INVALID_INDEX}, vbo_ {GL_INVALID_INDEX} { } ~Impl() = default; Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; Impl(const Impl&&) = delete; Impl& operator=(const Impl&&) = delete; std::shared_ptr context_; bool dirty_; bool visible_; float x_; float y_; float z_; float width_; float height_; float borderWidth_; boost::gil::rgba8_pixel_t borderColor_; std::optional fillColor_; std::shared_ptr shaderProgram_; GLint uMVPMatrixLocation_; GLuint vao_; GLuint vbo_; void Update(); }; Rectangle::Rectangle(std::shared_ptr context) : DrawItem(), p(std::make_unique(context)) { } Rectangle::~Rectangle() = default; Rectangle::Rectangle(Rectangle&&) noexcept = default; Rectangle& Rectangle::operator=(Rectangle&&) noexcept = default; void Rectangle::Initialize() { p->shaderProgram_ = p->context_->GetShaderProgram(":/gl/color.vert", ":/gl/color.frag"); p->uMVPMatrixLocation_ = glGetUniformLocation(p->shaderProgram_->id(), "uMVPMatrix"); if (p->uMVPMatrixLocation_ == -1) { logger_->warn("Could not find uMVPMatrix"); } glGenVertexArrays(1, &p->vao_); glGenBuffers(1, &p->vbo_); glBindVertexArray(p->vao_); glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); glBufferData( GL_ARRAY_BUFFER, sizeof(float) * BUFFER_LENGTH, nullptr, GL_DYNAMIC_DRAW); // NOLINTBEGIN(modernize-use-nullptr) // NOLINTBEGIN(performance-no-int-to-ptr) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, POINTS_PER_VERTEX * sizeof(float), static_cast(0)); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, POINTS_PER_VERTEX * sizeof(float), reinterpret_cast(3 * sizeof(float))); glEnableVertexAttribArray(1); // NOLINTEND(performance-no-int-to-ptr) // NOLINTEND(modernize-use-nullptr) p->dirty_ = true; } void Rectangle::Render(const QMapLibre::CustomLayerRenderParameters& params) { if (p->visible_) { glBindVertexArray(p->vao_); glBindBuffer(GL_ARRAY_BUFFER, p->vbo_); p->Update(); p->shaderProgram_->Use(); UseDefaultProjection(params, p->uMVPMatrixLocation_); if (p->fillColor_.has_value()) { // Draw fill // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) glDrawArrays(GL_TRIANGLES, 24, 6); } if (p->borderWidth_ > 0.0f) { // Draw border // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) glDrawArrays(GL_TRIANGLES, 0, 24); } } } void Rectangle::Deinitialize() { glDeleteVertexArrays(1, &p->vao_); glDeleteBuffers(1, &p->vbo_); } void Rectangle::SetBorder(float width, boost::gil::rgba8_pixel_t color) { if (p->borderWidth_ != width || p->borderColor_ != color) { p->borderWidth_ = width; p->borderColor_ = color; p->dirty_ = true; } } void Rectangle::SetFill(boost::gil::rgba8_pixel_t color) { if (p->fillColor_ != color) { p->fillColor_ = color; p->dirty_ = true; } } void Rectangle::SetPosition(float x, float y, float z) { if (p->x_ != x || p->y_ != y || p->z_ != z) { p->x_ = x; p->y_ = y; p->z_ = z; p->dirty_ = true; } } void Rectangle::SetSize(float width, float height) { if (p->width_ != width || p->height_ != height) { p->width_ = width; p->height_ = height; p->dirty_ = true; } } void Rectangle::SetVisible(bool visible) { p->visible_ = visible; } void Rectangle::Impl::Update() { if (dirty_) { const float lox = x_; const float rox = x_ + width_; const float boy = y_; const float toy = y_ + height_; const float lix = lox + borderWidth_; const float rix = rox - borderWidth_; const float biy = boy + borderWidth_; const float tiy = toy - borderWidth_; const float bc0 = borderColor_[0] / 255.0f; const float bc1 = borderColor_[1] / 255.0f; const float bc2 = borderColor_[2] / 255.0f; const float bc3 = borderColor_[3] / 255.0f; float fc0 = 0.0f; float fc1 = 0.0f; float fc2 = 0.0f; float fc3 = 0.0f; if (fillColor_.has_value()) { boost::gil::rgba8_pixel_t& fc = fillColor_.value(); fc0 = fc[0] / 255.0f; fc1 = fc[1] / 255.0f; fc2 = fc[2] / 255.0f; fc3 = fc[3] / 255.0f; } const float buffer[NUM_RECTANGLES][VERTICES_PER_RECTANGLE] [POINTS_PER_VERTEX] = // { // // Left Border { {lox, boy, z_, bc0, bc1, bc2, bc3}, // BL {lox, toy, z_, bc0, bc1, bc2, bc3}, // TL {lix, boy, z_, bc0, bc1, bc2, bc3}, // BR {lix, boy, z_, bc0, bc1, bc2, bc3}, // BR {lix, toy, z_, bc0, bc1, bc2, bc3}, // TR {lox, toy, z_, bc0, bc1, bc2, bc3} // TL }, // Right Border { {rox, boy, z_, bc0, bc1, bc2, bc3}, // BR {rox, toy, z_, bc0, bc1, bc2, bc3}, // TR {rix, boy, z_, bc0, bc1, bc2, bc3}, // BL {rix, boy, z_, bc0, bc1, bc2, bc3}, // BL {rix, toy, z_, bc0, bc1, bc2, bc3}, // TL {rox, toy, z_, bc0, bc1, bc2, bc3} // TR }, // Top Border { {lox, toy, z_, bc0, bc1, bc2, bc3}, // TL {rox, toy, z_, bc0, bc1, bc2, bc3}, // TR {rox, tiy, z_, bc0, bc1, bc2, bc3}, // BR {rox, tiy, z_, bc0, bc1, bc2, bc3}, // BR {lox, tiy, z_, bc0, bc1, bc2, bc3}, // BL {lox, toy, z_, bc0, bc1, bc2, bc3} // TL }, // Bottom Border { {lox, boy, z_, bc0, bc1, bc2, bc3}, // BL {rox, boy, z_, bc0, bc1, bc2, bc3}, // BR {rox, biy, z_, bc0, bc1, bc2, bc3}, // TR {rox, biy, z_, bc0, bc1, bc2, bc3}, // TR {lox, biy, z_, bc0, bc1, bc2, bc3}, // TL {lox, boy, z_, bc0, bc1, bc2, bc3} // BL }, // Fill { {lox, toy, z_, fc0, fc1, fc2, fc3}, // TL {rox, toy, z_, fc0, fc1, fc2, fc3}, // TR {rox, boy, z_, fc0, fc1, fc2, fc3}, // BR {rox, boy, z_, fc0, fc1, fc2, fc3}, // BR {lox, boy, z_, fc0, fc1, fc2, fc3}, // BL {lox, toy, z_, fc0, fc1, fc2, fc3} // TL }}; glBufferData(GL_ARRAY_BUFFER, sizeof(float) * BUFFER_LENGTH, static_cast(buffer), GL_DYNAMIC_DRAW); dirty_ = false; } } } // namespace scwx::qt::gl::draw