CSC3223_Graphics_For_Games_Software_Rasteriser / SoftwareRasteriser / SoftwareRasteriser.cpp
SoftwareRasteriser.cpp
Raw
#include "SoftwareRasteriser.h"
#include <cmath>
#include <math.h>
#define MAX_SIDES 16
const int INSIDE_S = 0;
const int LEFT_S = 1;
const int RIGHT_S = 2;
const int BOTTOM_S = 4;
const int TOP_S = 8;
const int FAR_S = 16;
const int NEAR_S = 32;
/*
While less 'neat' than just doing a 'new', like in the tutorials, it's usually
possible to render a bit quicker to use direct pointers to the drawing area
that the OS gives you. For a bit of a speedup, you can uncomment the define below
to switch to using this method.
For those of you new to the preprocessor, here's a quick explanation:
Preprocessor definitions like #define allow parts of a file to be selectively enabled
or disabled at compile time. This is useful for hiding parts of the codebase on a
per-platform basis: if you have support for linux and windows in your codebase, obviously
the linux platform won't have the windows platform headers available, so compilation will
fail. So instead you can hide away all the platform specific stuff:
#if PLATFORM_WINDOWS
 DoSomeWindowsStuff();
#elif PLATFORM_LINUX
 DoSomeLinuxStuff();
 #else
 #error Unsupported Platform Specified!
 #endif
 As in our usage, it also allows you to selectively compile in some different functionality
 without any 'run time' cost - if it's not enabled by the preprocessor, it won't make it to
 the compiler, so no assembly will be generated.
Also, I've implemented the Resize method for you, in a manner that'll behave itself
no matter which method you use. I kinda forgot to do that, so there was a chance you'd
get exceptions if you resized to a bigger screen area. Sorry about that.
*/
//#define USE_OS_BUFFERS

SoftwareRasteriser::SoftwareRasteriser(uint width, uint height)
    : Window(width, height)
{
  currentTexture = NULL;
  currentDrawBuffer = 0;
  texSampleState = SAMPLE_NEAREST;
  blendSampleState = SAMPLE_BLEND_REPLACE;

#ifndef USE_OS_BUFFERS
  // Hi! In the tutorials, it's mentioned that we need to form our front + back buffer like so:
  for (int i = 0; i < 2; ++i)
  {
    buffers[i] = new Colour[screenWidth * screenHeight];
  }
#else
  // This works, but we can actually save a memcopy by rendering directly into the memory the
  // windowing system gives us, which I've added to the Window class as the 'bufferData' pointers
  for (int i = 0; i < 2; ++i)
  {
    buffers[i] = (Colour *)bufferData[i];
  }
#endif

  depthBuffer = new unsigned short[screenWidth * screenHeight];

  float zScale = (pow(2.0f, 16) - 1) * 0.5f;

  Vector3 halfScreen = Vector3((screenWidth - 1) * 0.5f, (screenHeight - 1) * 0.5f, zScale);

  portMatrix = Matrix4::Translation(halfScreen) * Matrix4::Scale(halfScreen);
}

SoftwareRasteriser::~SoftwareRasteriser(void)
{
#ifndef USE_OS_BUFFERS
  for (int i = 0; i < 2; ++i)
  {
    delete[] buffers[i];
  }
#endif
  delete[] depthBuffer;
}

void SoftwareRasteriser::Resize()
{
  Window::Resize(); 

#ifndef USE_OS_BUFFERS
  for (int i = 0; i < 2; ++i)
  {
    delete[] buffers[i];
    buffers[i] = new Colour[screenWidth * screenHeight];
  }
#else
  for (int i = 0; i < 2; ++i)
  {
    buffers[i] = (Colour *)bufferData[i];
  }
#endif

  delete[] depthBuffer;
  depthBuffer = new unsigned short[screenWidth * screenHeight];

  float zScale = (pow(2.0f, 16) - 1) * 0.5f;

  Vector3 halfScreen = Vector3((screenWidth - 1) * 0.5f, (screenHeight - 1) * 0.5f, zScale);

  portMatrix = Matrix4::Translation(halfScreen) * Matrix4::Scale(halfScreen);
}

