#include #include #include #include "canvas.h" #include "backdrop.h" #include "glmesh.h" #include "mesh.h" Canvas::Canvas(const QGLFormat& format, QWidget *parent) : QGLWidget(format, parent), mesh(NULL), scale(1), zoom(1), tilt(90), yaw(0), status(" ") { // Nothing to do here } Canvas::~Canvas() { delete mesh; } void Canvas::load_mesh(Mesh* m) { mesh = new GLMesh(m); center = QVector3D(m->xmin() + m->xmax(), m->ymin() + m->ymax(), m->zmin() + m->zmax()) / 2; scale = 2 / sqrt( pow(m->xmax() - m->xmin(), 2) + pow(m->ymax() - m->ymin(), 2) + pow(m->zmax() - m->zmin(), 2)); update(); delete m; } void Canvas::set_status(const QString &s) { status = s; update(); } void Canvas::clear_status() { status = ""; update(); } void Canvas::initializeGL() { initializeGLFunctions(); mesh_shader.addShaderFromSourceFile(QGLShader::Vertex, ":/gl/mesh.vert"); mesh_shader.addShaderFromSourceFile(QGLShader::Fragment, ":/gl/mesh.frag"); mesh_shader.link(); backdrop = new Backdrop(); } void Canvas::paintEvent(QPaintEvent *event) { Q_UNUSED(event); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); backdrop->draw(); if (mesh) draw_mesh(); if (status.isNull()) return; QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.drawText(10, height() - 10, status); } void Canvas::draw_mesh() { mesh_shader.bind(); // Load the transform and view matrices into the shader glUniformMatrix4fv( mesh_shader.uniformLocation("transform_matrix"), 1, GL_FALSE, transform_matrix().data()); glUniformMatrix4fv( mesh_shader.uniformLocation("view_matrix"), 1, GL_FALSE, view_matrix().data()); // Compensate for z-flattening when zooming glUniform1f(mesh_shader.uniformLocation("zoom"), 1/zoom); // Find and enable the attribute location for vertex position const GLuint vp = mesh_shader.attributeLocation("vertex_position"); glEnableVertexAttribArray(vp); // Then draw the mesh with that vertex position mesh->draw(vp); // Clean up state machine glDisableVertexAttribArray(vp); mesh_shader.release(); } QMatrix4x4 Canvas::transform_matrix() const { QMatrix4x4 m; m.rotate(tilt, QVector3D(1, 0, 0)); m.rotate(yaw, QVector3D(0, 0, 1)); m.scale(scale); m.translate(-center); return m; } QMatrix4x4 Canvas::view_matrix() const { QMatrix4x4 m; if (width() > height()) { m.scale(-height() / float(width()), 1, 0.5); } else { m.scale(-1, width() / float(height()), 0.5); } m.scale(zoom, zoom, 1); return m; } void Canvas::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { mouse_pos = event->pos(); setCursor(Qt::ClosedHandCursor); } } void Canvas::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { unsetCursor(); } } void Canvas::mouseMoveEvent(QMouseEvent* event) { auto p = event->pos(); auto d = p - mouse_pos; if (event->buttons() & Qt::LeftButton) { yaw = fmod(yaw - d.x(), 360); tilt = fmax(0, fmin(180, tilt - d.y())); update(); } else if (event->buttons() & Qt::RightButton) { center = transform_matrix().inverted() * view_matrix().inverted() * QVector3D(-d.x() / (0.5*width()), d.y() / (0.5*height()), 0); update(); } mouse_pos = p; } void Canvas::wheelEvent(QWheelEvent *event) { // Find GL position before the zoom operation // (to zoom about mouse cursor) auto p = event->pos(); QVector3D v(1 - p.x() / (0.5*width()), p.y() / (0.5*height()) - 1, 0); QVector3D a = transform_matrix().inverted() * view_matrix().inverted() * v; if (event->delta() < 0) { for (int i=0; i > event->delta(); --i) zoom *= 1.001; } else if (event->delta() > 0) { for (int i=0; i < event->delta(); ++i) zoom /= 1.001; } // Then find the cursor's GL position post-zoom and adjust center. QVector3D b = transform_matrix().inverted() * view_matrix().inverted() * v; center += b - a; update(); }