#define VECTREX 1
/*

    After many previous attempts, I am having one final effort to create a C version
    of Tailgunner.  Graphics will consist of DrawLine(x1,y1, x2,y2, intensity) and Clear()
    so should be replacable on any system, while remaining easy enough to build on the
    Vectrex.
  
 */

#ifdef VECTREX
// Compile with -O2 option
#include <vectrex.h>
#define set_scale(A) dp_VIA_t1_cnt_lo = A
#define int8_t int
#define uint8_t unsigned int
#define int16_t long
//#define int32_t long long
static int InitGraphics(void) {
  return 0;
}

#else

#include <stdint.h>
#include <stdio.h>

static int debug = 0;

#define FPS 30

#define SHOW_FPS 1
#define HORIZONTAL 1

#define USE_FB_320X480 1
#ifdef USE_FB_320X480
#define USE_FB 1
#endif

#ifdef USE_FB
#define FBGL_IMPLEMENTATION 1
#include "fbgl.h"
fbgl_psf1_font_t *font;
#endif

#ifdef USE_FB
static fbgl_t fb;
static uint32_t fbcolor = 0xFFFFFF;

void fbDrawColor(uint8_t r, uint8_t g, uint8_t b, uint32_t c) {
  fbcolor = (c<<16) | (c<<8) | c;
}

void fbDrawPoint(int32_t x0, int32_t y0) {
  if (debug) fprintf(stdout, "fbDrawPoint(%d,%d);\r\n", x0, y0);
  int sx = (x0*fb.width/128)+fb.width/2;
  int sy = (y0*fb.width/128)+fb.height/2;
  if (debug) fprintf(stdout, "fbgl_put_pixel(%d,%d, %d,%d, %06x, %dp;\r\n", sx, sy,  fbcolor, &fb);
  fbgl_put_pixel(sx, (fb.height-1)-sy, fbcolor, &fb);
}

void fbDrawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
  if (debug) fprintf(stdout, "fbDrawLine(%d,%d, %d,%d);\r\n", x0, y0,  x1, y1);
  int sx0 = (x0*fb.width/128)+fb.width/2;
  int sy0 = (y0*fb.width/128)+fb.height/2;
  int sx1 = (x1*fb.width/128)+fb.width/2;
  int sy1 = (y1*fb.width/128)+fb.height/2;
  if (debug) fprintf(stdout, "fbgl_draw_line(%d,%d, %d,%d, %06x, %p);\r\n", sx0, sy0,  sx1, sy1,  fbcolor, &fb);
  fbgl_draw_line((fbgl_point_t){.x=sx0,.y=(fb.height-1)-sy0},
                 (fbgl_point_t){.x=sx1,.y=(fb.height-1)-sy1},
                 fbcolor, &fb);
}
#endif

static int8_t vscale = 127;
static int8_t vbright = 0x7F;
static int8_t vx = 0;
static int8_t vy = 0;

