#include "mesh_viewer.h" #include #include const GLchar *vertex_shader_source = R"glsl( attribute vec3 pos; attribute vec3 col; varying vec3 frag_col; uniform mat4 proj; uniform mat4 view; uniform mat4 model; void main() { gl_Position = proj * view * model * vec4(pos, 1.0); frag_col = col; } )glsl"; const GLchar *fragment_shader_source = R"glsl( varying vec3 frag_col; uniform vec3 wf_col; uniform bool wireframe; uniform float alpha; void main() { if (wireframe) gl_FragColor = vec4(wf_col, alpha); else gl_FragColor = vec4(frag_col, alpha); } )glsl"; MeshViewer::MeshViewer(QWidget *parent) : QOpenGLWidget(parent) { setMouseTracking(true); setFocus(); } void GLAPIENTRY opengl_debug_cb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { (void) source; (void) type; (void) id; (void) severity; (void) length; (void) userParam; qDebug() << "OpenGL debug output:" << message; } void MeshViewer::initializeGL() { // initializeOpenGLFunctions(); GLint major, minor; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); qDebug("OpenGL version %d.%d", major, minor); glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(opengl_debug_cb, 0); /* Compile the vertex shader. */ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); glCompileShader(vertex_shader); GLint status; glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { char log[1024]; glGetShaderInfoLog(vertex_shader, sizeof log, NULL, log); fprintf(stderr, "Failed to compile the vertex shader: %s\n", log); exit(1); } /* Compile the fragment shader. */ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); glCompileShader(fragment_shader); glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { char log[1024]; glGetShaderInfoLog(fragment_shader, sizeof log, NULL, log); fprintf(stderr, "Failed to compile the fragment shader: %s\n", log); exit(1); } /* Link the shader program. */ GLuint shader_program = glCreateProgram(); glAttachShader(shader_program, vertex_shader); glAttachShader(shader_program, fragment_shader); glBindFragDataLocation(shader_program, 0, "out_color"); glLinkProgram(shader_program); glGetProgramiv(shader_program, GL_LINK_STATUS, &status); if (status != GL_TRUE) { char log[1024]; glGetProgramInfoLog(shader_program, sizeof log, NULL, log); fprintf(stderr, "Failed to link the shader program: %s\n", log); exit(1); } /* Use it. */ glUseProgram(shader_program); /* Get the position attribute. */ pos_attr = glGetAttribLocation(shader_program, "pos"); col_attr = glGetAttribLocation(shader_program, "col"); proj_attr = glGetUniformLocation(shader_program, "proj"); view_attr = glGetUniformLocation(shader_program, "view"); model_attr = glGetUniformLocation(shader_program, "model"); wf_col_attr = glGetUniformLocation(shader_program, "wf_col"); wireframe_attr = glGetUniformLocation(shader_program, "wireframe"); alpha_attr = glGetUniformLocation(shader_program, "alpha"); glUniform1f(alpha_attr, 1); glUniform3f(wf_col_attr, WIREFRAME_COLOR); glClearColor(1, 1, 1, 0); glEnable(GL_DEPTH_TEST); glEnable(GL_MULTISAMPLE); emit initialized(); } void MeshViewer::resizeGL(int w, int h) { QMatrix4x4 projection; projection.perspective(FOV, (float) w/h, .01, 100); glUniformMatrix4fv(proj_attr, 1, GL_FALSE, projection.data()); } void MeshViewer::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); QMatrix4x4 trans; trans.translate(0, 0, -cam_dist); QMatrix4x4 view = trans * rot; glUniformMatrix4fv(view_attr, 1, GL_FALSE, view.data()); for (const OpenGLMesh m : meshes) { glUniformMatrix4fv(model_attr, 1, GL_FALSE, m.mat.data()); /* Mesh */ glBindVertexArray(m.vao); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0, 2); glDrawArrays(GL_TRIANGLES, 0, m.nverts); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_POLYGON_OFFSET_FILL); /* Wireframe */ glUniform1f(wireframe_attr, 1); glDrawArrays(GL_TRIANGLES, 0, m.nverts); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLineWidth(1); glUniform1f(wireframe_attr, 0); } } OpenGLMesh *MeshViewer::addOpenGLMesh(size_t nverts, const GLfloat *verts, QMatrix4x4 mat, QColor col) { makeCurrent(); GLuint vao, vbo; glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, nverts * 6 * sizeof (GLfloat), verts, GL_DYNAMIC_DRAW); glVertexAttribPointer(pos_attr, 3, GL_FLOAT, GL_FALSE, 6 * sizeof (GLfloat), 0); glEnableVertexAttribArray(pos_attr); glVertexAttribPointer(col_attr, 3, GL_FLOAT, GL_FALSE, 6 * sizeof (GLfloat), (const void *) (3 * sizeof (GLfloat))); glEnableVertexAttribArray(col_attr); glBindVertexArray(0); meshes.append({vao, vbo, nverts, mat, col}); doneCurrent(); update(); return &meshes.last(); } OpenGLMesh *MeshViewer::addOpenGLMeshFromOpenMesh(MyMesh* mesh, QMatrix4x4 mat, QColor col) { GLfloat *verts = new GLfloat[mesh->n_faces() * 3 * 6]; size_t i = 0; for (MyMesh::FaceHandle face : mesh->faces()) { for (MyMesh::VertexHandle vec : mesh->fv_range(face)) { verts[6*i + 0] = mesh->point(vec)[0]; verts[6*i + 1] = mesh->point(vec)[1]; verts[6*i + 2] = mesh->point(vec)[2]; verts[6*i + 3] = mesh->color(vec)[0]; verts[6*i + 4] = mesh->color(vec)[1]; verts[6*i + 5] = mesh->color(vec)[2]; i++; } } OpenGLMesh *ret = addOpenGLMesh(i, verts, mat, col); delete[] verts; return ret; } void MeshViewer::removeOpenGLMesh(OpenGLMesh *mesh) { glDeleteVertexArrays(1, &mesh->vao); glDeleteBuffers(1, &mesh->vbo); for (int i = 0; i < meshes.size(); i++) { if (&meshes[i] == mesh) meshes.removeAt(i); } } void MeshViewer::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { mouse_pos = e->pos(); } } void MeshViewer::mouseReleaseEvent(QMouseEvent *e) { (void) e; rot_start = rot; } void MeshViewer::mouseMoveEvent(QMouseEvent *e) { if (e->buttons() & Qt::LeftButton) { QPoint delta = e->pos() - mouse_pos; rot = rot_start; rot.rotate(delta.x() / 5., 0, 1, 0); rot.rotate(delta.y() / 5., QVector3D(1, 0, 0) * rot); update(); } } void MeshViewer::wheelEvent(QWheelEvent *e) { cam_dist -= e->angleDelta().y() / 1000. * cam_dist; update(); }