//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include <math.h>
#include <mmsystem.h>
#include "Camera.h"
#include "CullTreeNode.h"
#include "MapDefs.h"
#include "MapDoc.h"
#include "MapEntity.h"
#include "MapInstance.h"
#include "MapWorld.h"
#include "Render3DMS.h"
#include "SSolid.h"
#include "MapStudioModel.h"
#include "Material.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imesh.h"
#include "TextureSystem.h"
#include "ToolInterface.h"
#include "StudioModel.h"
#include "ibsplighting.h"
#include "MapDisp.h"
#include "ToolManager.h"
#include "mapview.h"
#include "hammer.h"
#include "IStudioRender.h"
#include <renderparm.h>
#include "materialsystem/itexture.h"
#include "maplightcone.h"
#include "map_utils.h"
#include "bitmap/float_bm.h"
#include "lpreview_thread.h"
#include "hammer.h"
#include "mainfrm.h"
#include "mathlib/halton.h"
#include "Manifest.h"

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


#define NUM_MIPLEVELS			4

#define CROSSHAIR_DIST_HORIZONTAL		5
#define CROSSHAIR_DIST_VERTICAL			6

#define TEXTURE_AXIS_LENGTH				10	// Texture axis length in world units


// dvs: experiment!
//extern int g_nClipPoints;
//extern Vector g_ClipPoints[4];

//
// Debugging / diagnostic stuff.
//
static bool g_bDrawWireFrameSelection = true;
static bool g_bShowStatistics = false;
static bool g_bUseCullTree = true;
static bool g_bRenderCullBoxes = false;

int g_nBitmapGenerationCounter = 1;

//-----------------------------------------------------------------------------
// Purpose: Callback comparison function for sorting objects clicked on while
//			in selection mode.
// Input  : pHit1 - First hit to compare.
//			pHit2 - Second hit to compare.
// Output : Sorts by increasing depth value. Returns -1, 0, or 1 per qsort spec.
//-----------------------------------------------------------------------------
static int _CompareHits(const void *pHit1, const void *pHit2)
{
	if (((HitInfo_t *)pHit1)->nDepth < ((HitInfo_t *)pHit2)->nDepth)
	{
		return(-1);
	}

	if (((HitInfo_t *)pHit1)->nDepth > ((HitInfo_t *)pHit2)->nDepth)
	{
		return(1);
	}

	return(0);
}


//-----------------------------------------------------------------------------
// Purpose: Callback comparison function for sorting objects clicked on while
//			in selection mode. The reverse sort is used for cards that return
//			depth values in reverse (larger numbers are closer to the camera).
// Input  : pHit1 - First hit to compare.
//			pHit2 - Second hit to compare.
// Output : Sorts by decreasing depth value. Returns -1, 0, or 1 per qsort spec.
//-----------------------------------------------------------------------------
static int _CompareHitsReverse(const void *pHit1, const void *pHit2)
{
	if (((HitInfo_t *)pHit1)->nDepth > ((HitInfo_t *)pHit2)->nDepth)
	{
		return(-1);
	}

	if (((HitInfo_t *)pHit1)->nDepth < ((HitInfo_t *)pHit2)->nDepth)
	{
		return(1);
	}

	return(0);
}

static bool TranslucentObjectsLessFunc( TranslucentObjects_t const&a, TranslucentObjects_t const&b )
{
	return (a.depth < b.depth);
}


