  /**************************************************/
  /*                                                */
  /*  Programm: RayTracer © 1992 by AMIGO!          */
  /*  Autor   : Armin Harich                        */
  /*                                                */
  /*  S.A.S_C 5.xx :LC -O -Lfdc -ff -v -w RayTracer */
  /*  Aztec_C 5.0  :CC -ff -so -ps RayTracer        */
  /*                LN RayTracer -lmf16 -lc16       */
  /*                                                */
  /**************************************************/

#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 <math.h>
  #include <functions.h>
#endif

#define MAX(x,y) ((x)>(y)?(x):(y))
#define ABS(x) ((x)<0?-(x):(x))
#define SCRMOD HAM

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,6,-1,0,SCRMOD,CUSTOMSCREEN};
struct NewWindow windef={0,1,0,0,-1,0,ACTIVEWINDOW,NOCAREREFRESH,NULL,NULL,NULL,NULL,NULL,0,0,0,0,CUSTOMSCREEN};

void close_all(char *err_txt)

{if (err_txt)			  puts(err_txt);
 if (win)           CloseWindow (win);
 if (scr)           CloseScreen (scr);
                    CloseLibrary ((struct Library *)GfxBase);
                    CloseLibrary ((struct Library *)IntuitionBase);
 exit(0);
 }

struct Punkt       {float x,y,z;};
struct Farbe       {float Rot,Gruen,Blau;}  Hintergrundhelligkeit = {0,0,.194},Schwarz={0,0,0},ScreenFarbe[16];

short rgb[16]={0x000,0xFFF,0x588,0xAAA,0x88B,0x558,0x669,0x77A,0xDDF,0x225,0xF8B,0x114,0x447,0xC47,0x3D6,0x003};

void open_all(void)

{short i;

 IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);
 GfxBase      =(struct GfxBase *)      OpenLibrary( "graphics.library",0L);

 windef.Width = scrdef.Width = GfxBase->NormalDisplayColumns >>                   1        ;
 windef.Height=(scrdef.Height= GfxBase->NormalDisplayRows    << (SCRMOD & LACE  ? 1 : 0))-1;

 if (!(windef.Screen = scr = OpenScreen (&scrdef))) close_all("Kein Screen");
 if (!(                win = OpenWindow (&windef))) close_all("Kein Window");

 vp=&(scr->ViewPort);
 rp=  win->RPort    ;
 CopyMem((char *)rgb,(char *)vp->ColorMap->ColorTable,32);
 RemakeDisplay();
 for (i=0;i<16;i++) /* Umrechnen der 16 gesetzten Farbe des HAM-Modes in float Werte */
  {ScreenFarbe[i].Rot  =(((rgb[i]>>8)&15))/15.49;
   ScreenFarbe[i].Gruen=(((rgb[i]>>4)&15))/15.49;
   ScreenFarbe[i].Blau =(((rgb[i]   )&15))/15.49;}
 }

#define ENDE            0
#define PARALLELOGRAMM  1
#define KUGEL           2
#define EBENE           4
#define SCHACHBRETT   256
#define LICHT         512
#define UNENDLICH    9.22337176E18
#define DUMMY  0,0,0,0,0
#define RADIUS Seite1.x

struct Objekt {short Typ;
               struct Punkt Mittelpunkt,Seite1,Seite2;
               struct Farbe Farbe;
               float  Reflektion;};

  /*************************************/
  /* Definition von Licht und Objekten */
  /*************************************/

struct Objekt Objekte[]={
{LICHT  | KUGEL,  500,-1000, 1500,  100, DUMMY,                            13,13,13,.0},
{LICHT  | KUGEL,- 500, -500,  500,  100, DUMMY,                            15,15,15,.0},
{         KUGEL, -200,   50, 1200,  100, DUMMY,                            .5,.1,.1,.5},
{         KUGEL,   50,   50, 1500,  100, DUMMY,                            .1,.5,.1,.5},
{         KUGEL,  200,   50, 1000,  100, DUMMY,                            .0,.0,.0,.8},
{SCHACHBRETT|
 EBENE         ,    0,  150, 1200,  100,    0,   0,      0,     0,  100,   .5,.5,.5,.5},
{ENDE}
};

