
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <vector>

// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <common/shader.cpp>
#include <common/texture.cpp>
#include <common/controls.cpp>
#include <common/objloader.cpp>
//#include <common/vboindexer.cpp>
#include <QDebug>

void initGL();

struct BufferData{
    GLuint vertexbuffer;
    GLuint uvbuffer;
    GLuint normalbuffer;
};

struct dataAboutOneModel{
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec2> uvs;
    std::vector<glm::vec3> normals;
    GLuint Texture;
    GLuint TextureID;
    const char *texturePath;
    const char *modelPath;
    BufferData BufferModel;
    std::vector<GLuint> TexturesAvailable;
    std::vector<GLuint> TexturesIDAvailable;
    std::vector<std::string> PathToTexturesAvailable;
    int CurrectTextureNum;
};

std::vector<std::string> ModelFilenames;
std::vector<std::string> TexutureFilenames;

dataAboutOneModel modelTmp;
BufferData        bufferTmp;

GLuint programID;

std::vector<dataAboutOneModel> dataAboutModels;
std::vector<BufferData>        BuffersData;

void ActivateBuffer(int numModel);
void initModel(dataAboutOneModel &model);
void loopGLinit(GLuint & programID,GLuint & MatrixID,GLuint & ModelMatrixID, GLuint & ViewMatrixID, GLuint & LightID);
void VertexBind();
void InitArrayPathModelTexture(std::vector<std::string> &ModelFilenamesInit,std::vector<std::string> &TexutureFilenamesInit);
GLuint VertexArrayID;
GLuint MatrixID;
GLuint ViewMatrixID;
GLuint ModelMatrixID;
int main( void )
{
    initGL();  
    InitArrayPathModelTexture(ModelFilenames,TexutureFilenames);

    for(int i = 0; i < ModelFilenames.size();i++){
        modelTmp.texturePath = TexutureFilenames[i].c_str();
        modelTmp.modelPath = ModelFilenames[i].c_str();
        initModel(modelTmp);
    }


    dataAboutModels[0].TexturesAvailable.push_back(dataAboutModels[0].Texture);
    dataAboutModels[0].TexturesIDAvailable.push_back(dataAboutModels[0].TextureID);

    dataAboutModels[0].TextureID  = glGetUniformLocation(programID, "myTextureSampler");
    dataAboutModels[0].Texture = loadDDS(TexutureFilenames[1].c_str());

    dataAboutModels[0].TexturesAvailable.push_back(dataAboutModels[0].Texture);
    dataAboutModels[0].TexturesIDAvailable.push_back(dataAboutModels[0].TextureID);



    for(int i = 0 ; i <dataAboutModels.size();i++ )
        ActivateBuffer(i);

    GLuint LightID = glGetUniformLocation(programID, "LightPosition_worldspace");
    glUseProgram(programID);
    do{
        loopGLinit(programID,MatrixID,ModelMatrixID,ViewMatrixID,LightID);
        VertexBind();
        glfwSwapBuffers(window);
        glfwPollEvents();
    } // Check if the ESC key was pressed or the window was closed
    while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
           glfwWindowShouldClose(window) == 0 );

    // Cleanup VBO and shader
    for(int i = 0 ; i < BuffersData.size(); i++){
        glDeleteBuffers(1, &BuffersData[i].vertexbuffer);
        glDeleteBuffers(1, &BuffersData[i].uvbuffer);
        glDeleteBuffers(1, &BuffersData[i].normalbuffer);
    }
    glDeleteProgram(programID);

    for(int i = 0 ; i <dataAboutModels.size();i++ )
        glDeleteTextures(1, &dataAboutModels[i].Texture);
    glDeleteVertexArrays(1, &VertexArrayID);

    // Close OpenGL window and terminate GLFW
    glfwTerminate();

    return 0;
}


void AddAvailableTextures(){

}


