/** 3DGPL *************************************************\
 *  (8bit deep bitmap)                                    *
 *  2D graphics and 2D clipping extentions for shaded     *
 *  and textured polygons (has to be linked with regular  *
 *  routines).                                            *
 *                                                        *
 *  Defines:                                              *
 *   G_ambient_polygon       regular polygon;             *
 *   G_shaded_polygon        shaded polygon;              *
 *   G_textured_polygon      textured polygon.            *
 *                                                        *
 *  Internals:                                            *
 *   GI_scan                 scanning an edge;            *
 *   GI_boarder_array_init   init left/right boundaries.  *
 *                                                        *
 *  (6/1995) By Sergei Savchenko. (savs@cs.mcgill.ca).    *
 *  Copyright (c) 1995 Sergei Savchenko.                  *
 *  THIS SOURCE CODE CAN'T BE USED FOR COMERCIAL PURPOSES *
 *  WITHOUT AUTHORISATION                                 *
\**********************************************************/

#include <limits.h>                         /* INT_MAX/INT_MIN */
#include "../hardware/hardware.h"           /* hardware specific stuff */
#include "../clipper/clipper.h"             /* 2D clipping */
#include "../graphics/graphics.h"           /* 2D macros */  
#include "../trans/trans.h"                 /* T_LOG_FOCUS */

extern unsigned char *G_buffer;             /* the bitmap's bits */

int G_start[HW_SCREEN_Y_SIZE];              /* polygon's */
int G_end[HW_SCREEN_Y_SIZE];                /* horizontal boundaries */
int G_miny,G_maxy;                          /* vertical boundaries */

#define G_P      10                         /* fixed point math prcision */
#define G_LINEAR 32                         /* linearely interpolate for */

signed_32_bit G_0_start[HW_SCREEN_Y_SIZE];  /* contains [32-G_P].[G_P] values */
signed_32_bit G_0_end[HW_SCREEN_Y_SIZE];    /* this thingie is to work faster */
signed_32_bit G_1_start[HW_SCREEN_Y_SIZE];  /* then multidimensional array */
signed_32_bit G_1_end[HW_SCREEN_Y_SIZE];    /* hope so, */
signed_32_bit G_2_start[HW_SCREEN_Y_SIZE];
signed_32_bit G_2_end[HW_SCREEN_Y_SIZE];