Colour *SoftwareRasteriser::GetCurrentBuffer()
{
  return buffers[currentDrawBuffer];
}

void SoftwareRasteriser::ClearBuffers()
{
  Colour *buffer = GetCurrentBuffer();

  unsigned int clearVal = 0xFF000000;
  unsigned int depthVal = ~0;

  for (uint y = 0; y < screenHeight; ++y)
  {
    for (uint x = 0; x < screenWidth; ++x)
    {
      buffer[(y * screenWidth) + x].c = clearVal;
      depthBuffer[(y * screenWidth) + x] = depthVal;
    }
  }
}

void SoftwareRasteriser::SwapBuffers()
{
  PresentBuffer(buffers[currentDrawBuffer]);
  currentDrawBuffer = !currentDrawBuffer;
}

void SoftwareRasteriser::DrawObject(RenderObject *o) {
  currentTexture = o->GetTexure();

  switch (o->GetMesh()->GetType())   {
  case PRIMITIVE_POINTS:
    RasterisePointsMesh(o);
    break;
  case PRIMITIVE_LINES:
    RasteriseLinesMesh(o);
    break;
  case PRIMITIVE_TRIANGLES:
    RasteriseTriMesh(o);
    break;
  case PRIMITIVE_TRIANGLES_STRIP: //
    RasteriseTriMeshStripPrimitives(o);
    break;
  case PRIMITIVE_TRIANGLES_FAN: //
    RasteriseTriMeshFanPrimitives(o);
    break;
  }
}

void SoftwareRasteriser::RasterisePointsMesh(RenderObject *o) {
	Matrix4 mvp = viewProjMatrix * o->GetModelMatrix();
	for (uint i = 0; i < o->GetMesh()->numVertices; i++) {
		Vector4 vertexPos = mvp * o->GetMesh()->vertices[i];
		vertexPos.SelfDivisionByW();
		Vector4 screenPos = portMatrix * vertexPos;
		BlendPixel((uint)screenPos.x, (uint)screenPos.y, o->GetMesh()->colours[i]);
	}
}

void SoftwareRasteriser::RasteriseLinesMesh(RenderObject *o) {
	const int one = 1;
	const int two = 2;
	
	Matrix4 mvp = viewProjMatrix * o->GetModelMatrix();
	for (uint i = 0; i < o->GetMesh()->numVertices; i = i + two) {
		Vector4 v0 = mvp * o->GetMesh()->vertices[i];
		Vector4 v1 = mvp * o->GetMesh()->vertices[i + 1];

		Colour c0 = o->GetMesh()->colours[0];
		Colour c1 = o->GetMesh()->colours[1];

		Vector3 t0 = Vector3(o->GetMesh()->textureCoords[i].x, o->GetMesh()->textureCoords[i].y, 1.0f);
		Vector3 t1 = Vector3(o->GetMesh()->textureCoords[i + 1].x, o->GetMesh()->textureCoords[i + 1].y, 1.0f);

		if (!RasteriseCohenLine(v0, v1, c0, c1, t0, t1)) {
			continue;
		}
		t0.z = 1.0f;
		t1.z = 1.0f;

		t0 /= v0.w;
		t1 /= v1.w;
		v0.SelfDivisionByW();
		v1.SelfDivisionByW();

		RasteriseLine(v0, v1, c0, c1, t0, t1);
	}
}

