#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;
}