/**************************************************************************/
/* 3-d.c                                                         13.12.87 */
/* Version 1.0                                                            */
/*                                                                        */
/*                                                                        */
/* Beispielprogramm zur Demonstration dreidimensionaler Grafiken          */
/*                                                                        */
/* Das Programm wird unter Angabe einer Arbeitsdatei ('datei') gestartet. */
/* Die  Definition  der  Koerper   erfolgt  in  einem File mit  dem Namen */
/* 'datei.dat', das  nach dem Starten des Programms eingelesen wird. Sol- */
/* te dieses File nicht existieren, so bricht das Programm ab.            */
/*                                                                        */
/* Das Erstellen des 'datei.dat' Files erfolgt folgendermassen:           */
/* ('datei.dat' ist eine ASCII-Datei und kann mit dem Befehl 'ed'erstellt */
/* werden.)                                                               */
/* Beginnt  eine  Zeile mit  einem Leerzeichen oder ist sie leer, so wird */
/* diese igmoriert, also                                                  */
/* ' ' ... bedeutet Rem-Zeile                                             */
/* 'P' x,y,z  definiert einen Punkt nach x-, y- und z-Koordinate.  (x,y,z */
/*            float-Werte zwischen 0 und 1.) Die  Punkte werden nach  der */
/*            Reihenfolge  ihrer Definition numeriert.  Der  Punkt 0 ist  */
/*            also der erste Punkt, der in '.dat' File definiert wurde.   */
/* 'F' P1,P2,P3...Pn definiert eine Flaeche, die aus den Punkten  P1, P2, */
/*            ...Pn begrenzt wird. Auch die Flaechen werden nach der      */
/*            Reihenfolge ihrer Definition numeriert.                     */
/* 'B' wert   'wert' gibt den Abstand zwischen Betrachter und  Bildschirm */
/*            an  und ist wichtig fuer die Perspektiveberechnung.  'wert' */
/*            ist ein float-Wert. 'wert=1' bedeutet maximale Verzerrung.  */
/*                                                                        */
/* Falls bei der Definition ein Fehler auftritt, wird dies gemeldet.  Die */
/* Ueberpruefgung ist aber nicht 100%!!                                   */
/*                                                                        */
/* Das Programm wird ueber die Tastatur bedient.                          */
/* Taste:                                                                 */
/* 'F1'   Verschiebung des Objekts in positive x-Richtung                 */
/* 'F2'   Verschiebung des Objekts in negative x-Richtung                 */
/* 'F3'   Verschiebung des Objekts in positive y-Richtung                 */
/* 'F4'   Verschiebung des Objekts in negative y-Richtung                 */
/* 'F5'   Verschiebung des Objekts in positive z-Richtung                 */
/* 'F6'   Verschiebung des Objekts in negative z-Richtung                 */
/* 'F9'   Verkleinern                                                     */
/* 'F10'  Vergroessern                                                    */
/* '1'    absolute Drehung um die x-Achse in pos. Richtung                */
/* '2'    absolute Drehung um die x-Achse in neg. Richtung                */
/* '3'    absolute Drehung um die y-Achse in pos. Richtung                */
/* '4'    absolute Drehung um die y-Achse in neg. Richtung                */
/* '5'    absolute Drehung um die z-Achse in pos. Richtung                */
/* '6'    absolute Drehung um die z-Achse in neg. Richtung                */
/* '7'    relative Drehung um die x-Achse in pos. Richtung                */
/* '8'    relative Drehung um die x-Achse in neg. Richtung                */
/* '9'    relative Drehung um die y-Achse in pos. Richtung                */
/* '0'    relative Drehung um die y-Achse in neg. Richtung                */
/* '-'    relative Drehung um die z-Achse in pos. Richtung                */
/* '='    relative Drehung um die z-Achse in neg. Richtung                */
/*        'relative'bedeutet Drehung vor der Translation, also Drehung im */
/*        Objektsystem. 'absolut' bedeutet dagegen Drehung nach der  Ver- */
/*        schiebung.                                                      */
/* 'R'    macht alle bisherigen  Drehungen, Verschiebungen  und Vergroes- */
/*        serungen ungeschehen.                                           */
/* 'E'    beendet das Programm                                            */
/* 'A'    animiert alle bisher eingegebenen Aktionen.                     */
/* 'S'    Speichern der Animation in einem 'datei.ani' File               */
/* 'L'    Laden einere Animation ('datei.ani')                            */
/*                                                                        */
/*                                                                        */
/* fuer Markt+Technik geschrieben von Heiko Knappe                        */
/*                                                                        */
/* 3D-Grafik                                                              */
/**************************************************************************/
/* Im Datenfile koennen Flaechen mit  beliebig vielen  Eckpunkten,  also */
/* Polygone definiert werden.                                            */


