#include <dos.h>
#include <math.h>
#include <malloc.h>
#include <wgt5.h>
#include <wgtvesa.h>
#include "wrend.h"

#define PHONG 8
#define PHONG_TEXTURE 9
#define TRANSLUCENT_PHONG 10

int rendermethod;
int motion_blur;
float percent_foreground;
float percent_background;
float motion_blur_factor;
block virt;                        /* Drawing buffer */
color pal[256];                    /* A palette */

/* For shaded texture mapping */
#define NUMSHADES 64               /* Number of shades in the shade table */
block shadetable;                  /* allocated dynamically, has a size
				      of (NUMSHADES+1) * 256 bytes */

block translucent_table;           /* allocated dynamically, has a size
				      of 256 * 256 bytes */

float scale = 1;                /* Scale used for mapping a texture onto
				      a plane */

float movectr = 5;                 /* Movement speed for camera, rotation... */

#define POINTBUF 10000
vertex vertices[POINTBUF];         /* Vertex list for world */

short vmodes[6] = { V640x350, V640x400, V640x480, V800x600, V1024x768 };


void set_initial_palette (void)
{
int i, c, r, g, b;
double u;

// Phong material properties
#define SHINE   80.0
#define SPEC    0.70
#define PI      3.141516
#define R0      63
#define G0      18
#define B0      15


/* Phong Palette */
if ((rendermethod == PHONG) || (rendermethod == TRANSLUCENT_PHONG))
 {   
   for (i = 0; i < 256; i++) 
     {
      u = SPEC*63.0*exp(SHINE*log(sin(0.5*PI*i/256.0)+0.001));
      if ((r = (i*R0/256) + u) > 63) r = 63;
      if ((g = (i*G0/256) + u) > 63) g = 63;
      if ((b = (i*B0/256) + u) > 63) b = 63;
      
      wsetrgb (i, r, g, b, pal);
     }

  }
else
  {
   /* Set up some initial color ranges */
   for (i = 0; i < 64; i++)
    {
     pal[i].r = i;
     pal[i].g = i;
     pal[i].b = 0;
    }
   for (i = 64; i < 128; i++)
    {
     pal[i].r = i - 64;
     pal[i].g = 0;
     pal[i].b = 0;
    }
  
   for (i = 128; i < 192; i++)
    {
     pal[i].r = i - 128;
     pal[i].g = 0;
     pal[i].b = i - 128;
    }
   for (i = 192; i < 256; i++)
    {
     pal[i].r = 0;
     pal[i].g = 0;
     pal[i].b = i - 192;
    }
  }
  
  wsetrgb (0, 0, 0, 0, pal);
  wsetpalette (0, 255, pal);
}


void save_table (char *filename, block table, long size)
{
FILE *out;
int i;

 out = fopen (filename, "wb");
 fwrite (table, size, 1, out);
 fclose (out);
}


block load_table (char *filename, long size)
{
FILE *in;
int i;
block table;

 in = fopen (filename, "rb");
 if (in != NULL)
  {
   table = (unsigned char *)malloc (size);
   fread (table, size, 1, in);
   fclose (in);
   return table;
  }
 else
  return NULL;
}




void load_ptext (int num)
{
FILE *in;
int i;

in = fopen ("face.txt", "rb");

 fread (ptext, sizeof(polytexture), num, in);   
 fclose (in);
}


void save_ptext (int num)
{
FILE *out;
int i;

 out = fopen ("face.txt", "wb");
 fwrite (ptext, sizeof (polytexture), num, out);
 fclose (out);
}






/* Shade Table Calculations */
void wcreateshade (short shade, short maxshade, unsigned char *shadetable,
		   color *palette)
