summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/swgfx.c100
1 files changed, 75 insertions, 25 deletions
diff --git a/src/swgfx.c b/src/swgfx.c
index ddd64f7..6074eac 100644
--- a/src/swgfx.c
+++ b/src/swgfx.c
@@ -24,7 +24,8 @@ static constexpr size_t SWGFX_TEXTURE_REGISTER_SIZE = SWGFX_MAX_TEXTURES + 1;
24 24
25static constexpr R DepthClearValue = 1.0f; 25static constexpr R DepthClearValue = 1.0f;
26 26
27static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; 27static constexpr sgVec3 Up3 = (sgVec3){0,1,0};
28static constexpr sgVec3 Zero3 = (sgVec3){0,0,0};
28 29
29typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; 30typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t;
30typedef struct sgAABB2 { sgVec2 pmin, pmax; } sgAABB2; 31typedef struct sgAABB2 { sgVec2 pmin, pmax; } sgAABB2;
@@ -46,6 +47,7 @@ typedef struct swgfx {
46 sgTextureId* texture; // Texture ID buffer. 47 sgTextureId* texture; // Texture ID buffer.
47 sgVec2* texcoords; // Texture coords buffer. 48 sgVec2* texcoords; // Texture coords buffer.
48 sgPixel* albedo; // Albedo buffer. 49 sgPixel* albedo; // Albedo buffer.
50 sgVec3* normals; // Normals buffer.
49 sgViewport_t viewport; 51 sgViewport_t viewport;
50 sgMat4 model; // Model matrix. 52 sgMat4 model; // Model matrix.
51 sgMat4 view; // View matrix. 53 sgMat4 view; // View matrix.
@@ -87,12 +89,14 @@ static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; }
87static inline sgVec2 lerp2(sgVec2 a, sgVec2 b, R t) { return add2(a, scale2(sub2(b,a), t)); } 89static inline sgVec2 lerp2(sgVec2 a, sgVec2 b, R t) { return add2(a, scale2(sub2(b,a), t)); }
88static inline sgVec2 mod2(sgVec2 v, R m) { return (sgVec2){mod1(v.x, m), mod1(v.y, m)}; } 90static inline sgVec2 mod2(sgVec2 v, R m) { return (sgVec2){mod1(v.x, m), mod1(v.y, m)}; }
89 91
92static inline sgVec3 max3(sgVec3 a, sgVec3 b) { return (sgVec3){fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z)}; }
90static inline sgVec3 add3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x + b.x, a.y + b.y, a.z + b.z}; } 93static inline sgVec3 add3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x + b.x, a.y + b.y, a.z + b.z}; }
91static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } 94static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; }
92static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; } 95static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; }
93static inline sgVec3 mul3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x * b.x, a.y * b.y, a.z * b.z}; } 96static inline sgVec3 mul3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x * b.x, a.y * b.y, a.z * b.z}; }
94static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; } 97static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; }
95static inline sgVec3 scale3(sgVec3 v, R s) { return (sgVec3){v.x * s, v.y * s, v.z * s}; } 98static inline sgVec3 scale3(sgVec3 v, R s) { return (sgVec3){v.x * s, v.y * s, v.z * s}; }
99static inline sgVec3 lerp3(sgVec3 a, sgVec3 b, R t) { return add3(a, scale3(sub3(b,a), t)); }
96static inline sgVec3 exp3(sgVec3 v, R exp) { return (sgVec3){powf(v.x, exp), powf(v.y, exp), powf(v.z, exp)};} 100static inline sgVec3 exp3(sgVec3 v, R exp) { return (sgVec3){powf(v.x, exp), powf(v.y, exp), powf(v.z, exp)};}
97static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } 101static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
98static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } 102static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
@@ -326,11 +330,13 @@ static inline void SetPixelColour(swgfx* gfx, const sgVec2i p, sgPixel colour) {
326#endif // SWGFX_PROFILING 330#endif // SWGFX_PROFILING
327} 331}
328 332
329static inline void SetPixelDeferred(swgfx* gfx, const sgVec2i p, R depth, sgTextureId texid, sgVec2 uv) { 333static inline void SetPixelDeferred(swgfx* gfx, const sgVec2i p, R depth, sgTextureId texid, sgVec2 uv, sgVec3 normal) {
330 assert(gfx); 334 assert(gfx);
331 gfx->depth[(p.y * gfx->dims.x) + p.x] = depth; 335 const int i = (p.y * gfx->dims.x) + p.x;
332 gfx->texture[(p.y * gfx->dims.x) + p.x] = texid; 336 gfx->depth[i] = depth;
333 gfx->texcoords[(p.y * gfx->dims.x) + p.x] = uv; 337 gfx->texture[i] = texid;
338 gfx->texcoords[i] = uv;
339 gfx->normals[i] = normalize3(normal); // Non-unit after interpolation.
334#if SWGFX_PROFILING 340#if SWGFX_PROFILING
335 gfx->counters.pixels++; 341 gfx->counters.pixels++;
336#endif // SWGFX_PROFILING 342#endif // SWGFX_PROFILING
@@ -514,7 +520,7 @@ static inline R IntersectSegmentPlane(R near, const sgVec3* const a, const sgVec
514 return t; 520 return t;
515} 521}
516 522
517/// Interpolate depth and vertex attributes at the in/out vertex 'out'. 523/// Interpolate vertex attributes at the in/out vertex 'out'.
518static void InterpolateAttributes(const sgVert4* const a, const sgVert4* const b, R t, sgVert4* out) { 524static void InterpolateAttributes(const sgVert4* const a, const sgVert4* const b, R t, sgVert4* out) {
519 assert(a); 525 assert(a);
520 assert(b); 526 assert(b);
@@ -522,8 +528,9 @@ static void InterpolateAttributes(const sgVert4* const a, const sgVert4* const b
522 assert(t >= 0.f); 528 assert(t >= 0.f);
523 assert(t <= 1.f); 529 assert(t <= 1.f);
524 const sgVec4 d = sub4(b->pos, a->pos); // Line direction. 530 const sgVec4 d = sub4(b->pos, a->pos); // Line direction.
525 out->pos = add4(a->pos, scale4(d, t)); 531 out->pos = add4(a->pos, scale4(d, t));
526 out->uv = lerp2(a->uv, b->uv, t); 532 out->uv = lerp2(a->uv, b->uv, t);
533 out->normal = lerp3(a->normal, b->normal, t);
527} 534}
528 535
529/// Clip a triangle, vertices in clip space. Return the number of output 536/// Clip a triangle, vertices in clip space. Return the number of output
@@ -597,10 +604,18 @@ static inline int TransformTri(const swgfx* gfx, const sgTri3* const tri, sgTri4
597 const sgVec4 p0_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p0.pos, 1)); 604 const sgVec4 p0_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p0.pos, 1));
598 const sgVec4 p1_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p1.pos, 1)); 605 const sgVec4 p1_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p1.pos, 1));
599 const sgVec4 p2_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p2.pos, 1)); 606 const sgVec4 p2_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p2.pos, 1));
607 // Model to world space for normals.
608 // This assumes the model matrix does not have non-uniform scaling.
609 // It seems more convenient to put the normals in world space instead of view
610 // space right now so that we don't have to transform lights to view space
611 // when lighting. But we might want to revisit this later.
612 const sgVec3 n0_view = Mat4MulVec3(gfx->model, tri->p0.normal, 0.f);
613 const sgVec3 n1_view = Mat4MulVec3(gfx->model, tri->p1.normal, 0.f);
614 const sgVec3 n2_view = Mat4MulVec3(gfx->model, tri->p2.normal, 0.f);
600 const sgTri4 tri_clip = { 615 const sgTri4 tri_clip = {
601 (sgVert4){ p0_clip, tri->p0.uv }, 616 (sgVert4){ p0_clip, tri->p0.uv, n0_view },
602 (sgVert4){ p1_clip, tri->p1.uv }, 617 (sgVert4){ p1_clip, tri->p1.uv, n1_view },
603 (sgVert4){ p2_clip, tri->p2.uv }}; 618 (sgVert4){ p2_clip, tri->p2.uv, n2_view }};
604 // Clip. 619 // Clip.
605 // Our perspective matrix maps the near plane to z=-1 in clip space. 620 // Our perspective matrix maps the near plane to z=-1 in clip space.
606 constexpr R near_clip = -1.f; 621 constexpr R near_clip = -1.f;
@@ -673,20 +688,22 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) {
673 if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { 688 if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) {
674 assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); 689 assert((bar.x + bar.y + bar.z - 1e7) <= 1.f);
675 const R p_one_over_z = dot3(bar, one_over_zs); 690 const R p_one_over_z = dot3(bar, one_over_zs);
676 const R p_u_over_z = dot3(bar, u_over_zs);
677 const R p_v_over_z = dot3(bar, v_over_zs);
678 const R p_depth = dot3(bar, depths); 691 const R p_depth = dot3(bar, depths);
679 const R z = 1.f / p_one_over_z; 692 const R z = 1.f / p_one_over_z;
680 const sgVec2 uv = (sgVec2){p_u_over_z * z, p_v_over_z * z};
681 const R* depth = Depth(gfx, x, y); 693 const R* depth = Depth(gfx, x, y);
682 if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { 694 if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) {
683 // TODO: When doing lighting, need to tone-map here and apply inverse 695 const R p_u_over_z = dot3(bar, u_over_zs);
684 // gamma here. 696 const R p_v_over_z = dot3(bar, v_over_zs);
697 const sgVec2 uv = (sgVec2){p_u_over_z * z, p_v_over_z * z};
698 const sgVec3 n0 = scale3(tri->p0.normal, bar.x * one_over_zs.x);
699 const sgVec3 n1 = scale3(tri->p1.normal, bar.y * one_over_zs.y);
700 const sgVec3 n2 = scale3(tri->p2.normal, bar.z * one_over_zs.z);
701 const sgVec3 normal = add3(add3(n0,n1),n2);
685 //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; 702 //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255};
686 //const sgPixel colour = {(int)(z*255.f), (int)(z*255.f), (int)(z*255.f), 255}; 703 //const sgPixel colour = {(int)(z*255.f), (int)(z*255.f), (int)(z*255.f), 255};
687 //const sgPixel colour = {255, 0, 255, 255}; 704 //const sgPixel colour = {255, 0, 255, 255};
688 //const sgPixel colour = {(int)(uv.x * 255.f), (int)(uv.y * 255.f), 255, 255}; 705 //const sgPixel colour = {(int)(uv.x * 255.f), (int)(uv.y * 255.f), 255, 255};
689 SetPixelDeferred(gfx, (sgVec2i){x,y}, p_depth, gfx->activeTexture, uv); 706 SetPixelDeferred(gfx, (sgVec2i){x,y}, p_depth, gfx->activeTexture, uv, normal);
690 } 707 }
691 } 708 }
692 } 709 }
@@ -742,6 +759,7 @@ size_t sgMem(int width, int height) {
742 Align(N * sizeof(sgTextureId)) + // Texture ID buffer. 759 Align(N * sizeof(sgTextureId)) + // Texture ID buffer.
743 Align(N * sizeof(sgVec2)) + // Texture coords buffer. 760 Align(N * sizeof(sgVec2)) + // Texture coords buffer.
744 Align(N * sizeof(sgPixel)) + // Albedo buffer. 761 Align(N * sizeof(sgPixel)) + // Albedo buffer.
762 Align(N * sizeof(sgVec3)) + // Normals buffer.
745 Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register. 763 Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register.
746 (SG_ALIGN - 1); // To make room to align allocations within the buffer. 764 (SG_ALIGN - 1); // To make room to align allocations within the buffer.
747} 765}
@@ -756,6 +774,7 @@ swgfx* sgNew(int width, int height, void* mem) {
756 gfx->texture = SG_ALLOC(&aligned, N, sgTextureId); 774 gfx->texture = SG_ALLOC(&aligned, N, sgTextureId);
757 gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); 775 gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2);
758 gfx->albedo = SG_ALLOC(&aligned, N, sgPixel); 776 gfx->albedo = SG_ALLOC(&aligned, N, sgPixel);
777 gfx->normals = SG_ALLOC(&aligned, N, sgVec3);
759 gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); 778 gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture);
760 gfx->activeTexture = DefaultTextureId; 779 gfx->activeTexture = DefaultTextureId;
761 gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; 780 gfx->defaultPixel = (sgPixel){255, 255, 255, 255};
@@ -922,11 +941,12 @@ void sgTriangles(swgfx* gfx, size_t count, const sgTri3* tris, const sgNormal*)
922 } 941 }
923} 942}
924 943
925void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, const sgVec3* positions, const sgVec2* texcoords) { 944void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, const sgVec3* positions, const sgVec2* texcoords, const sgVec3* normals) {
926 assert(gfx); 945 assert(gfx);
927 assert(indices); 946 assert(indices);
928 assert(positions); 947 assert(positions);
929 assert(texcoords); 948 assert(texcoords);
949 assert(normals);
930 for (size_t i = 0; i < numIndices; i+=3) { 950 for (size_t i = 0; i < numIndices; i+=3) {
931 const sgIdx i0 = indices[i]; 951 const sgIdx i0 = indices[i];
932 const sgIdx i1 = indices[i+1]; 952 const sgIdx i1 = indices[i+1];
@@ -937,25 +957,29 @@ void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, con
937 const sgVec2 uv0 = texcoords[i0]; 957 const sgVec2 uv0 = texcoords[i0];
938 const sgVec2 uv1 = texcoords[i1]; 958 const sgVec2 uv1 = texcoords[i1];
939 const sgVec2 uv2 = texcoords[i2]; 959 const sgVec2 uv2 = texcoords[i2];
960 const sgVec3 n0 = normals[i0];
961 const sgVec3 n1 = normals[i1];
962 const sgVec3 n2 = normals[i2];
940 const sgTri3 tri = (sgTri3){ 963 const sgTri3 tri = (sgTri3){
941 (sgVert3){p0, uv0}, 964 (sgVert3){p0, uv0, n0},
942 (sgVert3){p1, uv1}, 965 (sgVert3){p1, uv1, n1},
943 (sgVert3){p2, uv2}}; 966 (sgVert3){p2, uv2, n2}};
944 DrawTriangle3(gfx, &tri); 967 DrawTriangle3(gfx, &tri);
945 } 968 }
946} 969}
947 970
948void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tris, const sgVec3* positions, const sgVec2* texcoords) { 971void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tris, const sgVec3* positions, const sgVec2* texcoords, const sgVec3* normals) {
949 assert(gfx); 972 assert(gfx);
950 assert(tris); 973 assert(tris);
951 assert(positions); 974 assert(positions);
952 assert(texcoords); 975 assert(texcoords);
976 assert(normals);
953 for (size_t t = 0; t < numTris; ++t) { 977 for (size_t t = 0; t < numTris; ++t) {
954 const sgTriIdx* triIdx = &tris[t]; 978 const sgTriIdx* triIdx = &tris[t];
955 const sgTri3 tri = (sgTri3){ 979 const sgTri3 tri = (sgTri3){
956 (sgVert3){positions[triIdx->v0.pos], texcoords[triIdx->v0.uv]}, 980 (sgVert3){positions[triIdx->v0.pos], texcoords[triIdx->v0.uv], normals[triIdx->v0.normal]},
957 (sgVert3){positions[triIdx->v1.pos], texcoords[triIdx->v1.uv]}, 981 (sgVert3){positions[triIdx->v1.pos], texcoords[triIdx->v1.uv], normals[triIdx->v1.normal]},
958 (sgVert3){positions[triIdx->v2.pos], texcoords[triIdx->v2.uv]}}; 982 (sgVert3){positions[triIdx->v2.pos], texcoords[triIdx->v2.uv], normals[triIdx->v2.normal]}};
959 DrawTriangle3(gfx, &tri); 983 DrawTriangle3(gfx, &tri);
960 } 984 }
961} 985}
@@ -996,6 +1020,32 @@ void sgAmbient(swgfx* gfx, sgVec3 ambient) {
996 } 1020 }
997} 1021}
998 1022
1023void sgDepth(swgfx* gfx) {
1024 assert(gfx);
1025 const int N = gfx->dims.x * gfx->dims.y;
1026 for (int i = 0; i < N; ++i) {
1027 const R depth = gfx->depth[i];
1028 if (depth != DepthClearValue) {
1029 sgPixel* colour = &gfx->colour[i];
1030 const R d = gfx->depth[i];
1031 *colour = Vec3ToPixel((sgVec3){d,d,d}, 1.f);
1032 }
1033 }
1034}
1035
1036void sgNormals(swgfx* gfx) {
1037 assert(gfx);
1038 const int N = gfx->dims.x * gfx->dims.y;
1039 for (int i = 0; i < N; ++i) {
1040 const R depth = gfx->depth[i];
1041 if (depth != DepthClearValue) {
1042 sgPixel* colour = &gfx->colour[i];
1043 const sgVec3* normal = &gfx->normals[i];
1044 *colour = Vec3ToPixel(max3(Zero3, *normal), 1.f);
1045 }
1046 }
1047}
1048
999void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) { 1049void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) {
1000 assert(gfx); 1050 assert(gfx);
1001 assert(pixels); 1051 assert(pixels);