C++ - Qt Framework - Using QOpenGLWidget to display a window for moving shapes with keyboard and mouse

In the past tutorials about Qt and OpenGL we saw how to deal with some basic tasks.

Let's see this time something a bit more advanced with the QOpenGLWidget class in order to display a scene from a window with a shape.

This shape (a cube) can be rotated, translated, zoomed with the keyboard and the mouse.

I added some options to change this cube into a colored or texured shape directly from the GUI.

You can also enable or stop the rotation of the cube.

And of couse reset the shape or camera view (both as well).

First of all

This tutorial uses Qt 5.

I also used some classes (Camera3D, Transform3D and Input) from this project : https://www.trentreed.net/blog/qt5-opengl-part-3b-camera-control/

I adapted it to my needs, so feel free to explore and use it if you want to.

I also changed, in the Input.h header, the line: 

friend class Window;

by

friend class BadprogOpenGLWidget;

All code of these 3 classes, except this line, have been kept from the original sources.

So only my code will be displayed in the following tutorial.

Thus, in our example we are going to use a QOpenGLWidget in order to demonstrate how to incorporate an OpenGL scene in a Qt Widget.

I divided the scene in 2 parts: one with colors and the other with textures.

To change it just click the radioButton of your choice.

By default the cube is rotating so it's possible to stop this rotation with a checkBox.

Furthermore you can reset the position of the cube and also the position of the camera by clicking on their respective pushButton.

I created 2 programs, one for the colors and the other for the texture.

Why? 

To keep things simple to get.

For the program 1 (the color mode) we have the cube created with "GL_TRIANGLE_STRIP" primitive.

Then I created shapes (still in the color mode) with the "GL_LINES" primitive.

You can see that the cube is hard coded when the shapes, from lines, are dynamically created with the method createVAO.

I won't explain anymore all the code because there are tons of things to say.

So I prefer you dive into it by yourself to really understand what's going on.

Code

BadprogOpenGLWidgetWithKeyboardAndMouse.pro

QT       += core gui opengl


greaterThan(QT_MAJOR_VERSION, 4): QT += widgets


CONFIG += c++11


# The following define makes your compiler emit warnings if you use

# any Qt feature that has been marked deprecated (the exact warnings

# depend on your compiler). Please consult the documentation of the

# deprecated API in order to know how to port your code away from it.

DEFINES += QT_DEPRECATED_WARNINGS


# You can also make your code fail to compile if it uses deprecated APIs.

# In order to do so, uncomment the following line.

# You can also select to disable deprecated APIs only up to a certain version of Qt.

#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \

    badprogMainWindow.cpp \

    badprogOpenglWidget.cpp \

    camera3d.cpp \

    input.cpp \

    main.cpp \

    transform3d.cpp


HEADERS += \

    badprogMainWindow.h \

    badprogOpenglWidget.h \

    camera3d.h \

    input.h \

    transform3d.h


FORMS += \

    badprogMainWindow.ui


RESOURCES += \

    badprog.qrc


# Default rules for deployment.

qnx: target.path = /tmp/$${TARGET}/bin

else: unix:!android: target.path = /opt/$${TARGET}/bin

!isEmpty(target.path): INSTALLS += target

 

main.cpp

#include "badprogMainWindow.h"
#include <QApplication>

// badprog.com

int main(int argcchar *argv[])
{
    QApplication a(argc, argv);
    BadprogMainWindow w;
    w.show();
    return a.exec();
}

 

shader/shader_fragment_color.glsl

#version 330 core

// BadproG.com

// out
out vec4 FragColor;

// in
in vec3 shaderColor;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = vec4(shaderColor, 1.0f);
}

 

shader/shader_fragment_texture.glsl

#version 330 core

// BadproG.com

// out
out vec4 FragColor;

// texture
in vec2 shaderTexture;

// uniform texture
uniform sampler2D uniformTexture;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = texture2D(uniformTexture, shaderTexture);
}

 

shader/shader_vertex_color.glsl

#version 330 core

// BadproG.com

