/* (c) Daniel Lichtenberger 28.1.97-??
                            6.4.98: an ALLEGRO angepaát ->
                                    VESA 2.0 - Support, Pageflipping
                                    und entgltig zur Include-Datei
                                    "degradiert" (Testprogramm 3d_test.cc) */
#define fakt 8192
 
#define faktshift 13
 
#define coordfakt 128
 
#define coordshift 7
 
int sintable[361],costable[361];
#include "_vector.cc"
 

BITMAP *BaseAddr;
int ScrWidth,ScrHeight;
#include "_dlline.c"
 

#define angle 45        // "Blickwinkel"
 
#define Grad(x) ((x/PI)*180)    /* Bogenmaá -> Grad */
 
#define Rad(x) ((x/180)*PI)     /* Grad -> Bogenmaá */
 


typedef struct {
 int nr;
 int Intense;
 int tex_x,tex_y; // Koordinaten innerhalb der Textur (falls

                  // Texture Mapping verwendet wird, bei Gouraud bedeutungslos)

} TPointRef;    /* Fr die TBody-Klasse */

typedef struct {
 int ref[100];          // Nummer des K”rpers usw.

 int dist[100];         // Entfernung (= z-Koordinate)

} TZBuf;

/* TPoly: NICHT fr den Gebrauch in TBody, da TPoly ein v”llig eigenst„ndiges
   Polygon darstellt */

class TPoly {
 TVector PolyPoints[10];      
 char nPoint;

public:
 TPoly() { nPoint = 0; }
 void SetPoint(int x,int y,int z,char nr) { PolyPoints[nr].x = x; PolyPoints[nr].y = y; PolyPoints[nr].z = z; }
 void AddPoint(int x,int y,int z) { SetPoint(x,y,z,nPoint); nPoint++; }
 void SetnPoint(char n) { nPoint = n; }
 char GetnPoint() { return(nPoint); }
};

/* TBody: Die einzelnen Polygone bestehen nur aus Verweisen auf das BodyPoints-
   Punke-Array, aber nicht aus "echten" Koordinaten */
class TBody {
protected:

 TVector BodyPointsOrig[20];    // Nicht nicht-rotierten Eckpunkte


 TVector PolySchwp[20];             // Die Schwerpunkte der einzelnen Polygone

 TVector Schwerpunkt;
 int ShadeLight;          // Werden die Polygone von einer Lichtquelle beleuchtet

 int TextureMapping;      // Wird der K”rper "gemappt", bei 0 -> Gouraud Shading

 BITMAP *PaintAddr;   // Die Bitmap, wo der K”rper gezeichnet werden soll


public:
 int ColorRange[20][2];         // Bereich der Farbpalette, in dem Abstufungen der Grundfarbe zu finden sind (z.B. alle Rotstufen) - [0] = Start [1] = Ende

 BITMAP *Textures[20];          // Zeiger auf die Texturen fr alle Polygone

 TVector BodyPoints[20];        // Alle Eckpunkte des K”rpers

 TPointRef Poly[20][10];        // 1. Zahl - Nummer des Polygons / 2. Zahl - z.B. 4. Punkt des 2. Polygons Poly[2][4]

 int nBodyPoint,nPoly;
 TVector2D TranslPoints[20];    // Die auf den Bildschirm transformierten Eckpunkte 

 int nPoint[20];          // Anzahl der Punkte fr jedes Polygon

 int RecalcTrans;         // Muá die Position der transformierten Punkte auf dem Bildschirm neu berechnet werden?

 int DrawLines;           // sollen die Begrenzungslinien & Eckpunkte gezeichnet werden?


 int AktPoint;            // Dieser Punkt wird hervorgehoben (v.a. fr Editoren)

 int AktPoly;             // Dieses Polygon wird in anderer Farbe gezeichnet (-> Editoren)

 TBody() { nBodyPoint = 0; nPoly = 0; ShadeLight = 0;
   RecalcTrans = 1; DrawLines = 0; TextureMapping = 0; }

