text: /*
   main
  
   Copyright 2012 Thomas Dalling - http://tomdalling.com/
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
  
       http://www.apache.org/licenses/LICENSE-2.0
  
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   */
  
  #include "platform.hpp"
  
  // third-party libraries
  #include <GL/glew.h>
  #include <GLFW/glfw3.h>
  #include <glm/glm.hpp>
  
  // standard C++ libraries
  #include <cassert>
  #include <iostream>
  #include <stdexcept>
  #include <cmath>
  
  // tdogl classes
  #include "tdogl/Program.h"
  #include "tdogl/Texture.h"
  
  // constants
  const glm::vec2 SCREEN_SIZE(800, 600);
  
  // globals
  GLFWwindow* gWindow = NULL;
  tdogl::Texture* gTexture = NULL;
  tdogl::Program* gProgram = NULL;
  GLuint gVAO = 0;
  GLuint gVBO = 0;
  
  
  // loads the vertex shader and fragment shader, and links them to make the global gProgram
  static void LoadShaders() {
      std::vector<tdogl::Shader> shaders;
      shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("vertex-shader.txt"), GL_VERTEX_SHADER));
      shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("fragment-shader.txt"), GL_FRAGMENT_SHADER));
      gProgram = new tdogl::Program(shaders);
  }
  
  
  // loads a triangle into the VAO global
  static void LoadTriangle() {
      // make and bind the VAO
      glGenVertexArrays(1, &gVAO);
      glBindVertexArray(gVAO);
      
      // make and bind the VBO
      glGenBuffers(1, &gVBO);
      glBindBuffer(GL_ARRAY_BUFFER, gVBO);
      
      // Put the three triangle vertices (XYZ) and texture coordinates (UV) into the VBO
      GLfloat vertexData[] = {
          //  X     Y     Z       U     V
           0.0f, 0.8f, 0.0f,   0.5f, 1.0f,
          -0.8f,-0.8f, 0.0f,   0.0f, 0.0f,
           0.8f,-0.8f, 0.0f,   1.0f, 0.0f,
      };
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
  
      // connect the xyz to the "vert" attribute of the vertex shader
      glEnableVertexAttribArray(gProgram->attrib("vert"));
      glVertexAttribPointer(gProgram->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), NULL);
          
      // connect the uv coords to the "vertTexCoord" attribute of the vertex shader
      glEnableVertexAttribArray(gProgram->attrib("vertTexCoord"));
      glVertexAttribPointer(gProgram->attrib("vertTexCoord"), 2, GL_FLOAT, GL_TRUE,  5*sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));
  
      // unbind the VAO
      glBindVertexArray(0);
  }
  
  
  // loads the file "hazard.png" into gTexture
  static void LoadTexture() {
      tdogl::Bitmap bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("hazard.png"));
      bmp.flipVertically();
      gTexture = new tdogl::Texture(bmp);
  }
  
  
  // draws a single frame
  static void Render() {
      // clear everything
      glClearColor(0, 0, 0, 1); // black
      glClear(GL_COLOR_BUFFER_BIT);
      
      // bind the program (the shaders)
      gProgram->use();
          
      // bind the texture and set the "tex" uniform in the fragment shader
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, gTexture->object());
      gProgram->setUniform("tex", 0); //set to 0 because the texture is bound to GL_TEXTURE0
  
      // bind the VAO (the triangle)
      glBindVertexArray(gVAO);
      
      // draw the VAO
      glDrawArrays(GL_TRIANGLES, 0, 3);
      
      // unbind the VAO, the program and the texture
      glBindVertexArray(0);
      glBindTexture(GL_TEXTURE_2D, 0);
      gProgram->stopUsing();
      
      // swap the display buffers (displays what was just drawn)
      glfwSwapBuffers(gWindow);
  }
  
  void OnError(int errorCode, const char* msg) {
      throw std::runtime_error(msg);
  }
  
  // the program starts here
  void AppMain() {
      // initialise GLFW
      glfwSetErrorCallback(OnError);
      if(!glfwInit())
          throw std::runtime_error("glfwInit failed");
      
      // open a window with GLFW
      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
      glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
      gWindow = glfwCreateWindow((int)SCREEN_SIZE.x, (int)SCREEN_SIZE.y, "OpenGL Tutorial", NULL, NULL);
      if(!gWindow)
          throw std::runtime_error("glfwCreateWindow failed. Can your hardware handle OpenGL 3.2?");
  
      // GLFW settings
      glfwMakeContextCurrent(gWindow);
      
      // initialise GLEW
      glewExperimental = GL_TRUE; //stops glew crashing on OSX :-/
      if(glewInit() != GLEW_OK)
          throw std::runtime_error("glewInit failed");
  
      // print out some info about the graphics drivers
      std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
      std::cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
      std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl;
      std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
  
      // make sure OpenGL version 3.2 API is available
      if(!GLEW_VERSION_3_2)
          throw std::runtime_error("OpenGL 3.2 API is not available.");
  
      // OpenGL settings
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  
      // load vertex and fragment shaders into opengl
      LoadShaders();
  
      // load the texture
      LoadTexture();
  
      // create buffer and fill it with the points of the triangle
      LoadTriangle();
  
      // run while the window is open
      while(!glfwWindowShouldClose(gWindow)){
          // process pending events
          glfwPollEvents();
          
          // draw one frame
          Render();
      }
  
      // clean up and exit
      glfwTerminate();
  }
  
  
  int main(int argc, char *argv[]) {
      try {
          AppMain();
      } catch (const std::exception& e){
          std::cerr << "ERROR: " << e.what() << std::endl;
          return EXIT_FAILURE;
      }
  
      return EXIT_SUCCESS;
  }
  