bool GetRequiredMaterial( const char *pName, IMaterial* &pMaterial )
{
	pMaterial = NULL;
	IEditorTexture *pTex = g_Textures.FindActiveTexture( pName );
	if ( pTex )
		pMaterial = pTex->GetMaterial();

	if ( pMaterial )
	{
		return true;
	}
	else
	{
		char str[512];
		Q_snprintf( str, sizeof( str ), "Missing material '%s'. Go to Tools | Options | Game Configurations and verify that your game directory is correct.", pName );
		MessageBox( NULL, str, "FATAL ERROR", MB_OK );
		return false;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Calculates lighting for a given face.
// Input  : Normal - vector that is normal to the face being lit.
// Output : Returns a number from [0.2, 1.0]
//-----------------------------------------------------------------------------
float CRender3D::LightPlane(Vector& Normal)
{
	static Vector Light( 1.0f, 2.0f, 3.0f );
	static bool bFirst = true;

	if (bFirst)
	{
		VectorNormalize(Light);
		bFirst = false;
	}

	float fShade = 0.65f + (0.35f * DotProduct(Normal, Light));

	return(fShade);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CRender3D::CRender3D(void) :
	CRender()
{
	memset(&m_WinData, 0, sizeof(m_WinData));
	m_WinData.bAllowSoft = true;

	memset(m_FrustumPlanes, 0, sizeof(m_FrustumPlanes));

	m_pDropCamera = new CCamera;
	m_bDroppedCamera = false;
	m_DeferRendering = false;
	m_TranslucentSortRendering = false;

	m_fFrameRate = 0;
	m_nFramesThisSample = 0;
	m_dwTimeLastSample = 0;
	m_dwTimeLastFrame = 0;
	m_fTimeElapsed = 0;

	m_LastLPreviewCameraPos = Vector(1.0e22,1.0e22,1.0e22);
	m_nLastLPreviewWidth = -1;
	m_nLastLPreviewHeight = -1;

	memset(&m_Pick, 0, sizeof(m_Pick));
	m_Pick.bPicking = false;

	memset(&m_RenderState, 0, sizeof(m_RenderState));

	for (int i = 0; i < 2; ++i)
	{
		m_pVertexColor[i] = 0;
	}
	m_bLightingPreview = false;

	m_TranslucentRenderObjects.SetLessFunc( TranslucentObjectsLessFunc );

#ifdef _DEBUG
	m_bRenderFrustum = false;
	m_bRecomputeFrustumRenderGeometry = false;
#endif
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CRender3D::~CRender3D(void)
{
	if (m_pDropCamera != NULL)
	{
		delete m_pDropCamera;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called before rendering an object that should be hit tested when
//			rendering in selection mode.
// Input  : pObject - Map atom pointer that will be returned from the ObjectsAt
//			routine if this rendered object is positively hit tested.
//-----------------------------------------------------------------------------
void CRender3D::BeginRenderHitTarget(CMapAtom *pObject, unsigned int uHandle)
{
	if ( m_Pick.bPicking == false )
	{
		return;
	}

	if ( ( m_Pick.m_nFlags & FLAG_OBJECTS_AT_RESOLVE_INSTANCES ) == 0 && m_bInstanceRendering && !GetInstanceClass()->IsEditable() )
	{
		pObject = m_CurrentInstanceState.m_pTopInstanceClass; // GetInstanceClass();
		uHandle = 0;
	}

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	pRenderContext->PushSelectionName((unsigned int)pObject);
	pRenderContext->PushSelectionName(uHandle);
}


//-----------------------------------------------------------------------------
// Purpose: Called after rendering an object that should be hit tested when
//			rendering in selection mode.
// Input  : pObject - Map atom pointer that will be returned from the ObjectsAt
//			routine if this rendered object is positively hit tested.
// Input  : pObject - 
//-----------------------------------------------------------------------------
void CRender3D::EndRenderHitTarget(void)
{
	if ( m_Pick.bPicking )
	{
		//
		// Pop the name and the handle from the stack.
		//

		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		pRenderContext->PopSelectionName();
		pRenderContext->PopSelectionName();

		if ((pRenderContext->SelectionMode(true) != 0) && (m_Pick.nNumHits < MAX_PICK_HITS))
		{
			if (m_Pick.uSelectionBuffer[0] == 2)
			{
				m_Pick.Hits[m_Pick.nNumHits].pObject = (CMapClass *)m_Pick.uSelectionBuffer[3];
				m_Pick.Hits[m_Pick.nNumHits].uData = m_Pick.uSelectionBuffer[4];
				m_Pick.Hits[m_Pick.nNumHits].nDepth = m_Pick.uSelectionBuffer[1];
				m_Pick.Hits[m_Pick.nNumHits].m_LocalMatrix = m_LocalMatrix.Head();
				m_Pick.nNumHits++;
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : 
//-----------------------------------------------------------------------------
void CRender3D::AddTranslucentDeferredRendering( CMapPoint *pMapPoint )
{
	// object is translucent, render in 2nd batch
	Vector direction = m_pView->GetViewAxis();
	Vector center;	
	pMapPoint->GetOrigin(center);

	TranslucentObjects_t entry;
	if ( m_bInstanceRendering )
	{
		center += GetInstanceOrigin();
		entry.m_InstanceState = m_CurrentInstanceState;
		entry.m_bInstanceSelected = ( m_InstanceSelectionDepth != 0 );
	}
	else
	{
		entry.m_InstanceState.m_pInstanceClass = NULL;
	}

	entry.object = pMapPoint;
	entry.depth = center.Dot( direction );

	m_TranslucentRenderObjects.Insert(entry);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Output : 
//-----------------------------------------------------------------------------
float CRender3D::GetElapsedTime(void)
{
	return(m_fTimeElapsed);
}


//-----------------------------------------------------------------------------
// Computes us some geometry to render the frustum planes
//-----------------------------------------------------------------------------

void CRender3D::ComputeFrustumRenderGeometry(CCamera *pCamera)
{
#ifdef _DEBUG
	Vector viewPoint;
	pCamera->GetViewPoint(viewPoint);

	// Find lines along each of the plane intersections.
	// We know these lines are perpendicular to both plane normals,
	// so we can take the cross product to find them.
	static int edgeIdx[4][2] =
		{
			{ 0, 2 }, { 0, 3 }, { 1, 3 }, { 1, 2 }
		};

	int i;
	Vector edges[4];
	for ( i = 0; i < 4; ++i)
	{
		CrossProduct( m_FrustumPlanes[edgeIdx[i][0]].AsVector3D(),
					  m_FrustumPlanes[edgeIdx[i][1]].AsVector3D(), edges[i] );
		VectorNormalize( edges[i] );
	}

	// Figure out four near points by intersection lines with the near plane
	// Figure out four far points by intersection with lines against far plane
	for (i = 0; i < 4; ++i)
	{
		float t = (m_FrustumPlanes[4][3] - DotProduct(m_FrustumPlanes[4].AsVector3D(), viewPoint)) /
			DotProduct(m_FrustumPlanes[4].AsVector3D(), edges[i]);
		VectorMA( viewPoint, t, edges[i], m_FrustumRenderPoint[i] );

		/*
		  t = (m_FrustumPlanes[5][3] - DotProduct(m_FrustumPlanes[5], viewPoint)) /
		  DotProduct(m_FrustumPlanes[5], edges[i]);
		  VectorMA( viewPoint, t, edges[i], m_FrustumRenderPoint[i + 4] );
		*/
		if (t < 0)
		{
			edges[i] *= -1;
		}

		VectorMA( m_FrustumRenderPoint[i], 200.0, edges[i], m_FrustumRenderPoint[i + 4] );
	}
#endif
}

//-----------------------------------------------------------------------------
// renders the frustum
//-----------------------------------------------------------------------------

void CRender3D::RenderFrustum( )
{
#ifdef _DEBUG
	static int indices[] = 
		{
			0, 1, 1, 2, 2, 3, 3, 0,	// near square
			4, 5, 5, 6, 6, 7, 7, 4,	// far square
			0, 4, 1, 5, 2, 6, 3, 7	// connections between them
		};

	PushRenderMode( RENDER_MODE_WIREFRAME ); 
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh* pMesh = pRenderContext->GetDynamicMesh();

	int numIndices = sizeof(indices) / sizeof(int);
	CMeshBuilder meshBuilder3D;
	meshBuilder3D.Begin( pMesh, MATERIAL_LINES, 8, numIndices );

	for ( int i = 0; i < 8; ++i )
	{
		meshBuilder3D.Position3fv( m_FrustumRenderPoint[i].Base() );
		meshBuilder3D.Color4ub( 255, 255, 255, 255 );
		meshBuilder3D.AdvanceVertex();
	}

	for ( int i = 0; i < numIndices; ++i )
	{
		meshBuilder3D.Index( indices[i] );
		meshBuilder3D.AdvanceIndex();
	}

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

	PopRenderMode(); 
#endif
}


//-----------------------------------------------------------------------------
// Purpose: Returns the 3D grid spacing, in world units.
//-----------------------------------------------------------------------------
float CRender3D::GetGridDistance(void)
{
	return(m_RenderState.fGridDistance);
}


//-----------------------------------------------------------------------------
// Purpose: Returns the 3D grid spacing, in world units.
//-----------------------------------------------------------------------------
float CRender3D::GetGridSize(void)
{
	return(m_RenderState.fGridSpacing);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : hwnd - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CRender3D::SetView( CMapView *pView )
{
	if ( !CRender::SetView( pView ) )
		return false;

	HWND hwnd = pView->GetViewWnd()->GetSafeHwnd();
	CMapDoc *pDoc = pView->GetMapDoc();

	Assert(hwnd != NULL);
	Assert(pDoc != NULL);
	Assert(pDoc->GetMapWorld() != NULL);
	  
	if (!MaterialSystemInterface()->AddView( hwnd ))
	{
		return false;
	}

	MaterialSystemInterface()->SetView( hwnd );

	m_WinData.hWnd = hwnd;

	if ((m_WinData.hDC = GetDCEx(m_WinData.hWnd, NULL, DCX_CACHE | DCX_CLIPSIBLINGS)) == NULL)
	{
		ChangeDisplaySettings(NULL, 0);
		MessageBox(NULL, "GetDC on main window failed", "FATAL ERROR", MB_OK);
		return(false);
	}

	// Preload all our stuff (textures, etc) for rendering.
	Preload( pDoc->GetMapWorld() );

	// Store off the three materials we use most often...
	if ( !GetRequiredMaterial( "editor/vertexcolor", m_pVertexColor[0] ) )
	{
		return false;
	}
	
	m_pVertexColor[1] = m_pVertexColor[0];

	return(true);
}


//-----------------------------------------------------------------------------
// Purpose: Determines the visibility of the given axis-aligned bounding box.
// Input  : pBox - Bounding box to evaluate.
// Output : VIS_TOTAL if the box is entirely within the view frustum.
//			VIS_PARTIAL if the box is partially within the view frustum.
//			VIS_NONE if the box is entirely outside the view frustum.
//-----------------------------------------------------------------------------
Visibility_t CRender3D::IsBoxVisible(Vector const &BoxMins, Vector const &BoxMaxs)
{
	Vector NearVertex;
	Vector FarVertex;

	//
	// Build the near and far vertices based on the octant of the plane normal.
	//
	int nInPlanes = 0;
	for ( int i = 0; i < 6; i++ )
	{
		if (m_FrustumPlanes[i][0] > 0)
		{
			NearVertex[0] = BoxMins[0];
			FarVertex[0] = BoxMaxs[0];
		}
		else
		{
			NearVertex[0] = BoxMaxs[0];
			FarVertex[0] = BoxMins[0];
		}

		if (m_FrustumPlanes[i][1] > 0)
		{
			NearVertex[1] = BoxMins[1];
			FarVertex[1] = BoxMaxs[1];
		}
		else
		{
			NearVertex[1] = BoxMaxs[1];
			FarVertex[1] = BoxMins[1];
		}

		if (m_FrustumPlanes[i][2] > 0)
		{
			NearVertex[2] = BoxMins[2];
			FarVertex[2] = BoxMaxs[2];
		}
		else
		{
			NearVertex[2] = BoxMaxs[2];
			FarVertex[2] = BoxMins[2];
		}

		if (DotProduct(m_FrustumPlanes[i].AsVector3D(), NearVertex) >= m_FrustumPlanes[i][3])
		{
			return(VIS_NONE);
		}

		if (DotProduct(m_FrustumPlanes[i].AsVector3D(), FarVertex) < m_FrustumPlanes[i][3])
		{
			nInPlanes++;
		}
	}

	if (nInPlanes == 6)
	{
		return(VIS_TOTAL);
	}
	
	return(VIS_PARTIAL);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : eRenderState - 
// Output : Returns true if the render state is enabled, false if it is disabled.
//-----------------------------------------------------------------------------
bool CRender3D::IsEnabled(RenderState_t eRenderState)
{
	switch (eRenderState)
	{
		case RENDER_CENTER_CROSSHAIR:
		{
			return(m_RenderState.bCenterCrosshair);
		}

		case RENDER_GRID:
		{
			return(m_RenderState.bDrawGrid);
		}

		case RENDER_REVERSE_SELECTION:
		{
			return(m_RenderState.bReverseSelection);
		}
	}

	return(false);
}


//-----------------------------------------------------------------------------
// Purpose: Determines whether we are rendering for for selection or not.
// Output : Returns true if we are rendering for selection, false if rendering normally.
//-----------------------------------------------------------------------------
bool CRender3D::IsPicking(void)
{
	return(m_Pick.bPicking);
}


//-----------------------------------------------------------------------------
// Purpose: Returns the map objects within the rectangle whose upper left corner
//			is at the client coordinates (x, y) and whose width and height are
//			fWidth and fHeight.
// Input  : x - Leftmost point in the rectangle, in client coordinates.
//			y - Topmost point in the rectangle, in client coordinates.
//			fWidth - Width of rectangle, in client coordinates.
//			fHeight - Height of rectangle, in client coordinates.
//			pObjects - Pointer to buffer to receive objects intersecting the rectangle.
//			nMaxObjects - Maximum number of object pointers to place in the buffer.
// Output : Returns the number of object pointers placed in the buffer pointed to
//			by 'pObjects'.
//-----------------------------------------------------------------------------
int CRender3D::ObjectsAt( float x, float y, float fWidth, float fHeight, HitInfo_t *pObjects, int nMaxObjects, unsigned int nFlags )
{
	int width, height;

	GetCamera()->GetViewPort(width,height);

	m_Pick.fX = x;
	m_Pick.fY = height - (y + 1);
	m_Pick.fWidth = fWidth;
	m_Pick.fHeight = fHeight;
	m_Pick.pHitsDest = pObjects;
	m_Pick.nMaxHits = min(nMaxObjects, MAX_PICK_HITS);
	m_Pick.nNumHits = 0;

	if (!m_RenderState.bReverseSelection)
	{
		m_Pick.uLastZ = 0xFFFFFFFF;
	}
	else
	{
		m_Pick.uLastZ = 0;
	}

	m_Pick.m_nFlags = nFlags;
	m_Pick.bPicking = true;

	EditorRenderMode_t eOldMode = GetDefaultRenderMode();
	SetDefaultRenderMode( RENDER_MODE_TEXTURED );

	bool bOldLightPreview = IsInLightingPreview();
	SetInLightingPreview( false );

	Render();

	SetDefaultRenderMode( eOldMode );
	SetInLightingPreview( bOldLightPreview );

	m_Pick.bPicking = false;

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	pRenderContext->SelectionMode(false);

	return(m_Pick.nNumHits);
}

static ITexture *SetRenderTargetNamed(int nWhichTarget, char const *pRtName)
{
	CMatRenderContextPtr pRenderContext( materials );
	ITexture *dest_rt=materials->FindTexture(pRtName, TEXTURE_GROUP_RENDER_TARGET );
	pRenderContext->SetRenderTargetEx(nWhichTarget,dest_rt);
	return dest_rt;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::StartRenderFrame(void)
{
	CRender::StartRenderFrame();

	CCamera *pCamera = GetCamera();

	//
	// Determine the elapsed time since the last frame was rendered.
	//
	DWORD dwTimeNow = timeGetTime();
	if (m_dwTimeLastFrame == 0)
	{
		m_dwTimeLastFrame = dwTimeNow;
	}
	DWORD dwTimeElapsed = dwTimeNow - m_dwTimeLastFrame;
	m_fTimeElapsed = (float)dwTimeElapsed / 1000.0;
	m_dwTimeLastFrame = dwTimeNow;

	//
	// Animate the models based on elapsed time.
	//
	CMapStudioModel::AdvanceAnimation( GetElapsedTime() );

	// We're drawing to this view now
	MaterialSystemInterface()->SetView( m_WinData.hWnd );

	// view materialsystem viewport
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	int width, height;
	pCamera->GetViewPort( width, height );
	if (
		(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW2) ||
		(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED)
		)
	{
		AllocateLightingPreviewtextures();

		ITexture *first_rt=SetRenderTargetNamed(0,"_rt_albedo");
		SetRenderTargetNamed(1,"_rt_normal");
		SetRenderTargetNamed(2,"_rt_position");
		SetRenderTargetNamed(3,"_rt_flags");
		int nTargetWidth = min( width, first_rt->GetActualWidth() );
		int nTargetHeight = min( height, first_rt->GetActualHeight() );
		pRenderContext->
			Viewport(0, 0, nTargetWidth, nTargetHeight );
		pRenderContext->ClearColor3ub(0,1,0);
		pRenderContext->ClearBuffers( true, true );
	}
	else
		pRenderContext->Viewport(0, 0, width, height);

	//
	// Setup the camera position, orientation, and FOV.
	//
	//
	// Set up our perspective transformation.
	//

	// if picking, setup extra perspective matrix
	if ( m_Pick.bPicking )
	{
		pRenderContext->MatrixMode(MATERIAL_PROJECTION);
		pRenderContext->LoadIdentity();

		pRenderContext->PickMatrix(m_Pick.fX, m_Pick.fY, m_Pick.fWidth, m_Pick.fHeight);
		pRenderContext->SelectionBuffer(m_Pick.uSelectionBuffer, sizeof(m_Pick.uSelectionBuffer));
		pRenderContext->SelectionMode(true);
		pRenderContext->ClearSelectionNames();

		float aspect = (float)width / (float)height; 

		pRenderContext->PerspectiveX( pCamera->GetFOV(), 
			aspect, pCamera->GetNearClip(), pCamera->GetFarClip() );
	}
	else
	{
		//
		// Clear the frame buffer and Z buffer.
		//
		pRenderContext->ClearColor3ub( 0,0,0 );
		pRenderContext->ClearBuffers( true, true, true );
	}

	//
	// Build the frustum planes for view volume culling.
	//
	CCamera *pTempCamera = NULL;

	if (m_bDroppedCamera)
	{
		pTempCamera = pCamera;
		pCamera = m_pDropCamera;
	}

	pCamera->GetFrustumPlanes( m_FrustumPlanes);

	// For debugging frustum planes
#ifdef _DEBUG
	if (m_bRecomputeFrustumRenderGeometry)
	{
		ComputeFrustumRenderGeometry( pCamera );
		m_bRecomputeFrustumRenderGeometry = false;
	}
#endif

	if (m_bDroppedCamera)
	{
		pCamera = pTempCamera;
	}

	//
	// Cache per-frame information from the doc.
	//
	m_RenderState.fGridSpacing = m_pView->GetMapDoc()->GetGridSpacing();
	m_RenderState.fGridDistance = m_RenderState.fGridSpacing * 10;
	if (m_RenderState.fGridDistance > 2048)
	{
		m_RenderState.fGridDistance = 2048;
	}
	else if (m_RenderState.fGridDistance < 64)
	{
		m_RenderState.fGridDistance = 64;
	}

	// We do bizarro reverse culling in WC
	pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );

	Assert( m_TranslucentRenderObjects.Count() == 0 );
}


static void SetNamedMaterialVar(IMaterial *pMat, char const *pVName, float fValue)
{
	IMaterialVar *pVar = pMat->FindVar( pVName, NULL );
	pVar->SetFloatValue( fValue );
}

class CLightPreview_Light
{
public:
	LightDesc_t m_Light;
	float m_flDistanceToEye;
};

bool CompareLightPreview_Lights(CLightPreview_Light const &a, CLightPreview_Light const &b)
{
	return (a.m_flDistanceToEye > b.m_flDistanceToEye);
}

#define MAX_PREVIEW_LIGHTS 10								// max # of lights to process.


void CRender3D::SendShadowTriangles( void )
{
	static int LastSendTimeStamp=-1;
	if ( GetUpdateCounter( EVTYPE_FACE_CHANGED ) != LastSendTimeStamp )
	{
		LastSendTimeStamp = GetUpdateCounter( EVTYPE_FACE_CHANGED );
		CUtlVector<Vector> *tri_list=new CUtlVector<Vector>;
		CMapDoc *pDoc = m_pView->GetMapDoc();
		CMapWorld *pWorld = pDoc->GetMapWorld();
		
		if ( !pWorld )
			return;
		
		if (g_pLPreviewOutputBitmap)
			delete g_pLPreviewOutputBitmap;
		g_pLPreviewOutputBitmap = NULL;
		EnumChildrenPos_t pos;
		CMapClass *pChild = pWorld->GetFirstDescendent( pos );
		while ( pChild )
		{
			if (pChild->IsVisible())
				pChild->AddShadowingTriangles( *tri_list );
			pChild = pWorld->GetNextDescendent( pos );
		}
		if ( tri_list->Count() )
		{
			MessageToLPreview msg( LPREVIEW_MSG_GEOM_DATA );
			msg.m_pShadowTriangleList = tri_list;
			g_HammerToLPreviewMsgQueue.QueueMessage( msg );
		}
		else
			delete tri_list;
	}
	
}


static bool LightForString( char const *pLight, Vector& intensity )
{
	double r, g, b, scaler;

	VectorFill( intensity, 0 );

	// scanf into doubles, then assign, so it is vec_t size independent
	r = g = b = scaler = 0;
	double r_hdr,g_hdr,b_hdr,scaler_hdr;
	int argCnt = sscanf ( pLight, "%lf %lf %lf %lf %lf %lf %lf %lf", 
						  &r, &g, &b, &scaler, &r_hdr,&g_hdr,&b_hdr,&scaler_hdr );
	
	// This is a special case for HDR lights.  If we have a vector of [-1, -1, -1, 1], then we
	// need to fall back to the non-HDR lighting since the HDR lighting hasn't been defined
	// for this light source.
	if( ( argCnt == 3 && r == -1.0f && g == -1.0f && b == -1.0f ) ||
		( argCnt == 4 && r == -1.0f && g == -1.0f && b == -1.0f && scaler == 1.0f ) )
	{
		intensity.Init( -1.0f, -1.0f, -1.0f );
		return true;
	}
	
	if (argCnt==8) 											// 2 4-tuples
	{
		if (g_bHDR)
		{
			r=r_hdr;
			g=g_hdr;
			b=b_hdr;
			scaler=scaler_hdr;
		}
		argCnt=4;
	}
	
	intensity[0] = pow( r / 255.0, 2.2 ) * 255;				// convert to linear
	
	switch( argCnt)
	{
		case 1:
			// The R,G,B values are all equal.
			intensity[1] = intensity[2] = intensity[0]; 
			break;
			
		case 3:
		case 4:
			// Save the other two G,B values.
			intensity[1] = pow( g / 255.0, 2.2 ) * 255;
			intensity[2] = pow( b / 255.0, 2.2 ) * 255;
			
			// Did we also get an "intensity" scaler value too?
			if ( argCnt == 4 )
			{
				// Scale the normalized 0-255 R,G,B values by the intensity scaler
				VectorScale( intensity, scaler / 255.0, intensity );
			}
			break;

		default:
			printf("unknown light specifier type - %s\n",pLight);
			return false;
	}
	// change light to 0..1
	intensity *= (1.0/255);
	return true;
}

// ugly code copied from vrad and munged. Should move into a lib
static bool LightForKey (CMapEntity *ent, char *key, Vector& intensity )
{
	char const *pLight = ent->GetKeyValue( key );

	return LightForString( pLight, intensity );
}

static void GetVectorForKey( CMapEntity *e, char const *kname, Vector *out )
{
	Vector ret(-1,-1,-1);
	char const *pk = e->GetKeyValue( kname );
	if ( pk )
	{
		sscanf( pk, "%f %f %f", &(ret.x), &(ret.y), &(ret.z) );
	}
	*out=ret;
}
static float GetFloatForKey( CMapEntity *e, char const *kname)
{
	char const *pk = e->GetKeyValue( kname );
	if ( pk )
		return atof( pk );
	else
		return 0.0;
}

static void SetLightFalloffParams( CMapEntity *e, CLightingPreviewLightDescription &l)
{
	float d50=GetFloatForKey(e,"_fifty_percent_distance");
	if (d50)
	{
		float d0=GetFloatForKey(e,"_zero_percent_distance");
		l.SetupNewStyleAttenuation( d50, d0 );
	}
	else
	{
		float c = GetFloatForKey (e, "_constant_attn");
		float b = GetFloatForKey (e, "_linear_attn");
		float a = GetFloatForKey (e, "_quadratic_attn");
		
		l.SetupOldStyleAttenuation( a, b, c );
	}
}

static bool ParseLightAmbient( CMapEntity *e, CLightingPreviewLightDescription &out )
{
	if( LightForKey( e, "_ambient", out.m_Color ) == 0 )
		return false;
	return true;

}

static bool ParseLightGeneric( CMapEntity *e, CLightingPreviewLightDescription &out )
{
	// returns false if it doesn't like the light

	// get intensity
	if( g_bHDR )
	{
		if( LightForKey( e, "_lightHDR", out.m_Color ) == 0 ||
			( out.m_Color.x == -1.0f && 
			  out.m_Color.y == -1.0f && 
			  out.m_Color.z == -1.0f ) )
		{
			LightForKey( e, "_light", out.m_Color );
		}
	}
	else
	{
		LightForKey( e, "_light", out.m_Color );
	}
	
	// handle spot falloffs
	if ( out.m_Type == MATERIAL_LIGHT_SPOT )
	{
		out.m_Theta=GetFloatForKey(e, "_inner_cone");
		out.m_Theta *= (M_PI/180.0);
		out.m_Phi=GetFloatForKey(e,"_cone");
		out.m_Phi *= (M_PI/180.0);
		out.m_Falloff=GetFloatForKey(e,"_exponent");
	}


	// check angle, targets
#if 0														// !!bug!!
	target = e->m_KeyValues.GetValue( "target");
	if (target[0])
	{	// point towards target
		entity_t		*e2;
		char	        *target;
		e2 = FindTargetEntity (target);
		if (!e2)
			Warning("WARNING: light at (%i %i %i) has missing target\n",
					(int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
		else
		{
			Vector dest;
			GetVectorForKey (e2, "origin", &dest);
			VectorSubtract (dest, dl->light.origin, dl->light.normal);
			VectorNormalize (dl->light.normal);
		}
	}
	else
#endif
	{	
		// point down angle
		Vector angles;
		GetVectorForKey( e, "angles", &angles );
		float pitch = GetFloatForKey( e,"pitch");
		float angle = GetFloatForKey( e,"angle" );
		SetupLightNormalFromProps( QAngle( angles.x, angles.y, angles.z ), angle, pitch, 
								   out.m_Direction );
	}
	if ( out.m_Type == MATERIAL_LIGHT_DIRECTIONAL )
	{
		out.m_Range = 0;
		out.m_Attenuation2 = out.m_Attenuation1 = out.m_Attenuation0 = 0;
		out.m_Direction *= -1;
	}
	else
		SetLightFalloffParams( e, out );
	return true;
}

// when there are multiple lighting environments, we are supposed to ignore but the first
static bool s_bAddedLightEnvironmentAlready;


static void AddEntityLightToLightList( 
	CMapEntity *e,
	CUtlVector<CLightingPreviewLightDescription> &listout )
{
	char const *pszClassName=e->GetClassName();
	if (pszClassName)
	{
		CLightingPreviewLightDescription new_l;
		new_l.Init( e->m_nObjectID );
		e->GetOrigin( new_l.m_Position );
		new_l.m_Range = 0;

		if ( (! s_bAddedLightEnvironmentAlready ) &&
			 (! stricmp( pszClassName, "light_environment" ) ))
		{
			// lets add the sun to the list!
			new_l.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
			if ( ParseLightGeneric(e,new_l) )
			{
				new_l.m_Position = new_l.m_Direction * 100000;
				new_l.RecalculateDerivedValues();
				listout.AddToTail( new_l );
				s_bAddedLightEnvironmentAlready = true;
			}
			// now, add the ambient sphere. We will approximate as "N" directional lights
			if ( ParseLightAmbient( e, new_l ) )
			{
				DirectionalSampler_t sampler;
				Vector color = new_l.m_Color;
				for( int i = 0; i < 160; i++)
				{
					new_l.Init( 0x80000000 | i );			// special id for ambient
					new_l.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
					Vector dir = sampler.NextValue();
					new_l.m_Direction = dir;
					new_l.m_Position = new_l.m_Direction * 100000;
					new_l.m_Color = color * ( 1.0 / 160.0 );
					new_l.RecalculateDerivedValues();
					listout.AddToTail( new_l );
				}
			}
		}
		else if ( (! stricmp( pszClassName, "light" ) ))
		{
			// add point light to list
			new_l.m_Type = MATERIAL_LIGHT_POINT;
			if ( ParseLightGeneric(e,new_l) )
			{
				new_l.RecalculateDerivedValues();
				listout.AddToTail( new_l );
			}
		}
		else if ( (! stricmp( pszClassName, "light_spot" ) ))
		{
			// add point light to list
			new_l.m_Type = MATERIAL_LIGHT_SPOT;
			if ( ParseLightGeneric(e,new_l) )
			{
				new_l.RecalculateDerivedValues();
				listout.AddToTail( new_l );
			}
		}

	}
}


void CRender3D::BuildLightList( CUtlVector<CLightingPreviewLightDescription> *pList ) const
{
	CMapDoc *pDoc = m_pView->GetMapDoc();
	CMapWorld *pWorld = pDoc->GetMapWorld();
	
	if ( !pWorld )
		return;
	
	EnumChildrenPos_t pos;
	CMapClass *pChild = pWorld->GetFirstDescendent( pos );
	while ( pChild )
	{
		CMapEntity *pLightEntity=dynamic_cast<CMapEntity*>( pChild );
		if (pLightEntity && (pLightEntity->m_EntityTypeFlags & ENTITY_FLAG_IS_LIGHT ) &&
			(pLightEntity->IsVisible()) )
			AddEntityLightToLightList( pLightEntity, *pList );
		pChild = pWorld->GetNextDescendent( pos );
	}
}

void CRender3D::SendLightList( void )
{
	// send light list to lighting preview thread in priority order

	static int LastSendTimeStamp=-1;
	s_bAddedLightEnvironmentAlready = false;
	if ( GetUpdateCounter( EVTYPE_LIGHTING_CHANGED ) != LastSendTimeStamp )
	{
		LastSendTimeStamp = GetUpdateCounter( EVTYPE_LIGHTING_CHANGED );
		if (g_pLPreviewOutputBitmap)
			delete g_pLPreviewOutputBitmap;
		g_pLPreviewOutputBitmap = NULL;
		// now, get list of lights
		CUtlVector<CLightingPreviewLightDescription> *pList=new CUtlVector<CLightingPreviewLightDescription>;
		BuildLightList( pList );
		MessageToLPreview Msg( LPREVIEW_MSG_LIGHT_DATA );
		Msg.m_pLightList = pList;								// thread deletes
		CCamera *pCamera = GetCamera();
		pCamera->GetViewPoint( Msg.m_EyePosition );
	
		g_HammerToLPreviewMsgQueue.QueueMessage( Msg );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::EndRenderFrame(void)
{
	CRender::EndRenderFrame();

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	if (m_Pick.bPicking)
	{
		pRenderContext->Flush();

		//
		// Some OpenGL drivers, such as the ATI Rage Fury Max, return selection buffer Z values
		// in reverse order. For these cards, we must reverse the selection order.
		//
		if (m_Pick.nNumHits > 1)
		{
			if (!m_RenderState.bReverseSelection)
			{
				qsort(m_Pick.Hits, m_Pick.nNumHits, sizeof(m_Pick.Hits[0]), _CompareHits);
			}
			else
			{
				qsort(m_Pick.Hits, m_Pick.nNumHits, sizeof(m_Pick.Hits[0]), _CompareHitsReverse);
			}
		}

		//
		// Copy the requested number of nearest hits into the destination buffer.
		//
		int nHitsToCopy = min(m_Pick.nNumHits, m_Pick.nMaxHits);
		if (nHitsToCopy != 0)
		{
			memcpy(m_Pick.pHitsDest, m_Pick.Hits, sizeof(m_Pick.Hits[0]) * nHitsToCopy);
		}
	}

	//
	// Copy the GL buffer contents to our window's device context unless we're in pick mode.
	//
	if (!m_Pick.bPicking)
	{
		if (
			(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW2) ||
			(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED)
			)
		{
			pRenderContext->Flush();
			pRenderContext->SetRenderTarget( NULL );
			pRenderContext->SetRenderTargetEx( 1,NULL );
			pRenderContext->SetRenderTargetEx( 2,NULL );
			pRenderContext->SetRenderTargetEx( 3,NULL );


			ITexture *pRT = SetRenderTargetNamed(0,"_rt_accbuf_0");
			pRenderContext->ClearColor3ub(0,0,0);
			pRenderContext->ClearBuffers( true, true );

			CCamera *pCamera = GetCamera();
			int width, height;
			pCamera->GetViewPort( width, height );

			int nTargetWidth = min( width, pRT->GetActualWidth() );
			int nTargetHeight = min( height, pRT->GetActualHeight() );

			bool view_changed = false;

			Vector new_vp;
			pCamera->GetViewPoint( new_vp );

			if ( (pCamera->GetYaw() != m_fLastLPreviewAngles[0] ) ||
				 (pCamera->GetPitch() != m_fLastLPreviewAngles[1] ) ||
				 (pCamera->GetRoll() != m_fLastLPreviewAngles[2] ) ||
				 (m_nLastLPreviewHeight != height ) ||
				 (m_nLastLPreviewWidth != width ) ||
				 ( new_vp != m_LastLPreviewCameraPos ) ||
				 (pCamera->GetZoom() != m_fLastLPreviewZoom ) )
				view_changed = true;
			if (m_pView->m_bUpdateView && (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED))
			{

				static bool did_dump=false;
				static float Last_SendTime=0;
				// now, lets create floatbms with the deferred rendering data, so we can pass it to the lpreview thread
				float newtime=Plat_FloatTime();
				if (( n_gbufs_queued < 1 ) && ( newtime-Last_SendTime > 1.0) )
				{
					SendShadowTriangles();
					SendLightList();							// send light list to render thread
					if ( view_changed )
					{
						m_fLastLPreviewAngles[0] = pCamera->GetYaw();
						m_fLastLPreviewAngles[1] = pCamera->GetPitch();
						m_fLastLPreviewAngles[2] = pCamera->GetRoll();
						m_LastLPreviewCameraPos = new_vp;
						m_fLastLPreviewZoom = pCamera->GetZoom();
						m_nLastLPreviewHeight = height;
						m_nLastLPreviewWidth = width;


						g_nBitmapGenerationCounter++;
						Last_SendTime=newtime;
						if (g_pLPreviewOutputBitmap)
							delete g_pLPreviewOutputBitmap;
						g_pLPreviewOutputBitmap = NULL;
						static char const *rts_to_transmit[]={"_rt_albedo","_rt_normal","_rt_position",
															  "_rt_flags" };
						MessageToLPreview Msg(LPREVIEW_MSG_G_BUFFERS);
						for(int i=0; i < NELEMS( rts_to_transmit ); i++)
						{
							SetRenderTargetNamed(0,rts_to_transmit[i]);
							FloatBitMap_t *fbm = new FloatBitMap_t( nTargetWidth, nTargetHeight );
							Msg.m_pDefferedRenderingBMs[i]=fbm;
							pRenderContext->ReadPixels(0, 0, nTargetWidth, nTargetHeight, (uint8 *) &(fbm->Pixel(0,0,0)),
												  IMAGE_FORMAT_RGBA32323232F);
							if ( (i==0) && (! did_dump) )
							{
								fbm->WriteTGAFile("albedo.tga");
							}
							if ( (i==1) && (! did_dump) )
							{
								fbm->WriteTGAFile("normal.tga");
							}
						}
						pRenderContext->SetRenderTarget( NULL );
						did_dump = true;
						n_gbufs_queued++;
						pCamera->GetViewPoint( Msg.m_EyePosition );
						Msg.m_nBitmapGenerationCounter=g_nBitmapGenerationCounter;
						g_HammerToLPreviewMsgQueue.QueueMessage( Msg );
					}
				}
			}			
			
			// only update non-ray traced lpreview if we have no ray traced one or if the scene has changed
			if (m_pView->m_bUpdateView || (m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) || 
				(! g_pLPreviewOutputBitmap) )
			{
				SetRenderTargetNamed(0,"_rt_accbuf_0");
				pRenderContext->ClearColor3ub(0,0,0);
				MaterialSystemInterface()->ClearBuffers( true, true );

				
				pRenderContext->Viewport(0, 0, nTargetWidth, nTargetHeight );
				pRenderContext->ClearColor3ub(0,0,0);
				pRenderContext->ClearBuffers( true, true );
				// now, copy albedo to screen
				ITexture *dest_rt=materials->FindTexture("_rt_albedo", TEXTURE_GROUP_RENDER_TARGET );
				int xl,yl,dest_width,dest_height;
				pRenderContext->GetViewport( xl,yl,dest_width,dest_height);

				CMapDoc *pDoc = m_pView->GetMapDoc();
				CMapWorld *pWorld = pDoc->GetMapWorld();
			
				if ( !pWorld )
					return;
			
				// now, get list of lights
				CUtlVector<CLightingPreviewLightDescription> lightList;
				BuildLightList( &lightList );

				CUtlPriorityQueue<CLightPreview_Light> light_queue( 0, 0, CompareLightPreview_Lights);

				Vector eye_pnt;
				pCamera->GetViewPoint(eye_pnt);
				// now, add lights in priority order
				for( int i = 0; i < lightList.Count(); i++ )
				{
					LightDesc_t *pLight = &lightList[i];
					if (
						( pLight->m_Type == MATERIAL_LIGHT_SPOT ) ||
						( pLight->m_Type == MATERIAL_LIGHT_POINT ) )
					{
						Vector lpnt;
						CLightPreview_Light tmplight;
						tmplight.m_Light = *pLight;
						tmplight.m_flDistanceToEye = pLight->m_Position.DistTo( eye_pnt );
						light_queue.Insert(tmplight);
					}
				}
				if ( light_queue.Count() == 0 )
				{
					// no lights for gpu preview? lets add a fake one
					CLightPreview_Light tmplight;
					tmplight.m_Light.m_Type = MATERIAL_LIGHT_POINT;
					tmplight.m_Light.m_Color = Vector( 10, 10, 10 );
					tmplight.m_Light.m_Position = Vector( 0, 0, 30000 );
					tmplight.m_Light.m_Range = 1.0e20;
					tmplight.m_Light.m_Attenuation0 = 1.0;
					tmplight.m_Light.m_Attenuation1 = 0.0;
					tmplight.m_Light.m_Attenuation2 = 0.0;
					tmplight.m_flDistanceToEye = 1;
					light_queue.Insert(tmplight);
				}
				// because of no blend support on ati, we have to ping pong. This needs an nvidia-specifc
				// path for perf
				IMaterial *add_0_to_1=materials->FindMaterial("editor/addlight0",
															  TEXTURE_GROUP_OTHER,true);
				IMaterial *add_1_to_0=materials->FindMaterial("editor/addlight1",
															  TEXTURE_GROUP_OTHER,true);
				
				IMaterial *sample_last=materials->FindMaterial("editor/sample_result_0",
															   TEXTURE_GROUP_OTHER,true);
				IMaterial *sample_other=materials->FindMaterial("editor/sample_result_1",
																TEXTURE_GROUP_OTHER,true);

				ITexture *dest_rt_current=materials->FindTexture("_rt_accbuf_1", TEXTURE_GROUP_RENDER_TARGET );
				ITexture *dest_rt_other=materials->FindTexture("_rt_accbuf_0", TEXTURE_GROUP_RENDER_TARGET );
				pRenderContext->SetRenderTarget(dest_rt_other);
				pRenderContext->ClearColor3ub(0,0,0);
				pRenderContext->ClearBuffers( true, true );
				int nlights=min(MAX_PREVIEW_LIGHTS,light_queue.Count());
				for(int i=0;i<nlights;i++)
				{
					IMaterial *src_mat=add_0_to_1;
					LightDesc_t light = light_queue.ElementAtHead().m_Light;
					light.RecalculateDerivedValues();
					light_queue.RemoveAtHead();
					Vector lpnt = light.m_Position;
					SetNamedMaterialVar(src_mat,"$C0_X", lpnt.x);
					SetNamedMaterialVar(src_mat,"$C0_Y", lpnt.y );
					SetNamedMaterialVar(src_mat,"$C0_Z", lpnt.z );
					// now, get the facing direction.
					Vector spot_dir = light.m_Direction;
					SetNamedMaterialVar(src_mat,"$C1_X", spot_dir.x );
					SetNamedMaterialVar(src_mat,"$C1_Y", spot_dir.y );
					SetNamedMaterialVar(src_mat,"$C1_Z", spot_dir.z );
					
					// now, handle cone angle
					if ( light.m_Type == MATERIAL_LIGHT_POINT )
					{
						// model point as a spot with infinite inner radius
						SetNamedMaterialVar(src_mat, "$C0_W", 0.5 );
						SetNamedMaterialVar(src_mat, "$C1_W", 1.0e10 );
					}
					else
					{
						SetNamedMaterialVar(src_mat, "$C0_W", light.m_ThetaDot );
						SetNamedMaterialVar(src_mat, "$C1_W", light.m_PhiDot );
					}

					SetNamedMaterialVar( src_mat, "$C2_X", light.m_Attenuation2 );
					SetNamedMaterialVar( src_mat, "$C2_Y", light.m_Attenuation1 );
					SetNamedMaterialVar( src_mat, "$C2_Z", light.m_Attenuation0 );
					SetNamedMaterialVar( src_mat, "$C2_W", 1.0 );
				
					Vector color_intens = light.m_Color;
					SetNamedMaterialVar(src_mat, "$C3_X", color_intens.x);
					SetNamedMaterialVar(src_mat, "$C3_Y", color_intens.y);
					SetNamedMaterialVar(src_mat, "$C3_Z", color_intens.z);
				
					pRenderContext->SetRenderTarget(dest_rt_current);
					pRenderContext->DrawScreenSpaceRectangle(
						src_mat, 0, 0, nTargetWidth, nTargetHeight,
						0,0,
						nTargetWidth - 1, nTargetHeight -1,
						dest_rt->GetActualWidth(),
						dest_rt->GetActualHeight());
					V_swap(dest_rt_current,dest_rt_other);
					V_swap(sample_last,sample_other);
					V_swap(add_0_to_1,add_1_to_0);
				}
				pRenderContext->SetRenderTarget(NULL);
				pRenderContext->DrawScreenSpaceRectangle(
					sample_last, xl, yl, dest_width, dest_height,
					0,0,
					nTargetWidth, nTargetHeight,
					dest_rt->GetActualWidth(),
					dest_rt->GetActualHeight());
			
			}
		}
		MaterialSystemInterface()->SwapBuffers();

		if ( (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) &&
			 g_pLPreviewOutputBitmap )
		{
			// blit it
			BITMAPINFOHEADER mybmh;
			mybmh.biHeight=-g_pLPreviewOutputBitmap->Height();
			mybmh.biSize=sizeof(BITMAPINFOHEADER);
			// now, set up bitmapheader struct for StretchDIB
			mybmh.biWidth=g_pLPreviewOutputBitmap->Width();
			mybmh.biPlanes=1;
			mybmh.biBitCount=32;
			mybmh.biCompression=BI_RGB;
			mybmh.biSizeImage=g_pLPreviewOutputBitmap->Width()*g_pLPreviewOutputBitmap->Height();

			RECT wrect;
			memset(&wrect,0,sizeof(wrect));
  
			CCamera *pCamera = GetCamera();
			int width, height;
			pCamera->GetViewPort( width, height );
// 			StretchDIBits(
// 				m_WinData.hDC,0,0,width,height,
// 				0,0,g_pLPreviewOutputBitmap->m_nWidth, g_pLPreviewOutputBitmap->m_nHeight,
// 				g_pLPreviewOutputBitmap->m_pBits, (BITMAPINFO *) &mybmh,
// 				DIB_RGB_COLORS, SRCCOPY);

			// remember that we blitted it
			m_pView->m_nLastRaytracedBitmapRenderTimeStamp = 
				GetUpdateCounter( EVTYPE_BITMAP_RECEIVED_FROM_LPREVIEW );
		}

		if (g_bShowStatistics)
		{
			//
			// Calculate frame rate.
			//
			if (m_dwTimeLastSample != 0)
			{
				DWORD dwTimeNow = timeGetTime();
				DWORD dwTimeElapsed = dwTimeNow - m_dwTimeLastSample;
				if ((dwTimeElapsed > 1000) && (m_nFramesThisSample > 0))
				{
					float fTimeElapsed = (float)dwTimeElapsed / 1000.0;
					m_fFrameRate = m_nFramesThisSample / fTimeElapsed;
					m_nFramesThisSample = 0;
					m_dwTimeLastSample = dwTimeNow;
				}
			}
			else
			{
				m_dwTimeLastSample = timeGetTime();
			}
		
			m_nFramesThisSample++;

			//
			// Display the frame rate and camera position.
			//
			char szText[100];
			Vector ViewPoint;
			GetCamera()->GetViewPoint(ViewPoint);
			int nLen = sprintf(szText, "FPS=%3.2f Pos=[%.f %.f %.f]", m_fFrameRate, ViewPoint[0], ViewPoint[1], ViewPoint[2]);
			TextOut(m_WinData.hDC, 2, 18, szText, nLen);
		}
	}
}


void CRender3D::PushInstanceData( CMapInstance *pInstanceClass, Vector &InstanceOrigin, QAngle &InstanceAngles )
{
	__super::PushInstanceData( pInstanceClass, InstanceOrigin, InstanceAngles );

	if ( m_bInstanceRendering )
	{
		CMapFace::PushFaceQueue();
	}
}

void CRender3D::PopInstanceData( void )
{
	if ( m_bInstanceRendering )
	{
		CMapFace::PopFaceQueue();
	}

	__super::PopInstanceData();
}

//-----------------------------------------------------------------------------
// Renders the world axes 
//-----------------------------------------------------------------------------
void CRender3D::RenderWorldAxes()
{
	// Render the world axes.
	PushRenderMode( RENDER_MODE_WIREFRAME );

	CMeshBuilder meshBuilder3D;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh* pMesh = pRenderContext->GetDynamicMesh( );
	meshBuilder3D.Begin( pMesh, MATERIAL_LINES, 3 );

	meshBuilder3D.Color3ub(255, 0, 0);
	meshBuilder3D.Position3f(0, 0, 0);
	meshBuilder3D.AdvanceVertex();

	meshBuilder3D.Color3ub(255, 0, 0);
	meshBuilder3D.Position3f(100, 0, 0);
	meshBuilder3D.AdvanceVertex();

	meshBuilder3D.Color3ub(0, 255, 0);
	meshBuilder3D.Position3f(0, 0, 0);
	meshBuilder3D.AdvanceVertex();

	meshBuilder3D.Color3ub(0, 255, 0);
	meshBuilder3D.Position3f(0, 100, 0);
	meshBuilder3D.AdvanceVertex();
	
	meshBuilder3D.Color3ub(0, 0, 255);
	meshBuilder3D.Position3f(0, 0, 0);
	meshBuilder3D.AdvanceVertex();

	meshBuilder3D.Color3ub(0, 0, 255);
	meshBuilder3D.Position3f(0, 0, 100);
	meshBuilder3D.AdvanceVertex();

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

	PopRenderMode();
}


//-----------------------------------------------------------------------------
// Purpose: this will handle all translucent rendering, including local transforms for instance items
// Input  : none
// Output : none
//-----------------------------------------------------------------------------
void CRender3D::RenderTranslucentObjects( void )
{
	CMatRenderContextPtr	pRenderContext( MaterialSystemInterface() );
	bool					bAddedTransform = false;
	CMapInstance			*pInstanceClass = NULL;
	TInstanceState			SaveInstanceState = m_CurrentInstanceState;

	m_bInstanceRendering = false;

	// render translucent objects after all opaque objects
	while ( m_TranslucentRenderObjects.Count() > 0 )
	{
		TranslucentObjects_t current = m_TranslucentRenderObjects.ElementAtHead();
		m_TranslucentRenderObjects.RemoveAtHead();	

		if ( current.m_InstanceState.m_pInstanceClass )
		{
			if ( pInstanceClass != current.m_InstanceState.m_pInstanceClass || !m_bInstanceRendering || current.m_InstanceState.m_InstanceMatrix != m_CurrentInstanceState.m_InstanceMatrix  )
			{
				if ( bAddedTransform )
				{
					EndLocalTransfrom();
				}
				bAddedTransform = true;
				BeginLocalTransfrom( current.m_InstanceState.m_InstanceRenderMatrix, false );
				m_CurrentInstanceState = current.m_InstanceState;
				pInstanceClass = m_CurrentInstanceState.m_pInstanceClass;
				m_bInstanceRendering = true;
	
				if ( pInstanceClass->IsEditable() )
				{
					SetInstanceRendering( INSTANCE_STATE_OFF );
				}
				else
				{
					SetInstanceRendering( current.m_bInstanceSelected ? INSTANCE_STACE_SELECTED : INSTANCE_STATE_ON );
				}
			}
		}
		else
		{
			if ( m_bInstanceRendering )
			{
				if ( bAddedTransform )
				{
					EndLocalTransfrom();
					bAddedTransform = false;
				}

				SetInstanceRendering( INSTANCE_STATE_OFF );
				m_bInstanceRendering = false;
			}
		}

		current.object->Render3D( this );
	}

	m_bInstanceRendering = false;
	if ( bAddedTransform )
	{
		EndLocalTransfrom();
	}

	m_CurrentInstanceState = SaveInstanceState;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::Render(void)
{
	CMapDoc		*pDoc = m_pView->GetMapDoc();
	CMapWorld	*pMapWorld = pDoc->GetMapWorld();
	CManifest	*pManifest = pDoc->GetManifest();

	bool view_changed = false;
	
	CCamera *pCamera = GetCamera();
	Vector new_vp;
	pCamera->GetViewPoint( new_vp );
	int width, height;
	pCamera->GetViewPort( width, height );

	if ( GetMainWnd()->m_pLightingPreviewOutputWindow)
	{
		SendLightList();									// nop if nothing changed
		SendShadowTriangles();								// nop if nothing changed
	}

	if ( (pCamera->GetYaw() != m_fLastLPreviewAngles[0] ) ||
		 (pCamera->GetPitch() != m_fLastLPreviewAngles[1] ) ||
		 (pCamera->GetRoll() != m_fLastLPreviewAngles[2] ) ||
		 (m_nLastLPreviewHeight != height ) ||
		 (m_nLastLPreviewWidth != width ) ||
		 ( new_vp != m_LastLPreviewCameraPos ) ||
		 (pCamera->GetZoom() != m_fLastLPreviewZoom ) )
		view_changed = true;

	if ( (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) &&
		 g_pLPreviewOutputBitmap &&
		 (! view_changed ) )
	{
		// blit it
		BITMAPINFOHEADER mybmh;
		mybmh.biHeight=-g_pLPreviewOutputBitmap->Height();
		mybmh.biSize=sizeof(BITMAPINFOHEADER);
		// now, set up bitmapheader struct for StretchDIB
		mybmh.biWidth=g_pLPreviewOutputBitmap->Width();
		mybmh.biPlanes=1;
		mybmh.biBitCount=32;
		mybmh.biCompression=BI_RGB;
		mybmh.biSizeImage=g_pLPreviewOutputBitmap->Width()*g_pLPreviewOutputBitmap->Height();

		RECT wrect;
		memset(&wrect,0,sizeof(wrect));
  
		pCamera->GetViewPort( width, height );
// 		StretchDIBits(
// 			m_WinData.hDC,0,0,width,height,
// 			0,0,g_pLPreviewOutputBitmap->m_nWidth, g_pLPreviewOutputBitmap->m_nHeight,
// 			g_pLPreviewOutputBitmap->m_pBits, (BITMAPINFO *) &mybmh,
// 			DIB_RGB_COLORS, SRCCOPY);
		m_pView->m_nLastRaytracedBitmapRenderTimeStamp = 
			GetUpdateCounter( EVTYPE_BITMAP_RECEIVED_FROM_LPREVIEW );
//		return;
	}

	StartRenderFrame();
	
	if (
		( m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW2 ) &&
		( m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED )
		)
		RenderWorldAxes();

	//
	// Deferred rendering lets us sort everything here by material.
	//
	if (!IsPicking())
	{
		m_DeferRendering = true;
	}

	m_TranslucentSortRendering = true;

// 	if (IsInLightingPreview())
// 	{
// 		// Lighting preview?
// 		IBSPLighting *pBSPLighting = pDoc->GetBSPLighting();
// 		if (pBSPLighting)
// 		{
// 			pBSPLighting->Draw();
// 		}
// 	}

	//
	// Render the world using octree culling.
	//

	PrepareInstanceStencil();

	if ( pManifest )
	{
 		pMapWorld = pManifest->GetManifestWorld();
	}

	if (g_bUseCullTree)
	{
		RenderTree( pMapWorld );
	}
	//
	// Render the world without octree culling.
	//
	else
	{
		RenderMapClass( pMapWorld );
	}

	if ( m_DeferRendering )
	{
		m_DeferRendering = false;

		// An optimization... render tree doesn't actually render anythung
		// This here will do the rendering, sorted by material by pass
		CMapFace::RenderOpaqueFaces(this);
	}

	RenderTranslucentObjects();

	DrawInstanceStencil();

	m_TranslucentSortRendering = false;
	pDoc->RenderDocument( this );

	RenderTool();

	RenderPointsAndPortals();

    
#ifdef _DEBUG
	if (m_bRenderFrustum)
	{
		RenderFrustum();
	}
#endif

	//
	// Render any 2D elements that overlay the 3D view, like a center crosshair.
	//
	RenderOverlayElements();

	EndRenderFrame();

	// Purge any translucent detail objects that were added AFTER the translucent rendering loop
	if ( m_TranslucentRenderObjects.Count() )
		m_TranslucentRenderObjects.Purge();
}


//-----------------------------------------------------------------------------
// Purpose: render an arrow of a given color at a given position (start and end)
//          in world space
// Input  : vStartPt - the arrow starting point
//          vEndPt - the arrow ending point (the head of the arrow)
//          chRed, chGree, chBlue - the arrow color
//-----------------------------------------------------------------------------
void CRender3D::RenderArrow( Vector const &vStartPt, Vector const &vEndPt, 
							   unsigned char chRed, unsigned char chGreen, unsigned char chBlue )
{
	//
	// render the stick portion of the arrow
	//

	// set to a flat shaded render mode
	PushRenderMode( RENDER_MODE_FLAT );
	SetDrawColor( chRed, chGreen, chBlue );
	DrawLine( vStartPt, vEndPt );
	PopRenderMode();

	//
	// render the tip of the arrow
	//
	Vector coneAxis = vEndPt - vStartPt;
	float length = VectorNormalize( coneAxis );
	float length8 = length * 0.125;
	length -= length8;
	
	Vector vBasePt;
	vBasePt = vStartPt + coneAxis * length;

	RenderCone( vBasePt, vEndPt, ( length8 * 0.333 ), 6, chRed, chGreen, chBlue );
}


//-----------------------------------------------------------------------------
// Purpose: Renders a box in flat shaded or wireframe depending on our render mode.
// Input  : chRed - 
//			chGreen - 
//			chBlue - 
//-----------------------------------------------------------------------------
void CRender3D::RenderBox(const Vector &Mins, const Vector &Maxs,
							unsigned char chRed, unsigned char chGreen, unsigned char chBlue, SelectionState_t eBoxSelectionState)
{
	Vector FacePoints[8];

	PointsFromBox( Mins, Maxs, FacePoints );

	int nFaces[6][4] =
		{
			{ 0, 2, 3, 1 },
			{ 0, 1, 5, 4 },
			{ 4, 5, 7, 6 },
			{ 2, 6, 7, 3 },
			{ 1, 3, 7, 5 },
			{ 0, 4, 6, 2 }
		};

	EditorRenderMode_t eRenderModeThisPass;
	int nPasses;

	if ((eBoxSelectionState != SELECT_NONE) && (GetDefaultRenderMode() != RENDER_MODE_WIREFRAME))
	{
		nPasses = 2;
	}
	else
	{
		nPasses = 1;
	}

	for (int nPass = 1; nPass <= nPasses; nPass++)
	{
		if (nPass == 1)
		{
			eRenderModeThisPass = GetDefaultRenderMode();

			// There's no texture for a bounding box.
			if ((eRenderModeThisPass == RENDER_MODE_TEXTURED) ||
				(eRenderModeThisPass == RENDER_MODE_TEXTURED_SHADED) ||
				(eRenderModeThisPass == RENDER_MODE_LIGHT_PREVIEW2) ||
				(eRenderModeThisPass == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) ||
				(eRenderModeThisPass == RENDER_MODE_LIGHTMAP_GRID))
			{
				eRenderModeThisPass = RENDER_MODE_FLAT;
			}

			PushRenderMode(eRenderModeThisPass);
		}
		else
		{
			eRenderModeThisPass = RENDER_MODE_WIREFRAME;
			PushRenderMode(eRenderModeThisPass);
		}

		for (int nFace = 0; nFace < 6; nFace++)
		{
			Vector Edge1, Edge2, Normal;
			int nP1, nP2, nP3, nP4;

			nP1 = nFaces[nFace][0];
			nP2 = nFaces[nFace][1];
			nP3 = nFaces[nFace][2];
			nP4 = nFaces[nFace][3];

			VectorSubtract(FacePoints[nP4], FacePoints[nP1], Edge1);
			VectorSubtract(FacePoints[nP2], FacePoints[nP1], Edge2);
			CrossProduct(Edge1, Edge2, Normal);
			VectorNormalize(Normal);

			//
			// If we are rendering using one of the lit modes, calculate lighting.
			// 
			unsigned char color[3];

			assert( (eRenderModeThisPass != RENDER_MODE_TEXTURED) &&
					(eRenderModeThisPass != RENDER_MODE_TEXTURED_SHADED) && 
					(eRenderModeThisPass != RENDER_MODE_LIGHT_PREVIEW2) && 
					(eRenderModeThisPass != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) && 
					(eRenderModeThisPass != RENDER_MODE_LIGHTMAP_GRID) ); 
			if ((eRenderModeThisPass == RENDER_MODE_FLAT))
			{
				float fShade = LightPlane(Normal);

				//
				// For flat and textured mode use the face color with lighting.
				//
				if (eBoxSelectionState != SELECT_NONE)
				{
					color[0] = SELECT_FACE_RED * fShade;
					color[1] = SELECT_FACE_GREEN * fShade;
					color[2] = SELECT_FACE_BLUE * fShade;
				}
				else
				{
					color[0] = chRed * fShade;
					color[1] = chGreen * fShade;
					color[2] = chBlue * fShade;
				}
			}
			//
			// For wireframe mode use the face color without lighting.
			//
			else
			{
				if (eBoxSelectionState != SELECT_NONE)
				{
					color[0] = SELECT_FACE_RED;
					color[1] = SELECT_FACE_GREEN;
					color[2] = SELECT_FACE_BLUE;
				}
				else
				{
					color[0] = chRed;
					color[1] = chGreen;
					color[2] = chBlue;
				}
			}

			//
			// Draw the face.
			//
			bool wireframe = (eRenderModeThisPass == RENDER_MODE_WIREFRAME);

			CMeshBuilder meshBuilder3D;
			CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
			IMesh* pMesh = pRenderContext->GetDynamicMesh();
			meshBuilder3D.DrawQuad( pMesh, FacePoints[nP1].Base(), FacePoints[nP2].Base(),
								  FacePoints[nP3].Base(), FacePoints[nP4].Base(), color, wireframe );
		}

		PopRenderMode();
	}
}


//-----------------------------------------------------------------------------
// Purpose: render a cone of a given color at a given position in world space
// Intput : vBasePt - the start point of the cone (the base point)
//          vTipPt - the end point of the cone (the peak)
//          fRadius - the radius (at the base) of the cone
//          nSlices - the number of slices (segments) making up the cone
//          chRed, chGreen, chBlue - the cone color
//-----------------------------------------------------------------------------
void CRender3D::RenderCone( Vector const &vBasePt, Vector const &vTipPt, float fRadius, int nSlices,
		                      unsigned char chRed, unsigned char chGreen, unsigned char chBlue )
{
	// get the angle between slices (in radians)
	float sliceAngle = ( 2 * M_PI ) / ( float )nSlices;

	//
	// allocate ALIGNED!!!!!!! vectors for cone base
	//
	int size = nSlices * sizeof( Vector );
	size += 16 + sizeof( Vector* );
	byte *ptr = ( byte* )_alloca( size );
	long data = ( long )ptr;
	
	data += 16 + sizeof( Vector* ) - 1;
	data &= -16;

	(( void** )data)[-1] = ptr;

	Vector *pPts = ( Vector* )data;
	if( !pPts )
		return;

	//
	// calculate the cone's base points in a local space (x,y plane)
	//
	for( int i = 0; i < nSlices; i++ )
	{
		pPts[i].x = fRadius * cos( ( sliceAngle * -i ) );
		pPts[i].y = fRadius * sin( ( sliceAngle * -i ) );
		pPts[i].z = 0.0f;
	}

	//
	// get cone tip in local space
	//
	Vector coneAxis = vTipPt - vBasePt;
	float length = coneAxis.Length();
	Vector tipPt( 0.0f, 0.0f, length );

	//
	// create cone faces
	//
	CMapFaceList m_Faces;
	Vector ptList[3];
	
	// triangulate the base
	for( int i = 0; i < ( nSlices - 2 ); i++ )
	{	
		ptList[0] = pPts[0];
		ptList[1] = pPts[i+1];
		ptList[2] = pPts[i+2];

		// add face to list
		CMapFace *pFace = new CMapFace;
		if( !pFace )
			return;
		pFace->SetRenderColor( chRed, chGreen, chBlue );
		pFace->CreateFace( ptList, 3 );
		pFace->RenderUnlit( true );
		m_Faces.AddToTail( pFace );
	}

	// triangulate the sides
	for( int i = 0; i < nSlices; i++ )
	{
		ptList[0] = pPts[i];
		ptList[1] = tipPt;
		ptList[2] = pPts[(i+1)%nSlices];

		// add face to list
		CMapFace *pFace = new CMapFace;
		if( !pFace )
			return;
		pFace->SetRenderColor( chRed, chGreen, chBlue );
		pFace->CreateFace( ptList, 3 );
		pFace->RenderUnlit( true );
		m_Faces.AddToTail( pFace );
	}

	//
	// rotate base points into world space as they are being rendered
	//
	VectorNormalize( coneAxis );
	QAngle rotAngles;
	VectorAngles( coneAxis, rotAngles );
	rotAngles[PITCH] += 90;

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	pRenderContext->MatrixMode( MATERIAL_MODEL ); 
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();

	pRenderContext->Translate( vBasePt.x, vBasePt.y, vBasePt.z );

	pRenderContext->Rotate( rotAngles[YAW], 0, 0, 1 );
	pRenderContext->Rotate( rotAngles[PITCH], 0, 1, 0 );
	pRenderContext->Rotate( rotAngles[ROLL], 1, 0, 0 );

	// set to a flat shaded render mode
	PushRenderMode( RENDER_MODE_FLAT );

	for ( int i = 0; i < m_Faces.Count(); i++ )
	{
		CMapFace *pFace = m_Faces.Element( i );
		if( !pFace )
			continue;
		pFace->Render3D( this );
	}

	pRenderContext->PopMatrix();

	// set back to default render mode
	PopRenderMode();

	//
	// delete the faces in the list
	//
	for ( int i = 0; i < m_Faces.Count(); i++ )
	{
		CMapFace *pFace = m_Faces.Element( i );
		delete pFace;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : vCenter - 
//			flRadius - 
//			nTheta - Number of vertical slices in the sphere.
//			nPhi - Number of horizontal slices in the sphere.
//			chRed - 
//			chGreen - 
//			chBlue - 
//-----------------------------------------------------------------------------
void CRender3D::RenderSphere(Vector const &vCenter, float flRadius, int nTheta, int nPhi,
							   unsigned char chRed, unsigned char chGreen, unsigned char chBlue )
{
	PushRenderMode( RENDER_MODE_EXTERN );

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

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	pRenderContext->Bind( m_pVertexColor[0] );

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

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

	//
	// Build the index buffer.
	//
	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);

			float flScale = LightPlane(vecNormal);

			unsigned char red = chRed * flScale;
			unsigned char green = chGreen * flScale;
			unsigned char blue = chBlue * flScale;

			vecPos += vCenter;

			meshBuilder3D.Position3f( vecPos.x, vecPos.y, vecPos.z );
			meshBuilder3D.Color3ub( red, green, blue );
			meshBuilder3D.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;

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

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

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

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

	meshBuilder3D.End();
	pMesh->Draw();
	
	PopRenderMode();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::RenderWireframeSphere(Vector const &vCenter, float flRadius, int nTheta, int nPhi,
							            unsigned char chRed, unsigned char chGreen, unsigned char chBlue )
{
	PushRenderMode(RENDER_MODE_WIREFRAME);

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

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

	CMeshBuilder meshBuilder3D;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh* pMesh = pRenderContext->GetDynamicMesh();

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

	for ( int i = 0; i < nPhi; ++i )
	{
		for ( int 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;
			meshBuilder3D.Position3f( vCenter.x + ( flRadius * sin(phi) * cos(theta) ),
				                    vCenter.y + ( flRadius * sin(phi) * sin(theta) ), 
									vCenter.z + ( flRadius * cos(phi) ) );
			meshBuilder3D.Color3ub( chRed, chGreen, chBlue );
			meshBuilder3D.AdvanceVertex();
		}
	}

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

			meshBuilder3D.Index( idx );
			meshBuilder3D.AdvanceIndex();

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

			meshBuilder3D.Index( idx );
			meshBuilder3D.AdvanceIndex();

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

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

	PopRenderMode();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pDrawDC - 
//-----------------------------------------------------------------------------
void CRender3D::RenderPointsAndPortals(void)
{
	CMapDoc *pDoc = m_pView->GetMapDoc();

	if ( pDoc->m_PFPoints.Count() )
	{
		PushRenderMode(RENDER_MODE_WIREFRAME);

		int nPFPoints = pDoc->m_PFPoints.Count();
		Vector* pPFPoints = pDoc->m_PFPoints.Base();
		CMeshBuilder meshBuilder3D;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh* pMesh = pRenderContext->GetDynamicMesh( );
		meshBuilder3D.Begin( pMesh, MATERIAL_LINE_STRIP, nPFPoints - 1 );

		for (int i = 0; i < nPFPoints; i++)
		{
			meshBuilder3D.Position3f(pPFPoints[i][0], pPFPoints[i][1], pPFPoints[i][2]);
			meshBuilder3D.Color3ub(255, 0, 0);
			meshBuilder3D.AdvanceVertex();
		}

		meshBuilder3D.End();
		pMesh->Draw();
		PopRenderMode();
	}

	// draw any portal file that was loaded
	if ( pDoc->m_pPortalFile )
	{
		PushRenderMode(RENDER_MODE_FLAT_NOCULL);

		// each vert makes and edge and thus a quad
		int totalQuads = pDoc->m_pPortalFile->totalVerts;
		int nMaxVerts;
		int nMaxIndices;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		pRenderContext->GetMaxToRender( pRenderContext->GetDynamicMesh( ), false, &nMaxVerts, &nMaxIndices );

		int portalIndex = 0;
		int baseVert = 0;
		while ( totalQuads > 0 )
		{
			int quadLimit = totalQuads;
			int quadOut = 0;
			IMesh* pMesh = pRenderContext->GetDynamicMesh( );
			if ( (quadLimit * 4) > nMaxVerts )
			{
				quadLimit = nMaxVerts / 4;
			}
			if ( (quadLimit * 6) > nMaxIndices )
			{
				quadLimit = nMaxIndices / 6;
			}
			CMeshBuilder meshBuilder3D;
			meshBuilder3D.Begin( pMesh, MATERIAL_QUADS, quadLimit );

			const float edgeWidth = 2.0f;
			for (; portalIndex < pDoc->m_pPortalFile->vertCount.Count(); portalIndex++)
			{
				int vertCount = pDoc->m_pPortalFile->vertCount[portalIndex];

				if ( (quadOut + vertCount) > quadLimit )
					break;
				quadOut += vertCount;
				// compute a face normal
				Vector e0 = pDoc->m_pPortalFile->verts[baseVert+1] - pDoc->m_pPortalFile->verts[baseVert];
				Vector e1 = pDoc->m_pPortalFile->verts[baseVert+2] - pDoc->m_pPortalFile->verts[baseVert];
				Vector normal = CrossProduct( e1, e0 );
				VectorNormalize(normal);
				for ( int j = 0; j < vertCount; j++ )
				{
					int v0 = baseVert + j;
					int v1 = baseVert + ((j+1) % vertCount);
					// compute the direction in the plane of the face to extrude the edge toward the
					// face interior, use that to make a wide line with a quad
					e0 = pDoc->m_pPortalFile->verts[v1] - pDoc->m_pPortalFile->verts[v0];
					Vector dir = CrossProduct( e0, normal );
					VectorNormalize(dir);
					dir *= edgeWidth;
					meshBuilder3D.Position3fv( pDoc->m_pPortalFile->verts[v0].Base() );
					meshBuilder3D.Color3ub(0, 0, 255);
					meshBuilder3D.AdvanceVertex();
					meshBuilder3D.Position3fv( pDoc->m_pPortalFile->verts[v1].Base() );
					meshBuilder3D.Color3ub(0, 0, 255);
					meshBuilder3D.AdvanceVertex();
					meshBuilder3D.Position3fv( (pDoc->m_pPortalFile->verts[v1] + dir).Base() );
					meshBuilder3D.Color3ub(0, 0, 255);
					meshBuilder3D.AdvanceVertex();
					meshBuilder3D.Position3fv( (pDoc->m_pPortalFile->verts[v0] + dir).Base() );
					meshBuilder3D.Color3ub(0, 0, 255);
					meshBuilder3D.AdvanceVertex();
				}
				baseVert += vertCount;
			}

			meshBuilder3D.End();
			pMesh->Draw();
			totalQuads -= quadOut;
		}
		PopRenderMode();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Draws a wireframe box using the given color.
// Input  : pfMins - Pointer to the box minima in all 3 dimensions.
//			pfMins - Pointer to the box maxima in all 3 dimensions.
//			chRed, chGreen, chBlue - Red, green, and blue color compnents for the box.
//-----------------------------------------------------------------------------
void CRender3D::RenderWireframeBox(const Vector &Mins, const Vector &Maxs,
									 unsigned char chRed, unsigned char chGreen, unsigned char chBlue)
{
	//
	// Draw the box bottom, top, and one corner edge.
	//

	PushRenderMode( RENDER_MODE_WIREFRAME );
	SetDrawColor( chRed, chGreen, chBlue );
	DrawBox( Mins, Maxs );
	PopRenderMode();
}


//-----------------------------------------------------------------------------
// Purpose: Renders this object (and all of its children) if it is visible and
//			has not already been rendered this frame.
// Input  : pMapClass - Pointer to the object to be rendered.
//-----------------------------------------------------------------------------
void CRender3D::RenderMapClass(CMapClass *pMapClass)
{
	Assert(pMapClass != NULL);

	if ((pMapClass != NULL) && (pMapClass->GetRenderFrame() != m_nFrameCount))
	{
		if (pMapClass->IsVisible())
		{
			//
			// Render this object's culling box if it is enabled.
			//
			if (g_bRenderCullBoxes)
			{
				Vector mins,maxs;
				pMapClass->GetCullBox(mins, maxs);

				RenderWireframeBox(mins, maxs, 255, 0, 0);
			}

			bool should_appear=true;
			if (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW2)
				should_appear &= pMapClass->ShouldAppearInLightingPreview();

			if (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED)
				should_appear &= pMapClass->ShouldAppearInLightingPreview();
//				should_appear &= pMapClass->ShouldAppearInRaytracedLightingPreview();

			if ( should_appear == true && m_Pick.bPicking == true && ( m_Pick.m_nFlags & FLAG_OBJECTS_AT_ONLY_SOLIDS ) != 0 )
			{
				if ( pMapClass->IsSolid() == false )
				{
					should_appear = false;
				}
			}


			if ( should_appear )
			{
				//
				// If we should render this object after all the other objects,
				// just add it to a list of objects to render last. Otherwise, render it now.
				//
				if (!pMapClass->ShouldRenderLast())
				{
					pMapClass->Render3D(this);
				}
				else
				{
					if (
						(m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW2) &&
						(m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) )
					{
						AddTranslucentDeferredRendering( pMapClass );
					}
				}
			}
			//
			// Render this object's children.
			//
			const CMapObjectList *pChildren = pMapClass->GetChildren();
			
			FOR_EACH_OBJ( *pChildren, pos )
			{
				Vector vecMins,vecMaxs;
				
				CMapClass *pChild = pChildren->Element(pos);

				pChild->GetCullBox(vecMins, vecMaxs);

				if (IsBoxVisible(vecMins, vecMaxs) != VIS_NONE )
				{
					RenderMapClass(pChild);
				}
			}
		}

		//
		// Consider this object as handled for this frame.
		//
		pMapClass->SetRenderFrame(m_nFrameCount);
	}
}


//-----------------------------------------------------------------------------
// Purpose: this function will render an instance map at the specific offset and rotation
// Input  : pInstanceClass - the map class of the func_instance
//			pMapClass - the map class of the world spawn of the instance
//			InstanceOrigin - the translation offset
//			InstanceAngles - the axis rotation
// Output : none
//-----------------------------------------------------------------------------
void CRender3D::RenderInstanceMapClass( CMapInstance *pInstanceClass, CMapClass *pMapClass, Vector &InstanceOrigin, QAngle &InstanceAngles )
{
	if ( !pInstanceClass->IsInstanceVisible() )
	{
		return;
	}

	PushInstanceData( pInstanceClass, InstanceOrigin, InstanceAngles );

	m_nInstanceCount++;

	RenderInstanceMapClass_r( pMapClass );

	if ( m_DeferRendering )
	{
		CMapFace::RenderOpaqueFaces(this);
	}

	if ( m_TranslucentSortRendering == false )
	{	// translucent objects will do their own transforms
		RenderTranslucentObjects();
	}

	PopInstanceData();
}


//-----------------------------------------------------------------------------
// Purpose: this function will recursively render an instance and all of its children
// Input  : pObject - the object to be rendered
// Output : none
//-----------------------------------------------------------------------------
void CRender3D::RenderInstanceMapClass_r(CMapClass *pMapClass)
{
	Assert(pMapClass != NULL);

	if ( ( pMapClass != NULL ) && ( pMapClass->GetRenderFrame() != m_nInstanceCount ) )
	{
		if (pMapClass->IsVisible())
		{
			//
			// Render this object's culling box if it is enabled.
			//
			if (g_bRenderCullBoxes)
			{
				Vector vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs;
				pMapClass->GetCullBox( vecMins, vecMaxs );
				TransformInstanceAABB( vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs );

				RenderWireframeBox( vecExpandedMins, vecExpandedMaxs, 255, 0, 0 );
			}

			bool should_appear=true;
			if (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW2)
				should_appear &= pMapClass->ShouldAppearInLightingPreview();

			if (m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED)
				should_appear &= pMapClass->ShouldAppearInLightingPreview();
			//				should_appear &= pMapClass->ShouldAppearInRaytracedLightingPreview();

			if ( should_appear )
			{
				//
				// If we should render this object after all the other objects,
				// just add it to a list of objects to render last. Otherwise, render it now.
				//
				if (!pMapClass->ShouldRenderLast())
				{
					pMapClass->Render3D(this);
				}
				else
				{
					if (
						(m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW2) &&
						(m_eCurrentRenderMode != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) )
					{
						AddTranslucentDeferredRendering( pMapClass );
					}
				}
			}
			//
			// Render this object's children.
			//
			const CMapObjectList *pChildren = pMapClass->GetChildren();

			FOR_EACH_OBJ( *pChildren, pos )
			{
				Vector vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs;

				CMapClass *pChild = pChildren->Element( pos );

				pChild->GetCullBox( vecMins, vecMaxs );
				TransformInstanceAABB( vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs );

				if (IsBoxVisible( vecExpandedMins, vecExpandedMaxs ) != VIS_NONE )
				{
					RenderInstanceMapClass_r( pChild );
				}
			}
		}

		//
		// Consider this object as handled for this instance frame.
		//
		pMapClass->SetRenderFrame( m_nInstanceCount );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Prepares all objects in this node for rendering.
// Input  : pParent - 
//-----------------------------------------------------------------------------
void CRender3D::Preload(CMapClass *pParent)
{
	Assert(pParent != NULL);

	if (pParent != NULL)
	{
		//
		// Preload this object's children.
		//
		const CMapObjectList *pChildren = pParent->GetChildren();
		FOR_EACH_OBJ( *pChildren, pos )
		{
			pChildren->Element(pos)->RenderPreload(this, true);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Renders all objects in this node if this node is visible.
// Input  : pNode - The node to render.
//			bForce - If true, don't check for visibility, just render the node
//				and all of its children.
//-----------------------------------------------------------------------------
void CRender3D::RenderNode(CCullTreeNode *pNode, bool bForce )
{
	//
	// Render all child nodes first.
	//
	CCullTreeNode *pChild;
	int nChildren = pNode->GetChildCount();
	if (nChildren != 0)
	{
		for (int nChild = 0; nChild < nChildren; nChild++)
		{
			pChild = pNode->GetCullTreeChild(nChild);
			Assert(pChild != NULL);

			if (pChild != NULL)
			{
				//
				// Only bother checking nodes with children or objects.
				//
				if ((pChild->GetChildCount() != 0) || (pChild->GetObjectCount() != 0))
				{
					bool bForceThisChild = bForce;
					Visibility_t eVis = VIS_NONE;

					if (!bForceThisChild)
					{
						Vector vecMins;
						Vector vecMaxs;
						pChild->GetBounds(vecMins, vecMaxs);
						eVis = IsBoxVisible(vecMins, vecMaxs);
						if (eVis == VIS_TOTAL)
						{
							bForceThisChild = true;
						}
					}

					if ((bForceThisChild) || (eVis != VIS_NONE))
					{
						RenderNode(pChild, bForceThisChild);
					}
				}
			}
		}
	}
	else
	{
		//
		// Now render the contents of this node.
		//
		CMapClass *pObject;
		int nObjects = pNode->GetObjectCount();
		for (int nObject = 0; nObject < nObjects; nObject++)
		{
			pObject = pNode->GetCullTreeObject(nObject);
			Assert(pObject != NULL);

			Vector vecMins;
			Vector vecMaxs;
			pObject->GetCullBox(vecMins, vecMaxs);
			if (IsBoxVisible(vecMins, vecMaxs) != VIS_NONE)
			{
				RenderMapClass(pObject);
			}
		}
	}
}


void CRender3D::RenderCrossHair()
{
	int width, height;

	GetCamera()->GetViewPort( width, height );

	int nCenterX = width / 2;
	int nCenterY = height / 2;

	Assert( IsInClientSpace() );

	// Render the world axes
	PushRenderMode( RENDER_MODE_FLAT_NOZ );

	SetDrawColor(0,0,0);
   
	DrawLine(	Vector(nCenterX - CROSSHAIR_DIST_HORIZONTAL, nCenterY - 1, 0),
				Vector(nCenterX + CROSSHAIR_DIST_HORIZONTAL + 1, nCenterY - 1, 0) );

	DrawLine(	Vector(nCenterX - CROSSHAIR_DIST_HORIZONTAL, nCenterY + 1, 0),
				Vector(nCenterX + CROSSHAIR_DIST_HORIZONTAL + 1, nCenterY + 1, 0) );

	DrawLine(	Vector(nCenterX - 1, nCenterY - CROSSHAIR_DIST_VERTICAL, 0),
				Vector(nCenterX - 1, nCenterY + CROSSHAIR_DIST_VERTICAL, 0) );

	DrawLine(	Vector(nCenterX + 1, nCenterY - CROSSHAIR_DIST_VERTICAL, 0),
				Vector(nCenterX + 1, nCenterY + CROSSHAIR_DIST_VERTICAL, 0) );

	SetDrawColor(255,255,255);
	
	DrawLine(	Vector(nCenterX - CROSSHAIR_DIST_HORIZONTAL, nCenterY, 0),
				Vector(nCenterX + CROSSHAIR_DIST_HORIZONTAL + 1, nCenterY, 0) );

	DrawLine(	Vector(nCenterX, nCenterY - CROSSHAIR_DIST_VERTICAL, 0),
				Vector(nCenterX, nCenterY + CROSSHAIR_DIST_VERTICAL, 0) );

	PopRenderMode();
}

//-----------------------------------------------------------------------------
// Purpose: Renders 2D elements that overlay the 3D objects.
//-----------------------------------------------------------------------------
void CRender3D::RenderOverlayElements(void)
{
	bool bPopMode = BeginClientSpace();

	if (m_RenderState.bCenterCrosshair)
		RenderCrossHair();

	if ( bPopMode )
		EndClientSpace();

}


//-----------------------------------------------------------------------------
// Purpose: Gives all the tools a chance to render themselves.
//-----------------------------------------------------------------------------
void CRender3D::RenderTool(void)
{
	CMapDoc *pDoc = m_pView->GetMapDoc();

	CBaseTool *pTool = pDoc->GetTools()->GetActiveTool();

	if ( pTool )
	{
		pTool->RenderTool3D(this);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::RenderTree( CMapWorld *pWorld )
{
	if (pWorld == NULL)
	{
		return;
	}

	//
	// Recursively traverse the culling tree, rendering visible nodes.
	//
	CCullTreeNode *pTree = pWorld->CullTree_GetCullTree();
	if (pTree != NULL)
	{
		Vector vecMins;
		Vector vecMaxs;
		pTree->GetBounds(vecMins, vecMaxs);
		Visibility_t eVis = IsBoxVisible(vecMins, vecMaxs);

		if (eVis != VIS_NONE)
		{
			RenderNode(pTree, eVis == VIS_TOTAL);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Returns whether we are in lighting preview mode or not.
//-----------------------------------------------------------------------------
bool CRender3D::IsInLightingPreview()
{
	return false; //m_bLightingPreview;
}


//-----------------------------------------------------------------------------
// Purpose: Enables/disables lighting preview mode.
//-----------------------------------------------------------------------------
void CRender3D::SetInLightingPreview( bool bLightingPreview )
{
	m_bLightingPreview = false; //bLightingPreview;
}


void CRender3D::ResetFocus()
{
	// A bizarre workaround; the drop-down menu somehow
	// sets some wierd state that causes the whole screen to not be updated
	InvalidateRect( m_WinData.hWnd, 0, false );
}


//-----------------------------------------------------------------------------
// indicates we need to render an overlay pass...
//-----------------------------------------------------------------------------
bool CRender3D::NeedsOverlay() const
{
	return (m_eCurrentRenderMode == RENDER_MODE_LIGHTMAP_GRID) ||
		(m_eCurrentRenderMode == RENDER_MODE_TEXTURED_SHADED) ||
		(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW2) ||
		(m_eCurrentRenderMode == RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) ||
		(m_eCurrentRenderMode == RENDER_MODE_TEXTURED);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRender3D::ShutDown(void)
{
	MaterialSystemInterface()->RemoveView( m_WinData.hWnd );

	if (m_WinData.hDC)
	{
		m_WinData.hDC = NULL;
	}
	
	if (m_WinData.bFullScreen)
	{
		ChangeDisplaySettings(NULL, 0);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Uncaches all cached textures
//-----------------------------------------------------------------------------
void CRender3D::UncacheAllTextures()
{
}


//-----------------------------------------------------------------------------
// Purpose: Enables and disables various rendering parameters.
// Input  : eRenderState - Parameter to enable or disable. See RenderState_t.
//			bEnable - true to enable, false to disable the specified render state.
//-----------------------------------------------------------------------------
void CRender3D::RenderEnable(RenderState_t eRenderState, bool bEnable)
{
	switch (eRenderState)
	{
		case RENDER_POLYGON_OFFSET_FILL:
		{
			m_nDecalMode = bEnable?1:0;
			SetRenderMode( RENDER_MODE_CURRENT, true );
		}
		break;

		case RENDER_POLYGON_OFFSET_LINE:
		{
			assert(0);
			/* FIXME:
			   Think we'll need to have two versions of the wireframe material
			   one which ztests with offset + culling, the other which doesn't
			   ztest, doesn't offect, and doesn't cull??!?

			   m_pWireframeIgnoreZ->SetIntValue( bEnable );
			   m_pWireframe->GetMaterial()->InitializeStateSnapshots();
			   /*
			   if (bEnable)
			   {
			   glEnable(GL_POLYGON_OFFSET_LINE);
			   glPolygonOffset(-1, -1);
			   }
			   else
			   {
			   glDisable(GL_POLYGON_OFFSET_LINE);
			   }
			*/
			break;
		}

		case RENDER_CENTER_CROSSHAIR:
		{
			m_RenderState.bCenterCrosshair = bEnable;
			break;
		}

		case RENDER_GRID:
		{
			m_RenderState.bDrawGrid = bEnable;
			break;
		}

		case RENDER_FILTER_TEXTURES:
		{
			m_RenderState.bFilterTextures = bEnable;
			break;
		}

		case RENDER_REVERSE_SELECTION:
		{
			m_RenderState.bReverseSelection = bEnable;
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Groovy little debug hook; can be whatever I want or need.
// Input  : pData - 
//-----------------------------------------------------------------------------
void CRender3D::DebugHook1(void *pData)
{
	g_bShowStatistics = !g_bShowStatistics;

#ifdef _DEBUG
	m_bRecomputeFrustumRenderGeometry = true;
	m_bRenderFrustum = true;
#endif

	//if (!m_bDroppedCamera)
	//{
	//	*m_pDropCamera = *m_pCamera;
	//	m_bDroppedCamera = true;
	//}
	//else
	//{
	//	m_bDroppedCamera = false;
	//}
}


//-----------------------------------------------------------------------------
// Purpose: Another groovy little debug hook; can be whatever I want or need.
// Input  : pData - 
//-----------------------------------------------------------------------------
void CRender3D::DebugHook2(void *pData)
{
	g_bRenderCullBoxes = !g_bRenderCullBoxes;
}