2951 lines
80 KiB
C++
2951 lines
80 KiB
C++
//========= 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;
|
|
}
|
|
|