 void SetPaintAddr(BITMAP *bmp) { PaintAddr = bmp; }
 void SetShade(int on) { ShadeLight = on; }
 void SetMapping(int on) { TextureMapping = on; }
 void SetBodyPoint(int x,int y,int z,char n) { BodyPoints[n].x = x << coordshift; BodyPoints[n].y = y << coordshift; BodyPoints[n].z = z << coordshift; RecalcTrans = 1; }
 void AddBodyPoint(int x,int y,int z) { SetBodyPoint(x,y,z,nBodyPoint); nBodyPoint++; }
 TVector GetBodyPoint(int nr) { return BodyPoints[nr]; }
 void SetOrigPoints(void);
 TVector GetSchwerpunkt(void) { return Schwerpunkt; }
 void AddPoly(int RangeStart,int RangeEnd) { ColorRange[nPoly][0] = RangeStart; ColorRange[nPoly][1] = RangeEnd; nPoint[nPoly] = 0; nPoly++; }
 void DelPoly(int nr);
 void SetPolyPoint(char refnr,unsigned char intense,char nPol,char nPnt) { Poly[nPol][nPnt].nr = refnr; Poly[nPol][nPnt].Intense = intense; }
 void AddPolyPoint(char refnr,unsigned char intense,char nPol) { SetPolyPoint(refnr,intense,nPol,nPoint[nPol]); nPoint[nPol]++; }
 /* _AddPolyPoint: Wie AddPolyPoint, allerdings wird automatisch das letzte
    Polygon verwendet */
 void _AddPolyPoint(char refnr,unsigned char intense) { AddPolyPoint(refnr,intense,nPoly-1); }
 void SetTexture(int polynr, BITMAP *bmp) { Textures[polynr] = bmp; }
 void CalcSchwerpunkt();
 void Translate(TVector v);
 void Scale(int sx,int sy,int sz);      // Der K”rper wird um die Faktoren sx,sy und sz gestreckt / 1 = coordfakt

 void TranslateTranslPoints(TVector2D v); // Es wird nur die Position des K”rpers am Bildschirm ge„ndert, fr Spielereien


 void RotateX(int rotangle);
 void RotateY(int rotangle);
 void RotateZ(int rotangle);

 void Draw();
 void PrintPoly();
};

class TComplexBody {
protected:
 TZBuf BodyZBuf;
 TVector Schwerpunkt;
 BITMAP *PaintAddr;
public:
 int nBodies;
 TBody *bodies[20];
 int DrawLines;

 TComplexBody() { nBodies = 0; }
 TBody *GetBody(int n) { return(bodies[n]); }
 int GetNBodies() { return nBodies; }
 void SetBody(TBody *tb,int n) { bodies[n] = tb; }
 void SetnBodies(int n) { nBodies = n; }

 void Draw();
 void DelBody(int nr);
 void Translate(TVector v) { int i; for (i = 0; i < nBodies; i++) bodies[i]->Translate(v); Schwerpunkt = Schwerpunkt+v; }
 void SetPaintAddr(BITMAP *adr) { int i; PaintAddr = adr; for (i = 0; i < nBodies; i++) bodies[i]->SetPaintAddr(adr); }
 void SetShade(int on) { int i; for (i = 0; i < nBodies; i++) bodies[i]->SetShade(on); }
 void SetOrigPoints() { int i; for (i = 0; i < nBodies; i++) bodies[i]->SetOrigPoints(); }
 void SetDrawLines(int DL) { int i; for (i = 0; i < nBodies; i++) bodies[i]->DrawLines = DL; }
 void CalcSchwerpunkt();
};

class TViewPoint {
public:
 TVector at,up,from;

 TViewPoint() { }
 void SetAt(int x,int y,int z) { at.x = x; at.y = y; at.z = z; }
 void SetUp(int x,int y,int z) { up.x = x; up.y = y; up.z = z; }
 void SetFrom(int x,int y,int z) { from.x = x; from.y = y; from.z = z; }
 TVector GetAt(void) { return at; }
 TVector GetFrom(void) { return from; }
 TVector GetUp(void) { return up; }
 void SetViewPoint();   /* Macht diesen Viewpoint zum aktuellen Vp. fr alle
                        Transformationen. Auáerdem werden alle notwendigen
                        globalen Variablen berechnet */
};

/* -----------------------------------------------------------------------
   ----------------------------------------------------------------------- */

int RotateOrig;         // Wird der Ursprungsk”rper rotiert (BodyPointsOrig), oder der eventuell bereits rotierte (BodyPoints)?