// layout
layout (location = 0in vec3 layoutPosition;
layout (location = 1in vec3 layoutColor;

// out
out vec3 shaderColor;

uniform float pointSize;

// uniform
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    gl_Position =  projection * view * model * vec4(layoutPosition, 1.0f);
    gl_PointSize = pointSize;
    shaderColor = layoutColor;
}

 

shader/shader_vertex_texture.glsl

#version 330 core

// BadproG.com

// layout
layout (location = 0in vec3 layoutPosition;
layout (location = 2in vec3 layoutTexture;

// texture
out vec2 shaderTexture;

// uniform
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    gl_PointSize = 40;
    gl_Position =  projection * view * model*  vec4(layoutPosition, 1.0f);
    shaderTexture = vec2(layoutTexture.x, layoutTexture.y);
}

 

badprog.qrc

<RCC>

    <qresource prefix="/">

        <file alias="fragment_color">shader/shader_fragment_color.glsl</file>

        <file alias="fragment_texture">shader/shader_fragment_texture.glsl</file>

        <file alias="vertex_color">shader/shader_vertex_color.glsl</file>

        <file alias="vertex_texture">shader/shader_vertex_texture.glsl</file>

        <file alias="imageForTexture">image_for_texture.png</file>

    </qresource>

</RCC>

badprogMainWindow.cpp

#include "badprogMainWindow.h"
#include "badprogOpenglWidget.h"
#include <QKeyEvent>
#include <QDebug>

// badprog.com

// ----------------------------------------------------------------------------
BadprogMainWindow::BadprogMainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //
    ui.setupUi(this);

    //
    setWindowTitle("Badprog: OpenGLWidget");

    //
    _bpOpenGLWidget = new BadprogOpenGLWidget(this);
    _bpOpenGLWidget->setMinimumWidth(300);
    _bpOpenGLWidget->setMinimumHeight(300);

    //
    ui.verticalLayout->addWidget(_bpOpenGLWidget);

    //
    ui.checkBox->setText("On");
    ui.checkBox->setChecked(true);

    //
    ui.radioButton_color->setChecked(true);
    ui.pushButton_resetCamera->setText("Reset camera position");
    ui.pushButton_resetObject->setText("Reset object position");
    ui.pushButton_resetBoth->setText("Reset both positions");
    ui.label_activateScreenWithMouse->hide();

    //
    _switcher = false;

    //
    QObject::connect(ui.pushButton_resetCamera, &QPushButton::clicked, this, &BadprogMainWindow::slotResetCamera);
    QObject::connect(ui.pushButton_resetObject, &QPushButton::clicked, this, &BadprogMainWindow::slotResetObject);
    QObject::connect(ui.pushButton_resetBoth, &QPushButton::clicked, this, &BadprogMainWindow::slotResetBoth);
    QObject::connect(ui.checkBox, &QPushButton::clicked, this, &BadprogMainWindow::slotStopStart);
    QObject::connect(ui.radioButton_color, &QRadioButton::clicked, this, &BadprogMainWindow::slotRadioButtonSwitchColorTexture);
    QObject::connect(ui.radioButton_texture, &QRadioButton::clicked, this, &BadprogMainWindow::slotRadioButtonSwitchColorTexture);
}

// ----------------------------------------------------------------------------
BadprogMainWindow::~BadprogMainWindow() {
}

// ----------------------------------------------------------------------------`
void BadprogMainWindow::slotRadioButtonSwitchColorTexture() {
    //
    QObject *object = sender();
    int result = -1;

    //
    if (ui.radioButton_color == object) {
        result = APPAREANCE_TYPE::APPAREANCE_TYPE_COLOR;
    } else if (ui.radioButton_texture == object) {
        result = APPAREANCE_TYPE::APPAREANCE_TYPE_TEXTURE;
    }

    //
    _bpOpenGLWidget->retrieveAppearence(result);
}

// ----------------------------------------------------------------------------
void BadprogMainWindow::slotResetCamera() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetCamera();
    }
}

// ----------------------------------------------------------------------------
void BadprogMainWindow::slotResetObject() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetObject();
    }

}

// ----------------------------------------------------------------------------
void BadprogMainWindow::slotResetBoth() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetBoth();
    }
}

// ----------------------------------------------------------------------------
void BadprogMainWindow::slotActivateOpenGLScreen() {
    //
    ui.label_activateScreenWithMouse->hide();
}

// ----------------------------------------------------------------------------
void BadprogMainWindow::slotStopStart() {
    //
    bool result = _bpOpenGLWidget->changeTimerState();

    //
    QString textToDisplay = result ? "On" : "Off";

    //
    ui.checkBox->setText(textToDisplay);

    //
    _bpOpenGLWidget->rotateSwitcher(ui.checkBox->isChecked());
}

 

badprogMainWindow.h