/* Calculate a single palette (256) colors for the given shade */
{
float lightlevel;
float fr, fg, fb;
long ir, ig, ib;

long absr, absg, absb;


double u;
int r,g,b;

short col;
short findcol;

unsigned long lowest;
unsigned char bestfit;
unsigned long coldif;


 lightlevel = (float)shade / maxshade;
 lightlevel += 0.2;
 if (lightlevel > 1)
     lightlevel = 1;

 for (col = 0; col < 256; col++)
  {
   fr = (float)palette[col].r * (lightlevel);
   fg = (float)palette[col].g * (lightlevel);
   fb = (float)palette[col].b * (lightlevel);

   /* Phong shade table */
   if (rendermethod == PHONG_TEXTURE)
     {   
      u = SPEC*63.0*exp(SHINE*log(sin(0.5*PI*shade/maxshade)+0.001));
      if ((fr = (fr + u)) > 63) fr = 63;
      if ((fg = (fg + u)) > 63) fg = 63;
      if ((fb = (fb + u)) > 63) fb = 63;
     } 
     
   ir = fr;
   ig = fg;
   ib = fb;

   lowest = 655350;
   for  (findcol = 0; findcol < 256; findcol++)
    {
      absr = abs ( (long)palette[findcol].r - ir);
      absg = abs ( (long)palette[findcol].g - ig);
      absb = abs ( (long)palette[findcol].b - ib);

      //coldif = absr*absr*30 + absg*absg*59 + absb*absb*11;
      coldif = absr*absr + absg*absg + absb*absb;
      coldif = sqrt (coldif);
      if  (coldif < lowest)
      {
	lowest = coldif;
	bestfit = findcol;
      }
    }
   shadetable[shade*256 + col] = bestfit;
  }

}


block wcreate_shade_table (short numshades, color *palette)
/* Create a shade table for shaded texture mapping, with darker colors
   towards black. */
{
short i;
block table;

 table = (unsigned char *)malloc ( (NUMSHADES+1)*256L);
   
 wtextcolor (128);
 wtexttransparent (TEXTFGBG);
 wgtprintf (0, 0, NULL, "Making shade table...");

 wnormscreen ();
 for (i = 0; i <= numshades; i++)
  { 
   wgtprintf (0, 8, NULL, "Color %03hi", i);
   wcreateshade (i, numshades, table, palette);
  }

 return table;
}




block wcreate_translucent_table (color *pal)
/* Creates a translucent table */
{
float lightlevel1;
float lightlevel2;
float fr, fg, fb;
float fr2, fg2, fb2;
long ir, ig, ib;

long absr, absg, absb;

short col, col2;
short findcol;

unsigned long lowest;
unsigned char bestfit;
unsigned long coldif;
unsigned char *table;

 table = (unsigned char *)malloc (65536);

 lightlevel1 = percent_foreground;   /* Percent of color 1 (foreground) */
 lightlevel2 = percent_background;   /* Percent of color 2 (background) */

 /* Lightlevel1 and lightlevel2 must total to 1 */

 /* Translucency is created by taking two colors, multiplying the
 RGB values by a percentage, and adding the RGB values together.  The
 new color will contain a little bit of each oringal color. */


 /* For each of the 256 colors, we can mix with any other color, therefore
 we need a 256x256 table. */

 wtextcolor (128);
 wtexttransparent (TEXTFGBG);
 wgtprintf (0, 0, NULL, "Making translucency table... ");

 for (col2 = 0; col2 < 256; col2++)
 {
  for (col = 0; col < 256; col++)
  {
   fr = (float)pal[col].r * lightlevel1;
   fg = (float)pal[col].g * lightlevel1;
   fb = (float)pal[col].b * lightlevel1;

   fr2= (float)pal[col2].r * lightlevel2;
   fg2= (float)pal[col2].g * lightlevel2;
   fb2= (float)pal[col2].b * lightlevel2;

   ir = (fr + fr2);
   ig = (fg + fg2);
   ib = (fb + fb2);

   lowest = 655350;
   for  (findcol = 0; findcol < 256; findcol++)
    {
      absr = abs ( (long)pal[findcol].r - ir) * 30;
      absg = abs ( (long)pal[findcol].g - ig) * 59;
      absb = abs ( (long)pal[findcol].b - ib) * 11;

 //     coldif = sqrt(absr*absr + absg*absg + absb*absb);
     coldif = absr*absr + absg*absg + absb*absb;      
      if  (coldif < lowest)
      {
	lowest = coldif;
	bestfit = findcol;
      }
    }
   table[col2 * 256L + col] = bestfit;
  }

  wgtprintf (0, 8, NULL, "Color %03hi", col2);
 }

 return table;
}


long move_counter;
long frame_counter;

void movement_timer (void)
{
 frame_counter++;
}


