diff options
| -rw-r--r-- | include/swgfx.h | 2 | ||||
| -rw-r--r-- | src/swgfx.c | 243 |
2 files changed, 201 insertions, 44 deletions
diff --git a/include/swgfx.h b/include/swgfx.h index 9905382..cdeefe8 100644 --- a/include/swgfx.h +++ b/include/swgfx.h | |||
| @@ -34,11 +34,13 @@ typedef sgVec3 sgNormal; | |||
| 34 | typedef struct sgVert2i { sgVec2i pos; sgVec2 uv; } sgVert2i; | 34 | typedef struct sgVert2i { sgVec2i pos; sgVec2 uv; } sgVert2i; |
| 35 | typedef struct sgVert2 { sgVec2 pos; sgVec2 uv; } sgVert2; | 35 | typedef struct sgVert2 { sgVec2 pos; sgVec2 uv; } sgVert2; |
| 36 | typedef struct sgVert3 { sgVec3 pos; sgVec2 uv; } sgVert3; | 36 | typedef struct sgVert3 { sgVec3 pos; sgVec2 uv; } sgVert3; |
| 37 | typedef struct sgVert4 { sgVec4 pos; sgVec2 uv; } sgVert4; | ||
| 37 | 38 | ||
| 38 | typedef struct sgQuadi { sgVert2i p0, p1; } sgQuadi; | 39 | typedef struct sgQuadi { sgVert2i p0, p1; } sgQuadi; |
| 39 | typedef struct sgQuad { sgVert2 p0, p1; } sgQuad; | 40 | typedef struct sgQuad { sgVert2 p0, p1; } sgQuad; |
| 40 | typedef struct sgTri2 { sgVert2 p0, p1, p2; } sgTri2; | 41 | typedef struct sgTri2 { sgVert2 p0, p1, p2; } sgTri2; |
| 41 | typedef struct sgTri3 { sgVert3 p0, p1, p2; } sgTri3; | 42 | typedef struct sgTri3 { sgVert3 p0, p1, p2; } sgTri3; |
| 43 | typedef struct sgTri4 { sgVert4 p0, p1, p2; } sgTri4; | ||
| 42 | 44 | ||
| 43 | typedef uint16_t sgIdx; | 45 | typedef uint16_t sgIdx; |
| 44 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; | 46 | typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; |
diff --git a/src/swgfx.c b/src/swgfx.c index c54fdc1..2262dd8 100644 --- a/src/swgfx.c +++ b/src/swgfx.c | |||
| @@ -44,39 +44,44 @@ typedef struct swgfx { | |||
| 44 | // Make it so that changing the model matrix only requires one matrix | 44 | // Make it so that changing the model matrix only requires one matrix |
| 45 | // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection) | 45 | // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection) |
| 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 sgImage* texture;// User-specified texture. | 49 | const sgImage* texture; // User-specified texture. |
| 50 | sgImage defaultTexture; // A default for when no texture is provided. | 50 | sgImage defaultTexture; // A default for when no texture is provided. |
| 51 | sgPixel defaultPixel; // The single-pixel of the default texture. | 51 | sgPixel defaultPixel; // The single-pixel of the default texture. |
| 52 | } swgfx; | 52 | } swgfx; |
| 53 | 53 | ||
| 54 | static inline int mod(int a, int m) { return (m + (a % m)) % m; } | 54 | static inline int mod(int a, int m) { return (m + (a % m)) % m; } |
| 55 | static inline R frac(R a) { return a - (R)((int)a); } | ||
| 55 | 56 | ||
| 56 | static inline R frac(R a) { return a - (R)((int)a); } | ||
| 57 | |||
| 58 | static inline R rmin(R a, R b) { return (a <= b) ? a : b; } | ||
| 59 | static inline R rmax(R a, R b) { return (a >= b) ? a : b; } | ||
| 60 | static inline int imin(int a, int b) { return (a <= b) ? a : b; } | 57 | static inline int imin(int a, int b) { return (a <= b) ? a : b; } |
| 61 | static inline int imax(int a, int b) { return (a >= b) ? a : b; } | 58 | static inline int imax(int a, int b) { return (a >= b) ? a : b; } |
| 59 | static inline R rmin(R a, R b) { return (a <= b) ? a : b; } | ||
| 60 | static inline R rmax(R a, R b) { return (a >= b) ? a : b; } | ||
| 61 | static inline R lerp(R a, R b, R t) { return a + t*(b-a); } | ||
| 62 | 62 | ||
| 63 | static inline sgVec2 min2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; } | ||
| 64 | static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; } | ||
| 65 | static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; } | 63 | static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; } |
| 66 | static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; } | 64 | static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; } |
| 65 | static inline sgVec2 min2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; } | ||
| 66 | static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; } | ||
| 67 | static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; } | ||
| 68 | static inline sgVec2 sub2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x - b.x, a.y - b.y}; } | ||
| 69 | static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; } | ||
| 70 | static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; } | ||
| 71 | static inline sgVec2 lerp2(sgVec2 a, sgVec2 b, R t) { return add2(a, scale2(sub2(b,a), t)); } | ||
| 67 | 72 | ||
| 68 | static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; } | 73 | static inline sgVec3 add3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x + b.x, a.y + b.y, a.z + b.z}; } |
| 69 | |||
| 70 | static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; } | ||
| 71 | static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; } | ||
| 72 | |||
| 73 | static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } | 74 | static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } |
| 74 | static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; } | 75 | static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; } |
| 75 | static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; } | 76 | static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; } |
| 77 | static inline sgVec3 scale3(sgVec3 v, R s) { return (sgVec3){v.x * s, v.y * s, v.z * s}; } | ||
| 76 | static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } | 78 | static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } |
| 77 | static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } | 79 | static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } |
| 78 | static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } | 80 | static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } |
| 79 | 81 | ||
| 82 | static inline sgVec4 add4(sgVec4 a, sgVec4 b) { return (sgVec4){a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; } | ||
| 83 | static inline sgVec4 sub4(sgVec4 a, sgVec4 b) { return (sgVec4){a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; } | ||
| 84 | static inline sgVec4 scale4(sgVec4 v, R s) { return (sgVec4){v.x * s, v.y * s, v.z * s, v.w * s}; } | ||
| 80 | static inline sgVec4 lerp4(sgVec4 a, sgVec4 b, R t) { | 85 | static inline sgVec4 lerp4(sgVec4 a, sgVec4 b, R t) { |
| 81 | return (sgVec4){ | 86 | return (sgVec4){ |
| 82 | .x = a.x + t * (b.x - a.x), | 87 | .x = a.x + t * (b.x - a.x), |
| @@ -85,6 +90,14 @@ static inline sgVec4 lerp4(sgVec4 a, sgVec4 b, R t) { | |||
| 85 | .w = a.w + t * (b.w - a.w)}; | 90 | .w = a.w + t * (b.w - a.w)}; |
| 86 | } | 91 | } |
| 87 | 92 | ||
| 93 | /// Return the curl of 'a' towards 'b', which is defined as the z-coordinate of | ||
| 94 | /// the cross product a x b, or as the determinant det(a,b). | ||
| 95 | /// | ||
| 96 | /// The curl of 'a' towards 'b' is positive if 'a' curls towards 'b' like the | ||
| 97 | /// positive x-axis curls towards the positive y-axis. | ||
| 98 | static inline R curl2(sgVec2 a, sgVec2 b) { | ||
| 99 | return (a.x * b.y) - (a.y * b.x); | ||
| 100 | } | ||
| 88 | static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { | 101 | static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { |
| 89 | return (sgVec3) { | 102 | return (sgVec3) { |
| 90 | a.y * b.z - a.z * b.y, | 103 | a.y * b.z - a.z * b.y, |
| @@ -97,6 +110,7 @@ static inline sgVec3 normalize3(sgVec3 v) { | |||
| 97 | return (n > 0) ? (sgVec3){v.x / n, v.y / n, v.z / n} : (sgVec3){0, 0, 0}; | 110 | return (n > 0) ? (sgVec3){v.x / n, v.y / n, v.z / n} : (sgVec3){0, 0, 0}; |
| 98 | } | 111 | } |
| 99 | 112 | ||
| 113 | static inline sgVec2 Vec2FromVec4(sgVec4 v) { return (sgVec2){v.x, v.y}; } | ||
| 100 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } | 114 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } |
| 101 | 115 | ||
| 102 | static inline sgMat4 Mat4( | 116 | static inline sgMat4 Mat4( |
| @@ -240,15 +254,15 @@ static inline sgMat4 Mat4Look(sgVec3 position, sgVec3 forward, sgVec3 up) { | |||
| 240 | } | 254 | } |
| 241 | 255 | ||
| 242 | static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { | 256 | static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { |
| 243 | R f = (R)tan(fovy / 2.0); | 257 | assert(fovy > 0.f); |
| 244 | assert(f > 0.0); | 258 | assert(near < far); |
| 245 | f = 1.f / f; | 259 | const R f = 1.f / tanf(fovy / 2.f); |
| 246 | const R a = near - far; | 260 | const R a = near - far; |
| 247 | return Mat4( | 261 | return Mat4( |
| 248 | f / aspect, 0, 0, 0, | 262 | f / aspect, 0, 0, 0, |
| 249 | 0, f, 0, 0, | 263 | 0, f, 0, 0, |
| 250 | 0, 0, (far + near) / a, (2 * far * near / a), | 264 | 0, 0, (far + near) / a, (2 * far * near / a), |
| 251 | 0, 0, -1, 0); | 265 | 0, 0, -1, 0); |
| 252 | } | 266 | } |
| 253 | 267 | ||
| 254 | static inline sgVec4 PixelToVec4(sgPixel p) { | 268 | static inline sgVec4 PixelToVec4(sgPixel p) { |
| @@ -341,8 +355,10 @@ static inline sgPixel SampleBilinear(const sgImage* texture, sgVec2 uv) { | |||
| 341 | // TODO: Clamping and other addressing strategies. | 355 | // TODO: Clamping and other addressing strategies. |
| 342 | static inline sgPixel Sample(const sgImage* texture, sgVec2 uv) { | 356 | static inline sgPixel Sample(const sgImage* texture, sgVec2 uv) { |
| 343 | // TODO: Add a member to sgImage that determines how it should be filtered. | 357 | // TODO: Add a member to sgImage that determines how it should be filtered. |
| 344 | //return SampleNearest(texture, uv); | 358 | return SampleNearest(texture, uv); |
| 345 | return SampleBilinear(texture, uv); | 359 | // TODO: Debug bilinear. It's producing random colours on the ground, likely |
| 360 | // out-of-bounds memory accesses. | ||
| 361 | //return SampleBilinear(texture, uv); | ||
| 346 | } | 362 | } |
| 347 | 363 | ||
| 348 | static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { | 364 | static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { |
| @@ -437,29 +453,157 @@ static inline sgVec4 ViewportToWindow(sgViewport_t vp, sgVec4 p) { | |||
| 437 | return (sgVec4){p.x, (R)vp.height - p.y, p.z, p.w}; | 453 | return (sgVec4){p.x, (R)vp.height - p.y, p.z, p.w}; |
| 438 | } | 454 | } |
| 439 | 455 | ||
| 440 | static inline sgVec4 TransformPosition(const swgfx* gfx, sgVec3 p) { | 456 | /// Line segment-plane intersection special-case for the near camera plane. |
| 457 | /// All quantities assumed to be in camera space. | ||
| 458 | /// outP = a + outT*(b-a) | ||
| 459 | static inline R IntersectSegmentPlane(R near, const sgVec3* const a, const sgVec3* const b) { | ||
| 460 | // D = near plane distance = perpendicular distance from the origin to the plane. | ||
| 461 | // o = line origin = a | ||
| 462 | // d = line direction = b-a | ||
| 463 | // Plane normal = (0, 0, +1) --- Could be -1, need to be consistent with D. | ||
| 464 | // Point in plane: p=(0, 0, -near) | ||
| 465 | // <=> p dot n + D = 0 | ||
| 466 | // === -near * n.z + D = 0 | ||
| 467 | // === -near * 1 + D = 0 | ||
| 468 | // === D = near | ||
| 469 | // Denominator = n dot d = (0,0,1) dot d = d.z = (b.z - a.z) | ||
| 470 | const R t = (-near - a->z) / (b->z - a->z); | ||
| 471 | assert(t >= 0.f); | ||
| 472 | assert(t <= 1.f); | ||
| 473 | return t; | ||
| 474 | } | ||
| 475 | |||
| 476 | /// Interpolate depth and vertex attributes at the in/out vertex 'out'. | ||
| 477 | static void InterpolateAttributes(const sgVert4* const a, const sgVert4* const b, R t, sgVert4* out) { | ||
| 478 | assert(a); | ||
| 479 | assert(b); | ||
| 480 | assert(out); | ||
| 481 | assert(t >= 0.f); | ||
| 482 | assert(t <= 1.f); | ||
| 483 | const sgVec4 d = sub4(b->pos, a->pos); // Line direction. | ||
| 484 | out->pos = add4(a->pos, scale4(d, t)); | ||
| 485 | out->uv = lerp2(a->uv, b->uv, t); | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Clip a triangle, vertices in clip space. Return the number of output | ||
| 489 | /// triangles. | ||
| 490 | /// | ||
| 491 | /// 4 possible cases: | ||
| 492 | /// 1. All vertices in front of the camera near plane => draw. | ||
| 493 | /// 2. All vertices behind => discard. | ||
| 494 | /// 3. One vertex in front => draw 1 clipped triangle. | ||
| 495 | /// 4. Two vertices in front => draw 2 clipped triangles. | ||
| 496 | static inline int ClipTriangle(R near, const sgTri4* const tri, sgTri4 out[2]) { | ||
| 497 | #define VERTEX(IDX) (&tri->p0)[IDX] | ||
| 498 | #define VALID(X) ((0 <= (X)) && ((X) < 3)) | ||
| 499 | #define IN_FRONT(P) (P.z >= -near) // +Z points into the screen in clip space. | ||
| 500 | const bool f[3] = {IN_FRONT(tri->p0.pos), IN_FRONT(tri->p1.pos), IN_FRONT(tri->p2.pos)}; | ||
| 501 | const int numFront = f[0] + f[1] + f[2]; | ||
| 502 | int numTris; | ||
| 503 | if (numFront == 3) { | ||
| 504 | numTris = 1; | ||
| 505 | out[0] = *tri; | ||
| 506 | } else if (numFront == 2) { | ||
| 507 | numTris = 2; | ||
| 508 | int back = 0; | ||
| 509 | for (; f[back] && (back < 3); ++back) {} | ||
| 510 | assert(VALID(back)); | ||
| 511 | assert(!f[back]); | ||
| 512 | int front[2] = {(back+1)%3, (back+2)%3}; | ||
| 513 | assert(VALID(front[0])); | ||
| 514 | assert(VALID(front[1])); | ||
| 515 | const sgVert4* const backVert = &VERTEX(back); | ||
| 516 | sgVert4 p[2]; | ||
| 517 | for (int i = 0; i < 2; ++i) { | ||
| 518 | const R t = IntersectSegmentPlane(near, (const sgVec3*)&backVert->pos, (const sgVec3*)&VERTEX(front[i]).pos); | ||
| 519 | InterpolateAttributes(backVert, &VERTEX(front[i]), t, &p[i]); | ||
| 520 | } | ||
| 521 | // We must preserve the winding order here for culling. | ||
| 522 | // Note that p[i] corresponds to front[i] = back+(i+1). | ||
| 523 | out[0] = (sgTri4){p[1], p[0], VERTEX(front[1])}; | ||
| 524 | out[1] = (sgTri4){p[0], VERTEX(front[0]), VERTEX(front[1])}; | ||
| 525 | } else if (numFront == 1) { | ||
| 526 | numTris = 1; | ||
| 527 | int front = 0; | ||
| 528 | for (; !f[front] && (front < 3); ++front){} | ||
| 529 | assert(VALID(front)); | ||
| 530 | assert(f[front]); | ||
| 531 | int back[2] = {(front+1)%3, (front+2)%3}; | ||
| 532 | assert(VALID(back[0])); | ||
| 533 | assert(VALID(back[1])); | ||
| 534 | const sgVert4* const frontVert = &VERTEX(front); | ||
| 535 | sgVert4 p[2]; | ||
| 536 | for (int i = 0; i < 2; ++i) { | ||
| 537 | const R t = IntersectSegmentPlane(near, (const sgVec3*)&frontVert->pos, (const sgVec3*)&VERTEX(back[i]).pos); | ||
| 538 | InterpolateAttributes(frontVert, &VERTEX(back[i]), t, &p[i]); | ||
| 539 | } | ||
| 540 | // We must preserve the winding order here for culling. | ||
| 541 | // Note that p[i] corresponds to back[i] = front+(i+1). | ||
| 542 | out[0] = (sgTri4){*frontVert, p[0], p[1]}; | ||
| 543 | } else { | ||
| 544 | numTris = 0; | ||
| 545 | } | ||
| 546 | return numTris; | ||
| 547 | #undef IN_FRONT | ||
| 548 | #undef VALID | ||
| 549 | #undef VERTEX | ||
| 550 | } | ||
| 551 | |||
| 552 | static inline int TransformTri(const swgfx* gfx, const sgTri3* const tri, sgTri4 out[2]) { | ||
| 441 | assert(gfx); | 553 | assert(gfx); |
| 554 | assert(tri); | ||
| 442 | // Model to clip space. | 555 | // Model to clip space. |
| 443 | const sgVec4 p_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(p, 1)); | 556 | const sgVec4 p0_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p0.pos, 1)); |
| 444 | // TODO: Backface culling. | 557 | const sgVec4 p1_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p1.pos, 1)); |
| 445 | // Perspective divide. | 558 | const sgVec4 p2_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(tri->p2.pos, 1)); |
| 446 | const sgVec4 p_ndc = PerspDivide(p_clip); | 559 | const sgTri4 tri_clip = { |
| 447 | // TODO: Clip. | 560 | (sgVert4){ p0_clip, tri->p0.uv }, |
| 448 | const sgVec4 p_vp = ViewportTransform(gfx->viewport, p_ndc); | 561 | (sgVert4){ p1_clip, tri->p1.uv }, |
| 449 | return ViewportToWindow(gfx->viewport, p_vp); | 562 | (sgVert4){ p2_clip, tri->p2.uv }}; |
| 563 | // Clip. | ||
| 564 | // Our perspective matrix maps the near plane to z=-1 in clip space. | ||
| 565 | constexpr R near_clip = -1.f; | ||
| 566 | const int numTris = ClipTriangle(near_clip, &tri_clip, out); | ||
| 567 | assert((0 <= numTris) && (numTris <= 2)); | ||
| 568 | for (int i = 0; i < numTris; ++i) { | ||
| 569 | sgTri4* const tri4 = &out[i]; | ||
| 570 | // Perspective divide. | ||
| 571 | const sgVec4 p0_ndc = PerspDivide(tri4->p0.pos); | ||
| 572 | const sgVec4 p1_ndc = PerspDivide(tri4->p1.pos); | ||
| 573 | const sgVec4 p2_ndc = PerspDivide(tri4->p2.pos); | ||
| 574 | // To viewport. | ||
| 575 | const sgVec4 p0_vp = ViewportTransform(gfx->viewport, p0_ndc); | ||
| 576 | const sgVec4 p1_vp = ViewportTransform(gfx->viewport, p1_ndc); | ||
| 577 | const sgVec4 p2_vp = ViewportTransform(gfx->viewport, p2_ndc); | ||
| 578 | // To window. | ||
| 579 | const sgVec4 p0_wn = ViewportToWindow(gfx->viewport, p0_vp); | ||
| 580 | const sgVec4 p1_wn = ViewportToWindow(gfx->viewport, p1_vp); | ||
| 581 | const sgVec4 p2_wn = ViewportToWindow(gfx->viewport, p2_vp); | ||
| 582 | // Output. | ||
| 583 | tri4->p0.pos = p0_wn; | ||
| 584 | tri4->p1.pos = p1_wn; | ||
| 585 | tri4->p2.pos = p2_wn; | ||
| 586 | } | ||
| 587 | return numTris; | ||
| 450 | } | 588 | } |
| 451 | 589 | ||
| 452 | static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { | 590 | static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) { |
| 453 | assert(gfx); | 591 | assert(gfx); |
| 454 | assert(tri); | 592 | assert(tri); |
| 455 | // TODO: Inline the transform here and interleave its operations to perform | 593 | const sgVec4 p0 = tri->p0.pos; |
| 456 | // backface culling and clipping as early as possible. | 594 | const sgVec4 p1 = tri->p1.pos; |
| 457 | const sgVec4 p0 = TransformPosition(gfx, tri->p0.pos); | 595 | const sgVec4 p2 = tri->p2.pos; |
| 458 | const sgVec4 p1 = TransformPosition(gfx, tri->p1.pos); | ||
| 459 | const sgVec4 p2 = TransformPosition(gfx, tri->p2.pos); | ||
| 460 | const sgVec2 p0_2d = (sgVec2){p0.x, p0.y}; | 596 | const sgVec2 p0_2d = (sgVec2){p0.x, p0.y}; |
| 461 | const sgVec2 p1_2d = (sgVec2){p1.x, p1.y}; | 597 | const sgVec2 p1_2d = (sgVec2){p1.x, p1.y}; |
| 462 | const sgVec2 p2_2d = (sgVec2){p2.x, p2.y}; | 598 | const sgVec2 p2_2d = (sgVec2){p2.x, p2.y}; |
| 599 | // Backface culling, assume front face = ccw. | ||
| 600 | // In screen space, +Y goes down. | ||
| 601 | // p0p1p2 is ccw <=> p0p1 curls negatively towards p0p2. If the curl is | ||
| 602 | // positive (cw winding), cull. | ||
| 603 | if (curl2(sub2(p1_2d, p0_2d), | ||
| 604 | sub2(p2_2d, p0_2d)) > 0.f) { | ||
| 605 | return; | ||
| 606 | } | ||
| 463 | const sgAABB2 bbox = TriangleAabb2(p0_2d, p1_2d, p2_2d); | 607 | const sgAABB2 bbox = TriangleAabb2(p0_2d, p1_2d, p2_2d); |
| 464 | // We consider (x,y) to be the pixel center. | 608 | // We consider (x,y) to be the pixel center. |
| 465 | // Draw all pixels touched by the bounding box. TODO: Multi-sampling. | 609 | // Draw all pixels touched by the bounding box. TODO: Multi-sampling. |
| @@ -497,13 +641,12 @@ static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { | |||
| 497 | if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { | 641 | if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { |
| 498 | *depth = p_depth; | 642 | *depth = p_depth; |
| 499 | const sgPixel colour = Sample(gfx->texture, uv); | 643 | const sgPixel colour = Sample(gfx->texture, uv); |
| 500 | // TODO: When doing lighting, need to tone-map here. | 644 | // TODO: When doing lighting, need to tone-map here and apply inverse |
| 501 | /*const int d = (int)(z*255.f); | 645 | // gamma here. |
| 502 | const sgPixel colour = (sgPixel){d,d,d,255};*/ | 646 | //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; |
| 503 | //const sgPixel colour = (sgPixel){255, 0, 255, 255}; | 647 | //const sgPixel colour = {(int)(z*255.f), (int)(z*255.f), (int)(z*255.f), 255}; |
| 504 | /*const int r = (int)(uv.x * 255.f); | 648 | //const sgPixel colour = {255, 0, 255, 255}; |
| 505 | const int g = (int)(uv.y * 255.f); | 649 | //const sgPixel colour = {(int)(uv.x * 255.f), (int)(uv.y * 255.f), 255, 255}; |
| 506 | const sgPixel colour = (sgPixel){r, g, 255, 255};*/ | ||
| 507 | SetPixel(gfx, (sgVec2i){x,y}, colour); | 650 | SetPixel(gfx, (sgVec2i){x,y}, colour); |
| 508 | } | 651 | } |
| 509 | } | 652 | } |
| @@ -511,6 +654,17 @@ static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { | |||
| 511 | } | 654 | } |
| 512 | } | 655 | } |
| 513 | 656 | ||
| 657 | static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { | ||
| 658 | assert(gfx); | ||
| 659 | assert(tri); | ||
| 660 | sgTri4 tris[2]; | ||
| 661 | const int numTris = TransformTri(gfx, tri, tris); | ||
| 662 | assert((0 <= numTris) && (numTris <= 2)); | ||
| 663 | for (int i = 0; i < numTris; ++i) { | ||
| 664 | DrawTriangle3PostClip(gfx, &tris[i]); | ||
| 665 | } | ||
| 666 | } | ||
| 667 | |||
| 514 | #define is_pow2_or_0(X) ((X & (X - 1)) == 0) | 668 | #define is_pow2_or_0(X) ((X & (X - 1)) == 0) |
| 515 | #define SG_ALIGN 64 | 669 | #define SG_ALIGN 64 |
| 516 | #define SG_ALLOC(PP_MEM, COUNT, TYPE) (TYPE*)Alloc(PP_MEM, COUNT, sizeof(TYPE)) | 670 | #define SG_ALLOC(PP_MEM, COUNT, TYPE) (TYPE*)Alloc(PP_MEM, COUNT, sizeof(TYPE)) |
| @@ -557,6 +711,7 @@ swgfx* sgNew(int width, int height, void* mem) { | |||
| 557 | .height = 1, | 711 | .height = 1, |
| 558 | .pixels = &gfx->defaultPixel, | 712 | .pixels = &gfx->defaultPixel, |
| 559 | }; | 713 | }; |
| 714 | gfx->texture = &gfx->defaultTexture; | ||
| 560 | return gfx; | 715 | return gfx; |
| 561 | } | 716 | } |
| 562 | 717 | ||
