csgo-2018-source/engine/gl_matsysiface.cpp
2021-07-24 21:11:47 -07:00

1087 lines
33 KiB
C++
Raw Permalink Blame History

//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
// wrapper for the material system for the engine.
#include "render_pch.h"
#include "view.h"
#include "zone.h"
#include <float.h>
#include "sys_dll.h"
#include "materialsystem/imesh.h"
#include "gl_water.h"
#include "utlrbtree.h"
#include "istudiorender.h"
#include "tier0/dbg.h"
#include "keyvalues.h"
#include "vstdlib/random.h"
#include "lightcache.h"
#include "sysexternal.h"
#include "cmd.h"
#include "modelloader.h"
#include "tier0/icommandline.h"
#include "materialsystem/imaterial.h"
#include "toolframework/itoolframework.h"
#include "toolframework/itoolsystem.h"
#include "tier2/p4helpers.h"
#include "p4lib/ip4.h"
#include "vgui/ISystem.h"
#include <vgui_controls/Controls.h>
#include "paint.h"
extern ConVar developer;
#ifdef _WIN32
#include <crtdbg.h>
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifndef DEDICATED
IMaterial* g_materialWireframe;
IMaterial* g_materialTranslucentSingleColor;
IMaterial* g_materialTranslucentVertexColor;
IMaterial* g_materialWorldWireframe;
IMaterial* g_materialWorldWireframeZBuffer;
IMaterial* g_materialWorldWireframeGreen;
IMaterial* g_materialBrushWireframe;
IMaterial* g_materialDecalWireframe;
IMaterial* g_materialDebugLightmap;
IMaterial* g_materialDebugLightmapZBuffer;
IMaterial* g_materialDebugLuxels;
IMaterial* g_materialLeafVisWireframe;
IMaterial* g_pMaterialWireframeVertexColor;
IMaterial* g_pMaterialWireframeVertexColorIgnoreZ;
IMaterial* g_pMaterialLightSprite;
IMaterial* g_pMaterialShadowBuild;
IMaterial* g_pMaterialMRMWireframe;
IMaterial* g_pMaterialWriteZ;
IMaterial* g_pMaterialWaterDuDv;
IMaterial* g_pMaterialWaterFirstPass;
IMaterial* g_pMaterialWaterSecondPass;
IMaterial* g_pMaterialAmbientCube;
IMaterial* g_pMaterialDebugFlat;
IMaterial* g_pMaterialDepthWrite[2][2];
IMaterial* g_pMaterialSSAODepthWrite[ 2 ][ 2 ];
#ifdef NEWMESH
CUtlVector<IVertexBuffer *> g_WorldStaticMeshes; // fixme - rename to g_WorldStaticVertexBuffers
#else
CUtlVector<IMesh *> g_WorldStaticMeshes;
#endif
void WorldStaticMeshCreate( void );
void WorldStaticMeshDestroy( void );
bool TangentSpaceSurfaceSetup( SurfaceHandle_t surfID, Vector &tVect );
void TangentSpaceComputeBasis( Vector& tangent, Vector& binormal, const Vector& normal, const Vector& tVect, bool negateTangent );
//-----------------------------------------------------------------------------
// A console command edit a particular material
//-----------------------------------------------------------------------------
CON_COMMAND_F( mat_edit, "Bring up the material under the crosshair in the editor", FCVAR_CHEAT )
{
if ( !toolframework->InToolMode() )
return;
IMaterial* pMaterial = NULL;
if ( args.ArgC() < 2 )
{
pMaterial = GetMaterialAtCrossHair();
}
else
{
const char *pMaterialName = args[ 1 ];
pMaterial = materials->FindMaterial( pMaterialName, "edited materials", false );
}
if ( !pMaterial )
{
ConMsg( "no/bad material\n" );
}
else
{
IToolSystem *pToolSystem = toolframework->SwitchToTool( "Material Editor" );
if ( pToolSystem )
{
ConMsg( "editing material \"%s\"\n", pMaterial->GetName() );
KeyValues *pKeyValues = new KeyValues( "EditMaterial" );
pKeyValues->SetString( "material", pMaterial->GetName() );
pToolSystem->PostToolMessage( 0, pKeyValues );
pKeyValues->deleteThis();
}
}
}
//-----------------------------------------------------------------------------
// A console command to spew out the material under the crosshair
//-----------------------------------------------------------------------------
CON_COMMAND_F( mat_crosshair, "Display the name of the material under the crosshair", FCVAR_CHEAT )
{
IMaterial* pMaterial = GetMaterialAtCrossHair();
if (!pMaterial)
ConMsg ("no/bad material\n");
else
ConMsg ("hit material \"%s\"\n", pMaterial->GetName());
}
//-----------------------------------------------------------------------------
// A console command to open the material under the crosshair in the associated editor.
//-----------------------------------------------------------------------------
CON_COMMAND_F( mat_crosshair_edit, "open the material under the crosshair in the editor defined by mat_crosshair_edit_editor", FCVAR_CHEAT )
{
IMaterial* pMaterial = GetMaterialAtCrossHair();
if (!pMaterial)
{
ConMsg ("no/bad material\n");
}
else
{
char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0};
Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vmt", pMaterial->GetName() );
char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 );
if ( p4 )
{
CP4AutoEditAddFile autop4( szResolvedName );
}
else
{
Warning( "run with -p4 to get p4 operations upon mat_crosshair_edit\n" );
}
vgui::system()->ShellExecute( "open", szResolvedName );
}
}
//-----------------------------------------------------------------------------
// A console command to open the material under the crosshair in the associated editor.
//-----------------------------------------------------------------------------
CON_COMMAND_F( mat_crosshair_explorer, "open the material under the crosshair in explorer and highlight the vmt file", FCVAR_CHEAT )
{
IMaterial* pMaterial = GetMaterialAtCrossHair();
if (!pMaterial)
{
ConMsg ("no/bad material\n");
}
else
{
char chResolveName[ 256 ] = {0}, chResolveNameArg[ 256 ] = {0};
Q_snprintf( chResolveNameArg, sizeof( chResolveNameArg ) - 1, "materials/%s.vmt", pMaterial->GetName() );
char const *szResolvedName = g_pFileSystem->RelativePathToFullPath( chResolveNameArg, "game", chResolveName, sizeof( chResolveName ) - 1 );
char params[256];
Q_snprintf( params, sizeof( params ) - 1, "/E,/SELECT,%s", szResolvedName );
vgui::system()->ShellExecuteEx( "open", "explorer.exe", params );
}
}
//-----------------------------------------------------------------------------
// A console command to open the material under the crosshair in the associated editor.
//-----------------------------------------------------------------------------
CON_COMMAND_F( mat_crosshair_reloadmaterial, "reload the material under the crosshair", FCVAR_CHEAT )
{
IMaterial* pMaterial = GetMaterialAtCrossHair();
if (!pMaterial)
{
ConMsg ("no/bad material\n");
}
else
{
materials->ReloadMaterials( pMaterial->GetName() );
}
}
CON_COMMAND_F( mat_crosshair_printmaterial, "print the material under the crosshair", FCVAR_CHEAT )
{
IMaterial* pMaterial = GetMaterialAtCrossHair();
if (!pMaterial)
{
ConMsg ("no/bad material\n");
}
else
{
materials->DebugPrintUsedMaterials( pMaterial->GetName(), true );
}
}
static void RegisterLightmappedSurface( SurfaceHandle_t surfID )
{
int lightmapSize[2];
int allocationWidth, allocationHeight;
bool bNeedsBumpmap;
// fixme: lightmapSize needs to be in msurface_t once we
// switch over to having lightmap size untied to base texture
// size
lightmapSize[0] = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
lightmapSize[1] = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
// Allocate all bumped lightmaps next to each other so that we can just
// increment the s texcoord by pSurf->bumpSTexCoordOffset to render the next
// of the three lightmaps
bNeedsBumpmap = SurfNeedsBumpedLightmaps( surfID );
if( bNeedsBumpmap )
{
MSurf_Flags( surfID ) |= SURFDRAW_BUMPLIGHT;
allocationWidth = lightmapSize[0] * ( NUM_BUMP_VECTS+1 );
}
else
{
MSurf_Flags( surfID ) &= ~SURFDRAW_BUMPLIGHT;
allocationWidth = lightmapSize[0];
}
allocationHeight = lightmapSize[1];
// register this surface's lightmap
int offsetIntoLightmapPage[2];
MSurf_MaterialSortID( surfID ) = materials->AllocateLightmap(
allocationWidth,
allocationHeight,
offsetIntoLightmapPage,
MSurf_TexInfo( surfID )->material );
MSurf_OffsetIntoLightmapPage( surfID )[0] = offsetIntoLightmapPage[0];
MSurf_OffsetIntoLightmapPage( surfID )[1] = offsetIntoLightmapPage[1];
}
static void RegisterUnlightmappedSurface( SurfaceHandle_t surfID )
{
MSurf_MaterialSortID( surfID ) = materials->AllocateWhiteLightmap( MSurf_TexInfo( surfID )->material );
MSurf_OffsetIntoLightmapPage( surfID )[0] = 0;
MSurf_OffsetIntoLightmapPage( surfID )[1] = 0;
}
static bool LightmapLess( const SurfaceHandle_t& surfID1, const SurfaceHandle_t& surfID2 )
{
// FIXME: This really should be in the material system,
// as it completely depends on the behavior of the lightmap packer
bool hasLightmap1 = (MSurf_Flags( surfID1 ) & SURFDRAW_NOLIGHT) == 0;
bool hasLightmap2 = (MSurf_Flags( surfID2 ) & SURFDRAW_NOLIGHT) == 0;
// We want lightmapped surfaces to show up first
if (hasLightmap1 != hasLightmap2)
return hasLightmap1 > hasLightmap2;
// The sort by enumeration ID
IMaterial* pMaterial1 = MSurf_TexInfo( surfID1 )->material;
IMaterial* pMaterial2 = MSurf_TexInfo( surfID2 )->material;
int enum1 = pMaterial1->GetEnumerationID();
int enum2 = pMaterial2->GetEnumerationID();
if (enum1 != enum2)
return enum1 < enum2;
bool hasLightstyle1 = (MSurf_Flags( surfID1 ) & SURFDRAW_HASLIGHTSYTLES) == 0;
bool hasLightstyle2 = (MSurf_Flags( surfID2 ) & SURFDRAW_HASLIGHTSYTLES) == 0;
// We want Lightstyled surfaces to show up first
if (hasLightstyle1 != hasLightstyle2)
return hasLightstyle1 > hasLightstyle2;
// Then sort by lightmap area for better packing... (big areas first)
// NOTE: Don't care about bumpmap increasing area here because it is a linear factor
// (all surfs with the same material have the same bumpmapping cost)
#if 1
int area1 = MSurf_LightmapExtents( surfID1 )[0] * MSurf_LightmapExtents( surfID1 )[1];
int area2 = MSurf_LightmapExtents( surfID2 )[0] * MSurf_LightmapExtents( surfID2 )[1];
return area2 < area1;
#else
// Previous algorithm: pack minimum height first
// NOTE: In d1_trainstation_05, greatest area results in fewer material splits
// so I've switched over to that heuristic
return MSurf_LightmapExtents( surfID1 )[1] < MSurf_LightmapExtents( surfID2 )[1];
#endif
}
void MaterialSystem_RegisterLightmapSurfaces( void )
{
SurfaceHandle_t surfID = SURFACE_HANDLE_INVALID;
materials->BeginLightmapAllocation();
// Add all the surfaces to a list, sorted by lightmapped
// then by material enumeration then by area
CUtlRBTree< SurfaceHandle_t, int > surfaces( 0, host_state.worldbrush->numsurfaces, LightmapLess );
for( int surfaceIndex = 0; surfaceIndex < host_state.worldbrush->numsurfaces; surfaceIndex++ )
{
surfID = SurfaceHandleFromIndex( surfaceIndex );
if( ( MSurf_TexInfo( surfID )->flags & SURF_NOLIGHT ) ||
( MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT) )
{
MSurf_Flags( surfID ) |= SURFDRAW_NOLIGHT;
}
else
{
MSurf_Flags( surfID ) &= ~SURFDRAW_NOLIGHT;
}
surfaces.Insert(surfID);
}
// iterate sorted surfaces
surfID = SURFACE_HANDLE_INVALID;
for (int i = surfaces.FirstInorder(); i != surfaces.InvalidIndex(); i = surfaces.NextInorder(i) )
{
SurfaceHandle_t surfID = surfaces[i];
bool hasLightmap = ( MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT) == 0;
if ( hasLightmap )
{
RegisterLightmappedSurface( surfID );
}
else
{
RegisterUnlightmappedSurface( surfID );
}
}
materials->EndLightmapAllocation();
}
static void TestBumpSanity( SurfaceHandle_t surfID )
{
ASSERT_SURF_VALID( surfID );
// use the last one to check if we need a bumped lightmap, but don't have it so that we can warn.
bool needsBumpmap = SurfNeedsBumpedLightmaps( surfID );
bool hasBumpmap = SurfHasBumpedLightmaps( surfID );
if ( needsBumpmap && !hasBumpmap && MSurf_Samples( surfID ) )
{
Warning( "Need to rebuild map to get bumped lighting on material %s\n",
materialSortInfoArray[MSurf_MaterialSortID( surfID )].material->GetName() );
}
}
void MaterialSytsem_DoBumpWarnings( void )
{
int sortID;
IMaterial *pPrevMaterial = NULL;
for( sortID = 0; sortID < g_WorldStaticMeshes.Count(); sortID++ )
{
if( pPrevMaterial == materialSortInfoArray[sortID].material )
{
continue;
}
// Find one surface in each material sort info type
for ( int surfaceIndex = 0; surfaceIndex < host_state.worldbrush->numsurfaces; surfaceIndex++ )
{
SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfaceIndex );
if( MSurf_MaterialSortID( surfID ) == sortID )
{
TestBumpSanity( surfID );
break;
}
}
pPrevMaterial = materialSortInfoArray[sortID].material;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void GenerateTexCoordsForPrimVerts( void )
{
int j, k, l;
for ( int surfaceIndex = 0; surfaceIndex < host_state.worldbrush->numsurfaces; surfaceIndex++ )
{
SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfaceIndex );
/*
if( pSurf->numPrims > 0 )
{
ConMsg( "pSurf %d has %d prims (normal: %f %f %f dist: %f)\n",
( int )i, ( int )pSurf->numPrims,
pSurf->plane->normal[0], pSurf->plane->normal[1], pSurf->plane->normal[2],
pSurf->plane->dist );
ConMsg( "\tfirst primID: %d\n", ( int )pSurf->firstPrimID );
}
*/
for( j = 0; j < MSurf_NumPrims( surfID ); j++ )
{
mprimitive_t *pPrim;
assert( MSurf_FirstPrimID( surfID ) + j < host_state.worldbrush->numprimitives );
pPrim = &host_state.worldbrush->primitives[MSurf_FirstPrimID( surfID ) + j];
for( k = 0; k < pPrim->vertCount; k++ )
{
int lightmapSize[2];
int lightmapPageSize[2];
float sOffset, sScale, tOffset, tScale;
materials->GetLightmapPageSize(
SortInfoToLightmapPage( MSurf_MaterialSortID( surfID ) ),
&lightmapPageSize[0], &lightmapPageSize[1] );
lightmapSize[0] = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
lightmapSize[1] = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
sScale = 1.0f / ( float )lightmapPageSize[0];
sOffset = ( float )MSurf_OffsetIntoLightmapPage( surfID )[0] * sScale;
sScale = MSurf_LightmapExtents( surfID )[0] * sScale;
tScale = 1.0f / ( float )lightmapPageSize[1];
tOffset = ( float )MSurf_OffsetIntoLightmapPage( surfID )[1] * tScale;
tScale = MSurf_LightmapExtents( surfID )[1] * tScale;
for ( l = 0; l < pPrim->vertCount; l++ )
{
// world-space vertex
assert( l+pPrim->firstVert < host_state.worldbrush->numprimverts );
mprimvert_t &vert = host_state.worldbrush->primverts[l+pPrim->firstVert];
Vector& vec = vert.pos;
// base texture coordinate
vert.texCoord[0] = DotProduct (vec, MSurf_TexInfo( surfID )->textureVecsTexelsPerWorldUnits[0].AsVector3D()) +
MSurf_TexInfo( surfID )->textureVecsTexelsPerWorldUnits[0][3];
vert.texCoord[0] /= MSurf_TexInfo( surfID )->material->GetMappingWidth();
vert.texCoord[1] = DotProduct (vec, MSurf_TexInfo( surfID )->textureVecsTexelsPerWorldUnits[1].AsVector3D()) +
MSurf_TexInfo( surfID )->textureVecsTexelsPerWorldUnits[1][3];
vert.texCoord[1] /= MSurf_TexInfo( surfID )->material->GetMappingHeight();
if ( (MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT) )
{
vert.lightCoord[0] = 0.5f;
vert.lightCoord[1] = 0.5f;
}
else if ( MSurf_LightmapExtents( surfID )[0] == 0 )
{
vert.lightCoord[0] = sOffset;
vert.lightCoord[1] = tOffset;
}
else
{
vert.lightCoord[0] = DotProduct (vec, MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[0][3];
vert.lightCoord[0] -= MSurf_LightmapMins( surfID )[0];
vert.lightCoord[0] += 0.5f;
vert.lightCoord[0] /= ( float )MSurf_LightmapExtents( surfID )[0]; //pSurf->texinfo->texture->width;
vert.lightCoord[1] = DotProduct (vec, MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[1][3];
vert.lightCoord[1] -= MSurf_LightmapMins( surfID )[1];
vert.lightCoord[1] += 0.5f;
vert.lightCoord[1] /= ( float )MSurf_LightmapExtents( surfID )[1]; //pSurf->texinfo->texture->height;
vert.lightCoord[0] = sOffset + vert.lightCoord[0] * sScale;
vert.lightCoord[1] = tOffset + vert.lightCoord[1] * tScale;
}
}
}
}
}
}
struct sortmap_t
{
MaterialSystem_SortInfo_t info;
int index;
};
IMatRenderContext *pSortMapRenderContext;
int __cdecl SortMapCompareFunc( const void *pElem0, const void *pElem1 )
{
const sortmap_t *pMap0 = (const sortmap_t *)pElem0;
const sortmap_t *pMap1 = (const sortmap_t *)pElem1;
return pSortMapRenderContext->CompareMaterialCombos( pMap0->info.material, pMap1->info.material, pMap0->info.lightmapPageID, pMap1->info.lightmapPageID );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void MaterialSystem_CreateSortinfo( void )
{
Assert( !materialSortInfoArray );
int nSortIDs = materials->GetNumSortIDs();
materialSortInfoArray = ( MaterialSystem_SortInfo_t * )new MaterialSystem_SortInfo_t[ nSortIDs ];
Assert( materialSortInfoArray );
materials->GetSortInfo( materialSortInfoArray );
int i = 0;
sortmap_t *pMap = (sortmap_t *)stackalloc( sizeof(sortmap_t) * nSortIDs );
for ( i = 0; i < nSortIDs; i++ )
{
pMap[i].info = materialSortInfoArray[i];
pMap[i].index = i;
}
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pSortMapRenderContext = pRenderContext;
qsort( pMap, nSortIDs, sizeof( sortmap_t ), SortMapCompareFunc );
int *pSortIDRemap = (int *)stackalloc( sizeof(int) * nSortIDs );
for ( i = 0; i < nSortIDs; i++ )
{
materialSortInfoArray[i] = pMap[i].info;
pSortIDRemap[pMap[i].index] = i;
//Msg("Material %s, lightmap %d ", materialSortInfoArray[i].material->GetName(), materialSortInfoArray[i].lightmapPageID );
}
for ( int surfaceIndex = 0; surfaceIndex < host_state.worldbrush->numsurfaces; surfaceIndex++ )
{
SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfaceIndex );
int sortID = MSurf_MaterialSortID( surfID );
#if _DEBUG
IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
Assert ( materialSortInfoArray[pSortIDRemap[sortID]].material == pMaterial );
#endif
MSurf_MaterialSortID( surfID ) = pSortIDRemap[sortID];
}
// Create texcoords for subdivided surfaces
GenerateTexCoordsForPrimVerts();
// Create the hardware vertex buffers for each face
WorldStaticMeshCreate();
if ( developer.GetInt() )
{
MaterialSytsem_DoBumpWarnings();
}
}
bool SurfHasBumpedLightmaps( SurfaceHandle_t surfID )
{
ASSERT_SURF_VALID( surfID );
bool hasBumpmap = false;
if( ( MSurf_TexInfo( surfID )->flags & SURF_BUMPLIGHT ) &&
( !( MSurf_TexInfo( surfID )->flags & SURF_NOLIGHT ) ) &&
( host_state.worldbrush->lightdata ) &&
( MSurf_Samples( surfID ) ) )
{
hasBumpmap = true;
}
return hasBumpmap;
}
bool SurfNeedsBumpedLightmaps( SurfaceHandle_t surfID )
{
ASSERT_SURF_VALID( surfID );
assert( MSurf_TexInfo( surfID ) );
assert( MSurf_TexInfo( surfID )->material );
return MSurf_TexInfo( surfID )->material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
}
bool SurfHasLightmap( SurfaceHandle_t surfID )
{
ASSERT_SURF_VALID( surfID );
bool hasLightmap = false;
if( ( !( MSurf_TexInfo( surfID )->flags & SURF_NOLIGHT ) ) &&
( host_state.worldbrush->lightdata ) &&
( MSurf_Samples( surfID ) ) )
{
hasLightmap = true;
}
return hasLightmap;
}
bool SurfNeedsLightmap( SurfaceHandle_t surfID )
{
ASSERT_SURF_VALID( surfID );
assert( MSurf_TexInfo( surfID ) );
assert( MSurf_TexInfo( surfID )->material );
if (MSurf_TexInfo( surfID )->flags & SURF_NOLIGHT)
return false;
return MSurf_TexInfo( surfID )->material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
}
//-----------------------------------------------------------------------------
// Purpose: This registers paint surfaces
//-----------------------------------------------------------------------------
void MaterialSystem_RegisterPaintSurfaces( void )
{
IPaintmapDataManager *pPaintmapDataManager = NULL;
if ( g_PaintManager.m_bShouldRegister )
{
pPaintmapDataManager = &g_PaintManager;
}
materials->RegisterPaintmapDataManager( pPaintmapDataManager );
}
//-----------------------------------------------------------------------------
// Purpose: This builds the surface info for a terrain face
//-----------------------------------------------------------------------------
void BuildMSurfaceVerts( const worldbrushdata_t *pBrushData, SurfaceHandle_t surfID, Vector *verts,
Vector2D *texCoords, Vector2D lightCoords[][4] )
{
SurfaceCtx_t ctx;
SurfSetupSurfaceContext( ctx, surfID );
int vertCount = MSurf_VertCount( surfID );
int vertFirstIndex = MSurf_FirstVertIndex( surfID );
for ( int i = 0; i < vertCount; i++ )
{
int vertIndex = pBrushData->vertindices[vertFirstIndex + i];
// world-space vertex
Vector& vec = pBrushData->vertexes[vertIndex].position;
// output to mesh
if ( verts )
{
VectorCopy( vec, verts[i] );
}
if ( texCoords )
{
SurfComputeTextureCoordinate( surfID, vec, texCoords[i].Base() );
}
//
// garymct: normalized (within space of surface) lightmap texture coordinates
//
if ( lightCoords )
{
SurfComputeLightmapCoordinate( ctx, surfID, vec, lightCoords[i][0] );
if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
{
// bump maps appear left to right in lightmap page memory, calculate the offset for the
// width of a single map
for ( int bumpID = 1; bumpID <= NUM_BUMP_VECTS; bumpID++ )
{
lightCoords[i][bumpID][0] = lightCoords[i][0][0] + (bumpID * ctx.m_BumpSTexCoordOffset);
lightCoords[i][bumpID][1] = lightCoords[i][0][1];
}
}
}
}
}
void BuildMSurfacePrimVerts( worldbrushdata_t *pBrushData, mprimitive_t *prim, CMeshBuilder &builder, SurfaceHandle_t surfID )
{
Vector tVect;
bool negate = false;
// FIXME: For some reason, normals are screwed up on water surfaces. Revisit this once we have normals started in primverts.
if ( MSurf_Flags( surfID ) & SURFDRAW_TANGENTSPACE )
{
negate = TangentSpaceSurfaceSetup( surfID, tVect );
}
// Vector& normal = pBrushData->vertnormals[ pBrushData->vertnormalindices[MSurf_FirstVertNormal( surfID )] ];
for ( int i = 0; i < prim->vertCount; i++ )
{
mprimvert_t &primVert = pBrushData->primverts[prim->firstVert + i];
builder.Position3fv( primVert.pos.Base() );
builder.Normal3fv( MSurf_Plane( surfID ).normal.Base() );
// builder.Normal3fv( normal.Base() );
builder.TexCoord2fv( 0, primVert.texCoord );
builder.TexCoord2fv( 1, primVert.lightCoord );
if ( MSurf_Flags( surfID ) & SURFDRAW_TANGENTSPACE )
{
Vector tangentS, tangentT;
TangentSpaceComputeBasis( tangentS, tangentT, MSurf_Plane( surfID ).normal, tVect, false );
builder.TangentS3fv( tangentS.Base() );
builder.TangentT3fv( tangentT.Base() );
}
builder.AdvanceVertex();
}
}
void BuildMSurfacePrimIndices( worldbrushdata_t *pBrushData, mprimitive_t *prim, CMeshBuilder &builder )
{
for ( int i = 0; i < prim->indexCount; i++ )
{
unsigned short primIndex = pBrushData->primindices[prim->firstIndex + i];
builder.Index( primIndex - prim->firstVert );
builder.AdvanceIndex();
}
}
//-----------------------------------------------------------------------------
// Here's a version of the mesh builder used to allow for client DLL to draw brush models
//-----------------------------------------------------------------------------
void BuildBrushModelVertexArray(worldbrushdata_t *pBrushData, SurfaceHandle_t surfID, BrushVertex_t* pVerts )
{
SurfaceCtx_t ctx;
SurfSetupSurfaceContext( ctx, surfID );
Vector tVect;
bool negate = false;
if ( MSurf_Flags( surfID ) & SURFDRAW_TANGENTSPACE )
{
negate = TangentSpaceSurfaceSetup( surfID, tVect );
}
for ( int i = 0; i < MSurf_VertCount( surfID ); i++ )
{
int vertIndex = pBrushData->vertindices[MSurf_FirstVertIndex( surfID ) + i];
// world-space vertex
Vector& vec = pBrushData->vertexes[vertIndex].position;
// output to mesh
VectorCopy( vec, pVerts[i].m_Pos );
Vector2D uv;
SurfComputeTextureCoordinate( surfID, vec, pVerts[i].m_TexCoord.Base() );
// garymct: normalized (within space of surface) lightmap texture coordinates
SurfComputeLightmapCoordinate( ctx, surfID, vec, pVerts[i].m_LightmapCoord );
// Activate this if necessary
// if ( surf->flags & SURFDRAW_BUMPLIGHT )
// {
// // bump maps appear left to right in lightmap page memory, calculate
// // the offset for the width of a single map. The pixel shader will use
// // this to compute the actual texture coordinates
// builder.TexCoord2f( 2, ctx.m_BumpSTexCoordOffset, 0.0f );
// }
Vector& normal = pBrushData->vertnormals[ pBrushData->vertnormalindices[MSurf_FirstVertNormal( surfID ) + i] ];
VectorCopy( normal, pVerts[i].m_Normal );
if ( MSurf_Flags( surfID ) & SURFDRAW_TANGENTSPACE )
{
Vector tangentS, tangentT;
TangentSpaceComputeBasis( tangentS, tangentT, normal, tVect, negate );
VectorCopy( tangentS, pVerts[i].m_TangentS );
VectorCopy( tangentT, pVerts[i].m_TangentT );
}
}
}
#endif // DEDICATED
void CMSurfaceSortList::Init( int maxSortIDs, int minMaterialLists )
{
m_list.RemoveAll();
m_list.EnsureCapacity(minMaterialLists);
m_maxSortIDs = maxSortIDs;
int groupMax = maxSortIDs*MAX_MAT_SORT_GROUPS;
#ifndef _PS3
m_groups.RemoveAll();
m_groups.EnsureCount(groupMax);
int groupBytes = (groupMax+7)>>3;
m_groupUsed.EnsureCount(groupBytes);
Q_memset(m_groupUsed.Base(), 0, groupBytes);
#else
m_groupsShared.RemoveAll();
m_groupsShared.EnsureCapacity(maxSortIDs * 2); // 7LTODO Tune this
m_groupIndices.RemoveAll();
m_groupIndices.EnsureCount(groupMax);
Q_memset(m_groupIndices.Base(), 0xFF, groupMax*2);
#endif
for ( int i = 0; i < MAX_MAT_SORT_GROUPS; i++ )
{
m_sortGroupLists[i].RemoveAll();
#if defined(_PS3)
int cap = (i==0) ? 256 : 64;
#else
int cap = (i==0) ? 128 : 16;
#endif
m_sortGroupLists[i].EnsureCapacity(cap);
groupOffset[i] = m_maxSortIDs * i;
}
InitGroup(&m_emptyGroup);
}
void CMSurfaceSortList::InitGroup( surfacesortgroup_t *pGroup )
{
pGroup->listHead = -1;
pGroup->listTail = -1;
pGroup->vertexCount = 0;
pGroup->groupListIndex = -1;
pGroup->vertexCountNoDetail = 0;
pGroup->indexCountNoDetail = 0;
pGroup->triangleCount = 0;
pGroup->surfaceCount = 0;
}
void CMSurfaceSortList::Shutdown()
{
}
void CMSurfaceSortList::Reset()
{
Init( m_maxSortIDs, m_list.NumAllocated() );
}
#if defined(_PS3)
void CMSurfaceSortList::EnsureCapacityForSPU( int maxSortIDs, int minMaterialLists )
{
m_list.EnsureCapacity(minMaterialLists);
m_groupsShared.EnsureCapacity(maxSortIDs * 2); // 7LTODO Tune this
int groupMax = maxSortIDs*MAX_MAT_SORT_GROUPS;
m_groupIndices.EnsureCount(groupMax);
for ( int i = 0; i < MAX_MAT_SORT_GROUPS; i++ )
{
int cap = (i==0) ? 256 : 64;
m_sortGroupLists[i].EnsureCapacity(cap);
groupOffset[i] = m_maxSortIDs * i;
}
// InitGroup(&m_emptyGroup);
}
#endif
// this resizes the groups and groupUsed arrays
#if !defined(_PS3)
void CMSurfaceSortList::EnsureMaxSortIDs( int newMaxSortIDs )
{
if ( newMaxSortIDs > m_maxSortIDs )
{
int oldMax = m_maxSortIDs;
// compute new size, expand by minimum of 256
newMaxSortIDs += 255;
newMaxSortIDs -= (newMaxSortIDs&255);
int groupMax = newMaxSortIDs * MAX_MAT_SORT_GROUPS;
int groupBytes = (groupMax+7)>>3;
// resize the arrays
m_groups.EnsureCount(groupMax);
m_groupUsed.EnsureCount(groupBytes);
// now loop through the list backwards and move the old data over
for ( int i = MAX_MAT_SORT_GROUPS; --i >= 0; )
{
for ( int j = newMaxSortIDs; --j >= 0; )
{
int newIndex = (i * newMaxSortIDs) + j;
if ( j < oldMax )
{
// when i == 0, the group indices overlap so they don't need to be remapped
if ( i != 0 )
{
int oldIndex = (i * oldMax) + j;
MarkGroupNotUsed(newIndex);
if ( IsGroupUsed(oldIndex) )
{
MarkGroupNotUsed(oldIndex);
MarkGroupUsed(newIndex);
m_groups[newIndex] = m_groups[oldIndex];
InitGroup( &m_groups[oldIndex] );
}
}
if ( IsGroupUsed(newIndex) && m_groups[newIndex].groupListIndex >= 0 )
{
m_sortGroupLists[i][m_groups[newIndex].groupListIndex] = &m_groups[newIndex];
}
}
else
{
MarkGroupNotUsed(newIndex);
}
}
groupOffset[i] = i*newMaxSortIDs;
}
m_maxSortIDs = newMaxSortIDs;
}
}
#endif
void CMSurfaceSortList::AddSurfaceToTail( msurface2_t *pSurface, int sortGroup, int sortID )
{
Assert(sortGroup<MAX_MAT_SORT_GROUPS);
int index = groupOffset[sortGroup] + sortID;
#ifndef _PS3
surfacesortgroup_t * RESTRICT pGroup = &m_groups[index];
if ( !IsGroupUsed(index) )
{
MarkGroupUsed(index);
InitGroup(pGroup);
}
#else
surfacesortgroup_t * RESTRICT pGroup;
if ( !IsGroupUsed(index) )
{
MarkGroupUsed(index);
pGroup = &m_groupsShared[m_groupIndices[index]];
InitGroup(pGroup);
}
else
{
pGroup = &m_groupsShared[m_groupIndices[index]];
}
#endif
materiallist_t *pList = NULL;
short prevIndex = -1;
int vertCount = MSurf_VertCount(pSurface);
int triangleCount = vertCount - 2;
pGroup->vertexCount += vertCount;
if (MSurf_Flags(pSurface) & SURFDRAW_NODE)
{
pGroup->vertexCountNoDetail += vertCount;
pGroup->indexCountNoDetail += triangleCount * 3;
}
pGroup->triangleCount += triangleCount;
pGroup->surfaceCount++;
if ( pGroup->listTail != m_list.InvalidIndex() )
{
// existing block
pList = &m_list[pGroup->listTail];
if ( pList->count >= ARRAYSIZE(pList->pSurfaces) )
{
prevIndex = pGroup->listTail;
// no space in existing block
pList = NULL;
}
}
// use existing block?
if ( pList )
{
pList->pSurfaces[pList->count] = pSurface;
pList->count++;
}
else
{
// allocate a new block
short nextBlock = m_list.AddToTail();
if ( prevIndex >= 0 )
{
m_list[prevIndex].nextBlock = nextBlock;
}
pGroup->listTail = nextBlock;
// handle the first use case
if ( pGroup->listHead == m_list.InvalidIndex() )
{
// UNDONE: This should really be sorted by sortID would help reduce state changes
// NOTE: Doesn't seem to help much in benchmarks to sort this vector
int index = m_sortGroupLists[sortGroup].AddToTail( (surfacesortgroup_t *) pGroup);
pGroup->groupListIndex = index;
pGroup->listHead = nextBlock;
}
pList = &m_list[nextBlock];
pList->nextBlock = m_list.InvalidIndex();
pList->count = 1;
pList->pSurfaces[0] = pSurface;
}
}
msurface2_t *CMSurfaceSortList::GetSurfaceAtHead( const surfacesortgroup_t &group ) const
{
if ( group.listHead == m_list.InvalidIndex() )
return NULL;
Assert(m_list[group.listHead].count>0);
return m_list[group.listHead].pSurfaces[0];
}
void CMSurfaceSortList::GetSurfaceListForGroup( CUtlVector<msurface2_t *> &list, const surfacesortgroup_t &group ) const
{
MSL_FOREACH_SURFACE_IN_GROUP_BEGIN( *this, group, surfID )
{
list.AddToTail(surfID);
}
MSL_FOREACH_SURFACE_IN_GROUP_END()
}
#ifndef DEDICATED
IMaterial *GetMaterialAtCrossHair( void )
{
#ifdef _WIN32
Vector endPoint;
Vector lightmapColor;
// max_range * sqrt(3)
VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint );
SurfaceHandle_t hitSurfID = R_LightVec( MainViewOrigin(), endPoint, false, lightmapColor );
if( IS_SURF_VALID( hitSurfID ) )
{
return MSurf_TexInfo( hitSurfID )->material;
}
else
{
return NULL;
}
#else
Assert( false ); // return value was not defined for this platform - returning NULL
return NULL;
#endif
}
// hack
extern void DrawLightmapPage( int lightmapPageID );
static float textureS, textureT;
static SurfaceHandle_t s_CrossHairSurfID;;
static Vector crossHairDiffuseLightColor;
static Vector crossHairBaseColor;
static float lightmapCoords[2];
void SaveSurfAtCrossHair()
{
#ifdef _WIN32
Vector endPoint;
Vector lightmapColor;
// max_range * sqrt(3)
VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint );
s_CrossHairSurfID = R_LightVec( MainViewOrigin(), endPoint, false, lightmapColor,
&textureS, &textureT, &lightmapCoords[0], &lightmapCoords[1] );
#endif
}
void DebugDrawLightmapAtCrossHair()
{
return;
IMaterial *pMaterial;
int lightmapPageSize[2];
if( s_CrossHairSurfID <= 0 )
{
return;
}
materials->GetLightmapPageSize( materialSortInfoArray[MSurf_MaterialSortID( s_CrossHairSurfID )].lightmapPageID,
&lightmapPageSize[0], &lightmapPageSize[1] );
pMaterial = MSurf_TexInfo( s_CrossHairSurfID )->material;
// pMaterial->GetLowResColorSample( textureS, textureT, baseColor );
DrawLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( s_CrossHairSurfID )].lightmapPageID );
#if 0
int i;
for( i = 0; i < 2; i++ )
{
xy[i] =
( ( float )pCrossHairSurf->offsetIntoLightmapPage[i] / ( float )lightmapPageSize[i] ) +
lightmapCoord[i] * ( pCrossHairSurf->lightmapExtents[i] / ( float )lightmapPageSize[i] );
}
materials->Bind( g_materialWireframe );
IMesh* pMesh = materials->GetDynamicMesh( g_materialWireframe );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUAD, 1 );
meshBuilder.Position3f(
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
#endif
}
void ReleaseMaterialSystemObjects( int nChangeFlags );
void RestoreMaterialSystemObjects( int nChangeFlags );
void ForceMatSysRestore()
{
ReleaseMaterialSystemObjects( 0 );
RestoreMaterialSystemObjects( 0 );
}
#endif