Upload to unstable
[fstl] / src / canvas.cpp
1 #include <QMouseEvent>
2 #include <QDebug>
3
4 #include <cmath>
5
6 #include "canvas.h"
7 #include "backdrop.h"
8 #include "glmesh.h"
9 #include "mesh.h"
10
11 Canvas::Canvas(const QGLFormat& format, QWidget *parent)
12     : QGLWidget(format, parent), mesh(NULL),
13       scale(1), zoom(1), tilt(90), yaw(0), status(" ")
14 {
15     // Nothing to do here
16 }
17
18 Canvas::~Canvas()
19 {
20     delete mesh;
21 }
22
23 void Canvas::load_mesh(Mesh* m)
24 {
25     mesh = new GLMesh(m);
26     center = QVector3D(m->xmin() + m->xmax(),
27                        m->ymin() + m->ymax(),
28                        m->zmin() + m->zmax()) / 2;
29     scale = 2 / sqrt(
30                 pow(m->xmax() - m->xmin(), 2) +
31                 pow(m->ymax() - m->ymin(), 2) +
32                 pow(m->zmax() - m->zmin(), 2));
33
34     // Reset other camera parameters
35     zoom = 1;
36     yaw = 0;
37     tilt = 90;
38
39     update();
40
41     delete m;
42 }
43
44 void Canvas::set_status(const QString &s)
45 {
46     status = s;
47     update();
48 }
49
50 void Canvas::clear_status()
51 {
52     status = "";
53     update();
54 }
55
56 void Canvas::initializeGL()
57 {
58     initializeGLFunctions();
59
60     mesh_shader.addShaderFromSourceFile(QGLShader::Vertex, ":/gl/mesh.vert");
61     mesh_shader.addShaderFromSourceFile(QGLShader::Fragment, ":/gl/mesh.frag");
62     mesh_shader.link();
63
64     backdrop = new Backdrop();
65 }
66
67 void Canvas::paintEvent(QPaintEvent *event)
68 {
69     Q_UNUSED(event);
70
71     glClearColor(0.0, 0.0, 0.0, 0.0);
72     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
73     glEnable(GL_DEPTH_TEST);
74
75     backdrop->draw();
76     if (mesh)  draw_mesh();
77
78     if (status.isNull())    return;
79
80     QPainter painter(this);
81     painter.setRenderHint(QPainter::Antialiasing);
82     painter.drawText(10, height() - 10, status);
83 }
84
85
86 void Canvas::draw_mesh()
87 {
88     mesh_shader.bind();
89
90     // Load the transform and view matrices into the shader
91     glUniformMatrix4fv(
92                 mesh_shader.uniformLocation("transform_matrix"),
93                 1, GL_FALSE, transform_matrix().data());
94     glUniformMatrix4fv(
95                 mesh_shader.uniformLocation("view_matrix"),
96                 1, GL_FALSE, view_matrix().data());
97
98     // Compensate for z-flattening when zooming
99     glUniform1f(mesh_shader.uniformLocation("zoom"), 1/zoom);
100
101     // Find and enable the attribute location for vertex position
102     const GLuint vp = mesh_shader.attributeLocation("vertex_position");
103     glEnableVertexAttribArray(vp);
104
105     // Then draw the mesh with that vertex position
106     mesh->draw(vp);
107
108     // Clean up state machine
109     glDisableVertexAttribArray(vp);
110     mesh_shader.release();
111 }
112
113 QMatrix4x4 Canvas::transform_matrix() const
114 {
115     QMatrix4x4 m;
116     m.rotate(tilt, QVector3D(1, 0, 0));
117     m.rotate(yaw,  QVector3D(0, 0, 1));
118     m.scale(scale);
119     m.translate(-center);
120     return m;
121 }
122
123 QMatrix4x4 Canvas::view_matrix() const
124 {
125     QMatrix4x4 m;
126     if (width() > height())
127     {
128         m.scale(-height() / float(width()), 1, 0.5);
129     }
130     else
131     {
132         m.scale(-1, width() / float(height()), 0.5);
133     }
134     m.scale(zoom, zoom, 1);
135     return m;
136 }
137
138 void Canvas::mousePressEvent(QMouseEvent* event)
139 {
140     if (event->button() == Qt::LeftButton ||
141         event->button() == Qt::RightButton)
142     {
143         mouse_pos = event->pos();
144         setCursor(Qt::ClosedHandCursor);
145     }
146 }
147
148 void Canvas::mouseReleaseEvent(QMouseEvent* event)
149 {
150     if (event->button() == Qt::LeftButton ||
151         event->button() == Qt::RightButton)
152     {
153         unsetCursor();
154     }
155 }
156
157 void Canvas::mouseMoveEvent(QMouseEvent* event)
158 {
159     auto p = event->pos();
160     auto d = p - mouse_pos;
161
162     if (event->buttons() & Qt::LeftButton)
163     {
164         yaw = fmod(yaw - d.x(), 360);
165         tilt = fmax(0, fmin(180, tilt - d.y()));
166         update();
167     }
168     else if (event->buttons() & Qt::RightButton)
169     {
170         center = transform_matrix().inverted() *
171                  view_matrix().inverted() *
172                  QVector3D(-d.x() / (0.5*width()),
173                             d.y() / (0.5*height()), 0);
174         update();
175     }
176     mouse_pos = p;
177 }
178
179 void Canvas::wheelEvent(QWheelEvent *event)
180 {
181     // Find GL position before the zoom operation
182     // (to zoom about mouse cursor)
183     auto p = event->pos();
184     QVector3D v(1 - p.x() / (0.5*width()),
185                 p.y() / (0.5*height()) - 1, 0);
186     QVector3D a = transform_matrix().inverted() *
187                   view_matrix().inverted() * v;
188
189     if (event->delta() < 0)
190     {
191         for (int i=0; i > event->delta(); --i)
192             zoom *= 1.001;
193     }
194     else if (event->delta() > 0)
195     {
196         for (int i=0; i < event->delta(); ++i)
197             zoom /= 1.001;
198     }
199
200     // Then find the cursor's GL position post-zoom and adjust center.
201     QVector3D b = transform_matrix().inverted() *
202                   view_matrix().inverted() * v;
203     center += b - a;
204     update();
205 }