Basic 3D graphics for the MBED application-shield on-board LCD (initial/incomplete).

Dependents:   co657_lcdplay

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers g3d_render.cpp Source File

g3d_render.cpp

00001 /*
00002  *  g3d_render.cpp -- rendering stuff for G3D library
00003  *  Copyright (C) 2015 Fred Barnes, University of Kent
00004  *  GPL >= 2.0
00005  */
00006 
00007 
00008 #include "mbed.h"
00009 #include "C12832.h"
00010 #include "gfx3d.h"
00011 
00012 #define DISPLAY_WIDTH   (128)
00013 #define DISPLAY_HEIGHT  (32)
00014 
00015 #define ZB_YSHIFT       (7)
00016 
00017 /* grotty: single global Z buffer that matches the LCD size */
00018 static int16_t zbuffer[DISPLAY_HEIGHT * DISPLAY_WIDTH];
00019 
00020 #define ZBUFFER(X,Y) zbuffer[(Y << ZB_YSHIFT) | X]
00021 
00022 /**
00023  *  clears the Z buffer (sets all to maximum)
00024  */
00025 void gfx3d_clear_zb (void)
00026 {
00027     int i;
00028     int lim = (DISPLAY_HEIGHT * DISPLAY_WIDTH) >> 1;
00029     uint32_t *halfbuf = (uint32_t *)zbuffer;
00030     
00031     for (i=0; i<lim; i++) {
00032         halfbuf[i] = 0x7fff7fff;
00033     }
00034 }
00035 
00036 #if 0
00037 /**
00038  *  fixes scan-line starts/ends in a g3d_polyscan_t structure based on edge
00039  */
00040 static void gfx3d_polyfix (g3d_polyscan_t *dst, int16_t x1, int16_t y1, int32_t z1, int16_t x2, int16_t y2, int32_t z2, const float co_a, const float co_b, const float co_c, const float co_d)
00041 {
00042     if (y2 < y1) {
00043         /* swap around: make sure we go in the same direction (down) */
00044         int16_t t16;
00045         int32_t t32;
00046         
00047         t16 = x1;
00048         x1 = x2;
00049         x2 = t16;
00050         
00051         t16 = y1;
00052         y1 = y2;
00053         y2 = t16;
00054         
00055         t32 = z1;
00056         z1 = z2;
00057         z2 = t32;
00058     }
00059     
00060     /* scan limit */
00061     if (y1 < dst->scan_s) {
00062         dst->scan_s = y1;
00063     }
00064     if (y2 > dst->scan_e) {
00065         dst->scan_e = y2;
00066     }
00067     if (y1 == y2) {
00068         /* flat polygon */
00069         dst->scans[y1][0] = x1;
00070         dst->scans[y1][1] = x2;
00071         dst->zscan[y1][0] = z1;
00072         dst->zscan[y1][1] = z2;
00073     } else {
00074         /* vaguely complex polygon */
00075         int x, y, z, step, z_step;
00076         
00077         x = (int)x1 << 16;
00078         step = ((int)(x2 - x1) << 16) / (int)(y2 - y1);
00079         x += step;
00080         
00081         z = z1;
00082         z_step = (z2 - z1) / ((y2 - y1) + 1);
00083         y1++;
00084         
00085         for (y=y1; y<=y2; y++) {
00086             if ((y >= 0) && (y < 32)) {
00087                 int shx = x >> 16;
00088                 
00089                 if (x < 0) {
00090                     shx = shx - 65536;
00091                 }
00092                 if (dst->scans[y][0] == 0xff) {
00093                     /* start of scan */
00094                     uint8_t x8 = (uint8_t)(shx & 0xff);
00095                     
00096                     if (x8 == 0xff) {
00097                         x8 = 128;
00098                     }
00099                     dst->scans[y][0] = x8;
00100                     if (co_c != 0.0f) {
00101                         dst->zscan[y][0] = (int32_t)(((-(co_a * (float)shx) - (co_b * (float)y)) - co_d) / co_c);
00102                     } else {
00103                         dst->zscan[y][0] = z;
00104                     }
00105                 } else {
00106                     /* end of scan */
00107                     uint8_t x8 = (uint8_t)(shx & 0xff);
00108                     
00109                     if (x8 == 0xff) {
00110                         x8 = 128;
00111                     }
00112                     dst->scans[y][1] = x8;
00113                     if (co_c != 0.0f) {
00114                         dst->zscan[y][1] = (int32_t)(((-(co_a * (float)shx) - (co_b * (float)y)) - co_d) / co_c);
00115                     } else {
00116                         dst->zscan[y][1] = z;
00117                     }
00118                 }
00119             }
00120             x += step;
00121             z += z_step;
00122         }
00123     }
00124 }
00125 
00126 
00127 /**
00128  *  takes a polygon structure and generates a set of scanline data from it
00129  */
00130 void gfx3d_polyscan (const g3d_poly_t *src, g3d_polyscan_t *dst)
00131 {
00132     int i;
00133     float co_a, co_b, co_c, co_d;
00134     float x1 = (float)src->x[0];
00135     float y1 = (float)src->y[0];
00136     float z1 = (float)src->z[0];
00137     float x2 = (float)src->x[1];
00138     float y2 = (float)src->y[1];
00139     float z2 = (float)src->z[1];
00140     float x3 = (float)src->x[2];
00141     float y3 = (float)src->y[2];
00142     float z3 = (float)src->z[2];
00143     
00144     for (i=0; i<32; i++) {
00145         dst->scans[i][0] = 0xff;
00146         dst->scans[i][1] = 0xff;
00147         dst->zscan[i][0] = 0;
00148         dst->zscan[i][1] = 0;
00149     }
00150     dst->scan_s = 32;
00151     dst->scan_e = 0;
00152     dst->norm = src->norm;
00153     
00154     /* coefficients for plane equations, Herne & Baker p308 */
00155     co_a = ((y1 * (z2 - z3)) + (y2 * (z3 - z1))) + (y3 * (z1 - z2));
00156     co_b = ((z1 * (x2 - x3)) + (z2 * (x3 - x1))) + (z3 * (x1 - x2));
00157     co_c = ((x1 * (y2 - y3)) + (x2 * (y3 - y1))) + (x3 * (y1 - y2));
00158     co_d = ((-x1 * ((y2 * z3) - (y3 * z2))) - (x2 * ((y3 * z1) - (y1 * z3)))) - (x3 * ((y1 * z2) - (y2 * z1)));
00159     
00160     gfx3d_polyfix (dst, src->x[0], src->y[0], src->z[0], src->x[1], src->y[1], src->z[1], co_a, co_b, co_c, co_d);
00161     gfx3d_polyfix (dst, src->x[1], src->y[1], src->z[1], src->x[2], src->y[2], src->z[2], co_a, co_b, co_c, co_d);
00162     gfx3d_polyfix (dst, src->x[2], src->y[2], src->z[2], src->x[0], src->y[0], src->z[0], co_a, co_b, co_c, co_d);
00163     
00164     if (dst->scan_s < 0) {
00165         dst->scan_s = 0;
00166     } else if (dst->scan_s >= 32) {
00167         dst->scan_s = 31;
00168     }
00169     if (dst->scan_e < 0) {
00170         dst->scan_e = 0;
00171     } else if (dst->scan_e >= 32) {
00172         dst->scan_e = 31;
00173     }
00174     
00175 }
00176 #endif
00177 
00178 static inline void gfx3d_swap_points (g3d_2p3_t *p1, g3d_2p3_t *p2)
00179 {
00180     uint32_t *v1 = (uint32_t *)p1;
00181     uint32_t *v2 = (uint32_t *)p2;
00182     uint32_t tmp;
00183     
00184     tmp = *v1;
00185     *v1 = *v2;
00186     *v2 = tmp;
00187     
00188     v1++, v2++;
00189     tmp = *v1;
00190     *v1 = *v2;
00191     *v2 = tmp;
00192 }
00193 
00194 static inline void gfx3d_swap_txpoints (uint16_t *p1, uint16_t *p2)
00195 {
00196     uint16_t tmp = *p1;
00197     
00198     *p1 = *p2;
00199     *p2 = tmp;
00200     return;
00201 }
00202 
00203 void gfx3d_sort_poly (g3d_poly_t *p)
00204 {
00205     /* arranges points in the polygon into left->right order -- crude */
00206     if (p->pts[1].x < p->pts[0].x) {
00207         /* point in 1 left of 0, swap */
00208         gfx3d_swap_points (&(p->pts[0]), &(p->pts[1]));
00209         gfx3d_swap_txpoints (&(p->tx_pts[0]), &(p->tx_pts[1]));
00210     }
00211     if (p->pts[2].x < p->pts[0].x) {
00212         /* point in 2 left of 0, swap */
00213         gfx3d_swap_points (&(p->pts[0]), &(p->pts[2]));
00214         gfx3d_swap_txpoints (&(p->tx_pts[0]), &(p->tx_pts[2]));
00215     }
00216     if (p->pts[2].x < p->pts[1].x) {
00217         /* point in 2 left of 1, swap */
00218         gfx3d_swap_points (&(p->pts[1]), &(p->pts[2]));
00219         gfx3d_swap_txpoints (&(p->tx_pts[1]), &(p->tx_pts[2]));
00220     }
00221 }
00222 
00223 
00224 static inline void gfx3d_edgebuf_z_fixin (const g3d_2p3_t *p0, const g3d_2p3_t *p1, uint8_t *yedge, uint16_t *zedge, uint16_t xoff)
00225 {
00226     int32_t y = (p0->y << 16) | 0x8000;     /* half-way... */
00227     int32_t ydelta;
00228     int32_t z = (p0->z << 16) | 0x8000;     /* half-way... */
00229     int32_t zdelta;
00230     int x;
00231 
00232     if (p0->x == p1->x) {
00233         /* same X position -- should have been dealt with */
00234         return;
00235     }
00236     
00237     ydelta = ((int32_t)(p1->y - p0->y) << 16) / (int32_t)(p1->x - p0->x);
00238     zdelta = ((int32_t)(p1->z - p0->z) << 16) / (int32_t)(p1->x - p0->x);
00239     
00240     for (x = p0->x; x <= p1->x; x++) {
00241         int16_t rc_y;
00242         int16_t rc_z;
00243         
00244         if (x < 0) {
00245             y += ydelta;
00246             z += zdelta;
00247             continue;
00248         }
00249         if (x >= DISPLAY_WIDTH) {
00250             /* can't have any more */
00251             return;
00252         }
00253         
00254         rc_y = (y >> 16);
00255         if (rc_y < 0) {
00256             y += ydelta;
00257             z += zdelta;
00258             continue;
00259         }
00260         if (rc_y >= DISPLAY_HEIGHT) {
00261             y += ydelta;
00262             z += zdelta;
00263             continue;
00264         }
00265         yedge[x - xoff] = rc_y;
00266         y += ydelta;
00267         
00268         rc_z = (z >> 16);
00269         zedge[x - xoff] = rc_z;
00270         z += zdelta;
00271     }
00272 }
00273 
00274 
00275 /**
00276  *  takes a polygon and fills an edge-buffer with it.  Assumes triangular and sorted poly.
00277  */
00278 void gfx3d_edgebuf_z (const g3d_poly_t *src, g3d_edgebuf_t *dst)
00279 {
00280     if (src->pts[0].x < 0) {
00281         /* left-hand point off-screen */
00282         dst->xoff = 0;
00283     } else {
00284         dst->xoff = src->pts[0].x;
00285     }
00286     dst->s_end = (src->pts[2].x - src->pts[0].x) + 1;
00287     
00288     if (src->pts[0].x == src->pts[2].x) {
00289         /* vertical line only */
00290         if (src->pts[0].y <= src->pts[1].y) {
00291             /* p0 is above p1 */
00292             if (src->pts[1].y < 0) {
00293                 /* off top */
00294                 dst->s_end = 0;
00295             } else if (src->pts[0].y >= DISPLAY_HEIGHT) {
00296                 /* off bottom */
00297                 dst->s_end = 0;
00298             } else {
00299                 if (src->pts[0].y < 0) {
00300                     dst->start[0] = 0;
00301                 } else {
00302                     dst->start[0] = src->pts[0].y;
00303                 }
00304                 if (src->pts[1].y >= DISPLAY_HEIGHT) {
00305                     dst->end[0] = DISPLAY_HEIGHT - 1;
00306                 } else {
00307                     dst->end[0] = src->pts[1].y;
00308                 }
00309                 /* if we over-shot, this will be wrong */
00310                 dst->zstart[0] = src->pts[0].z;
00311                 dst->zend[0] = src->pts[1].z;
00312             }
00313         } else {
00314             if (src->pts[0].y < 0) {
00315                 /* off top */
00316                 dst->s_end = 0;
00317             } else if (src->pts[1].y >= DISPLAY_HEIGHT) {
00318                 /* off bottom */
00319                 dst->s_end = 0;
00320             } else {
00321                 if (src->pts[1].y < 0) {
00322                     dst->start[0] = 0;
00323                 } else {
00324                     dst->start[0] = src->pts[1].y;
00325                 }
00326                 if (src->pts[0].y >= DISPLAY_HEIGHT) {
00327                     dst->end[0] = DISPLAY_HEIGHT - 1;
00328                 } else {
00329                     dst->end[0] = src->pts[0].y;
00330                 }
00331                 /* if we over-shot, this will be wrong */
00332                 dst->zstart[0] = src->pts[1].z;
00333                 dst->zend[0] = src->pts[0].z;
00334             }
00335         }
00336         return;
00337     }
00338     
00339     /* figure out whether the middle point belongs above or below the longest edge */
00340     if (((src->pts[2].y - src->pts[0].y) * (src->pts[1].x - src->pts[0].x)) < ((src->pts[1].y - src->pts[0].y) * (src->pts[2].x - src->pts[0].x))) {
00341         /* middle point is on the top-portion */
00342         gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[1]), dst->start, dst->zstart, dst->xoff);
00343         gfx3d_edgebuf_z_fixin (&(src->pts[1]), &(src->pts[2]), dst->start, dst->zstart, dst->xoff);
00344         gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[2]), dst->end, dst->zend, dst->xoff);
00345     } else {
00346         /* middle point is on the bottom-portion */
00347         gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[2]), dst->start, dst->zstart, dst->xoff);
00348         gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[1]), dst->end, dst->zend, dst->xoff);
00349         gfx3d_edgebuf_z_fixin (&(src->pts[1]), &(src->pts[2]), dst->end, dst->zend, dst->xoff);
00350     }
00351 }
00352 
00353 
00354 /**
00355  *  takes a polygon (containing a texture) and an LCD and draws it.
00356  *
00357  *  @param src      Source polygon.
00358  *  @param lcd      Display LCD to use (size fixed in various places..).
00359  */
00360 void gfx3d_polytxmap (const g3d_poly_t *src, C12832 &lcd)
00361 {
00362     /* assert: polygon points are in order left-to-right */
00363     int16_t x, use_z = src->pts[0].z;
00364     int32_t dydx_ab, dydx_ac, dydx_bc;
00365     int32_t dudx_ab, dudx_ac, dudx_bc;
00366     int32_t dvdx_ab, dvdx_ac, dvdx_bc;
00367     
00368     if (src->pts[1].x > src->pts[0].x) {
00369         /* we have a left-hand side */
00370         dydx_ab = ((src->pts[1].y - src->pts[0].y) << 16) / (src->pts[1].x - src->pts[0].x);
00371         dudx_ab = (((int32_t)(src->tx_pts[1] & 0xff) - (int32_t)(src->tx_pts[0] & 0xff)) << 16) / (int32_t)(src->pts[1].x - src->pts[0].x);
00372         dvdx_ab = (((int32_t)(src->tx_pts[1] >> 8) - (int32_t)(src->tx_pts[0] >> 8)) << 16) / (int32_t)(src->pts[1].x - src->pts[0].x);
00373     } else {
00374         dydx_ab = 0;
00375         dudx_ab = 0;
00376         dvdx_ab = 0;
00377     }
00378     if (src->pts[2].x > src->pts[0].x) {
00379         /* we have the long X edge */
00380         dydx_ac = ((src->pts[2].y - src->pts[0].y) << 16) / (src->pts[2].x - src->pts[0].x);
00381         dudx_ac = (((int32_t)(src->tx_pts[2] & 0xff) - (int32_t)(src->tx_pts[0] & 0xff)) << 16) / (int32_t)(src->pts[2].x - src->pts[0].x);
00382         dvdx_ac = (((int32_t)(src->tx_pts[2] >> 8) - (int32_t)(src->tx_pts[0] >> 8)) << 16) / (int32_t)(src->pts[2].x - src->pts[0].x);
00383     } else {
00384         dydx_ac = 0;
00385         dudx_ac = 0;
00386         dvdx_ac = 0;
00387     }
00388     if (src->pts[2].x > src->pts[1].x) {
00389         /* we have a right-hand side */
00390         dydx_bc = ((src->pts[2].y - src->pts[1].y) << 16) / (src->pts[2].x - src->pts[1].x);
00391         dudx_bc = (((int32_t)(src->tx_pts[2] & 0xff) - (int32_t)(src->tx_pts[1] & 0xff)) << 16) / (int32_t)(src->pts[2].x - src->pts[1].x);
00392         dvdx_bc = (((int32_t)(src->tx_pts[2] >> 8) - (int32_t)(src->tx_pts[1] >> 8)) << 16) / (int32_t)(src->pts[2].x - src->pts[1].x);
00393     } else {
00394         dydx_bc = 0;
00395         dudx_bc = 0;
00396         dvdx_bc = 0;
00397     }
00398 
00399     /* Note:
00400      *  - mostly 16.16 fixpoint stuff.
00401      *  - could optimise more by better pixel plotting (directly into the buffer).
00402      */
00403 
00404     int32_t y_top = (src->pts[0].y << 16) + 0x8000;
00405     int32_t y_bot = (src->pts[0].y << 16) + 0x8000;
00406     int32_t tx_top_x = (src->tx_pts[0] & 0xff) << 16;
00407     int32_t tx_top_y = (src->tx_pts[0] >> 8) << 16;
00408     int32_t tx_bot_x = (src->tx_pts[0] & 0xff) << 16;
00409     int32_t tx_bot_y = (src->tx_pts[0] >> 8) << 16;
00410     int32_t tx_dudy, tx_dvdy;
00411     int16_t ys;
00412     
00413     /* the texture gradients (tx_dudy, tx_dvdy) are constant for any particular triangle */
00414 
00415     int32_t tx_ctemp, tx_tall, p_y, tp_x, tp_y;
00416     
00417     if (src->pts[2].x == src->pts[0].x) {
00418         /* skinny */
00419         tx_ctemp = 0;
00420     } else {
00421         tx_ctemp = ((src->pts[1].x - src->pts[0].x) << 16) / (src->pts[2].x - src->pts[0].x);       /* factor of AB to AC, (fixpoint 0->1) */
00422     }
00423     
00424     p_y = y_top + ((src->pts[2].y - src->pts[0].y) * tx_ctemp);
00425     tp_x = ((int32_t)(src->tx_pts[0] & 0xff) << 16) + ((int32_t)((src->tx_pts[2] & 0xff) - (src->tx_pts[0] & 0xff)) * tx_ctemp);
00426     tp_y = ((int32_t)(src->tx_pts[0] >> 8) << 16) + ((int32_t)((src->tx_pts[2] >> 8) - (src->tx_pts[0] >> 8)) * tx_ctemp);
00427     
00428     tx_tall = abs ((p_y - (src->pts[1].y << 16)) >> 8);                    /* height of tallest scan -- positive 24.8 fixpoint */
00429     
00430     if (tx_tall == 0) {
00431         /* flat in effect */
00432         tx_dudy = 0;
00433         tx_dvdy = 0;
00434         ys = 1;
00435     } else {
00436         tx_dudy = (tp_x - ((int32_t)(src->tx_pts[1] & 0xff) << 16));
00437         tx_dudy = (tx_dudy / tx_tall) << 8;
00438         tx_dvdy = (tp_y - ((int32_t)(src->tx_pts[1] >> 8) << 16));
00439         tx_dvdy = (tx_dvdy / tx_tall) << 8;
00440         ys = (p_y > (src->pts[1].y << 16)) ? 1 : -1;
00441     }
00442     
00443     /* left-hand side */
00444     for (x=src->pts[0].x; x<src->pts[1].x; x++) {
00445         int16_t pix_y = (y_top >> 16);
00446         int16_t bpix_y = (y_bot >> 16) + ys;            /* make sure we include the bottom pixel */
00447         int16_t y;
00448         int16_t tpix_y, tpix_x;
00449 
00450         int32_t stx_y = tx_top_y;
00451         int32_t stx_x = tx_top_x;
00452 
00453         y_top += dydx_ab;       y_bot += dydx_ac;
00454         tx_top_x += dudx_ab;    tx_top_y += dvdx_ab;
00455         tx_bot_x += dudx_ac;    tx_bot_y += dvdx_ac;
00456         
00457         if ((x < 0) || (x >= DISPLAY_WIDTH)) {
00458             continue;
00459         }
00460 
00461         for (y=pix_y; y != bpix_y; y += ys) {
00462             if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
00463                 goto yl_loop_advance;
00464             }
00465             if (use_z > ZBUFFER (x, y)) {
00466                 goto yl_loop_advance;
00467             }                
00468             ZBUFFER (x, y) = use_z;
00469             
00470             tpix_y = (stx_y >> 16) & (DISPLAY_HEIGHT - 1);
00471             tpix_x = (stx_x >> 16) & (DISPLAY_WIDTH - 1);
00472         
00473             lcd.pixel_nochk_norm (x, y, g3d_texture_bit (src->txptr, tpix_x, tpix_y));
00474 yl_loop_advance:            
00475             stx_x += tx_dudy;
00476             stx_y += tx_dvdy;
00477         }
00478     }
00479     
00480     y_top = (src->pts[1].y << 16) + 0x8000;
00481     tx_top_x = (src->tx_pts[1] & 0xff) << 16;
00482     tx_top_y = (src->tx_pts[1] >> 8) << 16;
00483     
00484     /* right-hand side */
00485     for (; x<src->pts[2].x; x++) {
00486         int16_t pix_y = (y_top >> 16);
00487         int16_t bpix_y = (y_bot >> 16) + ys;
00488         int16_t y;
00489         int16_t tpix_y, tpix_x;
00490 
00491         int32_t stx_y = tx_top_y;
00492         int32_t stx_x = tx_top_x;
00493 
00494         y_top += dydx_bc;       y_bot += dydx_ac;
00495         tx_top_x += dudx_bc;    tx_top_y += dvdx_bc;
00496         tx_bot_x += dudx_ac;    tx_bot_y += dvdx_ac;
00497                
00498         if ((x < 0) || (x >= DISPLAY_WIDTH)) {
00499             continue;
00500         }
00501 
00502         for (y=pix_y; y != bpix_y; y += ys) {
00503             if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
00504                 goto yr_loop_advance;
00505             }
00506             if (use_z > ZBUFFER (x, y)) {
00507                 goto yr_loop_advance;
00508             }                
00509             ZBUFFER (x, y) = use_z;
00510 
00511             tpix_y = (stx_y >> 16) & (DISPLAY_HEIGHT - 1);
00512             tpix_x = (stx_x >> 16) & (DISPLAY_WIDTH - 1);
00513 
00514             lcd.pixel_nochk_norm (x, y, g3d_texture_bit (src->txptr, tpix_x, tpix_y));
00515 
00516 yr_loop_advance:
00517             stx_x += tx_dudy;
00518             stx_y += tx_dvdy;
00519         }
00520     }
00521 }
00522 
00523 
00524 /**
00525  *  takes a polygon, drawing it on the given LCD with shading based on normal value.
00526  *
00527  *  @param src      Source polygon.
00528  *  @param lcd      Display LCD to use (size fixed in various places..).
00529  */
00530 void gfx3d_polynormmap (const g3d_poly_t *src, C12832 &lcd)
00531 {
00532     /* assert: polygon points are in order left-to-right */
00533     int16_t x, use_z = src->pts[0].z;
00534     int32_t dydx_ab, dydx_ac, dydx_bc;
00535     
00536     if (src->pts[1].x > src->pts[0].x) {
00537         /* we have a left-hand side */
00538         dydx_ab = ((src->pts[1].y - src->pts[0].y) << 16) / (src->pts[1].x - src->pts[0].x);
00539     } else {
00540         dydx_ab = 0;
00541     }
00542     if (src->pts[2].x > src->pts[0].x) {
00543         /* we have the long X edge */
00544         dydx_ac = ((src->pts[2].y - src->pts[0].y) << 16) / (src->pts[2].x - src->pts[0].x);
00545     } else {
00546         dydx_ac = 0;
00547     }
00548     if (src->pts[2].x > src->pts[1].x) {
00549         /* we have a right-hand side */
00550         dydx_bc = ((src->pts[2].y - src->pts[1].y) << 16) / (src->pts[2].x - src->pts[1].x);
00551     } else {
00552         dydx_bc = 0;
00553     }
00554 
00555     /* Note:
00556      *  - mostly 16.16 fixpoint stuff.
00557      *  - could optimise more by better pixel plotting (directly into the buffer).
00558      */
00559 
00560     int32_t y_top = (src->pts[0].y << 16) + 0x8000;
00561     int32_t y_bot = (src->pts[0].y << 16) + 0x8000;
00562     int16_t ys;
00563     int32_t tx_ctemp, tx_tall, p_y;
00564     int bitstep = (32 - __CLZ (src->norm)) >> 2;
00565     int pcount = 0;
00566     
00567     if (bitstep == 0) {
00568         bitstep = 1;
00569     }
00570     if (src->pts[2].x == src->pts[0].x) {
00571         /* skinny */
00572         tx_ctemp = 0;
00573     } else {
00574         tx_ctemp = ((src->pts[1].x - src->pts[0].x) << 16) / (src->pts[2].x - src->pts[0].x);       /* factor of AB to AC, (fixpoint 0->1) */
00575     }
00576     
00577     p_y = y_top + ((src->pts[2].y - src->pts[0].y) * tx_ctemp);
00578     tx_tall = abs ((p_y - (src->pts[1].y << 16)) >> 8);                    /* height of tallest scan -- positive 24.8 fixpoint */
00579     
00580     if (tx_tall == 0) {
00581         /* flat in effect */
00582         ys = 1;
00583     } else {
00584         ys = (p_y > (src->pts[1].y << 16)) ? 1 : -1;
00585     }
00586     
00587     /* left-hand side */
00588     for (x=src->pts[0].x; x<src->pts[1].x; x++) {
00589         int16_t pix_y = (y_top >> 16);
00590         int16_t bpix_y = (y_bot >> 16) + ys;            /* make sure we include the bottom pixel */
00591         int16_t y;
00592 
00593         y_top += dydx_ab;       y_bot += dydx_ac;
00594         
00595         if ((x < 0) || (x >= DISPLAY_WIDTH)) {
00596             continue;
00597         }
00598 
00599         pcount = 0;
00600         for (y=pix_y; y != bpix_y; y += ys) {
00601             if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
00602                 continue;
00603             }
00604             if (use_z > ZBUFFER (x, y)) {
00605                 continue;
00606             }                
00607             ZBUFFER (x, y) = use_z;
00608             
00609             lcd.pixel_nochk_norm (x, y, (pcount == 0) ? 1 : 0);
00610             if (!pcount) {
00611                 pcount = bitstep;
00612             }
00613             pcount--;
00614         }
00615     }
00616     
00617     y_top = (src->pts[1].y << 16) + 0x8000;
00618     
00619     /* right-hand side */
00620     for (; x<src->pts[2].x; x++) {
00621         int16_t pix_y = (y_top >> 16);
00622         int16_t bpix_y = (y_bot >> 16) + ys;
00623         int16_t y;
00624 
00625         y_top += dydx_bc;       y_bot += dydx_ac;
00626                
00627         if ((x < 0) || (x >= DISPLAY_WIDTH)) {
00628             continue;
00629         }
00630 
00631         pcount = 0;
00632         for (y=pix_y; y != bpix_y; y += ys) {
00633             if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
00634                 continue;
00635             }
00636             if (use_z > ZBUFFER (x, y)) {
00637                 continue;
00638             }                
00639             ZBUFFER (x, y) = use_z;
00640 
00641             lcd.pixel_nochk_norm (x, y, (pcount == 0) ? 1 : 0);
00642             if (!pcount) {
00643                 pcount = bitstep;
00644             }
00645             pcount--;
00646         }
00647     }
00648 }
00649 
00650 
00651 /**
00652  *  takes an edge-buffer and draws its wireframe on the given LCD (start and end).
00653  */
00654 void gfx3d_wireedge (const g3d_edgebuf_t *edge, C12832 &lcd)
00655 {
00656     int x;
00657     
00658     if (edge->s_end == 1) {
00659         /* special case: vertical line */
00660         int xp = edge->xoff;
00661         int y0 = edge->start[0];
00662         int y1 = edge->end[0];
00663         int ys = (y1 >= y0) ? 1 : -1;
00664         int y;
00665         
00666         int16_t z0 = edge->zstart[0];
00667         int16_t z1 = edge->zend[0];
00668         int32_t zval = (z0 << 16) | 0x8000;    /* half-way.. */
00669         
00670         if (xp >= DISPLAY_WIDTH) {
00671             /* off right-hand edge */
00672             return;
00673         } else if (xp < 0) {
00674             /* off left-hand edge */
00675             return;
00676         }
00677         
00678         if ((y0 == y1) || ((y0 + ys) == y1)) {
00679             /* one or two point, since we're narrow, just y0 */
00680             if (z0 <= ZBUFFER (xp, y0)) {
00681                 ZBUFFER (xp, y0) = z0;
00682                 lcd.pixel_nochk_norm (xp, y0, 1);
00683             }
00684         } else {
00685             /* long vertical poly, set all points except at y1 */
00686             int32_t zdelta = ((int32_t)(z1 - z0) << 16) / (int32_t)(y1 - y0);
00687             int16_t rc_z = z0;
00688 
00689             for (y = y0; y != y1; y += ys) {
00690                 zval += zdelta;
00691                 rc_z = (zval >> 16);
00692                 if (rc_z <= ZBUFFER (xp, y)) {
00693                     ZBUFFER (xp, y) = rc_z;
00694                     lcd.pixel_nochk_norm (xp, y, 1);
00695                 }
00696             }
00697         }
00698         return;
00699     }
00700     
00701     for (x=0; x<edge->s_end; x++) {
00702         int xp = x + edge->xoff;
00703         int y0 = edge->start[x];
00704         int y1 = edge->end[x];
00705         int ys = (y1 >= y0) ? 1 : -1;
00706         int y;
00707         
00708         int16_t z0 = edge->zstart[x];
00709         int16_t z1 = edge->zend[x];
00710         
00711         int32_t zval = (z0 << 16) | 0x8000;     /* half-way.. */
00712 
00713         if ((xp >= DISPLAY_WIDTH) || (xp < 0)) {
00714             continue;
00715         }
00716 
00717         if (y0 == y1) {
00718             /* point, but we don't plot assuming it belongs to the adjoining polygon */
00719         } else if ((y0 + ys) == y1) {
00720             /* two-point poly, just plot y0 */
00721             if (z0 <= ZBUFFER (xp, y0)) {
00722                 ZBUFFER (xp, y0) = z0;
00723                 lcd.pixel_nochk_norm (xp, y0, 1);
00724             }
00725         } else {
00726             /* more than two points, shrink y1 to avoid plotting the last one here */
00727             int32_t zdelta = ((int32_t)(z1 - z0) << 16) / (int32_t)(y1 - y0);
00728             int16_t rc_z = z0;
00729 
00730             y1 -= ys;
00731 
00732             /* start at y0 */
00733             y = y0;
00734             if (rc_z <= ZBUFFER (xp, y)) {
00735                 ZBUFFER (xp, y) = rc_z;
00736                 lcd.pixel_nochk_norm (xp, y, 1);
00737             }
00738 
00739             for (y += ys; y != y1; y += ys) {
00740                 zval += zdelta;
00741                 rc_z = (zval >> 16);
00742                 if (rc_z <= ZBUFFER (xp, y)) {
00743                     ZBUFFER (xp, y) = rc_z;
00744                     lcd.pixel_nochk_norm (xp, y, 0);
00745                 }
00746             }
00747             /* last pixel at y1,z1 */
00748             rc_z = z1;
00749             if (rc_z <= ZBUFFER (xp, y1)) {
00750                 ZBUFFER (xp, y1) = rc_z;
00751                 lcd.pixel_nochk_norm (xp, y1, 1);
00752             }
00753         }
00754         
00755         /* end of x-loop */
00756     }
00757 }
00758