/*
 * COPYRIGHT 1988 by Raymond S. Brand, All rights reserved.
 *
 * 19880904
 */

#include <math.h>
#include <exec/types.h>
#include <graphics/rastport.h>

#define XROTI 4
#define YROTI 5
#define ZROTI 6


#define SCREEN_DIAGONAL 13       /* inches */
#define SCREEN_WIDTH 640         /* pixels */
#define SCREEN_HEIGHT 200        /* pixels */
#define VIEW_DISTANCE 5          /* display units */
#define EYE_OFFSET 1             /* inches */

#define FLOAT float


FLOAT object[][4] =
  {  {  0.5,  0.5,  0.5,  1.0},
     { -0.5,  0.5,  0.5,  1.0},
     { -0.5,  0.5, -0.5,  1.0},
     {  0.5,  0.5, -0.5,  1.0},
     {  0.5, -0.5,  0.5,  1.0},
     { -0.5, -0.5,  0.5,  1.0},
     { -0.5, -0.5, -0.5,  1.0},
     {  0.5, -0.5, -0.5,  1.0}
  };

unsigned int numpoints = sizeof(object)/(sizeof(FLOAT)*4);

struct Line
  {
   unsigned int end1,end2;
  };

struct Line lines[] =
  {  {  0,  1},
     {  1,  2},
     {  2,  3},
     {  3,  0},
     {  4,  5},
     {  5,  6},
     {  6,  7},
     {  7,  4},
     {  0,  4},
     {  1,  5},
     {  2,  6},
     {  3,  7}
  };

unsigned int numlines = sizeof(lines)/sizeof(struct Line);

FLOAT display[ sizeof(object)/(sizeof(FLOAT)*4) ][4];

FLOAT objectMatrix[4][4];
FLOAT LeyeMatrix[4][4];
FLOAT ReyeMatrix[4][4];
FLOAT WorkMatrix1[4][4];
FLOAT WorkMatrix2[4][4];
FLOAT WorkMatrix3[4][4];

FLOAT XRot,YRot,ZRot;



void CopyMatrix(src, dst, rows)
   FLOAT (*src)[ ][4];
   FLOAT (*dst)[ ][4];
   unsigned int rows;
  {
   unsigned int i,j;

   for (i=0; i<rows; i++)
     {
      for (j=0; j<4; j++)
        {
         (*dst)[i][j] = (*src)[i][j];
        }
     }
  }



void ZeroMatrix(dst, rows)
   FLOAT (*dst)[ ][4];
   unsigned int rows;
  {
   unsigned int i,j;

   for (i=0; i<rows; i++)
     {
      for (j=0; j<4; j++)
        {
         (*dst)[i][j] = 0;
        }
     }
  }



void MultMatrix(src1, src2, rslt, rows)
   FLOAT (*src1)[ ][4];
   FLOAT (*src2)[4][4];
   FLOAT (*rslt)[ ][4];
   unsigned int rows;
  {
   unsigned int i,j,k;

   ZeroMatrix(rslt, rows);

   for (i=0; i<rows; i++)
     {
      for (j=0; j<4; j++)
        {
         for (k=0; k<4; k++)
           {
            (*rslt)[i][j] += (*src1)[i][k] * (*src2)[k][j];
           }
        }
     }
  }



void MultMatrixN(src1, src2, rslt, rows)
   FLOAT (*src1)[ ][4];
   FLOAT (*src2)[4][4];
   FLOAT (*rslt)[ ][4];
   unsigned int rows;
  {
   unsigned int i,j,k;

   ZeroMatrix(rslt, rows);

   for (i=0; i<rows; i++)
     {
      for (j=0; j<4; j++)
        {
         for (k=0; k<4; k++)
           {
            (*rslt)[i][j] += (*src1)[i][k] * (*src2)[k][j];
           }
        }
      for (j=0; j<4; j++)
        {
         (*rslt)[i][j] /= (*rslt)[i][3];
        }
     }
  }



