#include "vengine.h"


tObject *GetObject (long nbFaces, long nbPoints,tFloat x, tFloat y, tFloat z)
{
  tObject *obj=(tObject *)malloc (sizeof(tObject));

  obj->nbPoints=nbPoints;
  obj->nbFaces=nbFaces;
  obj->opoints=(tVector *)malloc (sizeof(tVector)*nbPoints);
  obj->bpoints=(tVector *)malloc (sizeof(tVector)*nbPoints);
  obj->faces=(tFace *)malloc (sizeof(tFace)*nbFaces);
  tInitMatrix (0, 0, 0, &obj->rotmatrix);
  obj->pos.x=x;
  obj->pos.y=y;
  obj->pos.z=z;
  obj->pos.flag=0;

  return (obj);
}


void FreeObject (tObject *obj)
{
  if (obj)
  {
    free (obj->opoints);
    free (obj->bpoints);
    free (obj->faces);
  }
}



tFloat tCos[SINUSFULL];
tFloat tSin[SINUSFULL];


void tInitSinus ()
{
  short i;
  float x=0;
  float dx=360;
  dx/=SINUSFULL;
  for (i=0;i<SINUSFULL;i++)
    {
      tCos[i]=cos(x/180*3.14);
      tSin[i]=sin(x/180*3.14);
      x+=dx;
    }
}





void tInitMatrix (long x,long y,long z,tMatrix *m)

{
  long  rx,ry,rz;

  rx=(x&(SINUSFULL-1));
  ry=(y&(SINUSFULL-1));
  rz=(z&(SINUSFULL-1));
  m->i.x=(tCos[ry]*tCos[rz]);
  m->i.y=(tCos[rx]*tSin[rz]+tSin[rx]*tSin[ry]*tCos[rz]);
  m->i.z=(tSin[rx]*tSin[rz]-tCos[rx]*tSin[ry]*tCos[rz]);
  m->j.x=(-tCos[ry]*tSin[rz]);
  m->j.y=(tCos[rx]*tCos[rz]-tSin[rx]*tSin[ry]*tSin[rz]);
  m->j.z=(tSin[rx]*tCos[rz]+tCos[rx]*tSin[ry]*tSin[rz]);
  m->k.x=(tSin[ry]);
  m->k.y=(-tSin[rx]*tCos[ry]);
  m->k.z=(tCos[rx]*tCos[ry]);


}


void tCalcObject (tObject *ob, tMatrix *rm, tVector *po)
{
  long  i;
  for (i=0;i<ob->nbPoints;i++)
  {
    tFloat px= ob->opoints[i].x+ob->pos.x-po->x;
    tFloat py= ob->opoints[i].y+ob->pos.y-po->y;
    tFloat pz= ob->opoints[i].z+ob->pos.z-po->z;

    ob->bpoints[i].x = px*ob->rotmatrix.i.x+py*ob->rotmatrix.j.x+pz*ob->rotmatrix.k.x;
    ob->bpoints[i].y = px*ob->rotmatrix.i.y+py*ob->rotmatrix.j.y+pz*ob->rotmatrix.k.y;
    ob->bpoints[i].z = px*ob->rotmatrix.i.z+py*ob->rotmatrix.j.z+pz*ob->rotmatrix.k.z;
    if (ob->bpoints[i].z<8) ob->bpoints[i].flag=1;
        else
        ob->bpoints[i].flag=0;
  }

}

void tCalcObject2 (tObject *ob, tMatrix *rm, tVector *po)
{
  long  i;
  for (i=0;i<ob->nbPoints;i++)
  {
    tFloat px=ob->opoints[i].x*ob->rotmatrix.i.x+ob->opoints[i].y*ob->rotmatrix.j.x+ob->opoints[i].z*ob->rotmatrix.k.x;
    tFloat py=ob->opoints[i].x*ob->rotmatrix.i.y+ob->opoints[i].y*ob->rotmatrix.j.y+ob->opoints[i].z*ob->rotmatrix.k.y/2;
    tFloat pz=ob->opoints[i].x*ob->rotmatrix.i.z+ob->opoints[i].y*ob->rotmatrix.j.z+ob->opoints[i].z*ob->rotmatrix.k.z;

    ob->bpoints[i].x = px+ob->pos.x-po->x;
    ob->bpoints[i].y = py+ob->pos.y-po->y;
    ob->bpoints[i].z = pz+ob->pos.z-po->z;
    if (ob->bpoints[i].z<8) ob->bpoints[i].flag=1;
        else
        ob->bpoints[i].flag=0;
  }

}





