Camera Control
==============

Open a window and type in the following code: ::

    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    #include <iostream>
    #include <SOIL.h>
    
    #define GLM_FORCE_RADIANS
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    #include <glm/gtc/type_ptr.hpp>
    
    #include "Shader.h"
    
    GLfloat mix_value=0.2f;
    bool keys[1024];
    GLfloat delta_time=0.0f;                // time between current frame and last frame
    GLfloat last_frame=0.0f;                // time of last frame
    GLfloat last_X=0.0f,last_Y=0.0f;
    GLfloat fov=45.0f;
    GLfloat yaw=-90.0f;                     // yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing
                                            // to the right (due to how Euler angles work) so we initially rotate a bit to the left.
    GLfloat pitch=0.0f;
    bool first_mouse=true;
    
    glm::vec3 camera_position=glm::vec3(0.0f,0.0f,3.0f);
    glm::vec3 camera_front=glm::vec3(0.0f,0.0f,-1.0f);
    glm::vec3 camera_up=glm::vec3(0.0f,1.0f,0.0f);
    
    void do_movement()
    {
        // camera controls
        GLfloat camera_speed=5.0f*delta_time;
        if(keys[GLFW_KEY_W]) camera_position+=camera_speed*camera_front;
        if(keys[GLFW_KEY_S]) camera_position-=camera_speed*camera_front;
        if(keys[GLFW_KEY_A]) camera_position-=glm::normalize(glm::cross(camera_front,camera_up))*camera_speed;
        if(keys[GLFW_KEY_D]) camera_position+=glm::normalize(glm::cross(camera_front,camera_up))*camera_speed;
    }
    
    void scroll_callback(GLFWwindow *window,double xoffset,double yoffset)
    {
        if(fov>=1.0f && fov<=45.0f)  fov-=yoffset;
        if(fov<=1.0f) fov=1.0f;
        if(fov>=45.0f) fov=45.0f;
    }
    
    void key_callback(GLFWwindow *window,int key,int scancode,int action,int mode)
    {
        if(key==GLFW_KEY_ESCAPE && action==GLFW_PRESS)
            glfwSetWindowShouldClose(window,GL_TRUE);
    
        if(key==GLFW_KEY_UP && action==GLFW_PRESS)
        {
            mix_value+=0.1f;
            if(mix_value>1.0f) mix_value=1.0f;
        }
    
        if(key==GLFW_KEY_DOWN && action==GLFW_PRESS)
        {
            mix_value-=0.1f;
            if(mix_value<0.0f) mix_value=0.0f;
        }
    
        if(action==GLFW_PRESS) keys[key]=true;
        else if(action==GLFW_RELEASE) keys[key]=false;
    }
    
    void mouse_callback(GLFWwindow *window,double xpos,double ypos)
    {
        if(first_mouse)
        {
            last_X=xpos;
            last_Y=ypos;
            first_mouse=false;
        }
    
        GLfloat x_offset=xpos-last_X;
        GLfloat y_offset=last_Y-ypos;
        last_X=xpos;last_Y=ypos;
    
        GLfloat sensitivity=0.05f;
        x_offset*=sensitivity;
        y_offset*=sensitivity;
    
        yaw+=x_offset;
        pitch+=y_offset;
    
        if(pitch>89.0f) pitch=89.0f;
        else if(pitch<-89.0f) pitch=-89.0f;
    
        glm::vec3 front;
        front.x=cos(glm::radians(pitch))*cos(glm::radians(yaw));
        front.y=sin(glm::radians(pitch));
        front.z=cos(glm::radians(pitch))*sin(glm::radians(yaw));
        camera_front=glm::normalize(front);
    }
    
    int main()
    {
        glfwInit();
    #if __APPLE__
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);
    #endif
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
        glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
    
        GLFWwindow *window=glfwCreateWindow(800,600,"Learn OpenGL",nullptr,nullptr);
        if(window==nullptr)
        {
            std::cout<<"Failed to create GLFW window!"<<std::endl;
            glfwTerminate();
            return -1;
        }
        glfwMakeContextCurrent(window);
        glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_DISABLED);
    
        glewExperimental=GL_TRUE;
        if(glewInit()!=GLEW_OK)
        {
            std::cout<<"Failed to initialize GLEW!"<<std::endl;
            return -1;
        }
    
        int width,height;
        glfwGetFramebufferSize(window,&width,&height);
        glViewport(0,0,width,height);
        last_X=width/2.0;last_Y=height/2.0;
    
        glfwSetKeyCallback(window,key_callback);
        glfwSetCursorPosCallback(window,mouse_callback);
        glfwSetScrollCallback(window,scroll_callback);
    
        Shader our_shader("shader.vs","shader.frag");
    
        GLuint texture1,texture2;
    
        // generate texture 1
        glGenTextures(1,&texture1);
        glBindTexture(GL_TEXTURE_2D,texture1);
        // set texture parameters
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
        // set texture filtering
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        // load, create and generate mipmaps
        unsigned char* image=SOIL_load_image("container.jpg",&width,&height,0,SOIL_LOAD_RGB);
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,image);
        glGenerateMipmap(GL_TEXTURE_2D);
        // free image data
        SOIL_free_image_data(image);
        glBindTexture(GL_TEXTURE_2D,0);
    
        // generate texture 2
        glGenTextures(1,&texture2);
        glBindTexture(GL_TEXTURE_2D,texture2);
        // set texture parameters
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
        // set texture filtering
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        // load, create and generate mipmaps
        image=SOIL_load_image("awesomeface.png",&width,&height,0,SOIL_LOAD_RGB);
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,image);
        glGenerateMipmap(GL_TEXTURE_2D);
        // free image data
        SOIL_free_image_data(image);
        glBindTexture(GL_TEXTURE_2D,0);
    
        GLfloat vertices[]={
            // positions        // textures
            -0.5f,-0.5f,-0.5f,  0.0f,0.0f,
             0.5f,-0.5f,-0.5f,  1.0f,0.0f,
             0.5f, 0.5f,-0.5f,  1.0f,1.0f,
             0.5f, 0.5f,-0.5f,  1.0f,1.0f,
            -0.5f, 0.5f,-0.5f,  0.0f,1.0f,
            -0.5f,-0.5f,-0.5f,  0.0f,0.0f,
    
            -0.5f,-0.5f, 0.5f,  0.0f,0.0f,
             0.5f,-0.5f, 0.5f,  1.0f,0.0f,
             0.5f, 0.5f, 0.5f,  1.0f,1.0f,
             0.5f, 0.5f, 0.5f,  1.0f,1.0f,
            -0.5f, 0.5f, 0.5f,  0.0f,1.0f,
            -0.5f,-0.5f, 0.5f,  0.0f,0.0f,
    
            -0.5f, 0.5f, 0.5f,  1.0f,0.0f,
            -0.5f, 0.5f,-0.5f,  1.0f,1.0f,
            -0.5f,-0.5f,-0.5f,  0.0f,1.0f,
            -0.5f,-0.5f,-0.5f,  0.0f,1.0f,
            -0.5f,-0.5f, 0.5f,  0.0f,0.0f,
            -0.5f, 0.5f, 0.5f,  1.0f,0.0f,
    
             0.5f, 0.5f, 0.5f,  1.0f,0.0f,
             0.5f, 0.5f,-0.5f,  1.0f,1.0f,
             0.5f,-0.5f,-0.5f,  0.0f,1.0f,
             0.5f,-0.5f,-0.5f,  0.0f,1.0f,
             0.5f,-0.5f, 0.5f,  0.0f,0.0f,
             0.5f, 0.5f, 0.5f,  1.0f,0.0f,
    
            -0.5f,-0.5f,-0.5f,  0.0f,1.0f,
             0.5f,-0.5f,-0.5f,  1.0f,1.0f,
             0.5f,-0.5f, 0.5f,  1.0f,0.0f,
             0.5f,-0.5f, 0.5f,  1.0f,0.0f,
            -0.5f,-0.5f, 0.5f,  0.0f,0.0f,
            -0.5f,-0.5f,-0.5f,  0.0f,1.0f,
    
            -0.5f, 0.5f,-0.5f,  0.0f,1.0f,
             0.5f, 0.5f,-0.5f,  1.0f,1.0f,
             0.5f, 0.5f, 0.5f,  1.0f,0.0f,
             0.5f, 0.5f, 0.5f,  1.0f,0.0f,
            -0.5f, 0.5f, 0.5f,  0.0f,0.0f,
            -0.5f, 0.5f,-0.5f,  0.0f,1.0f
        };
    
        glm::vec3 cube_positions[]={
            glm::vec3( 0.0f, 0.0f, 0.0f), 
            glm::vec3( 2.0f, 5.0f,-15.0f), 
            glm::vec3(-1.5f,-2.2f,-2.5f),  
            glm::vec3(-3.8f,-2.0f,-12.3f),  
            glm::vec3( 2.4f,-0.4f,-3.5f),  
            glm::vec3(-1.7f, 3.0f,-7.5f),  
            glm::vec3( 1.3f,-2.0f,-2.5f),  
            glm::vec3( 1.5f, 2.0f,-2.5f), 
            glm::vec3( 1.5f, 0.2f,-1.5f), 
            glm::vec3(-1.3f, 1.0f,-1.5f)
        };
    
        GLuint VAO,VBO;
        glGenBuffers(1,&VBO);
        glGenVertexArrays(1,&VAO);
    
        // bind vertex array object
        glBindVertexArray(VAO);
    
        // copy the vertices in a buffer
        glBindBuffer(GL_ARRAY_BUFFER,VBO);
        glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);
    
        // set position attribute pointers
        glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5*sizeof(GL_FLOAT),(GLvoid*)0);
        glEnableVertexAttribArray(0);
        // set texture attribute pointers
        glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,5*sizeof(GL_FLOAT),(GLvoid*)(3*sizeof(GLfloat)));
        glEnableVertexAttribArray(1);
    
        // unbind the vertex array object
        glBindVertexArray(0);
    
        glEnable(GL_DEPTH_TEST);
    
        GLfloat radius=10.0f;
    
        while(!glfwWindowShouldClose(window))
        {
            glfwPollEvents();
            do_movement();
            glClearColor(.2f,.3f,.3f,1.f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            GLfloat current_frame=glfwGetTime();
            delta_time=current_frame-last_frame;
            last_frame=current_frame;
    
            // use shader program
            our_shader.Use();
    
            // bind textures
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D,texture1);
            glUniform1i(glGetUniformLocation(our_shader.program,"our_texture1"),0);
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D,texture2);
            glUniform1i(glGetUniformLocation(our_shader.program,"our_texture2"),1);
    
            glUniform1f(glGetUniformLocation(our_shader.program,"mix_value"),mix_value);
    
            // view space transform
            glm::mat4 view=glm::lookAt(camera_position,camera_position+camera_front,camera_up);
    
            // projection matrix
            glfwGetFramebufferSize(window,&width,&height);
            glm::mat4 projection=glm::perspective(glm::radians(fov),(float)width/height,0.1f,100.0f);
    
            GLuint model_location=glGetUniformLocation(our_shader.program,"model");
            GLuint view_location=glGetUniformLocation(our_shader.program,"view");
            glUniformMatrix4fv(view_location,1,GL_FALSE,glm::value_ptr(view));
            GLuint projection_location=glGetUniformLocation(our_shader.program,"projection");
            glUniformMatrix4fv(projection_location,1,GL_FALSE,glm::value_ptr(projection));
    
            // draw
            glBindVertexArray(VAO);
            for(GLuint i=0;i<10;++i)
            {
                // world space transform
                glm::mat4 model(1.0f);
                model=glm::translate(model,cube_positions[i]);
                model=glm::rotate(model,glm::radians((GLfloat)glfwGetTime()*50.0f),glm::vec3(0.5f,1.0f,0.0f));
                GLfloat angle=glm::radians(20.0f*i);
                model=glm::rotate(model,angle,glm::vec3(1.0f,0.3f,0.5f));
                glUniformMatrix4fv(model_location,1,GL_FALSE,glm::value_ptr(model));
    
                glDrawArrays(GL_TRIANGLES,0,36);
            }
            glBindVertexArray(0);
    
            glfwSwapBuffers(window);
        }
    
        // deallocate all resources
        glDeleteVertexArrays(1,&VAO);
        glDeleteBuffers(1,&VBO);
        // terminate GLFW
        glfwTerminate();
    
        return 0;
    }

Save this file as ``main.cpp``. Open another window and type in the following
code: ::

    #version 330 core
    layout (location=0) in vec3 position;
    layout (location=1) in vec2 tex_coord;
    
    out vec2 TexCoord;
    
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;
    
    void main()
    {
        gl_Position=projection*view*model*vec4(position,1.0f);
        TexCoord=vec2(tex_coord.x,1.0f-tex_coord.y);
    }

Save this file as ``shader.vs``. Finally, open another window and type the
following code: ::

    #version 330 core
    in vec2 TexCoord;
    
    out vec4 color;
    
    uniform float mix_value;
    
    uniform sampler2D our_texture1;
    uniform sampler2D our_texture2;
    
    void main()
    {
        color=mix(texture(our_texture1,TexCoord),texture(our_texture2,TexCoord),mix_value);
    }

Save this file as ``shader.frag``.