Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

795 lines
24 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "vbsp.h"
  8. #include "bsplib.h"
  9. #include "tier1/UtlBuffer.h"
  10. #include "tier1/UtlVector.h"
  11. #include "bitmap/imageformat.h"
  12. #include <KeyValues.h>
  13. #include "tier1/strtools.h"
  14. #include "tier1/utlsymbol.h"
  15. #include "vtf/vtf.h"
  16. #include "materialpatch.h"
  17. #include "materialsystem/imaterialsystem.h"
  18. #include "materialsystem/imaterial.h"
  19. #include "materialsystem/imaterialvar.h"
  20. /*
  21. Meager documentation for how the cubemaps are assigned.
  22. While loading the map, it calls:
  23. *** Cubemap_SaveBrushSides
  24. Builds a list of what cubemaps manually were assigned to what faces
  25. in s_EnvCubemapToBrushSides.
  26. Immediately after loading the map, it calls:
  27. *** Cubemap_FixupBrushSidesMaterials
  28. Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
  29. side referenced by an env_cubemap manually.
  30. Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
  31. *** Cubemap_InitCubemapSideData:
  32. Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
  33. bHasEnvMapInMaterial is set if the side's material has $envmap.
  34. bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
  35. Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
  36. referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
  37. */
  38. struct PatchInfo_t
  39. {
  40. char *m_pMapName;
  41. int m_pOrigin[3];
  42. };
  43. struct CubemapInfo_t
  44. {
  45. int m_nTableId;
  46. bool m_bSpecular;
  47. };
  48. static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
  49. {
  50. return ( lhs.m_nTableId < rhs.m_nTableId );
  51. }
  52. typedef CUtlVector<int> IntVector_t;
  53. static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
  54. static CUtlVector<char *> s_DefaultCubemapNames;
  55. static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
  56. struct CubemapSideData_t
  57. {
  58. bool bHasEnvMapInMaterial;
  59. bool bManuallyPickedByAnEnvCubemap;
  60. };
  61. static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
  62. inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
  63. {
  64. return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
  65. }
  66. void Cubemap_InsertSample( const Vector& origin, int size )
  67. {
  68. dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
  69. pSample->origin[0] = ( int )origin[0];
  70. pSample->origin[1] = ( int )origin[1];
  71. pSample->origin[2] = ( int )origin[2];
  72. pSample->size = size;
  73. g_nCubemapSamples++;
  74. }
  75. static const char *FindSkyboxMaterialName( void )
  76. {
  77. for( int i = 0; i < g_MainMap->num_entities; i++ )
  78. {
  79. char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
  80. if (!strcmp(pEntity, "worldspawn"))
  81. {
  82. return ValueForKey( &g_MainMap->entities[i], "skyname" );
  83. }
  84. }
  85. return NULL;
  86. }
  87. static void BackSlashToForwardSlash( char *pname )
  88. {
  89. while ( *pname ) {
  90. if ( *pname == '\\' )
  91. *pname = '/';
  92. pname++;
  93. }
  94. }
  95. static void ForwardSlashToBackSlash( char *pname )
  96. {
  97. while ( *pname ) {
  98. if ( *pname == '/' )
  99. *pname = '\\';
  100. pname++;
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Finds materials that are used by a particular material
  105. //-----------------------------------------------------------------------------
  106. #define MAX_MATERIAL_NAME 512
  107. // This is the list of materialvars which are used in our codebase to look up dependent materials
  108. static const char *s_pDependentMaterialVar[] =
  109. {
  110. "$bottommaterial", // Used by water materials
  111. "$crackmaterial", // Used by shattered glass materials
  112. "$fallbackmaterial", // Used by all materials
  113. "", // Always must be last
  114. };
  115. static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
  116. {
  117. // FIXME: This is a terrible way of doing this! It creates a dependency
  118. // between vbsp and *all* code which reads dependent materials from materialvars
  119. // At the time of writing this function, that means the engine + studiorender.
  120. // We need a better way of figuring out how to do this, but for now I'm trying to do
  121. // the fastest solution possible since it's close to ship
  122. static char pDependentMaterialName[MAX_MATERIAL_NAME];
  123. for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
  124. {
  125. if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
  126. continue;
  127. if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
  128. {
  129. Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
  130. continue;
  131. }
  132. // Return the material var that caused the dependency
  133. if ( ppMaterialVar )
  134. {
  135. *ppMaterialVar = s_pDependentMaterialVar[i];
  136. }
  137. #ifdef _DEBUG
  138. // FIXME: Note that this code breaks if a material has more than 1 dependent material
  139. ++i;
  140. static char pDependentMaterialName2[MAX_MATERIAL_NAME];
  141. while( s_pDependentMaterialVar[i][0] )
  142. {
  143. Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
  144. ++i;
  145. }
  146. #endif
  147. return pDependentMaterialName;
  148. }
  149. return NULL;
  150. }
  151. void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
  152. {
  153. Q_strncpy( pDest, pSrcName, maxLen );
  154. if( !bHDR )
  155. {
  156. return;
  157. }
  158. char *pDot = Q_stristr( pDest, ".vtf" );
  159. if( !pDot )
  160. {
  161. return;
  162. }
  163. Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
  164. }
  165. #define DEFAULT_CUBEMAP_SIZE 32
  166. void CreateDefaultCubemaps( bool bHDR )
  167. {
  168. memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
  169. // Create the destination cubemap
  170. IVTFTexture *pDstCubemap = CreateVTFTexture();
  171. pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
  172. IMAGE_FORMAT_DEFAULT, TEXTUREFLAGS_ENVMAP, 1 );
  173. // First iterate over all frames
  174. for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
  175. {
  176. // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
  177. for (int iFace = 0; iFace < 6; ++iFace )
  178. {
  179. // Finally, iterate over all mip levels in the *destination*
  180. for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
  181. {
  182. unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
  183. int iSize = pDstCubemap->ComputeMipSize( iMip );
  184. // Make the default cubemap black
  185. memset( pDstBits, 0, iSize );
  186. }
  187. }
  188. }
  189. // Now that the bits are in place, compute the spheremaps...
  190. pDstCubemap->GenerateSpheremap();
  191. // Convert the cubemap to the final format
  192. pDstCubemap->ConvertImageFormat( bHDR ? IMAGE_FORMAT_RGBA16161616F : IMAGE_FORMAT_DXT5, false );
  193. // Write the puppy out!
  194. char dstVTFFileName[1024];
  195. if( bHDR )
  196. {
  197. sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
  198. }
  199. else
  200. {
  201. sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
  202. }
  203. CUtlBuffer outputBuf;
  204. if (!pDstCubemap->Serialize( outputBuf ))
  205. {
  206. Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
  207. return;
  208. }
  209. IZip *pak = GetPakFile();
  210. // spit out the default one.
  211. AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
  212. // spit out all of the ones that are attached to world geometry.
  213. int i;
  214. for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
  215. {
  216. char vtfName[MAX_PATH];
  217. VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
  218. if( FileExistsInPak( pak, vtfName ) )
  219. {
  220. continue;
  221. }
  222. AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
  223. }
  224. DestroyVTFTexture( pDstCubemap );
  225. }
  226. void Cubemap_CreateDefaultCubemaps( void )
  227. {
  228. CreateDefaultCubemaps( false );
  229. CreateDefaultCubemaps( true );
  230. }
  231. // Builds a list of what cubemaps manually were assigned to what faces
  232. // in s_EnvCubemapToBrushSides.
  233. void Cubemap_SaveBrushSides( const char *pSideListStr )
  234. {
  235. IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
  236. char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
  237. strcpy( pTmp, pSideListStr );
  238. const char *pScan = strtok( pTmp, " " );
  239. if( !pScan )
  240. {
  241. return;
  242. }
  243. do
  244. {
  245. int brushSideID;
  246. if( sscanf( pScan, "%d", &brushSideID ) == 1 )
  247. {
  248. brushSidesVector.AddToTail( brushSideID );
  249. }
  250. } while( ( pScan = strtok( NULL, " " ) ) );
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Generate patched material name
  254. //-----------------------------------------------------------------------------
  255. static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
  256. {
  257. const char *pSeparator = bMaterialName ? "_" : "";
  258. int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
  259. pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
  260. if ( bMaterialName )
  261. {
  262. Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
  263. if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
  264. {
  265. Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
  266. }
  267. }
  268. BackSlashToForwardSlash( pBuffer );
  269. Q_strlower( pBuffer );
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Patches the $envmap for a material and all its dependents, returns true if any patching happened
  273. //-----------------------------------------------------------------------------
  274. static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
  275. {
  276. // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
  277. // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
  278. // because we're using the 'replace' block, which will only add the env_cubemap if
  279. // $envmap is specified in the source material. But it will fail if someone adds
  280. // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
  281. // See if we have an $envmap to patch
  282. bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
  283. // See if we have a dependent material to patch
  284. bool bDependentMaterialPatched = false;
  285. const char *pDependentMaterialVar = NULL;
  286. const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
  287. if ( pDependentMaterial )
  288. {
  289. bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
  290. }
  291. // If we have neither to patch, we're done
  292. if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
  293. return false;
  294. // Otherwise we have to make a patched version of ourselves
  295. char pPatchedMaterialName[1024];
  296. GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
  297. MaterialPatchInfo_t pPatchInfo[2];
  298. int nPatchCount = 0;
  299. if ( bShouldPatchEnvCubemap )
  300. {
  301. pPatchInfo[nPatchCount].m_pKey = "$envmap";
  302. pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
  303. pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
  304. ++nPatchCount;
  305. }
  306. char pDependentPatchedMaterialName[1024];
  307. if ( bDependentMaterialPatched )
  308. {
  309. // FIXME: Annoying! I either have to pass back the patched dependent material name
  310. // or reconstruct it. Both are sucky.
  311. GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
  312. pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
  313. pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
  314. ++nPatchCount;
  315. }
  316. CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
  317. return true;
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Finds a texinfo that has a particular
  321. //-----------------------------------------------------------------------------
  322. //-----------------------------------------------------------------------------
  323. // Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
  324. // Returns the index of the new (or preexisting) texinfo referencing that VMT.
  325. //
  326. // Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
  327. // default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
  328. // runtime before they run buildcubemaps.
  329. //-----------------------------------------------------------------------------
  330. static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
  331. {
  332. // Don't make cubemap tex infos for nodes
  333. if ( originalTexInfo == TEXINFO_NODE )
  334. return originalTexInfo;
  335. texinfo_t *pTexInfo = &texinfo[originalTexInfo];
  336. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  337. const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  338. if ( g_IsCubemapTexData[pTexInfo->texdata] )
  339. {
  340. Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
  341. return originalTexInfo;
  342. }
  343. // Get out of here if the originalTexInfo is already a generated material for this position.
  344. char pStringToSearchFor[512];
  345. Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
  346. if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
  347. return originalTexInfo;
  348. // Package up information needed to generate patch names
  349. PatchInfo_t info;
  350. info.m_pMapName = mapbase;
  351. info.m_pOrigin[0] = origin[0];
  352. info.m_pOrigin[1] = origin[1];
  353. info.m_pOrigin[2] = origin[2];
  354. // Generate the name of the patched material
  355. char pGeneratedTexDataName[1024];
  356. GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
  357. // Make sure the texdata doesn't already exist.
  358. int nTexDataID = FindTexData( pGeneratedTexDataName );
  359. bool bHasTexData = (nTexDataID != -1);
  360. if( !bHasTexData )
  361. {
  362. // Generate the new "$envmap" texture name.
  363. char pTextureName[1024];
  364. GeneratePatchedName( "c", info, false, pTextureName, 1024 );
  365. // Hook the texture into the material and all dependent materials
  366. // but if no hooking was necessary, exit out
  367. if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
  368. return originalTexInfo;
  369. // Store off the name of the cubemap that we need to create since we successfully patched
  370. char pFileName[1024];
  371. int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
  372. int id = s_DefaultCubemapNames.AddToTail();
  373. s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
  374. strcpy( s_DefaultCubemapNames[id], pFileName );
  375. // Make a new texdata
  376. nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
  377. g_IsCubemapTexData[nTexDataID] = true;
  378. }
  379. Assert( nTexDataID != -1 );
  380. texinfo_t newTexInfo;
  381. newTexInfo = *pTexInfo;
  382. newTexInfo.texdata = nTexDataID;
  383. int nTexInfoID = -1;
  384. // See if we need to make a new texinfo
  385. bool bHasTexInfo = false;
  386. if( bHasTexData )
  387. {
  388. nTexInfoID = FindTexInfo( newTexInfo );
  389. bHasTexInfo = (nTexInfoID != -1);
  390. }
  391. // Make a new texinfo if we need to.
  392. if( !bHasTexInfo )
  393. {
  394. nTexInfoID = texinfo.AddToTail( newTexInfo );
  395. }
  396. Assert( nTexInfoID != -1 );
  397. return nTexInfoID;
  398. }
  399. static int SideIDToIndex( int brushSideID )
  400. {
  401. int i;
  402. for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
  403. {
  404. if( g_MainMap->brushsides[i].id == brushSideID )
  405. {
  406. return i;
  407. }
  408. }
  409. return -1;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
  413. // side referenced by an env_cubemap manually.
  414. //-----------------------------------------------------------------------------
  415. void Cubemap_FixupBrushSidesMaterials( void )
  416. {
  417. Msg( "fixing up env_cubemap materials on brush sides...\n" );
  418. Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
  419. int cubemapID;
  420. for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
  421. {
  422. IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
  423. int i;
  424. for( i = 0; i < brushSidesVector.Count(); i++ )
  425. {
  426. int brushSideID = brushSidesVector[i];
  427. int sideIndex = SideIDToIndex( brushSideID );
  428. if( sideIndex < 0 )
  429. {
  430. Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
  431. g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
  432. continue;
  433. }
  434. side_t *pSide = &g_MainMap->brushsides[sideIndex];
  435. #ifdef DEBUG
  436. if ( pSide->pMapDisp )
  437. {
  438. Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
  439. }
  440. #endif
  441. pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
  442. if ( pSide->pMapDisp )
  443. {
  444. pSide->pMapDisp->face.texinfo = pSide->texinfo;
  445. }
  446. }
  447. }
  448. }
  449. //-----------------------------------------------------------------------------
  450. //-----------------------------------------------------------------------------
  451. void Cubemap_ResetCubemapSideData( void )
  452. {
  453. for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
  454. {
  455. s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
  456. s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Returns true if the material or any of its dependents use an $envmap
  461. //-----------------------------------------------------------------------------
  462. bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
  463. {
  464. const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
  465. if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
  466. return true;
  467. const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
  468. if ( !pDependentMaterial )
  469. return false;
  470. return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Builds a list of all texdatas which need fixing up
  474. //-----------------------------------------------------------------------------
  475. void Cubemap_InitCubemapSideData( void )
  476. {
  477. // This tree is used to prevent re-parsing material vars multiple times
  478. CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
  479. // Fill in specular data.
  480. for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
  481. {
  482. side_t *pSide = &g_MainMap->brushsides[iSide];
  483. if ( !pSide )
  484. continue;
  485. if ( pSide->texinfo == TEXINFO_NODE )
  486. continue;
  487. texinfo_t *pTex = &texinfo[pSide->texinfo];
  488. if ( !pTex )
  489. continue;
  490. dtexdata_t *pTexData = GetTexData( pTex->texdata );
  491. if ( !pTexData )
  492. continue;
  493. CubemapInfo_t info;
  494. info.m_nTableId = pTexData->nameStringTableID;
  495. // Have we encountered this materal? If so, then copy the data we cached off before
  496. int i = lookup.Find( info );
  497. if ( i != lookup.InvalidIndex() )
  498. {
  499. s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
  500. continue;
  501. }
  502. // First time we've seen this material. Figure out if it uses env_cubemap
  503. const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  504. info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
  505. s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
  506. lookup.Insert( info );
  507. }
  508. // Fill in cube map data.
  509. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  510. {
  511. IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
  512. int nSideCount = sideList.Count();
  513. for ( int iSide = 0; iSide < nSideCount; ++iSide )
  514. {
  515. int nSideID = sideList[iSide];
  516. int nIndex = SideIDToIndex( nSideID );
  517. if ( nIndex < 0 )
  518. continue;
  519. s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
  520. }
  521. }
  522. }
  523. //-----------------------------------------------------------------------------
  524. //-----------------------------------------------------------------------------
  525. int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
  526. {
  527. if ( !pSide )
  528. return -1;
  529. // Return a valid (if random) cubemap if there's no winding
  530. if ( !pSide->winding )
  531. return 0;
  532. // Calculate the center point.
  533. Vector vecCenter;
  534. vecCenter.Init();
  535. for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
  536. {
  537. VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
  538. }
  539. VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
  540. vecCenter += entityOrigin;
  541. plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
  542. // Find the closest cubemap.
  543. int iMinCubemap = -1;
  544. float flMinDist = FLT_MAX;
  545. // Look for cubemaps in front of the surface first.
  546. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  547. {
  548. dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
  549. Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
  550. static_cast<float>( pSample->origin[1] ),
  551. static_cast<float>( pSample->origin[2] ) );
  552. Vector vecDelta;
  553. VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
  554. float flDist = vecDelta.NormalizeInPlace();
  555. float flDot = DotProduct( vecDelta, pPlane->normal );
  556. if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
  557. {
  558. flMinDist = flDist;
  559. iMinCubemap = iCubemap;
  560. }
  561. }
  562. // Didn't find anything in front search for closest.
  563. if( iMinCubemap == -1 )
  564. {
  565. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  566. {
  567. dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
  568. Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
  569. static_cast<float>( pSample->origin[1] ),
  570. static_cast<float>( pSample->origin[2] ) );
  571. Vector vecDelta;
  572. VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
  573. float flDist = vecDelta.Length();
  574. if ( flDist < flMinDist )
  575. {
  576. flMinDist = flDist;
  577. iMinCubemap = iCubemap;
  578. }
  579. }
  580. }
  581. return iMinCubemap;
  582. }
  583. //-----------------------------------------------------------------------------
  584. // For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
  585. //-----------------------------------------------------------------------------
  586. void Cubemap_AttachDefaultCubemapToSpecularSides( void )
  587. {
  588. Cubemap_ResetCubemapSideData();
  589. Cubemap_InitCubemapSideData();
  590. // build a mapping from side to entity id so that we can get the entity origin
  591. CUtlVector<int> sideToEntityIndex;
  592. sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
  593. int i;
  594. for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
  595. {
  596. sideToEntityIndex[i] = -1;
  597. }
  598. for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
  599. {
  600. int entityIndex = g_MainMap->mapbrushes[i].entitynum;
  601. for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
  602. {
  603. side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
  604. int sideIndex = side - g_MainMap->brushsides;
  605. sideToEntityIndex[sideIndex] = entityIndex;
  606. }
  607. }
  608. for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
  609. {
  610. side_t *pSide = &g_MainMap->brushsides[iSide];
  611. if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
  612. continue;
  613. int currentEntity = sideToEntityIndex[iSide];
  614. int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
  615. if ( iCubemap == -1 )
  616. continue;
  617. #ifdef DEBUG
  618. if ( pSide->pMapDisp )
  619. {
  620. Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
  621. }
  622. #endif
  623. pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
  624. if ( pSide->pMapDisp )
  625. {
  626. pSide->pMapDisp->face.texinfo = pSide->texinfo;
  627. }
  628. }
  629. }
  630. // Populate with cubemaps that were skipped
  631. void Cubemap_AddUnreferencedCubemaps()
  632. {
  633. char pTextureName[1024];
  634. char pFileName[1024];
  635. PatchInfo_t info;
  636. dcubemapsample_t *pSample;
  637. int i,j;
  638. for ( i=0; i<g_nCubemapSamples; ++i )
  639. {
  640. pSample = &g_CubemapSamples[i];
  641. // generate the formatted texture name based on cubemap origin
  642. info.m_pMapName = mapbase;
  643. info.m_pOrigin[0] = pSample->origin[0];
  644. info.m_pOrigin[1] = pSample->origin[1];
  645. info.m_pOrigin[2] = pSample->origin[2];
  646. GeneratePatchedName( "c", info, false, pTextureName, 1024 );
  647. // find or add
  648. for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
  649. {
  650. if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
  651. {
  652. // already added
  653. break;
  654. }
  655. }
  656. if ( j == s_DefaultCubemapNames.Count() )
  657. {
  658. int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
  659. int id = s_DefaultCubemapNames.AddToTail();
  660. s_DefaultCubemapNames[id] = new char[nLen + 1];
  661. strcpy( s_DefaultCubemapNames[id], pFileName );
  662. }
  663. }
  664. }