#include <conio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <dos.h>
#include <wgt4.h>
#include "wgt3d.h"

/* Virtual Modeller version 0.5 Beta
Requires Turbo C++, WGT v4.0 to recompile.

This source requires the WordUp Graphics Toolkit version 4.0 to recompile.
This library will not be available until the first quarter of 1994.

*/


void saveobject(void);
void loadobject(void);
void displaytitle(void);

point3d mypoints[400];   /* Stores 400 initial 3D points */
point3d finp[400];       /* Stores 400 rotated 3D points */

int ROTATIONS=8;	 /* Default number of rotations */

int i; 			 /* Looping */
int curx=0,cury=10,curz=170; /* Rotation amount in each axis */

block other;		/* Temporary screen */
color pal[256];		/* A VGA palette */

int d,e;		/* Misc/Looping */
int temp;

int xdir=1,ydir=1,xpos=0,ypos=0; /* Offset of object on screen (not used) */

tpolypoint mypoly[4];    /* Max polygon has 4 vertices */

int sortpoly[200];	 /* Sorted list for 300 polygons */
int ztotal[200];	 /* Average Z coord of 4 vertices for each polygon */
			 /* Polygons are sorted by this number */
int maxpoly=200;

int px[100],py[100];	 /* Stores coordinates of 100 points that you click
			    on. */

int faces[200][4];	 /* Stores 200 polygon faces */

int oldmode;		 /* Mode before program was run */

int textx[4],texty[4];   /* Offsets into our texture bitmap */
block ourtexture; 	 /* A texture bitmap */
int tmode;		 /* Texture mode, either normal or xray */

typedef struct {	 /* Used to keep track what portion of the */
  int x1,y1,x2,y2;	 /* screen has been changed, so we can update */
  } rect;		 /* the least amount of video memory */

rect lastrect,thisrect;

char c,polytype;


/* Saves a custom 3D object file format */
/* Not used in this program yet */
void saveobject(void)
{
int i;
FILE *objfile;

objfile=fopen("object.3d","wt");

fprintf(objfile,"WGT 3D Object File\n");

/* Number of points in this object */
fprintf(objfile,"%i\n",maxpoly);

/* Write the points */
for (i=0; i<maxpoly; i++)
 fprintf(objfile,"%i %i %i\n",mypoints[i].x,mypoints[i].y,mypoints[i].z);

/* Number of faces in this object */
fprintf(objfile,"\n%i\n",maxpoly-ROTATIONS);
for (i=0; i<maxpoly-ROTATIONS; i++)
    {
    for (e=0; e<4; e++)
       fprintf(objfile,"%i ",faces[i][e]);
     fprintf(objfile,"\n");
    }
fclose(objfile);
}

/* Loads a 3D object back into memory */
/* Not used in this program yet */
void loadobject(void)
{
int i;
FILE *objfile;
char blank[80];

objfile=fopen("object.3d","rt");

fscanf(objfile,"%s %s %s %s\n",blank,blank,blank,blank);

/* Number of points in this object */
fscanf(objfile,"%i\n",&maxpoly);


/* Write the points */
for (i=0; i<maxpoly; i++)
 fscanf(objfile,"%i %i %i\n",&mypoints[i].x,&mypoints[i].y,&mypoints[i].z);

/* Number of faces in this object */
fscanf(objfile,"\n%i\n",&maxpoly);
for (i=0; i<maxpoly; i++)
    {
    for (e=0; e<4; e++)
       fscanf(objfile,"%i ",&faces[i][e]);
     fscanf(objfile,"\n");
    }
fclose(objfile);
}



