#include "Rasteriser.h"

Rasteriser app;

Rasteriser::~Rasteriser()
{	
	_lights.~vector();
	_sceneModels.~vector();
}

void Rasteriser::DrawString(HDC hDc, int x, int y, LPCTSTR text, COLORREF textColor, COLORREF backgroundColor)
{	
	HFONT hFont, hOldFont;

	// Retrieve a handle to the variable stock font.  
	hFont = hFont = CreateFont(48, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Myfont"));

	// Select the variable stock font into the specified device context. 
	if (hOldFont = (HFONT)SelectObject(hDc, hFont))
	{
		SetTextColor(hDc, textColor);
		SetBkColor(hDc, backgroundColor);

		// Display the text string.  
		TextOut(hDc, x, y, text, lstrlen(text));

		// Restore the original font.        
		SelectObject(hDc, hOldFont);
	}
	DeleteObject(hFont);
}

bool Rasteriser::Initialise()
{
	Model modelA = Model(_modelDataLocation + "cube.MD2", _modelDataLocation + "lines.pcx");
	// Changed to adding the model to the vector first because doing it once the model has been loaded
	// caused the texture pointers to be at incorrect addresses.............
	_sceneModels.push_back(modelA);

	for (Model& currentModel : _sceneModels)
	{
		if (MD2Loader::LoadModel(currentModel.CGetModelFilename(), currentModel.CGetTextureFilename(), currentModel, &Model::AddPolygon, &Model::AddVertex, &Model::AddTextureUV))
		{
			currentModel.SetDrawMethod(DrawMethod::Textured);
			currentModel.SetActive(true);

		}
		else
		{
			return false;
		}
	}		
	
	_lights.push_back(new AmbientLight(50, 50, 50));
	//_lights.push_back(new AmbientLight(255, 255, 255));
	_lights.push_back(new DirectionalLight(Vector3D(-1, 0, -1), 150, 150, 150));
	//_lights.push_back(new PointLight(Vertex(0, 0, -10), 0, 1, 0.01f, 150, 0, 0));
	_cameras.push_back(Camera(0.0f, 0.0f, 0.0f, Vertex(0.0f, 0.0f, -50.0f)));

	_screenMinimized = false;

	return true;
}

void Rasteriser::SetCurrentCamera(int index)
{
	if (index >= 0 && index < _cameras.size())
	{
		_currentCamera = index;
	}	
}

Camera& Rasteriser::GetCamera(int index)
{
	return _cameras[index];
}

Camera& Rasteriser::GetCurrentCamera()
{
	return GetCamera(_currentCamera);
}

void Rasteriser::CalculateLighting(Model& currentModel, bool fullLightRender)
{	
	{
		for (int pi = 0; pi < currentModel.GetPolygonCount(); pi++)
		{
			if (!currentModel.GetPolygon(pi).GetCulled())
			{
				if (fullLightRender)
				{
					for (int i = 0; i < currentModel.GetPolygon(pi).GetPolygonVertexCount(); i++)
					{
						COLORREF colorWorking = RGB(0, 0, 0);
						for (Light* currentLight : _lights)
						{
							if (currentLight->GetLightActive())
							{
								colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetVertex(pi, i), colorWorking);
							}
						}
						currentModel.SetVertexColor(pi, i, colorWorking);
					}
				}
				else
				{
					COLORREF colorWorking = RGB(0, 0, 0);
					for (Light* currentLight : _lights)
					{
						if (currentLight->GetLightActive())
						{
							colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetPolygon(pi), colorWorking);
						}
					}
					currentModel.SetPolygonColor(pi, colorWorking);
				}
			}
		}
	}	
}

float Rasteriser::Interpolate(float y, float x0, float x1, float y0, float y1)
{
	return x0 + (y - y0) * ((x1 - x0) / (y1 - y0));
}

float Rasteriser::Interpolate(int y, int x0, int x1, int y0, int y1)
{
	return Interpolate((float)y, (float)x0, (float)x1, (float)y0, (float)y1);
}

float Rasteriser::Lerp(float a, float b, float t)
{
	return (1 - t) * a + t * b;
}

float Rasteriser::Lerp(int a, int b, float t)
{
	return Lerp((float)a, (float)b, t);
}

int toDraw = 1;
int drawn = 0;

