text: //
  //  main.cpp
  //  HelloTriangle
  //
  //  Created by yingliang on 2/17/16.
  //  Copyright © 2016 yingliang. All rights reserved.
  //
  #include <iostream>
  #include <GL/glew.h>
  #include <GLFW/glfw3.h>
  #include <Shader.h>
  #include <SOIL/SOIL.h>
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  
  const GLuint WIDTH = 800, HEIGHT = 600;
  glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
  glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
  glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
  bool keys[1024];
  GLfloat deltaTime = 0.0f;
  GLfloat lastFrame = 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 Eular angles work) so we initially rotate a bit to the left.
  GLfloat pitch = 0.0f;
  GLfloat lastX = WIDTH / 2.0;
  GLfloat lastY = HEIGHT / 2.0;
  
  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 >= 0 && key < 1024)
      {
          if (action == GLFW_PRESS)
          keys[key] = true;
          else if (action == GLFW_RELEASE)
          keys[key] = false;
      }
  }
  
  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 do_movement()
  {
      // Camera controls
      GLfloat cameraSpeed = 5.0f * deltaTime;
      if (keys[GLFW_KEY_W])
      cameraPos += cameraSpeed * cameraFront;
      if (keys[GLFW_KEY_S])
      cameraPos -= cameraSpeed * cameraFront;
      if (keys[GLFW_KEY_A])
      cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
      if (keys[GLFW_KEY_D])
      cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
  }
  
  bool firstMouse = true;
  void mouse_callback(GLFWwindow* window, double xpos, double ypos)
  {
      if (firstMouse)
      {
          lastX = xpos;
          lastY = ypos;
          firstMouse = false;
      }
      GLfloat xoffset = xpos - lastX;
      GLfloat yoffset = lastY - ypos;
      lastX = xpos;
      lastY = ypos;
      GLfloat sensitivity = 0.05f;
      xoffset *= sensitivity;
      yoffset *= sensitivity;
  
      yaw += xoffset;
      pitch += yoffset;
      if (pitch > 89.0f)
          pitch = 89.0f;
      if (pitch < -89.0f)
          pitch = -89.0f;
  
      glm::vec3 front;
      front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
      front.y = sin(glm::radians(pitch));
      front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
      cameraFront = glm::normalize(front);
  }
  
  
  
  int main()
  {
      glfwInit();
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
      //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
      glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
  
      GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
  
      if (window == nullptr)
      {
          std::cout << "Failed to create GLFW window" << std::endl;
          glfwTerminate();
          return -1;
      }
      glfwMakeContextCurrent(window);
  
      glfwSetKeyCallback(window, key_callback);
      glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
      glfwSetCursorPosCallback(window, mouse_callback);
      glfwSetScrollCallback(window, scroll_callback);
  
      glewExperimental = GL_TRUE;
      if (glewInit() != GLEW_OK)
      {
          std::cout << "Failed to initialize GLEW" << std::endl;
          return -1;
      }
      glViewport(0, 0, WIDTH, HEIGHT);
      glEnable(GL_DEPTH_TEST);
  
  
      GLfloat vertices[] = {
          -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 cubePositions[] = {
          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)
      };
  
  
  
      int width, height;
      unsigned char* image1 = SOIL_load_image("./container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
      GLuint texture1;
      glGenTextures(1, &texture1);
      glBindTexture(GL_TEXTURE_2D, texture1);
      // Set our texture parameters
      // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    // Note that we set our container wrapping method to GL_CLAMP_TO_EDGE
      // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    // Note that we set our container wrapping method to 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);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image1);
      glGenerateMipmap(GL_TEXTURE_2D);
      SOIL_free_image_data(image1);
      glBindTexture(GL_TEXTURE_2D, 0);
  
      unsigned char* image2 = SOIL_load_image("./awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
      GLuint texture2;
      glGenTextures(1, &texture2);
      glBindTexture(GL_TEXTURE_2D, texture2);
      // // Set our texture parameters
      // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // We want to repeat this pattern so we set kept it at GL_REPEAT
      // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   // We want to repeat this pattern so we set kept it at 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);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image2);
      glGenerateMipmap(GL_TEXTURE_2D);
      SOIL_free_image_data(image2);
      glBindTexture(GL_TEXTURE_2D, 0);
  
      Shader ourShader("./shader.vs", "./shader.frag");
  
      GLuint VAO;
      glGenVertexArrays(1, &VAO);
  
      glBindVertexArray(VAO);
      GLuint VBO;
      glGenBuffers(1, &VBO);
  
      glBindBuffer(GL_ARRAY_BUFFER, VBO);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  
      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
      glEnableVertexAttribArray(1);
      glBindVertexArray(0);
  
      while(!glfwWindowShouldClose(window))
      {
          GLfloat currentFrame = glfwGetTime();
          deltaTime = currentFrame - lastFrame;
          lastFrame = currentFrame;
          glfwPollEvents();
          glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          do_movement();
          ourShader.Use();
  
          glActiveTexture(GL_TEXTURE0);
          glBindTexture(GL_TEXTURE_2D, texture1);
          glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
          glActiveTexture(GL_TEXTURE1);
          glBindTexture(GL_TEXTURE_2D, texture2);
          glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
  
  
          glm::mat4 view;
          glm::mat4 projection;
  
          view = glm::lookAt(cameraPos, cameraPos + cameraFront, glm::vec3(0.0f, 1.0f, 0.0f));
  
          projection = glm::perspective(fov, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
  
  
  
          glUniformMatrix4fv(glGetUniformLocation(ourShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
          glUniformMatrix4fv(glGetUniformLocation(ourShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
  
          glBindVertexArray(VAO);
          for (GLuint i = 0; i < 10; i++)
          {
              glm::mat4 model;
              model = glm::translate(model, cubePositions[i]);
              GLfloat angle = 20.0f * i;
              if(i % 3 == 0)  // Every 3rd iteration (including the first) we set the angle using GLFW's time function.
              angle = glfwGetTime() * 25.0f;
              model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
              glUniformMatrix4fv(glGetUniformLocation(ourShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
              glDrawArrays(GL_TRIANGLES, 0, 36);
          }
          glBindVertexArray(0);
  
          glfwSwapBuffers(window);
      }
      glDeleteVertexArrays(1, &VAO);
      glDeleteBuffers(1, &VBO);
  
      glfwTerminate();
  
      return 0;
  }
  