/***********************************************************************/
/*                                                                     */
/*  Programm: 3-D Vector © 1991 by AMIGO!                              */
/*  Author  : Armin Harich                                             */
/*                                                                     */
/*  S.A.S_C 5.xx :LC -Lfdc -ff -v -w 3D                                */
/*  Aztec_C 5.0  :CC -ff -so -ps 3d                                    */
/*                LN 3d -lmf16 -lc16                                   */
/*  Aztec_C 3.6  :CC 3d                                                */
/*                LN 3d -lm -lc                                        */
/*  DICE         :DCC ? -ffp 3d.c -o3d ? (!Nur Registrierte Version!)  */
/*                                                                     */
/***********************************************************************/

#include <math.h>
#include <stdio.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#ifdef LATTICE_50
  #include <proto/dos.h>
  #include <proto/exec.h>
  #include <proto/graphics.h>
  #include <proto/intuition.h>
  #include <mffp.h>
#else
  #include <functions.h>
#endif

#define Z_MAX 5000
#define X_MAX (Z_MAX/2+500)
#define X_MIN -X_MAX
#define Z_MIN (X_MAX*800L/32000)

#define N     20         /* MAX_PKT/MAX_VER Bitte an N anpassen */
#define MAX_PKT 1500     /* MAX_PKT  Bitte  groß  genug  wählen */
#define MAX_VER 3000     /* MAX_VER ebenfalls groß genug wählen */

#define NICHT_PROJEZIERT -32000
#define MOVE 0
#define DRAW !MOVE
#define ANDERE_BMNR ((bmnr+1)&1)
#define RND_plus_minus(x) (rand()%(2*((x)+1))-(x))
#define RND(x)        (rand()%(x))
#define SWAP(x,y) {register short zw=x;x=y;y=zw;}

#define PKT   punkt[pkt_nr]
#define VER   verbindung[ver_nr]

typedef unsigned long  ulong ;
typedef unsigned short ushort;
typedef unsigned int   uint  ;

struct BitMap *bm[2],bm1;
ushort bmnr;
struct GfxBase       *GfxBase;
struct IntuitionBase *IntuitionBase;
struct RastPort  *rp;
struct ViewPort  *vp;
struct Screen *scr;
struct Window *win;
struct NewScreen scrdef={0,0,0,0,1,1,0, HIRES|SCREENQUIET,CUSTOMSCREEN};
struct NewWindow windef={0,0,0,0,0,0,CLOSEWINDOW|RAWKEY|MOUSEBUTTONS|ACTIVEWINDOW|INACTIVEWINDOW,WINDOWCLOSE|NOCAREREFRESH|ACTIVATE|BORDERLESS|RMBTRAP,NULL,NULL,NULL,NULL,NULL,0,0,0,0,CUSTOMSCREEN};

struct Punkt {short  x,y,z;
              short  x_bild,y_bild;}
       punkt[MAX_PKT];

struct Verbindung{ushort Punkt_Nr;
                   short Modus ;}
       verbindung[MAX_VER];

ushort anzpkt,pkt_nr;
ushort anzver,ver_nr;
 short x_pos=256*N,z_pos;
 short xsizeh,ysizeh;
 long z_bild10;
 long sin_dy,cos_dy;
 float dy;

void close_all(err_txt)

char *err_txt;

{if (err_txt)   puts(err_txt);
 if (bm1.Planes[0]) FreeRaster(bm1.Planes[0],(long)scrdef.Width,(long)scrdef.Height);
 if (win)      {rp->BitMap=bm[0];CloseWindow (win);}
 if (scr)     CloseScreen (scr);
 CloseLibrary ((struct Library *)GfxBase);
 CloseLibrary ((struct Library *)IntuitionBase);
 exit(0);
 }

void open_all()

{IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);
 GfxBase    =(struct GfxBase *)    OpenLibrary( "graphics.library",0L);
 windef.Width = scrdef.Width = GfxBase->NormalDisplayColumns;
 windef.Height= scrdef.Height= GfxBase->NormalDisplayRows;
 xsizeh  =      windef.Width >>1;
 ysizeh  =      windef.Height>>1;
 z_bild10=(long)windef.Width <<10;
 if (!(windef.Screen = scr = OpenScreen (&scrdef))) close_all("Kein Screen");
 if (!(          win = OpenWindow (&windef))) close_all("Kein Window");
 rp=  win->RPort    ;
 vp=&(scr->ViewPort);
 ShowTitle(scr,FALSE);
 bm[0]=rp->BitMap;
 bm[1]=&bm1;
 InitBitMap(&bm1,1L,(long)scrdef.Width,(long)scrdef.Height);
 if (!(bm1.Planes[0] = AllocRaster((long)scrdef.Width,(long)scrdef.Height))) close_all("Kein Chip-MEM");
 SetRGB4(vp,0L, 0L, 0L, 0L);
 SetRGB4(vp,1L,15L,15L,15L);
 }

ushort WelcherPunkt(x,y,z)

short x,y,z;

{register short nr;

 nr=pkt_nr;
 while(--nr >= 0 && !(punkt[nr].x==x && punkt[nr].y==y && punkt[nr].z==z));
 if (nr<0) {PKT.x=x;PKT.y=y;PKT.z=z;return(pkt_nr++);}
 else                               return((ushort)nr);
 }

void Move3D(x,y,z)

short x,y,z;

{VER.Modus   =MOVE;
 VER.Punkt_Nr=WelcherPunkt(x,y,z);
 ver_nr++;
 }

void Draw3D(x,y,z)

short x,y,z;

{VER.Modus   =DRAW;
 VER.Punkt_Nr=WelcherPunkt(x,y,z);
 ver_nr++;
 }

void baum(n,x,y,z)

ushort n;
short x,y,z;

{if (n>0)
  {Move3D(x,y,z);
   Draw3D(x=x+RND_plus_minus(32+(n<<5)),
          y=y-RND_plus_minus(16)-(10<<n),
          z=z+RND_plus_minus(32+(n<<5)));
   baum(n-1,x,y,z);
   baum(n-1,x,y,z);}
 }

void generiere_welt()

{register short x,z;

 for (z=0;z<N;z++)         /* generiere Schachbrett mit Schluchten */
  {for (x=0;x<N;x++)
    {if (x==0) Move3D(0,150,512*z);
     else      Draw3D(512 * x,
                      (x % 8 < 3 || z % 6 < 2) ? 150 : -100,
                      512 * z);
     }
   for (x=0;x<N;x++)
    {VER.Modus = x==0 ? MOVE : DRAW;
     VER.Punkt_Nr= x*N+z;
     ver_nr++;
     }
   }
 anzpkt=pkt_nr;            /* Nun noch ein paar Bäume */
 for (x=0;x<anzpkt;x++) {if (RND(20)==0 && punkt[x].y>0) baum(4,punkt[x].x,punkt[x].y,punkt[x].z);}
 anzpkt=pkt_nr;
 anzver=ver_nr;
 }

void zeichne_bild()

{register short new=TRUE;

 for (ver_nr=0;ver_nr<anzver;ver_nr++)
  {pkt_nr=VER.Punkt_Nr;
   if (PKT.x_bild!=NICHT_PROJEZIERT)
    {if (VER.Modus==MOVE || new)  Move(rp,(long)PKT.x_bild,(long)PKT.y_bild);
     else                         Draw(rp,(long)PKT.x_bild,(long)PKT.y_bild);
     new=FALSE;
     }
   else new=TRUE;
   }
 }

void naechstes_bild()

{bmnr=ANDERE_BMNR;    /* Benutzte unsichtbare BitMap und löschen sie*/
 rp->BitMap=bm[bmnr];
 BltClear(bm[bmnr]->Planes[0],((ulong)bm1.Rows<<16)+bm1.BytesPerRow,2L);
 zeichne_bild();      /* Zeichne die Projektion */
 vp->RasInfo->BitMap=bm[bmnr];
 ScrollVPort(vp);     /* und   zeige   sie   an */
 }