void main(void)
{
oldmode=wgetmode();
if (!vgadetected)
   {
   printf("VGA card not found. Aborting program!");
   exit(1);
   }
displaytitle();

printf("Enter the number of rotations: ");
scanf("%i",&ROTATIONS);
do {
printf("\n\nChoose the type of polygons rendered:\n");
printf("(W)ireframe\n");
printf("(S)olid\n");
printf("(G)ouraud Shaded\n");
printf("(T)exture Mapped\n");
c=toupper(getch());
if ((c !='W') & (c !='S') * (c !='G') & (c !='T'))
   printf("Invalid selection...Try again.\n");
} while ((c !='W') & (c !='S') * (c !='G') & (c !='T'));
polytype=c;

if (polytype=='T')
  {
  do {
  printf("\nShould color 0 be see-through? (Y/N) ");
  c=toupper(getch());
  } while ((c !='Y') & (c !='N'));
  if (c=='Y') tmode=1; else tmode=0;

  }
vga256();  /* Initialize VGA graphics mode */

if (polytype=='T')
{
ourtexture=wloadpcx256("texture.pcx",pal);
/* Load our texture bitmap */

/* Set up our texture source points */
textx[0]=0;
textx[1]=wgetblockwidth(ourtexture)-2;
textx[2]=wgetblockwidth(ourtexture)-2;
textx[3]=0;
texty[0]=0;
texty[1]=0;
texty[2]=wgetblockheight(ourtexture)-2;
texty[3]=wgetblockheight(ourtexture)-2;
}
else
  wloadpalette("vm.pal",pal);
winit3d();

other=wnewblock(0,0,319,199); /* Get a second virtual screen */

minit();   		      /* Initialize mouse */

do {
wsetrgb(0,0,0,0,pal);
wsetrgb(15,63,63,63,pal); /* Set color 15 to white so you can see the mouse */
wsetpalette(0,255,pal);

startover:
;
wnormscreen();
wcls(0);
mon();

maxpoly=0;

wsetcolor(15);
do {    			/* Object design loop */
  if (but==1)
    {
    moff();
    px[maxpoly]=mx;
    py[maxpoly]=my;
    if (maxpoly !=0)
      wline(px[maxpoly],py[maxpoly],px[maxpoly-1],py[maxpoly-1]);
    maxpoly++;
    noclick();
    mon();
    }
  } while (but !=2);


/* Create a 3D list of points from the 2D ones you clicked on. */
for (i=0; i<maxpoly; i++)
  for (d=0; d<ROTATIONS; d++)
    {
    if (d+i*ROTATIONS>400) {
	wtextcolor(255);
	wgtprintf(0,0,NULL,"Object too large!");
	delay(1000);
	goto startover;
	}
    mypoints[d+i*ROTATIONS].x=(icos[d*(360/ROTATIONS)]*px[i])>>14;
    mypoints[d+i*ROTATIONS].y=(isin[d*(360/ROTATIONS)]*px[i])>>14;
    mypoints[d+i*ROTATIONS].z=py[i]-100;
    }

maxpoly*=ROTATIONS;

    if (maxpoly>200) {
	wtextcolor(255);
	wgtprintf(0,0,NULL,"Object too large!");
	delay(1000);
	goto startover;
	}

/* Generate the list of polygons.
   Each polygon is made up of 4 3D points. */
d=0;
for (i=0; i<maxpoly-ROTATIONS; i++)
   {
    faces[i][0]=i;
     faces[i][1]=i+ROTATIONS;
    if (i % ROTATIONS+1==ROTATIONS)
     {
     faces[i][3]=(i-ROTATIONS+1);
     faces[i][2]=(i-ROTATIONS+1)+ROTATIONS;
    }
    else
     {
     faces[i][3]=i+1;
     faces[i][2]=i+ROTATIONS+1;
     }
    }

maxpoly-=ROTATIONS;

xv=0;
yv=0;
zv=0;

randomize();
wsetscreen(other);
wtextcolor(24);
moff();


lastrect.x1=0; lastrect.y1=0; lastrect.x2=319; lastrect.y2=199;

wsetrgb(15,0,0,0,pal); /* Restore color 15 to black */
wsetpalette(0,255,pal);

do {
  thisrect.x1=319; thisrect.y1=199; thisrect.x2=0; thisrect.y2=0;

/* 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 */

  wsetrotation(curx,cury,curz);
  wrotatepoints(mypoints,finp,maxpoly*4);
  /* Rotate our 3D points and store the rotated values into finp */

  /* Calculate a unique z value for each polygon, to be used for sorting */
  for (d=0; d<maxpoly; d++)
    {
    sortpoly[d]=d;
    ztotal[d]=0;
    for (e=0; e<4; e++)
      ztotal[d]+=finp[faces[d][e]].z;
    ztotal[d]/=4;
    }


  /* Do a slow bubble sort */
  for (d=0; d<maxpoly-1; d++)
   for (e=d+1; e<maxpoly; e++)
     {
      if (ztotal[sortpoly[e]]>ztotal[sortpoly[d]])
	     {
	     temp=sortpoly[e];
	     sortpoly[e]=sortpoly[d];
	     sortpoly[d]=temp;
	     }
      }


  /* Now go through the list of polygons and draw each in order */
  for (d=0; d<maxpoly; d++)
    {
    temp=sortpoly[d];

    /* Set up each vertex in the polygon */
    for (e=0; e<4; e++)
      {
      mypoly[e].x=finp[faces[temp][e]].x;
      mypoly[e].y=finp[faces[temp][e]].y;

      if (polytype=='T')
      /* Textured polygons also have an (x,y) offset into the texture bitmap*/
      {
       mypoly[e].sx=textx[e];
       mypoly[e].sy=texty[e];
      }
      else
      /* Store the intensity (0-255) in sx variable (used for colour) */
      {
       mypoly[e].sx=255-(finp[faces[temp][e]].z+80);
       if (mypoly[e].sx<2) mypoly[e].sx=2;
       if (mypoly[e].sx>250) mypoly[e].sx=250;

      }

      if (mypoly[e].x<thisrect.x1)
	 thisrect.x1=mypoly[e].x;
      if (mypoly[e].x>thisrect.x2)
	 thisrect.x2=mypoly[e].x;

      if (mypoly[e].y<thisrect.y1)
	 thisrect.y1=mypoly[e].y;
      if (mypoly[e].y>thisrect.y2)
	 thisrect.y2=mypoly[e].y;
      /* See if the polygon is larger than the current area to update.
	 If it is, enlarge the area so all polygons fit inside. */

      }

      /* Draw our polygons using WGT's commands */
      if (polytype=='T')
	 wtexturedpoly(mypoly,3,xpos,ypos,ourtexture,tmode);
      else if (polytype=='G')
	 wgouraudpoly(mypoly,3,xpos,ypos);
      else if (polytype=='S')
	 {
	 /* Get an average colour to use depending on the vertices */
	 wsetcolor((mypoly[0].sx+mypoly[1].sx+mypoly[2].sx+mypoly[3].sx)/4);
	 wsolidpoly(mypoly,3,xpos,ypos);
	 }
      else if (polytype=='W')
	 {
	 wsetcolor((mypoly[0].sx+mypoly[1].sx+mypoly[2].sx+mypoly[3].sx)/4);
	 whollowpoly(mypoly,3,xpos,ypos);
	 }
     }


  /* Adds a small amount to the rotation in each axis, and checks for
     the wrap around 360 degrees. */
  curx+=5;
  if (curx>359) curx-=360;
  cury+=4;
  if (cury>359) cury-=360;
  curz+=3;
  if (curz>359) curz-=360;

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 */

 } while (!kbhit());
} while (getch() !='q');
wfreeblock(ourtexture);
wsetmode(oldmode);
}


void displaytitle(void)
{
clrscr();
printf("                 л   л  ллллл   ллл   ллллл  л   л  ллллл  л     \n");
printf(" 	         л   л    л     л  л    л    л   л  л   л  л     \n");
printf("	         л   л    л     ллл     л    л   л  ллллл  л     \n");
printf("         	  л л     л     л  л    л    л   л  л   л  л     \n");
printf("	           л    лллллл  л  л    л    ллллл  л   л  ллллл \n");
printf("\n");
printf("                  л   л   лл   ллл   ллл  л    л    ллл  ллл    \n");
printf(" 	          лл лл  л  л  л  л  л    л    л    л    л  л   \n");
printf("  	          л л л  л  л  л  л  лл   л    л    лл   ллл    \n");
printf("  	          л   л  л  л  л  л  л    л    л    л    л  л   \n");
printf("	          л   л   лл   ллл   ллл  ллл  ллл  ллл  л  л   \n");
printf("\n");
printf("                     Public Domain - Written by Chris Egerter\n");
printf("                           Press any key to continue \n");

getch();
while (kbhit()) getch();
}