void Rasteriser::Update(const Bitmap& bitmap)
{			
	drawn = 0;
	if (bitmap.GetWidth() == 0 || bitmap.GetHeight() == 0)
	{
		_screenMinimized = true;
	}
	else
	{
		_screenMinimized = false;
	}

	if (!_screenMinimized)
	{
		int currentRot = 0;// 45; // (_rotation % 360);
		Vertex startPos = Vertex(0, 0, 20);
		int currentZOff = 0;
		int currentModelIndex = 0;

		for (Model& currentModel : _sceneModels)
		{
			if (currentModel.GetActive())
			{
				currentModel.EnqueueTransform(GetTranslateMatrix(startPos));
				currentModel.EnqueueTransform(GetRotationMatrix(Axis::Y, (float)currentRot));
				/*startPos.SetX(startPos.GetX() + 100);
				if ((currentModelIndex % 2) == 1)
				{
					currentZOff = 0;
				}
				else
				{
					currentZOff = 100;
				}
				startPos.SetZ((float)currentZOff);*/
				currentModelIndex++;
			}
		}

		_currentAspectRatio = (float)(bitmap.GetWidth() / (float)bitmap.GetHeight());
		_currentPerspectiveMatrix = GetPerspectiveProjectionMatrix(1, _currentAspectRatio);
		_currentViewMatrix = GetViewMatrix(1, bitmap.GetWidth(), bitmap.GetHeight());

		if (_rotation == 360)
		{
			_rotation = 0;
		}
		else
		{
			_rotation += 1;
		}
	}
}

void Rasteriser::Render(const Bitmap& bitmap)
{		
	if (!_screenMinimized)
	{
		ClearViewport(bitmap);
		SelectObject(bitmap.GetDC(), GetStockObject(DC_BRUSH));
		SelectObject(bitmap.GetDC(), GetStockObject(DC_PEN));

		for (Model& currentModel : _sceneModels)
		{
			if (currentModel.GetActive())
			{
				currentModel.SetReflectionCoefficient(0.5f, 0.5f, 0.5f);
				Matrix workingMatrix = workingMatrix.IdentityMatrix();
				for (Matrix currentTransform : currentModel.GetPendingTransforms())
				{
					workingMatrix *= currentTransform;
				}
				currentModel.ApplyTransformToLocalVertices(workingMatrix);
				currentModel.ApplyTransformToTransformedVertices(GetCurrentCamera().GetCurrentCameraTransformMatrix());

				currentModel.CalculateBackfaces(GetCurrentCamera());
				currentModel.Sort();

				switch (currentModel.GetDrawMethod())
				{
				case (DrawMethod::Wireframe):
					currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
					currentModel.DehomogenizeAllVertices();
					currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
					DrawWireFrame(bitmap.GetDC(), currentModel);
					break;
				case (DrawMethod::Flat):
					currentModel.CalculateVertexNormals();
					CalculateLighting(currentModel, false);

					currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
					currentModel.DehomogenizeAllVertices();
					currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
					DrawSolidFlat(bitmap.GetDC(), currentModel);
					break;
				case (DrawMethod::FlatRaster):
					currentModel.CalculateVertexNormals();
					CalculateLighting(currentModel, false);

					currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
					currentModel.DehomogenizeAllVertices();
					currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
					DrawRasterisedSolidFlat(bitmap.GetDC(), currentModel);
					break;
				case (DrawMethod::Smooth):
					currentModel.CalculateVertexNormals();
					CalculateLighting(currentModel, true);

					currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
					currentModel.DehomogenizeAllVertices();
					currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
					DrawGouraud(bitmap.GetDC(), currentModel);
					break;
				case (DrawMethod::Textured):
					currentModel.CalculateVertexNormals();
					CalculateLighting(currentModel, true);

					currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
					currentModel.DehomogenizeAllVertices();
					currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);					
					DrawSolidTextured(bitmap.GetDC(), currentModel);
					break;
				}					
				currentModel.ClearPendingTransforms();
			}
		}
				
		DrawString(bitmap.GetDC(), 10, 10, _T("Hello World"));
	}
}

void Rasteriser::ClearViewport(const Bitmap& bitmap)
{
	bitmap.Clear(RGB(0, 0, 0));
}

void Rasteriser::DrawSquare(HDC hDc, const vector<Vertex> verticies)
{		
	POINT pointArray[4];
	for (int i = 0; i < 4; i++)
	{
		pointArray[i].x = (long) verticies[i].GetX();
		pointArray[i].y = (long) verticies[i].GetY();
	}
	
	SetDCBrushColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256));
	SetDCPenColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256));
	Polygon(hDc, pointArray, 4);
}

