diff --git a/BaseFramework.vcxproj b/BaseFramework.vcxproj index 88e0270..a7510ed 100644 --- a/BaseFramework.vcxproj +++ b/BaseFramework.vcxproj @@ -158,6 +158,8 @@ + + @@ -177,6 +179,8 @@ + + diff --git a/BaseFramework.vcxproj.filters b/BaseFramework.vcxproj.filters index c0189fb..9108e2c 100644 --- a/BaseFramework.vcxproj.filters +++ b/BaseFramework.vcxproj.filters @@ -60,6 +60,12 @@ Source Files + + Source Files + + + Source Files + @@ -113,6 +119,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Light.cpp b/Light.cpp index 82ff655..a0319e8 100644 --- a/Light.cpp +++ b/Light.cpp @@ -3,11 +3,13 @@ Light::Light() { SetLightIntensity(255, 255, 255); + _active = true; } Light::Light(int red, int green, int blue) { SetLightIntensity(red, green, blue); + _active = true; } Light::Light(const Light& other) @@ -95,6 +97,21 @@ int Light::GetBlueLightIntensity() const return GetLightIntensity(ColRef::Blue); } +bool Light::GetLightActive() const +{ + return _active; +} + +void Light::SetLightActive(bool active) +{ + _active = active; +} + +void Light::ToggleLightActive() +{ + _active = !_active; +} + Light& Light::operator= (const Light& rhs) { if (this != &rhs) @@ -109,4 +126,5 @@ void Light::Copy(const Light& other) _liRed = other.GetRedLightIntensity(); _liGreen = other.GetGreenLightIntensity(); _liBlue = other.GetBlueLightIntensity(); + _active = other.GetLightActive(); } \ No newline at end of file diff --git a/Light.h b/Light.h index 07b39e8..2976491 100644 --- a/Light.h +++ b/Light.h @@ -26,6 +26,10 @@ public: int GetGreenLightIntensity() const; int GetBlueLightIntensity() const; + bool GetLightActive() const; + void SetLightActive(bool active); + void ToggleLightActive(); + virtual COLORREF CalculateLight(const Model& currentModel, const Polygon3D& currentPolygon, COLORREF colorIn) = 0; virtual COLORREF CalculateLight(const Model& currentModel, const Vertex& currentVertex, COLORREF colorIn) = 0; @@ -36,6 +40,8 @@ protected: int _liGreen; int _liBlue; + bool _active; + void Copy(const Light& other); }; diff --git a/MD2Loader.cpp b/MD2Loader.cpp index 3aac28c..34262c0 100644 --- a/MD2Loader.cpp +++ b/MD2Loader.cpp @@ -48,6 +48,13 @@ struct Md2Vertex BYTE lightNormalIndex; // Index to a normal vector for the lighting }; +// Texture co-ordinates +struct Md2TextureCoord +{ + short textureCoord[2]; +}; + + struct Md2Frame { float scale[3]; // Scale values @@ -56,14 +63,139 @@ struct Md2Frame Md2Vertex verts[1]; // First vertex of this frame }; -// ---------------------------------------------- -// LoadModel() - load model from file. -// ---------------------------------------------- +struct PcxHeader +{ + BYTE ID; + BYTE Version; + BYTE Encoding; + BYTE BitsPerPixel; + short XMin; + short YMin; + short XMax; + short YMax; + short HRes; + short VRes; + BYTE ClrMap[16 * 3]; + BYTE Reserved; + BYTE NumPlanes; + short BytesPerLine; + short Pal; + BYTE Filler[58]; +}; -bool MD2Loader::LoadModel(const char* md2Filename, Model& model, AddPolygon addPolygon, AddVertex addVertex) + +MD2Loader::MD2Loader() +{ +} + +MD2Loader::~MD2Loader() +{ +} + +bool LoadPCX(const char* textureFilename, Texture& texture, const Md2Header* md2Header) +{ + std::ifstream file; + + BYTE * paletteIndices = texture.GetPaletteIndices(); + COLORREF * palette = texture.GetPalette(); + + // Try to open file + file.open(textureFilename, std::ios::in | std::ios::binary); + if (file.fail()) + { + return false; + } + // Read PCX header + PcxHeader header; + file.read(reinterpret_cast(&header), sizeof(PcxHeader)); + + // Verify that this is a valid PCX file + + // We only handle those with 256 colour palette + if ((header.Version != 5) || (header.BitsPerPixel != 8) || + (header.Encoding != 1) || (header.NumPlanes != 1) || + (md2Header && (header.BytesPerLine != md2Header->skinWidth))) + { + // This is not valid supported PCX + file.close(); + return false; + } + + // Check dimensions + + int xSize = header.XMax - header.XMin + 1; + int ySize = header.YMax - header.YMin + 1; + int size = xSize * ySize; + + // Check that this matches our MD2 expected texture + // Note. valid size is <= because uses RLE (so potentially smaller) + if (md2Header && (size > (md2Header->skinHeight * md2Header->skinWidth))) + { + // Doesn't match expected MD2 skin size + file.close(); + return false; + } + + // Reading file data + + BYTE processByte, colourByte; + int count = 0; + while (count < size) + { + file.read(reinterpret_cast(&processByte), 1); + + // Run length encoding - test if byte is an RLE byte + if ((processByte & 192) == 192) + { + // Extract number of times repeated byte + processByte &= 63; + file.read(reinterpret_cast(&colourByte), 1); + for (int index = 0; index < processByte; ++index) + { + // repeatedly write colour + paletteIndices[count] = colourByte; + ++count; + } + } + else + { + // Byte is the colour + paletteIndices[count] = processByte; + ++count; + } + } + + bool returnValue = false; + + // read palette data... + file.seekg(-769, std::ios::end); // This offset from end of file + file.read(reinterpret_cast(&processByte), 1); + if (processByte == 12) + { + BYTE rawPalette[768]; + file.read(reinterpret_cast(&rawPalette), 768); + + // Build palette + for (int palIndex = 0; palIndex < 256; ++palIndex) + { + palette[palIndex] = RGB(rawPalette[palIndex * 3], + rawPalette[(palIndex * 3) + 1], + rawPalette[(palIndex * 3) + 2]); + } + returnValue = true; + } + + file.close(); + return returnValue; +} + +// Load model from file. + +bool MD2Loader::LoadModel(const char* md2Filename, const char * textureFilename, Model& model, AddPolygon addPolygon, AddVertex addVertex, AddTextureUV addTextureUV) { ifstream file; Md2Header header; + bool bHasTexture = false; // Try to open MD2 file file.open(md2Filename, ios::in | ios::binary); @@ -87,6 +219,7 @@ bool MD2Loader::LoadModel(const char* md2Filename, Model& model, AddPolygon addP // We are only interested in the first frame BYTE* frameBuffer = new BYTE[header.frameSize]; Md2Frame* frame = reinterpret_cast(frameBuffer); + Md2TextureCoord * textureCoords = new Md2TextureCoord[header.numTexCoords]; // Read polygon data... file.seekg(header.offsetTriangles, ios::beg); @@ -95,17 +228,28 @@ bool MD2Loader::LoadModel(const char* md2Filename, Model& model, AddPolygon addP // Read frame data... file.seekg(header.offsetFrames, ios::beg); file.read(reinterpret_cast(frame), header.frameSize); + + // Read texture coordinate data + file.seekg(header.offsetTexCoords, std::ios::beg); + file.read(reinterpret_cast(textureCoords), sizeof(Md2TextureCoord) * header.numTexCoords); // Close the file file.close(); - //---------------------------------------------------------------------------------------------- + // Attempt to load any texture + if (textureFilename) + { + model.GetTexture().SetTextureSize(header.skinWidth, header.skinHeight); + bHasTexture = LoadPCX(textureFilename, model.GetTexture(), &header); + } // Polygon array initialization for ( int i = 0; i < header.numTriangles; ++i ) { // Call supplied member function to add a new polygon to the list - std::invoke(addPolygon, model, triangles[i].vertexIndex[0], triangles[i].vertexIndex[1], triangles[i].vertexIndex[2]); + std::invoke(addPolygon, model, + triangles[i].vertexIndex[0], triangles[i].vertexIndex[1], triangles[i].vertexIndex[2], + triangles[i].uvIndex[0], triangles[i].uvIndex[1], triangles[i].uvIndex[2]); } // Vertex array initialization @@ -123,7 +267,14 @@ bool MD2Loader::LoadModel(const char* md2Filename, Model& model, AddPolygon addP static_cast((frame->verts[i].v[2] * frame->scale[2]) + frame->translate[2]), static_cast((frame->verts[i].v[1] * frame->scale[1]) + frame->translate[1])); } - + // Texture coordinates initialisation + if (bHasTexture) + { + for (int i = 0; i < header.numTexCoords; i++) + { + std::invoke(addTextureUV, model, textureCoords[i].textureCoord[0], textureCoords[i].textureCoord[1]); + } + } // Free dynamically allocated memory delete [] triangles; // NOTE: this is 'array' delete. Must be sure to use this triangles = 0; @@ -132,5 +283,8 @@ bool MD2Loader::LoadModel(const char* md2Filename, Model& model, AddPolygon addP frameBuffer = 0; frame = 0; + delete[] textureCoords; + textureCoords = 0; + return true; } diff --git a/MD2Loader.h b/MD2Loader.h index 3d9ada9..f469e01 100644 --- a/MD2Loader.h +++ b/MD2Loader.h @@ -1,14 +1,17 @@ #pragma once #include "Model.h" -// Declare typedefs used by the MD2Loader to call the methods to add a vertex and -// add a polygon to the lists +// Declare typedefs used by the MD2Loader to call the methods to add a vertex, +// add a polygon and add a texture UV to the lists typedef void (Model::*AddVertex)(float x, float y, float z); -typedef void (Model::*AddPolygon)(int i0, int i1, int i2); +typedef void (Model::*AddPolygon)(int i0, int i1, int i2, int uvIndex0, int uvIndex1, int uvIndex2); +typedef void (Model::*AddTextureUV)(float u, float v); class MD2Loader { public: - static bool LoadModel(const char* md2Filename, Model& model, AddPolygon addPolygon, AddVertex addVertex); + MD2Loader(); + ~MD2Loader(); + static bool LoadModel(const char* md2Filename, const char * textureFilename, Model& model, AddPolygon addPolygon, AddVertex addVertex, AddTextureUV addTextureUV); }; diff --git a/Matrix.h b/Matrix.h index a14733d..b7ab9d4 100644 --- a/Matrix.h +++ b/Matrix.h @@ -1,5 +1,4 @@ #pragma once - #include "Vertex.h" #include diff --git a/Model.cpp b/Model.cpp index fdb026c..5c0a4b5 100644 --- a/Model.cpp +++ b/Model.cpp @@ -5,10 +5,80 @@ Model::Model() _kdRed = 1.0f; _kdGreen = 1.0f; _kdBlue = 1.0f; + + _active = false; + _drawMethod = DrawMethod::Wireframe; + + _modelMD2Filename = ""; + _modelTextureFilename = ""; +} + + +Model::Model(string MD2Filename, string textureFilename) +{ + _kdRed = 1.0f; + _kdGreen = 1.0f; + _kdBlue = 1.0f; + + _active = false; + _drawMethod = DrawMethod::Wireframe; + + _modelMD2Filename = MD2Filename; + _modelTextureFilename = textureFilename; } Model::~Model() { + _vertices.~vector(); + _transformedVertices.~vector(); + _polygons.~vector(); + _pendingTransforms.~vector(); + _uvCoords.~vector(); +} + +string Model::GetModelFilename() const +{ + return _modelMD2Filename; +} + +const char* Model::CGetModelFilename() const +{ + return _modelMD2Filename.c_str(); +} + +string Model::GetTextureFilename() const +{ + return _modelTextureFilename; +} + +const char* Model::CGetTextureFilename() const +{ + return _modelTextureFilename.c_str(); +} + +void Model::SetActive(bool value) +{ + _active = value; +} + +void Model::ToggleActive() +{ + _active = !_active; +} + +bool Model::GetActive() const +{ + return _active; +} + +void Model::SetDrawMethod(DrawMethod method) +{ + _drawMethod = method; +} + +DrawMethod Model::GetDrawMethod() const +{ + return _drawMethod; } vector& Model::GetPolygons() @@ -21,6 +91,11 @@ vector& Model::GetVertices() return _vertices; } +vector& Model::GetUVCoords() +{ + return _uvCoords; +} + const vector& Model::GetPendingTransforms() { return _pendingTransforms; @@ -36,6 +111,11 @@ size_t Model::GetVerticesCount() return _vertices.size(); } +size_t Model::GetUVCoordCount() +{ + return _uvCoords.size(); +} + size_t Model::GetPendingTransformCount() { return _pendingTransforms.size(); @@ -47,9 +127,14 @@ void Model::AddVertex(float x, float y, float z) _transformedVertices.push_back(Vertex(x, y, z)); } -void Model::AddPolygon(int index0, int index1, int index2) +void Model::AddPolygon(int index0, int index1, int index2, int uvIndex0, int uvIndex1, int uvIndex2) { - _polygons.push_back(Polygon3D(index0, index1, index2)); + _polygons.push_back(Polygon3D(index0, index1, index2, uvIndex0, uvIndex1, uvIndex2)); +} + +void Model::AddTextureUV(float u, float v) +{ + _uvCoords.push_back(UVCoord(u, v)); } void Model::EnqueueTransform(Matrix transform) @@ -62,6 +147,11 @@ void Model::ClearPendingTransforms() _pendingTransforms.clear(); } +Texture& Model::GetTexture() +{ + return _texture; +} + const Polygon3D& Model::GetPolygon(int index) const { return _polygons[index]; @@ -77,6 +167,11 @@ const Vertex& Model::GetVertex(int index) const return _transformedVertices[index]; } +const UVCoord& Model::GetUVCoord(int index) const +{ + return _uvCoords[index]; +} + vector Model::GetPolygonVertexArray(int index) const { vector polygonVerticies = {}; @@ -234,6 +329,7 @@ void Model::CalculateVertexNormals() { _transformedVertices[_polygons[pi].GetIndex(i)].SetNormal(_transformedVertices[_polygons[pi].GetIndex(i)].GetNormal() + _polygons[pi].GetNormal()); _transformedVertices[_polygons[pi].GetIndex(i)].IncrementContributeCount(); + _transformedVertices[_polygons[pi].GetIndex(i)].SetUVIndex(_polygons[pi].GetUVIndex(i)); } } diff --git a/Model.h b/Model.h index 1742d03..a959115 100644 --- a/Model.h +++ b/Model.h @@ -4,33 +4,56 @@ #include "Polygon3D.h" #include "Matrix.h" #include "Camera.h" +#include "Texture.h" +#include "UVCoord.h" #include #include +#include using namespace std; +enum class DrawMethod { Wireframe, Flat, FlatRaster, Smooth, Textured }; + class Model { public: Model(); + Model(string MD2Filename, string textureFilename); ~Model(); + string GetModelFilename() const; + const char* CGetModelFilename() const; + string GetTextureFilename() const; + const char* CGetTextureFilename() const; + + void SetActive(bool value); + void ToggleActive(); + bool GetActive() const; + + void SetDrawMethod(DrawMethod method); + DrawMethod GetDrawMethod() const; + vector& GetPolygons(void); vector& GetVertices(void); + vector& GetUVCoords(void); const vector& GetPendingTransforms(void); size_t GetPolygonCount(void); size_t GetVerticesCount(void); + size_t GetUVCoordCount(void); size_t GetPendingTransformCount(void); void AddVertex(float x, float y, float z); - void AddPolygon(int index0, int index1, int index2); + void AddPolygon(int index0, int index1, int index2, int uvIndex0, int uvIndex1, int uvIndex2); + void AddTextureUV(float u, float v); void EnqueueTransform(Matrix transform); void ClearPendingTransforms(); + Texture& GetTexture(); const Polygon3D& GetPolygon(int index) const; const Vertex& GetVertex(int polygonIndex, int vertexPolygonIndex) const; - const Vertex& GetVertex(int index) const; + const Vertex& GetVertex(int index) const; + const UVCoord& GetUVCoord(int index) const; vector GetPolygonVertexArray(int index) const; void SetPolygonColor(int index, COLORREF color); @@ -60,9 +83,19 @@ private: vector _vertices; vector _transformedVertices; vector _pendingTransforms; + + vector _uvCoords; + Texture _texture; + float _kdRed; float _kdGreen; float _kdBlue; + + bool _active; + DrawMethod _drawMethod; + + string _modelMD2Filename; + string _modelTextureFilename; }; diff --git a/Polygon3D.cpp b/Polygon3D.cpp index 1b37b9f..04add6a 100644 --- a/Polygon3D.cpp +++ b/Polygon3D.cpp @@ -1,19 +1,30 @@ #include "Polygon3D.h" -Polygon3D::Polygon3D() : _indices{ 0 } +Polygon3D::Polygon3D() : _indices{ 0 }, _uvIndices { 0 } { _depth = 0.0f; - _color = RGB(0, 0, 0); + _r = 0; + _g = 0; + _b = 0; _culled = false; } -Polygon3D::Polygon3D(int index0, int index1, int index2) +Polygon3D::Polygon3D(int index0, int index1, int index2, int uvIndex0, int uvIndex1, int uvIndex2) { _indices[0] = index0; _indices[1] = index1; _indices[2] = index2; + + _uvIndices[0] = uvIndex0; + _uvIndices[1] = uvIndex1; + _uvIndices[2] = uvIndex2; + _depth = 0.0f; - _color = RGB(0, 0, 0); + + _r = 0; + _g = 0; + _b = 0; + _culled = false; } @@ -24,6 +35,7 @@ Polygon3D::Polygon3D(const Polygon3D& other) Polygon3D::~Polygon3D() { + } size_t Polygon3D::GetPolygonVertexCount() const @@ -31,11 +43,21 @@ size_t Polygon3D::GetPolygonVertexCount() const return sizeof(_indices) / sizeof(_indices[0]); } +size_t Polygon3D::GetPolygonUVCount() const +{ + return sizeof(_uvIndices) / sizeof(_uvIndices[0]); +} + int Polygon3D::GetIndex(const int index) const { return _indices[index]; } +int Polygon3D::GetUVIndex(const int index) const +{ + return _uvIndices[index]; +} + void Polygon3D::SetNormal(const Vector3D& normal) { _normal = normal; @@ -52,22 +74,52 @@ void Polygon3D::NormalizeNormal() } void Polygon3D::SetColor(int red, int green, int blue) -{ - int redChecked = BoundsCheck(0, 255, red); - int greenChecked = BoundsCheck(0, 255, green); - int blueChecked = BoundsCheck(0, 255, blue); - - _color = RGB(redChecked, greenChecked, blueChecked); +{ + _r = red; + _g = green; + _b = blue; } void Polygon3D::SetColor(const COLORREF color) { - _color = color; + _r = GetRValue(color); + _g = GetGValue(color); + _b = GetBValue(color); +} + +int Polygon3D::GetR() const +{ + return _r; +} + +void Polygon3D::SetR(const int r) +{ + _r = r; +} + +int Polygon3D::GetG() const +{ + return _g; +} + +void Polygon3D::SetG(const int g) +{ + _g = g; +} + +int Polygon3D::GetB() const +{ + return _b; +} + +void Polygon3D::SetB(const int b) +{ + _b = b; } COLORREF Polygon3D::GetColor() const { - return _color; + return RGB(_r, _g, _b); } void Polygon3D::SetDepth(float depth) @@ -105,8 +157,17 @@ void Polygon3D::Copy(const Polygon3D& other) { _indices[i] = other.GetIndex(i); } + + for (int i = 0; i < sizeof(_uvIndices) / sizeof(_uvIndices[0]); i++) + { + _uvIndices[i] = other.GetUVIndex(i); + } + _normal = other.GetNormal(); _culled = other.GetCulled(); _depth = other.GetDepth(); - _color = other.GetColor(); + + _r = other.GetR(); + _g = other.GetG(); + _b = other.GetB(); } \ No newline at end of file diff --git a/Polygon3D.h b/Polygon3D.h index 5307a87..b352e02 100644 --- a/Polygon3D.h +++ b/Polygon3D.h @@ -1,29 +1,36 @@ #pragma once #include "Vector3D.h" -#include "SharedTools.h" #include "windows.h" -using namespace SharedTools; - class Polygon3D { public: Polygon3D(); - Polygon3D(int index0, int index1, int index2); + Polygon3D(int index0, int index1, int index2, int uvIndex0, int uvIndex1, int uvIndex2); Polygon3D(const Polygon3D& other); ~Polygon3D(); size_t GetPolygonVertexCount() const; + size_t GetPolygonUVCount() const; int GetIndex(int index) const; + int GetUVIndex(int index) const; void SetNormal(const Vector3D& normal); const Vector3D& GetNormal() const; void NormalizeNormal(); - + void SetColor(int red, int green, int blue); void SetColor(const COLORREF color); + + int GetR() const; + void SetR(const int r); + int GetG() const; + void SetG(const int g); + int GetB() const; + void SetB(const int b); + COLORREF GetColor() const; void SetDepth(float depth); @@ -35,10 +42,15 @@ public: private: int _indices[3]; + int _uvIndices[3]; + Vector3D _normal; float _depth; bool _culled; - COLORREF _color; + + int _r; + int _g; + int _b; void Copy(const Polygon3D& other); }; diff --git a/Rasteriser.cpp b/Rasteriser.cpp index 39ba2d8..5e4bd32 100644 --- a/Rasteriser.cpp +++ b/Rasteriser.cpp @@ -2,23 +2,60 @@ 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; - if (MD2Loader::LoadModel(".\\marvin.MD2", modelA, &Model::AddPolygon, &Model::AddVertex)) + 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) { - _sceneModels.push_back(modelA); - } - else - { - return false; - } - + 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(150, 150, 150)); - //_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(-10, 0, 10), 0, 1, 0, 100, 0, 0)); + //_lights.push_back(new AmbientLight(50, 50, 50)); + _lights.push_back(new AmbientLight(255, 255, 255)); + //_lights.push_back(new DirectionalLight(Vector3D(-1, 0, 0), 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; @@ -58,7 +95,10 @@ void Rasteriser::CalculateLighting(Model& currentModel, bool fullLightRender) COLORREF colorWorking = RGB(0, 0, 0); for (Light* currentLight : _lights) { - colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetVertex(pi, i), colorWorking); + if (currentLight->GetLightActive()) + { + colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetVertex(pi, i), colorWorking); + } } currentModel.SetVertexColor(pi, i, colorWorking); } @@ -68,7 +108,10 @@ void Rasteriser::CalculateLighting(Model& currentModel, bool fullLightRender) COLORREF colorWorking = RGB(0, 0, 0); for (Light* currentLight : _lights) { - colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetPolygon(pi), colorWorking); + if (currentLight->GetLightActive()) + { + colorWorking = currentLight->CalculateLight(currentModel, currentModel.GetPolygon(pi), colorWorking); + } } currentModel.SetPolygonColor(pi, colorWorking); } @@ -77,6 +120,26 @@ void Rasteriser::CalculateLighting(Model& currentModel, bool fullLightRender) } } +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 a + (b - a) * t; +} + +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) @@ -97,19 +160,22 @@ void Rasteriser::Update(const Bitmap& bitmap) for (Model& currentModel : _sceneModels) { - currentModel.EnqueueTransform(GetTranslateMatrix(startPos)); - currentModel.EnqueueTransform(GetRotationMatrix(Axis::Y, (float)currentRot)); - //startPos.SetX(startPos.GetX() + 100); - if ((currentModelIndex % 2) == 1) + if (currentModel.GetActive()) { - currentZOff = 0; + 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++; } - else - { - currentZOff = 100; - } - //startPos.SetZ((float)currentZOff); - currentModelIndex++; } _currentAspectRatio = (float)(bitmap.GetWidth() / (float)bitmap.GetHeight()); @@ -137,31 +203,70 @@ void Rasteriser::Render(const Bitmap& bitmap) for (Model& currentModel : _sceneModels) { - currentModel.SetReflectionCoefficient(0.5f, 0.5f, 0.5f); - Matrix workingMatrix = workingMatrix.IdentityMatrix(); - for (Matrix currentTransform : currentModel.GetPendingTransforms()) + if (currentModel.GetActive()) { - workingMatrix *= currentTransform; + 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(); } - currentModel.ApplyTransformToLocalVertices(workingMatrix); - currentModel.ApplyTransformToTransformedVertices(GetCurrentCamera().GetCurrentCameraTransformMatrix()); - - currentModel.CalculateBackfaces(GetCurrentCamera()); - currentModel.Sort(); - - currentModel.CalculateVertexNormals(); - CalculateLighting(currentModel, true); - - currentModel.ApplyTransformToTransformedVertices(_currentPerspectiveMatrix); - currentModel.DehomogenizeAllVertices(); - currentModel.ApplyTransformToTransformedVertices(_currentViewMatrix); - - //DrawWireFrame(bitmap.GetDC(), currentModel); - //DrawSolidFlat(bitmap.GetDC(), currentModel); - //DrawRasterisedSolidFlat(bitmap.GetDC(), currentModel); - DrawGouraud(bitmap.GetDC(), currentModel); - currentModel.ClearPendingTransforms(); } + + DrawString(bitmap.GetDC(), 10, 10, _T("Hello World")); } } @@ -179,8 +284,8 @@ void Rasteriser::DrawSquare(HDC hDc, const vector verticies) pointArray[i].y = (long) verticies[i].GetY(); } - SetDCBrushColor(hDc, RGB(255, 0, 255)); - SetDCPenColor(hDc, RGB(0, 0, 255)); + SetDCBrushColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256)); + SetDCPenColor(hDc, RGB(rand() % 256, rand() % 256, rand() % 256)); Polygon(hDc, pointArray, 4); } @@ -225,6 +330,8 @@ void Rasteriser::DrawWireFrame(HDC hDc, Model& model) } } + SetDCBrushColor(hDc, RGB(1,1,1)); + SetDCPenColor(hDc, RGB(255,255,255)); PolyPolygon(hDc, pointArray.data(), sizeArray.data(), unculledPolyCount); } @@ -280,6 +387,20 @@ void Rasteriser::DrawGouraud(HDC hDc, Model& model) } } +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 vertexArray = model.GetPolygonVertexArray(i); + FillPolygonTextured(hDc, model, vertexArray); + } + } +} + bool VerticiesYCompareAsc(Vertex& v1, Vertex& v2) { return v1.GetY() < v2.GetY(); @@ -326,11 +447,11 @@ void Rasteriser::FillFlatSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v int dx2 = (int)ceil(abs(v3.GetX() - v1.GetX())); int dy2 = (int)ceil(abs(v3.GetY() - v1.GetY())); - int signx1 = (int)sgn(v2.GetX() - v1.GetX()); - int signx2 = (int)sgn(v3.GetX() - v1.GetX()); + int signx1 = (int)ceil(sgn(v2.GetX() - v1.GetX())); + int signx2 = (int)ceil(sgn(v3.GetX() - v1.GetX())); - int signy1 = (int)sgn(v2.GetY() - v1.GetY()); - int signy2 = (int)sgn(v3.GetY() - v1.GetY()); + int signy1 = (int)ceil(sgn(v2.GetY() - v1.GetY())); + int signy2 = (int)ceil(sgn(v3.GetY() - v1.GetY())); if (dy1 > dx1) { @@ -353,72 +474,73 @@ void Rasteriser::FillFlatSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v for (int i = 0; i <= dx1; i++) { - int startPoint; - int endPoint; + float leftEndPoint; + float rightEndPoint; - if (tempA.GetXInt() < tempB.GetXInt()) + if (tempA.GetX() < tempB.GetX()) { - startPoint = tempA.GetXInt(); - endPoint = tempB.GetXInt(); + leftEndPoint = tempA.GetX() - 1.0f; + rightEndPoint = tempB.GetX() + 1.0f; } else { - startPoint = tempB.GetXInt(); - endPoint = tempA.GetXInt(); + leftEndPoint = tempB.GetX() - 1.0f; + rightEndPoint = tempA.GetX() + 1.0f; } - for (int xi = (int)ceil(startPoint); xi <= endPoint; xi++) + for (int xi = (int)ceil(leftEndPoint); xi <= (int)ceil(rightEndPoint); xi++) { SetPixel(hDc, xi, tempA.GetYInt(), colorIn); } + while (e1 >= 0) { if (changed1) { - tempA.SetX(tempA.GetX() + signx1); + tempA.SetX((float)tempA.GetX() + (float)signx1); } else { - tempA.SetY(tempA.GetY() + signy1); + tempA.SetY((float)tempA.GetY() + (float)signy1); } e1 = e1 - 2 * dx1; } if (changed1) { - tempA.SetY(tempA.GetY() + signy1); + tempA.SetY((float)tempA.GetY() + (float)signy1); } else { - tempA.SetX(tempA.GetX() + signx1); + tempA.SetX((float)tempA.GetX() + (float)signx1); } e1 = e1 + 2 * dy1; - while (tempB.GetY() != tempA.GetY()) + while (tempB.GetYInt() != tempA.GetYInt()) { while (e2 >= 0) { if (changed2) { - tempB.SetX(tempB.GetX() + signx2); + tempB.SetX((float)tempB.GetXInt() + (float)signx2); } else { - tempB.SetY(tempB.GetY() + signy2); + tempB.SetY((float)tempB.GetYInt() + (float)signy2); } e2 = e2 - 2 * dx2; } if (changed2) { - tempB.SetY(tempB.GetY() + signy2); + tempB.SetY((float)tempB.GetYInt() + (float)signy2); } else { - tempB.SetX(tempB.GetX() + signx2); + tempB.SetX((float)tempB.GetXInt() + (float)signx2); } e2 = e2 + 2 * dy2; } @@ -426,112 +548,6 @@ void Rasteriser::FillFlatSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v } void Rasteriser::FillPolygonGouraud(HDC hDc, vector& verts) -{ - sort(verts.begin(), verts.end(), VerticiesYCompareAsc); - if (verts[1].GetY() == verts[2].GetY()) - { - FillGouraudBottomFlatTriangle(hDc, verts[0], verts[1], verts[2]); - } - else if (verts[0].GetY() == verts[1].GetY()) - { - FillGouraudTopFlatTriangle(hDc, verts[0], verts[1], verts[2]); - } - 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 cRed = GetRValue(verts[0].GetColor()) + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (GetRValue(verts[2].GetColor()) - GetRValue(verts[0].GetColor())); - float cGreen = GetGValue(verts[0].GetColor()) + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (GetGValue(verts[2].GetColor()) - GetGValue(verts[0].GetColor())); - float cBlue = GetBValue(verts[0].GetColor()) + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (GetBValue(verts[2].GetColor()) - GetBValue(verts[0].GetColor())); - temp.SetColor(RGB((int)cRed, (int)cGreen, (int)cBlue)); - - temp.SetColor(verts[1].GetColor()); - - FillGouraudBottomFlatTriangle(hDc, verts[0], verts[1], temp); - FillGouraudTopFlatTriangle(hDc, verts[1], temp, verts[2]); - } -} - -void Rasteriser::FillGouraudBottomFlatTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3) -{ - float slope1 = (float)(v2.GetX() - v1.GetX()) / (float)(v2.GetY() - v1.GetY()); - float slope2 = (float)(v3.GetX() - v1.GetX()) / (float)(v3.GetY() - v1.GetY()); - - float x1 = (float)v1.GetX(); - float x2 = (float)v1.GetX() + 0.5f; - - if (slope2 < slope1) - { - float slopeTmp = slope1; - slope1 = slope2; - slope2 = slopeTmp; - } - - for (int scanlineY = (int)v1.GetY(); scanlineY <= (int)v2.GetY(); scanlineY++) - { - float iRedA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetR() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetR(); - float iRedB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetR() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetR(); - float iGreenA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetG() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetG(); - float iGreenB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetG() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetG(); - float iBlueA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetB() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetB(); - float iBlueB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetB() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetB(); - - for (int xi = (int)ceil(x1); xi < (int)x2; xi++) - { - float redTmp = (x2 - xi) / (x2 - x1) * iRedA + (xi - x1) / (x2 - x1) * iRedB; - float greenTmp = (x2 - xi) / (x2 - x1) * iGreenA + (xi - x1) / (x2 - x1) * iGreenB; - float blueTmp = (x2 - xi) / (x2 - x1) * iBlueA + (xi - x1) / (x2 - x1) * iBlueB; - - COLORREF currentColor = RGB(BoundsCheck(0, 255, (int)redTmp), BoundsCheck(0, 255, (int)greenTmp), BoundsCheck(0, 255, (int)blueTmp)); - SetPixel(hDc, xi, scanlineY, currentColor); - } - - x1 += slope1; - x2 += slope2; - } -} - -void Rasteriser::FillGouraudTopFlatTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3) -{ - float slope1 = (float)(v3.GetX() - v1.GetX()) / (float)(v3.GetY() - v1.GetY()); - float slope2 = (float)(v3.GetX() - v2.GetX()) / (float)(v3.GetY() - v2.GetY()); - - float x1 = (float)v3.GetX(); - float x2 = (float)v3.GetX() + 0.5f; - - if (slope1 < slope2) - { - float slopeTmp = slope1; - slope1 = slope2; - slope2 = slopeTmp; - } - - for (int scanlineY = (int)v3.GetY(); scanlineY > (int)v1.GetY(); scanlineY--) - { - float iRedA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetR() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetR(); - float iRedB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetR() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetR(); - float iGreenA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetG() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetG(); - float iGreenB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetG() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetG(); - float iBlueA = (scanlineY - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetB() + (v1.GetY() - scanlineY) / (v1.GetY() - v2.GetY()) * v2.GetB(); - float iBlueB = (scanlineY - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetB() + (v1.GetY() - scanlineY) / (v1.GetY() - v3.GetY()) * v3.GetB(); - - for (int xi = (int)ceil(x1); xi < (int)x2; xi++) - { - float redTmp = (x2 - xi) / (x2 - x1) * iRedA - (xi - x1) / (x2 - x1) * iRedB; - float greenTmp = (x2 - xi) / (x2 - x1) * iGreenA - (xi - x1) / (x2 - x1) * iGreenB; - float blueTmp = (x2 - xi) / (x2 - x1) * iBlueA - (xi - x1) / (x2 - x1) * iBlueB; - - COLORREF currentColor = RGB(BoundsCheck(0, 255, (int)redTmp), BoundsCheck(0, 255, (int)greenTmp), BoundsCheck(0, 255, (int)blueTmp)); - SetPixel(hDc, xi, scanlineY, currentColor); - } - - x1 -= slope1; - x2 -= slope2; - } -} - -void Rasteriser::FillPolygonGouraudInt(HDC hDc, vector& verts) { sort(verts.begin(), verts.end(), VerticiesYCompareAsc); if (verts[1].GetY() == verts[2].GetY()) @@ -547,12 +563,13 @@ void Rasteriser::FillPolygonGouraudInt(HDC hDc, vector& verts) 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 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()); + 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()) + if (verts[1].GetX() <= temp.GetX()) { FillGouraudSideTriangle(hDc, verts[0], verts[1], temp); FillGouraudSideTriangle(hDc, verts[2], verts[1], temp); @@ -611,30 +628,35 @@ void Rasteriser::FillGouraudSideTriangle(HDC hDc, const Vertex& v1, const Vertex if (tempA.GetX() < tempB.GetX()) { - leftEndPoint = tempA.GetX(); - rightEndPoint = tempB.GetX(); + leftEndPoint = tempA.GetX() - 1.0f; + rightEndPoint = tempB.GetX() + 1.0f; } else { - leftEndPoint = tempB.GetX(); - rightEndPoint = tempA.GetX(); + leftEndPoint = tempB.GetX() - 1.0f; + rightEndPoint = tempA.GetX() + 1.0f; } - float iRedA = (tempA.GetY() - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetR() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v2.GetY()) * v2.GetR(); - float iRedB = (tempA.GetY() - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetR() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v3.GetY()) * v3.GetR(); - float iGreenA = (tempA.GetY() - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetG() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v2.GetY()) * v2.GetG(); - float iGreenB = (tempA.GetY() - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetG() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v3.GetY()) * v3.GetG(); - float iBlueA = (tempA.GetY() - v2.GetY()) / (v1.GetY() - v2.GetY()) * v1.GetB() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v2.GetY()) * v2.GetB(); - float iBlueB = (tempA.GetY() - v3.GetY()) / (v1.GetY() - v3.GetY()) * v1.GetB() + (v1.GetY() - tempA.GetY()) / (v1.GetY() - v3.GetY()) * v3.GetB(); + 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); - for (int xi = (int)ceil(leftEndPoint); xi <= (int)rightEndPoint; xi++) - { - float redTmp = (rightEndPoint - xi) / (rightEndPoint - leftEndPoint) * iRedA + (xi - leftEndPoint) / (rightEndPoint - leftEndPoint) * iRedB; - float greenTmp = (rightEndPoint - xi) / (rightEndPoint - leftEndPoint) * iGreenA + (xi - leftEndPoint) / (rightEndPoint - leftEndPoint) * iGreenB; - float blueTmp = (rightEndPoint - xi) / (rightEndPoint - leftEndPoint) * iBlueA + (xi - leftEndPoint) / (rightEndPoint - leftEndPoint) * iBlueB; + int xLength = (int)ceil(rightEndPoint) - (int)ceil(leftEndPoint); + int ci = 0; + + for (int xi = (int)ceil(leftEndPoint); xi <= (int)ceil(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++; } @@ -642,27 +664,27 @@ void Rasteriser::FillGouraudSideTriangle(HDC hDc, const Vertex& v1, const Vertex { if (changed1) { - tempA.SetX((float)tempA.GetXInt() + (float)signx1); + tempA.SetX((float)tempA.GetX() + (float)signx1); } else { - tempA.SetY((float)tempA.GetYInt() + (float)signy1); + tempA.SetY((float)tempA.GetY() + (float)signy1); } e1 = e1 - 2 * dx1; } if (changed1) { - tempA.SetY((float)tempA.GetYInt() + (float)signy1); + tempA.SetY((float)tempA.GetY() + (float)signy1); } else { - tempA.SetX((float)tempA.GetXInt() + (float)signx1); + tempA.SetX((float)tempA.GetX() + (float)signx1); } e1 = e1 + 2 * dy1; - while (tempB.GetY() < tempA.GetY() && tempB.GetY() > tempA.GetY()) + while (tempB.GetYInt() != tempA.GetYInt()) { while (e2 >= 0) { @@ -689,3 +711,224 @@ void Rasteriser::FillGouraudSideTriangle(HDC hDc, const Vertex& v1, const Vertex } } } + +void Rasteriser::FillPolygonTextured(HDC hDc, Model& model, vector& 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()); + + float tempU = model.GetUVCoord(verts[0].GetUVIndex()).GetU() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (model.GetUVCoord(verts[2].GetUVIndex()).GetU() - model.GetUVCoord(verts[0].GetUVIndex()).GetU()); + float tempV = model.GetUVCoord(verts[0].GetUVIndex()).GetV() + ((verts[1].GetY() - verts[0].GetY()) / (verts[2].GetY() - verts[0].GetY())) * (model.GetUVCoord(verts[2].GetUVIndex()).GetV() - model.GetUVCoord(verts[0].GetUVIndex()).GetV()); + + 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 (verts[1].GetX() <= temp.GetX()) + { + temp.SetUVIndex(verts[2].GetUVIndex()); + FillTexturedSideTriangle(hDc, model, verts[0], verts[1], temp, model.GetUVCoord(verts[0].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()), tempCoords); + temp.SetUVIndex(verts[0].GetUVIndex()); + FillTexturedSideTriangle(hDc, model, verts[2], verts[1], temp, model.GetUVCoord(verts[2].GetUVIndex()), model.GetUVCoord(verts[1].GetUVIndex()), tempCoords); + } + else + { + temp.SetUVIndex(verts[2].GetUVIndex()); + FillTexturedSideTriangle(hDc, model, verts[0], temp, verts[1], model.GetUVCoord(verts[0].GetUVIndex()), tempCoords, model.GetUVCoord(verts[1].GetUVIndex())); + temp.SetUVIndex(verts[0].GetUVIndex()); + FillTexturedSideTriangle(hDc, model, verts[2], temp, verts[1], model.GetUVCoord(verts[2].GetUVIndex()), tempCoords, model.GetUVCoord(verts[1].GetUVIndex())); + } + } +} + +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; + + int tempDu = du1; + du1 = dv1; + dv1 = tempDu; + + changed1 = true; + } + + if (dy2 > dx2) + { + int tempDx = dx2; + dx2 = dy2; + dy2 = tempDx; + + int tempDu = du2; + du2 = dv2; + dv2 = tempDu; + + 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 leftUV; + float rightUV; + + if (tempA.GetX() < tempB.GetX()) + { + leftEndPoint = tempA.GetX() - 1.0f; + rightEndPoint = tempB.GetX() + 1.0f; + leftUV = tempUVA.GetU(); + rightUV = tempUVB.GetU(); + } + else + { + leftEndPoint = tempB.GetX() - 1.0f; + rightEndPoint = tempA.GetX() + 1.0f; + leftUV = tempUVB.GetU(); + rightUV = tempUVA.GetU(); + } + + + float UCoordA = Interpolate(uv1.GetV() + i, uv1.GetU(), uv2.GetU(), uv1.GetV(), uv2.GetV()); + float UCoordB = Interpolate(uv1.GetV() + i, uv1.GetU(), uv3.GetU(), uv1.GetV(), uv3.GetV()); + + 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)ceil(leftEndPoint); xi <= (int)ceil(rightEndPoint); xi++) + { + float ti = (float)ci / (float)xLength; + float UCoordTmp = Lerp(leftUV, rightUV, 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)tempUVA.GetV()); + 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; + } + } +} \ No newline at end of file diff --git a/Rasteriser.h b/Rasteriser.h index 7ea264b..c948ce2 100644 --- a/Rasteriser.h +++ b/Rasteriser.h @@ -1,5 +1,7 @@ #pragma once #include +#include +#include #include "Framework.h" #include "Vector3D.h" #include "Vertex.h" @@ -23,13 +25,23 @@ public: void Update(const Bitmap& bitmap); void Render(const Bitmap& bitmap); void ClearViewport(const Bitmap& bitmap); + void DrawString(HDC hDc, int x, int y, LPCTSTR text, COLORREF textColor = 0x00FFFFFF, COLORREF backgroundColor = 0x00000000); + + ~Rasteriser(); void SetCurrentCamera(int index); Camera& GetCamera(int index); Camera& GetCurrentCamera(); + // Calculate the Lighting for the scene, fullLightRender sets the method to calculate the + // light values of all verticies otherwise light values are calculated at the polygon level void CalculateLighting(Model& currentModel, bool fullLightRender); + float Interpolate(float y, float x0, float x1, float y0, float y1); + float Interpolate(int y, int x0, int x1, int y0, int y1); + float Lerp(float a, float b, float t); + float Lerp(int a, int b, float t); + void DrawSquare(HDC hDc, const vector verticies); void DrawShape(HDC hDc, const vector verticies); @@ -37,17 +49,17 @@ public: void DrawSolidFlat(HDC hDc, Model& model); void DrawRasterisedSolidFlat(HDC hDc, Model& model); void DrawGouraud(HDC hDc, Model& model); + void DrawSolidTextured(HDC hDc, Model& model); void FillPolygonFlat(HDC hDc, vector& verticies, COLORREF colorIn); void FillFlatSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3, COLORREF colorIn); void FillPolygonGouraud(HDC hDc, vector& verticies); - void FillGouraudTopFlatTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3); - void FillGouraudBottomFlatTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3); - - void FillPolygonGouraudInt(HDC hDc, vector& verticies); void FillGouraudSideTriangle(HDC hDc, const Vertex& v1, const Vertex& v2, const Vertex& v3); + void FillPolygonTextured(HDC hDc, Model& model, vector& verticies); + void FillTexturedSideTriangle(HDC hDc, Model& model, const Vertex& v1, const Vertex& v2, const Vertex& v3, const UVCoord& uv1, const UVCoord& uv2, const UVCoord& uv3); + private: vector _sceneModels; vector _lights; @@ -60,6 +72,9 @@ private: float _currentAspectRatio = 0.0f; int _rotation = 0; + int _currentDrawMode = 0; + bool _screenMinimized = false; + string _modelDataLocation = ".//model_data/"; }; diff --git a/SharedTools.h b/SharedTools.h index d99ccd2..adf1bf0 100644 --- a/SharedTools.h +++ b/SharedTools.h @@ -1,4 +1,5 @@ #pragma once +#include "Vertex.h" #include "Matrix.h" #include @@ -9,6 +10,7 @@ template int sgn(T val) { return (T(0) < val) - (val < T(0)); } + namespace SharedTools { const float PI = (float)acos(-1); diff --git a/Texture.cpp b/Texture.cpp new file mode 100644 index 0000000..9017a32 --- /dev/null +++ b/Texture.cpp @@ -0,0 +1,80 @@ +#include "Texture.h" + +Texture::Texture() +{ + _width = 0; + _height = 0; + _paletteIndices = nullptr; + _palette = nullptr; +} + +Texture::~Texture() +{ + if (_paletteIndices != nullptr) + { + delete[] _paletteIndices; + _paletteIndices = nullptr; + } + if (_palette != nullptr) + { + delete[] _palette; + _palette = nullptr; + } +} + +void Texture::SetTextureSize(int width, int height) +{ + _width = width; + _height = height; + if (_paletteIndices != nullptr) + { + delete[] _paletteIndices; + } + _paletteIndices = new BYTE[_width * _height]; + if (_palette != nullptr) + { + delete[] _palette; + } + _palette = new COLORREF[256]; +} + +COLORREF Texture::GetTextureValue(int u, int v) const +{ + if (v < 0) + { + v = 0; + } + if (v >= _height) + { + v = _height - 1; + } + if (u < 0) + { + u = 0; + } + if (u >= _width) + { + u = _width - 1; + } + return _palette[_paletteIndices[v * _width + u]]; +} + +BYTE * Texture::GetPaletteIndices() +{ + return _paletteIndices; +} + +COLORREF * Texture::GetPalette() +{ + return _palette; +} + +int Texture::GetWidth() const +{ + return _width; +} + +int Texture::GetHeight() const +{ + return _height; +} diff --git a/Texture.h b/Texture.h new file mode 100644 index 0000000..5b1c206 --- /dev/null +++ b/Texture.h @@ -0,0 +1,22 @@ +#pragma once +#include "windows.h" + +class Texture +{ +public: + Texture(); + ~Texture(); + + void SetTextureSize(int width, int height); + COLORREF GetTextureValue(int u, int v) const; + BYTE * GetPaletteIndices(); + COLORREF * GetPalette(); + int GetWidth() const; + int GetHeight() const; + +private: + BYTE * _paletteIndices; + COLORREF * _palette; + int _width; + int _height; +}; \ No newline at end of file diff --git a/UVCoord.cpp b/UVCoord.cpp new file mode 100644 index 0000000..8e8222f --- /dev/null +++ b/UVCoord.cpp @@ -0,0 +1,57 @@ +#include "UVCoord.h" + +UVCoord::UVCoord() +{ + _u = 0.0f; + _v = 0.0f; +} + +UVCoord::UVCoord(float u, float v) +{ + _u = u; + _v = v; +} + +UVCoord::UVCoord(const UVCoord& other) +{ + Copy(other); +} + +UVCoord::~UVCoord() +{ +} + +float UVCoord::GetU() const +{ + return _u; +} + +void UVCoord::SetU(const float u) +{ + _u = u; +} + +float UVCoord::GetV() const +{ + return _v; +} + +void UVCoord::SetV(const float v) +{ + _v = v; +} + +UVCoord& UVCoord::operator= (const UVCoord& rhs) +{ + if (this != &rhs) + { + Copy(rhs); + } + return *this; +} + +void UVCoord::Copy(const UVCoord& other) +{ + _u = other.GetU(); + _v = other.GetV(); +} diff --git a/UVCoord.h b/UVCoord.h new file mode 100644 index 0000000..8f26d53 --- /dev/null +++ b/UVCoord.h @@ -0,0 +1,26 @@ +#pragma once + +class UVCoord +{ +public: + UVCoord(); + UVCoord(float u, float v); + UVCoord(const UVCoord& other); + + ~UVCoord(); + + float GetU() const; + void SetU(const float u); + float GetV() const; + void SetV(const float v); + + UVCoord& operator= (const UVCoord& rhs); + +private: + float _u; + float _v; + + void Copy(const UVCoord& other); + +}; + diff --git a/Vector3D.cpp b/Vector3D.cpp index 96dba3f..0af3c0d 100644 --- a/Vector3D.cpp +++ b/Vector3D.cpp @@ -81,6 +81,14 @@ Vector3D Vector3D::CrossProduct(const Vector3D v1, const Vector3D v2) return Vector3D(v1.GetY() * v2.GetZ() - v1.GetZ() * v2.GetY(), v1.GetZ() * v2.GetX() - v1.GetX() * v2.GetZ(), v1.GetX() * v2.GetY() - v1.GetY() * v2.GetX()); } +Vector3D& Vector3D::operator= (const Vector3D& rhs) +{ + if (this != &rhs) + { + Copy(rhs); + } + return *this; +} const Vector3D Vector3D::operator+ (const Vector3D& rhs) const { diff --git a/Vector3D.h b/Vector3D.h index f4bc9c5..42395d2 100644 --- a/Vector3D.h +++ b/Vector3D.h @@ -24,6 +24,8 @@ public: static float Length(const Vector3D v1, const Vector3D v2); static Vector3D CrossProduct(const Vector3D v1, const Vector3D v2); + Vector3D& operator= (const Vector3D& rhs); + const Vector3D operator+ (const Vector3D& rhs) const; const Vector3D operator/ (const int rhs) const; const Vector3D operator/ (const float rhs) const; diff --git a/Vertex.cpp b/Vertex.cpp index f878424..2c6fd37 100644 --- a/Vertex.cpp +++ b/Vertex.cpp @@ -140,16 +140,31 @@ int Vertex::GetR() const return _r; } +void Vertex::SetR(const int r) +{ + _r = r; +} + int Vertex::GetG() const { return _g; } +void Vertex::SetG(const int g) +{ + _g = g; +} + int Vertex::GetB() const { return _b; } +void Vertex::SetB(const int b) +{ + _b = b; +} + void Vertex::SetColor(int r, int g, int b) { _r = r; @@ -162,6 +177,16 @@ void Vertex::SetColor(const COLORREF colorIn) SetColor((int)GetRValue(colorIn), (int)GetGValue(colorIn), (int)GetBValue(colorIn)); } +int Vertex::GetUVIndex() const +{ + return _uvIndex; +} + +void Vertex::SetUVIndex(const int index) +{ + _uvIndex = index; +} + int Vertex::GetXInt(bool forceRoundUp) const { if (forceRoundUp) @@ -261,7 +286,11 @@ void Vertex::Copy(const Vertex& other) _w = other.GetW(); _contributeCount = other.GetContributeCount(); - SetNormal(other.GetNormal()); + _normal = other.GetNormal(); - SetColor(other.GetR(), other.GetG(), other.GetB()); + _r = other.GetR(); + _g = other.GetG(); + _b = other.GetB(); + + _uvIndex = other.GetUVIndex(); } diff --git a/Vertex.h b/Vertex.h index 86e3f13..026ba5e 100644 --- a/Vertex.h +++ b/Vertex.h @@ -1,5 +1,6 @@ #pragma once #include "Vector3D.h" +#include "UVCoord.h" #include "windows.h" class Vertex @@ -32,12 +33,18 @@ public: const COLORREF GetColor() const; int GetR() const; + void SetR(const int r); int GetG() const; + void SetG(const int g); int GetB() const; + void SetB(const int b); void SetColor(const int r, const int g, const int b); void SetColor(const COLORREF colorIn); - // Accessor Methods for returning the private x, y, z and w values as integeres instead of floats + int GetUVIndex() const; + void SetUVIndex(const int index); + + // Accessors for returning the private x, y, z and w values as integeres instead of floats // the ceil function to round the number up by defaults but using providing a false param will // use the floor function instead to round the number down int GetXInt(bool forceRoundUp = false) const; @@ -61,6 +68,8 @@ private: float _z; float _w; + float _zOriginal; + int _contributeCount; Vector3D _normal; @@ -68,6 +77,8 @@ private: int _g; int _b; + int _uvIndex; + void Copy(const Vertex& other); }; diff --git a/cheff.md2 b/model_data/cheff.md2 similarity index 100% rename from cheff.md2 rename to model_data/cheff.md2 diff --git a/cow.md2 b/model_data/cow.md2 similarity index 100% rename from cow.md2 rename to model_data/cow.md2 diff --git a/cube.md2 b/model_data/cube.md2 similarity index 100% rename from cube.md2 rename to model_data/cube.md2 diff --git a/fire.md2 b/model_data/fire.md2 similarity index 100% rename from fire.md2 rename to model_data/fire.md2 diff --git a/model_data/lines.pcx b/model_data/lines.pcx new file mode 100644 index 0000000..5f94e71 Binary files /dev/null and b/model_data/lines.pcx differ diff --git a/marvin.md2 b/model_data/marvin.md2 similarity index 100% rename from marvin.md2 rename to model_data/marvin.md2 diff --git a/model_data/marvin.pcx b/model_data/marvin.pcx new file mode 100644 index 0000000..c0ad227 Binary files /dev/null and b/model_data/marvin.pcx differ diff --git a/megaman.MD2 b/model_data/megaman.MD2 similarity index 100% rename from megaman.MD2 rename to model_data/megaman.MD2 diff --git a/teapot.md2 b/model_data/teapot.md2 similarity index 100% rename from teapot.md2 rename to model_data/teapot.md2 diff --git a/w_railgun.md2 b/model_data/w_railgun.md2 similarity index 100% rename from w_railgun.md2 rename to model_data/w_railgun.md2