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.

1135 lines
42 KiB

  1. #include "render_pch.h"
  2. #if !defined( DEDICATED ) && !defined( _GAMECONSOLE )
  3. #include "icliententitylist.h"
  4. #include "icliententity.h"
  5. #include "imagepacker.h"
  6. #include "bitmap/tgawriter.h"
  7. #include "client.h"
  8. #include "tier2/fileutils.h"
  9. #include "vstdlib/iprocessutils.h"
  10. #include "appframework/iappsystem.h"
  11. #include "appframework/IAppSystemGroup.h"
  12. #include "appframework/AppFramework.h"
  13. #include "../utils/common/bsplib.h"
  14. #include "ibsppack.h"
  15. #include "disp.h"
  16. // Set to 0 if you want to be able to see intermediate files in hlmv, etc.
  17. #define DELETE_INTERMEDIATE_FILES 1
  18. // THIS NEEDS TO BE THE SAME IN worldimposter_ps2x.fxc!!!!
  19. #define BASE_TIMES_LIGHTMAP_LINEAR_TONEMAP_SCALE 4.0f
  20. // THIS NEEDS TO BE THE SAME IN worldimposter_ps2x.fxc!!!!
  21. #define MAX_ATLAS_TEXTURE_DIMENSION 1024
  22. static IBSPPack *s_pBSPPack = NULL;
  23. static CSysModule *s_pBSPPackModule = NULL;
  24. static void LoadBSPPackInterface( void )
  25. {
  26. // load the bsppack dll
  27. s_pBSPPackModule = FileSystem_LoadModule( "bsppack" );
  28. if ( s_pBSPPackModule )
  29. {
  30. CreateInterfaceFn factory = Sys_GetFactory( s_pBSPPackModule );
  31. if ( factory )
  32. {
  33. s_pBSPPack = ( IBSPPack * )factory( IBSPPACK_VERSION_STRING, NULL );
  34. }
  35. }
  36. if( !s_pBSPPack )
  37. {
  38. Error( "can't get bsppack interface\n" );
  39. }
  40. }
  41. static void UnloadBSPPackInterface( void )
  42. {
  43. FileSystem_UnloadModule( s_pBSPPackModule );
  44. s_pBSPPack = NULL;
  45. s_pBSPPackModule = NULL;
  46. }
  47. static void RandomColor( Vector& color )
  48. {
  49. color[0] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  50. color[1] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  51. color[2] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  52. VectorNormalize( color );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Compute a context necessary for creating vertex data
  56. //-----------------------------------------------------------------------------
  57. static void SurfSetupSurfaceContextAtlased( SurfaceCtx_t &ctx, SurfaceHandle_t surfID, int x, int y, int nAtlasedTextureWidth, int nAtlasedTextureHeight )
  58. {
  59. ctx.m_LightmapPageSize[0] = nAtlasedTextureWidth;
  60. ctx.m_LightmapPageSize[1] = nAtlasedTextureHeight;
  61. ctx.m_LightmapSize[0] = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  62. ctx.m_LightmapSize[1] = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
  63. ctx.m_Scale.x = 1.0f / ( float )ctx.m_LightmapPageSize[0];
  64. ctx.m_Scale.y = 1.0f / ( float )ctx.m_LightmapPageSize[1];
  65. ctx.m_Offset.x = ( float )x * ctx.m_Scale.x;
  66. ctx.m_Offset.y = ( float )y * ctx.m_Scale.y;
  67. ctx.m_BumpSTexCoordOffset = 0.0f;
  68. }
  69. static void SurfComputeAtlasedTextureCoordinate( SurfaceCtx_t const& ctx, SurfaceHandle_t surfID, Vector const& vec, Vector2D& uv )
  70. {
  71. if ( (MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT) )
  72. {
  73. uv.x = uv.y = 0.5f;
  74. }
  75. else if ( MSurf_LightmapExtents( surfID )[0] == 0 )
  76. {
  77. uv = (0.5f * ctx.m_Scale + ctx.m_Offset);
  78. }
  79. else
  80. {
  81. mtexinfo_t* pTexInfo = MSurf_TexInfo( surfID );
  82. uv.x = DotProduct (vec, pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  83. pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3];
  84. uv.x -= MSurf_LightmapMins( surfID )[0];
  85. uv.x += 0.5f;
  86. uv.y = DotProduct (vec, pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  87. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3];
  88. uv.y -= MSurf_LightmapMins( surfID )[1];
  89. uv.y += 0.5f;
  90. uv *= ctx.m_Scale;
  91. uv += ctx.m_Offset;
  92. assert( uv.IsValid() );
  93. }
  94. #if _DEBUG
  95. // This was here for check against displacements and they actually get calculated later correctly.
  96. // CheckTexCoord( uv.x );
  97. // CheckTexCoord( uv.y );
  98. #endif
  99. uv.x = clamp(uv.x, 0.0f, 1.0f);
  100. uv.y = clamp(uv.y, 0.0f, 1.0f);
  101. }
  102. void WriteDisplacementSurfaceToSMD( SurfaceHandle_t surfID, CDispInfo *pDispInfo, const SurfaceCtx_t &ctxAtlased, FileHandle_t smdfp )
  103. {
  104. int nLightmapPageSize[2];
  105. materials->GetLightmapPageSize( SortInfoToLightmapPage( MSurf_MaterialSortID( surfID ) ), &nLightmapPageSize[0], &nLightmapPageSize[1] );
  106. Vector2D vOffset;
  107. float flPageSizeU = nLightmapPageSize[0];
  108. float flPageSizeV = nLightmapPageSize[1];
  109. vOffset.x = ( float )MSurf_OffsetIntoLightmapPage( surfID )[0] / flPageSizeU;
  110. vOffset.y = ( float )MSurf_OffsetIntoLightmapPage( surfID )[1] / flPageSizeV;
  111. for ( int i = 0; i < pDispInfo->m_nIndices; i += 3 )
  112. {
  113. g_pFullFileSystem->FPrintf( smdfp, "simpleworldmodel.tga\n" );
  114. for ( int j = 2; j >= 0; --j )
  115. {
  116. int nVert = pDispInfo->m_Indices[i + j] - pDispInfo->m_iVertOffset;
  117. CDispRenderVert *pVert = &pDispInfo->m_Verts[nVert];
  118. Vector vPos = pVert->m_vPos;
  119. Vector vNormal = pVert->m_vNormal;
  120. Vector2D uv = pVert->m_LMCoords;
  121. uv -= vOffset;
  122. uv.x *= flPageSizeU;
  123. uv.y *= flPageSizeV;
  124. uv *= ctxAtlased.m_Scale;
  125. uv += ctxAtlased.m_Offset;
  126. g_pFullFileSystem->FPrintf( smdfp, "%d %f %f %f %f %f %f %f %f\n", 0, vPos.x, vPos.y, vPos.z, vNormal.x, vNormal.y, vNormal.z, uv.x, 1.0f - uv.y );
  127. }
  128. }
  129. }
  130. void WriteSurfaceToSMD( worldbrushdata_t *pBrushData, SurfaceHandle_t surfID, int x, int y, FileHandle_t smdfp, int nAtlasedTextureWidth, int nAtlasedTextureHeight )
  131. {
  132. SurfaceCtx_t ctxAtlased;
  133. SurfSetupSurfaceContextAtlased( ctxAtlased, surfID, x, y, nAtlasedTextureWidth, nAtlasedTextureHeight );
  134. if ( surfID->pDispInfo )
  135. {
  136. CDispInfo *pDispInfo = static_cast<CDispInfo *>(surfID->pDispInfo);
  137. WriteDisplacementSurfaceToSMD( surfID, pDispInfo, ctxAtlased, smdfp );
  138. return;
  139. }
  140. int vertCount = MSurf_VertCount( surfID );
  141. for ( int triID = 0; triID < vertCount - 2; triID++ )
  142. {
  143. // This really refers to simpleworldmodel.vmt
  144. g_pFullFileSystem->FPrintf( smdfp, "simpleworldmodel.tga\n" );
  145. for ( int triVertID = 2; triVertID >= 0; triVertID-- )
  146. {
  147. int i;
  148. switch( triVertID )
  149. {
  150. case 0:
  151. i = 0;
  152. break;
  153. case 1:
  154. i = triID + 1;
  155. break;
  156. case 2:
  157. default:
  158. i = triID + 2;
  159. break;
  160. }
  161. int vertIndex = pBrushData->vertindices[MSurf_FirstVertIndex( surfID ) + i];
  162. // world-space vertex
  163. Vector &vec = pBrushData->vertexes[vertIndex].position;
  164. // output to mesh
  165. Vector2D pos;
  166. SurfComputeAtlasedTextureCoordinate( ctxAtlased, surfID, vec, pos );
  167. Vector &normal = pBrushData->vertnormals[ pBrushData->vertnormalindices[MSurf_FirstVertNormal( surfID ) + i] ];
  168. g_pFullFileSystem->FPrintf( smdfp, "%d %f %f %f %f %f %f %f %f\n", 0, vec.x, vec.y, vec.z, normal.x, normal.y, normal.z, pos.x, 1.0f - pos.y );
  169. }
  170. }
  171. }
  172. static const float LUXEL_WORLD_SPACE_EPSILON = 1e-3;
  173. // Based on code in lightmaptransfer.cpp in vbsp2lib.
  174. static void CalculateLuxelToWorldTransform( const mtexinfo_t *pTexInfo, const Vector &vFaceNormal, float flFaceDistance, Vector *pLuxelOrigin, Vector *pS, Vector *pT )
  175. {
  176. Vector vLuxelSpaceCross;
  177. vLuxelSpaceCross[0] =
  178. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] -
  179. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1];
  180. vLuxelSpaceCross[1] =
  181. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0] -
  182. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2];
  183. vLuxelSpaceCross[2] =
  184. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1] -
  185. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0];
  186. float flDeterminant = -DotProduct( vFaceNormal, vLuxelSpaceCross );
  187. if ( fabs( flDeterminant ) < 1e-6 )
  188. {
  189. // Warning( "Warning - UV vectors are parallel to face normal, bad lighting will be produced.\n" );
  190. ( *pLuxelOrigin ) = vec3_origin;
  191. }
  192. else
  193. {
  194. // invert the matrix
  195. ( *pS )[0] = (vFaceNormal[2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1] - vFaceNormal[1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2]) / flDeterminant;
  196. ( *pT )[0] = (vFaceNormal[1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] - vFaceNormal[2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1]) / flDeterminant;
  197. ( *pLuxelOrigin )[0] = -(flFaceDistance * vLuxelSpaceCross[0]) / flDeterminant;
  198. ( *pS )[1] = (vFaceNormal[0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] - vFaceNormal[2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0]) / flDeterminant;
  199. ( *pT )[1] = (vFaceNormal[2] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0] - vFaceNormal[0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2]) / flDeterminant;
  200. ( *pLuxelOrigin )[1] = -(flFaceDistance * vLuxelSpaceCross[1]) / flDeterminant;
  201. ( *pS )[2] = (vFaceNormal[1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0] - vFaceNormal[0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1]) / flDeterminant;
  202. ( *pT )[2] = (vFaceNormal[0] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1] - vFaceNormal[1] * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0]) / flDeterminant;
  203. ( *pLuxelOrigin )[2] = -(flFaceDistance * vLuxelSpaceCross[2]) / flDeterminant;
  204. // adjust for luxel offset
  205. VectorMA( ( *pLuxelOrigin ), -pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3], ( *pS ), ( *pLuxelOrigin ) );
  206. VectorMA( ( *pLuxelOrigin ), -pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3], ( *pT ), ( *pLuxelOrigin ) );
  207. Assert( fabsf( DOT_PRODUCT( *pLuxelOrigin, pTexInfo->lightmapVecsLuxelsPerWorldUnits[0] ) + pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3] ) < LUXEL_WORLD_SPACE_EPSILON );
  208. Assert( fabsf( DOT_PRODUCT( *pLuxelOrigin, pTexInfo->lightmapVecsLuxelsPerWorldUnits[1] ) + pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3] ) < LUXEL_WORLD_SPACE_EPSILON );
  209. }
  210. }
  211. static void R_ComputeSurfaceBasis( SurfaceHandle_t surfID, Vector &luxelBasePosition, Vector &sVect, Vector &tVect )
  212. {
  213. CalculateLuxelToWorldTransform( MSurf_TexInfo( surfID ), MSurf_Plane( surfID ).normal, MSurf_Plane( surfID ).dist, &luxelBasePosition, &sVect, &tVect );
  214. luxelBasePosition += sVect * MSurf_LightmapMins( surfID )[0];
  215. luxelBasePosition += tVect * MSurf_LightmapMins( surfID )[1];
  216. }
  217. static void LuxelSpaceToWorld( SurfaceHandle_t surfID, Vector &worldPosition, float u, float v )
  218. {
  219. Vector luxelBasePosition, sVect, tVect;
  220. R_ComputeSurfaceBasis( surfID, luxelBasePosition, sVect, tVect );
  221. worldPosition = luxelBasePosition;
  222. worldPosition += u * sVect * MSurf_LightmapExtents( surfID )[0];
  223. worldPosition += v * tVect * MSurf_LightmapExtents( surfID )[1];
  224. }
  225. static bool KeepSurface( SurfaceHandle_t surfID )
  226. {
  227. IMaterial *pMaterial = materialSortInfoArray[MSurf_MaterialSortID( surfID )].material;
  228. if( pMaterial->IsTranslucent() )
  229. {
  230. static unsigned int nWorldImposterVarCache = 0;
  231. if ( IMaterialVar *pMaterialVar = pMaterial->FindVarFast( "$worldimposter", &nWorldImposterVarCache ) )
  232. {
  233. if ( pMaterialVar->GetIntValue() != 0 )
  234. {
  235. return true;
  236. }
  237. }
  238. return false;
  239. }
  240. #if defined( CSTRIKE15 )
  241. if( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  242. {
  243. return false;
  244. }
  245. if( MSurf_Flags(surfID) & SURFDRAW_SKY )
  246. {
  247. return false;
  248. }
  249. #endif
  250. return true;
  251. }
  252. static bool ComputeMapName( char *pMapName, size_t nMapNameSize )
  253. {
  254. IClientEntity *world = entitylist->GetClientEntity( 0 );
  255. if( world && world->GetModel() )
  256. {
  257. const model_t *pModel = world->GetModel();
  258. const char *pModelName = modelloader->GetName( pModel );
  259. // This handles the case where you have a map in a directory under maps.
  260. // We need to keep everything after "maps/" so it looks for the BSP file in the right place.
  261. if ( Q_stristr( pModelName, "maps/" ) == pModelName ||
  262. Q_stristr( pModelName, "maps\\" ) == pModelName )
  263. {
  264. Q_strncpy( pMapName, &pModelName[5], nMapNameSize );
  265. Q_StripExtension( pMapName, pMapName, nMapNameSize );
  266. }
  267. else
  268. {
  269. Q_FileBase( pModelName, pMapName, nMapNameSize );
  270. }
  271. return true;
  272. }
  273. else
  274. {
  275. return false;
  276. }
  277. }
  278. static void ComputeAndMakeDirectories( const char *pMapName, char *pMatDir, size_t nMatDirSize, char *pMaterialSrcDir, size_t nMaterialSrcDirSize, char *pModelDir, size_t nModelDirSize, char *pModelSrcDir, size_t nModelSrcDirSize )
  279. {
  280. // materials dir
  281. Q_snprintf( pMatDir, nMatDirSize, "materials/models/maps/%s", pMapName );
  282. g_pFileSystem->CreateDirHierarchy( pMatDir, "DEFAULT_WRITE_PATH" );
  283. // materialsrc dir
  284. char pTemp[MAX_PATH];
  285. Q_snprintf( pTemp, sizeof( pTemp ), "materialsrc/models/maps/%s", pMapName );
  286. GetModContentSubdirectory( pTemp, pMaterialSrcDir, nMaterialSrcDirSize );
  287. g_pFileSystem->CreateDirHierarchy( pMaterialSrcDir, NULL );
  288. // model dir
  289. Q_snprintf( pModelDir, nModelDirSize, "models/maps//%s", pMapName );
  290. g_pFileSystem->CreateDirHierarchy( pModelDir, "DEFAULT_WRITE_PATH" );
  291. // model src dir
  292. Q_snprintf( pTemp, sizeof( pTemp ), "models/maps/%s", pMapName );
  293. GetModContentSubdirectory( pTemp, pModelSrcDir, nModelSrcDirSize );
  294. g_pFileSystem->CreateDirHierarchy( pModelSrcDir, NULL );
  295. }
  296. static bool CreateSimpleWorldModelVMT( const char *pMaterialDir, const char *mapName )
  297. {
  298. char vmtPath[MAX_PATH];
  299. V_strncpy( vmtPath, pMaterialDir, MAX_PATH );
  300. V_strncat( vmtPath, "/simpleworldmodel.vmt", MAX_PATH );
  301. FileHandle_t fp = g_pFullFileSystem->Open( vmtPath, "w" );
  302. if ( fp == FILESYSTEM_INVALID_HANDLE )
  303. {
  304. Warning( "can't create %s\n", vmtPath );
  305. return false;
  306. }
  307. g_pFullFileSystem->FPrintf( fp, "\"patch\"\n" );
  308. g_pFullFileSystem->FPrintf( fp, "{\n" );
  309. g_pFullFileSystem->FPrintf( fp, "\t\"include\" \"materials/engine/simpleworldmodel.vmt\"\n" );
  310. g_pFullFileSystem->FPrintf( fp, "\t\"insert\"\n" );
  311. g_pFullFileSystem->FPrintf( fp, "\t{\n" );
  312. g_pFullFileSystem->FPrintf( fp, "\t\t\"$basetexture\" \"models/maps/%s/simpleworldmodel\"\n", mapName );
  313. g_pFullFileSystem->FPrintf( fp, "\t\t\"$albedo\" \"models/maps/%s/simpleworldmodel_albedo\"\n", mapName );
  314. g_pFullFileSystem->FPrintf( fp, "\t\t\"$lightmap\" \"models/maps/%s/simpleworldmodel_lightmap\"\n", mapName );
  315. g_pFullFileSystem->FPrintf( fp, "\t}\n" );
  316. g_pFullFileSystem->FPrintf( fp, "}\n" );
  317. g_pFullFileSystem->Close( fp );
  318. return true;
  319. }
  320. static void CompileQC( const char *pFileName )
  321. {
  322. // Spawn studiomdl.exe process to generate .mdl and associated files
  323. char cmdline[ 2 * MAX_PATH + 256 ];
  324. V_snprintf( cmdline, sizeof( cmdline ), "studiomdl.exe -nop4 %s", pFileName );
  325. int nExitCode = g_pProcessUtils->SimpleRunProcess( cmdline );
  326. if ( nExitCode == -1 )
  327. {
  328. Msg( "Failed compiling %s\n", pFileName );
  329. return;
  330. }
  331. Msg( "Compilation of \"%s\" succeeded\n", pFileName );
  332. }
  333. static void CreateAndCompileQCFile( const char *pModelSrcDir, const char *mapName, bool bWater )
  334. {
  335. /*
  336. $cdmaterials "models/maps/sp_a2_laster_over_goo"
  337. $scale 1.0
  338. $surfaceprop "default"
  339. $staticprop
  340. $modelname "test/test.mdl"
  341. // --- geometry
  342. $body "Body" "smd/test.smd"
  343. // --- animation
  344. $sequence "idle" "smd/test.smd" fps 30
  345. */
  346. char qcPath[MAX_PATH];
  347. V_snprintf( qcPath, MAX_PATH, "%s/simpleworldmodel%s.qc", pModelSrcDir, bWater ? "_water" : "" );
  348. FileHandle_t fp = g_pFileSystem->Open( qcPath, "w" );
  349. if ( fp == FILESYSTEM_INVALID_HANDLE )
  350. {
  351. Warning( "can't create qc file %s\n", qcPath );
  352. return;
  353. }
  354. g_pFileSystem->FPrintf( fp, "// -- generated by buildmodelforworld --\n" );
  355. g_pFileSystem->FPrintf( fp, "$cdmaterials \"models/maps/%s\"\n", mapName );
  356. g_pFileSystem->FPrintf( fp, "$scale 1.0\n" );
  357. g_pFileSystem->FPrintf( fp, "$surfaceprop \"default\"\n" );
  358. g_pFileSystem->FPrintf( fp, "$staticprop\n" );
  359. g_pFileSystem->FPrintf( fp, "$modelname \"maps/%s/simpleworldmodel%s.mdl\"\n", mapName, bWater ? "_water" : "" );
  360. g_pFileSystem->FPrintf( fp, "$body \"Body\" \"simpleworldmodel%s.smd\"\n", bWater ? "_water" : "" );
  361. g_pFileSystem->FPrintf( fp, "$sequence \"idle\" \"simpleworldmodel%s.smd\" fps 30\n", bWater ? "_water" : "" );
  362. g_pFileSystem->Close( fp );
  363. CompileQC( qcPath );
  364. }
  365. static void CompileTGA( const char *pFileName )
  366. {
  367. // Spawn vtex.exe process to generate .vtf file
  368. char cmdline[ 2 * MAX_PATH + 256 ];
  369. V_snprintf( cmdline, sizeof( cmdline ), "vtex.exe -nop4 %s", pFileName );
  370. int nExitCode = g_pProcessUtils->SimpleRunProcess( cmdline );
  371. if ( nExitCode == -1 )
  372. {
  373. Msg( "Failed compiling %s\n", pFileName );
  374. return;
  375. }
  376. Msg( "Compilation of \"%s\" succeeded\n", pFileName );
  377. }
  378. static void AddFileToPackAndDeleteFile( const char *mapName, const char *gameDir, const char *pFormatString )
  379. {
  380. char relativePath[MAX_PATH];
  381. char fullPath[MAX_PATH];
  382. V_snprintf( relativePath, MAX_PATH, pFormatString, mapName );
  383. V_snprintf( fullPath, MAX_PATH, "%s/%s", gameDir, relativePath );
  384. s_pBSPPack->AddFileToPack( relativePath, fullPath );
  385. if ( DELETE_INTERMEDIATE_FILES )
  386. {
  387. g_pFileSystem->RemoveFile( fullPath );
  388. }
  389. }
  390. static void RemoveFileFromPack( const char *mapName, const char *gameDir, const char *pFormatString )
  391. {
  392. char relativePath[MAX_PATH];
  393. char fullPath[MAX_PATH];
  394. V_snprintf( relativePath, MAX_PATH, pFormatString, mapName );
  395. V_snprintf( fullPath, MAX_PATH, "%s/%s", gameDir, relativePath );
  396. s_pBSPPack->RemoveFileFromPack( relativePath );
  397. }
  398. static void RemoveContentFile( const char *mapName, const char *pFormatString )
  399. {
  400. if ( DELETE_INTERMEDIATE_FILES )
  401. {
  402. char pTemp[MAX_PATH];
  403. char absolutePath[MAX_PATH];
  404. Q_snprintf( pTemp, sizeof( pTemp ), pFormatString, mapName );
  405. GetModContentSubdirectory( pTemp, absolutePath, sizeof( absolutePath ) );
  406. g_pFileSystem->RemoveFile( absolutePath );
  407. }
  408. }
  409. class CPackedSurfaceInfo
  410. {
  411. public:
  412. SurfaceHandle_t m_SurfID;
  413. int m_nMins[2]; // texel offset in packed/atlased texture
  414. CPackedSurfaceInfo()
  415. {
  416. m_SurfID = SURFACE_HANDLE_INVALID;
  417. m_nMins[0] = -1;
  418. m_nMins[1] = -1;
  419. }
  420. };
  421. //
  422. // calculate the packing for all of the world geometry in order to know what size render target to allocate for buliding textures.
  423. //
  424. static void PackSurfacesAndBuildSurfaceList( CUtlVector<CPackedSurfaceInfo> &packedSurfaces, int &nWidth, int &nHeight )
  425. {
  426. worldbrushdata_t *pBrushData = host_state.worldbrush;
  427. int numSurfaces = pBrushData->nWorldFaceCount;
  428. CImagePacker imagePacker;
  429. imagePacker.Reset( 0, MAX_ATLAS_TEXTURE_DIMENSION, MAX_ATLAS_TEXTURE_DIMENSION );
  430. for( int surfaceIndex = 0; surfaceIndex < numSurfaces; surfaceIndex++ )
  431. {
  432. SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfaceIndex );
  433. // Get the size of the lightmap page.
  434. int lightmapSize[2];
  435. lightmapSize[0] = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  436. lightmapSize[1] = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
  437. if ( !KeepSurface( surfID ) )
  438. {
  439. continue;
  440. }
  441. CPackedSurfaceInfo &surfaceInfo = packedSurfaces[ packedSurfaces.AddToTail() ];
  442. surfaceInfo.m_SurfID = surfID;
  443. if ( !imagePacker.AddBlock( lightmapSize[0], lightmapSize[1], &surfaceInfo.m_nMins[0], &surfaceInfo.m_nMins[1] ) )
  444. {
  445. Warning( "failed allocating an atlased texture block in buildmodelforworld\n" );
  446. }
  447. }
  448. int nMinWidth, nMinHeight;
  449. imagePacker.GetMinimumDimensions( &nMinWidth, &nMinHeight );
  450. nWidth = nMinWidth;
  451. nHeight = nMinHeight;
  452. }
  453. static void WriteSMDHeader( FileHandle_t smdfp )
  454. {
  455. g_pFullFileSystem->FPrintf( smdfp, "version 1\n" );
  456. g_pFullFileSystem->FPrintf( smdfp, "nodes\n" );
  457. g_pFullFileSystem->FPrintf( smdfp, "0 \"polymsh_extracted2\" -1\n" );
  458. g_pFullFileSystem->FPrintf( smdfp, "end\n" );
  459. g_pFullFileSystem->FPrintf( smdfp, "skeleton\n" );
  460. g_pFullFileSystem->FPrintf( smdfp, "time 0\n" );
  461. g_pFullFileSystem->FPrintf( smdfp, "0 0.000000 0.000000 0.000000 1.570796 0.000000 0.000000\n" );
  462. g_pFullFileSystem->FPrintf( smdfp, "end\n" );
  463. g_pFullFileSystem->FPrintf( smdfp, "triangles\n" );
  464. }
  465. static void Push2DRenderingSetup( IMatRenderContext *pRenderContext, ITexture *pRenderTarget, int nAtlasedTextureWidth, int nAtlasedTextureHeight )
  466. {
  467. pRenderContext->PushRenderTargetAndViewport( pRenderTarget );
  468. pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
  469. materials->ClearBuffers( true, false, false );
  470. float flPixelOffset = 0.5f;
  471. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  472. pRenderContext->PushMatrix();
  473. pRenderContext->LoadIdentity();
  474. pRenderContext->Scale( 1, -1, 1 );
  475. pRenderContext->Ortho(
  476. flPixelOffset * ( 1.0f / nAtlasedTextureWidth ),
  477. flPixelOffset * ( 1.0f / nAtlasedTextureHeight ),
  478. ( flPixelOffset + nAtlasedTextureWidth ) * ( 1.0f / nAtlasedTextureWidth ),
  479. ( flPixelOffset + nAtlasedTextureHeight ) * ( 1.0f / nAtlasedTextureHeight ), -99999, 99999 );
  480. pRenderContext->MatrixMode( MATERIAL_VIEW );
  481. pRenderContext->PushMatrix();
  482. pRenderContext->LoadIdentity();
  483. pRenderContext->MatrixMode( MATERIAL_MODEL );
  484. pRenderContext->PushMatrix();
  485. pRenderContext->LoadIdentity();
  486. }
  487. static void Pop2DRenderingSetup( IMatRenderContext *pRenderContext )
  488. {
  489. pRenderContext->MatrixMode( MATERIAL_VIEW );
  490. pRenderContext->PopMatrix();
  491. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  492. pRenderContext->PopMatrix();
  493. pRenderContext->MatrixMode( MATERIAL_MODEL );
  494. pRenderContext->PopMatrix();
  495. pRenderContext->PopRenderTargetAndViewport();
  496. }
  497. enum RenderToAtlasedTextureRenderMode_t
  498. {
  499. RENDER_TO_ATLASED_TEXTURE_FULL_RENDERING = 0,
  500. RENDER_TO_ATLASED_TEXTURE_NO_LIGHTING,
  501. RENDER_TO_ATLASED_TEXTURE_LIGHTING_ONLY,
  502. };
  503. class CDisplacementData
  504. {
  505. public:
  506. CDisplacementData( HDISPINFOARRAY hDispInfo, int nDispInfo )
  507. {
  508. m_nDispStartVert.SetCount( nDispInfo );
  509. int nStartVert = 0;
  510. for ( int i = 0; i < nDispInfo; i++ )
  511. {
  512. CDispInfo *pDisp = static_cast<CDispInfo *> ( DispInfo_IndexArray( hDispInfo, i ) );
  513. m_nDispStartVert[i] = nStartVert;
  514. nStartVert += pDisp->NumVerts();
  515. }
  516. // total number of verts, size memory
  517. m_dispVerts.SetCount( nStartVert );
  518. // now load from disk
  519. CMapLoadHelper lh( LUMP_DISP_VERTS );
  520. lh.LoadLumpData( 0, nStartVert * sizeof(CDispVert), m_dispVerts.Base() );
  521. }
  522. CUtlVector<int> m_nDispStartVert;
  523. CUtlVector<CDispVert> m_dispVerts;
  524. };
  525. struct surfacerect_t
  526. {
  527. Vector m_vPos[4];
  528. Vector m_vPosWorld[4];
  529. Vector2D m_LMCoords[4];
  530. float m_flBumpSTexCoordOffset;
  531. int m_nTexCoordIndexOffset;
  532. };
  533. void DrawTexturedQuad( IMaterial *pMaterial, IMatRenderContext *pRenderContext, SurfaceHandle_t surfID, const surfacerect_t &rect, const Vector4D &vColor )
  534. {
  535. IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
  536. CMeshBuilder builder;
  537. builder.Begin( pMesh, MATERIAL_POLYGON, 4 );
  538. for ( int vertID = 0; vertID < 4; vertID++ )
  539. {
  540. const Vector &vecPageCoord = rect.m_vPos[vertID];
  541. Vector2D texCoord;
  542. SurfComputeTextureCoordinate( surfID, rect.m_vPosWorld[( vertID + rect.m_nTexCoordIndexOffset ) % 4], texCoord.Base() );
  543. // Need to figure out what the world position is of vecPageCoord.
  544. builder.Position3fv( vecPageCoord.Base() );
  545. builder.Color4fv( vColor.Base() );
  546. builder.TexCoord2fv( 0, texCoord.Base() );
  547. builder.TexCoord2fv( 1, rect.m_LMCoords[vertID].Base() );
  548. if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  549. {
  550. // bump maps appear left to right in lightmap page memory, calculate
  551. // the offset for the width of a single map. The pixel shader will use
  552. // this to compute the actual texture coordinates
  553. builder.TexCoord2f( 2, rect.m_flBumpSTexCoordOffset, 0.0f );
  554. }
  555. else
  556. {
  557. // PORTAL 2 FIX - paint shader assumes it can use 3 lightmapped coordinates in all cases, so set the offset to something reasonable
  558. builder.TexCoord2f( 2, 0.0f, 0.0f );
  559. }
  560. builder.Normal3f( 0.0f, 0.0f, 1.0f );
  561. builder.AdvanceVertex();
  562. }
  563. builder.End( false, true );
  564. }
  565. // includes left and top pixel, excludes right and bottom pixels
  566. // This draws the whole lightmap space for the surface including the half texel boards into the atlased texture.
  567. void DrawSurfaceRectToAtlasedTexture( float left, float top, float right, float bottom, int nRenderTargetWidth, int nRenderTargetHeight, SurfaceHandle_t surfID, RenderToAtlasedTextureRenderMode_t renderMode, const CDisplacementData &dispData )
  568. {
  569. float flOffset = 0.0f;
  570. // Could compute all this from the ctxAtlased instead. Make sure they generate the same result.
  571. left = ( flOffset + left ) * ( 1.0f / ( float )nRenderTargetWidth );
  572. right = ( flOffset + right ) * ( 1.0f / ( float )nRenderTargetWidth );
  573. top = ( flOffset + top ) * ( 1.0f / ( float )nRenderTargetHeight );
  574. bottom = ( flOffset + bottom ) * ( 1.0f / ( float )nRenderTargetHeight );
  575. SurfaceCtx_t ctx;
  576. SurfSetupSurfaceContext( ctx, surfID );
  577. SurfaceCtx_t ctxAtlased;
  578. SurfSetupSurfaceContextAtlased( ctxAtlased, surfID, left, top, nRenderTargetWidth, nRenderTargetHeight );
  579. Vector tVect;
  580. bool negate = false;
  581. if ( MSurf_Flags( surfID ) & SURFDRAW_TANGENTSPACE )
  582. {
  583. negate = TangentSpaceSurfaceSetup( surfID, tVect );
  584. }
  585. CMatRenderContextPtr pRenderContext( materials );
  586. IMaterial *pMaterial = materialSortInfoArray[ MSurf_MaterialSortID( surfID ) ].material;
  587. pRenderContext->Bind( pMaterial, NULL );
  588. if ( renderMode == RENDER_TO_ATLASED_TEXTURE_NO_LIGHTING )
  589. {
  590. if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  591. {
  592. pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  593. }
  594. else
  595. {
  596. pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE );
  597. }
  598. }
  599. else
  600. {
  601. pRenderContext->BindLightmapPage( materialSortInfoArray[ MSurf_MaterialSortID( surfID ) ].lightmapPageID );
  602. }
  603. float lightmapLeft = ctx.m_Offset.x;
  604. float lightmapTop = ctx.m_Offset.y;
  605. float lightmapRight = ctx.m_LightmapSize[0] * ctx.m_Scale.x + ctx.m_Offset.x;
  606. float lightmapBottom = ctx.m_LightmapSize[1] * ctx.m_Scale.y + ctx.m_Offset.y;
  607. Vector worldPosition;
  608. Vector2D testUV;
  609. LuxelSpaceToWorld( surfID, worldPosition, 0.0f, 0.0f );
  610. SurfComputeLightmapCoordinate( ctx, surfID, worldPosition, testUV );
  611. // -0.5f to account for half-texel border
  612. float flLeft = testUV.x - 0.5f * ctx.m_Scale.x;
  613. float flTop = testUV.y - 0.5f * ctx.m_Scale.y;
  614. LuxelSpaceToWorld( surfID, worldPosition, 1.0f, 1.0f );
  615. SurfComputeLightmapCoordinate( ctx, surfID, worldPosition, testUV );
  616. // +0.5f to account for half-texel border
  617. float flRight = testUV.x + 0.5f * ctx.m_Scale.x;
  618. float flBottom = testUV.y + 0.5f * ctx.m_Scale.y;
  619. surfacerect_t surfaceRect;
  620. surfaceRect.m_flBumpSTexCoordOffset = ctx.m_BumpSTexCoordOffset;
  621. surfaceRect.m_nTexCoordIndexOffset = 0;
  622. surfaceRect.m_vPos[0].Init( left, bottom, 0.5f );
  623. surfaceRect.m_vPos[1].Init( left, top, 0.5f );
  624. surfaceRect.m_vPos[2].Init( right, top, 0.5f );
  625. surfaceRect.m_vPos[3].Init( right, bottom, 0.5f );
  626. Vector4D vColor;
  627. RandomColor( vColor.AsVector3D() );
  628. vColor.w = 0.0f; // default alpha of 0.0f
  629. LuxelSpaceToWorld( surfID, surfaceRect.m_vPosWorld[0], 0.0f, 1.0f );
  630. LuxelSpaceToWorld( surfID, surfaceRect.m_vPosWorld[1], 0.0f, 0.0f );
  631. LuxelSpaceToWorld( surfID, surfaceRect.m_vPosWorld[2], 1.0f, 0.0f );
  632. LuxelSpaceToWorld( surfID, surfaceRect.m_vPosWorld[3], 1.0f, 1.0f );
  633. // round to the nearest integer in texels (+0.5f for rounding)
  634. float flLeftSnapped = ctx.m_Scale.x * ( float )( int )( ctx.m_LightmapPageSize[0] * flLeft + 0.5f );
  635. float flRightSnapped = ctx.m_Scale.x * ( float )( int )( ctx.m_LightmapPageSize[0] * flRight + 0.5f );
  636. float flTopSnapped = ctx.m_Scale.y * ( float )( int )( ctx.m_LightmapPageSize[1] * flTop + 0.5f );
  637. float flBottomSnapped = ctx.m_Scale.y * ( float )( int )( ctx.m_LightmapPageSize[1] * flBottom + 0.5f );
  638. Assert( flLeftSnapped == lightmapLeft );
  639. Assert( flRightSnapped == lightmapRight );
  640. Assert( flTopSnapped == lightmapTop );
  641. Assert( flBottomSnapped == lightmapBottom );
  642. surfaceRect.m_LMCoords[0].Init( flLeftSnapped, flBottomSnapped );
  643. surfaceRect.m_LMCoords[1].Init( flLeftSnapped, flTopSnapped );
  644. surfaceRect.m_LMCoords[2].Init( flRightSnapped, flTopSnapped );
  645. surfaceRect.m_LMCoords[3].Init( flRightSnapped, flBottomSnapped );
  646. int nTexCoordShift = 0;
  647. if ( surfID->pDispInfo )
  648. {
  649. CDispInfo *pDispInfo = static_cast<CDispInfo *>(surfID->pDispInfo);
  650. nTexCoordShift = pDispInfo->m_iPointStart;
  651. }
  652. DrawTexturedQuad( pMaterial, pRenderContext, surfID, surfaceRect, vColor );
  653. if ( surfID->pDispInfo && renderMode != RENDER_TO_ATLASED_TEXTURE_LIGHTING_ONLY )
  654. {
  655. CDispInfo *pDispInfo = static_cast<CDispInfo *>(surfID->pDispInfo);
  656. int nLightmapPageSize[2];
  657. materials->GetLightmapPageSize( SortInfoToLightmapPage( MSurf_MaterialSortID( surfID ) ), &nLightmapPageSize[0], &nLightmapPageSize[1] );
  658. Vector2D vOffset;
  659. float flPageSizeU = nLightmapPageSize[0];
  660. float flPageSizeV = nLightmapPageSize[1];
  661. vOffset.x = ( float )MSurf_OffsetIntoLightmapPage( surfID )[0] / flPageSizeU;
  662. vOffset.y = ( float )MSurf_OffsetIntoLightmapPage( surfID )[1] / flPageSizeV;
  663. pRenderContext->Bind( pMaterial, NULL );
  664. IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
  665. float flLightmapSizeU = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  666. float flLightmapSizeV = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
  667. int nTriangleCount = pDispInfo->m_nIndices / 3;
  668. CMeshBuilder builder;
  669. builder.Begin( pMesh, MATERIAL_TRIANGLES, nTriangleCount * 2 );
  670. int nDispIndex = DispInfo_ComputeIndex( host_state.worldbrush->hDispInfos, surfID->pDispInfo );
  671. int nStartVert = dispData.m_nDispStartVert[ nDispIndex ];
  672. Vector4D vColor(1,1,1,0);
  673. for ( int i = 0; i < pDispInfo->m_Verts.Count(); i++ )
  674. {
  675. // Need to figure out what the world position is of vecPageCoord.
  676. CDispRenderVert *pVert = &pDispInfo->m_Verts[i];
  677. const CDispVert *pDiskVert = &dispData.m_dispVerts[ nStartVert + i];
  678. vColor.w = pDiskVert->m_flAlpha * (1.0f / 255.0f);
  679. Vector2D uv = pVert->m_LMCoords;
  680. uv -= vOffset;
  681. uv.x *= flPageSizeU / flLightmapSizeU;
  682. uv.y *= flPageSizeV / flLightmapSizeV;
  683. // uv is now in [0,1] for lightmap space, use this to generate a position
  684. Vector vPos( left + uv.x * (right-left), top + uv.y * (bottom-top), 0.5f );
  685. // Vector2D vLuxel( flLeftSnapped + uv.x * (flRightSnapped-flLeftSnapped), flTopSnapped + uv.y * (flBottomSnapped-flTopSnapped) );
  686. Vector2D vLuxel( lightmapLeft + uv.x * (lightmapRight-lightmapLeft), lightmapTop + uv.y * (lightmapBottom-lightmapTop) );
  687. builder.Position3fv( vPos.Base() );
  688. builder.Color4fv( vColor.Base() );
  689. builder.TexCoord2fv( 0, pVert->m_vTexCoord.Base() );
  690. builder.TexCoord2fv( 1, vLuxel.Base() );//pVert->m_LMCoords.Base() );
  691. builder.TangentS3fv( pVert->m_vSVector.Base() );
  692. builder.TangentT3fv( pVert->m_vTVector.Base() );
  693. if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  694. {
  695. // bump maps appear left to right in lightmap page memory, calculate
  696. // the offset for the width of a single map. The pixel shader will use
  697. // this to compute the actual texture coordinates
  698. builder.TexCoord2f( 2, ctx.m_BumpSTexCoordOffset, 0.0f );
  699. }
  700. else
  701. {
  702. // PORTAL 2 FIX - paint shader assumes it can use 3 lightmapped coordinates in all cases, so set the offset to something reasonable
  703. builder.TexCoord2f( 2, 0.0f, 0.0f );
  704. }
  705. builder.Normal3f( 0.0f, 0.0f, 1.0f );
  706. builder.AdvanceVertex();
  707. }
  708. for ( int i = 0; i < pDispInfo->m_nIndices; i += 3 )
  709. {
  710. builder.FastIndex( pDispInfo->m_Indices[i+0] - pDispInfo->m_iVertOffset );
  711. builder.FastIndex( pDispInfo->m_Indices[i+1] - pDispInfo->m_iVertOffset );
  712. builder.FastIndex( pDispInfo->m_Indices[i+2] - pDispInfo->m_iVertOffset );
  713. // draw all triangles twice in case they are backfacing due to the projection into texture space
  714. builder.FastIndex( pDispInfo->m_Indices[i+2] - pDispInfo->m_iVertOffset );
  715. builder.FastIndex( pDispInfo->m_Indices[i+1] - pDispInfo->m_iVertOffset );
  716. builder.FastIndex( pDispInfo->m_Indices[i+0] - pDispInfo->m_iVertOffset );
  717. }
  718. builder.End( false, true );
  719. }
  720. }
  721. static void RenderToAtlasedTexture( const CUtlVector<CPackedSurfaceInfo> &packedSurfaces, IMatRenderContext *pRenderContext, ITexture *pRenderTarget, int nAtlasedTextureWidth, int nAtlasedTextureHeight, const char *pMaterialSrcDir, const char *pTextureBaseName, RenderToAtlasedTextureRenderMode_t renderMode, const CDisplacementData &dispData, float flToneMapScale )
  722. {
  723. // Disable specular for atlased texture generation.
  724. bool bSaveMatSpecular = mat_fastspecular.GetBool();
  725. mat_fastspecular.SetValue( "0" );
  726. if ( renderMode == RENDER_TO_ATLASED_TEXTURE_LIGHTING_ONLY )
  727. {
  728. mat_fullbright.SetValue( "2" );
  729. }
  730. else
  731. {
  732. mat_fullbright.SetValue( "0" );
  733. }
  734. UpdateMaterialSystemConfig();
  735. materials->EndFrame();
  736. materials->BeginFrame( host_frametime );
  737. pRenderContext->SetToneMappingScaleLinear( Vector( flToneMapScale, flToneMapScale, flToneMapScale ) );
  738. Push2DRenderingSetup( pRenderContext, pRenderTarget, nAtlasedTextureWidth, nAtlasedTextureHeight );
  739. // run through all of the faces and render them into the render target.
  740. FOR_EACH_VEC( packedSurfaces, packedSurfaceIndex )
  741. {
  742. const CPackedSurfaceInfo &packedSurface = packedSurfaces[packedSurfaceIndex];
  743. // Get the size of the lightmap subpage for this surface.
  744. int lightmapSize[2];
  745. lightmapSize[0] = ( MSurf_LightmapExtents( packedSurface.m_SurfID )[0] ) + 1;
  746. lightmapSize[1] = ( MSurf_LightmapExtents( packedSurface.m_SurfID )[1] ) + 1;
  747. DrawSurfaceRectToAtlasedTexture( packedSurface.m_nMins[0], packedSurface.m_nMins[1],
  748. packedSurface.m_nMins[0] + lightmapSize[0], packedSurface.m_nMins[1] + lightmapSize[1],
  749. nAtlasedTextureWidth, nAtlasedTextureHeight, packedSurface.m_SurfID, renderMode, dispData );
  750. }
  751. // Read the resulting image.
  752. unsigned char *pImage = new unsigned char[nAtlasedTextureWidth * nAtlasedTextureHeight * 4];
  753. pRenderContext->ReadPixels( 0, 0, nAtlasedTextureWidth, nAtlasedTextureHeight, pImage, IMAGE_FORMAT_RGBA8888 );
  754. // Write the image to a TGA file.
  755. char tgaPath[MAX_PATH];
  756. V_snprintf( tgaPath, MAX_PATH, "%s/%s.tga", pMaterialSrcDir, pTextureBaseName );
  757. TGAWriter::WriteTGAFile( tgaPath, nAtlasedTextureWidth, nAtlasedTextureHeight, IMAGE_FORMAT_RGBA8888, pImage, nAtlasedTextureWidth * 4 );
  758. delete [] pImage;
  759. // Write a txt file with the vtex options for the TGA file.
  760. char txtPath[MAX_PATH];
  761. V_snprintf( txtPath, MAX_PATH, "%s/%s.txt", pMaterialSrcDir, pTextureBaseName );
  762. FileHandle_t txtFP = g_pFullFileSystem->Open( txtPath, "w", NULL );
  763. g_pFullFileSystem->FPrintf( txtFP, "\"nocompress\" \"1\"\n" );
  764. g_pFullFileSystem->FPrintf( txtFP, "\"nomip\" \"1\"\n" );
  765. g_pFullFileSystem->FPrintf( txtFP, "\"nolod\" \"1\"\n" );
  766. g_pFullFileSystem->Close( txtFP );
  767. // Compile the TGA file.
  768. CompileTGA( tgaPath );
  769. if ( DELETE_INTERMEDIATE_FILES )
  770. {
  771. g_pFullFileSystem->RemoveFile( txtPath );
  772. g_pFullFileSystem->RemoveFile( tgaPath );
  773. }
  774. Pop2DRenderingSetup( pRenderContext );
  775. // restore specular
  776. if( bSaveMatSpecular )
  777. {
  778. mat_fastspecular.SetValue( "1" );
  779. }
  780. else
  781. {
  782. mat_fastspecular.SetValue( "0" );
  783. }
  784. mat_fullbright.SetValue( "0" );
  785. UpdateMaterialSystemConfig();
  786. materials->EndFrame();
  787. materials->BeginFrame( host_frametime );
  788. }
  789. static int WriteSMD( const CUtlVector<CPackedSurfaceInfo> &packedSurfaces, int nAtlasedTextureWidth, int nAtlasedTextureHeight, const char *pModelSrcDir, bool bWater )
  790. {
  791. char smdPath[MAX_PATH];
  792. V_snprintf( smdPath, MAX_PATH, "%s/simpleworldmodel%s.smd", pModelSrcDir, bWater ? "_water" : "" );
  793. FileHandle_t smdfp = g_pFullFileSystem->Open( smdPath, "w", NULL );
  794. WriteSMDHeader( smdfp );
  795. int nSurfaces = 0;
  796. // run through all of the faces and render them into the render target.
  797. worldbrushdata_t *pBrushData = host_state.worldbrush;
  798. FOR_EACH_VEC( packedSurfaces, packedSurfaceIndex )
  799. {
  800. const CPackedSurfaceInfo &packedSurface = packedSurfaces[packedSurfaceIndex];
  801. IMaterial *pMaterial = materialSortInfoArray[MSurf_MaterialSortID( packedSurface.m_SurfID )].material;
  802. bool bIsWater = ( V_stristr( pMaterial->GetShaderName(), "water" ) != 0 );
  803. if ( bIsWater != bWater )
  804. {
  805. continue;
  806. }
  807. nSurfaces++;
  808. // Get the size of the lightmap subpage for this surface.
  809. int lightmapSize[2];
  810. lightmapSize[0] = ( MSurf_LightmapExtents( packedSurface.m_SurfID )[0] ) + 1;
  811. lightmapSize[1] = ( MSurf_LightmapExtents( packedSurface.m_SurfID )[1] ) + 1;
  812. // Draw the face with both facings since we don't know which way it's going to face in the texture page.
  813. WriteSurfaceToSMD( pBrushData, packedSurface.m_SurfID, packedSurface.m_nMins[0], packedSurface.m_nMins[1], smdfp, nAtlasedTextureWidth, nAtlasedTextureHeight );
  814. }
  815. g_pFullFileSystem->FPrintf( smdfp, "end\n" );
  816. g_pFullFileSystem->Close( smdfp );
  817. return nSurfaces;
  818. }
  819. ConVar r_buildingmapforworld( "r_buildingmapforworld", "0" );
  820. CON_COMMAND( buildmodelforworld, "buildmodelforworld" )
  821. {
  822. r_buildingmapforworld.SetValue( 1 );
  823. extern void V_RenderVGuiOnly();
  824. if ( g_LostVideoMemory )
  825. {
  826. r_buildingmapforworld.SetValue( 0 );
  827. return;
  828. }
  829. // Make sure that the file is writable before building cubemaps.
  830. Assert( g_pFileSystem->FileExists( GetBaseLocalClient().m_szLevelName, "GAME" ) );
  831. if( !g_pFileSystem->IsFileWritable( GetBaseLocalClient().m_szLevelName, "GAME" ) )
  832. {
  833. Warning( "%s is not writable!!! Check it out before running buildmodelforworld.\n", GetBaseLocalClient().m_szLevelName );
  834. r_buildingmapforworld.SetValue( 0 );
  835. return;
  836. }
  837. char mapName[MAX_PATH];
  838. if ( !ComputeMapName( mapName, sizeof( mapName ) ) )
  839. {
  840. Warning( "can't buildmodelforworld. Map not loaded.\n" );
  841. r_buildingmapforworld.SetValue( 0 );
  842. return;
  843. }
  844. char matDir[MAX_PATH];
  845. char materialSrcDir[MAX_PATH];
  846. char modelDir[MAX_PATH];
  847. char modelSrcDir[MAX_PATH];
  848. ComputeAndMakeDirectories( mapName, matDir, MAX_PATH, materialSrcDir, MAX_PATH, modelDir, MAX_PATH, modelSrcDir, MAX_PATH );
  849. char gameDir[MAX_PATH];
  850. COM_GetGameDir( gameDir, sizeof( gameDir ) );
  851. if ( !CreateSimpleWorldModelVMT( matDir, mapName ) )
  852. {
  853. r_buildingmapforworld.SetValue( 0 );
  854. return;
  855. }
  856. // turn off queued material system while we are doing this work.
  857. bool bAllow = Host_AllowQueuedMaterialSystem( false );
  858. // do this to force a frame to render so the material system syncs up to single thread mode
  859. V_RenderVGuiOnly();
  860. // we need to load some data from disk again to make displacement alpha work
  861. CMapLoadHelper::Init( host_state.worldmodel, host_state.worldmodel->szPathName );
  862. CDisplacementData dispData( host_state.worldbrush->hDispInfos, host_state.worldbrush->numDispInfos );
  863. CMapLoadHelper::Shutdown();
  864. CMatRenderContextPtr pRenderContext( materials );
  865. CUtlVector<CPackedSurfaceInfo> packedSurfaces;
  866. int nAtlasedTextureWidth, nAtlasedTextureHeight;
  867. PackSurfacesAndBuildSurfaceList( packedSurfaces, nAtlasedTextureWidth, nAtlasedTextureHeight );
  868. // Allocate a render target to render into. THIS WILL LEAK!!!!! There isn't a good way in source 1 to delete the rendertarget,
  869. // so I'm going to spew this when we are done so that the user knows that they are going to leak.
  870. materials->ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly();
  871. materials->BeginRenderTargetAllocation();
  872. ITexture *pRenderTarget = materials->CreateRenderTargetTexture( nAtlasedTextureWidth, nAtlasedTextureHeight, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_RGBA8888, MATERIAL_RT_DEPTH_NONE );
  873. materials->EndRenderTargetAllocation();
  874. Assert( pRenderTarget );
  875. RenderToAtlasedTexture( packedSurfaces, pRenderContext, pRenderTarget, nAtlasedTextureWidth, nAtlasedTextureHeight, materialSrcDir, "simpleworldmodel", RENDER_TO_ATLASED_TEXTURE_FULL_RENDERING, dispData, BASE_TIMES_LIGHTMAP_LINEAR_TONEMAP_SCALE );
  876. RenderToAtlasedTexture( packedSurfaces, pRenderContext, pRenderTarget, nAtlasedTextureWidth, nAtlasedTextureHeight, materialSrcDir, "simpleworldmodel_lightmap", RENDER_TO_ATLASED_TEXTURE_LIGHTING_ONLY, dispData, 1.0f );
  877. RenderToAtlasedTexture( packedSurfaces, pRenderContext, pRenderTarget, nAtlasedTextureWidth, nAtlasedTextureHeight, materialSrcDir, "simpleworldmodel_albedo", RENDER_TO_ATLASED_TEXTURE_NO_LIGHTING, dispData, 1.0f );
  878. int nWaterSurfaces = WriteSMD( packedSurfaces, nAtlasedTextureWidth, nAtlasedTextureHeight, modelSrcDir, true /* bWater */ );
  879. int nNonWaterSurfaces = WriteSMD( packedSurfaces, nAtlasedTextureWidth, nAtlasedTextureHeight, modelSrcDir, false /* bWater */ );
  880. Host_AllowQueuedMaterialSystem( bAllow );
  881. if ( nWaterSurfaces > 0 )
  882. {
  883. CreateAndCompileQCFile( modelSrcDir, mapName, true /* bWater */ );
  884. }
  885. if ( nNonWaterSurfaces > 0 )
  886. {
  887. CreateAndCompileQCFile( modelSrcDir, mapName, false /* bWater */ );
  888. }
  889. LoadBSPPackInterface();
  890. char mapPath[MAX_PATH];
  891. Q_snprintf( mapPath, sizeof( mapPath ), "maps/%s.bsp", mapName );
  892. s_pBSPPack->LoadBSPFile( g_pFileSystem, mapPath );
  893. // add files to bsp zip file and nuke local copies (fixme. .move these closer to the code that creates and compiles the assets)
  894. if ( nNonWaterSurfaces > 0 )
  895. {
  896. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel.mdl" );
  897. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel.dx90.vtx" );
  898. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel.vvd" );
  899. }
  900. else
  901. {
  902. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel.mdl" );
  903. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel.dx90.vtx" );
  904. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel.vvd" );
  905. }
  906. if ( nWaterSurfaces > 0 )
  907. {
  908. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.mdl" );
  909. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.dx90.vtx" );
  910. AddFileToPackAndDeleteFile( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.vvd" );
  911. }
  912. else
  913. {
  914. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.mdl" );
  915. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.dx90.vtx" );
  916. RemoveFileFromPack( mapName, gameDir, "models/maps/%s/simpleworldmodel_water.vvd" );
  917. }
  918. if ( nWaterSurfaces > 0 || nNonWaterSurfaces > 0 )
  919. {
  920. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.vmt" );
  921. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.pwl.vtf" );
  922. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.vtf" );
  923. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_albedo.pwl.vtf" );
  924. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_albedo.vtf" );
  925. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_lightmap.pwl.vtf" );
  926. AddFileToPackAndDeleteFile( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_lightmap.vtf" );
  927. }
  928. else
  929. {
  930. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.vmt" );
  931. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.pwl.vtf" );
  932. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel.vtf" );
  933. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_albedo.pwl.vtf" );
  934. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_albedo.vtf" );
  935. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_lightmap.pwl.vtf" );
  936. RemoveFileFromPack( mapName, gameDir, "materials/models/maps/%s/simpleworldmodel_lightmap.vtf" );
  937. }
  938. s_pBSPPack->WriteBSPFile( mapPath );
  939. // nuke files generated in the content directory
  940. RemoveContentFile( mapName, "models/maps/%s/simpleworldmodel.qc" );
  941. RemoveContentFile( mapName, "models/maps/%s/simpleworldmodel.smd" );
  942. RemoveContentFile( mapName, "models/maps/%s/simpleworldmodel_water.qc" );
  943. RemoveContentFile( mapName, "models/maps/%s/simpleworldmodel_water.smd" );
  944. UnloadBSPPackInterface();
  945. Warning( "*****************It is recommended to quit the game after running buildmodelforworld! Leaks rendertargets!****************\n" );
  946. r_buildingmapforworld.SetValue( 0 );
  947. }
  948. #endif // !defined( DEDICATED ) && !defined( _GAMECONSOLE )