/*
==============================================================================
		      WordUp Graphics Toolkit Version 5.0                     
			     Demonstration Program 42                         
									      
 Demonstrates the polygon routines available.  It rotates a triangle         
 in the middle of the screen using either a hollow, solid or Gouraud shaded  
 polygon.  Also introduced is the dirty rectangle method of updating the     
 screen.                                                                     
									      
 Once the first three polygon methods are shown, a textured mapped polygon   
 is shown.                                                                   
									      
 *** PROJECT ***                                                             
 This program requires the file WGT5_WC.LIB to be linked.                    
									      
 *** DATA FILES ***                                                          
 TEXTMAP.CEL must be in your executable directory.
							   WATCOM C++ VERSION 
==============================================================================
*/

#include <math.h>
#include <wgt5.h>

short num_sides;           /* Number of sides for the current polygon.
			    For the first loop this is 3, and the second 
			    loop it is 4. */

short polytype;            /* Current polygon type to draw */
short currot;              /* Current rotation of the triangle */

short isin[360];           /* Sin values 0-360,  multiplied by 1024 */
short icos[360];           /* Cos values 0-360,  multiplied by 1024 */

typedef struct {         /* Dirty rectangle structure */
  short x1;
  short y1;
  short x2;
  short y2;              /* Keeps track of which area on the screen */
} rect;                  /* has been changed */
rect lastrect;           /* Dirty rectangle for last frame */
rect thisrect;           /* Dirty rectangle for current frame */

tpolypoint plainpoly[4]; /* Holds polygon points before rotation */
tpolypoint rotatedpoly[4];  /* After rotation */

color pal[256];
short oldmode;
block other;             /* A background page for dirty rectangles */
block texturemap;        /* A texture bitmap (128x128) */
short square_size;         /* Size of the texture mapped square */
short size_dir;            /* 1 or -1, added to square_size */


void clear_last(void)
{
  /* Do clipping for smallest area update */
  if (lastrect.x1 < 0)
    lastrect.x1 = 0;
  if (lastrect.x2 > 319)
    lastrect.x2 = 319;
  if (lastrect.y1 < 0)
    lastrect.y1 = 0;
  if (lastrect.y2 > 199)
    lastrect.y2 = 199;
  wsetcolor (0);
  wbar (lastrect.x1, lastrect.y1, lastrect.x2, lastrect.y2);
  /* Clear out the area that was drawn in last frame */
}


void update_screen(void)
{
  if (lastrect.x1 > thisrect.x1)
    lastrect.x1 = thisrect.x1;
  if (lastrect.x2 < thisrect.x2)
    lastrect.x2 = thisrect.x2;
  if (lastrect.y1 > thisrect.y1)
    lastrect.y1 = thisrect.y1;
  if (lastrect.y2 < thisrect.y2)
    lastrect.y2 = thisrect.y2;
  /* See if the previous frame was larger in any direction. If it is, enlarge
     the area so it will copy black over the previous frame. */

  /* Do clipping */
  if (lastrect.x1 < 0)
    lastrect.x1 = 0;
  if (lastrect.x2 > 319)
    lastrect.x2 = 319;
  if (lastrect.y1 < 0)
    lastrect.y1 = 0;
  if (lastrect.y2 > 199)
    lastrect.y2 = 199;

  wcopyscreen(lastrect.x1, lastrect.y1, lastrect.x2, lastrect.y2, other,
	      lastrect.x1, lastrect.y1, NULL);
  /* Copy from our second page to the visual page. */

  lastrect.x1 = thisrect.x1;
  lastrect.y1 = thisrect.y1;
  lastrect.x2 = thisrect.x2;
  lastrect.y2 = thisrect.y2;
  /* Make the last rectangle = current rectangle */
}


void makesin_cos_tables (void)
/* Calculates the tables for our 2D rotation */
{
  short i;

  for (i = 0; i < 360; i++)
  {
    isin[i] = sin(3.1415 * ((double)i / 180.0)) * 1024;
    icos[i] = cos(3.1415 * ((double)i / 180.0)) * 1024;
  }
}


void rotatepoly (short cnx, short cny, tpolypoint *plain, tpolypoint *rotated, 
		 short numpoints, short rotation)
/* Rotates a set of polygon points, given the center of the polygon, number
   of points, and the rotation in degrees. The points to be rotated are in 
   "plain", and the new points are stored in "rotated". */
{
  short i;
  short x, y;
  short x2, y2;

  for (i = 0; i < numpoints; i++)
  {
    x = plain[i].x - cnx;
    y = plain[i].y - cny;

    x2 = ((int)x * icos[rotation] - (int)y * isin[rotation]) >> 10;
    y2 = ((int)x * isin[rotation] + (int)y * icos[rotation]) >> 10;
    rotated[i].x = x2 + cnx;
    rotated[i].y = y2 + cny;
    rotated[i].sx = plain[i].sx;
    rotated[i].sy = plain[i].sy;
  }
}


void make_triangle (void)
/* Set up some points to make a triangle */
{
  plainpoly[0].x = 160;
  plainpoly[0].y = 60;
  plainpoly[0].sx = 5;

  plainpoly[1].x = 120;
  plainpoly[1].y = 140;
  plainpoly[1].sx = 50;

  plainpoly[2].x = 200;
  plainpoly[2].y = 140;
  plainpoly[2].sx = 100;
}


