diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-31 14:46:33 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-31 14:46:33 -0800 |
| commit | 1ae1b370da0ce0cf8501b9e6be8c6ac25e249e1a (patch) | |
| tree | a9312684a6c616ace9a8d0eb997b239131eb29d4 | |
| parent | 57c6cf991b498aa19cc1cb3d736fec08d3643490 (diff) | |
Initial texturing changes
| -rw-r--r-- | include/swgfx.h | 16 | ||||
| -rw-r--r-- | 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; | |||
| 38 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; | 38 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; |
| 39 | typedef struct sgTriIdx { sgVertIdx v0, v1, v2; } sgTriIdx; | 39 | typedef struct sgTriIdx { sgVertIdx v0, v1, v2; } sgTriIdx; |
| 40 | 40 | ||
| 41 | typedef struct sgBgra { uint8_t b, g, r, a; } sgBgra; | ||
| 42 | typedef struct sgRgba { uint8_t r, g, b, a; } sgRgba; | ||
| 41 | // TODO: Should we use real-valued colours? | 43 | // TODO: Should we use real-valued colours? |
| 42 | typedef struct sgPixel { uint8_t r, g, b, a; } sgPixel; | 44 | typedef sgRgba sgPixel; |
| 45 | // TODO: Expose a macro to control the desired surface format. | ||
| 46 | typedef sgBgra sgScreenPixel; | ||
| 47 | |||
| 48 | typedef struct sgTexture_t { | ||
| 49 | int width; | ||
| 50 | int height; | ||
| 51 | sgPixel* pixels; | ||
| 52 | } sgTexture_t; | ||
| 43 | 53 | ||
| 44 | typedef struct swgfx swgfx; | 54 | typedef struct swgfx swgfx; |
| 45 | 55 | ||
| @@ -49,7 +59,7 @@ void sgDel(swgfx**); | |||
| 49 | 59 | ||
| 50 | // TODO: Write client app first, then implement the functions below in the C file. | 60 | // TODO: Write client app first, then implement the functions below in the C file. |
| 51 | 61 | ||
| 52 | void sgPresent(swgfx*, sgVec2i dimensions, sgPixel* screen); | 62 | void sgPresent(swgfx*, sgVec2i dimensions, sgScreenPixel* screen); |
| 53 | 63 | ||
| 54 | void sgModelId (swgfx*); | 64 | void sgModelId (swgfx*); |
| 55 | void sgModel (swgfx*, sgVec3 position, sgVec3 right, sgVec3 up, sgVec3 forward); | 65 | 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); | |||
| 58 | void sgPerspective(swgfx*, R fovy, R aspect, R near, R far); | 68 | void sgPerspective(swgfx*, R fovy, R aspect, R near, R far); |
| 59 | void sgViewport (swgfx*, int x0, int y0, int width, int height); | 69 | void sgViewport (swgfx*, int x0, int y0, int width, int height); |
| 60 | 70 | ||
| 71 | void sgTexture(swgfx*, const sgTexture_t*); | ||
| 72 | |||
| 61 | void sgClear(swgfx*); | 73 | void sgClear(swgfx*); |
| 62 | void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour); | 74 | void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour); |
| 63 | void sgQuads (swgfx*, size_t count, const sgQuad*); | 75 | 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 { | |||
| 46 | // before rendering the model's triangles. | 46 | // before rendering the model's triangles. |
| 47 | sgMat4 viewProj; // View-projection matrix. | 47 | sgMat4 viewProj; // View-projection matrix. |
| 48 | sgMat4 mvp; // Model-view-projection matrix. | 48 | sgMat4 mvp; // Model-view-projection matrix. |
| 49 | const sgTexture_t* texture;// User-specified texture. | ||
| 50 | sgTexture_t defaultTexture; // A default for when no texture is provided. | ||
| 51 | sgPixel defaultPixel; // The single-pixel of the default texture. | ||
| 49 | } swgfx; | 52 | } swgfx; |
| 50 | 53 | ||
| 51 | static inline R rmin(R a, R b) { return (a <= b) ? a : b; } | 54 | 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) { | |||
| 265 | *Depth(gfx, p.x, p.y) = depth; | 268 | *Depth(gfx, p.x, p.y) = depth; |
| 266 | } | 269 | } |
| 267 | 270 | ||
| 271 | // TODO: Mipmapping. | ||
| 272 | sgPixel Sample(const sgTexture_t* texture, sgVec2 uv) { | ||
| 273 | assert(texture); | ||
| 274 | assert(texture->pixels); | ||
| 275 | // TODO: (1/2, 1/2) is the center of the pixel. Do we need to do something | ||
| 276 | // about it here? | ||
| 277 | #define INDEX(X,Y) texture->pixels[(Y) * texture->width + (X)] | ||
| 278 | // Doing a nearest sample for now. TODO: Other sampling strategies. | ||
| 279 | const int x = (int)(uv.x * (R)texture->width); | ||
| 280 | const int y = (int)(uv.y * (R)texture->height); | ||
| 281 | // Repeat for now. TODO: Clamping and other strategies. | ||
| 282 | const int xx = x % texture->width; | ||
| 283 | const int yy = y % texture->height; | ||
| 284 | return INDEX(xx,yy); | ||
| 285 | } | ||
| 286 | |||
| 268 | static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { | 287 | static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { |
| 269 | return (sgAABB2){.pmin = min2(min2(p0, p1), p2), | 288 | return (sgAABB2){.pmin = min2(min2(p0, p1), p2), |
| 270 | .pmax = max2(max2(p0, p1), p2)}; | 289 | .pmax = max2(max2(p0, p1), p2)}; |
| @@ -340,12 +359,16 @@ static void DrawTriangle2(swgfx* gfx, const sgTri3* const tri) { | |||
| 340 | R* depth = Depth(gfx, x, y); | 359 | R* depth = Depth(gfx, x, y); |
| 341 | if ((0.f <= z) && (z <= 1.f) && (z <= *depth)) { | 360 | if ((0.f <= z) && (z <= 1.f) && (z <= *depth)) { |
| 342 | *depth = z; | 361 | *depth = z; |
| 343 | const sgVec3 depths = (sgVec3){tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z}; | 362 | const sgVec3 depths = (sgVec3){tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z}; |
| 344 | const sgVec2 uv = PerspectiveInterp2(bar, depths, z, tri->p0.uv, tri->p1.uv, tri->p2.uv); | 363 | const sgVec2 uv = PerspectiveInterp2(bar, depths, z, tri->p0.uv, tri->p1.uv, tri->p2.uv); |
| 345 | const int r = (int)(uv.x * 255.f); | 364 | const sgPixel colour = Sample(gfx->texture, uv); |
| 365 | //const sgPixel colour = (sgPixel){255, 0, 255, 255}; | ||
| 366 | // TODO: When doing lighting, need to tone-map here. | ||
| 367 | /*const int r = (int)(uv.x * 255.f); | ||
| 346 | const int g = (int)(uv.y * 255.f); | 368 | const int g = (int)(uv.y * 255.f); |
| 347 | const sgVec2i pix = (sgVec2i){(int)x, (int)y}; | 369 | const sgPixel colour = (sgPixel){r, g, 255, 255};*/ |
| 348 | SetPixel(gfx, pix, (sgPixel){r, g, 255, 255}); | 370 | const sgVec2i pix = (sgVec2i){x,y}; |
| 371 | SetPixel(gfx, pix, colour); | ||
| 349 | } | 372 | } |
| 350 | } | 373 | } |
| 351 | } | 374 | } |
| @@ -436,6 +459,12 @@ swgfx* sgNew(int width, int height, void* mem) { | |||
| 436 | gfx->dims = (sgVec2i){width, height}; | 459 | gfx->dims = (sgVec2i){width, height}; |
| 437 | gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); | 460 | gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); |
| 438 | gfx->depth = SG_ALLOC(&aligned, width * height, R); | 461 | gfx->depth = SG_ALLOC(&aligned, width * height, R); |
| 462 | gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; | ||
| 463 | gfx->defaultTexture = (sgTexture_t){ | ||
| 464 | .width = 1, | ||
| 465 | .height = 1, | ||
| 466 | .pixels = &gfx->defaultPixel, | ||
| 467 | }; | ||
| 439 | return gfx; | 468 | return gfx; |
| 440 | } | 469 | } |
| 441 | 470 | ||
| @@ -446,7 +475,7 @@ void sgDel(swgfx** ppSwgfx) { | |||
| 446 | } | 475 | } |
| 447 | } | 476 | } |
| 448 | 477 | ||
| 449 | void sgPresent(swgfx* gfx, sgVec2i dimensions, sgPixel* screen) { | 478 | void sgPresent(swgfx* gfx, sgVec2i dimensions, sgScreenPixel* screen) { |
| 450 | assert(gfx); | 479 | assert(gfx); |
| 451 | assert(screen); | 480 | assert(screen); |
| 452 | // Integer scaling only. | 481 | // Integer scaling only. |
| @@ -457,16 +486,18 @@ void sgPresent(swgfx* gfx, sgVec2i dimensions, sgPixel* screen) { | |||
| 457 | const int sy = dimensions.y / gfx->dims.y; | 486 | const int sy = dimensions.y / gfx->dims.y; |
| 458 | 487 | ||
| 459 | const sgPixel* src = gfx->colour; | 488 | const sgPixel* src = gfx->colour; |
| 460 | sgPixel* dst = screen; | ||
| 461 | 489 | ||
| 462 | // Replicate each row 'sy' times. | ||
| 463 | for (int y = 0; y < gfx->dims.y; ++y, src += gfx->dims.x) { | 490 | for (int y = 0; y < gfx->dims.y; ++y, src += gfx->dims.x) { |
| 464 | for (int yy = y*sy; yy < (y+1)*sy; ++yy) { | 491 | // Replicate each row 'sy' times. |
| 465 | // Replicate each column 'sx' times. | 492 | for (int yy = 0; yy < sy; ++yy) { |
| 466 | const sgPixel* src_col = src; | 493 | const sgPixel* src_col = src; |
| 467 | for (int x = 0; x < gfx->dims.x; ++x, ++src_col) { | 494 | for (int x = 0; x < gfx->dims.x; ++x, ++src_col) { |
| 468 | for (int xx = x*sx; xx < (x+1)*sx; ++xx, ++dst) { | 495 | // Replicate each column 'sx' times. |
| 469 | *dst = *src_col; | 496 | for (int xx = 0; xx < sx; ++xx, ++screen) { |
| 497 | screen->r = src_col->r; | ||
| 498 | screen->g = src_col->g; | ||
| 499 | screen->b = src_col->b; | ||
| 500 | screen->a = src_col->a; | ||
| 470 | } | 501 | } |
| 471 | } | 502 | } |
| 472 | } | 503 | } |
| @@ -518,6 +549,12 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { | |||
| 518 | gfx->viewport = (sgViewport_t){x0, y0, width, height}; | 549 | gfx->viewport = (sgViewport_t){x0, y0, width, height}; |
| 519 | } | 550 | } |
| 520 | 551 | ||
| 552 | void sgTexture(swgfx* gfx, const sgTexture_t* texture) { | ||
| 553 | assert(gfx); | ||
| 554 | assert(texture); | ||
| 555 | gfx->texture = texture; | ||
| 556 | } | ||
| 557 | |||
| 521 | void sgClear(swgfx* gfx) { | 558 | void sgClear(swgfx* gfx) { |
| 522 | assert(gfx); | 559 | assert(gfx); |
| 523 | const int N = gfx->dims.x * gfx->dims.y; | 560 | const int N = gfx->dims.x * gfx->dims.y; |