#include<lattice/stdio.h>
#include<lattice/math.h>
#include<exec/types.h>
#include<libraries/dos.h>
#include<intuition/intuition.h>

#define PI 3.14
#define ANZ 200
#define PANZ 500
#define FANZ 200
#define N 10000
#define WIDTH 320  /* Bildgroesse */
#define HEIGHT 200
#define DEPTH 5
#define WSTEP .1   /* Winkelzunahme */
#define STEP .05   /* Zunahme bei der Translation */
#define MUL .05    /* Vergroesserungsfaktor */

/* wichtige Struktur-Definitionen */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct IntuiMessage *MyMessage;
struct Screen *MyScreen;
struct Window *MyWindow;
struct RastPort *rp;
struct NewWindow MyNewWindow=
 {0,0,WIDTH,HEIGHT,0,1,RAWKEY,ACTIVATE|NOCAREREFRESH|BORDERLESS,0,0,0,
  0,0,0,0,0,0,CUSTOMSCREEN};
struct NewScreen MyNewScreen=
 {0,0,WIDTH,HEIGHT,DEPTH,0,1,0,CUSTOMSCREEN,0,0,0,0};

/* Variablen-Definitionen                       */
/* Definitionen des realen Koerpers             */
/* Punkt-Koordinaten (x,y,z) im Realsystem      */
float px[ANZ],py[ANZ],pz[ANZ];
/* nach der Transformation                      */
float pxh[ANZ],pyh[ANZ],pzh[ANZ];
/* Punktetabelle                                */
int p[PANZ];
/* Flaechentabelle, Hilfstabelle um Sortieren   */
short f[FANZ],fh[FANZ];
/* Definition der Bildschirmkoordinaten-Tabelle */
short bx[N],by[N];
/* Zaehler */
int n,np,nf,i,j,k;
short anzcol;  /* Anzahl der Farben des Screens */
/* Perspektive Parameter                        */
float b=1;
/* Definition der Winkel und Transformationsparameter */
float winkelax=0,winkelay=0,winkelaz=0;
float winkelrx=0,winkelry=0,winkelrz=0;
float winkelp=30;
float transx=0,transy=0,transz=0;
/* Zaehler fuer die Bildschirmkoordinaten */
int count,countlast=0;
/* Vergroesserungfaktor */
float m=1;
/* Name des Bildes */
char name[128],name_[128];


/*********************/
/* Hauptprogramm     */
/*********************/

main(argc,argv)
int argc;
char *argv[];
{
 if(argc!=2)
 {
    printf("Eingabe: %s file.dat\n",argv[0]);
    close_();
 }
 strcpy(name,argv[1]);
 einlesen();
  open_();
 new();
 for(;;)
 {
    while((MyMessage=GetMsg(MyWindow->UserPort))==0);
    ReplyMsg(MyMessage);
    if((MyMessage->Class)==RAWKEY) do_key();
 }
}


/*********************/
/* Tastatureingabe   */
/* testen            */
/*********************/