struct Sichtstrahl {struct Punkt Ausgangspunkt,Richtung;
                    float Intensitaet;};

  /*********************************/
  /* wichtige Vektorrechenroutinen */
  /*********************************/

void Add(struct Punkt *erg,struct Punkt *pkt1,struct Punkt *pkt2)

{erg->x=pkt1->x+pkt2->x;
 erg->y=pkt1->y+pkt2->y;
 erg->z=pkt1->z+pkt2->z;}

void Dif(struct Punkt *erg,struct Punkt *pkt1,struct Punkt *pkt2)

{erg->x=pkt1->x-pkt2->x;
 erg->y=pkt1->y-pkt2->y;
 erg->z=pkt1->z-pkt2->z;}

float SkalarProdukt(struct Punkt *pkt1,struct Punkt *pkt2)

{return( pkt1->x*pkt2->x + pkt1->y*pkt2->y + pkt1->z*pkt2->z );}

void VektorMul(struct Punkt *erg,struct Punkt *pkt1,struct Punkt *pkt2)

{erg->x=pkt1->y*pkt2->z - pkt1->z*pkt2->y;
 erg->y=pkt1->z*pkt2->x - pkt1->x*pkt2->z;
 erg->z=pkt1->x*pkt2->y - pkt1->y*pkt2->x;
 }

void SkalareMul(struct Punkt *erg,float Faktor,struct Punkt *pkt)

{erg->x=Faktor*pkt->x;
 erg->y=Faktor*pkt->y;
 erg->z=Faktor*pkt->z;}

float Normiere(struct Punkt *pkt)

{float laenge=sqrt(SkalarProdukt(pkt,pkt));
 pkt->x*=1/laenge;
 pkt->y*=1/laenge;
 pkt->z*=1/laenge;
 return(laenge);}

struct Schnittpunkt {struct Punkt   Punkt;             /* Schnittpunkt */
                     struct Objekt *Objekt;            /* Geschnittenes Objekt */
                     float          Entfernung;        /* Schnittpunkt zu Ausgangspunkt des Sichtstrahles */
                     float          Oberfl_x,Oberfl_y; /* x,y Koordinate bei EBENE und PARALLELOGRAMM */
                     struct Punkt   Normale;};         /* Oberflächennormale am Schnittpunkt */

struct Sichtstrahl  *Reflektionsstrahl(struct Sichtstrahl *Sichtstrahl,struct Schnittpunkt *Schnittpunkt)

{struct Sichtstrahl  Ausfallenderstrahl;

 Ausfallenderstrahl.Ausgangspunkt=Schnittpunkt->Punkt;
 SkalareMul(&Ausfallenderstrahl.Richtung,-2*SkalarProdukt(&Sichtstrahl->Richtung,&Schnittpunkt->Normale),&Schnittpunkt->Normale);
 Add(&Ausfallenderstrahl.Richtung,&Ausfallenderstrahl.Richtung,&Sichtstrahl->Richtung);
 Normiere(&Ausfallenderstrahl.Richtung);
 return(&Ausfallenderstrahl);}

void BerechneSchnittpunkt(struct Schnittpunkt *Schnittpunkt,struct Objekt *Objekt,struct Sichtstrahl *Sichtstrahl)