long Filter;
long tBpp;

typedef
struct tpoints
{
  long x,y;
}tPoints;

long tLeftOutLine[1024];
long tRightOutLine[1024];

long miny,maxy;

long nbPoints;
tPoints points[6];

long ExtraLinesLeft=0;
tPoints ELeft[2];


void tInsertOutline (long x1,long y1, long x2, long y2, long f,tWindow *win,long *tp)
{
  tFloat px,dx;
  long y;
  long bx;


  if (y1>y2)
  {
    long tmp;
    tmp=y1;
    y1=y2;
    y2=tmp;
    tmp=x1;
    x1=x2;
    x2=tmp;
  }


  dx=((tFloat)(x2-x1))/(y2-y1);
  px=x1;

  if (y1<y2)
  {
    if (y1<miny) miny=y1;
    if (y2>maxy) maxy=y2;
  }

    for (y=y1;y<y2;y++)
    {
      bx=(long)px;
      if (bx<0) bx=0;
      if (bx>(win->x-1)) bx=win->x-1;
      tp[y]=bx;
   /*
      if (&tRightOutLine[0]==tp) *(Buffer+bx*3+y*320*3)+=128;
            else
             *(Buffer+bx*3+y*320*3)+=64;
     */
      px+=dx;
    }
    bx=(long)x2;

    if (bx<0) bx=0;
    if (bx>(win->x-1)) bx=win->x-1;
      tp[y]=bx;

}


void tClipping (long x1,long y1, long x2, long y2, long f,tWindow *win)
{

// 2D Clipping

  long *tp=&tLeftOutLine[0];

  if (y1<y2)
  {
    long tmp;
    tmp=y1;
    y1=y2;
    y2=tmp;
    tmp=x1;
    x1=x2;
    x2=tmp;
    f=!f;
    tp=&tRightOutLine[0];
  }

  if (y1<=0) return;
  if (y2>=(win->y-1)) return;

  if (y1>(win->y-1))
  {
    x1=(long)(x1-(tFloat)(x2-x1)*(y1-(win->y-1))/(y2-y1));
    y1=win->y-1;
  }

  if (y2<0)
  {
    x2=(long)(x2-(tFloat)(x1-x2)*(y2)/(y1-y2));
    y2=0;
  }

  if (x1<x2)
  {
    long tmp;
    tmp=y1;
    y1=y2;
    y2=tmp;
    tmp=x1;
    x1=x2;
    x2=tmp;
    f=!f;

  }

  tInsertOutline (x1,y1,x2,y2,f,win,tp);
}


void tInitOutline (tVector *v1, tVector *v2, tWindow *win)
{

  tVector *tmp;
  tFloat ZClip=8;
  tFloat KameraZ=256;
  tFloat x2,y2;
  long f=0;

  if (v1->z<v2->z)
  {
    tmp=v1;
    v1=v2;
    v2=tmp;
    f=1;
  }

  if (v1->z<ZClip) return;       //v1.z = groessere von beiden
  if (v2->z>=ZClip)
  {
    if (f)
    {
      points[nbPoints].x=(long)(v2->x*KameraZ/v2->z+win->kx);
      points[nbPoints].y=(long)(v2->y*KameraZ/v2->z+win->ky);
      nbPoints++;
    }
      else
    {
      points[nbPoints].x=(long)(v1->x*KameraZ/v1->z +win->kx);
      points[nbPoints].y=(long)(v1->y*KameraZ/v1->z+win->ky);
      nbPoints++;
    }
  }
    else
  {
    tFloat v;
// 3D Clipping


    v=(ZClip-v1->z)/(v2->z-v1->z);
    x2=((v2->x-v1->x)*v+v1->x)*KameraZ/ZClip +win->kx;
    y2=((v2->y-v1->y)*v+v1->y)*KameraZ/ZClip+win->ky;

    if (f)
      {

        points[nbPoints].x=(long)x2;
        points[nbPoints].y=(long)y2;
        nbPoints++;

      }
      else
      {
        points[nbPoints].x=(long)(v1->x*KameraZ/v1->z +win->kx);
        points[nbPoints].y=(long)(v1->y*KameraZ/v1->z+win->ky);
        nbPoints++;

        points[nbPoints].x=(long)(x2);
        points[nbPoints].y=(long)(y2);
        nbPoints++;
       }
  }



//  *(Buffer+(long)x1+(long)y1*320)=64;
//  *(Buffer+(long)x2+(long)y2*320)=64;

}


