diff --git a/include/assetloader.hpp b/include/assetloader.hpp new file mode 100644 index 0000000..f0b8384 --- /dev/null +++ b/include/assetloader.hpp @@ -0,0 +1,41 @@ +#ifndef DELTA_ASSETLOADER +#define DELTA_ASSETLOADER + +#include +#include +#include +#include +#include + +#include "math.hpp" + +struct SDL_SurfaceDeleter { + void operator()(SDL_Surface* surface) const { + if (surface) { + SDL_DestroySurface(surface); + } + } +}; + +class DeltaAssetLoader { + public: + ~DeltaAssetLoader() { + for (auto& [id, surface] : textures) { + SDL_DestroySurface(surface); + } + } + + void loadTexture(std::string id, const char *path); + void loadModel(std::string id, const char *path); + SDL_Surface* getSurface(const std::string& id); + void getModel(std::string id, const char *path); + private: + std::string base_path = "assets"; + std::string img_path = "images"; + std::string model_path = "models"; + + std::unordered_map textures; + std::unordered_map models; +}; + +#endif \ No newline at end of file diff --git a/include/globals.hpp b/include/globals.hpp index 6dee423..484f08e 100644 --- a/include/globals.hpp +++ b/include/globals.hpp @@ -1,8 +1,8 @@ #ifndef DELTA_GLOBALS #define DELTA_GLOBALS -const int SIMULATED_WIDTH = 800; -const int SIMULATED_HEIGHT = 600; +const int SIMULATED_WIDTH = 1200; +const int SIMULATED_HEIGHT = 800; const int FOCAL = 300; #endif \ No newline at end of file diff --git a/include/math.hpp b/include/math.hpp index 17a8240..acd9803 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -1,6 +1,8 @@ #ifndef DELTA_MATH #define DELTA_MATH +#include + class DeltaVector2 { public: float x, y; @@ -35,9 +37,6 @@ class DeltaVector3 { float length() const; }; -float getDeterminant(const DeltaVector2 &pointA, const DeltaVector2 &pointB, const DeltaVector2 &candidate); -DeltaVector2 barycentricToScreen(const DeltaVector3 &baryPoint, const DeltaVector2 &a, const DeltaVector2 &b, const DeltaVector2 &c); -DeltaVector3 intersect(DeltaVector3 a, DeltaVector3 b, float nearZ); struct DeltaTriangle { DeltaVector3 a, b, c; @@ -47,14 +46,34 @@ struct DeltaTriangle2D { DeltaVector2 a, b, c; }; - -struct DeltaRenderTriangle { - DeltaVector3 a, b, c; - DeltaVector2 uva, uvb, uvc; +struct DeltaVertex { + DeltaVector3 pos; + DeltaVector2 uv; }; +struct DeltaModelConnector { + int ind1, ind2; +}; + +struct DeltaRenderTriangle { + DeltaVertex a, b, c; + bool invert; +}; + +struct DeltaModel { + std::vector vertexes; + std::vector lines; + std::vector triangles; +}; + + +float getDeterminant(const DeltaVector2 &pointA, const DeltaVector2 &pointB, const DeltaVector2 &candidate); +DeltaVector2 barycentricToScreen(const DeltaVector3 &baryPoint, const DeltaVector2 &a, const DeltaVector2 &b, const DeltaVector2 &c); +DeltaVertex intersect(DeltaVertex a, DeltaVertex b, float nearZ); int clipTriangleAgainstNearPlane(float nearY, const DeltaRenderTriangle &in, DeltaRenderTriangle &out1, DeltaRenderTriangle &out2); +std::vector triangulatePolygon(std::vector& points); + uint32_t SDLColorToUint32(SDL_Color color); #endif \ No newline at end of file diff --git a/include/renderer/calls.hpp b/include/renderer/calls.hpp index fc14c67..8ec9d42 100644 --- a/include/renderer/calls.hpp +++ b/include/renderer/calls.hpp @@ -4,9 +4,11 @@ #include #include "math.hpp" +#include "assetloader.hpp" -void rasterizeTriangle(SDL_Renderer *renderer, const DeltaVector2 &a, const DeltaVector2 &b, const DeltaVector2 &c); +// void rasterizeTriangle(SDL_Renderer *renderer, const DeltaVector2 &a, const DeltaVector2 &b, const DeltaVector2 &c); void rasterizeTriangleFast(SDL_Surface* surface, const DeltaVector2& v0, const DeltaVector2& v1, const DeltaVector2& v2, uint32_t color); -void rasterizeTriangleSurface(SDL_Surface* surface, const DeltaVector2& v0, const DeltaVector2& v1, const DeltaVector2& v2, uint32_t color, float u0, float v0_uv, float u1, float v1_uv, float u2, float v2_uv); +void rasterizeTriangleSurface(DeltaAssetLoader& assets, std::vector& depthbuffer, SDL_Surface* surface, const DeltaVector3& v0, const DeltaVector3& v1, const DeltaVector3& v2, const std::string& textureID, float u0, float v0_uv, float u1, float v1_uv, float u2, float v2_uv); void setPixel(SDL_Surface* surface, int x, int y, uint32_t color); +uint32_t getPixel(SDL_Surface* surface, int x, int y); #endif \ No newline at end of file diff --git a/include/renderer/camera.hpp b/include/renderer/camera.hpp index 5ac2f6c..4d15eb9 100644 --- a/include/renderer/camera.hpp +++ b/include/renderer/camera.hpp @@ -11,9 +11,9 @@ class DeltaCamera { DeltaCamera() {pos = DeltaVector3(); angle = DeltaVector2();}; DeltaCamera(float x, float y, float z); DeltaVector3 translatePoint(const DeltaVector3 &worldPos); - DeltaVector2 projectPointVec(const DeltaVector3 &worldPos); - int projectTriangle(const DeltaTriangle &in, DeltaTriangle2D &out1, DeltaTriangle2D &out2); - DeltaVector2 projectPoint(float worldX, float worldY, float worldZ); + DeltaVector3 projectPointVec(const DeltaVector3 &worldPos); + int projectTriangle(const DeltaRenderTriangle &in, DeltaTriangle &out1, DeltaTriangle &out2, DeltaTriangle2D &uv1, DeltaTriangle2D &uv2); + DeltaVector3 projectPoint(float worldX, float worldY, float worldZ); }; #endif \ No newline at end of file diff --git a/make-release.bat b/make-release.bat new file mode 100644 index 0000000..30a5715 --- /dev/null +++ b/make-release.bat @@ -0,0 +1,6 @@ +g++ -c src/*.cpp -O3 -ffast-math -IC:\\common\include -Iinclude/ +g++ -c src/renderer/*.cpp -O3 -ffast-math -IC:\\common\include -Iinclude/ +g++ -c src/math/*.cpp -O3 -ffast-math -IC:\\common\include -Iinclude/ +g++ *.o -LC:\\common\lib -lSDL3 -mwindows -o out\release.exe +del .\*.o +echo release version built. available as ./out/release.exe \ No newline at end of file diff --git a/make.bat b/make.bat index dc47efa..375c401 100644 --- a/make.bat +++ b/make.bat @@ -1,5 +1,8 @@ g++ -c src/*.cpp -IC:\\common\include -Iinclude/ g++ -c src/renderer/*.cpp -IC:\\common\include -Iinclude/ +g++ -c src/math/*.cpp -IC:\\common\include -Iinclude/ g++ *.o -LC:\\common\lib -lSDL3 -o out\debug.exe del .\*.o -.\out\debug.exe \ No newline at end of file +cd .\out\ +.\debug.exe +cd .. \ No newline at end of file diff --git a/out/assets/images/stone.png b/out/assets/images/stone.png new file mode 100644 index 0000000..a310937 Binary files /dev/null and b/out/assets/images/stone.png differ diff --git a/out/assets/levels/level.txt b/out/assets/levels/level.txt new file mode 100644 index 0000000..e69de29 diff --git a/out/assets/models/teapot.mtl b/out/assets/models/teapot.mtl new file mode 100644 index 0000000..178fc3a --- /dev/null +++ b/out/assets/models/teapot.mtl @@ -0,0 +1,4 @@ +newmtl FrontColor +Ka 0.000000 0.000000 0.000000 +Kd 1.000000 1.000000 1.000000 +Ks 0.330000 0.330000 0.330000 diff --git a/src/assetloader.cpp b/src/assetloader.cpp new file mode 100644 index 0000000..6e2e22b --- /dev/null +++ b/src/assetloader.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +#include "assetloader.hpp" + +void DeltaAssetLoader::loadTexture(std::string id, const char* path) { + std::string fullPath = base_path + "\\" + img_path + "\\" + path; + SDL_Surface* surface = SDL_LoadPNG(fullPath.c_str()); + if (!surface) { + SDL_Log(SDL_GetError()); + } + + if (surface) { + SDL_Surface* optimized = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); + SDL_DestroySurface(surface); + + if (textures.count(id)) SDL_DestroySurface(textures[id]); + textures[id] = optimized; + } +} + +SDL_Surface* DeltaAssetLoader::getSurface(const std::string& id) { + auto it = textures.find(id); + return (it != textures.end()) ? it->second : nullptr; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 81493a8..9bce159 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "renderer/camera.hpp" #include "renderer/calls.hpp" #include "player.hpp" +#include "assetloader.hpp" static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; @@ -22,11 +23,14 @@ DeltaVector2 topleft, bottomright, p; Player player = Player(200, -200, 20); double prev = 0, now = 0, deltaTime; -SDL_Surface* backbuffer = NULL; +SDL_Surface* backbuffer = NULL; +std::vector depthbuffer; std::vector triangles; SDL_Texture* gpuTexture = NULL; +DeltaAssetLoader assets; + SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { SDL_HideCursor(); @@ -37,7 +41,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) return SDL_APP_FAILURE; } - if (!SDL_CreateWindowAndRenderer("game", SIMULATED_WIDTH, SIMULATED_HEIGHT, 0, &window, &renderer)) { + if (!SDL_CreateWindowAndRenderer("game", SIMULATED_WIDTH, SIMULATED_HEIGHT, SDL_WINDOW_FULLSCREEN, &window, &renderer)) { SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); return SDL_APP_FAILURE; } @@ -46,9 +50,27 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) SDL_SetRenderLogicalPresentation(renderer, SIMULATED_WIDTH, SIMULATED_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX); - triangles.push_back({ {40, 10, 200}, {40, 210, 0}, {40, 10, 0} , {0, 0}, {1, 1}, {0, 1}}); - triangles.push_back({ {40, 210, 200}, {40, 210, 0}, {40, 10, 200}, {1, 0}, {1, 1}, {0, 0} }); + // SDL_SetHint(SDL_HINT_RENDER_DEFAULT, "0"); + + // delta shenanigans + + for (int i = 0; i < 10; i++) { + triangles.push_back({ {{40 - i * 200.0f, 10, 200}, {0, 0}} , {{40 - i * 200.0f, 210, 0}, {1, 1}}, {{40 - i * 200.0f, 10, 0}, {0, 1}}}); + triangles.push_back({ {{40 - i * 200.0f, 210, 200}, {1, 0}}, {{40 - i * 200.0f, 210, 0}, {1, 1}}, {{40 - i * 200.0f, 10, 200}, {0, 0}} }); + } + + // triangles.push_back({ {{10, 10, 200}, {0, 0}} , {{10, 410, 0}, {2, 1}}, {{10, 10, 0}, {0, 1}} , false}); + // triangles.push_back({ {{10, 410, 200}, {2, 0}}, {{10, 410, 0}, {2, 1}}, {{10, 10, 200}, {0, 0}} , false}); + // triangles.push_back({ {{410, 10, 0}, {0, 1}} , {{410, 410, 0}, {2, 1}}, {{410, 10, 200}, {0, 0}} , true}); + // triangles.push_back({ {{410, 10, 200}, {0, 0}}, {{410, 410, 0}, {2, 1}}, {{410, 410, 200}, {2, 0}} , true}); + // triangles.push_back({ {{10, 410, 200}, {0, 0}} , {{410, 410, 0}, {2, 1}}, {{10, 410, 0}, {0, 1}} , true}); + // triangles.push_back({ {{10, 410, 200}, {0, 0}}, {{410, 410, 200}, {2, 0}}, {{410, 410, 0}, {2, 1}} , true}); + // triangles.push_back({ {{10, 10, 200}, {0, 0}} , {{10, 10, 0}, {0, 1}}, {{410, 10, 0}, {2, 1}} , true}); + // triangles.push_back({ {{410, 10, 200}, {2, 0}} , {{10, 10, 200}, {0, 0}}, {{410, 10, 0}, {2, 1}} , true}); + + backbuffer = SDL_CreateSurface(SIMULATED_WIDTH, SIMULATED_HEIGHT, SDL_PIXELFORMAT_ARGB8888); + depthbuffer.resize(SIMULATED_WIDTH * SIMULATED_HEIGHT); gpuTexture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ARGB8888, @@ -57,6 +79,10 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) SIMULATED_HEIGHT ); + SDL_SetTextureScaleMode(gpuTexture, SDL_SCALEMODE_NEAREST); + + assets.loadTexture("stonebricks", "stone.png"); + SDL_SetRenderVSync(renderer, 1); return SDL_APP_CONTINUE; /* carry on with the program! */ @@ -93,17 +119,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) SDL_FillSurfaceRect(backbuffer, NULL, 0xFF000000); - // SDL_SetRenderDrawColor(renderer, 255, 0, 255, SDL_ALPHA_OPAQUE); - // rasterizeTriangle(renderer, temp1, temp2, temp3); - - // DeltaVector2 rendered = player.camera.projectPointVec({ 40, 10, 30 }); - // SDL_RenderPoint(renderer, rendered.x, rendered.y); - // rendered = player.camera.projectPointVec({ 40, 210, 30 }); - // SDL_RenderPoint(renderer, rendered.x, rendered.y); - // rendered = player.camera.projectPointVec({ 40, 10, -170 }); - // SDL_RenderPoint(renderer, rendered.x, rendered.y); - // rendered = player.camera.projectPointVec({ 40, 210, -170 }); - // SDL_RenderPoint(renderer, rendered.x, rendered.y); + std::fill(depthbuffer.begin(), depthbuffer.end(), 0.0f); if (SDL_MUSTLOCK(backbuffer)) { SDL_LockSurface(backbuffer); @@ -112,23 +128,24 @@ SDL_AppResult SDL_AppIterate(void *appstate) DeltaTriangle triangle1, triangle2; for (auto triangle : triangles) { // DeltaVector2 pointA = player.camera.projectPointVec(triangle.a), pointB = player.camera.projectPointVec(triangle.b), pointC = player.camera.projectPointVec(triangle.c); - DeltaTriangle2D out1, out2; + DeltaTriangle out1, out2; + DeltaTriangle2D uv1, uv2; - int numtriangles = player.camera.projectTriangle({ triangle.a, triangle.b, triangle.c }, out1, out2); + int numtriangles = player.camera.projectTriangle(triangle, out1, out2, uv1, uv2); // SDL_Log(std::to_string(numtriangles).c_str()); if (numtriangles == 1) { // rasterizeTriangleFast(backbuffer, out1.a, out1.b, out1.c, SDLColorToUint32({ 255, 0, 0, 255 })); - // rasterizeTriangleFast(backbuffer, out1.c, out1.b, out1.a, SDLColorToUint32({ 0, 0, 255, 255 })); - rasterizeTriangleSurface(backbuffer, out1.a, out1.b, out1.c, SDLColorToUint32({ 0, 255, 0, 255 }), triangle.uva.x, triangle.uva.y, triangle.uvb.x, triangle.uvb.y, triangle.uvc.x, triangle.uvc.y); + // rasterizeTriangleFast(backbuffer, out1.a, out1.b, out1.c, SDLColorToUint32({ 0, 0, 255, 255 })); + rasterizeTriangleSurface(assets, depthbuffer, backbuffer, out1.a, out1.b, out1.c, "stonebricks", uv1.a.x, uv1.a.y, uv1.b.x, uv1.b.y, uv1.c.x, uv1.c.y); // SDL_Log(std::to_string(triangle.a.y).c_str()); // SDL_Log(std::to_string(triangle.b.y).c_str()); // SDL_Log(std::to_string(triangle.c.y).c_str()); } if (numtriangles == 2) { // rasterizeTriangleFast(backbuffer, out1.a, out1.b, out1.c, SDLColorToUint32({ 255, 0, 0, 255 })); - rasterizeTriangleSurface(backbuffer, out1.a, out1.b, out1.c, SDLColorToUint32({ 0, 255, 0, 255 }), triangle.uva.x, triangle.uva.y, triangle.uvb.x, triangle.uvb.y, triangle.uvc.x, triangle.uvc.y); + rasterizeTriangleSurface(assets, depthbuffer, backbuffer, out1.a, out1.b, out1.c, "stonebricks", uv1.a.x, uv1.a.y, uv1.b.x, uv1.b.y, uv1.c.x, uv1.c.y); // rasterizeTriangleFast(backbuffer, out1.c, out1.b, out1.a, SDLColorToUint32({ 0, 255, 0, 255 })); - rasterizeTriangleSurface(backbuffer, out2.a, out2.b, out2.c, SDLColorToUint32({ 0, 255, 0, 255 }), triangle.uva.x, triangle.uva.y, triangle.uvb.x, triangle.uvb.y, triangle.uvc.x, triangle.uvc.y); + rasterizeTriangleSurface(assets, depthbuffer, backbuffer, out2.a, out2.b, out2.c, "stonebricks", uv2.a.x, uv2.a.y, uv2.b.x, uv2.b.y, uv2.c.x, uv2.c.y); } // int intersected = clipTriangleAgainstNearPlane(0.1, { triangle.a, triangle.b, triangle.c }, triangle1, triangle2); @@ -139,7 +156,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) // rasterizeTriangleFast(backbuffer, pointA, pointB, pointC, SDLColorToUint32({ 255, 0, 0, 255 })); // SDL_Log("==="); } - SDL_Log("====="); + // SDL_Log("====="); if (SDL_MUSTLOCK(backbuffer)) { SDL_UnlockSurface(backbuffer); @@ -153,7 +170,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) SDL_UpdateTexture(gpuTexture, NULL, backbuffer->pixels, backbuffer->pitch); - SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); + SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255); SDL_RenderClear(renderer); SDL_RenderTexture(renderer, gpuTexture, NULL, NULL); @@ -166,6 +183,6 @@ SDL_AppResult SDL_AppIterate(void *appstate) void SDL_AppQuit(void *appstate, SDL_AppResult result) { - + SDL_Log(SDL_GetError()); } diff --git a/src/math.cpp b/src/math.cpp index bb6b07a..ca3b4fd 100644 --- a/src/math.cpp +++ b/src/math.cpp @@ -122,61 +122,41 @@ uint32_t SDLColorToUint32(SDL_Color color) { return (uint32_t)((color.a << 24) | (color.r << 16) | (color.g << 8) | (color.b << 0)); } -DeltaVector3 intersect(DeltaVector3 a, DeltaVector3 b, float nearZ) { - if (std::abs(b.y - a.y) < 0.0001f) { - SDL_Log("IMAGINEEEE"); +DeltaVertex intersect(DeltaVertex a, DeltaVertex b, float nearZ) { + if (std::abs(b.pos.y - a.pos.y) < 0.0001f) { return a; } - float t = (nearZ - a.y) / (b.y - a.y); - return DeltaVector3( - a.x + t * (b.x - a.x), + float t = (nearZ - a.pos.y) / (b.pos.y - a.pos.y); + + return { + {a.pos.x + t * (b.pos.x - a.pos.x), nearZ, - a.z + t * (b.z - a.z) - ); + a.pos.z + t * (b.pos.z - a.pos.z)}, + {a.uv.x + t * (b.uv.x - a.uv.x), + a.uv.y + t * (b.uv.y - a.uv.y)} + }; +} + +DeltaVector2 intersectUV(DeltaVector3 a, DeltaVector3 b, DeltaVector2 uv1, DeltaVector2 uv2, float nearZ) { + if (std::abs(b.y - a.y) < 0.0001f) { + return uv1; + } + + float t = (nearZ - a.y) / (b.y - a.y); + return { + uv1.x + t * (uv2.x - uv1.x), + uv1.y + t * (uv2.y - uv1.y) + }; } int clipTriangleAgainstNearPlane(float nearY, const DeltaRenderTriangle &in, DeltaRenderTriangle &out1, DeltaRenderTriangle &out2) { - // DeltaVector3 inside[3], outside[3]; - // DeltaVector3 cur; - // int nIn = 0, nOut = 0; - - // for (int i = 0; i < 3; i++) { - // if (i == 0) cur = in.a; - // else if (i == 1) cur = in.b; - // else cur = in.c; - // if (cur.y > 0.1f) inside[nIn++] = cur; - // else outside[nOut++] = cur; - // } - - // if (nIn == 0) return 0; - // if (nIn == 3) { out1 = in; return 1; } - - // if (nIn == 1) { - // out1.c = inside[0]; - // out1.b = intersect(inside[0], outside[0], nearZ); - // out1.a = intersect(inside[0], outside[1], nearZ); - // return 1; - // } - - // if (nIn == 2) { - // out1.a = inside[0]; - // out1.b = inside[1]; - // out1.c = intersect(inside[1], outside[0], nearZ); - - // out2.a = inside[0]; - // out2.b = out1.a; - // out2.c = intersect(outside[0], inside[0], nearZ); - - // return 2; - // } - - const DeltaVector3* v[3] = { &in.a, &in.b, &in.c }; + const DeltaVertex* v[3] = { &in.a, &in.b, &in.c }; bool inside[3]; int insideCount = 0; for (int i = 0; i < 3; i++) { - inside[i] = (v[i]->y >= nearY); + inside[i] = (v[i]->pos.y >= nearY); if (inside[i]) insideCount++; } @@ -192,6 +172,7 @@ int clipTriangleAgainstNearPlane(float nearY, const DeltaRenderTriangle &in, Del out1.a = *v[i0]; out1.b = intersect(*v[i0], *v[i1], nearY); out1.c = intersect(*v[i0], *v[i2], nearY); + return 1; } diff --git a/src/math/triangulate.cpp b/src/math/triangulate.cpp new file mode 100644 index 0000000..4d8fff8 --- /dev/null +++ b/src/math/triangulate.cpp @@ -0,0 +1,9 @@ +#include +#include +#include + +#include "math.hpp" + +// std::vector triangulatePolygon(std::vector& points) { + // DeltaVertex v0 = points +// } \ No newline at end of file diff --git a/src/renderer/calls.cpp b/src/renderer/calls.cpp index cbda368..b6a0ba3 100644 --- a/src/renderer/calls.cpp +++ b/src/renderer/calls.cpp @@ -1,9 +1,11 @@ #include #include #include +#include #include "globals.hpp" #include "renderer/calls.hpp" +#include "assetloader.hpp" void setPixel(SDL_Surface* surface, int x, int y, uint32_t color) { // 1. Boundary check to prevent memory corruption (Crashes) @@ -15,12 +17,25 @@ void setPixel(SDL_Surface* surface, int x, int y, uint32_t color) { *(uint32_t*)pixel_ptr = color; } -void rasterizeTriangleSurface(SDL_Surface* surface, const DeltaVector2& v0, const DeltaVector2& v1, const DeltaVector2& v2, uint32_t color, float u0, float v0_uv, float u1, float v1_uv, float u2, float v2_uv) { +uint32_t getPixel(SDL_Surface* surface, int x, int y) { + // Check bounds! (Important for 3D clipping) + if (x < 0 || x >= surface->w || y < 0 || y >= surface->h) return 0; + + // Pitch is in bytes, so we divide by 4 to get 'pitch in uint32_ts' + uint32_t* pixels = (uint32_t*)surface->pixels; + int pitchInWords = surface->pitch / 4; + + return pixels[y * pitchInWords + x]; +} + +void rasterizeTriangleSurface(DeltaAssetLoader& assets, std::vector& depthbuffer, SDL_Surface* surface, const DeltaVector3& v0, const DeltaVector3& v1, const DeltaVector3& v2, const std::string& textureID, float u0, float v0_uv, float u1, float v1_uv, float u2, float v2_uv) { int minX = std::max(0, (int)std::min({v0.x, v1.x, v2.x})); int maxX = std::min((int)surface->w - 1, (int)std::max({v0.x, v1.x, v2.x})); int minY = std::max(0, (int)std::min({v0.y, v1.y, v2.y})); int maxY = std::min((int)surface->h - 1, (int)std::max({v0.y, v1.y, v2.y})); + SDL_Surface* textureOrigin = assets.getSurface(textureID); + // precompute edge coefficients // edge function: E(x, y) = (v_i.y - v_j.y)x + (v_j.x - v_i.x)y + (v_i.x * v_j.y - v_j.x * v_i.y) float a0 = v1.y - v2.y, b0 = v2.x - v1.x, c0 = v1.x * v2.y - v2.x * v1.y; @@ -37,7 +52,19 @@ void rasterizeTriangleSurface(SDL_Surface* surface, const DeltaVector2& v0, cons float invArea = 1.0f / area; uint32_t* pixels = (uint32_t*)surface->pixels; + uint32_t* texpixels = (uint32_t*)textureOrigin->pixels; int pitch = surface->pitch / sizeof(uint32_t); + int pitchInWords = textureOrigin->pitch / 4; + + float iw0 = 1.0f / v0.z; + float iw1 = 1.0f / v1.z; + float iw2 = 1.0f / v2.z; + + float pu0 = u0 * iw0, pv0 = v0_uv * iw0; + float pu1 = u1 * iw1, pv1 = v1_uv * iw1; + float pu2 = u2 * iw2, pv2 = v2_uv * iw2; + + int texW = textureOrigin->w, texH = textureOrigin->h; // main raster loop for (int y = minY; y <= maxY; y++) { @@ -54,14 +81,37 @@ void rasterizeTriangleSurface(SDL_Surface* surface, const DeltaVector2& v0, cons float w1 = e1 * invArea; float w2 = e2 * invArea; - float u = w0 * u0 + w1 * u1 + w2 * u2; - float v = w0 * v0_uv + w1 * v1_uv + w2 * v2_uv; + // 2. interpolate the 1/w and the u/w, v/w + float interW = w0 * iw0 + w1 * iw1 + w2 * iw2; + float interU = w0 * pu0 + w1 * pu1 + w2 * pu2; + float interV = w0 * pv0 + w1 * pv1 + w2 * pv2; - uint8_t r = (uint8_t)(u * 255.0f); - uint8_t g = (uint8_t)(v * 255.0f); - uint8_t b = 128; + int pixelInd = y * surface->w + x; + if (interW > depthbuffer[pixelInd]) { + depthbuffer[pixelInd] = interW; + + float realInvW = 1.0f / interW; + float u = interU * realInvW; + float v = interV * realInvW; + + // uint8_t r = (uint8_t)(u * textureOrigin->w); + // uint8_t g = (uint8_t)(v * textureOrigin->h); + // uint8_t b = 128; + + // texpixels[y * pitchInWords + x]; + + int tx = (int)(u * texW) & (texW - 1); + int ty = (int)(v * texH) & (texH - 1); + + // tx %= texW; + // ty %= texH; + + // rowPtr[x] = SDL_MapRGB(SDL_GetPixelFormatDetails(surface->format), NULL, (uint8_t)(u * 255), (uint8_t)(v * 255), 0); //getPixel(textureOrigin, (int)(u * textureOrigin->w), (int)(v * textureOrigin->h)); + // rowPtr[x] = getPixel(textureOrigin, (int)(u * textureOrigin->w) % textureOrigin->w, (int)(v * textureOrigin->h) % textureOrigin->h); + // (int)(u * textureOrigin->w) & (textureOrigin->w - 1) + rowPtr[x] = texpixels[ty * pitchInWords + tx]; + } - rowPtr[x] = (255 << 24) | (r << 16) | (g << 8) | b; } // step horizontally: just 3 additions @@ -77,55 +127,6 @@ void rasterizeTriangleSurface(SDL_Surface* surface, const DeltaVector2& v0, cons } } -void rasterizeTriangle(SDL_Renderer *renderer, const DeltaVector2 &v0, const DeltaVector2 &v1, const DeltaVector2 &v2) { - int minX = std::max(0, (int)std::min({v0.x, v1.x, v2.x})); - int maxX = std::min((int)SIMULATED_WIDTH - 1, (int)std::max({v0.x, v1.x, v2.x})); - int minY = std::max(0, (int)std::min({v0.y, v1.y, v2.y})); - int maxY = std::min((int)SIMULATED_HEIGHT - 1, (int)std::max({v0.y, v1.y, v2.y})); - - float a0 = v1.y - v2.y, b0 = v2.x - v1.x, c0 = v1.x * v2.y - v2.x * v1.y; - float a1 = v2.y - v0.y, b1 = v0.x - v2.x, c1 = v2.x * v0.y - v0.x * v2.y; - float a2 = v0.y - v1.y, b2 = v1.x - v0.x, c2 = v0.x * v1.y - v1.x * v0.y; - - __m128 stepX = _mm_set_ps(3.0f, 2.0f, 1.0f, 0.0f); - __m128 a0_4 = _mm_set1_ps(a0); - __m128 a1_4 = _mm_set1_ps(a1); - __m128 a2_4 = _mm_set1_ps(a2); - - for (int y = minY; y <= maxY; y++) { - float fy = (float)y; - - __m128 e0 = _mm_add_ps(_mm_set1_ps(a0 * minX + b0 * fy + c0), _mm_mul_ps(a0_4, stepX)); - __m128 e1 = _mm_add_ps(_mm_set1_ps(a1 * minX + b1 * fy + c1), _mm_mul_ps(a1_4, stepX)); - __m128 e2 = _mm_add_ps(_mm_set1_ps(a2 * minX + b2 * fy + c2), _mm_mul_ps(a2_4, stepX)); - - __m128 e0_step = _mm_set1_ps(a0 * 4.0f); - __m128 e1_step = _mm_set1_ps(a1 * 4.0f); - __m128 e2_step = _mm_set1_ps(a2 * 4.0f); - - for (int x = minX; x <= maxX; x += 4) { - __m128 mask = _mm_and_ps(_mm_cmpge_ps(e0, _mm_setzero_ps()), - _mm_and_ps(_mm_cmpge_ps(e1, _mm_setzero_ps()), - _mm_cmpge_ps(e2, _mm_setzero_ps()))); - - int bitmask = _mm_movemask_ps(mask); - - if (bitmask != 0) { - for (int i = 0; i < 4; i++) { - if ((bitmask >> i) & 1) { - if (x + i <= maxX) - SDL_RenderPoint(renderer, (float)(x + i), fy); - } - } - } - - e0 = _mm_add_ps(e0, e0_step); - e1 = _mm_add_ps(e1, e1_step); - e2 = _mm_add_ps(e2, e2_step); - } - } -} - void rasterizeTriangleFast(SDL_Surface* surface, const DeltaVector2& v0, const DeltaVector2& v1, const DeltaVector2& v2, uint32_t color) { int minX = std::max(0, (int)std::min({v0.x, v1.x, v2.x})); int maxX = std::min((int)surface->w - 1, (int)std::max({v0.x, v1.x, v2.x})); @@ -177,34 +178,4 @@ void rasterizeTriangleFast(SDL_Surface* surface, const DeltaVector2& v0, const D e2 = _mm_add_ps(e2, e2_step); } } -} - -// void rasterizeTriangle(SDL_Renderer *renderer, const DeltaVector2 &a, const DeltaVector2 &b, const DeltaVector2 &c) { -// DeltaVector2 begin, end, p; -// begin.x = std::min(a.x, std::min(b.x, c.x)); -// begin.y = std::min(a.y, std::min(b.y, c.y)); -// end.x = std::max(a.x, std::max(b.x, c.x)); -// end.y = std::max(a.y, std::max(b.y, c.y)); - -// int entrance, exit; - -// for (int y = std::max(1.0f, begin.y); y < std::min(end.y, SIMULATED_HEIGHT + 0.0f); y++) { -// entrance = -1; -// exit = -1; -// for (int x = std::max(1.0f, begin.x); x < std::min(end.x, SIMULATED_WIDTH + 0.0f); x++) { -// p.x = x; p.y = y; - -// if (getDeterminant(a, b, p) >= 0 && getDeterminant(b, c, p) >= 0 && getDeterminant(c, a, p) >= 0 && entrance == -1) { -// SDL_RenderPoint(renderer, p.x, p.y); -// // entrance = x; -// } -// else if (!(getDeterminant(a, b, p) >= 0 && getDeterminant(b, c, p) >= 0 && getDeterminant(c, a, p) >= 0) && entrance != -1) { -// exit = x; -// break; -// } - -// } - -// // SDL_RenderLine(renderer, entrance, y, exit, y); -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/renderer/camera.cpp b/src/renderer/camera.cpp index 3aa79b7..6195ed1 100644 --- a/src/renderer/camera.cpp +++ b/src/renderer/camera.cpp @@ -45,7 +45,7 @@ DeltaVector3 DeltaCamera::translatePoint(const DeltaVector3 &worldPos) { return calculatedPos; } -DeltaVector2 DeltaCamera::projectPointVec(const DeltaVector3 &worldPos) { +DeltaVector3 DeltaCamera::projectPointVec(const DeltaVector3 &worldPos) { DeltaVector3 calculatedPos; DeltaVector2 projected; calculatedPos = worldPos; @@ -61,17 +61,22 @@ DeltaVector2 DeltaCamera::projectPointVec(const DeltaVector3 &worldPos) { projected.y = SIMULATED_HEIGHT - projected.y; // we are done =D - return projected; + return {projected.x, projected.y, calculatedPos.y}; } -int DeltaCamera::projectTriangle(const DeltaTriangle &in, DeltaTriangle2D &out1, DeltaTriangle2D &out2) { - DeltaTriangle translated = { translatePoint(in.a), translatePoint(in.b), translatePoint(in.c) }, clipped1, clipped2; +int DeltaCamera::projectTriangle(const DeltaRenderTriangle &in, DeltaTriangle &out1, DeltaTriangle &out2, DeltaTriangle2D &uv1, DeltaTriangle2D &uv2) { + DeltaTriangle translated = { translatePoint(in.a.pos), translatePoint(in.b.pos), translatePoint(in.c.pos) }; + DeltaRenderTriangle translatedUV = {{translated.a, in.a.uv}, {translated.b, in.b.uv}, {translated.c, in.c.uv}}, clipped1, clipped2; - int numClipped = clipTriangleAgainstNearPlane(0.01, translated, clipped1, clipped2); + int numClipped = clipTriangleAgainstNearPlane(0.01, translatedUV, clipped1, clipped2); if (numClipped == 1) { - out1.a = projectPointVec(clipped1.a); - out1.b = projectPointVec(clipped1.b); - out1.c = projectPointVec(clipped1.c); + out1.a = projectPointVec(clipped1.a.pos); + out1.b = projectPointVec(clipped1.b.pos); + out1.c = projectPointVec(clipped1.c.pos); + + uv1.a = clipped1.a.uv; + uv1.b = clipped1.b.uv; + uv1.c = clipped1.c.uv; // out1.a = projectPointVec(translated.a); // out1.b = projectPointVec(translated.b); @@ -80,18 +85,26 @@ int DeltaCamera::projectTriangle(const DeltaTriangle &in, DeltaTriangle2D &out1, if (numClipped == 2) { - out1.a = projectPointVec(clipped1.a); - out1.b = projectPointVec(clipped1.b); - out1.c = projectPointVec(clipped1.c); + out1.a = projectPointVec(clipped1.a.pos); + out1.b = projectPointVec(clipped1.b.pos); + out1.c = projectPointVec(clipped1.c.pos); - out2.a = projectPointVec(clipped2.a); - out2.b = projectPointVec(clipped2.b); - out2.c = projectPointVec(clipped2.c); + out2.a = projectPointVec(clipped2.a.pos); + out2.b = projectPointVec(clipped2.b.pos); + out2.c = projectPointVec(clipped2.c.pos); + + uv1.a = clipped1.a.uv; + uv1.b = clipped1.b.uv; + uv1.c = clipped1.c.uv; + + uv2.a = clipped2.a.uv; + uv2.b = clipped2.b.uv; + uv2.c = clipped2.c.uv; } return numClipped; } -DeltaVector2 DeltaCamera::projectPoint(float worldX, float worldY, float worldZ) { +DeltaVector3 DeltaCamera::projectPoint(float worldX, float worldY, float worldZ) { // simple conversion, dont iterate further return projectPointVec({ worldX, worldY, worldZ }); } \ No newline at end of file