void SoftwareRasteriser::RasteriseLine(const Vector4 &vertA, const Vector4 &vertB, const Colour &colA,
	const Colour &colB, const Vector3 &texA, const Vector3 &texB)
{
	Vector4 v0 = portMatrix * vertA;
	Vector4 v1 = portMatrix * vertB;
	Vector4 dir = v1 - v0;

	int xDir = (dir.x < 0.0f) ? -1 : 1;
	int yDir = (dir.y < 0.0f) ? -1 : 1;

	int x = (int)v0.x;
	int y = (int)v0.y;

	int* target = NULL;
	int* scan = NULL;
	int targetVal = 0;
	int scanVal = 0;

	float slope = 0.0f;
	int range = 0;

	if (abs(dir.y) > abs(dir.x)) {
		slope = dir.x / dir.y;
		range = (int)abs(dir.y);

		target = &x;
		scan = &y;
		targetVal = xDir;
		scanVal = yDir;
	}
	else {
		slope = dir.y / dir.x;
		range = (int)abs(dir.x);

		target = &y;
		scan = &x;
		targetVal = yDir;
		scanVal = xDir;
	}

	float absSlope = abs(slope);
	float error = 0.0f;

	const float reciprocalRange = 1.0f / range;

	for (int i = 0; i < range; i++) {
		const float t = i * reciprocalRange;
		const float zVal = v1.z * (i * reciprocalRange) + v0.z * (1.0 - (i * reciprocalRange));

		Colour c;
		if (currentTexture == NULL) {
			c = colB * t + colA * (1.0f - t);
		}
		else {
			Vector3 subTex = texA * (i * reciprocalRange) + texB * (1.0f - (i * reciprocalRange));
			subTex.x /= subTex.z;
			subTex.y /= subTex.z;
			c = currentTexture->ColourAtPoint((int)subTex.x, (int)subTex.y, 0);
		}

		if (DepthFunc((int)x, (int)y, zVal)) {
			BlendPixel(x, y, c);
		}
		error += absSlope;

		if (error > 0.5f) {
			error -= 1.0f;
			(*target) += targetVal;
		}
		(*scan) += scanVal;
	}
}

BoundingBox SoftwareRasteriser::CalculateBoxForTri(const Vector4 &a, const Vector4 &b, const Vector4 &c) {
	BoundingBox box;

	box.topLeft.x = a.x;
	box.topLeft.x = min(box.topLeft.x, b.x);
	box.topLeft.x = min(box.topLeft.x, c.x);
	box.topLeft.x = max(box.topLeft.x, 0.0f);

	box.topLeft.y = a.y;
	box.topLeft.y = min(box.topLeft.y, b.y);
	box.topLeft.y = min(box.topLeft.y, c.y);
	box.topLeft.y = max(box.topLeft.y, 0.0f);

	box.bottomRight.x = a.x;
	box.bottomRight.x = max(box.bottomRight.x, b.x);
	box.bottomRight.x = max(box.bottomRight.x, c.x);
	box.bottomRight.x = min(box.bottomRight.x, screenWidth);

	box.bottomRight.y = a.y;
	box.bottomRight.y = max(box.bottomRight.y, b.y);
	box.bottomRight.y = max(box.bottomRight.y, c.y);
	box.bottomRight.y = min(box.bottomRight.y, screenHeight);

	return box;
}

float SoftwareRasteriser::ScreenAreaOfTri(const Vector4 &a, const Vector4 &b, const Vector4 &c) {
	float area = ((a.x * b.y) + (b.x * c.y) + (c.x * a.y)) -
		((b.x * a.y) + (c.x * b.y) + (a.x * c.y));
	return area * 0.5f;
}

void SoftwareRasteriser::RasteriseTriMesh(RenderObject*o) {
	Matrix4 mvp = viewProjMatrix * o->GetModelMatrix();

	for (uint i = 0; i < o->GetMesh()->numVertices; i += 3) {
		Vector4 v0 = mvp * o->GetMesh()->vertices[i];
		Vector4 v1 = mvp * o->GetMesh()->vertices[i + 1];
		Vector4 v2 = mvp * o->GetMesh()->vertices[i + 2];

		Vector3 t0 = Vector3(
			o->GetMesh()->textureCoords[i].x,
			o->GetMesh()->textureCoords[i].y, 1.0f) / v0.w;

		Vector3 t1 = Vector3(
			o->GetMesh()->textureCoords[i + 1].x,
			o->GetMesh()->textureCoords[i + 1].y, 1.0f) / v1.w;

		Vector3 t2 = Vector3(
			o->GetMesh()->textureCoords[i + 2].x,
			o->GetMesh()->textureCoords[i + 2].y, 1.0f) / v2.w;

		v0.SelfDivisionByW();
		v1.SelfDivisionByW();
		v2.SelfDivisionByW();

		RasteriseTri(v0, v1, v2,
			o->GetMesh()->colours[i],
			o->GetMesh()->colours[i + 1],
			o->GetMesh()->colours[i + 2],
			t0, t1, t2
		);
	}
}

