#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.0f, 0.0f, -5.0f);
    _transform2.translate(-1.0f, 0.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.5f, 0.4f, 0.3f, 0.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.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f) }, // 0
        { QVector3D( 0.5f, -0.5f,  0.5f), QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f) }, // 1
        { QVector3D(-0.5f,  0.5f,  0.5f), QVector3D(0.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f) }, // 2
        { QVector3D( 0.5f,  0.5f,  0.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f) }, // 3
        { QVector3D(-0.5f, -0.5f,  -0.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f) }, // 4
        { QVector3D( 0.5f, -0.5f,  -0.5f), QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f) }, // 5
        { QVector3D(-0.5f,  0.5f,  -0.5f), QVector3D(0.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f) }, // 6
        { QVector3D( 0.5f,  0.5f,  -0.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f) }, // 7
    };
    // Array index
    GLushort arrayIndex[] = {
        0, 1, 2, 3,
        3, 1, 7, 5,
        5, 4, 7, 6,
        6, 2, 7, 3,
        3, 2, 6, 0,
        4, 5, 1, 0
    };
    _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(arrayVertex, sizeof (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, bpOffset, bpTuple, bpStride);
    //
    // Buffer 2
    _vbo1_index.create();
    _vbo1_index.bind();
    //    _vbo1_index.allocate(arrayIndex, sizeof (GLushort) * 4);
    _vbo1_index.allocate(arrayIndex, sizeof (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, bpOffset, bpTuple, bpStride);
    // Release.
    _vbo1_position.release();
    _vbo1_index.release();
    _vao1.release();
    StructVertex  arrayVertex2[] = {
        // First triangle                // Color                     // texture
        { QVector3D(-0.5f, -0.5f,  0.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f) }, // 0
        { QVector3D(0.0f, 0.5f,  -0.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f) }, // 1
        { QVector3D(0.5f, 0.5f,  0.5f), QVector3D(0.2f, 0.8f, 1.0f), QVector2D(0.0f, 1.0f) }, // 2
        { QVector3D(-0.7f, -0.7f,  0.5f), QVector3D(0.5f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f) }, // 3
        { QVector3D(-0.1f, -0.5f,  0.5f), QVector3D(0.5f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f) }, // 4
        { QVector3D(0.7f, 0.7f,  -0.5f), QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f) }, // 5
        { QVector3D(-0.3f, -0.3f,  0.5f), QVector3D(0.3f, 0.2f, 0.0f), QVector2D(0.0f, 0.0f) }, // 6
        { QVector3D(-0.8f, -0.8f,  -0.5f), QVector3D(0.4f, 0.6f, 0.1f), QVector2D(1.0f, 1.0f) }, // 7
        { QVector3D(0.6f, 0.6f,  -0.5f), QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f) }, // 8
    };
    //
    GLushort arrayIndex2[] = { 0, 1, 1, 2, 2, 0 };
    GLushort arrayIndex3[] = { 3, 4, 4, 5, 5, 3 };
    GLushort arrayIndex4[] = { 1, 8, 8, 4, 4, 1 };
    //
    _NUMBER_OF_ELEMENTS2 = sizeof(arrayIndex2)/ sizeof(GLushort);
    //
    createVAO(arrayVertex2, arrayIndex2[0], 4);
    createVAO(arrayVertex2, arrayIndex3[0], 5);
    createVAO(arrayVertex2, arrayIndex4[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, bpOffset, bpTuple, bpStride);
    // 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, bpOffset, bpTuple, bpStride);
    //
    _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.0f, 0.0f, -4.0f); // default camera position
    //
    QObject::connect(this, &QOpenGLWidget::frameSwapped, this, &BadprogOpenGLWidget::slotFrameSwapped);
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::createVAO(StructVertex  arrayVertexFromLoop[], GLushort &arrayIndex2, int 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(arrayVertexFromLoop, sizeof(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, bpOffset, bpTuple, bpStride);
        // 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, bpOffset, bpTuple, bpStride);
        //
        // 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(&arrayIndex2, sizeof (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 width, int height) {
    _projection.setToIdentity();
    _projection.perspective(60.0f, width / float(height), 0.1f, 100.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.0f, QVector3D(0.4f, 0.3f, 0.3f));
        _transform2.rotate(-1.0f, QVector3D(0.9f, 0.1f, 0.7f));
    } else {
        _transform.rotate(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
        _transform2.rotate(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
    }
    // Schedule a redraw
    QOpenGLWidget::update();
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::rotateSwitcher(bool state) {
    //
    _stateRotate = state;
    //
    Input::reset();
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetCamera() {
    _camera.setRotation(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
    _camera.setTranslation(QVector3D(0.0f, 0.0f, 0.0f));
    //
    Input::reset();
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetObject() {
    //
    _transform.setRotation(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
    _transform2.setRotation(0.0f, QVector3D(0.0f, 0.0f, 0.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;
    }
}
 
Add new comment