#include "Aktor.h"

Aktor::Aktor()
:liczbaWerteksów(-1), MacierzŚwiata(Macierz4::Jednostkowa),
MateriałŚwiatłoOtoczenia(Wektor4(0.1f, 0.1f, 0.1f, 1)),
 MateriałŚwiatłoRozpraszane(Wektor4(1, 1, 1, 1)),
 MateriałŚwiatłoRozbłysku(Wektor4(0, 1, 0, 1)),
 MateriałWykładnikRozbłysku(100), IndeksTekstury(-1)
 {
 }

Aktor::~Aktor()
{
	UsuńBuforWerteksów();
}

void Aktor::Inicjuj(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor)
{
	InicjujBuforWerteksów();

	glVertexAttribPointer(atrybutPołożenie, CWerteks::liczbaWspółrzędnychPołożenia, GL_FLOAT, GL_FALSE, CWerteks::rozmiarWerteksu, 0);
	glEnableVertexAttribArray(atrybutPołożenie);
	glVertexAttribPointer(atrybutNormalna, CWerteks::liczbaWspółrzędnychNormalnej, GL_FLOAT, GL_FALSE, CWerteks::rozmiarWerteksu, (const GLvoid*)CWerteks::rozmiarWektoraPołożenia);
	glEnableVertexAttribArray(atrybutNormalna);
	glVertexAttribPointer(atrybutWspółrzędneTeksturowania, CWerteks::liczbaWspółrzędnychTeksturowania, GL_FLOAT, GL_FALSE, CWerteks::rozmiarWerteksu, (const GLvoid*)(CWerteks::rozmiarWektoraPołożenia + CWerteks::rozmiarNormalnej));
	glEnableVertexAttribArray(atrybutWspółrzędneTeksturowania);
	glVertexAttribPointer(atrybutKolor, CWerteks::liczbaSkładowychKoloru, GL_FLOAT, GL_FALSE, CWerteks::rozmiarWerteksu, (const GLvoid*)(CWerteks::rozmiarWektoraPołożenia + CWerteks::rozmiarNormalnej + CWerteks::rozmiarWspółrzędnychTeksturowania));
	glEnableVertexAttribArray(atrybutKolor);
}

void Aktor::InicjujBuforWerteksów()
{
	//Vertex Array Object (VAO)
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	//Vertex Buffer Object (VBO)
	glGenBuffers(1, &vbo); //tworzy bufory
	glBindBuffer(GL_ARRAY_BUFFER, vbo);

	CWerteks* werteksy = NULL;
	liczbaWerteksów = TwórzTablicęWerteksów(werteksy);
	glBufferData(GL_ARRAY_BUFFER, liczbaWerteksów*sizeof(CWerteks), werteksy, GL_STATIC_DRAW);
	delete[] werteksy;
}

void Aktor::UsuńBuforWerteksów()
{
	glDeleteBuffers(1, &vbo);
	glDeleteVertexArrays(1, &vao);
}

void Aktor::Rysuj() //default implementation for triangle strip
{
	assert(liczbaWerteksów > 0);

	//bez bufora indeksów
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, liczbaWerteksów);
	//glBindBuffer(GL_ARRAY_BUFFER, NULL);
	//glBindVertexArray(NULL);
}

//--------------------------------------------

AktorZBuforemIndeksów::AktorZBuforemIndeksów()
:Aktor(), liczbaIndeksów(-1)
{
}

AktorZBuforemIndeksów::~AktorZBuforemIndeksów()
{
	//UsuńBuforWerteksów();
	Aktor::~Aktor();
	UsuńBuforIndeksów();
}

void AktorZBuforemIndeksów::Inicjuj(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor)
{
	Aktor::Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);
	InicjujBuforIndeksów();
}

