//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:	Debugging overlay functions
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//===========================================================================//

#include "render_pch.h"
#include "edict.h"
#include "client.h"
#include "debugoverlay.h"
#include "cdll_int.h"
#include "ivideomode.h"
#include "materialsystem/imesh.h"
#include "gl_matsysiface.h"
#include "server.h"
#include "client_class.h"
#include "icliententitylist.h"
#include "mathlib/vmatrix.h"
#include "icliententity.h"
#include "overlaytext.h"
#include "engine/ivdebugoverlay.h"
#include "cmodel_engine.h"
#include "vphysics_interface.h"
#include "materialsystem/imaterial.h"
#include "tier2/renderutils.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern edict_t *EDICT_NUM(int n);

ConVar enable_debug_overlays( "enable_debug_overlays", "1", FCVAR_GAMEDLL | FCVAR_CHEAT, "Enable rendering of debug overlays" );

int GetOverlayTick()
{
	if ( sv.IsActive() )
		return sv.m_nTickCount;

	return cl.GetClientTickCount();
}

bool OverlayText_t::IsDead()
{
	if ( IsXbox() && cl.IsPaused() )
		return false;

	if ( m_nServerCount != cl.m_nServerCount )
		return true;

	if ( m_nCreationTick != -1 )
	{
		if ( GetOverlayTick() > m_nCreationTick )
			return true;

		return false;
	}

	if ( m_flEndTime == NDEBUG_PERSIST_TILL_NEXT_SERVER )
		return false;

	return (cl.GetTime() >= m_flEndTime);
}

void OverlayText_t::SetEndTime( float duration )
{
	m_nServerCount = cl.m_nServerCount;

	if ( duration <= 0.0f )
	{
		m_flEndTime = 0.0f;
		m_nCreationTick = GetOverlayTick();
		return;
	}

	if ( duration == NDEBUG_PERSIST_TILL_NEXT_SERVER )
	{
		m_flEndTime = NDEBUG_PERSIST_TILL_NEXT_SERVER;
	}
	else
	{
	m_flEndTime = cl.GetTime() + duration;
	}
}

namespace CDebugOverlay
{

enum OverlayType_t
{
	OVERLAY_BOX = 0,
	OVERLAY_SPHERE,
	OVERLAY_LINE,
	OVERLAY_TRIANGLE,
	OVERLAY_SWEPT_BOX,
	OVERLAY_BOX2
};

struct OverlayBase_t
{
	OverlayBase_t()
	{
		m_Type = OVERLAY_BOX;
		m_nServerCount = -1;
		m_nCreationTick = -1;
		m_flEndTime = 0.0f;
		m_pNextOverlay = NULL;
	}

	bool IsDead()
	{
		if ( IsXbox() && cl.IsPaused() )
			return false;

		if ( m_nServerCount != cl.m_nServerCount )
			return true;

		if ( m_nCreationTick != -1 )
		{
			if ( GetOverlayTick() > m_nCreationTick )
				return true;

			return false;
		}

		if ( m_flEndTime == NDEBUG_PERSIST_TILL_NEXT_SERVER )
			return false;

		return (cl.GetTime() >= m_flEndTime) ;
	}

	void SetEndTime( float duration )
	{
		m_nServerCount = cl.m_nServerCount;

		if ( duration <= 0.0f )
		{
			m_nCreationTick = GetOverlayTick();	// stay alive for only one frame
			return;
		}
		
		if ( duration == NDEBUG_PERSIST_TILL_NEXT_SERVER )
		{
			m_flEndTime = NDEBUG_PERSIST_TILL_NEXT_SERVER;
		}
		else
		{
			m_flEndTime = cl.GetTime() + duration;
		}
	}

	OverlayType_t	m_Type;				// What type of overlay is it?
	int				m_nCreationTick;	// Duration -1 means go away after this frame #
	int				m_nServerCount;		// Latch server count, too
	float			m_flEndTime;		// When does this box go away
	OverlayBase_t	*m_pNextOverlay;
};

struct OverlayBox_t : public OverlayBase_t
{
	OverlayBox_t() { m_Type = OVERLAY_BOX; }

	Vector			origin;
	Vector			mins;
	Vector			maxs;
	QAngle			angles;
	int				r;
	int				g;
	int				b;
	int				a;
};

struct OverlayBox2_t : public OverlayBase_t
{
	OverlayBox2_t() { m_Type = OVERLAY_BOX2; }

	Vector			origin;
	Vector			mins;
	Vector			maxs;
	QAngle			angles;
	Color			edgeColor;
	Color			faceColor;
};

struct OverlaySphere_t : public OverlayBase_t
{
	OverlaySphere_t() { m_Type = OVERLAY_SPHERE; }