static int fromx = 0, fromy = 0;
static void G_MoveAbs(int8_t x, int8_t y, uint8_t scale) {
  if (debug) fprintf(stdout, "G_MoveAbs(%d,%d, scale=%u);\r\n", x, y, scale);
  fromx = (x * scale) / 256; fromy = (y * scale) / 256;
  if (debug) fprintf(stdout, "fromx = %d; fromy = %d;\r\n", fromx, fromy);
}
static void G_MoveRel(int8_t x, int8_t y, uint8_t scale) {
  if (debug) fprintf(stdout, "G_MoveRel(%d,%d, scale=%u);\r\n", x, y, scale);
  fromx += (x * scale) / 256; fromy += (y * scale) / 256;
  if (debug) fprintf(stdout, "fromx = %d; fromy = %d;\r\n", fromx, fromy);
}
static void G_LineRel(int8_t x, int8_t y, uint8_t intensity, uint8_t scale) {
  if (debug) fprintf(stdout, "G_LineRel(%d,%d, intensity=%d, scale=%d);\r\n", x, y, intensity, scale);
  int tox, toy;
  tox = (x * scale) / 256; toy = (y * scale) / 256;
  // draw [from,fromy] to [tox,toy]
#ifdef USE_FB
  fbDrawColor(intensity, intensity, intensity, intensity);
  fbDrawLine(fromx, fromy, fromx+tox, fromy+toy);
#endif
  fromx += tox; fromy += toy;
}
static void Clear(void) {
  // clear screen
  if (debug) fprintf(stdout, "clear();\r\n");
#ifdef USE_FB
  fbgl_clear(&fb);
#endif
}
static int InitGraphics(void) {
#ifdef USE_FB
  if (debug) fprintf(stderr, "InitGraphics();\r\n");
  // Load PSF1 font
  font = fbgl_load_psf1_font("font-16.psf");
  if (!font) {
    fprintf(stderr, "Failed to load PSF1 font-16.psf\r\n");
    return 1;
  }

  if (fbgl_init("/dev/fb1", &fb) == -1) {
    fprintf(stdout, "Error: could not open framebuffer device\r\n");
    return -1;
  }
  // Initialize keyboard
  if (fbgl_keyboard_init() != 0) {
    fprintf(stderr, "Failed to initialize keyboard\r\n");
    fbgl_destroy(&fb);
    return 1;
  }
  fbgl_set_fps(FPS);
#endif
}
static void set_scale(uint8_t scale) {
  vscale = scale;
}
static void Wait_Recal(void) {
  if (debug) fprintf(stdout, "Wait_Recal();\r\n");
  usleep(1000000/FPS);
  Clear();
}
static void Intensity_7F(void) {
  vbright = 0x7F;
}
static void Intensity_a(uint8_t i) {
  vbright = i;
}
static void Reset0Ref(void) {
  if (debug) fprintf(stdout, "Reset0Ref();\r\n");
  G_MoveAbs(vx = 0, vy = 0, vscale);
}
static void Moveto_d(int8_t y, int8_t x) {
  if (debug) fprintf(stdout, "Moveto_d(y=%d, x=%d);\r\n", y, x);
  G_MoveRel(vx = x, vy = y, vscale);
}
static void Draw_Line_d(int8_t y, int8_t x) {
  if (debug) fprintf(stdout, "Draw_Line_d(y=%d, x=%d);\r\n", y, x);
  G_LineRel(x, y, vbright, vscale);
}
#endif

// define this to test with a single object (which does run under 30K cycles)
//#define JUST_ONE 1

// Note: doesn't correct for slightly non-square aspect ratio,
//       so a cube will be stretched vertically a smidgeon.
//       Also, we don't handle perspective, but who cares?

#define MAX_VERTICES 100

typedef struct {
    int8_t x, y;
} screen;

static inline int8_t trig_mul(int8_t a, int8_t b) {
  return (int8_t)(((int16_t)a * (int16_t)b) >> 6L);
}

// Although this table could be shortened, a full table is faster...
static const int8_t sin_table[256] = {
  0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
  24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
  45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
  59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
  59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
  45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
  24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
  0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23,
  -24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44,
  -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, -59,
  -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -64, -64, -64, -64, -64,
  -64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60,
  -59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46,
  -45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26,
  -24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2,
};

static inline int8_t sin_fixed(uint8_t angle) {
    return sin_table[angle&0xFF];
}

static inline int8_t cos_fixed(uint8_t angle) {
  return sin_table[(angle + 64U)&0xFF]; // &0xFF is implicit using 8-bit ints
}

// Objects are stored as 8-bit coordinates centered on 0,0,0

// A model is a list of vertices, plus a list of lines to be drawn from vertex to vertex.

// Sin and Cos tables are lookups returning 8 bit values in the range -64:64, where
// 64 represents 1.0f and -64 is -1.0f  This is to avoid the need for an extra byte
// in calculations if -128:128 were used, or expensive divides if -127:127 were used.

// We will apply the transformations to the vertices first, then draw the ship using
// the transformed vertices.  This differs from the original code where each line was
// being transformed resulting in redundant transformations being calculated any time
// two lines met at a point.  Drawing is optimised by only issuing a move when the
// next line does not follow the proceding line.  In those cases a reset0ref is also
// performed.

typedef struct {
    int8_t x, y, z;
} point3d;

typedef struct {
    int8_t from, to;
} edge_t;

typedef struct {
  const edge_t *edges;
  const point3d *vertices;
  const uint8_t count;
  const uint8_t edge_count;
} object_t;

