summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/swgfx.h2
-rw-r--r--src/swgfx.c243
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;
34typedef struct sgVert2i { sgVec2i pos; sgVec2 uv; } sgVert2i; 34typedef struct sgVert2i { sgVec2i pos; sgVec2 uv; } sgVert2i;
35typedef struct sgVert2 { sgVec2 pos; sgVec2 uv; } sgVert2; 35typedef struct sgVert2 { sgVec2 pos; sgVec2 uv; } sgVert2;
36typedef struct sgVert3 { sgVec3 pos; sgVec2 uv; } sgVert3; 36typedef struct sgVert3 { sgVec3 pos; sgVec2 uv; } sgVert3;
37typedef struct sgVert4 { sgVec4 pos; sgVec2 uv; } sgVert4;
37 38
38typedef struct sgQuadi { sgVert2i p0, p1; } sgQuadi; 39typedef struct sgQuadi { sgVert2i p0, p1; } sgQuadi;
39typedef struct sgQuad { sgVert2 p0, p1; } sgQuad; 40typedef struct sgQuad { sgVert2 p0, p1; } sgQuad;
40typedef struct sgTri2 { sgVert2 p0, p1, p2; } sgTri2; 41typedef struct sgTri2 { sgVert2 p0, p1, p2; } sgTri2;
41typedef struct sgTri3 { sgVert3 p0, p1, p2; } sgTri3; 42typedef struct sgTri3 { sgVert3 p0, p1, p2; } sgTri3;
43typedef struct sgTri4 { sgVert4 p0, p1, p2; } sgTri4;
42 44
43typedef uint16_t sgIdx; 45typedef uint16_t sgIdx;
44typedef struct sgVertIdx { sgIdx pos, uv, normal; } sgVertIdx; 46typedef 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
54static inline int mod(int a, int m) { return (m + (a % m)) % m; } 54static inline int mod(int a, int m) { return (m + (a % m)) % m; }
55static inline R frac(R a) { return a - (R)((int)a); }
55 56
56static inline R frac(R a) { return a - (R)((int)a); }
57
58static inline R rmin(R a, R b) { return (a <= b) ? a : b; }
59static inline R rmax(R a, R b) { return (a >= b) ? a : b; }
60static inline int imin(int a, int b) { return (a <= b) ? a : b; } 57static inline int imin(int a, int b) { return (a <= b) ? a : b; }
61static inline int imax(int a, int b) { return (a >= b) ? a : b; } 58static inline int imax(int a, int b) { return (a >= b) ? a : b; }
59static inline R rmin(R a, R b) { return (a <= b) ? a : b; }
60static inline R rmax(R a, R b) { return (a >= b) ? a : b; }
61static inline R lerp(R a, R b, R t) { return a + t*(b-a); }
62 62
63static inline sgVec2 min2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; }
64static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; }
65static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; } 63static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; }
66static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; } 64static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; }
65static inline sgVec2 min2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; }
66static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; }
67static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; }
68static inline sgVec2 sub2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x - b.x, a.y - b.y}; }
69static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; }
70static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; }
71static inline sgVec2 lerp2(sgVec2 a, sgVec2 b, R t) { return add2(a, scale2(sub2(b,a), t)); }
67 72
68static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; } 73static inline sgVec3 add3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x + b.x, a.y + b.y, a.z + b.z}; }
69
70static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; }
71static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; }
72
73static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } 74static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; }
74static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; } 75static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; }
75static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; } 76static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; }
77static inline sgVec3 scale3(sgVec3 v, R s) { return (sgVec3){v.x * s, v.y * s, v.z * s}; }
76static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } 78static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
77static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } 79static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
78static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } 80static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); }
79 81
82static 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}; }
83static 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}; }
84static inline sgVec4 scale4(sgVec4 v, R s) { return (sgVec4){v.x * s, v.y * s, v.z * s, v.w * s}; }
80static inline sgVec4 lerp4(sgVec4 a, sgVec4 b, R t) { 85static 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.
98static inline R curl2(sgVec2 a, sgVec2 b) {
99 return (a.x * b.y) - (a.y * b.x);
100}
88static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { 101static 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
113static inline sgVec2 Vec2FromVec4(sgVec4 v) { return (sgVec2){v.x, v.y}; }
100static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } 114static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; }
101 115
102static inline sgMat4 Mat4( 116static inline sgMat4 Mat4(
@@ -240,15 +254,15 @@ static inline sgMat4 Mat4Look(sgVec3 position, sgVec3 forward, sgVec3 up) {
240} 254}
241 255
242static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { 256static 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
254static inline sgVec4 PixelToVec4(sgPixel p) { 268static 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.
342static inline sgPixel Sample(const sgImage* texture, sgVec2 uv) { 356static 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
348static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { 364static 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
440static 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)
459static 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'.
477static 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.
496static 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
552static 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
452static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { 590static 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
657static 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