summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/swgfx.c278
1 files changed, 223 insertions, 55 deletions
diff --git a/src/swgfx.c b/src/swgfx.c
index 772a691..3b9ce29 100644
--- a/src/swgfx.c
+++ b/src/swgfx.c
@@ -6,6 +6,11 @@ Matrices:
6Coordinate systems: 6Coordinate systems:
7 - Right-handed. 7 - Right-handed.
8 - NDC in [-1, +1]. 8 - NDC in [-1, +1].
9 - Viewport goes up and to the right.
10 - Window goes down and to the right.
11 - (x,y) is the center of a pixel.
12 - Top-left: (x - 1/2, y - 1/2)
13 - Bottom-right: (x + 1/2, y + 1/2)
9*/ 14*/
10#include <swgfx.h> 15#include <swgfx.h>
11 16
@@ -15,7 +20,7 @@ Coordinate systems:
15#include <stdlib.h> 20#include <stdlib.h>
16#include <string.h> 21#include <string.h>
17 22
18static const sgVec3 Up3 = (sgVec3){0,1,0}; 23static constexpr sgVec3 Up3 = (sgVec3){0,1,0};
19 24
20typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; 25typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t;
21typedef struct sgTri2 { sgVec2 p0, p1, p2; } sgTri2; 26typedef struct sgTri2 { sgVec2 p0, p1, p2; } sgTri2;
@@ -30,8 +35,18 @@ typedef struct swgfx {
30 sgVec2i dims; // Colour buffer dimensions. 35 sgVec2i dims; // Colour buffer dimensions.
31 sgPixel* colour; // Colour buffer. 36 sgPixel* colour; // Colour buffer.
32 sgViewport_t viewport; 37 sgViewport_t viewport;
38 sgMat4 model; // Model matrix.
33 sgMat4 view; // View matrix. 39 sgMat4 view; // View matrix.
34 sgMat4 proj; // Projection matrix. 40 sgMat4 proj; // Projection matrix.
41 // Pre-multiplied matrices.
42 // The model matrix changes once per object, more frequently than view or
43 // projection. View and projection are expected to change infrequently, maybe
44 // once per frame.
45 // Make it so that changing the model matrix only requires one matrix
46 // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection)
47 // before rendering the model's triangles.
48 sgMat4 viewProj; // View-projection matrix.
49 sgMat4 mvp; // Model-view-projection matrix.
35} swgfx; 50} swgfx;
36 51
37static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } 52static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; }
@@ -40,6 +55,10 @@ static inline sgVec3 sub3(sgVec3 a, sgVec3 b) {
40 return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; 55 return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z};
41} 56}
42 57
58static inline R dot3(sgVec3 a, sgVec3 b) {
59 return a.x * b.x + a.y * b.y + a.z * b.z;
60}
61
43static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { 62static inline sgVec3 cross3(sgVec3 a, sgVec3 b) {
44 return (sgVec3) { 63 return (sgVec3) {
45 a.y * b.z - a.z * b.y, 64 a.y * b.z - a.z * b.y,
@@ -48,8 +67,7 @@ static inline sgVec3 cross3(sgVec3 a, sgVec3 b) {
48} 67}
49 68
50static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } 69static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
51 70static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); }
52static inline R norm3(sgVec3 v) { return sqrt(normsq3(v)); }
53 71
54static inline sgVec3 normalize3(sgVec3 v) { 72static inline sgVec3 normalize3(sgVec3 v) {
55 const R n = norm3(v); 73 const R n = norm3(v);
@@ -57,6 +75,10 @@ static inline sgVec3 normalize3(sgVec3 v) {
57 return (sgVec3){v.x / n, v.y / n, v.z / n}; 75 return (sgVec3){v.x / n, v.y / n, v.z / n};
58} 76}
59 77
78static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) {
79 return (sgVec4){v.x, v.y, v.z, w};
80}
81
60static inline sgMat4 Mat4( 82static inline sgMat4 Mat4(
61 R m00, R m01, R m02, R m03, // v0.x v1.x v2.x v3.x 83 R m00, R m01, R m02, R m03, // v0.x v1.x v2.x v3.x
62 R m10, R m11, R m12, R m13, // v0.y v1.y v2.y v3.y 84 R m10, R m11, R m12, R m13, // v0.y v1.y v2.y v3.y
@@ -78,6 +100,10 @@ static inline sgMat4 Mat4FromVec3(sgVec3 right, sgVec3 up, sgVec3 forward, sgVec
78} 100}
79 101
80static inline R Mat4At(sgMat4 m, int row, int col) { return m.val[col][row]; } 102static inline R Mat4At(sgMat4 m, int row, int col) { return m.val[col][row]; }
103static inline sgVec3 Mat4v0(sgMat4 m) { return *((sgVec3*)m.val[0]); }
104static inline sgVec3 Mat4v1(sgMat4 m) { return *((sgVec3*)m.val[1]); }
105static inline sgVec3 Mat4v2(sgMat4 m) { return *((sgVec3*)m.val[2]); }
106static inline sgVec3 Mat4v3(sgMat4 m) { return *((sgVec3*)m.val[3]); }
81 107
82static inline sgMat4 Mat4Mul(sgMat4 A, sgMat4 B) { 108static inline sgMat4 Mat4Mul(sgMat4 A, sgMat4 B) {
83 R m00 = Mat4At(A, 0, 0) * Mat4At(B, 0, 0) + 109 R m00 = Mat4At(A, 0, 0) * Mat4At(B, 0, 0) +
@@ -162,6 +188,31 @@ static inline sgVec3 Mat4MulVec3(sgMat4 m, sgVec3 v, R w) {
162 .z = Mat4At(m, 2, 0) * v.x + Mat4At(m, 2, 1) * v.y + Mat4At(m, 2, 2) * v.z + Mat4At(m, 2, 3) * w}; 188 .z = Mat4At(m, 2, 0) * v.x + Mat4At(m, 2, 1) * v.y + Mat4At(m, 2, 2) * v.z + Mat4At(m, 2, 3) * w};
163} 189}
164 190
191static inline sgVec4 Mat4MulVec4(sgMat4 m, sgVec4 v) {
192 sgVec4 u;
193 u.x = Mat4At(m, 0, 0) * v.x + Mat4At(m, 0, 1) * v.y +
194 Mat4At(m, 0, 2) * v.z + Mat4At(m, 0, 3) * v.w;
195 u.y = Mat4At(m, 1, 0) * v.x + Mat4At(m, 1, 1) * v.y +
196 Mat4At(m, 1, 2) * v.z + Mat4At(m, 1, 3) * v.w;
197 u.z = Mat4At(m, 2, 0) * v.x + Mat4At(m, 2, 1) * v.y +
198 Mat4At(m, 2, 2) * v.z + Mat4At(m, 2, 3) * v.w;
199 u.w = Mat4At(m, 3, 0) * v.x + Mat4At(m, 3, 1) * v.y +
200 Mat4At(m, 3, 2) * v.z + Mat4At(m, 3, 3) * v.w;
201 return u;
202}
203
204static inline sgMat4 Mat4InverseTransform(sgMat4 m) {
205 const sgVec3 r = Mat4v0(m);
206 const sgVec3 u = Mat4v1(m);
207 const sgVec3 f = Mat4v2(m);
208 const sgVec3 t = Mat4v3(m);
209 return Mat4(
210 r.x, r.y, r.z, -dot3(r, t),
211 u.x, u.y, u.z, -dot3(u, t),
212 f.x, f.y, f.z, -dot3(f, t),
213 0.f, 0.f, 0.f, 1.f);
214}
215
165static inline sgMat4 Mat4Look(sgVec3 position, sgVec3 forward, sgVec3 up) { 216static inline sgMat4 Mat4Look(sgVec3 position, sgVec3 forward, sgVec3 up) {
166 const sgVec3 right = normalize3(cross3(forward, up)); 217 const sgVec3 right = normalize3(cross3(forward, up));
167 up = normalize3(cross3(right, forward)); 218 up = normalize3(cross3(right, forward));
@@ -169,43 +220,59 @@ static inline sgMat4 Mat4Look(sgVec3 position, sgVec3 forward, sgVec3 up) {
169} 220}
170 221
171static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { 222static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) {
172 R f = tan(fovy / 2.0); 223 R f = (R)tan(fovy / 2.0);
173 assert(f > 0.0); 224 assert(f > 0.0);
174 f = 1.0 / f; 225 f = 1.f / f;
175 const R a = near - far; 226 const R a = near - far;
176 return Mat4( 227 return Mat4(
177 f / aspect, 0, 0, 0, 228 f / aspect, 0, 0, 0,
178 0, f, 0, 0, 229 0, f, 0, 0,
179 0, 0, (far + near) / a, (2 * far * near / a), 230 0, 0, (far + near) / a, (2 * far * near / a),
180 0, 0, -1, 0); 231 0, 0, -1, 0);
181} 232}
182 233
183static inline sgPixel* PixelRow(sgPixel* image, int width, int y) { 234#ifndef _NDEBUG
184 return image + (y * width); 235static bool InBounds(int width, int height, sgVec2i p) {
236 return (0 <= p.x) && (p.x < width) &&
237 (0 <= p.y) && (p.y < height);
185} 238}
239#endif // _NDEBUG
186 240
187static inline sgPixel* Pixel(sgPixel* image, int width, int x, int y) { 241static inline sgPixel* Pixel(sgPixel* image, int width, int height, int x, int y) {
242 assert(InBounds(width, height, (sgVec2i){x,y}));
188 return image + (y * width) + x; 243 return image + (y * width) + x;
189} 244}
190 245
191#define XY(X,Y) Pixel(gfx->colour, gfx->dims.x, X, Y) 246static inline R rmin(R a, R b) { return (a <= b) ? a : b; }
192 247static inline R rmax(R a, R b) { return (a >= b) ? a : b; }
193static inline R rmin(R a, R b) { return (a <= b) ? a : b; } 248static inline int imin(int a, int b) { return (a <= b) ? a : b; }
194static inline R rmax(R a, R b) { return (a >= b) ? a : b; } 249static inline int imax(int a, int b) { return (a >= b) ? a : b; }
195
196static inline sgVec2 min2(sgVec2 a, sgVec2 b) { 250static inline sgVec2 min2(sgVec2 a, sgVec2 b) {
197 return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; 251 return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) };
198} 252}
199
200static inline sgVec2 max2(sgVec2 a, sgVec2 b) { 253static inline sgVec2 max2(sgVec2 a, sgVec2 b) {
201 return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; 254 return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) };
202} 255}
256static inline sgVec2i min2i(sgVec2i a, sgVec2i b) {
257 return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) };
258}
259static inline sgVec2i max2i(sgVec2i a, sgVec2i b) {
260 return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) };
261}
203 262
204static inline sgAABB2 TriangleAabb2(const sgTri2 tri) { 263static inline sgAABB2 TriangleAabb2(const sgTri2 tri) {
205 return (sgAABB2){.pmin = min2(min2(tri.p0, tri.p1), tri.p2), 264 return (sgAABB2){.pmin = min2(min2(tri.p0, tri.p1), tri.p2),
206 .pmax = max2(max2(tri.p0, tri.p1), tri.p2)}; 265 .pmax = max2(max2(tri.p0, tri.p1), tri.p2)};
207} 266}
208 267
268static inline sgVec2i Clip(const swgfx* gfx, const sgVec2i p) {
269 assert(gfx);
270 constexpr sgVec2i lower = (sgVec2i){0,0};
271 const sgVec2i upper = (sgVec2i){gfx->viewport.width - 1,
272 gfx->viewport.height - 1};
273 return max2i(lower, min2i(upper, p));
274}
275
209static inline R f(sgVec2 a, sgVec2 b, sgVec2 p) { 276static inline R f(sgVec2 a, sgVec2 b, sgVec2 p) {
210 return (a.y - b.y)*p.x + (b.x - a.x)*p.y + a.x*b.y - b.x*a.y; 277 return (a.y - b.y)*p.x + (b.x - a.x)*p.y + a.x*b.y - b.x*a.y;
211} 278}
@@ -222,10 +289,77 @@ static inline sgVec3 Barycentric(const sgTri2 tri, sgVec2 p) {
222 f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2)};*/ 289 f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2)};*/
223 const R b = f(tri.p0, tri.p2, p) / f(tri.p0, tri.p2, tri.p1); 290 const R b = f(tri.p0, tri.p2, p) / f(tri.p0, tri.p2, tri.p1);
224 const R c = f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2); 291 const R c = f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2);
225 const R a = /*f(tri.p1, tri.p2, p) / f(tri.p1, tri.p2, tri.p0);*/1 - b - c - 1e-7; 292 const R a = /*f(tri.p1, tri.p2, p) / f(tri.p1, tri.p2, tri.p0);*/1.f - b - c - (R)1e-7;
226 return (sgVec3){a,b,c}; 293 return (sgVec3){a,b,c};
227} 294}
228 295
296static void DrawTriangle2(swgfx* gfx, const sgTri2* tri) {
297 assert(gfx);
298 assert(tri);
299 const sgAABB2 bbox = TriangleAabb2(*tri);
300 // We consider (x,y) to be the pixel center.
301 // Draw all pixels touched by the bounding box. TODO: Multi-sampling.
302 sgVec2i pmin = (sgVec2i){(int)bbox.pmin.x, (int)bbox.pmin.y};
303 sgVec2i pmax = (sgVec2i){(int)(bbox.pmax.x + 0.5f), (int)(bbox.pmax.y + 0.5f)};
304 // Clip to screen space.
305 pmin = Clip(gfx, pmin);
306 pmax = Clip(gfx, pmax);
307 // Draw.
308 for (int y = pmin.y; y <= pmax.y; ++y) {
309 for (int x = pmin.x; x <= pmax.x; ++x) {
310 const sgVec2 p = (sgVec2){(R)x, (R)y};
311 // TODO: there is an incremental optimization to computing barycentric coordinates;
312 // read more about it.
313 const sgVec3 bar = Barycentric(*tri, p);
314 // We need to check the third coordinate.
315 // a + b + c = 1
316 // So, e.g., if a >= 0 and b >= 0, then we have c <= 1, but we could also have c <= 0.
317 // In the case c <= 0, then point is outside the triangle.
318 if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) {
319 const sgVec2i pi = (sgVec2i){(int)x, (int)y};
320 sgPixels(gfx, 1, &pi, (sgPixel){255, 255, 255, 255});
321 }
322 }
323 }
324}
325
326static inline sgVec3 PerspDivide(sgVec4 v) {
327 return (sgVec3){v.x / v.w, v.y / v.w, v.z / v.w};
328}
329
330// TODO: Compute a viewport matrix in sgViewport() instead.
331static inline sgVec2 ViewportTransform(sgViewport_t vp, sgVec3 ndc) {
332 return (sgVec2){
333 .x = (ndc.x+1.f) * ((R)vp.width/2.f) + (R)vp.x0,
334 .y = (ndc.y+1.f) * ((R)vp.height/2.f) + (R)vp.y0};
335}
336
337static inline sgVec2 ViewportToWindow(sgViewport_t vp, sgVec2 p) {
338 return (sgVec2){p.x, (R)vp.height - p.y};
339}
340
341static inline sgVec2 TransformPosition(const swgfx* gfx, sgVec3 p) {
342 assert(gfx);
343 // Model to clip space.
344 const sgVec4 p_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(p, 1));
345 // TODO: Backface culling.
346 // Perspective divide.
347 const sgVec3 p_ndc = PerspDivide(p_clip);
348 // TODO: Clip.
349 const sgVec2 p_vp = ViewportTransform(gfx->viewport, p_ndc);
350 return ViewportToWindow(gfx->viewport, p_vp);
351}
352
353static void DrawTriangle3(swgfx* gfx, const sgTri3* tri) {
354 assert(gfx);
355 assert(tri);
356 const sgVec2 p0 = TransformPosition(gfx, tri->p0);
357 const sgVec2 p1 = TransformPosition(gfx, tri->p1);
358 const sgVec2 p2 = TransformPosition(gfx, tri->p2);
359 const sgTri2 tri2 = (sgTri2){p0, p1, p2};
360 DrawTriangle2(gfx, &tri2);
361}
362
229#define is_pow2_or_0(X) ((X & (X - 1)) == 0) 363#define is_pow2_or_0(X) ((X & (X - 1)) == 0)
230 364
231static size_t align(size_t size) { 365static size_t align(size_t size) {
@@ -295,14 +429,44 @@ void sgPresent(swgfx* gfx, sgVec2i dimensions, sgPixel* screen) {
295 } 429 }
296} 430}
297 431
298void sgCam(swgfx* gfx, sgVec3 position, sgVec3 forward) { 432static void sgUpdateViewProjection(swgfx* gfx) {
433 assert(gfx);
434 gfx->viewProj = Mat4Mul(gfx->proj, gfx->view);
435}
436
437static void sgUpdateMvp(swgfx* gfx) {
438 assert(gfx);
439 gfx->mvp = Mat4Mul(gfx->viewProj, gfx->model);
440}
441
442void sgModelId(swgfx* gfx) {
299 assert(gfx); 443 assert(gfx);
300 gfx->view = Mat4Look(position, forward, Up3); 444 sgModel(gfx,
445 (sgVec3){0,0,0},
446 (sgVec3){1, 0, 0},
447 (sgVec3){0, 1, 0},
448 (sgVec3){0, 0, 1});
449}
450
451void sgModel(swgfx* gfx, sgVec3 position, sgVec3 right, sgVec3 up, sgVec3 forward) {
452 assert(gfx);
453 gfx->model = Mat4FromVec3(right, up, forward, position);
454 sgUpdateMvp(gfx);
455}
456
457void sgView(swgfx* gfx, sgVec3 position, sgVec3 forward) {
458 assert(gfx);
459 const sgMat4 camera = Mat4Look(position, forward, Up3);
460 gfx->view = Mat4InverseTransform(camera);
461 sgUpdateViewProjection(gfx);
462 sgUpdateMvp(gfx);
301} 463}
302 464
303void sgPerspective(swgfx* gfx, R fovy, R aspect, R near, R far) { 465void sgPerspective(swgfx* gfx, R fovy, R aspect, R near, R far) {
304 assert(gfx); 466 assert(gfx);
305 gfx->proj = Mat4Perspective(fovy, aspect, near, far); 467 gfx->proj = Mat4Perspective(fovy, aspect, near, far);
468 sgUpdateViewProjection(gfx);
469 sgUpdateMvp(gfx);
306} 470}
307 471
308void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { 472void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) {
@@ -317,34 +481,13 @@ void sgClear(swgfx* gfx) {
317 481
318void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { 482void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) {
319 assert(gfx); 483 assert(gfx);
484#define XY(X,Y) Pixel(gfx->colour, gfx->dims.x, gfx->dims.y, X, Y)
320 for (size_t i = 0; i < count; ++i) { 485 for (size_t i = 0; i < count; ++i) {
321 const sgVec2i p = positions[i]; 486 const sgVec2i p = positions[i];
322 *XY(p.x, p.y) = colour; 487 *XY(p.x, p.y) = colour;
323 } 488 }
324} 489}
325 490
326static void DrawTriangle2(swgfx* gfx, const sgTri2* tri) {
327 assert(gfx);
328 assert(tri);
329 const sgAABB2 bbox = TriangleAabb2(*tri);
330 for (int y = bbox.pmin.y; y <= bbox.pmax.y; ++y) {
331 for (int x = bbox.pmin.x; x <= bbox.pmax.x; ++x) {
332 const sgVec2 p = (sgVec2){x, y};
333 // TODO: there is an incremental optimization to computing barycentric coordinates;
334 // read more about it.
335 const sgVec3 bar = Barycentric(*tri, p);
336 // We need to check the third coordinate.
337 // a + b + c = 1
338 // So, e.g., if a > 0 and b > 0, then we have c < 1, but we could also have c < 0.
339 // In the case c < 0, then point is outside the triangle.
340 if ((bar.x > 0) && (bar.y > 0) && (bar.z > 0)) {
341 const sgVec2i pi = (sgVec2i){(int)x, (int)y};
342 sgPixels(gfx, 1, &pi, (sgPixel){255, 255, 255, 255});
343 }
344 }
345 }
346}
347
348// TODO: DrawTriangle3 with clipping. Leave DrawTriangle2 to not clip for 491// TODO: DrawTriangle3 with clipping. Leave DrawTriangle2 to not clip for
349// performance; assume that 2D triangles are within bounds. 492// performance; assume that 2D triangles are within bounds.
350// TODO: If the triangle is out of bounds, skip entirely. 493// TODO: If the triangle is out of bounds, skip entirely.
@@ -365,26 +508,51 @@ void sgTriangles2(swgfx* gfx, size_t count, const sgTri2* tris) {
365 508
366void sgTriangles(swgfx* gfx, size_t count, const sgTri3* tris, const sgNormal*) { 509void sgTriangles(swgfx* gfx, size_t count, const sgTri3* tris, const sgNormal*) {
367 assert(gfx); 510 assert(gfx);
511 assert(tris);
368 for (size_t i = 0; i < count; ++i) { 512 for (size_t i = 0; i < count; ++i) {
369 // Ignore projection matrix for now. Rasterize 2D triangles. 513 const sgTri3* tri = &tris[i];
370 const sgTri3* tri3 = &tris[i]; 514 DrawTriangle3(gfx, tri);
371 const sgTri2 tri2 = (sgTri2) { 515 }
372 .p0 = (sgVec2){tri3->p0.x, tri3->p0.y}, 516}
373 .p1 = (sgVec2){tri3->p1.x, tri3->p1.y}, 517
374 .p2 = (sgVec2){tri3->p2.x, tri3->p2.y}, 518void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, const sgVec3* positions) {
375 }; 519 assert(gfx);
376 DrawTriangle2(gfx, &tri2); 520 assert(indices);
521 assert(positions);
522 for (size_t i = 0; i < numIndices; i+=3) {
523 const sgIdx i0 = indices[i];
524 const sgIdx i1 = indices[i+1];
525 const sgIdx i2 = indices[i+2];
526 const sgVec3 p0 = positions[i0];
527 const sgVec3 p1 = positions[i1];
528 const sgVec3 p2 = positions[i2];
529 const sgTri3 tri = (sgTri3){p0, p1, p2};
530 DrawTriangle3(gfx, &tri);
531 }
532}
533
534void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tris, const sgVec3* positions) {
535 assert(gfx);
536 assert(tris);
537 assert(positions);
538 for (size_t t = 0; t < numTris; ++t) {
539 const sgTriIdx* triIdx = &tris[t];
540 const sgTri3 tri = (sgTri3){
541 positions[triIdx->v0.position],
542 positions[triIdx->v1.position],
543 positions[triIdx->v2.position]};
544 DrawTriangle3(gfx, &tri);
377 } 545 }
378} 546}
379 547
380static inline void AssertViewportWithinBuffer(swgfx* gfx) { 548static bool ViewportWithinBuffer(swgfx* gfx) {
381 assert(gfx); 549 assert(gfx);
382 const sgViewport_t vp = gfx->viewport; 550 const sgViewport_t vp = gfx->viewport;
383 assert((vp.x0 + vp.width) <= gfx->dims.x); 551 return ((vp.x0 + vp.width) <= gfx->dims.x) &&
384 assert((vp.y0 + vp.height) <= gfx->dims.y); 552 ((vp.y0 + vp.height) <= gfx->dims.y);
385} 553}
386 554
387void sgCheck(swgfx* gfx) { 555void sgCheck(swgfx* gfx) {
388 assert(gfx); 556 assert(gfx);
389 AssertViewportWithinBuffer(gfx); 557 assert(ViewportWithinBuffer(gfx));
390} 558}