Skip to content

Commit f6a1c55

Browse files
committed
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
1 parent 4ea4bf5 commit f6a1c55

9 files changed

Lines changed: 319 additions & 193 deletions

File tree

src/display/MapCanvasData.cpp

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
#include <glm/gtc/epsilon.hpp>
1313

14+
#include <QNativeGestureEvent>
1415
#include <QPointF>
16+
#include <QTouchEvent>
1517

1618
const MMapper::Array<RoomTintEnum, NUM_ROOM_TINTS> &getAllRoomTints()
1719
{
@@ -57,6 +59,12 @@ std::optional<glm::vec3> MapCanvasViewport::project(const glm::vec3 &v) const
5759
//
5860
// output: world coordinates.
5961
glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
62+
{
63+
return unproject_raw(mouse_depth, m_viewProj);
64+
}
65+
66+
glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth,
67+
const glm::mat4 &viewProj) const
6068
{
6169
const float depth = mouse_depth.z;
6270
assert(::isClamped(depth, 0.f, 1.f));
@@ -67,7 +75,7 @@ glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
6775
const glm::vec3 screen{screen2d, depth};
6876
const auto ndc = screen * 2.f - 1.f;
6977

70-
const auto tmp = glm::inverse(m_viewProj) * glm::vec4(ndc, 1.f);
78+
const auto tmp = glm::inverse(viewProj) * glm::vec4(ndc, 1.f);
7179
// clamp to avoid division by zero
7280
constexpr float limit = 1e-6f;
7381
const auto w = (std::abs(tmp.w) < limit) ? std::copysign(limit, tmp.w) : tmp.w;
@@ -79,12 +87,18 @@ glm::vec3 MapCanvasViewport::unproject_raw(const glm::vec3 &mouse_depth) const
7987
// note: the returned coordinate may not be visible,
8088
// because it could be
8189
glm::vec3 MapCanvasViewport::unproject_clamped(const glm::vec2 &mouse) const
90+
{
91+
return unproject_clamped(mouse, m_viewProj);
92+
}
93+
94+
glm::vec3 MapCanvasViewport::unproject_clamped(const glm::vec2 &mouse,
95+
const glm::mat4 &viewProj) const
8296
{
8397
const auto flayer = static_cast<float>(m_currentLayer);
8498
const auto &x = mouse.x;
8599
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
100+
const auto a = unproject_raw(glm::vec3{x, y, 0.f}, viewProj); // near
101+
const auto b = unproject_raw(glm::vec3{x, y, 1.f}, viewProj); // far
88102
const float t = (flayer - a.z) / (b.z - a.z);
89103
const auto result = glm::mix(a, b, std::clamp(t, 0.f, 1.f));
90104
return glm::vec3{glm::vec2{result}, flayer};
@@ -93,13 +107,30 @@ glm::vec3 MapCanvasViewport::unproject_clamped(const glm::vec2 &mouse) const
93107
glm::vec2 MapCanvasViewport::getMouseCoords(const QInputEvent *const event) const
94108
{
95109
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());
110+
const auto x = static_cast<float>(mouse->position().x());
111+
const auto y = static_cast<float>(height() - mouse->position().y());
98112
return glm::vec2{x, y};
99113
} else if (const auto *const wheel = dynamic_cast<const QWheelEvent *>(event)) {
100114
const auto x = static_cast<float>(wheel->position().x());
101115
const auto y = static_cast<float>(height() - wheel->position().y());
102116
return glm::vec2{x, y};
117+
} else if (const auto *const gesture = dynamic_cast<const QNativeGestureEvent *>(event)) {
118+
const auto x = static_cast<float>(gesture->position().x());
119+
const auto y = static_cast<float>(height() - gesture->position().y());
120+
return glm::vec2{x, y};
121+
} else if (const auto *const touch = dynamic_cast<const QTouchEvent *>(event)) {
122+
const auto &points = touch->points();
123+
if (points.isEmpty()) {
124+
throw std::invalid_argument("no touch points");
125+
}
126+
QPointF centroid(0, 0);
127+
for (const auto &p : points) {
128+
centroid += p.position();
129+
}
130+
centroid /= static_cast<qreal>(points.size());
131+
const auto x = static_cast<float>(centroid.x());
132+
const auto y = static_cast<float>(height() - centroid.y());
133+
return glm::vec2{x, y};
103134
} else {
104135
throw std::invalid_argument("event");
105136
}
@@ -109,7 +140,16 @@ glm::vec2 MapCanvasViewport::getMouseCoords(const QInputEvent *const event) cons
109140
// output is the world space intersection with the current layer
110141
std::optional<glm::vec3> MapCanvasViewport::unproject(const QInputEvent *const event) const
111142
{
112-
const auto xy = getMouseCoords(event);
143+
try {
144+
const auto xy = getMouseCoords(event);
145+
return unproject(xy);
146+
} catch (const std::invalid_argument &) {
147+
return std::nullopt;
148+
}
149+
}
150+
151+
std::optional<glm::vec3> MapCanvasViewport::unproject(const glm::vec2 &xy) const
152+
{
113153
// We don't actually know the depth we're trying to unproject;
114154
// technically we're solving for a ray, so we should unproject
115155
// two different depths and find where the ray intersects the
@@ -130,7 +170,17 @@ std::optional<glm::vec3> MapCanvasViewport::unproject(const QInputEvent *const e
130170

131171
std::optional<MouseSel> MapCanvasViewport::getUnprojectedMouseSel(const QInputEvent *const event) const
132172
{
133-
const auto opt_v = unproject(event);
173+
try {
174+
const auto xy = getMouseCoords(event);
175+
return getUnprojectedMouseSel(xy);
176+
} catch (const std::invalid_argument &) {
177+
return std::nullopt;
178+
}
179+
}
180+
181+
std::optional<MouseSel> MapCanvasViewport::getUnprojectedMouseSel(const glm::vec2 &xy) const
182+
{
183+
const auto opt_v = unproject(xy);
134184
if (!opt_v.has_value()) {
135185
return std::nullopt;
136186
}

src/display/MapCanvasData.h

Lines changed: 12 additions & 22 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,26 +96,29 @@ 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;
121+
NODISCARD std::optional<MouseSel> getUnprojectedMouseSel(const glm::vec2 &xy) const;
132122
NODISCARD glm::vec2 getMouseCoords(const QInputEvent *event) const;
133123
};
134124

0 commit comments

Comments
 (0)