double DVal;
int DVal_Int;
TVector a1,a2,a3;
int offset_x,offset_y,offset_z;
int W2Dev_a,W2Dev_c,W2Dev_d;
TVector light;
int LightBetr;
TViewPoint viewpoint;
TZBuf ZBuf;           // Die K”rper, nach Entfernung geordnet 

int nDraw;


// ----------------------------------------------------------------------- 



#include "3d_add.cc"
 

//   -----------------------------------------------------------------------


//      --------------- Die Texture-Mapping-Routinen --------------- (c) DL


        // DrawTxtLine: Zeichnet eine (horizontale) Linie, deren Pixel aus einer Geraden zwischen x1/y1 und x2/y2 aus der Textur txt kommen

        // x1,y1,x2,y2: Start/End-Punkte einer Linie in der Textzre

        // len:         Die L„nge der horizontalen Linie

        // xscr,yscr:   Die Startposition der Linie auf dem Bildschirm

        // *txt:        Zeiger auf eine Bitmap

        // txtw,txth:   Dimensionen der Textur


void DrawTxtLine(int x1,int y1,int x2,int y2,int len,int xscr,int yscr, unsigned char *txt, int txtw,int txth) {
 TInterpol ipol;
 unsigned char pix,*scrp;
 int xpc,x,y,dx,dy,offset;     // (Achtung: x,y beziehen sich auf Koordinaten innerhalb der Textur, nicht auf den Bildschirm!!)


  if (len <= 0) return;
  dx = ((x2-x1) << faktshift) / len;
  dy = ((y2-y1) << faktshift) / len;
  x = x1 << faktshift;
  y = y1 << faktshift;
  xpc = xscr;
  scrp = (unsigned char *)BaseAddr+yscr*ScrWidth+xpc;
  do {
   offset = (y >> faktshift)*txtw+(x >> faktshift);
   if ((offset < txtw*txth) || (offset > 0))
           *scrp = txt[offset];
   x += dx;
   y += dy;
   scrp++;
   xpc++;
  } while (xpc < xscr+len);
}

   // Prft, ob das Polygon sichtbar ist (unsichtbar, wenn das Polygon

   //  entgegen dem Uhrzeigersinn gespeichert ist), prft auáerdem,

   // ob das Polygon berhaupt auf dem Bildschirm liegt

   // Entnommen aus DL_HLineGouraud256, deshalb etwas lang

