CSC3223_Graphics_For_Games_OpenGL_Rasteriser / OpenGLGraphics / Mesh.cpp
Mesh.cpp
Raw
#include "Mesh.h"

Mesh::Mesh(void)	{
	//Most objects in OpenGL are represented as 'names' - an unsigned int
	//index, really. They are always generated and destroyed by OpenGL 
	//functions. Most of these functions allow you to generate multiple
	//names at once (the first parameter here is a count).
	glGenVertexArrays(1, &arrayObject);
	
	for(int i = 0; i < MAX_BUFFER; ++i) {
		bufferObject[i] = 0;
	}

	numVertices  = 0;
	type		 = GL_TRIANGLES;

	//Later tutorial stuff
	numIndices    = 0;
	vertices	  = NULL;
	textureCoords = NULL;
	normals		  = NULL;
	tangents	  = NULL;
	indices		  = NULL;
	colours		  = NULL;
}

Mesh::~Mesh(void)	{
	glDeleteVertexArrays(1, &arrayObject);			//Delete our VAO
	glDeleteBuffers(MAX_BUFFER, bufferObject);		//Delete our VBOs

	//Later tutorial stuff
	delete[]vertices;
	delete[]indices;
	delete[]textureCoords;
	delete[]tangents;
	delete[]normals;
	delete[]colours;
}

void Mesh::Draw()	{
	/*
	To render with a mesh in OpenGL, we need to bind all of the buffers
	containing vertex data to the pipeline, and attach them to the
	generic attributes referenced in our vertex shader.
	That's quite a lot of code to set up, but fear not - all of the state
	is cached within our Vertex Array Object, so once that's been set up
	by the BufferData function, all we need to do is bind the VAO and it
	all happens automagically
	*/
	glBindVertexArray(arrayObject);
	//There are two draw functions in OpenGL, depending on whether we're 
	//using indices or not. Both start off taking a primitive type - 
	//triangles, quads, lines, points etc. 

	if(bufferObject[INDEX_BUFFER]) {
		/*
		If we have an index buffer, we tell OpenGL how to parse that
		buffer data (is it bytes/ints/shorts), and how many data
		elements there are. The last parameter should nearly always be 0,
		it's part of the old OpenGL spec.
		*/
		glDrawElements(type, numIndices, GL_UNSIGNED_INT, 0);
	}
	else{
		/*
		If we don't have indices, we can just use this function.
		Its extra parameters define which is the first vertex
		to draw, and how many vertices past this point to draw.
		*/
		glDrawArrays(type, 0, numVertices);	//Draw the triangle!
	}
	/*
	We don't strictly have to do this, but 'undoing' whatever
	we do to OpenGL in a function generally keeps the pipeline
	from getting incorrect states, or otherwise not doing what
	you want it to do.
	*/
	glBindVertexArray(0);	
}

Mesh* Mesh::GenerateTriangle()	{
	Mesh*m = new Mesh();
	m->numVertices = 3;

	m->vertices		= new Vector3[m->numVertices];
	m->vertices[0]	= Vector3(0.0f,	0.5f,	0.0f);
	m->vertices[1]	= Vector3(0.5f,  -0.5f,	0.0f);
	m->vertices[2]	= Vector3(-0.5f, -0.5f,	0.0f);

	m->textureCoords	= new Vector2[m->numVertices];
	m->textureCoords[0] = Vector2(0.5f,	0.0f);
	m->textureCoords[1] = Vector2(1.0f,	1.0f);
	m->textureCoords[2] = Vector2(0.0f,	1.0f);

	m->colours		= new Vector4[m->numVertices];
	m->colours[0]	= Vector4(1.0f, 0.0f, 0.0f,1.0f); 
	m->colours[1]	= Vector4(0.0f, 1.0f, 0.0f,1.0f);
	m->colours[2]	= Vector4(0.0f, 0.0f, 1.0f,1.0f);

	m->BufferData();

	return m;
}

//Added in Tutorial 18
Mesh* Mesh::GeneratePoints(unsigned int count) {
	Mesh* m = new Mesh();
	m->numVertices = count;
	m->vertices = new Vector3[count];
	m->type = GL_POINTS;

	for (int i = 0; i < count; ++i) {
		float x = (float)(rand() % 1000 - 500);
		float y = (float)(rand() % 1000 - 500);
		float z = (float)(rand() % 1000 - 500);
		m->vertices[i] = Vector3(x, y, z);
	}
	m->BufferData();
	return m;
}



