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

Dependents:   co657_lcdplay

Revision:
5:2aaaf4e78a53
Child:
6:0bd002c936bb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/g3d_render.cpp	Wed Nov 25 23:58:07 2015 +0000
@@ -0,0 +1,617 @@
+/*
+ *  g3d_render.cpp -- rendering stuff for G3D library
+ *  Copyright (C) 2015 Fred Barnes, University of Kent
+ *  GPL >= 2.0
+ */
+
+
+#include "mbed.h"
+#include "C12832.h"
+#include "gfx3d.h"
+
+#define DISPLAY_WIDTH   (128)
+#define DISPLAY_HEIGHT  (32)
+
+#define ZB_YSHIFT       (7)
+
+/* grotty: single global Z buffer that matches the LCD size */
+static int16_t zbuffer[DISPLAY_HEIGHT * DISPLAY_WIDTH];
+
+#define ZBUFFER(X,Y) zbuffer[(Y << ZB_YSHIFT) | X]
+
+/**
+ *  clears the Z buffer (sets all to maximum)
+ */
+void gfx3d_clear_zb (void)
+{
+    int i;
+    int lim = (DISPLAY_HEIGHT * DISPLAY_WIDTH) >> 1;
+    uint32_t *halfbuf = (uint32_t *)zbuffer;
+    
+    for (i=0; i<lim; i++) {
+        halfbuf[i] = 0x7fff7fff;
+    }
+}
+
+#if 0
+/**
+ *  fixes scan-line starts/ends in a g3d_polyscan_t structure based on edge
+ */
+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)
+{
+    if (y2 < y1) {
+        /* swap around: make sure we go in the same direction (down) */
+        int16_t t16;
+        int32_t t32;
+        
+        t16 = x1;
+        x1 = x2;
+        x2 = t16;
+        
+        t16 = y1;
+        y1 = y2;
+        y2 = t16;
+        
+        t32 = z1;
+        z1 = z2;
+        z2 = t32;
+    }
+    
+    /* scan limit */
+    if (y1 < dst->scan_s) {
+        dst->scan_s = y1;
+    }
+    if (y2 > dst->scan_e) {
+        dst->scan_e = y2;
+    }
+    if (y1 == y2) {
+        /* flat polygon */
+        dst->scans[y1][0] = x1;
+        dst->scans[y1][1] = x2;
+        dst->zscan[y1][0] = z1;
+        dst->zscan[y1][1] = z2;
+    } else {
+        /* vaguely complex polygon */
+        int x, y, z, step, z_step;
+        
+        x = (int)x1 << 16;
+        step = ((int)(x2 - x1) << 16) / (int)(y2 - y1);
+        x += step;
+        
+        z = z1;
+        z_step = (z2 - z1) / ((y2 - y1) + 1);
+        y1++;
+        
+        for (y=y1; y<=y2; y++) {
+            if ((y >= 0) && (y < 32)) {
+                int shx = x >> 16;
+                
+                if (x < 0) {
+                    shx = shx - 65536;
+                }
+                if (dst->scans[y][0] == 0xff) {
+                    /* start of scan */
+                    uint8_t x8 = (uint8_t)(shx & 0xff);
+                    
+                    if (x8 == 0xff) {
+                        x8 = 128;
+                    }
+                    dst->scans[y][0] = x8;
+                    if (co_c != 0.0f) {
+                        dst->zscan[y][0] = (int32_t)(((-(co_a * (float)shx) - (co_b * (float)y)) - co_d) / co_c);
+                    } else {
+                        dst->zscan[y][0] = z;
+                    }
+                } else {
+                    /* end of scan */
+                    uint8_t x8 = (uint8_t)(shx & 0xff);
+                    
+                    if (x8 == 0xff) {
+                        x8 = 128;
+                    }
+                    dst->scans[y][1] = x8;
+                    if (co_c != 0.0f) {
+                        dst->zscan[y][1] = (int32_t)(((-(co_a * (float)shx) - (co_b * (float)y)) - co_d) / co_c);
+                    } else {
+                        dst->zscan[y][1] = z;
+                    }
+                }
+            }
+            x += step;
+            z += z_step;
+        }
+    }
+}
+
+
+/**
+ *  takes a polygon structure and generates a set of scanline data from it
+ */
+void gfx3d_polyscan (const g3d_poly_t *src, g3d_polyscan_t *dst)
+{
+    int i;
+    float co_a, co_b, co_c, co_d;
+    float x1 = (float)src->x[0];
+    float y1 = (float)src->y[0];
+    float z1 = (float)src->z[0];
+    float x2 = (float)src->x[1];
+    float y2 = (float)src->y[1];
+    float z2 = (float)src->z[1];
+    float x3 = (float)src->x[2];
+    float y3 = (float)src->y[2];
+    float z3 = (float)src->z[2];
+    
+    for (i=0; i<32; i++) {
+        dst->scans[i][0] = 0xff;
+        dst->scans[i][1] = 0xff;
+        dst->zscan[i][0] = 0;
+        dst->zscan[i][1] = 0;
+    }
+    dst->scan_s = 32;
+    dst->scan_e = 0;
+    dst->norm = src->norm;
+    
+    /* coefficients for plane equations, Herne & Baker p308 */
+    co_a = ((y1 * (z2 - z3)) + (y2 * (z3 - z1))) + (y3 * (z1 - z2));
+    co_b = ((z1 * (x2 - x3)) + (z2 * (x3 - x1))) + (z3 * (x1 - x2));
+    co_c = ((x1 * (y2 - y3)) + (x2 * (y3 - y1))) + (x3 * (y1 - y2));
+    co_d = ((-x1 * ((y2 * z3) - (y3 * z2))) - (x2 * ((y3 * z1) - (y1 * z3)))) - (x3 * ((y1 * z2) - (y2 * z1)));
+    
+    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);
+    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);
+    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);
+    
+    if (dst->scan_s < 0) {
+        dst->scan_s = 0;
+    } else if (dst->scan_s >= 32) {
+        dst->scan_s = 31;
+    }
+    if (dst->scan_e < 0) {
+        dst->scan_e = 0;
+    } else if (dst->scan_e >= 32) {
+        dst->scan_e = 31;
+    }
+    
+}
+#endif
+
+static inline void gfx3d_swap_points (g3d_2p3_t *p1, g3d_2p3_t *p2)
+{
+    uint32_t *v1 = (uint32_t *)p1;
+    uint32_t *v2 = (uint32_t *)p2;
+    uint32_t tmp;
+    
+    tmp = *v1;
+    *v1 = *v2;
+    *v2 = tmp;
+    
+    v1++, v2++;
+    tmp = *v1;
+    *v1 = *v2;
+    *v2 = tmp;
+}
+
+static inline void gfx3d_swap_txpoints (uint16_t *p1, uint16_t *p2)
+{
+    uint16_t tmp = *p1;
+    
+    *p1 = *p2;
+    *p2 = tmp;
+    return;
+}
+
+void gfx3d_sort_poly (g3d_poly_t *p)
+{
+    /* arranges points in the polygon into left->right order -- crude */
+    if (p->pts[1].x < p->pts[0].x) {
+        /* point in 1 left of 0, swap */
+        gfx3d_swap_points (&(p->pts[0]), &(p->pts[1]));
+        gfx3d_swap_txpoints (&(p->tx_pts[0]), &(p->tx_pts[1]));
+    }
+    if (p->pts[2].x < p->pts[0].x) {
+        /* point in 2 left of 0, swap */
+        gfx3d_swap_points (&(p->pts[0]), &(p->pts[2]));
+        gfx3d_swap_txpoints (&(p->tx_pts[0]), &(p->tx_pts[2]));
+    }
+    if (p->pts[2].x < p->pts[1].x) {
+        /* point in 2 left of 1, swap */
+        gfx3d_swap_points (&(p->pts[1]), &(p->pts[2]));
+        gfx3d_swap_txpoints (&(p->tx_pts[1]), &(p->tx_pts[2]));
+    }
+}
+
+
+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)
+{
+    int32_t y = (p0->y << 16) | 0x8000;     /* half-way... */
+    int32_t ydelta;
+    int32_t z = (p0->z << 16) | 0x8000;     /* half-way... */
+    int32_t zdelta;
+    int x;
+
+    if (p0->x == p1->x) {
+        /* same X position -- should have been dealt with */
+        return;
+    }
+    
+    ydelta = ((int32_t)(p1->y - p0->y) << 16) / (int32_t)(p1->x - p0->x);
+    zdelta = ((int32_t)(p1->z - p0->z) << 16) / (int32_t)(p1->x - p0->x);
+    
+    for (x = p0->x; x <= p1->x; x++) {
+        int16_t rc_y;
+        int16_t rc_z;
+        
+        if (x < 0) {
+            y += ydelta;
+            z += zdelta;
+            continue;
+        }
+        if (x >= DISPLAY_WIDTH) {
+            /* can't have any more */
+            return;
+        }
+        
+        rc_y = (y >> 16);
+        if (rc_y < 0) {
+            y += ydelta;
+            z += zdelta;
+            continue;
+        }
+        if (rc_y >= DISPLAY_HEIGHT) {
+            y += ydelta;
+            z += zdelta;
+            continue;
+        }
+        yedge[x - xoff] = rc_y;
+        y += ydelta;
+        
+        rc_z = (z >> 16);
+        zedge[x - xoff] = rc_z;
+        z += zdelta;
+    }
+}
+
+
+/**
+ *  takes a polygon and fills an edge-buffer with it.  Assumes triangular and sorted poly.
+ */
+void gfx3d_edgebuf_z (const g3d_poly_t *src, g3d_edgebuf_t *dst)
+{
+    if (src->pts[0].x < 0) {
+        /* left-hand point off-screen */
+        dst->xoff = 0;
+    } else {
+        dst->xoff = src->pts[0].x;
+    }
+    dst->s_end = (src->pts[2].x - src->pts[0].x) + 1;
+    
+    if (src->pts[0].x == src->pts[2].x) {
+        /* vertical line only */
+        if (src->pts[0].y <= src->pts[1].y) {
+            /* p0 is above p1 */
+            if (src->pts[1].y < 0) {
+                /* off top */
+                dst->s_end = 0;
+            } else if (src->pts[0].y >= DISPLAY_HEIGHT) {
+                /* off bottom */
+                dst->s_end = 0;
+            } else {
+                if (src->pts[0].y < 0) {
+                    dst->start[0] = 0;
+                } else {
+                    dst->start[0] = src->pts[0].y;
+                }
+                if (src->pts[1].y >= DISPLAY_HEIGHT) {
+                    dst->end[0] = DISPLAY_HEIGHT - 1;
+                } else {
+                    dst->end[0] = src->pts[1].y;
+                }
+                /* if we over-shot, this will be wrong */
+                dst->zstart[0] = src->pts[0].z;
+                dst->zend[0] = src->pts[1].z;
+            }
+        } else {
+            if (src->pts[0].y < 0) {
+                /* off top */
+                dst->s_end = 0;
+            } else if (src->pts[1].y >= DISPLAY_HEIGHT) {
+                /* off bottom */
+                dst->s_end = 0;
+            } else {
+                if (src->pts[1].y < 0) {
+                    dst->start[0] = 0;
+                } else {
+                    dst->start[0] = src->pts[1].y;
+                }
+                if (src->pts[0].y >= DISPLAY_HEIGHT) {
+                    dst->end[0] = DISPLAY_HEIGHT - 1;
+                } else {
+                    dst->end[0] = src->pts[0].y;
+                }
+                /* if we over-shot, this will be wrong */
+                dst->zstart[0] = src->pts[1].z;
+                dst->zend[0] = src->pts[0].z;
+            }
+        }
+        return;
+    }
+    
+    /* figure out whether the middle point belongs above or below the longest edge */
+    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))) {
+        /* middle point is on the top-portion */
+        gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[1]), dst->start, dst->zstart, dst->xoff);
+        gfx3d_edgebuf_z_fixin (&(src->pts[1]), &(src->pts[2]), dst->start, dst->zstart, dst->xoff);
+        gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[2]), dst->end, dst->zend, dst->xoff);
+    } else {
+        /* middle point is on the bottom-portion */
+        gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[2]), dst->start, dst->zstart, dst->xoff);
+        gfx3d_edgebuf_z_fixin (&(src->pts[0]), &(src->pts[1]), dst->end, dst->zend, dst->xoff);
+        gfx3d_edgebuf_z_fixin (&(src->pts[1]), &(src->pts[2]), dst->end, dst->zend, dst->xoff);
+    }
+}
+
+
+/**
+ *  takes a polygon, a texture and an LCD and draws it
+ */
+void gfx3d_polytxmap (const g3d_poly_t *src, uint8_t *txbuf, C12832 &lcd)
+{
+    /* assert: polygon points are in order left-to-right */
+    int16_t x, use_z = src->pts[0].z;
+    int32_t dydx_ab, dydx_ac, dydx_bc;
+    int32_t dudx_ab, dudx_ac, dudx_bc;
+    int32_t dvdx_ab, dvdx_ac, dvdx_bc;
+    
+    if (src->pts[1].x > src->pts[0].x) {
+        /* we have a left-hand side */
+        dydx_ab = ((src->pts[1].y - src->pts[0].y) << 16) / (src->pts[1].x - src->pts[0].x);
+        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);
+        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);
+    } else {
+        dydx_ab = 0;
+        dudx_ab = 0;
+        dvdx_ab = 0;
+    }
+    if (src->pts[2].x > src->pts[0].x) {
+        /* we have the long X edge */
+        dydx_ac = ((src->pts[2].y - src->pts[0].y) << 16) / (src->pts[2].x - src->pts[0].x);
+        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);
+        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);
+    } else {
+        dydx_ac = 0;
+        dudx_ac = 0;
+        dvdx_ac = 0;
+    }
+    if (src->pts[2].x > src->pts[1].x) {
+        /* we have a right-hand side */
+        dydx_bc = ((src->pts[2].y - src->pts[1].y) << 16) / (src->pts[2].x - src->pts[1].x);
+        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);
+        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);
+    } else {
+        dydx_bc = 0;
+        dudx_bc = 0;
+        dvdx_bc = 0;
+    }
+    
+    int32_t y_top = (src->pts[0].y << 16) + 0x8000;
+    int32_t y_bot = (src->pts[0].y << 16) + 0x8000;
+    int32_t tx_top_x = (src->tx_pts[0] & 0xff) << 16;
+    int32_t tx_top_y = (src->tx_pts[0] >> 8) << 16;
+    int32_t tx_bot_x = (src->tx_pts[0] & 0xff) << 16;
+    int32_t tx_bot_y = (src->tx_pts[0] >> 8) << 16;
+
+    /* FIXME: the texture gradients (tx_dudy, tx_dvdy) are constant for any particular triangle */
+
+    /* left-hand side */
+    for (x=src->pts[0].x; x<src->pts[1].x; x++) {
+        int16_t ys = (y_bot > y_top) ? 1 : -1;
+        int16_t pix_y = (y_top >> 16);
+        int16_t bpix_y = (y_bot >> 16) + ys;            /* make sure we include the bottom pixel */
+        int16_t y;
+
+        int32_t stx_y = tx_top_y;
+        int32_t stx_x = tx_top_x;
+
+        int32_t tx_dudy, tx_dvdy;
+        
+        if (y_top != y_bot) {
+            tx_dudy = (tx_bot_x - tx_top_x) / (((y_bot - y_top) >> 16) * ys);
+            tx_dvdy = (tx_bot_y - tx_top_y) / (((y_bot - y_top) >> 16) * ys);
+        } else {
+            tx_dudy = 0;
+            tx_dvdy = 0;
+        }
+
+        
+        y_top += dydx_ab;       y_bot += dydx_ac;
+        tx_top_x += dudx_ab;    tx_top_y += dvdx_ab;
+        tx_bot_x += dudx_ac;    tx_bot_y += dvdx_ac;
+        
+        if ((x < 0) || (x >= DISPLAY_WIDTH)) {
+            continue;
+        }
+
+        for (y=pix_y; y != bpix_y; y += ys) {
+            if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
+                stx_x += tx_dudy;
+                stx_y += tx_dvdy;
+                continue;
+            }
+            if (use_z > ZBUFFER (x, y)) {
+                stx_x += tx_dudy;
+                stx_y += tx_dvdy;
+                continue;
+            }                
+            ZBUFFER (x, y) = use_z;
+            
+            int16_t tpix_y = (stx_y >> 16) & (DISPLAY_HEIGHT - 1);
+            int16_t tpix_x = (stx_x >> 16) & (DISPLAY_WIDTH - 1);
+        
+            if (g3d_texture_bit (txbuf, tpix_x, tpix_y)) {
+                lcd.pixel_nochk (x, y, 1);
+            } else {
+                lcd.pixel_nochk (x, y, 0);
+            }
+            
+            stx_x += tx_dudy;
+            stx_y += tx_dvdy;
+        }
+    }
+    
+    y_top = (src->pts[1].y << 16) + 0x8000;
+    tx_top_x = (src->tx_pts[1] & 0xff) << 16;
+    tx_top_y = (src->tx_pts[1] >> 8) << 16;
+    
+    /* right-hand side */
+    for (; x<src->pts[2].x; x++) {
+        int16_t ys = (y_bot > y_top) ? 1 : -1;
+        int16_t pix_y = (y_top >> 16);
+        int16_t bpix_y = (y_bot >> 16) + ys;
+        int16_t y;
+
+        int32_t stx_y = tx_top_y;
+        int32_t stx_x = tx_top_x;
+        
+        int32_t tx_dudy, tx_dvdy;
+        
+        if (y_top != y_bot) {
+            tx_dudy = (tx_bot_x - tx_top_x) / (((y_bot - y_top) >> 16) * ys);
+            tx_dvdy = (tx_bot_y - tx_top_y) / (((y_bot - y_top) >> 16) * ys);
+        } else {
+            tx_dudy = 0;
+            tx_dvdy = 0;
+        }
+        
+        y_top += dydx_bc;       y_bot += dydx_ac;
+        tx_top_x += dudx_bc;    tx_top_y += dvdx_bc;
+        tx_bot_x += dudx_ac;    tx_bot_y += dvdx_ac;
+               
+        if ((x < 0) || (x >= DISPLAY_WIDTH)) {
+            continue;
+        }
+
+        for (y=pix_y; y != bpix_y; y += ys) {
+            if ((y < 0) || (y >= DISPLAY_HEIGHT)) {
+                stx_x += tx_dudy;
+                stx_y += tx_dvdy;
+                continue;
+            }
+            if (use_z > ZBUFFER (x, y)) {
+                stx_x += tx_dudy;
+                stx_y += tx_dvdy;
+                continue;
+            }                
+            ZBUFFER (x, y) = use_z;
+
+            int16_t tpix_y = (stx_y >> 16) & (DISPLAY_HEIGHT - 1);
+            int16_t tpix_x = (stx_x >> 16) & (DISPLAY_WIDTH - 1);
+
+            if (g3d_texture_bit (txbuf, tpix_x, tpix_y)) {
+                lcd.pixel_nochk (x, y, 1);
+            } else {
+                lcd.pixel_nochk (x, y, 0);
+            }
+
+            stx_x += tx_dudy;
+            stx_y += tx_dvdy;
+        }
+    }
+}
+
+
+/**
+ *  takes an edge-buffer and draws its wireframe on the given LCD (start and end).
+ */
+void gfx3d_wireedge (const g3d_edgebuf_t *edge, C12832 &lcd)
+{
+    int x;
+    
+    if (edge->s_end == 1) {
+        /* special case: vertical line */
+        int xp = edge->xoff;
+        int y0 = edge->start[0];
+        int y1 = edge->end[0];
+        int ys = (y1 >= y0) ? 1 : -1;
+        int y;
+        
+        int16_t z0 = edge->zstart[0];
+        int16_t z1 = edge->zend[0];
+        int32_t zval = (z0 << 16) | 0x8000;    /* half-way.. */
+        
+        if ((y0 == y1) || ((y0 + ys) == y1)) {
+            /* one or two point, since we're narrow, just y0 */
+            if (z0 <= ZBUFFER (xp, y0)) {
+                ZBUFFER (xp, y0) = z0;
+                lcd.pixel (xp, y0, 1);
+            }
+        } else {
+            /* long vertical poly, set all points except at y1 */
+            int32_t zdelta = ((int32_t)(z1 - z0) << 16) / (int32_t)(y1 - y0);
+            int16_t rc_z = z0;
+
+            for (y = y0; y != y1; y += ys) {
+                zval += zdelta;
+                rc_z = (zval >> 16);
+                if (rc_z <= ZBUFFER (xp, y)) {
+                    ZBUFFER (xp, y) = rc_z;
+                    lcd.pixel (xp, y, 1);
+                }
+            }
+        }
+        return;
+    }
+    
+    for (x=0; x<edge->s_end; x++) {
+        int xp = x + edge->xoff;
+        int y0 = edge->start[x];
+        int y1 = edge->end[x];
+        int ys = (y1 >= y0) ? 1 : -1;
+        int y;
+        
+        int16_t z0 = edge->zstart[x];
+        int16_t z1 = edge->zend[x];
+        
+        int32_t zval = (z0 << 16) | 0x8000;     /* half-way.. */
+        
+        if (y0 == y1) {
+            /* point, but we don't plot assuming it belongs to the adjoining polygon */
+        } else if ((y0 + ys) == y1) {
+            /* two-point poly, just plot y0 */
+            if (z0 <= ZBUFFER (xp, y0)) {
+                ZBUFFER (xp, y0) = z0;
+                lcd.pixel (xp, y0, 1);
+            }
+        } else {
+            /* more than two points, shrink y1 to avoid plotting the last one here */
+            int32_t zdelta = ((int32_t)(z1 - z0) << 16) / (int32_t)(y1 - y0);
+            int16_t rc_z = z0;
+
+            y1 -= ys;
+
+            /* start at y0 */
+            y = y0;
+            if (rc_z <= ZBUFFER (xp, y)) {
+                ZBUFFER (xp, y) = rc_z;
+                lcd.pixel (xp, y, 1);
+            }
+
+            for (y += ys; y != y1; y += ys) {
+                zval += zdelta;
+                rc_z = (zval >> 16);
+                if (rc_z <= ZBUFFER (xp, y)) {
+                    ZBUFFER (xp, y) = rc_z;
+                    lcd.pixel (xp, y, 0);
+                }
+            }
+            /* last pixel at y1,z1 */
+            rc_z = z1;
+            if (rc_z <= ZBUFFER (xp, y1)) {
+                ZBUFFER (xp, y1) = rc_z;
+                lcd.pixel (xp, y1, 1);
+            }
+        }
+        
+        /* end of x-loop */
+    }
+}
+