void Init3D()
  {
   FLOAT eyeoffset;
   FLOAT eyedistance;
   FLOAT cosalpha;
   FLOAT sinalpha;

   XRot = 0;
   YRot = 0;
   ZRot = 0;

   eyeoffset = EYE_OFFSET * 2.5 / SCREEN_DIAGONAL;
   eyedistance = sqrt(VIEW_DISTANCE*VIEW_DISTANCE + eyeoffset*eyeoffset);

   cosalpha = VIEW_DISTANCE / eyedistance;
   sinalpha = sqrt(1.0 - cosalpha*cosalpha);

   ZeroMatrix(&WorkMatrix1, 4);
   ZeroMatrix(&WorkMatrix2, 4);

   WorkMatrix1[0][0] =  cosalpha;
   WorkMatrix1[0][2] = -sinalpha;
   WorkMatrix1[1][1] =  1.0;
   WorkMatrix1[2][0] =  sinalpha;
   WorkMatrix1[2][2] =  cosalpha;
   WorkMatrix1[3][3] =  1.0;

   WorkMatrix2[0][0] =  1.0;
   WorkMatrix2[1][1] =  1.0;
   WorkMatrix2[2][2] =  1.0;
   WorkMatrix2[2][3] =  1/eyedistance;
   WorkMatrix2[3][3] =  1.0;

   MultMatrix(&WorkMatrix1, &WorkMatrix2, &ReyeMatrix, 4);

   WorkMatrix1[0][0] =  cosalpha;
   WorkMatrix1[0][2] =  sinalpha;
   WorkMatrix1[1][1] =  1.0;
   WorkMatrix1[2][0] = -sinalpha;
   WorkMatrix1[2][2] =  cosalpha;
   WorkMatrix1[3][3] =  1.0;

   MultMatrix(&WorkMatrix1, &WorkMatrix2, &LeyeMatrix, 4);

   ZeroMatrix(&WorkMatrix1, 4);

   WorkMatrix1[0][0] =  SCREEN_WIDTH * 3/8;
   WorkMatrix1[1][1] =  SCREEN_HEIGHT/2;
   WorkMatrix1[2][2] =  1.0;
   WorkMatrix1[3][0] =  SCREEN_WIDTH/2;
   WorkMatrix1[3][1] =  SCREEN_HEIGHT/2;
   WorkMatrix1[3][3] =  1.0;

   MultMatrix(&LeyeMatrix, &WorkMatrix1, &WorkMatrix2, 4);
   CopyMatrix(&WorkMatrix2, &LeyeMatrix, 4);

   MultMatrix(&ReyeMatrix, &WorkMatrix1, &WorkMatrix2, 4);
   CopyMatrix(&WorkMatrix2, &ReyeMatrix, 4);
  }



void MakeRotMatrix()
  {
   FLOAT rad,cosalpha,sinalpha;

   ZeroMatrix(&WorkMatrix1, 4);
   ZeroMatrix(&WorkMatrix2, 4);

   rad = XRot*PI/180;
   cosalpha = cos(rad);
   sinalpha = sin(rad);

   WorkMatrix1[0][0] =  1;
   WorkMatrix1[1][1] =  cosalpha;
   WorkMatrix1[1][2] =  sinalpha;
   WorkMatrix1[2][1] = -sinalpha;
   WorkMatrix1[2][2] =  cosalpha;
   WorkMatrix1[3][3] =  1;

   rad = YRot*PI/180;
   cosalpha = cos(rad);
   sinalpha = sin(rad);

   WorkMatrix2[0][0] =  cosalpha;
   WorkMatrix2[0][2] = -sinalpha;
   WorkMatrix2[1][1] =  1;
   WorkMatrix2[2][0] =  sinalpha;
   WorkMatrix2[2][2] =  cosalpha;
   WorkMatrix2[3][3] =  1;

   MultMatrix(&WorkMatrix1, &WorkMatrix2, &WorkMatrix3, 4);

   ZeroMatrix(&WorkMatrix1, 4);

   rad = ZRot*PI/180;
   cosalpha = cos(rad);
   sinalpha = sin(rad);

   WorkMatrix1[0][0] =  cosalpha;
   WorkMatrix1[0][1] =  sinalpha;
   WorkMatrix1[1][0] = -sinalpha;
   WorkMatrix1[1][1] =  cosalpha;
   WorkMatrix1[2][2] =  1;
   WorkMatrix1[3][3] =  1;

   MultMatrix(&WorkMatrix3, &WorkMatrix1, &objectMatrix, 4);
  }



