#include <stdio.h>
#include <stdlib.h>

#define PORTABILITY_HACK1 1
typedef long fp14; // There are some architecture dependencies in this code (long vs long long, signed shifts etc)

//----------------------------------------------
//
// Add and subtract fixed point numbers. :-)
//

#define fpAdd(a1, a2) ((a1) + (a2))
#define fpSub(s1, s2) ((s1) - (s2))

//----------------------------------------------
//
// Convert an integer to n N.14 fixed point
// number and back.
//

#define int2fp(x) ((x) << 14)
#define fp2int(x) ((x) >> 14)

//----------------------------------------------
//
// Get the fractional part of an N.14 fixed point
// number.

#define fpFract(x) ((x) & 0x3fff)

//----------------------------------------------
//
// Convert a N.14 fixed point number to a
// double and back. Handy for printing and for
// those times when you really do want to work
// with floating point values.
//


#ifdef PORTABILITY_HACK1
#define fp2float(x) ( (double) (((double)(x)) / (double)(1 << 14))  )
#define float2fp(x) ((fp14) ((double)(x) * (double)(1 << 14)))
#endif

//----------------------------------------------
//
// Table based fixed point sine and cosine 
// functions.
//

#define fpSin(x) (sine[(x) & 0xff])
#define fpCos(x) (sine[((x)+64) & 0xff])

//----------------------------------------------
//
// 18.14 fixed point sine and cosine tables.
//
// These table assume that there are 256 angles
// in a circle.
//

fp14 sine[256] =
{
     0,    402,    803,   1205,   1605,   2005,   2404,   2801, 
  3196,   3589,   3980,   4369,   4756,   5139,   5519,   5896, 
  6269,   6639,   7005,   7366,   7723,   8075,   8423,   8765, 
  9102,   9434,   9759,  10079,  10393,  10701,  11002,  11297, 
 11585,  11866,  12139,  12406,  12665,  12916,  13159,  13395, 
 13622,  13842,  14053,  14255,  14449,  14634,  14810,  14978, 
 15136,  15286,  15426,  15557,  15678,  15790,  15892,  15985, 
 16069,  16142,  16206,  16260,  16305,  16339,  16364,  16379, 
 16384,  16379,  16364,  16339,  16305,  16260,  16206,  16142, 
 16069,  15985,  15892,  15790,  15678,  15557,  15426,  15286, 
 15136,  14978,  14810,  14634,  14449,  14255,  14053,  13842, 
 13622,  13395,  13159,  12916,  12665,  12406,  12139,  11866, 
 11585,  11297,  11002,  10701,  10393,  10079,   9759,   9434, 
  9102,   8765,   8423,   8075,   7723,   7366,   7005,   6639, 
  6269,   5896,   5519,   5139,   4756,   4369,   3980,   3589, 
  3196,   2801,   2404,   2005,   1605,   1205,    803,    402, 
     0,   -402,   -803,  -1205,  -1605,  -2005,  -2404,  -2801, 
 -3196,  -3589,  -3980,  -4369,  -4756,  -5139,  -5519,  -5896, 
 -6269,  -6639,  -7005,  -7366,  -7723,  -8075,  -8423,  -8765, 
 -9102,  -9434,  -9759, -10079, -10393, -10701, -11002, -11297, 
-11585, -11866, -12139, -12406, -12665, -12916, -13159, -13395, 
-13622, -13842, -14053, -14255, -14449, -14634, -14810, -14978, 
-15136, -15286, -15426, -15557, -15678, -15790, -15892, -15985, 
-16069, -16142, -16206, -16260, -16305, -16339, -16364, -16379, 
-16384, -16379, -16364, -16339, -16305, -16260, -16206, -16142, 
-16069, -15985, -15892, -15790, -15678, -15557, -15426, -15286, 
-15136, -14978, -14810, -14634, -14449, -14255, -14053, -13842, 
-13622, -13395, -13159, -12916, -12665, -12406, -12139, -11866, 
-11585, -11297, -11002, -10701, -10393, -10079,  -9759,  -9434, 
 -9102,  -8765,  -8423,  -8075,  -7723,  -7366,  -7005,  -6639, 
 -6269,  -5896,  -5519,  -5139,  -4756,  -4369,  -3980,  -3589, 
 -3196,  -2801,  -2404,  -2005,  -1605,  -1205,   -803,   -402, 
};


