Files
Graphics-Rasterizer/Rasteriser.cpp
2025-07-04 17:32:39 +01:00

1274 lines
36 KiB
C++

#include "Rasteriser.h"
Rasteriser app;
Rasteriser::~Rasteriser()
{
_lights.~vector();
_sceneModels.~vector();
}
void Rasteriser::DrawString(HDC hDc, int x, int y, LPCTSTR text, int fontSize, COLORREF textColor, COLORREF backgroundColor)
{
HFONT hFont, hOldFont;
// Retrieve a handle to the variable stock font.
hFont = hFont = CreateFont(fontSize, 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;
//Set the current Time for the automation
time(&_startTime);
time(&_currentTime);
_currentState = 0;
_maxStates = 5;
for (int i = 0; i < (sizeof _counters / sizeof _counters[0]); i++)
{
_counters[i] = 0;
}
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);
}
void Rasteriser::Update(const Bitmap& bitmap)
{
if (bitmap.GetWidth() == 0 || bitmap.GetHeight() == 0)
{
_screenMinimized = true;
}
else
{
_screenMinimized = false;
}
int duration = 0;
switch (_currentState)
{
case -1:
duration = -1;
case 0:
duration = 10;
break;
case 1:
duration = 10;
break;
case 2:
duration = 10;
break;
case 3:
duration = 10;
break;
case 4:
duration = 10;
break;
}
if (!_screenMinimized)
{
for (Model& currentModel : _sceneModels)
{
if (currentModel.GetActive())
{
switch (_currentState)
{
case -1:
break;
case 0:
currentModel.SetDrawMethod(DrawMethod::Wireframe);
break;
case 1:
currentModel.SetDrawMethod(DrawMethod::Flat);
break;
case 2:
currentModel.SetDrawMethod(DrawMethod::FlatRaster);
break;
case 3:
currentModel.SetDrawMethod(DrawMethod::Smooth);
break;
case 4:
currentModel.SetDrawMethod(DrawMethod::Textured);
break;
}
//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);*/
}
}
_currentAspectRatio = (float)(bitmap.GetWidth() / (float)bitmap.GetHeight());
_currentPerspectiveMatrix = GetPerspectiveProjectionMatrix(1, _currentAspectRatio);
_currentViewMatrix = GetViewMatrix(1, bitmap.GetWidth(), bitmap.GetHeight());
}
if (((int)_currentTime > (int)_startTime + duration) && (duration != -1))
{
if (_currentState == _maxStates)
{
_currentState = 0;
}
else
{
_currentState++;
}
time(&_startTime);
}
time(&_currentTime);
}
void Rasteriser::Render(const Bitmap& bitmap)
{
bool titleDrawn = false;
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):
if (!titleDrawn)
{
DrawString(bitmap.GetDC(), 10, 10, _T("Wireframe"));
titleDrawn = true;
}
currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
currentModel.DehomogenizeAllVertices();
currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
DrawWireFrame(bitmap.GetDC(), currentModel);
break;
case (DrawMethod::Flat):
if (!titleDrawn)
{
DrawString(bitmap.GetDC(), 10, 10, TEXT("Flat Polygon"));
titleDrawn = true;
}
currentModel.CalculateVertexNormals();
CalculateLighting(currentModel, false);
currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
currentModel.DehomogenizeAllVertices();
currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
DrawSolidFlat(bitmap.GetDC(), currentModel);
break;
case (DrawMethod::FlatRaster):
if (!titleDrawn)
{
DrawString(bitmap.GetDC(), 10, 10, _T("Flat Raster"));
titleDrawn = true;
}
currentModel.CalculateVertexNormals();
CalculateLighting(currentModel, false);
currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
currentModel.DehomogenizeAllVertices();
currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
DrawRasterisedSolidFlat(bitmap.GetDC(), currentModel);
break;
case (DrawMethod::Smooth):
if (!titleDrawn)
{
DrawString(bitmap.GetDC(), 10, 10, _T("Gouraud Shading"));
titleDrawn = true;
}
currentModel.CalculateVertexNormals();
CalculateLighting(currentModel, true);
currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
currentModel.DehomogenizeAllVertices();
currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
DrawGouraud(bitmap.GetDC(), currentModel);
break;
case (DrawMethod::Textured):
if (!titleDrawn)
{
DrawString(bitmap.GetDC(), 10, 10, _T("Textured"));
titleDrawn = true;
}
currentModel.CalculateVertexNormals();
CalculateLighting(currentModel, true);
currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix);
currentModel.DehomogenizeAllVertices();
currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix);
DrawSolidTextured(bitmap.GetDC(), currentModel);
break;
}
currentModel.ClearPendingTransforms();
}
}
}
}
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)
{
model.CalculateUVPerspective();
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)
{
// Check if any of the vertices match and dont draw the triangle if they do, this should stop the infinite loop that can happen with small vertex float numbers
if ((v1.GetXInt() == v2.GetXInt() && v1.GetYInt() == v2.GetYInt()) || (v1.GetXInt() == v3.GetXInt() && v1.GetYInt() == v3.GetYInt()) || (v2.GetXInt() == v3.GetXInt() && v3.GetYInt() == v2.GetYInt()))
{
return;
}
Vertex tempA = Vertex(v1);
Vertex tempB = Vertex(v1);
bool changed1 = false;
bool changed2 = false;
int dx1 = (int)abs(v2.GetXInt() - v1.GetXInt());
int dy1 = (int)abs(v2.GetYInt() - v1.GetYInt());
int dx2 = (int)abs(v3.GetXInt() - v1.GetXInt());
int dy2 = (int)abs(v3.GetYInt() - v1.GetYInt());
int signx1 = (int)sgn(v2.GetXInt() - v1.GetXInt());
int signx2 = (int)sgn(v3.GetXInt() - v1.GetXInt());
int signy1 = (int)sgn(v2.GetYInt() - v1.GetYInt());
int signy2 = (int)sgn(v3.GetYInt() - v1.GetYInt());
if (dy1 > dx1)
{
swap(dx1, dy1);
changed1 = true;
}
if (dy2 > dx2)
{
swap(dx2, dy2);
changed2 = true;
}
int e1 = 2 * dy1 - dx1;
int e2 = 2 * dy2 - dx2;
for (int i = 0; i <= dx1; i++)
{
int leftEndPoint;
int rightEndPoint;
if (tempA.GetXInt() < tempB.GetXInt())
{
leftEndPoint = tempA.GetXInt();
rightEndPoint = tempB.GetXInt();
}
else
{
leftEndPoint = tempB.GetXInt();
rightEndPoint = tempA.GetXInt();
}
for (int xi = leftEndPoint; xi < rightEndPoint; xi++)
{
SetPixel(hDc, xi, tempA.GetYInt(), colorIn);
}
while (e1 >= 0)
{
if (changed1)
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
else
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
e1 = e1 - 2 * dx1;
}
if (changed1)
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
else
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
e1 = e1 + 2 * dy1;
while (tempB.GetYInt(true) != tempA.GetYInt(true))
{
while (e2 >= 0)
{
if (changed2)
{
tempB.SetX((float)(tempB.GetXInt() + signx2));
}
else
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
e2 = e2 - 2 * dx2;
}
if (changed2)
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
else
{
tempB.SetX((float)(tempB.GetXInt() + 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)
{
// Check if any of the vertices match and dont draw the triangle if they do, this should stop the infinite loop that can happen with small vertex float numbers
if ((v1.GetXInt() == v2.GetXInt() && v1.GetYInt() == v2.GetYInt()) || (v1.GetXInt() == v3.GetXInt() && v1.GetYInt() == v3.GetYInt()) || (v2.GetXInt() == v3.GetXInt() && v3.GetYInt() == v2.GetYInt()))
{
return;
}
Vertex tempA = Vertex(v1);
Vertex tempB = Vertex(v1);
bool changed1 = false;
bool changed2 = false;
int dx1 = (int)abs(v2.GetXInt() - v1.GetXInt());
int dy1 = (int)abs(v2.GetYInt() - v1.GetYInt());
int dx2 = (int)abs(v3.GetXInt() - v1.GetXInt());
int dy2 = (int)abs(v3.GetYInt() - v1.GetYInt());
int signx1 = (int)sgn(v2.GetXInt() - v1.GetXInt());
int signx2 = (int)sgn(v3.GetXInt() - v1.GetXInt());
int signy1 = (int)sgn(v2.GetYInt() - v1.GetYInt());
int signy2 = (int)sgn(v3.GetYInt() - v1.GetYInt());
if (dy1 > dx1)
{
swap(dx1, dy1);
changed1 = true;
}
if (dy2 > dx2)
{
swap(dx2, dy2);
changed2 = true;
}
int e1 = 2 * dy1 - dx1;
int e2 = 2 * dy2 - dx2;
for (int i = 0; i <= dx1; i++)
{
int leftEndPoint;
int rightEndPoint;
int sideFix;
if (tempA.GetXInt() < tempB.GetXInt())
{
leftEndPoint = tempA.GetXInt();
rightEndPoint = tempB.GetXInt();
sideFix = 0;
}
else
{
leftEndPoint = tempB.GetXInt();
rightEndPoint = tempA.GetXInt();
sideFix = 1;
}
// 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 = rightEndPoint - leftEndPoint;
int ci = xLength * sideFix;
for (int xi = leftEndPoint; xi < 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);
if (sideFix == 1)
{
ci--;
}
else
{
ci++;
}
}
while (e1 >= 0)
{
if (changed1)
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
else
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
e1 = e1 - 2 * dx1;
}
if (changed1)
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
else
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
e1 = e1 + 2 * dy1;
while (tempB.GetYInt(true) != tempA.GetYInt(true))
{
while (e2 >= 0)
{
if (changed2)
{
tempB.SetX((float)(tempB.GetXInt() + signx2));
}
else
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
e2 = e2 - 2 * dx2;
}
if (changed2)
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
else
{
tempB.SetX((float)(tempB.GetXInt() + 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]);
}
else if (verts[0].GetY() == verts[1].GetY())
{
FillTexturedSideTriangle(hDc, model, verts[2], verts[0], verts[1]);
}
else
{
Vertex temp = verts[1];
temp.SetX(verts[0].GetX() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetX() - verts[0].GetX()));
float tempZr = verts[0].GetZRecip() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetZRecip() - verts[0].GetZRecip());
temp.SetZRecip(tempZr);
float tempU = verts[0].GetUOverZ() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetUOverZ() - verts[0].GetUOverZ());
float tempV = verts[0].GetVOverZ() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetVOverZ() - verts[0].GetVOverZ());
temp.SetUOverZ(tempU);
temp.SetVOverZ(tempV);
// Get the RGB value at the new vertex point
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 (verts[1].GetX() < temp.GetX())
{
FillTexturedSideTriangle(hDc, model, verts[0], verts[1], temp);
FillTexturedSideTriangle(hDc, model, verts[2], verts[1], temp);
}
else
{
FillTexturedSideTriangle(hDc, model, verts[0], temp, verts[1]);
FillTexturedSideTriangle(hDc, model, verts[2], temp, verts[1]);
}
}
}
void Rasteriser::FillTexturedSideTriangle(HDC hDc, Model& model, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
// Check if any of the vertices match and dont draw the triangle if they do, this should stop the infinite loop that can happen with small vertex float numbers
if ((v1.GetXInt() == v2.GetXInt() && v1.GetYInt() == v2.GetYInt()) || (v1.GetXInt() == v3.GetXInt() && v1.GetYInt() == v3.GetYInt()) || (v2.GetXInt() == v3.GetXInt() && v3.GetYInt() == v2.GetYInt()))
{
return;
}
Vertex tempA = Vertex(v1);
Vertex tempB = Vertex(v1);
bool changed1 = false;
bool changed2 = false;
int dx1 = (int)abs(v2.GetXInt() - v1.GetXInt());
int dy1 = (int)abs(v2.GetYInt() - v1.GetYInt());
int dx2 = (int)abs(v3.GetXInt() - v1.GetXInt());
int dy2 = (int)abs(v3.GetYInt() - v1.GetYInt());
int signx1 = (int)sgn(v2.GetXInt() - v1.GetXInt());
int signx2 = (int)sgn(v3.GetXInt() - v1.GetXInt());
int signy1 = (int)sgn(v2.GetYInt() - v1.GetYInt());
int signy2 = (int)sgn(v3.GetYInt() - v1.GetYInt());
if (dy1 > dx1)
{
swap(dx1, dy1);
changed1 = true;
}
if (dy2 > dx2)
{
swap(dx2, dy2);
changed2 = true;
}
int e1 = 2 * dy1 - dx1;
int e2 = 2 * dy2 - dx2;
for (int i = 0; i <= dx1; i++)
{
int leftEndPoint;
int rightEndPoint;
int sideFix;
if (tempA.GetXInt() < tempB.GetXInt())
{
leftEndPoint = tempA.GetXInt();
rightEndPoint = tempB.GetXInt();
sideFix = 0;
}
else
{
leftEndPoint = tempB.GetXInt();
rightEndPoint = tempA.GetXInt();
sideFix = 1;
}
float currentPoint = (float)i / (float)dx1;
float UCoordA = Interpolate(tempA.GetY(), v1.GetUOverZ(), v2.GetUOverZ(), v1.GetY(), v2.GetY());
float UCoordB = Interpolate(tempA.GetY(), v1.GetUOverZ(), v3.GetUOverZ(), v1.GetY(), v3.GetY());
float VCoordA = Interpolate(tempA.GetY(), v1.GetVOverZ(), v2.GetVOverZ(), v1.GetY(), v2.GetY());
float VCoordB = Interpolate(tempA.GetY(), v1.GetVOverZ(), v3.GetVOverZ(), v1.GetY(), v3.GetY());
float ZRecipA = Interpolate(tempA.GetY(), v1.GetZRecip(), v2.GetZRecip(), v1.GetY(), v2.GetY());
float ZRecipB = Interpolate(tempA.GetY(), v1.GetZRecip(), v3.GetZRecip(), v1.GetY(), v3.GetY());
float iRedA = Lerp(v1.GetR(), v2.GetR(), currentPoint);
float iRedB = Lerp(v1.GetR(), v3.GetR(), currentPoint);
float iGreenA = Lerp(v1.GetG(), v2.GetG(), currentPoint);
float iGreenB = Lerp(v1.GetG(), v3.GetG(), currentPoint);
float iBlueA = Lerp(v1.GetB(), v2.GetB(), currentPoint);
float iBlueB = Lerp(v1.GetB(), v3.GetB(), currentPoint);
int xLength = rightEndPoint - leftEndPoint;
int ci = xLength * sideFix;
for (int xi = leftEndPoint; xi < rightEndPoint; xi++)
{
float ti = ((float)ci / (float)xLength);
float UCoordTmp = Lerp(UCoordA, UCoordB, ti);
float VCoordTmp = Lerp(VCoordA, VCoordB, ti);
float ZRecipTmp = Lerp(ZRecipA, ZRecipB, ti);
float redTmp = Lerp(iRedA, iRedB, ti) / 100;
float greenTmp = Lerp(iGreenA, iGreenB, ti) / 100;
float blueTmp = Lerp(iBlueA, iBlueB, ti) / 100;
float uFinal = UCoordTmp / ZRecipTmp;
float vFinal = VCoordTmp / ZRecipTmp;
COLORREF textureColor = model.GetTexture().GetTextureValue((int)uFinal, (int)vFinal);
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);
if (sideFix == 1)
{
ci--;
}
else
{
ci++;
}
}
while (e1 >= 0)
{
if (changed1)
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
else
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
e1 = e1 - 2 * dx1;
}
if (changed1)
{
tempA.SetY((float)(tempA.GetYInt() + signy1));
}
else
{
tempA.SetX((float)(tempA.GetXInt() + signx1));
}
e1 = e1 + 2 * dy1;
while (tempB.GetYInt(true) != tempA.GetYInt(true))
{
while (e2 >= 0)
{
if (changed2)
{
tempB.SetX((float)(tempB.GetXInt() + signx2));
}
else
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
e2 = e2 - 2 * dx2;
}
if (changed2)
{
tempB.SetY((float)(tempB.GetYInt() + signy2));
}
else
{
tempB.SetX((float)(tempB.GetXInt() + signx2));
}
e2 = e2 + 2 * dy2;
}
}
}
void Rasteriser::FillPolygonTexturedA(HDC hDc, Model& model, vector<Vertex>& verts)
{
sort(verts.begin(), verts.end(), VerticiesYCompareAsc);
if (verts[1].GetY() == verts[2].GetY())
{
FillTexturedBottomFlatTriangle(hDc, model, verts[0], verts[1], verts[2]);
}
else if (verts[0].GetY() == verts[1].GetY())
{
FillTexturedTopFlatTriangle(hDc, model, verts[0], verts[1], verts[2]);
}
else
{
Vertex temp = verts[1];
temp.SetX(verts[0].GetX() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetX() - verts[0].GetX()));
float tempZr = verts[1].GetZOriginal() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (verts[2].GetZOriginal() - verts[0].GetZOriginal());
temp.SetZOriginal(tempZr);
float uc1 = model.GetUVCoord(verts[0].GetUVIndex()).GetU();
float vc1 = model.GetUVCoord(verts[0].GetUVIndex()).GetV();
float uc2 = model.GetUVCoord(verts[1].GetUVIndex()).GetU();
float vc2 = model.GetUVCoord(verts[1].GetUVIndex()).GetV();
float uc3 = model.GetUVCoord(verts[2].GetUVIndex()).GetU();
float vc3 = model.GetUVCoord(verts[2].GetUVIndex()).GetV();
float tempU = Interpolate(verts[1].GetY(), uc1, uc3, verts[0].GetY(), verts[2].GetY()); // uc1 + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (uc3 - uc1);
float tempV = Interpolate(verts[1].GetY(), vc1, vc3, verts[0].GetY(), verts[2].GetY()); // vc2 + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (vc3 - vc2);
temp.UVCorrect(tempU, tempV);
//temp.SetUOverZ(tempU);
//temp.SetVOverZ(tempV);
//x0 + (y - y0) * ((x1 - x0) / (y1 - y0));
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));
FillTexturedBottomFlatTriangle(hDc, model, verts[0], verts[1], temp);
FillTexturedTopFlatTriangle(hDc, model, verts[1], temp, verts[2]);
}
}
void Rasteriser::FillTexturedBottomFlatTriangle(HDC hDc, Model& model, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
float slope1 = (v2.GetX() - v1.GetX()) / (v2.GetY() - v1.GetY());
float slope2 = (v3.GetX() - v1.GetX()) / (v3.GetY() - v1.GetY());
float x1 = v1.GetX() - 1.0f;
float x2 = v1.GetX() + 1.0f;
float v2v1Diff = (v2.GetY() - v1.GetY());
float colorSlopeBlue1 = (v2.GetB() - v1.GetB()) / v2v1Diff;
float colorSlopeRed1 = (v2.GetR() - v1.GetR()) / v2v1Diff;
float colorSlopeGreen1 = (v2.GetG() - v1.GetG()) / v2v1Diff;
float uSlope1 = (v2.GetUOverZ() - v1.GetUOverZ()) / v2v1Diff;
float vSlope1 = (v2.GetVOverZ() - v1.GetVOverZ()) / v2v1Diff;
float zrSlope1 = (v2.GetZRecip() - v1.GetZRecip()) / v2v1Diff;
float v3v1Diff = (v3.GetY() - v1.GetY());
float colorSlopeBlue2 = (v3.GetB() - v1.GetB()) / v3v1Diff;
float colorSlopeRed2 = (v3.GetR() - v1.GetR()) / v3v1Diff;
float colorSlopeGreen2 = (v3.GetG() - v1.GetG()) / v3v1Diff;
float uSlope2 = (v3.GetUOverZ() - v1.GetUOverZ()) / v3v1Diff;
float vSlope2 = (v3.GetVOverZ() - v1.GetVOverZ()) / v3v1Diff;
float zrSlope2 = (v3.GetZRecip() - v1.GetZRecip()) / v3v1Diff;
float cBlue1 = (float)v1.GetB();
float cRed1 = (float)v1.GetR();
float cGreen1 = (float)v1.GetG();
float uc1 = v1.GetUOverZ();
float vc1 = v1.GetVOverZ();
float zr1 = v1.GetZRecip();
float cBlue2 = (float)v1.GetB();
float cRed2 = (float)v1.GetR();
float cGreen2 = (float)v1.GetG();
float uc2 = v1.GetUOverZ();
float vc2 = v1.GetVOverZ();
float zr2 = v1.GetZRecip();
if (slope2 < slope1)
{
float slopeTmp = slope1;
slope1 = slope2;
slope2 = slopeTmp;
slopeTmp = colorSlopeRed1;
colorSlopeRed1 = colorSlopeRed2;
colorSlopeRed2 = slopeTmp;
slopeTmp = colorSlopeGreen1;
colorSlopeGreen1 = colorSlopeGreen2;
colorSlopeGreen2 = slopeTmp;
slopeTmp = colorSlopeBlue1;
colorSlopeBlue1 = colorSlopeBlue2;
colorSlopeBlue2 = slopeTmp;
slopeTmp = uSlope1;
uSlope1 = uSlope2;
uSlope2 = slopeTmp;
slopeTmp = vSlope1;
vSlope1 = vSlope2;
vSlope2 = slopeTmp;
slopeTmp = zrSlope1;
zrSlope1 = zrSlope2;
zrSlope2 = slopeTmp;
}
for (float scanlineY = v1.GetY(); scanlineY < v2.GetY() + 0.5f; scanlineY++)
{
for (int xPos = (int)ceil(x1); xPos < (int)x2; xPos++)
{
float t = (xPos - x1) / (x2 - x1);
float redTmp = Lerp(cRed1, cRed2, t) / 100;
float greenTmp = Lerp(cGreen1, cGreen2, t) / 100;
float blueTmp = Lerp(cBlue1, cBlue2, t) / 100;
float UCoordTmp = Lerp(uc1, uc2, t);
float VCoordTmp = Lerp(vc1, vc2, t);
float ZRecipTmp = Lerp(zr1, zr2, t);
float uFinal = UCoordTmp / ZRecipTmp;
float vFinal = VCoordTmp / ZRecipTmp;
COLORREF textureColor = model.GetTexture().GetTextureValue((int)uFinal, (int)vFinal);
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, xPos, (int)scanlineY, currentColor);
}
x1 += slope1;
x2 += slope2;
cRed1 += colorSlopeRed1;
cGreen1 += colorSlopeGreen1;
cBlue1 += colorSlopeBlue1;
uc1 += uSlope1;
vc1 += vSlope1;
zr1 += zrSlope1;
cRed2 += colorSlopeRed2;
cGreen2 += colorSlopeGreen2;
cBlue2 += colorSlopeBlue2;
uc2 += uSlope2;
vc2 += vSlope2;
zr2 += zrSlope2;
}
}
void Rasteriser::FillTexturedTopFlatTriangle(HDC hDc, Model& model, const Vertex& v1, const Vertex& v2, const Vertex& v3)
{
float slope1 = (v3.GetX() - v1.GetX()) / (v3.GetY() - v1.GetY());
float slope2 = (v3.GetX() - v2.GetX()) / (v3.GetY() - v2.GetY());
float x1 = v3.GetX() - 1.0f;
float x2 = v3.GetX() + 1.0f;
float v3v1Diff = (v3.GetY() - v1.GetY());
float colorSlopeBlue1 = (v3.GetB() - v1.GetB()) / v3v1Diff;
float colorSlopeRed1 = (v3.GetR() - v1.GetR()) / v3v1Diff;
float colorSlopeGreen1 = (v3.GetG() - v1.GetG()) / v3v1Diff;
float uSlope1 = (v3.GetUOverZ() - v1.GetUOverZ()) / v3v1Diff;
float vSlope1 = (v3.GetVOverZ() - v1.GetVOverZ()) / v3v1Diff;
float zrSlope1 = (v3.GetZRecip() - v1.GetZRecip()) / v3v1Diff;
float v3v2Diff = (v3.GetY() - v2.GetY());
float colorSlopeBlue2 = (v3.GetB() - v2.GetB()) / v3v2Diff;
float colorSlopeRed2 = (v3.GetR() - v2.GetR()) / v3v2Diff;
float colorSlopeGreen2 = (v3.GetG() - v2.GetG()) / v3v2Diff;
float uSlope2 = (v3.GetUOverZ() - v2.GetUOverZ()) / v3v2Diff;
float vSlope2 = (v3.GetVOverZ() - v2.GetVOverZ()) / v3v2Diff;
float zrSlope2 = (v3.GetZRecip() - v2.GetZRecip()) / v3v2Diff;
float cBlue1 = (float)v3.GetB();
float cRed1 = (float)v3.GetR();
float cGreen1 = (float)v3.GetG();
float uc1 = v3.GetUOverZ();
float vc1 = v3.GetVOverZ();
float zr1 = v3.GetZRecip();
float cBlue2 = (float)v3.GetB();
float cRed2 = (float)v3.GetR();
float cGreen2 = (float)v3.GetG();
float uc2 = v3.GetUOverZ();
float vc2 = v3.GetVOverZ();
float zr2 = v3.GetZRecip();
if (slope1 < slope2)
{
float slopeTmp = slope1;
slope1 = slope2;
slope2 = slopeTmp;
slopeTmp = colorSlopeRed1;
colorSlopeRed1 = colorSlopeRed2;
colorSlopeRed2 = slopeTmp;
slopeTmp = colorSlopeGreen1;
colorSlopeGreen1 = colorSlopeGreen2;
colorSlopeGreen2 = slopeTmp;
slopeTmp = colorSlopeBlue1;
colorSlopeBlue1 = colorSlopeBlue2;
colorSlopeBlue2 = slopeTmp;
slopeTmp = uSlope1;
uSlope1 = uSlope2;
uSlope2 = slopeTmp;
slopeTmp = vSlope1;
vSlope1 = vSlope2;
vSlope2 = slopeTmp;
slopeTmp = zrSlope1;
zrSlope1 = zrSlope2;
zrSlope2 = slopeTmp;
}
for (float scanlineY = v3.GetY(); scanlineY > v1.GetY(); scanlineY--)
{
for (int xPos = (int)ceil(x1); xPos < (int)x2; xPos++)
{
float t = (xPos - x1) / (x2 - x1);
float redTmp = Lerp(cRed1, cRed2, t) / 100;
float greenTmp = Lerp(cGreen1, cGreen2, t) / 100;
float blueTmp = Lerp(cBlue1, cBlue2, t) / 100;
float UCoordTmp = Lerp(uc1, uc2, t);
float VCoordTmp = Lerp(vc1, vc2, t);
float ZRecipTmp = Lerp(zr1, zr2, t);
float uFinal = UCoordTmp / ZRecipTmp;
float vFinal = VCoordTmp / ZRecipTmp;
COLORREF textureColor = model.GetTexture().GetTextureValue((int)uFinal, (int)vFinal);
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, xPos, (int)scanlineY, currentColor);
}
x1 -= slope1;
x2 -= slope2;
cRed1 -= colorSlopeRed1;
cGreen1 -= colorSlopeGreen1;
cBlue1 -= colorSlopeBlue1;
uc1 -= uSlope1;
vc1 -= vSlope1;
zr1 -= zrSlope1;
cRed2 -= colorSlopeRed2;
cGreen2 -= colorSlopeGreen2;
cBlue2 -= colorSlopeBlue2;
uc2 -= uSlope2;
vc2 -= vSlope2;
zr2 -= zrSlope2;
}
}