Spinning dna graphics demo for the Gameduino

Dependencies:   Gameduino mbed

main.cpp

Committer:
TheChrisyd
Date:
2012-12-21
Revision:
0:87bd6fb28ffb

File content as of revision 0:87bd6fb28ffb:

#include "mbed.h"
#include "GD.h"
#include "shield.h"
#include "arduino.h"

GDClass GD(ARD_MOSI, ARD_MISO, ARD_SCK, ARD_D9, USBTX, USBRX) ;
SPI spimain(ARD_MOSI, ARD_MISO, ARD_SCK); // mosi, miso, sclk
//#include <stdlib.h>
//#include <spimainmain.h>
//#include <GD.h>
Serial pcmain(USBTX, USBRX);
static uint16_t SWAP_RB(uint16_t color) { // Swap red and blue channel
    byte r = (color >> 10) & 31;
    byte g = (color >> 5) & 31;
    byte b = color & 31;
    return (color & 0x8000) | (b << 10) | (g << 5) | (r);
}

static uint16_t SWAP_RG(uint16_t color) { // Swap red and blue channel
    byte r = (color >> 10) & 31;
    byte g = (color >> 5) & 31;
    byte b = color & 31;
    return (color & 0x8000) | (g << 10) | (r << 5) | (b);
}

#include "dna.h"

////////////////////////////////////////////////////////////////////////////////
//                                  3D Projection
////////////////////////////////////////////////////////////////////////////////

static float mats[2][9];

static float mat[9];

// Taken from glRotate()
static void rotation(float phi) {
    float x = 0.57735026918962573;
    float y = 0.57735026918962573;
    float z = 0.57735026918962573;

    float s = sin(phi);
    float c = cos(phi);

    mat[0] = x*x*(1-c)+c;
    mat[1] = x*y*(1-c)-z*s;
    mat[2] = x*z*(1-c)+y*s;

    mat[3] = y*x*(1-c)+z*s;
    mat[4] = y*y*(1-c)+c;
    mat[5] = y*z*(1-c)-x*s;

    mat[6] = x*z*(1-c)-y*s;
    mat[7] = y*z*(1-c)+x*s;
    mat[8] = z*z*(1-c)+c;
}

#ifdef MAPLE_IDE
#define NVERTICES 250
#else
#define NVERTICES 220   // Arduino does not have enough RAM for all 250 
#endif

struct screenpt {
    short x, y, z;
};
static struct screenpt projected[NVERTICES];

void project(float distance) {
    byte vx;
    const prog_char *pm = cloud;
    const prog_char *pm_e = cloud + (NVERTICES*3);
    struct screenpt *dst = projected;
    signed char x, y, z;

    while (pm < pm_e) {
        x = pgm_read_byte_near(pm++);
        y = pgm_read_byte_near(pm++);
        z = pgm_read_byte_near(pm++);
        float xx = x * mat[0] + y * mat[3] + z * mat[6];
        float yy = x * mat[1] + y * mat[4] + z * mat[7];
        float zz = x * mat[2] + y * mat[5] + z * mat[8] + distance;
        int scale = 200;
        float q = scale / (250 + zz);
        dst->x = (511 & (int)(200 + xx * q)) | (((x^y^z) & 3) << 14);
        dst->y = (int)(150 + yy * q);
        dst->z = (int)(zz * 100);
        dst++;
    }
}

// point depth comparison, for depth sort
int ptcmp(const void *va, const void *vb) {
    struct screenpt *a = (struct screenpt *)va;
    struct screenpt *b = (struct screenpt *)vb;

    if (a->z < b->z)
        return 1;
    else if (a->z > b->z)
        return -1;
    else
        return 0;
}

// load a sprite at (x,y).  z is distance 0-63, color is 0-3
static void render_sphere(int x, int y, byte z, byte color) {
    static byte pals[4][2] = {
        { 4,6 },
        { 5,7 },
        { 0,1 },
        { 2,3 }
    };

    int ox = x - 8;
    int oy = y - 8;
    byte palette = pals[color][z & 1];
    byte image = (z >> 1);

    spimain.write(lowByte(ox));
    spimain.write((palette << 4) | (highByte(ox) & 1));
    spimain.write(lowByte(oy));
    spimain.write((image << 1) | (highByte(oy) & 1));
}

void draw(float distance) {
    project(distance);
    qsort(projected, NVERTICES, sizeof(struct screenpt), ptcmp);

    static byte flip;
    GD.__wstartspr(flip ? 256 : 0);
    for (int i = 0; i < NVERTICES; i++) {
        byte color = (projected[i].x >> 14) & 3;
        short x = projected[i].x & 511;
        short y = projected[i].y;
        int z = max(0, min(63, int(32 + projected[i].z / 500)));
        render_sphere(x, y, z, color);
    }
    GD.__end();
    GD.wr(SPR_PAGE, flip);
    flip = !flip;
}

static float phi;    // Current rotation angle

// Draw one frame of ship
void cycle(float distance) {
    rotation(phi);
    phi += 0.02;
    draw(distance);

    // report frame rate in top-right
    static byte every;
    if (++every == 4) {
        static long tprev;
        long t = micros();
        every = 0;

        char msg[30];
        int fps10 = int(4 * 10000000UL / (t - tprev));
        sprintf(msg, "%3d.%d fps  ", fps10 / 10, fps10 % 10);
        GD.putstr(41, 0, msg);
        tprev = t;
    }

}

// this function has been changed to handle the gameduino being in little endian
static uint16_t rdpal(int i) {
    uint16_t swapped =  (sphere_pal[(i << 1)+1] << 8) | ((sphere_pal[(i << 1)]));
    return swapped;
}

void setup() {
    GD.begin();
    GD.ascii();

    for (byte y = 0; y < 38; y++) {
        prog_uchar *src = ramp_pic + y * 4;
        for (byte x = 0; x < 50; x++)
            GD.wr(RAM_PIC + y * 64 + x, pgm_read_byte(src + random(4)));
    }
    GD.copy(RAM_CHR + 128 * 16, ramp_chr, sizeof(ramp_chr));
    GD.copy(RAM_PAL + 128 * 8, ramp_pal, sizeof(ramp_pal));

    GD.copy(PALETTE16A, sphere_pal, sizeof(sphere_pal));
    for (byte i = 0; i < 16; i++) {
        GD.wr16(PALETTE16B + 2 * i, SWAP_RB(rdpal(i)));
    }

    for (int i = 0; i < 256; i++) {
        // palette 0 decodes low nibble, hence (i & 15)
        GD.wr16(RAM_SPRPAL + (i << 1), SWAP_RG(rdpal(i & 15)));
        // palette 1 decodes nigh nibble, hence (i >> 4)
        GD.wr16(RAM_SPRPAL + 512 + (i << 1), SWAP_RG(rdpal(i >> 4)));

        // palette 0 decodes low nibble, hence (i & 15)
        GD.wr16(RAM_SPRPAL + 1024 + (i << 1), SWAP_RB(SWAP_RG(rdpal(i & 15))));
        // palette 1 decodes nigh nibble, hence (i >> 4)
        GD.wr16(RAM_SPRPAL + 1024 + 512 + (i << 1), SWAP_RB(SWAP_RG(rdpal(i >> 4))));
    }

    GD.copy(RAM_SPRIMG, sphere_img, sizeof(sphere_img));
    GD.putstr(0, 0, "DNA demo: Gameduino with Mbed");

}

void loop() {
    cycle(0.0);
}

int main() {
    timer_start();
    setup();
    while (1) {
        loop();
    }
}