From 1ae1b370da0ce0cf8501b9e6be8c6ac25e249e1a Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Wed, 31 Dec 2025 14:46:33 -0800 Subject: Initial texturing changes --- include/swgfx.h | 16 +++++++++++++-- src/swgfx.c | 61 +++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/include/swgfx.h b/include/swgfx.h index 970e3fa..4a98116 100644 --- a/include/swgfx.h +++ b/include/swgfx.h @@ -38,8 +38,18 @@ typedef uint16_t sgIdx; typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; typedef struct sgTriIdx { sgVertIdx v0, v1, v2; } sgTriIdx; +typedef struct sgBgra { uint8_t b, g, r, a; } sgBgra; +typedef struct sgRgba { uint8_t r, g, b, a; } sgRgba; // TODO: Should we use real-valued colours? -typedef struct sgPixel { uint8_t r, g, b, a; } sgPixel; +typedef sgRgba sgPixel; +// TODO: Expose a macro to control the desired surface format. +typedef sgBgra sgScreenPixel; + +typedef struct sgTexture_t { + int width; + int height; + sgPixel* pixels; +} sgTexture_t; typedef struct swgfx swgfx; @@ -49,7 +59,7 @@ void sgDel(swgfx**); // TODO: Write client app first, then implement the functions below in the C file. -void sgPresent(swgfx*, sgVec2i dimensions, sgPixel* screen); +void sgPresent(swgfx*, sgVec2i dimensions, sgScreenPixel* screen); void sgModelId (swgfx*); void sgModel (swgfx*, sgVec3 position, sgVec3 right, sgVec3 up, sgVec3 forward); @@ -58,6 +68,8 @@ void sgOrtho (swgfx*, R left, R right, R top, R bottom, R near, R far); void sgPerspective(swgfx*, R fovy, R aspect, R near, R far); void sgViewport (swgfx*, int x0, int y0, int width, int height); +void sgTexture(swgfx*, const sgTexture_t*); + void sgClear(swgfx*); void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour); void sgQuads (swgfx*, size_t count, const sgQuad*); diff --git a/src/swgfx.c b/src/swgfx.c index c298fb4..612f82d 100644 --- a/src/swgfx.c +++ b/src/swgfx.c @@ -46,6 +46,9 @@ typedef struct swgfx { // before rendering the model's triangles. sgMat4 viewProj; // View-projection matrix. sgMat4 mvp; // Model-view-projection matrix. + const sgTexture_t* texture;// User-specified texture. + sgTexture_t defaultTexture; // A default for when no texture is provided. + sgPixel defaultPixel; // The single-pixel of the default texture. } swgfx; static inline R rmin(R a, R b) { return (a <= b) ? a : b; } @@ -265,6 +268,22 @@ void SetDepth(swgfx* gfx, const sgVec2i p, R depth) { *Depth(gfx, p.x, p.y) = depth; } +// TODO: Mipmapping. +sgPixel Sample(const sgTexture_t* texture, sgVec2 uv) { + assert(texture); + assert(texture->pixels); + // TODO: (1/2, 1/2) is the center of the pixel. Do we need to do something + // about it here? +#define INDEX(X,Y) texture->pixels[(Y) * texture->width + (X)] + // Doing a nearest sample for now. TODO: Other sampling strategies. + const int x = (int)(uv.x * (R)texture->width); + const int y = (int)(uv.y * (R)texture->height); + // Repeat for now. TODO: Clamping and other strategies. + const int xx = x % texture->width; + const int yy = y % texture->height; + return INDEX(xx,yy); +} + static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { return (sgAABB2){.pmin = min2(min2(p0, p1), p2), .pmax = max2(max2(p0, p1), p2)}; @@ -340,12 +359,16 @@ static void DrawTriangle2(swgfx* gfx, const sgTri3* const tri) { R* depth = Depth(gfx, x, y); if ((0.f <= z) && (z <= 1.f) && (z <= *depth)) { *depth = z; - const sgVec3 depths = (sgVec3){tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z}; - const sgVec2 uv = PerspectiveInterp2(bar, depths, z, tri->p0.uv, tri->p1.uv, tri->p2.uv); - const int r = (int)(uv.x * 255.f); + const sgVec3 depths = (sgVec3){tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z}; + const sgVec2 uv = PerspectiveInterp2(bar, depths, z, tri->p0.uv, tri->p1.uv, tri->p2.uv); + const sgPixel colour = Sample(gfx->texture, uv); + //const sgPixel colour = (sgPixel){255, 0, 255, 255}; + // TODO: When doing lighting, need to tone-map here. + /*const int r = (int)(uv.x * 255.f); const int g = (int)(uv.y * 255.f); - const sgVec2i pix = (sgVec2i){(int)x, (int)y}; - SetPixel(gfx, pix, (sgPixel){r, g, 255, 255}); + const sgPixel colour = (sgPixel){r, g, 255, 255};*/ + const sgVec2i pix = (sgVec2i){x,y}; + SetPixel(gfx, pix, colour); } } } @@ -436,6 +459,12 @@ swgfx* sgNew(int width, int height, void* mem) { gfx->dims = (sgVec2i){width, height}; gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); gfx->depth = SG_ALLOC(&aligned, width * height, R); + gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; + gfx->defaultTexture = (sgTexture_t){ + .width = 1, + .height = 1, + .pixels = &gfx->defaultPixel, + }; return gfx; } @@ -446,7 +475,7 @@ void sgDel(swgfx** ppSwgfx) { } } -void sgPresent(swgfx* gfx, sgVec2i dimensions, sgPixel* screen) { +void sgPresent(swgfx* gfx, sgVec2i dimensions, sgScreenPixel* screen) { assert(gfx); assert(screen); // Integer scaling only. @@ -457,16 +486,18 @@ void sgPresent(swgfx* gfx, sgVec2i dimensions, sgPixel* screen) { const int sy = dimensions.y / gfx->dims.y; const sgPixel* src = gfx->colour; - sgPixel* dst = screen; - // Replicate each row 'sy' times. for (int y = 0; y < gfx->dims.y; ++y, src += gfx->dims.x) { - for (int yy = y*sy; yy < (y+1)*sy; ++yy) { - // Replicate each column 'sx' times. + // Replicate each row 'sy' times. + for (int yy = 0; yy < sy; ++yy) { const sgPixel* src_col = src; for (int x = 0; x < gfx->dims.x; ++x, ++src_col) { - for (int xx = x*sx; xx < (x+1)*sx; ++xx, ++dst) { - *dst = *src_col; + // Replicate each column 'sx' times. + for (int xx = 0; xx < sx; ++xx, ++screen) { + screen->r = src_col->r; + screen->g = src_col->g; + screen->b = src_col->b; + screen->a = src_col->a; } } } @@ -518,6 +549,12 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { gfx->viewport = (sgViewport_t){x0, y0, width, height}; } +void sgTexture(swgfx* gfx, const sgTexture_t* texture) { + assert(gfx); + assert(texture); + gfx->texture = texture; +} + void sgClear(swgfx* gfx) { assert(gfx); const int N = gfx->dims.x * gfx->dims.y; -- cgit v1.2.3