//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A set of utilities to render standard shapes
//
//===========================================================================//

#include "tier2/renderutils.h"
#include "tier2/tier2.h"
#include "tier1/KeyValues.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imesh.h"
#include "materialsystem/imaterial.h"
#include "tier0/vprof.h"
#include "tier0/basetypes.h"
#ifdef DX_TO_GL_ABSTRACTION
#include "togl/rendermechanism.h"
#endif

#if !defined(M_PI)
	#define M_PI			3.14159265358979323846
#endif

//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
static bool s_bMaterialsInitialized = false;
static IMaterial *s_pWireframe;
static IMaterial *s_pWireframeIgnoreZ;
static IMaterial *s_pVertexColor;
static IMaterial *s_pVertexColorIgnoreZ;


//-----------------------------------------------------------------------------
// Initializes standard materials
//-----------------------------------------------------------------------------
void InitializeStandardMaterials()
{
	if ( s_bMaterialsInitialized )
		return;

	s_bMaterialsInitialized = true;

	KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
	pVMTKeyValues->SetInt( "$vertexcolor", 1 );
	s_pWireframe = g_pMaterialSystem->CreateMaterial( "__utilWireframe", pVMTKeyValues );

	pVMTKeyValues = new KeyValues( "wireframe" );
	pVMTKeyValues->SetInt( "$vertexcolor", 1 );
	pVMTKeyValues->SetInt( "$vertexalpha", 1 );
	pVMTKeyValues->SetInt( "$ignorez", 1 );
	s_pWireframeIgnoreZ = g_pMaterialSystem->CreateMaterial( "__utilWireframeIgnoreZ", pVMTKeyValues );

	pVMTKeyValues = new KeyValues( "unlitgeneric" );
	pVMTKeyValues->SetInt( "$vertexcolor", 1 );
	pVMTKeyValues->SetInt( "$vertexalpha", 1 );
	s_pVertexColor = g_pMaterialSystem->CreateMaterial( "__utilVertexColor", pVMTKeyValues );

	pVMTKeyValues = new KeyValues( "unlitgeneric" );
	pVMTKeyValues->SetInt( "$vertexcolor", 1 );
	pVMTKeyValues->SetInt( "$vertexalpha", 1 );
	pVMTKeyValues->SetInt( "$ignorez", 1 );
	s_pVertexColorIgnoreZ = g_pMaterialSystem->CreateMaterial( "__utilVertexColorIgnoreZ", pVMTKeyValues );
}

void ShutdownStandardMaterials()
{
	if ( !s_bMaterialsInitialized )
		return;

	s_bMaterialsInitialized = false;

	s_pWireframe->DecrementReferenceCount();
	s_pWireframe = NULL;

	s_pWireframeIgnoreZ->DecrementReferenceCount();
	s_pWireframeIgnoreZ = NULL;

	s_pVertexColor->DecrementReferenceCount();
	s_pVertexColor = NULL;

	s_pVertexColorIgnoreZ->DecrementReferenceCount();
	s_pVertexColorIgnoreZ = NULL;
}