do_key()
{
 short key;

 key=MyMessage->Code;
 switch(key)
 {
    case 1 : /* '1' Drehung um x-Achse in pos. Richtung */
             /* im Koordinatensystem */
             winkelax+=WSTEP;
             new();
             break;
    case 2 : /* '2' Drehung um x-Achse in neg. Richtung */
             /* im Koordinatensystem */
             winkelax-=WSTEP;
             new();
             break;
    case 3 : /* '3' Drehung um y-Achse in pos. Richtung */
             /* im Koordinatensystem */
             winkelay+=WSTEP;
             new();
             break;
    case 4 : /* '4' Drehung um y-Achse in neg. Richtung */
             /* im Koordinatensystem */
             winkelay-=WSTEP;
             new();
             break;
    case 5 : /* '5' Drehung um z-Achse in pos. Richtung */
             /* im Koordinatensystem */
             winkelaz+=WSTEP;
             new();
             break;
    case 6 : /* '6' Drehung um z-Achse in neg. Richtung */
             /* im Koordinatensystem */
             winkelaz-=WSTEP;
             new();
             break;
    case 7 : /* '7' Drehung um x-Achse in pos. Richtung */
             /* im Objektsystem */
             winkelrx+=WSTEP;
             new();
             break;
    case 8 : /* '8' Drehung um x-Achse in neg. Richtung */
             /* im Objektsystem */
             winkelrx-=WSTEP;
             new();
             break;
    case 9 : /* '9' Drehung um y-Achse in pos. Richtung */
             /* im Objektsystem */
             winkelry+=WSTEP;
             new();
             break;
    case 10: /* '0' Drehung um y-Achse in neg. Richtung */
             /* im Objektsystem */
             winkelry-=WSTEP;
             new();
             break;
    case 11: /* '?' ('-')  Drehung um z-Achse in pos. Richtung */
             /* im Objektsystem */
             winkelrz+=WSTEP;
             new();
             break;
    case 12: /* '´' Drehung um z-Achse in neg. Richtung */
             /* im Objektsystem */
             winkelrz-=WSTEP;
             new();
             break;
    case 85: /* 'F6' Bewegung in pos. z-Richtung */
             transz+=STEP;
             new();
             break;
    case 84: /* 'F5' Bewegung in neg. z-Richtung */
             transz-=STEP;
             new();
             break;
    case 83: /* 'F4' Bewegung in pos. y-Richtung */
             transy+=STEP;
             new();
             break;
    case 82: /* 'F3' Bewegung in neg. y-Richtung */
             transy-=STEP;
             new();
             break;
    case 81: /* 'F2' Bewegung in pos. x-Richtung */
             transx+=STEP;
             new();
             break;
    case 80: /* 'F1' Bewegung in neg. x-Richtung */
             transx-=STEP;
             new();
             break;
    case 88: /* 'F9' Vergroessern des Objekts */
             m+=MUL;
             new();
             break;
    case 89: /* 'F10' Verkleinern des Objekts */
             if(m>0) m-=MUL;
             new();
             break;
    case 18: /* 'E' Ende */
             close_();
    case 19: /* 'R' Reset */
             winkelrx=winkelry=winkelrz=0;
             winkelax=winkelay=winkelaz=0;
             transx=transy=transz=0;
             count=countlast=0;
             m=1;
             new();
             break;
    case 32: /* 'A' Animation */
             ausgabe(0);
             break;
    case 33: /* 'S' Speichern der Animation */
             save_();
             break;
    case 40: /* 'L' Laden der Animation */
             load_();
             break;
    default: /* keine belegte Taste gedrueckt */
             break;
 }
}


/*********************/
/* neues Bild        */
/* zeichnen          */
/*********************/

new()
{
 copy();
 rotx(winkelrx);
 roty(winkelry);
 rotz(winkelrz);
 trans(transx,transy,transz);
 rotx(winkelax);
 roty(winkelay);
 rotz(winkelaz);
 per(b);
 transform(winkelp);
 SetRast(rp,0);
 ausgabe(countlast);
}


/*********************/
/* Initialisierung   */
/*********************/

open_()
{
 if((IntuitionBase=OpenLibrary("intuition.library",0))==0)
    close_();
 if((GfxBase=OpenLibrary("graphics.library",0))==0)
    close_();
 if((MyScreen=OpenScreen(&MyNewScreen))==0)
    close_();
 MyNewWindow.Screen=MyScreen;
 if((MyWindow=OpenWindow(&MyNewWindow))==0)
    close_();
 rp=MyWindow->RPort;
 set_color();
}

close_()
{
 if(IntuitionBase!=0) CloseLibrary(IntuitionBase);
 if(GfxBase!=0) CloseLibrary(GfxBase);
 if(MyWindow!=0) CloseWindow(MyWindow);
 if(MyScreen!=0) CloseScreen(MyScreen);
 exit(0);
}

set_color()
{
 for(anzcol=1,i=0;i<DEPTH;++i,anzcol*=2);
 for(i=0;i<anzcol;++i)
    SetRGB4(&(MyScreen->ViewPort),i,0,0,(short)(15*(float)i/(float)anzcol));
}


/*********************/
/* Laden, Speichern  */
/*********************/

