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.

1146 lines
34 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. // HDRFIXME: reduce the number of include files here.
  10. #include "render_pch.h"
  11. #include "client.h"
  12. #include "cdll_int.h"
  13. #include "lightcache.h"
  14. #include "client_class.h"
  15. #include "icliententitylist.h"
  16. #include "traceinit.h"
  17. #include "server.h"
  18. #include "ispatialpartitioninternal.h"
  19. #include "cdll_engine_int.h"
  20. #include "filesystem.h"
  21. #include "filesystem_engine.h"
  22. #include "ivtex.h"
  23. #include "materialsystem/itexture.h"
  24. #include "view.h"
  25. #include "tier0/dbg.h"
  26. #include "tier2/fileutils.h"
  27. #include "staticpropmgr.h"
  28. #include "icliententity.h"
  29. #include "gl_drawlights.h"
  30. #include "Overlay.h"
  31. #include "vmodes.h"
  32. #include "gl_cvars.h"
  33. #include "utlbuffer.h"
  34. #include "vtf/vtf.h"
  35. #include "bitmap/imageformat.h"
  36. #include "cbenchmark.h"
  37. #include "r_decal.h"
  38. #include "ivideomode.h"
  39. #include "tier0/icommandline.h"
  40. #include "dmxloader/dmxelement.h"
  41. #include "dmxloader/dmxloader.h"
  42. #include "bitmap/float_bm.h"
  43. #include "tier2/tier2.h"
  44. #include "../utils/common/bsplib.h"
  45. #include "ibsppack.h"
  46. // memdbgon must be the last include file in a .cpp file!!!
  47. #include "tier0/memdbgon.h"
  48. // putting this here so that it is replicated to the client.dll and materialsystem.dll
  49. ConVar dynamic_tonemap( "mat_dynamic_tonemapping", "1", FCVAR_CHEAT );
  50. ConVar building_cubemaps( "building_cubemaps", "0" );
  51. ConVar reload_materials( "reload_materials", "0" );
  52. ConVar r_DrawBeams( "r_DrawBeams", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" );
  53. static ConVar mat_force_tonemap_scale( "mat_force_tonemap_scale", "0.0", FCVAR_CHEAT );
  54. static const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
  55. //-----------------------------------------------------------------------------
  56. // Load, unload vtex
  57. //-----------------------------------------------------------------------------
  58. IVTex* VTex_Load( CSysModule** pModule )
  59. {
  60. // load the vtex dll
  61. IVTex *pIVTex = NULL;
  62. *pModule = FileSystem_LoadModule( "vtex_dll" );
  63. if ( *pModule )
  64. {
  65. CreateInterfaceFn factory = Sys_GetFactory( *pModule );
  66. if ( factory )
  67. {
  68. pIVTex = ( IVTex * )factory( IVTEX_VERSION_STRING, NULL );
  69. }
  70. }
  71. if ( !pIVTex )
  72. {
  73. ConMsg( "Can't load vtex_dll.dll\n" );
  74. }
  75. return pIVTex;
  76. }
  77. void VTex_Unload( CSysModule *pModule )
  78. {
  79. FileSystem_UnloadModule( pModule );
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Main entry point for taking cubemap snapshots
  83. //-----------------------------------------------------------------------------
  84. static void TakeCubemapSnapshot( const Vector &origin, const char *pFileNameBase, int screenBufSize,
  85. int tgaSize, bool bPFM )
  86. {
  87. if ( IsX360() )
  88. return;
  89. if ( g_LostVideoMemory )
  90. return;
  91. ITexture *pSaveRenderTarget = NULL;
  92. CMatRenderContextPtr pRenderContext( materials );
  93. // HDRFIXME: push/pop
  94. if( bPFM )
  95. {
  96. pSaveRenderTarget = pRenderContext->GetRenderTarget();
  97. pRenderContext->SetRenderTarget( NULL );
  98. }
  99. // HACK HACK HACK!!!!
  100. // If this is lower than the size of the render target (I think) we don't get water.
  101. screenBufSize = 512;
  102. char name[1024];
  103. CViewSetup view;
  104. memset( &view, 0, sizeof(view) );
  105. view.origin = origin;
  106. view.m_flAspectRatio = 1.0f;
  107. view.m_bRenderToSubrectOfLargerScreen = true;
  108. // garymcthack
  109. view.zNear = 8.0f;
  110. view.zFar = 28400.0f;
  111. view.x = 0;
  112. view.y = 0;
  113. view.width = ( float )screenBufSize;
  114. view.height = ( float )screenBufSize;
  115. const char *pExtension = ".tga";
  116. if( bPFM )
  117. {
  118. pExtension = ".pfm";
  119. }
  120. Shader_BeginRendering();
  121. if( bPFM )
  122. {
  123. int backbufferWidth, backbufferHeight;
  124. materials->GetBackBufferDimensions( backbufferWidth, backbufferHeight );
  125. pRenderContext->Viewport( 0, 0, backbufferWidth, backbufferHeight );
  126. pRenderContext->ClearColor3ub( 128, 128, 128 );
  127. pRenderContext->ClearBuffers( true, true );
  128. }
  129. int nFlags = VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH;
  130. // NOTE: This is for a workaround on ATI with building cubemaps.
  131. // Clearing just the viewport doesn't seem to work properly.
  132. nFlags |= VIEW_CLEAR_FULL_TARGET;
  133. static float angle0[6]={0,0,0,0,-90,90};
  134. static float angle1[6]={0,180,90,270,0,0};
  135. static CubeMapFaceIndex_t face_idx[6]={CUBEMAP_FACE_RIGHT,CUBEMAP_FACE_LEFT,
  136. CUBEMAP_FACE_BACK,CUBEMAP_FACE_FRONT,
  137. CUBEMAP_FACE_UP,CUBEMAP_FACE_DOWN};
  138. static int engine_cubemap_idx_to_fbm_idx[6]={4,3,0,2,5,1};
  139. if (bPFM)
  140. {
  141. FloatCubeMap_t Envmap(tgaSize, tgaSize);
  142. for(int side=0;side<6;side++)
  143. {
  144. view.angles[0] = angle0[side];
  145. view.angles[1] = angle1[side];
  146. view.angles[2] = 0;
  147. view.fov = 90;
  148. view.fovViewmodel = 90;
  149. view.origin = origin;
  150. if (g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER)
  151. {
  152. FloatBitMap_t &hdr_map=Envmap.face_maps[engine_cubemap_idx_to_fbm_idx[side]];
  153. hdr_map.Clear(0,0,0,1);
  154. // we are going to need to render multiple exposures
  155. float exposure=16.0;
  156. bool bOverExposedTexels=true;
  157. while( bOverExposedTexels && (exposure>0.05))
  158. {
  159. mat_force_tonemap_scale.SetValue(0.0f);
  160. pRenderContext->ResetToneMappingScale( exposure );
  161. g_ClientDLL->RenderView( view, nFlags, 0 );
  162. uint8 *pImage = new uint8[ screenBufSize * screenBufSize * 4 ];
  163. uint8 *pImage1 = new uint8[ tgaSize * tgaSize * 4 ];
  164. // Get Bits from the material system
  165. pRenderContext->ReadPixels( 0, 0, screenBufSize, screenBufSize,
  166. pImage, IMAGE_FORMAT_RGBA8888 );
  167. ImageLoader::ResampleInfo_t info;
  168. info.m_pSrc = pImage;
  169. info.m_pDest = pImage1;
  170. info.m_nSrcWidth = screenBufSize;
  171. info.m_nSrcHeight = screenBufSize;
  172. info.m_nDestWidth = tgaSize;
  173. info.m_nDestHeight = tgaSize;
  174. info.m_flSrcGamma = 1.0f;
  175. info.m_flDestGamma = 1.0f;
  176. if( !ImageLoader::ResampleRGBA8888( info ) )
  177. {
  178. Sys_Error( "Can't resample\n" );
  179. }
  180. FloatBitMap_t ldr_map(tgaSize,tgaSize);
  181. for(int x1=0;x1<tgaSize;x1++)
  182. for(int y1=0;y1<tgaSize;y1++)
  183. for(int c=0;c<3;c++)
  184. ldr_map.Pixel(x1,y1,c)=pImage1[c+4*(x1+tgaSize*y1)]*(1/255.0);
  185. delete[] pImage;
  186. delete[] pImage1;
  187. ldr_map.RaiseToPower(2.2); // gamma to linear
  188. float scale=1.0/exposure;
  189. bOverExposedTexels=false;
  190. for(int x=0;x<hdr_map.Width;x++)
  191. for(int y=0;y<hdr_map.Height;y++)
  192. for(int c=0;c<3;c++)
  193. {
  194. float texel=ldr_map.Pixel(x,y,c);
  195. if (texel>0.98)
  196. bOverExposedTexels=true;
  197. texel*=scale;
  198. hdr_map.Pixel(x,y,c)=max(hdr_map.Pixel(x,y,c),texel);
  199. }
  200. exposure*=0.75;
  201. materials->SwapBuffers();
  202. }
  203. Q_snprintf( name, sizeof( name ), "%s%s%s", pFileNameBase, facingName[side],pExtension );
  204. // hdr_map.WritePFM(name);
  205. }
  206. else
  207. {
  208. g_ClientDLL->RenderView( view, nFlags, 0 );
  209. Q_snprintf( name, sizeof( name ), "%s%s%s", pFileNameBase, facingName[side],pExtension );
  210. Assert( strlen( name ) < 1023 );
  211. videomode->TakeSnapshotTGARect( name, 0, 0, screenBufSize, screenBufSize, tgaSize, tgaSize, bPFM, face_idx[side]);
  212. }
  213. }
  214. if (g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER)
  215. {
  216. // FloatCubeMap_t OutEnvmap(tgaSize, tgaSize);
  217. // for(int f=0;f<6;f++)
  218. // OutEnvmap.face_maps[f].Clear(0,0,0,1);
  219. // Envmap.Resample(OutEnvmap,15.0);
  220. Q_snprintf( name, sizeof( name ), "%s", pFileNameBase);
  221. Envmap.WritePFMs( name );
  222. // Q_snprintf( name, sizeof( name ), "%s_filtered", pFileNameBase);
  223. // OutEnvmap.WritePFMs( name );
  224. }
  225. }
  226. else
  227. {
  228. for(int side=0;side<6;side++)
  229. {
  230. view.angles[0] = angle0[side];
  231. view.angles[1] = angle1[side];
  232. view.angles[2] = 0;
  233. view.fov = 90;
  234. view.fovViewmodel = 90;
  235. view.origin = origin;
  236. g_ClientDLL->RenderView( view, nFlags, 0 );
  237. Q_snprintf( name, sizeof( name ), "%s%s%s", pFileNameBase, facingName[side],pExtension );
  238. Assert( strlen( name ) < 1023 );
  239. videomode->TakeSnapshotTGARect( name, 0, 0, screenBufSize, screenBufSize, tgaSize, tgaSize, bPFM, face_idx[side]);
  240. }
  241. }
  242. if( bPFM )
  243. {
  244. materials->SwapBuffers();
  245. }
  246. // HDRFIXME: push/pop
  247. if( bPFM )
  248. {
  249. pRenderContext->SetRenderTarget( pSaveRenderTarget );
  250. }
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Interface factory for VTex
  254. //-----------------------------------------------------------------------------
  255. void* CubemapsFSFactory( const char *pName, int *pReturnCode )
  256. {
  257. if ( IsX360() )
  258. return NULL;
  259. if ( Q_stricmp( pName, FILESYSTEM_INTERFACE_VERSION ) == 0 )
  260. return g_pFileSystem;
  261. return NULL;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Generates a cubemap .vtf from .TGA snapshots
  265. //-----------------------------------------------------------------------------
  266. static void BuildSingleCubemap( const char *pVTFName, const Vector &vecOrigin,
  267. int nSize, bool bHDR, const char *pGameDir, IVTex *ivt )
  268. {
  269. if ( IsX360() )
  270. return;
  271. int nScreenBufSize = 4 * nSize;
  272. TakeCubemapSnapshot( vecOrigin, pVTFName, nScreenBufSize, nSize, bHDR );
  273. char pTXTName[ MAX_PATH ];
  274. Q_strncpy( pTXTName, pVTFName, sizeof(pTXTName) );
  275. Q_SetExtension( pTXTName, ".txt", sizeof(pTXTName) );
  276. // HDRFIXME: Make this go to a buffer instead.
  277. FileHandle_t fp = g_pFileSystem->Open( pTXTName, "w" );
  278. if( bHDR )
  279. {
  280. g_pFileSystem->FPrintf( fp, "\"pfm\" \"1\"\n" );
  281. // HDRFIXME: Make sure that we can mip and lod and get rid of this.
  282. }
  283. // don't let any dest alpha creep into the image
  284. g_pFileSystem->FPrintf( fp, "\"stripalphachannel\" \"1\"\n" );
  285. g_pFileSystem->Close( fp );
  286. if ( ivt )
  287. {
  288. char *argv[64];
  289. int iArg = 0;
  290. argv[iArg++] = "";
  291. argv[iArg++] = "-quiet";
  292. argv[iArg++] = "-UseStandardError"; // These are only here for the -currently released- version of vtex.dll.
  293. argv[iArg++] = "-WarningsAsErrors";
  294. argv[iArg++] = pTXTName;
  295. ivt->VTex( CubemapsFSFactory, pGameDir, iArg, argv );
  296. }
  297. g_pFileSystem->RemoveFile( pTXTName, NULL );
  298. const char *pSrcExtension = bHDR ? ".pfm" : ".tga";
  299. for( int i = 0; i < 6; i++ )
  300. {
  301. char pTempName[MAX_PATH];
  302. Q_snprintf( pTempName, sizeof( pTempName ), "%s%s", pVTFName, facingName[i] );
  303. Q_SetExtension( pTempName, pSrcExtension, sizeof(pTempName) );
  304. g_pFileSystem->RemoveFile( pTempName, NULL );
  305. }
  306. }
  307. #if !defined( SWDS )
  308. //-----------------------------------------------------------------------------
  309. // Grab six views for environment mapping tests
  310. //-----------------------------------------------------------------------------
  311. CON_COMMAND( envmap, "" )
  312. {
  313. if ( IsX360() )
  314. return;
  315. char base[ 256 ];
  316. IClientEntity *world = entitylist->GetClientEntity( 0 );
  317. if( world && world->GetModel() )
  318. {
  319. Q_FileBase( modelloader->GetName( ( model_t *)world->GetModel() ), base, sizeof( base ) );
  320. }
  321. else
  322. {
  323. Q_strncpy( base, "Env", sizeof( base ) );
  324. }
  325. int strLen = strlen( base ) + strlen( "cubemap_screenshots/" ) + 1;
  326. char *str = ( char * )_alloca( strLen );
  327. Q_snprintf( str, strLen, "cubemap_screenshots/%s", base );
  328. g_pFileSystem->CreateDirHierarchy( "cubemap_screenshots", "DEFAULT_WRITE_PATH" );
  329. TakeCubemapSnapshot( MainViewOrigin(), str, mat_envmapsize.GetInt(), mat_envmaptgasize.GetInt(),
  330. g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE );
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Write lighting information to a DMX file
  334. //-----------------------------------------------------------------------------
  335. static void WriteLightProbe( const char *pBasePath, const LightingState_t& state, bool bHDR )
  336. {
  337. char pFullPath[MAX_PATH];
  338. Q_strncpy( pFullPath, pBasePath, sizeof(pFullPath) );
  339. Q_SetExtension( pFullPath, ".prb", sizeof(pFullPath) );
  340. DECLARE_DMX_CONTEXT();
  341. CDmxElement *pLightProbe = CreateDmxElement( "DmeElement" );
  342. const char *pCubemap = pBasePath + Q_strlen( "materials/" );
  343. CDmxElementModifyScope modify( pLightProbe );
  344. pLightProbe->SetValue( "name", "lightprobe" );
  345. pLightProbe->SetValue( "cubemap", pCubemap );
  346. if ( bHDR )
  347. {
  348. char pTemp[MAX_PATH];
  349. Q_snprintf( pTemp, sizeof(pTemp), "%s_hdr", pCubemap );
  350. pLightProbe->SetValue( "cubemapHdr", pTemp );
  351. }
  352. CDmxAttribute *pAmbientCube = pLightProbe->AddAttribute( "ambientCube" );
  353. CUtlVector< Vector >& vec = pAmbientCube->GetArrayForEdit<Vector>();
  354. for ( int i = 0; i < 6 ; ++i )
  355. {
  356. vec.AddToTail( state.r_boxcolor[i] );
  357. }
  358. CDmxAttribute *pLocalLightList = pLightProbe->AddAttribute( "localLights" );
  359. CUtlVector< CDmxElement* >& lights = pLocalLightList->GetArrayForEdit<CDmxElement*>();
  360. modify.Release();
  361. for ( int i = 0; i < state.numlights; ++i )
  362. {
  363. CDmxElement* pLight = CreateDmxElement( "DmeElement" );
  364. lights.AddToTail( pLight );
  365. const dworldlight_t &wl = *state.locallight[i];
  366. pLight->SetValue( "color", wl.intensity );
  367. switch( wl.type )
  368. {
  369. case emit_point:
  370. pLight->SetValue( "name", "Point" );
  371. pLight->SetValue( "origin", wl.origin );
  372. pLight->SetValue( "attenuation", Vector( wl.constant_attn, wl.linear_attn, wl.quadratic_attn ) );
  373. pLight->SetValue( "maxDistance", wl.radius );
  374. break;
  375. case emit_spotlight:
  376. pLight->SetValue( "name", "Spot" );
  377. pLight->SetValue( "origin", wl.origin );
  378. pLight->SetValue( "direction", wl.normal );
  379. pLight->SetValue( "attenuation", Vector( wl.constant_attn, wl.linear_attn, wl.quadratic_attn ) );
  380. pLight->SetValue( "theta", 2.0f * acos( wl.stopdot ) );
  381. pLight->SetValue( "phi", 2.0f * acos( wl.stopdot2 ) );
  382. pLight->SetValue( "exponent", wl.exponent ? wl.exponent : 1.0f );
  383. pLight->SetValue( "maxDistance", wl.radius );
  384. break;
  385. case emit_surface:
  386. pLight->SetValue( "name", "Spot" );
  387. pLight->SetValue( "origin", wl.origin );
  388. pLight->SetValue( "direction", wl.normal );
  389. pLight->SetValue( "attenuation", Vector( 0.0f, 0.0f, 1.0f ) );
  390. pLight->SetValue( "theta", 0.0f );
  391. pLight->SetValue( "phi", 0.0f );
  392. pLight->SetValue( "exponent", 1.0f );
  393. pLight->SetValue( "maxDistance", wl.radius );
  394. break;
  395. case emit_skylight:
  396. pLight->SetValue( "name", "Directional" );
  397. pLight->SetValue( "direction", wl.normal );
  398. break;
  399. }
  400. }
  401. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  402. if ( SerializeDMX( buf, pLightProbe, pFullPath ) )
  403. {
  404. g_pFullFileSystem->WriteFile( pFullPath, "MOD", buf );
  405. }
  406. CleanupDMX( pLightProbe );
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Grab an envmap @ the view position + write lighting information
  410. //-----------------------------------------------------------------------------
  411. CON_COMMAND( lightprobe,
  412. "Samples the lighting environment.\n"
  413. "Creates a cubemap and a file indicating the local lighting in a subdirectory called 'materials/lightprobes'\n."
  414. "The lightprobe command requires you specify a base file name.\n" )
  415. {
  416. if ( IsX360() )
  417. return;
  418. if ( args.ArgC() < 2 )
  419. {
  420. ConMsg( "sample_lighting usage: lightprobe <base file name> [cubemap dimension]\n" );
  421. return;
  422. }
  423. int nTGASize = mat_envmaptgasize.GetInt();
  424. if ( args.ArgC() >= 3 )
  425. {
  426. nTGASize = atoi( args[2] );
  427. }
  428. CSysModule *pModule;
  429. IVTex *pIVTex = VTex_Load( &pModule );
  430. if ( !pIVTex )
  431. return;
  432. char pBasePath[MAX_PATH];
  433. Q_snprintf( pBasePath, sizeof(pBasePath), "materials/lightprobes/%s", args[1] );
  434. Q_StripFilename( pBasePath );
  435. g_pFileSystem->CreateDirHierarchy( pBasePath, "DEFAULT_WRITE_PATH" );
  436. char pTemp[MAX_PATH];
  437. char pMaterialSrcPath[MAX_PATH];
  438. Q_snprintf( pTemp, sizeof(pTemp), "materialsrc/lightprobes/%s", args[1] );
  439. GetModContentSubdirectory( pTemp, pMaterialSrcPath, sizeof(pMaterialSrcPath) );
  440. Q_StripFilename( pMaterialSrcPath );
  441. g_pFileSystem->CreateDirHierarchy( pMaterialSrcPath, NULL );
  442. char pGameDir[MAX_OSPATH];
  443. COM_GetGameDir( pGameDir, sizeof( pGameDir ) );
  444. bool bHDR = g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE;
  445. if ( bHDR )
  446. {
  447. char pTemp2[MAX_PATH];
  448. Q_snprintf( pTemp2, sizeof(pTemp2), "materialsrc/lightprobes/%s_hdr", args[1] );
  449. GetModContentSubdirectory( pTemp2, pMaterialSrcPath, sizeof(pMaterialSrcPath) );
  450. BuildSingleCubemap( pMaterialSrcPath, MainViewOrigin(), nTGASize, true, pGameDir, pIVTex );
  451. }
  452. GetModContentSubdirectory( pTemp, pMaterialSrcPath, sizeof(pMaterialSrcPath) );
  453. BuildSingleCubemap( pMaterialSrcPath, MainViewOrigin(), nTGASize, false, pGameDir, pIVTex );
  454. VTex_Unload( pModule );
  455. // Get the lighting at the point
  456. LightingState_t lightingState;
  457. LightcacheGetDynamic_Stats stats;
  458. LightcacheGetDynamic( MainViewOrigin(), lightingState, stats );
  459. Q_snprintf( pBasePath, sizeof(pBasePath), "materials/lightprobes/%s", args[1] );
  460. WriteLightProbe( pBasePath, lightingState, bHDR );
  461. }
  462. static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxBaseName )
  463. {
  464. if ( IsX360() )
  465. return false;
  466. int i;
  467. for( i = 0; i < 6; i++ )
  468. {
  469. // !!! FIXME: This needs to open the vmt (or some other method) to find the correct LDR or HDR set of skybox textures! Look in vbsp\cubemap.cpp!
  470. char srcVTFFileName[1024];
  471. Q_snprintf( srcVTFFileName, sizeof( srcVTFFileName ), "materials/skybox/%s%s.vtf", pSkyboxBaseName, facingName[i] );
  472. CUtlBuffer buf;
  473. if ( !g_pFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
  474. return false;
  475. pSrcVTFTextures[i] = CreateVTFTexture();
  476. if (!pSrcVTFTextures[i]->Unserialize(buf))
  477. {
  478. Warning("*** Error unserializing skybox texture: %s\n", pSkyboxBaseName );
  479. return false;
  480. }
  481. // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
  482. if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
  483. ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
  484. ( pSrcVTFTextures[i]->Flags() != pSrcVTFTextures[0]->Flags() ) )
  485. {
  486. Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxBaseName );
  487. return false;
  488. }
  489. }
  490. return true;
  491. }
  492. #define DEFAULT_CUBEMAP_SIZE 32
  493. void Cubemap_CreateDefaultCubemap( const char *pMapName, IBSPPack *iBSPPack )
  494. {
  495. if ( IsX360() )
  496. return;
  497. // NOTE: This implementation depends on the fact that all VTF files contain
  498. // all mipmap levels
  499. ConVarRef skyboxBaseNameConVar( "sv_skyname" );
  500. IVTFTexture *pSrcVTFTextures[6];
  501. if ( !skyboxBaseNameConVar.IsValid() || !skyboxBaseNameConVar.GetString() )
  502. {
  503. Warning( "Couldn't create default cubemap\n" );
  504. return;
  505. }
  506. const char *pSkyboxBaseName = skyboxBaseNameConVar.GetString();
  507. if( !LoadSrcVTFFiles( pSrcVTFTextures, pSkyboxBaseName ) )
  508. {
  509. Warning( "Can't load skybox file %s to build the default cubemap!\n", pSkyboxBaseName );
  510. return;
  511. }
  512. Msg( "Creating default cubemaps for env_cubemap using skybox %s...\n", pSkyboxBaseName );
  513. // Figure out the mip differences between the two textures
  514. int iMipLevelOffset = 0;
  515. int tmp = pSrcVTFTextures[0]->Width();
  516. while( tmp > DEFAULT_CUBEMAP_SIZE )
  517. {
  518. iMipLevelOffset++;
  519. tmp >>= 1;
  520. }
  521. // Create the destination cubemap
  522. IVTFTexture *pDstCubemap = CreateVTFTexture();
  523. pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
  524. pSrcVTFTextures[0]->Format(), pSrcVTFTextures[0]->Flags() | TEXTUREFLAGS_ENVMAP,
  525. pSrcVTFTextures[0]->FrameCount() );
  526. // First iterate over all frames
  527. for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
  528. {
  529. // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
  530. for (int iFace = 0; iFace < 6; ++iFace )
  531. {
  532. // Finally, iterate over all mip levels in the *destination*
  533. for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
  534. {
  535. // Copy the bits from the source images into the cube faces
  536. unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
  537. unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
  538. int iSize = pDstCubemap->ComputeMipSize( iMip );
  539. int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
  540. // !!! FIXME: Set this to black until the LDR/HDR issues are fixed on line ~563 in this file
  541. memset( pDstBits, 0, iSize );
  542. continue;
  543. if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
  544. {
  545. // Force mip level 2 to get the 1x1 face
  546. pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
  547. iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
  548. // Replicate 1x1 mip level across entire face
  549. //memset( pDstBits, 0, iSize );
  550. for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
  551. {
  552. memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
  553. }
  554. }
  555. else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
  556. {
  557. if ( iSrcMipSize != iSize )
  558. {
  559. Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", pSkyboxBaseName, iSrcMipSize, iSize );
  560. memset( pDstBits, 0, iSize );
  561. }
  562. else
  563. {
  564. // Just copy the mip level
  565. memcpy( pDstBits, pSrcBits, iSize );
  566. }
  567. }
  568. else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
  569. {
  570. int iMipWidth, iMipHeight, iMipDepth;
  571. pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
  572. if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
  573. {
  574. Warning( "%s - ERROR building default cube map! %d*2 != %d\n", pSkyboxBaseName, iSrcMipSize, iSize );
  575. memset( pDstBits, 0, iSize );
  576. }
  577. else
  578. {
  579. // Copy row at a time and repeat last row
  580. memcpy( pDstBits, pSrcBits, iSize/2 );
  581. //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
  582. int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
  583. int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
  584. if ( nSrcRowSize != nDstRowSize )
  585. {
  586. Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", pSkyboxBaseName, nSrcRowSize, nDstRowSize );
  587. memset( pDstBits, 0, iSize );
  588. }
  589. else
  590. {
  591. for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
  592. {
  593. memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
  594. }
  595. }
  596. }
  597. }
  598. else
  599. {
  600. // ERROR! This code only supports square and rectangluar 2x wide
  601. Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", pSkyboxBaseName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
  602. memset( pDstBits, 0, iSize );
  603. return;
  604. }
  605. }
  606. }
  607. }
  608. int flagUnion = 0;
  609. int i;
  610. for( i = 0; i < 6; i++ )
  611. {
  612. flagUnion |= pSrcVTFTextures[i]->Flags();
  613. }
  614. bool bHasAlpha =
  615. ( ( flagUnion & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ) != 0 );
  616. // Convert the cube to format that we can apply tools to it...
  617. // ImageFormat originalFormat = pDstCubemap->Format();
  618. pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
  619. if( !bHasAlpha )
  620. {
  621. // set alpha to zero since the source doesn't have any alpha in it
  622. unsigned char *pImageData = pDstCubemap->ImageData();
  623. int size = pDstCubemap->ComputeTotalSize(); // in bytes!
  624. unsigned char *pEnd = pImageData + size;
  625. for( ; pImageData < pEnd; pImageData += 4 )
  626. {
  627. pImageData[3] = ( unsigned char )0;
  628. }
  629. }
  630. // Fixup the cubemap facing
  631. pDstCubemap->FixCubemapFaceOrientation();
  632. // Now that the bits are in place, compute the spheremaps...
  633. pDstCubemap->GenerateSpheremap();
  634. // Convert the cubemap to the final format
  635. pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DXT5, false );
  636. // Write the puppy out!
  637. char dstVTFFileName[1024];
  638. Q_snprintf( dstVTFFileName, sizeof( dstVTFFileName ), "materials/maps/%s/cubemapdefault.vtf", pMapName );
  639. CUtlBuffer outputBuf;
  640. if (!pDstCubemap->Serialize( outputBuf ))
  641. {
  642. Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
  643. return;
  644. }
  645. // spit out the default one.
  646. iBSPPack->AddBufferToPack( dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
  647. // Clean up the textures
  648. for( i = 0; i < 6; i++ )
  649. {
  650. DestroyVTFTexture( pSrcVTFTextures[i] );
  651. }
  652. DestroyVTFTexture( pDstCubemap );
  653. }
  654. static void AddSampleToBSPFile( bool bHDR, mcubemapsample_t *pSample, const char *matDir, IBSPPack *iBSPPack )
  655. {
  656. if ( IsX360() )
  657. return;
  658. char textureName[MAX_PATH] = { 0 };
  659. const char *pHDRExtension = "";
  660. if( bHDR )
  661. {
  662. pHDRExtension = ".hdr";
  663. }
  664. Q_snprintf( textureName, sizeof( textureName ), "%s/c%d_%d_%d%s.vtf", matDir, ( int )pSample->origin[0],
  665. ( int )pSample->origin[1], ( int )pSample->origin[2], pHDRExtension );
  666. char localPath[MAX_PATH] = { 0 };
  667. if ( !g_pFileSystem->RelativePathToFullPath_safe( textureName, "DEFAULT_WRITE_PATH", localPath ) || !*localPath )
  668. {
  669. Warning("vtex failed to compile cubemap!\n");
  670. }
  671. else
  672. {
  673. Q_FixSlashes( localPath );
  674. iBSPPack->AddFileToPack( textureName, localPath );
  675. }
  676. g_pFileSystem->RemoveFile( textureName, "DEFAULT_WRITE_PATH" );
  677. }
  678. /*
  679. ===============
  680. R_BuildCubemapSamples
  681. Take a cubemap at each "cubemap" entity in the current map.
  682. ===============
  683. */
  684. // HOLY CRAP THIS NEEDS TO BE CLEANED UP
  685. //Added these to seperate from R_BuildCubemapSamples to a) clean it up abit and b) fix the issue where if it fails, mouse is disabled.
  686. static bool saveShadows = true;
  687. static bool bDrawWater = true;
  688. static int nSaveLightStyle = -1;
  689. static int bSaveDrawBeams = true;
  690. static bool bSaveMatSpecular = true;
  691. static int nOldOcclusionVal = 1;
  692. static int nOldBloomDisable = 0;
  693. static int originaldrawMRMModelsVal = 1;
  694. void R_BuildCubemapSamples_PreBuild()
  695. {
  696. // disable the mouse so that it won't be recentered all the bloody time.
  697. ConVarRef cl_mouseenable( "cl_mouseenable" );
  698. if( cl_mouseenable.IsValid() )
  699. {
  700. cl_mouseenable.SetValue( 0 );
  701. }
  702. ConVarRef r_shadows( "r_shadows" );
  703. saveShadows = true;
  704. if ( r_shadows.IsValid() )
  705. {
  706. saveShadows = r_shadows.GetBool();
  707. r_shadows.SetValue( 0 );
  708. }
  709. // Clear the water surface.
  710. ConVarRef mat_drawwater( "mat_drawwater" );
  711. bDrawWater = true;
  712. if ( mat_drawwater.IsValid() )
  713. {
  714. bDrawWater = mat_drawwater.GetBool();
  715. mat_drawwater.SetValue( 0 );
  716. }
  717. nSaveLightStyle = -1;
  718. ConVarRef r_lightstyleRef( "r_lightstyle" );
  719. if ( r_lightstyleRef.IsValid() )
  720. {
  721. nSaveLightStyle = r_lightstyleRef.GetInt();
  722. r_lightstyleRef.SetValue( 0 );
  723. R_RedownloadAllLightmaps();
  724. }
  725. bSaveDrawBeams = r_DrawBeams.GetInt();
  726. r_DrawBeams.SetValue( 0 );
  727. bSaveMatSpecular = mat_fastspecular.GetBool();
  728. // ConVar *r_drawtranslucentworld = ( ConVar * )cv->FindVar( "r_drawtranslucentworld" );
  729. // ConVar *r_drawtranslucentrenderables = ( ConVar * )cv->FindVar( "r_drawtranslucentrenderables" );
  730. // bool bSaveDrawTranslucentWorld = true;
  731. // bool bSaveDrawTranslucentRenderables = true;
  732. // if( r_drawtranslucentworld )
  733. // {
  734. // bSaveDrawTranslucentWorld = r_drawtranslucentworld->GetBool();
  735. // NOTE! : We use to set this to 0 for HDR.
  736. // r_drawtranslucentworld->SetValue( 0 );
  737. // }
  738. // if( r_drawtranslucentrenderables )
  739. // {
  740. // bSaveDrawTranslucentRenderables = r_drawtranslucentrenderables->GetBool();
  741. // NOTE! : We use to set this to 0 for HDR.
  742. // r_drawtranslucentrenderables->SetValue( 0 );
  743. // }
  744. building_cubemaps.SetValue( 1 );
  745. ConVarRef r_portalsopenall( "r_portalsopenall" );
  746. if( r_portalsopenall.IsValid() )
  747. {
  748. r_portalsopenall.SetValue( 1 );
  749. }
  750. nOldOcclusionVal = 1;
  751. ConVarRef r_occlusion( "r_occlusion" );
  752. if( r_occlusion.IsValid() )
  753. {
  754. nOldOcclusionVal = r_occlusion.GetInt();
  755. r_occlusion.SetValue( 0 );
  756. }
  757. ConVarRef mat_disable_bloom( "mat_disable_bloom" );
  758. nOldBloomDisable = 0;
  759. if ( mat_disable_bloom.IsValid() )
  760. {
  761. nOldBloomDisable = mat_disable_bloom.GetInt();
  762. mat_disable_bloom.SetValue( 1 );
  763. }
  764. ConVarRef drawMRMModelsCVar( "r_drawothermodels" );
  765. if( drawMRMModelsCVar.IsValid() )
  766. originaldrawMRMModelsVal = drawMRMModelsCVar.GetInt();
  767. }
  768. void R_BuildCubemapSamples_PostBuild()
  769. {
  770. // re-enable the mouse.
  771. ConVarRef cl_mouseenable( "cl_mouseenable" );
  772. if( cl_mouseenable.IsValid() )
  773. {
  774. cl_mouseenable.SetValue( 1 );
  775. }
  776. ConVarRef r_shadows( "r_shadows" );
  777. if( r_shadows.IsValid() )
  778. {
  779. r_shadows.SetValue( saveShadows );
  780. }
  781. ConVarRef mat_drawwater( "mat_drawwater" );
  782. if ( mat_drawwater.IsValid() )
  783. {
  784. mat_drawwater.SetValue( bDrawWater );
  785. }
  786. if( bSaveMatSpecular )
  787. {
  788. mat_fastspecular.SetValue( "1" );
  789. }
  790. else
  791. {
  792. mat_fastspecular.SetValue( "0" );
  793. }
  794. ConVarRef r_lightstyleRef( "r_lightstyle" );
  795. if( r_lightstyleRef.IsValid() )
  796. {
  797. r_lightstyleRef.SetValue( nSaveLightStyle );
  798. R_RedownloadAllLightmaps();
  799. }
  800. ConVarRef r_portalsopenall( "r_portalsopenall" );
  801. if( r_portalsopenall.IsValid() )
  802. {
  803. r_portalsopenall.SetValue( 0 );
  804. }
  805. ConVarRef r_occlusion( "r_occlusion" );
  806. if( r_occlusion.IsValid() )
  807. {
  808. r_occlusion.SetValue( nOldOcclusionVal );
  809. }
  810. ConVarRef mat_disable_bloom( "mat_disable_bloom" );
  811. if ( mat_disable_bloom.IsValid() )
  812. {
  813. mat_disable_bloom.SetValue( nOldBloomDisable);
  814. }
  815. r_DrawBeams.SetValue( bSaveDrawBeams );
  816. ConVarRef drawMRMModelsCVar( "r_drawothermodels" );
  817. if( drawMRMModelsCVar.IsValid() )
  818. {
  819. drawMRMModelsCVar.SetValue( originaldrawMRMModelsVal );
  820. }
  821. building_cubemaps.SetValue( 0 );
  822. }
  823. void R_BuildCubemapSamples( int numIterations )
  824. {
  825. if ( IsX360() )
  826. return;
  827. // Make sure that the file is writable before building cubemaps.
  828. Assert( g_pFileSystem->FileExists( cl.m_szLevelFileName, "GAME" ) );
  829. if( !g_pFileSystem->IsFileWritable( cl.m_szLevelFileName, "GAME" ) )
  830. {
  831. Warning( "%s is not writable!!! Check it out before running buildcubemaps.\n", cl.m_szLevelFileName );
  832. return;
  833. }
  834. R_BuildCubemapSamples_PreBuild();
  835. int bounce;
  836. for( bounce = 0; bounce < numIterations; bounce++ )
  837. {
  838. if( bounce == 0 )
  839. {
  840. mat_fastspecular.SetValue( "0" );
  841. }
  842. else
  843. {
  844. mat_fastspecular.SetValue( "1" );
  845. }
  846. UpdateMaterialSystemConfig();
  847. IClientEntity *world = entitylist->GetClientEntity( 0 );
  848. if( !world || !world->GetModel() )
  849. {
  850. ConDMsg( "R_BuildCubemapSamples: No map loaded!\n" );
  851. R_BuildCubemapSamples_PostBuild();
  852. return;
  853. }
  854. int oldDrawMRMModelsVal = 1;
  855. ConVarRef drawMRMModelsCVar( "r_drawothermodels" );
  856. if( drawMRMModelsCVar.IsValid() )
  857. {
  858. oldDrawMRMModelsVal = drawMRMModelsCVar.GetInt();
  859. drawMRMModelsCVar.SetValue( 0 );
  860. }
  861. bool bOldLightSpritesActive = ActivateLightSprites( true );
  862. // load the vtex dll
  863. CSysModule *pModule;
  864. IVTex *ivt = VTex_Load( &pModule );
  865. if ( !ivt )
  866. return;
  867. char matDir[MAX_PATH];
  868. Q_snprintf( matDir, sizeof(matDir), "materials/maps/%s", cl.m_szLevelBaseName );
  869. g_pFileSystem->CreateDirHierarchy( matDir, "DEFAULT_WRITE_PATH" );
  870. char pTemp[MAX_PATH];
  871. Q_snprintf( pTemp, sizeof(pTemp), "materialsrc/maps/%s", cl.m_szLevelBaseName );
  872. char pMaterialSrcDir[MAX_PATH];
  873. GetModContentSubdirectory( pTemp, pMaterialSrcDir, sizeof(pMaterialSrcDir) );
  874. g_pFileSystem->CreateDirHierarchy( pMaterialSrcDir, NULL );
  875. char gameDir[MAX_OSPATH];
  876. COM_GetGameDir( gameDir, sizeof( gameDir ) );
  877. model_t *pWorldModel = ( model_t *)world->GetModel();
  878. int i;
  879. for( i = 0; i < pWorldModel->brush.pShared->m_nCubemapSamples; i++ )
  880. {
  881. mcubemapsample_t *pCubemapSample = &pWorldModel->brush.pShared->m_pCubemapSamples[i];
  882. int tgaSize = ( pCubemapSample->size == 0 ) ? mat_envmaptgasize.GetInt() : 1 << ( pCubemapSample->size-1 );
  883. int screenBufSize = 4 * tgaSize;
  884. if ( (screenBufSize > videomode->GetModeWidth()) || (screenBufSize > videomode->GetModeHeight()) )
  885. {
  886. Warning( "Cube map buffer size %d x %d is bigger than screen!\nRun at a higher resolution! or reduce your cubemap resolution (needs 4X)\n", screenBufSize, screenBufSize );
  887. // BUGBUG: We'll leak DLLs/handles if we break out here, but this should be infrequent.
  888. R_BuildCubemapSamples_PostBuild();
  889. return;
  890. }
  891. }
  892. bool bSupportsHDR = g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE;
  893. for( i = 0; i < pWorldModel->brush.pShared->m_nCubemapSamples; i++ )
  894. {
  895. Warning( "bounce: %d/%d sample: %d/%d\n", bounce+1, numIterations, i+1, pWorldModel->brush.pShared->m_nCubemapSamples );
  896. mcubemapsample_t *pCubemapSample = &pWorldModel->brush.pShared->m_pCubemapSamples[i];
  897. char pVTFName[ MAX_PATH ];
  898. Q_snprintf( pVTFName, sizeof( pVTFName ), "%s/c%d_%d_%d", pMaterialSrcDir,
  899. ( int )pCubemapSample->origin[0], ( int )pCubemapSample->origin[1],
  900. ( int )pCubemapSample->origin[2] );
  901. int nTgaSize = ( pCubemapSample->size == 0 ) ? mat_envmaptgasize.GetInt() : 1 << ( pCubemapSample->size-1 );
  902. BuildSingleCubemap( pVTFName, pCubemapSample->origin, nTgaSize, bSupportsHDR, gameDir, ivt );
  903. }
  904. ActivateLightSprites( bOldLightSpritesActive );
  905. VTex_Unload( pModule );
  906. // load the bsppack dll
  907. IBSPPack *iBSPPack = NULL;
  908. pModule = FileSystem_LoadModule( "bsppack" );
  909. if ( pModule )
  910. {
  911. CreateInterfaceFn factory = Sys_GetFactory( pModule );
  912. if ( factory )
  913. {
  914. iBSPPack = ( IBSPPack * )factory( IBSPPACK_VERSION_STRING, NULL );
  915. }
  916. }
  917. if( !iBSPPack )
  918. {
  919. ConMsg( "Can't load bsppack.dll\n" );
  920. R_BuildCubemapSamples_PostBuild();
  921. return;
  922. }
  923. iBSPPack->SetHDRMode( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE );
  924. iBSPPack->LoadBSPFile( g_pFileSystem, cl.m_szLevelFileName );
  925. // Cram the textures into the bsp.
  926. Q_snprintf( matDir, sizeof(matDir), "materials/maps/%s", cl.m_szLevelBaseName );
  927. for ( i=0 ; i < pWorldModel->brush.pShared->m_nCubemapSamples ; i++ )
  928. {
  929. mcubemapsample_t *pSample = &pWorldModel->brush.pShared->m_pCubemapSamples[i];
  930. AddSampleToBSPFile( bSupportsHDR, pSample, matDir, iBSPPack );
  931. }
  932. Cubemap_CreateDefaultCubemap( cl.m_szLevelBaseName, iBSPPack );
  933. // Resolve levelfilename to absolute to ensure we are writing the exact file we loaded and not preferentially to
  934. // DEFAULT_WRITE_PATH
  935. char szAbsFile[MAX_PATH] = { 0 };
  936. g_pFullFileSystem->RelativePathToFullPath( cl.m_szLevelFileName, NULL, szAbsFile, sizeof( szAbsFile ) );
  937. if ( !*szAbsFile )
  938. {
  939. ConMsg( "Failed to resolve absolute path of map: %s\n", cl.m_szLevelFileName );
  940. R_BuildCubemapSamples_PostBuild();
  941. return;
  942. }
  943. iBSPPack->WriteBSPFile( szAbsFile );
  944. iBSPPack->ClearPackFile();
  945. FileSystem_UnloadModule( pModule );
  946. Cbuf_AddText( "restart setpos\n" );
  947. }
  948. R_BuildCubemapSamples_PostBuild();
  949. UpdateMaterialSystemConfig();
  950. // after map reloads, run any state that had to wait for map to reload
  951. reload_materials.SetValue( 1 );
  952. }
  953. #if !defined( _X360 )
  954. CON_COMMAND( buildcubemaps, "Rebuild cubemaps." )
  955. {
  956. extern void V_RenderVGuiOnly();
  957. bool bAllow = Host_AllowQueuedMaterialSystem(false);
  958. // do this to force a frame to render so the material system syncs up to single thread mode
  959. V_RenderVGuiOnly();
  960. if ( args.ArgC() == 1 )
  961. {
  962. R_BuildCubemapSamples( 1 );
  963. }
  964. else if( args.ArgC() == 2 )
  965. {
  966. R_BuildCubemapSamples( atoi( args[ 1 ] ) );
  967. }
  968. else
  969. {
  970. ConMsg( "Usage: buildcubemaps [numBounces]\n" );
  971. }
  972. Host_AllowQueuedMaterialSystem(bAllow);
  973. }
  974. #endif // SWDS
  975. #endif