//-----------------------------------------------------------------------------
// Renders a wireframe sphere
//-----------------------------------------------------------------------------
void RenderWireframeSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer )
{
	InitializeStandardMaterials();

	// Make one more coordinate because (u,v) is discontinuous.
	++nTheta;

	int nVertices = nPhi * nTheta; 
	int nIndices = ( nTheta - 1 ) * 4 * ( nPhi - 1 );

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );

	CMeshBuilder meshBuilder;
	IMesh* pMesh = pRenderContext->GetDynamicMesh();

	meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertices, nIndices );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	int i, j;
	for ( i = 0; i < nPhi; ++i )
	{
		for ( j = 0; j < nTheta; ++j )
		{
			float u = j / ( float )( nTheta - 1 );
			float v = i / ( float )( nPhi - 1 );
			float theta = 2.0f * M_PI * u;
			float phi = M_PI * v;

			meshBuilder.Position3f( vCenter.x + ( flRadius * sin(phi) * cos(theta) ),
				vCenter.y + ( flRadius * sin(phi) * sin(theta) ), 
				vCenter.z + ( flRadius * cos(phi) ) );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.AdvanceVertex();
		}
	}

	for ( i = 0; i < nPhi - 1; ++i )
	{
		for ( j = 0; j < nTheta - 1; ++j )
		{
			int idx = nTheta * i + j;

			meshBuilder.Index( idx );
			meshBuilder.AdvanceIndex();

			meshBuilder.Index( idx + nTheta );
			meshBuilder.AdvanceIndex();

			meshBuilder.Index( idx );
			meshBuilder.AdvanceIndex();

			meshBuilder.Index( idx + 1 );
			meshBuilder.AdvanceIndex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
// Draws a sphere
//-----------------------------------------------------------------------------
void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, IMaterial *pMaterial )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( materials );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	// Two extra degenerate triangles per row (except the last one)
	int nTriangles = 2 * nTheta * ( nPhi - 1 ); 
	int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
	if ( nTriangles == 0 )
		return;

	pRenderContext->Bind( pMaterial );
	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;

	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nTriangles, nIndices );

	// Build the index buffer.
	float flOONPhi = 1.0f / (nPhi-1);
	float flOONTheta = 1.0f / (nTheta-1);

	int i, j;
	for ( i = 0; i < nPhi; ++i )
	{
		for ( j = 0; j < nTheta; ++j )
		{
			float u = j / ( float )( nTheta - 1 );
			float v = i / ( float )( nPhi - 1 );
			float theta = 2.0f * M_PI * u;
			float phi = M_PI * v;

			Vector vecPos;
			vecPos.x = flRadius * sin(phi) * cos(theta);
			vecPos.y = flRadius * sin(phi) * sin(theta); 
			vecPos.z = flRadius * cos(phi);

			Vector vecNormal = vecPos;
			VectorNormalize(vecNormal);

			vecPos += vCenter;

			meshBuilder.Position3f( vecPos.x, vecPos.y, vecPos.z );
			meshBuilder.Normal3f( vecNormal.x, vecNormal.y, vecNormal.z );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.TexCoord2f( 0, j * flOONTheta, i * flOONPhi );
			meshBuilder.AdvanceVertex();
		}
	}

	// Emit the triangle strips.
	int idx = 0;
	for ( i = 0; i < nPhi - 1; ++i )
	{
		for ( j = 0; j < nTheta; ++j )
		{
			idx = nTheta * i + j;

			meshBuilder.Index( idx + nTheta );
			meshBuilder.AdvanceIndex();

			meshBuilder.Index( idx );
			meshBuilder.AdvanceIndex();
		}

		// Emit a degenerate triangle to skip to the next row without a connecting triangle
		if ( i < nPhi - 2 )
		{
			meshBuilder.Index( idx );
			meshBuilder.AdvanceIndex();

			meshBuilder.Index( idx + nTheta + 1 );
			meshBuilder.AdvanceIndex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}

void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer )
{
	IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
	Color cActual( c.r(), c.g(), c.b(), c.a() );
	RenderSphere( vCenter, flRadius, nTheta, nPhi, cActual, pMaterial );
}


//-----------------------------------------------------------------------------
// Box vertices
//-----------------------------------------------------------------------------
static int s_pBoxFaceIndices[6][4] = 
{
	{ 0, 4, 6, 2 }, // -x
	{ 5, 1, 3, 7 }, // +x
	{ 0, 1, 5, 4 }, // -y
	{ 2, 6, 7, 3 }, // +y
	{ 0, 2, 3, 1 },	// -z
	{ 4, 5, 7, 6 }	// +z
};

static int s_pBoxFaceIndicesInsideOut[6][4] = 
{
	{ 0, 2, 6, 4 }, // -x
	{ 5, 7, 3, 1 }, // +x
	{ 0, 4, 5, 1 }, // -y
	{ 2, 3, 7, 6 }, // +y
	{ 0, 1, 3, 2 },	// -z
	{ 4, 6, 7, 5 }	// +z
};