void make_square (void)
/* Set up some points to make a square */
{
  plainpoly[0].x = 160 - square_size;
  plainpoly[0].y = 100 - square_size;
  plainpoly[0].sx = 0;   /* This sets the offset into the texture bitmap */
  plainpoly[0].sy = 0;

  plainpoly[1].x = 160 + square_size;
  plainpoly[1].y = 100 - square_size;
  plainpoly[1].sx = 127;
  plainpoly[1].sy = 0;

  plainpoly[2].x = 160 + square_size;
  plainpoly[2].y = 100 + square_size;
  plainpoly[2].sx = 127;
  plainpoly[2].sy = 127;

  plainpoly[3].x = 160 - square_size;
  plainpoly[3].y = 100 + square_size;
  plainpoly[3].sx = 0;
  plainpoly[3].sy = 127;
}


void make_palette (void)
/* Make a shaded palette for Gouraud shading */
{
  short i;

  for (i = 0; i < 64; i++)
  {
    wsetrgb (i, 0, i / 2, i, pal);
    wsetrgb (i + 64, i, (63 - i) / 2, (63 - i), pal);
  }
  wsetrgb (1, 63, 63, 63, pal);
  wsetpalette (0, 255, pal);
}


void find_edges (void)
/* Scans through all points in the polygon, and sees if the dirty rectangle
   area should be increased to include the point. */
{
  short i;

  for (i = 0; i < num_sides; i++)
  {
    if (rotatedpoly[i].x < thisrect.x1)
      thisrect.x1 = rotatedpoly[i].x;
    if (rotatedpoly[i].x > thisrect.x2)
      thisrect.x2 = rotatedpoly[i].x;
    if (rotatedpoly[i].y < thisrect.y1)
      thisrect.y1 = rotatedpoly[i].y;
    if (rotatedpoly[i].y > thisrect.y2)
      thisrect.y2 = rotatedpoly[i].y;
    /* See if the polygon is larger than the current area to update.
       If it is, enlarge the area so all polygons fit inside. */
  }
}


void main(void)
{
  oldmode = wgetmode ();

  printf ("WGT Example #42\n\n");
  printf ("Polygons and texture-mapping are demonstrated using some optimized dirty-\n");
  printf ("rectangle code. Press a key to advance to the next type of polygon.\n");
  printf ("\n\nPress any key to continue.\n");
  getch ();

  vga256();
  other = wnewblock (0, 0, 319, 199);
  winitpoly (WGT_SYS.yres);
  makesin_cos_tables ();
  make_palette ();
  make_triangle ();

  wsetscreen (other);
  lastrect.x1 = 0; 
  lastrect.y1 = 0; 
  lastrect.x2 = 319; 
  lastrect.y2 = 199;
  currot = 0;
  num_sides = 3;

  do {
    thisrect.x1 = 319; 
    thisrect.y1 = 199; 
    thisrect.x2 = 0; 
    thisrect.y2 = 0;
    clear_last ();
    rotatepoly (160, 100, plainpoly, rotatedpoly, num_sides, currot);

    find_edges ();

    wsetcolor (60);  
    switch (polytype)
    {
      case 0: whollowpoly (rotatedpoly, num_sides, 0, 0, CLOSED_POLY);
	      break;
      case 1: wsolidpoly (rotatedpoly, num_sides, 0, 0, NULL);
	      break;
      case 2: wgouraudpoly (rotatedpoly, num_sides, 0, 0);
	      break;
    } 
    update_screen ();

    currot++;             /* Rotate the polygon */
    if (currot > 359)
      currot -= 360;

    if (kbhit ())
    {
      getch ();
      polytype++;         /* Go to the next type of polygon */
    }
  } while (polytype < 3);

  /* Now do a texture mapped polygon */
  num_sides = 4;
  make_square ();
  wcls (0);
  lastrect.x1 = 0; 
  lastrect.y1 = 0; 
  lastrect.x2 = 319; 
  lastrect.y2 = 199;
  currot = 0;
  texturemap = wloadcel ("textmap.cel", pal);
  wsetpalette (0, 255, pal);
  square_size = 63;
  size_dir = -1;

  do {
    thisrect.x1 = 319; 
    thisrect.y1 = 199; 
    thisrect.x2 = 0; 
    thisrect.y2 = 0;

    square_size += size_dir;
    if (square_size < 30)         /* Make the polygon get bigger and smaller */
      size_dir = 1;
    if (square_size > 100)
      size_dir = -1;
    make_square ();

    clear_last ();
    rotatepoly (160, 100, plainpoly, rotatedpoly, num_sides, currot);
    find_edges ();

    wtexturedpoly (rotatedpoly, num_sides, 0, 0, texturemap, NORMAL);
    update_screen ();

    currot += 3 * size_dir;             /* Rotate the polygon */
    if (currot > 359)
      currot -= 360;
    if (currot < 0)
      currot += 360;
  } while (!kbhit ());
  wdeinitpoly ();
  wfreeblock (other);
  wfreeblock (texturemap);
  wsetmode (oldmode);
}