void AktorZBuforemIndeksów::InicjujBuforIndeksów()
{
	glGenBuffers(1, &vbo_indeksy);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indeksy);

	GLuint* indeksy = NULL;
	liczbaIndeksów = TwórzTablicęIndeksów(indeksy);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, liczbaIndeksów*sizeof(GLuint), indeksy, GL_STATIC_DRAW);
	delete[] indeksy;
}

void AktorZBuforemIndeksów::UsuńBuforIndeksów()
{
	glDeleteBuffers(1, &vbo_indeksy);
}

void AktorZBuforemIndeksów::Rysuj()
{
	assert(liczbaIndeksów > 0);

	//z buforem indeksów
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER, vbo_indeksy);
	glDrawElements(GL_TRIANGLE_STRIP, liczbaIndeksów, GL_UNSIGNED_INT, 0);
	//glBindBuffer(GL_ARRAY_BUFFER, NULL);
	//glBindVertexArray(NULL);
}

//--------------------------------------------

Kwadrat::Kwadrat(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor, float długośćKrawędzi)
:Aktor(), długośćKrawędzi(długośćKrawędzi)
{
	Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);
}

unsigned int Kwadrat::TwórzTablicęWerteksów(CWerteks*& werteksy)
{
	const float x0 = długośćKrawędzi/2.0f;
	const float y0 = długośćKrawędzi/2.0f;

	werteksy = new CWerteks[4];
	werteksy[0] = CWerteks(-x0, -y0, 0.0f, 0, 0, 1, 0, 0, 1, 1, 0, 1); //dolny lewy
	werteksy[1] = CWerteks(x0, -y0, 0.0f, 0, 0, 1, 1, 0, 1, 0, 1, 1); //dolny prawy
	werteksy[2] = CWerteks(-x0, y0, 0.0f, 0, 0, 1, 0, 1, 0, 1, 1, 1); //górny lewy
	werteksy[3] = CWerteks(x0, y0, 0.0f, 0, 0, 1, 1, 1, 1, 1, 1, 1);  //górny prawy

	return 4;
}

//--------------------------------------------

Trójkąt::Trójkąt(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor, float długośćPodstawy, float wysokość)
:Aktor(), długośćPodstawy(długośćPodstawy), wysokość(wysokość)
{
	Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);
}

unsigned int Trójkąt::TwórzTablicęWerteksów(CWerteks*& werteksy)
{
	const float x0 = długośćPodstawy / 2.0f;
	const float y0 = wysokość / 2.0f;

	werteksy = new CWerteks[3];
	werteksy[0] = CWerteks(-x0, -y0, 0.0f, 0, 0, 1, 0, 0, 1, 0, 0, 1); //dolny lewy
	werteksy[1] = CWerteks(x0, -y0, 0.0f, 0, 0, 1, 1, 0, 0, 1, 0, 1); //dolny prawy
	werteksy[2] = CWerteks(0, y0, 0.0f, 0, 0, 1, 0.5f, 1, 0, 0, 1, 1); //górny lewy

	return 3;
}

//--------------------------------------------

KwadratZBuforemIndeksów::KwadratZBuforemIndeksów(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor, float długośćKrawędzi)
:AktorZBuforemIndeksów(), długośćKrawędzi(długośćKrawędzi)
{
	Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);
}

unsigned int KwadratZBuforemIndeksów::TwórzTablicęWerteksów(CWerteks*& werteksy)
{
	const float x0 = długośćKrawędzi / 2.0f;
	const float y0 = długośćKrawędzi / 2.0f;

	werteksy = new CWerteks[5];
	werteksy[0] = CWerteks(-x0, -y0, 0.0f, 0, 0, 1, 0, 0, 1, 0, 0, 1); //dolny lewy
	werteksy[1] = CWerteks(x0, -y0, 0.0f, 0, 0, 1, 1, 0, 0, 1, 0, 1); //dolny prawy
	werteksy[2] = CWerteks(0, y0, 0.0f, 0, 0, 1, 0.5f, 1, 0, 0, 1, 1); //górny
	werteksy[3] = CWerteks(-x0, y0, 0.0f, 0, 0, 1, 0, 1, 0, 0, 1, 1); //górny lewy
	werteksy[4] = CWerteks(x0, y0, 0.0f, 0, 0, 1, 1, 1, 0, 0, 1, 1);  //górny prawy

	return 5;
}

