summaryrefslogtreecommitdiff
path: root/src/swgfx.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2026-02-14 19:06:49 -0800
committer3gg <3gg@shellblade.net>2026-02-14 19:06:49 -0800
commite0dd69febc8f73b0e39e14d070ecf6b73bbe2d4f (patch)
tree51b438b9447b50cdbc6dd6c2ae2ce42a384acfe6 /src/swgfx.c
parentb0544549c551dfa0b52e7c685580f954861240ba (diff)
Use float colour. First step towards directional lighting
Diffstat (limited to 'src/swgfx.c')
-rw-r--r--src/swgfx.c191
1 files changed, 107 insertions, 84 deletions
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
43typedef struct swgfx { 43typedef 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
136static inline sgVec2 Vec2FromVec4(sgVec4 v) { return (sgVec2){v.x, v.y}; } 136static inline sgVec3 Vec3FromVec4(sgVec4 v) { return (sgVec3){v.x, v.y, v.z}; }
137static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } 137static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; }
138 138
139static inline sgMat4 Mat4( 139static 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
291static inline sgVec3 PixelToVec3(sgPixel p) { 291static 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}
294static inline sgPixel Vec3ToPixel(sgVec3 p, R a) { 294static 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}
297static inline sgVec4 PixelToVec4(sgPixel p) { 297static 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}
300static inline sgPixel Vec4ToPixel(sgVec4 p) { 300static 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}
303static 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
311static inline sgPixel* Pixel(swgfx* gfx, int x, int y) { 314static 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
325static inline void SetPixelColour(swgfx* gfx, const sgVec2i p, sgPixel colour) { 328static 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
345static inline sgPixel ReadTexture(const sgImage* texture, sgVec2i xy) { 348static 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].
352static inline sgVec4 ReadTextureFloat(const sgImage* texture, sgVec2i xy) { 355static inline sgColour4 ReadImageFloat(const sgImage* image, sgVec2i xy) {
353 return PixelToVec4(ReadTexture(texture, xy)); 356 return TexelToColour(ReadImage(image, xy));
354} 357}
355 358
356static inline sgVec2i UvToIndex(const sgImage* texture, sgVec2 uv) { 359static 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
363static inline sgVec2i TextureRepeat(const sgImage* texture, sgVec2i p) { 366static 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
367static inline sgPixel FilterNearest(const sgImage* texture, sgVec2 uv) { 370static 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
375static inline sgPixel FilterBilinear(const sgImage* texture, sgVec2 uv) { 378static 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.
399static inline sgPixel Sample(const sgImage* texture, sgTextureFilter filter, sgVec2 uv) { 402static 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) {
754size_t sgMem(int width, int height) { 758size_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
800sgPixel* sgColourBuffer(swgfx* gfx) { 804sgColour4* 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
910void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { 914void 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
987static 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
995void sgLighting(swgfx* gfx) { 991void 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
1010void sgAmbient(swgfx* gfx, sgVec3 ambient) { 1006void 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
1019void 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
1049void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) { 1064void 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
1055void sgGammaInv(swgfx* gfx, sgPixel* pixels, int width, int height) { 1074void 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
1061sgCounters sgGetCounters(const swgfx* gfx) { 1084sgCounters sgGetCounters(const swgfx* gfx) {