static void GenerateBoxVertices( const Vector &vOrigin, const QAngle& angles, const Vector &vMins, const Vector &vMaxs, Vector pVerts[8] )
{
	// Build a rotation matrix from orientation
	matrix3x4_t fRotateMatrix;
	AngleMatrix( angles, fRotateMatrix );

	Vector vecPos;
	for ( int i = 0; i < 8; ++i )
	{
		vecPos[0] = ( i & 0x1 ) ? vMaxs[0] : vMins[0];
		vecPos[1] = ( i & 0x2 ) ? vMaxs[1] : vMins[1];
		vecPos[2] = ( i & 0x4 ) ? vMaxs[2] : vMins[2];

		VectorRotate( vecPos, fRotateMatrix, pVerts[i] );
		pVerts[i] += vOrigin;
	}
}


//-----------------------------------------------------------------------------
// Renders a wireframe box relative to an origin 
//-----------------------------------------------------------------------------
void RenderWireframeBox( const Vector &vOrigin, const QAngle& angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );

	Vector p[8];
	GenerateBoxVertices( vOrigin, angles, vMins, vMaxs, p );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 24 );

	// Draw the box
	for ( int i = 0; i < 6; i++ )
	{
		int *pFaceIndex = s_pBoxFaceIndices[i];

		for ( int j = 0; j < 4; ++j )
		{
			meshBuilder.Position3fv( p[pFaceIndex[j]].Base() );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.AdvanceVertex();

			meshBuilder.Position3fv( p[pFaceIndex[ (j == 3) ? 0 : j+1 ] ].Base() );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.AdvanceVertex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
// Renders a solid box 
//-----------------------------------------------------------------------------
void RenderBox( const Vector& vOrigin, const QAngle& angles, const Vector& vMins, const Vector& vMaxs, Color c, IMaterial *pMaterial, bool bInsideOut )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->Bind( pMaterial );

	Vector p[8];
	GenerateBoxVertices( vOrigin, angles, vMins, vMaxs, p );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 12 );

	// Draw the box
	Vector vecNormal;
	for ( int i = 0; i < 6; i++ )
	{
		vecNormal.Init();
		vecNormal[ i/2 ] = ( i & 0x1 ) ? 1.0f : -1.0f;
				 
		int *ppFaceIndices = bInsideOut ? s_pBoxFaceIndicesInsideOut[i] : s_pBoxFaceIndices[i];
		for ( int j = 1; j < 3; ++j )
		{
			int i0 = ppFaceIndices[0];
			int i1 = ppFaceIndices[j];
			int i2 = ppFaceIndices[j+1];

			meshBuilder.Position3fv( p[i0].Base() );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.Normal3fv( vecNormal.Base() );
			meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
			meshBuilder.AdvanceVertex();

			meshBuilder.Position3fv( p[i2].Base() );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.Normal3fv( vecNormal.Base() );
			meshBuilder.TexCoord2f( 0, 1.0f, ( j == 1 ) ? 1.0f : 0.0f );
			meshBuilder.AdvanceVertex();

			meshBuilder.Position3fv( p[i1].Base() );
			meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
			meshBuilder.Normal3fv( vecNormal.Base() );
			meshBuilder.TexCoord2f( 0, ( j == 1 ) ? 0.0f : 1.0f, 1.0f );
			meshBuilder.AdvanceVertex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}


void RenderBox( const Vector& vOrigin, const QAngle& angles, const Vector& vMins, const Vector& vMaxs, Color c, bool bZBuffer, bool bInsideOut )
{
	IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
	Color cActual( c.r(), c.g(), c.b(), c.a() );
	RenderBox( vOrigin, angles, vMins, vMaxs, cActual, pMaterial, bInsideOut );
}


//-----------------------------------------------------------------------------
// Renders axes, red->x, green->y, blue->z
//-----------------------------------------------------------------------------
void RenderAxes( const Vector &vOrigin, float flScale, bool bZBuffer )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
	IMesh *pMesh = pRenderContext->GetDynamicMesh( );

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 255, 0, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x + flScale, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 255, 0, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 0, 255, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y + flScale, vOrigin.z );
	meshBuilder.Color4ub( 0, 255, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 0, 0, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z + flScale );
	meshBuilder.Color4ub( 0, 0, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();
	pMesh->Draw();
}


void RenderAxes( const matrix3x4_t &transform, float flScale, bool bZBuffer )
{
	InitializeStandardMaterials();

	Vector xAxis, yAxis, zAxis, vOrigin, temp;
	MatrixGetColumn( transform, 0, xAxis );
	MatrixGetColumn( transform, 1, yAxis );
	MatrixGetColumn( transform, 2, zAxis );
	MatrixGetColumn( transform, 3, vOrigin );

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
	IMesh *pMesh = pRenderContext->GetDynamicMesh( );

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );

	meshBuilder.Position3fv( vOrigin.Base() );
	meshBuilder.Color4ub( 255, 0, 0, 255 );
	meshBuilder.AdvanceVertex();

	VectorMA( vOrigin, flScale, xAxis, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( 255, 0, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 0, 255, 0, 255 );
	meshBuilder.AdvanceVertex();

	VectorMA( vOrigin, flScale, yAxis, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( 0, 255, 0, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
	meshBuilder.Color4ub( 0, 0, 255, 255 );
	meshBuilder.AdvanceVertex();

	VectorMA( vOrigin, flScale, zAxis, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( 0, 0, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
// Render a line
//-----------------------------------------------------------------------------
void RenderLine( const Vector& v1, const Vector& v2, Color c, bool bZBuffer )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );

	meshBuilder.Position3fv( v1.Base() );
	meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( v2.Base() );
	meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
// Draws a triangle
//-----------------------------------------------------------------------------
void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, IMaterial *pMaterial )
{
	InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->Bind( pMaterial );

	unsigned char chRed = c.r();
	unsigned char chGreen = c.g();
	unsigned char chBlue = c.b();
	unsigned char chAlpha = c.a();

	Vector vecNormal;
	Vector vecDelta1, vecDelta2;
	VectorSubtract( p2, p1, vecDelta1 );
	VectorSubtract( p3, p1, vecDelta2 );
	CrossProduct( vecDelta1, vecDelta2, vecNormal );
	VectorNormalize( vecNormal );

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 1 );

	meshBuilder.Position3fv( p1.Base() );
	meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
	meshBuilder.Normal3fv( vecNormal.Base() );
	meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( p2.Base() );
	meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
	meshBuilder.Normal3fv( vecNormal.Base() );
	meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( p3.Base() );
	meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
	meshBuilder.Normal3fv( vecNormal.Base() );
	meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();
	pMesh->Draw();
}


void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, bool bZBuffer )
{
	IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
	Color cActual( c.r(), c.g(), c.b(), c.a() );
	RenderTriangle( p1, p2, p3, cActual, pMaterial );
}



//-----------------------------------------------------------------------------
// Renders an extruded box
//-----------------------------------------------------------------------------
static void DrawAxes( const Vector& origin, Vector* pts, int idx, Color c, CMeshBuilder& meshBuilder )
{
	Vector start, temp;
	VectorAdd( pts[idx], origin, start );
	meshBuilder.Position3fv( start.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	int endidx = (idx & 0x1) ? idx - 1 : idx + 1;
	VectorAdd( pts[endidx], origin, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( start.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	endidx = (idx & 0x2) ? idx - 2 : idx + 2;
	VectorAdd( pts[endidx], origin, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( start.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	endidx = (idx & 0x4) ? idx - 4 : idx + 4;
	VectorAdd( pts[endidx], origin, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();
}

static void DrawExtrusionFace( const Vector& start, const Vector& end,
							  Vector* pts, int idx1, int idx2, Color c, CMeshBuilder& meshBuilder )
{
	Vector temp;
	VectorAdd( pts[idx1], start, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	VectorAdd( pts[idx2], start, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	VectorAdd( pts[idx2], end, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	VectorAdd( pts[idx1], end, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();

	VectorAdd( pts[idx1], start, temp );
	meshBuilder.Position3fv( temp.Base() );
	meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
	meshBuilder.AdvanceVertex();
}

void RenderSweptBox( const Vector &vStart, const Vector &vEnd, const QAngle &angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer )
{
    InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->Bind( bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ );

	Color cActual( c.r(), c.g(), c.b(), c.a() );

	// Build a rotation matrix from angles
	matrix3x4_t fRotateMatrix;
	AngleMatrix( angles, fRotateMatrix );

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 30 );

	Vector vDelta;
	VectorSubtract( vEnd, vStart, vDelta );

	// Compute the box points, rotated but without the origin added
	Vector temp;
	Vector pts[8];
	float dot[8];
	int minidx = 0;
	for ( int i = 0; i < 8; ++i )
	{
		temp.x = (i & 0x1) ? vMaxs[0] : vMins[0];
		temp.y = (i & 0x2) ? vMaxs[1] : vMins[1];
		temp.z = (i & 0x4) ? vMaxs[2] : vMins[2];

		// Rotate the corner point
		VectorRotate( temp, fRotateMatrix, pts[i] );

		// Find the dot product with dir
		dot[i] = DotProduct( pts[i], vDelta );
		if ( dot[i] < dot[minidx] )
		{
			minidx = i;
		}
	}

	// Choose opposite corner
	int maxidx = minidx ^ 0x7;

	// Draw the start + end axes...
	DrawAxes( vStart, pts, minidx, cActual, meshBuilder );
	DrawAxes( vEnd, pts, maxidx, cActual, meshBuilder );

	// Draw the extrusion faces
	for (int j = 0; j < 3; ++j )
	{
		int dirflag1 = ( 1 << ((j+1)%3) );
		int dirflag2 = ( 1 << ((j+2)%3) );

		int idx1, idx2, idx3;
		idx1 = (minidx & dirflag1) ? minidx - dirflag1 : minidx + dirflag1;
		idx2 = (minidx & dirflag2) ? minidx - dirflag2 : minidx + dirflag2;
		idx3 = (minidx & dirflag2) ? idx1 - dirflag2 : idx1 + dirflag2;

		DrawExtrusionFace( vStart, vEnd, pts, idx1, idx3, cActual, meshBuilder );
		DrawExtrusionFace( vStart, vEnd, pts, idx2, idx3, cActual, meshBuilder );
	}
	meshBuilder.End();
	pMesh->Draw();
}

void RenderWireframeSweptBox( const Vector &vStart, const Vector &vEnd, const QAngle &angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer )
{
    InitializeStandardMaterials();

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );

	Color cActual( c.r(), c.g(), c.b(), c.a() );

	// Build a rotation matrix from angles
	matrix3x4_t fRotateMatrix;
	AngleMatrix( angles, fRotateMatrix );

	IMesh *pMesh = pRenderContext->GetDynamicMesh( );
	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 30 );

	Vector vDelta;
	VectorSubtract( vEnd, vStart, vDelta );

	// Compute the box points, rotated but without the origin added
	Vector temp;
	Vector pts[8];
	float dot[8];
	int minidx = 0;
	for ( int i = 0; i < 8; ++i )
	{
		temp.x = (i & 0x1) ? vMaxs[0] : vMins[0];
		temp.y = (i & 0x2) ? vMaxs[1] : vMins[1];
		temp.z = (i & 0x4) ? vMaxs[2] : vMins[2];

		// Rotate the corner point
		VectorRotate( temp, fRotateMatrix, pts[i] );

		// Find the dot product with dir
		dot[i] = DotProduct( pts[i], vDelta );
		if ( dot[i] < dot[minidx] )
		{
			minidx = i;
		}
	}

	// Choose opposite corner
	int maxidx = minidx ^ 0x7;

	// Draw the start + end axes...
	DrawAxes( vStart, pts, minidx, cActual, meshBuilder );
	DrawAxes( vEnd, pts, maxidx, cActual, meshBuilder );

	// Draw the extrusion faces
	for (int j = 0; j < 3; ++j )
	{
		int dirflag1 = ( 1 << ((j+1)%3) );
		int dirflag2 = ( 1 << ((j+2)%3) );

		int idx1, idx2, idx3;
		idx1 = (minidx & dirflag1) ? minidx - dirflag1 : minidx + dirflag1;
		idx2 = (minidx & dirflag2) ? minidx - dirflag2 : minidx + dirflag2;
		idx3 = (minidx & dirflag2) ? idx1 - dirflag2 : idx1 + dirflag2;

		DrawExtrusionFace( vStart, vEnd, pts, idx1, idx3, cActual, meshBuilder );
		DrawExtrusionFace( vStart, vEnd, pts, idx2, idx3, cActual, meshBuilder );
	}
	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
// Draws a axis-aligned quad
//-----------------------------------------------------------------------------
void RenderQuad( IMaterial *pMaterial, float x, float y, float w, float h, 
	float z, float s0, float t0, float s1, float t1, const Color& clr )
{
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );

	meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
	meshBuilder.TexCoord2f( 0, s0, t0 );
	meshBuilder.Position3f( x, y, z );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
	meshBuilder.TexCoord2f( 0, s1, t0 );
	meshBuilder.Position3f( x + w, y, z );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
	meshBuilder.TexCoord2f( 0, s1, t1 );
	meshBuilder.Position3f( x + w, y + h, z );
	meshBuilder.AdvanceVertex();

	meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
	meshBuilder.TexCoord2f( 0, s0, t1 );
	meshBuilder.Position3f( x, y + h, z );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();

	pMesh->Draw();
}

//-----------------------------------------------------------------------------
// Renders a screen space quad
//-----------------------------------------------------------------------------

void DrawScreenSpaceRectangle( IMaterial *pMaterial, 
								  int nDestX, int nDestY, int nWidth, int nHeight,	// Rect to draw into in screen space
								  float flSrcTextureX0, float flSrcTextureY0,		// which texel you want to appear at destx/y
								  float flSrcTextureX1, float flSrcTextureY1,		// which texel you want to appear at destx+width-1, desty+height-1
								  int nSrcTextureWidth, int nSrcTextureHeight,		// needed for fixup
								  void *pClientRenderable,							// Used to pass to the bind proxies
								  int nXDice, int nYDice,							// Amount to tessellate the mesh
								  float fDepth )									// what Z value to put in the verts (def 0.0)
{
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );

	if ( ( nWidth <= 0 ) || ( nHeight <= 0 ) )
		return;

	tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );

	pRenderContext->MatrixMode( MATERIAL_VIEW );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();

	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();

	pRenderContext->Bind( pMaterial, pClientRenderable );

	int xSegments = max( nXDice, 1);
	int ySegments = max( nYDice, 1);

	CMeshBuilder meshBuilder;
	
	IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
	meshBuilder.Begin( pMesh, MATERIAL_QUADS, xSegments * ySegments );

	int nScreenWidth, nScreenHeight;
	pRenderContext->GetRenderTargetDimensions( nScreenWidth, nScreenHeight );

	float flOffset = 0.5f;
	
	float flLeftX = nDestX - flOffset;
	float flRightX = nDestX + nWidth - flOffset;

	float flTopY = nDestY - flOffset;
	float flBottomY = nDestY + nHeight - flOffset;

	float flSubrectWidth = flSrcTextureX1 - flSrcTextureX0;
	float flSubrectHeight = flSrcTextureY1 - flSrcTextureY0;

	float flTexelsPerPixelX = ( nWidth > 1 ) ? flSubrectWidth / ( nWidth - 1 ) : 0.0f;
	float flTexelsPerPixelY = ( nHeight > 1 ) ? flSubrectHeight / ( nHeight - 1 ) : 0.0f;

	float flLeftU = flSrcTextureX0 + 0.5f - ( 0.5f * flTexelsPerPixelX );
	float flRightU = flSrcTextureX1 + 0.5f + ( 0.5f * flTexelsPerPixelX );
	float flTopV = flSrcTextureY0 + 0.5f - ( 0.5f * flTexelsPerPixelY );
	float flBottomV = flSrcTextureY1 + 0.5f + ( 0.5f * flTexelsPerPixelY );

	float flOOTexWidth = 1.0f / nSrcTextureWidth;
	float flOOTexHeight = 1.0f / nSrcTextureHeight;
	flLeftU *= flOOTexWidth;
	flRightU *= flOOTexWidth;
	flTopV *= flOOTexHeight;
	flBottomV *= flOOTexHeight;

	// Get the current viewport size
	int vx, vy, vw, vh;
	pRenderContext->GetViewport( vx, vy, vw, vh );

	// map from screen pixel coords to -1..1
	flRightX = FLerp( -1, 1, 0, vw, flRightX );
	flLeftX = FLerp( -1, 1, 0, vw, flLeftX );
	flTopY = FLerp( 1, -1, 0, vh ,flTopY );
	flBottomY = FLerp( 1, -1, 0, vh, flBottomY );

	// Dice the quad up...
	if ( ( xSegments > 1 ) || ( ySegments > 1 ) )
	{
		// Screen height and width of a subrect
		float flWidth  = (flRightX - flLeftX) / (float) xSegments;
		float flHeight = (flTopY - flBottomY) / (float) ySegments;

		// UV height and width of a subrect
		float flUWidth  = (flRightU - flLeftU) / (float) xSegments;
		float flVHeight = (flBottomV - flTopV) / (float) ySegments;

		for ( int x=0; x < xSegments; x++ )
		{
			for ( int y=0; y < ySegments; y++ )
			{
				// Top left
				meshBuilder.Position3f( flLeftX   + (float) x * flWidth, flTopY - (float) y * flHeight, fDepth );
				meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
				meshBuilder.TexCoord2f( 0, flLeftU   + (float) x * flUWidth, flTopV + (float) y * flVHeight);
				meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
				meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
				meshBuilder.AdvanceVertex();

				// Top right (x+1)
				meshBuilder.Position3f( flLeftX   + (float) (x+1) * flWidth, flTopY - (float) y * flHeight, fDepth );
				meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
				meshBuilder.TexCoord2f( 0, flLeftU   + (float) (x+1) * flUWidth, flTopV + (float) y * flVHeight);
				meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
				meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
				meshBuilder.AdvanceVertex();

				// Bottom right (x+1), (y+1)
				meshBuilder.Position3f( flLeftX   + (float) (x+1) * flWidth, flTopY - (float) (y+1) * flHeight, fDepth );
				meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
				meshBuilder.TexCoord2f( 0, flLeftU   + (float) (x+1) * flUWidth, flTopV + (float)(y+1) * flVHeight);
				meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
				meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
				meshBuilder.AdvanceVertex();

				// Bottom left (y+1)
				meshBuilder.Position3f( flLeftX   + (float) x * flWidth, flTopY - (float) (y+1) * flHeight, fDepth );
				meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
				meshBuilder.TexCoord2f( 0, flLeftU   + (float) x * flUWidth, flTopV + (float)(y+1) * flVHeight);
				meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
				meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
				meshBuilder.AdvanceVertex();
			}
		}
	}
	else // just one quad
	{
		for ( int corner=0; corner<4; corner++ )
		{
			bool bLeft = (corner==0) || (corner==3);
			meshBuilder.Position3f( (bLeft) ? flLeftX : flRightX, (corner & 2) ? flBottomY : flTopY, fDepth );
			meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
			meshBuilder.TexCoord2f( 0, (bLeft) ? flLeftU : flRightU, (corner & 2) ? flBottomV : flTopV );
			meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
			meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
			meshBuilder.AdvanceVertex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();

	pRenderContext->MatrixMode( MATERIAL_VIEW );
	pRenderContext->PopMatrix();

	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PopMatrix();
}