signed_32_bit *G_rest_start[C_MAX_DIMENSIONS]={G_0_start,G_1_start,G_2_start};
signed_32_bit *G_rest_end[C_MAX_DIMENSIONS]={G_0_end,G_1_end,G_2_end};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 *  INTERNAL: Scan converting a N dimensional line.      *
 *  ---------                                            *  
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void GI_scan(int *edges,int dimension,int skip)
{
 signed_32_bit cur_v[C_MAX_DIMENSIONS];     /* initial values for Z+ dims */
 signed_32_bit inc_v[C_MAX_DIMENSIONS];     /* increment for Z+ dimensions */
 signed_32_bit tmp;
 int dx,dy,long_d,short_d;
 register int d,add_dh,add_dl;              
 register int inc_xh,inc_yh,inc_xl,inc_yl;  
 int x,y,i,j;
 int *v1,*v2;                               /* first and second vertices */

 v1=edges; edges+=skip; v2=edges;           /* length ints in each */

 if(C_line_y_clipping(&v1,&v2,dimension))   /* vertical clipping */
 {
  dx=*v2++; dy=*v2++;                       /* extracting 2-D coordinates */
  x=*v1++; y=*v1++;                         /* v2/v1 point remaining dim-2 */
  dimension-=2; 

  if(y<G_miny) G_miny=y;
  if(y>G_maxy) G_maxy=y;
  if(dy<G_miny) G_miny=dy;
  if(dy>G_maxy) G_maxy=dy;                  /* updating vertical size */

  dx-=x; dy-=y;                             /* ranges */

  if(dx<0){dx=-dx; inc_xh=-1; inc_xl=-1;}   /* making sure dx and dy >0 */
  else    {        inc_xh=1;  inc_xl=1; }   /* adjusting increments */
  if(dy<0){dy=-dy; inc_yh=-1; inc_yl=-1;}
  else    {        inc_yh=1;  inc_yl=1; }

  if(dx>dy){long_d=dx;short_d=dy;inc_yl=0;} /* long range,&making sure either */
  else     {long_d=dy;short_d=dx;inc_xl=0;} /* x or y is changed in L case */

  d=2*short_d-long_d;                       /* initial value of d */
  add_dl=2*short_d;                         /* d adjustment for H case */
  add_dh=2*short_d-2*long_d;                /* d adjustment for L case */

  for(i=0;i<dimension;i++)                  /* for all remaining dimensions */
  {
   tmp=v1[i]; tmp<<=G_P; cur_v[i]=tmp;      /* f.p. start value */
   tmp=v2[i]-v1[i]; tmp<<=G_P;              /* f.p. increment */
   if(long_d>0) inc_v[i]=tmp/long_d;        /* if needed (non 0 lines) */
  }

  for(i=0;i<=long_d;i++)                    /* for all points in longer range */
  {
   if(x<G_start[y])                         /* further then rightmost */
   {
    G_start[y]=x;                           /* the begining of scan line */
    for(j=0;j<dimension;j++)
     G_rest_start[j][y]=cur_v[j];            /* all other dimensions */
   }

   if(G_end[y]<x)                           /* further the leftmost */
   {
    G_end[y]=x;                             /* the end of scan line */
    for(j=0;j<dimension;j++)
     G_rest_end[j][y]=cur_v[j];              /* and for other dimension */
   }

   if(d>=0){x+=inc_xh;y+=inc_yh;d+=add_dh;} /* previous point was H type */
   else    {x+=inc_xl;y+=inc_yl;d+=add_dl;} /* previous point was L type */
   for(j=0;j<dimension;j++) 
    cur_v[j]+=inc_v[j];                     /* for all other dimensions */
  }
 }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 *  INTERNAL: Initialization of polygon boundaries.      *
 *  ---------                                            *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void GI_boarder_array_init(void)
{
 G_miny=INT_MAX;                            /* polygon starts here */
 G_maxy=INT_MIN;                            /* polygon ends here */

 HW_set_int(G_start,HW_SCREEN_Y_SIZE,INT_MAX);
 HW_set_int(G_end,HW_SCREEN_Y_SIZE,INT_MIN);/* limiting values */
}

/**********************************************************\
 *  Rendering a polygon.                                  *
 *  Accepts 2-tuples (X,Y) per vertex.                    *
\**********************************************************/

void G_ambient_polygon(int *edges,int length,unsigned char colour)
{
 int new_edges[G_MAX_TUPLES];               /* verticaly clipped polygon */
 int new_length;                            /* although no edges there yet */
 register unsigned char *l_adr,*adr;        
 register int beg,end,i;

 GI_boarder_array_init();                   /* initializing the arrays */

 new_length=C_polygon_x_clipping(edges,new_edges,2,length);

 for(i=0;i<new_length;i++)
  GI_scan(&new_edges[i*2],2,2);             /* Searching polygon boarders */

 if(G_miny<=G_maxy)                         /* nothing to do? */
 {
  l_adr=G_buffer+G_miny*HW_SCREEN_X_SIZE;   /* addr of 1st byte of 1st line */

  for(; G_miny<=G_maxy; G_miny++,
      l_adr+=HW_SCREEN_X_SIZE)              /* rendering all lines */
  {
   adr=l_adr+(beg=G_start[G_miny]);         /* addr of the current line */
   end=G_end[G_miny]-beg+1;                 /* ends here */

   HW_set_char((char*)adr,end,colour);      /* rendering single line */
  }
 }
}

/**********************************************************\
 *  Rendering an Interpolatively shaded polygon.          *
 *  Accepts 3-tuples (X,Y,ColourIntensity) per vertex.    *
\**********************************************************/

void G_shaded_polygon(int *edges,int length)
{
 int new_edges[G_MAX_SHADED_TUPLES];        /* verticaly clipped polygon */
 int new_length;
 register unsigned char *l_adr,*adr;        
 register int beg,end,i;
 register signed_32_bit cur_c,inc_c;        /* current colour and it's inc */

 GI_boarder_array_init();                   /* initializing the array */

 new_length=C_polygon_x_clipping(edges,new_edges,3,length);

 for(i=0;i<new_length;i++)
  GI_scan(&new_edges[i*3],3,3);             /* Searching polygon boarders */

 if(G_miny<=G_maxy)                         /* nothing to do? */
 {
  l_adr=G_buffer+G_miny*HW_SCREEN_X_SIZE;   /* addr of 1st byte of 1st line */

  for(; G_miny<=G_maxy; G_miny++,
      l_adr+=HW_SCREEN_X_SIZE)              /* rendering all lines */
  {
   adr=l_adr+(beg=G_start[G_miny]);         /* addr of the current line */
   end=G_end[G_miny];                       /* ends here */

   cur_c=G_0_start[G_miny];                 /* colour starts with this value */
   inc_c=G_0_end[G_miny]-cur_c;
   if(end>beg) inc_c/=end-beg+1;            /* f.p. increment */

   for(;beg<=end;beg++)                     /* render this lines */
   {
    *adr++=cur_c>>G_P;                      /* rendering single point */
    cur_c+=inc_c;                           /* incrementing colour */
   }
  }
 }
}

/**********************************************************\
 *  Rendering a texture rendered polygon.                 *
 *  Accepts 4-tuples - (X,Y,TextureX,TextureY) per vertex.*
 *  The applied texture is a 8bit deep square bitmap with:*
 *                                                        *
 *              log_texture_size=log texture_size         *
 *                                  2                     *
\**********************************************************/

void G_textured_polygon(int *edges,int length,int *O,int *u,int *v,
			unsigned char *texture,int log_texture_size,
					       int log_texture_scale
		       )
{
 int new_edges[G_MAX_SHADED_TUPLES];        /* verticaly clipped polygon */
 int new_length;
 signed_32_bit Vx,Vy,Vz;
 signed_32_bit Ux,Uy,Uz;                    /* extracting vectors */
 signed_32_bit x0,y0,z0;
 signed_32_bit ui,uj,uc;
 signed_32_bit vi,vj,vc;
 signed_32_bit zi,zj,zc;                    /* back to texture coeficients */
 signed_32_bit v0,u0;
 signed_32_bit xx,yy,zz,zzz;                
 int xstart,xend;
 signed_32_bit txstart,tystart;
 signed_32_bit txend,tyend;
 signed_32_bit txinc,tyinc;
 register unsigned char *g_adr,*adr;        
 register int i,x,y;
 signed_32_bit txtrmasc=(0x1<<(log_texture_size+G_P))-0x1;
 log_texture_scale+=G_P;

 GI_boarder_array_init();                   /* initializing the array */

 new_length=C_polygon_x_clipping(edges,new_edges,4,length);

 for(i=0;i<new_length;i++)
  GI_scan(&new_edges[i*4],2,4);             /* Searching polygon boarders */

 if(G_miny<=G_maxy)                         /* nothing to do? */
 {
  x0=O[0]; y0=O[1]; z0=O[2]; 
  u0=O[3]<<G_P; v0=O[4]<<G_P;               /* world point <-> texture point */

  Vx=v[0]; Vy=v[1]; Vz=v[2];
  Ux=u[0]; Uy=u[1]; Uz=u[2];                /* extracting vectors */
 
  ui=(Vz*y0)-(Vy*z0);
  uj=(Vx*z0)-(Vz*x0);
  uc=(Vy*x0)-(Vx*y0);
  vi=(Uy*z0)-(Uz*y0);
  vj=(Uz*x0)-(Ux*z0);
  vc=(Ux*y0)-(Uy*x0);
  zi=(Vy*Uz)-(Vz*Uy);
  zj=(Vz*Ux)-(Vx*Uz);
  zc=(Vx*Uy)-(Vy*Ux);                       /* back to texture */

  g_adr=G_buffer+G_miny*HW_SCREEN_X_SIZE;   /* addr of 1st byte of 1st line */

  for(; G_miny<=G_maxy; G_miny++,
      g_adr+=HW_SCREEN_X_SIZE)              /* rendering all lines */
  {
   xstart=G_start[G_miny];
   adr=g_adr+xstart;
   xstart-=HW_SCREEN_X_CENTRE;
   x=xstart;
   xend=G_end[G_miny]-HW_SCREEN_X_CENTRE;   
   y=G_miny-HW_SCREEN_Y_CENTRE;

   xx=((y*uj)>>T_LOG_FOCUS)+uc;
   yy=((y*vj)>>T_LOG_FOCUS)+vc;
   zz=((y*zj)>>T_LOG_FOCUS)+zc;             /* valid for the hole run */

   if(( zzz=zz+((x*zi)>>T_LOG_FOCUS) )!=0) 
   { 
    txend=( (xx+ ((x*ui)>>T_LOG_FOCUS)) <<log_texture_scale )/zzz +u0;
    tyend=( (yy+ ((x*vi)>>T_LOG_FOCUS)) <<log_texture_scale )/zzz +v0; 
   }

   for(;xstart<xend;)
   {
    x+=G_LINEAR; if(x>xend) x=xend;         /* size of linear run */
    txstart=txend;
    tystart=tyend;

    if(( zzz=zz+((x*zi)>>T_LOG_FOCUS) )!=0) 
    { 
     txend=( (xx+ ((x*ui)>>T_LOG_FOCUS)) <<log_texture_scale )/zzz +u0;
     tyend=( (yy+ ((x*vi)>>T_LOG_FOCUS)) <<log_texture_scale )/zzz +v0; 
    }

    length=x-xstart;                        /* ends here */
    if(length!=0) { txinc=(txend-txstart)/length; 
		    tyinc=(tyend-tystart)/length;
		  }

    for(;xstart<x;xstart++)                 /* linear run */
    {
     txstart&=txtrmasc; tystart&=txtrmasc;  /* wrap around */
     *adr++=*(texture+((tystart>>G_P)<<log_texture_size)+(txstart>>G_P));
     txstart+=txinc; tystart+=tyinc;        /* new position in the texture */
    }
   }
  }
 }
}

/**********************************************************/
