Skip to content

Commit 7b6ca51

Browse files
nschimmeKasparMetsa
andcommitted
refactor MapCanvas to QOpenGLWindow for better WebGL2 support
- Added platform-specific gesture support for macOS and touch devices - Implemented smooth world-coordinate dragging and zoom Co-authored-by: KasparMetsa <kasparmetsa@gmail.com>
1 parent 4ea4bf5 commit 7b6ca51

10 files changed

Lines changed: 449 additions & 252 deletions

File tree

src/display/MapCanvasData.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66

77
#include "../opengl/LineRendering.h"
88

9+
#include <cassert>
910
#include <cmath>
1011
#include <optional>
1112

1213
#include <glm/gtc/epsilon.hpp>
1314

15+
#include <QDebug>
16+
#include <QNativeGestureEvent>
1417
#include <QPointF>
18+
#include <QTouchEvent>
1519

1620
const MMapper::Array<RoomTintEnum, NUM_ROOM_TINTS> &getAllRoomTints()
1721
{
@@ -57,6 +61,12 @@ std::optional<glm::vec3> MapCanvasViewport::project(const glm::vec3 &v) const
5761
//
5862
// output: world coordinates.
5963
glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
64+
{
65+
return unproject_raw(mouse_depth, m_viewProj);
66+
}
67+
68+
glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth,
69+
const glm::mat4 &viewProj) const
6070
{
6171
const float depth = mouse_depth.z;
6272
assert(::isClamped(depth, 0.f, 1.f));
@@ -67,7 +77,7 @@ glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
6777
const glm::vec3 screen{screen2d, depth};
6878
const auto ndc = screen * 2.f - 1.f;
6979

70-
const auto tmp = glm::inverse(m_viewProj) * glm::vec4(ndc, 1.f);
80+
const auto tmp = glm::inverse(viewProj) * glm::vec4(ndc, 1.f);
7181
// clamp to avoid division by zero
7282
constexpr float limit = 1e-6f;
7383
const auto w = (std::abs(tmp.w) < limit) ? std::copysign(limit, tmp.w) : tmp.w;
@@ -79,29 +89,54 @@ glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
7989
// note: the returned coordinate may not be visible,
8090
// because it could be
8191
glm::vec3 MapCanvasViewport::unproject_clamped(const glm::vec2 &mouse) const
92+
{
93+
return unproject_clamped(mouse, m_viewProj);
94+
}
95+
96+
glm::vec3 MapCanvasViewport::unproject_clamped(const glm::vec2 &mouse,
97+
const glm::mat4 &viewProj) const
8298
{
8399
const auto flayer = static_cast<float>(m_currentLayer);
84100
const auto &x = mouse.x;
85101
const auto &y = mouse.y;
86-
const auto a = unproject_raw(glm::vec3{x, y, 0.f}); // near
87-
const auto b = unproject_raw(glm::vec3{x, y, 1.f}); // far
102+
const auto a = unproject_raw(glm::vec3{x, y, 0.f}, viewProj); // near
103+
const auto b = unproject_raw(glm::vec3{x, y, 1.f}, viewProj); // far
88104
const float t = (flayer - a.z) / (b.z - a.z);
89105
const auto result = glm::mix(a, b, std::clamp(t, 0.f, 1.f));
90106
return glm::vec3{glm::vec2{result}, flayer};
91107
}
92108

93-
glm::vec2 MapCanvasViewport::getMouseCoords(const QInputEvent *const event) const
109+
std::optional<glm::vec2> MapCanvasViewport::getMouseCoords(const QInputEvent *const event) const
94110
{
95111
if (const auto *const mouse = dynamic_cast<const QMouseEvent *>(event)) {
96-
const auto x = static_cast<float>(mouse->pos().x());
97-
const auto y = static_cast<float>(height() - mouse->pos().y());
112+
const auto x = static_cast<float>(mouse->position().x());
113+
const auto y = static_cast<float>(height() - mouse->position().y());
98114
return glm::vec2{x, y};
99115
} else if (const auto *const wheel = dynamic_cast<const QWheelEvent *>(event)) {
100116
const auto x = static_cast<float>(wheel->position().x());
101117
const auto y = static_cast<float>(height() - wheel->position().y());
102118
return glm::vec2{x, y};
119+
} else if (const auto *const gesture = dynamic_cast<const QNativeGestureEvent *>(event)) {
120+
const auto x = static_cast<float>(gesture->position().x());
121+
const auto y = static_cast<float>(height() - gesture->position().y());
122+
return glm::vec2{x, y};
123+
} else if (const auto *const touch = dynamic_cast<const QTouchEvent *>(event)) {
124+
const auto &points = touch->points();
125+
if (points.isEmpty()) {
126+
return std::nullopt;
127+
}
128+
QPointF centroid(0, 0);
129+
for (const auto &p : points) {
130+
centroid += p.position();
131+
}
132+
centroid /= static_cast<qreal>(points.size());
133+
const auto x = static_cast<float>(centroid.x());
134+
const auto y = static_cast<float>(height() - centroid.y());
135+
return glm::vec2{x, y};
103136
} else {
104-
throw std::invalid_argument("event");
137+
qWarning() << "MapCanvasViewport::getMouseCoords: unhandled event type" << event->type();
138+
assert(false);
139+
return std::nullopt;
105140
}
106141
}
107142

@@ -110,6 +145,14 @@ glm::vec2 MapCanvasViewport::getMouseCoords(const QInputEvent *const event) cons
110145
std::optional<glm::vec3> MapCanvasViewport::unproject(const QInputEvent *const event) const
111146
{
112147
const auto xy = getMouseCoords(event);
148+
if (!xy) {
149+
return std::nullopt;
150+
}
151+
return unproject(*xy);
152+
}
153+
154+
std::optional<glm::vec3> MapCanvasViewport::unproject(const glm::vec2 &xy) const
155+
{
113156
// We don't actually know the depth we're trying to unproject;
114157
// technically we're solving for a ray, so we should unproject
115158
// two different depths and find where the ray intersects the
@@ -130,7 +173,16 @@ std::optional<glm::vec3> MapCanvasViewport::unproject(const QInputEvent *const e
130173

131174
std::optional<MouseSel> MapCanvasViewport::getUnprojectedMouseSel(const QInputEvent *const event) const
132175
{
133-
const auto opt_v = unproject(event);
176+
const auto xy = getMouseCoords(event);
177+
if (!xy) {
178+
return std::nullopt;
179+
}
180+
return getUnprojectedMouseSel(*xy);
181+
}
182+
183+
std::optional<MouseSel> MapCanvasViewport::getUnprojectedMouseSel(const glm::vec2 &xy) const
184+
{
185+
const auto opt_v = unproject(xy);
134186
if (!opt_v.has_value()) {
135187
return std::nullopt;
136188
}

src/display/MapCanvasData.h

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <unordered_map>
2121

2222
#include <QOpenGLTexture>
23-
#include <QWidget>
23+
#include <QWindow>
2424
#include <QtGui/QMatrix4x4>
2525
#include <QtGui/QMouseEvent>
2626
#include <QtGui/qopengl.h>
@@ -48,8 +48,6 @@ struct NODISCARD ScaleFactor final
4848

4949
private:
5050
float m_scaleFactor = 1.f;
51-
// pinch gesture
52-
float m_pinchFactor = 1.f;
5351

5452
private:
5553
NODISCARD static float clamp(float x)
@@ -63,21 +61,10 @@ struct NODISCARD ScaleFactor final
6361

6462
public:
6563
NODISCARD float getRaw() const { return clamp(m_scaleFactor); }
66-
NODISCARD float getTotal() const { return clamp(m_scaleFactor * m_pinchFactor); }
64+
NODISCARD float getTotal() const { return clamp(m_scaleFactor); }
6765

6866
public:
6967
void set(const float scale) { m_scaleFactor = clamp(scale); }
70-
void setPinch(const float pinch)
71-
{
72-
// Don't bother to clamp this, since the total is clamped.
73-
m_pinchFactor = pinch;
74-
}
75-
void endPinch()
76-
{
77-
const float total = getTotal();
78-
m_scaleFactor = total;
79-
m_pinchFactor = 1.f;
80-
}
8168
void reset() { *this = ScaleFactor(); }
8269

8370
public:
@@ -100,7 +87,7 @@ struct NODISCARD ScaleFactor final
10087
struct NODISCARD MapCanvasViewport
10188
{
10289
private:
103-
QWidget &m_sizeWidget;
90+
QWindow &m_window;
10491

10592
public:
10693
glm::mat4 m_viewProj{1.f};
@@ -109,27 +96,30 @@ struct NODISCARD MapCanvasViewport
10996
int m_currentLayer = 0;
11097

11198
public:
112-
explicit MapCanvasViewport(QWidget &sizeWidget)
113-
: m_sizeWidget{sizeWidget}
99+
explicit MapCanvasViewport(QWindow &window)
100+
: m_window{window}
114101
{}
115102

116103
public:
117-
NODISCARD auto width() const { return m_sizeWidget.width(); }
118-
NODISCARD auto height() const { return m_sizeWidget.height(); }
104+
NODISCARD auto width() const { return m_window.width(); }
105+
NODISCARD auto height() const { return m_window.height(); }
119106
NODISCARD Viewport getViewport() const
120107
{
121-
const auto &r = m_sizeWidget.rect();
122-
return Viewport{glm::ivec2{r.x(), r.y()}, glm::ivec2{r.width(), r.height()}};
108+
return Viewport{glm::ivec2{0, 0}, glm::ivec2{m_window.width(), m_window.height()}};
123109
}
124110
NODISCARD float getTotalScaleFactor() const { return m_scaleFactor.getTotal(); }
125111

126112
public:
127113
NODISCARD std::optional<glm::vec3> project(const glm::vec3 &) const;
128114
NODISCARD glm::vec3 unproject_raw(const glm::vec3 &) const;
115+
NODISCARD glm::vec3 unproject_raw(const glm::vec3 &, const glm::mat4 &) const;
129116
NODISCARD glm::vec3 unproject_clamped(const glm::vec2 &) const;
117+
NODISCARD glm::vec3 unproject_clamped(const glm::vec2 &, const glm::mat4 &) const;
130118
NODISCARD std::optional<glm::vec3> unproject(const QInputEvent *event) const;
119+
NODISCARD std::optional<glm::vec3> unproject(const glm::vec2 &xy) const;
131120
NODISCARD std::optional<MouseSel> getUnprojectedMouseSel(const QInputEvent *event) const;
132-
NODISCARD glm::vec2 getMouseCoords(const QInputEvent *event) const;
121+
NODISCARD std::optional<MouseSel> getUnprojectedMouseSel(const glm::vec2 &xy) const;
122+
NODISCARD std::optional<glm::vec2> getMouseCoords(const QInputEvent *event) const;
133123
};
134124

135125
class NODISCARD MapScreen final

0 commit comments

Comments
 (0)