int IsPolyVisible(char nPoint,int points[][2]) {

int x,y;
int i,top_point,bottom_point,top_point2;   /* Der h”chste und der niedrigste Punkt des Polygons */
int point_left,point_right,opoint;       /* Nummer des akt. rechten/linken Eckpunktes des Polygons */
int leftswap,rightswap;
int top_y,bottom_y;
TInterpol ipol_left,ipol_right;     /* Interpolationsvariablen fr beide Seiten */
int left_x,right_x;          /* x-Koordinate des interpolierten linken/rechten Punkts */
int leftpointx,leftpointy;
int rightpointx,rightpointy;

 top_y = 0xFFFF;       /* Vorsicht: "top" bedeutet NIEDRIGE y-Koordinate */
 bottom_y = -0xFFFF;

 for (i = 0; i < nPoint; i++) {       /* H”chsten und niedrigsten Punkt des Polygons finden */
  x = points[i][0];
  y = points[i][1];
  if (y <= top_y) {
   if (y == top_y) top_point2 = i;
   else {
     top_y = y;
     top_point = top_point2 = i;
   }
  }
  if (y > bottom_y) {
   bottom_y = y;
   bottom_point = i;
  }
 }

 if ((bottom_y-top_y < 1) || ((top_y <= 0) && (bottom_y <= 0)) ||
     ((top_y >= ScrHeight) && (bottom_y >= ScrHeight))) return(0);
 if ((top_point != top_point2) && (top_point == 0) && (top_point2 == nPoint-1) &&
     (points[top_point][0] < points[top_point2][0])) return(0);

 /* Annahme: Polygon ist im Uhrzeigersinn gespeichert */
 point_left = top_point-1; if (point_left < 0) point_left = nPoint-1;
 if (point_left == top_point2) point_left--;
 point_right = top_point2+1; if (point_right > nPoint-1) point_right = 0;
 if (point_right == top_point) point_right++;

 if (points[top_point][0] > points[top_point2][0])
   if ((top_point == 0) && (top_point2 == nPoint-1)) xchg(top_point,top_point2);
   else
    if (!drawback) return(0); 
   else xchg(top_point,top_point2);

 leftpointx = points[point_left][0]; leftpointy = points[point_left][1];
 rightpointx = points[point_right][0]; rightpointy = points[point_right][1];
 SetLine_Interpol(points[top_point][0],points[top_point][1],
                  leftpointx,leftpointy,&ipol_left);

 SetLine_Interpol(points[top_point2][0],points[top_point2][1],
                  rightpointx,rightpointy,&ipol_right);

 leftswap = ipol_left.swp;
 rightswap = ipol_right.swp;
 /* --------- Die Hauptschleife ---------------- */
 for (y = top_y; y <= bottom_y; y++) {

  if (leftswap) {
   left_x = ipol_left.ipcoord;
   Do_Interpol(&ipol_left);
  }
  else {
   left_x = ipol_left.fcoord;
   if (ipol_left.Dec_a)
    do { } while (!Do_Interpol(&ipol_left));
   }
    
  if (rightswap) {
   right_x = ipol_right.ipcoord;
   Do_Interpol(&ipol_right);
  }
  else {
   right_x = ipol_right.fcoord;
   if (ipol_right.Dec_a)
    do { } while (!Do_Interpol(&ipol_right));
   }
  
  if (left_x > right_x) return(0);
  else ;

  /* --- Eventuell neue Eckpunkte setzen ---- */
   if (y == leftpointy) {       /* Wenn eine linke Kante fertiggezeichnet ist, neuen Punkt berechnen */
    opoint = point_left;
    point_left--;
    if (point_left < 0) point_left += nPoint;
    else if (point_left > nPoint-1) point_left -= nPoint;
    leftpointx = points[point_left][0]; leftpointy = points[point_left][1];

    SetLine_Interpol(points[opoint][0],points[opoint][1],
                   leftpointx,leftpointy,&ipol_left);
    leftswap = ipol_left.swp;
   }
   if (y == rightpointy) {      /* Fr die rechte Kante ebenso.... */
    opoint = point_right;
    point_right++;
    if (point_right < 0) point_right += nPoint;
    else if (point_right > nPoint-1) point_right -= nPoint;
    rightpointx = points[point_right][0]; rightpointy = points[point_right][1];

     SetLine_Interpol(points[opoint][0],points[opoint][1],
                   rightpointx,rightpointy,&ipol_right);
     rightswap = ipol_right.swp;
   }
  }
 return(1);
}


void ZSort(TBody *body[],int nBodies,TZBuf *zbuf) {
int i,k,j,nz,point,zmax;
TVector v;
TVector2D v2d;
 for (i = 0; i < 50; i++) {
  zbuf->ref[i] = 0;
  zbuf->dist[i] = -0xFFFFFFFF;
 }
 nDraw = 0;
 for (i = 0; i < nBodies; i++) {
  v = body[i]->GetSchwerpunkt() >> coordshift;
  nz = v.x*a3.x + a3.y*v.y + a3.z*v.z + offset_z;
/*  zmax = 0;
  for (point = 0; point < body[i]->nBodyPoint; point++) {
   v = body[i]->BodyPoints[point] >> coordshift;
   nz = v.x*a3.x + a3.y*v.y + a3.z*v.z + offset_z;
   if (nz > zmax) zmax = nz;
  }*/
  if (nz > 0) {
   k = 0;
   while ((zbuf->dist[k] > nz) && (k < i)) k++; 
   for (j = i+1; j > k; j--) {
        zbuf->ref[j] = zbuf->ref[j-1];
        zbuf->dist[j] = zbuf->dist[j-1];
   }
   zbuf->ref[k] = i;
   zbuf->dist[k] = nz;
   nDraw++;
  }
 }
}


/* -----------------------------------------------------------------------
   ----------------------------------------------------------------------- */