void Rasteriser::DrawShape(HDC hDc, const vector<Vertex> verticies)
{	
	vector<POINT> pointArray;
	for (int i = 0; i < verticies.size(); i++)
	{
		POINT newPoint;
		newPoint.x = (long)verticies[i].GetX();
		newPoint.y = (long)verticies[i].GetY();
		pointArray.push_back(newPoint);
	}

	SetDCBrushColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256));
	SetDCPenColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256));
	Polygon(hDc, pointArray.data(), (int) verticies.size());
}

void Rasteriser::DrawWireFrame(HDC hDc, Model& model)
{
	vector<POINT> pointArray;
	vector<int> sizeArray;	

	int unculledPolyCount = 0;
	int modelPolygonCount = (int) model.GetPolygonCount();
	for (int i = 0; i < modelPolygonCount; i++)
	{	
		
		if (!model.GetPolygon(i).GetCulled())
		{
			int currentPolygonVertexCount = (int)model.GetPolygon(i).GetPolygonVertexCount();
			for (int j = 0; j < currentPolygonVertexCount; j++)
			{
				POINT newPoint;
				newPoint.x = (long)model.GetVertex(i, j).GetX();
				newPoint.y = (long)model.GetVertex(i, j).GetY();
				pointArray.push_back(newPoint);
			}
			sizeArray.push_back(currentPolygonVertexCount);
			unculledPolyCount++;
		}
	}

	SetDCBrushColor(hDc, RGB(1,1,1));
	SetDCPenColor(hDc, RGB(255,255,255));
	PolyPolygon(hDc, pointArray.data(), sizeArray.data(), unculledPolyCount);
}

void Rasteriser::DrawSolidFlat(HDC hDc, Model& model)
{	
	int modelPolygonCount = (int)model.GetPolygonCount();
	for (int i = 0; i < modelPolygonCount; i++)
	{

		if (!model.GetPolygon(i).GetCulled())
		{
			vector<POINT> pointArray;
			int currentPolygonVertexCount = (int)model.GetPolygon(i).GetPolygonVertexCount();
			for (int j = 0; j < currentPolygonVertexCount; j++)
			{
				POINT newPoint;
				newPoint.x = (long)model.GetVertex(i, j).GetX();
				newPoint.y = (long)model.GetVertex(i, j).GetY();
				pointArray.push_back(newPoint);
			}
			SetDCBrushColor(hDc, model.GetPolygon(i).GetColor());
			SetDCPenColor(hDc, model.GetPolygon(i).GetColor());
			Polygon(hDc, pointArray.data(), currentPolygonVertexCount);
		}
	}
}

void Rasteriser::DrawRasterisedSolidFlat(HDC hDc, Model& model)
{
	int modelPolygonCount = (int)model.GetPolygonCount();
	for (int i = 0; i < modelPolygonCount; i++)
	{

		if (!model.GetPolygon(i).GetCulled())
		{
			vector<Vertex> vertexArray = model.GetPolygonVertexArray(i);
			FillPolygonFlat(hDc, vertexArray, model.GetPolygon(i).GetColor());
		}
	}
}

void Rasteriser::DrawGouraud(HDC hDc, Model& model)
{
	int modelPolygonCount = (int)model.GetPolygonCount();
	for (int i = 0; i < modelPolygonCount; i++)
	{

		if (!model.GetPolygon(i).GetCulled())
		{
			vector<Vertex> vertexArray = model.GetPolygonVertexArray(i);
			FillPolygonGouraud(hDc, vertexArray);
		}
	}
}

void Rasteriser::DrawSolidTextured(HDC hDc, Model& model)
{
	int modelPolygonCount = (int)model.GetPolygonCount();
	for (int i = 0; i < modelPolygonCount; i++)
	{

		if (!model.GetPolygon(i).GetCulled())
		{
			vector<Vertex> vertexArray = model.GetPolygonVertexArray(i);
			FillPolygonTextured(hDc, model, vertexArray);
		}
	}
}

bool VerticiesYCompareAsc(Vertex& v1, Vertex& v2)
{
	return v1.GetY() < v2.GetY();
}

