//create Plane
geometry* createPlane(int width, int depth)
{
	geometry* geom = new geometry();
	glm::vec3 vertices[4];
	for (int x = 0; x < width; ++x)
	{
		for (int z = 0; z < depth; ++z)
		{
			vertices[0] = glm::vec3(-((float)width / 2.0f) + x, 0.0f, -((float)depth / 2.0f) + z);
			vertices[1] = glm::vec3(-((float)width / 2.0f) + (x + 1), 0.0f, -((float)depth / 2.0f) + z);
			vertices[2] = glm::vec3(-((float)width / 2.0f) + x, 0.0f, -((float)depth / 2.0f) + (z + 1));
			vertices[3] = glm::vec3(-((float)width / 2.0f) + (x+1), 0.0f, -((float)depth / 2.0f) + (z + 1));
			// Triangle 1
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(vertices[0]));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(vertices[3]));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(vertices[2]));
			// Triangle 2
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(vertices[0]));
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(vertices[1]));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(vertices[3]));
		}
	}
	
	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	return geom;
}
geometry* createSphere(int divisions)
{
	geometry* geom = new geometry();
	glm::vec3 v[4] = 
	{
		glm::vec3(0.0f, 0.0f, 1.0f),
		glm::vec3(0.0f, 0.942806, -0.333333),
		glm::vec3(-0.816497, -0.471405, -0.333333),
		glm::vec3(0.816497, -0.471405, -0.333333)
	};

	glm::vec2 t[4] = 
	{
		glm::vec2(v[0].x, v[0].y),
		glm::vec2(v[1].x, v[1].y),
		glm::vec2(v[2].x, v[2].y),
		glm::vec2(v[3].x, v[3].y)
	};
	
	divideTriangle(geom, v[0], v[1], v[2], divisions);
	divideTriangle(geom, v[3], v[2], v[1], divisions);
	divideTriangle(geom, v[0], v[3], v[1], divisions);
	divideTriangle(geom, v[0], v[2], v[3], divisions);

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	createTexBuffer(geom);
	glBindVertexArray(0);
	return geom;
}
 //-------------------------------------------------------------------------
 void
 ManualObject::normal( const Ogre::Vector3 &normal )
 {
     if( m_normal == nullptr )
     {
         createNormalBuffer();
     }
     *(m_normal++) = normal;
 }
