/*
   Einfaches Programm zur Darstellung von Vektorgraphik mit Goraudshading
   (C) 1995 by Marko Kleine Berkenbusch
*/

?use80386
?parsecommandline TRUE
?resize TRUE

?include "VIDEO.H--"
?include "DOS.H--"
?include "FILE.H--"
?include "STRING.H--"
?include "VGA.H--"
?include "MATH.H--"
?include "TRIG.H--"
?include "KEYCODES.H--"
?include "WRITE.H--"

?define maxAnzPolys  400
?define maxAnzPunkte 500
?define HScale	     300
?define	VScale      -220
?define XStart	       0
?define XEnd	     319
?define YStart	       0
?define YEnd	     199

long BPos_x,BPos_y,BPos_z;
long Punkte_x[maxAnzPunkte],Punkte_y[maxAnzPunkte],Punkte_z[maxAnzPunkte],
     GPunkte_x[maxAnzPunkte],GPunkte_y[maxAnzPunkte],GPunkte_z[maxAnzPunkte];
word Polygone_p1[maxAnzPolys],Polygone_p2[maxAnzPolys],
     Polygone_p3[maxAnzPolys],Polygone_p4[maxAnzPolys];
word AnzPolys,AnzPunkte;
word VMem;
int xW,yW,zW;

