text: /*********************************************************************
  * This file provide implementations for the functions declared
  * in Geometry.h
  * It serves as an alternative to the now deprecated fixed-function
  * pipeline that GLUT utilizes.
  * Author: Hoang Tran 
  * Written in July 2016.                                             
  *********************************************************************/
  
  #include "Geometry.h"
  
  // Avoiding linker error caused by multiply defined variables
  GLuint defaultVAO, defaultVBO, defaultNBO, defaultEBO;
  GLuint vertexshader, fragmentshader, shaderprogram;
  GLuint modelviewPos;
  glm::mat4 model;
  shape lastUsed = NONE;
  /** TEAPOT RELATED **/
  std::vector <glm::vec3> teapotVertices;
  std::vector <glm::vec3> teapotNormals;
  std::vector <unsigned int> teapotIndices;
  
  // Initialize the buffer objects. Can only be called after OpenGL is initialized.
  void initBufferObjects() {
  	// Tell OpenGL to allocate us some space for the VAO
  	glGenVertexArrays(1, &defaultVAO);
  	// Now allocate some space for all the buffer objects
  	glGenBuffers(1, &defaultVBO);
  	glGenBuffers(1, &defaultNBO);
  	glGenBuffers(1, &defaultEBO);
  }
  
  // Free up any dynamically allocated memory here
  void destroyBufferObjects() {
  	glDeleteVertexArrays(1, &defaultVAO);
  	glDeleteBuffers(1, &defaultVBO);
  	glDeleteBuffers(1, &defaultNBO);
  	glDeleteBuffers(1, &defaultEBO);
  }
  
  // OBJ file parser func
  void parse(const char * filepath) {
  	FILE* fp;
  	float x, y, z;
  	int fx, fy, fz, ignore;
  	int c1, c2;
  	float minY = INFINITY, minZ = INFINITY;
  	float maxY = -INFINITY, maxZ = -INFINITY;
  
  	fp = fopen(filepath, "rb");
  
  	if (fp == NULL) {
  		std::cerr << "Error loading file: " << filepath << std::endl;
  		std::getchar();
  		exit(-1);
  	}
  
  	while (!feof(fp)) {
  		c1 = fgetc(fp);
  		while (!(c1 == 'v' || c1 == 'f')) {
  			c1 = fgetc(fp);
  			if (feof(fp))
  				break;
  		}
  		c2 = fgetc(fp);
  
  		if ((c1 == 'v') && (c2 == ' ')) {
  			fscanf(fp, "%f %f %f", &x, &y, &z);
  			teapotVertices.push_back(glm::vec3(x, y, z));
  			if (y < minY) minY = y;
  			if (z < minZ) minZ = z;
  			if (y > maxY) maxY = y;
  			if (z > maxZ) maxZ = z;
  		}
  		else if ((c1 == 'v') && (c2 == 'n')) {
  			fscanf(fp, "%f %f %f", &x, &y, &z);
  			teapotNormals.push_back(glm::normalize(glm::vec3(x, y, z)));
  		}
  		else if (c1 == 'f')
  		{
  			fscanf(fp, "%d//%d %d//%d %d//%d", &fx, &ignore, &fy, &ignore, &fz, &ignore);
  			teapotIndices.push_back(fx - 1);
  			teapotIndices.push_back(fy - 1);
  			teapotIndices.push_back(fz - 1);
  		}
  	}
  
  	fclose(fp); // Finished parsing
  	// Recenter the teapot
  	float avgY = (minY + maxY) / 2.0f - 0.0234f; // Closest possible approximation
  	float avgZ = (minZ + maxZ) / 2.0f;
  	for (unsigned int i = 0; i < teapotVertices.size(); ++i) {
  		glm::vec3 shiftedVertex = (teapotVertices[i] - glm::vec3(0.0f, avgY, avgZ)) * glm::vec3(0.975f, 0.975f, 0.975f);
  		teapotVertices[i] = shiftedVertex;
  	}
  }
  
  void bindTeapot() {
  	glBindVertexArray(defaultVAO);
  
  	glBindBuffer(GL_ARRAY_BUFFER, defaultVBO);
  	glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * teapotVertices.size(), &teapotVertices[0], GL_STATIC_DRAW);
  	glEnableVertexAttribArray(0); // This allows usage of layout location 0 in the vertex shader
  	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
  
  	glBindBuffer(GL_ARRAY_BUFFER, defaultNBO);
  	glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * teapotNormals.size(), &teapotNormals[0], GL_STATIC_DRAW);
  	glEnableVertexAttribArray(1); // This allows usage of layout location 1 in the vertex shader
  	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
  
  	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, defaultEBO);
  	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * teapotIndices.size(), &teapotIndices[0], GL_STATIC_DRAW);
  
  	glBindBuffer(GL_ARRAY_BUFFER, 0);
  	glBindVertexArray(0);
  	lastUsed = TEAPOT;
  }
  
  void solidTeapot(float size) {
  	model = glm::scale(glm::mat4(1.0f), glm::vec3(size, size, size));
  	glUniformMatrix4fv(modelviewPos, 1, GL_FALSE, &(view * model)[0][0]);
  
  	if (lastUsed != TEAPOT) {
  		bindTeapot();
  	}
  
  	glBindVertexArray(defaultVAO);
  	glDrawElements(GL_TRIANGLES, teapotIndices.size(), GL_UNSIGNED_INT, 0);
  	glBindVertexArray(0); // Unbind the VAO when done
  }
  