void Update3D(Lrp, Rrp)
   struct RastPort *Lrp;
   struct RastPort *Rrp;
  {


   XRot += XROTI;
   YRot += YROTI;
   ZRot += ZROTI;

   while (XRot < -180)
     {
      XRot += 360;
     }

   while (YRot < -180)
     {
      YRot += 360;
     }

   while (ZRot < -180)
     {
      ZRot += 360;
     }

   while (XRot > 180)
     {
      XRot -= 360;
     }

   while (YRot > 180)
     {
      YRot -= 360;
     }

   while (ZRot > 180)
     {
      ZRot -= 360;
     }

   MakeRotMatrix();

/*
 * LEFT
 */

   MultMatrix(&objectMatrix, &LeyeMatrix, &WorkMatrix1, 4);

   MultMatrixN(&object, &WorkMatrix1, &display, numpoints);

   DisplayObject(Lrp);

/*
 * RIGHT
 */

   MultMatrix(&objectMatrix, &ReyeMatrix, &WorkMatrix1, 4);

   MultMatrixN(&object, &WorkMatrix1, &display, numpoints);

   DisplayObject(Rrp);

  }



unsigned int checkbounds(x, lo, hi)
   FLOAT x,lo,hi;
  {
   unsigned int status;

   status = 0;

   if (x < lo)
     {
      status |= 1;
     }

   if (x > hi)
     {
      status |= 2;
     }

   return status;
  }



int clip(x1,y1,z1,x2,y2,z2,LM,RM,TM,BM,HM,YM)
   FLOAT *x1,*y1,*z1,*x2,*y2,*z2;
   FLOAT LM,RM,TM,BM,HM,YM;
  {
   unsigned int end1,end2;

   for (;;)
     {
      end1 = 0;
      end2 = 0;

      end1 |= checkbounds(*x1, LM, RM);
      end1 |= checkbounds(*y1, TM, BM) << 2;
      end1 |= checkbounds(*z1, HM, YM) << 4;

      end2 |= checkbounds(*x2, LM, RM);
      end2 |= checkbounds(*y2, TM, BM) << 2;
      end2 |= checkbounds(*z2, HM, YM) << 4;

      if ((end1 & end2) != 0)
        {
         return 0;
        }

      if ((end1 | end2) == 0)
        {
         return 1;
        }





      return 0;




     }
  }



int DisplayObject(rp)
   struct RastPort *rp;
  {
   unsigned int i;
   FLOAT x1,y1,z1;
   FLOAT x2,y2,z2;

   for (i=0; i<numlines; i++)
     {
      x1 = display[lines[i].end1][0];
      y1 = display[lines[i].end1][1];
      z1 = display[lines[i].end1][2];
      x2 = display[lines[i].end2][0];
      y2 = display[lines[i].end2][1];
      z2 = display[lines[i].end2][2];

      if (clip(&x1, &y1, &z1, &x2, &y2, &z2,
            (FLOAT)(0), (FLOAT)(SCREEN_WIDTH-1),
            (FLOAT)(0), (FLOAT)(SCREEN_HEIGHT-1),
            (FLOAT)(-3),(FLOAT)(100)))
        {
         Move(rp,(int)x1,(int)y1);
         Draw(rp,(int)x2,(int)y2);
        }
     }
  }

