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

Dependents:   co657_lcdplay

gfx3d.cpp

Committer:
co657_frmb
Date:
2015-11-29
Revision:
9:db4ec6f7d8b2
Parent:
8:55ee7af49f47

File content as of revision 9:db4ec6f7d8b2:

/*
 *  gfx3d.cpp -- 3D stuff for MBED (just playing!)
 *  Copyright (C) 2015 Fred Barnes, University of Kent  <frmb@kent.ac.uk>
 */


#include "mbed.h"
#include "C12832.h"
#include "gfx3d.h"

#define DISPLAY_WIDTH   (128)
#define DISPLAY_HEIGHT  (32)


static float g3d_z_depth = G3D_Z_DEPTH;
static float g3d_x_scale = G3D_X_SCALE;
static float g3d_y_scale = G3D_Y_SCALE;


/** Sets the Z depth */
void gfx3d_set_z_depth (const float zd)
{
    g3d_z_depth = zd;
}


/** rotates a base set of points into a new set (demoscene style)
 *
 *  @param src Source points.
 *  @param dst Destination points.
 *  @param npnts Number of points.
 *  @param a Angle to rotate by (0-255).
 */
void gfx3d_rotate_demo (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const angle_t a)
{
    float sinval = gfx3d_sin (a);
    float cosval = gfx3d_cos (a);
    int i;
    
    for (i=0; i<npnts; i++) {
        float x1 = (src[i].x * cosval) + (src[i].y * sinval);
        float y1 = (src[i].y * cosval) - (src[i].x * sinval);
        float z1 = (src[i].z * cosval) - (x1 * sinval);
        float t;
        
        dst[i].x = (x1 * cosval) + (src[i].z * sinval);
        t = (y1 * cosval) + (z1 * sinval);
        dst[i].z = (z1 * cosval) - (y1 * sinval);
        dst[i].y = t;
    }
    
}


/** rotates a set of points around the X axis.
 *
 *  @param src Source points.
 *  @param dst Destination points.
 *  @param npnts Number of points.
 *  @param a Angle to rotate by (0-255).
 */
void gfx3d_rotate_x (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const angle_t a)
{
    float sinval = gfx3d_sin (a);
    float cosval = gfx3d_cos (a);
    int i;
    
    for (i=0; i<npnts; i++) {
        float t = src[i].y;
        
        dst[i].x = src[i].x;
        dst[i].y = (src[i].y * cosval) + (src[i].z * sinval);
        dst[i].z = (src[i].z * cosval) - (t * sinval);
    }
    
}


void gfx3d_rotate_y (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const angle_t a)
{
    float sinval = gfx3d_sin (a);
    float cosval = gfx3d_cos (a);
    int i;
    
    for (i=0; i<npnts; i++) {
        float t = src[i].x;
        
        dst[i].x = (src[i].x * cosval) + (src[i].z * sinval);
        dst[i].y = src[i].y;
        dst[i].z = (src[i].z * cosval) - (t * sinval);
    }
    
}


void gfx3d_rotate_z (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const angle_t a)
{
    float sinval = gfx3d_sin (a);
    float cosval = gfx3d_cos (a);
    int i;
    
    for (i=0; i<npnts; i++) {
        float t = src[i].x;
        
        dst[i].x = (src[i].x * cosval) + (src[i].y * sinval);
        dst[i].y = (src[i].y * cosval) - (t * sinval);
        dst[i].z = src[i].z;
    }
    
}


/*
 *  translates a set of 3D points.  'src' and 'dst' can be the same
 */
void gfx3d_translate (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const g3d_p3_t tx)
{
    int i;
    
    if (tx.x != 0.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].x = src[i].x + tx.x;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].x = src[i].x;
        }
    }
    if (tx.y != 0.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].y = src[i].y + tx.y;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].y = src[i].y;
        }
    }
    if (tx.z != 0.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].z = src[i].z + tx.z;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].z = src[i].z;
        }
    }
}


/** Scales a set of points.
 *
 */
void gfx3d_scale (const g3d_p3_t *src, g3d_p3_t *dst, const int npnts, const g3d_p3_t scl)
{
    int i;
    
    if (scl.x != 1.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].x = src[i].x * scl.x;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].x = src[i].x;
        }
    }
    if (scl.y != 1.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].y = src[i].y * scl.y;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].y = src[i].y;
        }
    }
    if (scl.z != 1.0f) {
        for (i=0; i<npnts; i++) {
            dst[i].z = src[i].z * scl.z;
        }
    } else if (src != dst) {
        for (i=0; i<npnts; i++) {
            dst[i].z = src[i].z;
        }
    }
}



/**
 * projects a set of 3D points into a 2D space (pretty crude)
 */
void gfx3d_project (const g3d_p3_t *src, g3d_2p3_t *dst, const int npnts)
{
    int i;
    
    for (i=0; i<npnts; i++) {
        float ez = src[i].z;
        
        dst[i].z = (int16_t)((ez + G3D_ZBADD) * G3D_ZBSCALE);
        ez += g3d_z_depth;
        dst[i].x = (int16_t)((src[i].x * g3d_x_scale) / ez) + G3D_X2_SHIFT;
        dst[i].y = (int16_t)((src[i].y * g3d_y_scale) / ez) + G3D_Y2_SHIFT;
    }
}


/** Takes a set of 8 projected points and creates a set of <=12 triangular polygons representing the surface of a cube.
 *  Also attaches texture pointers if given.
 */