fp14
fpDiv(fp14 d1, fp14 d2)
{
#ifdef PORTABILITY_HACK1
  return float2fp(fp2float(d1)/fp2float(d2)); // alternative portable version
#else
  // tested on LCC on windows and GCC on 64 bit linux, but still may not be portable.  Signedness is an issue.
  return ((fp14)((((long long)d1<<14LL) / (long long)d2)<<14LL));
#endif
}

fp14
fpMul(fp14 m1, fp14 m2)
{
#ifdef PORTABILITY_HACK1
  return float2fp(fp2float(m1)*fp2float(m2)); // alternative portable version
#else
  // see above
  return ((fp14)(((long long)m1 * (long long)m2)>>14LL));
#endif
}

typedef struct VPoint
{
        int x,y,z;
} VPoint;

// for convenience and shorter code on Vectrex - need to do more of this...
// in GCC many of these short functions could be 'static inline' ...
int mult_sin(int coord, int angle)
{
  return fp2int(fpMul(int2fp(coord), fpSin(angle)));
}

int mult_cos(int coord, int angle)
{
  return fp2int(fpMul(int2fp(coord), fpCos(angle)));
}

// x,y,z - 'real' world (1024x1024x1024)
// xangle,yangle,zangle - rotate object around its own axis (note: need quaternions to avoid gimbal lock)
// screenx, screeny, screenz - 'virtual pixel' offset in screen coords (1280x1024)