void InitArrayPathModelTexture(std::vector<std::string> &ModelFilenamesInit, std::vector<string> &TexutureFilenamesInit){

    //    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/null.dds");
    //    ModelFilenamesInit.push_back("/home/user/openGL/Generator_Tris01.obj");

    /* XML rewrite */

    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/DG_Pult_Menu_A.dds");
    ModelFilenamesInit.push_back("/home/user/DG_Pult_02/Mesh/DG_Pult_Menu.obj");

    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/null.dds");
    ModelFilenamesInit.push_back("/home/user/DG_Pult_02/Mesh/DG_Pult_Body.obj");

    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/null_button.dds");
    ModelFilenamesInit.push_back("/home/user/DG_Pult_02/Mesh/DG_Pult_Button_1.obj");

    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/null_button.dds");
    ModelFilenamesInit.push_back("/home/user/DG_Pult_02/Mesh/DG_Pult_Button_2.obj");

    TexutureFilenamesInit.push_back("/home/user/DG_Pult_02/Textures/null_button.dds");
    ModelFilenamesInit.push_back("/home/user/DG_Pult_02/Mesh/DG_Pult_Button_3.obj");
/*
Подгрузка текстур и моделей - код различается только путями файлов.
*/
}

void initModel(dataAboutOneModel &model){
    model.TextureID  = glGetUniformLocation(programID, "myTextureSampler");
    model.Texture = loadDDS(model.texturePath);
    bool res = loadOBJ(model.modelPath, model.vertices, model.uvs, model.normals);
    if(res==true)
        dataAboutModels.push_back(model);
    else
        qDebug() << "Incorrect path *.obj file.";


    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);

    programID = LoadShaders( "/home/user/openGL/StandardShading.vertexshader", "/home/user/openGL/StandardShading.fragmentshader" );
    MatrixID = glGetUniformLocation(programID, "MVP");
    ViewMatrixID = glGetUniformLocation(programID, "V");
    ModelMatrixID = glGetUniformLocation(programID, "M");
}



void VertexBind(){
    for(int i = 0; i < BuffersData.size();i++){
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, dataAboutModels[i].Texture);
        glUniform1i(dataAboutModels[i].TextureID, 0);

        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, BuffersData[i].vertexbuffer);
        glVertexAttribPointer(
                    0,                  // attribute
                    3,                  // size
                    GL_FLOAT,           // type
                    GL_FALSE,           // normalized?
                    0,                  // stride
                    (void*)0            // array buffer offset
                    );

        // 2nd attribute buffer : UVs
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, BuffersData[i].uvbuffer);
        glVertexAttribPointer(
                    1,                                // attribute
                    2,                                // size
                    GL_FLOAT,                         // type
                    GL_FALSE,                         // normalized?
                    0,                                // stride
                    (void*)0                          // array buffer offset
                    );

        // 3rd attribute buffer : normals
        glEnableVertexAttribArray(2);
        glBindBuffer(GL_ARRAY_BUFFER, BuffersData[i].normalbuffer);
        glVertexAttribPointer(
                    2,                                // attribute
                    3,                                // size
                    GL_FLOAT,                         // type
                    GL_FALSE,                         // normalized?
                    0,                                // stride
                    (void*)0                          // array buffer offset
                    );


        // Draw the triangles !
        glDrawArrays(GL_TRIANGLES, 0, dataAboutModels[i].vertices.size() );

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);



    }
}

void loopGLinit(GLuint & programID,GLuint & MatrixID,GLuint & ModelMatrixID, GLuint & ViewMatrixID, GLuint & LightID){

    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Use our shader
    glUseProgram(programID);

    // Compute the MVP matrix from keyboard and mouse input
    computeMatricesFromInputs();
    glm::mat4 ProjectionMatrix = getProjectionMatrix();
    glm::mat4 ViewMatrix = getViewMatrix();
    glm::mat4 ModelMatrix = glm::mat4(1.0);
    glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;

    // Send our transformation to the currently bound shader,
    // in the "MVP" uniform
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
    glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);

    glm::vec3 lightPos = glm::vec3(4,4,4);
    glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);


}

