summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/swgfx.c89
1 files changed, 59 insertions, 30 deletions
diff --git a/src/swgfx.c b/src/swgfx.c
index 78d5434..8663b62 100644
--- a/src/swgfx.c
+++ b/src/swgfx.c
@@ -19,7 +19,8 @@ Coordinate systems:
19#include <stdint.h> 19#include <stdint.h>
20#include <string.h> 20#include <string.h>
21 21
22 22static constexpr sgTextureId DefaultTextureId = SWGFX_MAX_TEXTURES;
23static constexpr size_t SWGFX_TEXTURE_REGISTER_SIZE = SWGFX_MAX_TEXTURES + 1;
23 24
24static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; 25static constexpr sgVec3 Up3 = (sgVec3){0,1,0};
25 26
@@ -31,14 +32,21 @@ typedef struct sgMat4 {
31 R val[4][4]; // (col, row) 32 R val[4][4]; // (col, row)
32} sgMat4; 33} sgMat4;
33 34
35typedef struct sgTexture {
36 const sgImage* image;
37 sgTextureFilter filter;
38} sgTexture;
39
34typedef struct swgfx { 40typedef struct swgfx {
35 sgVec2i dims; // Colour buffer dimensions. 41 sgVec2i dims; // Colour buffer dimensions.
36 sgPixel* colour; // Colour buffer. 42 sgPixel* colour; // Colour buffer.
37 R* depth; // Depth buffer. 43 R* depth; // Depth buffer.
38 sgViewport_t viewport; 44 sgTextureId* textureId; // Texture ID buffer.
39 sgMat4 model; // Model matrix. 45 sgVec2* texcoords; // Texture coords buffer.
40 sgMat4 view; // View matrix. 46 sgViewport_t viewport;
41 sgMat4 proj; // Projection matrix. 47 sgMat4 model; // Model matrix.
48 sgMat4 view; // View matrix.
49 sgMat4 proj; // Projection matrix.
42 // Pre-multiplied matrices. 50 // Pre-multiplied matrices.
43 // The model matrix changes once per object, more frequently than view or 51 // The model matrix changes once per object, more frequently than view or
44 // projection. View and projection are expected to change infrequently, maybe 52 // projection. View and projection are expected to change infrequently, maybe
@@ -46,12 +54,12 @@ typedef struct swgfx {
46 // Make it so that changing the model matrix only requires one matrix 54 // Make it so that changing the model matrix only requires one matrix
47 // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection) 55 // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection)
48 // before rendering the model's triangles. 56 // before rendering the model's triangles.
49 sgMat4 viewProj; // View-projection matrix. 57 sgMat4 viewProj; // View-projection matrix.
50 sgMat4 mvp; // Model-view-projection matrix. 58 sgMat4 mvp; // Model-view-projection matrix.
51 const sgImage* texture; // User-specified texture. 59 sgTexture* textureRegister; // Indexed by texture id.
52 sgTextureFilter textureFilter; // Filter method for the texture. 60 sgTextureId activeTexture;
53 sgImage defaultTexture; // A default for when no texture is provided. 61 sgPixel defaultPixel; // The single-pixel of the default texture.
54 sgPixel defaultPixel; // The single-pixel of the default texture. 62 sgImage defaultImage; // Image for the default texture.
55 sgCounters counters; 63 sgCounters counters;
56} swgfx; 64} swgfx;
57 65
@@ -435,6 +443,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) {
435 // Clip to screen space. 443 // Clip to screen space.
436 pmin = Clip(gfx, pmin); 444 pmin = Clip(gfx, pmin);
437 pmax = Clip(gfx, pmax); 445 pmax = Clip(gfx, pmax);
446 const sgTexture* texture = &gfx->textureRegister[gfx->activeTexture];
438 // Draw. 447 // Draw.
439 for (int y = pmin.y; y <= pmax.y; ++y) { 448 for (int y = pmin.y; y <= pmax.y; ++y) {
440 for (int x = pmin.x; x <= pmax.x; ++x) { 449 for (int x = pmin.x; x <= pmax.x; ++x) {
@@ -449,7 +458,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) {
449 if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { 458 if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) {
450 assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); 459 assert((bar.x + bar.y + bar.z - 1e7) <= 1.f);
451 const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); 460 const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv);
452 const sgPixel colour = Sample(gfx->texture, gfx->textureFilter, uv); 461 const sgPixel colour = Sample(texture->image, texture->filter, uv);
453 SetPixel(gfx, (sgVec2i){x,y}, colour); 462 SetPixel(gfx, (sgVec2i){x,y}, colour);
454 } 463 }
455 } 464 }
@@ -641,6 +650,7 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) {
641 const sgVec3 one_over_zs = (sgVec3){1.f / p0.w, 1.f / p1.w, 1.f/ p2.w}; 650 const sgVec3 one_over_zs = (sgVec3){1.f / p0.w, 1.f / p1.w, 1.f/ p2.w};
642 const sgVec3 u_over_zs = (sgVec3){tri->p0.uv.x / p0.w, tri->p1.uv.x / p1.w, tri->p2.uv.x / p2.w}; 651 const sgVec3 u_over_zs = (sgVec3){tri->p0.uv.x / p0.w, tri->p1.uv.x / p1.w, tri->p2.uv.x / p2.w};
643 const sgVec3 v_over_zs = (sgVec3){tri->p0.uv.y / p0.w, tri->p1.uv.y / p1.w, tri->p2.uv.y / p2.w}; 652 const sgVec3 v_over_zs = (sgVec3){tri->p0.uv.y / p0.w, tri->p1.uv.y / p1.w, tri->p2.uv.y / p2.w};
653 const sgTexture* texture = &gfx->textureRegister[gfx->activeTexture];
644 // Draw. 654 // Draw.
645 for (int y = pmin.y; y <= pmax.y; ++y) { 655 for (int y = pmin.y; y <= pmax.y; ++y) {
646 for (int x = pmin.x; x <= pmax.x; ++x) { 656 for (int x = pmin.x; x <= pmax.x; ++x) {
@@ -663,7 +673,7 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) {
663 R* depth = Depth(gfx, x, y); 673 R* depth = Depth(gfx, x, y);
664 if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { 674 if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) {
665 *depth = p_depth; 675 *depth = p_depth;
666 const sgPixel colour = Sample(gfx->texture, gfx->textureFilter, uv); 676 const sgPixel colour = Sample(texture->image, texture->filter, uv);
667 // TODO: When doing lighting, need to tone-map here and apply inverse 677 // TODO: When doing lighting, need to tone-map here and apply inverse
668 // gamma here. 678 // gamma here.
669 //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; 679 //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255};
@@ -719,25 +729,37 @@ static void* Alloc(void** ppMem, size_t count, size_t size) {
719} 729}
720 730
721size_t sgMem(int width, int height) { 731size_t sgMem(int width, int height) {
732 const int N = width * height;
722 return Align(sizeof(swgfx)) + 733 return Align(sizeof(swgfx)) +
723 Align(width * height * sizeof(sgPixel)) + 734 Align(N * sizeof(sgPixel)) + // Colour buffer.
724 Align(width * height * sizeof(R)) + 735 Align(N * sizeof(R)) + // Depth buffer.
736 Align(N * sizeof(sgTextureId)) + // Texture ID buffer.
737 Align(N * sizeof(sgVec2)) + // Texture coords buffer.
738 Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register.
725 (SG_ALIGN - 1); // To make room to align allocations within the buffer. 739 (SG_ALIGN - 1); // To make room to align allocations within the buffer.
726} 740}
727 741
728swgfx* sgNew(int width, int height, void* mem) { 742swgfx* sgNew(int width, int height, void* mem) {
729 void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem(). 743 const int N = width * height;
730 swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); 744 void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem().
731 gfx->dims = (sgVec2i){width, height}; 745 swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx);
732 gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); 746 gfx->dims = (sgVec2i){width, height};
733 gfx->depth = SG_ALLOC(&aligned, width * height, R); 747 gfx->colour = SG_ALLOC(&aligned, N, sgPixel);
734 gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; 748 gfx->depth = SG_ALLOC(&aligned, N, R);
735 gfx->defaultTexture = (sgImage){ 749 gfx->textureId = SG_ALLOC(&aligned, N, sgTextureId);
750 gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2);
751 gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture);
752 gfx->activeTexture = DefaultTextureId;
753 gfx->defaultPixel = (sgPixel){255, 255, 255, 255};
754 gfx->defaultImage = (sgImage){
736 .width = 1, 755 .width = 1,
737 .height = 1, 756 .height = 1,
738 .pixels = &gfx->defaultPixel, 757 .pixels = &gfx->defaultPixel,
739 }; 758 };
740 gfx->texture = &gfx->defaultTexture; 759 gfx->textureRegister[DefaultTextureId] = (sgTexture){
760 .image = &gfx->defaultImage,
761 .filter = sgNearest,
762 };
741 return gfx; 763 return gfx;
742} 764}
743 765
@@ -831,11 +853,18 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) {
831 gfx->viewport = (sgViewport_t){x0, y0, width, height}; 853 gfx->viewport = (sgViewport_t){x0, y0, width, height};
832} 854}
833 855
834void sgTexture(swgfx* gfx, const sgImage* texture, sgTextureFilter filter) { 856void sgTextureRegister(swgfx* gfx, sgTextureId id, const sgImage* image, sgTextureFilter filter) {
835 assert(gfx); 857 assert(gfx);
836 assert(texture); 858 assert(id < SWGFX_MAX_TEXTURES);
837 gfx->texture = texture; 859 assert(id != DefaultTextureId);
838 gfx->textureFilter = filter; 860 assert(image);
861 gfx->textureRegister[id] = (sgTexture){image, filter};
862}
863
864void sgTextureActivate(swgfx* gfx, sgTextureId id) {
865 assert(gfx);
866 assert(id < SWGFX_MAX_TEXTURES);
867 gfx->activeTexture = id;
839} 868}
840 869
841void sgClear(swgfx* gfx) { 870void sgClear(swgfx* gfx) {