nuclear@5: #include "config.h" nuclear@5: #include nuclear@6: #include nuclear@5: #include "polyfill.h" nuclear@5: #include "fixed.h" nuclear@5: #include "gbasys.h" nuclear@5: nuclear@15: /* TODO: constant interpolant optimization */ nuclear@15: nuclear@9: #define VNEXT(x, n) (((x) + 1) % (n)) nuclear@9: #define VPREV(x, n) ((x) > 0 ? (x) - 1 : (n) - 1) nuclear@9: nuclear@15: static void fill_scanline_pal(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1, nuclear@15: int32_t v0, int32_t v1, uint8_t color); nuclear@15: static void fill_scanline_rgb(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1, nuclear@17: int32_t v0, int32_t v1, uint16_t color, struct texture *tex); nuclear@14: static int winding(int32_t x0, int32_t y0, int32_t x1, int32_t y1); nuclear@5: nuclear@17: void draw_poly(int num, const pvec3 *verts, const pvec2 *texcoords, uint16_t color, nuclear@17: struct texture *tex) nuclear@5: { nuclear@5: int i, topidx = 0, botidx = 0; nuclear@9: int lidx[2] = {-1, -1}, ridx[2] = {-1, -1}; nuclear@5: int32_t y, topy, boty; nuclear@9: int32_t ldy = 0, rdy = 0, ldxdy, rdxdy; nuclear@5: int32_t lx, rx; nuclear@5: int start, end; nuclear@14: pvec3 v0, v1; nuclear@14: nuclear@15: int32_t lu, lv, ru, rv, ldudy, ldvdy, rdudy, rdvdy; /* texture interpolants */ nuclear@15: nuclear@14: v0.x = verts[1].x - verts[0].x; nuclear@14: v0.y = verts[1].y - verts[0].y; nuclear@14: nuclear@14: v1.x = verts[2].x - verts[0].x; nuclear@14: v1.y = verts[2].y - verts[0].y; nuclear@14: nuclear@14: if(winding(v0.x, v0.y, v1.x, v1.y) < 0) { nuclear@14: return; /* backface */ nuclear@14: } nuclear@5: nuclear@5: topy = boty = verts[0].y; nuclear@5: for(i=1; i boty) { nuclear@5: boty = y; nuclear@5: botidx = i; nuclear@5: } nuclear@5: } nuclear@5: nuclear@5: lidx[0] = ridx[0] = topidx; nuclear@9: nuclear@9: /* find starting left edge */ nuclear@9: lidx[1] = VPREV(lidx[0], num); nuclear@9: ldy = verts[lidx[1]].y - verts[lidx[0]].y; nuclear@9: nuclear@9: while(ldy == 0) { nuclear@9: lidx[0] = lidx[1]; nuclear@9: lidx[1] = VPREV(lidx[1], num); nuclear@9: nuclear@9: if(lidx[1] == topidx) { nuclear@9: return; /* degenerate */ nuclear@9: } nuclear@9: nuclear@9: ldy = verts[lidx[1]].y - verts[lidx[0]].y; nuclear@9: } nuclear@9: lx = verts[lidx[0]].x; nuclear@9: ldxdy = x16div(verts[lidx[1]].x - lx, ldy); nuclear@17: lu = texcoords[lidx[0]].x; nuclear@17: ldudy = x16div(texcoords[lidx[1]].x - lu, ldy); nuclear@17: lv = texcoords[lidx[0]].y; nuclear@17: ldvdy = x16div(texcoords[lidx[1]].y - lv, ldy); nuclear@9: nuclear@9: /* find starting right edge */ nuclear@9: ridx[1] = VNEXT(ridx[0], num); nuclear@9: rdy = verts[ridx[1]].y - verts[ridx[0]].y; nuclear@9: nuclear@9: while(rdy == 0) { nuclear@9: ridx[0] = ridx[1]; nuclear@9: ridx[1] = VNEXT(ridx[1], num); nuclear@9: nuclear@9: if(ridx[1] == topidx) { nuclear@9: return; /* degenerate */ nuclear@9: } nuclear@9: nuclear@9: rdy = verts[ridx[1]].y - verts[ridx[0]].y; nuclear@9: } nuclear@9: rx = verts[ridx[0]].x; nuclear@9: rdxdy = x16div(verts[ridx[1]].x - rx, rdy); nuclear@17: ru = texcoords[ridx[0]].x; nuclear@17: rdudy = x16div(texcoords[ridx[1]].x - ru, rdy); nuclear@17: rv = texcoords[ridx[0]].y; nuclear@17: rdvdy = x16div(texcoords[ridx[1]].y - rv, rdy); nuclear@9: nuclear@5: start = topy >> 16; nuclear@5: end = boty >> 16; nuclear@5: nuclear@6: if(end >= HEIGHT) end = HEIGHT - 1; nuclear@6: nuclear@5: y = topy; nuclear@14: for(i=start; i<=end; i++) { nuclear@5: nuclear@5: if(y >= verts[lidx[1]].y) { nuclear@9: lx = verts[lidx[1]].x; nuclear@5: lidx[0] = lidx[1]; nuclear@9: lidx[1] = VPREV(lidx[1], num); nuclear@5: ldy = verts[lidx[1]].y - verts[lidx[0]].y; nuclear@5: if(ldy < 0) { nuclear@5: break; nuclear@5: } nuclear@15: nuclear@17: lu = texcoords[lidx[0]].x; nuclear@17: lv = texcoords[lidx[0]].y; nuclear@15: nuclear@14: if(ldy) { nuclear@14: ldxdy = x16div(verts[lidx[1]].x - lx, ldy); nuclear@17: ldudy = x16div(texcoords[lidx[1]].x - lu, ldy); nuclear@17: ldvdy = x16div(texcoords[lidx[1]].y - lv, ldy); nuclear@14: } else { nuclear@14: ldxdy = verts[lidx[1]].x - lx; nuclear@17: ldudy = texcoords[lidx[1]].x - lu; nuclear@17: ldvdy = texcoords[lidx[1]].y - lv; nuclear@14: } nuclear@5: } nuclear@5: if(y >= verts[ridx[1]].y) { nuclear@9: rx = verts[ridx[1]].x; nuclear@5: ridx[0] = ridx[1]; nuclear@9: ridx[1] = VNEXT(ridx[1], num); nuclear@5: rdy = verts[ridx[1]].y - verts[ridx[0]].y; nuclear@5: if(rdy < 0) { nuclear@5: break; nuclear@5: } nuclear@15: nuclear@17: ru = texcoords[ridx[0]].x; nuclear@17: rv = texcoords[ridx[0]].y; nuclear@15: nuclear@14: if(rdy) { nuclear@14: rdxdy = x16div(verts[ridx[1]].x - rx, rdy); nuclear@17: rdudy = x16div(texcoords[ridx[1]].x - ru, rdy); nuclear@17: rdvdy = x16div(texcoords[ridx[1]].y - rv, rdy); nuclear@14: } else { nuclear@14: rdxdy = verts[ridx[1]].x - rx; nuclear@17: rdudy = texcoords[ridx[1]].x - ru; nuclear@17: rdvdy = texcoords[ridx[1]].y - rv; nuclear@14: } nuclear@5: } nuclear@5: nuclear@15: if(i >= 0) { nuclear@9: #ifdef PALMODE nuclear@15: fill_scanline_pal(i, lx, rx, lu, ru, lv, rv, (uint8_t)color); nuclear@9: #else nuclear@17: fill_scanline_rgb(i, lx, rx, lu, ru, lv, rv, color, tex); nuclear@9: #endif nuclear@9: } nuclear@9: nuclear@5: lx += ldxdy; nuclear@5: rx += rdxdy; nuclear@6: y += 65536; nuclear@15: nuclear@15: lu += ldudy; nuclear@15: lv += ldvdy; nuclear@15: ru += rdudy; nuclear@15: rv += rdvdy; nuclear@5: } nuclear@5: } nuclear@5: nuclear@9: nuclear@15: static void fill_scanline_pal(int y, int32_t x0, int32_t x1, int32_t u0, int32_t u1, nuclear@15: int32_t v0, int32_t v1, uint8_t color) nuclear@9: { nuclear@15: int ix0, ix1; nuclear@15: int32_t dx; nuclear@15: int32_t u, v, dudx, dvdx; nuclear@15: nuclear@15: int i, num_pairs, num_pix; nuclear@15: uint16_t *pixels; nuclear@9: uint16_t colpair = (uint16_t)color | ((uint16_t)color << 8); nuclear@9: nuclear@15: if(x0 > x1) { nuclear@15: int32_t tmp = x0; nuclear@15: x0 = x1; nuclear@15: x1 = tmp; nuclear@15: } nuclear@15: nuclear@15: dx = x1 - x0; nuclear@15: nuclear@15: u = u0; nuclear@15: v = v0; nuclear@15: if(dx) { nuclear@15: dudx = x16div(u1 - u0, dx); nuclear@15: dvdx = x16div(v1 - v0, dx); nuclear@15: } else { nuclear@15: dudx = u1 - u0; nuclear@15: dvdx = v1 - v0; nuclear@15: } nuclear@15: nuclear@15: ix0 = (x0 + 32768) >> 16; nuclear@15: ix1 = (x1 + 32768) >> 16; nuclear@15: nuclear@15: if(ix0 < 0) ix0 = 0; nuclear@15: if(ix1 >= WIDTH - 1) ix1 = WIDTH - 1; nuclear@15: nuclear@15: num_pix = ix1 - ix0; nuclear@15: pixels = (uint16_t*)back_buffer->pixels + (y * WIDTH + ix0) / 2; nuclear@15: nuclear@15: if(ix0 & 1) { nuclear@9: uint16_t pix = *pixels & 0xff; nuclear@9: *pixels++ = pix | ((uint16_t)color << 8); nuclear@9: --num_pix; nuclear@15: u += dudx; nuclear@15: v += dvdx; nuclear@9: } nuclear@9: nuclear@9: num_pairs = (num_pix & 0xfffe) / 2; nuclear@9: nuclear@9: for(i=0; i x1) { nuclear@15: int32_t tmp = x0; nuclear@14: x0 = x1; nuclear@15: x1 = tmp; nuclear@14: } nuclear@14: nuclear@15: dx = x1 - x0; nuclear@14: nuclear@15: u = u0; nuclear@15: v = v0; nuclear@15: if(dx) { nuclear@15: dudx = x16div(u1 - u0, dx); nuclear@15: dvdx = x16div(v1 - v0, dx); nuclear@15: } else { nuclear@15: dudx = u1 - u0; nuclear@15: dvdx = v1 - v0; nuclear@15: } nuclear@15: nuclear@15: ix0 = (x0 + 32768) >> 16; nuclear@15: ix1 = (x1 + 32768) >> 16; nuclear@15: nuclear@15: if(ix0 < 0) ix0 = 0; nuclear@15: if(ix1 >= WIDTH - 1) ix1 = WIDTH - 1; nuclear@15: nuclear@15: pixels = (uint16_t*)back_buffer->pixels + y * WIDTH + ix0; nuclear@15: for(i=ix0; i> 8; nuclear@15: int cg = v >> 8; nuclear@15: if(cr > 255) cr = 255; nuclear@15: if(cg > 255) cg = 255; nuclear@15: nuclear@17: if(tex) { nuclear@17: int tx = (u >> (16 - tex->ushift)) & tex->umask; nuclear@17: int ty = (v >> (16 - tex->vshift)) & tex->vmask; nuclear@17: uint16_t texel = ((uint16_t*)tex->pixels)[ty * tex->xsz + tx]; nuclear@17: *pixels++ = texel; nuclear@17: } else { nuclear@17: *pixels++ = color; nuclear@17: } nuclear@15: nuclear@15: u += dudx; nuclear@15: v += dvdx; nuclear@5: } nuclear@5: } nuclear@8: nuclear@8: nuclear@8: void draw_point(const pvec3 *v, uint16_t color) nuclear@8: { nuclear@8: int x = v->x >> 16; nuclear@8: int y = v->y >> 16; nuclear@8: uint16_t *pixels = (uint16_t*)back_buffer->pixels; nuclear@8: nuclear@8: if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) { nuclear@8: return; nuclear@8: } nuclear@8: nuclear@9: #ifdef PALMODE nuclear@9: pixels += (y * WIDTH + x) / 2; nuclear@9: if(x & 1) { nuclear@9: *pixels = (*pixels & 0xff) | (color << 8); nuclear@9: } else { nuclear@9: *pixels = (*pixels & 0xff00) | color; nuclear@9: } nuclear@9: #else nuclear@8: pixels[y * WIDTH + x] = color; nuclear@9: #endif nuclear@8: } nuclear@14: nuclear@14: static int winding(int32_t x0, int32_t y0, int32_t x1, int32_t y1) nuclear@14: { nuclear@14: return x16mul(x0, y1) - x16mul(y0, x1); nuclear@14: }