void verschiebe_drehe_und_projeziere()

{short x_min,x_max,z_min,z_max;

 x_min=( X_MAX*cos_dy-Z_MAX*sin_dy) >> 14; /* Hier  werden die Eckpunkte des */
 x_max=( X_MIN*cos_dy-Z_MAX*sin_dy) >> 14; /* des 1. Viereckes zur  Geschwin- */
 if (cos_dy>0) SWAP(x_min,x_max)           /* dikeitsoptimierung    bestimmt */

 z_min=( Z_MAX*cos_dy+X_MAX*sin_dy) >> 14;
 z_max=( Z_MAX*cos_dy+X_MIN*sin_dy) >> 14;
 if (sin_dy>0) SWAP(z_min,z_max)

 if (x_min>-500) x_min=-500;
 if (x_max< 500) x_max= 500;
 if (z_min>-500) z_min=-500;
 if (z_max< 500) z_max= 500;

 x_min+=x_pos;
 x_max+=x_pos;
 z_min+=z_pos;
 z_max+=z_pos;

 for (pkt_nr=0;pkt_nr<anzpkt;pkt_nr++)            /* Für alle Punkte : */
  {register short x,z,xx;
   if (PKT.x>x_min && PKT.x<x_max && PKT.z>z_min && PKT.z<z_max)/* 1.Viereck */
    {xx=PKT.x-x_pos;                              /* verschiebe */
     z =PKT.z-z_pos;
     x = ( xx * cos_dy + z * sin_dy) >> 14;       /* drehe */
     z = (-xx * sin_dy + z * cos_dy) >> 14;
     if (z>Z_MIN && z<Z_MAX && x>X_MIN && x<X_MAX)/* 2.Viereck */
      {register long f=(z_bild10)/z;
       PKT.x_bild=(short)(    x*f >> 10)+xsizeh;  /* projeziere */
       PKT.y_bild=(short)(PKT.y*f >> 11)+ysizeh;}
     else
       PKT.x_bild = NICHT_PROJEZIERT;
     }
   else
     PKT.x_bild = NICHT_PROJEZIERT;
   }

 }

void main()

{short fx=0,vx=0,vz;
 struct IntuiMessage *mess;

 open_all();
 generiere_welt();
 for (;;)
  {if (mess=(struct IntuiMessage *)GetMsg(win->UserPort))
    {switch (mess->Class)
      {case CLOSEWINDOW   :   /* Eingaben auswerten */
             case RAWKEY        :switch (mess->Code)
                  {case 0x00:
                   case 0x10:
                   case 0x45:close_all(NULL);
                   case 0x44:
                   case 0x40:x_pos=256*N;z_pos=vx=fx=0;dy=0;break;}
                 break;
       case INACTIVEWINDOW:WaitPort(win->UserPort);break;
       case MOUSEBUTTONS  :switch (mess->Code)
                  {case MENUDOWN  :fx+=3;break;
                   case SELECTDOWN:fx-=3;break;
                   default        :fx=0;break;}
       }
     ReplyMsg((struct Message *)mess);
     }
   vx+=fx;                                /* Geschwindikeit und         */
   vz =-win->MouseY+ysizeh+(ysizeh>>1);   /* Richtung    setzen         */
   dy-=(win->MouseX-xsizeh)/3000.;
   sin_dy=(1<<14)*sin(dy);                /* Geschwindikeitsoptimierung */
   cos_dy=(1<<14)*cos(dy);                /* Da   dy   constant  bleibt */
   x_pos+=(  vx*cos_dy - vz*sin_dy) >> 14;/* Eigene Position ausrechnen */
   z_pos+=(+ vx*sin_dy + vz*cos_dy) >> 14;
   verschiebe_drehe_und_projeziere();
   naechstes_bild();
   }
 }
