diff options
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | include/swgfx.h | 29 | ||||
| -rw-r--r-- | src/swgfx.c | 191 |
3 files changed, 125 insertions, 98 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ec4d307..39e087b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -18,7 +18,8 @@ target_include_directories(swgfx PUBLIC | |||
| 18 | target_link_libraries(swgfx PRIVATE | 18 | target_link_libraries(swgfx PRIVATE |
| 19 | math) | 19 | math) |
| 20 | 20 | ||
| 21 | target_compile_options(swgfx PRIVATE -Wall -Wextra) | 21 | # Using -Wno-missing-braces to conveniently union xyzw and rgba without warnings. |
| 22 | target_compile_options(swgfx PRIVATE -Wall -Wextra -Wno-missing-braces) | ||
| 22 | 23 | ||
| 23 | # Tests. | 24 | # Tests. |
| 24 | 25 | ||
diff --git a/include/swgfx.h b/include/swgfx.h index 452847c..d527f02 100644 --- a/include/swgfx.h +++ b/include/swgfx.h | |||
| @@ -30,8 +30,8 @@ typedef float R; | |||
| 30 | 30 | ||
| 31 | typedef struct sgVec2i { int x, y; } sgVec2i; | 31 | typedef struct sgVec2i { int x, y; } sgVec2i; |
| 32 | typedef struct sgVec2 { R x, y; } sgVec2; | 32 | typedef struct sgVec2 { R x, y; } sgVec2; |
| 33 | typedef struct sgVec3 { R x, y, z; } sgVec3; | 33 | typedef struct sgVec3 { union { struct { R r, g, b; }; struct { R x, y, z; }; }; } sgVec3; |
| 34 | typedef struct sgVec4 { R x, y, z, w; } sgVec4; | 34 | typedef struct sgVec4 { union { struct { R r, g, b, a; }; struct { R x, y, z, w; }; }; } sgVec4; |
| 35 | 35 | ||
| 36 | typedef sgVec3 sgNormal; | 36 | typedef sgVec3 sgNormal; |
| 37 | 37 | ||
| @@ -50,14 +50,17 @@ typedef uint16_t sgIdx; | |||
| 50 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; | 50 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; |
| 51 | typedef struct sgTriIdx { sgVertIdx v0, v1, v2; } sgTriIdx; | 51 | typedef struct sgTriIdx { sgVertIdx v0, v1, v2; } sgTriIdx; |
| 52 | 52 | ||
| 53 | typedef struct sgBgra { uint8_t b, g, r, a; } sgBgra; | 53 | typedef struct sgRgba { uint8_t r, g, b, a; } sgRgba; |
| 54 | typedef struct sgRgba { uint8_t r, g, b, a; } sgRgba; | 54 | typedef struct sgBgra { uint8_t b, g, r, a; } sgBgra; |
| 55 | // TODO: Should we use real-valued colours? | 55 | |
| 56 | typedef sgVec3 sgColour3; | ||
| 57 | typedef sgVec4 sgColour4; | ||
| 56 | typedef sgRgba sgPixel; | 58 | typedef sgRgba sgPixel; |
| 57 | // TODO: Expose a macro to control the desired surface format. | 59 | // TODO: Expose a macro to control the desired surface format. |
| 58 | typedef sgBgra sgScreenPixel; | 60 | typedef sgBgra sgScreenPixel; |
| 59 | 61 | ||
| 60 | typedef uint16_t sgTextureId; | 62 | typedef uint16_t sgTextureId; |
| 63 | typedef sgRgba sgTexel; | ||
| 61 | 64 | ||
| 62 | typedef enum sgTextureFilter { | 65 | typedef enum sgTextureFilter { |
| 63 | sgNearest, | 66 | sgNearest, |
| @@ -67,7 +70,7 @@ typedef enum sgTextureFilter { | |||
| 67 | typedef struct sgImage { | 70 | typedef struct sgImage { |
| 68 | int width; | 71 | int width; |
| 69 | int height; | 72 | int height; |
| 70 | sgPixel* pixels; | 73 | sgTexel* pixels; |
| 71 | } sgImage; | 74 | } sgImage; |
| 72 | 75 | ||
| 73 | typedef struct swgfx swgfx; | 76 | typedef struct swgfx swgfx; |
| @@ -87,8 +90,8 @@ void sgDel(swgfx**); | |||
| 87 | 90 | ||
| 88 | // TODO: Write client app first, then implement the functions below in the C file. | 91 | // TODO: Write client app first, then implement the functions below in the C file. |
| 89 | 92 | ||
| 90 | sgPixel* sgColourBuffer(swgfx*); | 93 | sgColour4* sgColourBuffer(swgfx*); |
| 91 | void sgPresent(swgfx*, sgVec2i dimensions, sgScreenPixel* screen); | 94 | void sgPresent(swgfx*, sgVec2i dimensions, sgScreenPixel* screen); |
| 92 | 95 | ||
| 93 | void sgModelId (swgfx*); | 96 | void sgModelId (swgfx*); |
| 94 | void sgModel (swgfx*, sgVec3 position, sgVec3 right, sgVec3 up, sgVec3 forward); | 97 | void sgModel (swgfx*, sgVec3 position, sgVec3 right, sgVec3 up, sgVec3 forward); |
| @@ -101,7 +104,7 @@ void sgTextureRegister(swgfx*, sgTextureId, const sgImage*, sgTextureFilter); | |||
| 101 | void sgTextureActivate(swgfx*, sgTextureId); | 104 | void sgTextureActivate(swgfx*, sgTextureId); |
| 102 | 105 | ||
| 103 | void sgClear(swgfx*); | 106 | void sgClear(swgfx*); |
| 104 | void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour); | 107 | void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgColour4); |
| 105 | void sgQuads (swgfx*, size_t count, const sgQuad*); | 108 | void sgQuads (swgfx*, size_t count, const sgQuad*); |
| 106 | void sgQuadsi(swgfx*, size_t count, const sgQuadi*); | 109 | void sgQuadsi(swgfx*, size_t count, const sgQuadi*); |
| 107 | void sgTriangles2 (swgfx*, size_t count, const sgTri2*); | 110 | void sgTriangles2 (swgfx*, size_t count, const sgTri2*); |
| @@ -112,13 +115,13 @@ void sgTrianglesIndexed(swgfx*, size_t numIndices, const sgIdx* indices, const s | |||
| 112 | void sgTrianglesIndexedNonUniform(swgfx*, size_t numTris, const sgTriIdx* tris, const sgVec3* positions, const sgVec2* texcoords, const sgVec3* normals); | 115 | void sgTrianglesIndexedNonUniform(swgfx*, size_t numTris, const sgTriIdx* tris, const sgVec3* positions, const sgVec2* texcoords, const sgVec3* normals); |
| 113 | 116 | ||
| 114 | void sgLighting(swgfx*); | 117 | void sgLighting(swgfx*); |
| 115 | void sgAmbient(swgfx*, sgVec3 ambient); | 118 | void sgAmbient (swgfx*, sgColour3); |
| 116 | // TODO: Implement directional lights. | 119 | void sgDirectional(swgfx*, sgColour3, sgVec3 direction); |
| 117 | 120 | ||
| 118 | void sgDepth(swgfx*); | 121 | void sgDepth(swgfx*); |
| 119 | void sgNormals(swgfx*); | 122 | void sgNormals(swgfx*); |
| 120 | 123 | ||
| 121 | void sgGamma (swgfx*, sgPixel*, int width, int height); | 124 | void sgGamma (swgfx*, sgTexel*, int width, int height); |
| 122 | void sgGammaInv(swgfx*, sgPixel*, int width, int height); | 125 | void sgGammaInv(swgfx*, sgColour4*, int width, int height); |
| 123 | 126 | ||
| 124 | sgCounters sgGetCounters(const swgfx*); | 127 | sgCounters sgGetCounters(const swgfx*); |
diff --git a/src/swgfx.c b/src/swgfx.c index 6074eac..33b453d 100644 --- a/src/swgfx.c +++ b/src/swgfx.c | |||
| @@ -42,11 +42,11 @@ typedef struct sgTexture { | |||
| 42 | 42 | ||
| 43 | typedef struct swgfx { | 43 | typedef struct swgfx { |
| 44 | sgVec2i dims; // Colour buffer dimensions. | 44 | sgVec2i dims; // Colour buffer dimensions. |
| 45 | sgPixel* colour; // Colour buffer. | 45 | sgColour4* colour; // Colour buffer. |
| 46 | R* depth; // Depth buffer. | 46 | R* depth; // Depth buffer. |
| 47 | sgTextureId* texture; // Texture ID buffer. | 47 | sgTextureId* texture; // Texture ID buffer. |
| 48 | sgVec2* texcoords; // Texture coords buffer. | 48 | sgVec2* texcoords; // Texture coords buffer. |
| 49 | sgPixel* albedo; // Albedo buffer. | 49 | sgColour3* albedo; // Albedo buffer. |
| 50 | sgVec3* normals; // Normals buffer. | 50 | sgVec3* normals; // Normals buffer. |
| 51 | sgViewport_t viewport; | 51 | sgViewport_t viewport; |
| 52 | sgMat4 model; // Model matrix. | 52 | sgMat4 model; // Model matrix. |
| @@ -63,7 +63,7 @@ typedef struct swgfx { | |||
| 63 | sgMat4 mvp; // Model-view-projection matrix. | 63 | sgMat4 mvp; // Model-view-projection matrix. |
| 64 | sgTexture* textureRegister; // Indexed by texture id. | 64 | sgTexture* textureRegister; // Indexed by texture id. |
| 65 | sgTextureId activeTexture; | 65 | sgTextureId activeTexture; |
| 66 | sgPixel defaultPixel; // The single-pixel of the default texture. | 66 | sgTexel defaultPixel; // The single-pixel of the default texture. |
| 67 | sgImage defaultImage; // Image for the default texture. | 67 | sgImage defaultImage; // Image for the default texture. |
| 68 | sgCounters counters; | 68 | sgCounters counters; |
| 69 | } swgfx; | 69 | } swgfx; |
| @@ -133,7 +133,7 @@ static inline sgVec3 normalize3(sgVec3 v) { | |||
| 133 | return (n > 0) ? (sgVec3){v.x / n, v.y / n, v.z / n} : (sgVec3){0, 0, 0}; | 133 | return (n > 0) ? (sgVec3){v.x / n, v.y / n, v.z / n} : (sgVec3){0, 0, 0}; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | static inline sgVec2 Vec2FromVec4(sgVec4 v) { return (sgVec2){v.x, v.y}; } | 136 | static inline sgVec3 Vec3FromVec4(sgVec4 v) { return (sgVec3){v.x, v.y, v.z}; } |
| 137 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } | 137 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } |
| 138 | 138 | ||
| 139 | static inline sgMat4 Mat4( | 139 | static inline sgMat4 Mat4( |
| @@ -288,17 +288,20 @@ static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { | |||
| 288 | 0, 0, -1, 0); | 288 | 0, 0, -1, 0); |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | static inline sgVec3 PixelToVec3(sgPixel p) { | 291 | static inline uint8_t ColourToUint(R x) { |
| 292 | return (sgVec3){(R)p.r / 255.f, (R)p.g / 255.f, (R)p.b / 255.f}; | 292 | return (uint8_t)(x * 255.f); |
| 293 | } | 293 | } |
| 294 | static inline sgPixel Vec3ToPixel(sgVec3 p, R a) { | 294 | static inline R UintToColour(uint8_t x) { |
| 295 | return (sgPixel){(uint8_t)(p.x * 255.f), (uint8_t)(p.y * 255.f), (uint8_t)(p.z * 255.f), (uint8_t)(a * 255.f)}; | 295 | return (R)x / 255.f; |
| 296 | } | 296 | } |
| 297 | static inline sgVec4 PixelToVec4(sgPixel p) { | 297 | static inline sgTexel Colour3ToTexel(sgColour3 c, R a) { |
| 298 | return (sgVec4){(R)p.r / 255.f, (R)p.g / 255.f, (R)p.b / 255.f, (R)p.a / 255.f}; | 298 | return (sgTexel){ColourToUint(c.r), ColourToUint(c.g), ColourToUint(c.b), ColourToUint(a)}; |
| 299 | } | 299 | } |
| 300 | static inline sgPixel Vec4ToPixel(sgVec4 p) { | 300 | static inline sgColour4 TexelToColour(sgTexel p) { |
| 301 | return (sgPixel){(uint8_t)(p.x * 255.f), (uint8_t)(p.y * 255.f), (uint8_t)(p.z * 255.f), (uint8_t)(p.w * 255.f)}; | 301 | return (sgColour4){UintToColour(p.r), UintToColour(p.g), UintToColour(p.b), UintToColour(p.a)}; |
| 302 | } | ||
| 303 | static inline sgColour3 TexelToColour3(sgTexel p) { | ||
| 304 | return (sgColour3){UintToColour(p.r), UintToColour(p.g), UintToColour(p.b)}; | ||
| 302 | } | 305 | } |
| 303 | 306 | ||
| 304 | #ifndef _NDEBUG | 307 | #ifndef _NDEBUG |
| @@ -308,7 +311,7 @@ static bool InBounds(int width, int height, int x, int y) { | |||
| 308 | } | 311 | } |
| 309 | #endif // _NDEBUG | 312 | #endif // _NDEBUG |
| 310 | 313 | ||
| 311 | static inline sgPixel* Pixel(swgfx* gfx, int x, int y) { | 314 | static inline sgColour4* Colour(swgfx* gfx, int x, int y) { |
| 312 | assert(gfx); | 315 | assert(gfx); |
| 313 | assert(gfx->colour); | 316 | assert(gfx->colour); |
| 314 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); | 317 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); |
| @@ -322,9 +325,9 @@ static inline const R* Depth(swgfx* gfx, int x, int y) { | |||
| 322 | return gfx->depth + (y * gfx->dims.x) + x; | 325 | return gfx->depth + (y * gfx->dims.x) + x; |
| 323 | } | 326 | } |
| 324 | 327 | ||
| 325 | static inline void SetPixelColour(swgfx* gfx, const sgVec2i p, sgPixel colour) { | 328 | static inline void SetPixelColour(swgfx* gfx, const sgVec2i p, sgColour4 colour) { |
| 326 | assert(gfx); | 329 | assert(gfx); |
| 327 | *Pixel(gfx, p.x, p.y) = colour; | 330 | *Colour(gfx, p.x, p.y) = colour; |
| 328 | #if SWGFX_PROFILING | 331 | #if SWGFX_PROFILING |
| 329 | gfx->counters.pixels++; | 332 | gfx->counters.pixels++; |
| 330 | #endif // SWGFX_PROFILING | 333 | #endif // SWGFX_PROFILING |
| @@ -342,65 +345,66 @@ static inline void SetPixelDeferred(swgfx* gfx, const sgVec2i p, R depth, sgText | |||
| 342 | #endif // SWGFX_PROFILING | 345 | #endif // SWGFX_PROFILING |
| 343 | } | 346 | } |
| 344 | 347 | ||
| 345 | static inline sgPixel ReadTexture(const sgImage* texture, sgVec2i xy) { | 348 | static inline sgTexel ReadImage(const sgImage* image, sgVec2i xy) { |
| 346 | assert(texture); | 349 | assert(image); |
| 347 | assert(texture->pixels); | 350 | assert(image->pixels); |
| 348 | assert(InBounds(texture->width, texture->height, xy.x, xy.y)); | 351 | assert(InBounds(image->width, image->height, xy.x, xy.y)); |
| 349 | return texture->pixels[xy.y * texture->width + xy.x]; | 352 | return image->pixels[xy.y * image->width + xy.x]; |
| 350 | } | 353 | } |
| 351 | // Output normalized to [0,1]. | 354 | // Output normalized to [0,1]. |
| 352 | static inline sgVec4 ReadTextureFloat(const sgImage* texture, sgVec2i xy) { | 355 | static inline sgColour4 ReadImageFloat(const sgImage* image, sgVec2i xy) { |
| 353 | return PixelToVec4(ReadTexture(texture, xy)); | 356 | return TexelToColour(ReadImage(image, xy)); |
| 354 | } | 357 | } |
| 355 | 358 | ||
| 356 | static inline sgVec2i UvToIndex(const sgImage* texture, sgVec2 uv) { | 359 | static inline sgVec2i UvToIndex(const sgImage* image, sgVec2 uv) { |
| 357 | assert(texture); | 360 | assert(image); |
| 358 | return (sgVec2i){ | 361 | return (sgVec2i){ |
| 359 | (int)(uv.x * (R)(texture->width - 1)), | 362 | (int)(uv.x * (R)(image->width - 1)), |
| 360 | (int)(uv.y * (R)(texture->height - 1))}; | 363 | (int)(uv.y * (R)(image->height - 1))}; |
| 361 | } | 364 | } |
| 362 | 365 | ||
| 363 | static inline sgVec2i TextureRepeat(const sgImage* texture, sgVec2i p) { | 366 | static inline sgVec2i TextureRepeat(const sgImage* texture, sgVec2i p) { |
| 364 | return (sgVec2i){mod(p.x, texture->width), mod(p.y, texture->height)}; | 367 | return (sgVec2i){mod(p.x, texture->width), mod(p.y, texture->height)}; |
| 365 | } | 368 | } |
| 366 | 369 | ||
| 367 | static inline sgPixel FilterNearest(const sgImage* texture, sgVec2 uv) { | 370 | static inline sgColour4 FilterNearest(const sgImage* image, sgVec2 uv) { |
| 368 | assert(texture); | 371 | assert(image); |
| 369 | assert(texture->pixels); | 372 | assert(image->pixels); |
| 370 | const sgVec2i xy = UvToIndex(texture, uv); | 373 | const sgVec2i xy = UvToIndex(image, uv); |
| 371 | const sgVec2i xy2 = TextureRepeat(texture, xy); | 374 | const sgVec2i xy2 = TextureRepeat(image, xy); |
| 372 | return ReadTexture(texture, xy2); | 375 | return ReadImageFloat(image, xy2); |
| 373 | } | 376 | } |
| 374 | 377 | ||
| 375 | static inline sgPixel FilterBilinear(const sgImage* texture, sgVec2 uv) { | 378 | static inline sgColour4 FilterBilinear(const sgImage* image, sgVec2 uv) { |
| 376 | assert(texture); | 379 | assert(image); |
| 377 | assert(texture->pixels); | 380 | assert(image->pixels); |
| 378 | #define ADDR(x,y) TextureRepeat(texture, (sgVec2i){x,y}) | 381 | #define ADDR(x,y) TextureRepeat(image, (sgVec2i){x,y}) |
| 379 | const sgVec2 uv01 = mod2(uv, 1.f); | 382 | const sgVec2 uv01 = mod2(uv, 1.f); |
| 380 | // Find the closest grid vertex, then interpolate the 4 neighbouring pixel | 383 | // Find the closest grid vertex, then interpolate the 4 neighbouring pixel |
| 381 | // centers. | 384 | // centers. |
| 382 | const sgVec2i tl = UvToIndex(texture, uv01); | 385 | const sgVec2i tl = UvToIndex(image, uv01); |
| 383 | const sgVec2i tr = ADDR(tl.x+1, tl.y); | 386 | const sgVec2i tr = ADDR(tl.x+1, tl.y); |
| 384 | const sgVec2i bl = ADDR(tl.x, tl.y+1); | 387 | const sgVec2i bl = ADDR(tl.x, tl.y+1); |
| 385 | const sgVec2i br = ADDR(tl.x+1, tl.y+1); | 388 | const sgVec2i br = ADDR(tl.x+1, tl.y+1); |
| 386 | const sgVec2 t = frac2(uv01); | 389 | const sgVec2 t = frac2(uv01); |
| 387 | const sgVec4 tl_pix = ReadTextureFloat(texture, tl); | 390 | const sgVec4 tl_pix = ReadImageFloat(image, tl); |
| 388 | const sgVec4 tr_pix = ReadTextureFloat(texture, tr); | 391 | const sgVec4 tr_pix = ReadImageFloat(image, tr); |
| 389 | const sgVec4 bl_pix = ReadTextureFloat(texture, bl); | 392 | const sgVec4 bl_pix = ReadImageFloat(image, bl); |
| 390 | const sgVec4 br_pix = ReadTextureFloat(texture, br); | 393 | const sgVec4 br_pix = ReadImageFloat(image, br); |
| 391 | const sgVec4 x1 = lerp4(tl_pix, tr_pix, t.x); | 394 | const sgVec4 x1 = lerp4(tl_pix, tr_pix, t.x); |
| 392 | const sgVec4 x2 = lerp4(bl_pix, br_pix, t.x); | 395 | const sgVec4 x2 = lerp4(bl_pix, br_pix, t.x); |
| 393 | const sgVec4 y = lerp4(x1, x2, t.y); | 396 | const sgVec4 y = lerp4(x1, x2, t.y); |
| 394 | return Vec4ToPixel(y); | 397 | return y; |
| 395 | } | 398 | } |
| 396 | 399 | ||
| 397 | // TODO: Mipmapping. | 400 | // TODO: Mipmapping. |
| 398 | // TODO: Clamping and other addressing strategies. | 401 | // TODO: Clamping and other addressing strategies. |
| 399 | static inline sgPixel Sample(const sgImage* texture, sgTextureFilter filter, sgVec2 uv) { | 402 | static inline sgColour4 Sample(const sgTexture* texture, sgVec2 uv) { |
| 400 | switch (filter) { | 403 | assert(texture); |
| 401 | case sgNearest: return FilterNearest(texture, uv); | 404 | switch (texture->filter) { |
| 402 | case sgBilinear: return FilterBilinear(texture, uv); | 405 | case sgNearest: return FilterNearest(texture->image, uv); |
| 403 | default: assert(false); return (sgPixel){0}; | 406 | case sgBilinear: return FilterBilinear(texture->image, uv); |
| 407 | default: assert(false); return (sgColour4){0}; | ||
| 404 | } | 408 | } |
| 405 | } | 409 | } |
| 406 | 410 | ||
| @@ -473,7 +477,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) { | |||
| 473 | if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { | 477 | if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { |
| 474 | assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); | 478 | assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); |
| 475 | const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); | 479 | const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); |
| 476 | const sgPixel colour = Sample(texture->image, texture->filter, uv); | 480 | const sgColour4 colour = Sample(texture, uv); |
| 477 | SetPixelColour(gfx, (sgVec2i){x,y}, colour); | 481 | SetPixelColour(gfx, (sgVec2i){x,y}, colour); |
| 478 | } | 482 | } |
| 479 | } | 483 | } |
| @@ -754,11 +758,11 @@ static void* Alloc(void** ppMem, size_t count, size_t size) { | |||
| 754 | size_t sgMem(int width, int height) { | 758 | size_t sgMem(int width, int height) { |
| 755 | const int N = width * height; | 759 | const int N = width * height; |
| 756 | return Align(sizeof(swgfx)) + | 760 | return Align(sizeof(swgfx)) + |
| 757 | Align(N * sizeof(sgPixel)) + // Colour buffer. | 761 | Align(N * sizeof(sgColour4)) + // Colour buffer. |
| 758 | Align(N * sizeof(R)) + // Depth buffer. | 762 | Align(N * sizeof(R)) + // Depth buffer. |
| 759 | Align(N * sizeof(sgTextureId)) + // Texture ID buffer. | 763 | Align(N * sizeof(sgTextureId)) + // Texture ID buffer. |
| 760 | Align(N * sizeof(sgVec2)) + // Texture coords buffer. | 764 | Align(N * sizeof(sgVec2)) + // Texture coords buffer. |
| 761 | Align(N * sizeof(sgPixel)) + // Albedo buffer. | 765 | Align(N * sizeof(sgColour3)) + // Albedo buffer. |
| 762 | Align(N * sizeof(sgVec3)) + // Normals buffer. | 766 | Align(N * sizeof(sgVec3)) + // Normals buffer. |
| 763 | Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register. | 767 | Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register. |
| 764 | (SG_ALIGN - 1); // To make room to align allocations within the buffer. | 768 | (SG_ALIGN - 1); // To make room to align allocations within the buffer. |
| @@ -769,11 +773,11 @@ swgfx* sgNew(int width, int height, void* mem) { | |||
| 769 | void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem(). | 773 | void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem(). |
| 770 | swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); | 774 | swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); |
| 771 | gfx->dims = (sgVec2i){width, height}; | 775 | gfx->dims = (sgVec2i){width, height}; |
| 772 | gfx->colour = SG_ALLOC(&aligned, N, sgPixel); | 776 | gfx->colour = SG_ALLOC(&aligned, N, sgColour4); |
| 773 | gfx->depth = SG_ALLOC(&aligned, N, R); | 777 | gfx->depth = SG_ALLOC(&aligned, N, R); |
| 774 | gfx->texture = SG_ALLOC(&aligned, N, sgTextureId); | 778 | gfx->texture = SG_ALLOC(&aligned, N, sgTextureId); |
| 775 | gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); | 779 | gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); |
| 776 | gfx->albedo = SG_ALLOC(&aligned, N, sgPixel); | 780 | gfx->albedo = SG_ALLOC(&aligned, N, sgColour3); |
| 777 | gfx->normals = SG_ALLOC(&aligned, N, sgVec3); | 781 | gfx->normals = SG_ALLOC(&aligned, N, sgVec3); |
| 778 | gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); | 782 | gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); |
| 779 | gfx->activeTexture = DefaultTextureId; | 783 | gfx->activeTexture = DefaultTextureId; |
| @@ -797,7 +801,7 @@ void sgDel(swgfx** ppSwgfx) { | |||
| 797 | } | 801 | } |
| 798 | } | 802 | } |
| 799 | 803 | ||
| 800 | sgPixel* sgColourBuffer(swgfx* gfx) { | 804 | sgColour4* sgColourBuffer(swgfx* gfx) { |
| 801 | assert(gfx); | 805 | assert(gfx); |
| 802 | return gfx->colour; | 806 | return gfx->colour; |
| 803 | } | 807 | } |
| @@ -812,19 +816,19 @@ void sgPresent(swgfx* gfx, sgVec2i dimensions, sgScreenPixel* screen) { | |||
| 812 | const int sx = dimensions.x / gfx->dims.x; | 816 | const int sx = dimensions.x / gfx->dims.x; |
| 813 | const int sy = dimensions.y / gfx->dims.y; | 817 | const int sy = dimensions.y / gfx->dims.y; |
| 814 | 818 | ||
| 815 | const sgPixel* src = gfx->colour; | 819 | const sgColour4* src = gfx->colour; |
| 816 | 820 | ||
| 817 | for (int y = 0; y < gfx->dims.y; ++y, src += gfx->dims.x) { | 821 | for (int y = 0; y < gfx->dims.y; ++y, src += gfx->dims.x) { |
| 818 | // Replicate each row 'sy' times. | 822 | // Replicate each row 'sy' times. |
| 819 | for (int yy = 0; yy < sy; ++yy) { | 823 | for (int yy = 0; yy < sy; ++yy) { |
| 820 | const sgPixel* src_col = src; | 824 | const sgColour4* src_col = src; |
| 821 | for (int x = 0; x < gfx->dims.x; ++x, ++src_col) { | 825 | for (int x = 0; x < gfx->dims.x; ++x, ++src_col) { |
| 822 | // Replicate each column 'sx' times. | 826 | // Replicate each column 'sx' times. |
| 823 | for (int xx = 0; xx < sx; ++xx, ++screen) { | 827 | for (int xx = 0; xx < sx; ++xx, ++screen) { |
| 824 | screen->r = src_col->r; | 828 | screen->r = ColourToUint(src_col->r); |
| 825 | screen->g = src_col->g; | 829 | screen->g = ColourToUint(src_col->g); |
| 826 | screen->b = src_col->b; | 830 | screen->b = ColourToUint(src_col->b); |
| 827 | screen->a = src_col->a; | 831 | screen->a = ColourToUint(src_col->a); |
| 828 | } | 832 | } |
| 829 | } | 833 | } |
| 830 | } | 834 | } |
| @@ -907,7 +911,7 @@ void sgClear(swgfx* gfx) { | |||
| 907 | } | 911 | } |
| 908 | } | 912 | } |
| 909 | 913 | ||
| 910 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { | 914 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgColour4 colour) { |
| 911 | assert(gfx); | 915 | assert(gfx); |
| 912 | for (size_t i = 0; i < count; ++i) { | 916 | for (size_t i = 0; i < count; ++i) { |
| 913 | SetPixelColour(gfx, positions[i], colour); | 917 | SetPixelColour(gfx, positions[i], colour); |
| @@ -984,14 +988,6 @@ void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tr | |||
| 984 | } | 988 | } |
| 985 | } | 989 | } |
| 986 | 990 | ||
| 987 | static void ImageExp(sgPixel* pixels, int width, int height, R exp) { | ||
| 988 | assert(pixels); | ||
| 989 | for (int i = 0; i < width * height; ++i) { | ||
| 990 | sgPixel* p = &pixels[i]; | ||
| 991 | *p = Vec3ToPixel(exp3(PixelToVec3(*p), exp), p->a); | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | void sgLighting(swgfx* gfx) { | 991 | void sgLighting(swgfx* gfx) { |
| 996 | assert(gfx); | 992 | assert(gfx); |
| 997 | const int N = gfx->dims.x * gfx->dims.y; | 993 | const int N = gfx->dims.x * gfx->dims.y; |
| @@ -1001,21 +997,40 @@ void sgLighting(swgfx* gfx) { | |||
| 1001 | const sgTextureId texid = gfx->texture[i]; | 997 | const sgTextureId texid = gfx->texture[i]; |
| 1002 | const sgTexture* texture = &gfx->textureRegister[texid]; | 998 | const sgTexture* texture = &gfx->textureRegister[texid]; |
| 1003 | const sgVec2 uv = gfx->texcoords[i]; | 999 | const sgVec2 uv = gfx->texcoords[i]; |
| 1004 | sgPixel* albedo = &gfx->albedo[i]; | 1000 | sgColour3* albedo = &gfx->albedo[i]; |
| 1005 | *albedo = Sample(texture->image, texture->filter, uv); | 1001 | *albedo = Vec3FromVec4(Sample(texture, uv)); |
| 1006 | } | 1002 | } |
| 1007 | } | 1003 | } |
| 1008 | } | 1004 | } |
| 1009 | 1005 | ||
| 1010 | void sgAmbient(swgfx* gfx, sgVec3 ambient) { | 1006 | void sgAmbient(swgfx* gfx, sgColour3 ambient) { |
| 1011 | assert(gfx); | 1007 | assert(gfx); |
| 1012 | const int N = gfx->dims.x * gfx->dims.y; | 1008 | const int N = gfx->dims.x * gfx->dims.y; |
| 1013 | for (int i = 0; i < N; ++i) { | 1009 | for (int i = 0; i < N; ++i) { |
| 1014 | const R depth = gfx->depth[i]; | 1010 | const R depth = gfx->depth[i]; |
| 1015 | if (depth != DepthClearValue) { | 1011 | if (depth != DepthClearValue) { |
| 1016 | const sgPixel* albedo = &gfx->albedo[i]; | 1012 | const sgColour3* albedo = &gfx->albedo[i]; |
| 1017 | sgPixel* colour = &gfx->colour[i]; | 1013 | sgColour4* colour = &gfx->colour[i]; |
| 1018 | *colour = Vec3ToPixel(mul3(PixelToVec3(*albedo), ambient), (R)albedo->a/255.f); | 1014 | *colour = Vec4FromVec3(mul3(*albedo, ambient), 1.f); |
| 1015 | } | ||
| 1016 | } | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | void sgDirectional(swgfx* gfx, sgColour3 lightColour, sgVec3 direction) { | ||
| 1020 | assert(gfx); | ||
| 1021 | const sgVec3 L = neg3(direction); | ||
| 1022 | const int N = gfx->dims.x * gfx->dims.y; | ||
| 1023 | for (int i = 0; i < N; ++i) { | ||
| 1024 | const R depth = gfx->depth[i]; | ||
| 1025 | if (depth != DepthClearValue) { | ||
| 1026 | const sgColour3* albedo = &gfx->albedo[i]; | ||
| 1027 | const sgVec3* normal = &gfx->normals[i]; | ||
| 1028 | const R NdotL = fmaxf(0.f, dot3(*normal, L)); | ||
| 1029 | sgColour4* colour = &gfx->colour[i]; | ||
| 1030 | // TODO: Here and in sgAmbient() we need to accumulate colour, so this | ||
| 1031 | // should be +=. But colour also needs to be sgVec4, not sgPixel. We | ||
| 1032 | // should transform sgVec4 to sgPixel colour on sgPresent() instead. | ||
| 1033 | *colour = Vec4FromVec3(scale3(mul3(*albedo, lightColour), NdotL), 1.f); | ||
| 1019 | } | 1034 | } |
| 1020 | } | 1035 | } |
| 1021 | } | 1036 | } |
| @@ -1026,9 +1041,9 @@ void sgDepth(swgfx* gfx) { | |||
| 1026 | for (int i = 0; i < N; ++i) { | 1041 | for (int i = 0; i < N; ++i) { |
| 1027 | const R depth = gfx->depth[i]; | 1042 | const R depth = gfx->depth[i]; |
| 1028 | if (depth != DepthClearValue) { | 1043 | if (depth != DepthClearValue) { |
| 1029 | sgPixel* colour = &gfx->colour[i]; | 1044 | sgColour4* colour = &gfx->colour[i]; |
| 1030 | const R d = gfx->depth[i]; | 1045 | const R d = gfx->depth[i]; |
| 1031 | *colour = Vec3ToPixel((sgVec3){d,d,d}, 1.f); | 1046 | *colour = (sgColour4){d,d,d, 1.f}; |
| 1032 | } | 1047 | } |
| 1033 | } | 1048 | } |
| 1034 | } | 1049 | } |
| @@ -1039,23 +1054,31 @@ void sgNormals(swgfx* gfx) { | |||
| 1039 | for (int i = 0; i < N; ++i) { | 1054 | for (int i = 0; i < N; ++i) { |
| 1040 | const R depth = gfx->depth[i]; | 1055 | const R depth = gfx->depth[i]; |
| 1041 | if (depth != DepthClearValue) { | 1056 | if (depth != DepthClearValue) { |
| 1042 | sgPixel* colour = &gfx->colour[i]; | 1057 | sgColour4* colour = &gfx->colour[i]; |
| 1043 | const sgVec3* normal = &gfx->normals[i]; | 1058 | const sgVec3* normal = &gfx->normals[i]; |
| 1044 | *colour = Vec3ToPixel(max3(Zero3, *normal), 1.f); | 1059 | *colour = Vec4FromVec3(max3(Zero3, *normal), 1.f); |
| 1045 | } | 1060 | } |
| 1046 | } | 1061 | } |
| 1047 | } | 1062 | } |
| 1048 | 1063 | ||
| 1049 | void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) { | 1064 | void sgGamma(swgfx* gfx, sgTexel* texels, int width, int height) { |
| 1050 | assert(gfx); | 1065 | assert(gfx); |
| 1051 | assert(pixels); | 1066 | assert(texels); |
| 1052 | ImageExp(pixels, width, height, 2.2f); | 1067 | constexpr R exp = 2.2f; |
| 1068 | for (int i = 0; i < width * height; ++i) { | ||
| 1069 | sgTexel* const p = &texels[i]; | ||
| 1070 | *p = Colour3ToTexel(exp3(TexelToColour3(*p), exp), p->a); | ||
| 1071 | } | ||
| 1053 | } | 1072 | } |
| 1054 | 1073 | ||
| 1055 | void sgGammaInv(swgfx* gfx, sgPixel* pixels, int width, int height) { | 1074 | void sgGammaInv(swgfx* gfx, sgColour4* pixels, int width, int height) { |
| 1056 | assert(gfx); | 1075 | assert(gfx); |
| 1057 | assert(pixels); | 1076 | assert(pixels); |
| 1058 | ImageExp(pixels, width, height, 1.0f/2.2f); | 1077 | constexpr R exp = 1.0f/2.2f; |
| 1078 | for (int i = 0; i < width * height; ++i) { | ||
| 1079 | sgColour4* const p = &pixels[i]; | ||
| 1080 | *p = Vec4FromVec3(exp3(Vec3FromVec4(*p), exp), p->a); | ||
| 1081 | } | ||
| 1059 | } | 1082 | } |
| 1060 | 1083 | ||
| 1061 | sgCounters sgGetCounters(const swgfx* gfx) { | 1084 | sgCounters sgGetCounters(const swgfx* gfx) { |