void wshadesource (block shadetable, block source, int length);
#pragma aux wshadesource = \
 "push ebp" \
 "push ecx" \
 "cld" \
 "mov ebp, edx" \
 "mov ebx, 0" \
 "mov edi, esi" \
 "shr ecx, 1" \
 "cmp ecx, 0" \
 "je oneshadepixel"\
 "shadeloop: mov bl, [esi]" \
 "mov al, [ebp + ebx]" \
 "inc esi" \
 "mov bl, [esi]" \
 "mov ah, [ebp + ebx]" \
 "mov [edi], ax" \
 "add edi, 2" \
 "inc esi" \
 "dec ecx" \
 "jnz shadeloop" \
 "oneshadepixel: pop ecx" \
 "and ecx, 1" \
 "jz slinedone" \
 "mov bl, [esi]" \
 "mov al, [ebp + ebx]" \
 "mov [edi], al" \
 "slinedone: pop ebp" \
parm [edx] [esi] [ecx] \
modify exact [eax ebx ecx edx esi edi] nomemory;


unsigned char shadesourcetable[256];

void shade_screen (void)
{
 wshadesource (shadesourcetable, virt+4, 64000L);
}


void wcreate_shadesource_table (color *palette)
{
float fr, fg, fb;
long ir, ig, ib;

long absr, absg, absb;


int r,g,b;

short col;
short findcol;

unsigned long lowest;
unsigned char bestfit;
unsigned long coldif;

 for (col = 0; col < 256; col++)
  {

   fr = (float)palette[col].r * (motion_blur_factor);
   fg = (float)palette[col].g * (motion_blur_factor);
   fb = (float)palette[col].b * (motion_blur_factor);

   ir = fr;
   ig = fg;
   ib = fb;

   lowest = 655350;
   for  (findcol = 0; findcol < 256; findcol++)
    {
      absr = abs ( (long)palette[findcol].r - ir);
      absg = abs ( (long)palette[findcol].g - ig);
      absb = abs ( (long)palette[findcol].b - ib);

      coldif = absr + absg + absb;
      if  ((coldif < lowest) && (findcol != col))
      {
	lowest = coldif;
	bestfit = findcol;
      }
    }
   shadesourcetable[col] = bestfit;
  }

}