{Schnittpunkt->Objekt=Objekt;
 Schnittpunkt->Entfernung=UNENDLICH;

 switch ((char)(Objekt->Typ))
  {case KUGEL:
    {float         b,c,d;
     struct Punkt  v;
     Dif(&v,&Sichtstrahl->Ausgangspunkt,&Objekt->Mittelpunkt);
     b=-2*SkalarProdukt(&Sichtstrahl->Richtung,&                    v);
     c=SkalarProdukt(&                    v,&                    v) - Objekt->RADIUS * Objekt->RADIUS;
     d=b*b-4*c;
     if (d>=0)
      {d=sqrt(d);
       c= b-d < 1 ? b+d : b-d;
        if (c>1)
        {Schnittpunkt->Entfernung=0.5*c;
         Schnittpunkt->Punkt.x=Sichtstrahl->Ausgangspunkt.x+Schnittpunkt->Entfernung*Sichtstrahl->Richtung.x;
         Schnittpunkt->Punkt.y=Sichtstrahl->Ausgangspunkt.y+Schnittpunkt->Entfernung*Sichtstrahl->Richtung.y;
         Schnittpunkt->Punkt.z=Sichtstrahl->Ausgangspunkt.z+Schnittpunkt->Entfernung*Sichtstrahl->Richtung.z;
         Dif(&Schnittpunkt->Normale,&Schnittpunkt->Punkt,&Objekt->Mittelpunkt);
         Normiere(&Schnittpunkt->Normale);}}
     break;}
   case PARALLELOGRAMM:
   case EBENE:
     VektorMul(&Schnittpunkt->Normale,&Objekt->Seite1,&Objekt->Seite2);
    {struct Punkt dif;
     float Nenner =SkalarProdukt(&Sichtstrahl->Richtung,&Schnittpunkt->Normale),
           Entfernung;
     if (Nenner)
      {Dif(&dif,&Objekt->Mittelpunkt,&Sichtstrahl->Ausgangspunkt);
       Entfernung=SkalarProdukt(&dif,&Schnittpunkt->Normale)/Nenner;
       if (Entfernung>1) /* Abfrage auf beste Entfernung !!! */
         {short i,j,k;
          float z1,z2;
          Schnittpunkt->Punkt.x=Sichtstrahl->Ausgangspunkt.x+Entfernung*Sichtstrahl->Richtung.x;
          Schnittpunkt->Punkt.y=Sichtstrahl->Ausgangspunkt.y+Entfernung*Sichtstrahl->Richtung.y;
          Schnittpunkt->Punkt.z=Sichtstrahl->Ausgangspunkt.z+Entfernung*Sichtstrahl->Richtung.z;
          for ( i=0 ; (Nenner=((float *)&Schnittpunkt->Normale)[i])==0 ; i++ );
          j=(i+1)%3;k=(i+2)%3;
          Schnittpunkt->Oberfl_x=
            (((float*)&Objekt->Seite1)[k]*(z1=((float *)&Schnittpunkt->Punkt)[j]-((float *)&Objekt->Mittelpunkt)[j])
           - ((float*)&Objekt->Seite1)[j]*(z2=((float *)&Schnittpunkt->Punkt)[k]-((float *)&Objekt->Mittelpunkt)[k]))
           / Nenner;
          if (Objekt->Typ&PARALLELOGRAMM && (-1>Schnittpunkt->Oberfl_x || Schnittpunkt->Oberfl_x>1)) break;
          Schnittpunkt->Oberfl_y=
             ((float*)&Objekt->Seite2)[k]* z1
           - ((float*)&Objekt->Seite2)[j]* z2
           / Nenner;
          if (Objekt->Typ&PARALLELOGRAMM && (-1>Schnittpunkt->Oberfl_y || Schnittpunkt->Oberfl_y>1)) break;
          Schnittpunkt->Entfernung=Entfernung;
          Normiere(&Schnittpunkt->Normale);
          }
       }
     }
     break;
   }
 }


void /*__stdargs*/ MattOberflaechenfarbe(struct Farbe *Farbe,float HauptIntensitaet,struct Schnittpunkt *Schnittpunkt)

