#include "pch.hpp"
//===========================================================================//
// File:	 3DRasterizer.cpp												 //
// Project:  GameOS					                                         //
// Contents: Low level 3D rasterizer										 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 02/26/98  AG		Created								 					 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "3DRasterizer.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "Debugger.hpp"
#include "Texture Manager.hpp"
//
//
//
RenderModeType RenderMode=Normal;		// Current renderer mode
//
//
//
void DoRenderState( gos_RenderState RenderState, int Value );
//
//
//
DWORD InUpdateRenderers=0;				// True when in 'Update Renderers'
DWORD TriangleColor=0;
DWORD CurrentBGCol;
DWORD CurrentBGFill;
//
//
//
//
// Externs for validate results
//
DWORD ValidatePasses=0;
DWORD ValidateResult=0;
//
//
//
bool InsideBeginScene=0;		// Set when in Begin/End scene
bool NeedToInitRenderStates=0;	// Set when renderstates need initing
bool ViewPortChanged=0;			// Set when game changes viewport
//
// Used for 'pick' triangle storage
//
DWORD PickStates[gos_MaxState];				// Used to save all render states
gos_VERTEX Pickv1,Pickv2,Pickv3;
gos_VERTEX PickZoom[3];
DWORD PickFound=0;							// 1=Quad, 2=Triangle, 3=Indexed array, 4=Strip, 5=Fan
DWORD PickDepth=0;							// Number of triangles found
DWORD PickNumber=1;							// Which of the above to return
DWORD DrawingPolys=0;						// Current polygon
DWORD CulledMax=0;
DWORD DrawingMax=0;							// Maximum polys to draw
DWORD DrawingHighest=0;
//
// Array of quad indices
//
WORD QuadIndex[192];
//
// Statistics
//
DWORD	NumSpecular=0;
DWORD	NumPerspective=0;
DWORD	NumAlpha=0;
DWORD	NumTextured=0;
DWORD	VerticesUploaded=0;
DWORD	PrimitivesRendered=0;		// Number of each primitive type rendered
DWORD	PointsRendered=0;
DWORD	LinesRendered=0;
DWORD	TrianglesRendered=0;
DWORD	QuadsRendered=0;
DWORD	IndexedTriangleCalls=0;
float	IndexedTriangleLength=0;
DWORD	IndexedVBTriangleCalls=0;
float	IndexedVBTriangleLength=0;
DWORD	LastSpecularPrim=0;
DWORD	LastPerspectivePrim=0;
DWORD	LastAlphaPrim=0;
DWORD	LastTexturePrim=0;
DWORD	LastFilterPrim=0;
DWORD	NumPointSampled=0;
DWORD	NumBilinear=0;
DWORD	NumTrilinear=0;

DWORD	LastZComparePrim=0;
DWORD	NumZCompare=0;
DWORD	LastZWritePrim=0;
DWORD	NumZWrite=0;
DWORD	LastAlphaTestPrim=0;
DWORD	NumAlphaTest=0;
DWORD	LastDitheredPrim=0;
DWORD	NumDithered=0;
DWORD	LastClipped=0;
DWORD	NumGuardBandClipped=0;
DWORD	NumClipped=0;
DWORD	LastCulledPrim=0;
DWORD	NumCulled=0;
DWORD	LastTextureAddressPrim=0;
DWORD	NumWrapped=0;
DWORD	NumClamped=0;
DWORD	LastShadePrim=0;
DWORD	NumFlat=0;
DWORD	NumGouraud=0;
DWORD	LastBlendPrim=0;
DWORD	NumDecal=0;
DWORD	NumModulate=0;
DWORD	NumModulateAlpha=0;
DWORD	LastMonoPrim=0;
DWORD	NumMono=0;
DWORD	LastFogPrim=0;
DWORD	NumFog=0;
DWORD	gCulledTriangles=0;

double	TotalPixelsDrawn=0.0;
extern DWORD PixelAreaBucket[171];
extern DWORD PixelAreaResults[171];
extern DWORD TotalAreaTriangles;
extern DWORD BGMask;
extern DWORD DisableUVAssert;


DWORD	SetupViewportCalls=0;		// Number of calls to setup viewport
DWORD	SetupViewportClearCalls=0;
DWORD	SetupViewportClearCallsZ=0;




//
// Capabilities of the mode
//
int		HasGuardBandClipping;	// Can clip to at least -2048,-2048 to 2047,2047
float	MinGuardBandClip;		// Minimum guard band clip
float	MaxGuardBandClip;		// Maximum guard band clip
int		HasAGP;					// AGP memory available?
int		HasSpecular;			// Specular available
int		HasDither;				// Dithering available
int		HasAntiAlias;			// Can do sort independant antialias
int		HasMipLodBias;			// MipMap LOD bias can be altered
int		HasDecal;				// Can DECAL
int		HasModulateAlpha;		// Can MODULATEALPHA
int		HasAdd;					// Can ADD
int		HasMipMap;				// Can do mip mapping
int		HasBumpEnvMap;			// Can do bump mapping

int	 HasMaxUV;				// There is a maximum UV coord for this render mode (and texture)
float MaxUCoord;			// The maximum u coord (for current texture)//
float MinUCoord;			// The minimum u coord (for current texture)// Various renderstates based on the capabilities of the current video card
float MaxVCoord;			// The maximum v coord (for current texture)//
float MinVCoord;			// The minimum v coord (for current texture)// Various renderstates based on the capabilities of the current video card