void main(int argc, char *argv[])
{
short oldmode;
int drawnum;

short i;

int vx, vy;
int mx, my;

float rx, ry, rz;
float trx, try, trz;

  oldmode = wgetmode();

  if (argc == 1)
  {
    printf ("\n3D_CAM filename.3ds [vmode]\n\nWhere vmode is one of:\n");
    printf ("\n1 VESA  640x350x256");
    printf ("\n2 VESA  640x400x256");
    printf ("\n3 VESA  640x480x256");
    printf ("\n4 VESA  800x600x256");
    printf ("\n5 VESA 1024x768x256\n");
    exit (0);
  }

  printf ("Enter the rendering method: \n");
  printf ("0 = Wireframe\n");
  printf ("1 = Solid\n");
  printf ("2 = Gouraud Shaded\n");
  printf ("3 = Texture Mapped\n");
  printf ("4 = Flat Shaded Texture Mapped\n");
  printf ("5 = Gouraud Shaded Texture Mapped\n");
  printf ("6 = Translucent Texture Mapped\n");
  printf ("7 = Translucent Gouraud Shaded\n");
  printf ("8 = Gouraud with Phong Palette\n");
  printf ("9 = Gouraud Texture with Phong Palette\n");
  printf ("10 = Translucent Gouraud with Phong Palette\n");
  
  scanf ("%d", &rendermethod);
  if ((rendermethod < 0) || (rendermethod > 10))
    exit (0);
  
  printf ("Motion Blur? 0 = NO, 1 = YES\n");
  scanf ("%d", &motion_blur);
  if ((motion_blur < 0) || (motion_blur > 1))
    exit (0);
  
  if (motion_blur)
   {
    printf ("Fade Factor: (percentage of previous image)\n");
    scanf ("%f", &motion_blur_factor);
   }

  load_3ds (argv[1], vertices);
  
  for (i = 0; i < totalobjects; i++)
   {
    wset_object_color (i, 0);
    
    if (rendermethod == PHONG)
      wset_object_type (i, GOURAUD);
    else if (rendermethod == TRANSLUCENT_PHONG)
      wset_object_type (i, TRANSLUCENT_GOURAUD);
    else if (rendermethod == PHONG_TEXTURE)
      wset_object_type (i, GOURAUD_SHADED_TEXTURE);
    else 
      wset_object_type (i, rendermethod);
   }

  printf ("\n\n\n\n\n\nTotal of: %8d points\n", worldpoints);
  printf ("Total of: %8d faces\n", totalpoly);
  printf ("Total of: %8d objects\n", totalobjects);
  printf ("Start obj 1 = %d\n", objectlist[0].start_poly);
  printf ("End obj 1   = %d\n", objectlist[0].end_poly);

  printf ("Memory free: %8d\n\n\n", getmemfree() );
  printf ("Keyboard Controls:\n");  
  printf ("LEFT & RIGHT                 Dec/Inc X Camera position\n");
  printf ("  UP & DOWN                  Dec/Inc Y Camera position\n");
  printf ("PGUP & PGDN                  Dec/Inc Z Camera position\n");
  printf ("PLUS & MINUS                 Inc/Dec Camera movement scale\n");
  printf ("CTRL & one of the above      Rotate object\n");
  printf ("   I & O                     Inc/Dec Texture Scale\n");
  printf ("                        ESC           Quit\n");
  getch ();
  
  if ((rendermethod == TRANSLUCENT_GOURAUD) ||
      (rendermethod == TRANSLUCENT_PHONG) ||
      (rendermethod == TRANSLUCENT_TEXTURE))
     {
      translucent_table = load_table ("trans.dat", 65536L);
      if (translucent_table == NULL)
	{
	 printf ("Translucent Factors\n");
	 printf ("Percentage of foreground:\n");
	 scanf ("%f", &percent_foreground);
	 printf ("Percentage of background:\n");
	 scanf ("%f", &percent_background);
	}
     }
  

  vga256 ();
  
  if ((argc == 3) && (atoi (argv[2]) < 6) && (atoi (argv[2]) > 0))
    wvesa_init (vmodes[atoi (argv[2]) - 1] );
  /* Enter SVGA mode */
  
  render_all = 0;       /* Perform backface removal */
  set_initial_palette ();  
  wsetpalette (0, 255, pal);


  if ((rendermethod == FLAT_SHADED_TEXTURE) ||
      (rendermethod == TEXTURE) ||      
      (rendermethod == GOURAUD_SHADED_TEXTURE) ||
      (rendermethod == TRANSLUCENT_TEXTURE) ||
      (rendermethod == PHONG_TEXTURE))
  /* Using some kind of texture mapping */
   {
    textures[0] = wloadpcx ("texture.pcx", pal);
    wsetpalette (0, 255, pal);
    /* Load in the texture and set the palette */


    /* Make any shade tables needed */
    if ((rendermethod == FLAT_SHADED_TEXTURE) ||
	(rendermethod == GOURAUD_SHADED_TEXTURE))
      { 
       shadetable = load_table ("shade.dat", (NUMSHADES + 1) * 256L);
       if (shadetable == NULL)
	 {
	  shadetable = wcreate_shade_table (NUMSHADES, pal);
	  save_table ("shade.dat", shadetable, (NUMSHADES + 1) * 256L);
	 }
       render_shadetable = shadetable;
      }
    else if (rendermethod == PHONG_TEXTURE)
      { 
       shadetable = load_table ("phong.dat", (NUMSHADES + 1) * 256L);
       if (shadetable == NULL)
	 {
	  shadetable = wcreate_shade_table (NUMSHADES, pal);
	  save_table ("phong.dat", shadetable, (NUMSHADES + 1) * 256L);
	 }
       render_shadetable = shadetable;
      }
    }

  if ((rendermethod == TRANSLUCENT_GOURAUD) ||
      (rendermethod == TRANSLUCENT_PHONG) ||
      (rendermethod == TRANSLUCENT_TEXTURE))
     {
      if (translucent_table == NULL)
	{
	 translucent_table = wcreate_translucent_table (pal);
	 save_table ("trans.dat", translucent_table, 65536L);
	}

      render_all = 1;
      /* Render everything, since we will be able to see through the object */
     
      render_shadetable = translucent_table;
      
     }

  

  wclip (0, 0, WGT_SYS.xres - 1, WGT_SYS.yres - 1);
  
  winit_triangle_renderer (WGT_SYS.yres);

  virt = wnewblock (0, 0, WGT_SYS.xres - 1, WGT_SYS.yres - 1);
  render_shades = 64;
  
  if (rendermethod == GOURAUD)
   for (i = 0; i < totalobjects; i++)
      wset_object_color (i, 0);
   

  if ((rendermethod == PHONG) || (rendermethod == TRANSLUCENT_PHONG))
   { 
    for (i = 0; i < totalobjects; i++)
      wset_object_color (i, 0);
    render_shades = 256;
   }
  
  if (rendermethod == WIREFRAME) 
  { 
   for (i = 0; i < totalobjects; i++)
      wset_object_color (i, 63);
   render_all = 1;
  } 


 if (motion_blur)
  wcreate_shadesource_table (pal);


 wsetscreen (virt);

 installkbd ();

 winittimer();
 wstarttimer (movement_timer, TICKS(64));

 for (i = 0; i < totalobjects; i++)
    map_points (i, vertices, scale);
  
  /* Main loop */
  do
   {
     drawnum = 0;
     
     wset_light (camera_x, camera_y, camera_z);
     wset_camera (camera_x, camera_y, camera_z);
     wset_focus (focus_x, focus_y, focus_z);
     wset_view ();

     for (i = 0; i < totalobjects; i++)
       wworld_2_view (&objectlist[i], &drawnum, vertices);

     wsetscreen (virt);
     
     if (!motion_blur)
      wcls (0);
     else
       shade_screen ();
     
     draw_polys (vertices, drawnum);

     wtextcolor (255);   
     wgtprintf (0, 0, NULL, "Polys: %d", drawnum);

     if (kbdon[0x3B]) /* F1 Load */
	load_ptext (totalpoly);

     if (kbdon[0x3D]) /* F3 Save */
	save_ptext (totalpoly);

     if (kbdon[29]) /* CTRL */
       {
	rx = ry = rz = 0;

	if (kbdon[72]) // up
	  ry -= movectr;
	if (kbdon[80]) // down
	  ry += movectr;
	if (kbdon[0x4b]) // left
	  rx -= movectr;
	if (kbdon[0x4d]) // right
	  rx += movectr;
	if (kbdon[73]) // PGUP
	  rz -= movectr;
	if (kbdon[81]) // PGDN
	  rz += movectr;

	if ((rx != 0) || (ry != 0) || (rz != 0))
          {      
            for (i = 0; i < totalobjects; i++)
             rotate_object (&objectlist[i], vertices, rx, ry, rz, 0, 0, 0);
          }
       }
     else
       {
	if (kbdon[72]) /* up */
	    camera_y -= movectr;
	if (kbdon[80]) /* down */
	    camera_y += movectr;
	if (kbdon[0x4b]) /* left */
	   camera_x -= movectr;
	if (kbdon[0x4d]) /* right */
	   camera_x += movectr;
	if (kbdon[73]) /* PGUP */
	   camera_z -= movectr;
	if (kbdon[81]) /* PGDN */
	   camera_z += movectr;
       }

    wtextcolor (255);
    wtexttransparent (TEXTFGBG);
    if (kbdon[78]) /* PLUS key */
     {
	movectr = movectr + 0.3;
	wgtprintf (0, 0, NULL, "New movement amount for camera: %7.1f", movectr);
     }

    if (kbdon[74]) /* MINUS key */
     {
	movectr = movectr - 0.3;
	wgtprintf (0, 0, NULL, "New movement amount for camera: %7.1f", movectr);
     }

    if (kbdon[23]) /* I key */
     {
	scale += 0.1;
        for (i = 0; i < totalobjects; i++)
           map_points (i, vertices, scale);
     }

    if (kbdon[24]) /* O key */
     {
	scale -= 0.1;
        for (i = 0; i < totalobjects; i++)
           map_points (i, vertices, scale);
     }

    

    if (movectr < 0.3)
      movectr = 0.3;

    wnormscreen ();
    if (WGT_SYS.bankswitch != NULL)
      wvesa_putblock (0, 0, virt, NORMAL);
    else memcpy (abuf, virt+4, 64000);
    //wputblock (0, 0, virt, NORMAL);
  
  } while (!kbdon[1]);  /* Until ESC is hit */
 
  wstoptimer ();
  wdonetimer ();
  
  wsetmode (oldmode);

  uninstallkbd ();

  wdeinit_triangle_renderer ();

  wfreeblock (virt);

  if ((rendermethod == FLAT_SHADED_TEXTURE) ||
      (rendermethod == GOURAUD_SHADED_TEXTURE) ||
      (rendermethod == PHONG_TEXTURE))
    free (shadetable);
  if ((rendermethod >= TEXTURE) && (rendermethod <= TRANSLUCENT_TEXTURE))
    wfreeblock (textures[0]);
  if ((rendermethod == TRANSLUCENT_TEXTURE) || 
      (rendermethod == TRANSLUCENT_GOURAUD) ||
      (rendermethod == TRANSLUCENT_PHONG))
    free (translucent_table);
}