Mesh*	Mesh::LoadMeshFile(const string &filename) {
	ifstream f(filename);

	if(!f) {
		return NULL;
	}

	Mesh*m = new Mesh();
	f >> m->numVertices;

	int hasTex = 0;
	int hasColour = 0;

	f >> hasTex;
	f >> hasColour;

	m->vertices = new Vector3[m->numVertices];

	if(hasTex) {
		m->textureCoords = new Vector2[m->numVertices];
		m->colours		 = new Vector4[m->numVertices];
	}

	for (unsigned int i = 0; i < m->numVertices; ++i) {
		f >> m->vertices[i].x;
		f >> m->vertices[i].y;
		f >> m->vertices[i].z;
	}

	if (hasColour) {
		for (unsigned int i = 0; i < m->numVertices; ++i) {
			unsigned char r, g, b, a;

			f >> r;
			f >> g;
			f >> b;
			f >> a;
			//OpenGL can use floats for colours directly - this will take up 4x as
			//much space, but could avoid any byte / float conversions happening
			//behind the scenes in our shader executions
			m->colours[i] = Vector4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
		}
	}

	if (hasTex) {
		for (unsigned int i = 0; i < m->numVertices; ++i) {
			f >> m->textureCoords[i].x;
			f >> m->textureCoords[i].y;
		}
	}
	m->GenerateNormals();
	m->BufferData();
	return m;
}

void	Mesh::BufferData()	{
	/*
		To more efficiently bind and unbind the states required to draw a mesh,
		we can encapsulate them all inside a Vertex Array Object.

		When a VAO is bound, all further changes to vertex buffers and vertex
		attributes are cached inside the VAO, and will be reapplied whenever
		that VAO is later bound again
	*/
	glBindVertexArray(arrayObject);

	/*
	To put some vertex data on the GPU, we must create a buffer object to store it.

	To upload data to a vertex buffer, it must be bound, with a specific 'target',
	which defines what the buffer is to be used for.

	Then, once bound, data can be uploaded using the glBufferData function, which
	takes a pointer to the data to be sent, and the size of that data, as well as
	a usage parameter - this lets OpenGL know how the data will be accessed.

	To turn a vertex buffer into a vertex attribute suitable for sending to a
	vertex shader, the glVertexAttribPointer function must be called. 
	This takes in an attribute 'slot', how many elements each data entry has
	(i.e 2 for a vector2 etc), and what datatype (usually float) it is.
	This will bind the currently bound vertex buffer to that attribute slot.
	To actually enable that attribute slot, the glEnableVertexAttribArray is called.

	Note that we use the value VERTEX_BUFFER for the 'slot' parameter - this is just
	an enum value, that equates to 0. It's common to use enums as indices into arrays
	in this way, as it keeps everything consistent. It's also pretty sensible to always
	bind the same data types to the same attribute slots, it makes life much easier!

	These last two functions, along with the glBindBuffer call, are examples of 
	functionality that is cached in the actual VAO.
	*/

	//Buffer vertex data
	glGenBuffers(1, &bufferObject[VERTEX_BUFFER]);
	glBindBuffer(GL_ARRAY_BUFFER, bufferObject[VERTEX_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(Vector3), vertices, GL_STATIC_DRAW);
	glVertexAttribPointer(VERTEX_BUFFER, 3, GL_FLOAT, GL_FALSE, 0, 0); 
	glEnableVertexAttribArray(VERTEX_BUFFER);

	////Buffer texture data
	if (textureCoords) {
		glGenBuffers(1, &bufferObject[TEXTURE_BUFFER]);
		glBindBuffer(GL_ARRAY_BUFFER, bufferObject[TEXTURE_BUFFER]);
		glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(Vector2), textureCoords, GL_STATIC_DRAW);
		glVertexAttribPointer(TEXTURE_BUFFER, 2, GL_FLOAT, GL_FALSE, 0, 0);
		glEnableVertexAttribArray(TEXTURE_BUFFER);
	}

	//buffer colour data
	if (colours)	{
		glGenBuffers(1, &bufferObject[COLOUR_BUFFER]);
		glBindBuffer(GL_ARRAY_BUFFER, bufferObject[COLOUR_BUFFER]);
		glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(Vector4), colours, GL_STATIC_DRAW);
		glVertexAttribPointer(COLOUR_BUFFER, 4, GL_FLOAT, GL_FALSE, 0, 0); 
		glEnableVertexAttribArray(COLOUR_BUFFER);
	}

	//buffer index data
	if(indices) {
		glGenBuffers(1, &bufferObject[INDEX_BUFFER]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObject[INDEX_BUFFER]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices*sizeof(GLuint), indices, GL_STATIC_DRAW);
	}

