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