unsigned int KwadratZBuforemIndeksów::TwórzTablicęIndeksów(GLuint*& indeksy)
{
	indeksy = new GLuint[4];
	indeksy[0] = 0;
	indeksy[1] = 1;
	indeksy[2] = 3;
	indeksy[3] = 4;

	return 4;
}

//--------------------------------------------

Prostopadłościan::Prostopadłościan(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor, float długośćKrawędziX, float długośćKrawędziY, float długośćKrawędziZ, bool koloruj)
:Aktor(), długośćKrawędziX(długośćKrawędziX), długośćKrawędziY(długośćKrawędziY), długośćKrawędziZ(długośćKrawędziZ), koloruj(koloruj)
{
	Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);
}

unsigned int Prostopadłościan::TwórzTablicęWerteksów(CWerteks*& werteksy)
{
	const float x0 = długośćKrawędziX / 2.0f;
	const float y0 = długośćKrawędziY / 2.0f;
	const float z0 = długośćKrawędziZ / 2.0f;

	werteksy = new CWerteks[24];

	float r = 1.0f;
	float g = 1.0f;
	float b = 1.0f;

	if (koloruj) { r = 1.0f; g = 0.0f; b = 0.0f; }
	//tylnia
	werteksy[0] = CWerteks(x0, -y0, -z0, 0, 0, -1, 0, 0, r, g, b);
	werteksy[1] = CWerteks(-x0, -y0, -z0, 0, 0, -1, 1, 0, r, g, b);
	werteksy[2] = CWerteks(x0, y0, -z0, 0, 0, -1, 0, 1, r, g, b);
	werteksy[3] = CWerteks(-x0, y0, -z0, 0, 0, -1, 1, 1, r, g, b);
	//przednia
	werteksy[4] = CWerteks(-x0, -y0, z0, 0, 0, 1, 0, 0, r, g, b);
	werteksy[5] = CWerteks(x0, -y0, z0, 0, 0, 1, 1, 0, r, g, b);
	werteksy[6] = CWerteks(-x0, y0, z0, 0, 0, 1, 0, 1, r, g, b);
	werteksy[7] = CWerteks(x0, y0, z0, 0, 0, 1, 1, 1, r, g, b);
	/*
	//tylnia
	werteksy[0] = CWerteks(x0, -y0, -z0, 0, 0, -1, 0.25f, 0.2f, r, g, b);
	werteksy[1] = CWerteks(-x0, -y0, -z0, 0, 0, -1, 0.75f, 0.2f, r, g, b);
	werteksy[2] = CWerteks(x0, y0, -z0, 0, 0, -1, 0.25f, 0.8f, r, g, b);
	werteksy[3] = CWerteks(-x0, y0, -z0, 0, 0, -1, 0.75f, 0.8f, r, g, b);
	//przednia
	werteksy[4] = CWerteks(-x0, -y0, z0, 0, 0, 1, -1, 0, r, g, b);
	werteksy[5] = CWerteks(x0, -y0, z0, 0, 0, 1, 2, 0, r, g, b);
	werteksy[6] = CWerteks(-x0, y0, z0, 0, 0, 1, -1, 2, r, g, b);
	werteksy[7] = CWerteks(x0, y0, z0, 0, 0, 1, 2, 2, r, g, b);
	*/

	if (koloruj) { r = 0.0f; g = 1.0f; b = 0.0f; }
	//prawa
	werteksy[8] = CWerteks(x0, -y0, z0, 1, 0, 0, 0, 0, r, g, b);
	werteksy[9] = CWerteks(x0, -y0, -z0, 1, 0, 0, 1, 0, r, g, b);
	werteksy[10] = CWerteks(x0, y0, z0, 1, 0, 0, 0, 1, r, g, b);
	werteksy[11] = CWerteks(x0, y0, -z0, 1, 0, 0, 1, 1, r, g, b);
	//lewa
	werteksy[12] = CWerteks(-x0, -y0, -z0, -1, 0, 0, 0, 0, r, g, b);
	werteksy[13] = CWerteks(-x0, -y0, z0, -1, 0, 0, 1, 0, r, g, b);
	werteksy[14] = CWerteks(-x0, y0, -z0, -1, 0, 0, 0, 1, r, g, b);
	werteksy[15] = CWerteks(-x0, y0, z0, -1, 0, 0, 1, 1, r, g, b);

	if (koloruj) { r = 0.0f; g = 0.0f; b = 1.0f; }
	//górna
	werteksy[16] = CWerteks(-x0, y0, z0, 0, 1, 0, 0, 0, r, g, b);
	werteksy[17] = CWerteks(x0, y0, z0, 0, 1, 0, 1, 0, r, g, b);
	werteksy[18] = CWerteks(-x0, y0, -z0, 0, 1, 0, 0, 1, r, g, b);
	werteksy[19] = CWerteks(x0, y0, -z0, 0, 1, 0, 1, 1, r, g, b);
	//dolna
	werteksy[20] = CWerteks(-x0, -y0, -z0, 0, -1, 0, 0, 0, r, g, b);
	werteksy[21] = CWerteks(x0, -y0, -z0, 0, -1, 0, 1, 0, r, g, b);
	werteksy[22] = CWerteks(-x0, -y0, z0, 0, -1, 0, 0, 0, r, g, b);
	werteksy[23] = CWerteks(x0, -y0, z0, 0, -1, 0, 1, 0, r, g, b);

	return 24;
}