# define normalIndex 3
	if (normals) { //
		glGenBuffers(1, &normalBuffer);
		glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
		glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vector3),
		normals, GL_STATIC_DRAW);
	
		glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
		glEnableVertexAttribArray(normalIndex);
}


	//Once we're done with the vertex buffer binding, we can unbind the VAO,
	//ready to reapply later, such as in the Draw function above!
	glBindVertexArray(0);
}

 Mesh* Mesh::GenerateQuadPatch() {
	Mesh* m = new Mesh();
	m->numVertices = 4; //Numberofvertices
	m->type = GL_PATCHES; //TheOpenGLrendertype

	m->vertices = new Vector3[m->numVertices];
	m->textureCoords = new Vector2[m->numVertices];

	m->vertices[0] = Vector3(-1.0f, -1.0f, 0.0f);
	m->vertices[1] = Vector3(-1.0f, 1.0f, 0.0f);
	m->vertices[2] = Vector3(1.0f, -1.0f, 0.0f);
	m->vertices[3] = Vector3(1.0f, 1.0f, 0.0f);

	m->textureCoords[0] = Vector2(0.0f, 1.0f);
	m->textureCoords[1] = Vector2(0.0f, 0.0f);
	m->textureCoords[2] = Vector2(1.0f, 1.0f);
	m->textureCoords[3] = Vector2(1.0f, 0.0f);

	m->BufferData();

	return m;
}

 Mesh * Mesh::GenerateSegmentedQuads(unsigned int numSegments) {
	 Mesh * m = new Mesh();
	 m -> numVertices = 2 * numSegments;
	 m -> type = GL_TRIANGLE_STRIP;
	
	 m -> vertices = new Vector3[m -> numVertices];
	 m -> textureCoords = new Vector2[m -> numVertices];
	 m -> colours = new Vector4[m -> numVertices];
	
	 for (int i = 0; i < m -> numVertices; i += 2) {
	 m -> vertices[i] = Vector3(-1, i, 0.0f);
	 m -> vertices[i + 1] = Vector3(1, i, 0.0f);
	
	 m -> textureCoords[i] = Vector2(0.0f, i);
	 m -> textureCoords[i + 1] = Vector2(1.0f, i);
	
	 int jointNum = i / 2;
	
	 m -> colours[i] = Vector4(jointNum, jointNum - 1, 0.5f, 0.5f);
	 m -> colours[i + 1] = Vector4(jointNum, jointNum - 1, 0.5f, 0.5f);
	
	}
	 m -> colours[0] = Vector4(0, 0, 0.5f, 0.5f);
	 m -> colours[1] = Vector4(0, 0, 0.5f, 0.5f);
	
	 m -> BufferData();
	
	 return m;
 }


void Mesh::GenerateNormals() {
	if (!normals) {
	 normals = new Vector3[numVertices];
	}
	 for (unsigned int i = 0; i < numVertices; i += 3) {
	 Vector3 & a = vertices[i];
	 Vector3 & b = vertices[i + 1];
	 Vector3 & c = vertices[i + 2];
	
	 Vector3 normal = Vector3::Cross(b - a, c - a);
	 normal.Normalise();
	
	 normals[i] = normal;
	 normals[i + 1] = normal;
	 normals[i + 2] = normal;	
	}	
 }


Mesh *Mesh::GenerateLine(const Vector3 &begin, const Vector3 &finish)
{
	Mesh *m = new Mesh();
	m->type = GL_LINES;
	m->numVertices = 2;

	m->vertices = new Vector3[m->numVertices];
	m->vertices[0] = begin;
	m->vertices[1] = finish;

	m->textureCoords = new Vector2[m->numVertices];
	m->textureCoords[0] = Vector2(1.0f, 1.0f);
	m->textureCoords[1] = Vector2(0.0f, 1.0f);

	m->colours = new Vector4[m->numVertices];
	m->colours[0] = Vector4(1.0f, 0.0f, 0.0f, 1.0f);
	m->colours[1] = Vector4(1.0f, 0.0f, 0.0f, 1.0f);

	m->BufferData();
	return m;
}