void Rasteriser::FillPolygonFlat(HDC hDc, vector<Vertex>& verts, COLORREF colorIn)
{
	sort(verts.begin(), verts.end(), VerticiesYCompareAsc);
	if (verts[1].GetY() == verts[2].GetY())
	{
		FillFlatSideTriangle(hDc, verts[0], verts[1], verts[2], colorIn);
	}
	else if (verts[0].GetY() == verts[1].GetY())
	{
		FillFlatSideTriangle(hDc, verts[2], verts[0], verts[1], colorIn);
	}
	else
	{
		Vertex temp = Vertex(verts[0].GetX() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetX() - verts[0].GetX()), verts[1].GetY(), verts[1].GetZ());
		if (verts[1].GetX() < temp.GetX())
		{
			FillFlatSideTriangle(hDc, verts[0], verts[1], temp, colorIn);
			FillFlatSideTriangle(hDc, verts[2], verts[1], temp, colorIn);
		}
		else
		{
			FillFlatSideTriangle(hDc, verts[0], temp, verts[1], colorIn);
			FillFlatSideTriangle(hDc, verts[2], temp, verts[1], colorIn);
		}		
	}
}

void Rasteriser::FillFlatSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3, COLORREF colorIn)
{
	Vertex tempA = Vertex(v1);
	Vertex tempB = Vertex(v1);

	bool changed1 = false;
	bool changed2 = false;

	int dx1 = (int)ceil(abs(v2.GetX() - v1.GetX()));
	int dy1 = (int)ceil(abs(v2.GetY() - v1.GetY()));

	int dx2 = (int)ceil(abs(v3.GetX() - v1.GetX()));
	int dy2 = (int)ceil(abs(v3.GetY() - v1.GetY()));

	int signx1 = (int)ceil(sgn(v2.GetX() - v1.GetX()));
	int signx2 = (int)ceil(sgn(v3.GetX() - v1.GetX()));

	int signy1 = (int)ceil(sgn(v2.GetY() - v1.GetY()));
	int signy2 = (int)ceil(sgn(v3.GetY() - v1.GetY()));

	if (dy1 > dx1)
	{
		int tempInt = dx1;
		dx1 = dy1;
		dy1 = tempInt;
		changed1 = true;
	}

	if (dy2 > dx2)
	{
		int tempInt = dx2;
		dx2 = dy2;
		dy2 = tempInt;
		changed2 = true;
	}

	int e1 = 2 * dy1 - dx1;
	int e2 = 2 * dy2 - dx2;	

	for (int i = 0; i <= dx1; i++)
	{
		float leftEndPoint;
		float rightEndPoint;

		if (tempA.GetX() < tempB.GetX())
		{
			leftEndPoint = tempA.GetX() - 1.0f;
			rightEndPoint = tempB.GetX() + 1.0f;
		}
		else
		{
			leftEndPoint = tempB.GetX() - 1.0f;
			rightEndPoint = tempA.GetX() + 1.0f;
		}

		for (int xi = (int)ceil(leftEndPoint); xi <= (int)ceil(rightEndPoint); xi++)
		{
			SetPixel(hDc, xi, tempA.GetYInt(), colorIn);
		}
		
				

		while (e1 >= 0)
		{
			if (changed1)
			{
				tempA.SetX((float)tempA.GetX() + (float)signx1);
			}
			else
			{
				tempA.SetY((float)tempA.GetY() + (float)signy1);
			}
			e1 = e1 - 2 * dx1;
		}

		if (changed1)
		{
			tempA.SetY((float)tempA.GetY() + (float)signy1);
		}
		else
		{
			tempA.SetX((float)tempA.GetX() + (float)signx1);
		}

		e1 = e1 + 2 * dy1;

		while (tempB.GetYInt() != tempA.GetYInt())
		{
			while (e2 >= 0)
			{
				if (changed2)
				{
					tempB.SetX((float)tempB.GetXInt() + (float)signx2);
				}
				else
				{
					tempB.SetY((float)tempB.GetYInt() + (float)signy2);
				}
				e2 = e2 - 2 * dx2;
			}

			if (changed2)
			{
				tempB.SetY((float)tempB.GetYInt() + (float)signy2);
			}
			else
			{
				tempB.SetX((float)tempB.GetXInt() + (float)signx2);
			}
			e2 = e2 + 2 * dy2;
		}
	}
}

