]> git.sur5r.net Git - fstl/blob - src/canvas.cpp
New upstream version 0.10.0
[fstl] / src / canvas.cpp
1 #include <QMouseEvent>
2
3 #include <cmath>
4
5 #include "canvas.h"
6 #include "backdrop.h"
7 #include "axis.h"
8 #include "glmesh.h"
9 #include "mesh.h"
10
11 const float Canvas::P_PERSPECTIVE = 0.25f;
12 const float Canvas::P_ORTHOGRAPHIC = 0.0f;
13
14 Canvas::Canvas(const QSurfaceFormat& format, QWidget *parent)
15     : QOpenGLWidget(parent), mesh(nullptr),
16       scale(1), zoom(1), tilt(90), yaw(0),
17       anim(this, "perspective"), status(" "),
18       meshInfo("")
19 {
20     setFormat(format);
21     QFile styleFile(":/qt/style.qss");
22     styleFile.open( QFile::ReadOnly );
23     setStyleSheet(styleFile.readAll());
24
25     anim.setDuration(100);
26 }
27
28 Canvas::~Canvas()
29 {
30     makeCurrent();
31     delete mesh;
32     delete mesh_vertshader;
33     delete backdrop;
34     delete axis;
35     doneCurrent();
36 }
37
38 void Canvas::view_anim(float v)
39 {
40     anim.setStartValue(perspective);
41     anim.setEndValue(v);
42     anim.start();
43 }
44
45 void Canvas::view_perspective(float p, bool animate){
46     if(animate)
47     {
48         view_anim(p);
49     }
50     else
51     {
52         set_perspective(p);
53     }
54 }
55
56 void Canvas::draw_axes(bool d)
57 {
58     drawAxes = d;
59     update();
60 }
61
62 void Canvas::invert_zoom(bool d)
63 {
64     invertZoom = d;
65     update();
66 }
67
68 void Canvas::load_mesh(Mesh* m, bool is_reload)
69 {
70     delete mesh;
71     mesh = new GLMesh(m);
72     QVector3D lower(m->xmin(), m->ymin(), m->zmin());
73     QVector3D upper(m->xmax(), m->ymax(), m->zmax());
74     if (!is_reload)
75     {
76         center = (lower + upper) / 2;
77         scale = 2 / (upper - lower).length();
78
79         // Reset other camera parameters
80         zoom = 1;
81         yaw = 0;
82         tilt = 90;
83     }
84     meshInfo = QStringLiteral("Triangles: %1\nX: [%2, %3]\nY: [%4, %5]\nZ: [%6, %7]").arg(m->triCount());
85     for(int dIdx = 0; dIdx < 3; dIdx++) meshInfo = meshInfo.arg(lower[dIdx]).arg(upper[dIdx]);
86     axis->setScale(lower, upper);
87     update();
88
89     delete m;
90 }
91
92 void Canvas::set_status(const QString &s)
93 {
94     status = s;
95     update();
96 }
97
98 void Canvas::set_perspective(float p)
99 {
100     perspective = p;
101     update();
102 }
103
104 void Canvas::set_drawMode(enum DrawMode mode)
105 {
106     drawMode = mode;
107     update();
108 }
109
110 void Canvas::clear_status()
111 {
112     status = "";
113     update();
114 }
115
116 void Canvas::initializeGL()
117 {
118     initializeOpenGLFunctions();
119
120     mesh_vertshader = new QOpenGLShader(QOpenGLShader::Vertex);
121     mesh_vertshader->compileSourceFile(":/gl/mesh.vert");
122     mesh_shader.addShader(mesh_vertshader);
123     mesh_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh.frag");
124     mesh_shader.link();
125     mesh_wireframe_shader.addShader(mesh_vertshader);
126     mesh_wireframe_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_wireframe.frag");
127     mesh_wireframe_shader.link();
128     mesh_surfaceangle_shader.addShader(mesh_vertshader);
129     mesh_surfaceangle_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_surfaceangle.frag");
130     mesh_surfaceangle_shader.link();
131
132     backdrop = new Backdrop();
133     axis = new Axis();
134 }
135
136
137 void Canvas::paintGL()
138 {
139     glClearColor(0.0, 0.0, 0.0, 0.0);
140     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
141     glEnable(GL_DEPTH_TEST);
142     backdrop->draw();
143     if (mesh)  draw_mesh();
144     if (drawAxes) axis->draw(transform_matrix(), view_matrix(),
145         orient_matrix(), aspect_matrix(), width() / float(height()));
146
147     QPainter painter(this);
148     painter.setRenderHint(QPainter::Antialiasing);
149     float textHeight = painter.fontInfo().pointSize();
150     if (drawAxes) painter.drawText(QRect(10, textHeight, width(), height()), meshInfo);
151     painter.drawText(10, height() - textHeight, status);
152 }
153
154 void Canvas::draw_mesh()
155 {
156     QOpenGLShaderProgram* selected_mesh_shader = NULL;
157     if(drawMode == wireframe)
158     {
159         selected_mesh_shader = &mesh_wireframe_shader;
160         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
161     }
162     else
163     {
164         if(drawMode == shaded)
165         {
166             selected_mesh_shader = &mesh_shader;
167         }
168         else
169         {
170             selected_mesh_shader = &mesh_surfaceangle_shader;
171         }
172         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
173     }
174
175     selected_mesh_shader->bind();
176
177     // Load the transform and view matrices into the shader
178     glUniformMatrix4fv(
179                 selected_mesh_shader->uniformLocation("transform_matrix"),
180                 1, GL_FALSE, transform_matrix().data());
181     glUniformMatrix4fv(
182                 selected_mesh_shader->uniformLocation("view_matrix"),
183                 1, GL_FALSE, view_matrix().data());
184
185     // Compensate for z-flattening when zooming
186     glUniform1f(selected_mesh_shader->uniformLocation("zoom"), 1/zoom);
187
188     // Find and enable the attribute location for vertex position
189     const GLuint vp = selected_mesh_shader->attributeLocation("vertex_position");
190     glEnableVertexAttribArray(vp);
191
192     // Then draw the mesh with that vertex position
193     mesh->draw(vp);
194
195     // Reset draw mode for the background and anything else that needs to be drawn
196     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
197
198     // Clean up state machine
199     glDisableVertexAttribArray(vp);
200     selected_mesh_shader->release();
201 }
202 QMatrix4x4 Canvas::orient_matrix() const
203 {
204     QMatrix4x4 m;
205     m.rotate(tilt, QVector3D(1, 0, 0));
206     m.rotate(yaw,  QVector3D(0, 0, 1));
207     //We want the x axis to the right, and the z axis up
208     m.scale(-1, 1, -1);
209     return m;
210 }
211 QMatrix4x4 Canvas::transform_matrix() const
212 {
213     QMatrix4x4 m = orient_matrix();
214     m.scale(scale);
215     m.translate(-center);
216     return m;
217 }
218 QMatrix4x4 Canvas::aspect_matrix() const
219 {
220     QMatrix4x4 m;
221     if (width() > height())
222     {
223         m.scale(-height() / float(width()), 1, 0.5);
224     }
225     else
226     {
227         m.scale(-1, width() / float(height()), 0.5);
228     }
229     return m;
230 }
231 QMatrix4x4 Canvas::view_matrix() const
232 {
233     QMatrix4x4 m = aspect_matrix();
234     m.scale(zoom, zoom, 1);
235     m(3, 2) = perspective;
236     return m;
237 }
238
239 void Canvas::mousePressEvent(QMouseEvent* event)
240 {
241     if (event->button() == Qt::LeftButton ||
242         event->button() == Qt::RightButton)
243     {
244         mouse_pos = event->pos();
245         setCursor(Qt::ClosedHandCursor);
246     }
247 }
248
249 void Canvas::mouseReleaseEvent(QMouseEvent* event)
250 {
251     if (event->button() == Qt::LeftButton ||
252         event->button() == Qt::RightButton)
253     {
254         unsetCursor();
255     }
256 }
257
258 void Canvas::mouseMoveEvent(QMouseEvent* event)
259 {
260     auto p = event->pos();
261     auto d = p - mouse_pos;
262
263
264     if (event->buttons() & Qt::LeftButton)
265     {
266         yaw = fmod(yaw - d.x(), 360);
267         tilt = fmod(tilt - d.y(), 360);
268         update();
269     }
270     else if (event->buttons() & Qt::RightButton)
271     {
272         center = transform_matrix().inverted() *
273                  view_matrix().inverted() *
274                  QVector3D(-d.x() / (0.5*width()),
275                             d.y() / (0.5*height()), 0);
276         update();
277     }
278     mouse_pos = p;
279 }
280
281 void Canvas::wheelEvent(QWheelEvent *event)
282 {
283     // Find GL position before the zoom operation
284     // (to zoom about mouse cursor)
285     auto p = event->position();
286     QVector3D v(1 - p.x() / (0.5*width()),
287                 p.y() / (0.5*height()) - 1, 0);
288     QVector3D a = transform_matrix().inverted() *
289                   view_matrix().inverted() * v;
290
291     if (event->angleDelta().y() < 0)
292     {
293         for (int i=0; i > event->angleDelta().y(); --i)
294             if (invertZoom)
295                 zoom /= 1.001;
296             else 
297                 zoom *= 1.001;
298     }
299     else if (event->angleDelta().y() > 0)
300     {
301         for (int i=0; i < event->angleDelta().y(); ++i)
302             if (invertZoom) 
303                 zoom *= 1.001;
304             else 
305                 zoom /= 1.001;
306     }
307
308     // Then find the cursor's GL position post-zoom and adjust center.
309     QVector3D b = transform_matrix().inverted() *
310                   view_matrix().inverted() * v;
311     center += b - a;
312     update();
313 }
314
315 void Canvas::resizeGL(int width, int height)
316 {
317     glViewport(0, 0, width, height);
318 }