#ifndef BADPROG_MAIN_WINDOW_H
#define BADPROG_MAIN_WINDOW_H

// badprog.com

#include <QMainWindow>
#include "ui_badprogMainWindow.h"

class QKeyEvent;
class BadprogOpenGLWidget;

class BadprogMainWindow : public QMainWindow
{
    Q_OBJECT

public:
    BadprogMainWindow(QWidget *parent = nullptr);
    ~BadprogMainWindow();

protected slots:
    void slotStopStart();
    void slotRadioButtonSwitchColorTexture();
    void slotResetCamera();
    void slotResetObject();
    void slotResetBoth();
    void slotActivateOpenGLScreen();

private:
    Ui::BadprogMainWindow ui;
    BadprogOpenGLWidget_bpOpenGLWidget;
    bool _switcher;
};
#endif // !BADPROG_MAIN_WINDOW_H

 

badprogMainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>BadprogMainWindow</class>
 <widget class="QMainWindow" name="BadprogMainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>626</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>BadprogMainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout_4" columnstretch="5,1">
    <item row="0" column="1">
     <widget class="QGroupBox" name="groupBox_classicalWidgets">
      <property name="maximumSize">
       <size>
        <width>16777215</width>
        <height>16777215</height>
       </size>
      </property>
      <property name="title">
       <string>Classical UI widgets</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_2">
       <item row="2" column="0">
        <widget class="QPushButton" name="pushButton_resetCamera">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
       <item row="0" column="0">
        <layout class="QHBoxLayout" name="horizontalLayout">
         <property name="leftMargin">
          <number>5</number>
         </property>
         <property name="topMargin">
          <number>5</number>
         </property>
         <property name="rightMargin">
          <number>5</number>
         </property>
         <property name="bottomMargin">
          <number>5</number>
         </property>
         <item>
          <widget class="QLabel" name="label_objectRotation">
           <property name="text">
            <string>Object rotation</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QCheckBox" name="checkBox">
           <property name="text">
            <string>CheckBox</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item row="4" column="0">
        <widget class="QPushButton" name="pushButton_resetBoth">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
       <item row="5" column="0">
        <widget class="QGroupBox" name="groupBox_3">
         <property name="title">
          <string>Type of display</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
         </property>
         <layout class="QFormLayout" name="formLayout">
          <item row="0" column="0">
           <layout class="QVBoxLayout" name="verticalLayout_2">
            <item>
             <widget class="QRadioButton" name="radioButton_color">
              <property name="text">
               <string>Color</string>
              </property>
             </widget>
            </item>
            <item>
             <widget class="QRadioButton" name="radioButton_texture">
              <property name="text">
               <string>Texture</string>
              </property>
             </widget>
            </item>
           </layout>
          </item>
         </layout>
        </widget>
       </item>
       <item row="3" column="0">
        <widget class="QPushButton" name="pushButton_resetObject">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item row="0" column="0">
     <widget class="QGroupBox" name="groupBox_openGLScreen">
      <property name="title">
       <string>OpenGLWidget</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_3">
       <item row="0" column="0">
        <layout class="QVBoxLayout" name="verticalLayout"/>
       </item>
      </layout>
     </widget>
    </item>
    <item row="1" column="0">
     <widget class="QLabel" name="label_activateScreenWithMouse">
      <property name="text">
       <string>Touch the OpenGLWidget area with your mouse to activate it.</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

 

badprogOpenGLWidget.cpp

#include "badprogOpenglWidget.h"

#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>

#include <QTimer>
#include <QDebug>
#include <QKeyEvent>
#include <QMouseEvent>

#include "input.h"

// badprog.com

// ----------------------------------------------------------------------------
BadprogOpenGLWidget::BadprogOpenGLWidget(QWidget *parent)
    : _vbo1_index(QOpenGLBuffer::IndexBuffer)
{
    //
    _transform.translate(0.0f0.0f, -5.0f);
    _transform2.translate(-1.0f0.0f, -5.0f);
    _NUMBER_OF_ELEMENTS     = 4;
    _NUMBER_OF_ELEMENTS2    = 4;
    _stateRotate            = true;
    _firstRound             = true;
    _NUMBER_OF_VAO          = 10;
    _counter                = 0;

    //
    setFocusPolicy(Qt::StrongFocus);
}

