text: //
  // Created by yu cao on 2019-01-04.
  //
  
  #ifndef OPENGL_CLION_MACOS_MESH_H
  #define OPENGL_CLION_MACOS_MESH_H
  
  #include <glad/glad.h>
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  
  #include "MyShader.h"
  
  #include <string>
  #include <fstream>
  #include <sstream>
  #include <iostream>
  #include <vector>
  
  struct Vertex
  {
      glm::vec3 Position;
      glm::vec3 Normal;
      glm::vec2 TexCoords;
      glm::vec3 Tangent;
      glm::vec3 Bitangent;
  };
  
  struct Texture
  {
      unsigned int id;
      std::string type;
      std::string path;
  };
  
  class Mesh
  {
  public:
      // Mesh Data
      std::vector<Vertex> vertices;
      std::vector<unsigned int> indices;
      std::vector<Texture> textures;
      unsigned int VAO;
  
      Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices, std::vector<Texture> textures)
      {
          this->vertices = vertices;
          this->indices = indices;
          this->textures = textures;
  
          setupMesh();// set the vertex buffers and its attribute pointers
      }
  
      // render the mesh
      void Draw(Shader shader)
      {
          // bind the appropriate textures
          unsigned int diffuseNr = 1;
          unsigned int specularNr = 1;
          unsigned int normalNr = 1;
          unsigned int heightNr = 1;
          for (unsigned int i = 0; i < textures.size(); i++)
          {
              glActiveTexture(GL_TEXTURE0 + i);// active the proper texture unit to bind
  
              // retrieve texture number
              std::string number;
              std::string name = textures[i].type;
              if (name == "texture_diffuse")
                  number = std::to_string(diffuseNr++);
              else if (name == "texture_specular")
                  number = std::to_string(specularNr++);
              else if (name == "texture_normal")
                  number = std::to_string(normalNr++);
              else if (name == "texture_height")
                  number = std::to_string(heightNr++);
  
              // now set the sampler to the correct texture unit
              // store the texture as "texture_diffuse1", "texture_specular2" etc.
              shader.setFloat(("material." + name + number).c_str(), i);
              // and finally bind the texture
              glBindTexture(GL_TEXTURE_2D, textures[i].id);
          }
          // draw mesh
          glBindVertexArray(VAO);
          glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
          glBindVertexArray(0);
  
          // always good practice to set everything back to defaults once configured.
          glActiveTexture(GL_TEXTURE0);
      }
  
  private:
      /*  Render data  */
      unsigned int VBO, EBO;
  
      /*  Functions    */
      // initializes all the buffer objects/arrays
      void setupMesh()
      {
          // create buffers/arrays
          glGenVertexArrays(1, &VAO);
          glGenBuffers(1, &VBO);
          glGenBuffers(1, &EBO);
  
          glBindVertexArray(VAO);
          // load data into vertex buffers
          glBindBuffer(GL_ARRAY_BUFFER, VBO);
          // A great thing about structs is that their memory layout is sequential for all its items.
          // The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which
          // again translates to 3/2 floats which translates to a byte array.
          glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
  
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
          glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
  
          // set the vertex attribute pointers
          // vertex Positions
          glEnableVertexAttribArray(0);
          glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) 0);
          // vertex normals
          glEnableVertexAttribArray(1);
          glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, Normal));
          // vertex texture coords
          glEnableVertexAttribArray(2);
          glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, TexCoords));
          // vertex tangent
          glEnableVertexAttribArray(3);
          glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, Tangent));
          // vertex bitangent
          glEnableVertexAttribArray(4);
          glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, Bitangent));
  
          glBindVertexArray(0);
      }
  };
  
  
  #endif //OPENGL_CLION_MACOS_MESH_H
  