gba-x3dtest

annotate src/polyfill.c @ 17:0a7f402892b3

texture mapping
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 26 Jun 2014 06:57:51 +0300
parents b755fb002f17
children 62390f9cc93e
rev   line source
nuclear@5 1 #include "config.h"
nuclear@5 2 #include <string.h>
nuclear@6 3 #include <assert.h>
nuclear@5 4 #include "polyfill.h"
nuclear@5 5 #include "fixed.h"
nuclear@5 6 #include "gbasys.h"
nuclear@5 7
nuclear@15 8 /* TODO: constant interpolant optimization */
nuclear@15 9
nuclear@9 10 #define VNEXT(x, n) (((x) + 1) % (n))
nuclear@9 11 #define VPREV(x, n) ((x) > 0 ? (x) - 1 : (n) - 1)
nuclear@9 12
nuclear@15 13 static void fill_scanline_pal(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1,
nuclear@15 14 int32_t v0, int32_t v1, uint8_t color);
nuclear@15 15 static void fill_scanline_rgb(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1,
nuclear@17 16 int32_t v0, int32_t v1, uint16_t color, struct texture *tex);
nuclear@14 17 static int winding(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
nuclear@5 18
nuclear@17 19 void draw_poly(int num, const pvec3 *verts, const pvec2 *texcoords, uint16_t color,
nuclear@17 20 struct texture *tex)
nuclear@5 21 {
nuclear@5 22 int i, topidx = 0, botidx = 0;
nuclear@9 23 int lidx[2] = {-1, -1}, ridx[2] = {-1, -1};
nuclear@5 24 int32_t y, topy, boty;
nuclear@9 25 int32_t ldy = 0, rdy = 0, ldxdy, rdxdy;
nuclear@5 26 int32_t lx, rx;
nuclear@5 27 int start, end;
nuclear@14 28 pvec3 v0, v1;
nuclear@14 29
nuclear@15 30 int32_t lu, lv, ru, rv, ldudy, ldvdy, rdudy, rdvdy; /* texture interpolants */
nuclear@15 31
nuclear@14 32 v0.x = verts[1].x - verts[0].x;
nuclear@14 33 v0.y = verts[1].y - verts[0].y;
nuclear@14 34
nuclear@14 35 v1.x = verts[2].x - verts[0].x;
nuclear@14 36 v1.y = verts[2].y - verts[0].y;
nuclear@14 37
nuclear@14 38 if(winding(v0.x, v0.y, v1.x, v1.y) < 0) {
nuclear@14 39 return; /* backface */
nuclear@14 40 }
nuclear@5 41
nuclear@5 42 topy = boty = verts[0].y;
nuclear@5 43 for(i=1; i<num; i++) {
nuclear@5 44 int32_t y = verts[i].y;
nuclear@5 45 if(y < topy) {
nuclear@5 46 topy = y;
nuclear@5 47 topidx = i;
nuclear@5 48 }
nuclear@5 49 if(y > boty) {
nuclear@5 50 boty = y;
nuclear@5 51 botidx = i;
nuclear@5 52 }
nuclear@5 53 }
nuclear@5 54
nuclear@5 55 lidx[0] = ridx[0] = topidx;
nuclear@9 56
nuclear@9 57 /* find starting left edge */
nuclear@9 58 lidx[1] = VPREV(lidx[0], num);
nuclear@9 59 ldy = verts[lidx[1]].y - verts[lidx[0]].y;
nuclear@9 60
nuclear@9 61 while(ldy == 0) {
nuclear@9 62 lidx[0] = lidx[1];
nuclear@9 63 lidx[1] = VPREV(lidx[1], num);
nuclear@9 64
nuclear@9 65 if(lidx[1] == topidx) {
nuclear@9 66 return; /* degenerate */
nuclear@9 67 }
nuclear@9 68
nuclear@9 69 ldy = verts[lidx[1]].y - verts[lidx[0]].y;
nuclear@9 70 }
nuclear@9 71 lx = verts[lidx[0]].x;
nuclear@9 72 ldxdy = x16div(verts[lidx[1]].x - lx, ldy);
nuclear@17 73 lu = texcoords[lidx[0]].x;
nuclear@17 74 ldudy = x16div(texcoords[lidx[1]].x - lu, ldy);
nuclear@17 75 lv = texcoords[lidx[0]].y;
nuclear@17 76 ldvdy = x16div(texcoords[lidx[1]].y - lv, ldy);
nuclear@9 77
nuclear@9 78 /* find starting right edge */
nuclear@9 79 ridx[1] = VNEXT(ridx[0], num);
nuclear@9 80 rdy = verts[ridx[1]].y - verts[ridx[0]].y;
nuclear@9 81
nuclear@9 82 while(rdy == 0) {
nuclear@9 83 ridx[0] = ridx[1];
nuclear@9 84 ridx[1] = VNEXT(ridx[1], num);
nuclear@9 85
nuclear@9 86 if(ridx[1] == topidx) {
nuclear@9 87 return; /* degenerate */
nuclear@9 88 }
nuclear@9 89
nuclear@9 90 rdy = verts[ridx[1]].y - verts[ridx[0]].y;
nuclear@9 91 }
nuclear@9 92 rx = verts[ridx[0]].x;
nuclear@9 93 rdxdy = x16div(verts[ridx[1]].x - rx, rdy);
nuclear@17 94 ru = texcoords[ridx[0]].x;
nuclear@17 95 rdudy = x16div(texcoords[ridx[1]].x - ru, rdy);
nuclear@17 96 rv = texcoords[ridx[0]].y;
nuclear@17 97 rdvdy = x16div(texcoords[ridx[1]].y - rv, rdy);
nuclear@9 98
nuclear@5 99 start = topy >> 16;
nuclear@5 100 end = boty >> 16;
nuclear@5 101
nuclear@6 102 if(end >= HEIGHT) end = HEIGHT - 1;
nuclear@6 103
nuclear@5 104 y = topy;
nuclear@14 105 for(i=start; i<=end; i++) {
nuclear@5 106
nuclear@5 107 if(y >= verts[lidx[1]].y) {
nuclear@9 108 lx = verts[lidx[1]].x;
nuclear@5 109 lidx[0] = lidx[1];
nuclear@9 110 lidx[1] = VPREV(lidx[1], num);
nuclear@5 111 ldy = verts[lidx[1]].y - verts[lidx[0]].y;
nuclear@5 112 if(ldy < 0) {
nuclear@5 113 break;
nuclear@5 114 }
nuclear@15 115
nuclear@17 116 lu = texcoords[lidx[0]].x;
nuclear@17 117 lv = texcoords[lidx[0]].y;
nuclear@15 118
nuclear@14 119 if(ldy) {
nuclear@14 120 ldxdy = x16div(verts[lidx[1]].x - lx, ldy);
nuclear@17 121 ldudy = x16div(texcoords[lidx[1]].x - lu, ldy);
nuclear@17 122 ldvdy = x16div(texcoords[lidx[1]].y - lv, ldy);
nuclear@14 123 } else {
nuclear@14 124 ldxdy = verts[lidx[1]].x - lx;
nuclear@17 125 ldudy = texcoords[lidx[1]].x - lu;
nuclear@17 126 ldvdy = texcoords[lidx[1]].y - lv;
nuclear@14 127 }
nuclear@5 128 }
nuclear@5 129 if(y >= verts[ridx[1]].y) {
nuclear@9 130 rx = verts[ridx[1]].x;
nuclear@5 131 ridx[0] = ridx[1];
nuclear@9 132 ridx[1] = VNEXT(ridx[1], num);
nuclear@5 133 rdy = verts[ridx[1]].y - verts[ridx[0]].y;
nuclear@5 134 if(rdy < 0) {
nuclear@5 135 break;
nuclear@5 136 }
nuclear@15 137
nuclear@17 138 ru = texcoords[ridx[0]].x;
nuclear@17 139 rv = texcoords[ridx[0]].y;
nuclear@15 140
nuclear@14 141 if(rdy) {
nuclear@14 142 rdxdy = x16div(verts[ridx[1]].x - rx, rdy);
nuclear@17 143 rdudy = x16div(texcoords[ridx[1]].x - ru, rdy);
nuclear@17 144 rdvdy = x16div(texcoords[ridx[1]].y - rv, rdy);
nuclear@14 145 } else {
nuclear@14 146 rdxdy = verts[ridx[1]].x - rx;
nuclear@17 147 rdudy = texcoords[ridx[1]].x - ru;
nuclear@17 148 rdvdy = texcoords[ridx[1]].y - rv;
nuclear@14 149 }
nuclear@5 150 }
nuclear@5 151
nuclear@15 152 if(i >= 0) {
nuclear@9 153 #ifdef PALMODE
nuclear@15 154 fill_scanline_pal(i, lx, rx, lu, ru, lv, rv, (uint8_t)color);
nuclear@9 155 #else
nuclear@17 156 fill_scanline_rgb(i, lx, rx, lu, ru, lv, rv, color, tex);
nuclear@9 157 #endif
nuclear@9 158 }
nuclear@9 159
nuclear@5 160 lx += ldxdy;
nuclear@5 161 rx += rdxdy;
nuclear@6 162 y += 65536;
nuclear@15 163
nuclear@15 164 lu += ldudy;
nuclear@15 165 lv += ldvdy;
nuclear@15 166 ru += rdudy;
nuclear@15 167 rv += rdvdy;
nuclear@5 168 }
nuclear@5 169 }
nuclear@5 170
nuclear@9 171
nuclear@15 172 static void fill_scanline_pal(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1,
nuclear@15 173 int32_t v0, int32_t v1, uint8_t color)
nuclear@9 174 {
nuclear@15 175 int ix0, ix1;
nuclear@15 176 int32_t dx;
nuclear@15 177 int32_t u, v, dudx, dvdx;
nuclear@15 178
nuclear@15 179 int i, num_pairs, num_pix;
nuclear@15 180 uint16_t *pixels;
nuclear@9 181 uint16_t colpair = (uint16_t)color | ((uint16_t)color << 8);
nuclear@9 182
nuclear@15 183 if(x0 > x1) {
nuclear@15 184 int32_t tmp = x0;
nuclear@15 185 x0 = x1;
nuclear@15 186 x1 = tmp;
nuclear@15 187 }
nuclear@15 188
nuclear@15 189 dx = x1 - x0;
nuclear@15 190
nuclear@15 191 u = u0;
nuclear@15 192 v = v0;
nuclear@15 193 if(dx) {
nuclear@15 194 dudx = x16div(u1 - u0, dx);
nuclear@15 195 dvdx = x16div(v1 - v0, dx);
nuclear@15 196 } else {
nuclear@15 197 dudx = u1 - u0;
nuclear@15 198 dvdx = v1 - v0;
nuclear@15 199 }
nuclear@15 200
nuclear@15 201 ix0 = (x0 + 32768) >> 16;
nuclear@15 202 ix1 = (x1 + 32768) >> 16;
nuclear@15 203
nuclear@15 204 if(ix0 < 0) ix0 = 0;
nuclear@15 205 if(ix1 >= WIDTH - 1) ix1 = WIDTH - 1;
nuclear@15 206
nuclear@15 207 num_pix = ix1 - ix0;
nuclear@15 208 pixels = (uint16_t*)back_buffer->pixels + (y * WIDTH + ix0) / 2;
nuclear@15 209
nuclear@15 210 if(ix0 & 1) {
nuclear@9 211 uint16_t pix = *pixels & 0xff;
nuclear@9 212 *pixels++ = pix | ((uint16_t)color << 8);
nuclear@9 213 --num_pix;
nuclear@15 214 u += dudx;
nuclear@15 215 v += dvdx;
nuclear@9 216 }
nuclear@9 217
nuclear@9 218 num_pairs = (num_pix & 0xfffe) / 2;
nuclear@9 219
nuclear@9 220 for(i=0; i<num_pairs; i++) {
nuclear@9 221 *pixels++ = colpair;
nuclear@15 222 u += dudx * 2;
nuclear@15 223 v += dvdx * 2;
nuclear@9 224 }
nuclear@9 225
nuclear@9 226 if(num_pix & 1) {
nuclear@9 227 uint16_t pix = *pixels & 0xff00;
nuclear@9 228 *pixels = pix | color;
nuclear@9 229 }
nuclear@9 230 }
nuclear@9 231
nuclear@15 232 static void fill_scanline_rgb(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1,
nuclear@17 233 int32_t v0, int32_t v1, uint16_t color, struct texture *tex)
nuclear@5 234 {
nuclear@15 235 int i, ix0, ix1;
nuclear@14 236 uint16_t *pixels;
nuclear@15 237 int32_t dx;
nuclear@15 238 int32_t u, v, dudx, dvdx;
nuclear@5 239
nuclear@14 240 if(x0 > x1) {
nuclear@15 241 int32_t tmp = x0;
nuclear@14 242 x0 = x1;
nuclear@15 243 x1 = tmp;
nuclear@14 244 }
nuclear@14 245
nuclear@15 246 dx = x1 - x0;
nuclear@14 247
nuclear@15 248 u = u0;
nuclear@15 249 v = v0;
nuclear@15 250 if(dx) {
nuclear@15 251 dudx = x16div(u1 - u0, dx);
nuclear@15 252 dvdx = x16div(v1 - v0, dx);
nuclear@15 253 } else {
nuclear@15 254 dudx = u1 - u0;
nuclear@15 255 dvdx = v1 - v0;
nuclear@15 256 }
nuclear@15 257
nuclear@15 258 ix0 = (x0 + 32768) >> 16;
nuclear@15 259 ix1 = (x1 + 32768) >> 16;
nuclear@15 260
nuclear@15 261 if(ix0 < 0) ix0 = 0;
nuclear@15 262 if(ix1 >= WIDTH - 1) ix1 = WIDTH - 1;
nuclear@15 263
nuclear@15 264 pixels = (uint16_t*)back_buffer->pixels + y * WIDTH + ix0;
nuclear@15 265 for(i=ix0; i<ix1; i++) {
nuclear@15 266 /**pixels++ = color;*/
nuclear@15 267 int cr = u >> 8;
nuclear@15 268 int cg = v >> 8;
nuclear@15 269 if(cr > 255) cr = 255;
nuclear@15 270 if(cg > 255) cg = 255;
nuclear@15 271
nuclear@17 272 if(tex) {
nuclear@17 273 int tx = (u >> (16 - tex->ushift)) & tex->umask;
nuclear@17 274 int ty = (v >> (16 - tex->vshift)) & tex->vmask;
nuclear@17 275 uint16_t texel = ((uint16_t*)tex->pixels)[ty * tex->xsz + tx];
nuclear@17 276 *pixels++ = texel;
nuclear@17 277 } else {
nuclear@17 278 *pixels++ = color;
nuclear@17 279 }
nuclear@15 280
nuclear@15 281 u += dudx;
nuclear@15 282 v += dvdx;
nuclear@5 283 }
nuclear@5 284 }
nuclear@8 285
nuclear@8 286
nuclear@8 287 void draw_point(const pvec3 *v, uint16_t color)
nuclear@8 288 {
nuclear@8 289 int x = v->x >> 16;
nuclear@8 290 int y = v->y >> 16;
nuclear@8 291 uint16_t *pixels = (uint16_t*)back_buffer->pixels;
nuclear@8 292
nuclear@8 293 if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) {
nuclear@8 294 return;
nuclear@8 295 }
nuclear@8 296
nuclear@9 297 #ifdef PALMODE
nuclear@9 298 pixels += (y * WIDTH + x) / 2;
nuclear@9 299 if(x & 1) {
nuclear@9 300 *pixels = (*pixels & 0xff) | (color << 8);
nuclear@9 301 } else {
nuclear@9 302 *pixels = (*pixels & 0xff00) | color;
nuclear@9 303 }
nuclear@9 304 #else
nuclear@8 305 pixels[y * WIDTH + x] = color;
nuclear@9 306 #endif
nuclear@8 307 }
nuclear@14 308
nuclear@14 309 static int winding(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
nuclear@14 310 {
nuclear@14 311 return x16mul(x0, y1) - x16mul(y0, x1);
nuclear@14 312 }