#include "glview.h" #include #include #include #include #include GlView::GlView(QWidget *parent) : QOpenGLWidget(parent) , m_yawDeg(-30.0f) , m_pitchDeg(25.0f) , m_distance(4.0f) , m_minDistance(0.5f) , m_maxDistance(2000.0f) , m_center(0.0f, 0.0f, 0.0f) , m_radius(1.0f) , m_panX(0.0f) , m_panY(0.0f) , m_surfaceVisible(true) { } void GlView::setData(const QVector &points, const QVector &triangles) { m_points = points; m_triangles = triangles; if (!m_points.isEmpty()) { QVector3D bmin(m_points[0].x, m_points[0].y, m_points[0].z); QVector3D bmax = bmin; for (const core::Point3f &p : m_points) { bmin.setX(qMin(bmin.x(), p.x)); bmin.setY(qMin(bmin.y(), p.y)); bmin.setZ(qMin(bmin.z(), p.z)); bmax.setX(qMax(bmax.x(), p.x)); bmax.setY(qMax(bmax.y(), p.y)); bmax.setZ(qMax(bmax.z(), p.z)); } m_center = (bmin + bmax) * 0.5f; m_radius = qMax(1e-3f, (bmax - bmin).length() * 0.5f); m_distance = qMax(2.5f * m_radius, 1.0f); m_minDistance = qMax(0.05f * m_radius, 0.01f); m_maxDistance = qMax(100.0f * m_radius, m_distance * 2.0f); m_panX = 0.0f; m_panY = 0.0f; } update(); } void GlView::setSurfaceVisible(const bool visible) { if (m_surfaceVisible == visible) { return; } m_surfaceVisible = visible; update(); } void GlView::initializeGL() { initializeOpenGLFunctions(); glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec3 aPos;\n" "uniform mat4 uMvp;\n" "void main() {\n" " gl_Position = uMvp * vec4(aPos, 1.0);\n" "}\n"); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform vec3 uColor;\n" "void main() {\n" " gl_FragColor = vec4(uColor, 1.0);\n" "}\n"); m_program.link(); } void GlView::resizeGL(int w, int h) { glViewport(0, 0, w, h); } void GlView::paintGL() { glClearColor(0.08f, 0.09f, 0.11f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (m_points.isEmpty()) { return; } QVector vertices; vertices.reserve(m_points.size() * 3); for (const core::Point3f &p : m_points) { vertices.push_back(p.x - m_center.x()); vertices.push_back(p.y - m_center.y()); vertices.push_back(p.z - m_center.z()); } QVector indices; indices.reserve(m_triangles.size() * 3); QVector edgeIndices; edgeIndices.reserve(m_triangles.size() * 6); for (const core::Triangle &t : m_triangles) { indices.push_back(static_cast(t.i0)); indices.push_back(static_cast(t.i1)); indices.push_back(static_cast(t.i2)); edgeIndices.push_back(static_cast(t.i0)); edgeIndices.push_back(static_cast(t.i1)); edgeIndices.push_back(static_cast(t.i1)); edgeIndices.push_back(static_cast(t.i2)); edgeIndices.push_back(static_cast(t.i2)); edgeIndices.push_back(static_cast(t.i0)); } QMatrix4x4 projection; projection.perspective(45.0f, static_cast(width()) / qMax(1, height()), qMax(0.001f, m_distance * 0.001f), qMax(m_distance + 4.0f * m_radius, m_distance * 2.0f)); QMatrix4x4 view; view.translate(m_panX, m_panY, -m_distance); view.rotate(m_pitchDeg, 1.0f, 0.0f, 0.0f); view.rotate(m_yawDeg, 0.0f, 1.0f, 0.0f); const QMatrix4x4 mvp = projection * view; m_program.bind(); m_program.setUniformValue("uMvp", mvp); m_program.enableAttributeArray("aPos"); m_program.setAttributeArray("aPos", GL_FLOAT, vertices.constData(), 3); if (m_surfaceVisible) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0f, 1.0f); m_program.setUniformValue("uColor", QVector3D(0.20f, 0.72f, 0.95f)); if (!indices.isEmpty()) { glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, indices.constData()); } glDisable(GL_POLYGON_OFFSET_FILL); glLineWidth(2.2f); m_program.setUniformValue("uColor", QVector3D(1.0f, 0.2f, 0.08f)); if (!edgeIndices.isEmpty()) { glDrawElements(GL_LINES, edgeIndices.size(), GL_UNSIGNED_INT, edgeIndices.constData()); } } glPointSize(4.0f); m_program.setUniformValue("uColor", QVector3D(1.0f, 0.85f, 0.2f)); glDrawArrays(GL_POINTS, 0, m_points.size()); m_program.disableAttributeArray("aPos"); m_program.release(); } void GlView::mousePressEvent(QMouseEvent *event) { m_lastMousePos = event->pos(); } void GlView::mouseMoveEvent(QMouseEvent *event) { const QPoint delta = event->pos() - m_lastMousePos; m_lastMousePos = event->pos(); if (event->buttons() & Qt::LeftButton) { m_yawDeg += static_cast(delta.x()) * 0.5f; m_pitchDeg += static_cast(delta.y()) * 0.5f; m_pitchDeg = qBound(-89.0f, m_pitchDeg, 89.0f); update(); } else if (event->buttons() & (Qt::RightButton | Qt::MiddleButton)) { const float panScale = qMax(0.0005f * m_distance, 0.0005f); m_panX += static_cast(delta.x()) * panScale; m_panY -= static_cast(delta.y()) * panScale; update(); } } void GlView::wheelEvent(QWheelEvent *event) { const float step = static_cast(event->angleDelta().y()) / 120.0f; const float zoomFactor = qPow(1.15f, step); m_distance /= zoomFactor; m_distance = qBound(m_minDistance, m_distance, m_maxDistance); update(); }