	Vector			vOrigin;
	float			flRadius;
	int				nTheta;
	int				nPhi;
	int				r;
	int				g;
	int				b;
	int				a;
};

struct OverlayLine_t : public OverlayBase_t 
{
	OverlayLine_t() { m_Type = OVERLAY_LINE; }

	Vector			origin;
	Vector			dest;
	int				r;
	int				g;
	int				b;
	int				a;
	bool			noDepthTest;
};

struct OverlayTriangle_t : public OverlayBase_t 
{
	OverlayTriangle_t() { m_Type = OVERLAY_TRIANGLE; }

	Vector			p1;
	Vector			p2;
	Vector			p3;
	int				r;
	int				g;
	int				b;
	int				a;
	bool			noDepthTest;
};

struct OverlaySweptBox_t : public OverlayBase_t 
{
	OverlaySweptBox_t() { m_Type = OVERLAY_SWEPT_BOX; }

	Vector			start;
	Vector			end;
	Vector			mins;
	Vector			maxs;
	QAngle			angles;
	int				r;
	int				g;
	int				b;
	int				a;
};


//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
void DrawOverlays();
void DrawGridOverlay();
void ClearAllOverlays();
void ClearDeadOverlays();


//-----------------------------------------------------------------------------
// Init static member variables
//-----------------------------------------------------------------------------
OverlayText_t*	s_pOverlayText = NULL;	// text is handled differently; for backward compatibility reasons
OverlayBase_t*	s_pOverlays = NULL; 
Vector			s_vGridPosition(0,0,0);
bool			s_bDrawGrid = false;
CThreadFastMutex s_OverlayMutex;


//-----------------------------------------------------------------------------
// Purpose: Hack to allow this code to run on a client that's not connected to a server
//  (i.e., demo playback, or multiplayer game )
// Input  : ent_num - 
//			origin - 
//			mins - 
//			maxs - 
// Output : static void
//-----------------------------------------------------------------------------
static bool GetEntityOriginClientOrServer( int ent_num, Vector& origin )
{
	AUTO_LOCK( s_OverlayMutex );
	// Assume failure
	origin.Init();

	if ( sv.IsActive() )
	{
		edict_t *e = EDICT_NUM( ent_num );
		if ( e )
		{
			IServerEntity *serverEntity = e->GetIServerEntity();
			if ( serverEntity )
			{
				CM_WorldSpaceCenter( serverEntity->GetCollideable(), &origin );
			}

			return true;
		}
	}
	else
	{
		IClientEntity *clent = entitylist->GetClientEntity( ent_num );
		if ( clent )
		{
			CM_WorldSpaceCenter( clent->GetCollideable(), &origin );
			return true;
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: Given a point, return the screen position
// Input  :
// Output :
//-----------------------------------------------------------------------------
int ScreenPosition(const Vector& point, Vector& screen)
{
	AUTO_LOCK( s_OverlayMutex );
	CMatRenderContextPtr pRenderContext( materials );

	int retval = g_EngineRenderer->ClipTransform(point,&screen);
	
	int x, y, w, h;
	pRenderContext->GetViewport( x, y, w, h );

	screen[0] =  0.5 * screen[0] * w;
	screen[1] = -0.5 * screen[1] * h;
	screen[0] += 0.5 * w;
	screen[1] += 0.5 * h;
	return retval;
}

//-----------------------------------------------------------------------------
// Purpose: Given an xy screen pos (0-1), return the screen position 
// Input  :
// Output :
//-----------------------------------------------------------------------------
int ScreenPosition(float flXPos, float flYPos, Vector& screen)
{
	if (flXPos > 1.0 || flYPos > 1.0 || flXPos < 0.0 || flYPos < 0.0 )
		return 1; // Fail

	AUTO_LOCK( s_OverlayMutex );
	CMatRenderContextPtr pRenderContext( materials );

	int x, y, w, h;
	pRenderContext->GetViewport( x, y, w, h );

	screen[0] =  flXPos * w;
	screen[1] =  flYPos * h;
	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: Add new entity positioned overlay text
// Input  : Entity to attach text to
//			How many lines to offset text from entity origin
//			The text to print
// Output :
//-----------------------------------------------------------------------------
void AddEntityTextOverlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *text)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	Vector myPos, myMins, myMaxs;

	GetEntityOriginClientOrServer( ent_index, myPos );

	VectorCopy(myPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= line_offset;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= r;
	new_overlay->g			= g;
	new_overlay->b			= b;
	new_overlay->a			= a;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay text
// Input  : Position of text & text
// Output :
//-----------------------------------------------------------------------------
void AddGridOverlay(const Vector& vPos) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	s_vGridPosition[0]		= vPos[0];
	s_vGridPosition[1]		= vPos[1];
	s_vGridPosition[2]		= vPos[2];
	s_bDrawGrid				= true;
}

void AddCoordFrameOverlay(const matrix3x4_t& frame, float flScale, int vColorTable[3][3]/*=NULL*/)
{
	static int s_defaultColorTable[3][3] =
	{
		{ 255,   0, 0   },
		{ 0  , 255, 0   },
		{ 0  ,   0, 255 }
	};

	AUTO_LOCK( s_OverlayMutex );
	if ( vColorTable == NULL )
		vColorTable = s_defaultColorTable;

	Vector startPt, endPt;
	MatrixGetColumn( frame, 3, startPt );

	for (int k = 0; k < 3; k++)
	{
		endPt.x = frame[0][3] + frame[0][k] * flScale;
		endPt.y = frame[1][3] + frame[1][k] * flScale;
		endPt.z = frame[2][3] + frame[2][k] * flScale;

		AddLineOverlay(
			startPt,
			endPt,
			vColorTable[k][0], vColorTable[k][1], vColorTable[k][2], 255,
			true,
			NDEBUG_PERSIST_TILL_NEXT_SERVER
		);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay text
// Input  : Position of text & text
// Output :
//-----------------------------------------------------------------------------
void AddTextOverlay(const Vector& textPos, float duration, const char *text) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	VectorCopy(textPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= 0;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= 255;
	new_overlay->g			= 255;
	new_overlay->b			= 255;
	new_overlay->a			= 255;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay text
// Input  : Position of text & text
// Output :
//-----------------------------------------------------------------------------
void AddTextOverlay(const Vector& textPos, float duration, float alpha, const char *text) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	VectorCopy(textPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= 0;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= 255;
	new_overlay->g			= 255;
	new_overlay->b			= 255;
	new_overlay->a			= (int)clamp(alpha * 255.f,0.f,255.f);

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

//------------------------------------------------------------------------------
// Purpose : 
// Input   :
// Output  :
//------------------------------------------------------------------------------
void AddScreenTextOverlay(float flXPos, float flYPos, int line_offset, float duration, int r, int g, int b, int a, const char *text)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->flXPos		= flXPos;
	new_overlay->flYPos		= flYPos;
	new_overlay->bUseOrigin = false;
	new_overlay->lineOffset	= line_offset;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= r;
	new_overlay->g			= g;
	new_overlay->b			= b;
	new_overlay->a			= a;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

void AddScreenTextOverlay( float flXPos, float flYPos, float duration, int r, int g, int b, int a, const char *text )
{
	AddScreenTextOverlay( flXPos, flYPos, 0, duration, r, g, b, a, text );
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay text
// Input  : Position of text 
//			How many lines to offset text from position
//			ext
// Output :
//-----------------------------------------------------------------------------
void AddTextOverlay(const Vector& textPos, int line_offset, float duration, const char *text) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	VectorCopy(textPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= line_offset;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= 255;
	new_overlay->g			= 255;
	new_overlay->b			= 255;
	new_overlay->a			= 255;
	new_overlay->bUseOrigin = true;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

void AddTextOverlay(const Vector& textPos, int line_offset, float duration, float alpha, const char *text)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	VectorCopy(textPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= line_offset;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= 255;
	new_overlay->g			= 255;
	new_overlay->b			= 255;
	new_overlay->a			= (int)clamp(alpha * 255.f,0.f,255.f);
	new_overlay->bUseOrigin = true;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

void AddTextOverlay(const Vector& textPos, int line_offset, float duration, float r, float g, float b, float alpha, const char *text)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t *new_overlay = new OverlayText_t;

	VectorCopy(textPos,new_overlay->origin);
	Q_strncpy(new_overlay->text,text, sizeof( new_overlay->text ) );
	new_overlay->bUseOrigin = true;
	new_overlay->lineOffset	= line_offset;
	new_overlay->SetEndTime( duration );
	new_overlay->r			= (int)clamp(r * 255.f,0.f,255.f);
	new_overlay->g			= (int)clamp(g * 255.f,0.f,255.f);
	new_overlay->b			= (int)clamp(b * 255.f,0.f,255.f);
	new_overlay->a			= (int)clamp(alpha * 255.f,0.f,255.f);
	new_overlay->bUseOrigin = true;

	new_overlay->nextOverlayText = s_pOverlayText;
	s_pOverlayText = new_overlay;
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay box
// Input  : Position of box
//			size of box
//			angles of box
//			color & alpha
//			duration
// Output :
//-----------------------------------------------------------------------------
void AddBoxOverlay(const Vector& origin, const Vector& mins, const Vector& maxs, QAngle const& angles, int r, int g, int b, int a, float flDuration) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayBox_t *new_overlay = new OverlayBox_t;

	new_overlay->origin = origin;

	new_overlay->mins[0] = mins[0];
	new_overlay->mins[1] = mins[1];
	new_overlay->mins[2] = mins[2];

	new_overlay->maxs[0] = maxs[0];
	new_overlay->maxs[1] = maxs[1];
	new_overlay->maxs[2] = maxs[2];

	new_overlay->angles = angles;

	new_overlay->r = r;
	new_overlay->g = g;
	new_overlay->b = b;
	new_overlay->a = a;

	new_overlay->SetEndTime( flDuration );

	new_overlay->m_pNextOverlay = s_pOverlays;
	s_pOverlays = new_overlay;
}

void AddBoxOverlay2( const Vector& origin, const Vector& mins, const Vector& maxs, QAngle const& orientation, const Color& faceColor, const Color& edgeColor, float duration )
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayBox2_t *new_overlay = new OverlayBox2_t;

	new_overlay->origin = origin;

	new_overlay->mins[0] = mins[0];
	new_overlay->mins[1] = mins[1];
	new_overlay->mins[2] = mins[2];

	new_overlay->maxs[0] = maxs[0];
	new_overlay->maxs[1] = maxs[1];
	new_overlay->maxs[2] = maxs[2];

	new_overlay->angles = orientation;

	new_overlay->faceColor = faceColor;
	new_overlay->edgeColor = edgeColor;

	new_overlay->SetEndTime( duration );

	new_overlay->m_pNextOverlay = s_pOverlays;
	s_pOverlays = new_overlay;
}

//-----------------------------------------------------------------------------
// Purpose: Add new overlay sphere
// Input  : Position of sphere
//			radius of sphere
//			color & alpha
//			duration
// Output :
//-----------------------------------------------------------------------------
void AddSphereOverlay(const Vector& vOrigin, float flRadius, int nTheta, int nPhi, int r, int g, int b, int a, float flDuration)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlaySphere_t *new_overlay = new OverlaySphere_t;

	new_overlay->vOrigin = vOrigin;
	new_overlay->flRadius = flRadius;

	new_overlay->nTheta = nTheta;
	new_overlay->nPhi = nPhi;

	new_overlay->r = r;
	new_overlay->g = g;
	new_overlay->b = b;
	new_overlay->a = a;

	new_overlay->SetEndTime( flDuration );

	new_overlay->m_pNextOverlay = s_pOverlays;
	s_pOverlays = new_overlay;
}


void AddSweptBoxOverlay(const Vector& start, const Vector& end, 
	const Vector& mins, const Vector& maxs, QAngle const& angles, int r, int g, int b, int a, float flDuration)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlaySweptBox_t *new_overlay = new OverlaySweptBox_t;

	new_overlay->start = start;
	new_overlay->end = end;

	new_overlay->mins[0] = mins[0];
	new_overlay->mins[1] = mins[1];
	new_overlay->mins[2] = mins[2];

	new_overlay->maxs[0] = maxs[0];
	new_overlay->maxs[1] = maxs[1];
	new_overlay->maxs[2] = maxs[2];

	new_overlay->angles = angles;

	new_overlay->r = r;
	new_overlay->g = g;
	new_overlay->b = b;
	new_overlay->a = a;

	new_overlay->SetEndTime( flDuration );

	new_overlay->m_pNextOverlay = s_pOverlays;
	s_pOverlays = new_overlay;
}


//-----------------------------------------------------------------------------
// Purpose: Add new overlay text
// Input  : Entity to attach text to
//			How many lines to offset text from entity origin
//			The text to print
// Output :
//-----------------------------------------------------------------------------
void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b, int a, bool noDepthTest, float flDuration) 
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayLine_t *new_loverlay = new OverlayLine_t;

	new_loverlay->origin[0] = origin[0];
	new_loverlay->origin[1] = origin[1];
	new_loverlay->origin[2] = origin[2];

	new_loverlay->dest[0] = dest[0];
	new_loverlay->dest[1] = dest[1];
	new_loverlay->dest[2] = dest[2];

	new_loverlay->r = r;
	new_loverlay->g = g;
	new_loverlay->b = b;
	new_loverlay->a = a;

	new_loverlay->noDepthTest = noDepthTest;

	new_loverlay->SetEndTime( flDuration );

	new_loverlay->m_pNextOverlay = s_pOverlays;
	s_pOverlays = new_loverlay;
}


//-----------------------------------------------------------------------------
// Purpose: Add new triangle overlay
//-----------------------------------------------------------------------------
void AddTriangleOverlay(const Vector& p1, const Vector& p2, const Vector &p3, 
				int r, int g, int b, int a, bool noDepthTest, float flDuration)
{
	if ( cl.IsPaused() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayTriangle_t *pTriangle = new OverlayTriangle_t;
	pTriangle->p1 = p1;
	pTriangle->p2 = p2;
	pTriangle->p3 = p3;

	pTriangle->r = r;
	pTriangle->g = g;
	pTriangle->b = b;
	pTriangle->a = a;

	pTriangle->noDepthTest = noDepthTest;
	pTriangle->SetEndTime( flDuration );

	pTriangle->m_pNextOverlay = s_pOverlays;
	s_pOverlays = pTriangle;
}


//------------------------------------------------------------------------------
// Purpose :	Draw a grid around the s_vGridPosition
// Input   :
// Output  :
//------------------------------------------------------------------------------
void DrawGridOverlay(void)
{
	AUTO_LOCK( s_OverlayMutex );
	static int gridSpacing		= 100;
	static int numHorzSpaces	= 16;
	static int numVertSpaces	= 3;

	Vector startGrid;
	startGrid[0] = gridSpacing*((int)s_vGridPosition[0]/gridSpacing);
	startGrid[1] = gridSpacing*((int)s_vGridPosition[1]/gridSpacing);
	startGrid[2] = s_vGridPosition[2];

	// Shift to the left
	startGrid[0] -= (numHorzSpaces/2)*gridSpacing;
	startGrid[1] -= (numHorzSpaces/2)*gridSpacing;

	Vector color( 20, 180, 190 );
	for (int i=1;i<numVertSpaces+1;i++)
	{
		// Draw x axis lines
		Vector startLine;
		VectorCopy(startGrid,startLine);
		for (int j=0;j<numHorzSpaces+1;j++)
		{
			Vector endLine;
			VectorCopy(startLine,endLine);
			endLine[0] += gridSpacing*numHorzSpaces;
			RenderLine( startLine, endLine, Color( color.x, color.y, color.z, 255 ), true );

			Vector bottomStartLine;
			VectorCopy(startLine,bottomStartLine);
			for ( int k=0; k<numHorzSpaces+1; k++ )
			{
				Vector bottomEndLine;
				VectorCopy(bottomStartLine,bottomEndLine);
				bottomEndLine[2] -= gridSpacing;
				RenderLine( bottomStartLine, bottomEndLine, Color( color.x, color.y, color.z, 255 ), true );
				bottomStartLine[0] += gridSpacing;
			}
			startLine[1] += gridSpacing;
		}

		// Draw y axis lines
		VectorCopy(startGrid,startLine);
		for ( int j=0; j<numHorzSpaces+1; j++ )
		{
			Vector endLine;
			VectorCopy(startLine,endLine);

			endLine[1] += gridSpacing*numHorzSpaces;
			RenderLine( startLine, endLine, Color( color.x, color.y, color.z, 255 ), true );
			startLine[0] += gridSpacing;
		}
		VectorScale( color, 0.7, color );
		startGrid[2] -= gridSpacing;
	}
	s_bDrawGrid	= false;
}


//------------------------------------------------------------------------------
// Draws a generic overlay
//------------------------------------------------------------------------------
void DrawOverlay( OverlayBase_t *pOverlay )
{
	AUTO_LOCK( s_OverlayMutex );
	switch( pOverlay->m_Type)
	{
	case OVERLAY_LINE:
		{
			// Draw the line
			OverlayLine_t *pLine = static_cast<OverlayLine_t*>(pOverlay);
			RenderLine( pLine->origin, pLine->dest, Color( pLine->r, pLine->g, pLine->b, pLine->a ), !pLine->noDepthTest);
		}
		break;

	case OVERLAY_BOX:
		{
			// Draw the box
			OverlayBox_t *pCurrBox = static_cast<OverlayBox_t*>(pOverlay);
			if ( pCurrBox->a > 0 ) 
			{
				RenderBox( pCurrBox->origin, pCurrBox->angles, pCurrBox->mins, pCurrBox->maxs, Color( pCurrBox->r, pCurrBox->g, pCurrBox->b, pCurrBox->a ), false );
			}
			RenderWireframeBox( pCurrBox->origin, pCurrBox->angles, pCurrBox->mins, pCurrBox->maxs, Color( pCurrBox->r, pCurrBox->g, pCurrBox->b, 255 ), true );
		}
		break;

	case OVERLAY_BOX2:
		{
			// Draw the box
			OverlayBox2_t *pCurrBox = static_cast<OverlayBox2_t*>(pOverlay);
			if ( pCurrBox->faceColor.a() > 0 ) 
			{
				RenderBox( pCurrBox->origin, pCurrBox->angles, pCurrBox->mins, pCurrBox->maxs, pCurrBox->faceColor, false );
			}
			if ( pCurrBox->edgeColor.a() > 0 )
			{
				RenderWireframeBox( pCurrBox->origin, pCurrBox->angles, pCurrBox->mins, pCurrBox->maxs, pCurrBox->edgeColor, false );
			}
		}
		break;

	case OVERLAY_SPHERE:
		{
			// Draw the sphere
			OverlaySphere_t *pSphere = static_cast<OverlaySphere_t*>(pOverlay);
			RenderSphere( pSphere->vOrigin, pSphere->flRadius, pSphere->nTheta, pSphere->nPhi, Color( pSphere->r, pSphere->g, pSphere->b, pSphere->a ), g_pMaterialAmbientCube );
		}
		break;

	case OVERLAY_SWEPT_BOX:
		{
			OverlaySweptBox_t *pBox = static_cast<OverlaySweptBox_t*>(pOverlay);
			RenderWireframeSweptBox( pBox->start, pBox->end, pBox->angles, pBox->mins, pBox->maxs, Color( pBox->r, pBox->g, pBox->b, pBox->a ), true );
		}
		break;

	case OVERLAY_TRIANGLE:
		{
			OverlayTriangle_t *pTriangle = static_cast<OverlayTriangle_t*>(pOverlay);
			RenderTriangle( pTriangle->p1, pTriangle->p2, pTriangle->p3, 
				Color( pTriangle->r, pTriangle->g, pTriangle->b, pTriangle->a ), !pTriangle->noDepthTest );
		}
		break;

	default:
		Assert(0);
	}
}

void DestroyOverlay( OverlayBase_t *pOverlay )
{
	AUTO_LOCK( s_OverlayMutex );
	switch( pOverlay->m_Type)
	{
	case OVERLAY_LINE:
		{
			OverlayLine_t *pCurrLine = static_cast<OverlayLine_t*>(pOverlay);
			delete pCurrLine;
		}
		break;

	case OVERLAY_BOX:
		{
			OverlayBox_t *pCurrBox = static_cast<OverlayBox_t*>(pOverlay);
			delete pCurrBox;
		}
		break;

	
	case OVERLAY_BOX2:
		{
			OverlayBox2_t *pCurrBox = static_cast<OverlayBox2_t*>(pOverlay);
			delete pCurrBox;
		}
		break;

	case OVERLAY_SPHERE:
		{
			OverlaySphere_t *pCurrSphere = static_cast<OverlaySphere_t*>(pOverlay);
			delete pCurrSphere;
		}
		break;

	case OVERLAY_SWEPT_BOX:
		{
			OverlaySweptBox_t *pCurrBox = static_cast<OverlaySweptBox_t*>(pOverlay);
			delete pCurrBox;
		}
		break;

	case OVERLAY_TRIANGLE:
		{
			OverlayTriangle_t *pTriangle = static_cast<OverlayTriangle_t*>(pOverlay);
			delete pTriangle;
		}
		break;

	default:
		Assert(0);
	}
}


//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void DrawAllOverlays(void)
{
	if ( !enable_debug_overlays.GetBool() )
		return;

	AUTO_LOCK( s_OverlayMutex );
	OverlayBase_t* pCurrOverlay = s_pOverlays;
	OverlayBase_t* pPrevOverlay = NULL;
	OverlayBase_t* pNextOverlay;

	while (pCurrOverlay) 
	{
		// Is it time to kill this overlay?
		if ( pCurrOverlay->IsDead() )
		{
			if (pPrevOverlay)
			{
				// If I had a last overlay reset it's next pointer
				pPrevOverlay->m_pNextOverlay = pCurrOverlay->m_pNextOverlay;
			}
			else
			{
				// If the first line, reset the s_pOverlays pointer
				s_pOverlays = pCurrOverlay->m_pNextOverlay;
			}

			pNextOverlay = pCurrOverlay->m_pNextOverlay;
			DestroyOverlay( pCurrOverlay );
			pCurrOverlay = pNextOverlay;
		}
		else
		{
			DrawOverlay( pCurrOverlay );
			pPrevOverlay = pCurrOverlay;
			pCurrOverlay = pCurrOverlay->m_pNextOverlay;
		}
	}
}

//------------------------------------------------------------------------------
// Purpose : Remove from the linkedlist overlays that have the special "until next
//			 server tick" frametime
// Input   :
// Output  :
//------------------------------------------------------------------------------
//0.01234f 
void PurgeServerOverlays( void )
{
	AUTO_LOCK( s_OverlayMutex );
	OverlayBase_t* pCurrOverlay = s_pOverlays;

	while (pCurrOverlay)
	{
		if ( pCurrOverlay->m_flEndTime == NDEBUG_PERSIST_TILL_NEXT_SERVER )
		{
			pCurrOverlay->m_flEndTime = cl.GetTime() + host_state.interval_per_tick;
		}

		pCurrOverlay = pCurrOverlay->m_pNextOverlay;
	}

	OverlayText_t* pCurrText = s_pOverlayText;
	while (pCurrText)
	{
		if ( pCurrText->m_flEndTime == NDEBUG_PERSIST_TILL_NEXT_SERVER )
		{
			pCurrText->m_flEndTime = cl.GetTime() + host_state.interval_per_tick;
		}

		pCurrText = pCurrText->nextOverlayText;
	}
}

void PurgeTextOverlays( void )
{
	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t* pCurrOverlay = s_pOverlayText;
	while ( pCurrOverlay ) 
	{
		if ( pCurrOverlay->m_flEndTime == 0.0f &&
			 pCurrOverlay->m_nCreationTick != -1 )
		{
			pCurrOverlay->m_nCreationTick = 0;
		}
		pCurrOverlay = pCurrOverlay->nextOverlayText;
	}
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void Draw3DOverlays(void)
{
	// Clear overlays every frame
	AUTO_LOCK( s_OverlayMutex );
	static int previous_servercount = 0;
	if ( previous_servercount != cl.m_nServerCount )
	{
		ClearAllOverlays();
		previous_servercount = cl.m_nServerCount;
	}

	DrawAllOverlays();

	if (s_bDrawGrid)
	{
		DrawGridOverlay();
	}
}


//------------------------------------------------------------------------------
// Purpose : Deletes all overlays
// Input   :
// Output  :
//------------------------------------------------------------------------------
void ClearAllOverlays(void)
{
	AUTO_LOCK( s_OverlayMutex );
	while (s_pOverlays) 
	{
		OverlayBase_t *pOldOverlay = s_pOverlays;
		s_pOverlays = s_pOverlays->m_pNextOverlay;
		DestroyOverlay( pOldOverlay );
	}

	while (s_pOverlayText) 
	{
		OverlayText_t *cur_ol = s_pOverlayText;
		s_pOverlayText = s_pOverlayText->nextOverlayText;
		delete cur_ol;
	}

	s_bDrawGrid = false;
}

void ClearDeadOverlays( void )
{
	AUTO_LOCK( s_OverlayMutex );
	OverlayText_t* pCurrText = s_pOverlayText;
	OverlayText_t* pLastText = NULL;
	OverlayText_t* pNextText = NULL;
	while (pCurrText) 
	{
		// Is it time to kill this Text?
		if ( pCurrText->IsDead() )
		{
			// If I had a last Text reset it's next pointer
			if (pLastText)
			{
				pLastText->nextOverlayText = pCurrText->nextOverlayText;
			}
			// If the first Text, reset the s_pOverlayText pointer
			else
			{
				s_pOverlayText = pCurrText->nextOverlayText;
			}
			pNextText = pCurrText->nextOverlayText;
			delete pCurrText;
			pCurrText = pNextText;
		}
		else
		{
			pLastText = pCurrText;
			pCurrText = pCurrText->nextOverlayText;
		}
	}
}

}	// end namespace CDebugOverlay


//-----------------------------------------------------------------------------
// Purpose:	export debug overlay to client DLL
// Input  :
// Output :
//-----------------------------------------------------------------------------
class CIVDebugOverlay : public IVDebugOverlay, public IVPhysicsDebugOverlay
{
private:
	char m_text[1024];
	va_list	m_argptr;

public:
	void AddEntityTextOverlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...)
	{
		va_start( m_argptr, format );
		Q_vsnprintf( m_text, sizeof( m_text ), format, m_argptr );
		va_end( m_argptr );

		CDebugOverlay::AddEntityTextOverlay(ent_index, line_offset, duration, r, g, b, a, m_text);
	}

	void AddBoxOverlay(const Vector& origin, const Vector& mins, const Vector& max, QAngle const& angles, int r, int g, int b, int a, float duration)
	{
		CDebugOverlay::AddBoxOverlay(origin, mins, max, angles, r, g, b, a, duration);
	}

	void AddSweptBoxOverlay(const Vector& start, const Vector& end, const Vector& mins, const Vector& max, const QAngle & angles, int r, int g, int b, int a, float flDuration)
	{
		CDebugOverlay::AddSweptBoxOverlay(start, end, mins, max, angles, r, g, b, a, flDuration);
	}

	void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration)
	{
		CDebugOverlay::AddLineOverlay(origin, dest, r, g, b, 255, noDepthTest, duration);
	}

	void AddTriangleOverlay(const Vector& p1, const Vector& p2, const Vector &p3, int r, int g, int b, int a, bool noDepthTest, float duration)
	{
		CDebugOverlay::AddTriangleOverlay(p1, p2, p3, r, g, b, a, noDepthTest, duration);
	}

	void AddTextOverlay(const Vector& origin, float duration, const char *format, ...)
	{
		va_start( m_argptr, format );
		Q_vsnprintf( m_text, sizeof( m_text ), format, m_argptr );
		va_end( m_argptr );

		CDebugOverlay::AddTextOverlay(origin, duration, m_text);
	}

	void AddTextOverlay(const Vector& origin, int line_offset, float duration, const char *format, ...)
	{
		va_start( m_argptr, format );
		Q_vsnprintf( m_text, sizeof( m_text ), format, m_argptr );
		va_end( m_argptr );

		CDebugOverlay::AddTextOverlay(origin, line_offset, duration, m_text);
	}

	void AddTextOverlayRGB(const Vector& origin, int line_offset, float duration, float r, float g, float b, float alpha, const char *format, ...)
	{
		va_start( m_argptr, format );
		Q_vsnprintf( m_text, sizeof( m_text ), format, m_argptr );
		va_end( m_argptr );

		CDebugOverlay::AddTextOverlay(origin, line_offset, duration, r, g, b, alpha, m_text);
	}

	void AddTextOverlayRGB(const Vector& origin, int line_offset, float flDuration, int r, int g, int b, int alpha, const char *format, ...)
	{
		va_start( m_argptr, format );
		Q_vsnprintf( m_text, sizeof( m_text ), format, m_argptr );
		va_end( m_argptr );

		CDebugOverlay::AddTextOverlay( origin, line_offset, flDuration, r * 1.0f/255.0f, g * 1.0f/255.0f, b * 1.0f/255.0f, alpha * 1.0f/255.0f, m_text );
	}

	void AddScreenTextOverlay(float flXPos, float flYPos,float flDuration, int r, int g, int b, int a, const char *text)
	{
		CDebugOverlay::AddScreenTextOverlay( flXPos, flYPos, flDuration, r, g, b, a, text );
	}
	
	void AddGridOverlay(const Vector& origin)
	{
		CDebugOverlay::AddGridOverlay( origin );
	}

	void AddCoordFrameOverlay(const matrix3x4_t& frame, float flScale, int vColorTable[3][3] = NULL)
	{
		CDebugOverlay::AddCoordFrameOverlay( frame, flScale, vColorTable );
	}

	int ScreenPosition(const Vector& point, Vector& screen)
	{
		return CDebugOverlay::ScreenPosition( point, screen );
	}

	int ScreenPosition(float flXPos, float flYPos, Vector& screen)
	{
		return CDebugOverlay::ScreenPosition( flXPos, flYPos, screen );
	}

	virtual OverlayText_t *GetFirst( void )
	{
		return CDebugOverlay::s_pOverlayText;
	}

	virtual OverlayText_t *GetNext( OverlayText_t *current )
	{
		return current->nextOverlayText;
	}

	virtual void ClearDeadOverlays( void )
	{
		CDebugOverlay::ClearDeadOverlays();
	}

	virtual void ClearAllOverlays()
	{
		CDebugOverlay::ClearAllOverlays();
	}

	void AddLineOverlayAlpha(const Vector& origin, const Vector& dest, int r, int g, int b, int a, bool noDepthTest, float duration)
	{
		CDebugOverlay::AddLineOverlay(origin, dest, r, g, b, a, noDepthTest, duration);
	}

	void AddBoxOverlay2( const Vector& origin, const Vector& mins, const Vector& maxs, QAngle const& orientation, const Color& faceColor, const Color& edgeColor, float duration )
	{
		CDebugOverlay::AddBoxOverlay2(origin, mins, maxs, orientation, faceColor, edgeColor, duration );
	}

	void PurgeTextOverlays()
	{
		CDebugOverlay::PurgeTextOverlays();
	}
};

static CIVDebugOverlay g_DebugOverlay;

EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CIVDebugOverlay, IVDebugOverlay, VDEBUG_OVERLAY_INTERFACE_VERSION, g_DebugOverlay );
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CIVDebugOverlay, IVPhysicsDebugOverlay, VPHYSICS_DEBUG_OVERLAY_INTERFACE_VERSION, g_DebugOverlay );