// ----------------------------------------------------------------------------
BadprogOpenGLWidget::~BadprogOpenGLWidget() {
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initializeGL() {
    //
    initializeOpenGLFunctions();
    
    //
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.5f0.4f0.3f0.5f);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glLineWidth(2);

    // ========================================================================
    //
    // Program 1 - Cube color
    //
    // ========================================================================
    initProgramColor();

    // Array of vertex
    StructVertex  arrayVertex[] = {
        // First triangle                // Color                     // texture
        { QVector3D(-0.5f, -0.5f,  0.5f), QVector3D(1.0f0.0f0.0f), QVector2D(0.0f0.0f) }, // 0
        { QVector3D0.5f, -0.5f,  0.5f), QVector3D(0.0f1.0f0.0f), QVector2D(1.0f0.0f) }, // 1
        { QVector3D(-0.5f,  0.5f,  0.5f), QVector3D(0.0f0.0f1.0f), QVector2D(0.0f1.0f) }, // 2
        { QVector3D0.5f,  0.5f,  0.5f), QVector3D(1.0f0.5f0.1f), QVector2D(1.0f1.0f) }, // 3

        { QVector3D(-0.5f, -0.5f,  -0.5f), QVector3D(1.0f0.0f0.0f), QVector2D(0.0f0.0f) }, // 4
        { QVector3D0.5f, -0.5f,  -0.5f), QVector3D(0.0f1.0f0.0f), QVector2D(1.0f0.0f) }, // 5
        { QVector3D(-0.5f,  0.5f,  -0.5f), QVector3D(0.0f0.0f1.0f), QVector2D(0.0f1.0f) }, // 6
        { QVector3D0.5f,  0.5f,  -0.5f), QVector3D(1.0f0.5f0.1f), QVector2D(1.0f1.0f) }, // 7
    };

    // Array index
    GLushort arrayIndex[] = {
        0123,
        3175,
        5476,
        6273,
        3260,
        4510
    };

    _NUMBER_OF_ELEMENTS = sizeof(arrayIndex)/ sizeof(GLushort);
    // ------------------------------------------
    // VAO 1
    // ------------------------------------------
    _vao1.create();
    _vao1.bind();

    // Buffer 1
    _vbo1_position.create();
    _vbo1_position.bind();
    _vbo1_position.setUsagePattern(QOpenGLBuffer::StreamDraw);
    _vbo1_position.allocate(arrayVertexsizeof (StructVertex) * _NUMBER_OF_ELEMENTS);

    // VBO 1
    int bpOffset  = 0; // offset position in the array
    int bpTuple   = 3; // number of element in QVector3D
    int bpStride  = sizeof (StructVertex); // number of elements to skip before the next position element in the whole line (position, color and texture --> QVector3D, QVector3D, QVector2D) inthe array

    // program
    _program1->enableAttributeArray(_attribute1_position);
    _program1->setAttributeBuffer(_attribute1_position, GL_FLOAT, bpOffsetbpTuplebpStride);

    //
    // Buffer 2
    _vbo1_index.create();
    _vbo1_index.bind();
    //    _vbo1_index.allocate(arrayIndex, sizeof (GLushort) * 4);
    _vbo1_index.allocate(arrayIndexsizeof (GLushort) * _NUMBER_OF_ELEMENTS);

    // VBO 2 (index)
    bpOffset += sizeof (QVector3D); // offset color in the array
    bpTuple     = 3;                // number of elements in the QVector3D in the array

    // program
    _program1->enableAttributeArray(_attribute1_color);
    _program1->setAttributeBuffer(_attribute1_color, GL_FLOAT, bpOffsetbpTuplebpStride);

    // Release.
    _vbo1_position.release();
    _vbo1_index.release();
    _vao1.release();

    StructVertex  arrayVertex2[] = {
        // First triangle                // Color                     // texture
        { QVector3D(-0.5f, -0.5f,  0.5f), QVector3D(1.0f0.0f0.0f), QVector2D(0.0f0.0f) }, // 0
        { QVector3D(0.0f0.5f,  -0.5f), QVector3D(1.0f0.5f0.1f), QVector2D(1.0f1.0f) }, // 1
        { QVector3D(0.5f0.5f,  0.5f), QVector3D(0.2f0.8f1.0f), QVector2D(0.0f1.0f) }, // 2

        { QVector3D(-0.7f, -0.7f,  0.5f), QVector3D(0.5f0.0f0.0f), QVector2D(0.0f0.0f) }, // 3
        { QVector3D(-0.1f, -0.5f,  0.5f), QVector3D(0.5f0.5f0.1f), QVector2D(1.0f1.0f) }, // 4
        { QVector3D(0.7f0.7f,  -0.5f), QVector3D(1.0f0.0f1.0f), QVector2D(0.0f1.0f) }, // 5

        { QVector3D(-0.3f, -0.3f,  0.5f), QVector3D(0.3f0.2f0.0f), QVector2D(0.0f0.0f) }, // 6
        { QVector3D(-0.8f, -0.8f,  -0.5f), QVector3D(0.4f0.6f0.1f), QVector2D(1.0f1.0f) }, // 7
        { QVector3D(0.6f0.6f,  -0.5f), QVector3D(1.0f0.0f1.0f), QVector2D(0.0f1.0f) }, // 8

    };

    //
    GLushort arrayIndex2[] = { 011220 };
    GLushort arrayIndex3[] = { 344553 };
    GLushort arrayIndex4[] = { 188441 };

    //
    _NUMBER_OF_ELEMENTS2 = sizeof(arrayIndex2)/ sizeof(GLushort);

    //
    createVAO(arrayVertex2arrayIndex2[0], 4);
    createVAO(arrayVertex2arrayIndex3[0], 5);
    createVAO(arrayVertex2arrayIndex4[0], 10);

    // ========================================================================
    //
    // Program 2 - Cube texture
    //
    // ========================================================================
    initProgramTexture();

    // VAO 1
    _vao1.bind();

    // VBO 1 - Position
    _vbo1_position.bind();

    // VBO 1
    bpOffset  = 0; // offset position in the array
    bpTuple   = 3; // number of elements in the QVector3D in the array
    bpStride  = sizeof (StructVertex); // number of elements to skip before the next position element in the whole line (position, color and texture --> QVector3D, QVector3D, QVector2D) inthe array

    // program
    _program2->enableAttributeArray(_attribute2_position);
    _program2->setAttributeBuffer(_attribute2_position, GL_FLOAT, bpOffsetbpTuplebpStride);

    // VBO 1 - Index
    _vbo1_index.bind();

    // VBO 1 - Textures
    bpOffset += sizeof (QVector3D) * 2; // we jump from offset 0 to offset (position + color vectors)
    bpTuple     = 2; // number of elements in the texture QVector2D in the array

    // program
    _program2->enableAttributeArray(_attribute2_texture);
    _program2->setAttributeBuffer(_attribute2_texture, GL_FLOAT, bpOffsetbpTuplebpStride);

    //
    _texture = new QOpenGLTexture(QImage(":/wood3").mirrored());

    // Release.
    _vbo1_position.release();
    _vbo1_index.release();
    _vao1.release();
    _program2->release();

    //
    initTimer();

    //
    _appareanceType = APPAREANCE_TYPE_COLOR;
    _vector3DForTranslation = QVector3D(0.0f0.0f, -4.0f); // default camera position

    //
    QObject::connect(this, &QOpenGLWidget::frameSwapped, this, &BadprogOpenGLWidget::slotFrameSwapped);

}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::createVAO(StructVertex  arrayVertexFromLoop[], GLushort &arrayIndex2int numberToLoop) {
    //
    if (_firstRound) {
        _counter = 0;
        _firstRound = false;
    } else {
        numberToLoop += _counter;
    }

    //
    while (_counter < numberToLoop) {
        // Fills the vector of VAOs with create() and bind() calls.
        _vectorOfVaos.push_back(new QOpenGLVertexArrayObject());
        _vectorOfVaos.at(_counter)->create();
        _vectorOfVaos.at(_counter)->bind();

        //
        // Creates buffer 1: the VBO Position.
        //
        _vectorOfVboPositions.push_back(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
        _vectorOfVboPositions.at(_counter)->create();
        _vectorOfVboPositions.at(_counter)->bind();

        float x;
        float y;

        float newSize = _counter;

        if (_counter > 10) {
            newSize = _counter / 10;
        }

        x = arrayVertexFromLoop->elementPosition.x() + (_counter / 20);
        y = arrayVertexFromLoop->elementPosition.y() + (_counter / 20);

        arrayVertexFromLoop[0].elementPosition.setX(x);
        arrayVertexFromLoop[0].elementPosition.setY(y);
        arrayVertexFromLoop[3].elementPosition.setX(x);
        arrayVertexFromLoop[3].elementPosition.setY(y);

        _vectorOfVboPositions.at(_counter)->allocate(arrayVertexFromLoopsizeof(StructVertex) * _NUMBER_OF_ELEMENTS2);

        // VBO 1: Where is the attribute "position" in the array.
        int bpOffset  = 0; // offset position in the array
        int bpTuple   = 3; // number of element in QVector3D
        int bpStride  = sizeof (StructVertex); // number of elements to skip before the next position element in the whole line (position, color and texture --> QVector3D, QVector3D, QVector2D) inthe array

        // program
        _program1->enableAttributeArray(_attribute1_position);
        _program1->setAttributeBuffer(_attribute1_position, GL_FLOAT, bpOffsetbpTuplebpStride);
        // VBO 1: Where is the attribute "color" in the array.
        bpOffset += sizeof (QVector3D); // offset color in the array
        bpTuple     = 3;                // number of elements in the QVector3D in the array

        // program
        _program1->enableAttributeArray(_attribute1_color);
        _program1->setAttributeBuffer(_attribute1_color, GL_FLOAT, bpOffsetbpTuplebpStride);
        //
        // Creates buffer 2: the VBO Index.
        //
        _vectorOfVboIndices.push_back(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer));
        _vectorOfVboIndices.at(_counter)->create();
        _vectorOfVboIndices.at(_counter)->bind();
        _vectorOfVboIndices.at(_counter)->allocate(&arrayIndex2sizeof (GLushort) * _NUMBER_OF_ELEMENTS2);

        ++_counter;
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initTimer() {
    //
    _stateTimer = true;

    //
    _timer = new QTimer(this);

    //
    QObject::connect(_timer, &QTimer::timeout, this, &BadprogOpenGLWidget::slotUpdateTimer);
    _timer->start(1);
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::retrieveAppearence(int appareance) {
    //
    if (APPAREANCE_TYPE_COLOR == appareance) {
        initProgramColor();
    } else if (APPAREANCE_TYPE_TEXTURE == appareance) {
        initProgramTexture();
    } else {
        qDebug() << "Problem in the place";
    }

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initProgramColor() {
    //
    _appareanceType = APPAREANCE_TYPE_COLOR;

    // Program.
    _program1 = new QOpenGLShaderProgram(this);
    _program1->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_color");
    _program1->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_color");

    // Linking.
    _program1->link();

    // Attributes.
    _attribute1_position        = _program1->attributeLocation("layoutPosition");
    _attribute1_color           = _program1->attributeLocation("layoutColor");

    // uniform
    _uniformMatrixId_model      = _program1->uniformLocation("model");

    // uniform
    _uniformMatrixId_view       = _program1->uniformLocation("view");

    // uniform projection / cameraToView
    _uniformMatrixId_projection = _program1->uniformLocation("projection");
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initProgramTexture() {
    //
    _appareanceType = APPAREANCE_TYPE_TEXTURE;

    // Program.
    _program2 = new QOpenGLShaderProgram(this);
    _program2->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_texture");
    _program2->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_texture");

    // Linking.
    _program2->link();

    // Attributes.
    _attribute2_position        = _program2->attributeLocation("layoutPosition");
    _attribute2_texture         = _program2->attributeLocation("layoutTexture");

    // uniform
    _uniformMatrixId_model      = _program2->uniformLocation("model");

    // uniform
    _uniformMatrixId_view       = _program2->uniformLocation("view");

    // uniform projection / cameraToView
    _uniformMatrixId_projection = _program2->uniformLocation("projection");
}
// ----------------------------------------------------------------------------
bool BadprogOpenGLWidget::changeTimerState() {
    //
    if (_stateTimer) {
        _timer->stop();
    } else {
        _timer->start(1);
    }

    //
    _stateTimer = !_stateTimer;

    //
    return _stateTimer;
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::slotUpdateTimer() {
    QOpenGLWidget::update();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resizeGL(int widthint height) {
    _projection.setToIdentity();
    _projection.perspective(60.0fwidth / float(height), 0.1f100.0f);
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::keyPressEvent(QKeyEvent *event) {
    //
    if (event->key() == Qt::Key_Escape) {
        close();
    } else if (event->key() == Qt::Key_W) {
        _key = event->key();
    } else if (event->key() == Qt::Key_S) {
        _key = event->key();
    } else if (event->key() == Qt::Key_A) {
        _key = event->key();
    } else if (event->key() == Qt::Key_D) {
        _key = event->key();
    } else if (event->key() == Qt::Key_E) {
        _key = event->key();
    } else if (event->key() == Qt::Key_Q) {
        _key = event->key();
    } else {
        QWidget::keyPressEvent(event);
    }

    // 
    if (event->isAutoRepeat())
    {
        event->ignore();
    }
    else
    {
        Input::registerKeyPress(event->key());
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::keyReleaseEvent(QKeyEvent *event)
{
    if (event->isAutoRepeat())
    {
        event->ignore();
    }
    else
    {
        Input::registerKeyRelease(event->key());
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    Input::registerMousePress(event->button());
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Input::registerMouseRelease(event->button());
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::slotFrameSwapped() {
    //
    Input::update();

    //
    static const float transSpeed = 0.09f;     // keyboard
    static const float rotSpeed   = 0.05f;    // mouse

    // Camera Transformation
    if (Input::buttonPressed(Qt::LeftButton)) {
        // Handle rotations
        _camera.rotate(-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
        _camera.rotate(-rotSpeed * Input::mouseDelta().y(), _camera.right());
    }

    // Handle translations
    QVector3D translation;
    if (Input::keyPressed(Qt::Key_W)) {
        translation += _camera.forward();
    }
    if (Input::keyPressed(Qt::Key_S)) {
        translation -= _camera.forward();
    }
    if (Input::keyPressed(Qt::Key_A)) {
        translation -= _camera.right();
    }
    if (Input::keyPressed(Qt::Key_D)) {
        translation += _camera.right();
    }
    if (Input::keyPressed(Qt::Key_Q)) {
        translation -= _camera.up();
    }
    if (Input::keyPressed(Qt::Key_E)) {
        translation += _camera.up();
    }

    // Resets the camera with:
    // rotation angle = 0 and position 0, 0, 0 (x, y, z)
    // translation = 0, 0, 0 (x, y, z).
    if (Input::keyPressed(Qt::Key_P)){
        resetCamera();
    }
    _camera.translate(transSpeed * translation);

    //
    if (_stateRotate) {
        _transform.rotate(1.0fQVector3D(0.4f0.3f0.3f));
        _transform2.rotate(-1.0fQVector3D(0.9f0.1f0.7f));
    } else {
        _transform.rotate(0.0fQVector3D(0.0f0.0f0.0f));
        _transform2.rotate(0.0fQVector3D(0.0f0.0f0.0f));
    }

    // Schedule a redraw
    QOpenGLWidget::update();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::rotateSwitcher(bool state) {
    //
    _stateRotate = state;

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetCamera() {
    _camera.setRotation(0.0fQVector3D(0.0f0.0f0.0f));
    _camera.setTranslation(QVector3D(0.0f0.0f0.0f));

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetObject() {
    //
    _transform.setRotation(0.0fQVector3D(0.0f0.0f0.0f));
    _transform2.setRotation(0.0fQVector3D(0.0f0.0f0.0f));

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetBoth() {
    //
    resetCamera();
    resetObject();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::managerUniformValue1() {
    // projection / cameraToView
    _program1->setUniformValue(_uniformMatrixId_projection_projection);

    //
    _program1->setUniformValue(_uniformMatrixId_view_camera.toMatrix());

    //
    _program1->setUniformValue(_uniformMatrixId_model_transform.toMatrix());
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::managerUniformValue2() {
    // projection / cameraToView
    _program1->setUniformValue(_uniformMatrixId_projection_projection);

    //
    _program1->setUniformValue(_uniformMatrixId_view_camera.toMatrix());

    //
    _program1->setUniformValue(_uniformMatrixId_model_transform2.toMatrix());
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::paintGL() {
    //
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //
    if (APPAREANCE_TYPE_COLOR == _appareanceType) {
        //
        _program1->bind();

        //
        managerUniformValue1();
        drawObject1();

        //
        managerUniformValue2();
        drawObject2();

    } else if (APPAREANCE_TYPE_TEXTURE == _appareanceType) {
        //
        _program2->bind();

        //
        _texture->bind();

        //
        managerUniformValue1();
        drawObject1();
    }

    //
    if (APPAREANCE_TYPE_COLOR == _appareanceType) {
        _program1->release();
    } else if (APPAREANCE_TYPE_TEXTURE == _appareanceType) {
        _texture->release();
        _program2->release();
    }

}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::drawObject1() {
    // VAO 1
    _vao1.bind();

    // VBO 1 - Index
    _vbo1_index.bind();
    glDrawElements(GL_TRIANGLE_STRIP, _NUMBER_OF_ELEMENTS, GL_UNSIGNED_SHORT, 0);
    _vbo1_index.release();
    _vao1.release();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::drawObject2() {
    //
    int i = 0;
    for (auto &element : _vectorOfVaos) {
        element->bind();
        _vectorOfVboIndices.at(i)->bind();
        glDrawElements(GL_LINES, _NUMBER_OF_ELEMENTS2, GL_UNSIGNED_SHORT, 0);
        _vectorOfVboIndices.at(i)->release();
        ++i;
    }
}

 

badprogOpenGLWidget.h

#ifndef BADPROG_OPENGL_WIDGET_H
#define BADPROG_OPENGL_WIDGET_H

// badprog.com

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include "camera3d.h"
#include "transform3d.h"

class QTimer;
class QWidget;
class QOpenGLTexture;
class QKeyEvent;
class QMouseEvent;

enum APPAREANCE_TYPE {
    APPAREANCE_TYPE_COLOR,
    APPAREANCE_TYPE_TEXTURE,
    APPAREANCE_TYPE_SIZE
};

struct StructVertex {
    QVector3D elementPosition;
    QVector3D elementColor;
    QVector2D elementTexture;
};

class BadprogOpenGLWidget : public QOpenGLWidgetprotected QOpenGLFunctions
{
public:
    BadprogOpenGLWidget();
    BadprogOpenGLWidget(QWidget *parent = nullptr);
    ~BadprogOpenGLWidget() override;

    //
    bool changeTimerState();
    void initProgramColor();
    void initProgramTexture();
    void initTimer();
    void retrieveAppearence(int appareance);
    void rotateSwitcher(bool state);
    void resetCamera();
    void resetObject();
    void resetBoth();
    void managerUniformValue1();
    void managerUniformValue2();
    void drawObject1();
    void drawObject2();
    void createVAO(StructVertex  arrayVertexFromLoop[], GLushort &arrayIndex2int numberToLoop);

public slots:
    void slotUpdateTimer();
    void slotFrameSwapped();

protected:
    // QOpenGLWidget interface
    void initializeGL() override;
    void resizeGL(int wint hoverride;
    void paintGL() override;
    void keyPressEvent(QKeyEvent *eventoverride; // From QWidget
    void keyReleaseEvent(QKeyEvent *eventoverride; // From QWidget
    void mousePressEvent(QMouseEvent *eventoverride; // From QWidget
    void mouseReleaseEvent(QMouseEvent *eventoverride; // From QWidget

    // methods
private:
    // variables
private:
    //
    QMatrix4x4 _projection;
    GLuint _uniformMatrixId_model;
    GLuint _uniformMatrixId_view;
    GLuint _uniformMatrixId_projection;
    int _NUMBER_OF_ELEMENTS;
    int _NUMBER_OF_ELEMENTS2;
    QVector3D _vector3DForTranslation;
    std::vector<QOpenGLVertexArrayObject *> _vectorOfVaos;
    std::vector<QOpenGLBuffer *> _vectorOfVboPositions;
    std::vector<QOpenGLBuffer *> _vectorOfVboIndices;

    // ========================================================================
    // Color
    // ========================================================================
    QOpenGLShaderProgram* _program1;
    GLuint _attribute1_position;
    GLuint _attribute1_color;

    // ========================================================================
    // Texture
    // ========================================================================
    QOpenGLShaderProgram* _program2;
    GLuint _attribute2_position;
    GLuint _attribute2_texture;

    //
    QOpenGLTexture *_texture;

    // VAO, VBO
    QOpenGLVertexArrayObject _vao1;
    QOpenGLBuffer _vbo1_position;
    QOpenGLBuffer _vbo1_index;

    //
    QTimer_timer;
    bool _stateTimer;
    APPAREANCE_TYPE _appareanceType;

    int _key;

    Camera3D _camera;
    Transform3D _transform;
    Transform3D _transform2;

    bool _stateRotate;
    float _NUMBER_OF_VAO;
    float _counter;
    bool _firstRound;
};

#endif // !BADPROG_OPENGL_WIDGET_H

 

You also need an image for the texture.

In the code it's called "imageForTexture" from the badprog.qrc file.

Just add image_for_texture.png where there are other files.

Conclusion

If you see the window open then it's a good sign. 

Indeed, you are now able to add all widgets of your choice in your GUI to manipulate every kind of OpenGL shape.

Quite interesting.

Good job you did it. cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.