void Prostopadłościan::Rysuj()
{
	//bez bufora indeksów
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	for (int i = 0; i < 6; ++i)
	{
		glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
	}
	//glBindBuffer(GL_ARRAY_BUFFER, NULL);
	//glBindVertexArray(NULL);
}

// (3) Definicje metod klasy TeselowanyAktor.
TeselowanyAktor::TeselowanyAktor()
:Aktor(), liczbaIndeksów(-1)
{
}

TeselowanyAktor::~TeselowanyAktor()
{
	Aktor::~Aktor();
	UsuńBuforIndeksów();
}

void TeselowanyAktor::Inicjuj(GLuint atrybutPołożenie, GLuint atrybutNormalna, GLuint atrybutWspółrzędneTeksturowania, GLuint atrybutKolor)
{
	Aktor::Inicjuj(atrybutPołożenie, atrybutNormalna, atrybutWspółrzędneTeksturowania, atrybutKolor);

	glPatchParameteri(GL_PATCH_VERTICES, 3); // Typ prymitywu i liczba wierzchołków prymitywu.

	InicjujBuforIndeksów();
}

void TeselowanyAktor::InicjujBuforIndeksów()
{
	glGenBuffers(1, &vbo_indeksy);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indeksy);

	GLuint* indeksy = NULL;
	liczbaIndeksów = TwórzTablicęIndeksów(indeksy);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, liczbaIndeksów*sizeof(GLuint), indeksy, GL_STATIC_DRAW);
	delete[] indeksy;
}

void TeselowanyAktor::UsuńBuforIndeksów()
{
	glDeleteBuffers(1, &vbo_indeksy);
}

void TeselowanyAktor::Rysuj()
{
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER, vbo_indeksy);
	glDrawElements(GL_PATCHES, liczbaIndeksów, GL_UNSIGNED_INT, 0);
}