//Fill in the Strip primitive
void SoftwareRasteriser::RasteriseTriMeshStripPrimitives(RenderObject *o) {
	Matrix4 mvp = viewProjMatrix * o->GetModelMatrix();
	Mesh *m = o->GetMesh();
	const int one = 1;
	const int two = 2;
	for (uint i = 0; i < o->GetMesh()->numVertices - two; ++i) {
		Vector4 v0 = mvp * m->vertices[i];
		Vector4 v1 = mvp * m->vertices[i + one];
		Vector4 v2 = mvp * m->vertices[i + two];

		RasteriseHodgmanTri(v0, v1, v2, m->colours[i], m->colours[i + one], m->colours[i + two],
			m->textureCoords[i], m->textureCoords[i + one], m->textureCoords[i + two]);
	}
}


//Fill Planet
void SoftwareRasteriser::RasteriseTriMeshFanPrimitives(RenderObject *o) {
	const int one = 1;
	Matrix4 mvp = viewProjMatrix * o->GetModelMatrix();
	Mesh *m = o->GetMesh();
	Vector4 v0 = mvp * m->vertices[0];

	for (uint i = 1; i < o->GetMesh()->numVertices - one; ++i) {
		Vector4 v1 = mvp * m->vertices[i];
		Vector4 v2 = mvp * m->vertices[i + one];

		RasteriseHodgmanTri(v0, v1, v2, m->colours[0], m->colours[i], m->colours[i + one],
			m->textureCoords[0], m->textureCoords[i], m->textureCoords[i + one]);
	}
}