save_()
{
 int file;
 
 /* an den Bildnamen wird '.ani' angehaengt */
 strcpy(name_,name);
 strcpy(name_+strlen(name_),".ani");
 file=Open(name_,MODE_NEWFILE);
 if(file==0)
 {
    printf("Animations-File '%s' kann nicht geoeffnet werden!\n",name_);
    close_();
 }
 Write(file,&count,4); Write(file,&countlast,4);
 Write(file,&b,4); Write(file,&m,4);
 Write(file,&n,4); Write(file,&np,4); Write(file,&nf,4);
 Write(file,&transx,4); Write(file,&transy,4); Write(file,&transz,4);
 Write(file,&winkelax,4); Write(file,&winkelay,4); Write(file,&winkelaz,4);
 Write(file,&winkelrx,4); Write(file,&winkelry,4); Write(file,&winkelrz,4);
 Write(file,&bx[0],2*count);
 Write(file,&by[0],2*count);
 Close(file);
}

load_()
{
 int file;

 /* an den Bildnamen wird '.ani' angehaengt */
 strcpy(name_,name);
 strcpy(name_+strlen(name_),".ani");
 file=Open(name_,MODE_OLDFILE);
 if(file==0)
 {
    printf("Animations-File '%s' kann nicht geoeffnet werden!\n",name_);
    close_();
 }
 Read(file,&count,4); Read(file,&countlast,4);
 Read(file,&b,4); Read(file,&m,4);
 Read(file,&n,4); Read(file,&np,4); Read(file,&nf,4);
 Read(file,&transx,4); Read(file,&transy,4); Read(file,&transz,4);
 Read(file,&winkelax,4); Read(file,&winkelay,4); Read(file,&winkelaz,4);
 Read(file,&winkelrx,4); Read(file,&winkelry,4); Read(file,&winkelrz,4);
 Read(file,&bx[0],2*count);
 Read(file,&by[0],2*count);
 Close(file);
 new();
}

/* Einlesen der Datei '.dat' */
einlesen()
{
 int file;
 short zeile=0;
 char line[80],*ii;

 /* an den Bildnamen wird '.dat' angehaengt */
 strcpy(name_,name);
 strcpy(name_+strlen(name_),".dat");
 file=Open(name_,MODE_OLDFILE);
 if(file==0)
 {
    printf("Daten-File '%s' kann nicht geoeffnet werden!\n",name_);
    close_();
 }
 while(inline(line,file,80)!=0)
 {
    ++zeile;
    switch(line[0])
    {
       case ' ': /* Rem-Zeile */
                 break;
       case 10 : /* Return-Zeile */
                 break;
       case 'P': /* Punktdefinition */
                 if(sscanf(line+2,"%f,%f,%f",&px[n],&py[n],&pz[n])!=3)
                    err(zeile,line);
                 else
                    ++n;
                 break;
       case 'F': /* Flaechendefinition */
                 i=1; j=0;
                 while(sscanf(line+i,"%d",&p[np+j])==1)
                 {
                    ++j;
                    ii=strchr(line+i,',');
                    if(i!=0) i=ii-line+1;
                    else break;
                 }
                 if(j<3) /* falsche Eingabe */
                    err(zeile,line);
                 else
                 {
                    /* Mit Punkt '0' beginnen */
                    for(i=0;i<j;--p[np+i],++i);
                    f[nf]=np;
                    ++nf;
                    np+=j;
                    f[nf]=np;
                 }
                 break;
       case 'B': /* Abstand Screen - Betrachter */
                 if(sscanf(line+2,"%f",&b)!=1)
                 {
                    err(zeile,line);
                    b=1;
                 }
                 break;
       default : /* Fehler aufgetreten */
                 err(zeile,line);
                 break;
    }
 }
 /* falls nicht sortiert wird! */
 for(i=0;i<(nf+1);fh[i]=i,++i);
 Close(file);
}

err(z,l) /* Fehler? */
int  z;
char *l;
{
 printf("Fehler in Zeile %d:\n  -> %s",z,l);
}

inline(c,handle,len) /* eine Zeile des Files einlesen */
char c[];
long *handle;
short len;
{
 short i=0,ende=0;

 while((i<len)&&(c[i-1]!=10)&&((ende=Read(handle,&c[i],1))!=0))
    ++i;
 c[i]=0;
 return(ende);
}


/*********************/
/* Ausgaberoutine    */
/*********************/

ausgabe(counter)
int counter;
{
 short xh,yh;
 char col;

 /* bx[]=-3 bedeutet Endkennung des Feldes fuer die Ausgabe */
 for(;(counter<N)&&(bx[counter]!=-3);++counter)
 {
    /* faengt ein neues Bild an? (bx[]=-2) */
    if(bx[counter]==-2)
    {
       ++counter;
       SetRast(rp,0);
    }
    /* faengt eine neue Flaeche an? (bx[]=-1) */
    if(bx[counter]==-1)
    {
       ++counter;
       if(bx[counter-2]!=-2) /* falls nicht erste Flaeche */
          Draw(rp,xh,yh);  /* alte Flaeche schliessen   */
       col=bx[counter++];
       SetAPen(rp,col);
       xh=bx[counter];
       yh=by[counter];
       Move(rp,xh,yh);
    }
    else
       Draw(rp,bx[counter],by[counter]);
 }
 Draw(rp,xh,yh);  /* letzte Flaeche schliessen   */
}