int readint(word f)
byte b,buffer[7];
{
	FOR(;;){
		read(f,#b,1);
		IF(b=='-')||(b>='0')&&(b<='9')BREAK;
	}
	buffer[0]=b;
	SI=1;
	FOR(;;){
		read(f,#b,1);
		IF(b>='0')&&(b<='9'){
			buffer[SI]=b;
			SI++;
		}
		ELSE BREAK;
	}
	buffer[SI]=0;
	return(STRTOINT(#buffer));
}

void LiesDaten()
word F,i;
int  h;
{
	IF(@PARAMCOUNT()==0){
		@WRITESTR("Syntax: Goraud <filename>\n");
		@EXIT(0);
	}
	F=open(@PARAMSTR(0),F_READ);
	if(F!=0){
		AnzPunkte=readint(F);
		i=AnzPunkte;
		loop(i){
			h=readint(F);
			BX=AnzPunkte-i<<2;
			Punkte_x[BX]=h;
			h=readint(F);
			BX=AnzPunkte-i<<2;
			Punkte_y[BX]=h;
			h=readint(F);
			BX=AnzPunkte-i<<2;
			Punkte_z[BX]=h;
		}
		AnzPolys=readint(F);
		i=AnzPolys;
		loop(i){
			h=readint(F);
			BX=AnzPolys-i<<1;
			Polygone_p1[BX]=h-1;
			h=readint(F);
			BX=AnzPolys-i<<1;
			Polygone_p2[BX]=h-1;
			h=readint(F);
			BX=AnzPolys-i<<1;
			Polygone_p3[BX]=h-1;
			h=readint(F);
			BX=AnzPolys-i<<1;
			Polygone_p4[BX]=h-1;
		}
		close(F);
	}
}

void DrehePunkte(int xWi,yWi,zWi)
int xs,xc,ys,yc,zs,zc;
long h_x,h_y,h_z;
{
	BX=xWi<<1;
	xs=sin[BX];
	xc=cos[BX];
	BX=yWi<<1;
	ys=sin[BX];
	yc=cos[BX];
	BX=zWi<<1;
	zs=sin[BX];
	zc=cos[BX];
	CX=AnzPunkte;
	loop(CX){
		SI=CX-1<<2;
		h_y=Punkte_y[SI];
		h_z=Punkte_z[SI];
		GPunkte_x[SI]=Punkte_x[SI];
		GPunkte_z[SI]=-xs*h_y;
		GPunkte_z[SI]+=xc*h_z;
		GPunkte_y[SI]=xc*h_y;
		GPunkte_y[SI]+=xs*h_z;
		GPunkte_z[SI]=GPunkte_z[SI]>>7;
		GPunkte_y[SI]=GPunkte_y[SI]>>7;
		h_x=GPunkte_x[SI];
		h_z=GPunkte_z[SI];
		GPunkte_x[SI]=-ys*h_z;
		GPunkte_x[SI]+=yc*h_x;
		GPunkte_z[SI]=yc*h_z;
		GPunkte_z[SI]+=ys*h_x;
		GPunkte_x[SI]=GPunkte_x[SI]>>7;
		GPunkte_z[SI]=GPunkte_z[SI]>>7;
		h_x=GPunkte_x[SI];
		h_y=GPunkte_y[SI];
		GPunkte_x[SI]=-zs*h_y;
		GPunkte_x[SI]+=zc*h_x;
		GPunkte_y[SI]=zc*h_y;
		GPunkte_y[SI]+=zs*h_x;
		GPunkte_x[SI]=GPunkte_x[SI]>>7;
		GPunkte_y[SI]=GPunkte_y[SI]>>7;
	}
}

void SwapPolys(word x,y)
{
	DI=x<<1;
	SI=y<<1;
	Polygone_p1[SI]><Polygone_p1[DI];
	Polygone_p2[SI]><Polygone_p2[DI];
	Polygone_p3[SI]><Polygone_p3[DI];
	Polygone_p4[SI]><Polygone_p4[DI];
}

void SortierePolygone()
long PMD[maxAnzPolys];
word i,k1,k2;
{
	CX=AnzPolys;
	loop(CX){
		SI=CX-1<<1;
		DI=CX-1<<2;
		BX=Polygone_p1[SI]<<2;
		PMD[DI]=GPunkte_z[BX];
		BX=Polygone_p2[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p3[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p4[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		PMD[DI]=PMD[DI]>>2;
	}
	i=AnzPolys;
	loop(i){
		CX=i;
		loop(CX){
			SI=AnzPolys-i<<2;
			DI=AnzPolys-CX<<2;
			k1=AnzPolys-CX;
			k2=AnzPolys-i;
			IF(PMD[DI]>PMD[SI])SwapPolys(k1,k2);
		}
	}
}

void Clear()
{
	ES=VMem;
	DI=0;
	CX=16000;
	EAX=0;
	$CLD
	$REPZ
	$STOSD
}

void FlipPages()
{
	$PUSH DS
	DS=VMem;
	ES=0xA000;
	SI=0;
	DI=0;
	CX=16000;
	$REPZ
	$MOVSD
	$POP DS
}

void HLineDown(word x1,x2,y;byte cols,cole)
{
	ES=VMem;
	DI=y<<8;
	AX=y<<6;
	DI=DI+AX+x1;
	CX=x2-x1;
	IF(CX>1){
		AH=cols-cole;
		AL=0;
		DX=0;
		$DIV CX
		DX><AX;
		AH=cols;
		AL=0;
		loop(CX){
			ESBYTE[DI]=AH;
			DI++;
			AX-=DX;
		}
	}
}

void HLineUp(word x1,x2,y;byte cols,cole)
{
	ES=VMem;
	DI=y<<8;
	AX=y<<6;
	DI=DI+AX+x1;
	CX=x2-x1;
	IF(CX>1){
		AH=cole-cols;
		AL=0;
		DX=0;
		$DIV CX
		DX><AX;
		AH=cols;
		AL=0;
		loop(CX){
			ESBYTE[DI]=AH;
			DI++;
			AX+=DX;
		}
	}
}

void HLine(word x1,x2,y;byte cols,cole)
{
	IF(ABS(x1-x2)<270)&&(cole>cols)HLineUp(x1,x2,y,cols,cole);
	ELSE HLineDown(x1,x2,y,cols,cole);
}

void DrawPoly(int x1,y1,x2,y2,x3,y3,x4,y4;int c1,c2,c3,c4)
int x,mny,mxy,mnx,mxx,yc,mul1,div1,mul2,div2,mul3,div3,mul4,div4,i;
byte b1,b2;
int dcs,dce,h1,h2;
{
	mny=y1;
	mxy=y1;
	IF(y2<mny)mny=y2;
	IF(y2>mxy)mxy=y2;
	IF(y3<mny)mny=y3;
	IF(y3>mxy)mxy=y3;
	IF(y4<mny)mny=y4;
	IF(y4>mxy)mxy=y4;
	IF(mny<YStart)mny=YStart;
	IF(mxy>YEnd)mxy=YEnd ;
	if(mny<>mxy)&&(mny<YEnd){
		if(mxy>YStart){
			mul1=x1-x4;
			div1=y1-y4;
			mul2=x2-x1;
			div2=y2-y1;
			mul3=x3-x2;
			div3=y3-y2;
			mul4=x4-x3;
			div4=y4-y3;
			i=mxy-mny+1;
			loop(i){
				yc=mxy-i;
				mnx=XEnd+1;
				mxx=XStart-1;
				b1=FALSE;
				b2=FALSE;
				IF(y4>=yc)||(y1>=yc)b1=TRUE;
				IF(y4<=yc)||(y1 <= yc)b2=TRUE;
				if(b1==TRUE)&&(b2==TRUE)&&(y4<>y1){
					x=yc-y4*mul1/div1+x4;
					IF(x<mnx){
						mnx=x;
						h1=y4-y1;
						h2=c4-c1;
						dcs=yc-y1*h2/h1+c1;
					}
					IF(x>mxx){
						mxx=x;
						h1=y4-y1;
						h2=c4-c1;
						dce=yc-y1*h2/h1+c1;
					}
				}
				b1=FALSE;
				b2=FALSE;
				IF(y1>=yc)||(y2>=yc)b1=TRUE;
				IF(y1<=yc)||(y2<=yc)b2=TRUE;
				if(b1==TRUE)&&(b2==TRUE)&&(y1<>y2){
					x=yc-y1*mul2/div2+x1;
					IF(x<mnx){
						mnx=x;
						h1=y2-y1;
						h2=c2-c1;
						dcs=yc-y1*h2/h1+c1;
					}
					IF(x>mxx){
						mxx=x;
						h1=y2-y1;
						h2=c2-c1;
						dce=yc-y1*h2/h1+c1;
					}
				}
				b1=FALSE;
				b2=FALSE;
				IF(y2>=yc)||(y3>=yc)b1=TRUE;
				IF(y2<=yc)||(y3<=yc)b2=TRUE;
				if(b1==TRUE)&&(b2==TRUE)&&(y2<>y3){
					x=yc-y2*mul3/div3+x2;
					IF(x<mnx){
						mnx=x;
						h1=y3-y2;
						h2=c3-c2;
						dcs=yc-y2*h2/h1+c2;
					}
					IF(x>mxx){
						mxx=x;
						h1=y3-y2;
						h2=c3-c2;
						dce=yc-y2*h2/h1+c2;
					}
				}
				b1=FALSE;
				b2=FALSE;
				IF(y3>=yc)||(y4>=yc)b1=TRUE;
				IF(y3<=yc)||(y4<=yc)b2=TRUE;
				if(b1==TRUE)&&(b2==TRUE)&&(y3<>y4){
					x=yc-y3*mul4/div4+x3;
					IF(x<mnx){
						mnx=x;
						h1=y4-y3;
						h2=c4-c3;
						dcs=yc-y3*h2/h1+c3;
					}
					IF(x>mxx){
						mxx=x;
						h1=y4-y3;
						h2=c4-c3;
						dce=yc-y3*h2/h1+c3;
					}
				}
				IF(mnx<XStart)mnx=XStart;
				IF(mxx>=XEnd)mxx=XEnd-1;
				IF(mnx<=mxx)HLine(mnx-1,mxx+1,yc,dcs,dce);
			}
		}
	}
}

void MapPoly(long x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4)
long dz,bx1,by1,bx2,by2,bx3,by3,bx4,by4,c1,c2,c3,c4;
{
	dz=BPos_z-z1;
	bx1=BPos_x-x1*HScale/dz+160;
	by1=BPos_y-y1*VScale/dz+100;
	dz=BPos_z-z2;
	bx2=BPos_x-x2*HScale/dz+160;
	by2=BPos_y-y2*VScale/dz+100;
	dz=BPos_z-z3;
	bx3=BPos_x-x3*HScale/dz+160;
	by3=BPos_y-y3*VScale/dz+100;
	dz=BPos_z-z4;
	bx4=BPos_x-x4*HScale/dz+160;
	by4=BPos_y-y4*VScale/dz+100;
	c1=z1+12000<<6/20000;
	c2=z2+12000<<6/20000;
	c3=z3+12000<<6/20000;
	c4=z4+12000<<6/20000;
	IF(long c1<1)c1=1;
	IF(long c1>80)c1=80;
	IF(long c2<1)c2=1;
	IF(long c2>80)c2=80;
	IF(long c3<1)c3=1;
	IF(long c3>80)c3=80;
	IF(long c4<1)c4=1;
	IF(long c4>80)c4=80;
	DrawPoly(word bx1,word by1,word bx2,word by2,word bx3,word by3,word bx4,word by4,word c1,word c2,word c3,word c4);
}

void DrawPolys()
word i;
long x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4,z0;
{
	i=AnzPolys;
	z0=BPos_z+200;
	loop(i){
		DI=AnzPolys-i<<1;
		BX=Polygone_p1[DI]<<2;
		x1=GPunkte_x[BX];
		y1=GPunkte_y[BX];
		z1=GPunkte_z[BX];
		BX=Polygone_p2[DI]<<2;
		x2=GPunkte_x[BX];
		y2=GPunkte_y[BX];
		z2=GPunkte_z[BX];
		BX=Polygone_p3[DI]<<2;
		x3=GPunkte_x[BX];
		y3=GPunkte_y[BX];
		z3=GPunkte_z[BX];
		BX=Polygone_p4[DI]<<2;
		x4=GPunkte_x[BX];
		y4=GPunkte_y[BX];
		z4=GPunkte_z[BX];
		BX=AnzPolys-i<<1;
		IF(long z1>z0)&&(long z2>z0)&&(long z3>z0)&&(long z4>z0)
			MapPoly(long x1,long y1,long z1,long x2,long y2,long z2,long x3,long y3,long z3,long x4,long y4,long z4);
	}
}

void InitPalette()
byte p[3],j;
{
	j=64;
	loop(j){
		p[0]=64-j;
		p[1]=0;
		p[2]=64-j;
		SETVGADAC(j+16,,3,,,#p);
	}
	j=16;
	loop(j){
		p[0]=63;
		p[1]=16-j<<2;
		p[2]=63;
		SETVGADAC(j,,3,,,#p);
	}
}

void InitAll()
{
	BPos_x=0;
	BPos_y=0;
	BPos_z=-65000;
	xW=0;
	yW=0;
	zW=0;
	LiesDaten();
	SETVIDEOMODE(0x13);
	InitPalette();
	VMem=GETMEM(4000);
}

void CleanUp()
{
	@FREEMEM(VMem);
	SETVIDEOMODE(0x03);
}

main()
{
	InitAll();
	do{
		Clear();
		xW++;
		IF(xW>359)xW=0;
		yW++;
		IF(yW>359)yW=0;
		zW++;
		IF(zW>359)zW=0;
		DrehePunkte(xW,yW,zW);
		SortierePolygone();
		DrawPolys();
		FlipPages();
	}while(@BIOSKEYCHECK()==0);
	@BIOSREADKEY();
	CleanUp();
}