void SoftwareRasteriser::RasteriseTri(const Vector4 &triA, const Vector4 &triB, const Vector4 &triC,
	const Colour &colA, const Colour &colB, const Colour &colC,
	const Vector3 &texA, const Vector3 &texB, const Vector3 &texC)
{
	Vector4 v0 = portMatrix * triA;
	Vector4 v1 = portMatrix * triB;
	Vector4 v2 = portMatrix * triC;

	BoundingBox b = CalculateBoxForTri(v0, v1, v2);
	float triArea = abs(ScreenAreaOfTri(v0, v1, v2));

	const float areaRecip = 1.0f / triArea;

	float subTriArea[3];
	Vector4 screenPos(0, 0, 0, 1);

	for (float y = b.topLeft.y; y < b.bottomRight.y; ++y) {
		for (float x = b.topLeft.x; x < b.bottomRight.x; ++x) {
			screenPos.x = x;
			screenPos.y = y;

			subTriArea[0] = abs(ScreenAreaOfTri(v0, screenPos, v1));
			subTriArea[1] = abs(ScreenAreaOfTri(v1, screenPos, v2));
			subTriArea[2] = abs(ScreenAreaOfTri(v2, screenPos, v0));

			float triSum = subTriArea[0] + subTriArea[1] + subTriArea[2];

			if (triSum > (triArea + 1.0f)) {
				continue;
			}
			if (triSum < 1.0f) {
				continue;
			}

			//ShadePixel((int)x, (int)y, Colour::White);

			const float alpha = subTriArea[1] * areaRecip;
			const float beta = subTriArea[2] * areaRecip;
			const float gamma = subTriArea[0] * areaRecip;

			float zVal = (v0.z * alpha) + (v1.z * beta) + (v2.z * gamma);
			if (!DepthFunc((int)x, (int)y, zVal)) {
				continue;
			}
				Colour c = ((colA * alpha) + (colB * beta) + (colC * gamma));
				BlendPixel((int)x, (int)y, c); //if only

				if (currentTexture) {
					Vector3 subTex = (texA * alpha) + (texB * beta) + (texC * gamma);
					subTex.x /= subTex.z;
					subTex.y /= subTex.z;

					if (texSampleState == SAMPLE_BILINEAR) {
						BlendPixel((int)x, (int)y, currentTexture->BilinearTexSample(subTex));
					}

					else if (texSampleState == SAMPLE_MIPMAP_NEAREST) {
						float xAlpha, xBeta, xGamma, yAlpha, yBeta, yGamma;

						CalculateWeights(v0, v1, v2, screenPos + Vector4(1, 0, 0, 0), xAlpha, xBeta, xGamma);
						CalculateWeights(v0, v1, v2, screenPos + Vector4(0, 1, 0, 0), yAlpha, yBeta, yGamma);

						Vector3 xDerivs = (texA * xAlpha) + (texB * xBeta) + (texC * xGamma);
						Vector3 yDerivs = (texA * yAlpha) + (texB * yBeta) + (texC * yGamma);

						xDerivs.x /= xDerivs.z;
						xDerivs.y /= xDerivs.z;

						yDerivs.x /= yDerivs.z;
						yDerivs.y /= yDerivs.z;

						xDerivs = xDerivs - subTex;
						yDerivs = yDerivs - subTex;

						const float maxU = max(abs(xDerivs.x), abs(yDerivs.y));
						const float maxV = max(abs(xDerivs.y), abs(yDerivs.y));
						const float maxChange = abs(max(maxU, maxV));
						const int lambda = (int)(abs(log(maxChange) / log(2.0)));

						BlendPixel((int)x, (int)y, currentTexture->NearestTexSample(subTex, lambda));
						break;
					}
					else {
						BlendPixel((int)x, (int)y, currentTexture->NearestTexSample(subTex));
						break;
				}
			}
		}
	}
}

inline bool SoftwareRasteriser::DepthFunc(int x, int y, float depthValue) {
	if (y < 0 || x < 0 || y >= screenHeight || x >= screenWidth) {
		return false;
	}
	int index = (y * screenWidth) + x;
	unsigned int castVal = (unsigned int)depthValue;
	if (castVal > depthBuffer[index])
		return false;
	depthBuffer[index] = castVal;
	return true;
}

void SoftwareRasteriser::CalculateWeights(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, const Vector4 &p, float &alpha, float &beta, float &gamma) {
  const float triArea = ScreenAreaOfTri(v0, v1, v2);
  const float areaRecip = 1.0f / triArea;

  float subTriArea[3];
  subTriArea[0] = abs(ScreenAreaOfTri(v0, p, v1));
  subTriArea[1] = abs(ScreenAreaOfTri(v1, p, v2));
  subTriArea[2] = abs(ScreenAreaOfTri(v2, p, v0));

  alpha = subTriArea[1] * areaRecip;
  beta = subTriArea[2] * areaRecip;
  gamma = subTriArea[0] * areaRecip;
}

bool SoftwareRasteriser::RasteriseCohenLine(Vector4 &v0, Vector4 &v1, Colour &colA, Colour &colB, Vector3 &texA, Vector3 &texB) {
  const int max = 6;
  for (int i = 0; i < max; ++i) {
    int index = 1 << i;
    int outsidePixelA = (OutPixel(v0) & index);
    int outsidePixelB = (OutPixel(v1) & index);

	if (outsidePixelA && outsidePixelB) {
		return false;
	}
	if (!outsidePixelA && !outsidePixelB) {
		continue;
	}
    float position = RefineEdges(v0, v1, index);
    if (outsidePixelA) {
      texA = Vector3::Lerp(texA, texB, position);
      colA = Colour::Lerp(colA, colB, position);
	  v0 = Vector4::Lerp(v0, v1, position);
    }
    else {
      texB = Vector3::Lerp(texA, texB, position);
      colB = Colour::Lerp(colA, colB, position);
	  v1 = Vector4::Lerp(v0, v1, position);
    }
  }
  return true;
}