//
//
//
DWORD	ModeMagBiLinear;		// MAG mode for Bilinear
DWORD	ModeMinNone;			// MIN mode for non filtering
DWORD	ModeMinBiLinear;		// MIN mode for bilinear
DWORD	ModeMinTriLinear;		// MIN mode for trilinear




void CheckVertices2( gos_VERTEX_2UV* pVertexArray, DWORD NumberVertices )
{
//
// Show memory useage of vertices uploaded in profiler
//
	VerticesUploaded+=NumberVertices<<5;
//
// Loop through all vertices checking all fields
//
	for( int t0=0; t0<NumberVertices; t0++ )
	{
//
// Force vertex alpha to 1.0 to see invisible polys
//
		if( gForceAlpha && !InDebugger )
		{
			if( gForceAlpha&1 )
				pVertexArray[t0].argb|=0xFF000000;
			else if( gForceAlpha&8 )
				pVertexArray[t0].argb&=0xFF000000;
			if( gForceAlpha&2 )
				pVertexArray[t0].argb|=0x00FFFFFF;
			else if( gForceAlpha&4 )
				pVertexArray[t0].argb&=0x00FFFFFF;
		}
//
// Now check all fields of the vertex for valid values.
//		
		if( pVertexArray[t0].rhw==0.0f )
			PAUSE(( "w is %.12f",(double)pVertexArray[t0].rhw ));

		if( pVertexArray[t0].z<0.0f || pVertexArray[t0].z>=1.0f )
			PAUSE(( "z is %.12f",(double)pVertexArray[t0].z ));

		if( *((DWORD*)&pVertexArray[t0].u1)==0xff81ff81 || *((DWORD*)&pVertexArray[t0].v1)==0xff81ff81 )
			PAUSE(( "u1 or v1 are #SNAN" ));
		if( *((DWORD*)&pVertexArray[t0].u2)==0xff81ff81 || *((DWORD*)&pVertexArray[t0].v2)==0xff81ff81 )
			PAUSE(( "u2 or v2 are #SNAN" ));

		if( HasMaxUV && !DisableUVAssert )
		{
			if( (pVertexArray[t0].u1>=MaxUCoord || pVertexArray[t0].u1<=MinUCoord || pVertexArray[t0].v1>=MaxVCoord || pVertexArray[t0].v1<=MinVCoord) )
				PAUSE(( "u1 or v1 are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u1,pVertexArray[t0].v1,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
			if( (pVertexArray[t0].u2>=MaxUCoord || pVertexArray[t0].u2<=MinUCoord || pVertexArray[t0].v2>=MaxVCoord || pVertexArray[t0].v2<=MinVCoord) )
				PAUSE(( "u2 or v2 are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u2,pVertexArray[t0].v2,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
		}

		if( 0==RenderStates[gos_State_Clipping] )
		{
			if( pVertexArray[t0].x<0.0f || pVertexArray[t0].x>Environment.screenWidth )
				PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
			if( pVertexArray[t0].y<0.0f || pVertexArray[t0].y>Environment.screenHeight )
				PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
		}
		else if( 2==RenderStates[gos_State_Clipping] && gos_GetMachineInformation(gos_Info_HasGuardBandClipping) )
		{
			if( pVertexArray[t0].x<MinGuardBandClip || pVertexArray[t0].x>MaxGuardBandClip )
				PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
			if( pVertexArray[t0].y<MinGuardBandClip || pVertexArray[t0].y>MaxGuardBandClip )
				PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
		}
//
// Vertex data can be spied upon if this is enabled
//	
		if( gShowVertexData )			// Use ^Break debugger to enable (show vertex data)
		{
			SPEW(( GROUP_DIRECT3D, "x,y=%6.2f, %6.2f  z,rhw=%4.2f,%4.2f  u1,v1=%5.2f,%5.2f  u2,v2=%5.2f,%5.2f  argb=0x%x  frgb=0x%x",
			(double)pVertexArray[t0].x,
			(double)pVertexArray[t0].y,
			(double)pVertexArray[t0].z,
			(double)pVertexArray[t0].rhw,
			(double)pVertexArray[t0].u1,
			(double)pVertexArray[t0].v1,
			(double)pVertexArray[t0].u2,
			(double)pVertexArray[t0].v2,
			pVertexArray[t0].argb,
			pVertexArray[t0].frgb ));
		}
	}
}

void CheckVertices3( gos_VERTEX_3UV* pVertexArray, DWORD NumberVertices )
{
//
// Show memory useage of vertices uploaded in profiler
//
	VerticesUploaded+=NumberVertices<<5;
//
// Loop through all vertices checking all fields
//
	for( int t0=0; t0<NumberVertices; t0++ )
	{
//
// Force vertex alpha to 1.0 to see invisible polys
//
		if( gForceAlpha && !InDebugger )
		{
			if( gForceAlpha&1 )
				pVertexArray[t0].argb|=0xFF000000;
			else if( gForceAlpha&8 )
				pVertexArray[t0].argb&=0xFF000000;
			if( gForceAlpha&2 )
				pVertexArray[t0].argb|=0x00FFFFFF;
			else if( gForceAlpha&4 )
				pVertexArray[t0].argb&=0x00FFFFFF;
		}
//
// Now check all fields of the vertex for valid values.
//		
		if( pVertexArray[t0].rhw==0.0f )
			PAUSE(( "w is %.12f",(double)pVertexArray[t0].rhw ));

		if( pVertexArray[t0].z<0.0f || pVertexArray[t0].z>=1.0f )
			PAUSE(( "z is %.12f",(double)pVertexArray[t0].z ));

		if( *((DWORD*)&pVertexArray[t0].u1)==0xff81ff81 || *((DWORD*)&pVertexArray[t0].v1)==0xff81ff81 )
			PAUSE(( "u1 or v1 are #SNAN" ));
		if( *((DWORD*)&pVertexArray[t0].u2)==0xff81ff81 || *((DWORD*)&pVertexArray[t0].v2)==0xff81ff81 )
			PAUSE(( "u2 or v2 are #SNAN" ));

		if( HasMaxUV && !DisableUVAssert )
		{
			if( (pVertexArray[t0].u1>=MaxUCoord || pVertexArray[t0].u1<=MinUCoord || pVertexArray[t0].v1>=MaxVCoord || pVertexArray[t0].v1<=MinVCoord) )
				PAUSE(( "u1 or v1 are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u1,pVertexArray[t0].v1,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
			if( (pVertexArray[t0].u2>=MaxUCoord || pVertexArray[t0].u2<=MinUCoord || pVertexArray[t0].v2>=MaxVCoord || pVertexArray[t0].v2<=MinVCoord) )
				PAUSE(( "u2 or v2 are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u2,pVertexArray[t0].v2,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
			if( (pVertexArray[t0].u3>=MaxUCoord || pVertexArray[t0].u3<=MinUCoord || pVertexArray[t0].v3>=MaxVCoord || pVertexArray[t0].v3<=MinVCoord) )
				PAUSE(( "u3 or v3 are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u3,pVertexArray[t0].v3,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
		}

		if( 0==RenderStates[gos_State_Clipping] )
		{
			if( pVertexArray[t0].x<0.0f || pVertexArray[t0].x>Environment.screenWidth )
				PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
			if( pVertexArray[t0].y<0.0f || pVertexArray[t0].y>Environment.screenHeight )
				PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
		}
		else if( 2==RenderStates[gos_State_Clipping] && gos_GetMachineInformation(gos_Info_HasGuardBandClipping) )
		{
			if( pVertexArray[t0].x<MinGuardBandClip || pVertexArray[t0].x>MaxGuardBandClip )
				PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
			if( pVertexArray[t0].y<MinGuardBandClip || pVertexArray[t0].y>MaxGuardBandClip )
				PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
		}
//
// Vertex data can be spied upon if this is enabled
//	
		if( gShowVertexData )			// Use ^Break debugger to enable (show vertex data)
		{
			SPEW(( GROUP_DIRECT3D, "x,y=%6.2f, %6.2f  z,rhw=%4.2f,%4.2f  u1,v1=%5.2f,%5.2f  u2,v2=%5.2f,%5.2f u3,v3=%5.2f,%5.2f  argb=0x%x  frgb=0x%x",
			(double)pVertexArray[t0].x,
			(double)pVertexArray[t0].y,
			(double)pVertexArray[t0].z,
			(double)pVertexArray[t0].rhw,
			(double)pVertexArray[t0].u1,
			(double)pVertexArray[t0].v1,
			(double)pVertexArray[t0].u2,
			(double)pVertexArray[t0].v2,
			(double)pVertexArray[t0].u3,
			(double)pVertexArray[t0].v3,
			pVertexArray[t0].argb,
			pVertexArray[t0].frgb ));
		}
	}
}



//
//
// Check vertices are all valid
//
//
void CheckVertices( gos_VERTEX* pVertexArray, DWORD NumberVertices, bool PointsLines )
{
	LOG_BLOCK("Gos3D::CheckVertices");
//
// Show memory useage of vertices uploaded in profiler
//
	VerticesUploaded+=NumberVertices<<5;
//
// Loop through all vertices checking all fields
//
	for( int t0=0; t0<NumberVertices; t0++ )
	{
//
// Force vertex alpha to 1.0 to see invisible polys
//
		if( gForceAlpha && !InDebugger )
		{
			if( gForceAlpha&1 )
				pVertexArray[t0].argb|=0xFF000000;
			else if( gForceAlpha&8 )
				pVertexArray[t0].argb&=0xFF000000;
			if( gForceAlpha&2 )
				pVertexArray[t0].argb|=0x00FFFFFF;
			else if( gForceAlpha&4 )
				pVertexArray[t0].argb&=0x00FFFFFF;
		}
//
// Now check all fields of the vertex for valid values.
//		
		if( pVertexArray[t0].rhw==0.0f )
			PAUSE(( "w is %.12f",(double)pVertexArray[t0].rhw ));

		if( pVertexArray[t0].z<0.0f || pVertexArray[t0].z>=1.0f )
			PAUSE(( "z is %.12f",(double)pVertexArray[t0].z ));

		if( *((DWORD*)&pVertexArray[t0].u)==0xff81ff81 || *((DWORD*)&pVertexArray[t0].v)==0xff81ff81 )
			PAUSE(( "u or v are #SNAN" ));

		if( HasMaxUV && !DisableUVAssert )
		{
			if( (pVertexArray[t0].u>=MaxUCoord || pVertexArray[t0].u<=MinUCoord || pVertexArray[t0].v>=MaxVCoord || pVertexArray[t0].v<=MinVCoord) )
				PAUSE(( "u or v are out of range (%.0f,%.0f) Range=(%.0f,%.0f)..(%.0f,%.0f)", pVertexArray[t0].u,pVertexArray[t0].v,MinUCoord,MinVCoord,MaxUCoord,MaxVCoord ));
		}

		if( 0==RenderStates[gos_State_Clipping] )
		{
			if( PointsLines )
			{
				if( pVertexArray[t0].x<0.0f || pVertexArray[t0].x>Environment.screenWidth-1 )
					PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
				if( pVertexArray[t0].y<0.0f || pVertexArray[t0].y>Environment.screenHeight-1 )
					PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
			}
			else
			{
				if( pVertexArray[t0].x<0.0f || pVertexArray[t0].x>Environment.screenWidth )
					PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
				if( pVertexArray[t0].y<0.0f || pVertexArray[t0].y>Environment.screenHeight )
					PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
			}
		}
		else if( 2==RenderStates[gos_State_Clipping] && gos_GetMachineInformation(gos_Info_HasGuardBandClipping) )
		{
			if( pVertexArray[t0].x<MinGuardBandClip || pVertexArray[t0].x>MaxGuardBandClip )
				PAUSE(( "x is %f",(double)pVertexArray[t0].x ));
			if( pVertexArray[t0].y<MinGuardBandClip || pVertexArray[t0].y>MaxGuardBandClip )
				PAUSE(( "y is %f",(double)pVertexArray[t0].y ));
		}
//
// Vertex data can be spied upon if this is enabled
//	
		if( gShowVertexData )			// Use ^Break debugger to enable (show vertex data)
		{
			SPEW(( GROUP_DIRECT3D, "x,y=%6.2f, %6.2f  z,rhw=%4.2f,%4.2f  u,v=%5.2f,%5.2f  argb=0x%x  frgb=0x%x",
			(double)pVertexArray[t0].x,
			(double)pVertexArray[t0].y,
			(double)pVertexArray[t0].z,
			(double)pVertexArray[t0].rhw,
			(double)pVertexArray[t0].u,
			(double)pVertexArray[t0].v,
			pVertexArray[t0].argb,
			pVertexArray[t0].frgb ));
		}
	}
}









#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)


//
// Used in examine mipmaps and debugger - returns a color associated with a mipmap level
//
// Level = -7 to 0 to +7
//
DWORD GetMipmapColor( int Mipmap )
{
	DWORD Color;
	if( Mipmap<0 )
	{
		Color=0xff00002a + (-0x00001e*Mipmap);
	}
	else if( Mipmap>0 )
	{
		Color=0xff2a0000 + (0x1e0000*Mipmap);
	}
	else
	{
		Color=0xff000000;
	}
	return Color;
}




void DebugTriangle_2UV( gos_VERTEX_2UV* v1, gos_VERTEX_2UV* v2, gos_VERTEX_2UV* v3 )
{
	DebugTriangle( (gos_VERTEX*)v1, (gos_VERTEX*)v2, (gos_VERTEX*)v3 );
}

void DebugTriangle_3UV( gos_VERTEX_3UV* v1, gos_VERTEX_3UV* v2, gos_VERTEX_3UV* v3 )
{
	DebugTriangle( (gos_VERTEX*)v1, (gos_VERTEX*)v2, (gos_VERTEX*)v3 );
}



//
// Display triangle 
//
void DebugTriangle( gos_VERTEX* v1, gos_VERTEX* v2, gos_VERTEX* v3 )
{
//
// Should I CULL the triangle?
//
	if( RenderStates[gos_State_Culling]!=gos_Cull_None )
	{
		float fDet = (v2->x - v1->x) * (v3->y - v1->y) - (v1->x - v3->x) * (v1->y - v2->y);
		
		if( RenderStates[gos_State_Culling]==gos_Cull_CCW )
		{
			if( fDet<0 )
			{
				gCulledTriangles++;
				return;
			}
		}
		else
		{
			if( fDet>0 )
			{
				gCulledTriangles++;
				return;
			}
		}
	}
//
// Select which render mode to use
//
	switch( RenderMode )
	{
//
// Draw each triangle in an incrementing color
//
		case ShowTriangleColors:
		{
			gos_VERTEX nv[3];
			nv[0]=*v1;
			nv[1]=*v2;					// Copy vertices so can be altered
			nv[2]=*v3;

			DWORD SaveAlpha=RenderStates[gos_State_AlphaMode];
			DWORD SaveTexture=RenderStates[gos_State_Texture];
			DWORD SaveBlend=RenderStates[gos_State_TextureMapBlend];
			DWORD SaveFilter=RenderStates[gos_State_Filter];
			DWORD SaveShade=RenderStates[gos_State_ShadeMode];
			DWORD SaveMono=RenderStates[gos_State_MonoEnable];
			DWORD SaveAlphaTest=RenderStates[gos_State_AlphaTest];

			gos_SetRenderState( gos_State_Texture, 0 );
			gos_SetRenderState( gos_State_AlphaTest, 0 );
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
			gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
			gos_SetRenderState( gos_State_Filter, gos_FilterNone );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
			gos_SetRenderState( gos_State_MonoEnable, 0 );

			nv[0].argb=TriangleColor;
			nv[1].argb=TriangleColor;
			nv[2].argb=TriangleColor;

			TriangleColor+=0x20;
			if( 0==(TriangleColor&0xff) )
			{
				TriangleColor+=0x1f00;
				if( 0==(TriangleColor&0xff00) )
				{
					TriangleColor+=0x1f0000;
				}
			}

			if( DirtyStates )
				FlushRenderStates();

			SafeFPU();					// Make sure exceptions cannot be generated
			wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
			GameOSFPU();				// Restore current GameOS FPU control word setup

			gos_SetRenderState( gos_State_Texture, SaveTexture );
			gos_SetRenderState( gos_State_ShadeMode, SaveShade );
			gos_SetRenderState( gos_State_AlphaMode, SaveAlpha );
			gos_SetRenderState( gos_State_Filter, SaveFilter );
			gos_SetRenderState( gos_State_TextureMapBlend, SaveBlend );
			gos_SetRenderState( gos_State_MonoEnable, SaveMono );
			gos_SetRenderState( gos_State_AlphaTest, SaveAlphaTest );
			return;
		}
//
// Draw triangle as normal and them draw a textured border around it (fall through to WireframeGlobalColor)
//
		case WireframeGlobalTexture:
		{
			if( DirtyStates )
				FlushRenderStates();

			gos_VERTEX nv[3];
			nv[0]=*v1;
			nv[1]=*v2;					// Copy vertices so can be altered
			nv[2]=*v3;
			SafeFPU();					// Make sure exceptions cannot be generated
			wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
			GameOSFPU();				// Restore current GameOS FPU control word setup
		}
//
// Draw just the triangle border
//
		case WireframeGlobalColor:
		{
			gos_VERTEX nv[4];
			nv[0]=*v1;
			nv[1]=*v2;					// Copy vertices so can be altered
			nv[2]=*v3;
			nv[3]=*v1;

			DWORD SaveAlpha=RenderStates[gos_State_AlphaMode];
			DWORD SaveTexture=RenderStates[gos_State_Texture];
			DWORD SaveZCompare=RenderStates[gos_State_ZCompare];
			DWORD SaveBlend=RenderStates[gos_State_TextureMapBlend];

			gos_SetRenderState( gos_State_Texture, 0 );
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
			gos_SetRenderState( gos_State_ZCompare, 0 );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );

			nv[0].argb=0xffffffff;
			nv[1].argb=0xffffffff;
			nv[2].argb=0xffffffff;
			nv[3].argb=0xffffffff;

			if( DirtyStates )
				FlushRenderStates();

			SafeFPU();					// Make sure exceptions cannot be generated
			wDrawPrimitive( d3dDevice7, D3DPT_LINESTRIP, D3DFVF_TLVERTEX, &nv, 4, 0 );
			GameOSFPU();				// Restore current GameOS FPU control word setup

			gos_SetRenderState( gos_State_TextureMapBlend, SaveBlend );
			gos_SetRenderState( gos_State_ZCompare, SaveZCompare );
			gos_SetRenderState( gos_State_AlphaMode, SaveAlpha );
			gos_SetRenderState( gos_State_Texture, SaveTexture );
			return;
		}
//
// Calculate triange areas
//
		case ExamineTriangleSize:
		{
			double area=0.5f * fabs( v2->x*v3->y + v1->x*v2->y + v3->x*v1->y - v2->x*v1->y - v1->x*v3->y - v3->x*v2->y );

			TotalPixelsDrawn+=area;

			int t0=0;
			
			DWORD iArea=(DWORD)area;
			
			while( iArea>PixelAreaBucket[t0] && t0<170 )
			{
				t0++;
			}

			PixelAreaResults[t0]++;
			TotalAreaTriangles++;
			break;
		}
//
// Calculate triange areas - only draw triangles that are 1 or 2 pixels
//
		case ExamineTriangleSize1:
		{
			double area=0.5f * fabs( v2->x*v3->y + v1->x*v2->y + v3->x*v1->y - v2->x*v1->y - v1->x*v3->y - v3->x*v2->y );

			TotalPixelsDrawn+=area;

			int t0=0;
			
			DWORD iArea=(DWORD)area;
			
			while( iArea>PixelAreaBucket[t0] && t0<170 )
			{
				t0++;
			}

			PixelAreaResults[t0]++;
			TotalAreaTriangles++;

			if( t0>2 )
				return;
			break;
		}
//
// Examine drawing order for the scene
//
		case ExamineDrawing:
		{
			DrawingPolys++;
			if( DrawingPolys > DrawingMax )
				return;
			break;
		}
//
// Examine area, when the user selects an area of the screen with the mouse display information on the polygons CONTAINED within that area
//
		case ExamineArea:
		{
			if( v1->x>=AreaL && v2->x>=AreaL && v3->x>=AreaL && 
				v1->x<=AreaR && v2->x<=AreaR && v3->x<=AreaR && 
				v1->y>=AreaT && v2->y>=AreaT && v3->y>=AreaT && 
				v1->y<=AreaB && v2->y<=AreaB && v3->y<=AreaB )
			{
				PolygonsInArea++;
			}
			break;
		}
//
// Examine color, give detailed color about point under cursor - fall through to examine scene
//
		case ExamineColor:
//
// Examine scene, zoom in and pick current triangle - Check to see if cursor is above current triangle, if so save renderstates and info
//
		case ExamineScene:
		{
			int Counter=0;

			if( DirtyStates )
				FlushRenderStates();

			float MouseX=(float)ExMouseX;
			float MouseY=(float)ExMouseY;
			
			if( Environment.Renderer==3 && (RenderStates[gos_State_MonoEnable]&256) )
			{
				MouseX/=2;
				MouseY/=2;
			}

			if( MouseY >= MIN(v1->y,v2->y) )							// Is mouse inside triangle?
			{
				if( MouseY <= MAX(v1->y,v2->y) )
				{
					if( MouseX <= MAX(v1->x,v2->x) )
					{
						if( v1->y != v2->y )
						{
							if( v1->x == v2->x || MouseX <= ((MouseY-v1->y)*(v2->x-v1->x)/(v2->y-v1->y)+v1->x) )
							{
								Counter++;
							}
						}
					}
				}
			}
			if( MouseY >= MIN(v2->y,v3->y) )
			{
				if( MouseY <= MAX(v2->y,v3->y) )
				{
					if( MouseX <= MAX(v2->x,v3->x) )
					{
						if( v2->y != v3->y )
						{
							if( v2->x == v3->x || MouseX <= ((MouseY-v2->y)*(v3->x-v2->x)/(v3->y-v2->y)+v2->x) )
							{
								Counter++;
							}
						}
					}
				}
			}
			if( MouseY >= MIN(v3->y,v1->y) )
			{
				if( MouseY <= MAX(v3->y,v1->y) )
				{
					if( MouseX <= MAX(v3->x,v1->x) )
					{
						if( v3->y != v1->y )
						{
							if( v3->x == v1->x || MouseX <= ((MouseY-v3->y)*(v1->x-v3->x)/(v1->y-v3->y)+v3->x) )
							{
								Counter++;
							}
						}
					}
				}
			}
			if( (Counter&1) == 0 )
				break;

			PickDepth++;
			if( PickDepth!=PickNumber )										// When more than one triangle, have I picked the right one?
				break;

			memcpy( &PickStates, &RenderStates, sizeof(PickStates) );		// Found - Save renderstates

			Pickv1=*v1;
			Pickv2=*v2;														// Save triangle verts
			Pickv3=*v3;

			PickZoom[0]=*v1;
			PickZoom[1]=*v2;												// Save zoomed-in triangle verts
			PickZoom[2]=*v3;

			PickZoom[0].x-=(MouseX-PickZoom[0].x)*ZoomMode;
			PickZoom[0].y-=(MouseY-PickZoom[0].y)*ZoomMode;
			PickZoom[1].x-=(MouseX-PickZoom[1].x)*ZoomMode;
			PickZoom[1].y-=(MouseY-PickZoom[1].y)*ZoomMode;
			PickZoom[2].x-=(MouseX-PickZoom[2].x)*ZoomMode;
			PickZoom[2].y-=(MouseY-PickZoom[2].y)*ZoomMode;
//
// Allow the debugger to be entered at a specific triangle in the show triangle mode
//
			if( GetAsyncKeyState( VK_BACK )&0x8000 )						// Is key held?
				PAUSE(( "Stopped at selected triangle" ));

			PickFound=1;
			break;
		}
//
// Show Mipmaps and color code some triangles
//
		case ExamineMipmaps:
		{
			if( WhichImage )
				break;

			int MipmapUsed=GetMipmapUsed( RenderStates[gos_State_Texture]-1, v1, v2, v3 );

			if( MipmapUsed>8 )
				MipmapUsed=8;
			if( MipmapUsed<-8 )
				MipmapUsed=-8;

			if( MipmapUsed==0 )
				break;

			gos_VERTEX nv[3];
			nv[0]=*v1;
			nv[1]=*v2;					// Copy vertices so can be altered
			nv[2]=*v3;

			DWORD SaveAlpha=RenderStates[gos_State_AlphaMode];
			DWORD SaveTexture=RenderStates[gos_State_Texture];
			DWORD SaveBlend=RenderStates[gos_State_TextureMapBlend];
			DWORD SaveFilter=RenderStates[gos_State_Filter];

			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
			gos_SetRenderState( gos_State_Texture, 0 );
			gos_SetRenderState( gos_State_Filter, gos_FilterNone );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );

			nv[0].argb=nv[1].argb=nv[2].argb=GetMipmapColor( MipmapUsed );

			if( DirtyStates )
				FlushRenderStates();

			SafeFPU();					// Make sure exceptions cannot be generated
			wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
			GameOSFPU();				// Restore current GameOS FPU control word setup

			gos_SetRenderState( gos_State_AlphaMode, SaveAlpha );
			gos_SetRenderState( gos_State_TextureMapBlend, SaveBlend );
			gos_SetRenderState( gos_State_Filter, SaveFilter );
			gos_SetRenderState( gos_State_Texture, SaveTexture );
		}
		return;
		case ExamineMipmaps2:
		{
			if( WhichImage )
				break;

			gos_VERTEX nv[3];
			nv[0]=*v1;
			nv[1]=*v2;					// Copy vertices so can be altered
			nv[2]=*v3;

			float factor = 1.f;// 256.0f / 32.0f;	// TBD: divide current texture width instead of 256
			nv[0].u *= factor;
			nv[0].v *= factor;
			nv[1].u *= factor;
			nv[1].v *= factor;
			nv[2].u *= factor;
			nv[2].v *= factor;

			DWORD SaveTexture=RenderStates[gos_State_Texture];
			gos_SetRenderState( gos_State_Texture, MipColorTexture );

			if( DirtyStates )
				FlushRenderStates();

			SafeFPU();					// Make sure exceptions cannot be generated
			wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
			GameOSFPU();				// Restore current GameOS FPU control word setup

			gos_SetRenderState( gos_State_Texture, SaveTexture );

		}
		return;
//
// Defaults - just draw triangle
//
		case ExamineRenderer:
		case ExamineDepth:
			break;
	}
//
// Default - Just draw triangle
//
	if( DirtyStates )
		FlushRenderStates();
//
// Copy vertices so they can be altered
//
	gos_VERTEX nv[3];
	nv[0]=*v1;
	nv[1]=*v2;
	nv[2]=*v3;
//
// In examine mode and zoomed into the screen? - Always use D3D to clip
//
	if( ZoomMode )
	{
		int MouseX=DBMouseX;
		int MouseY=DBMouseY;
		
		if( Environment.Renderer==3 && (RenderStates[gos_State_MonoEnable]&256) )
		{
			MouseX/=2;
			MouseY/=2;
		}

		nv[0].x-=(MouseX-nv[0].x)*ZoomMode;
		nv[0].y-=(MouseY-nv[0].y)*ZoomMode;
		nv[1].x-=(MouseX-nv[1].x)*ZoomMode;
		nv[1].y-=(MouseY-nv[1].y)*ZoomMode;
		nv[2].x-=(MouseX-nv[2].x)*ZoomMode;
		nv[2].y-=(MouseY-nv[2].y)*ZoomMode;

		wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
		return;
	}
//
// Draw the triangle
//
	SafeFPU();					// Make sure exceptions cannot be generated
	wDrawPrimitive( d3dDevice7, D3DPT_TRIANGLELIST, D3DFVF_TLVERTEX, &nv, 3, 0 );
	GameOSFPU();				// Restore current GameOS FPU control word setup
}

























//
// Close down Direct3D
//
void Destroy3D()
{
//
// Ignore any states pushed on the stack
//
	while( pStateStack )
	{
		SAVESTATE* ptemp=pStateStack;
		pStateStack=pStateStack->pNext;
		gos_Free( ptemp );
	}
//
// End any current scene
//
	if( InsideBeginScene )
	{
		End3DScene();
		InsideBeginScene=0;
	}
}



//
// Called to set 3D Renderer back to known state
//
void ReInit3D()
{
	Destroy3D();
	InsideBeginScene=0;
	NeedToInitRenderStates=1;
	InitRenderer();
}




//
// Sets the region of screen that will be rendered to. (The default values set at the start of each frame are 0, 0, Environment.screenWidth, Environment.screenHeight)
//
void __stdcall gos_SetViewport( DWORD LeftX, DWORD TopY, DWORD Width, DWORD Height, float MinZ, float MaxZ )
{
	LOG_BLOCK("Gos3D::SetViewport");
	gosASSERT( LeftX<Environment.screenWidth && LeftX+Width<=Environment.screenWidth && TopY<Environment.screenHeight && TopY+Height<=Environment.screenHeight && MinZ>=0.0f && MaxZ<=1.0f );

	viewData.dwX		= LeftX;
	viewData.dwY		= TopY;
	viewData.dwWidth	= Width;
	viewData.dwHeight	= Height;
	viewData.dvMinZ		= MinZ;
	viewData.dvMaxZ		= MaxZ;

    wSetViewport( d3dDevice7, &viewData );
	ViewPortChanged=1;
}







//
// Setup a viewport, must be called before drawing ANYTHING
//
// Defines the region of the monitor the 3D objects will be rendered too.
// 0.0 0.0 1.0 1.0 would be whole screen with 0,0 in the top left
// 1.0 0.0 0.0 1.0 would be whole screen with 0,0 in bottom left
// -0.5 -0.5 0.5 0.5 would be whole screen with 0.0 in the middle
// 
// If FillZ is true the ZBuffer will be cleared with the value Zbuffer (0.0 to 1.0)
// If FillBG is true the background will be cleared to the color BGColor
//
// Normal applications will fill the Z buffer with 1.0
//
// You can only clear the backbuffer or Z buffer on the FIRST setup viewport (outside the Begin/End Scene
//

//sanghoon begin
void __cdecl ClearTargetCameraBackBuffer()
{
	D3DRECT rc={0,10,120,112};
	wClear(d3dDevice7,1,&rc,D3DCLEAR_TARGET,0xFF00FFFF,0,0);
	return ;
}
//sanghoon end

void __stdcall gos_SetupViewport( bool FillZ, float ZBuffer, bool FillBG, DWORD BGColor, float top, float left, float bottom, float right, bool ClearStencil, DWORD StencilValue )
{
	//sanghoon
	//This violates the normal rules, so we suppress the ASSERT.
	//gosASSERT( !InsideBeginScene || !(FillZ || FillBG) );
//
// Work out size of viewport
//
	float LeftEdge,Width,TopEdge,Height;
	if( right>left )
	{
		Width=right-left;
		LeftEdge=left;
	}
	else
	{
		Width=left-right;
		LeftEdge=right;
	}
	if( bottom>top )
	{
		Height=bottom-top;
		TopEdge=left;
	}
	else
	{
		Height=top-bottom;
		TopEdge=right;
	}
	gosASSERT( Width<=1.0 && Height<=1.0 );
//
// Trying to clear the z buffer when it's disabled?
//
	if( Environment.disableZBuffer && FillZ )
	{
		FillZ=0;
	}
//
// Change color if gamma correction in use
//
	if( FillBG && UseGammaCorrection )
	{
		BGColor=(GammaTable[(BGColor>>0)&0xff]<<0)+(GammaTable[(BGColor>>8)&0xff]<<8)+(GammaTable[(BGColor>>16)&0xff]<<16);
	}
//
// Debugger overriding the clear?
//
	#ifdef LAB_ONLY
	if( gForceNoClear )
		FillBG=0;
//
// In a wireframe mode, FORCE the first setupviewport to clear the display (in case app assumes polys fill screem)
//
	if( !InsideBeginScene && ((RenderMode==WireframeGlobalColor || RenderMode==ExamineTriangleSize1 || RenderMode==ExamineDrawing) && !FillBG) )
	{
		FillBG=1;
		BGColor=0xa07020;		// Pick a nice color!
	}
//
// Update profile information
//
	SetupViewportCalls++;
	if( FillBG )
		SetupViewportClearCalls++;
	if( FillZ )
		SetupViewportClearCallsZ++;
	#endif
//
// Update vertex transform variables
//
	gosViewportMulX= (right-left) * Environment.screenWidth;
	gosViewportMulY= (bottom-top) * Environment.screenHeight;
	gosViewportAddX= left * Environment.screenWidth;
	gosViewportAddY=  top * Environment.screenHeight;
//
// Mask off color bits so that BG color matches textures better
//
	BGColor&=BGMask;
//
// If requested, clear background and z buffer
//
	if( FillZ || FillBG || ClearStencil )
	{
		SafeFPU();
		AllowFail=1;
		HRESULT result=wClear( d3dDevice7, 0, 0, (FillZ ? D3DCLEAR_ZBUFFER : 0) + (FillBG ? D3DCLEAR_TARGET : 0) + ((Environment.Stencil && ClearStencil) ? D3DCLEAR_STENCIL : 0), BGColor, ZBuffer, StencilValue );
		AllowFail=0;
		GameOSFPU();
		if( FAILED(result) )
		{
			SPEW(( "","Failed 0x%x - %s",result,ErrorNumberToMessage(result) ));
			EnterWindowMode();
		}
	}
//
// Store fill color
//
	if( FillBG )
	{
		CurrentBGCol=BGColor;
		CurrentBGFill=1;
	}
//
// If first gos_SetupViewport in frame, begin the scene
//
	if( !InsideBeginScene )
	{
//
// Before the 3D scene is entered, make sure any regions are cleared
//
		RestoreAreas();
//
// Set flag signifying inside Begin/End Scene
//
		wBeginScene( d3dDevice7 );
		InsideBeginScene=1;
		pStateStack=0;
		StackDepth=0;
		TriangleColor=0;
//
// Send initial renderstates
//
		if( NeedToInitRenderStates )
		{
			InitRenderStates();
			NeedToInitRenderStates=0;
		}
//
// Need to setup the viewport again?
//
		if( ViewPortChanged )
		{
			gos_SetViewport( 0, 0, Environment.screenWidth, Environment.screenHeight );
			ViewPortChanged=0;
		}
//
// Setup all renderstates to their default values
//
		memset( &RenderStates, 0, sizeof(RenderStates) );
		RenderStates[ gos_State_Filter 			] = gos_FilterNone;
		RenderStates[ gos_State_ZCompare 		] = 1;
		RenderStates[ gos_State_ZWrite 			] = 1;
		RenderStates[ gos_State_Perspective		] = 1;
		RenderStates[ gos_State_AlphaMode 		] = gos_Alpha_OneZero;
		RenderStates[ gos_State_TextureAddress 	] = gos_TextureWrap;
		RenderStates[ gos_State_ShadeMode 		] = gos_ShadeGouraud;
		RenderStates[ gos_State_TextureMapBlend ] = gos_BlendModulateAlpha;
		RenderStates[ gos_State_Culling			] = gos_Cull_None;
		RenderStates[ gos_State_StencilFunc		] = gos_Cmp_Never;
		RenderStates[ gos_State_StencilMask		] = 0xffffffff;
		RenderStates[ gos_State_StencilFail		] = gos_Stencil_Keep;
		RenderStates[ gos_State_StencilZFail	] = gos_Stencil_Keep;
		RenderStates[ gos_State_StencilPass		] = gos_Stencil_Keep;
		RenderStates[ gos_State_Ambient			] = 0xffffff;

		for( int t0=gos_MaxState-1; t0; t0-- )
		{
			DoRenderState( (gos_RenderState)t0, RenderStates[t0] );				// Trigger all states to enable overrides to work
		}
	}
}









//
// Application visible function
//
// The values returned must be used to transform the x,y coords into the current
// viewport window. This is how the values should be used.
//
// x = x*ViewportMulX + ViewportAddX
// y = y*ViewportMulY + ViewportAddY
//
// If you call gos_SetupViewport you should call this function again. You should also
// call this function at least once per frame as when the window size changes, these
// values may change.
//
void __stdcall gos_GetViewport( float* pViewportMulX, float* pViewportMulY, float* pViewportAddX, float* pViewportAddY )
{
	gosASSERT( InsideBeginScene );

	*pViewportMulX=gosViewportMulX;
	*pViewportMulY=gosViewportMulY;
	*pViewportAddX=gosViewportAddX;
	*pViewportAddY=gosViewportAddY;
}






//
// Setup default renderstates at start of game
//
void InitRenderer()
{
	SPEW(( GROUP_GAMEOS, "InitRenderer()" ));
//
// Setup quad index array
//
	WORD* pIndex=QuadIndex;
	for( WORD Vertex=0; Vertex<128; Vertex+=4 )
	{
		*pIndex++=(WORD)(Vertex+1);
		*pIndex++=(WORD)(Vertex+0);
		*pIndex++=(WORD)(Vertex+2);

		*pIndex++=(WORD)(Vertex+0);
		*pIndex++=(WORD)(Vertex+3);
		*pIndex++=(WORD)(Vertex+2);
	}
//
// Clear variables
//
	RenderMode=Normal;
	InUpdateRenderers=0;
	SPEW(( GROUP_GAMEOS, "InitRenderer() - Finished" ));
}