// A test model:
#define CUBE_VERTICES 8
#define CUBE_EDGES 8
#define CUBE_SIZE 62 // No model coordinates larger than this value.
point3d cube_vertices[CUBE_VERTICES] = {
    {-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE},
    { CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE},
    { CUBE_SIZE,  CUBE_SIZE, -CUBE_SIZE},
    {-CUBE_SIZE,  CUBE_SIZE, -CUBE_SIZE},
    {-CUBE_SIZE, -CUBE_SIZE,  CUBE_SIZE},
    { CUBE_SIZE, -CUBE_SIZE,  CUBE_SIZE},
    { CUBE_SIZE,  CUBE_SIZE,  CUBE_SIZE},
    {-CUBE_SIZE,  CUBE_SIZE,  CUBE_SIZE}
};
const edge_t cube_edges[] = { // values are from,to indexes into a vertex array
    {0, 1}, {1, 2}, {2, 3}, {3, 0},
    {4, 5}, {5, 6}, {6, 7}, {7, 4},
    {0, 4}, {1, 5}, {2, 6}, {3, 7},
    {-1,-1}
};
object_t cube0 = {
  cube_edges, cube_vertices, CUBE_VERTICES, CUBE_EDGES
};
object_t cube1 = {
  cube_edges, cube_vertices, CUBE_VERTICES, CUBE_EDGES
};
object_t cube2 = {
  cube_edges, cube_vertices, CUBE_VERTICES, CUBE_EDGES
};

#define DEMO_POINTS1 12
static const point3d ship1_vertex[DEMO_POINTS1] = {
  /* 0 */ { 0, 0, -100/2 },
  /* 1 */ { 0, 0, -20/2 },

  /* 2 */ { 0, -40/2, -60/2 },
  /* 3 */ { 0, 40/2, -60/2 },

  /* 4 */ { -100/2, -40/2, 80/2 },
  /* 5 */ { 100/2, -40/2, 80/2 },

  /* 6 */ { -20/2, 20/2, 120/2 },
  /* 7 */ { 20/2, 20/2, 120/2 },

  /* 8 */ { -30/2, 0, 120/2 },
  /* 9 */ { 30/2, 0, 120/2 },

  /* 10 */ { -40/2, 0, -60/2 },
  /* 11 */ { 40/2, 0, -60/2 },
};
#define DEMO_EDGES1 20
static const edge_t ship1_edge[DEMO_EDGES1+1] = {
  // These got corrupted but I was able to repair them by hand.
  {  0, 10 },
  { 10,  1 },
  {  1, 11 },
  { 11,  0 },

  {  0,  2 },
  {  2,  1 },
  {  1,  3 },
  {  3,  0 },

  {  1,  9 },
  {  1,  7 },
  {  1,  6 },
  {  1,  8 },

  {  9,  7 },
  {  7,  6 },
  {  6,  8 },
  {  8,  9 },

  {  9,  5 },
  {  5,  1 },

  {  8,  4 },
  {  4,  1 },

  { -1,-1 }
};
object_t ship1 = {
  ship1_edge, ship1_vertex, DEMO_POINTS1, DEMO_EDGES1
};

#define DEMO_POINTS2 15
static const point3d ship2_vertex[DEMO_POINTS2] = {
  /*  0 */          { 0, 0, -90/2 },
  /*  1 */          { 0, 0, 90/2 },
  /*  2 */          { 0, -34/2, -128/2 },
  /*  3 */          { -45/2, 0, 127/2 },
  /*  4 */          { 45/2, 0, 127/2 },
  /*  5 */          { -45/2, 0, -60/2 },
  /*  6 */          { -45/2, 0, 60/2 },
  /*  7 */          { 45/2, 0, -60/2 },
  /*  8 */          { 45/2, 0, 60/2 },
  /*  9 */          { -60/2, 24/2, 127/2 },
  /* 10 */          { 60/2, 24/2, 127/2 },
  /* 11 */          { -60/2, 24/2, 60/2 },
  /* 12 */          { 60/2, 24/2, 60/2 },
  /* 13 */          { -60/2, -27/2, -90/2 },
  /* 14 */          { 60/2, -27/2, -90/2 },
};
#define DEMO_EDGES2 21
static const edge_t ship2_edge[DEMO_EDGES2+1] = {
  {  0,  7},
  {  7,  8},
  {  8,  1},
  {  1,  6},
  {  6,  5},
  {  5,  0},
  {  8, 12},
  { 12, 10},
  { 10,  4},
  {  4,  8},
  {  6, 11},
  { 11,  9},
  {  9,  3},
  {  3,  6},
  {  6, 13},
  { 13,  2},
  {  2, 14},
  { 14,  8},
  {  0,  2},
  {  7, 14},
  {  5, 13},
  { -1,-1 }
};

object_t ship2 = {
  ship2_edge, ship2_vertex, DEMO_POINTS2, DEMO_EDGES2
};