void SoftwareRasteriser::RasteriseHodgmanTri(Vector4 &triA, Vector4 &triB, Vector4 &triC, const Colour &colA, const Colour &colB, const Colour &colC, const Vector2 &texA, const Vector2 &texB, const Vector2 &texC) {
  Vector4 positionIn[MAX_SIDES];
  Colour colourIn[MAX_SIDES];
  Vector3 textureIn[MAX_SIDES];

  Vector4 positionInOut[MAX_SIDES];
  Colour colourOut[MAX_SIDES];
  Vector3 textureOut[MAX_SIDES];
  positionIn[0] = triA;
  positionIn[1] = triB;
  positionIn[2] = triC;

  colourIn[0] = colA;
  colourIn[1] = colB;
  colourIn[2] = colC;

  textureIn[0] = Vector3(texA.x, texA.y, 1);
  textureIn[1] = Vector3(texB.x, texB.y, 1);
  textureIn[2] = Vector3(texC.x, texC.y, 1);

  int inputSize = 3;
  const int max = 6;

  for (int i = 0; i <= max; i++) {
    int index = 1 << i;
    Vector4 previousPosition = positionIn[inputSize - 1];
    Colour previousColour = colourIn[inputSize - 1];
    Vector3 previousTexture = textureIn[inputSize - 1];

    int outputSize = 0;

    for (int j = 0; j < inputSize; ++j) {
      int outsideA = OutPixel(positionIn[j]) & index;
      int outsideB = OutPixel(previousPosition) & index;

      if (outsideA ^ outsideB) {
        float clipRatio = RefineEdges(positionIn[j], previousPosition, index);

		colourOut[outputSize] = Colour::Lerp(colourIn[j], previousColour, clipRatio);
		textureOut[outputSize] = Vector3::Lerp(textureIn[j], previousTexture, clipRatio);
		positionInOut[outputSize] = Vector4::Lerp(positionIn[j], previousPosition, clipRatio);
		outputSize++;
      }

      if (!outsideA) {
		positionInOut[outputSize] = positionIn[j];
		colourOut[outputSize] = colourIn[j];
		textureOut[outputSize] = textureIn[j];
		outputSize++;
      }

	  previousPosition = positionIn[j];
	  previousColour = colourIn[j];
	  previousTexture = textureIn[j];
    }

    for (int j = 0; j < outputSize; ++j) {
	  positionIn[j] = positionInOut[j];
	  colourIn[j] = colourOut[j];
	  textureIn[j] = textureOut[j];
    }

	inputSize = outputSize;
  }

  for (int i = 0; i < inputSize; ++i) {
	  textureIn[i] = Vector3(textureIn[i].x, textureIn[i].y, 1.0f) / positionIn[i].w;
	positionIn[i].SelfDivisionByW();
  }

  for (int i = 2; i < inputSize; ++i) {
    RasteriseTri(positionIn[0], positionIn[i - 1], positionIn[i], colourIn[0], colourIn[i - 1], colourIn[i], textureIn[0],
		textureIn[i - 1], textureIn[i]);
  }
}

float SoftwareRasteriser::RefineEdges(const Vector4 &v0, const Vector4 &v1, int edge) {
  float position = 0.0f;

  if (edge == LEFT_S) {
	  position = (-v0.w - v0.x) / ((v1.x - v0.x) + v1.w - v0.w);
  }
  if (edge == RIGHT_S) {
	  position = (v0.w - v0.x) / ((v1.x - v0.x) + v1.w - v0.w);
  }
  if (edge == BOTTOM_S) {
	  position = (-v0.w - v0.y) / ((v1.y - v0.y) + v1.w - v0.w);
  }
  if (edge == TOP_S) {
	  position = (v0.w - v0.y) / ((v1.y - v0.y) + v1.w - v0.w);
  }
  if (edge == NEAR_S) {
	  position = (-v0.w - v0.z) / ((v1.z - v0.z) + v1.w - v0.w);
  }
  if (edge == FAR_S) {
	  position = (v0.w - v0.z) / ((v1.z - v0.z) + v1.w - v0.w);
  }
  return min(1.0f, position);
}