void gfx3d_cubify_points (const g3d_2p3_t *src, g3d_poly_t *dst, int *npoly, const int backfaces, const uint8_t **txptrs)
{
    static const int cubemap3[12][3] = {{3,0,1}, {1,2,3}, {2,1,5}, {5,6,2}, {6,5,4}, {4,7,6}, {7,4,0}, {0,3,7}, {1,0,4}, {4,5,1}, {7,3,2}, {2,6,7}};
    static const uint16_t txmap[2][3] = {{0x1f00, 0x0000, 0x001f}, {0x001f, 0x1f1f, 0x1f00}};               /* 0xYYXX */
    
    int i, pidx;
    int norms[6];
    
    *npoly = 12;                /* assume all to start with */
    
    /* compute normals */
    for (i=0; i<6; i++) {
        const int *face = cubemap3[i*2];
        
        int norm = ((src[face[0]].y - src[face[1]].y) * (src[face[2]].x - src[face[1]].x)) -
                    ((src[face[2]].y - src[face[1]].y) * (src[face[0]].x - src[face[1]].x));
        if (!backfaces && (norm < 0)) {
            /* not showing this one */
            *npoly = *npoly - 2;
        }
        norms[i] = norm;
    }
    
    /* wind polygons */
    pidx = 0;
    for (i=0; (i<12) && (pidx < *npoly); i++) {
        if (!backfaces && (norms[i>>1] < 0)) {
            /* not showing this one */
        } else {
            int p;
            
            dst[pidx].norm = norms[i>>1];
            dst[pidx].txptr = (txptrs == NULL) ? NULL : (uint8_t *)txptrs[i>>1];
            
            for (p=0; p<3; p++) {
                dst[pidx].pts[p].x = src[cubemap3[i][p]].x;
                dst[pidx].pts[p].y = src[cubemap3[i][p]].y;
                dst[pidx].pts[p].z = src[cubemap3[i][p]].z;
                dst[pidx].tx_pts[p] = txmap[i&1][p];
            }
            pidx++;
        }
    }
}


/**
 * takes a set of 4 projected points and creates a set of <=2 triangular polygons representing the square
 */
void gfx3d_squarify_points (const g3d_2p3_t *src, g3d_poly_t *dst, int *npoly, const int backfaces)
{
    static const int squaremap3[2][3] = {{3,0,1}, {1,2,3}};
    static const uint16_t txmap[2][3] = {{0x1f00, 0x0000, 0x001f}, {0x001f, 0x1f1f, 0x1f00}};               /* 0xYYXX */
    
    int i, pidx;
    int norm = ((src[3].y - src[0].y) * (src[1].x - src[0].x)) -
                ((src[1].y - src[0].y) * (src[3].x - src[0].x));
    
    if (!backfaces && (norm < 0)) {
        /* not showing anything */
        *npoly = 0;
        return;
    }

    *npoly = 2;
    
    /* wind polygons */
    for (i=0; i<2; i++) {
        int p;
        
        dst[i].norm = norm;
        for (p=0; p<3; p++) {
            dst[i].pts[p].x = src[squaremap3[i][p]].x;
            dst[i].pts[p].y = src[squaremap3[i][p]].y;
            dst[i].pts[p].z = src[squaremap3[i][p]].z;
            dst[i].tx_pts[p] = txmap[i][p];
        }
    }
    return;
}



/**
 *  takes a polygon and draws its wireframe on the given LCD.
 */
void gfx3d_wirepoly (const g3d_poly_t *src, C12832 &lcd)
{
    #if G3D_MAX_POLY_POINTS == 3
    lcd.line (src->pts[0].x, src->pts[0].y, src->pts[1].x, src->pts[1].y, 1);
    lcd.line (src->pts[1].x, src->pts[1].y, src->pts[2].x, src->pts[2].y, 1);
    lcd.line (src->pts[2].x, src->pts[2].y, src->pts[0].x, src->pts[0].y, 1);
    #endif
}


/**
 *  takes a polygon and draws its wireframe on the given LCD, taking Z buffering into consideration
 */
void gfx3d_wirepoly_z (const g3d_poly_t *src, C12832 &lcd)
{
    #if G3D_MAX_POLY_POINTS == 3
    #endif
}



/**
 *  takes a set of 8 projected points and draws a wireframe cube on the given LCD.
 */
void gfx3d_wirecube (const g3d_2p3_t *src, C12832 &lcd)
{
    lcd.line (src[0].x, src[0].y, src[1].x, src[1].y, 1);
    lcd.line (src[1].x, src[1].y, src[2].x, src[2].y, 1);
    lcd.line (src[2].x, src[2].y, src[3].x, src[3].y, 1);
    lcd.line (src[3].x, src[3].y, src[0].x, src[0].y, 1);
    
    lcd.line (src[4].x, src[4].y, src[5].x, src[5].y, 1);
    lcd.line (src[5].x, src[5].y, src[6].x, src[6].y, 1);
    lcd.line (src[6].x, src[6].y, src[7].x, src[7].y, 1);
    lcd.line (src[7].x, src[7].y, src[4].x, src[4].y, 1);

    lcd.line (src[0].x, src[0].y, src[4].x, src[4].y, 1);
    lcd.line (src[1].x, src[1].y, src[5].x, src[5].y, 1);
    lcd.line (src[2].x, src[2].y, src[6].x, src[6].y, 1);
    lcd.line (src[3].x, src[3].y, src[7].x, src[7].y, 1);
}