|
|
//===== Copyright � 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
|