Team Fortress 2 Source Code as on 22/4/2020
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.

995 lines
31 KiB

  1. //========= Copyright 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. //-----------------------------------------------------------------------------
  152. // Loads VTF files
  153. //-----------------------------------------------------------------------------
  154. static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
  155. int *pUnionTextureFlags, bool bHDR )
  156. {
  157. const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
  158. int i;
  159. for( i = 0; i < 6; i++ )
  160. {
  161. char srcMaterialName[1024];
  162. sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
  163. IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
  164. //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
  165. IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
  166. const char *vtfName = pSkyTextureVar->GetStringValue();
  167. char srcVTFFileName[MAX_PATH];
  168. Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
  169. CUtlBuffer buf;
  170. if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
  171. {
  172. // Try looking for a compressed HDR texture
  173. if ( bHDR )
  174. {
  175. /* // FIXME: We need a way to uncompress this format!
  176. bool bHDRCompressed = true;
  177. pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
  178. vtfName = pSkyTextureVar->GetStringValue();
  179. Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
  180. if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
  181. */
  182. {
  183. return false;
  184. }
  185. }
  186. else
  187. {
  188. return false;
  189. }
  190. }
  191. pSrcVTFTextures[i] = CreateVTFTexture();
  192. if (!pSrcVTFTextures[i]->Unserialize(buf))
  193. {
  194. Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
  195. return false;
  196. }
  197. *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
  198. int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
  199. int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
  200. // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
  201. if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
  202. ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
  203. ( flagsNoAlpha != flagsFirstNoAlpha ) )
  204. {
  205. Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
  206. return false;
  207. }
  208. if ( bHDR )
  209. {
  210. pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
  211. pSrcVTFTextures[i]->GenerateMipmaps();
  212. pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
  213. }
  214. }
  215. return true;
  216. }
  217. void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
  218. {
  219. Q_strncpy( pDest, pSrcName, maxLen );
  220. if( !bHDR )
  221. {
  222. return;
  223. }
  224. char *pDot = Q_stristr( pDest, ".vtf" );
  225. if( !pDot )
  226. {
  227. return;
  228. }
  229. Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
  230. }
  231. #define DEFAULT_CUBEMAP_SIZE 32
  232. void CreateDefaultCubemaps( bool bHDR )
  233. {
  234. memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
  235. // NOTE: This implementation depends on the fact that all VTF files contain
  236. // all mipmap levels
  237. const char *pSkyboxBaseName = FindSkyboxMaterialName();
  238. char skyboxMaterialName[MAX_PATH];
  239. Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
  240. IVTFTexture *pSrcVTFTextures[6];
  241. if( !skyboxMaterialName )
  242. {
  243. if( s_DefaultCubemapNames.Count() )
  244. {
  245. Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
  246. }
  247. return;
  248. }
  249. int unionTextureFlags = 0;
  250. if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
  251. {
  252. Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
  253. return;
  254. }
  255. Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
  256. " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
  257. // Figure out the mip differences between the two textures
  258. int iMipLevelOffset = 0;
  259. int tmp = pSrcVTFTextures[0]->Width();
  260. while( tmp > DEFAULT_CUBEMAP_SIZE )
  261. {
  262. iMipLevelOffset++;
  263. tmp >>= 1;
  264. }
  265. // Create the destination cubemap
  266. IVTFTexture *pDstCubemap = CreateVTFTexture();
  267. pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
  268. pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
  269. pSrcVTFTextures[0]->FrameCount() );
  270. // First iterate over all frames
  271. for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
  272. {
  273. // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
  274. for (int iFace = 0; iFace < 6; ++iFace )
  275. {
  276. // Finally, iterate over all mip levels in the *destination*
  277. for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
  278. {
  279. // Copy the bits from the source images into the cube faces
  280. unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
  281. unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
  282. int iSize = pDstCubemap->ComputeMipSize( iMip );
  283. int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
  284. // !!! FIXME: Set this to black until HDR cubemaps are built properly!
  285. memset( pDstBits, 0, iSize );
  286. continue;
  287. if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
  288. {
  289. // Force mip level 2 to get the 1x1 face
  290. unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
  291. int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
  292. // Replicate 1x1 mip level across entire face
  293. //memset( pDstBits, 0, iSize );
  294. for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
  295. {
  296. memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
  297. }
  298. }
  299. else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
  300. {
  301. if ( iSrcMipSize != iSize )
  302. {
  303. Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
  304. memset( pDstBits, 0, iSize );
  305. }
  306. else
  307. {
  308. // Just copy the mip level
  309. memcpy( pDstBits, pSrcBits, iSize );
  310. }
  311. }
  312. else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
  313. {
  314. int iMipWidth, iMipHeight, iMipDepth;
  315. pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
  316. if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
  317. {
  318. Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
  319. memset( pDstBits, 0, iSize );
  320. }
  321. else
  322. {
  323. // Copy row at a time and repeat last row
  324. memcpy( pDstBits, pSrcBits, iSize/2 );
  325. //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
  326. int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
  327. int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
  328. if ( nSrcRowSize != nDstRowSize )
  329. {
  330. Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
  331. memset( pDstBits, 0, iSize );
  332. }
  333. else
  334. {
  335. for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
  336. {
  337. memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
  338. }
  339. }
  340. }
  341. }
  342. else
  343. {
  344. // ERROR! This code only supports square and rectangluar 2x wide
  345. Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
  346. memset( pDstBits, 0, iSize );
  347. return;
  348. }
  349. }
  350. }
  351. }
  352. ImageFormat originalFormat = pDstCubemap->Format();
  353. if( !bHDR )
  354. {
  355. // Convert the cube to format that we can apply tools to it...
  356. pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
  357. }
  358. // Fixup the cubemap facing
  359. pDstCubemap->FixCubemapFaceOrientation();
  360. // Now that the bits are in place, compute the spheremaps...
  361. pDstCubemap->GenerateSpheremap();
  362. if( !bHDR )
  363. {
  364. // Convert the cubemap to the final format
  365. pDstCubemap->ConvertImageFormat( originalFormat, false );
  366. }
  367. // Write the puppy out!
  368. char dstVTFFileName[1024];
  369. if( bHDR )
  370. {
  371. sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
  372. }
  373. else
  374. {
  375. sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
  376. }
  377. CUtlBuffer outputBuf;
  378. if (!pDstCubemap->Serialize( outputBuf ))
  379. {
  380. Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
  381. return;
  382. }
  383. IZip *pak = GetPakFile();
  384. // spit out the default one.
  385. AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
  386. // spit out all of the ones that are attached to world geometry.
  387. int i;
  388. for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
  389. {
  390. char vtfName[MAX_PATH];
  391. VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
  392. if( FileExistsInPak( pak, vtfName ) )
  393. {
  394. continue;
  395. }
  396. AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
  397. }
  398. // Clean up the textures
  399. for( i = 0; i < 6; i++ )
  400. {
  401. DestroyVTFTexture( pSrcVTFTextures[i] );
  402. }
  403. DestroyVTFTexture( pDstCubemap );
  404. }
  405. void Cubemap_CreateDefaultCubemaps( void )
  406. {
  407. CreateDefaultCubemaps( false );
  408. CreateDefaultCubemaps( true );
  409. }
  410. // Builds a list of what cubemaps manually were assigned to what faces
  411. // in s_EnvCubemapToBrushSides.
  412. void Cubemap_SaveBrushSides( const char *pSideListStr )
  413. {
  414. IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
  415. char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
  416. strcpy( pTmp, pSideListStr );
  417. const char *pScan = strtok( pTmp, " " );
  418. if( !pScan )
  419. {
  420. return;
  421. }
  422. do
  423. {
  424. int brushSideID;
  425. if( sscanf( pScan, "%d", &brushSideID ) == 1 )
  426. {
  427. brushSidesVector.AddToTail( brushSideID );
  428. }
  429. } while( ( pScan = strtok( NULL, " " ) ) );
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Generate patched material name
  433. //-----------------------------------------------------------------------------
  434. static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
  435. {
  436. const char *pSeparator = bMaterialName ? "_" : "";
  437. int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
  438. pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
  439. if ( bMaterialName )
  440. {
  441. Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
  442. if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
  443. {
  444. Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
  445. }
  446. }
  447. BackSlashToForwardSlash( pBuffer );
  448. Q_strlower( pBuffer );
  449. }
  450. //-----------------------------------------------------------------------------
  451. // Patches the $envmap for a material and all its dependents, returns true if any patching happened
  452. //-----------------------------------------------------------------------------
  453. static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
  454. {
  455. // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
  456. // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
  457. // because we're using the 'replace' block, which will only add the env_cubemap if
  458. // $envmap is specified in the source material. But it will fail if someone adds
  459. // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
  460. // See if we have an $envmap to patch
  461. bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
  462. // See if we have a dependent material to patch
  463. bool bDependentMaterialPatched = false;
  464. const char *pDependentMaterialVar = NULL;
  465. const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
  466. if ( pDependentMaterial )
  467. {
  468. bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
  469. }
  470. // If we have neither to patch, we're done
  471. if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
  472. return false;
  473. // Otherwise we have to make a patched version of ourselves
  474. char pPatchedMaterialName[1024];
  475. GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
  476. MaterialPatchInfo_t pPatchInfo[2];
  477. int nPatchCount = 0;
  478. if ( bShouldPatchEnvCubemap )
  479. {
  480. pPatchInfo[nPatchCount].m_pKey = "$envmap";
  481. pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
  482. pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
  483. ++nPatchCount;
  484. }
  485. char pDependentPatchedMaterialName[1024];
  486. if ( bDependentMaterialPatched )
  487. {
  488. // FIXME: Annoying! I either have to pass back the patched dependent material name
  489. // or reconstruct it. Both are sucky.
  490. GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
  491. pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
  492. pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
  493. ++nPatchCount;
  494. }
  495. CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
  496. return true;
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Finds a texinfo that has a particular
  500. //-----------------------------------------------------------------------------
  501. //-----------------------------------------------------------------------------
  502. // Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
  503. // Returns the index of the new (or preexisting) texinfo referencing that VMT.
  504. //
  505. // Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
  506. // default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
  507. // runtime before they run buildcubemaps.
  508. //-----------------------------------------------------------------------------
  509. static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
  510. {
  511. // Don't make cubemap tex infos for nodes
  512. if ( originalTexInfo == TEXINFO_NODE )
  513. return originalTexInfo;
  514. texinfo_t *pTexInfo = &texinfo[originalTexInfo];
  515. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  516. const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  517. if ( g_IsCubemapTexData[pTexInfo->texdata] )
  518. {
  519. Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
  520. return originalTexInfo;
  521. }
  522. // Get out of here if the originalTexInfo is already a generated material for this position.
  523. char pStringToSearchFor[512];
  524. Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
  525. if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
  526. return originalTexInfo;
  527. // Package up information needed to generate patch names
  528. PatchInfo_t info;
  529. info.m_pMapName = mapbase;
  530. info.m_pOrigin[0] = origin[0];
  531. info.m_pOrigin[1] = origin[1];
  532. info.m_pOrigin[2] = origin[2];
  533. // Generate the name of the patched material
  534. char pGeneratedTexDataName[1024];
  535. GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
  536. // Make sure the texdata doesn't already exist.
  537. int nTexDataID = FindTexData( pGeneratedTexDataName );
  538. bool bHasTexData = (nTexDataID != -1);
  539. if( !bHasTexData )
  540. {
  541. // Generate the new "$envmap" texture name.
  542. char pTextureName[1024];
  543. GeneratePatchedName( "c", info, false, pTextureName, 1024 );
  544. // Hook the texture into the material and all dependent materials
  545. // but if no hooking was necessary, exit out
  546. if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
  547. return originalTexInfo;
  548. // Store off the name of the cubemap that we need to create since we successfully patched
  549. char pFileName[1024];
  550. int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
  551. int id = s_DefaultCubemapNames.AddToTail();
  552. s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
  553. strcpy( s_DefaultCubemapNames[id], pFileName );
  554. // Make a new texdata
  555. nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
  556. g_IsCubemapTexData[nTexDataID] = true;
  557. }
  558. Assert( nTexDataID != -1 );
  559. texinfo_t newTexInfo;
  560. newTexInfo = *pTexInfo;
  561. newTexInfo.texdata = nTexDataID;
  562. int nTexInfoID = -1;
  563. // See if we need to make a new texinfo
  564. bool bHasTexInfo = false;
  565. if( bHasTexData )
  566. {
  567. nTexInfoID = FindTexInfo( newTexInfo );
  568. bHasTexInfo = (nTexInfoID != -1);
  569. }
  570. // Make a new texinfo if we need to.
  571. if( !bHasTexInfo )
  572. {
  573. nTexInfoID = texinfo.AddToTail( newTexInfo );
  574. }
  575. Assert( nTexInfoID != -1 );
  576. return nTexInfoID;
  577. }
  578. static int SideIDToIndex( int brushSideID )
  579. {
  580. int i;
  581. for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
  582. {
  583. if( g_MainMap->brushsides[i].id == brushSideID )
  584. {
  585. return i;
  586. }
  587. }
  588. return -1;
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
  592. // side referenced by an env_cubemap manually.
  593. //-----------------------------------------------------------------------------
  594. void Cubemap_FixupBrushSidesMaterials( void )
  595. {
  596. Msg( "fixing up env_cubemap materials on brush sides...\n" );
  597. Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
  598. int cubemapID;
  599. for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
  600. {
  601. IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
  602. int i;
  603. for( i = 0; i < brushSidesVector.Count(); i++ )
  604. {
  605. int brushSideID = brushSidesVector[i];
  606. int sideIndex = SideIDToIndex( brushSideID );
  607. if( sideIndex < 0 )
  608. {
  609. Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
  610. g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
  611. continue;
  612. }
  613. side_t *pSide = &g_MainMap->brushsides[sideIndex];
  614. #ifdef DEBUG
  615. if ( pSide->pMapDisp )
  616. {
  617. Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
  618. }
  619. #endif
  620. pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
  621. if ( pSide->pMapDisp )
  622. {
  623. pSide->pMapDisp->face.texinfo = pSide->texinfo;
  624. }
  625. }
  626. }
  627. }
  628. //-----------------------------------------------------------------------------
  629. //-----------------------------------------------------------------------------
  630. void Cubemap_ResetCubemapSideData( void )
  631. {
  632. for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
  633. {
  634. s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
  635. s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
  636. }
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Returns true if the material or any of its dependents use an $envmap
  640. //-----------------------------------------------------------------------------
  641. bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
  642. {
  643. const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
  644. if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
  645. return true;
  646. const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
  647. if ( !pDependentMaterial )
  648. return false;
  649. return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
  650. }
  651. //-----------------------------------------------------------------------------
  652. // Builds a list of all texdatas which need fixing up
  653. //-----------------------------------------------------------------------------
  654. void Cubemap_InitCubemapSideData( void )
  655. {
  656. // This tree is used to prevent re-parsing material vars multiple times
  657. CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
  658. // Fill in specular data.
  659. for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
  660. {
  661. side_t *pSide = &g_MainMap->brushsides[iSide];
  662. if ( !pSide )
  663. continue;
  664. if ( pSide->texinfo == TEXINFO_NODE )
  665. continue;
  666. texinfo_t *pTex = &texinfo[pSide->texinfo];
  667. if ( !pTex )
  668. continue;
  669. dtexdata_t *pTexData = GetTexData( pTex->texdata );
  670. if ( !pTexData )
  671. continue;
  672. CubemapInfo_t info;
  673. info.m_nTableId = pTexData->nameStringTableID;
  674. // Have we encountered this materal? If so, then copy the data we cached off before
  675. int i = lookup.Find( info );
  676. if ( i != lookup.InvalidIndex() )
  677. {
  678. s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
  679. continue;
  680. }
  681. // First time we've seen this material. Figure out if it uses env_cubemap
  682. const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  683. info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
  684. s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
  685. lookup.Insert( info );
  686. }
  687. // Fill in cube map data.
  688. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  689. {
  690. IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
  691. int nSideCount = sideList.Count();
  692. for ( int iSide = 0; iSide < nSideCount; ++iSide )
  693. {
  694. int nSideID = sideList[iSide];
  695. int nIndex = SideIDToIndex( nSideID );
  696. if ( nIndex < 0 )
  697. continue;
  698. s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
  699. }
  700. }
  701. }
  702. //-----------------------------------------------------------------------------
  703. //-----------------------------------------------------------------------------
  704. int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
  705. {
  706. if ( !pSide )
  707. return -1;
  708. // Return a valid (if random) cubemap if there's no winding
  709. if ( !pSide->winding )
  710. return 0;
  711. // Calculate the center point.
  712. Vector vecCenter;
  713. vecCenter.Init();
  714. for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
  715. {
  716. VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
  717. }
  718. VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
  719. vecCenter += entityOrigin;
  720. plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
  721. // Find the closest cubemap.
  722. int iMinCubemap = -1;
  723. float flMinDist = FLT_MAX;
  724. // Look for cubemaps in front of the surface first.
  725. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  726. {
  727. dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
  728. Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
  729. static_cast<float>( pSample->origin[1] ),
  730. static_cast<float>( pSample->origin[2] ) );
  731. Vector vecDelta;
  732. VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
  733. float flDist = vecDelta.NormalizeInPlace();
  734. float flDot = DotProduct( vecDelta, pPlane->normal );
  735. if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
  736. {
  737. flMinDist = flDist;
  738. iMinCubemap = iCubemap;
  739. }
  740. }
  741. // Didn't find anything in front search for closest.
  742. if( iMinCubemap == -1 )
  743. {
  744. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
  745. {
  746. dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
  747. Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
  748. static_cast<float>( pSample->origin[1] ),
  749. static_cast<float>( pSample->origin[2] ) );
  750. Vector vecDelta;
  751. VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
  752. float flDist = vecDelta.Length();
  753. if ( flDist < flMinDist )
  754. {
  755. flMinDist = flDist;
  756. iMinCubemap = iCubemap;
  757. }
  758. }
  759. }
  760. return iMinCubemap;
  761. }
  762. //-----------------------------------------------------------------------------
  763. // For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
  764. //-----------------------------------------------------------------------------
  765. void Cubemap_AttachDefaultCubemapToSpecularSides( void )
  766. {
  767. Cubemap_ResetCubemapSideData();
  768. Cubemap_InitCubemapSideData();
  769. // build a mapping from side to entity id so that we can get the entity origin
  770. CUtlVector<int> sideToEntityIndex;
  771. sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
  772. int i;
  773. for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
  774. {
  775. sideToEntityIndex[i] = -1;
  776. }
  777. for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
  778. {
  779. int entityIndex = g_MainMap->mapbrushes[i].entitynum;
  780. for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
  781. {
  782. side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
  783. int sideIndex = side - g_MainMap->brushsides;
  784. sideToEntityIndex[sideIndex] = entityIndex;
  785. }
  786. }
  787. for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
  788. {
  789. side_t *pSide = &g_MainMap->brushsides[iSide];
  790. if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
  791. continue;
  792. int currentEntity = sideToEntityIndex[iSide];
  793. int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
  794. if ( iCubemap == -1 )
  795. continue;
  796. #ifdef DEBUG
  797. if ( pSide->pMapDisp )
  798. {
  799. Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
  800. }
  801. #endif
  802. pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
  803. if ( pSide->pMapDisp )
  804. {
  805. pSide->pMapDisp->face.texinfo = pSide->texinfo;
  806. }
  807. }
  808. }
  809. // Populate with cubemaps that were skipped
  810. void Cubemap_AddUnreferencedCubemaps()
  811. {
  812. char pTextureName[1024];
  813. char pFileName[1024];
  814. PatchInfo_t info;
  815. dcubemapsample_t *pSample;
  816. int i,j;
  817. for ( i=0; i<g_nCubemapSamples; ++i )
  818. {
  819. pSample = &g_CubemapSamples[i];
  820. // generate the formatted texture name based on cubemap origin
  821. info.m_pMapName = mapbase;
  822. info.m_pOrigin[0] = pSample->origin[0];
  823. info.m_pOrigin[1] = pSample->origin[1];
  824. info.m_pOrigin[2] = pSample->origin[2];
  825. GeneratePatchedName( "c", info, false, pTextureName, 1024 );
  826. // find or add
  827. for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
  828. {
  829. if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
  830. {
  831. // already added
  832. break;
  833. }
  834. }
  835. if ( j == s_DefaultCubemapNames.Count() )
  836. {
  837. int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
  838. int id = s_DefaultCubemapNames.AddToTail();
  839. s_DefaultCubemapNames[id] = new char[nLen + 1];
  840. strcpy( s_DefaultCubemapNames[id], pFileName );
  841. }
  842. }
  843. }