void TViewPoint::SetViewPoint() {
TVector_Real _at,_from,_up;
TVector_Real _a1,_a2,_a3;
TVector_Real temp,temp2;

// DVal = 1/(sin(0.39)/cos(0.39));

// DVal_Int = (int)(DVal*fakt);

 DVal_Int = 14188;
 _at.x = at.x; _at.y = at.y; _at.z = at.z;
 _from.x = from.x; _from.y = from.y; _from.z = from.z;
 _up.x = up.x; _up.y = up.y; _up.z = up.z;

 temp2 = _at-_from;
 _a3 = temp2/Betrag_Real(temp2);
 KreuzProdukt_Real(_up,temp2,&temp);
 _a1 = temp/Betrag_Real(temp);
 KreuzProdukt_Real(_a3,_a1,&temp);
 _a2 = temp/Betrag_Real(temp);

 a1.x = (int)(_a1.x*fakt);  a1.y = (int)(_a1.y*fakt);  a1.z = (int)(_a1.z*fakt);
 a2.x = (int)(_a2.x*fakt);  a2.y = (int)(_a2.y*fakt);  a2.z = (int)(_a2.z*fakt);
 a3.x = (int)(_a3.x*fakt);  a3.y = (int)(_a3.y*fakt);  a3.z = (int)(_a3.z*fakt);
 offset_x = -from.x*a1.x-from.y*a1.y-from.z*a1.z;
 offset_y = -a2.x*from.x-a2.y*from.y-a2.z*from.z;
 offset_z = -a3.x*from.x-a3.y*from.y-a3.z*from.z;
}

void TBody::SetOrigPoints() {
int i;
 for (i = 0; i < nBodyPoint; i++)
  CopyVector(BodyPoints[i],&BodyPointsOrig[i]);
}

void TBody::Draw() {
int i,k;
int nx,ny,nz;
int xpc,ypc;
char nr;
unsigned char *scrp;
int points[10][2];
int colors[10];
int LightAngle,DeltaColor;
TVector NormV,v1,v2;
int b;
V3D *AllegPoints[10];

 for (i = 0; i < 10; i++)
  AllegPoints[i] = malloc(24);
 if (RecalcTrans) {
  RecalcTrans = 0;
  if (!IsOnScreen(Schwerpunkt)) return;
 
  for (i = 0; i < nBodyPoint; i++)
   if (!TransformXYZ(BodyPoints[i],&TranslPoints[i])) return;

 }

 drawback = 0;
 for (i = 0; i < nPoly; i++) {

  if (!TextureMapping) {   // Gouraudshading

   if (ShadeLight) {
    v1 = BodyPoints[Poly[i][1].nr]-BodyPoints[Poly[i][0].nr];
    v2 = BodyPoints[Poly[i][2].nr]-BodyPoints[Poly[i][1].nr];
    v1 = v1 >> coordshift;
    v2 = v2 >> coordshift;
    KreuzProdukt(v1,v2,&NormV);
    b = Betrag(NormV);
    if (b > 1000000) {
     NormV = NormV >> 10;
     b >>= 10;
    }
    LightAngle = CalcAngle2(light,NormV,LightBetr,b);
    if (LightAngle < 0)
     DeltaColor = -LightAngle >> 10;
    else DeltaColor = 0;
   } else DeltaColor = 0;

   for (k = 0; k < nPoint[i]; k++) {
     nr = Poly[i][k].nr;
     colors[k] = Poly[i][k].Intense+DeltaColor;
     if (colors[k] > ColorRange[i][1]) colors[k] = ColorRange[i][1]-1;
     points[k][0] = TranslPoints[nr].x;
     points[k][1] = TranslPoints[nr].y;
    }
    DL_FillPolyGouraud(nPoint[i],points,colors);
   }
  else {       // Texturemapping

   for (k = 0; k < nPoint[i]; k++) {
    nr = Poly[i][k].nr;
    AllegPoints[k]->x = itofix(TranslPoints[nr].x);
    AllegPoints[k]->y = itofix(TranslPoints[nr].y);
    AllegPoints[k]->z = itofix(100);
    AllegPoints[k]->u = itofix(Poly[i][k].tex_x);
    AllegPoints[k]->v = itofix(Poly[i][k].tex_y);
    points[k][0] = TranslPoints[nr].x;
    points[k][1] = TranslPoints[nr].y;
   }
    // Die Adresse ist BaseAddr (muá im Hauptprogramm definiert werden), aus Kompatibilt„tsgrnden

   if (IsPolyVisible(nPoint[i],points))
    polygon3d(BaseAddr,POLYTYPE_ATEX,Textures[i],nPoint[i],AllegPoints);
  }

  if (DrawLines) {
   for (k = 0; k < nPoint[AktPoly]; k++) {
    nr = Poly[AktPoly][k].nr;
    colors[k] = 9;
    points[k][0] = TranslPoints[nr].x;
    points[k][1] = TranslPoints[nr].y;
   }
   drawback = 1;
   DL_FillPolyGouraud(nPoint[AktPoly],points,colors);
   drawback = 0;
   for (k = 0; k < nBodyPoint; k++)
    if (k == AktPoint) putpixel(BaseAddr,TranslPoints[k].x,TranslPoints[k].y,255);
    else putpixel(BaseAddr,TranslPoints[k].x,TranslPoints[k].y,5);
  }
 }
}