int SoftwareRasteriser::OutPixel(const Vector4 &input)
{
  int outsidePixel = INSIDE_S;

  if (input.x < -input.w) {
	  outsidePixel |= LEFT_S;
  }
  else if (input.x > input.w) {
	  outsidePixel |= RIGHT_S;
  }
  if (input.y < -input.w) {
	  outsidePixel |= BOTTOM_S;
  }
  else if (input.y > input.w) {
	  outsidePixel |= TOP_S;
  }
  if (input.z < -input.w) {
	  outsidePixel |= NEAR_S;
  }
  else if (input.z > input.w) {
	  outsidePixel |= FAR_S;
  }
  return outsidePixel;
}

void SoftwareRasteriser::RasteriseTriSpans(const Vector4 &triA, const Vector4 &triB, const Vector4 &triC,
                                           const Colour &colA, const Colour &colB, const Colour &colC,
                                           const Vector3 &texA, const Vector3 &texB, const Vector3 &t2texC)
{
  Vector4 v0 = portMatrix * triA;
  Vector4 v1 = portMatrix * triB;
  Vector4 v2 = portMatrix * triC;

  float position0 = abs(v0.y - v1.y);
  float position1 = abs(v1.y - v2.y);
  float position2 = abs(v2.y - v0.y);

  if (position0 >= position1 && position0 >= position2) {
    RasteriseTriEdgeSpans(v0, v1, v2, v0);
    RasteriseTriEdgeSpans(v0, v1, v1, v2);
  }
  else if (position1 >= position0 && position1 >= position2) {
    RasteriseTriEdgeSpans(v1, v2, v2, v0);
    RasteriseTriEdgeSpans(v1, v2, v0, v1);
  }
  else {
    RasteriseTriEdgeSpans(v2, v0, v0, v1);
    RasteriseTriEdgeSpans(v2, v0, v1, v2);
  }
}

void SoftwareRasteriser::RasteriseTriEdgeSpans(const Vector4 &triA, const Vector4 &triB,
                                               const Vector4 &triC, const Vector4 &triD)
{
  Vector4 EdgeX0 = triA;
  Vector4 EdgeX1 = triB;
  Vector4 EdgeY0 = triC;
  Vector4 EdgeY1 = triD;

  if (EdgeX1.y < EdgeX0.y) {
	EdgeX0 = triB;
	EdgeX1 = triA;
  }
  if (EdgeX1.y < EdgeY0.y) {
	  EdgeY0 = triD;
	  EdgeY1 = triC;
  }

  Vector4 longDiffEdge = EdgeX1 - EdgeX0;
  Vector4 shortDiffEdge = EdgeY1 - EdgeY0;

  float longStepEdge = longDiffEdge.x / longDiffEdge.y;
  float shortStepEdge = shortDiffEdge.x / shortDiffEdge.y;

  float startOffEdge = (EdgeY0.y - EdgeX0.y) / longDiffEdge.y;
  Vector4 startEdge = EdgeX0 + (longDiffEdge * startOffEdge);

  float endOffEdge = (startEdge.y - EdgeY0.y) / shortDiffEdge.y;
  Vector4 endEdge = EdgeY0 + (shortDiffEdge * endOffEdge);

  for (float y = EdgeY0.y; y < EdgeY1.y; ++y)
  {
    float minX = min(startEdge.x, endEdge.x);
    float maxX = max(startEdge.x, endEdge.x);

    for (float x = minX; x < maxX; ++x)
      BlendPixel((int)x, (int)y, Colour::White);

    startEdge.x += longStepEdge;
    endEdge.x += shortStepEdge;
  }
}