void tDrawFace (tFace* tmp,tVector *Points,tWindow *win)
{
  long y,zflag=0;
  tFloat h1,h2,h3,h4,h5,h6,h7,h8,h9,a,b,c,kx,ky;
  tFloat x1,y1,x2,y2;
  long i;
  tVector faceo,facer1,facer2;
  tFloat KameraZ=256;
  char *Buffer;



  faceo=Points[tmp->p1];
  facer1=Points[tmp->p2];
  facer2=Points[tmp->p3];


  faceo.flag=0;
  facer1.flag=0;
  facer2.flag=0;

  if ( faceo.z<128) zflag++,faceo.flag=1;
  if ( facer1.z<128) zflag++,facer1.flag=1;
  if ( facer2.z<128) zflag++,facer2.flag=1;

  if (zflag==3) return;      //Not visible

// Init Outlines

  miny=4096;
  maxy=0;
  nbPoints=0;
  tInitOutline (&faceo, &facer1, win);
  tInitOutline (&facer1, &facer2, win);
  tInitOutline (&facer2, &faceo, win);

  x1=points[1].x-points[0].x;
  y1=points[1].y-points[0].y;
  x2=points[2].x-points[0].x;
  y2=points[2].y-points[0].y;
  if ((x1*y2-x2*y1)<=0) return;


  if (nbPoints)
  {
    for (i=0;i<(nbPoints-1);i++)
    {
      tClipping (points[i].x,points[i].y,points[i+1].x,points[i+1].y,0,win);
    }
    tClipping (points[i].x,points[i].y,points[0].x,points[0].y,0,win);
  }
    else return;

  facer1.x-=faceo.x;
  facer1.y-=faceo.y;
  facer1.z-=faceo.z;

  facer2.x-=faceo.x;
  facer2.y-=faceo.y;
  facer2.z-=faceo.z;


// Rechne konst. teil der Determinanten


  h1=facer1.y*facer2.z-facer1.z*facer2.y;
  h2=facer1.x*facer2.z-facer1.z*facer2.x;
  h3=(facer1.x*facer2.y-facer1.y*facer2.x)*KameraZ;

  h4=faceo.z*facer2.y-faceo.y*facer2.z;
  h5=faceo.z*facer2.x-faceo.x*facer2.z;
  h6=(faceo.y*facer2.x-faceo.x*facer2.y)*KameraZ;

  h7=facer1.z*faceo.y-facer1.y*faceo.z;
  h8=facer1.z*faceo.x-facer1.x*faceo.z;
  h9=(facer1.y*faceo.x-facer1.x*faceo.y)*KameraZ;


 Buffer=win->Screen+miny*win->bpr;


 for (y=miny;y<=maxy;y++)
 {
   char *screen=Buffer+(tLeftOutLine[y]);


   Buffer+=win->bpr;

   if (tLeftOutLine[y]<tRightOutLine[y])
   {
     tFloat ic,ac,bc;
     long prx1,pry1;
     long prx2,pry2;
     long dtx,dty,bx,tx,ty;

     kx=tLeftOutLine[y]-win->kx;
     ky=y-win->ky+1;
     c=kx*h1-ky*h2+h3;
     a=kx*h4-ky*h5+h6;
     b=kx*h7-ky*h8+h9;

     ic=1/c;
     ac=(a*ic);
     bc=(b*ic);
     prx1=((long)((ac*tmp->u2+bc*tmp->u3+tmp->u1)*256));
     pry1=((long)((ac*tmp->v2+bc*tmp->v3+tmp->v1)*256));       //errechne ersten Punkt



     {

     bx=tRightOutLine[y]-tLeftOutLine[y];

     a+=h4*bx;
     b+=h7*bx;
     c+=h1*bx;
     ic=1/c;
     ac=(a*ic);
     bc=(b*ic);

     prx2=((long)((ac*tmp->u2+bc*tmp->u3+tmp->u1)*256));
     pry2=((long)((ac*tmp->v2+bc*tmp->v3+tmp->v1)*256));       //errechne ersten Punkt

     dtx=(prx2-prx1)/(bx);
     dty=(pry2-pry1)/(bx);

     tx=(long)prx1;
     ty=(long)pry1;

     prx1=prx2;
     pry1=pry2;



   for (;bx>0;bx--)
    {
      *screen++=*(tmp->text+( (tx>>8)&0xff )+(ty&0xff00));
      tx+=dtx;
      ty+=dty;
    }

  }

 }
 }
 }

