polygon filler, rudimentary GL
[gba_blender] / src / polyfill.c
diff --git a/src/polyfill.c b/src/polyfill.c
new file mode 100644 (file)
index 0000000..2595429
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include "polyfill.h"
+#include "debug.h"
+
+static unsigned char *fb;
+static int fbwidth, fbheight;
+static short scantab[2][160] __attribute__((section(".iwram")));
+
+void polyfill_framebuffer(unsigned char *ptr, int w, int h)
+{
+       fb = ptr;
+       fbwidth = w;
+       fbheight = h;
+}
+
+#define VNEXT(p)       (((p) == vlast) ? varr : (p) + 1)
+#define VPREV(p)       ((p) == varr ? vlast : (p) - 1)
+#define VSUCC(p, side) ((side) == 0 ? VNEXT(p) : VPREV(p))
+
+void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col)
+{
+       int i, line, top, bot;
+       struct pvertex *vlast, *v, *vn;
+       int32_t x, y0, y1, dx, dy, slope, fx, fy;
+       short *tab, start, len;
+       unsigned char *fbptr;
+
+       vlast = varr + vnum - 1;
+       top = fbheight;
+       bot = 0;
+
+       for(i=0; i<vnum; i++) {
+               v = varr + i;
+               vn = VNEXT(v);
+
+               if(vn->y == v->y) continue;
+
+               if(vn->y > v->y) {
+                       tab = scantab[0];
+               } else {
+                       tab = scantab[1];
+                       v = vn;
+                       vn = varr + i;
+               }
+
+               dx = vn->x - v->x;
+               dy = vn->y - v->y;
+               slope = (dx << 8) / dy;
+
+               y0 = (v->y + 0x100) & 0xffffff00;       /* start from the next scanline */
+               fy = y0 - v->y;                                         /* fractional part before the next scanline */
+               fx = (fy * slope) >> 8;                         /* X adjust for the step to the next scanline */
+               x = v->x + fx;                                          /* adjust X */
+               y1 = vn->y & 0xffffff00;                        /* last scanline of the edge <= vn->y */
+
+               line = y0 >> 8;
+               if(line < top) top = line;
+               if((y1 >> 8) > bot) bot = y1 >> 8;
+
+               if(line > 0) tab += line;
+
+               while(line < (y1 >> 8) && line < fbheight) {
+                       if(line >= 0) {
+                               int val = x < 0 ? 0 : x >> 8;
+                               *tab++ = val < fbwidth ? val : fbwidth - 1;
+                       }
+                       x += slope;
+                       line++;
+               }
+       }
+
+       fbptr = fb + top * fbwidth;
+       for(i=top; i<bot; i++) {
+               start = scantab[0][i];
+               len = scantab[1][i] - start;
+
+               if(len > 0) {
+                       memset(fbptr + start, col, len);
+               }
+               fbptr += fbwidth;
+       }
+}