From 9e5db7c33df67d80f49f4efd7b8ace5f27e2e7cb Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sun, 1 Feb 2026 18:35:45 -0800 Subject: Fix bilinear filter --- src/swgfx.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/swgfx.c b/src/swgfx.c index 2262dd8..85844e0 100644 --- a/src/swgfx.c +++ b/src/swgfx.c @@ -53,12 +53,13 @@ typedef struct swgfx { static inline int mod(int a, int m) { return (m + (a % m)) % m; } static inline R frac(R a) { return a - (R)((int)a); } - static inline int imin(int a, int b) { return (a <= b) ? a : b; } static inline int imax(int a, int b) { return (a >= b) ? a : b; } -static inline R rmin(R a, R b) { return (a <= b) ? a : b; } -static inline R rmax(R a, R b) { return (a >= b) ? a : b; } -static inline R lerp(R a, R b, R t) { return a + t*(b-a); } + +static inline R rmin(R a, R b) { return (a <= b) ? a : b; } +static inline R rmax(R a, R b) { return (a >= b) ? a : b; } +static inline R lerp(R a, R b, R t) { return a + t*(b-a); } +static inline R mod1(R a, R m) { return fmodf(1.f + fmodf(a, m), 1.f); } static inline sgVec2i min2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imin(a.x, b.x), .y = imin(a.y, b.y) }; } static inline sgVec2i max2i(sgVec2i a, sgVec2i b) { return (sgVec2i){.x = imax(a.x, b.x), .y = imax(a.y, b.y) }; } @@ -69,6 +70,7 @@ static inline sgVec2 sub2(sgVec2 a, sgVec2 b) { return (sgVec2){a.x - b.x, a.y static inline sgVec2 scale2(sgVec2 v, R s) { return (sgVec2){v.x * s, v.y * s}; } static inline sgVec2 frac2(sgVec2 v) { return (sgVec2){frac(v.x), frac(v.y)}; } static inline sgVec2 lerp2(sgVec2 a, sgVec2 b, R t) { return add2(a, scale2(sub2(b,a), t)); } +static inline sgVec2 mod2(sgVec2 v, R m) { return (sgVec2){mod1(v.x, m), mod1(v.y, m)}; } static inline sgVec3 add3(sgVec3 a, sgVec3 b) { return (sgVec3){a.x + b.x, a.y + b.y, a.z + b.z}; } static inline sgVec3 neg3(sgVec3 v) { return (sgVec3){-v.x, -v.y, -v.z}; } @@ -314,33 +316,37 @@ static inline sgVec4 ReadTextureFloat(const sgImage* texture, sgVec2i xy) { return PixelToVec4(ReadTexture(texture, xy)); } +static inline sgVec2i UvToIndex(const sgImage* texture, sgVec2 uv) { + assert(texture); + return (sgVec2i){ + (int)(uv.x * (R)(texture->width - 1)), + (int)(uv.y * (R)(texture->height - 1))}; +} + static inline sgVec2i TextureRepeat(const sgImage* texture, sgVec2i p) { return (sgVec2i){mod(p.x, texture->width), mod(p.y, texture->height)}; } -static inline sgPixel SampleNearest(const sgImage* texture, sgVec2 uv) { +static inline sgPixel FilterNearest(const sgImage* texture, sgVec2 uv) { assert(texture); assert(texture->pixels); - const sgVec2i xy = { - (int)(uv.x * (R)texture->width), - (int)(uv.y * (R)texture->height)}; + const sgVec2i xy = UvToIndex(texture, uv); const sgVec2i xy2 = TextureRepeat(texture, xy); return ReadTexture(texture, xy2); } -static inline sgPixel SampleBilinear(const sgImage* texture, sgVec2 uv) { +static inline sgPixel FilterBilinear(const sgImage* texture, sgVec2 uv) { assert(texture); assert(texture->pixels); #define ADDR(x,y) TextureRepeat(texture, (sgVec2i){x,y}) + const sgVec2 uv01 = mod2(uv, 1.f); // Find the closest grid vertex, then interpolate the 4 neighbouring pixel // centers. - const sgVec2i tl = ADDR( - (int)(uv.x * (R)(texture->width - 1)), - (int)(uv.y * (R)(texture->height - 1))); + const sgVec2i tl = UvToIndex(texture, uv01); const sgVec2i tr = ADDR(tl.x+1, tl.y); const sgVec2i bl = ADDR(tl.x, tl.y+1); const sgVec2i br = ADDR(tl.x+1, tl.y+1); - const sgVec2 t = frac2(uv); + const sgVec2 t = frac2(uv01); const sgVec4 tl_pix = ReadTextureFloat(texture, tl); const sgVec4 tr_pix = ReadTextureFloat(texture, tr); const sgVec4 bl_pix = ReadTextureFloat(texture, bl); @@ -355,10 +361,8 @@ static inline sgPixel SampleBilinear(const sgImage* texture, sgVec2 uv) { // TODO: Clamping and other addressing strategies. static inline sgPixel Sample(const sgImage* texture, sgVec2 uv) { // TODO: Add a member to sgImage that determines how it should be filtered. - return SampleNearest(texture, uv); - // TODO: Debug bilinear. It's producing random colours on the ground, likely - // out-of-bounds memory accesses. - //return SampleBilinear(texture, uv); + //return FilterNearest(texture, uv); + return FilterBilinear(texture, uv); } static inline sgAABB2 TriangleAabb2(sgVec2 p0, sgVec2 p1, sgVec2 p2) { -- cgit v1.2.3