#define DEMO_POINTS3 11
static const point3d ship3_vertex[DEMO_POINTS3] = {
  /*  0 */          { 0, 0, -100/2 },
  /*  1 */          { 0, 0, 20/2 },
  /*  2 */          { 0, 40/2, 100/2 },
  /*  3 */          { 0, 40/2, -40/2 },
  /*  4 */          { 0, 80/2, 100/2 },
  /*  5 */          { -100/2, -40/2, 100/2 },
  /*  6 */          { 100/2, -40/2, 100/2 },
  /*  7 */          { -40/2, -20/2, 100/2 },
  /*  8 */          { 40/2, -20/2, 100/2 },
  /*  9 */          { -60/2, 0, -40/2 },
  /* 10 */          { 60/2, 0, -40/2 },
};

#define DEMO_EDGES3 17
static const edge_t ship3_edge[DEMO_EDGES3+1] = {
  {  0, 10},
  { 10,  1},
  {  1,  9},
  {  9,  0},
  {  0,  3},
  {  3,  1},
  { 10,  3},
  {  3,  9},
  {  1,  4},
  {  4,  2},
  {  2,  1},
  {  1,  6},
  {  6,  8},
  {  8,  1},
  {  1,  5},
  {  5,  7},
  {  7,  1},
  { -1,-1 }
};
object_t ship3 = {
  ship3_edge, ship3_vertex, DEMO_POINTS3, DEMO_EDGES3
};

#define DEMO_POINTS4 11
static const point3d ship4_vertex[DEMO_POINTS4] = {
  /*  0 */          { 0, 10/2, -20/2 },
  /*  1 */          { 0, -25/2, -45/2 },
  /*  2 */          { 0, 2/2, -92/2 },
  /*  3 */          { -20/2, -10/2, 110/2 },
  /*  4 */          { 20/2, -10/2, 110/2 },
  /*  5 */          { -22/2, 15/2, -60/2 },
  /*  6 */          { 22/2, 15/2, -60/2 },
  /*  7 */          { -30/2, 2/2, -70/2 },
  /*  8 */          { 30/2, 2/2, -70/2 },
  /*  9 */          { -90/2, 20/2, 90/2 },
  /* 10 */          { 90/2, 20/2, 90/2 },
};

#define DEMO_EDGES4 23
static const edge_t ship4_edge[DEMO_EDGES4+1] = {
  {  2,  8},
  {  8,  0},
  {  0,  7},
  {  7,  2},
  {  2,  1},
  {  1,  0},
  {  8,  6},
  {  7,  5},
  {  8,  1},
  {  7,  1},
  {  6,  5},
  {  6,  0},
  {  5,  0},
  {  8,  7},
  {  0,  3},
  {  0,  4},
  {  3,  4},
  {  1,  3},
  {  1,  4},
  {  0,  9},
  {  9,  3},
  {  0, 10},
  { 10,  4},
  { -1,-1 }
};
object_t ship4 = {
  ship4_edge, ship4_vertex, DEMO_POINTS4, DEMO_EDGES4
};

static screen projected_vertices[MAX_VERTICES]; // As many vertices as largest object requires.

static inline void draw_object(const edge_t *edge, uint8_t scale, int8_t ox, int8_t oy) {
  int8_t i = 0, from, to, last_to = -1, vfx, vfy;
  set_scale(scale);
  for (;;) {
    if ((from = edge[i].from) < 0) return;
    to = edge[i].to; vfx = projected_vertices[from].x; vfy = projected_vertices[from].y;
    if (from != last_to) { Reset0Ref(); Moveto_d(oy, ox);  Moveto_d(vfy, vfx); }
    last_to = to;
    Draw_Line_d(projected_vertices[to].y - vfy, projected_vertices[to].x - vfx);
    i += 1;
  }
}

static inline void project_vertex(const point3d *v, uint8_t ax, uint8_t ay, uint8_t az, screen *vec) {
    int8_t x = v->x;
    int8_t y = v->y;
    int8_t z = v->z;

    // Rotate around X axis
    int8_t cosy = cos_fixed(ax);
    int8_t siny = sin_fixed(ax);
    int8_t y1 = trig_mul(y, cosy) -  trig_mul(z, siny);
    int8_t z1 = trig_mul(y, siny) +  trig_mul(z, cosy);
    y = y1; z = z1;

    // Rotate around Y axis
    int8_t cosx = cos_fixed(ay);
    int8_t sinx = sin_fixed(ay);
    int8_t x1 =   trig_mul(x, cosx) +  trig_mul(z, sinx);
    int8_t z2 = - trig_mul(x, sinx) +  trig_mul(z, cosx);
    x = x1; z = z2;

    // Rotate around Z axis
    int8_t cosz = cos_fixed(az);
    int8_t sinz = sin_fixed(az);
    int8_t x2 =  trig_mul(x, cosz) -  trig_mul(y, sinz);
    int8_t y2 =  trig_mul(x, sinz) +  trig_mul(y, cosz);

    // A simple x,y projection will suffice!
    // However if we really needed perspective to be applied, we could map the X value
    // using a lookup table based on the point's Z coordinate.
    vec->x = x2;
    vec->y = y2;

}

