2013-10-28 13:19:25 +00:00

264 lines
5.6 KiB
Plaintext

#include <SPI.h>
#include <GD.h>
////////////////////////////////////////////////////////////////////////////////
// Plotter
////////////////////////////////////////////////////////////////////////////////
#include "wireframe.h"
#include "eraser.h"
// replicate a 2-bit color across the whole byte.
byte replicate(byte color)
{
return (color << 6) | (color << 4) | (color << 2) | color;
}
#define BLACK RGB(0,0,0)
#define WHITE RGB(255,255,255)
class PlotterClass
{
public:
void begin();
void line(byte x0, byte y0, byte x1, byte y1);
void show();
private:
byte flip;
byte plotting;
void erase();
void waitready();
};
PlotterClass Plotter;
void PlotterClass::waitready()
{
while (GD.rd(COMM+7))
;
}
void PlotterClass::erase()
{
byte color = flip ? 1 : 2;
plotting = 0;
GD.wr(J1_RESET, 1);
GD.wr(COMM+7, 1);
GD.wr(COMM+8, replicate(color ^ 3));
GD.microcode(eraser_code, sizeof(eraser_code));
}
void PlotterClass::begin()
{
// Draw 256 sprites left to right, top to bottom, all in 4-color
// palette mode. By doing them in column-wise order, the address
// calculation in setpixel is made simpler.
// First 64 use bits 0-1, next 64 use bits 2-4, etc.
// This gives a 256 x 256 4-color bitmap.
unsigned int i;
for (i = 0; i < 256; i++) {
int x = 72 + 16 * ((i >> 4) & 15);
int y = 22 + 16 * (i & 15);
int image = i & 63; /* image 0-63 */
int pal = 3 - (i >> 6); /* palettes bits in columns 3,2,1,0 */
GD.sprite(i, x, y, image, 0x8 | (pal << 1), 0);
}
flip = 0;
plotting = 0;
erase();
show();
}
void PlotterClass::show()
{
waitready();
if (flip == 1) {
GD.wr16(PALETTE4A, BLACK);
GD.wr16(PALETTE4A + 2, WHITE);
GD.wr16(PALETTE4A + 4, BLACK);
GD.wr16(PALETTE4A + 6, WHITE);
} else {
GD.wr16(PALETTE4A, BLACK);
GD.wr16(PALETTE4A + 2, BLACK);
GD.wr16(PALETTE4A + 4, WHITE);
GD.wr16(PALETTE4A + 6, WHITE);
}
flip ^= 1;
erase();
}
void PlotterClass::line(byte x0, byte y0, byte x1, byte y1)
{
byte swap;
#define SWAP(a, b) (swap = (a), (a) = (b), (b) = swap)
byte steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
SWAP(x0, y0);
SWAP(x1, y1);
}
if (x0 > x1) {
SWAP(x0, x1);
SWAP(y0, y1);
}
int deltax = x1 - x0;
int deltay = abs(y1 - y0);
int error = deltax / 2;
signed char ystep;
if (y0 < y1)
ystep = 1;
else
ystep = -1;
byte x;
byte y = y0;
waitready();
if (!plotting) {
GD.microcode(wireframe_code, sizeof(wireframe_code));
plotting = 1;
byte color = flip ? 1 : 2;
GD.wr(COMM+8, color << 6);
}
GD.__wstart(COMM+0);
SPI.transfer(x0);
SPI.transfer(y0);
SPI.transfer(x1);
SPI.transfer(y1);
SPI.transfer(steep);
SPI.transfer(deltax);
SPI.transfer(deltay);
SPI.transfer(ystep);
GD.__end();
}
////////////////////////////////////////////////////////////////////////////////
// 3D Projection
////////////////////////////////////////////////////////////////////////////////
struct ship
{
const char *name;
byte nvertices;
prog_char *vertices;
byte nedges;
prog_uchar *edges;
};
#include "eliteships.h"
#define NSHIPS (sizeof(eliteships) / sizeof(eliteships[0]))
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;
}
static byte projected[40 * 2];
void project(struct ship *s, float distance)
{
byte vx;
prog_char *pm = s->vertices;
prog_char *pm_e = pm + (s->nvertices * 3);
byte *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;
float q = 140 / (140 + zz);
*dst++ = byte(128 + xx * q);
*dst++ = byte(128 + yy * q);
}
}
void draw(struct ship *s, float distance)
{
project(s, distance);
prog_uchar *pe = s->edges;
prog_uchar *pe_e = pe + (s->nedges * 2);
while (pe < pe_e) {
byte *v0 = &projected[pgm_read_byte_near(pe++) << 1];
byte *v1 = &projected[pgm_read_byte_near(pe++) << 1];
Plotter.line(v0[0], v0[1], v1[0], v1[1]);
}
}
void setup()
{
GD.begin();
GD.ascii();
GD.putstr(0, 0, "Accelerated wireframe");
Plotter.begin();
}
static byte sn; // Ship number, 0-NSHIPS
static float phi; // Current rotation angle
// Draw one frame of ship
void cycle(float distance)
{
rotation(phi);
phi += 0.02;
draw(&eliteships[sn], distance);
// GD.waitvblank(); // uncomment this to sync to 72Hz frame rate
Plotter.show();
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;
}
}
void loop()
{
const char *name = eliteships[sn].name;
GD.putstr(0, 36, " ");
GD.putstr(25 - strlen(name) / 2, 36, name);
int d;
for (d = 0; d < 100; d++)
cycle(1000 - 10 * d);
for (d = 0; d < 72*6; d++)
cycle(0.0);
for (d = 0; d < 100; d++)
cycle(10 * d);
sn = (sn + 1) % NSHIPS;
}