void TBody::PrintPoly() {
int i,k;
char nr;
 printf("\nSchwerpunkt: (%d/%d/%d)\n",Schwerpunkt.x >> coordshift,Schwerpunkt.y >> coordshift,Schwerpunkt.z >> coordshift);
 for (i = 0; i < nPoly; i++) {
  printf("\nPolygon #%d: %d Punkte\n",i,nPoint[i]);
  for (k = 0; k < nPoint[i]; k++) {
   nr = Poly[i][k].nr;
   printf(" (%d/%d/%d),",BodyPoints[nr].x >> coordshift,BodyPoints[nr].y >> coordshift,BodyPoints[nr].z >> coordshift);
  }
 }
}

void TBody::CalcSchwerpunkt() {
int i,k;
TVector Schwp;
 ClearVector(Schwerpunkt);
 for (i = 0; i < nPoly; i++) {
  ClearVector(Schwp);
   for (k = 0; k < nPoint[i]; k++)
    Schwp = Schwp+BodyPoints[Poly[i][k].nr];
  Schwp = Schwp/nPoint[i];
  CopyVector(Schwp,&PolySchwp[i]);
  Schwerpunkt = Schwerpunkt+Schwp;
 }
 Schwerpunkt = (Schwerpunkt/nPoly);
}

void TBody::Translate(TVector v) {
int i;
 RecalcTrans = 1;
 for (i = 0; i < nBodyPoint; i++) {
  BodyPoints[i] = BodyPoints[i]+v;
  BodyPointsOrig[i] = BodyPointsOrig[i]+v;
 }

 Schwerpunkt = Schwerpunkt+v;
}

void TBody::TranslateTranslPoints(TVector2D v) {
int i;
 for (i = 0; i < nBodyPoint; i++) 
  TranslPoints[i] = TranslPoints[i]+v;
}

void TBody::Scale(int sx,int sy,int sz) {
int i;
TVector tmp;
 RecalcTrans = 1;
 CopyVector(Schwerpunkt,&tmp);
 NegVector(tmp);
 Translate(tmp);
 for (i = 0; i < nBodyPoint; i++) {
  BodyPoints[i].x = (BodyPoints[i].x*sx) >> coordshift;
  BodyPoints[i].y = (BodyPoints[i].y*sy) >> coordshift;
  BodyPoints[i].z = (BodyPoints[i].z*sz) >> coordshift;
  CopyVector(BodyPoints[i],&BodyPointsOrig[i]);
 }
 NegVector(tmp);
 Translate(tmp);
}

void TBody::RotateX(int rotangle) {
TVector OldSchwp,tmp;
int i;
 RecalcTrans = 1;
 CopyVector(Schwerpunkt,&OldSchwp);
 CopyVector(Schwerpunkt,&tmp);
 NegVector(tmp);
 Translate(tmp);        // Der K”rper kann nur im Nullpunkt gedreht werden

 if (RotateOrig)
  for (i = 0; i < nBodyPoint; i++)
   v_RotateX(BodyPointsOrig[i],rotangle,&BodyPoints[i]);
 else
  for (i = 0; i < nBodyPoint; i++)
   v_RotateX(BodyPoints[i],rotangle,&BodyPoints[i]);
 Translate(OldSchwp);
}

