From cdb940f6aa3c08f6e97b5230380631fd5427a0b7 Mon Sep 17 00:00:00 2001 From: IDunnoDev Date: Sat, 11 Dec 2021 20:31:39 +0000 Subject: [PATCH] Week10 [30/11] - [06/12] Added active Variable to the Lights Classes Added updated MD2Loader Class Added Model Filename and Texture File Name to the Model Class Added Active Flag to the Model Class Added DrawMode to the Model Class Added Texture and UV Variables to the Model Class Added UV Coordinates to the Polygon3D Class Added DrawString method to the Rasterizer to write text to the screen space Added Interpolate and Lerp Methods to the Rasterizer Added Select statement for drawing a current models draw mode Added DrawTextured Method to the Rasterizer Class Added UVCoord Class Added Texture Class Added = operator to the Vector3D Class Added UVCoord to the Vertex Class Moved Models and Texture Files into a subfolder Fixed issue with textures not loading properly because of the vector address problem --- BaseFramework.vcxproj | 4 + BaseFramework.vcxproj.filters | 12 + Light.cpp | 18 + Light.h | 6 + MD2Loader.cpp | 168 +++++- MD2Loader.h | 11 +- Matrix.h | 1 - Model.cpp | 100 +++- Model.h | 37 +- Polygon3D.cpp | 87 ++- Polygon3D.h | 24 +- Rasteriser.cpp | 645 +++++++++++++++------- Rasteriser.h | 23 +- SharedTools.h | 2 + Texture.cpp | 80 +++ Texture.h | 22 + UVCoord.cpp | 57 ++ UVCoord.h | 26 + Vector3D.cpp | 8 + Vector3D.h | 2 + Vertex.cpp | 33 +- Vertex.h | 13 +- cheff.md2 => model_data/cheff.md2 | Bin cow.md2 => model_data/cow.md2 | Bin cube.md2 => model_data/cube.md2 | Bin fire.md2 => model_data/fire.md2 | Bin model_data/lines.pcx | Bin 0 -> 4942 bytes marvin.md2 => model_data/marvin.md2 | Bin model_data/marvin.pcx | Bin 0 -> 81867 bytes megaman.MD2 => model_data/megaman.MD2 | Bin teapot.md2 => model_data/teapot.md2 | Bin w_railgun.md2 => model_data/w_railgun.md2 | Bin 32 files changed, 1136 insertions(+), 243 deletions(-) create mode 100644 Texture.cpp create mode 100644 Texture.h create mode 100644 UVCoord.cpp create mode 100644 UVCoord.h rename cheff.md2 => model_data/cheff.md2 (100%) rename cow.md2 => model_data/cow.md2 (100%) rename cube.md2 => model_data/cube.md2 (100%) rename fire.md2 => model_data/fire.md2 (100%) create mode 100644 model_data/lines.pcx rename marvin.md2 => model_data/marvin.md2 (100%) create mode 100644 model_data/marvin.pcx rename megaman.MD2 => model_data/megaman.MD2 (100%) rename teapot.md2 => model_data/teapot.md2 (100%) rename w_railgun.md2 => model_data/w_railgun.md2 (100%) 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 0000000000000000000000000000000000000000..5f94e7166c22fd604bf7612929d0c49930133c87 GIT binary patch literal 4942 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 W_Sav0@#NEkk3RVRyZ0X6-~0dNa_h4I literal 0 HcmV?d00001 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