// (5)  Definicje metod klasy TeselowanaSfera.
unsigned int TeselowanaSfera::TwórzTablicęWerteksów(CWerteks*& werteksy)
{
	const unsigned int całkowitaLiczbaWerteksów = (liczbaSekcjiNaRównoleżnikach + 1) * (liczbaSekcjiNaPołudnikach + 1);
	werteksy = new CWerteks[całkowitaLiczbaWerteksów];

	const double przyrostKątaTheta = M_PI / liczbaSekcjiNaPołudnikach;
	const double przyrostKątaPhi = 2 * M_PI / liczbaSekcjiNaRównoleżnikach;

	for (unsigned int i = 0; i <= liczbaSekcjiNaPołudnikach; i++)
	{
		double kątTheta = i * przyrostKątaTheta;
		float _wysokość = (float)(promień * cos(kątTheta));
		float _promień = (float)(promień * sin(kątTheta));

		for (unsigned int j = 0; j <= liczbaSekcjiNaRównoleżnikach; j++)
		{
			double kątPhi = j*przyrostKątaPhi;
			int indeks = j + i*(liczbaSekcjiNaRównoleżnikach + 1);

			werteksy[indeks].x = _promień*(float)cos(kątPhi);
			werteksy[indeks].y = _promień*(float)sin(kątPhi);
			werteksy[indeks].z = _wysokość;

			werteksy[indeks].r = i/(liczbaSekcjiNaPołudnikach-1.0);
			werteksy[indeks].g = 0.5;
			werteksy[indeks].b = 0.5;
			werteksy[indeks].a = 1;

			if (i != 0 && i != liczbaSekcjiNaPołudnikach)
			{
				werteksy[indeks].nx = werteksy[indeks].x / _promień;
				werteksy[indeks].ny = werteksy[indeks].y / _promień;
				werteksy[indeks].nz = werteksy[indeks].z / _promień;
			}
			else
			{
				werteksy[indeks].nx = 0;
				werteksy[indeks].ny = 0;
				werteksy[indeks].nz = werteksy[indeks].z / (float)fabs(werteksy[indeks].z);
			}
			werteksy[indeks].s = (float)(1 - kątPhi / 2 / M_PI);
			werteksy[indeks].t = (float)(kątTheta / M_PI);
		}
	}

	return całkowitaLiczbaWerteksów;
}

unsigned int TeselowanaSfera::TwórzTablicęIndeksów(GLuint*& indeksy)
{
	const unsigned int liczbaIndeksówWPaśmie = 6 * liczbaSekcjiNaRównoleżnikach;
	const unsigned int całkowitaLiczbaIndeksów =
		liczbaIndeksówWPaśmie * liczbaSekcjiNaPołudnikach;
	indeksy = new GLuint[całkowitaLiczbaIndeksów];

	for (unsigned int i = 0; i < liczbaSekcjiNaPołudnikach; i++)
	{
		for (unsigned int j = 0; j < liczbaSekcjiNaRównoleżnikach; j++)
		{
			int szeregowo = i*liczbaSekcjiNaRównoleżnikach+j; 
			indeksy[6 * szeregowo] = i * (liczbaSekcjiNaRównoleżnikach+1) + j; 
			indeksy[6 * szeregowo + 1] = i * (liczbaSekcjiNaRównoleżnikach+1) + j + (liczbaSekcjiNaRównoleżnikach+1);
			indeksy[6 * szeregowo + 2] = i * (liczbaSekcjiNaRównoleżnikach+1) + j + liczbaSekcjiNaRównoleżnikach + 1 + 1;
			indeksy[6 * szeregowo + 3] = i * (liczbaSekcjiNaRównoleżnikach+1) + j;
			indeksy[6 * szeregowo + 4] = i * (liczbaSekcjiNaRównoleżnikach+1) + j + (liczbaSekcjiNaRównoleżnikach+1) + 1;
			indeksy[6 * szeregowo + 5] = i * (liczbaSekcjiNaRównoleżnikach+1) + j + 1;
		}
	}

	return całkowitaLiczbaIndeksów;
}