void Rasteriser::FillPolygonGouraud(HDC hDc, vector<Vertex>& verts)
{
	sort(verts.begin(), verts.end(), VerticiesYCompareAsc);
	if (verts[1].GetY() == verts[2].GetY())
	{
		FillGouraudSideTriangle(hDc, verts[0], verts[1], verts[2]);
	}
	else if (verts[0].GetY() == verts[1].GetY())
	{
		FillGouraudSideTriangle(hDc, verts[2], verts[0], verts[1]);
	}
	else
	{
		Vertex temp = Vertex(verts[0].GetX() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetX() - verts[0].GetX()), verts[1].GetY(), verts[1].GetZ());		
		temp.SetNormal(verts[1].GetNormal());

		float vDiff = ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY()));
		float cRed = Lerp(verts[0].GetR(), verts[2].GetR(), vDiff);
		float cGreen = Lerp(verts[0].GetG(), verts[2].GetG(), vDiff);
		float cBlue = Lerp(verts[0].GetB(), verts[2].GetB(), vDiff);
		temp.SetColor(BoundsCheck(0, 255, (int)cRed), BoundsCheck(0, 255, (int)cGreen), BoundsCheck(0, 255, (int)cBlue));

		if (verts[1].GetX() < temp.GetX())
		{
			FillGouraudSideTriangle(hDc, verts[0], verts[1], temp);
			FillGouraudSideTriangle(hDc, verts[2], verts[1], temp);
		}
		else
		{
			FillGouraudSideTriangle(hDc, verts[0], temp, verts[1]);
			FillGouraudSideTriangle(hDc, verts[2], temp, verts[1]);
		}
	}
}

void Rasteriser::FillGouraudSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
	Vertex tempA = Vertex(v1);
	Vertex tempB = Vertex(v1);

	bool changed1 = false;
	bool changed2 = false;

	int dx1 = (int)ceil(abs(v2.GetX() - v1.GetX()));
	int dy1 = (int)ceil(abs(v2.GetY() - v1.GetY()));

	int dx2 = (int)ceil(abs(v3.GetX() - v1.GetX()));
	int dy2 = (int)ceil(abs(v3.GetY() - v1.GetY()));

	int signx1 = (int)ceil(sgn(v2.GetX() - v1.GetX()));
	int signx2 = (int)ceil(sgn(v3.GetX() - v1.GetX()));

	int signy1 = (int)ceil(sgn(v2.GetY() - v1.GetY()));
	int signy2 = (int)ceil(sgn(v3.GetY() - v1.GetY()));

	if (dy1 > dx1)
	{
		int tempDx = dx1;
		dx1 = dy1;
		dy1 = tempDx;
		changed1 = true;
	}

	if (dy2 > dx2)
	{
		int tempDx = dx2;
		dx2 = dy2;
		dy2 = tempDx;
		changed2 = true;
	}

	int e1 = 2 * (int)dy1 - (int)dx1;
	int e2 = 2 * (int)dy2 - (int)dx2;

	for (int i = 0; i <= (int)dx1; i++)
	{
		float leftEndPoint;
		float rightEndPoint;

		if (tempA.GetX() < tempB.GetX())
		{
			leftEndPoint = tempA.GetX();
			rightEndPoint = tempB.GetX() + 1.0f;
		}
		else
		{
			leftEndPoint = tempB.GetX();
			rightEndPoint = tempA.GetX() + 1.0f;
		}

		//float iRedA = Interpolate(tempA.GetY(), (float)v1.GetR(), (float)v2.GetR(), v1.GetY(), v2.GetY());
		//float iRedB = Interpolate(tempA.GetY(), (float)v1.GetR(), (float)v3.GetR(), v1.GetY(), v3.GetY());
		//float iGreenA = Interpolate(tempA.GetY(), (float)v1.GetG(), (float)v2.GetG(), v1.GetY(), v2.GetY());
		//float iGreenB = Interpolate(tempA.GetY(), (float)v1.GetG(), (float)v3.GetG(), v1.GetY(), v3.GetY());
		//float iBlueA = Interpolate(tempA.GetY(), (float)v1.GetB(), (float)v2.GetB(), v1.GetY(), v2.GetY());
		//float iBlueB = Interpolate(tempA.GetY(), (float)v1.GetB(), (float)v3.GetB(), v1.GetY(), v3.GetY());

		// Interpolate the Edge Colors of the 3 Vertex, Lerping seems to give better results given the 
		// non precise nature of converting the floats into ints since it works on the start and end, and
		// interpolates fixed between those values (regular interpolation would sometimes overflow and cause
		// a value higher then 255 to be used.
		float iRedA = Lerp(v1.GetR(), v2.GetR(), (float)i / (float)dx1);
		float iRedB = Lerp(v1.GetR(), v3.GetR(), (float)i / (float)dx1);
		float iGreenA = Lerp(v1.GetG(), v2.GetG(), (float)i / (float)dx1);
		float iGreenB = Lerp(v1.GetG(), v3.GetG(), (float)i / (float)dx1);
		float iBlueA = Lerp(v1.GetB(), v2.GetB(), (float)i / (float)dx1);
		float iBlueB = Lerp(v1.GetB(), v3.GetB(), (float)i / (float)dx1);

		int xLength = (int)ceil(rightEndPoint) - (int)ceil(leftEndPoint);
		int ci = 0;

		for (int xi = (int)leftEndPoint; xi <= (int)rightEndPoint; xi++)
		{				
			float ti = (float)ci / (float)xLength;
			float redTmp = Lerp(iRedA, iRedB, ti);
			float greenTmp = Lerp(iGreenA, iGreenB, ti);
			float blueTmp = Lerp(iBlueA, iBlueB, ti);

			COLORREF currentColor = RGB(BoundsCheck(0, 255, (int)redTmp), BoundsCheck(0, 255, (int)greenTmp), BoundsCheck(0, 255, (int)blueTmp));
			SetPixel(hDc, xi, tempA.GetYInt(), currentColor);
			ci++;
		}


		while (e1 >= 0)
		{
			if (changed1)
			{
				tempA.SetX((float)tempA.GetX() + (float)signx1);
			}
			else
			{
				tempA.SetY((float)tempA.GetY() + (float)signy1);
			}
			e1 = e1 - 2 * dx1;
		}

		if (changed1)
		{
			tempA.SetY((float)tempA.GetY() + (float)signy1);
		}
		else
		{
			tempA.SetX((float)tempA.GetX() + (float)signx1);
		}

		e1 = e1 + 2 * dy1;

		while (tempB.GetYInt() != tempA.GetYInt())
		{
			while (e2 >= 0)
			{
				if (changed2)
				{
					tempB.SetX((float)tempB.GetX() + (float)signx2);
				}
				else
				{
					tempB.SetY((float)tempB.GetY() + (float)signy2);
				}
				e2 = e2 - 2 * dx2;
			}

			if (changed2)
			{
				tempB.SetY((float)tempB.GetY() + (float)signy2);
			}
			else
			{
				tempB.SetX((float)tempB.GetX() + (float)signx2);
			}
			e2 = e2 + 2 * dy2;
		}
	}
}

