void QOpenGLTextureBlitterPrivate::blit(GLuint texture,
                                        const QMatrix4x4 &vertexTransform,
                                        const QMatrix3x3 &textureTransform)
{
    TextureBinder binder(texture);
    prepareProgram(vertexTransform);

    program->setUniformValue(textureTransformUniformPos, textureTransform);
    textureMatrixUniformState = User;

    QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, 0, 6);
}
/** @brief Iterate Functional Test cases.
 *
 *  @return Iteration result.
 */
tcu::TestCase::IterateResult TextureCubeMapArrayETC2Support::iterate(void)
{
	prepareFramebuffer();
	prepareProgram();
	prepareVertexArrayObject();
	prepareTexture();
	draw();

	if (isRenderedImageValid())
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");

	clean();
	return STOP;
}
void QOpenGLTextureBlitterPrivate::blit(GLuint texture,
                                        const QMatrix4x4 &vertexTransform,
                                        QOpenGLTextureBlitter::Origin origin)
{
    TextureBinder binder(texture);
    prepareProgram(vertexTransform);

    if (origin == QOpenGLTextureBlitter::OriginTopLeft) {
        if (textureMatrixUniformState != IdentityFlipped) {
            QMatrix3x3 flipped;
            flipped(1,1) = -1;
            flipped(1,2) = 1;
            program->setUniformValue(textureTransformUniformPos, flipped);
            textureMatrixUniformState = IdentityFlipped;
        }
    } else if (textureMatrixUniformState != Identity) {
        program->setUniformValue(textureTransformUniformPos, QMatrix3x3());
        textureMatrixUniformState = Identity;
    }

    QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, 0, 6);
}
Esempio n. 4
0
int main() {
    if(glfwInit() == GL_FALSE) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }
    defer(glfwTerminate());

    glfwDefaultWindowHints();

    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_SAMPLES, 4);

    GLFWwindow* window = glfwCreateWindow(800, 600, "Models", nullptr, nullptr);
    if(window == nullptr) {
        std::cerr << "Failed to open GLFW window" << std::endl;
        return -1;
    }
    defer(glfwDestroyWindow(window));

    glfwMakeContextCurrent(window);

    if(glxwInit()) {
        std::cerr << "Failed to init GLXW" << std::endl;
        return -1;
    }

    glfwSwapInterval(1);
    glfwSetWindowSizeCallback(window, windowSizeCallback);
    glfwShowWindow(window);

    bool errorFlag = false;
    std::vector<GLuint> shaders;

    GLuint vertexShaderId = loadShader("shaders/vertexShader.glsl", GL_VERTEX_SHADER, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to load vertex shader (invalid working directory?)" << std::endl;
        return -1;
    }

    shaders.push_back(vertexShaderId);

    GLuint fragmentShaderId = loadShader("shaders/fragmentShader.glsl", GL_FRAGMENT_SHADER, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to load fragment shader (invalid working directory?)" << std::endl;
        return -1;
    }
    shaders.push_back(fragmentShaderId);

    GLuint programId = prepareProgram(shaders, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to prepare program" << std::endl;
        return -1;
    }
    defer(glDeleteProgram(programId));

    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);

    // === prepare textures ===
    GLuint textureArray[3];
    int texturesNum = sizeof(textureArray)/sizeof(textureArray[0]);
    glGenTextures(texturesNum, textureArray);
    defer(glDeleteTextures(texturesNum, textureArray));

    GLuint grassTexture = textureArray[0];
    GLuint skyboxTexture = textureArray[1];
    GLuint towerTexture = textureArray[2];

    if(!loadDDSTexture("textures/grass.dds", grassTexture)) return -1;

    if(!loadDDSTexture("textures/skybox.dds", skyboxTexture)) return -1;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    if(!loadDDSTexture("textures/tower.dds", towerTexture)) return -1;

    // === prepare VAOs ===
    GLuint vaoArray[3];
    int vaosNum = sizeof(vaoArray)/sizeof(vaoArray[0]);
    glGenVertexArrays(vaosNum, vaoArray);
    defer(glDeleteVertexArrays(vaosNum, vaoArray));

    GLuint grassVAO = vaoArray[0];
    GLuint skyboxVAO = vaoArray[1];
    GLuint towerVAO = vaoArray[2];

    // === prepare VBOs ===
    GLuint vboArray[6];
    int vbosNum = sizeof(vboArray)/sizeof(vboArray[0]);
    glGenBuffers(vbosNum, vboArray);
    defer(glDeleteBuffers(vbosNum, vboArray));

    GLuint grassVBO = vboArray[0];
    GLuint skyboxVBO = vboArray[1];
    GLuint towerVBO = vboArray[2];
    GLuint grassIndicesVBO = vboArray[3];
    GLuint skyboxIndicesVBO = vboArray[4];
    GLuint towerIndicesVBO = vboArray[5];

    // === load models ===

    GLsizei grassIndicesNumber, skyboxIndicesNumber, towerIndicesNumber;
    GLenum grassIndexType, skyboxIndexType, towerIndexType;
    if(!modelLoad("models/grass.emd", grassVAO, grassVBO, grassIndicesVBO, &grassIndicesNumber, &grassIndexType)) return -1;
    if(!modelLoad("models/skybox.emd", skyboxVAO, skyboxVBO, skyboxIndicesVBO, &skyboxIndicesNumber, &skyboxIndexType)) return -1;
    if(!modelLoad("models/tower.emd", towerVAO, towerVBO, towerIndicesVBO, &towerIndicesNumber, &towerIndexType)) return -1;

    glm::mat4 projection = glm::perspective(70.0f, 4.0f / 3.0f, 0.3f, 250.0f);

    GLint matrixId = glGetUniformLocation(programId, "MVP");
    GLint samplerId = glGetUniformLocation(programId, "textureSampler");

    auto startTime = std::chrono::high_resolution_clock::now();
    auto prevTime = startTime;

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // hide cursor

    Camera camera(window, glm::vec3(0, 0, 5), 3.14f /* toward -Z */, 0.0f /* look at the horizon */);

    glEnable(GL_DOUBLEBUFFER);
    glEnable(GL_CULL_FACE);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glClearColor(0, 0, 0, 1);

    glUniform1i(samplerId, 0);

    while(glfwWindowShouldClose(window) == GL_FALSE) {
        if(glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) break;

        if(glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) {
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        }

        if(glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) {
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        }

        auto currentTime = std::chrono::high_resolution_clock::now();
        float startDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
        float prevDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - prevTime).count();
        prevTime = currentTime;

        float rotationTimeMs = 100000.0f;
        float currentRotation = startDeltaTimeMs / rotationTimeMs;
        float islandAngle = 360.0f*(currentRotation - (long)currentRotation);

        glm::mat4 view;
        camera.getViewMatrix(prevDeltaTimeMs, &view);
        glm::mat4 vp = projection * view;

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(programId);

        glm::mat4 towerMVP = vp * glm::rotate(islandAngle, 0.0f, 1.0f, 0.0f) * glm::translate(-1.5f, -1.0f, -1.5f);

        glBindTexture(GL_TEXTURE_2D, towerTexture);
        glBindVertexArray(towerVAO);
        glUniformMatrix4fv(matrixId, 1, GL_FALSE, &towerMVP[0][0]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, towerIndicesVBO);
        glDrawElements(GL_TRIANGLES, towerIndicesNumber, towerIndexType, nullptr);

        glm::mat4 grassMVP = vp * glm::rotate(islandAngle, 0.0f, 1.0f, 0.0f) * glm::translate(0.0f, -1.0f, 0.0f);

        glBindTexture(GL_TEXTURE_2D, grassTexture);
        glBindVertexArray(grassVAO);
        glUniformMatrix4fv(matrixId, 1, GL_FALSE, &grassMVP[0][0]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, grassIndicesVBO);
        glDrawElements(GL_TRIANGLES, grassIndicesNumber, grassIndexType, nullptr);

        glm::vec3 cameraPos;
        camera.getPosition(&cameraPos);
        glm::mat4 skyboxMatrix = glm::translate(cameraPos) * glm::scale(100.0f,100.0f,100.0f);
        glm::mat4 skyboxMVP = vp * skyboxMatrix;

        // TODO: implement modelDraw procedure (or maybe Model object?)
        glBindTexture(GL_TEXTURE_2D, skyboxTexture);
        glBindVertexArray(skyboxVAO);
        glUniformMatrix4fv(matrixId, 1, GL_FALSE, &skyboxMVP[0][0]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, skyboxIndicesVBO);
        glDrawElements(GL_TRIANGLES, skyboxIndicesNumber, skyboxIndexType, nullptr);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    return 0;
}
Esempio n. 5
0
int main() {
    if(glfwInit() == GL_FALSE) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }
    defer(glfwTerminate());

    glfwDefaultWindowHints();

    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_SAMPLES, 4);

    GLFWwindow* window = glfwCreateWindow(800, 600, "Lighting", nullptr, nullptr);
    if(window == nullptr) {
        std::cerr << "Failed to open GLFW window" << std::endl;
        return -1;
    }
    defer(glfwDestroyWindow(window));

    glfwMakeContextCurrent(window);

    if(glxwInit()) {
        std::cerr << "Failed to init GLXW" << std::endl;
        return -1;
    }

    glfwSwapInterval(1);
    glfwSetWindowSizeCallback(window, windowSizeCallback);
    glfwShowWindow(window);

    bool errorFlag = false;
    std::vector<GLuint> shaders;

    GLuint vertexShaderId = loadShader("shaders/vertexShader.glsl", GL_VERTEX_SHADER, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to load vertex shader (invalid working directory?)" << std::endl;
        return -1;
    }

    shaders.push_back(vertexShaderId);

    GLuint fragmentShaderId = loadShader("shaders/fragmentShader.glsl", GL_FRAGMENT_SHADER, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to load fragment shader (invalid working directory?)" << std::endl;
        return -1;
    }
    shaders.push_back(fragmentShaderId);

    GLuint programId = prepareProgram(shaders, &errorFlag);
    if(errorFlag) {
        std::cerr << "Failed to prepare program" << std::endl;
        return -1;
    }
    defer(glDeleteProgram(programId));

    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);

    // === prepare textures ===
    GLuint textureArray[6];
    int texturesNum = sizeof(textureArray)/sizeof(textureArray[0]);
    glGenTextures(texturesNum, textureArray);
    defer(glDeleteTextures(texturesNum, textureArray));

    GLuint grassTexture = textureArray[0];
    GLuint skyboxTexture = textureArray[1];
    GLuint towerTexture = textureArray[2];
    GLuint garkGreenTexture = textureArray[3];
    GLuint redTexture = textureArray[4];
    GLuint blueTexture = textureArray[5];

    if(!loadDDSTexture("textures/grass.dds", grassTexture)) return -1;

    if(!loadDDSTexture("textures/skybox.dds", skyboxTexture)) return -1;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    if(!loadDDSTexture("textures/tower.dds", towerTexture)) return -1;

    loadOneColorTexture(0.05f, 0.5f, 0.1f, garkGreenTexture);
    loadOneColorTexture(1.0f, 0.0f, 0.0f, redTexture);
    loadOneColorTexture(0.0f, 0.0f, 1.0f, blueTexture);

    // === prepare VAOs ===
    GLuint vaoArray[5];
    int vaosNum = sizeof(vaoArray)/sizeof(vaoArray[0]);
    glGenVertexArrays(vaosNum, vaoArray);
    defer(glDeleteVertexArrays(vaosNum, vaoArray));

    GLuint grassVAO = vaoArray[0];
    GLuint skyboxVAO = vaoArray[1];
    GLuint towerVAO = vaoArray[2];
    GLuint torusVAO = vaoArray[3];
    GLuint sphereVAO = vaoArray[4];

    // === prepare VBOs ===
    GLuint vboArray[10];
    int vbosNum = sizeof(vboArray)/sizeof(vboArray[0]);
    glGenBuffers(vbosNum, vboArray);
    defer(glDeleteBuffers(vbosNum, vboArray));

    GLuint grassVBO = vboArray[0];
    GLuint grassIndicesVBO = vboArray[1];
    GLuint skyboxVBO = vboArray[2];
    GLuint skyboxIndicesVBO = vboArray[3];
    GLuint towerVBO = vboArray[4];
    GLuint towerIndicesVBO = vboArray[5];
    GLuint torusVBO = vboArray[6];
    GLuint torusIndicesVBO = vboArray[7];
    GLuint sphereVBO = vboArray[8];
    GLuint sphereIndicesVBO = vboArray[9];
    // === load models ===

    GLsizei grassIndicesNumber, skyboxIndicesNumber, towerIndicesNumber, torusIndicesNumber, sphereIndicesNumber;
    GLenum grassIndexType, skyboxIndexType, towerIndexType, torusIndexType, sphereIndexType;
    if(!modelLoad("models/grass.emd", grassVAO, grassVBO, grassIndicesVBO, &grassIndicesNumber, &grassIndexType)) return -1;
    if(!modelLoad("models/skybox.emd", skyboxVAO, skyboxVBO, skyboxIndicesVBO, &skyboxIndicesNumber, &skyboxIndexType)) return -1;
    if(!modelLoad("models/tower.emd", towerVAO, towerVBO, towerIndicesVBO, &towerIndicesNumber, &towerIndexType)) return -1;
    if(!modelLoad("models/torus.emd", torusVAO, torusVBO, torusIndicesVBO, &torusIndicesNumber, &torusIndexType)) return -1;
    if(!modelLoad("models/sphere.emd", sphereVAO, sphereVBO, sphereIndicesVBO, &sphereIndicesNumber, &sphereIndexType)) return -1;

    glm::mat4 projection = glm::perspective(70.0f, 4.0f / 3.0f, 0.3f, 250.0f);

    GLint uniformMVP = getUniformLocation(programId, "MVP");
    GLint uniformM = getUniformLocation(programId, "M");
    GLint uniformTextureSample = getUniformLocation(programId, "textureSampler");
    GLint uniformCameraPos = getUniformLocation(programId, "cameraPos");

    GLint uniformMaterialSpecularFactor = getUniformLocation(programId, "materialSpecularFactor");
    GLint uniformMaterialSpecularIntensity = getUniformLocation(programId, "materialSpecularIntensity");
    GLint uniformMaterialEmission = getUniformLocation(programId, "materialEmission");

    auto startTime = std::chrono::high_resolution_clock::now();
    auto prevTime = startTime;

    Camera camera(window, glm::vec3(0, 0, 5), 3.14f /* toward -Z */, 0.0f /* look at the horizon */);

    glEnable(GL_DOUBLEBUFFER);
    glEnable(GL_CULL_FACE);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glClearColor(0, 0, 0, 1);

    glUseProgram(programId);

    glUniform1i(uniformTextureSample, 0);

    bool directionalLightEnabled = true;
    bool pointLightEnabled = true;
    bool spotLightEnabled = true;
    bool wireframesModeEnabled = false;
    float lastKeyPressCheck = 0.0;
    const float keyPressCheckIntervalMs = 250.0f;

    setupLights(programId, directionalLightEnabled, pointLightEnabled, spotLightEnabled);

    while(glfwWindowShouldClose(window) == GL_FALSE) {
        if(glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) break;

        auto currentTime = std::chrono::high_resolution_clock::now();
        float startDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
        float prevDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - prevTime).count();
        prevTime = currentTime;

        float rotationTimeMs = 100000.0f;
        float currentRotation = startDeltaTimeMs / rotationTimeMs;
        float islandAngle = 360.0f*(currentRotation - (long)currentRotation);

        if(startDeltaTimeMs - lastKeyPressCheck > keyPressCheckIntervalMs) {
            if(glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) {
                lastKeyPressCheck = startDeltaTimeMs;
                wireframesModeEnabled = !wireframesModeEnabled;
                if(wireframesModeEnabled) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            }
            if(glfwGetKey(window, GLFW_KEY_M) == GLFW_PRESS) {
                lastKeyPressCheck = startDeltaTimeMs;
                bool enabled = camera.getMouseInterception();
                camera.setMouseInterception(!enabled);
            }
            if(glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) {
                lastKeyPressCheck = startDeltaTimeMs;
                directionalLightEnabled = !directionalLightEnabled;
                setupLights(programId, directionalLightEnabled, pointLightEnabled, spotLightEnabled);
            }
            if(glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) {
                lastKeyPressCheck = startDeltaTimeMs;
                pointLightEnabled = !pointLightEnabled;
                setupLights(programId, directionalLightEnabled, pointLightEnabled, spotLightEnabled);
            }
            if(glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) {
                lastKeyPressCheck = startDeltaTimeMs;
                spotLightEnabled = !spotLightEnabled;
                setupLights(programId, directionalLightEnabled, pointLightEnabled, spotLightEnabled);
            }
        }

        glm::vec3 cameraPos;
        camera.getPosition(&cameraPos);

        glUniform3f(uniformCameraPos, cameraPos.x, cameraPos.y, cameraPos.z);

        glm::mat4 view;
        camera.getViewMatrix(prevDeltaTimeMs, &view);
        glm::mat4 vp = projection * view;

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // TODO implement ModelLoader and Model classes
        // tower

        glm::mat4 towerM = glm::rotate(islandAngle, 0.0f, 1.0f, 0.0f) * glm::translate(-1.5f, -1.0f, -1.5f);
        glm::mat4 towerMVP = vp * towerM;

        glBindTexture(GL_TEXTURE_2D, towerTexture);
        glBindVertexArray(towerVAO);
        glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &towerMVP[0][0]);
        glUniformMatrix4fv(uniformM, 1, GL_FALSE, &towerM[0][0]);
        glUniform1f(uniformMaterialSpecularFactor, 1.0f);
        glUniform1f(uniformMaterialSpecularIntensity, 0.0f);
        glUniform3f(uniformMaterialEmission, 0.0f, 0.0f, 0.0f);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, towerIndicesVBO);
        glDrawElements(GL_TRIANGLES, towerIndicesNumber, towerIndexType, nullptr);

        // torus

        glm::mat4 torusM = glm::translate(0.0f, 1.0f, 0.0f) * glm::rotate((60.0f - 3.0f*islandAngle), 0.0f, 0.5f, 0.0f);
        glm::mat4 torusMVP = vp * torusM;

        glBindTexture(GL_TEXTURE_2D, garkGreenTexture);
        glBindVertexArray(torusVAO);
        glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &torusMVP[0][0]);
        glUniformMatrix4fv(uniformM, 1, GL_FALSE, &torusM[0][0]);
        glUniform1f(uniformMaterialSpecularFactor, 1.0f);
        glUniform1f(uniformMaterialSpecularIntensity, 1.0f);
        glUniform3f(uniformMaterialEmission, 0.0f, 0.0f, 0.0f);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, torusIndicesVBO);
        glDrawElements(GL_TRIANGLES, torusIndicesNumber, torusIndexType, nullptr);

        // grass

        glm::mat4 grassM = glm::rotate(islandAngle, 0.0f, 1.0f, 0.0f) * glm::translate(0.0f, -1.0f, 0.0f);
        glm::mat4 grassMVP = vp * grassM;

        glBindTexture(GL_TEXTURE_2D, grassTexture);
        glBindVertexArray(grassVAO);
        glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &grassMVP[0][0]);
        glUniformMatrix4fv(uniformM, 1, GL_FALSE, &grassM[0][0]);
        glUniform1f(uniformMaterialSpecularFactor, 32.0f);
        glUniform1f(uniformMaterialSpecularIntensity, 2.0f);
        glUniform3f(uniformMaterialEmission, 0.0f, 0.0f, 0.0f);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, grassIndicesVBO);
        glDrawElements(GL_TRIANGLES, grassIndicesNumber, grassIndexType, nullptr);

        // skybox

        glm::mat4 skyboxM = glm::translate(cameraPos) * glm::scale(100.0f,100.0f,100.0f);
        glm::mat4 skyboxMVP = vp * skyboxM;

        glBindTexture(GL_TEXTURE_2D, skyboxTexture);
        glBindVertexArray(skyboxVAO);
        glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &skyboxMVP[0][0]);
        glUniformMatrix4fv(uniformM, 1, GL_FALSE, &skyboxM[0][0]);
        glUniform1f(uniformMaterialSpecularFactor, 1.0f);
        glUniform1f(uniformMaterialSpecularIntensity, 0.0f);
        glUniform3f(uniformMaterialEmission, 0.0f, 0.0f, 0.0f);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, skyboxIndicesVBO);
        glDrawElements(GL_TRIANGLES, skyboxIndicesNumber, skyboxIndexType, nullptr);

        // point light source
        if(pointLightEnabled) {
            glm::mat4 pointLightM = glm::translate(pointLightPos);
            glm::mat4 pointLightMVP = vp * pointLightM;

            glBindTexture(GL_TEXTURE_2D, redTexture);
            glBindVertexArray(sphereVAO);
            glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &pointLightMVP[0][0]);
            glUniformMatrix4fv(uniformM, 1, GL_FALSE, &pointLightM[0][0]);
            glUniform1f(uniformMaterialSpecularFactor, 1.0f);
            glUniform1f(uniformMaterialSpecularIntensity, 1.0f);
            glUniform3f(uniformMaterialEmission, 0.5f, 0.5f, 0.5f);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereIndicesVBO);
            glDrawElements(GL_TRIANGLES, sphereIndicesNumber, sphereIndexType, nullptr);
        }

        // spot light source
        if(spotLightEnabled) {
            glm::mat4 spotLightM = glm::translate(spotLightPos);
            glm::mat4 spotLightMVP = vp * spotLightM;

            glBindTexture(GL_TEXTURE_2D, blueTexture);
            glBindVertexArray(sphereVAO);
            glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, &spotLightMVP[0][0]);
            glUniformMatrix4fv(uniformM, 1, GL_FALSE, &spotLightM[0][0]);
            glUniform1f(uniformMaterialSpecularFactor, 1.0f);
            glUniform1f(uniformMaterialSpecularIntensity, 1.0f);
            glUniform3f(uniformMaterialEmission, 0.5f, 0.5f, 0.5f);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereIndicesVBO);
            glDrawElements(GL_TRIANGLES, sphereIndicesNumber, sphereIndexType, nullptr);
        }

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    return 0;
}
int main() {
  if(glfwInit() == GL_FALSE) {
    std::cerr << "Failed to initialize GLFW" << std::endl;
    return -1;
  }
  defer(std::cout << "Calling glfwTerminate()" << std::endl; glfwTerminate());

  glfwDefaultWindowHints();

  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

  GLFWwindow* window = glfwCreateWindow(800, 600, "Rotating cube", nullptr, nullptr);
  if(window == nullptr) {
    std::cerr << "Failed to open GLFW window" << std::endl;
    return -1;
  }
  defer(std::cout << "Calling glfwDestroyWindow()" << std::endl; glfwDestroyWindow(window));

  glfwMakeContextCurrent(window);

  if(glxwInit()) {
    std::cerr << "Failed to init GLXW" << std::endl;
    return -1;
  }

  glfwSwapInterval(1);

  glfwSetWindowSizeCallback(window, windowSizeCallback);

  glfwShowWindow(window);

  bool errorFlag = false;
  std::vector<GLuint> shaders;

  GLuint vertexShaderId = loadShader("shaders/vertexShader.glsl", GL_VERTEX_SHADER, &errorFlag);
  if(errorFlag) {
    std::cerr << "Failed to load vertex shader (invalid working directory?)" << std::endl;
    return -1;
  }

  shaders.push_back(vertexShaderId);

  GLuint fragmentShaderId = loadShader("shaders/fragmentShader.glsl", GL_FRAGMENT_SHADER, &errorFlag);
  if(errorFlag) {
    std::cerr << "Failed to load fragment shader (invalid working directory?)" << std::endl;
    return -1;
  }
  shaders.push_back(fragmentShaderId);

  GLuint programId = prepareProgram(shaders, &errorFlag);
  if(errorFlag) {
    std::cerr << "Failed to prepare program" << std::endl;
    return -1;
  }
  defer(glDeleteProgram(programId));

  glDeleteShader(vertexShaderId);
  glDeleteShader(fragmentShaderId);

  GLuint vertexVBO;
  glGenBuffers(1, &vertexVBO);
  defer(glDeleteBuffers(1, &vertexVBO));

  glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(globVertexBufferData), globVertexBufferData, GL_STATIC_DRAW);

  GLuint colorVBO;
  glGenBuffers(1, &colorVBO);
  defer(glDeleteBuffers(1, &colorVBO));

  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glBufferData(GL_ARRAY_BUFFER, sizeof(globColorBufferData), globColorBufferData, GL_STATIC_DRAW);

  GLuint vao;
  glGenVertexArrays(1, &vao);
  defer(glDeleteVertexArrays(1, &vao));

  glBindVertexArray(vao);

  glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
  glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind VBO

  glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
  glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind VBO

  glBindVertexArray(0); // unbind VAO

  glm::mat4 projection = glm::perspective(80.0f, 4.0f / 3.0f, 0.3f, 100.0f);

  GLint matrixId = glGetUniformLocation(programId, "MVP");

  auto startTime = std::chrono::high_resolution_clock::now();
  auto prevTime = startTime;

  // hide cursor
  glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

  Camera camera(window, glm::vec3(0, 0, 5), 3.14f /* toward -Z */, 0.0f /* look at the horizon */);

  glEnable(GL_DOUBLEBUFFER);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glDepthFunc(GL_LESS);

  glClearColor(0, 0, 0, 1);

  while(glfwWindowShouldClose(window) == GL_FALSE) {
    if(glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) break;

    if(glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) {
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    if(glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) {
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    auto currentTime = std::chrono::high_resolution_clock::now();
    float startDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
    float prevDeltaTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - prevTime).count();
    prevTime = currentTime;

    float rotationTimeMs = 3000.0f;
    float currentRotation = startDeltaTimeMs / rotationTimeMs;
    float angle = 360.0f*(currentRotation - (long)currentRotation);

    glm::mat4 view;
    camera.getViewMatrix(prevDeltaTimeMs, &view);

    glm::mat4 model = glm::rotate(angle, 0.0f, 1.0f, 0.0f);
    glm::mat4 mvp = projection * view * model; // matrix multiplication is the other way around
    glUniformMatrix4fv(matrixId, 1, GL_FALSE, &mvp[0][0]);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(programId);

    glBindVertexArray(vao);
    glEnableVertexAttribArray(0); // could be done once before while loop ... ->
    glEnableVertexAttribArray(1);
    glDrawArrays(GL_TRIANGLES, 0, 3*12);
    glDisableVertexAttribArray(1); // -> ... in this cast remove these two lines
    glDisableVertexAttribArray(0);
    
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  return 0;
}