From e155c9551c2c9aa2669e496343ad6e89e8dcb4e1 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Feb 2026 13:11:14 -0700 Subject: [PATCH] refactor: Migrate ViewCube to shader-based rendering Co-authored-by: aider (gemini/gemini-2.5-pro) --- src/ViewCube.cpp | 223 ++++++++++++++++++++++++--------------- src/ViewCube.h | 25 ++++- src/ViewportWidget.cpp | 10 +- src/shaders/texture.frag | 12 +++ src/shaders/texture.vert | 15 +++ 5 files changed, 192 insertions(+), 93 deletions(-) create mode 100644 src/shaders/texture.frag create mode 100644 src/shaders/texture.vert diff --git a/src/ViewCube.cpp b/src/ViewCube.cpp index 69a0f24..fe39673 100644 --- a/src/ViewCube.cpp +++ b/src/ViewCube.cpp @@ -1,18 +1,38 @@ #include "ViewCube.h" #include #include +#include +#include +#include ViewCube::ViewCube() { + for (int i = 0; i < 6; ++i) { + m_faceTextures[i] = nullptr; + } +} + +ViewCube::~ViewCube() +{ + for (int i = 0; i < 6; ++i) { + delete m_faceTextures[i]; + } + delete m_textureShaderProgram; + m_cubeVbo.destroy(); + m_cubeVao.destroy(); + m_axesVbo.destroy(); + m_axesVao.destroy(); } void ViewCube::initializeGL() { initializeOpenGLFunctions(); + initShaders(); createFaceTextures(); + setupBuffers(); } -void ViewCube::paintGL(const QMatrix4x4& viewMatrix, int width, int height) +void ViewCube::paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height) { int viewCubeSize = 150; glViewport(width - viewCubeSize, height - viewCubeSize, viewCubeSize, viewCubeSize); @@ -21,20 +41,13 @@ void ViewCube::paintGL(const QMatrix4x4& viewMatrix, int width, int height) QMatrix4x4 viewCubeProjection; viewCubeProjection.ortho(-2, 2, -2, 2, -10, 10); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(viewCubeProjection.constData()); - - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(viewMatrix.constData()); - - drawViewCube(); - drawAxes(); + drawViewCube(viewCubeProjection, viewMatrix); + drawAxes(simpleShader, simpleShaderColorLoc, viewCubeProjection, viewMatrix); } void ViewCube::createFaceTextures() { QStringList labels = {"FRONT", "BACK", "TOP", "BOTTOM", "RIGHT", "LEFT"}; - glGenTextures(6, faceTextures); for (int i = 0; i < 6; ++i) { QImage image(128, 128, QImage::Format_RGBA8888); @@ -45,94 +58,138 @@ void ViewCube::createFaceTextures() painter.setFont(QFont("Arial", 24, QFont::Bold)); painter.drawText(image.rect(), Qt::AlignCenter, labels[i]); - QImage glImage = image.mirrored(false, true); - - glBindTexture(GL_TEXTURE_2D, faceTextures[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glImage.width(), glImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits()); + m_faceTextures[i] = new QOpenGLTexture(image.mirrored(false, true)); + m_faceTextures[i]->setMinificationFilter(QOpenGLTexture::Linear); + m_faceTextures[i]->setMagnificationFilter(QOpenGLTexture::Linear); } } -void ViewCube::drawViewCube() +void ViewCube::initShaders() +{ + m_textureShaderProgram = new QOpenGLShaderProgram(); + if (!m_textureShaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/texture.vert")) { + qCritical() << "ViewCube vertex shader compilation failed:" << m_textureShaderProgram->log(); + return; + } + if (!m_textureShaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/texture.frag")) { + qCritical() << "ViewCube fragment shader compilation failed:" << m_textureShaderProgram->log(); + return; + } + if (!m_textureShaderProgram->link()) { + qCritical() << "ViewCube shader program linking failed:" << m_textureShaderProgram->log(); + return; + } +} + +void ViewCube::setupBuffers() { float size = 0.75; - glEnable(GL_TEXTURE_2D); - glColor3f(1.0, 1.0, 1.0); // Use white so texture colors are not modulated + GLfloat vertices[] = { + // positions // texture Coords + // Front face + -size, -size, size, 0.0f, 0.0f, + size, -size, size, 1.0f, 0.0f, + size, size, size, 1.0f, 1.0f, + -size, size, size, 0.0f, 1.0f, + // Back face + -size, -size, -size, 1.0f, 0.0f, + -size, size, -size, 1.0f, 1.0f, + size, size, -size, 0.0f, 1.0f, + size, -size, -size, 0.0f, 0.0f, + // Top face + -size, size, -size, 0.0f, 1.0f, + -size, size, size, 0.0f, 0.0f, + size, size, size, 1.0f, 0.0f, + size, size, -size, 1.0f, 1.0f, + // Bottom face + -size, -size, -size, 1.0f, 1.0f, + size, -size, -size, 0.0f, 1.0f, + size, -size, size, 0.0f, 0.0f, + -size, -size, size, 1.0f, 0.0f, + // Right face + size, -size, -size, 1.0f, 0.0f, + size, size, -size, 1.0f, 1.0f, + size, size, size, 0.0f, 1.0f, + size, -size, size, 0.0f, 0.0f, + // Left face + -size, -size, -size, 0.0f, 0.0f, + -size, -size, size, 1.0f, 0.0f, + -size, size, size, 1.0f, 1.0f, + -size, size, -size, 0.0f, 1.0f, + }; - // Front face - glBindTexture(GL_TEXTURE_2D, faceTextures[0]); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); glVertex3f(-size, -size, size); - glTexCoord2f(1.0, 0.0); glVertex3f(size, -size, size); - glTexCoord2f(1.0, 1.0); glVertex3f(size, size, size); - glTexCoord2f(0.0, 1.0); glVertex3f(-size, size, size); - glEnd(); + m_cubeVao.create(); + QOpenGLVertexArrayObject::Binder vaoBinder(&m_cubeVao); + m_cubeVbo.create(); + m_cubeVbo.bind(); + m_cubeVbo.allocate(vertices, sizeof(vertices)); - // Back face - glBindTexture(GL_TEXTURE_2D, faceTextures[1]); - glBegin(GL_QUADS); - glTexCoord2f(1.0, 0.0); glVertex3f(-size, -size, -size); - glTexCoord2f(1.0, 1.0); glVertex3f(-size, size, -size); - glTexCoord2f(0.0, 1.0); glVertex3f(size, size, -size); - glTexCoord2f(0.0, 0.0); glVertex3f(size, -size, -size); - glEnd(); + m_textureShaderProgram->enableAttributeArray(0); + m_textureShaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat)); + m_textureShaderProgram->enableAttributeArray(1); + m_textureShaderProgram->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat)); + m_cubeVbo.release(); - // Top face - glBindTexture(GL_TEXTURE_2D, faceTextures[2]); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 1.0); glVertex3f(-size, size, -size); - glTexCoord2f(0.0, 0.0); glVertex3f(-size, size, size); - glTexCoord2f(1.0, 0.0); glVertex3f(size, size, size); - glTexCoord2f(1.0, 1.0); glVertex3f(size, size, -size); - glEnd(); + GLfloat axes_vertices[] = { + 0.0f, 0.0f, 0.0f, 1.5f, 0.0f, 0.0f, // X + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.5f, // Y + 0.0f, 0.0f, 0.0f, 0.0f, 1.5f, 0.0f // Z + }; - // Bottom face - glBindTexture(GL_TEXTURE_2D, faceTextures[3]); - glBegin(GL_QUADS); - glTexCoord2f(1.0, 1.0); glVertex3f(-size, -size, -size); - glTexCoord2f(0.0, 1.0); glVertex3f(size, -size, -size); - glTexCoord2f(0.0, 0.0); glVertex3f(size, -size, size); - glTexCoord2f(1.0, 0.0); glVertex3f(-size, -size, size); - glEnd(); - - // Right face - glBindTexture(GL_TEXTURE_2D, faceTextures[4]); - glBegin(GL_QUADS); - glTexCoord2f(1.0, 0.0); glVertex3f(size, -size, -size); - glTexCoord2f(1.0, 1.0); glVertex3f(size, size, -size); - glTexCoord2f(0.0, 1.0); glVertex3f(size, size, size); - glTexCoord2f(0.0, 0.0); glVertex3f(size, -size, size); - glEnd(); - - // Left face - glBindTexture(GL_TEXTURE_2D, faceTextures[5]); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); glVertex3f(-size, -size, -size); - glTexCoord2f(1.0, 0.0); glVertex3f(-size, -size, size); - glTexCoord2f(1.0, 1.0); glVertex3f(-size, size, size); - glTexCoord2f(0.0, 1.0); glVertex3f(-size, size, -size); - glEnd(); - - glDisable(GL_TEXTURE_2D); + m_axesVao.create(); + QOpenGLVertexArrayObject::Binder axesVaoBinder(&m_axesVao); + m_axesVbo.create(); + m_axesVbo.bind(); + m_axesVbo.allocate(axes_vertices, sizeof(axes_vertices)); + m_axesVbo.release(); } -void ViewCube::drawAxes() +void ViewCube::drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view) { + if (!m_textureShaderProgram || !m_textureShaderProgram->isLinked()) return; + + glEnable(GL_CULL_FACE); + m_textureShaderProgram->bind(); + m_textureShaderProgram->setUniformValue("projectionMatrix", projection); + m_textureShaderProgram->setUniformValue("modelViewMatrix", view); + m_textureShaderProgram->setUniformValue("texture_diffuse1", 0); + + QOpenGLVertexArrayObject::Binder vaoBinder(&m_cubeVao); + for (int i = 0; i < 6; ++i) { + m_faceTextures[i]->bind(); + glDrawArrays(GL_QUADS, i * 4, 4); + } + m_textureShaderProgram->release(); + glDisable(GL_CULL_FACE); +} + +void ViewCube::drawAxes(QOpenGLShaderProgram* simpleShader, int colorLoc, const QMatrix4x4& projection, const QMatrix4x4& view) +{ + if (!simpleShader || !simpleShader->isLinked()) return; + + simpleShader->bind(); + simpleShader->setUniformValue("projectionMatrix", projection); + simpleShader->setUniformValue("modelViewMatrix", view); + + QOpenGLVertexArrayObject::Binder vaoBinder(&m_axesVao); + m_axesVbo.bind(); + simpleShader->enableAttributeArray(0); + simpleShader->setAttributeBuffer(0, GL_FLOAT, 0, 3, 3 * sizeof(GLfloat)); + m_axesVbo.release(); + glLineWidth(2.0f); - glBegin(GL_LINES); + // X-axis (red) - glColor3f(1.0, 0.0, 0.0); - glVertex3f(0.0, 0.0, 0.0); - glVertex3f(1.5, 0.0, 0.0); + simpleShader->setUniformValue(colorLoc, QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); + glDrawArrays(GL_LINES, 0, 2); + // Y-axis (green) - glColor3f(0.0, 1.0, 0.0); - glVertex3f(0.0, 0.0, 0.0); - glVertex3f(0.0, 0.0, 1.5); + simpleShader->setUniformValue(colorLoc, QVector4D(0.0f, 1.0f, 0.0f, 1.0f)); + glDrawArrays(GL_LINES, 2, 2); + // Z-axis (blue) - glColor3f(0.0, 0.0, 1.0); - glVertex3f(0.0, 0.0, 0.0); - glVertex3f(0.0, 1.5, 0.0); - glEnd(); + simpleShader->setUniformValue(colorLoc, QVector4D(0.0f, 0.0f, 1.0f, 1.0f)); + glDrawArrays(GL_LINES, 4, 2); + glLineWidth(1.0f); } diff --git a/src/ViewCube.h b/src/ViewCube.h index 8408427..c003cd1 100644 --- a/src/ViewCube.h +++ b/src/ViewCube.h @@ -3,22 +3,37 @@ #include #include -#include // For GLuint +#include +#include + +class QOpenGLShaderProgram; +class QOpenGLTexture; class ViewCube : protected QOpenGLFunctions { public: ViewCube(); + ~ViewCube(); void initializeGL(); - void paintGL(const QMatrix4x4& viewMatrix, int width, int height); + void paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height); private: void createFaceTextures(); - void drawViewCube(); - void drawAxes(); + void initShaders(); + void setupBuffers(); + void drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view); + void drawAxes(QOpenGLShaderProgram* simpleShader, int colorLoc, const QMatrix4x4& projection, const QMatrix4x4& view); - GLuint faceTextures[6]; + QOpenGLTexture* m_faceTextures[6]; + + QOpenGLShaderProgram* m_textureShaderProgram = nullptr; + + QOpenGLVertexArrayObject m_cubeVao; + QOpenGLBuffer m_cubeVbo; + + QOpenGLVertexArrayObject m_axesVao; + QOpenGLBuffer m_axesVbo; }; #endif // VIEWCUBE_H diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index 00acd12..66fffa1 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -245,11 +245,11 @@ void ViewportWidget::paintGL() m_shaderProgram->release(); - // View cube rendering - temporarily disabled as it uses the fixed-function pipeline - // QMatrix4x4 viewCubeModel; - // viewCubeModel.rotate(m_camera->xRotation() / 16.0f, 1, 0, 0); - // viewCubeModel.rotate(m_camera->yRotation() / 16.0f, 0, 1, 0); - // m_viewCube->paintGL(viewCubeModel, width(), height()); + // View cube rendering + QMatrix4x4 viewCubeModel; + viewCubeModel.rotate(m_camera->xRotation() / 16.0f, 1, 0, 0); + viewCubeModel.rotate(m_camera->yRotation() / 16.0f, 0, 1, 0); + m_viewCube->paintGL(m_shaderProgram, m_colorLoc, viewCubeModel, width(), height()); glViewport(0, 0, width(), height()); glDisable(GL_DEPTH_TEST); diff --git a/src/shaders/texture.frag b/src/shaders/texture.frag new file mode 100644 index 0000000..d8ef8cd --- /dev/null +++ b/src/shaders/texture.frag @@ -0,0 +1,12 @@ +#version 330 core + +out vec4 FragColor; + +in vec2 TexCoord; + +uniform sampler2D texture_diffuse1; + +void main() +{ + FragColor = texture(texture_diffuse1, TexCoord); +} diff --git a/src/shaders/texture.vert b/src/shaders/texture.vert new file mode 100644 index 0000000..deffb0f --- /dev/null +++ b/src/shaders/texture.vert @@ -0,0 +1,15 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; + +out vec2 TexCoord; + +uniform mat4 modelViewMatrix; +uniform mat4 projectionMatrix; + +void main() +{ + gl_Position = projectionMatrix * modelViewMatrix * vec4(aPos, 1.0); + TexCoord = aTexCoord; +}