summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/swgfx.h6
-rw-r--r--src/swgfx.c95
2 files changed, 82 insertions, 19 deletions
diff --git a/include/swgfx.h b/include/swgfx.h
index d1f56df..9905382 100644
--- a/include/swgfx.h
+++ b/include/swgfx.h
@@ -51,11 +51,11 @@ typedef sgRgba sgPixel;
51// TODO: Expose a macro to control the desired surface format. 51// TODO: Expose a macro to control the desired surface format.
52typedef sgBgra sgScreenPixel; 52typedef sgBgra sgScreenPixel;
53 53
54typedef struct sgTexture_t { 54typedef struct sgImage {
55 int width; 55 int width;
56 int height; 56 int height;
57 sgPixel* pixels; 57 sgPixel* pixels;
58} sgTexture_t; 58} sgImage;
59 59
60typedef struct swgfx swgfx; 60typedef struct swgfx swgfx;
61 61
@@ -74,7 +74,7 @@ void sgOrtho (swgfx*, R left, R right, R top, R bottom, R near, R far);
74void sgPerspective(swgfx*, R fovy, R aspect, R near, R far); 74void sgPerspective(swgfx*, R fovy, R aspect, R near, R far);
75void sgViewport (swgfx*, int x0, int y0, int width, int height); 75void sgViewport (swgfx*, int x0, int y0, int width, int height);
76 76
77void sgTexture(swgfx*, const sgTexture_t*); 77void sgTexture(swgfx*, const sgImage*);
78 78
79void sgClear(swgfx*); 79void sgClear(swgfx*);
80void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour); 80void sgPixels(swgfx*, size_t count, const sgVec2i* positions, sgPixel colour);
diff --git a/src/swgfx.c b/src/swgfx.c
index 7ade051..c54fdc1 100644
--- a/src/swgfx.c
+++ b/src/swgfx.c
@@ -46,11 +46,15 @@ typedef struct swgfx {
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 sgTexture_t* texture;// User-specified texture. 49 const sgImage* texture;// User-specified texture.
50 sgTexture_t 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; }
55
56static inline R frac(R a) { return a - (R)((int)a); }
57
54static inline R rmin(R a, R b) { return (a <= b) ? a : b; } 58static inline R rmin(R a, R b) { return (a <= b) ? a : b; }
55static inline R rmax(R a, R b) { return (a >= b) ? a : b; } 59static inline R rmax(R a, R b) { return (a >= b) ? a : b; }
56static inline int imin(int a, int b) { return (a <= b) ? a : b; } 60static inline int imin(int a, int b) { return (a <= b) ? a : b; }
@@ -61,6 +65,8 @@ static inline sgVec2 max2(sgVec2 a, sgVec2 b) { return (sgVec2){.x = rmax(a.x,
61static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(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) }; }
62static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(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) }; }
63 67
68static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; }
69
64static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; } 70static inline sgVec2 add2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x + b.x, a.y + b.y}; }
65static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; } 71static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; }
66 72
@@ -71,6 +77,14 @@ static inline R dot3(sgVec3 a, sgVec3 b) { return a.x * b.x + a.y * b.y + a.z *
71static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } 77static inline R normsq3(sgVec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
72static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); } 78static inline R norm3 (sgVec3 v) { return (R)sqrt(normsq3(v)); }
73 79
80static inline sgVec4 lerp4(sgVec4 a, sgVec4 b, R t) {
81 return (sgVec4){
82 .x = a.x + t * (b.x - a.x),
83 .y = a.y + t * (b.y - a.y),
84 .z = a.z + t * (b.z - a.z),
85 .w = a.w + t * (b.w - a.w)};
86}
87
74static inline sgVec3 cross3(sgVec3 a, sgVec3 b) { 88static inline sgVec3 cross3(sgVec3 a, sgVec3 b) {
75 return (sgVec3) { 89 return (sgVec3) {
76 a.y * b.z - a.z * b.y, 90 a.y * b.z - a.z * b.y,
@@ -237,6 +251,13 @@ static inline sgMat4 Mat4Perspective(R fovy, R aspect, R near, R far) {
237 0, 0, -1, 0); 251 0, 0, -1, 0);
238} 252}
239 253
254static inline sgVec4 PixelToVec4(sgPixel p) {
255 return (sgVec4){(R)p.r / 255.f, (R)p.g / 255.f, (R)p.b / 255.f, (R)p.a / 255.f};
256}
257static inline sgPixel Vec4ToPixel(sgVec4 p) {
258 return (sgPixel){(int)(p.x * 255.f), (int)(p.y * 255.f), (int)(p.z * 255.f), (int)(p.w * 255.f)};
259}
260
240#ifndef _NDEBUG 261#ifndef _NDEBUG
241static bool InBounds(int width, int height, int x, int y) { 262static bool InBounds(int width, int height, int x, int y) {
242 return (0 <= x) && (x < width) && 263 return (0 <= x) && (x < width) &&
@@ -258,28 +279,70 @@ static inline R* Depth(swgfx* gfx, int x, int y) {
258 return gfx->depth + (y * gfx->dims.x) + x; 279 return gfx->depth + (y * gfx->dims.x) + x;
259} 280}
260 281
261void SetPixel(swgfx* gfx, const sgVec2i p, sgPixel colour) { 282static inline void SetPixel(swgfx* gfx, const sgVec2i p, sgPixel colour) {
262 assert(gfx); 283 assert(gfx);
263 *Pixel(gfx, p.x, p.y) = colour; 284 *Pixel(gfx, p.x, p.y) = colour;
264} 285}
265 286
266void SetDepth(swgfx* gfx, const sgVec2i p, R depth) { 287static inline void SetDepth(swgfx* gfx, const sgVec2i p, R depth) {
267 assert(gfx); 288 assert(gfx);
268 *Depth(gfx, p.x, p.y) = depth; 289 *Depth(gfx, p.x, p.y) = depth;
269} 290}
270 291
271// TODO: Mipmapping. 292static inline sgPixel ReadTexture(const sgImage* texture, sgVec2i xy) {
272sgPixel Sample(const sgTexture_t* texture, sgVec2 uv) { 293 assert(texture);
294 assert(texture->pixels);
295 assert(InBounds(texture->width, texture->height, xy.x, xy.y));
296 return texture->pixels[xy.y * texture->width + xy.x];
297}
298// Output normalized to [0,1].
299static inline sgVec4 ReadTextureFloat(const sgImage* texture, sgVec2i xy) {
300 return PixelToVec4(ReadTexture(texture, xy));
301}
302
303static inline sgVec2i TextureRepeat(const sgImage* texture, sgVec2i p) {
304 return (sgVec2i){mod(p.x, texture->width), mod(p.y, texture->height)};
305}
306
307static inline sgPixel SampleNearest(const sgImage* texture, sgVec2 uv) {
308 assert(texture);
309 assert(texture->pixels);
310 const sgVec2i xy = {
311 (int)(uv.x * (R)texture->width),
312 (int)(uv.y * (R)texture->height)};
313 const sgVec2i xy2 = TextureRepeat(texture, xy);
314 return ReadTexture(texture, xy2);
315}
316
317static inline sgPixel SampleBilinear(const sgImage* texture, sgVec2 uv) {
273 assert(texture); 318 assert(texture);
274 assert(texture->pixels); 319 assert(texture->pixels);
275#define INDEX(X,Y) texture->pixels[(Y) * texture->width + (X)] 320#define ADDR(x,y) TextureRepeat(texture, (sgVec2i){x,y})
276 // Doing a nearest sample for now. TODO: Other sampling strategies. 321 // Find the closest grid vertex, then interpolate the 4 neighbouring pixel
277 const int x = (int)(uv.x * (R)texture->width); 322 // centers.
278 const int y = (int)(uv.y * (R)texture->height); 323 const sgVec2i tl = ADDR(
279 // Repeat for now. TODO: Clamping and other strategies. 324 (int)(uv.x * (R)(texture->width - 1)),
280 const int xx = x % texture->width; 325 (int)(uv.y * (R)(texture->height - 1)));
281 const int yy = y % texture->height; 326 const sgVec2i tr = ADDR(tl.x+1, tl.y);
282 return INDEX(xx,yy); 327 const sgVec2i bl = ADDR(tl.x, tl.y+1);
328 const sgVec2i br = ADDR(tl.x+1, tl.y+1);
329 const sgVec2 t = frac2(uv);
330 const sgVec4 tl_pix = ReadTextureFloat(texture, tl);
331 const sgVec4 tr_pix = ReadTextureFloat(texture, tr);
332 const sgVec4 bl_pix = ReadTextureFloat(texture, bl);
333 const sgVec4 br_pix = ReadTextureFloat(texture, br);
334 const sgVec4 x1 = lerp4(tl_pix, tr_pix, t.x);
335 const sgVec4 x2 = lerp4(bl_pix, br_pix, t.x);
336 const sgVec4 y = lerp4(x1, x2, t.y);
337 return Vec4ToPixel(y);
338}
339
340// TODO: Mipmapping.
341// TODO: Clamping and other addressing strategies.
342static inline sgPixel Sample(const sgImage* texture, sgVec2 uv) {
343 // TODO: Add a member to sgImage that determines how it should be filtered.
344 //return SampleNearest(texture, uv);
345 return SampleBilinear(texture, uv);
283} 346}
284 347
285static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { 348static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) {
@@ -489,7 +552,7 @@ swgfx* sgNew(int width, int height, void* mem) {
489 gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); 552 gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel);
490 gfx->depth = SG_ALLOC(&aligned, width * height, R); 553 gfx->depth = SG_ALLOC(&aligned, width * height, R);
491 gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; 554 gfx->defaultPixel = (sgPixel){255, 255, 255, 255};
492 gfx->defaultTexture = (sgTexture_t){ 555 gfx->defaultTexture = (sgImage){
493 .width = 1, 556 .width = 1,
494 .height = 1, 557 .height = 1,
495 .pixels = &gfx->defaultPixel, 558 .pixels = &gfx->defaultPixel,
@@ -578,7 +641,7 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) {
578 gfx->viewport = (sgViewport_t){x0, y0, width, height}; 641 gfx->viewport = (sgViewport_t){x0, y0, width, height};
579} 642}
580 643
581void sgTexture(swgfx* gfx, const sgTexture_t* texture) { 644void sgTexture(swgfx* gfx, const sgImage* texture) {
582 assert(gfx); 645 assert(gfx);
583 assert(texture); 646 assert(texture);
584 gfx->texture = texture; 647 gfx->texture = texture;