static inline void project_object(const object_t *object,
                                  uint8_t angle_x, uint8_t angle_y, uint8_t angle_z,
                                  uint8_t scale,
                                  int8_t x, int8_t y) {
  Intensity_a(((255U-scale)>>2)+48U);
#ifndef NOT_FAILED_EXPERIMENT
  // Going with the original code ... this turns out to be faster than the attempts below.
  for (uint8_t i = 0; i < object->count; i++) {
    project_vertex(&object->vertices[i], angle_x, angle_y, angle_z, &projected_vertices[i]);
  }
#else // 17 20 21 23
  // for unfathomable reasons this is *much* slower.
  const point3d *objectvertexptr = &object->vertices[0];
  screen *scr = &projected_vertices[0];
#ifndef NOT_ANOTHER_FAILURE
  for (uint8_t i = 0; i < object->count; i++) {
    project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  }
#else
  // Slow.  Even if project_vertex is defined as inline
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  if (object->edge_count == 17) goto next;
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  if (object->edge_count == 20) goto next;
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  if (object->edge_count == 21) goto next;
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
  project_vertex(objectvertexptr++, angle_x, angle_y, angle_z, scr++);
next:
#endif
#endif
  draw_object(object->edges, scale, x, y);
}

int main(void) {
  object_t *ship[4] = { &ship1, &ship2, &ship3, &ship4 };
  int8_t x[4], y[4];

  // rotational values for 4 ships. (Though TG only needs 3 copies of the same ship)
  uint8_t angle_x[4] = {5,5,5,0}, angle_y[4] = {5,0,4,0}, angle_z[4] = {5,0,250,0};  // 0:255 angles make a circle.

  uint8_t scale = 5;  // We'll use hardware scaling and adjust it separately from the viewing angle transformation

  (void)InitGraphics();
  x[0] = -120; y[0] = -40;
  x[1] = 0; y[1] = 60+(int8_t)(scale>>2U);
  x[2] = 120; y[2] = -40;
  x[3] = 0; y[3] = -90;
  for (;;) {
#ifdef USE_FB
    fbgl_key_t key = fbgl_poll_key(); // poor library interface - short term hack
    if (key == FBGL_KEY_ESCAPE) {
      fbgl_destroy(&fb);
      exit(EXIT_SUCCESS);
    }
#endif
    Wait_Recal();
    Reset0Ref();
    Intensity_7F();
    scale += 1;

    // staggering the ships (as in the real game) avoids having all 3 on screen at
    // full magnification at the same time and so cuts down on the maximum cycles.

    x[1] = 0; y[1] = 60+(int8_t)(scale>>2U);
#ifdef REMOVED
    // This is *much* slower than the code below.  Must be due to implementation of 'static inline'.
    for (int i = 0; i < 3; i++) {
      project_object(ship[i], angle_x[i], angle_y[i], angle_z[i], scale+40, x[i],y[i]);
    }
#else
#ifdef LESS_BAD
    project_object(ship[1], angle_x[0], angle_y[0], angle_z[0], scale+40, -120, -40);
    project_object(ship[2], angle_x[1], angle_y[1], angle_z[1], scale,    0,  60+(int8_t)(scale>>2U));
    project_object(ship[3], angle_x[2], angle_y[2], angle_z[2], scale+80,  120, -40);
    //project_object(ship[4], angle_x[3], angle_y[3], angle_z[3], projected_vertices, scale+40,  0, -90);
#else
    // this is the one we're using...
    (void)ship; (void)angle_x; (void)angle_y; (void)angle_z;
    project_object(&ship2, 5,5,5, scale+40, -120, -40);
    project_object(&ship3, 5,0,0, scale,    0,  60+(int8_t)(scale>>2U));
    project_object(&ship4, 5,4,250,scale+80,  120, -40);
    //project_object(ship3, angle_x[3], angle_y[3], angle_z[3], projected_vertices, scale+40,  0, -90);
#endif
#endif
    if (scale == 255) scale = 10;
  }
}
