diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-30 17:24:42 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-30 17:24:42 -0800 |
| commit | 57c6cf991b498aa19cc1cb3d736fec08d3643490 (patch) | |
| tree | f50d652ffde35c13a024ee87a4429ef9c32c5ef5 /src/swgfx.c | |
| parent | 75ee45cc65b35d40355178f48ef0c2d90040b863 (diff) | |
Depth buffering and texture coords
Diffstat (limited to 'src/swgfx.c')
| -rw-r--r-- | src/swgfx.c | 202 |
1 files changed, 128 insertions, 74 deletions
diff --git a/src/swgfx.c b/src/swgfx.c index 54f7432..c298fb4 100644 --- a/src/swgfx.c +++ b/src/swgfx.c | |||
| @@ -22,7 +22,6 @@ Coordinate systems: | |||
| 22 | static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; | 22 | static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; |
| 23 | 23 | ||
| 24 | typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; | 24 | typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; |
| 25 | typedef struct sgTri2 { sgVec2 p0, p1, p2; } sgTri2; | ||
| 26 | typedef struct sgAABB2 { sgVec2 pmin, pmax; } sgAABB2; | 25 | typedef struct sgAABB2 { sgVec2 pmin, pmax; } sgAABB2; |
| 27 | 26 | ||
| 28 | // Column-major math, column-major storage. | 27 | // Column-major math, column-major storage. |
| @@ -33,6 +32,7 @@ typedef struct sgMat4 { | |||
| 33 | typedef struct swgfx { | 32 | typedef struct swgfx { |
| 34 | sgVec2i dims; // Colour buffer dimensions. | 33 | sgVec2i dims; // Colour buffer dimensions. |
| 35 | sgPixel* colour; // Colour buffer. | 34 | sgPixel* colour; // Colour buffer. |
| 35 | R* depth; // Depth buffer. | ||
| 36 | sgViewport_t viewport; | 36 | sgViewport_t viewport; |
| 37 | sgMat4 model; // Model matrix. | 37 | sgMat4 model; // Model matrix. |
| 38 | sgMat4 view; // View matrix. | 38 | sgMat4 view; // View matrix. |
| @@ -48,15 +48,25 @@ typedef struct swgfx { | |||
| 48 | sgMat4 mvp; // Model-view-projection matrix. | 48 | sgMat4 mvp; // Model-view-projection matrix. |
| 49 | } swgfx; | 49 | } swgfx; |
| 50 | 50 | ||
| 51 | static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } | 51 | static inline R rmin(R a, R b) { return (a <= b) ? a : b; } |
| 52 | static inline R rmax(R a, R b) { return (a >= b) ? a : b; } | ||
| 53 | static inline int imin(int a, int b) { return (a <= b) ? a : b; } | ||
| 54 | static inline int imax(int a, int b) { return (a >= b) ? a : b; } | ||
| 52 | 55 | ||
| 53 | static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { | 56 | static inline sgVec2 min2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; } |
| 54 | return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; | 57 | static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; } |
| 55 | } | 58 | static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; } |
| 59 | static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; } | ||
| 56 | 60 | ||
| 57 | static inline R dot3(sgVec3 a, sgVec3 b) { | 61 | static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; } |
| 58 | return a.x * b.x + a.y * b.y + a.z * b.z; | 62 | static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; } |
| 59 | } | 63 | |
| 64 | static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } | ||
| 65 | static inline sgVec3 sub3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x - b.x, a.y - b.y, a.z - b.z}; } | ||
| 66 | static inline sgVec3 div3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x / b.x, a.y / b.y, a.z / b.z}; } | ||
| 67 | static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } | ||
| 68 | static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } | ||
| 69 | static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } | ||
| 60 | 70 | ||
| 61 | static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { | 71 | static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { |
| 62 | return (sgVec3) { | 72 | return (sgVec3) { |
| @@ -65,18 +75,12 @@ static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { | |||
| 65 | a.x * b.y - a.y * b.x}; | 75 | a.x * b.y - a.y * b.x}; |
| 66 | } | 76 | } |
| 67 | 77 | ||
| 68 | static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } | ||
| 69 | static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } | ||
| 70 | |||
| 71 | static inline sgVec3 normalize3(sgVec3 v) { | 78 | static inline sgVec3 normalize3(sgVec3 v) { |
| 72 | const R n = norm3(v); | 79 | const R n = norm3(v); |
| 73 | assert(n > 0); | 80 | return (n > 0) ? (sgVec3){v.x / n, v.y / n, v.z / n} : (sgVec3){0, 0, 0}; |
| 74 | return (sgVec3){v.x / n, v.y / n, v.z / n}; | ||
| 75 | } | 81 | } |
| 76 | 82 | ||
| 77 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { | 83 | static inline sgVec4 Vec4FromVec3(sgVec3 v, R w) { return (sgVec4){v.x, v.y, v.z, w}; } |
| 78 | return (sgVec4){v.x, v.y, v.z, w}; | ||
| 79 | } | ||
| 80 | 84 | ||
| 81 | static inline sgMat4 Mat4( | 85 | static inline sgMat4 Mat4( |
| 82 | R m00, R m01, R m02, R m03, // v0.x v1.x v2.x v3.x | 86 | R m00, R m01, R m02, R m03, // v0.x v1.x v2.x v3.x |
| @@ -231,37 +235,39 @@ static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) { | |||
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | #ifndef _NDEBUG | 237 | #ifndef _NDEBUG |
| 234 | static bool InBounds(int width, int height, sgVec2i p) { | 238 | static bool InBounds(int width, int height, int x, int y) { |
| 235 | return (0 <= p.x) && (p.x < width) && | 239 | return (0 <= x) && (x < width) && |
| 236 | (0 <= p.y) && (p.y < height); | 240 | (0 <= y) && (y < height); |
| 237 | } | 241 | } |
| 238 | #endif // _NDEBUG | 242 | #endif // _NDEBUG |
| 239 | 243 | ||
| 240 | static inline sgPixel* Pixel(sgPixel* image, int width, int height, int x, int y) { | 244 | static inline sgPixel* Pixel(swgfx* gfx, int x, int y) { |
| 241 | assert(InBounds(width, height, (sgVec2i){x,y})); | 245 | assert(gfx); |
| 242 | return image + (y * width) + x; | 246 | assert(gfx->colour); |
| 247 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); | ||
| 248 | return gfx->colour + (y * gfx->dims.x) + x; | ||
| 243 | } | 249 | } |
| 244 | 250 | ||
| 245 | static inline R rmin(R a, R b) { return (a <= b) ? a : b; } | 251 | static inline R* Depth(swgfx* gfx, int x, int y) { |
| 246 | static inline R rmax(R a, R b) { return (a >= b) ? a : b; } | 252 | assert(gfx); |
| 247 | static inline int imin(int a, int b) { return (a <= b) ? a : b; } | 253 | assert(gfx->depth); |
| 248 | static inline int imax(int a, int b) { return (a >= b) ? a : b; } | 254 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); |
| 249 | static inline sgVec2 min2(sgVec2 a, sgVec2 b) { | 255 | return gfx->depth + (y * gfx->dims.x) + x; |
| 250 | return (sgVec2){.x = rmin(a.x, b.x), .y = rmin(a.y, b.y) }; | ||
| 251 | } | ||
| 252 | static inline sgVec2 max2(sgVec2 a, sgVec2 b) { | ||
| 253 | return (sgVec2){.x = rmax(a.x, b.x), .y = rmax(a.y, b.y) }; | ||
| 254 | } | 256 | } |
| 255 | static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { | 257 | |
| 256 | return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; | 258 | void SetPixel(swgfx* gfx, const sgVec2i p, sgPixel colour) { |
| 259 | assert(gfx); | ||
| 260 | *Pixel(gfx, p.x, p.y) = colour; | ||
| 257 | } | 261 | } |
| 258 | static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { | 262 | |
| 259 | return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; | 263 | void SetDepth(swgfx* gfx, const sgVec2i p, R depth) { |
| 264 | assert(gfx); | ||
| 265 | *Depth(gfx, p.x, p.y) = depth; | ||
| 260 | } | 266 | } |
| 261 | 267 | ||
| 262 | static inline sgAABB2 TriangleAabb2(const sgTri2 tri) { | 268 | static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { |
| 263 | return (sgAABB2){.pmin = min2(min2(tri.p0, tri.p1), tri.p2), | 269 | return (sgAABB2){.pmin = min2(min2(p0, p1), p2), |
| 264 | .pmax = max2(max2(tri.p0, tri.p1), tri.p2)}; | 270 | .pmax = max2(max2(p0, p1), p2)}; |
| 265 | } | 271 | } |
| 266 | 272 | ||
| 267 | static inline sgVec2i Clip(const swgfx* gfx, const sgVec2i p) { | 273 | static inline sgVec2i Clip(const swgfx* gfx, const sgVec2i p) { |
| @@ -272,30 +278,45 @@ static inline sgVec2i Clip(const swgfx* gfx, const sgVec2i p) { | |||
| 272 | return max2i(lower, min2i(upper, p)); | 278 | return max2i(lower, min2i(upper, p)); |
| 273 | } | 279 | } |
| 274 | 280 | ||
| 281 | static inline R BarycentricInterp1(sgVec3 bar, R a, R b, R c) { | ||
| 282 | return bar.x*a + bar.y*b + bar.z*c; | ||
| 283 | } | ||
| 284 | static inline sgVec2 BarycentricInterp2(sgVec3 bar, sgVec2 a, sgVec2 b, sgVec2 c) { | ||
| 285 | return add2(add2(scale2(a, bar.x), scale2(b, bar.y)), scale2(c, bar.z)); | ||
| 286 | } | ||
| 287 | static inline sgVec2 PerspectiveInterp2(sgVec3 bar, sgVec3 depths, R z, sgVec2 a, sgVec2 b, sgVec2 c) { | ||
| 288 | return scale2(BarycentricInterp2(div3(bar, depths), a, b, c), z); | ||
| 289 | } | ||
| 290 | |||
| 275 | static inline R f(sgVec2 a, sgVec2 b, sgVec2 p) { | 291 | static inline R f(sgVec2 a, sgVec2 b, sgVec2 p) { |
| 276 | return (a.y - b.y)*p.x + (b.x - a.x)*p.y + a.x*b.y - b.x*a.y; | 292 | return (a.y - b.y)*p.x + (b.x - a.x)*p.y + a.x*b.y - b.x*a.y; |
| 277 | } | 293 | } |
| 278 | 294 | ||
| 279 | static inline sgVec3 Barycentric(const sgTri2 tri, sgVec2 p) { | 295 | static inline sgVec3 Barycentric(sgVec2 p0, sgVec2 p1, sgVec2 p2, sgVec2 p) { |
| 280 | // There is no need to compute the third coordinate explicitly: a + b + c = 1. | 296 | // There is no need to compute the third coordinate explicitly: a + b + c = 1. |
| 281 | // But this results in a worse rasterization of the triangle along one of the edges. | 297 | // But this results in a worse rasterization of the triangle along one of the edges. |
| 282 | // It seems we can patch it with a small epsilon, though. | 298 | // It seems we can patch it with a small epsilon, though. |
| 283 | // --- | 299 | // --- |
| 284 | // Division by zero is only possible if the triangle has zero area. | 300 | // Division by zero is only possible if the triangle has zero area. |
| 285 | /*return (sgVec3){ | 301 | /*return (sgVec3){ |
| 286 | f(tri.p1, tri.p2, p) / f(tri.p1, tri.p2, tri.p0), | 302 | f(p1, p2, p) / f(p1, p2, p0), |
| 287 | f(tri.p2, tri.p0, p) / f(tri.p2, tri.p0, tri.p1), | 303 | f(p2, p0, p) / f(p2, p0, p1), |
| 288 | f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2)};*/ | 304 | f(p0, p1, p) / f(p0, p1, p2)};*/ |
| 289 | const R b = f(tri.p0, tri.p2, p) / f(tri.p0, tri.p2, tri.p1); | 305 | const R b = f(p0, p2, p) / f(p0, p2, p1); |
| 290 | const R c = f(tri.p0, tri.p1, p) / f(tri.p0, tri.p1, tri.p2); | 306 | const R c = f(p0, p1, p) / f(p0, p1, p2); |
| 291 | const R a = /*f(tri.p1, tri.p2, p) / f(tri.p1, tri.p2, tri.p0);*/1.f - b - c - (R)1e-7; | 307 | const R a = /*f(p1, p2, p) / f(p1, p2, p0);*/1.f - b - c - (R)1e-7; |
| 292 | return (sgVec3){a,b,c}; | 308 | return (sgVec3){a,b,c}; |
| 293 | } | 309 | } |
| 294 | 310 | ||
| 295 | static void DrawTriangle2(swgfx* gfx, const sgTri2* tri) { | 311 | // Input triangle in screen space. Retains Z for depth testing and vertex |
| 312 | // attribute interpolation. | ||
| 313 | static void DrawTriangle2(swgfx* gfx, const sgTri3* const tri) { | ||
| 296 | assert(gfx); | 314 | assert(gfx); |
| 297 | assert(tri); | 315 | assert(tri); |
| 298 | const sgAABB2 bbox = TriangleAabb2(*tri); | 316 | const sgVec2 p0 = (sgVec2){tri->p0.pos.x, tri->p0.pos.y}; |
| 317 | const sgVec2 p1 = (sgVec2){tri->p1.pos.x, tri->p1.pos.y}; | ||
| 318 | const sgVec2 p2 = (sgVec2){tri->p2.pos.x, tri->p2.pos.y}; | ||
| 319 | const sgAABB2 bbox = TriangleAabb2(p0, p1, p2); | ||
| 299 | // We consider (x,y) to be the pixel center. | 320 | // We consider (x,y) to be the pixel center. |
| 300 | // Draw all pixels touched by the bounding box. TODO: Multi-sampling. | 321 | // Draw all pixels touched by the bounding box. TODO: Multi-sampling. |
| 301 | sgVec2i pmin = (sgVec2i){(int)bbox.pmin.x, (int)bbox.pmin.y}; | 322 | sgVec2i pmin = (sgVec2i){(int)bbox.pmin.x, (int)bbox.pmin.y}; |
| @@ -309,14 +330,23 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* tri) { | |||
| 309 | const sgVec2 p = (sgVec2){(R)x, (R)y}; | 330 | const sgVec2 p = (sgVec2){(R)x, (R)y}; |
| 310 | // TODO: there is an incremental optimization to computing barycentric coordinates; | 331 | // TODO: there is an incremental optimization to computing barycentric coordinates; |
| 311 | // read more about it. | 332 | // read more about it. |
| 312 | const sgVec3 bar = Barycentric(*tri, p); | 333 | const sgVec3 bar = Barycentric(p0, p1, p2, p); |
| 313 | // We need to check the third coordinate. | 334 | // We need to check the third coordinate. |
| 314 | // a + b + c = 1 | 335 | // a + b + c = 1 |
| 315 | // So, e.g., if a >= 0 and b >= 0, then we have c <= 1, but we could also have c <= 0. | 336 | // So, e.g., if a >= 0 and b >= 0, then we have c <= 1, but we could also have c <= 0. |
| 316 | // In the case c <= 0, then point is outside the triangle. | 337 | // In the case c <= 0, then point is outside the triangle. |
| 317 | if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { | 338 | if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { |
| 318 | const sgVec2i pi = (sgVec2i){(int)x, (int)y}; | 339 | const R z = BarycentricInterp1(bar, tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z); |
| 319 | sgPixels(gfx, 1, &pi, (sgPixel){255, 255, 255, 255}); | 340 | R* depth = Depth(gfx, x, y); |
| 341 | if ((0.f <= z) && (z <= 1.f) && (z <= *depth)) { | ||
| 342 | *depth = z; | ||
| 343 | const sgVec3 depths = (sgVec3){tri->p0.pos.z, tri->p1.pos.z, tri->p2.pos.z}; | ||
| 344 | const sgVec2 uv = PerspectiveInterp2(bar, depths, z, tri->p0.uv, tri->p1.uv, tri->p2.uv); | ||
| 345 | const int r = (int)(uv.x * 255.f); | ||
| 346 | const int g = (int)(uv.y * 255.f); | ||
| 347 | const sgVec2i pix = (sgVec2i){(int)x, (int)y}; | ||
| 348 | SetPixel(gfx, pix, (sgPixel){r, g, 255, 255}); | ||
| 349 | } | ||
| 320 | } | 350 | } |
| 321 | } | 351 | } |
| 322 | } | 352 | } |
| @@ -327,17 +357,18 @@ static inline sgVec3 PerspDivide(sgVec4 v) { | |||
| 327 | } | 357 | } |
| 328 | 358 | ||
| 329 | // TODO: Compute a viewport matrix in sgViewport() instead. | 359 | // TODO: Compute a viewport matrix in sgViewport() instead. |
| 330 | static inline sgVec2 ViewportTransform(sgViewport_t vp, sgVec3 ndc) { | 360 | static inline sgVec3 ViewportTransform(sgViewport_t vp, sgVec3 ndc) { |
| 331 | return (sgVec2){ | 361 | return (sgVec3){ |
| 332 | .x = (ndc.x+1.f) * ((R)vp.width/2.f) + (R)vp.x0, | 362 | .x = (ndc.x+1.f) * ((R)vp.width/2.f) + (R)vp.x0, |
| 333 | .y = (ndc.y+1.f) * ((R)vp.height/2.f) + (R)vp.y0}; | 363 | .y = (ndc.y+1.f) * ((R)vp.height/2.f) + (R)vp.y0, |
| 364 | .z = ndc.z}; | ||
| 334 | } | 365 | } |
| 335 | 366 | ||
| 336 | static inline sgVec2 ViewportToWindow(sgViewport_t vp, sgVec2 p) { | 367 | static inline sgVec3 ViewportToWindow(sgViewport_t vp, sgVec3 p) { |
| 337 | return (sgVec2){p.x, (R)vp.height - p.y}; | 368 | return (sgVec3){p.x, (R)vp.height - p.y, p.z}; |
| 338 | } | 369 | } |
| 339 | 370 | ||
| 340 | static inline sgVec2 TransformPosition(const swgfx* gfx, sgVec3 p) { | 371 | static inline sgVec3 TransformPosition(const swgfx* gfx, sgVec3 p) { |
| 341 | assert(gfx); | 372 | assert(gfx); |
| 342 | // Model to clip space. | 373 | // Model to clip space. |
| 343 | const sgVec4 p_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(p, 1)); | 374 | const sgVec4 p_clip = Mat4MulVec4(gfx->mvp, Vec4FromVec3(p, 1)); |
| @@ -345,18 +376,24 @@ static inline sgVec2 TransformPosition(const swgfx* gfx, sgVec3 p) { | |||
| 345 | // Perspective divide. | 376 | // Perspective divide. |
| 346 | const sgVec3 p_ndc = PerspDivide(p_clip); | 377 | const sgVec3 p_ndc = PerspDivide(p_clip); |
| 347 | // TODO: Clip. | 378 | // TODO: Clip. |
| 348 | const sgVec2 p_vp = ViewportTransform(gfx->viewport, p_ndc); | 379 | const sgVec3 p_vp = ViewportTransform(gfx->viewport, p_ndc); |
| 349 | return ViewportToWindow(gfx->viewport, p_vp); | 380 | return ViewportToWindow(gfx->viewport, p_vp); |
| 350 | } | 381 | } |
| 351 | 382 | ||
| 352 | static void DrawTriangle3(swgfx* gfx, const sgTri3* tri) { | 383 | static void DrawTriangle3(swgfx* gfx, const sgTri3* const tri) { |
| 353 | assert(gfx); | 384 | assert(gfx); |
| 354 | assert(tri); | 385 | assert(tri); |
| 355 | const sgVec2 p0 = TransformPosition(gfx, tri->p0); | 386 | const sgVec3 p0 = TransformPosition(gfx, tri->p0.pos); |
| 356 | const sgVec2 p1 = TransformPosition(gfx, tri->p1); | 387 | const sgVec3 p1 = TransformPosition(gfx, tri->p1.pos); |
| 357 | const sgVec2 p2 = TransformPosition(gfx, tri->p2); | 388 | const sgVec3 p2 = TransformPosition(gfx, tri->p2.pos); |
| 358 | const sgTri2 tri2 = (sgTri2){p0, p1, p2}; | 389 | const sgVec2 uv0 = tri->p0.uv; |
| 359 | DrawTriangle2(gfx, &tri2); | 390 | const sgVec2 uv1 = tri->p1.uv; |
| 391 | const sgVec2 uv2 = tri->p2.uv; | ||
| 392 | const sgTri3 tri_screen = (sgTri3){ | ||
| 393 | (sgVert3){p0, uv0}, | ||
| 394 | (sgVert3){p1, uv1}, | ||
| 395 | (sgVert3){p2, uv2}}; | ||
| 396 | DrawTriangle2(gfx, &tri_screen); | ||
| 360 | } | 397 | } |
| 361 | 398 | ||
| 362 | #define is_pow2_or_0(X) ((X & (X - 1)) == 0) | 399 | #define is_pow2_or_0(X) ((X & (X - 1)) == 0) |
| @@ -389,6 +426,7 @@ static void* Alloc(void** ppMem, size_t count, size_t size) { | |||
| 389 | size_t sgMem(int width, int height) { | 426 | size_t sgMem(int width, int height) { |
| 390 | return Align(sizeof(swgfx)) + | 427 | return Align(sizeof(swgfx)) + |
| 391 | Align(width * height * sizeof(sgPixel)) + | 428 | Align(width * height * sizeof(sgPixel)) + |
| 429 | Align(width * height * sizeof(R)) + | ||
| 392 | (SG_ALIGN - 1); // To make room to align allocations within the buffer. | 430 | (SG_ALIGN - 1); // To make room to align allocations within the buffer. |
| 393 | } | 431 | } |
| 394 | 432 | ||
| @@ -397,6 +435,7 @@ swgfx* sgNew(int width, int height, void* mem) { | |||
| 397 | swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); | 435 | swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); |
| 398 | gfx->dims = (sgVec2i){width, height}; | 436 | gfx->dims = (sgVec2i){width, height}; |
| 399 | gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); | 437 | gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); |
| 438 | gfx->depth = SG_ALLOC(&aligned, width * height, R); | ||
| 400 | return gfx; | 439 | return gfx; |
| 401 | } | 440 | } |
| 402 | 441 | ||
| @@ -481,15 +520,17 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { | |||
| 481 | 520 | ||
| 482 | void sgClear(swgfx* gfx) { | 521 | void sgClear(swgfx* gfx) { |
| 483 | assert(gfx); | 522 | assert(gfx); |
| 484 | memset(gfx->colour, 0, gfx->dims.x * gfx->dims.y * sizeof(sgPixel)); | 523 | const int N = gfx->dims.x * gfx->dims.y; |
| 524 | memset(gfx->colour, 0, N * sizeof(*gfx->colour)); | ||
| 525 | for (int i = 0; i < N; ++i) { | ||
| 526 | gfx->depth[i] = 1.0f; | ||
| 527 | } | ||
| 485 | } | 528 | } |
| 486 | 529 | ||
| 487 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { | 530 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { |
| 488 | assert(gfx); | 531 | assert(gfx); |
| 489 | #define XY(X,Y) Pixel(gfx->colour, gfx->dims.x, gfx->dims.y, X, Y) | ||
| 490 | for (size_t i = 0; i < count; ++i) { | 532 | for (size_t i = 0; i < count; ++i) { |
| 491 | const sgVec2i p = positions[i]; | 533 | SetPixel(gfx, positions[i], colour); |
| 492 | *XY(p.x, p.y) = colour; | ||
| 493 | } | 534 | } |
| 494 | } | 535 | } |
| 495 | 536 | ||
| @@ -507,7 +548,12 @@ void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour | |||
| 507 | void sgTriangles2(swgfx* gfx, size_t count, const sgTri2* tris) { | 548 | void sgTriangles2(swgfx* gfx, size_t count, const sgTri2* tris) { |
| 508 | assert(gfx); | 549 | assert(gfx); |
| 509 | for (size_t i = 0; i < count; ++i) { | 550 | for (size_t i = 0; i < count; ++i) { |
| 510 | DrawTriangle2(gfx, &tris[i]); | 551 | const sgTri3 tri3 = (sgTri3) { |
| 552 | .p0 = (sgVert3){.pos = (sgVec3){tris[i].p0.pos.x, tris[i].p0.pos.y, 0}, .uv = tris[i].p0.uv}, | ||
| 553 | .p1 = (sgVert3){.pos = (sgVec3){tris[i].p1.pos.x, tris[i].p1.pos.y, 0}, .uv = tris[i].p1.uv}, | ||
| 554 | .p2 = (sgVert3){.pos = (sgVec3){tris[i].p2.pos.x, tris[i].p2.pos.y, 0}, .uv = tris[i].p2.uv}, | ||
| 555 | }; | ||
| 556 | DrawTriangle2(gfx, &tri3); | ||
| 511 | } | 557 | } |
| 512 | } | 558 | } |
| 513 | 559 | ||
| @@ -520,10 +566,11 @@ void sgTriangles(swgfx* gfx, size_t count, const sgTri3* tris, const sgNormal*) | |||
| 520 | } | 566 | } |
| 521 | } | 567 | } |
| 522 | 568 | ||
| 523 | void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, const sgVec3* positions) { | 569 | void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, const sgVec3* positions, const sgVec2* texcoords) { |
| 524 | assert(gfx); | 570 | assert(gfx); |
| 525 | assert(indices); | 571 | assert(indices); |
| 526 | assert(positions); | 572 | assert(positions); |
| 573 | assert(texcoords); | ||
| 527 | for (size_t i = 0; i < numIndices; i+=3) { | 574 | for (size_t i = 0; i < numIndices; i+=3) { |
| 528 | const sgIdx i0 = indices[i]; | 575 | const sgIdx i0 = indices[i]; |
| 529 | const sgIdx i1 = indices[i+1]; | 576 | const sgIdx i1 = indices[i+1]; |
| @@ -531,21 +578,28 @@ void sgTrianglesIndexed(swgfx* gfx, size_t numIndices, const sgIdx* indices, con | |||
| 531 | const sgVec3 p0 = positions[i0]; | 578 | const sgVec3 p0 = positions[i0]; |
| 532 | const sgVec3 p1 = positions[i1]; | 579 | const sgVec3 p1 = positions[i1]; |
| 533 | const sgVec3 p2 = positions[i2]; | 580 | const sgVec3 p2 = positions[i2]; |
| 534 | const sgTri3 tri = (sgTri3){p0, p1, p2}; | 581 | const sgVec2 uv0 = texcoords[i0]; |
| 582 | const sgVec2 uv1 = texcoords[i1]; | ||
| 583 | const sgVec2 uv2 = texcoords[i2]; | ||
| 584 | const sgTri3 tri = (sgTri3){ | ||
| 585 | (sgVert3){p0, uv0}, | ||
| 586 | (sgVert3){p1, uv1}, | ||
| 587 | (sgVert3){p2, uv2}}; | ||
| 535 | DrawTriangle3(gfx, &tri); | 588 | DrawTriangle3(gfx, &tri); |
| 536 | } | 589 | } |
| 537 | } | 590 | } |
| 538 | 591 | ||
| 539 | void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tris, const sgVec3* positions) { | 592 | void sgTrianglesIndexedNonUniform(swgfx* gfx, size_t numTris, const sgTriIdx* tris, const sgVec3* positions, const sgVec2* texcoords) { |
| 540 | assert(gfx); | 593 | assert(gfx); |
| 541 | assert(tris); | 594 | assert(tris); |
| 542 | assert(positions); | 595 | assert(positions); |
| 596 | assert(texcoords); | ||
| 543 | for (size_t t = 0; t < numTris; ++t) { | 597 | for (size_t t = 0; t < numTris; ++t) { |
| 544 | const sgTriIdx* triIdx = &tris[t]; | 598 | const sgTriIdx* triIdx = &tris[t]; |
| 545 | const sgTri3 tri = (sgTri3){ | 599 | const sgTri3 tri = (sgTri3){ |
| 546 | positions[triIdx->v0.position], | 600 | (sgVert3){positions[triIdx->v0.pos], texcoords[triIdx->v0.uv]}, |
| 547 | positions[triIdx->v1.position], | 601 | (sgVert3){positions[triIdx->v1.pos], texcoords[triIdx->v1.uv]}, |
| 548 | positions[triIdx->v2.position]}; | 602 | (sgVert3){positions[triIdx->v2.pos], texcoords[triIdx->v2.uv]}}; |
| 549 | DrawTriangle3(gfx, &tri); | 603 | DrawTriangle3(gfx, &tri); |
| 550 | } | 604 | } |
| 551 | } | 605 | } |