void Rasteriser::FillPolygonTextured(HDC hDc, Model& model, vector<Vertex>& verts)
{
	sort(verts.begin(), verts.end(), VerticiesYCompareAsc);
	if (verts[1].GetY() == verts[2].GetY())
	{
		FillTexturedSideTriangle(hDc, model, verts[0], verts[1], verts[2], model.GetUVCoord(verts[0].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()), model.GetUVCoord(verts[2].GetUVIndex()));
	}
	else if (verts[0].GetY() == verts[1].GetY())
	{
		FillTexturedSideTriangle(hDc, model, verts[2], verts[0], verts[1], model.GetUVCoord(verts[2].GetUVIndex()), model.GetUVCoord(verts[0].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()));
	}
	else
	{
		Vertex temp = Vertex(verts[0].GetX() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetX() - verts[0].GetX()), verts[1].GetY(), verts[1].GetZ());
		temp.SetNormal(verts[1].GetNormal());		
				
		//model.GetUVCoord(verts[1].GetUVIndex()).GetU() + ((model.GetUVCoord(verts[1].GetUVIndex()).GetV() - model.GetUVCoord(verts[0].GetUVIndex()).GetV()) / (model.GetUVCoord(verts[2].GetUVIndex()).GetV() - model.GetUVCoord(verts[0].GetUVIndex()).GetV())) * (model.GetUVCoord(verts[2].GetUVIndex()).GetU() - model.GetUVCoord(verts[0].GetUVIndex()).GetU());

		float uc0 = model.GetUVCoord(verts[0].GetUVIndex()).GetU();
		float vc0 = model.GetUVCoord(verts[0].GetUVIndex()).GetV();
		float uc1 = model.GetUVCoord(verts[1].GetUVIndex()).GetU();
		float vc1 = model.GetUVCoord(verts[1].GetUVIndex()).GetV();
		float uc2 = model.GetUVCoord(verts[2].GetUVIndex()).GetU();
		float vc2 = model.GetUVCoord(verts[2].GetUVIndex()).GetV();

		float tempU = uc0 + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (uc2 - uc0);
		float tempV = vc0 + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (vc2 - vc0);
		UVCoord tempCoords = UVCoord(tempU, tempV);

		float cRed = verts[0].GetR() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetR() - verts[0].GetR());
		float cGreen = verts[0].GetG() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetG() - verts[0].GetG());
		float cBlue = verts[0].GetB() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetB() - verts[0].GetB());
		temp.SetColor(BoundsCheck(0, 255, (int)cRed), BoundsCheck(0, 255, (int)cGreen), BoundsCheck(0, 255, (int)cBlue));

		if (drawn < toDraw + 100)
		{
			if (verts[1].GetX() < temp.GetX())
			{
				FillTexturedSideTriangle(hDc, model, verts[0], verts[1], temp, model.GetUVCoord(verts[0].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()), tempCoords);
				FillTexturedSideTriangle(hDc, model, verts[2], verts[1], temp, model.GetUVCoord(verts[2].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()), tempCoords);
				drawn++;
			}
			else
			{
				FillTexturedSideTriangle(hDc, model, verts[0], temp, verts[1], model.GetUVCoord(verts[0].GetUVIndex()), tempCoords, model.GetUVCoord(verts[1].GetUVIndex()));
				FillTexturedSideTriangle(hDc, model, verts[2], temp, verts[1], model.GetUVCoord(verts[2].GetUVIndex()), tempCoords, model.GetUVCoord(verts[1].GetUVIndex()));
				drawn++;
			}
		}
	}
}