/*********************/
/* Transformations-  */
/* routinen          */
/*********************/

/* alle Transformationen finden in Hilfskoordinaten statt! */
rotx(wx)     /* Drehung um die x-Achse */
float wx;
{
 float sinwx,coswx,yh,zh;
 
 sinwx=sin(wx);
 coswx=cos(wx);
 for(i=0;i<n;++i)      /* alle Punkte */
 {
    yh=pyh[i]*coswx-pzh[i]*sinwx;
    zh=pyh[i]*sinwx+pzh[i]*coswx;
    pyh[i]=yh;
    pzh[i]=zh;
 }     
}

roty(wy)     /* Drehung um die y-Achse */
float wy;
{
 float sinwy,coswy,xh,zh;
 
 sinwy=sin(wy);
 coswy=cos(wy);
 for(i=0;i<n;++i)      /* alle Punkte */
 {
    xh=pxh[i]*coswy+pzh[i]*sinwy;
    zh=-pxh[i]*sinwy+pzh[i]*coswy;
    pxh[i]=xh;
    pzh[i]=zh;
 }     
}

rotz(wz)     /* Drehung um die z-Achse */
float wz;
{
 float sinwz,coswz,xh,yh;
 
 sinwz=sin(wz);
 coswz=cos(wz);
 for(i=0;i<n;++i)      /* alle Punkte */
 {
    xh=pxh[i]*coswz-pyh[i]*sinwz;
    yh=pxh[i]*sinwz+pyh[i]*coswz;
    pxh[i]=xh;
    pyh[i]=yh;
 }     
}

trans(x,y,z) /* Translation um (x,y,z) */
float x,y,z;
{
 for(i=0;i<n;++i)      /* alle Punkte */
 {
    pxh[i]=pxh[i]+x;
    pyh[i]=pyh[i]+y;
    pzh[i]=pzh[i]+z;
 }     
}

per(d)     /* Perspektive berechnen  */
float d;
{
 float fak;

 /* Perspektive wird nur fuer b<1 beruecksichtigt, sonst Projektion */
 /* mit Hilfe der z-Koordinate */
 if(d>=1) return(0);
 for(i=0;i<n;++i)      /* alle Punkte */
 {
    if(pzh[i]>d/2) fak=2;
    else fak=d/(d-pzh[i]);
    pxh[i]=fak*pxh[i];
    pyh[i]=fak*pyh[i];
    pzh[i]=0;
 }
}

copy()     /* Original-Daten kopieren */
{
 for(i=0;i<n;++i)
 {
    pxh[i]=px[i]*m;
    pyh[i]=py[i]*m;
    pzh[i]=pz[i]*m;
 }
}

transform(wp) /* Bildpunkte berechnen */
float wp;     /* Projektionswinkel    */
{
 float coswp,sinwp;

 if(count>=(N-(f[nf]+2*nf+1)))
    return(0);
 countlast=count;
 coswp=cos(wp);
 sinwp=sin(wp);
 for(i=0;i<n;++i)
 {
   pxh[i]=pxh[i]-pzh[i]*sinwp;
   pyh[i]=pyh[i]-pzh[i]*coswp;
 }
 bx[count++]=-2;    /* neues Bild   */
 for(i=0;i<nf;++i)
 {
    bx[count++]=-1; /* neue Flaeche */
    bx[count++]=anzcol-1; /* Farbe        */
    for(j=f[fh[i]];j<f[fh[i]+1];++j)
    {
        bx[count]=(short)(HEIGHT/2*(1+pxh[p[j]]));
        by[count]=(short)(HEIGHT/2*(1-pyh[p[j]]));
        /* 'Clipping' */
        if(bx[count]<0) bx[count]=0;
        if(bx[count]>WIDTH) bx[count]=WIDTH-1;
        if(by[count]<0) by[count]=0;
        if(by[count]>HEIGHT) by[count]=HEIGHT-1;
        ++count;
    }
 }
 bx[count]=-3;      /* Endkennung   */
}
