/*************************************************************************
*
*	Title:	Tunnel.h
*	Desc:	Tunnel Hedaer file
*
*	Note:
*
**************************************************************************/
#ifndef TUNNEL_H
#define TUNNEL_H TRUE
#include "fpu.h--"

/*************************************************************************/
// Global Functions
/*************************************************************************/

void Tunnel_Init( void );
void Tunnel_Kill( void );
void Tunnel_Update( void );

dword m_TextureMap;
dword m_TunnelLUT;

/*************************************************************************/
// Functions
/*************************************************************************/

#define DXD 2.0/319.0
#define DYD 2.0/239.0

/*************************************************************************
*
*	Function:	Tunnel_Init()
*
*	Desc:		Inits tunnel datat
*
*	Notes:		1) Computes the lookup table
*				2) This is a generic tunnel, you can just as easily make a textured torus or
*					generaly any solvable Function of (x,y,z)
*
***************************************************************************/
void Tunnel_Init( void )
	float x, y;
	float xD, Dy, Dz;
	float Dist;
	float t;
	float Ix, Iy, Iz;
	float tu, tv;
	float Radius, Radius2;
	dword TunnelLUT;
	long i;
	dword File;

{
	$push ESI,EDI
	// allocate texture map
	m_TextureMap =GlobalAlloc(GMEM_FIXED, 2 * 256 * 256 );

	// allocate LUT
	m_TunnelLUT = GlobalAlloc(GMEM_FIXED, sizeof(long) * 320 *240 );

	TunnelLUT = EAX;

	// load it
	File=CreateFileA("Texture.tga",GENERIC_READ,
						FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
	IF(EAX!=-1){
		//skip header file
		ReadFile(File,m_TextureMap,18,#i,0);
		//load bitmap
		ReadFile(File,m_TextureMap,2*256*256,#i,0);
		CloseHandle(File);
	}

	// set cylender radius
	Radius = 1.0;
	Radius2 = Radius * Radius;

	// generate lookup
	for (y = -1.0; y < 1.0; y += DYD){
		for (x = -1.0; x < 1.0; x += DXD){
			// calculate ray direction
			xD = x;
			Dy = y;
			Dz = 1.0;

			// normalize
			asm{
				fld xD
				fmul ST,ST(0)
				fld Dy
				fmul ST,st(0)
				fld Dz
				fmul ST,st(0)
				faddp ST(1)
				faddp ST(1)
				fsqrt
				fld1
				fdivrp st(1)
				fstp Dist
				fwait
			}
			xD *= Dist;
			Dy *= Dist;
			Dz *= Dist;

			// calculate intersection with object
			//
			// Eq Cylinder x^2 + y^2 = 1									Eq (1)
			//
			// Paremetric Eq of our ray -> (0,0,0) + (Dx, Dy, Dz)*t = 0		Eq (2)
			//
			// Substue (2) into Eq (1)
			//
			// -> ( Dx*t )^2 + (Dy*t)^2	= Radius^2
			//
			// -> t^2 ( Dx^2 + Dy^2 )	= Radius^2
			//
			// ->					  t = sqrt(Radius^2 / (Dx^2 - Dy^2) )
			//
			asm{
				fld xD
				fmul ST,st(0)
				fld Dy
				fmul ST,st(0)
				faddp ST(1)
				fstp t
				fwait
			}
			t =sqrt( Radius2  / t);

			// get intersection point
			Ix = xD * t;
			Iy = Dy * t;
			Iz = Dz * t;

			// use a sort of cylndrical texture projection

			// use how far down the z axis the point is as the y texture compenent
			tv = fabs(Iz)* 0.2;
			i=0;
		 ÿWHILE ( tv > 1.0){
				i++;
				tv -= 1.0;
			}

			// use the angle around the cylenders z axis as the x texture component
			tu =atan2( Iy, Ix )+3.1415/3.1415/2.0;
			IF (tu < 0.0) tu = 0.0;
			IF (tv > 1.0) tv = 1.0;

			// stop tunnel at a certain distance
			IF (i <= 2){
				// save address
/*  This variant is shorter, but not so evident
				t=256;
				asm{
					fld t
					fld st(0)
					fmul tv
					fistp i
					fmul tu
					fwait
				}
				EAX=i*256;
				asm{
					fistp i
					fwait
				}
				EAX+=i;*/

				t=tu*256;
				float2numer(t);
				EAX><EBX;
				t=256*tv;
				float2numer(t);
				EAX=EAX*256+EBX;
			}
			ELSE{
				EAX = 0;
			}
			EDI=TunnelLUT;
			DSDWORD[EDI]=EAX;

			// next tunnel LUT entry
			TunnelLUT+=4;
		}
	}
	$pop EDI,ESI
}

/****************************************************************************
*
*	Function:	Tunnel_Kill()
*
*	Desc:		Frees any resources
*
*	Notes:
*
*****************************************************************************/
void Tunnel_Kill( void )
{
	// release texture map memory
	IF (m_TextureMap){
		GlobalFree( m_TextureMap );
		m_TextureMap = NULL;
	}

	// release LUT memory
	IF (m_TunnelLUT){
		GlobalFree( m_TunnelLUT );
		m_TunnelLUT = NULL;
	}
}

/****************************************************************************
*
*	Function:	Tunnel_Update()
*
*	Desc:		Updates one frame of the tunnel effect
*
*	Notes:
*
****************************************************************************/
void Tunnel_Update( void )
{
	$push ESI,EDI
	// get display page
	$CALL VV_GetAddress
	EDI=EAX;

	// loop over every pixel
	EDX=m_TunnelLUT ;
	EBX=m_TextureMap;
	FOR(ESI=0;ESI<320*240;ESI++){
		// end of the tunnel?
		ECX=DSDWORD[EDX+ESI*4];
		IF (ECX!=0){
			// copy pixel
			AX=DSWORD[EBX+ECX*2];
		}
		ELSE{
			// back ground color
			AX=0;
		}
		DSWORD[EDI+ESI*2]=AX;
	}

	// copy first row to the bottom
	ESI=m_TextureMap;
	EDI=255*256*2+ESI;
	ECX=2*256/4;
	$cld
	$rep
	$movsd

	// move the texture upward 1 row
	EDI=m_TextureMap;
	ECX=255*256*2/4;
	$rep
	$movsd

	$pop EDI,ESI
}

#endif //TUNNEL_H