void Rasteriser::FillTexturedSideTriangle(HDC hDc, Model& model, const Vertex& v1, const Vertex& v2, const Vertex& v3, const UVCoord& uv1, const UVCoord& uv2, const UVCoord& uv3)
{
	Vertex tempA = Vertex(v1);
	Vertex tempB = Vertex(v1);
	UVCoord tempUVA = UVCoord(uv1);
	UVCoord tempUVB = UVCoord(uv1);

	bool changed1 = false;
	bool changed2 = false;

	float uc0 = uv1.GetU();
	float vc0 = uv1.GetV();
	float uc1 = uv2.GetU();
	float vc1 = uv2.GetV();
	float uc2 = uv3.GetU();
	float vc2 = uv3.GetV();

	int dx1 = (int)ceil(abs(v2.GetX() - v1.GetX()));
	int dy1 = (int)ceil(abs(v2.GetY() - v1.GetY()));	
	int du1 = (int)ceil(abs(uc1 - uc0));
	int dv1 = (int)ceil(abs(vc1 - vc0));

	int dx2 = (int)ceil(abs(v3.GetX() - v1.GetX()));
	int dy2 = (int)ceil(abs(v3.GetY() - v1.GetY()));
	int du2 = (int)ceil(abs(uc2 - uc0));
	int dv2 = (int)ceil(abs(vc2 - vc0));

	int signx1 = (int)ceil(sgn(v2.GetX() - v1.GetX()));
	int signx2 = (int)ceil(sgn(v3.GetX() - v1.GetX()));
	int signu1 = (int)ceil(sgn(uc1 - uc0));
	int signu2 = (int)ceil(sgn(uc2 - uc0));

	int signy1 = (int)ceil(sgn(v2.GetY() - v1.GetY()));
	int signy2 = (int)ceil(sgn(v3.GetY() - v1.GetY()));
	int signv1 = (int)ceil(sgn(vc1 - vc0));
	int signv2 = (int)ceil(sgn(vc2 - vc0));

	if (dy1 > dx1)
	{
		int tempDx = dx1;
		dx1 = dy1;
		dy1 = tempDx;

		changed1 = true;
	}

	if (dy2 > dx2)
	{
		int tempDx = dx2;
		dx2 = dy2;
		dy2 = tempDx;

		changed2 = true;
	}

	int e1 = 2 * (int)dy1 - (int)dx1;
	int e2 = 2 * (int)dy2 - (int)dx2;

	for (int i = 0; i <= (int)dx1; i++)
	{
		float leftEndPoint;
		float rightEndPoint;
		float leftU;
		float rightU;
		float leftV;
		float rightV;

		if (tempA.GetX() < tempB.GetX())
		{
			leftEndPoint = tempA.GetX() - 1.0f;
			rightEndPoint = tempB.GetX() + 1.0f;		
			leftU = tempUVA.GetU();
			rightU = tempUVB.GetU();
			leftV = tempUVA.GetV();
			rightV = tempUVB.GetV();
		}
		else
		{
			leftEndPoint = tempB.GetX() - 1.0f;
			rightEndPoint = tempA.GetX() + 1.0f;
			leftU = tempUVB.GetU();
			rightU = tempUVA.GetU();
			leftV = tempUVB.GetV();
			rightV = tempUVA.GetV();
		}
				
		float uy1 = Lerp(uv1.GetV(), uv2.GetV(), (float)i / (float)dx1);
		float uy2 = Lerp(uv1.GetV(), uv3.GetV(), (float)i / (float)dx1);

		//x0 + (y - y0) * ((x1 - x0) / (y1 - y0))
		//a + (b - a) * t
		float UCoordA = Interpolate(tempA.GetY(), uv1.GetU(), uv2.GetU(), v1.GetY(), v2.GetY());
		float UCoordB = Interpolate(tempA.GetY(), uv1.GetU(), uv3.GetU(), v1.GetY(), v3.GetY());
		float VCoordA = Interpolate(tempA.GetY(), uv1.GetV(), uv2.GetV(), v1.GetY(), v2.GetY());
		float VCoordB = Interpolate(tempA.GetY(), uv1.GetV(), uv3.GetV(), v1.GetY(), v3.GetY());

		float UCoord = (rightU - leftU) / (rightEndPoint - leftEndPoint);
		float VCoord = (rightV - leftV) / (rightEndPoint - leftEndPoint);

		float iRedA = Lerp(v1.GetR(), v2.GetR(), (float)i / (float)dx1);
		float iRedB = Lerp(v1.GetR(), v3.GetR(), (float)i / (float)dx1);
		float iGreenA = Lerp(v1.GetG(), v2.GetG(), (float)i / (float)dx1);
		float iGreenB = Lerp(v1.GetG(), v3.GetG(), (float)i / (float)dx1);
		float iBlueA = Lerp(v1.GetB(), v2.GetB(), (float)i / (float)dx1);
		float iBlueB = Lerp(v1.GetB(), v3.GetB(), (float)i / (float)dx1);

		int xLength = (int)ceil(rightEndPoint) - (int)ceil(leftEndPoint);
		int ci = 0;

		if (i < 10000)
		{
			for (int xi = (int)ceil(leftEndPoint); xi <= (int)ceil(rightEndPoint); xi++)
			{
				float ti = (float)ci / (float)xLength;
				float UCoordTmp = Lerp(UCoordA, UCoordB, ti);
				float VCoordTmp = Lerp(VCoordA, VCoordB, ti);
				float redTmp = Lerp(iRedA, iRedB, ti) / 100;
				float greenTmp = Lerp(iGreenA, iGreenB, ti) / 100;
				float blueTmp = Lerp(iBlueA, iBlueB, ti) / 100;

				COLORREF textureColor = model.GetTexture().GetTextureValue((int)UCoordTmp, (int)VCoordTmp);
				COLORREF currentColor = RGB(BoundsCheck(0, 255, (int)(GetRValue(textureColor) * redTmp)), BoundsCheck(0, 255, (int)(GetGValue(textureColor) * greenTmp)), BoundsCheck(0, 255, (int)(GetBValue(textureColor) * blueTmp)));

				SetPixel(hDc, xi, tempA.GetYInt(), currentColor);
				ci++;
			}
		}


		while (e1 >= 0)
		{
			if (changed1)
			{
				tempA.SetX((float)tempA.GetXInt() + (float)signx1);
				tempUVA.SetU((float)tempUVA.GetU() + (float)signu1);
			}
			else
			{
				tempA.SetY((float)tempA.GetYInt() + (float)signy1);
				tempUVA.SetV((float)tempUVA.GetV() + (float)signv1);
			}
			e1 = e1 - 2 * dx1;
		}

		if (changed1)
		{
			tempA.SetY((float)tempA.GetYInt() + (float)signy1);
			tempUVA.SetV((float)tempUVA.GetV() + (float)signv1);
		}
		else
		{
			tempA.SetX((float)tempA.GetXInt() + (float)signx1);
			tempUVA.SetU((float)tempUVA.GetU() + (float)signu1);
		}

		e1 = e1 + 2 * dy1;

		while (tempB.GetYInt(true) != tempA.GetYInt(true))
		{
			while (e2 >= 0)
			{
				if (changed2)
				{
					tempB.SetX((float)tempB.GetXInt() + (float)signx2);
					tempUVB.SetU((float)tempUVB.GetU() + (float)signu2);
				}
				else
				{
					tempB.SetY((float)tempB.GetYInt() + (float)signy2);
					tempUVB.SetV((float)tempUVB.GetV() + (float)signv2);
				}
				e2 = e2 - 2 * dx2;
			}

			if (changed2)
			{
				tempB.SetY((float)tempB.GetYInt() + (float)signy2);
				tempUVB.SetV((float)tempUVB.GetV() + (float)signv2);
			}
			else
			{
				tempB.SetX((float)tempB.GetXInt() + (float)signx2);
				tempUVB.SetU((float)tempUVB.GetU() + (float)signu2);
			}
			e2 = e2 + 2 * dy2;
		}
	}
}