{struct Objekt *Licht,*Objekt=Schnittpunkt->Objekt,*Obj;
 float        Intensitaet,Entfernung,CosEinfallswinkel;
 struct Schnittpunkt SchnittpunktLicht;
 struct Sichtstrahl  Lichtstrahl;

 Farbe->Rot  += HauptIntensitaet * Objekt->Farbe.Rot   * Hintergrundhelligkeit.Rot  ;
 Farbe->Gruen+= HauptIntensitaet * Objekt->Farbe.Gruen * Hintergrundhelligkeit.Gruen;
 Farbe->Blau += HauptIntensitaet * Objekt->Farbe.Blau  * Hintergrundhelligkeit.Blau ;

 for ( Licht=Objekte ; Licht->Typ ; Licht++ )
  {if ( Licht->Typ & LICHT )
    {if (Objekt->Typ & LICHT )
       *Farbe=Objekt->Farbe;
     else
      {Lichtstrahl.Ausgangspunkt=Schnittpunkt->Punkt;
       Dif(&Lichtstrahl.Richtung,&Licht->Mittelpunkt,&Schnittpunkt->Punkt);
       Entfernung=Normiere(&Lichtstrahl.Richtung);
       for ( Obj=Objekte ; Obj->Typ ; Obj++ )
         if ( Obj!=Licht )
          {BerechneSchnittpunkt(&SchnittpunktLicht,Obj,&Lichtstrahl);
           if ( SchnittpunktLicht.Entfernung < Entfernung ) break;}
       
       if ( SchnittpunktLicht.Entfernung >= Entfernung )
        {CosEinfallswinkel=SkalarProdukt(&Lichtstrahl.Richtung,&Schnittpunkt->Normale);
         Intensitaet= HauptIntensitaet * CosEinfallswinkel / Entfernung * (Licht->Typ & KUGEL ? Licht->RADIUS : sqrt(SkalarProdukt(&Licht->Seite1,&Licht->Seite1)*SkalarProdukt(&Licht->Seite2,&Licht->Seite2)));
         if (Objekt->Typ&SCHACHBRETT && ((short)(1e3+Schnittpunkt->Oberfl_x)+(short)(1e3+Schnittpunkt->Oberfl_y))&1)
             Intensitaet*=.25;
         if (Intensitaet>0)
          {Farbe->Rot  +=Intensitaet*Licht->Farbe.Rot  *Objekt->Farbe.Rot  ;
           Farbe->Gruen+=Intensitaet*Licht->Farbe.Gruen*Objekt->Farbe.Gruen;
           Farbe->Blau +=Intensitaet*Licht->Farbe.Blau *Objekt->Farbe.Blau ;}}
         }
     }
   }
 }

void /*__stdargs*/ RayTrace(struct Farbe *Farbe,float Intensitaet,struct Sichtstrahl Sichtstrahl)

{struct Objekt *Objekt;
 struct Schnittpunkt NaechsterSchnittpunkt,SchnPkt;

 if (Intensitaet>.1)
  {/**********************************************/
   /* Bestimmung des nächsten Schnittpunktes von */
   /* Sichtstrahles und dem nächsten Objekte     */
   /**********************************************/

   NaechsterSchnittpunkt.Objekt=NULL;
   NaechsterSchnittpunkt.Entfernung=UNENDLICH;
  
   for ( Objekt=Objekte ; Objekt->Typ ; Objekt++ )
    {BerechneSchnittpunkt(&SchnPkt,Objekt,&Sichtstrahl);
     if ( SchnPkt.Entfernung < NaechsterSchnittpunkt.Entfernung )
       NaechsterSchnittpunkt=SchnPkt;}
  
   /**********************************************************/
   /* Reflektionsstrahl und matte Oberflächenfarbe bestimmen */
   /**********************************************************/
 
   if ( Objekt=NaechsterSchnittpunkt.Objekt )
    {if (Objekt->Reflektion>0)
       RayTrace(Farbe,Intensitaet*Objekt->Reflektion,*Reflektionsstrahl(&Sichtstrahl,&NaechsterSchnittpunkt));
     if (Objekt->Farbe.Rot || Objekt->Farbe.Gruen || Objekt->Farbe.Blau)
       MattOberflaechenfarbe(Farbe,Intensitaet,&NaechsterSchnittpunkt);
     }
   else
    {Farbe->Rot  += Intensitaet*Hintergrundhelligkeit.Rot  ;
     Farbe->Gruen+= Intensitaet*Hintergrundhelligkeit.Gruen;
     Farbe->Blau += Intensitaet*Hintergrundhelligkeit.Blau ;}
   }
}

void WriteHAMPixel(short x,short y,struct Farbe *Farbe)