void DrawShip(int ship, int x, int y, int z, int xangle, int yangle, int zangle, int screenx, int screeny)
{
int i;
// Ships hand-coded by Peter Hirschberger.  Great work, Peter!
// Original data was given to me as doubles (eg lines of 1.5 length) but I've scaled everything to ints
#define nNumVectors0 20
	VPoint vectors0[nNumVectors0*2] = 
	{
            { 0, 0, -100 },
            { 40, 0, -60 },
            { 40, 0, -60 },
            { 0, 0, -20 },
            { 0, 0, -20 },
            { -40, 0, -60 },
            { -40, 0, -60 },
            { 0, 0, -100 },
            { 0, 0, -100 },
            { 0, 40, -60 },
            { 0, 40, -60 },
            { 0, 0, -20 },
            { 0, 0, -20 },
            { 0, -40, -60 },
            { 0, -40, -60 },
            { 0, 0, -100 },
            { 0, 0, -20 },
            { 30, 0, 120 },
            { 0, 0, -20 },
            { 20, 20, 120 },
            { 0, 0, -20 },
            { -20, 20, 120 },
            { 0, 0, -20 },
            { -30, 0, 120 },
            { 30, 0, 120 },
            { 20, 20, 120 },
            { 20, 20, 120 },
            { -20, 20, 120 },
            { -20, 20, 120 },
            { -30, 0, 120 },
            { -30, 0, 120 },
            { 30, 0, 120 },
            { 30, 0, 120 },
            { 100, -40, 80 },
            { 100, -40, 80 },
            { 0, 0, -20 },
            { -30, 0, 120 },
            { -100, -40, 80 },
            { -100, -40, 80 },
            { 0, 0, -20 },
	};
#define nNumVectors1 21
	VPoint vectors1[nNumVectors1*2] = 
	{
            { 0, 0, -90 },
            { 45, 0, -60 },
            { 45, 0, -60 },
            { 45, 0, 60 },
            { 45, 0, 60 },
            { 0, 0, 90 },
            { 0, 0, 90 },
            { -45, 0, 60 },
            { -45, 0, 60 },
            { -45, 0, -60 },
            { -45, 0, -60 },
            { 0, 0, -90 },
            { 45, 0, 60 },
            { 60, 24, 60 },
            { 60, 24, 60 },
            { 60, 24, 150 },
            { 60, 24, 150 },
            { 45, 0, 150 },
            { 45, 0, 150 },
            { 45, 0, 60 },
            { -45, 0, 60 },
            { -60, 24, 60 },
            { -60, 24, 60 },
            { -60, 24, 150 },
            { -60, 24, 150 },
            { -45, 0, 150 },
            { -45, 0, 150 },
            { -45, 0, 60 },
            { -45, 0, 60 },
            { -60, -27, -90 },
            { -60, -27, -90 },
            { 0, -34, -129 },
            { 0, -34, -129 },
            { 60, -27, -90 },
            { 60, -27, -90 },
            { 45, 0, 60 },
            { 0, 0, -90 },
            { 0, -34, -129 },
            { 45, 0, -60 },
            { 60, -27, -90 },
            { -45, 0, -60 },
            { -60, -27, -90 },
	};
#define nNumVectors2 17
	VPoint vectors2[nNumVectors2*2] = 
	{
            { 0, 0, -100 },
            { 60, 0, -40 },
            { 60, 0, -40 },
            { 0, 0, 20 },
            { 0, 0, 20 },
            { -60, 0, -40 },
            { -60, 0, -40 },
            { 0, 0, -100 },
            { 0, 0, -100 },
            { 0, 40, -40 },
            { 0, 40, -40 },
            { 0, 0, 20 },
            { 60, 0, -40 },
            { 0, 40, -40 },
            { 0, 40, -40 },
            { -60, 0, -40 },
            { 0, 0, 20 },
            { 0, 80, 100 },
            { 0, 80, 100 },
            { 0, 40, 100 },
            { 0, 40, 100 },
            { 0, 0, 20 },
            { 0, 0, 20 },
            { 100, -40, 100 },
            { 100, -40, 100 },
            { 40, -20, 100 },
            { 40, -20, 100 },
            { 0, 0, 20 },
            { 0, 0, 20 },
            { -100, -40, 100 },
            { -100, -40, 100 },
            { -40, -20, 100 },
            { -40, -20, 100 },
            { 0, 0, 20 },
	};
#define nNumVectors3 23
	VPoint vectors3[nNumVectors3*2] = 
	{
            { 0, 2, -92 },
            { 30, 2, -70 },
            { 30, 2, -70 },
            { 0, 10, -20 },
            { 0, 10, -20 },
            { -30, 2, -70 },
            { -30, 2, -70 },
            { 0, 2, -92 },
            { 0, 2, -92 },
            { 0, -25, -45 },
            { 0, -25, -45 },
            { 0, 10, -20 },
            { 30, 2, -70 },
            { 22, 15, -60 },
            { -30, 2, -70 },
            { -22, 15, -60 },
            { 30, 2, -70 },
            { 0, -25, -45 },
            { -30, 2, -70 },
            { 0, -25, -45 },
            { 22, 15, -60 },
            { -22, 15, -60 },
            { 22, 15, -60 },
            { 0, 10, -20 },
            { -22, 15, -60 },
            { 0, 10, -20 },
            { 30, 2, -70 },
            { -30, 2, -70 },
            { 0, 10, -20 },
            { -20, -10, 110 },
            { 0, 10, -20 },
            { 20, -10, 110 },
            { -20, -10, 110 },
            { 20, -10, 110 },
            { 0, -25, -45 },
            { -20, -10, 110 },
            { 0, -25, -45 },
            { 20, -10, 110 },
            { 0, 10, -20 },
            { -90, 20, 90 },
            { -90, 20, 90 },
            { -20, -10, 110 },
            { 0, 10, -20 },
            { 90, 20, 90 },
            { 90, 20, 90 },
            { 20, -10, 110 },
	};
        
int     nvec[4] = {nNumVectors0,nNumVectors1,nNumVectors2,nNumVectors3}; 
        // The pvec array initialisation below caused LCC to crash:
        // so rather than have 'ifdef's for one compiler, I rewrote it as an explicit initialisation.
VPoint *pvec[4];// = {vectors0, vectors1, vectors2, vectors3};
int     nNumVectors = nvec[ship];

        pvec[0] = vectors0;
        pvec[1] = vectors1;
        pvec[2] = vectors2;
        pvec[3] = vectors3;
 
	for (i=0; i<nNumVectors*2; i+=2)
	{
          int x0, y0, z0, x1, y1, z1, tx0, ty0, tz0, tx1, ty1, tz1, xa, ya, xb, yb;
          x0 = pvec[ship][i].x*2/3; // last minute rescaling as the ships looked a little
          y0 = pvec[ship][i].y*2/3; // too large when entering space on the back plane.
          z0 = pvec[ship][i].z*2/3; // remove this later... rescale the data or fix it when we move to OpenGL matrices

          x1 = pvec[ship][i+1].x*2/3;
          y1 = pvec[ship][i+1].y*2/3;
          z1 = pvec[ship][i+1].z*2/3;

          /*
             The matrix code below is not ideal for plotting
             a pausible flight path (the 'Gimbal lock' problem).
             It would be much better to handle pitch/yaw/roll
             using quaternions.  I'm hacking it by restricting
             the degrees of freedom when a ship switches from
             a 2D path on the rear face to a 3D path approaching
             the viewport.

             THIS IS VERY HACKY CODE.  best solution would be to punt to OpenGL in a more major rewrite...

           */

          // rotate around z
          tx0 = mult_cos(x0, zangle)-mult_sin(y0, zangle);
          ty0 = mult_sin(x0, zangle)+mult_cos(y0, zangle);
          tz0 = z0;

          tx1 = mult_cos(x1, zangle)-mult_sin(y1, zangle);
          ty1 = mult_sin(x1, zangle)+mult_cos(y1, zangle);
          tz1 = z1;
          x0=tx0;y0=ty0;z0=tz0; x1=tx1;y1=ty1;z1=tz1;

          // rotate around y
          tz0 = mult_cos(z0, yangle)-mult_sin(x0, yangle);
          tx0 = mult_sin(z0, yangle)+mult_cos(x0, yangle);
          ty0 = y0;

          tz1 = mult_cos(z1, yangle)-mult_sin(x1, yangle);
          tx1 = mult_sin(z1, yangle)+mult_cos(x1, yangle);
          ty1 = y1;
          x0=tx0;y0=ty0;z0=tz0; x1=tx1;y1=ty1;z1=tz1;

          // rotate around x
          ty0 = mult_cos(y0, xangle)-mult_sin(z0, xangle);
          tz0 = mult_sin(y0, xangle)+mult_cos(z0, xangle);
          tx0 = x0;

          ty1 = mult_cos(y1, xangle)-mult_sin(z1, xangle);
          tz1 = mult_sin(y1, xangle)+mult_cos(z1, xangle);
          tx1 = x1;
          tx0 = tx0+x; ty0 = ty0+y; tx1 = tx1+x; ty1 = ty1+y;

          // treat 512,340^H^H^H400 as 0,0 for perspective purposes.  ANOTHER HACKY ALGORITHM!
	  // Good old 8-bit micro style coding...
#define TWEAK 2048 /* was 1024 */
          xa = ((TWEAK*tx0) / (TWEAK+tz0));
          ya = ((TWEAK*ty0) / (TWEAK+tz0));
          xb = ((TWEAK*tx1) / (TWEAK+tz1));
          yb = ((TWEAK*ty1) / (TWEAK+tz1));
//#undef TWEAK

          // scale by depth  ">>10" is short for "/ZDEPTH"
          xa = (xa*(z+200))>>10; ya=(ya*(z+200))>>10; xb=(xb*(z+200))>>10; yb=(yb*(z+200))>>10;

          // then re-correct back to center of screen at 512,400
          //CinemaVectorData(xa+512+screenx,ya+400+screeny,xb+512+screenx,yb+400+screeny, 7);
          fprintf(stdout, "%d,%d -> %d,%d\n", xa+screenx,ya+screeny,xb+screenx,yb+screeny);
	}
#undef nNumVectors0
#undef nNumVectors1
#undef nNumVectors2
#undef nNumVectors3
	printf("\n");
}

int main(int argc, char **argv) {
  int z;
  //for (z = 0; z < 4096; z+=512) 
    DrawShip(0, /* AT */ 0,0,1024, /* ROT */ 0,0,0, /* SCREEN */ 0,0);
  exit(0);
  return 0;
}