// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example cube
    \ingroup examples-widgets-opengl
    \title Cube OpenGL ES 2.0 example
    \examplecategory {Data Visualization & 3D}

    \brief Shows how to manually rotate a textured 3D cube with user input.

    The Cube OpenGL ES 2.0 example shows how to manually rotate a textured 3D
    cube with user input, using OpenGL ES 2.0 with Qt. It shows how to
    handle polygon geometries efficiently and how to write a simple vertex and
    fragment shader for a programmable graphics pipeline. In addition it
    shows how to use quaternions for representing 3D object orientation.

    This example has been written for OpenGL ES 2.0 but it works also on
    desktop OpenGL because this example is simple enough and for the
    most parts desktop OpenGL API is same. It compiles also without OpenGL
    support but then it just shows a label stating that OpenGL support is
    required.

    \image cube.png Screenshot of the Cube example running on N900

    The example consist of two classes:

    \list
    \li \c MainWidget extends QOpenGLWidget and contains OpenGL ES 2.0
        initialization and drawing and mouse and timer event handling
    \li \c GeometryEngine handles polygon geometries. Transfers polygon geometry
        to vertex buffer objects and draws geometries from vertex buffer objects.
    \endlist

    We'll start by initializing OpenGL ES 2.0 in \c MainWidget.

    \tableofcontents

    \section1 Initializing OpenGL ES 2.0

    Since OpenGL ES 2.0 doesn't support fixed graphics pipeline anymore it has to
    be implemented by ourselves. This makes graphics pipeline very flexible but
    in the same time it becomes more difficult because user has to implement graphics
    pipeline to get even the simplest example running. It also makes graphics pipeline
    more efficient because user can decide what kind of pipeline is needed for the
    application.

    First we have to implement vertex shader. It gets vertex data and
    model-view-projection matrix (MVP) as parameters. It transforms vertex position
    using MVP matrix to screen space and passes texture coordinate to
    fragment shader. Texture coordinate will be automatically interpolated on polygon
    faces.

    \snippet cube/vshader.glsl 0

    After that we need to implement second part of the graphics pipeline - fragment
    shader. For this exercise we need to implement fragment shader that handles
    texturing. It gets interpolated texture coordinate as a parameter and looks up
    fragment color from the given texture.

    \snippet cube/fshader.glsl 0

    Using QOpenGLShaderProgram we can compile, link and bind shader code to
    graphics pipeline. This code uses Qt Resource files to access shader source code.

    \snippet cube/mainwidget.cpp 3

    The following code enables depth buffering and back face culling.

    \snippet cube/mainwidget.cpp 2

    \section1 Loading Textures from Qt Resource Files

    The \c QOpenGLWidget interface implements methods for loading textures from QImage
    to OpenGL texture memory. We still need to use OpenGL provided functions for
    specifying the OpenGL texture unit and configuring texture filtering options.

    \snippet cube/mainwidget.cpp 4

    \section1 Cube Geometry

    There are many ways to render polygons in OpenGL but the most efficient way is
    to use only triangle strip primitives and render vertices from graphics hardware
    memory. OpenGL has a mechanism to create buffer objects to this memory area and
    transfer vertex data to these buffers. In OpenGL terminology these are referred
    as Vertex Buffer Objects (VBO).

    \image cube_faces.png Cube faces and vertices

    This is how cube faces break down to triangles. Vertices are ordered this way
    to get vertex ordering correct using triangle strips. OpenGL determines triangle
    front and back face based on vertex ordering. By default OpenGL uses
    counter-clockwise order for front faces. This information is used by back face
    culling which improves rendering performance by not rendering back faces of the
    triangles. This way graphics pipeline can omit rendering sides of the triangle that
    aren't facing towards screen.

    Creating vertex buffer objects and transferring data to them is quite simple using
    QOpenGLBuffer. MainWidget makes sure the GeometryEngine instance is created and
    destroyed with the OpenGL context current. This way we can use OpenGL resources
    in the constructor and perform proper cleanup in the destructor.

    \snippet cube/geometryengine.cpp 0

    \snippet cube/geometryengine.cpp 1

    Drawing primitives from VBOs and telling programmable graphics pipeline how to
    locate vertex data requires few steps. First we need to bind VBOs to be used.
    After that we bind shader program attribute names and configure what
    kind of data it has in the bound VBO. Finally we'll draw triangle
    strip primitives using indices from the other VBO.

    \snippet cube/geometryengine.cpp 2

    \section1 Perspective Projection

    Using \c QMatrix4x4 helper methods it's really easy to calculate perpective
    projection matrix. This matrix is used to project vertices to screen space.

    \snippet cube/mainwidget.cpp 5

    \section1 Orientation of the 3D Object

    Quaternions are handy way to represent orientation of the 3D object. Quaternions
    involve quite complex mathematics but fortunately all the necessary mathematics
    behind quaternions is implemented in \c QQuaternion. That allows us to store
    cube orientation in quaternion and rotating cube around given axis is quite
    simple.

    The following code calculates rotation axis and angular speed based on mouse events.

    \snippet cube/mainwidget.cpp 0

    \c QBasicTimer is used to animate scene and update cube orientation. Rotations
    can be concatenated simply by multiplying quaternions.

    \snippet cube/mainwidget.cpp 1

    Model-view matrix is calculated using the quaternion and by moving world by Z axis.
    This matrix is multiplied with the projection matrix to get MVP matrix for shader
    program.

    \snippet cube/mainwidget.cpp 6

*/