geometry* createSierpinski(int divisions)
{
	glm::vec3 vertices[4];
	std::memcpy(vertices, tetrahedron_vertices, sizeof(glm::vec3) * 4);
	geometry* geom = new geometry();
	divide_sierpinski(geom, vertices, divisions);
	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	createIndexBuffer(geom);
	return geom;
}
void initialiseBuffers(geometry* geom)
{
	if (geom->vertices.size() > 0) //if we have vertices, then add to the vertex array object
		createVertexBuffer(geom);

	if (geom->normals.size() > 0)//if we have normals, then add to the vertex array object
		createNormalBuffer(geom);

	if (geom->texcoords.size() > 0)//if we have texcoords, then add to the vertex array object
		createTexBuffer(geom);

	glBindVertexArray(0);
}
//create Torus
geometry* createTorus(float radius, int stacks, int slices)
{
	geometry* geom = new geometry();
	float deltaStack = 2.0f * glm::pi<float>() / stacks;
	float deltaSlice = 2.0f * glm::pi<float>() / slices;
	for (int i = 0; i < stacks; ++i)
	{
		float a0 = i * deltaStack;
		float a1 = a0 + deltaStack;
		glm::vec3 vertices[4];
		glm::vec3 normals[4];
		for (int j = 0; j <= slices; ++j)
		{
			float b = j * deltaSlice;
			float c = cos(j * deltaSlice);
			float r = c + radius;
			vertices[0] = glm::vec3(sin(a0) * r, sin(j * deltaSlice), cos(a0) * r);
			normals[0] = glm::vec3(sin(a0) * c, sin(j * deltaSlice), cos(a0) * c);
			vertices[1] = glm::vec3(sin(a1) * r, sin(j * deltaSlice), cos(a1) * r);
			normals[1] = glm::vec3(sin(a1) * c, sin(j * deltaSlice), cos(a0) * c);
			c = cos((j + 1) * deltaSlice);
			r = c + radius;
			vertices[2] = glm::vec3(sin(a0) * r, sin((j + 1) * deltaSlice), cos(a0) * r);
			normals[2] = glm::vec3(sin(a0) * c, sin(j * deltaSlice), cos(a0) * c);
			vertices[3] = glm::vec3(sin(a1) * r, sin((j + 1) * deltaSlice), cos(a1) * r);
			normals[3] = glm::vec3(sin(a1) * c, sin(j * deltaSlice), cos(a0) * c);
			// Triangle 1
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(normals[0]));
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(normals[1]));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(normals[2]));
			// Triangle 2
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(normals[1]));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(normals[3]));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(normals[2]));
		}
	}

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	return geom;
}
//Create sphere
geometry* createSphere(int stacks, int slices)
{
	geometry* geom = new geometry();
	//create required values
	float deltaRho = glm::pi<float>() / stacks;
	float deltaTheta = 2.0f * glm::pi<float>() / slices;
	for (int i = 0; i < stacks; ++i)
	{
		float rho = i * deltaRho;
		glm::vec3 vertices[4];
		for (int j = 0; j < slices; ++j)
		{
			// Vertex 0 
			float theta = j * deltaTheta; 
			vertices[0] = glm::vec3(-sin(theta) * sin(rho), cos(theta) * sin(rho), cos(rho));
			// Vertex 1
			vertices[1] = glm::vec3(-sin(theta) * sin(rho + deltaRho), cos(theta) * sin(rho + deltaRho), cos(rho + deltaRho));
			// Vertex 2
			theta = ((j + 1) == slices) ? 0.0f : (j + 1) * deltaTheta;
			vertices[2] = glm::vec3(-sin(theta) * sin(rho), cos(theta) * sin(rho), cos(rho));
			// Vertex 3
			vertices[3] = glm::vec3(-sin(theta) * sin(rho + deltaRho), cos(theta) * sin(rho + deltaRho), cos(rho + deltaRho));

			// Triangle 1
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(vertices[0]));
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(vertices[1]));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(vertices[2]));
			// Triangle 2
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(vertices[1]));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(vertices[3]));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(vertices[2]));
		}
	}

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	return geom;
}
geometry* createTetrahedron()
{
	geometry* geom = new geometry();
	for (int i = 0; i < 4; ++i)
	{
		geom->vertices.push_back(tetrahedron_vertices[i]);
		geom->normals.push_back(tetrahedron_normals[i]);
	}
	for (int i = 0; i < 12; ++i)
		geom->indices.push_back(tetrahedron_indices[i]);
	
	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	createIndexBuffer(geom);

	return geom;
}
geometry* createBox()
{
	geometry* geom = new geometry();
	for(int i = 0; i < 8; ++i)
	{
		geom->vertices.push_back(box_vertices[i]);
		geom->normals.push_back(box_normals[i]);
	}
	for (int i = 0; i < 36; ++i)
		geom->indices.push_back(box_indices[i]);

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	createIndexBuffer(geom);
	
	return geom;
}
geometry* createPyramid()
{
	geometry* geom = new geometry();
	for (int i = 0; i < 5; ++i)
	{
		geom->vertices.push_back(pyramid_vertices[i]);
		geom->normals.push_back(pyramid_normals[i]);
	}
	for (int i = 0; i < 18; ++i)
		geom->indices.push_back(pyramid_indices[i]);

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	createIndexBuffer(geom);

	return geom;
}
//create a disk
geometry* createDisk(int slices)
{
	geometry* geom = new geometry();
	glm::vec3 centre(0.0f, 0.0f, 0.0f);
	glm::vec3 vertex_prev(1.0f, 0.01, 0.0f);
	glm::vec3 vertex_current;
	float deltaAngle = (2.0f * glm::pi<float>()) / slices;
	for (int i = 1; i <= slices; ++i)
	{
		vertex_current = glm::vec3(cos(i * deltaAngle), 0.0f, sin(i * deltaAngle));
		geom->vertices.push_back(centre);
		geom->vertices.push_back(vertex_prev);
		geom->vertices.push_back(vertex_current);
		for (int j = 0; j < 3; ++j)
			geom->normals.push_back(glm::normalize(glm::vec3(0.0f, 1.0f, 0.0f)));
		vertex_prev = vertex_current;
	}

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	return geom;
}
//create cylinder
geometry* createCylinder(int stacks, int slices)
{
	geometry* geom = new geometry();
	//Create top
	glm::vec3 centre(0.0f, 1.0f, 0.0f);
	glm::vec3 vertex_prev(1.0f, 1.0, 0.0f);
	glm::vec3 vertex_current;
	float deltaAngle = (2 * glm::pi<float>()) / slices;
	for (int i = 1; i <= slices; ++i)
	{
		vertex_current = glm::vec3(cos(i * deltaAngle), 1.0f, sin(i * deltaAngle));
		geom->vertices.push_back(centre);
		geom->normals.push_back(glm::normalize(glm::vec3(0.0f, 1.0f, 0.0f)));
		geom->vertices.push_back(vertex_prev);
		geom->normals.push_back(glm::normalize(glm::vec3(vertex_prev.x, 1.0f, vertex_prev.z)));
		geom->vertices.push_back(vertex_current);
		geom->normals.push_back(glm::normalize(glm::vec3(vertex_current.x, 1.0f, vertex_current.z)));
		vertex_prev = vertex_current;
	}
	
	//create Bottom
	centre = glm::vec3(0.0f, -1.0f, 0.0f);
	vertex_prev = glm::vec3(1.0f, -1.0f, 0.0f);
	for (int i = 1; i <= slices; ++i)
	{
		vertex_current = glm::vec3(cos(i * deltaAngle), -1.0f, sin(i * deltaAngle));
		geom->vertices.push_back(centre);
		geom->normals.push_back(glm::normalize(glm::vec3(0.0f, -1.0f, 0.0f)));
		geom->vertices.push_back(vertex_prev);
		geom->normals.push_back(glm::normalize(glm::vec3(vertex_prev.x, -1.0f, vertex_prev.z)));
		geom->vertices.push_back(vertex_current);
		geom->normals.push_back(glm::normalize(glm::vec3(vertex_current.x, -1.0f, vertex_current.z)));
		vertex_prev = vertex_current;
	}

	//Create Stack
	glm::vec3 vertices[4];
	float deltaHeight = 2.0f / stacks;
	for (int i = 0; i < stacks; ++i)
	{
		for (int j = 0; j < slices; ++j)
		{
			vertices[0] = glm::vec3(cos(j * deltaAngle), 1.0f - (deltaHeight * i), sin(j * deltaAngle));
			vertices[1] = glm::vec3(cos((j + 1) * deltaAngle), 1.0f - (deltaHeight * i), sin((j + 1) * deltaAngle));
			vertices[2] = glm::vec3(cos(j * deltaAngle), 1.0f - (deltaHeight * (i + 1)), sin(j * deltaAngle));
			vertices[3] = glm::vec3(cos((j + 1) * deltaAngle), 1.0f - (deltaHeight *(i + 1)), sin((j + 1) * deltaAngle));
			//triangle 1
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[0].x, 0.0f, vertices[0].z)));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[3].x, 0.0f, vertices[3].z)));
			geom->vertices.push_back(vertices[2]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[2].x, 0.0f, vertices[2].z)));
			//triangle 2
			geom->vertices.push_back(vertices[0]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[0].x, 0.0f, vertices[0].z)));
			geom->vertices.push_back(vertices[1]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[1].x, 0.0f, vertices[1].z)));
			geom->vertices.push_back(vertices[3]);
			geom->normals.push_back(glm::normalize(glm::vec3(vertices[3].x, 0.0f, vertices[3].z)));
		}
	}

	createVertexBuffer(geom);
	createNormalBuffer(geom);
	glBindVertexArray(0);
	return geom;
}