//========= Copyright 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 #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 extern ConVar developer; #ifdef _WIN32 #include #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifndef SWDS extern IMaterialSystem *materials; IMaterial* g_materialWireframe; IMaterial* g_materialTranslucentSingleColor; IMaterial* g_materialTranslucentVertexColor; IMaterial* g_materialWorldWireframe; IMaterial* g_materialWorldWireframeZBuffer; 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 g_WorldStaticMeshes; // fixme - rename to g_WorldStaticVertexBuffers #else CUtlVector 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->PostMessage( 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) ) { 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 *)_alloca( 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 *)_alloca( 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; if ( !HushAsserts() ) { 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 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( ctx, surfID, vec, texCoords[i] ); } // // 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( ctx, surfID, vec, pVerts[i].m_TexCoord ); // 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 // SWDS void CMSurfaceSortList::Init( int maxSortIDs, int minMaterialLists ) { m_list.RemoveAll(); m_list.EnsureCapacity(minMaterialLists); m_maxSortIDs = maxSortIDs; int groupMax = maxSortIDs*MAX_MAT_SORT_GROUPS; m_groups.RemoveAll(); m_groups.EnsureCount(groupMax); int groupBytes = (groupMax+7)>>3; m_groupUsed.EnsureCount(groupBytes); Q_memset(m_groupUsed.Base(), 0, groupBytes); for ( int i = 0; i < MAX_MAT_SORT_GROUPS; i++ ) { m_sortGroupLists[i].RemoveAll(); int cap = (i==0) ? 128 : 16; 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() ); } // this resizes the groups and groupUsed arrays 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; } } void CMSurfaceSortList::AddSurfaceToTail( msurface2_t *pSurface, int sortGroup, int sortID ) { Assert(sortGrouptriangleCount += triangleCount; pGroup->surfaceCount++; pGroup->vertexCount += vertCount; if (MSurf_Flags(pSurface) & SURFDRAW_NODE) { pGroup->vertexCountNoDetail += vertCount; pGroup->indexCountNoDetail += triangleCount * 3; } 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 index = m_sortGroupLists[sortGroup].AddToTail(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 &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 SWDS IMaterial *GetMaterialAtCrossHair( void ) { 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; } } // 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() { 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] ); } 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(); void RestoreMaterialSystemObjects( int nChangeFlags ); void ForceMatSysRestore() { ReleaseMaterialSystemObjects(); RestoreMaterialSystemObjects( 0 ); } #endif