void ActivateBuffer(int numModel){

    dataAboutOneModel modelTmpAB = dataAboutModels[numModel];
    glGenBuffers(1, &bufferTmp.vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, bufferTmp.vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, modelTmpAB.vertices.size() * sizeof(glm::vec3), &modelTmpAB.vertices[0], GL_STATIC_DRAW);

    //bufferTmp.uvbuffer;
    glGenBuffers(1, &bufferTmp.uvbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, bufferTmp.uvbuffer);
    glBufferData(GL_ARRAY_BUFFER, modelTmpAB.uvs.size() * sizeof(glm::vec2), &modelTmpAB.uvs[0], GL_STATIC_DRAW);

    //bufferTmp.normalbuffer;
    glGenBuffers(1, &bufferTmp.normalbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, bufferTmp.normalbuffer);
    glBufferData(GL_ARRAY_BUFFER, modelTmpAB.normals.size() * sizeof(glm::vec3), &modelTmpAB.normals[0], GL_STATIC_DRAW);

    BuffersData.push_back(bufferTmp);
}

void initGL(){
    if( !glfwInit() )
    {
        fprintf( stderr, "Failed to initialize GLFW\n" );
        getchar();
        return ;
    }

    glfwWindowHint(GLFW_SAMPLES, 32);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // Open a window and create its OpenGL context
    window = glfwCreateWindow( 1024, 768, "Пульт управления", NULL, NULL);
    if( window == NULL ){
        fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
        getchar();
        glfwTerminate();
        return ;
    }
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        getchar();
        glfwTerminate();
        return ;
    }

    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    // Hide the mouse and enable unlimited mouvement
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set the mouse at the center of the screen
    glfwPollEvents();
   // glfwSetCursorPos(window, 1024/2, 768/2);

    // Dark blue background
    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

    // Enable depth test
    glEnable(GL_DEPTH_TEST);
    // Accept fragment if it closer to the camera than the former one
    glDepthFunc(GL_LESS);

    // Cull triangles which normal is not towards the camera
    glEnable(GL_CULL_FACE);
}


void nextTexture(){
    qDebug() << "NextTexture";



    if(dataAboutModels[0].TexturesAvailable.size() > 1){
        if(    dataAboutModels[0].CurrectTextureNum < dataAboutModels[0].TexturesAvailable.size()){
            dataAboutModels[0].Texture = dataAboutModels[0].TexturesAvailable[dataAboutModels[0].CurrectTextureNum];
            dataAboutModels[0].TextureID = dataAboutModels[0].TexturesAvailable[dataAboutModels[0].CurrectTextureNum];
            dataAboutModels[0].CurrectTextureNum++;
        }else{
            dataAboutModels[0].CurrectTextureNum =0;
            nextTexture();
        }
    }
}
</code>


Фрагментный шейдер: 
<code>
#version 330 core

// Interpolated values from the vertex shaders
in vec2 UV;
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;

// Ouput data
out vec3 color;

// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;

void main(){

	// Light emission properties
	// You probably want to put them as uniforms
        vec3 LightColor = vec3(1,1,1);
        float LightPower = 50.0f;
	
	// Material properties
	vec3 MaterialDiffuseColor = texture( myTextureSampler, UV ).rgb;
        vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor;
	vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);

	// Distance to the light
	float distance = length( LightPosition_worldspace - Position_worldspace );

	// Normal of the computed fragment, in camera space
	vec3 n = normalize( Normal_cameraspace );
	// Direction of the light (from the fragment to the light)
	vec3 l = normalize( LightDirection_cameraspace );
	// Cosine of the angle between the normal and the light direction, 
	// clamped above 0
	//  - light is at the vertical of the triangle -> 1
	//  - light is perpendicular to the triangle -> 0
	//  - light is behind the triangle -> 0
        float cosTheta = clamp( dot( n,l ), 0 ,1 );
	
	// Eye vector (towards the camera)
	vec3 E = normalize(EyeDirection_cameraspace);
	// Direction in which the triangle reflects the light
	vec3 R = reflect(-l,n);
	// Cosine of the angle between the Eye vector and the Reflect vector,
	// clamped to 0
	//  - Looking into the reflection -> 1
	//  - Looking elsewhere -> < 1
        float cosAlpha = clamp( dot( E,R ), 0,1 );
	
	color = 
		// Ambient : simulates indirect lighting
		MaterialAmbientColor +
		// Diffuse : "color" of the object
		MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
		// Specular : reflective highlight, like a mirror
		MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance);

}