void TBody::RotateY(int rotangle) {
TVector OldSchwp,tmp;
int i;
 RecalcTrans = 1;
 CopyVector(Schwerpunkt,&OldSchwp);
 CopyVector(Schwerpunkt,&tmp);
 NegVector(tmp);
 Translate(tmp);        // Der K”rper kann nur im Nullpunkt gedreht werden

 if (RotateOrig)
  for (i = 0; i < nBodyPoint; i++)
   v_RotateY(BodyPointsOrig[i],rotangle,BodyPoints[i]);
 else
  for (i = 0; i < nBodyPoint; i++) 
   v_RotateY(BodyPoints[i],rotangle,BodyPoints[i]);
 Translate(OldSchwp);
}

void TBody::RotateZ(int rotangle) {
TVector OldSchwp,tmp;
int i;
 RecalcTrans = 1;
 CopyVector(Schwerpunkt,&OldSchwp);
 CopyVector(Schwerpunkt,&tmp);
 NegVector(tmp);
 Translate(tmp);        // Der K”rper kann nur im Nullpunkt gedreht werden

 if (RotateOrig)
  for (i = 0; i < nBodyPoint; i++)
   v_RotateZ(BodyPointsOrig[i],rotangle,&BodyPoints[i]);
 else
  for (i = 0; i < nBodyPoint; i++) 
   v_RotateZ(BodyPoints[i],rotangle,&BodyPoints[i]);
 Translate(OldSchwp);
}

void TBody::DelPoly(int nr) {
 int i,k;
  if (nPoly <= 1) return;

  for (i = nr; i < nPoly-1; i++) {
   for (k = 0; k < nPoint[i+1]; k++)
    Poly[i][k] = Poly[i+1][k];
   nPoint[i] = nPoint[i+1];
   PolySchwp[i] = PolySchwp[i+1];
   ColorRange[i][0] = ColorRange[i+1][0];
   ColorRange[i][1] = ColorRange[i+1][1];
  }
  nPoly--;
}

void TComplexBody::Draw() {
 int i;
  ZSort(bodies,nBodies,&BodyZBuf);
  for (i = 0; i < nDraw; i++) {
   bodies[BodyZBuf.ref[i]]->RecalcTrans = 1;
   bodies[BodyZBuf.ref[i]]->Draw();
  }
}

void TComplexBody::DelBody(int nr) {
 int i;

  if (nBodies <= 1) return;
  for (i = nr; i < nBodies-1; i++)
   bodies[i] = bodies[i+1];
//  free(bodies[nBodies-1]);

  nBodies--;
  CalcSchwerpunkt();
}

void TComplexBody::CalcSchwerpunkt() {
 int i;
 TVector Schwp;
  ClearVector(Schwp);
  for (i = 0; i < nBodies; i++) {
   bodies[i]->CalcSchwerpunkt();
   Schwp = Schwp+bodies[i]->GetSchwerpunkt();
  }
  Schwerpunkt = Schwp / nBodies;
}

void DrawAllBodies(int nBody, TBody *bodies[]) {
int i;
 ZSort(bodies,nBody,&ZBuf);
 for (i = 0; i < nDraw; i++) {
  bodies[ZBuf.ref[i]]->RecalcTrans = 1;
  bodies[ZBuf.ref[i]]->Draw();
 }
}

void LoadBody(int n, char *fn, TBody *bodies[]) {
 bodies[n] = new TBody;
 Read3DBody(fn,bodies[n]);
 bodies[n]->SetShade(1);
 bodies[n]->CalcSchwerpunkt();
 bodies[n]->SetOrigPoints();
}

void LoadComplexBody(int n, char *fn, TComplexBody *complexbodies[]) {
 complexbodies[n] = new TComplexBody;
 ReadComplex3DBody(fn,complexbodies[n]);
 complexbodies[n]->SetShade(1);
 complexbodies[n]->CalcSchwerpunkt();
 complexbodies[n]->SetOrigPoints();
}

void Init3D() {   // Erst NACH der Initialisierung des Grafikmodus' aufrufen!!

int i;

 W2Dev_a = ScrWidth/2;
 W2Dev_c = -ScrHeight/2;
 W2Dev_d = ScrHeight + W2Dev_c;
 drawback = 0;
 for (i = 0; i <= 360; i++) {
  sintable[i] = (int)(sin(Rad((float)i))*fakt);
  costable[i] = (int)(cos(Rad((float)i))*fakt);
 }
}