{       struct Farbe Dif;
 static struct Farbe AlteFarbe,Dithering={0,0,0};
 float  max,abs,minabs;
 char  zw;
 short  col,mincol;

 if (x==0) *Farbe=ScreenFarbe[0];

 Farbe->Rot  +=Dithering.Rot  ;
 Farbe->Gruen+=Dithering.Gruen;
 Farbe->Blau +=Dithering.Blau ;

 max=MAX(MAX(Farbe->Rot,Farbe->Gruen),Farbe->Blau);
 if (max>1) SkalareMul((struct Punkt *)Farbe,1/max,(struct Punkt *)Farbe);

/****************************************************************/
/* Berechnung, wie die Farbe möglichst gut erreicht werden kann */
/****************************************************************/

 Dif.Rot  =ABS(Farbe->Rot  -AlteFarbe.Rot  );
 Dif.Gruen=ABS(Farbe->Gruen-AlteFarbe.Gruen);
 Dif.Blau =ABS(Farbe->Blau -AlteFarbe.Blau );
 if (Dif.Rot<Dif.Gruen)
   if (Dif.Gruen<Dif.Blau)
     minabs=Dif.Rot+Dif.Gruen;
   else
     minabs=Dif.Rot+Dif.Blau;
 else
   if (Dif.Rot<Dif.Blau)
     minabs=Dif.Rot+Dif.Gruen;
   else    
     minabs=Dif.Gruen+Dif.Blau;
 mincol=-1;

 for (col=0;col<16;col++)
   {abs=1.2*(ABS(Farbe->Rot  -ScreenFarbe[col].Rot  )
            +ABS(Farbe->Gruen-ScreenFarbe[col].Gruen)
            +ABS(Farbe->Blau -ScreenFarbe[col].Blau ));
    if (abs< minabs)
      {mincol=col;
       minabs=abs;}
    }

 if (mincol==-1)
  {Dithering=Schwarz;
   if (Dif.Rot<Dif.Gruen)
     if (Dif.Gruen<Dif.Blau)
      {Blau:
       AlteFarbe.Blau=Farbe->Blau;
       zw=(char)(15.49*Farbe->Blau+.5);
       Dithering.Blau=(15.49*Farbe->Blau-zw)/16;
       rp->Mask=0x10+zw;}
     else
      {AlteFarbe.Gruen=Farbe->Gruen;
       zw=(char)(15.49*Farbe->Gruen+.5);
       Dithering.Gruen=(15.49*Farbe->Gruen-zw)/16;
       rp->Mask=0x30+zw;}
   else
     if (Dif.Rot<Dif.Blau)
       goto Blau;
     else
      {AlteFarbe.Rot=Farbe->Rot;
       zw=(char)(15.49*Farbe->Rot+.5);
       Dithering.Rot=(15.49*Farbe->Rot-zw)/16;
       rp->Mask=0x20+zw;}}
  else
  {rp->Mask=mincol;
   AlteFarbe.Rot  =ScreenFarbe[mincol].Rot  ;
   AlteFarbe.Gruen=ScreenFarbe[mincol].Gruen;
   AlteFarbe.Blau =ScreenFarbe[mincol].Blau ;
   Dithering.Rot  =(15.49*Farbe->Rot  -(char)(15.49*Farbe->Rot  +.5))/16;
   Dithering.Gruen=(15.49*Farbe->Gruen-(char)(15.49*Farbe->Gruen+.5))/16;
   Dithering.Blau =(15.49*Farbe->Blau -(char)(15.49*Farbe->Blau +.5))/16;
   }
 WritePixel(rp,x,y);
 }

void BerechneBild(void)

{short x,y;
 struct Sichtstrahl Sichtstrahl={{0,0,0},{0,0,0},1.};
 struct Farbe Farbe;

 for ( y=60  ; y<windef.Height ; y++ )
  {for ( x=0 ; x<windef.Width  ; x++ )
    {if (GetMsg(win->UserPort)) close_all(NULL);
     Sichtstrahl.Richtung.x=x-windef.Width*0.5-.25;
     Sichtstrahl.Richtung.y=y-windef.Height*0.3-.25;
     if (SCRMOD&LACE) Sichtstrahl.Richtung.y*=.5;
     Sichtstrahl.Richtung.z=512;
     Normiere(&Sichtstrahl.Richtung);
     Farbe=Schwarz;
     RayTrace(&Farbe,1.,Sichtstrahl);
     WriteHAMPixel(x,y,&Farbe);
     }
   }
 }

void main(char *xxx)

{open_all();
 BerechneBild();
 WaitPort(win->UserPort);
 close_all(NULL);
 }
