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.

5056 lines
165 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // models are the only shared resource between a client and server running
  4. // on the same machine.
  5. //===========================================================================//
  6. #include "render_pch.h"
  7. #include "client.h"
  8. #include "gl_model_private.h"
  9. #include "studio.h"
  10. #include "phyfile.h"
  11. #include "cdll_int.h"
  12. #include "istudiorender.h"
  13. #include "client_class.h"
  14. #include "float.h"
  15. #include "materialsystem/imaterialsystemhardwareconfig.h"
  16. #include "materialsystem/ivballoctracker.h"
  17. #include "modelloader.h"
  18. #include "lightcache.h"
  19. #include "studio_internal.h"
  20. #include "cdll_engine_int.h"
  21. #include "vphysics_interface.h"
  22. #include "utllinkedlist.h"
  23. #include "studio.h"
  24. #include "icliententitylist.h"
  25. #include "engine/ivmodelrender.h"
  26. #include "optimize.h"
  27. #include "icliententity.h"
  28. #include "sys_dll.h"
  29. #include "debugoverlay.h"
  30. #include "enginetrace.h"
  31. #include "l_studio.h"
  32. #include "filesystem_engine.h"
  33. #include "ModelInfo.h"
  34. #include "cl_main.h"
  35. #include "tier0/vprof.h"
  36. #include "r_decal.h"
  37. #include "vstdlib/random.h"
  38. #include "datacache/idatacache.h"
  39. #include "materialsystem/materialsystem_config.h"
  40. #include "materialsystem/itexture.h"
  41. #include "IHammer.h"
  42. #if defined( _WIN32 ) && !defined( _X360 )
  43. #include <xmmintrin.h>
  44. #endif
  45. #include "staticpropmgr.h"
  46. #include "materialsystem/hardwaretexels.h"
  47. #include "materialsystem/hardwareverts.h"
  48. #include "tier1/callqueue.h"
  49. #include "filesystem/IQueuedLoader.h"
  50. #include "tier2/tier2.h"
  51. #include "tier1/UtlSortVector.h"
  52. #include "tier1/lzmaDecoder.h"
  53. #include "ipooledvballocator.h"
  54. #include "shaderapi/ishaderapi.h"
  55. // memdbgon must be the last include file in a .cpp file!!!
  56. #include "tier0/memdbgon.h"
  57. // #define VISUALIZE_TIME_AVERAGE 1
  58. extern ConVar r_flashlight_version2;
  59. //-----------------------------------------------------------------------------
  60. // Forward declarations
  61. //-----------------------------------------------------------------------------
  62. void R_StudioInitLightingCache( void );
  63. float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta, bool bNoRadiusCheck = false );
  64. void SetRootLOD_f( IConVar *var, const char *pOldString, float flOldValue );
  65. void r_lod_f( IConVar *var, const char *pOldValue, float flOldValue );
  66. void FlushLOD_f();
  67. class CColorMeshData;
  68. static void CreateLightmapsFromData(CColorMeshData* _colorMeshData);
  69. //-----------------------------------------------------------------------------
  70. // Global variables
  71. //-----------------------------------------------------------------------------
  72. ConVar r_drawmodelstatsoverlay( "r_drawmodelstatsoverlay", "0", FCVAR_CHEAT );
  73. ConVar r_drawmodelstatsoverlaydistance( "r_drawmodelstatsoverlaydistance", "500", FCVAR_CHEAT );
  74. ConVar r_drawmodellightorigin( "r_DrawModelLightOrigin", "0", FCVAR_CHEAT );
  75. extern ConVar r_worldlights;
  76. ConVar r_lod( "r_lod", "-1", 0, "", r_lod_f );
  77. static ConVar r_entity( "r_entity", "-1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  78. static ConVar r_lightaverage( "r_lightaverage", "1", 0, "Activates/deactivate light averaging" );
  79. static ConVar r_lightinterp( "r_lightinterp", "5", FCVAR_CHEAT, "Controls the speed of light interpolation, 0 turns off interpolation" );
  80. static ConVar r_eyeglintlodpixels( "r_eyeglintlodpixels", "20.0", FCVAR_CHEAT, "The number of pixels wide an eyeball has to be before rendering an eyeglint. Is a floating point value." );
  81. ConVar r_rootlod( "r_rootlod", "0", FCVAR_MATERIAL_SYSTEM_THREAD | FCVAR_ARCHIVE, "Root LOD", true, 0, true, MAX_NUM_LODS-1, SetRootLOD_f );
  82. static ConVar r_decalstaticprops( "r_decalstaticprops", "1", 0, "Decal static props test" );
  83. static ConCommand r_flushlod( "r_flushlod", FlushLOD_f, "Flush and reload LODs." );
  84. ConVar r_debugrandomstaticlighting( "r_debugrandomstaticlighting", "0", FCVAR_CHEAT, "Set to 1 to randomize static lighting for debugging. Must restart for change to take affect." );
  85. ConVar r_proplightingfromdisk( "r_proplightingfromdisk", "1", FCVAR_CHEAT, "0=Off, 1=On, 2=Show Errors" );
  86. static ConVar r_itemblinkmax( "r_itemblinkmax", ".3", FCVAR_CHEAT );
  87. static ConVar r_itemblinkrate( "r_itemblinkrate", "4.5", FCVAR_CHEAT );
  88. static ConVar r_proplightingpooling( "r_proplightingpooling", "-1.0", FCVAR_CHEAT, "0 - off, 1 - static prop color meshes are allocated from a single shared vertex buffer (on hardware that supports stream offset)" );
  89. //-----------------------------------------------------------------------------
  90. // StudioRender config
  91. //-----------------------------------------------------------------------------
  92. static ConVar r_showenvcubemap( "r_showenvcubemap", "0", FCVAR_CHEAT );
  93. static ConVar r_eyemove ( "r_eyemove", "1", FCVAR_ARCHIVE ); // look around
  94. static ConVar r_eyeshift_x ( "r_eyeshift_x", "0", FCVAR_ARCHIVE ); // eye X position
  95. static ConVar r_eyeshift_y ( "r_eyeshift_y", "0", FCVAR_ARCHIVE ); // eye Y position
  96. static ConVar r_eyeshift_z ( "r_eyeshift_z", "0", FCVAR_ARCHIVE ); // eye Z position
  97. static ConVar r_eyesize ( "r_eyesize", "0", FCVAR_ARCHIVE ); // adjustment to iris textures
  98. static ConVar mat_softwareskin( "mat_softwareskin", "0", FCVAR_CHEAT );
  99. static ConVar r_nohw ( "r_nohw", "0", FCVAR_CHEAT );
  100. static ConVar r_nosw ( "r_nosw", "0", FCVAR_CHEAT );
  101. static ConVar r_teeth ( "r_teeth", "1" );
  102. static ConVar r_drawentities ( "r_drawentities", "1", FCVAR_CHEAT );
  103. static ConVar r_flex ( "r_flex", "1" );
  104. static ConVar r_eyes ( "r_eyes", "1" );
  105. static ConVar r_skin ( "r_skin", "0", FCVAR_CHEAT );
  106. static ConVar r_modelwireframedecal ( "r_modelwireframedecal", "0", FCVAR_CHEAT );
  107. static ConVar r_maxmodeldecal ( "r_maxmodeldecal", "50" );
  108. static StudioRenderConfig_t s_StudioRenderConfig;
  109. void UpdateStudioRenderConfig( void )
  110. {
  111. // This can happen during initialization
  112. if ( !g_pMaterialSystemConfig || !g_pStudioRender )
  113. return;
  114. memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) );
  115. s_StudioRenderConfig.bEyeMove = !!r_eyemove.GetInt();
  116. s_StudioRenderConfig.fEyeShiftX = r_eyeshift_x.GetFloat();
  117. s_StudioRenderConfig.fEyeShiftY = r_eyeshift_y.GetFloat();
  118. s_StudioRenderConfig.fEyeShiftZ = r_eyeshift_z.GetFloat();
  119. s_StudioRenderConfig.fEyeSize = r_eyesize.GetFloat();
  120. if ( IsPC() && ( mat_softwareskin.GetInt() || ShouldDrawInWireFrameMode() ) )
  121. {
  122. s_StudioRenderConfig.bSoftwareSkin = true;
  123. }
  124. else
  125. {
  126. s_StudioRenderConfig.bSoftwareSkin = false;
  127. }
  128. s_StudioRenderConfig.bNoHardware = !!r_nohw.GetInt();
  129. s_StudioRenderConfig.bNoSoftware = !!r_nosw.GetInt();
  130. s_StudioRenderConfig.bTeeth = !!r_teeth.GetInt();
  131. s_StudioRenderConfig.drawEntities = r_drawentities.GetInt();
  132. s_StudioRenderConfig.bFlex = !!r_flex.GetInt();
  133. s_StudioRenderConfig.bEyes = !!r_eyes.GetInt();
  134. s_StudioRenderConfig.bWireframe = ShouldDrawInWireFrameMode();
  135. s_StudioRenderConfig.bDrawNormals = mat_normals.GetBool();
  136. s_StudioRenderConfig.skin = r_skin.GetInt();
  137. s_StudioRenderConfig.maxDecalsPerModel = r_maxmodeldecal.GetInt();
  138. s_StudioRenderConfig.bWireframeDecals = r_modelwireframedecal.GetInt() != 0;
  139. s_StudioRenderConfig.fullbright = g_pMaterialSystemConfig->nFullbright;
  140. s_StudioRenderConfig.bSoftwareLighting = g_pMaterialSystemConfig->bSoftwareLighting;
  141. s_StudioRenderConfig.bShowEnvCubemapOnly = r_showenvcubemap.GetInt() ? true : false;
  142. s_StudioRenderConfig.fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels.GetFloat();
  143. g_pStudioRender->UpdateConfig( s_StudioRenderConfig );
  144. }
  145. void R_InitStudio( void )
  146. {
  147. #ifndef SWDS
  148. R_StudioInitLightingCache();
  149. #endif
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Converts world lights to materialsystem lights
  153. //-----------------------------------------------------------------------------
  154. #define MIN_LIGHT_VALUE 0.03f
  155. bool WorldLightToMaterialLight( dworldlight_t* pWorldLight, LightDesc_t& light )
  156. {
  157. // BAD
  158. light.m_Attenuation0 = 0.0f;
  159. light.m_Attenuation1 = 0.0f;
  160. light.m_Attenuation2 = 0.0f;
  161. switch(pWorldLight->type)
  162. {
  163. case emit_spotlight:
  164. light.m_Type = MATERIAL_LIGHT_SPOT;
  165. light.m_Attenuation0 = pWorldLight->constant_attn;
  166. light.m_Attenuation1 = pWorldLight->linear_attn;
  167. light.m_Attenuation2 = pWorldLight->quadratic_attn;
  168. light.m_Theta = 2.0 * acos( pWorldLight->stopdot );
  169. light.m_Phi = 2.0 * acos( pWorldLight->stopdot2 );
  170. light.m_ThetaDot = pWorldLight->stopdot;
  171. light.m_PhiDot = pWorldLight->stopdot2;
  172. light.m_Falloff = pWorldLight->exponent ? pWorldLight->exponent : 1.0f;
  173. break;
  174. case emit_surface:
  175. // A 180 degree spotlight
  176. light.m_Type = MATERIAL_LIGHT_SPOT;
  177. light.m_Attenuation2 = 1.0;
  178. light.m_Theta = M_PI;
  179. light.m_Phi = M_PI;
  180. light.m_ThetaDot = 0.0f;
  181. light.m_PhiDot = 0.0f;
  182. light.m_Falloff = 1.0f;
  183. break;
  184. case emit_point:
  185. light.m_Type = MATERIAL_LIGHT_POINT;
  186. light.m_Attenuation0 = pWorldLight->constant_attn;
  187. light.m_Attenuation1 = pWorldLight->linear_attn;
  188. light.m_Attenuation2 = pWorldLight->quadratic_attn;
  189. break;
  190. case emit_skylight:
  191. light.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
  192. break;
  193. // NOTE: Can't do quake lights in hardware (x-r factor)
  194. case emit_quakelight: // not supported
  195. case emit_skyambient: // doesn't factor into local lighting
  196. // skip these
  197. return false;
  198. }
  199. // No attenuation case..
  200. if ((light.m_Attenuation0 == 0.0f) && (light.m_Attenuation1 == 0.0f) &&
  201. (light.m_Attenuation2 == 0.0f))
  202. {
  203. light.m_Attenuation0 = 1.0f;
  204. }
  205. // renormalize light intensity...
  206. memcpy( &light.m_Position, &pWorldLight->origin, 3 * sizeof(float) );
  207. memcpy( &light.m_Direction, &pWorldLight->normal, 3 * sizeof(float) );
  208. light.m_Color[0] = pWorldLight->intensity[0];
  209. light.m_Color[1] = pWorldLight->intensity[1];
  210. light.m_Color[2] = pWorldLight->intensity[2];
  211. // Make it stop when the lighting gets to min%...
  212. float intensity = sqrtf( DotProduct( light.m_Color, light.m_Color ) );
  213. // Compute the light range based on attenuation factors
  214. if (pWorldLight->radius != 0)
  215. {
  216. light.m_Range = pWorldLight->radius;
  217. }
  218. else
  219. {
  220. // FALLBACK: older lights use this
  221. if (light.m_Attenuation2 == 0.0f)
  222. {
  223. if (light.m_Attenuation1 == 0.0f)
  224. {
  225. light.m_Range = sqrtf(FLT_MAX);
  226. }
  227. else
  228. {
  229. light.m_Range = (intensity / MIN_LIGHT_VALUE - light.m_Attenuation0) / light.m_Attenuation1;
  230. }
  231. }
  232. else
  233. {
  234. float a = light.m_Attenuation2;
  235. float b = light.m_Attenuation1;
  236. float c = light.m_Attenuation0 - intensity / MIN_LIGHT_VALUE;
  237. float discrim = b * b - 4 * a * c;
  238. if (discrim < 0.0f)
  239. light.m_Range = sqrtf(FLT_MAX);
  240. else
  241. {
  242. light.m_Range = (-b + sqrtf(discrim)) / (2.0f * a);
  243. if (light.m_Range < 0)
  244. light.m_Range = 0;
  245. }
  246. }
  247. }
  248. light.m_Flags = LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED;
  249. if( light.m_Attenuation0 != 0.0f )
  250. {
  251. light.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
  252. }
  253. if( light.m_Attenuation1 != 0.0f )
  254. {
  255. light.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
  256. }
  257. if( light.m_Attenuation2 != 0.0f )
  258. {
  259. light.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
  260. }
  261. return true;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Sets the hardware lighting state
  265. //-----------------------------------------------------------------------------
  266. static void R_SetNonAmbientLightingState( int numLights, dworldlight_t *locallight[MAXLOCALLIGHTS],
  267. int *pNumLightDescs, LightDesc_t *pLightDescs, bool bUpdateStudioRenderLights )
  268. {
  269. Assert( numLights >= 0 && numLights <= MAXLOCALLIGHTS );
  270. // convert dworldlight_t's to LightDesc_t's and send 'em down to g_pStudioRender->
  271. *pNumLightDescs = 0;
  272. LightDesc_t *pLightDesc;
  273. for ( int i = 0; i < numLights; i++)
  274. {
  275. pLightDesc = &pLightDescs[*pNumLightDescs];
  276. if (!WorldLightToMaterialLight( locallight[i], *pLightDesc ))
  277. continue;
  278. // Apply lightstyle
  279. float bias = LightStyleValue( locallight[i]->style );
  280. // Deal with overbrighting + bias
  281. pLightDesc->m_Color[0] *= bias;
  282. pLightDesc->m_Color[1] *= bias;
  283. pLightDesc->m_Color[2] *= bias;
  284. *pNumLightDescs += 1;
  285. Assert( *pNumLightDescs <= MAXLOCALLIGHTS );
  286. }
  287. if ( bUpdateStudioRenderLights )
  288. {
  289. g_pStudioRender->SetLocalLights( *pNumLightDescs, pLightDescs );
  290. }
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Computes the center of the studio model for illumination purposes
  294. //-----------------------------------------------------------------------------
  295. void R_ComputeLightingOrigin( IClientRenderable *pRenderable, studiohdr_t* pStudioHdr, const matrix3x4_t &matrix, Vector& center )
  296. {
  297. int nAttachmentIndex = pStudioHdr->IllumPositionAttachmentIndex();
  298. if ( nAttachmentIndex <= 0 )
  299. {
  300. VectorTransform( pStudioHdr->illumposition, matrix, center );
  301. }
  302. else
  303. {
  304. matrix3x4_t attachment;
  305. pRenderable->GetAttachment( nAttachmentIndex, attachment );
  306. VectorTransform( pStudioHdr->illumposition, attachment, center );
  307. }
  308. }
  309. #if 0
  310. // garymct - leave this in here for now. . we might need this for bumped models
  311. void R_StudioCalculateVirtualLightAndLightCube( Vector& mid, Vector& virtualLightPosition,
  312. Vector& virtualLightColor, Vector* lightBoxColor )
  313. {
  314. int i, j;
  315. Vector delta;
  316. float dist2, ratio;
  317. byte *pvis;
  318. float t;
  319. static ConVar bumpLightBlendRatioMin( "bump_light_blend_ratio_min", "0.00002" );
  320. static ConVar bumpLightBlendRatioMax( "bump_light_blend_ratio_max", "0.00004" );
  321. if ( g_pMaterialSystemConfig->nFullbright == 1 )
  322. return;
  323. VectorClear( virtualLightPosition );
  324. VectorClear( virtualLightColor );
  325. for( i = 0; i < 6; i++ )
  326. {
  327. VectorClear( lightBoxColor[i] );
  328. }
  329. byte pvs[MAX_MAP_LEAFS/8];
  330. pvis = CM_Vis( pvs, sizeof(pvs), CM_LeafCluster( CM_PointLeafnum( mid ), DVIS_PVS );
  331. float sumBumpBlendParam = 0;
  332. for (i = 0; i < host_state.worldbrush->numworldlights; i++)
  333. {
  334. dworldlight_t *wl = &host_state.worldbrush->worldlights[i];
  335. if (wl->cluster < 0)
  336. continue;
  337. // only do it if the entity can see into the lights leaf
  338. if (!BIT_SET( pvis, (wl->cluster)))
  339. continue;
  340. // hack: for this test, only deal with point light sources.
  341. if( wl->type != emit_point )
  342. continue;
  343. // check distance
  344. VectorSubtract( wl->origin, mid, delta );
  345. dist2 = DotProduct( delta, delta );
  346. ratio = R_WorldLightDistanceFalloff( wl, delta );
  347. VectorNormalize( delta );
  348. ratio = ratio * R_WorldLightAngle( wl, wl->normal, delta, delta );
  349. float bumpBlendParam; // 0.0 = all cube, 1.0 = all bump
  350. // lerp
  351. bumpBlendParam =
  352. ( ratio - bumpLightBlendRatioMin.GetFloat() ) /
  353. ( bumpLightBlendRatioMax.GetFloat() - bumpLightBlendRatioMin.GetFloat() );
  354. if( bumpBlendParam > 0.0 )
  355. {
  356. // Get the bit that goes into the bump light
  357. sumBumpBlendParam += bumpBlendParam;
  358. VectorMA( virtualLightPosition, bumpBlendParam, wl->origin, virtualLightPosition );
  359. VectorMA( virtualLightColor, bumpBlendParam, wl->intensity, virtualLightColor );
  360. }
  361. if( bumpBlendParam < 1.0f )
  362. {
  363. // Get the bit that goes into the cube
  364. float cubeBlendParam;
  365. cubeBlendParam = 1.0f - bumpBlendParam;
  366. if( cubeBlendParam < 0.0f )
  367. {
  368. cubeBlendParam = 0.0f;
  369. }
  370. for (j = 0; j < numBoxDir; j++)
  371. {
  372. t = DotProduct( r_boxdir[j], delta );
  373. if (t > 0)
  374. {
  375. VectorMA( lightBoxColor[j], ratio * t * cubeBlendParam, wl->intensity, lightBoxColor[j] );
  376. }
  377. }
  378. }
  379. }
  380. // Get the final virtual light position and color.
  381. VectorMultiply( virtualLightPosition, 1.0f / sumBumpBlendParam, virtualLightPosition );
  382. VectorMultiply( virtualLightColor, 1.0f / sumBumpBlendParam, virtualLightColor );
  383. }
  384. #endif
  385. // TODO: move cone calcs to position
  386. // TODO: cone clipping calc's wont work for boxlight since the player asks for a single point. Not sure what the volume is.
  387. float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta, bool bNoRadiusCheck )
  388. {
  389. float falloff;
  390. switch (wl->type)
  391. {
  392. case emit_surface:
  393. #if 1
  394. // Cull out stuff that's too far
  395. if (wl->radius != 0)
  396. {
  397. if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
  398. return 0.0f;
  399. }
  400. return InvRSquared(delta);
  401. #else
  402. // 1/r*r
  403. falloff = DotProduct( delta, delta );
  404. if (falloff < 1)
  405. return 1.f;
  406. else
  407. return 1.f / falloff;
  408. #endif
  409. break;
  410. case emit_skylight:
  411. return 1.f;
  412. break;
  413. case emit_quakelight:
  414. // X - r;
  415. falloff = wl->linear_attn - FastSqrt( DotProduct( delta, delta ) );
  416. if (falloff < 0)
  417. return 0.f;
  418. return falloff;
  419. break;
  420. case emit_skyambient:
  421. return 1.f;
  422. break;
  423. case emit_point:
  424. case emit_spotlight: // directional & positional
  425. {
  426. float dist2, dist;
  427. dist2 = DotProduct( delta, delta );
  428. dist = FastSqrt( dist2 );
  429. // Cull out stuff that's too far
  430. if (!bNoRadiusCheck && (wl->radius != 0) && (dist > wl->radius))
  431. return 0.f;
  432. return 1.f / (wl->constant_attn + wl->linear_attn * dist + wl->quadratic_attn * dist2);
  433. }
  434. break;
  435. default:
  436. // Bug: need to return an error
  437. break;
  438. }
  439. return 1.f;
  440. }
  441. /*
  442. light_normal (lights normal translated to same space as other normals)
  443. surface_normal
  444. light_direction_normal | (light_pos - vertex_pos) |
  445. */
  446. float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
  447. {
  448. float dot, dot2, ratio = 0;
  449. switch (wl->type)
  450. {
  451. case emit_surface:
  452. dot = DotProduct( snormal, delta );
  453. if (dot < 0)
  454. return 0;
  455. dot2 = -DotProduct (delta, lnormal);
  456. if (dot2 <= ON_EPSILON/10)
  457. return 0; // behind light surface
  458. return dot * dot2;
  459. case emit_point:
  460. dot = DotProduct( snormal, delta );
  461. if (dot < 0)
  462. return 0;
  463. return dot;
  464. case emit_spotlight:
  465. // return 1.0; // !!!
  466. dot = DotProduct( snormal, delta );
  467. if (dot < 0)
  468. return 0;
  469. dot2 = -DotProduct (delta, lnormal);
  470. if (dot2 <= wl->stopdot2)
  471. return 0; // outside light cone
  472. ratio = dot;
  473. if (dot2 >= wl->stopdot)
  474. return ratio; // inside inner cone
  475. if ((wl->exponent == 1) || (wl->exponent == 0))
  476. {
  477. ratio *= (dot2 - wl->stopdot2) / (wl->stopdot - wl->stopdot2);
  478. }
  479. else
  480. {
  481. ratio *= pow((dot2 - wl->stopdot2) / (wl->stopdot - wl->stopdot2), wl->exponent );
  482. }
  483. return ratio;
  484. case emit_skylight:
  485. dot2 = -DotProduct( snormal, lnormal );
  486. if (dot2 < 0)
  487. return 0;
  488. return dot2;
  489. case emit_quakelight:
  490. // linear falloff
  491. dot = DotProduct( snormal, delta );
  492. if (dot < 0)
  493. return 0;
  494. return dot;
  495. case emit_skyambient:
  496. // not supported
  497. return 1;
  498. default:
  499. // Bug: need to return an error
  500. break;
  501. }
  502. return 0;
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Allocator for color mesh vertex buffers (for use with static props only).
  506. // It uses a trivial allocation scheme, which assumes that allocations and
  507. // deallocations are not interleaved (you do all allocs, then all deallocs).
  508. //-----------------------------------------------------------------------------
  509. class CPooledVBAllocator_ColorMesh : public IPooledVBAllocator
  510. {
  511. public:
  512. CPooledVBAllocator_ColorMesh();
  513. virtual ~CPooledVBAllocator_ColorMesh();
  514. // Allocate the shared mesh (vertex buffer)
  515. virtual bool Init( VertexFormat_t format, int numVerts );
  516. // Free the shared mesh (after Deallocate is called for all sub-allocs)
  517. virtual void Clear();
  518. // Get the shared mesh (vertex buffer) from which sub-allocations are made
  519. virtual IMesh *GetSharedMesh() { return m_pMesh; }
  520. // Get a pointer to the start of the vertex buffer data
  521. virtual void *GetVertexBufferBase() { return m_pVertexBufferBase; }
  522. virtual int GetNumVertsAllocated() { return m_totalVerts; }
  523. // Allocate a sub-range of 'numVerts' from free space in the shared vertex buffer
  524. // (returns the byte offset from the start of the VB to the new allocation)
  525. virtual int Allocate( int numVerts );
  526. // Deallocate an existing allocation
  527. virtual void Deallocate( int offset, int numVerts );
  528. private:
  529. // Assert/warn that the allocator is in a clear/empty state (returns FALSE if not)
  530. bool CheckIsClear( void );
  531. IMesh *m_pMesh; // The shared mesh (vertex buffer) from which sub-allocations are made
  532. void *m_pVertexBufferBase; // A pointer to the start of the vertex buffer data
  533. int m_totalVerts; // The number of verts in the shared vertex buffer
  534. int m_vertexSize; // The stride of the shared vertex buffer
  535. int m_numAllocations; // The number of extant allocations
  536. int m_numVertsAllocated; // The number of vertices in extant allocations
  537. int m_nextFreeOffset; // The offset to be returned by the next call to Allocate()
  538. // (incremented as a simple stack)
  539. bool m_bStartedDeallocation; // This is set when Deallocate() is called for the first time,
  540. // at which point Allocate() cannot be called again until all
  541. // extant allocations have been deallocated.
  542. };
  543. struct colormeshparams_t
  544. {
  545. int m_nMeshes;
  546. int m_nTotalVertexes;
  547. // Given memory alignment (VBs must be 4-KB aligned on X360, for example), it can be more efficient
  548. // to allocate many color meshes out of a single shared vertex buffer (using vertex 'stream offset')
  549. IPooledVBAllocator *m_pPooledVBAllocator;
  550. int m_nVertexes[256];
  551. FileNameHandle_t m_fnHandle;
  552. };
  553. class CColorMeshData
  554. {
  555. public:
  556. void DestroyResource()
  557. {
  558. g_pFileSystem->AsyncFinish( m_hAsyncControlVertex, true );
  559. g_pFileSystem->AsyncRelease( m_hAsyncControlVertex );
  560. g_pFileSystem->AsyncFinish( m_hAsyncControlTexel, true );
  561. g_pFileSystem->AsyncRelease( m_hAsyncControlTexel );
  562. // release the array of meshes
  563. CMatRenderContextPtr pRenderContext( materials );
  564. for ( int i=0; i<m_nMeshes; i++ )
  565. {
  566. if ( m_pMeshInfos[i].m_pPooledVBAllocator )
  567. {
  568. // Let the pooling allocator dealloc this sub-range of the shared vertex buffer
  569. m_pMeshInfos[i].m_pPooledVBAllocator->Deallocate( m_pMeshInfos[i].m_nVertOffsetInBytes, m_pMeshInfos[i].m_nNumVerts );
  570. }
  571. else
  572. {
  573. // Free this standalone mesh
  574. pRenderContext->DestroyStaticMesh( m_pMeshInfos[i].m_pMesh );
  575. }
  576. if (m_pMeshInfos[i].m_pLightmap)
  577. {
  578. m_pMeshInfos[i].m_pLightmap->Release();
  579. m_pMeshInfos[i].m_pLightmap = NULL;
  580. }
  581. if (m_pMeshInfos[i].m_pLightmapData)
  582. {
  583. delete [] m_pMeshInfos[i].m_pLightmapData->m_pTexelData;
  584. delete m_pMeshInfos[i].m_pLightmapData;
  585. }
  586. }
  587. delete [] m_pMeshInfos;
  588. delete [] m_ppTargets;
  589. delete this;
  590. }
  591. CColorMeshData *GetData()
  592. {
  593. return this;
  594. }
  595. unsigned int Size()
  596. {
  597. // TODO: This is wrong because we don't currently account for the size of the textures we create.
  598. // However, that data isn't available until way after this query is made, so just live with
  599. // this for now I guess?
  600. return m_nTotalSize;
  601. }
  602. static CColorMeshData *CreateResource( const colormeshparams_t &params )
  603. {
  604. CColorMeshData *data = new CColorMeshData;
  605. data->m_bHasInvalidVB = false;
  606. data->m_bColorMeshValid = false;
  607. data->m_bColorTextureValid = false;
  608. data->m_bColorTextureCreated = false;
  609. data->m_bNeedsRetry = false;
  610. data->m_hAsyncControlVertex = NULL;
  611. data->m_hAsyncControlTexel = NULL;
  612. data->m_fnHandle = params.m_fnHandle;
  613. data->m_nTotalSize = params.m_nMeshes * sizeof( IMesh* ) + params.m_nTotalVertexes * 4;
  614. data->m_nMeshes = params.m_nMeshes;
  615. data->m_pMeshInfos = new ColorMeshInfo_t[params.m_nMeshes];
  616. Q_memset( data->m_pMeshInfos, 0, params.m_nMeshes*sizeof( ColorMeshInfo_t ) );
  617. data->m_ppTargets = new unsigned char *[params.m_nMeshes];
  618. CMeshBuilder meshBuilder;
  619. CMatRenderContextPtr pRenderContext( materials );
  620. for ( int i=0; i<params.m_nMeshes; i++ )
  621. {
  622. VertexFormat_t vertexFormat = VERTEX_SPECULAR;
  623. data->m_pMeshInfos[i].m_pMesh = NULL;
  624. data->m_pMeshInfos[i].m_pPooledVBAllocator = params.m_pPooledVBAllocator;
  625. data->m_pMeshInfos[i].m_nVertOffsetInBytes = 0;
  626. data->m_pMeshInfos[i].m_nNumVerts = params.m_nVertexes[i];
  627. data->m_pMeshInfos[i].m_pLightmapData = NULL;
  628. data->m_pMeshInfos[i].m_pLightmap = NULL;
  629. if ( params.m_pPooledVBAllocator != NULL )
  630. {
  631. // Allocate a portion of a single, shared VB for each color mesh
  632. data->m_pMeshInfos[i].m_nVertOffsetInBytes = params.m_pPooledVBAllocator->Allocate( params.m_nVertexes[i] );
  633. if ( data->m_pMeshInfos[i].m_nVertOffsetInBytes == -1 )
  634. {
  635. // Failed (fall back to regular allocations)
  636. data->m_pMeshInfos[i].m_pPooledVBAllocator = NULL;
  637. data->m_pMeshInfos[i].m_nVertOffsetInBytes = 0;
  638. }
  639. else
  640. {
  641. // Set up the mesh+data pointers
  642. data->m_pMeshInfos[i].m_pMesh = params.m_pPooledVBAllocator->GetSharedMesh();
  643. data->m_ppTargets[i] = ( (unsigned char *)params.m_pPooledVBAllocator->GetVertexBufferBase() ) + data->m_pMeshInfos[i].m_nVertOffsetInBytes;
  644. }
  645. }
  646. if ( data->m_pMeshInfos[i].m_pMesh == NULL )
  647. {
  648. if ( g_VBAllocTracker )
  649. g_VBAllocTracker->TrackMeshAllocations( "CColorMeshData::CreateResource" );
  650. // Allocate a standalone VB per color mesh
  651. data->m_pMeshInfos[i].m_pMesh = pRenderContext->CreateStaticMesh( vertexFormat, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_COLOR );
  652. if ( g_VBAllocTracker )
  653. g_VBAllocTracker->TrackMeshAllocations( NULL );
  654. }
  655. Assert( data->m_pMeshInfos[i].m_pMesh );
  656. if ( !data->m_pMeshInfos[i].m_pMesh )
  657. {
  658. data->DestroyResource();
  659. data = NULL;
  660. break;
  661. }
  662. }
  663. return data;
  664. }
  665. static unsigned int EstimatedSize( const colormeshparams_t &params )
  666. {
  667. // each vertex is a 4 byte color
  668. return params.m_nMeshes * sizeof( IMesh* ) + params.m_nTotalVertexes * 4;
  669. }
  670. int m_nMeshes;
  671. ColorMeshInfo_t *m_pMeshInfos;
  672. unsigned char **m_ppTargets;
  673. unsigned int m_nTotalSize;
  674. FSAsyncControl_t m_hAsyncControlVertex;
  675. FSAsyncControl_t m_hAsyncControlTexel;
  676. unsigned int m_bHasInvalidVB : 1;
  677. unsigned int m_bColorMeshValid : 1;
  678. unsigned int m_bColorTextureValid : 1; // Whether the texture data is valid, but not necessarily created
  679. unsigned int m_bColorTextureCreated : 1; // Whether the texture data has actually been created.
  680. unsigned int m_bNeedsRetry : 1;
  681. FileNameHandle_t m_fnHandle;
  682. };
  683. //-----------------------------------------------------------------------------
  684. //
  685. // Implementation of IVModelRender
  686. //
  687. //-----------------------------------------------------------------------------
  688. // UNDONE: Move this to hud export code, subsume previous functions
  689. class CModelRender : public IVModelRender,
  690. public CManagedDataCacheClient< CColorMeshData, colormeshparams_t >
  691. {
  692. public:
  693. // members of the IVModelRender interface
  694. virtual void ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType = OVERRIDE_NORMAL );
  695. virtual int DrawModel(
  696. int flags, IClientRenderable *cliententity,
  697. ModelInstanceHandle_t instance, int entity_index, const model_t *model,
  698. const Vector& origin, QAngle const& angles,
  699. int skin, int body, int hitboxset,
  700. const matrix3x4_t* pModelToWorld,
  701. const matrix3x4_t *pLightingOffset );
  702. virtual void SetViewTarget( const CStudioHdr *pStudioHdr, int nBodyIndex, const Vector& target );
  703. // Creates, destroys instance data to be associated with the model
  704. virtual ModelInstanceHandle_t CreateInstance( IClientRenderable *pRenderable, LightCacheHandle_t* pHandle );
  705. virtual void SetStaticLighting( ModelInstanceHandle_t handle, LightCacheHandle_t* pCache );
  706. virtual LightCacheHandle_t GetStaticLighting( ModelInstanceHandle_t handle );
  707. virtual void DestroyInstance( ModelInstanceHandle_t handle );
  708. virtual bool ChangeInstance( ModelInstanceHandle_t handle, IClientRenderable *pRenderable );
  709. // Creates a decal on a model instance by doing a planar projection
  710. // along the ray. The material is the decal material, the radius is the
  711. // radius of the decal to create.
  712. virtual void AddDecal( ModelInstanceHandle_t handle, Ray_t const& ray,
  713. const Vector& decalUp, int decalIndex, int body, bool noPokethru = false, int maxLODToDecal = ADDDECAL_TO_ALL_LODS );
  714. virtual void AddColoredDecal( ModelInstanceHandle_t handle, Ray_t const& ray,
  715. const Vector& decalUp, int decalIndex, int body, Color cColor, bool noPokethru = false, int maxLODToDecal = ADDDECAL_TO_ALL_LODS );
  716. virtual void GetMaterialOverride( IMaterial** ppOutForcedMaterial, OverrideType_t* pOutOverrideType );
  717. // Removes all the decals on a model instance
  718. virtual void RemoveAllDecals( ModelInstanceHandle_t handle );
  719. // Remove all decals from all models
  720. virtual void RemoveAllDecalsFromAllModels();
  721. // Shadow rendering (render-to-texture)
  722. virtual matrix3x4_t* DrawModelShadowSetup( IClientRenderable *pRenderable, int body, int skin, DrawModelInfo_t *pInfo, matrix3x4_t *pBoneToWorld );
  723. virtual void DrawModelShadow( IClientRenderable *pRenderable, const DrawModelInfo_t &info, matrix3x4_t *pBoneToWorld );
  724. // Used to allow the shadow mgr to manage a list of shadows per model
  725. unsigned short& FirstShadowOnModelInstance( ModelInstanceHandle_t handle ) { return m_ModelInstances[handle].m_FirstShadow; }
  726. // This gets called when overbright, etc gets changed to recompute static prop lighting.
  727. virtual bool RecomputeStaticLighting( ModelInstanceHandle_t handle );
  728. // Handlers for alt-tab
  729. virtual void ReleaseAllStaticPropColorData( void );
  730. virtual void RestoreAllStaticPropColorData( void );
  731. // Extended version of drawmodel
  732. virtual bool DrawModelSetup( ModelRenderInfo_t &pInfo, DrawModelState_t *pState, matrix3x4_t *pBoneToWorld, matrix3x4_t** ppBoneToWorldOut );
  733. virtual int DrawModelEx( ModelRenderInfo_t &pInfo );
  734. virtual int DrawModelExStaticProp( ModelRenderInfo_t &pInfo );
  735. virtual int DrawStaticPropArrayFast( StaticPropRenderInfo_t *pProps, int count, bool bShadowDepth );
  736. // Sets up lighting context for a point in space
  737. virtual void SetupLighting( const Vector &vecCenter );
  738. virtual void SuppressEngineLighting( bool bSuppress );
  739. inline vertexFileHeader_t *CacheVertexData() { return g_pMDLCache->GetVertexData( (MDLHandle_t)(int)m_pStudioHdr->virtualModel&0xffff ); }
  740. bool Init();
  741. void Shutdown();
  742. bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen );
  743. struct staticPropAsyncContext_t
  744. {
  745. DataCacheHandle_t m_ColorMeshHandle;
  746. CColorMeshData *m_pColorMeshData;
  747. int m_nMeshes;
  748. unsigned int m_nRootLOD;
  749. char m_szFilenameVertex[MAX_PATH];
  750. char m_szFilenameTexel[MAX_PATH];
  751. };
  752. void StaticPropColorMeshCallback( void *pContext, const void *pData, int numReadBytes, FSAsyncStatus_t asyncStatus );
  753. void StaticPropColorTexelCallback(void *pContext, const void *pData, int numReadBytes, FSAsyncStatus_t asyncStatus);
  754. // 360 holds onto static prop color meshes during same map transitions
  755. void PurgeCachedStaticPropColorData();
  756. bool IsStaticPropColorDataCached( const char *pName );
  757. DataCacheHandle_t GetCachedStaticPropColorData( const char *pName );
  758. virtual void SetupColorMeshes( int nTotalVerts );
  759. private:
  760. enum
  761. {
  762. CURRENT_LIGHTING_UNINITIALIZED = -999999
  763. };
  764. enum ModelInstanceFlags_t
  765. {
  766. MODEL_INSTANCE_HAS_STATIC_LIGHTING = 0x1,
  767. MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR = 0x2,
  768. MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD = 0x4,
  769. MODEL_INSTANCE_HAS_COLOR_DATA = 0x8
  770. };
  771. struct ModelInstance_t
  772. {
  773. IClientRenderable* m_pRenderable;
  774. // Need to store off the model. When it changes, we lose all instance data..
  775. model_t* m_pModel;
  776. StudioDecalHandle_t m_DecalHandle;
  777. // Stores off the current lighting state
  778. LightingState_t m_CurrentLightingState;
  779. LightingState_t m_AmbientLightingState;
  780. Vector m_flLightIntensity[MAXLOCALLIGHTS];
  781. float m_flLightingTime;
  782. // First shadow projected onto the model
  783. unsigned short m_FirstShadow;
  784. unsigned short m_nFlags;
  785. // Static lighting
  786. LightCacheHandle_t m_LightCacheHandle;
  787. // Color mesh managed by cache
  788. DataCacheHandle_t m_ColorMeshHandle;
  789. };
  790. // Sets up the render state for a model
  791. matrix3x4_t* SetupModelState( IClientRenderable *pRenderable );
  792. int ComputeLOD( const ModelRenderInfo_t &info, studiohwdata_t *pStudioHWData );
  793. void DrawModelExecute( const DrawModelState_t &state, const ModelRenderInfo_t &pInfo, matrix3x4_t *pCustomBoneToWorld = NULL );
  794. void InitColormeshParams( ModelInstance_t &instance, studiohwdata_t *pStudioHWData, colormeshparams_t *pColorMeshParams );
  795. CColorMeshData *FindOrCreateStaticPropColorData( ModelInstanceHandle_t handle );
  796. void DestroyStaticPropColorData( ModelInstanceHandle_t handle );
  797. bool UpdateStaticPropColorData( IHandleEntity *pEnt, ModelInstanceHandle_t handle );
  798. void ProtectColorDataIfQueued( DataCacheHandle_t );
  799. void ValidateStaticPropColorData( ModelInstanceHandle_t handle );
  800. bool LoadStaticPropColorData( IHandleEntity *pProp, DataCacheHandle_t colorMeshHandle, studiohwdata_t *pStudioHWData );
  801. // Returns true if the model instance is valid
  802. bool IsModelInstanceValid( ModelInstanceHandle_t handle );
  803. void DebugDrawLightingOrigin( const DrawModelState_t& state, const ModelRenderInfo_t &pInfo );
  804. LightingState_t *TimeAverageLightingState( ModelInstanceHandle_t handle,
  805. LightingState_t *pLightingState, int nEntIndex, const Vector *pLightingOrigin );
  806. // Cause the current lighting state to match the given one
  807. void SnapCurrentLightingState( ModelInstance_t &inst, LightingState_t *pLightingState );
  808. // Sets up lighting state for rendering
  809. void StudioSetupLighting( const DrawModelState_t &state, const Vector& absEntCenter,
  810. LightCacheHandle_t* pLightcache, bool bVertexLit, bool bNeedsEnvCubemap, bool &bStaticLighting,
  811. DrawModelInfo_t &drawInfo, const ModelRenderInfo_t &pInfo, int drawFlags );
  812. // Time average the ambient term
  813. void TimeAverageAmbientLight( LightingState_t &actualLightingState, ModelInstance_t &inst,
  814. float flAttenFactor, LightingState_t *pLightingState, const Vector *pLightingOrigin );
  815. // Old-style computation of vertex lighting
  816. void ComputeModelVertexLightingOld( mstudiomodel_t *pModel,
  817. matrix3x4_t& matrix, const LightingState_t &lightingState, color24 *pLighting,
  818. bool bUseConstDirLighting, float flConstDirLightAmount );
  819. // New-style computation of vertex lighting
  820. void ComputeModelVertexLighting( IHandleEntity *pProp,
  821. mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD,
  822. matrix3x4_t& matrix, Vector4D *pTempMem, color24 *pLighting );
  823. // Internal Decal
  824. void AddDecalInternal( ModelInstanceHandle_t handle, Ray_t const& ray, const Vector& decalUp, int decalIndex, int body, bool bUseColor, Color cColor, bool noPokeThru, int maxLODToDecal);
  825. // Model instance data
  826. CUtlLinkedList< ModelInstance_t, ModelInstanceHandle_t > m_ModelInstances;
  827. // current active model
  828. studiohdr_t *m_pStudioHdr;
  829. bool m_bSuppressEngineLighting;
  830. CUtlDict< DataCacheHandle_t, int > m_CachedStaticPropColorData;
  831. CThreadFastMutex m_CachedStaticPropMutex;
  832. // Allocator for static prop color mesh vertex buffers (all are pooled into one VB)
  833. CPooledVBAllocator_ColorMesh m_colorMeshVBAllocator;
  834. };
  835. static CModelRender s_ModelRender;
  836. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CModelRender, IVModelRender, VENGINE_HUDMODEL_INTERFACE_VERSION, s_ModelRender );
  837. IVModelRender* modelrender = &s_ModelRender;
  838. //-----------------------------------------------------------------------------
  839. // Resource loading for static prop lighting
  840. //-----------------------------------------------------------------------------
  841. class CResourcePreloadPropLighting : public CResourcePreload
  842. {
  843. virtual bool CreateResource( const char *pName )
  844. {
  845. if ( !r_proplightingfromdisk.GetBool() )
  846. {
  847. // do nothing, not an error
  848. return true;
  849. }
  850. char szBasename[MAX_PATH];
  851. char szFilename[MAX_PATH];
  852. V_FileBase( pName, szBasename, sizeof( szBasename ) );
  853. V_snprintf( szFilename, sizeof( szFilename ), "%s%s.vhv", szBasename, GetPlatformExt() );
  854. // static props have the same name across maps
  855. // can check if loading the same map and early out if data present
  856. if ( g_pQueuedLoader->IsSameMapLoading() && s_ModelRender.IsStaticPropColorDataCached( szFilename ) )
  857. {
  858. // same map is loading, all disk prop lighting was left in the cache
  859. // otherwise the pre-purge operation below will do the cleanup
  860. return true;
  861. }
  862. // create an anonymous job to get the lighting data in memory, claim during static prop instancing
  863. LoaderJob_t loaderJob;
  864. loaderJob.m_pFilename = szFilename;
  865. loaderJob.m_pPathID = "GAME";
  866. loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  867. g_pQueuedLoader->AddJob( &loaderJob );
  868. return true;
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Pre purge operation before i/o commences
  872. //-----------------------------------------------------------------------------
  873. virtual void PurgeUnreferencedResources()
  874. {
  875. if ( g_pQueuedLoader->IsSameMapLoading() )
  876. {
  877. // do nothing, same map is loading, correct disk prop lighting will still be in data cache
  878. return;
  879. }
  880. // Map is different, need to purge any existing disk prop lighting
  881. // before anonymous i/o commences, otherwise 2x memory usage
  882. s_ModelRender.PurgeCachedStaticPropColorData();
  883. }
  884. virtual void PurgeAll()
  885. {
  886. s_ModelRender.PurgeCachedStaticPropColorData();
  887. }
  888. };
  889. static CResourcePreloadPropLighting s_ResourcePreloadPropLighting;
  890. //-----------------------------------------------------------------------------
  891. // Init, shutdown studiorender
  892. //-----------------------------------------------------------------------------
  893. void InitStudioRender( void )
  894. {
  895. UpdateStudioRenderConfig();
  896. s_ModelRender.Init();
  897. }
  898. void ShutdownStudioRender( void )
  899. {
  900. s_ModelRender.Shutdown();
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Hook needed for shadows to work
  904. //-----------------------------------------------------------------------------
  905. unsigned short& FirstShadowOnModelInstance( ModelInstanceHandle_t handle )
  906. {
  907. return s_ModelRender.FirstShadowOnModelInstance( handle );
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Purpose:
  911. //-----------------------------------------------------------------------------
  912. void R_RemoveAllDecalsFromAllModels()
  913. {
  914. s_ModelRender.RemoveAllDecalsFromAllModels();
  915. }
  916. //-----------------------------------------------------------------------------
  917. //
  918. //-----------------------------------------------------------------------------
  919. bool CModelRender::Init()
  920. {
  921. // start a managed section in the cache
  922. CCacheClientBaseClass::Init( g_pDataCache, "ColorMesh" );
  923. if ( IsX360() )
  924. {
  925. g_pQueuedLoader->InstallLoader( RESOURCEPRELOAD_STATICPROPLIGHTING, &s_ResourcePreloadPropLighting );
  926. }
  927. return true;
  928. }
  929. //-----------------------------------------------------------------------------
  930. //
  931. //-----------------------------------------------------------------------------
  932. void CModelRender::Shutdown()
  933. {
  934. // end the managed section
  935. CCacheClientBaseClass::Shutdown();
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Used by the client to allow it to set lighting state instead of this code
  939. //-----------------------------------------------------------------------------
  940. void CModelRender::SuppressEngineLighting( bool bSuppress )
  941. {
  942. m_bSuppressEngineLighting = bSuppress;
  943. }
  944. //-----------------------------------------------------------------------------
  945. //
  946. //-----------------------------------------------------------------------------
  947. bool CModelRender::GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen )
  948. {
  949. CColorMeshData *pColorMeshData = (CColorMeshData *)pItem;
  950. g_pFileSystem->String( pColorMeshData->m_fnHandle, pDest, nMaxLen );
  951. return true;
  952. }
  953. //-----------------------------------------------------------------------------
  954. // Cause the current lighting state to match the given one
  955. //-----------------------------------------------------------------------------
  956. void CModelRender::SnapCurrentLightingState( ModelInstance_t &inst, LightingState_t *pLightingState )
  957. {
  958. inst.m_CurrentLightingState = *pLightingState;
  959. for ( int i = 0; i < MAXLOCALLIGHTS; ++i )
  960. {
  961. if ( i < pLightingState->numlights )
  962. {
  963. inst.m_flLightIntensity[i] = pLightingState->locallight[i]->intensity;
  964. }
  965. else
  966. {
  967. inst.m_flLightIntensity[i].Init( 0.0f, 0.0f, 0.0f );
  968. }
  969. }
  970. #ifndef SWDS
  971. inst.m_flLightingTime = cl.GetTime();
  972. #endif
  973. }
  974. #define AMBIENT_MAX 8.0f
  975. //-----------------------------------------------------------------------------
  976. // Time average the ambient term
  977. //-----------------------------------------------------------------------------
  978. void CModelRender::TimeAverageAmbientLight( LightingState_t &actualLightingState,
  979. ModelInstance_t &inst, float flAttenFactor, LightingState_t *pLightingState, const Vector *pLightingOrigin )
  980. {
  981. flAttenFactor = clamp( flAttenFactor, 0.f, 1.f ); // don't need this but alex is a coward
  982. Vector vecDelta;
  983. for ( int i = 0; i < 6; ++i )
  984. {
  985. VectorSubtract( pLightingState->r_boxcolor[i], inst.m_CurrentLightingState.r_boxcolor[i], vecDelta );
  986. vecDelta *= flAttenFactor;
  987. inst.m_CurrentLightingState.r_boxcolor[i] = pLightingState->r_boxcolor[i] - vecDelta;
  988. #if defined( VISUALIZE_TIME_AVERAGE ) && !defined( SWDS )
  989. if ( pLightingOrigin )
  990. {
  991. Vector vecDir = vec3_origin;
  992. vecDir[ i >> 1 ] = (i & 0x1) ? -1.0f : 1.0f;
  993. CDebugOverlay::AddLineOverlay( *pLightingOrigin, *pLightingOrigin + vecDir * 20,
  994. 255 * inst.m_CurrentLightingState.r_boxcolor[i].x,
  995. 255 * inst.m_CurrentLightingState.r_boxcolor[i].y,
  996. 255 * inst.m_CurrentLightingState.r_boxcolor[i].z, 255, false, 5.0f );
  997. CDebugOverlay::AddLineOverlay( *pLightingOrigin + Vector(5, 5, 5), *pLightingOrigin + vecDir * 50,
  998. 255 * pLightingState->r_boxcolor[i].x,
  999. 255 * pLightingState->r_boxcolor[i].y,
  1000. 255 * pLightingState->r_boxcolor[i].z, 255, true, 5.0f );
  1001. }
  1002. #endif
  1003. // haven't been able to find this rare bug which results in ambient light getting "stuck"
  1004. // on the viewmodel extremely rarely , presumably with infinities. So, mask the bug
  1005. // (hopefully) and warn by clamping.
  1006. #ifndef NDEBUG
  1007. Assert( inst.m_CurrentLightingState.r_boxcolor[i].IsValid() );
  1008. for( int nComp = 0 ; nComp < 3; nComp++ )
  1009. {
  1010. Assert( inst.m_CurrentLightingState.r_boxcolor[i][nComp] >= 0.0 );
  1011. Assert( inst.m_CurrentLightingState.r_boxcolor[i][nComp] <= AMBIENT_MAX );
  1012. }
  1013. #endif
  1014. inst.m_CurrentLightingState.r_boxcolor[i].x = clamp( inst.m_CurrentLightingState.r_boxcolor[i].x, 0.f, AMBIENT_MAX );
  1015. inst.m_CurrentLightingState.r_boxcolor[i].y = clamp( inst.m_CurrentLightingState.r_boxcolor[i].y, 0.f, AMBIENT_MAX );
  1016. inst.m_CurrentLightingState.r_boxcolor[i].z = clamp( inst.m_CurrentLightingState.r_boxcolor[i].z, 0.f, AMBIENT_MAX );
  1017. }
  1018. memcpy( &actualLightingState.r_boxcolor, &inst.m_CurrentLightingState.r_boxcolor, sizeof(inst.m_CurrentLightingState.r_boxcolor) );
  1019. }
  1020. //-----------------------------------------------------------------------------
  1021. // Do time averaging of the lighting state to avoid popping...
  1022. //-----------------------------------------------------------------------------
  1023. LightingState_t *CModelRender::TimeAverageLightingState( ModelInstanceHandle_t handle, LightingState_t *pLightingState, int nEntIndex, const Vector *pLightingOrigin )
  1024. {
  1025. if ( r_lightaverage.GetInt() == 0 )
  1026. return pLightingState;
  1027. #ifndef SWDS
  1028. float flInterpFactor = r_lightinterp.GetFloat();
  1029. if ( flInterpFactor == 0 )
  1030. return pLightingState;
  1031. if ( handle == MODEL_INSTANCE_INVALID)
  1032. return pLightingState;
  1033. ModelInstance_t &inst = m_ModelInstances[handle];
  1034. if ( inst.m_flLightingTime == CURRENT_LIGHTING_UNINITIALIZED )
  1035. {
  1036. SnapCurrentLightingState( inst, pLightingState );
  1037. return pLightingState;
  1038. }
  1039. float dt = (cl.GetTime() - inst.m_flLightingTime);
  1040. if ( dt <= 0.0f )
  1041. {
  1042. dt = 0.0f;
  1043. }
  1044. else
  1045. {
  1046. inst.m_flLightingTime = cl.GetTime();
  1047. }
  1048. static LightingState_t actualLightingState;
  1049. static dworldlight_t s_WorldLights[MAXLOCALLIGHTS];
  1050. // I'm creating the equation v = vf - (vf-vi)e^-at
  1051. // where vf = this frame's lighting value, vi = current time averaged lighting value
  1052. int i;
  1053. Vector vecDelta;
  1054. float flAttenFactor = exp( -flInterpFactor * dt );
  1055. TimeAverageAmbientLight( actualLightingState, inst, flAttenFactor, pLightingState, pLightingOrigin );
  1056. // Max # of lights...
  1057. int nWorldLights;
  1058. if ( !g_pMaterialSystemConfig->bSoftwareLighting )
  1059. {
  1060. nWorldLights = min( g_pMaterialSystemHardwareConfig->MaxNumLights(), r_worldlights.GetInt() );
  1061. }
  1062. else
  1063. {
  1064. nWorldLights = r_worldlights.GetInt();
  1065. }
  1066. // Create a mapping of identical lights
  1067. int nMatchCount = 0;
  1068. bool pMatch[MAXLOCALLIGHTS];
  1069. Vector pLight[MAXLOCALLIGHTS];
  1070. dworldlight_t *pSourceLight[MAXLOCALLIGHTS];
  1071. memset( pMatch, 0, sizeof(pMatch) );
  1072. for ( i = 0; i < pLightingState->numlights; ++i )
  1073. {
  1074. // By default, assume the light doesn't match an existing light, so blend up from 0
  1075. pLight[i].Init( 0.0f, 0.0f, 0.0f );
  1076. int j;
  1077. for ( j = 0; j < inst.m_CurrentLightingState.numlights; ++j )
  1078. {
  1079. if ( pLightingState->locallight[i] == inst.m_CurrentLightingState.locallight[j] )
  1080. {
  1081. // Ok, we found a matching light, so use the intensity of that light at the moment
  1082. ++nMatchCount;
  1083. pMatch[j] = true;
  1084. pLight[i] = inst.m_flLightIntensity[j];
  1085. break;
  1086. }
  1087. }
  1088. }
  1089. // For the lights in the current lighting state, attenuate them toward their actual value
  1090. for ( i = 0; i < pLightingState->numlights; ++i )
  1091. {
  1092. actualLightingState.locallight[i] = &s_WorldLights[i];
  1093. memcpy( &s_WorldLights[i], pLightingState->locallight[i], sizeof(dworldlight_t) );
  1094. // Light already exists? Attenuate to it...
  1095. VectorSubtract( pLightingState->locallight[i]->intensity, pLight[i], vecDelta );
  1096. vecDelta *= flAttenFactor;
  1097. s_WorldLights[i].intensity = pLightingState->locallight[i]->intensity - vecDelta;
  1098. pSourceLight[i] = pLightingState->locallight[i];
  1099. }
  1100. // Ramp down any light we can; we may not be able to ramp them all down
  1101. int nCurrLight = pLightingState->numlights;
  1102. for ( i = 0; i < inst.m_CurrentLightingState.numlights; ++i )
  1103. {
  1104. if ( pMatch[i] )
  1105. continue;
  1106. // Has it faded out to black? Then remove it.
  1107. if ( inst.m_flLightIntensity[i].LengthSqr() < 1 )
  1108. continue;
  1109. if ( nCurrLight >= MAXLOCALLIGHTS )
  1110. break;
  1111. actualLightingState.locallight[nCurrLight] = &s_WorldLights[nCurrLight];
  1112. memcpy( &s_WorldLights[nCurrLight], inst.m_CurrentLightingState.locallight[i], sizeof(dworldlight_t) );
  1113. // Attenuate to black (fade out)
  1114. VectorMultiply( inst.m_flLightIntensity[i], flAttenFactor, vecDelta );
  1115. s_WorldLights[nCurrLight].intensity = vecDelta;
  1116. pSourceLight[nCurrLight] = inst.m_CurrentLightingState.locallight[i];
  1117. if (( nCurrLight >= nWorldLights ) && pLightingOrigin)
  1118. {
  1119. AddWorldLightToAmbientCube( &s_WorldLights[nCurrLight], *pLightingOrigin, actualLightingState.r_boxcolor );
  1120. }
  1121. ++nCurrLight;
  1122. }
  1123. actualLightingState.numlights = min( nCurrLight, nWorldLights );
  1124. inst.m_CurrentLightingState.numlights = nCurrLight;
  1125. for ( i = 0; i < nCurrLight; ++i )
  1126. {
  1127. inst.m_CurrentLightingState.locallight[i] = pSourceLight[i];
  1128. inst.m_flLightIntensity[i] = s_WorldLights[i].intensity;
  1129. #if defined( VISUALIZE_TIME_AVERAGE ) && !defined( SWDS )
  1130. Vector vecColor = pSourceLight[i]->intensity;
  1131. float flMax = max( vecColor.x, vecColor.y );
  1132. flMax = max( flMax, vecColor.z );
  1133. if ( flMax == 0.0f )
  1134. {
  1135. flMax = 1.0f;
  1136. }
  1137. vecColor *= 255.0f / flMax;
  1138. float flRatio = inst.m_flLightIntensity[i].Length() / pSourceLight[i]->intensity.Length();
  1139. vecColor *= flRatio;
  1140. CDebugOverlay::AddLineOverlay( *pLightingOrigin, pSourceLight[i]->origin,
  1141. vecColor.x, vecColor.y, vecColor.z, 255, false, 5.0f );
  1142. #endif
  1143. }
  1144. return &actualLightingState;
  1145. #else
  1146. return pLightingState;
  1147. #endif
  1148. }
  1149. // Ambient boost settings
  1150. static ConVar r_ambientboost( "r_ambientboost", "1", FCVAR_ARCHIVE, "Set to boost ambient term if it is totally swamped by local lights" );
  1151. static ConVar r_ambientmin( "r_ambientmin", "0.3", FCVAR_ARCHIVE, "Threshold above which ambient cube will not boost (i.e. it's already sufficiently bright" );
  1152. static ConVar r_ambientfraction( "r_ambientfraction", "0.1", FCVAR_CHEAT, "Fraction of direct lighting that ambient cube must be below to trigger boosting" );
  1153. static ConVar r_ambientfactor( "r_ambientfactor", "5", FCVAR_ARCHIVE, "Boost ambient cube by no more than this factor" );
  1154. static ConVar r_lightcachemodel ( "r_lightcachemodel", "-1", FCVAR_CHEAT, "" );
  1155. static ConVar r_drawlightcache ("r_drawlightcache", "0", FCVAR_CHEAT, "0: off\n1: draw light cache entries\n2: draw rays\n");
  1156. //-----------------------------------------------------------------------------
  1157. // Sets up lighting state for rendering
  1158. //-----------------------------------------------------------------------------
  1159. void CModelRender::StudioSetupLighting( const DrawModelState_t &state, const Vector& absEntCenter,
  1160. LightCacheHandle_t* pLightcache, bool bVertexLit, bool bNeedsEnvCubemap, bool &bStaticLighting,
  1161. DrawModelInfo_t &drawInfo, const ModelRenderInfo_t &pInfo, int drawFlags )
  1162. {
  1163. if ( m_bSuppressEngineLighting )
  1164. return;
  1165. #ifndef SWDS
  1166. ITexture *pEnvCubemapTexture = NULL;
  1167. LightingState_t lightingState;
  1168. Vector pSaveLightPos[MAXLOCALLIGHTS];
  1169. Vector *pDebugLightingOrigin = NULL;
  1170. Vector vecDebugLightingOrigin = vec3_origin;
  1171. // Cache off lighting data for rendering decals - only on dx8/dx9.
  1172. LightingState_t lightingDecalState;
  1173. drawInfo.m_bStaticLighting = bStaticLighting && g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting();
  1174. drawInfo.m_nLocalLightCount = 0;
  1175. // Compute lighting origin from input
  1176. Vector vLightingOrigin( 0.0f, 0.0f, 0.0f );
  1177. CMatRenderContextPtr pRenderContext( materials );
  1178. if ( pInfo.pLightingOrigin )
  1179. {
  1180. vLightingOrigin = *pInfo.pLightingOrigin;
  1181. }
  1182. else
  1183. {
  1184. vLightingOrigin = absEntCenter;
  1185. if ( pInfo.pLightingOffset )
  1186. {
  1187. VectorTransform( absEntCenter, *pInfo.pLightingOffset, vLightingOrigin );
  1188. }
  1189. }
  1190. // Set the lighting origin state
  1191. pRenderContext->SetLightingOrigin( vLightingOrigin );
  1192. ModelInstance_t *pModelInst = NULL;
  1193. bool bHasDecals = false;
  1194. if ( pInfo.instance != m_ModelInstances.InvalidIndex() )
  1195. {
  1196. pModelInst = &m_ModelInstances[pInfo.instance];
  1197. if ( pModelInst )
  1198. {
  1199. bHasDecals = ( pModelInst->m_DecalHandle != STUDIORENDER_DECAL_INVALID );
  1200. }
  1201. }
  1202. if ( pLightcache )
  1203. {
  1204. // static prop case.
  1205. if ( bStaticLighting )
  1206. {
  1207. if ( g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting() )
  1208. {
  1209. LightingState_t *pLightingState = NULL;
  1210. // dx8 and dx9 case. . .hardware can do baked lighting plus other dynamic lighting
  1211. // We already have the static part baked into a color mesh, so just get the dynamic stuff.
  1212. if ( StaticLightCacheAffectedByDynamicLight( *pLightcache ) )
  1213. {
  1214. pLightingState = LightcacheGetStatic( *pLightcache, &pEnvCubemapTexture );
  1215. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1216. }
  1217. else
  1218. {
  1219. pLightingState = LightcacheGetStatic( *pLightcache, &pEnvCubemapTexture, LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE );
  1220. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1221. }
  1222. lightingState = *pLightingState;
  1223. }
  1224. else
  1225. {
  1226. // dx6 and dx7 case. . .hardware can either do software lighting or baked lighting only.
  1227. if ( StaticLightCacheAffectedByDynamicLight( *pLightcache ) ||
  1228. StaticLightCacheAffectedByAnimatedLightStyle( *pLightcache ) )
  1229. {
  1230. bStaticLighting = false;
  1231. }
  1232. else if ( StaticLightCacheNeedsSwitchableLightUpdate( *pLightcache ) )
  1233. {
  1234. // Need to rebake lighting since a switch has turned off/on.
  1235. UpdateStaticPropColorData( state.m_pRenderable->GetIClientUnknown(), pInfo.instance );
  1236. }
  1237. }
  1238. }
  1239. if ( !bStaticLighting )
  1240. {
  1241. lightingState = *(LightcacheGetStatic( *pLightcache, &pEnvCubemapTexture ));
  1242. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1243. }
  1244. if ( r_decalstaticprops.GetBool() && pModelInst && drawInfo.m_bStaticLighting && bHasDecals )
  1245. {
  1246. for ( int iCube = 0; iCube < 6; ++iCube )
  1247. {
  1248. drawInfo.m_vecAmbientCube[iCube] = pModelInst->m_AmbientLightingState.r_boxcolor[iCube] + lightingState.r_boxcolor[iCube];
  1249. }
  1250. lightingDecalState.CopyLocalLights( pModelInst->m_AmbientLightingState );
  1251. lightingDecalState.AddAllLocalLights( lightingState );
  1252. Assert( lightingDecalState.numlights >= 0 && lightingDecalState.numlights <= MAXLOCALLIGHTS );
  1253. }
  1254. }
  1255. else // !pLightcache
  1256. {
  1257. vecDebugLightingOrigin = vLightingOrigin;
  1258. pDebugLightingOrigin = &vecDebugLightingOrigin;
  1259. // If we don't have a lightcache entry, but we have bStaticLighting, that means
  1260. // that we are a prop_physics that has fallen asleep.
  1261. if ( bStaticLighting )
  1262. {
  1263. LightcacheGetDynamic_Stats stats;
  1264. pEnvCubemapTexture = LightcacheGetDynamic( vLightingOrigin, lightingState,
  1265. stats, LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE );
  1266. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1267. // Deal with all the dx6/dx7 issues (ie. can't do anything besides either baked lighting
  1268. // or software dynamic lighting.
  1269. if ( !g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting() )
  1270. {
  1271. if ( ( stats.m_bHasDLights || stats.m_bHasNonSwitchableLightStyles ) )
  1272. {
  1273. // We either have a light switch, or a dynamic light. . do it in software.
  1274. // We'll reget the cache entry with different flags below.
  1275. bStaticLighting = false;
  1276. }
  1277. else if ( stats.m_bNeedsSwitchableLightStyleUpdate )
  1278. {
  1279. // Need to rebake lighting since a switch has turned off/on.
  1280. UpdateStaticPropColorData( state.m_pRenderable->GetIClientUnknown(), pInfo.instance );
  1281. }
  1282. }
  1283. }
  1284. if ( !bStaticLighting )
  1285. {
  1286. LightcacheGetDynamic_Stats stats;
  1287. // For special r_drawlightcache mode, we only draw models containing the substring set in r_lightcachemodel
  1288. bool bDebugModel = false;
  1289. if( r_drawlightcache.GetInt() == 5 )
  1290. {
  1291. if ( pModelInst && pModelInst->m_pModel && !pModelInst->m_pModel->strName.IsEmpty() )
  1292. {
  1293. const char *szModelName = r_lightcachemodel.GetString();
  1294. bDebugModel = V_stristr( pModelInst->m_pModel->strName, szModelName ) != NULL;
  1295. }
  1296. }
  1297. pEnvCubemapTexture = LightcacheGetDynamic( vLightingOrigin, lightingState, stats,
  1298. LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST, bDebugModel );
  1299. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1300. }
  1301. if ( pInfo.pLightingOffset && !pInfo.pLightingOrigin )
  1302. {
  1303. for ( int i = 0; i < lightingState.numlights; ++i )
  1304. {
  1305. pSaveLightPos[i] = lightingState.locallight[i]->origin;
  1306. VectorITransform( pSaveLightPos[i], *pInfo.pLightingOffset, lightingState.locallight[i]->origin );
  1307. }
  1308. }
  1309. // Cache lighting for decals.
  1310. if ( pModelInst && drawInfo.m_bStaticLighting && bHasDecals )
  1311. {
  1312. // Only do this on dx8/dx9.
  1313. LightcacheGetDynamic_Stats stats;
  1314. LightcacheGetDynamic( vLightingOrigin, lightingDecalState, stats,
  1315. LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST );
  1316. Assert( lightingDecalState.numlights >= 0 && lightingDecalState.numlights <= MAXLOCALLIGHTS);
  1317. for ( int iCube = 0; iCube < 6; ++iCube )
  1318. {
  1319. VectorCopy( lightingDecalState.r_boxcolor[iCube], drawInfo.m_vecAmbientCube[iCube] );
  1320. }
  1321. if ( pInfo.pLightingOffset && !pInfo.pLightingOrigin )
  1322. {
  1323. for ( int i = 0; i < lightingDecalState.numlights; ++i )
  1324. {
  1325. pSaveLightPos[i] = lightingDecalState.locallight[i]->origin;
  1326. VectorITransform( pSaveLightPos[i], *pInfo.pLightingOffset, lightingDecalState.locallight[i]->origin );
  1327. }
  1328. }
  1329. }
  1330. }
  1331. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1332. // Do time averaging of the lighting state to avoid popping...
  1333. LightingState_t *pState;
  1334. if ( !bStaticLighting && !pLightcache )
  1335. {
  1336. pState = TimeAverageLightingState( pInfo.instance, &lightingState, pInfo.entity_index, pDebugLightingOrigin );
  1337. }
  1338. else
  1339. {
  1340. pState = &lightingState;
  1341. }
  1342. if ( bNeedsEnvCubemap && pEnvCubemapTexture )
  1343. {
  1344. pRenderContext->BindLocalCubemap( pEnvCubemapTexture );
  1345. }
  1346. if ( g_pMaterialSystemConfig->nFullbright == 1 )
  1347. {
  1348. pRenderContext->SetAmbientLight( 1.0, 1.0, 1.0 );
  1349. static Vector white[6] =
  1350. {
  1351. Vector( 1.0, 1.0, 1.0 ),
  1352. Vector( 1.0, 1.0, 1.0 ),
  1353. Vector( 1.0, 1.0, 1.0 ),
  1354. Vector( 1.0, 1.0, 1.0 ),
  1355. Vector( 1.0, 1.0, 1.0 ),
  1356. Vector( 1.0, 1.0, 1.0 ),
  1357. };
  1358. g_pStudioRender->SetAmbientLightColors( white );
  1359. // Disable all the lights..
  1360. pRenderContext->DisableAllLocalLights();
  1361. }
  1362. else if ( bVertexLit )
  1363. {
  1364. if( drawFlags & STUDIORENDER_DRAW_ITEM_BLINK )
  1365. {
  1366. float add = r_itemblinkmax.GetFloat() * ( FastCos( r_itemblinkrate.GetFloat() * Sys_FloatTime() ) + 1.0f );
  1367. Vector additiveColor( add, add, add );
  1368. static Vector temp[6];
  1369. int i;
  1370. for( i = 0; i < 6; i++ )
  1371. {
  1372. temp[i] = pState->r_boxcolor[i] + additiveColor;
  1373. }
  1374. g_pStudioRender->SetAmbientLightColors( temp );
  1375. }
  1376. else
  1377. {
  1378. // If we have any lights and want to do ambient boost on this model
  1379. if ( (pState->numlights > 0) && (pInfo.pModel->flags & MODELFLAG_STUDIOHDR_AMBIENT_BOOST) && r_ambientboost.GetBool() )
  1380. {
  1381. Vector lumCoeff( 0.3f, 0.59f, 0.11f );
  1382. float avgCubeLuminance = 0.0f;
  1383. float minCubeLuminance = FLT_MAX;
  1384. float maxCubeLuminance = 0.0f;
  1385. // Compute average luminance of ambient cube
  1386. for( int i = 0; i < 6; i++ )
  1387. {
  1388. float luminance = DotProduct( pState->r_boxcolor[i], lumCoeff ); // compute luminance
  1389. minCubeLuminance = fpmin(minCubeLuminance, luminance); // min luminance
  1390. maxCubeLuminance = fpmax(maxCubeLuminance, luminance); // max luminance
  1391. avgCubeLuminance += luminance; // accumulate luminance
  1392. }
  1393. avgCubeLuminance /= 6.0f; // average luminance
  1394. // Compute the amount of direct light reaching the center of the model (attenuated by distance)
  1395. float fDirectLight = 0.0f;
  1396. for( int i = 0; i < pState->numlights; i++ )
  1397. {
  1398. Vector vLight = pState->locallight[i]->origin - vLightingOrigin;
  1399. float d2 = DotProduct( vLight, vLight );
  1400. float d = sqrtf( d2 );
  1401. float fAtten = 1.0f;
  1402. float denom = pState->locallight[i]->constant_attn +
  1403. pState->locallight[i]->linear_attn * d +
  1404. pState->locallight[i]->quadratic_attn * d2;
  1405. if ( denom > 0.00001f )
  1406. {
  1407. fAtten = 1.0f / denom;
  1408. }
  1409. Vector vLit = pState->locallight[i]->intensity * fAtten;
  1410. fDirectLight += DotProduct( vLit, lumCoeff );
  1411. }
  1412. // If ambient cube is sufficiently dim in absolute terms and ambient cube is swamped by direct lights
  1413. if ( avgCubeLuminance < r_ambientmin.GetFloat() && (avgCubeLuminance < (fDirectLight * r_ambientfraction.GetFloat())) )
  1414. {
  1415. Vector vFinalAmbientCube[6];
  1416. float fBoostFactor = min( (fDirectLight * r_ambientfraction.GetFloat()) / maxCubeLuminance, 5.f ); // boost no more than a certain factor
  1417. for( int i = 0; i < 6; i++ )
  1418. {
  1419. vFinalAmbientCube[i] = pState->r_boxcolor[i] * fBoostFactor;
  1420. }
  1421. g_pStudioRender->SetAmbientLightColors( vFinalAmbientCube ); // Boost
  1422. }
  1423. else
  1424. {
  1425. g_pStudioRender->SetAmbientLightColors( pState->r_boxcolor ); // No Boost
  1426. }
  1427. }
  1428. else // Don't bother with ambient boost, just use the ambient cube as is
  1429. {
  1430. g_pStudioRender->SetAmbientLightColors( pState->r_boxcolor ); // No Boost
  1431. }
  1432. }
  1433. pRenderContext->SetAmbientLight( 0.0, 0.0, 0.0 );
  1434. R_SetNonAmbientLightingState( pState->numlights, pState->locallight,
  1435. &drawInfo.m_nLocalLightCount, &drawInfo.m_LocalLightDescs[0], true );
  1436. // Cache lighting for decals.
  1437. if( pModelInst && drawInfo.m_bStaticLighting && bHasDecals )
  1438. {
  1439. R_SetNonAmbientLightingState( lightingDecalState.numlights, lightingDecalState.locallight,
  1440. &drawInfo.m_nLocalLightCount, &drawInfo.m_LocalLightDescs[0], false );
  1441. }
  1442. }
  1443. if ( pInfo.pLightingOffset && !pInfo.pLightingOrigin )
  1444. {
  1445. for ( int i = 0; i < lightingState.numlights; ++i )
  1446. {
  1447. lightingState.locallight[i]->origin = pSaveLightPos[i];
  1448. }
  1449. }
  1450. #endif
  1451. }
  1452. //-----------------------------------------------------------------------------
  1453. // Uses this material instead of the one the model was compiled with
  1454. //-----------------------------------------------------------------------------
  1455. // FIXME: a duplicate of what's in CEngineTool::GetLightingConditions
  1456. int GetLightingConditions( const Vector &vecLightingOrigin, Vector *pColors, int nMaxLocalLights, LightDesc_t *pLocalLights, ITexture *&pEnvCubemapTexture )
  1457. {
  1458. #ifndef SWDS
  1459. LightcacheGetDynamic_Stats stats;
  1460. LightingState_t state;
  1461. pEnvCubemapTexture = NULL;
  1462. pEnvCubemapTexture = LightcacheGetDynamic( vecLightingOrigin, state, stats );
  1463. Assert( state.numlights >= 0 && state.numlights <= MAXLOCALLIGHTS );
  1464. memcpy( pColors, state.r_boxcolor, sizeof(state.r_boxcolor) );
  1465. int nLightCount = 0;
  1466. for ( int i = 0; i < state.numlights; ++i )
  1467. {
  1468. LightDesc_t *pLightDesc = &pLocalLights[nLightCount];
  1469. if (!WorldLightToMaterialLight( state.locallight[i], *pLightDesc ))
  1470. continue;
  1471. // Apply lightstyle
  1472. float bias = LightStyleValue( state.locallight[i]->style );
  1473. // Deal with overbrighting + bias
  1474. pLightDesc->m_Color[0] *= bias;
  1475. pLightDesc->m_Color[1] *= bias;
  1476. pLightDesc->m_Color[2] *= bias;
  1477. if ( ++nLightCount >= nMaxLocalLights )
  1478. break;
  1479. }
  1480. return nLightCount;
  1481. #endif
  1482. return 0;
  1483. }
  1484. // FIXME: a duplicate of what's in CCDmeMdlRenderable<T>::SetUpLighting and CDmeEmitter::SetUpLighting
  1485. void CModelRender::SetupLighting( const Vector &vecCenter )
  1486. {
  1487. #ifndef SWDS
  1488. // Set up lighting conditions
  1489. Vector vecAmbient[6];
  1490. Vector4D vecAmbient4D[6];
  1491. LightDesc_t desc[2];
  1492. ITexture *pEnvCubemapTexture = NULL;
  1493. int nLightCount = GetLightingConditions( vecCenter, vecAmbient, 2, desc, pEnvCubemapTexture );
  1494. int nMaxLights = g_pMaterialSystemHardwareConfig->MaxNumLights();
  1495. if( nLightCount > nMaxLights )
  1496. {
  1497. nLightCount = nMaxLights;
  1498. }
  1499. int i;
  1500. for( i = 0; i < 6; i++ )
  1501. {
  1502. VectorCopy( vecAmbient[i], vecAmbient4D[i].AsVector3D() );
  1503. vecAmbient4D[i][3] = 1.0f;
  1504. }
  1505. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1506. pRenderContext->SetAmbientLightCube( vecAmbient4D );
  1507. if ( pEnvCubemapTexture )
  1508. {
  1509. pRenderContext->BindLocalCubemap( pEnvCubemapTexture );
  1510. }
  1511. for( i = 0; i < nLightCount; i++ )
  1512. {
  1513. LightDesc_t *pLight = &desc[i];
  1514. pLight->m_Flags = 0;
  1515. if( pLight->m_Attenuation0 != 0.0f )
  1516. {
  1517. pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
  1518. }
  1519. if( pLight->m_Attenuation1 != 0.0f )
  1520. {
  1521. pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
  1522. }
  1523. if( pLight->m_Attenuation2 != 0.0f )
  1524. {
  1525. pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
  1526. }
  1527. pRenderContext->SetLight( i, desc[i] );
  1528. }
  1529. for( ; i < nMaxLights; i++ )
  1530. {
  1531. LightDesc_t disableDesc;
  1532. disableDesc.m_Type = MATERIAL_LIGHT_DISABLE;
  1533. pRenderContext->SetLight( i, disableDesc );
  1534. }
  1535. #endif
  1536. }
  1537. //-----------------------------------------------------------------------------
  1538. // Uses this material instead of the one the model was compiled with
  1539. //-----------------------------------------------------------------------------
  1540. void CModelRender::ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType )
  1541. {
  1542. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1543. g_pStudioRender->ForcedMaterialOverride( newMaterial, nOverrideType );
  1544. }
  1545. //-----------------------------------------------------------------------------
  1546. // Sets up the render state for a model
  1547. //-----------------------------------------------------------------------------
  1548. matrix3x4_t* CModelRender::SetupModelState( IClientRenderable *pRenderable )
  1549. {
  1550. const model_t *pModel = pRenderable->GetModel();
  1551. if ( !pModel )
  1552. return NULL;
  1553. studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( const_cast<model_t*>(pModel) );
  1554. if ( pStudioHdr->numbodyparts == 0 )
  1555. return NULL;
  1556. matrix3x4_t *pBoneMatrices = NULL;
  1557. #ifndef SWDS
  1558. // Set up skinning state
  1559. Assert ( pRenderable );
  1560. {
  1561. int nBoneCount = pStudioHdr->numbones;
  1562. pBoneMatrices = g_pStudioRender->LockBoneMatrices( pStudioHdr->numbones );
  1563. pRenderable->SetupBones( pBoneMatrices, nBoneCount, BONE_USED_BY_ANYTHING, cl.GetTime() ); // hack hack
  1564. g_pStudioRender->UnlockBoneMatrices();
  1565. }
  1566. #endif
  1567. return pBoneMatrices;
  1568. }
  1569. struct ModelDebugOverlayData_t
  1570. {
  1571. DrawModelInfo_t m_ModelInfo;
  1572. DrawModelResults_t m_ModelResults;
  1573. Vector m_Origin;
  1574. ModelDebugOverlayData_t() {}
  1575. private:
  1576. ModelDebugOverlayData_t( const ModelDebugOverlayData_t &vOther );
  1577. };
  1578. static CUtlVector<ModelDebugOverlayData_t> s_SavedModelInfo;
  1579. void DrawModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_t &results, const Vector &origin, float r = 1.0f, float g = 1.0f, float b = 1.0f )
  1580. {
  1581. #ifndef SWDS
  1582. float alpha = 1;
  1583. if( r_drawmodelstatsoverlaydistance.GetFloat() == 1 )
  1584. {
  1585. alpha = 1.f - clamp( CurrentViewOrigin().DistTo( origin ) / r_drawmodelstatsoverlaydistance.GetFloat(), 0.f, 1.f );
  1586. }
  1587. else
  1588. {
  1589. float flDistance = CurrentViewOrigin().DistTo( origin );
  1590. // The view model keeps throwing up its data and it looks like garbage, so I am trying to get rid of it.
  1591. if ( flDistance < 36.0f )
  1592. return;
  1593. if ( flDistance > r_drawmodelstatsoverlaydistance.GetFloat() )
  1594. return;
  1595. }
  1596. Assert( info.m_pStudioHdr );
  1597. Assert( info.m_pStudioHdr->pszName() );
  1598. Assert( info.m_pHardwareData );
  1599. float duration = 0.0f;
  1600. int lineOffset = 0;
  1601. if( !info.m_pStudioHdr || !info.m_pStudioHdr->pszName() || !info.m_pHardwareData )
  1602. {
  1603. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, "This model has problems. . see a programmer." );
  1604. return;
  1605. }
  1606. char buf[1024];
  1607. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, info.m_pStudioHdr->pszName() );
  1608. Q_snprintf( buf, sizeof( buf ), "lod: %d/%d\n", results.m_nLODUsed+1, ( int )info.m_pHardwareData->m_NumLODs );
  1609. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1610. Q_snprintf( buf, sizeof( buf ), "tris: %d\n", results.m_ActualTriCount );
  1611. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1612. Q_snprintf( buf, sizeof( buf ), "hardware bones: %d\n", results.m_NumHardwareBones );
  1613. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1614. Q_snprintf( buf, sizeof( buf ), "num batches: %d\n", results.m_NumBatches );
  1615. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1616. Q_snprintf( buf, sizeof( buf ), "has shadow lod: %s\n", ( info.m_pStudioHdr->flags & STUDIOHDR_FLAGS_HASSHADOWLOD ) ? "true" : "false" );
  1617. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1618. Q_snprintf( buf, sizeof( buf ), "num materials: %d\n", results.m_NumMaterials );
  1619. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1620. int i;
  1621. for( i = 0; i < results.m_Materials.Count(); i++ )
  1622. {
  1623. IMaterial *pMaterial = results.m_Materials[i];
  1624. if( pMaterial )
  1625. {
  1626. int numPasses = pMaterial->GetNumPasses();
  1627. Q_snprintf( buf, sizeof( buf ), "\t%s (%d %s)\n", results.m_Materials[i]->GetName(), numPasses, numPasses > 1 ? "passes" : "pass" );
  1628. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1629. }
  1630. }
  1631. if( results.m_Materials.Count() > results.m_NumMaterials )
  1632. {
  1633. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, "(Remaining materials not shown)\n" );
  1634. }
  1635. if( r_drawmodelstatsoverlay.GetInt() == 2 )
  1636. {
  1637. Q_snprintf( buf, sizeof( buf ), "Render Time: %0.1f ms\n", results.m_RenderTime.GetDuration().GetMillisecondsF());
  1638. CDebugOverlay::AddTextOverlay( origin, lineOffset++, duration, r, g, b, alpha, buf );
  1639. }
  1640. //Q_snprintf( buf, sizeof( buf ), "Render Time: %0.1f ms\n", info.m_pClientEntity
  1641. #endif
  1642. }
  1643. void AddModelDebugOverlay( const DrawModelInfo_t& info, const DrawModelResults_t &results, const Vector& origin )
  1644. {
  1645. ModelDebugOverlayData_t &tmp = s_SavedModelInfo[s_SavedModelInfo.AddToTail()];
  1646. tmp.m_ModelInfo = info;
  1647. tmp.m_ModelResults = results;
  1648. tmp.m_Origin = origin;
  1649. }
  1650. void ClearSaveModelDebugOverlays( void )
  1651. {
  1652. s_SavedModelInfo.RemoveAll();
  1653. }
  1654. int SavedModelInfo_Compare_f( const void *l, const void *r )
  1655. {
  1656. ModelDebugOverlayData_t *left = ( ModelDebugOverlayData_t * )l;
  1657. ModelDebugOverlayData_t *right = ( ModelDebugOverlayData_t * )r;
  1658. return left->m_ModelResults.m_RenderTime.GetDuration().GetSeconds() < right->m_ModelResults.m_RenderTime.GetDuration().GetSeconds();
  1659. }
  1660. static ConVar r_drawmodelstatsoverlaymin( "r_drawmodelstatsoverlaymin", "0.1", FCVAR_ARCHIVE, "time in milliseconds that a model must take to render before showing an overlay in r_drawmodelstatsoverlay 2" );
  1661. static ConVar r_drawmodelstatsoverlaymax( "r_drawmodelstatsoverlaymax", "1.5", FCVAR_ARCHIVE, "time in milliseconds beyond which a model overlay is fully red in r_drawmodelstatsoverlay 2" );
  1662. void DrawSavedModelDebugOverlays( void )
  1663. {
  1664. if( s_SavedModelInfo.Count() == 0 )
  1665. {
  1666. return;
  1667. }
  1668. float min = r_drawmodelstatsoverlaymin.GetFloat();
  1669. float max = r_drawmodelstatsoverlaymax.GetFloat();
  1670. float ooRange = 1.0f / ( max - min );
  1671. int i;
  1672. for( i = 0; i < s_SavedModelInfo.Count(); i++ )
  1673. {
  1674. float r, g, b;
  1675. float t = s_SavedModelInfo[i].m_ModelResults.m_RenderTime.GetDuration().GetMillisecondsF();
  1676. if( t > min )
  1677. {
  1678. if( t >= max )
  1679. {
  1680. r = 1.0f; g = 0.0f; b = 0.0f;
  1681. }
  1682. else
  1683. {
  1684. r = ( t - min ) * ooRange;
  1685. g = 1.0f - r;
  1686. b = 0.0f;
  1687. }
  1688. DrawModelDebugOverlay( s_SavedModelInfo[i].m_ModelInfo, s_SavedModelInfo[i].m_ModelResults, s_SavedModelInfo[i].m_Origin, r, g, b );
  1689. }
  1690. }
  1691. ClearSaveModelDebugOverlays();
  1692. }
  1693. void CModelRender::DebugDrawLightingOrigin( const DrawModelState_t& state, const ModelRenderInfo_t &pInfo )
  1694. {
  1695. #ifndef SWDS
  1696. // determine light origin in world space
  1697. Vector illumPosition;
  1698. Vector lightOrigin;
  1699. if ( pInfo.pLightingOrigin )
  1700. {
  1701. illumPosition = *pInfo.pLightingOrigin;
  1702. lightOrigin = illumPosition;
  1703. }
  1704. else
  1705. {
  1706. R_ComputeLightingOrigin( state.m_pRenderable, state.m_pStudioHdr, *state.m_pModelToWorld, illumPosition );
  1707. lightOrigin = illumPosition;
  1708. if ( pInfo.pLightingOffset )
  1709. {
  1710. VectorTransform( illumPosition, *pInfo.pLightingOffset, lightOrigin );
  1711. }
  1712. }
  1713. // draw z planar cross at lighting origin
  1714. Vector pt0;
  1715. Vector pt1;
  1716. pt0 = lightOrigin;
  1717. pt1 = lightOrigin;
  1718. pt0.x -= 4;
  1719. pt1.x += 4;
  1720. CDebugOverlay::AddLineOverlay( pt0, pt1, 0, 255, 0, 255, true, 0.0f );
  1721. pt0 = lightOrigin;
  1722. pt1 = lightOrigin;
  1723. pt0.y -= 4;
  1724. pt1.y += 4;
  1725. CDebugOverlay::AddLineOverlay( pt0, pt1, 0, 255, 0, 255, true, 0.0f );
  1726. // draw lines from the light origin to the hull boundaries to identify model
  1727. Vector pt;
  1728. pt0.x = state.m_pStudioHdr->hull_min.x;
  1729. pt0.y = state.m_pStudioHdr->hull_min.y;
  1730. pt0.z = state.m_pStudioHdr->hull_min.z;
  1731. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1732. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1733. pt0.x = state.m_pStudioHdr->hull_min.x;
  1734. pt0.y = state.m_pStudioHdr->hull_max.y;
  1735. pt0.z = state.m_pStudioHdr->hull_min.z;
  1736. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1737. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1738. pt0.x = state.m_pStudioHdr->hull_max.x;
  1739. pt0.y = state.m_pStudioHdr->hull_max.y;
  1740. pt0.z = state.m_pStudioHdr->hull_min.z;
  1741. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1742. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1743. pt0.x = state.m_pStudioHdr->hull_max.x;
  1744. pt0.y = state.m_pStudioHdr->hull_min.y;
  1745. pt0.z = state.m_pStudioHdr->hull_min.z;
  1746. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1747. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1748. pt0.x = state.m_pStudioHdr->hull_min.x;
  1749. pt0.y = state.m_pStudioHdr->hull_min.y;
  1750. pt0.z = state.m_pStudioHdr->hull_max.z;
  1751. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1752. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1753. pt0.x = state.m_pStudioHdr->hull_min.x;
  1754. pt0.y = state.m_pStudioHdr->hull_max.y;
  1755. pt0.z = state.m_pStudioHdr->hull_max.z;
  1756. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1757. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1758. pt0.x = state.m_pStudioHdr->hull_max.x;
  1759. pt0.y = state.m_pStudioHdr->hull_max.y;
  1760. pt0.z = state.m_pStudioHdr->hull_max.z;
  1761. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1762. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1763. pt0.x = state.m_pStudioHdr->hull_max.x;
  1764. pt0.y = state.m_pStudioHdr->hull_min.y;
  1765. pt0.z = state.m_pStudioHdr->hull_max.z;
  1766. VectorTransform( pt0, *state.m_pModelToWorld, pt1 );
  1767. CDebugOverlay::AddLineOverlay( lightOrigin, pt1, 100, 100, 150, 255, true, 0.0f );
  1768. #endif
  1769. }
  1770. //-----------------------------------------------------------------------------
  1771. // Actually renders the model
  1772. //-----------------------------------------------------------------------------
  1773. void CModelRender::DrawModelExecute( const DrawModelState_t &state, const ModelRenderInfo_t &pInfo, matrix3x4_t *pBoneToWorld )
  1774. {
  1775. #ifndef SWDS
  1776. bool bShadowDepth = (pInfo.flags & STUDIO_SHADOWDEPTHTEXTURE) != 0;
  1777. bool bSSAODepth = ( pInfo.flags & STUDIO_SSAODEPTHTEXTURE ) != 0;
  1778. // Bail if we're rendering into shadow depth map and this model doesn't cast shadows
  1779. if ( bShadowDepth && ( ( pInfo.pModel->flags & MODELFLAG_STUDIOHDR_DO_NOT_CAST_SHADOWS ) != 0 ) )
  1780. return;
  1781. // Shadow state...
  1782. g_pShadowMgr->SetModelShadowState( pInfo.instance );
  1783. if ( g_bTextMode )
  1784. return;
  1785. // Sets up flexes
  1786. float *pFlexWeights = NULL;
  1787. float *pFlexDelayedWeights = NULL;
  1788. int nFlexCount = state.m_pStudioHdr->numflexdesc;
  1789. if ( nFlexCount > 0 )
  1790. {
  1791. // Does setup for flexes
  1792. Assert( pBoneToWorld );
  1793. bool bUsesDelayedWeights = state.m_pRenderable->UsesFlexDelayedWeights();
  1794. g_pStudioRender->LockFlexWeights( nFlexCount, &pFlexWeights, bUsesDelayedWeights ? &pFlexDelayedWeights : NULL );
  1795. state.m_pRenderable->SetupWeights( pBoneToWorld, nFlexCount, pFlexWeights, pFlexDelayedWeights );
  1796. g_pStudioRender->UnlockFlexWeights();
  1797. }
  1798. // OPTIMIZE: Try to precompute part of this mess once a frame at the very least.
  1799. bool bUsesBumpmapping = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 ) && ( pInfo.pModel->flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING );
  1800. bool bStaticLighting = ( state.m_drawFlags & STUDIORENDER_DRAW_STATIC_LIGHTING ) &&
  1801. ( state.m_pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) &&
  1802. ( !bUsesBumpmapping ) &&
  1803. ( pInfo.instance != MODEL_INSTANCE_INVALID ) &&
  1804. g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream();
  1805. bool bVertexLit = ( pInfo.pModel->flags & MODELFLAG_VERTEXLIT ) != 0;
  1806. bool bNeedsEnvCubemap = r_showenvcubemap.GetInt() || ( pInfo.pModel->flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP );
  1807. if ( r_drawmodellightorigin.GetBool() && !bShadowDepth && !bSSAODepth )
  1808. {
  1809. DebugDrawLightingOrigin( state, pInfo );
  1810. }
  1811. ColorMeshInfo_t *pColorMeshes = NULL;
  1812. DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE;
  1813. if ( bStaticLighting )
  1814. {
  1815. // have static lighting, get from cache
  1816. hColorMeshData = m_ModelInstances[pInfo.instance].m_ColorMeshHandle;
  1817. CColorMeshData *pColorMeshData = CacheGet( hColorMeshData );
  1818. if ( !pColorMeshData || pColorMeshData->m_bNeedsRetry )
  1819. {
  1820. // color meshes are not present, try to re-establish
  1821. if ( RecomputeStaticLighting( pInfo.instance ) )
  1822. {
  1823. pColorMeshData = CacheGet( hColorMeshData );
  1824. }
  1825. else if ( !pColorMeshData || !pColorMeshData->m_bNeedsRetry )
  1826. {
  1827. // can't draw
  1828. return;
  1829. }
  1830. }
  1831. if ( pColorMeshData && ( pColorMeshData->m_bColorMeshValid || pColorMeshData->m_bColorTextureValid ) )
  1832. {
  1833. pColorMeshes = pColorMeshData->m_pMeshInfos;
  1834. if (pColorMeshData->m_bColorTextureValid && !pColorMeshData->m_bColorTextureCreated)
  1835. {
  1836. CreateLightmapsFromData(pColorMeshData);
  1837. }
  1838. }
  1839. else
  1840. {
  1841. // failed, draw without static lighting
  1842. bStaticLighting = false;
  1843. }
  1844. }
  1845. DrawModelInfo_t info;
  1846. info.m_bStaticLighting = false;
  1847. // get lighting from ambient light sources and radiosity bounces
  1848. // also set up the env_cubemap from the light cache if necessary.
  1849. if ( ( bVertexLit || bNeedsEnvCubemap ) && !bShadowDepth && !bSSAODepth )
  1850. {
  1851. // See if we're using static lighting
  1852. LightCacheHandle_t* pLightCache = NULL;
  1853. if ( pInfo.instance != MODEL_INSTANCE_INVALID )
  1854. {
  1855. if ( ( m_ModelInstances[pInfo.instance].m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) && m_ModelInstances[pInfo.instance].m_LightCacheHandle )
  1856. {
  1857. pLightCache = &m_ModelInstances[pInfo.instance].m_LightCacheHandle;
  1858. }
  1859. }
  1860. // Choose the lighting origin
  1861. Vector entOrigin;
  1862. R_ComputeLightingOrigin( state.m_pRenderable, state.m_pStudioHdr, *state.m_pModelToWorld, entOrigin );
  1863. // Set up lighting based on the lighting origin
  1864. StudioSetupLighting( state, entOrigin, pLightCache, bVertexLit, bNeedsEnvCubemap, bStaticLighting, info, pInfo, state.m_drawFlags );
  1865. }
  1866. // Set up the camera state
  1867. g_pStudioRender->SetViewState( CurrentViewOrigin(), CurrentViewRight(), CurrentViewUp(), CurrentViewForward() );
  1868. // Color + alpha modulation
  1869. g_pStudioRender->SetColorModulation( r_colormod );
  1870. g_pStudioRender->SetAlphaModulation( r_blend );
  1871. Assert( modelloader->IsLoaded( pInfo.pModel ) );
  1872. info.m_pStudioHdr = state.m_pStudioHdr;
  1873. info.m_pHardwareData = state.m_pStudioHWData;
  1874. info.m_Skin = pInfo.skin;
  1875. info.m_Body = pInfo.body;
  1876. info.m_HitboxSet = pInfo.hitboxset;
  1877. info.m_pClientEntity = (void*)state.m_pRenderable;
  1878. info.m_Lod = state.m_lod;
  1879. info.m_pColorMeshes = pColorMeshes;
  1880. // Don't do decals if shadow depth mapping...
  1881. info.m_Decals = ( bShadowDepth || bSSAODepth ) ? STUDIORENDER_DECAL_INVALID : state.m_decals;
  1882. // Get perf stats if we are going to use them.
  1883. int overlayVal = r_drawmodelstatsoverlay.GetInt();
  1884. int drawFlags = state.m_drawFlags;
  1885. if ( bShadowDepth )
  1886. {
  1887. drawFlags |= STUDIORENDER_DRAW_OPAQUE_ONLY;
  1888. drawFlags |= STUDIORENDER_SHADOWDEPTHTEXTURE;
  1889. }
  1890. if ( bSSAODepth == true )
  1891. {
  1892. drawFlags |= STUDIORENDER_DRAW_OPAQUE_ONLY;
  1893. drawFlags |= STUDIORENDER_SSAODEPTHTEXTURE;
  1894. }
  1895. if ( overlayVal && !bShadowDepth && !bSSAODepth )
  1896. {
  1897. drawFlags |= STUDIORENDER_DRAW_GET_PERF_STATS;
  1898. }
  1899. if ( ( pInfo.flags & STUDIO_GENERATE_STATS ) != 0 )
  1900. {
  1901. drawFlags |= STUDIORENDER_GENERATE_STATS;
  1902. }
  1903. DrawModelResults_t results;
  1904. g_pStudioRender->DrawModel( &results, info, pBoneToWorld, pFlexWeights,
  1905. pFlexDelayedWeights, pInfo.origin, drawFlags );
  1906. info.m_Lod = results.m_nLODUsed;
  1907. if ( overlayVal && !bShadowDepth && !bSSAODepth )
  1908. {
  1909. if ( overlayVal != 2 )
  1910. {
  1911. DrawModelDebugOverlay( info, results, pInfo.origin );
  1912. }
  1913. else
  1914. {
  1915. AddModelDebugOverlay( info, results, pInfo.origin );
  1916. }
  1917. }
  1918. if ( pColorMeshes)
  1919. {
  1920. ProtectColorDataIfQueued( hColorMeshData );
  1921. }
  1922. #endif
  1923. }
  1924. //-----------------------------------------------------------------------------
  1925. // Main entry point for model rendering in the engine
  1926. //-----------------------------------------------------------------------------
  1927. int CModelRender::DrawModel(
  1928. int flags,
  1929. IClientRenderable *pRenderable,
  1930. ModelInstanceHandle_t instance,
  1931. int entity_index,
  1932. const model_t *pModel,
  1933. const Vector &origin,
  1934. const QAngle &angles,
  1935. int skin,
  1936. int body,
  1937. int hitboxset,
  1938. const matrix3x4_t* pModelToWorld,
  1939. const matrix3x4_t *pLightingOffset )
  1940. {
  1941. ModelRenderInfo_t sInfo;
  1942. sInfo.flags = flags;
  1943. sInfo.pRenderable = pRenderable;
  1944. sInfo.instance = instance;
  1945. sInfo.entity_index = entity_index;
  1946. sInfo.pModel = pModel;
  1947. sInfo.origin = origin;
  1948. sInfo.angles = angles;
  1949. sInfo.skin = skin;
  1950. sInfo.body = body;
  1951. sInfo.hitboxset = hitboxset;
  1952. sInfo.pModelToWorld = pModelToWorld;
  1953. sInfo.pLightingOffset = pLightingOffset;
  1954. if ( (r_entity.GetInt() == -1) || (r_entity.GetInt() == entity_index) )
  1955. {
  1956. return DrawModelEx( sInfo );
  1957. }
  1958. return 0;
  1959. }
  1960. int CModelRender::ComputeLOD( const ModelRenderInfo_t &info, studiohwdata_t *pStudioHWData )
  1961. {
  1962. int lod = r_lod.GetInt();
  1963. // FIXME!!! This calc should be in studiorender, not here!!!!! But since the bone setup
  1964. // is done here, and we need the bone mask, we'll do it here for now.
  1965. if ( lod == -1 )
  1966. {
  1967. CMatRenderContextPtr pRenderContext( materials );
  1968. float screenSize = pRenderContext->ComputePixelWidthOfSphere(info.pRenderable->GetRenderOrigin(), 0.5f );
  1969. float metric = pStudioHWData->LODMetric(screenSize);
  1970. lod = pStudioHWData->GetLODForMetric(metric);
  1971. }
  1972. else
  1973. {
  1974. if ( ( info.flags & STUDIOHDR_FLAGS_HASSHADOWLOD ) && ( lod > pStudioHWData->m_NumLODs - 2 ) )
  1975. {
  1976. lod = pStudioHWData->m_NumLODs - 2;
  1977. }
  1978. else if ( lod > pStudioHWData->m_NumLODs - 1 )
  1979. {
  1980. lod = pStudioHWData->m_NumLODs - 1;
  1981. }
  1982. else if( lod < 0 )
  1983. {
  1984. lod = 0;
  1985. }
  1986. }
  1987. if ( lod < 0 )
  1988. {
  1989. lod = 0;
  1990. }
  1991. else if ( lod >= pStudioHWData->m_NumLODs )
  1992. {
  1993. lod = pStudioHWData->m_NumLODs - 1;
  1994. }
  1995. // clamp to root lod
  1996. if (lod < pStudioHWData->m_RootLOD)
  1997. {
  1998. lod = pStudioHWData->m_RootLOD;
  1999. }
  2000. Assert( lod >= 0 && lod < pStudioHWData->m_NumLODs );
  2001. return lod;
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. // Purpose:
  2005. // Input : &pInfo -
  2006. //-----------------------------------------------------------------------------
  2007. bool CModelRender::DrawModelSetup( ModelRenderInfo_t &pInfo, DrawModelState_t *pState, matrix3x4_t *pCustomBoneToWorld, matrix3x4_t** ppBoneToWorldOut )
  2008. {
  2009. *ppBoneToWorldOut = NULL;
  2010. #ifdef SWDS
  2011. return false;
  2012. #endif
  2013. #if _DEBUG
  2014. if ( (char*)pInfo.pRenderable < (char*)1024 )
  2015. {
  2016. Error( "CModelRender::DrawModel: pRenderable == 0x%p", pInfo.pRenderable );
  2017. }
  2018. #endif
  2019. // Can only deal with studio models
  2020. Assert( pInfo.pModel->type == mod_studio );
  2021. Assert( modelloader->IsLoaded( pInfo.pModel ) );
  2022. DrawModelState_t &state = *pState;
  2023. state.m_pStudioHdr = g_pMDLCache->GetStudioHdr( pInfo.pModel->studio );
  2024. state.m_pRenderable = pInfo.pRenderable;
  2025. // Quick exit if we're just supposed to draw a specific model...
  2026. if ( (r_entity.GetInt() != -1) && (r_entity.GetInt() != pInfo.entity_index) )
  2027. return false;
  2028. // quick exit
  2029. if (state.m_pStudioHdr->numbodyparts == 0)
  2030. return false;
  2031. if ( !pInfo.pModelToWorld )
  2032. {
  2033. Assert( 0 );
  2034. return false;
  2035. }
  2036. state.m_pModelToWorld = pInfo.pModelToWorld;
  2037. Assert ( pInfo.pRenderable );
  2038. state.m_pStudioHWData = g_pMDLCache->GetHardwareData( pInfo.pModel->studio );
  2039. if ( !state.m_pStudioHWData )
  2040. return false;
  2041. state.m_lod = ComputeLOD( pInfo, state.m_pStudioHWData );
  2042. int boneMask = BONE_USED_BY_VERTEX_AT_LOD( state.m_lod );
  2043. // Why isn't this always set?!?
  2044. if ( ( pInfo.flags & STUDIO_RENDER ) == 0 )
  2045. {
  2046. // no rendering, just force a bone setup. Don't copy the bones
  2047. bool bOk = pInfo.pRenderable->SetupBones( NULL, MAXSTUDIOBONES, boneMask, cl.GetTime() );
  2048. return bOk;
  2049. }
  2050. int nBoneCount = state.m_pStudioHdr->numbones;
  2051. matrix3x4_t *pBoneToWorld = pCustomBoneToWorld;
  2052. if ( !pCustomBoneToWorld )
  2053. {
  2054. pBoneToWorld = g_pStudioRender->LockBoneMatrices( nBoneCount );
  2055. }
  2056. const bool bOk = pInfo.pRenderable->SetupBones( pBoneToWorld, nBoneCount, boneMask, cl.GetTime() );
  2057. if ( !pCustomBoneToWorld )
  2058. {
  2059. g_pStudioRender->UnlockBoneMatrices();
  2060. }
  2061. if ( !bOk )
  2062. return false;
  2063. *ppBoneToWorldOut = pBoneToWorld;
  2064. // Convert the instance to a decal handle.
  2065. state.m_decals = STUDIORENDER_DECAL_INVALID;
  2066. if (pInfo.instance != MODEL_INSTANCE_INVALID)
  2067. {
  2068. state.m_decals = m_ModelInstances[pInfo.instance].m_DecalHandle;
  2069. }
  2070. state.m_drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL;
  2071. if ( pInfo.flags & STUDIO_TWOPASS )
  2072. {
  2073. if (pInfo.flags & STUDIO_TRANSPARENCY)
  2074. {
  2075. state.m_drawFlags = STUDIORENDER_DRAW_TRANSLUCENT_ONLY;
  2076. }
  2077. else
  2078. {
  2079. state.m_drawFlags = STUDIORENDER_DRAW_OPAQUE_ONLY;
  2080. }
  2081. }
  2082. if ( pInfo.flags & STUDIO_STATIC_LIGHTING )
  2083. {
  2084. state.m_drawFlags |= STUDIORENDER_DRAW_STATIC_LIGHTING;
  2085. }
  2086. if( pInfo.flags & STUDIO_ITEM_BLINK )
  2087. {
  2088. state.m_drawFlags |= STUDIORENDER_DRAW_ITEM_BLINK;
  2089. }
  2090. if ( pInfo.flags & STUDIO_WIREFRAME )
  2091. {
  2092. state.m_drawFlags |= STUDIORENDER_DRAW_WIREFRAME;
  2093. }
  2094. if ( pInfo.flags & STUDIO_NOSHADOWS )
  2095. {
  2096. state.m_drawFlags |= STUDIORENDER_DRAW_NO_SHADOWS;
  2097. }
  2098. if ( r_drawmodelstatsoverlay.GetInt() == 2)
  2099. {
  2100. state.m_drawFlags |= STUDIORENDER_DRAW_ACCURATETIME;
  2101. }
  2102. if ( pInfo.flags & STUDIO_SHADOWDEPTHTEXTURE )
  2103. {
  2104. state.m_drawFlags |= STUDIORENDER_SHADOWDEPTHTEXTURE;
  2105. }
  2106. return true;
  2107. }
  2108. int CModelRender::DrawModelEx( ModelRenderInfo_t &pInfo )
  2109. {
  2110. #ifndef SWDS
  2111. DrawModelState_t state;
  2112. matrix3x4_t tmpmat;
  2113. if ( !pInfo.pModelToWorld )
  2114. {
  2115. pInfo.pModelToWorld = &tmpmat;
  2116. // Turns the origin + angles into a matrix
  2117. AngleMatrix( pInfo.angles, pInfo.origin, tmpmat );
  2118. }
  2119. matrix3x4_t *pBoneToWorld;
  2120. if ( !DrawModelSetup( pInfo, &state, NULL, &pBoneToWorld ) )
  2121. return 0;
  2122. if ( pInfo.flags & STUDIO_RENDER )
  2123. {
  2124. DrawModelExecute( state, pInfo, pBoneToWorld );
  2125. }
  2126. return 1;
  2127. #else
  2128. return 0;
  2129. #endif
  2130. }
  2131. int CModelRender::DrawModelExStaticProp( ModelRenderInfo_t &pInfo )
  2132. {
  2133. #ifndef SWDS
  2134. bool bShadowDepth = ( pInfo.flags & STUDIO_SHADOWDEPTHTEXTURE ) != 0;
  2135. #if _DEBUG
  2136. if ( (char*)pInfo.pRenderable < (char*)1024 )
  2137. {
  2138. Error( "CModelRender::DrawModel: pRenderable == %p", pInfo.pRenderable );
  2139. }
  2140. // Can only deal with studio models
  2141. if ( pInfo.pModel->type != mod_studio )
  2142. return 0;
  2143. #endif
  2144. Assert( modelloader->IsLoaded( pInfo.pModel ) );
  2145. DrawModelState_t state;
  2146. state.m_pStudioHdr = g_pMDLCache->GetStudioHdr( pInfo.pModel->studio );
  2147. state.m_pRenderable = pInfo.pRenderable;
  2148. // quick exit
  2149. if ( state.m_pStudioHdr->numbodyparts == 0 || g_bTextMode )
  2150. return 1;
  2151. state.m_pStudioHWData = g_pMDLCache->GetHardwareData( pInfo.pModel->studio );
  2152. if ( !state.m_pStudioHWData )
  2153. return 0;
  2154. Assert( pInfo.pModelToWorld );
  2155. state.m_pModelToWorld = pInfo.pModelToWorld;
  2156. Assert ( pInfo.pRenderable );
  2157. int lod = ComputeLOD( pInfo, state.m_pStudioHWData );
  2158. // int boneMask = BONE_USED_BY_VERTEX_AT_LOD( lod );
  2159. // Why isn't this always set?!?
  2160. if ( !(pInfo.flags & STUDIO_RENDER) )
  2161. return 0;
  2162. // Convert the instance to a decal handle.
  2163. StudioDecalHandle_t decalHandle = STUDIORENDER_DECAL_INVALID;
  2164. if ( (pInfo.instance != MODEL_INSTANCE_INVALID) && !(pInfo.flags & STUDIO_SHADOWDEPTHTEXTURE) )
  2165. {
  2166. decalHandle = m_ModelInstances[pInfo.instance].m_DecalHandle;
  2167. }
  2168. int drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL;
  2169. if ( pInfo.flags & STUDIO_TWOPASS )
  2170. {
  2171. if ( pInfo.flags & STUDIO_TRANSPARENCY )
  2172. {
  2173. drawFlags = STUDIORENDER_DRAW_TRANSLUCENT_ONLY;
  2174. }
  2175. else
  2176. {
  2177. drawFlags = STUDIORENDER_DRAW_OPAQUE_ONLY;
  2178. }
  2179. }
  2180. if ( pInfo.flags & STUDIO_STATIC_LIGHTING )
  2181. {
  2182. drawFlags |= STUDIORENDER_DRAW_STATIC_LIGHTING;
  2183. }
  2184. if ( pInfo.flags & STUDIO_WIREFRAME )
  2185. {
  2186. drawFlags |= STUDIORENDER_DRAW_WIREFRAME;
  2187. }
  2188. if ( pInfo.flags & STUDIO_GENERATE_STATS )
  2189. {
  2190. drawFlags |= STUDIORENDER_GENERATE_STATS;
  2191. }
  2192. // Shadow state...
  2193. g_pShadowMgr->SetModelShadowState( pInfo.instance );
  2194. // OPTIMIZE: Try to precompute part of this mess once a frame at the very least.
  2195. bool bUsesBumpmapping = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 ) && ( pInfo.pModel->flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING );
  2196. bool bStaticLighting = (( drawFlags & STUDIORENDER_DRAW_STATIC_LIGHTING ) &&
  2197. ( state.m_pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) &&
  2198. ( !bUsesBumpmapping ) &&
  2199. ( pInfo.instance != MODEL_INSTANCE_INVALID ) &&
  2200. g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() );
  2201. bool bVertexLit = ( pInfo.pModel->flags & MODELFLAG_VERTEXLIT ) != 0;
  2202. bool bNeedsEnvCubemap = r_showenvcubemap.GetInt() || ( pInfo.pModel->flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP );
  2203. if ( r_drawmodellightorigin.GetBool() )
  2204. {
  2205. DebugDrawLightingOrigin( state, pInfo );
  2206. }
  2207. ColorMeshInfo_t *pColorMeshes = NULL;
  2208. DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE;
  2209. if ( bStaticLighting )
  2210. {
  2211. // have static lighting, get from cache
  2212. hColorMeshData = m_ModelInstances[pInfo.instance].m_ColorMeshHandle;
  2213. CColorMeshData *pColorMeshData = CacheGet( hColorMeshData );
  2214. if ( !pColorMeshData || pColorMeshData->m_bNeedsRetry )
  2215. {
  2216. // color meshes are not present, try to re-establish
  2217. if ( RecomputeStaticLighting( pInfo.instance ) )
  2218. {
  2219. pColorMeshData = CacheGet( hColorMeshData );
  2220. }
  2221. else if ( !pColorMeshData || !pColorMeshData->m_bNeedsRetry )
  2222. {
  2223. // can't draw
  2224. return 0;
  2225. }
  2226. }
  2227. if ( pColorMeshData && ( pColorMeshData->m_bColorMeshValid || pColorMeshData->m_bColorTextureValid ) )
  2228. {
  2229. pColorMeshes = pColorMeshData->m_pMeshInfos;
  2230. if (pColorMeshData->m_bColorTextureValid && !pColorMeshData->m_bColorTextureCreated)
  2231. {
  2232. CreateLightmapsFromData(pColorMeshData);
  2233. }
  2234. }
  2235. else
  2236. {
  2237. // failed, draw without static lighting
  2238. bStaticLighting = false;
  2239. }
  2240. }
  2241. DrawModelInfo_t info;
  2242. info.m_bStaticLighting = false;
  2243. // Get lighting from ambient light sources and radiosity bounces
  2244. // also set up the env_cubemap from the light cache if necessary.
  2245. // Don't bother if we're rendering to shadow depth texture
  2246. if ( ( bVertexLit || bNeedsEnvCubemap ) && !bShadowDepth )
  2247. {
  2248. // See if we're using static lighting
  2249. LightCacheHandle_t* pLightCache = NULL;
  2250. if ( pInfo.instance != MODEL_INSTANCE_INVALID )
  2251. {
  2252. if ( ( m_ModelInstances[pInfo.instance].m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) && m_ModelInstances[pInfo.instance].m_LightCacheHandle )
  2253. {
  2254. pLightCache = &m_ModelInstances[pInfo.instance].m_LightCacheHandle;
  2255. }
  2256. }
  2257. // Choose the lighting origin
  2258. Vector entOrigin;
  2259. if ( !pLightCache )
  2260. {
  2261. R_ComputeLightingOrigin( state.m_pRenderable, state.m_pStudioHdr, *state.m_pModelToWorld, entOrigin );
  2262. }
  2263. // Set up lighting based on the lighting origin
  2264. StudioSetupLighting( state, entOrigin, pLightCache, bVertexLit, bNeedsEnvCubemap, bStaticLighting, info, pInfo, drawFlags );
  2265. }
  2266. Assert( modelloader->IsLoaded( pInfo.pModel ) );
  2267. info.m_pStudioHdr = state.m_pStudioHdr;
  2268. info.m_pHardwareData = state.m_pStudioHWData;
  2269. info.m_Decals = decalHandle;
  2270. info.m_Skin = pInfo.skin;
  2271. info.m_Body = pInfo.body;
  2272. info.m_HitboxSet = pInfo.hitboxset;
  2273. info.m_pClientEntity = (void*)state.m_pRenderable;
  2274. info.m_Lod = lod;
  2275. info.m_pColorMeshes = pColorMeshes;
  2276. if ( bShadowDepth )
  2277. {
  2278. drawFlags |= STUDIORENDER_SHADOWDEPTHTEXTURE;
  2279. }
  2280. #ifdef _DEBUG
  2281. Vector tmp;
  2282. MatrixGetColumn( *pInfo.pModelToWorld, 3, &tmp );
  2283. Assert( VectorsAreEqual( pInfo.origin, tmp, 1e-3 ) );
  2284. #endif
  2285. g_pStudioRender->DrawModelStaticProp( info, *pInfo.pModelToWorld, drawFlags );
  2286. if ( pColorMeshes)
  2287. {
  2288. ProtectColorDataIfQueued( hColorMeshData );
  2289. }
  2290. return 1;
  2291. #else
  2292. return 0;
  2293. #endif
  2294. }
  2295. struct robject_t
  2296. {
  2297. const matrix3x4_t *pMatrix;
  2298. IClientRenderable *pRenderable;
  2299. ColorMeshInfo_t *pColorMeshes;
  2300. ITexture *pEnvCubeMap;
  2301. Vector *pLightingOrigin;
  2302. short modelIndex;
  2303. short lod;
  2304. ModelInstanceHandle_t instance;
  2305. short skin;
  2306. short lightIndex;
  2307. short pad0;
  2308. };
  2309. struct rmodel_t
  2310. {
  2311. const model_t * pModel;
  2312. studiohdr_t* pStudioHdr;
  2313. studiohwdata_t* pStudioHWData;
  2314. float maxArea;
  2315. short lodStart;
  2316. byte lodCount;
  2317. byte bVertexLit : 1;
  2318. byte bNeedsCubemap : 1;
  2319. byte bStaticLighting : 1;
  2320. };
  2321. class CRobjectLess
  2322. {
  2323. public:
  2324. bool Less( const robject_t& lhs, const robject_t& rhs, void *pContext )
  2325. {
  2326. rmodel_t *pModels = static_cast<rmodel_t *>(pContext);
  2327. if ( lhs.modelIndex == rhs.modelIndex )
  2328. {
  2329. if ( lhs.skin != rhs.skin )
  2330. return lhs.skin < rhs.skin;
  2331. return lhs.lod < rhs.lod;
  2332. }
  2333. if ( pModels[lhs.modelIndex].maxArea == pModels[rhs.modelIndex].maxArea )
  2334. return lhs.modelIndex < rhs.modelIndex;
  2335. return pModels[lhs.modelIndex].maxArea > pModels[rhs.modelIndex].maxArea;
  2336. }
  2337. };
  2338. struct rdecalmodel_t
  2339. {
  2340. short objectIndex;
  2341. short lightIndex;
  2342. };
  2343. /*
  2344. // ----------------------------------------
  2345. // not yet implemented
  2346. struct rlod_t
  2347. {
  2348. short groupCount;
  2349. short groupStart;
  2350. };
  2351. struct rgroup_t
  2352. {
  2353. IMesh *pMesh;
  2354. short batchCount;
  2355. short batchStart;
  2356. short colorMeshIndex;
  2357. short pad0;
  2358. };
  2359. struct rbatch_t
  2360. {
  2361. IMaterial *pMaterial;
  2362. short primitiveType;
  2363. short pad0;
  2364. unsigned short indexOffset;
  2365. unsigned short indexCount;
  2366. };
  2367. // ----------------------------------------
  2368. */
  2369. inline int FindModel( const CUtlVector<rmodel_t> &list, const model_t *pModel )
  2370. {
  2371. for ( int j = list.Count(); --j >= 0 ; )
  2372. {
  2373. if ( list[j].pModel == pModel )
  2374. return j;
  2375. }
  2376. return -1;
  2377. }
  2378. // NOTE: UNDONE: This is a work in progress of a new static prop rendering pipeline
  2379. // UNDONE: Expose drawing commands from studiorender and draw here
  2380. // UNDONE: Build a similar pipeline for non-static prop models
  2381. // UNDONE: Split this into several functions in a sub-object
  2382. ConVar r_staticprop_lod("r_staticprop_lod", "-1");
  2383. int CModelRender::DrawStaticPropArrayFast( StaticPropRenderInfo_t *pProps, int count, bool bShadowDepth )
  2384. {
  2385. #ifndef SWDS
  2386. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  2387. CMatRenderContextPtr pRenderContext( materials );
  2388. const int MAX_OBJECTS = 1024;
  2389. CUtlSortVector<robject_t, CRobjectLess> objectList(0, MAX_OBJECTS);
  2390. CUtlVector<rmodel_t> modelList(0,256);
  2391. CUtlVector<short> lightObjects(0,256);
  2392. CUtlVector<short> shadowObjects(0,64);
  2393. CUtlVector<rdecalmodel_t> decalObjects(0,64);
  2394. CUtlVector<LightingState_t> lightStates(0,256);
  2395. bool bForceCubemap = r_showenvcubemap.GetBool();
  2396. int drawnCount = 0;
  2397. int forcedLodSetting = r_lod.GetInt();
  2398. if ( r_staticprop_lod.GetInt() >= 0 )
  2399. {
  2400. forcedLodSetting = r_staticprop_lod.GetInt();
  2401. }
  2402. // build list of objects and unique models
  2403. for ( int i = 0; i < count; i++ )
  2404. {
  2405. drawnCount++;
  2406. // UNDONE: This is a perf hit in some scenes! Use a hash?
  2407. int modelIndex = FindModel( modelList, pProps[i].pModel );
  2408. if ( modelIndex < 0 )
  2409. {
  2410. modelIndex = modelList.AddToTail();
  2411. modelList[modelIndex].pModel = pProps[i].pModel;
  2412. modelList[modelIndex].pStudioHWData = g_pMDLCache->GetHardwareData( modelList[modelIndex].pModel->studio );
  2413. }
  2414. if ( modelList[modelIndex].pStudioHWData )
  2415. {
  2416. robject_t obj;
  2417. obj.pMatrix = pProps[i].pModelToWorld;
  2418. obj.pRenderable = pProps[i].pRenderable;
  2419. obj.modelIndex = modelIndex;
  2420. obj.instance = pProps[i].instance;
  2421. obj.skin = pProps[i].skin;
  2422. obj.lod = 0;
  2423. obj.pColorMeshes = NULL;
  2424. obj.pEnvCubeMap = NULL;
  2425. obj.lightIndex = -1;
  2426. obj.pLightingOrigin = pProps[i].pLightingOrigin;
  2427. objectList.InsertNoSort(obj);
  2428. }
  2429. }
  2430. // process list of unique models
  2431. int lodStart = 0;
  2432. for ( int i = 0; i < modelList.Count(); i++ )
  2433. {
  2434. const model_t *pModel = modelList[i].pModel;
  2435. Assert( modelloader->IsLoaded( pModel ) );
  2436. unsigned int flags = pModel->flags;
  2437. modelList[i].pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  2438. modelList[i].maxArea = 1.0f;
  2439. modelList[i].lodStart = lodStart;
  2440. modelList[i].lodCount = modelList[i].pStudioHWData ? modelList[i].pStudioHWData->m_NumLODs : 0;
  2441. bool bBumpMapped = (flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING) != 0;
  2442. modelList[i].bStaticLighting = (( modelList[i].pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) != 0) && !bBumpMapped;
  2443. modelList[i].bVertexLit = ( flags & MODELFLAG_VERTEXLIT ) != 0;
  2444. modelList[i].bNeedsCubemap = ( flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP ) != 0;
  2445. lodStart += modelList[i].lodCount;
  2446. }
  2447. // -1 is automatic lod
  2448. if ( forcedLodSetting < 0 )
  2449. {
  2450. // compute the lod of each object
  2451. for ( int i = 0; i < objectList.Count(); i++ )
  2452. {
  2453. Vector org;
  2454. MatrixGetColumn( *objectList[i].pMatrix, 3, org );
  2455. float screenSize = pRenderContext->ComputePixelWidthOfSphere(org, 0.5f );
  2456. const rmodel_t &model = modelList[objectList[i].modelIndex];
  2457. float metric = model.pStudioHWData->LODMetric(screenSize);
  2458. objectList[i].lod = model.pStudioHWData->GetLODForMetric(metric);
  2459. if ( objectList[i].lod < model.pStudioHWData->m_RootLOD )
  2460. {
  2461. objectList[i].lod = model.pStudioHWData->m_RootLOD;
  2462. }
  2463. modelList[objectList[i].modelIndex].maxArea = max(modelList[objectList[i].modelIndex].maxArea, screenSize);
  2464. }
  2465. }
  2466. else
  2467. {
  2468. // force the lod of each object
  2469. for ( int i = 0; i < objectList.Count(); i++ )
  2470. {
  2471. const rmodel_t &model = modelList[objectList[i].modelIndex];
  2472. objectList[i].lod = clamp(forcedLodSetting, model.pStudioHWData->m_RootLOD, model.lodCount-1);
  2473. }
  2474. }
  2475. // UNDONE: Don't sort if rendering transparent objects - for now this isn't called in the transparent case
  2476. // sort by model, then by lod
  2477. objectList.SetLessContext( static_cast<void *>(modelList.Base()) );
  2478. objectList.RedoSort(true);
  2479. ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
  2480. // now build out the lighting states
  2481. if ( !bShadowDepth )
  2482. {
  2483. for ( int i = 0; i < objectList.Count(); i++ )
  2484. {
  2485. robject_t &obj = objectList[i];
  2486. rmodel_t &model = modelList[obj.modelIndex];
  2487. bool bStaticLighting = (model.bStaticLighting && obj.instance != MODEL_INSTANCE_INVALID);
  2488. bool bVertexLit = model.bVertexLit;
  2489. bool bNeedsEnvCubemap = bForceCubemap || model.bNeedsCubemap;
  2490. bool bHasDecals = ( m_ModelInstances[obj.instance].m_DecalHandle != STUDIORENDER_DECAL_INVALID ) ? true : false;
  2491. LightingState_t *pDecalLightState = NULL;
  2492. if ( bHasDecals )
  2493. {
  2494. rdecalmodel_t decalModel;
  2495. decalModel.lightIndex = lightStates.AddToTail();
  2496. pDecalLightState = &lightStates[decalModel.lightIndex];
  2497. decalModel.objectIndex = i;
  2498. decalObjects.AddToTail( decalModel );
  2499. }
  2500. // for now we skip models that have shadows - will update later to include them in a post-pass
  2501. if ( g_pShadowMgr->ModelHasShadows( obj.instance ) )
  2502. {
  2503. shadowObjects.AddToTail(i);
  2504. }
  2505. // get the static lighting from the cache
  2506. DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE;
  2507. if ( bStaticLighting )
  2508. {
  2509. // have static lighting, get from cache
  2510. hColorMeshData = m_ModelInstances[obj.instance].m_ColorMeshHandle;
  2511. CColorMeshData *pColorMeshData = CacheGet( hColorMeshData );
  2512. if ( !pColorMeshData || pColorMeshData->m_bNeedsRetry )
  2513. {
  2514. // color meshes are not present, try to re-establish
  2515. if ( UpdateStaticPropColorData( obj.pRenderable->GetIClientUnknown(), obj.instance ) )
  2516. {
  2517. pColorMeshData = CacheGet( hColorMeshData );
  2518. }
  2519. else if ( !pColorMeshData || !pColorMeshData->m_bNeedsRetry )
  2520. {
  2521. // can't draw
  2522. continue;
  2523. }
  2524. }
  2525. if ( pColorMeshData && ( pColorMeshData->m_bColorMeshValid || pColorMeshData->m_bColorTextureValid ) )
  2526. {
  2527. obj.pColorMeshes = pColorMeshData->m_pMeshInfos;
  2528. if (pColorMeshData->m_bColorTextureValid && !pColorMeshData->m_bColorTextureCreated)
  2529. {
  2530. CreateLightmapsFromData(pColorMeshData);
  2531. }
  2532. if ( pCallQueue )
  2533. {
  2534. if ( CacheLock( hColorMeshData ) ) // CacheCreate above will call functions that won't take place until later. If color mesh isn't used right away, it could get dumped
  2535. {
  2536. pCallQueue->QueueCall( this, &CModelRender::CacheUnlock, hColorMeshData );
  2537. }
  2538. }
  2539. }
  2540. else
  2541. {
  2542. // failed, draw without static lighting
  2543. bStaticLighting = false;
  2544. }
  2545. }
  2546. // Get lighting from ambient light sources and radiosity bounces
  2547. // also set up the env_cubemap from the light cache if necessary.
  2548. if ( ( bVertexLit || bNeedsEnvCubemap ) )
  2549. {
  2550. // See if we're using static lighting
  2551. LightCacheHandle_t* pLightCache = NULL;
  2552. ITexture *pEnvCubemapTexture = NULL;
  2553. if ( obj.instance != MODEL_INSTANCE_INVALID )
  2554. {
  2555. if ( ( m_ModelInstances[obj.instance].m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) && m_ModelInstances[obj.instance].m_LightCacheHandle )
  2556. {
  2557. pLightCache = &m_ModelInstances[obj.instance].m_LightCacheHandle;
  2558. }
  2559. }
  2560. Assert(pLightCache);
  2561. LightingState_t lightingState;
  2562. LightingState_t *pState = &lightingState;
  2563. if ( pLightCache )
  2564. {
  2565. // dx8 and dx9 case. . .hardware can do baked lighting plus other dynamic lighting
  2566. // We already have the static part baked into a color mesh, so just get the dynamic stuff.
  2567. if ( !bStaticLighting || StaticLightCacheAffectedByDynamicLight( *pLightCache ) )
  2568. {
  2569. pState = LightcacheGetStatic( *pLightCache, &pEnvCubemapTexture, LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE );
  2570. Assert( pState->numlights >= 0 && pState->numlights <= MAXLOCALLIGHTS );
  2571. }
  2572. else
  2573. {
  2574. pState = LightcacheGetStatic( *pLightCache, &pEnvCubemapTexture, LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE );
  2575. Assert( pState->numlights >= 0 && pState->numlights <= MAXLOCALLIGHTS );
  2576. }
  2577. if ( bHasDecals )
  2578. {
  2579. for ( int iCube = 0; iCube < 6; ++iCube )
  2580. {
  2581. pDecalLightState->r_boxcolor[iCube] = m_ModelInstances[obj.instance].m_AmbientLightingState.r_boxcolor[iCube] + pState->r_boxcolor[iCube];
  2582. }
  2583. pDecalLightState->CopyLocalLights( m_ModelInstances[obj.instance].m_AmbientLightingState );
  2584. pDecalLightState->AddAllLocalLights( *pState );
  2585. }
  2586. }
  2587. else // !pLightcache
  2588. {
  2589. // UNDONE: is it possible to end up here in the static prop case?
  2590. Vector vLightingOrigin = *obj.pLightingOrigin;
  2591. int lightCacheFlags = bStaticLighting ? (LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE)
  2592. : (LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST);
  2593. LightcacheGetDynamic_Stats stats;
  2594. pEnvCubemapTexture = LightcacheGetDynamic( vLightingOrigin, lightingState,
  2595. stats, lightCacheFlags, false );
  2596. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  2597. pState = &lightingState;
  2598. if ( bHasDecals )
  2599. {
  2600. LightcacheGetDynamic_Stats tempStats;
  2601. LightcacheGetDynamic( vLightingOrigin, *pDecalLightState, tempStats,
  2602. LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST );
  2603. }
  2604. }
  2605. if ( bNeedsEnvCubemap && pEnvCubemapTexture )
  2606. {
  2607. obj.pEnvCubeMap = pEnvCubemapTexture;
  2608. }
  2609. if ( bVertexLit )
  2610. {
  2611. // if we have any real lighting state we need to save it for this object
  2612. if ( pState->numlights || pState->HasAmbientColors() )
  2613. {
  2614. obj.lightIndex = lightStates.AddToTail(*pState);
  2615. lightObjects.AddToTail( i );
  2616. }
  2617. }
  2618. }
  2619. }
  2620. }
  2621. // now render the baked lighting props with no lighting state
  2622. float color[3];
  2623. color[0] = color[1] = color[2] = 1.0f;
  2624. g_pStudioRender->SetColorModulation(color);
  2625. g_pStudioRender->SetAlphaModulation(1.0f);
  2626. g_pStudioRender->SetViewState( CurrentViewOrigin(), CurrentViewRight(), CurrentViewUp(), CurrentViewForward() );
  2627. pRenderContext->MatrixMode( MATERIAL_MODEL );
  2628. pRenderContext->PushMatrix();
  2629. pRenderContext->LoadIdentity();
  2630. g_pStudioRender->ClearAllShadows();
  2631. pRenderContext->DisableAllLocalLights();
  2632. DrawModelInfo_t info;
  2633. for ( int i = 0; i < 6; i++ )
  2634. info.m_vecAmbientCube[i].Init();
  2635. g_pStudioRender->SetAmbientLightColors( info.m_vecAmbientCube );
  2636. pRenderContext->SetAmbientLight( 0.0, 0.0, 0.0 );
  2637. info.m_nLocalLightCount = 0;
  2638. info.m_bStaticLighting = false;
  2639. int drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_STATIC_LIGHTING;
  2640. if (bShadowDepth)
  2641. drawFlags |= STUDIO_SHADOWDEPTHTEXTURE;
  2642. info.m_Decals = STUDIORENDER_DECAL_INVALID;
  2643. info.m_Body = 0;
  2644. info.m_HitboxSet = 0;
  2645. for ( int i = 0; i < objectList.Count(); i++ )
  2646. {
  2647. robject_t &obj = objectList[i];
  2648. if ( obj.lightIndex >= 0 )
  2649. continue;
  2650. rmodel_t &model = modelList[obj.modelIndex];
  2651. if ( obj.pEnvCubeMap )
  2652. {
  2653. pRenderContext->BindLocalCubemap( obj.pEnvCubeMap );
  2654. }
  2655. info.m_pStudioHdr = model.pStudioHdr;
  2656. info.m_pHardwareData = model.pStudioHWData;
  2657. info.m_Skin = obj.skin;
  2658. info.m_pClientEntity = static_cast<void*>(obj.pRenderable);
  2659. info.m_Lod = obj.lod;
  2660. info.m_pColorMeshes = obj.pColorMeshes;
  2661. g_pStudioRender->DrawModelStaticProp( info, *obj.pMatrix, drawFlags );
  2662. }
  2663. // now render the vertex lit props
  2664. int nLocalLightCount = 0;
  2665. LightDesc_t localLightDescs[4];
  2666. drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_STATIC_LIGHTING;
  2667. if ( lightObjects.Count() )
  2668. {
  2669. for ( int i = 0; i < lightObjects.Count(); i++ )
  2670. {
  2671. robject_t &obj = objectList[lightObjects[i]];
  2672. rmodel_t &model = modelList[obj.modelIndex];
  2673. if ( obj.pEnvCubeMap )
  2674. {
  2675. pRenderContext->BindLocalCubemap( obj.pEnvCubeMap );
  2676. }
  2677. LightingState_t *pState = &lightStates[obj.lightIndex];
  2678. g_pStudioRender->SetAmbientLightColors( pState->r_boxcolor );
  2679. pRenderContext->SetLightingOrigin( *obj.pLightingOrigin );
  2680. R_SetNonAmbientLightingState( pState->numlights, pState->locallight, &nLocalLightCount, localLightDescs, true );
  2681. info.m_pStudioHdr = model.pStudioHdr;
  2682. info.m_pHardwareData = model.pStudioHWData;
  2683. info.m_Skin = obj.skin;
  2684. info.m_pClientEntity = static_cast<void*>(obj.pRenderable);
  2685. info.m_Lod = obj.lod;
  2686. info.m_pColorMeshes = obj.pColorMeshes;
  2687. g_pStudioRender->DrawModelStaticProp( info, *obj.pMatrix, drawFlags );
  2688. }
  2689. }
  2690. if ( !IsX360() && ( r_flashlight_version2.GetInt() == 0 ) && shadowObjects.Count() )
  2691. {
  2692. drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL;
  2693. for ( int i = 0; i < shadowObjects.Count(); i++ )
  2694. {
  2695. // draw just the shadows!
  2696. robject_t &obj = objectList[shadowObjects[i]];
  2697. rmodel_t &model = modelList[obj.modelIndex];
  2698. g_pShadowMgr->SetModelShadowState( obj.instance );
  2699. info.m_pStudioHdr = model.pStudioHdr;
  2700. info.m_pHardwareData = model.pStudioHWData;
  2701. info.m_Skin = obj.skin;
  2702. info.m_pClientEntity = static_cast<void*>(obj.pRenderable);
  2703. info.m_Lod = obj.lod;
  2704. info.m_pColorMeshes = obj.pColorMeshes;
  2705. g_pStudioRender->DrawStaticPropShadows( info, *obj.pMatrix, drawFlags );
  2706. }
  2707. g_pStudioRender->ClearAllShadows();
  2708. }
  2709. for ( int i = 0; i < decalObjects.Count(); i++ )
  2710. {
  2711. // draw just the decals!
  2712. robject_t &obj = objectList[decalObjects[i].objectIndex];
  2713. rmodel_t &model = modelList[obj.modelIndex];
  2714. LightingState_t *pState = &lightStates[decalObjects[i].lightIndex];
  2715. g_pStudioRender->SetAmbientLightColors( pState->r_boxcolor );
  2716. pRenderContext->SetLightingOrigin( *obj.pLightingOrigin );
  2717. R_SetNonAmbientLightingState( pState->numlights, pState->locallight, &nLocalLightCount, localLightDescs, true );
  2718. info.m_pStudioHdr = model.pStudioHdr;
  2719. info.m_pHardwareData = model.pStudioHWData;
  2720. info.m_Decals = m_ModelInstances[obj.instance].m_DecalHandle;
  2721. info.m_Skin = obj.skin;
  2722. info.m_pClientEntity = static_cast<void*>(obj.pRenderable);
  2723. info.m_Lod = obj.lod;
  2724. info.m_pColorMeshes = obj.pColorMeshes;
  2725. g_pStudioRender->DrawStaticPropDecals( info, *obj.pMatrix );
  2726. }
  2727. // Restore the matrices if we're skinning
  2728. pRenderContext->MatrixMode( MATERIAL_MODEL );
  2729. pRenderContext->PopMatrix();
  2730. return drawnCount;
  2731. #else // SWDS
  2732. return 0;
  2733. #endif // SWDS
  2734. }
  2735. //-----------------------------------------------------------------------------
  2736. // Shadow rendering
  2737. //-----------------------------------------------------------------------------
  2738. matrix3x4_t* CModelRender::DrawModelShadowSetup( IClientRenderable *pRenderable, int body, int skin, DrawModelInfo_t *pInfo, matrix3x4_t *pCustomBoneToWorld )
  2739. {
  2740. #ifndef SWDS
  2741. DrawModelInfo_t &info = *pInfo;
  2742. static ConVar r_shadowlod("r_shadowlod", "-1");
  2743. static ConVar r_shadowlodbias("r_shadowlodbias", "2");
  2744. model_t const* pModel = pRenderable->GetModel();
  2745. if ( !pModel )
  2746. return NULL;
  2747. // FIXME: Make brush shadows work
  2748. if ( pModel->type != mod_studio )
  2749. return NULL;
  2750. Assert( modelloader->IsLoaded( pModel ) && ( pModel->type == mod_studio ) );
  2751. info.m_pStudioHdr = g_pMDLCache->GetStudioHdr( pModel->studio );
  2752. info.m_pColorMeshes = NULL;
  2753. // quick exit
  2754. if (info.m_pStudioHdr->numbodyparts == 0)
  2755. return NULL;
  2756. Assert ( pRenderable );
  2757. info.m_pHardwareData = g_pMDLCache->GetHardwareData( pModel->studio );
  2758. if ( !info.m_pHardwareData )
  2759. return NULL;
  2760. info.m_Decals = STUDIORENDER_DECAL_INVALID;
  2761. info.m_Skin = skin;
  2762. info.m_Body = body;
  2763. info.m_pClientEntity = (void*)pRenderable;
  2764. info.m_HitboxSet = 0;
  2765. info.m_Lod = r_shadowlod.GetInt();
  2766. // If the .mdl has a shadowlod, force the use of that one instead
  2767. if ( info.m_pStudioHdr->flags & STUDIOHDR_FLAGS_HASSHADOWLOD )
  2768. {
  2769. info.m_Lod = info.m_pHardwareData->m_NumLODs-1;
  2770. }
  2771. else if ( info.m_Lod == USESHADOWLOD )
  2772. {
  2773. int lastlod = info.m_pHardwareData->m_NumLODs - 1;
  2774. info.m_Lod = lastlod;
  2775. }
  2776. else if ( info.m_Lod < 0 )
  2777. {
  2778. CMatRenderContextPtr pRenderContext( materials );
  2779. // Compute the shadow LOD...
  2780. float factor = r_shadowlodbias.GetFloat() > 0.0f ? 1.0f / r_shadowlodbias.GetFloat() : 1.0f;
  2781. float screenSize = factor * pRenderContext->ComputePixelWidthOfSphere( pRenderable->GetRenderOrigin(), 0.5f );
  2782. info.m_Lod = g_pStudioRender->ComputeModelLod( info.m_pHardwareData, screenSize );
  2783. info.m_Lod = info.m_pHardwareData->m_NumLODs-2;
  2784. if ( info.m_Lod < 0 )
  2785. {
  2786. info.m_Lod = 0;
  2787. }
  2788. }
  2789. // clamp to root lod
  2790. if (info.m_Lod < info.m_pHardwareData->m_RootLOD)
  2791. {
  2792. info.m_Lod = info.m_pHardwareData->m_RootLOD;
  2793. }
  2794. matrix3x4_t *pBoneToWorld = pCustomBoneToWorld;
  2795. if ( !pBoneToWorld )
  2796. {
  2797. pBoneToWorld = g_pStudioRender->LockBoneMatrices( info.m_pStudioHdr->numbones );
  2798. }
  2799. const bool bOk = pRenderable->SetupBones( pBoneToWorld, info.m_pStudioHdr->numbones, BONE_USED_BY_VERTEX_AT_LOD(info.m_Lod), cl.GetTime() );
  2800. g_pStudioRender->UnlockBoneMatrices();
  2801. if ( !bOk )
  2802. return NULL;
  2803. return pBoneToWorld;
  2804. #else
  2805. return NULL;
  2806. #endif
  2807. }
  2808. void CModelRender::DrawModelShadow( IClientRenderable *pRenderable, const DrawModelInfo_t &info, matrix3x4_t *pBoneToWorld )
  2809. {
  2810. #ifndef SWDS
  2811. // Needed because we don't call SetupWeights
  2812. g_pStudioRender->SetEyeViewTarget( info.m_pStudioHdr, info.m_Body, vec3_origin );
  2813. // Color + alpha modulation
  2814. Vector white( 1, 1, 1 );
  2815. g_pStudioRender->SetColorModulation( white.Base() );
  2816. g_pStudioRender->SetAlphaModulation( 1.0f );
  2817. if ((info.m_pStudioHdr->flags & STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS) == 0)
  2818. {
  2819. g_pStudioRender->ForcedMaterialOverride( g_pMaterialShadowBuild, OVERRIDE_BUILD_SHADOWS );
  2820. }
  2821. g_pStudioRender->DrawModel( NULL, info, pBoneToWorld, NULL, NULL, pRenderable->GetRenderOrigin(),
  2822. STUDIORENDER_DRAW_NO_SHADOWS | STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_NO_FLEXES );
  2823. g_pStudioRender->ForcedMaterialOverride( 0 );
  2824. #endif
  2825. }
  2826. void CModelRender::SetViewTarget( const CStudioHdr *pStudioHdr, int nBodyIndex, const Vector& target )
  2827. {
  2828. g_pStudioRender->SetEyeViewTarget( pStudioHdr->GetRenderHdr(), nBodyIndex, target );
  2829. }
  2830. void CModelRender::InitColormeshParams( ModelInstance_t &instance, studiohwdata_t *pStudioHWData, colormeshparams_t *pColorMeshParams )
  2831. {
  2832. pColorMeshParams->m_nMeshes = 0;
  2833. pColorMeshParams->m_nTotalVertexes = 0;
  2834. pColorMeshParams->m_pPooledVBAllocator = NULL;
  2835. if ( ( instance.m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) &&
  2836. g_pMaterialSystemHardwareConfig->SupportsStreamOffset() &&
  2837. ( r_proplightingpooling.GetInt() == 1 ) )
  2838. {
  2839. // Color meshes can be allocated in a shared pool for static props
  2840. // (saves memory on X360 due to 4-KB VB alignment)
  2841. pColorMeshParams->m_pPooledVBAllocator = (IPooledVBAllocator *)&m_colorMeshVBAllocator;
  2842. }
  2843. for ( int lodID = pStudioHWData->m_RootLOD; lodID < pStudioHWData->m_NumLODs; lodID++ )
  2844. {
  2845. studioloddata_t *pLOD = &pStudioHWData->m_pLODs[lodID];
  2846. for ( int meshID = 0; meshID < pStudioHWData->m_NumStudioMeshes; meshID++ )
  2847. {
  2848. studiomeshdata_t *pMesh = &pLOD->m_pMeshData[meshID];
  2849. for ( int groupID = 0; groupID < pMesh->m_NumGroup; groupID++ )
  2850. {
  2851. pColorMeshParams->m_nVertexes[pColorMeshParams->m_nMeshes++] = pMesh->m_pMeshGroup[groupID].m_NumVertices;
  2852. Assert( pColorMeshParams->m_nMeshes <= ARRAYSIZE( pColorMeshParams->m_nVertexes ) );
  2853. pColorMeshParams->m_nTotalVertexes += pMesh->m_pMeshGroup[groupID].m_NumVertices;
  2854. }
  2855. }
  2856. }
  2857. }
  2858. //-----------------------------------------------------------------------------
  2859. // Allocates the static prop color data meshes
  2860. //-----------------------------------------------------------------------------
  2861. // FIXME? : Move this to StudioRender?
  2862. CColorMeshData *CModelRender::FindOrCreateStaticPropColorData( ModelInstanceHandle_t handle )
  2863. {
  2864. if ( handle == MODEL_INSTANCE_INVALID || !g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() )
  2865. {
  2866. // the card can't support it
  2867. return NULL;
  2868. }
  2869. ModelInstance_t& instance = m_ModelInstances[handle];
  2870. CColorMeshData *pColorMeshData = CacheGet( instance.m_ColorMeshHandle );
  2871. if ( pColorMeshData )
  2872. {
  2873. // found in cache
  2874. return pColorMeshData;
  2875. }
  2876. if ( !instance.m_pModel )
  2877. {
  2878. return NULL;
  2879. }
  2880. Assert( modelloader->IsLoaded( instance.m_pModel ) && ( instance.m_pModel->type == mod_studio ) );
  2881. studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( instance.m_pModel->studio );
  2882. Assert( pStudioHWData );
  2883. if ( !pStudioHWData )
  2884. return NULL;
  2885. colormeshparams_t params;
  2886. InitColormeshParams( instance, pStudioHWData, &params );
  2887. if ( params.m_nMeshes <= 0 )
  2888. {
  2889. // nothing to create
  2890. return NULL;
  2891. }
  2892. // create the meshes
  2893. params.m_fnHandle = instance.m_pModel->fnHandle;
  2894. instance.m_ColorMeshHandle = CacheCreate( params );
  2895. ProtectColorDataIfQueued( instance.m_ColorMeshHandle );
  2896. pColorMeshData = CacheGet( instance.m_ColorMeshHandle );
  2897. return pColorMeshData;
  2898. }
  2899. //-----------------------------------------------------------------------------
  2900. // Allocates the static prop color data meshes
  2901. //-----------------------------------------------------------------------------
  2902. // FIXME? : Move this to StudioRender?
  2903. void CModelRender::ProtectColorDataIfQueued( DataCacheHandle_t hColorMesh )
  2904. {
  2905. if ( hColorMesh != DC_INVALID_HANDLE)
  2906. {
  2907. CMatRenderContextPtr pRenderContext( materials );
  2908. ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
  2909. if ( pCallQueue )
  2910. {
  2911. if ( CacheLock( hColorMesh ) ) // CacheCreate above will call functions that won't take place until later. If color mesh isn't used right away, it could get dumped
  2912. {
  2913. pCallQueue->QueueCall( this, &CModelRender::CacheUnlock, hColorMesh );
  2914. }
  2915. }
  2916. }
  2917. }
  2918. //-----------------------------------------------------------------------------
  2919. // Old-style computation of vertex lighting ( Currently In Use )
  2920. //-----------------------------------------------------------------------------
  2921. void CModelRender::ComputeModelVertexLightingOld( mstudiomodel_t *pModel,
  2922. matrix3x4_t& matrix, const LightingState_t &lightingState, color24 *pLighting,
  2923. bool bUseConstDirLighting, float flConstDirLightAmount )
  2924. {
  2925. Vector worldPos, worldNormal, destColor;
  2926. int nNumLightDesc;
  2927. LightDesc_t lightDesc[MAXLOCALLIGHTS];
  2928. LightingState_t *pLightingState;
  2929. pLightingState = (LightingState_t*)&lightingState;
  2930. // build the lighting descriptors
  2931. R_SetNonAmbientLightingState( pLightingState->numlights, pLightingState->locallight, &nNumLightDesc, lightDesc, false );
  2932. const thinModelVertices_t *thinVertData = NULL;
  2933. const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
  2934. mstudiovertex_t *pFatVerts = NULL;
  2935. if ( vertData )
  2936. {
  2937. pFatVerts = vertData->Vertex( 0 );
  2938. }
  2939. else
  2940. {
  2941. thinVertData = pModel->GetThinVertexData();
  2942. if ( !thinVertData )
  2943. return;
  2944. }
  2945. bool bHasSSE = MathLib_SSEEnabled();
  2946. // light all vertexes
  2947. for ( int i = 0; i < pModel->numvertices; ++i )
  2948. {
  2949. if ( vertData )
  2950. {
  2951. #ifdef _WIN32
  2952. if (bHasSSE)
  2953. {
  2954. // hint the next vertex
  2955. // data is loaded with one extra vertex for read past
  2956. #if !defined( _X360 ) // X360TBD
  2957. _mm_prefetch( (char*)&pFatVerts[i+1], _MM_HINT_T0 );
  2958. #endif
  2959. }
  2960. #endif
  2961. VectorTransform( pFatVerts[i].m_vecPosition, matrix, worldPos );
  2962. VectorRotate( pFatVerts[i].m_vecNormal, matrix, worldNormal );
  2963. }
  2964. else
  2965. {
  2966. Vector position;
  2967. Vector normal;
  2968. thinVertData->GetModelPosition( pModel, i, &position );
  2969. thinVertData->GetModelNormal( pModel, i, &normal );
  2970. VectorTransform( position, matrix, worldPos );
  2971. VectorRotate( normal, matrix, worldNormal );
  2972. }
  2973. if ( bUseConstDirLighting )
  2974. {
  2975. g_pStudioRender->ComputeLightingConstDirectional( pLightingState->r_boxcolor,
  2976. nNumLightDesc, lightDesc, worldPos, worldNormal, destColor, flConstDirLightAmount );
  2977. }
  2978. else
  2979. {
  2980. g_pStudioRender->ComputeLighting( pLightingState->r_boxcolor,
  2981. nNumLightDesc, lightDesc, worldPos, worldNormal, destColor );
  2982. }
  2983. // to gamma space
  2984. destColor[0] = LinearToVertexLight( destColor[0] );
  2985. destColor[1] = LinearToVertexLight( destColor[1] );
  2986. destColor[2] = LinearToVertexLight( destColor[2] );
  2987. Assert( (destColor[0] >= 0.0f) && (destColor[0] <= 1.0f) );
  2988. Assert( (destColor[1] >= 0.0f) && (destColor[1] <= 1.0f) );
  2989. Assert( (destColor[2] >= 0.0f) && (destColor[2] <= 1.0f) );
  2990. pLighting[i].r = FastFToC(destColor[0]);
  2991. pLighting[i].g = FastFToC(destColor[1]);
  2992. pLighting[i].b = FastFToC(destColor[2]);
  2993. }
  2994. }
  2995. //-----------------------------------------------------------------------------
  2996. // New-style computation of vertex lighting ( Not Used Yet )
  2997. //-----------------------------------------------------------------------------
  2998. void CModelRender::ComputeModelVertexLighting( IHandleEntity *pProp,
  2999. mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD,
  3000. matrix3x4_t& matrix, Vector4D *pTempMem, color24 *pLighting )
  3001. {
  3002. #ifndef SWDS
  3003. if ( IsX360() )
  3004. return;
  3005. int i;
  3006. unsigned char *pInSolid = (unsigned char*)stackalloc( ((pModel->numvertices + 7) >> 3) * sizeof(unsigned char) );
  3007. Vector worldPos, worldNormal;
  3008. const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
  3009. Assert( vertData );
  3010. if ( !vertData )
  3011. return;
  3012. for ( i = 0; i < pModel->numvertices; ++i )
  3013. {
  3014. const Vector &pos = *vertData->Position( i );
  3015. const Vector &normal = *vertData->Normal( i );
  3016. VectorTransform( pos, matrix, worldPos );
  3017. VectorRotate( normal, matrix, worldNormal );
  3018. bool bNonSolid = ComputeVertexLightingFromSphericalSamples( worldPos, worldNormal, pProp, &(pTempMem[i].AsVector3D()) );
  3019. int nByte = i >> 3;
  3020. int nBit = i & 0x7;
  3021. if ( bNonSolid )
  3022. {
  3023. pTempMem[i].w = 1.0f;
  3024. pInSolid[ nByte ] &= ~(1 << nBit);
  3025. }
  3026. else
  3027. {
  3028. pTempMem[i].Init( );
  3029. pInSolid[ nByte ] |= (1 << nBit);
  3030. }
  3031. }
  3032. // Must iterate over each triangle to average out the colors for those
  3033. // vertices in solid.
  3034. // Iterate over all the meshes....
  3035. for (int meshID = 0; meshID < pModel->nummeshes; ++meshID)
  3036. {
  3037. Assert( pModel->nummeshes == pVtxLOD->numMeshes );
  3038. mstudiomesh_t* pMesh = pModel->pMesh(meshID);
  3039. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh(meshID);
  3040. // Iterate over all strip groups.
  3041. for( int stripGroupID = 0; stripGroupID < pVtxMesh->numStripGroups; ++stripGroupID )
  3042. {
  3043. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup(stripGroupID);
  3044. // Iterate over all indices
  3045. Assert( pStripGroup->numIndices % 3 == 0 );
  3046. for (i = 0; i < pStripGroup->numIndices; i += 3)
  3047. {
  3048. unsigned short nIndex1 = *pStripGroup->pIndex( i );
  3049. unsigned short nIndex2 = *pStripGroup->pIndex( i + 1 );
  3050. unsigned short nIndex3 = *pStripGroup->pIndex( i + 2 );
  3051. int v[3];
  3052. v[0] = pStripGroup->pVertex( nIndex1 )->origMeshVertID + pMesh->vertexoffset;
  3053. v[1] = pStripGroup->pVertex( nIndex2 )->origMeshVertID + pMesh->vertexoffset;
  3054. v[2] = pStripGroup->pVertex( nIndex3 )->origMeshVertID + pMesh->vertexoffset;
  3055. Assert( v[0] < pModel->numvertices );
  3056. Assert( v[1] < pModel->numvertices );
  3057. Assert( v[2] < pModel->numvertices );
  3058. bool bSolid[3];
  3059. bSolid[0] = ( pInSolid[ v[0] >> 3 ] & ( 1 << ( v[0] & 0x7 ) ) ) != 0;
  3060. bSolid[1] = ( pInSolid[ v[1] >> 3 ] & ( 1 << ( v[1] & 0x7 ) ) ) != 0;
  3061. bSolid[2] = ( pInSolid[ v[2] >> 3 ] & ( 1 << ( v[2] & 0x7 ) ) ) != 0;
  3062. int nValidCount = 0;
  3063. int nAverage[3];
  3064. if ( !bSolid[0] ) { nAverage[nValidCount++] = v[0]; }
  3065. if ( !bSolid[1] ) { nAverage[nValidCount++] = v[1]; }
  3066. if ( !bSolid[2] ) { nAverage[nValidCount++] = v[2]; }
  3067. if ( nValidCount == 3 )
  3068. continue;
  3069. Vector vecAverage( 0, 0, 0 );
  3070. for ( int j = 0; j < nValidCount; ++j )
  3071. {
  3072. vecAverage += pTempMem[nAverage[j]].AsVector3D();
  3073. }
  3074. if (nValidCount != 0)
  3075. {
  3076. vecAverage /= nValidCount;
  3077. }
  3078. if ( bSolid[0] ) { pTempMem[ v[0] ].AsVector3D() += vecAverage; pTempMem[ v[0] ].w += 1.0f; }
  3079. if ( bSolid[1] ) { pTempMem[ v[1] ].AsVector3D() += vecAverage; pTempMem[ v[1] ].w += 1.0f; }
  3080. if ( bSolid[2] ) { pTempMem[ v[2] ].AsVector3D() += vecAverage; pTempMem[ v[2] ].w += 1.0f; }
  3081. }
  3082. }
  3083. }
  3084. Vector destColor;
  3085. for ( i = 0; i < pModel->numvertices; ++i )
  3086. {
  3087. if ( pTempMem[i].w != 0.0f )
  3088. {
  3089. pTempMem[i] /= pTempMem[i].w;
  3090. }
  3091. destColor[0] = LinearToVertexLight( pTempMem[i][0] );
  3092. destColor[1] = LinearToVertexLight( pTempMem[i][1] );
  3093. destColor[2] = LinearToVertexLight( pTempMem[i][2] );
  3094. ColorClampTruncate( destColor );
  3095. pLighting[i].r = FastFToC(destColor[0]);
  3096. pLighting[i].g = FastFToC(destColor[1]);
  3097. pLighting[i].b = FastFToC(destColor[2]);
  3098. }
  3099. #endif
  3100. }
  3101. //-----------------------------------------------------------------------------
  3102. // Sanity check and setup the compiled color mesh for an optimal async load
  3103. // during runtime.
  3104. //-----------------------------------------------------------------------------
  3105. void CModelRender::ValidateStaticPropColorData( ModelInstanceHandle_t handle )
  3106. {
  3107. if ( !r_proplightingfromdisk.GetBool() )
  3108. {
  3109. return;
  3110. }
  3111. ModelInstance_t *pInstance = &m_ModelInstances[handle];
  3112. IHandleEntity* pProp = pInstance->m_pRenderable->GetIClientUnknown();
  3113. if ( !g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() || !StaticPropMgr()->IsStaticProp( pProp ) )
  3114. {
  3115. // can't support it or not a static prop
  3116. return;
  3117. }
  3118. if ( !g_bLoadedMapHasBakedPropLighting || StaticPropMgr()->PropHasBakedLightingDisabled( pProp ) )
  3119. {
  3120. return;
  3121. }
  3122. MEM_ALLOC_CREDIT();
  3123. // fetch the header
  3124. CUtlBuffer utlBuf;
  3125. char fileName[MAX_PATH];
  3126. if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE || g_bBakedPropLightingNoSeparateHDR )
  3127. {
  3128. Q_snprintf( fileName, sizeof( fileName ), "sp_%d%s.vhv", StaticPropMgr()->GetStaticPropIndex( pProp ), GetPlatformExt() );
  3129. }
  3130. else
  3131. {
  3132. Q_snprintf( fileName, sizeof( fileName ), "sp_hdr_%d%s.vhv", StaticPropMgr()->GetStaticPropIndex( pProp ), GetPlatformExt() );
  3133. }
  3134. if ( IsX360() )
  3135. {
  3136. DataCacheHandle_t hColorMesh = GetCachedStaticPropColorData( fileName );
  3137. if ( hColorMesh != DC_INVALID_HANDLE )
  3138. {
  3139. // already have it
  3140. pInstance->m_ColorMeshHandle = hColorMesh;
  3141. pInstance->m_nFlags &= ~MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD;
  3142. pInstance->m_nFlags |= MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR;
  3143. return;
  3144. }
  3145. }
  3146. if ( !g_pFileSystem->ReadFile( fileName, "GAME", utlBuf, sizeof( HardwareVerts::FileHeader_t ), 0 ) )
  3147. {
  3148. // not available
  3149. return;
  3150. }
  3151. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( pInstance->m_pModel->studio );
  3152. HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
  3153. if ( pVhvHdr->m_nVersion != VHV_VERSION ||
  3154. pVhvHdr->m_nChecksum != (unsigned int)pStudioHdr->checksum ||
  3155. pVhvHdr->m_nVertexSize != 4 )
  3156. {
  3157. // out of sync
  3158. // mark for debug visualization
  3159. pInstance->m_nFlags |= MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD;
  3160. return;
  3161. }
  3162. // async callback can safely stream data into targets
  3163. pInstance->m_nFlags &= ~MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD;
  3164. pInstance->m_nFlags |= MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR;
  3165. }
  3166. //-----------------------------------------------------------------------------
  3167. // Async loader callback
  3168. // Called from async i/o thread - must spend minimal cycles in this context
  3169. //-----------------------------------------------------------------------------
  3170. void CModelRender::StaticPropColorMeshCallback( void *pContext, const void *pData, int numReadBytes, FSAsyncStatus_t asyncStatus )
  3171. {
  3172. // get our preserved data
  3173. Assert( pContext );
  3174. staticPropAsyncContext_t *pStaticPropContext = (staticPropAsyncContext_t *)pContext;
  3175. HardwareVerts::FileHeader_t *pVhvHdr;
  3176. byte *pOriginalData = NULL;
  3177. int numLightingComponents = 1;
  3178. if ( asyncStatus != FSASYNC_OK )
  3179. {
  3180. // any i/o error
  3181. goto cleanUp;
  3182. }
  3183. if ( IsX360() )
  3184. {
  3185. // only the 360 has compressed VHV data
  3186. // the compressed data is after the header
  3187. byte *pCompressedData = (byte *)pData + sizeof( HardwareVerts::FileHeader_t );
  3188. if ( CLZMA::IsCompressed( pCompressedData ) )
  3189. {
  3190. // create a buffer that matches the original
  3191. int actualSize = CLZMA::GetActualSize( pCompressedData );
  3192. pOriginalData = (byte *)malloc( sizeof( HardwareVerts::FileHeader_t ) + actualSize );
  3193. // place the header, then uncompress directly after it
  3194. V_memcpy( pOriginalData, pData, sizeof( HardwareVerts::FileHeader_t ) );
  3195. int outputLength = CLZMA::Uncompress( pCompressedData, pOriginalData + sizeof( HardwareVerts::FileHeader_t ) );
  3196. if ( outputLength != actualSize )
  3197. {
  3198. goto cleanUp;
  3199. }
  3200. pData = pOriginalData;
  3201. }
  3202. }
  3203. pVhvHdr = (HardwareVerts::FileHeader_t *)pData;
  3204. int startMesh;
  3205. for ( startMesh=0; startMesh<pVhvHdr->m_nMeshes; startMesh++ )
  3206. {
  3207. // skip past higher detail lod meshes that must be ignored
  3208. // find first mesh that matches desired lod
  3209. if ( pVhvHdr->pMesh( startMesh )->m_nLod == pStaticPropContext->m_nRootLOD )
  3210. {
  3211. break;
  3212. }
  3213. }
  3214. int meshID;
  3215. for ( meshID = startMesh; meshID<pVhvHdr->m_nMeshes; meshID++ )
  3216. {
  3217. int numVertexes = pVhvHdr->pMesh( meshID )->m_nVertexes;
  3218. if ( numVertexes != pStaticPropContext->m_pColorMeshData->m_pMeshInfos[meshID-startMesh].m_nNumVerts )
  3219. {
  3220. // meshes are out of sync, discard data
  3221. break;
  3222. }
  3223. int nID = meshID-startMesh;
  3224. unsigned char *pIn = (unsigned char *) pVhvHdr->pVertexBase( meshID );
  3225. unsigned char *pOut = NULL;
  3226. CMeshBuilder meshBuilder;
  3227. meshBuilder.Begin( pStaticPropContext->m_pColorMeshData->m_pMeshInfos[ nID ].m_pMesh, MATERIAL_HETEROGENOUS, numVertexes, 0 );
  3228. if ( numLightingComponents > 1 )
  3229. {
  3230. pOut = reinterpret_cast< unsigned char * >( const_cast< float * >( meshBuilder.Normal() ) );
  3231. }
  3232. else
  3233. {
  3234. pOut = meshBuilder.Specular();
  3235. }
  3236. #ifdef DX_TO_GL_ABSTRACTION
  3237. // OPENGL_SWAP_COLORS
  3238. for ( int i=0; i < (numVertexes * numLightingComponents ); i++ )
  3239. {
  3240. unsigned char red = *pIn++;
  3241. unsigned char green = *pIn++;
  3242. unsigned char blue = *pIn++;
  3243. *pOut++ = blue;
  3244. *pOut++ = green;
  3245. *pOut++ = red;
  3246. *pOut++ = *pIn++; // Alpha goes straight across
  3247. }
  3248. #else
  3249. V_memcpy( pOut, pIn, numVertexes * 4 * numLightingComponents );
  3250. #endif
  3251. meshBuilder.End();
  3252. }
  3253. cleanUp:
  3254. if ( IsX360() )
  3255. {
  3256. AUTO_LOCK( m_CachedStaticPropMutex );
  3257. // track the color mesh's datacache handle so that we can find it long after the model instance's are gone
  3258. // the static prop filenames are guaranteed uniquely decorated
  3259. m_CachedStaticPropColorData.Insert( pStaticPropContext->m_szFilenameVertex, pStaticPropContext->m_ColorMeshHandle );
  3260. // No support for lightmap textures on X360.
  3261. }
  3262. // mark as completed in single atomic operation
  3263. pStaticPropContext->m_pColorMeshData->m_bColorMeshValid = true;
  3264. CacheUnlock( pStaticPropContext->m_ColorMeshHandle );
  3265. delete pStaticPropContext;
  3266. if ( pOriginalData )
  3267. {
  3268. free( pOriginalData );
  3269. }
  3270. }
  3271. //-----------------------------------------------------------------------------
  3272. // Async loader callback
  3273. // Called from async i/o thread - must spend minimal cycles in this context
  3274. //-----------------------------------------------------------------------------
  3275. void CModelRender::StaticPropColorTexelCallback(void *pContext, const void *pData, int numReadBytes, FSAsyncStatus_t asyncStatus)
  3276. {
  3277. // get our preserved data
  3278. Assert(pContext);
  3279. staticPropAsyncContext_t *pStaticPropContext = (staticPropAsyncContext_t *)pContext;
  3280. HardwareTexels::FileHeader_t *pVhtHdr;
  3281. // This needs to be above the goto or clang complains "goto into protected scope."
  3282. bool anyTextures = false;
  3283. if (asyncStatus != FSASYNC_OK)
  3284. {
  3285. // any i/o error
  3286. goto cleanUp;
  3287. }
  3288. pVhtHdr = (HardwareTexels::FileHeader_t *)pData;
  3289. int startMesh;
  3290. for (startMesh = 0; startMesh < pVhtHdr->m_nMeshes; startMesh++)
  3291. {
  3292. // skip past higher detail lod meshes that must be ignored
  3293. // find first mesh that matches desired lod
  3294. if (pVhtHdr->pMesh(startMesh)->m_nLod == pStaticPropContext->m_nRootLOD)
  3295. {
  3296. break;
  3297. }
  3298. }
  3299. int meshID;
  3300. for ( meshID = startMesh; meshID < pVhtHdr->m_nMeshes; meshID++ )
  3301. {
  3302. const HardwareTexels::MeshHeader_t* pMeshData = pVhtHdr->pMesh( meshID );
  3303. // We can't create the real texture here because that's just how the material system works.
  3304. // So instead, squirrel away what we need for later.
  3305. ColorTexelsInfo_t* newCTI = new ColorTexelsInfo_t;
  3306. newCTI->m_nWidth = pMeshData->m_nWidth;
  3307. newCTI->m_nHeight = pMeshData->m_nHeight;
  3308. newCTI->m_nMipmapCount = ImageLoader::GetNumMipMapLevels( newCTI->m_nWidth, newCTI->m_nHeight );
  3309. newCTI->m_ImageFormat = ( ImageFormat ) pVhtHdr->m_nTexelFormat;
  3310. newCTI->m_nByteCount = pVhtHdr->pMesh( meshID )->m_nBytes;
  3311. newCTI->m_pTexelData = new byte[ newCTI->m_nByteCount ];
  3312. Q_memcpy( newCTI->m_pTexelData, pVhtHdr->pTexelBase( meshID ), newCTI->m_nByteCount );
  3313. pStaticPropContext->m_pColorMeshData->m_pMeshInfos[ meshID - startMesh ].m_pLightmapData = newCTI;
  3314. Assert( pStaticPropContext->m_pColorMeshData->m_pMeshInfos[ meshID - startMesh ].m_pLightmap == NULL );
  3315. anyTextures = true;
  3316. }
  3317. // This only gets set if we actually have texel data. Otherwise, it remains false.
  3318. pStaticPropContext->m_pColorMeshData->m_bColorTextureValid = anyTextures;
  3319. cleanUp:
  3320. // mark as completed in single atomic operation
  3321. CacheUnlock( pStaticPropContext->m_ColorMeshHandle );
  3322. delete pStaticPropContext;
  3323. }
  3324. //-----------------------------------------------------------------------------
  3325. // Async loader callback
  3326. // Called from async i/o thread - must spend minimal cycles in this context
  3327. //-----------------------------------------------------------------------------
  3328. static void StaticPropColorMeshCallback( const FileAsyncRequest_t &request, int numReadBytes, FSAsyncStatus_t asyncStatus )
  3329. {
  3330. s_ModelRender.StaticPropColorMeshCallback( request.pContext, request.pData, numReadBytes, asyncStatus );
  3331. }
  3332. //-----------------------------------------------------------------------------
  3333. // Async loader callback
  3334. // Called from async i/o thread - must spend minimal cycles in this context
  3335. //-----------------------------------------------------------------------------
  3336. static void StaticPropColorTexelCallback( const FileAsyncRequest_t &request, int numReadBytes, FSAsyncStatus_t asyncStatus )
  3337. {
  3338. s_ModelRender.StaticPropColorTexelCallback( request.pContext, request.pData, numReadBytes, asyncStatus );
  3339. }
  3340. //-----------------------------------------------------------------------------
  3341. // Queued loader callback
  3342. // Called from async i/o thread - must spend minimal cycles in this context
  3343. //-----------------------------------------------------------------------------
  3344. static void QueuedLoaderCallback_PropLighting( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  3345. {
  3346. // translate error
  3347. FSAsyncStatus_t asyncStatus = ( loaderError == LOADERERROR_NONE ? FSASYNC_OK : FSASYNC_ERR_READING );
  3348. // mimic async i/o completion
  3349. s_ModelRender.StaticPropColorMeshCallback( pContext, pData, nSize, asyncStatus );
  3350. }
  3351. //-----------------------------------------------------------------------------
  3352. // Loads the serialized static prop color data.
  3353. // Returns false if legacy path should be used.
  3354. //-----------------------------------------------------------------------------
  3355. bool CModelRender::LoadStaticPropColorData( IHandleEntity *pProp, DataCacheHandle_t colorMeshHandle, studiohwdata_t *pStudioHWData )
  3356. {
  3357. if ( !g_bLoadedMapHasBakedPropLighting || !r_proplightingfromdisk.GetBool() )
  3358. {
  3359. return false;
  3360. }
  3361. // lock the mesh memory during async transfer
  3362. // the color meshes should already have low quality data to be used during rendering
  3363. CColorMeshData *pColorMeshData = CacheLock( colorMeshHandle );
  3364. if ( !pColorMeshData )
  3365. {
  3366. return false;
  3367. }
  3368. if ( pColorMeshData->m_hAsyncControlVertex || pColorMeshData->m_hAsyncControlTexel )
  3369. {
  3370. // load in progress, ignore additional request
  3371. // or already loaded, ignore until discarded from cache
  3372. CacheUnlock( colorMeshHandle );
  3373. return true;
  3374. }
  3375. // each static prop has its own compiled color mesh
  3376. char fileName[MAX_PATH];
  3377. if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE || g_bBakedPropLightingNoSeparateHDR )
  3378. {
  3379. Q_snprintf( fileName, sizeof( fileName ), "sp_%d%s.vhv", StaticPropMgr()->GetStaticPropIndex( pProp ), GetPlatformExt() );
  3380. }
  3381. else
  3382. {
  3383. Q_snprintf( fileName, sizeof( fileName ), "sp_hdr_%d%s.vhv", StaticPropMgr()->GetStaticPropIndex( pProp ), GetPlatformExt() );
  3384. }
  3385. // mark as invalid, async callback will set upon completion
  3386. // prevents rendering during async transfer into locked mesh, otherwise d3drip
  3387. pColorMeshData->m_bColorMeshValid = false;
  3388. pColorMeshData->m_bColorTextureValid = false;
  3389. pColorMeshData->m_bColorTextureCreated = false;
  3390. // async load high quality lighting from file
  3391. // can't optimal async yet, because need flat ppColorMesh[], so use callback to distribute
  3392. // create our private context of data for the callback
  3393. staticPropAsyncContext_t *pContextVertex = new staticPropAsyncContext_t;
  3394. pContextVertex->m_nRootLOD = pStudioHWData->m_RootLOD;
  3395. pContextVertex->m_nMeshes = pColorMeshData->m_nMeshes;
  3396. pContextVertex->m_ColorMeshHandle = colorMeshHandle;
  3397. pContextVertex->m_pColorMeshData = pColorMeshData;
  3398. V_strncpy( pContextVertex->m_szFilenameVertex, fileName, sizeof( pContextVertex->m_szFilenameVertex ) );
  3399. if ( IsX360() && g_pQueuedLoader->IsMapLoading() )
  3400. {
  3401. if ( !g_pQueuedLoader->ClaimAnonymousJob( fileName, QueuedLoaderCallback_PropLighting, (void *)pContextVertex ) )
  3402. {
  3403. // not there as expected
  3404. // as a less optimal fallback during loading, issue as a standard queued loader job
  3405. LoaderJob_t loaderJob;
  3406. loaderJob.m_pFilename = fileName;
  3407. loaderJob.m_pPathID = "GAME";
  3408. loaderJob.m_pCallback = QueuedLoaderCallback_PropLighting;
  3409. loaderJob.m_pContext = (void *)pContextVertex;
  3410. loaderJob.m_Priority = LOADERPRIORITY_BEFOREPLAY;
  3411. g_pQueuedLoader->AddJob( &loaderJob );
  3412. }
  3413. return true;
  3414. }
  3415. // async load the file
  3416. FileAsyncRequest_t fileRequest;
  3417. fileRequest.pContext = (void *)pContextVertex;
  3418. fileRequest.pfnCallback = ::StaticPropColorMeshCallback;
  3419. fileRequest.pData = NULL;
  3420. fileRequest.pszFilename = fileName;
  3421. fileRequest.nOffset = 0;
  3422. fileRequest.flags = 0; // FSASYNC_FLAGS_SYNC;
  3423. fileRequest.nBytes = 0;
  3424. fileRequest.priority = -1;
  3425. fileRequest.pszPathID = "GAME";
  3426. // This must be done before sending pContextVertex down
  3427. staticPropAsyncContext_t* pContextTexel = new staticPropAsyncContext_t( *pContextVertex );
  3428. // queue vertex data for async load
  3429. {
  3430. MEM_ALLOC_CREDIT();
  3431. g_pFileSystem->AsyncRead( fileRequest, &pColorMeshData->m_hAsyncControlVertex );
  3432. }
  3433. Q_snprintf( fileName, sizeof( fileName ), "texelslighting_%d.ppl", StaticPropMgr()->GetStaticPropIndex( pProp ) );
  3434. V_strncpy( pContextTexel->m_szFilenameTexel, fileName, sizeof( pContextTexel->m_szFilenameTexel ) );
  3435. // We are already locked, but we will unlock twice--so lock once more for the texel processing.
  3436. CacheLock( colorMeshHandle );
  3437. // queue texel data for async load
  3438. fileRequest.pContext = pContextTexel;
  3439. fileRequest.pfnCallback = ::StaticPropColorTexelCallback;
  3440. fileRequest.pData = NULL;
  3441. fileRequest.pszFilename = fileName; // This doesn't need to happen, but included for clarity.
  3442. {
  3443. MEM_ALLOC_CREDIT();
  3444. g_pFileSystem->AsyncRead( fileRequest, &pColorMeshData->m_hAsyncControlTexel );
  3445. }
  3446. return true;
  3447. }
  3448. //-----------------------------------------------------------------------------
  3449. // Computes the static prop color data.
  3450. // Data calculation may be delayed if data is disk based.
  3451. // Returns FALSE if data not available or error. For retry polling pattern.
  3452. // Resturns TRUE if operation succesful or in progress (succeeds later).
  3453. //-----------------------------------------------------------------------------
  3454. bool CModelRender::UpdateStaticPropColorData( IHandleEntity *pProp, ModelInstanceHandle_t handle )
  3455. {
  3456. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  3457. #ifndef SWDS
  3458. // find or allocate color meshes
  3459. CColorMeshData *pColorMeshData = FindOrCreateStaticPropColorData( handle );
  3460. if ( !pColorMeshData )
  3461. {
  3462. return false;
  3463. }
  3464. // HACK: on PC, VB creation can fail due to device loss
  3465. if ( IsPC() && pColorMeshData->m_bHasInvalidVB )
  3466. {
  3467. // Don't retry until color data is flushed by device restore
  3468. pColorMeshData->m_bColorMeshValid = false;
  3469. pColorMeshData->m_bNeedsRetry = false;
  3470. return false;
  3471. }
  3472. unsigned char debugColor[3] = {0};
  3473. bool bDebugColor = false;
  3474. if ( r_debugrandomstaticlighting.GetBool() )
  3475. {
  3476. // randomize with bright colors, skip black and white
  3477. // purposely not deterministic to catch bugs with excessive re-baking (i.e. disco)
  3478. Vector fRandomColor;
  3479. int nColor = RandomInt(1,6);
  3480. fRandomColor.x = (nColor>>2) & 1;
  3481. fRandomColor.y = (nColor>>1) & 1;
  3482. fRandomColor.z = nColor & 1;
  3483. VectorNormalize( fRandomColor );
  3484. debugColor[0] = fRandomColor[0] * 255.0f;
  3485. debugColor[1] = fRandomColor[1] * 255.0f;
  3486. debugColor[2] = fRandomColor[2] * 255.0f;
  3487. bDebugColor = true;
  3488. }
  3489. // FIXME? : Move this to StudioRender?
  3490. ModelInstance_t &inst = m_ModelInstances[handle];
  3491. Assert( inst.m_pModel );
  3492. Assert( modelloader->IsLoaded( inst.m_pModel ) && ( inst.m_pModel->type == mod_studio ) );
  3493. if ( r_proplightingfromdisk.GetInt() == 2 )
  3494. {
  3495. // This visualization debug mode is strictly to debug which static prop models have valid disk
  3496. // based lighting. There should be no red models, only green or yellow. Yellow models denote the legacy
  3497. // lower quality runtime baked lighting.
  3498. if ( inst.m_nFlags & MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD )
  3499. {
  3500. // prop was compiled for static prop lighting, but out of sync
  3501. // bad disk data for model, show as red
  3502. debugColor[0] = 255.0f;
  3503. debugColor[1] = 0;
  3504. debugColor[2] = 0;
  3505. }
  3506. else if ( inst.m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR )
  3507. {
  3508. // valid disk data, show as green
  3509. debugColor[0] = 0;
  3510. debugColor[1] = 255.0f;
  3511. debugColor[2] = 0;
  3512. }
  3513. else
  3514. {
  3515. // no disk based data, using runtime method, show as yellow
  3516. // identifies a prop that wasn't compiled for static prop lighting
  3517. debugColor[0] = 255.0f;
  3518. debugColor[1] = 255.0f;
  3519. debugColor[2] = 0;
  3520. }
  3521. bDebugColor = true;
  3522. }
  3523. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( inst.m_pModel->studio );
  3524. studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( inst.m_pModel->studio );
  3525. Assert( pStudioHdr && pStudioHWData );
  3526. if ( !bDebugColor && ( inst.m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) )
  3527. {
  3528. // start an async load on available higher quality disc based data
  3529. if ( LoadStaticPropColorData( pProp, inst.m_ColorMeshHandle, pStudioHWData ) )
  3530. {
  3531. // async in progress, operation expected to succeed
  3532. // async callback handles finalization
  3533. return true;
  3534. }
  3535. }
  3536. // lighting calculation path
  3537. // calculation may abort due to lack of async requested data, caller should retry
  3538. pColorMeshData->m_bColorMeshValid = false;
  3539. pColorMeshData->m_bColorTextureValid = false;
  3540. pColorMeshData->m_bColorTextureCreated = false;
  3541. pColorMeshData->m_bNeedsRetry = true;
  3542. if ( !bDebugColor )
  3543. {
  3544. // vertexes must be available for lighting calculation
  3545. vertexFileHeader_t *pVertexHdr = g_pMDLCache->GetVertexData( (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff );
  3546. if ( !pVertexHdr )
  3547. {
  3548. // data not available yet
  3549. return false;
  3550. }
  3551. }
  3552. inst.m_nFlags |= MODEL_INSTANCE_HAS_COLOR_DATA;
  3553. // calculate lighting, set for access to verts
  3554. m_pStudioHdr = pStudioHdr;
  3555. // Sets the model transform state in g_pStudioRender
  3556. matrix3x4_t matrix;
  3557. AngleMatrix( inst.m_pRenderable->GetRenderAngles(), inst.m_pRenderable->GetRenderOrigin(), matrix );
  3558. // Get static lighting only!! We'll add dynamic and lightstyles in in the vertex shader. . .
  3559. unsigned int lightCacheFlags = LIGHTCACHEFLAGS_STATIC;
  3560. if ( !g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting() )
  3561. {
  3562. // . . . unless we can't do anything but static or dynamic simulaneously. . then
  3563. // we'll bake the lightstyle info here.
  3564. lightCacheFlags |= LIGHTCACHEFLAGS_LIGHTSTYLE;
  3565. }
  3566. LightingState_t lightingState;
  3567. if ( (inst.m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING) && inst.m_LightCacheHandle )
  3568. {
  3569. lightingState = *(LightcacheGetStatic( inst.m_LightCacheHandle, NULL, lightCacheFlags ));
  3570. }
  3571. else
  3572. {
  3573. // Choose the lighting origin
  3574. Vector entOrigin;
  3575. R_ComputeLightingOrigin( inst.m_pRenderable, pStudioHdr, matrix, entOrigin );
  3576. LightcacheGetDynamic_Stats stats;
  3577. LightcacheGetDynamic( entOrigin, lightingState, stats, lightCacheFlags );
  3578. }
  3579. // See if the studiohdr wants to use constant directional light, ie
  3580. // the surface normal plays no part in determining light intensity
  3581. bool bUseConstDirLighting = false;
  3582. float flConstDirLightingAmount = 0.0;
  3583. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT )
  3584. {
  3585. bUseConstDirLighting = true;
  3586. flConstDirLightingAmount = (float)( pStudioHdr->constdirectionallightdot ) / 255.0;
  3587. }
  3588. CUtlMemory< color24 > tmpLightingMem;
  3589. // Iterate over every body part...
  3590. for ( int bodyPartID = 0; bodyPartID < pStudioHdr->numbodyparts; ++bodyPartID )
  3591. {
  3592. mstudiobodyparts_t* pBodyPart = pStudioHdr->pBodypart( bodyPartID );
  3593. // Iterate over every submodel...
  3594. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  3595. {
  3596. mstudiomodel_t* pModel = pBodyPart->pModel(modelID);
  3597. if ( pModel->numvertices == 0 )
  3598. continue;
  3599. // Make sure we've got enough space allocated
  3600. tmpLightingMem.EnsureCapacity( pModel->numvertices );
  3601. if ( !bDebugColor )
  3602. {
  3603. // Compute lighting for each unique vertex in the model exactly once
  3604. ComputeModelVertexLightingOld( pModel, matrix, lightingState, tmpLightingMem.Base(), bUseConstDirLighting, flConstDirLightingAmount );
  3605. }
  3606. else
  3607. {
  3608. for ( int i=0; i<pModel->numvertices; i++ )
  3609. {
  3610. tmpLightingMem[i].r = debugColor[0];
  3611. tmpLightingMem[i].g = debugColor[1];
  3612. tmpLightingMem[i].b = debugColor[2];
  3613. }
  3614. }
  3615. // distribute the lighting results to the mesh's vertexes
  3616. for ( int lodID = pStudioHWData->m_RootLOD; lodID < pStudioHWData->m_NumLODs; ++lodID )
  3617. {
  3618. studioloddata_t *pStudioLODData = &pStudioHWData->m_pLODs[lodID];
  3619. studiomeshdata_t *pStudioMeshData = pStudioLODData->m_pMeshData;
  3620. // Iterate over all the meshes....
  3621. for ( int meshID = 0; meshID < pModel->nummeshes; ++meshID)
  3622. {
  3623. mstudiomesh_t* pMesh = pModel->pMesh( meshID );
  3624. // Iterate over all strip groups.
  3625. for ( int stripGroupID = 0; stripGroupID < pStudioMeshData[pMesh->meshid].m_NumGroup; ++stripGroupID )
  3626. {
  3627. studiomeshgroup_t* pMeshGroup = &pStudioMeshData[pMesh->meshid].m_pMeshGroup[stripGroupID];
  3628. ColorMeshInfo_t* pColorMeshInfo = &pColorMeshData->m_pMeshInfos[pMeshGroup->m_ColorMeshID];
  3629. CMeshBuilder meshBuilder;
  3630. meshBuilder.Begin( pColorMeshInfo->m_pMesh, MATERIAL_HETEROGENOUS, pMeshGroup->m_NumVertices, 0 );
  3631. if ( !meshBuilder.VertexSize() )
  3632. {
  3633. meshBuilder.End();
  3634. return false; // Aborting processing, since something was wrong with D3D
  3635. }
  3636. // We need to account for the stream offset used by pool-allocated (static-lit) color meshes:
  3637. int streamOffset = pColorMeshInfo->m_nVertOffsetInBytes / meshBuilder.VertexSize();
  3638. meshBuilder.AdvanceVertices( streamOffset );
  3639. // Iterate over all vertices
  3640. for ( int i = 0; i < pMeshGroup->m_NumVertices; ++i)
  3641. {
  3642. int nVertIndex = pMesh->vertexoffset + pMeshGroup->m_pGroupIndexToMeshIndex[i];
  3643. Assert( nVertIndex < pModel->numvertices );
  3644. meshBuilder.Specular3ub( tmpLightingMem[nVertIndex].r, tmpLightingMem[nVertIndex].g, tmpLightingMem[nVertIndex].b );
  3645. meshBuilder.AdvanceVertex();
  3646. }
  3647. meshBuilder.End();
  3648. }
  3649. }
  3650. }
  3651. }
  3652. }
  3653. pColorMeshData->m_bColorMeshValid = true;
  3654. pColorMeshData->m_bNeedsRetry = false;
  3655. #endif
  3656. return true;
  3657. }
  3658. //-----------------------------------------------------------------------------
  3659. // FIXME? : Move this to StudioRender?
  3660. //-----------------------------------------------------------------------------
  3661. void CModelRender::DestroyStaticPropColorData( ModelInstanceHandle_t handle )
  3662. {
  3663. #ifndef SWDS
  3664. if ( handle == MODEL_INSTANCE_INVALID )
  3665. return;
  3666. if ( m_ModelInstances[handle].m_ColorMeshHandle != DC_INVALID_HANDLE )
  3667. {
  3668. CacheRemove( m_ModelInstances[handle].m_ColorMeshHandle );
  3669. m_ModelInstances[handle].m_ColorMeshHandle = DC_INVALID_HANDLE;
  3670. }
  3671. #endif
  3672. }
  3673. void CModelRender::ReleaseAllStaticPropColorData( void )
  3674. {
  3675. FOR_EACH_LL( m_ModelInstances, i )
  3676. {
  3677. DestroyStaticPropColorData( i );
  3678. }
  3679. if ( IsX360() )
  3680. {
  3681. PurgeCachedStaticPropColorData();
  3682. }
  3683. }
  3684. void CModelRender::RestoreAllStaticPropColorData( void )
  3685. {
  3686. #if !defined( SWDS )
  3687. if ( !host_state.worldmodel )
  3688. return;
  3689. // invalidate all static lighting cache data
  3690. InvalidateStaticLightingCache();
  3691. // rebake
  3692. FOR_EACH_LL( m_ModelInstances, i )
  3693. {
  3694. UpdateStaticPropColorData( m_ModelInstances[i].m_pRenderable->GetIClientUnknown(), i );
  3695. }
  3696. #endif
  3697. }
  3698. void RestoreAllStaticPropColorData( void )
  3699. {
  3700. s_ModelRender.RestoreAllStaticPropColorData();
  3701. }
  3702. //-----------------------------------------------------------------------------
  3703. // Creates, destroys instance data to be associated with the model
  3704. //-----------------------------------------------------------------------------
  3705. ModelInstanceHandle_t CModelRender::CreateInstance( IClientRenderable *pRenderable, LightCacheHandle_t *pCache )
  3706. {
  3707. Assert( pRenderable );
  3708. // ensure all components are available
  3709. model_t *pModel = (model_t*)pRenderable->GetModel();
  3710. // We're ok, allocate a new instance handle
  3711. ModelInstanceHandle_t handle = m_ModelInstances.AddToTail();
  3712. ModelInstance_t& instance = m_ModelInstances[handle];
  3713. instance.m_pRenderable = pRenderable;
  3714. instance.m_DecalHandle = STUDIORENDER_DECAL_INVALID;
  3715. instance.m_pModel = (model_t*)pModel;
  3716. instance.m_ColorMeshHandle = DC_INVALID_HANDLE;
  3717. instance.m_flLightingTime = CURRENT_LIGHTING_UNINITIALIZED;
  3718. instance.m_nFlags = 0;
  3719. instance.m_LightCacheHandle = 0;
  3720. instance.m_AmbientLightingState.ZeroLightingState();
  3721. for ( int i = 0; i < 6; ++i )
  3722. {
  3723. // To catch errors with uninitialized m_AmbientLightingState...
  3724. // force to pure red
  3725. instance.m_AmbientLightingState.r_boxcolor[i].x = 1.0;
  3726. }
  3727. #ifndef SWDS
  3728. instance.m_FirstShadow = g_pShadowMgr->InvalidShadowIndex();
  3729. #endif
  3730. // Static props use baked lighting for performance reasons
  3731. if ( pCache )
  3732. {
  3733. SetStaticLighting( handle, pCache );
  3734. // validate static color meshes once, now at load/create time
  3735. ValidateStaticPropColorData( handle );
  3736. // 360 persists the color meshes across same map loads
  3737. if ( !IsX360() || instance.m_ColorMeshHandle == DC_INVALID_HANDLE )
  3738. {
  3739. // builds out color meshes or loads disk colors, now at load/create time
  3740. RecomputeStaticLighting( handle );
  3741. }
  3742. else
  3743. if ( r_decalstaticprops.GetBool() && instance.m_LightCacheHandle )
  3744. {
  3745. instance.m_AmbientLightingState = *(LightcacheGetStatic( *pCache, NULL, LIGHTCACHEFLAGS_STATIC ));
  3746. }
  3747. }
  3748. return handle;
  3749. }
  3750. //-----------------------------------------------------------------------------
  3751. // Assigns static lighting to the model instance
  3752. //-----------------------------------------------------------------------------
  3753. void CModelRender::SetStaticLighting( ModelInstanceHandle_t handle, LightCacheHandle_t *pCache )
  3754. {
  3755. // FIXME: If we make static lighting available for client-side props,
  3756. // we must clean up the lightcache handles as the model instances are removed.
  3757. // At the moment, since only the static prop manager uses this, it cleans up all LightCacheHandles
  3758. // at level shutdown.
  3759. // The reason I moved the lightcache handles into here is because this place needs
  3760. // to know about lighting overrides when restoring meshes for alt-tab reasons
  3761. // It was a real pain to do this from within the static prop mgr, where the
  3762. // lightcache handle used to reside
  3763. if (handle != MODEL_INSTANCE_INVALID)
  3764. {
  3765. ModelInstance_t& instance = m_ModelInstances[handle];
  3766. if ( pCache )
  3767. {
  3768. instance.m_LightCacheHandle = *pCache;
  3769. instance.m_nFlags |= MODEL_INSTANCE_HAS_STATIC_LIGHTING;
  3770. }
  3771. else
  3772. {
  3773. instance.m_LightCacheHandle = 0;
  3774. instance.m_nFlags &= ~MODEL_INSTANCE_HAS_STATIC_LIGHTING;
  3775. }
  3776. }
  3777. }
  3778. LightCacheHandle_t CModelRender::GetStaticLighting( ModelInstanceHandle_t handle )
  3779. {
  3780. if (handle != MODEL_INSTANCE_INVALID)
  3781. {
  3782. ModelInstance_t& instance = m_ModelInstances[handle];
  3783. if ( instance.m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING )
  3784. return instance.m_LightCacheHandle;
  3785. return 0;
  3786. }
  3787. return NULL;
  3788. }
  3789. //-----------------------------------------------------------------------------
  3790. // This gets called when overbright, etc gets changed to recompute static prop lighting.
  3791. // Returns FALSE if needed async data not available to complete computation or an error (don't draw).
  3792. // Returns TRUE if operation succeeded or computation skipped (ok to draw).
  3793. // Callers use this to track state in a retry pattern, so the expensive computation
  3794. // only happens once as needed or can continue to be polled until success.
  3795. //-----------------------------------------------------------------------------
  3796. bool CModelRender::RecomputeStaticLighting( ModelInstanceHandle_t handle )
  3797. {
  3798. #ifndef SWDS
  3799. if ( handle == MODEL_INSTANCE_INVALID )
  3800. {
  3801. return false;
  3802. }
  3803. if ( !g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() )
  3804. {
  3805. // static lighting not supported, but callers can proceed
  3806. return true;
  3807. }
  3808. ModelInstance_t& instance = m_ModelInstances[handle];
  3809. Assert( modelloader->IsLoaded( instance.m_pModel ) && ( instance.m_pModel->type == mod_studio ) );
  3810. // get data, possibly delayed due to async
  3811. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( instance.m_pModel->studio );
  3812. if ( !pStudioHdr )
  3813. {
  3814. // data not available
  3815. return false;
  3816. }
  3817. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP )
  3818. {
  3819. // get data, possibly delayed due to async
  3820. studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( instance.m_pModel->studio );
  3821. if ( !pStudioHWData )
  3822. {
  3823. // data not available
  3824. return false;
  3825. }
  3826. if ( r_decalstaticprops.GetBool() && instance.m_LightCacheHandle )
  3827. {
  3828. instance.m_AmbientLightingState = *(LightcacheGetStatic( instance.m_LightCacheHandle, NULL, LIGHTCACHEFLAGS_STATIC ));
  3829. }
  3830. return UpdateStaticPropColorData( instance.m_pRenderable->GetIClientUnknown(), handle );
  3831. }
  3832. #endif
  3833. // success
  3834. return true;
  3835. }
  3836. void CModelRender::PurgeCachedStaticPropColorData( void )
  3837. {
  3838. // valid for 360 only
  3839. Assert( IsX360() );
  3840. if ( IsPC() )
  3841. {
  3842. return;
  3843. }
  3844. // flush all the color mesh data
  3845. GetCacheSection()->Flush( true, true );
  3846. DataCacheStatus_t status;
  3847. GetCacheSection()->GetStatus( &status );
  3848. if ( status.nBytes )
  3849. {
  3850. DevWarning( "CModelRender: ColorMesh %d bytes failed to flush!\n", status.nBytes );
  3851. }
  3852. m_colorMeshVBAllocator.Clear();
  3853. m_CachedStaticPropColorData.Purge();
  3854. }
  3855. bool CModelRender::IsStaticPropColorDataCached( const char *pName )
  3856. {
  3857. // valid for 360 only
  3858. Assert( IsX360() );
  3859. if ( IsPC() )
  3860. {
  3861. return false;
  3862. }
  3863. DataCacheHandle_t hColorMesh = DC_INVALID_HANDLE;
  3864. {
  3865. AUTO_LOCK( m_CachedStaticPropMutex );
  3866. int iIndex = m_CachedStaticPropColorData.Find( pName );
  3867. if ( m_CachedStaticPropColorData.IsValidIndex( iIndex ) )
  3868. {
  3869. hColorMesh = m_CachedStaticPropColorData[iIndex];
  3870. }
  3871. }
  3872. CColorMeshData *pColorMeshData = CacheGetNoTouch( hColorMesh );
  3873. if ( pColorMeshData )
  3874. {
  3875. // color mesh data is in cache
  3876. return true;
  3877. }
  3878. return false;
  3879. }
  3880. DataCacheHandle_t CModelRender::GetCachedStaticPropColorData( const char *pName )
  3881. {
  3882. // valid for 360 only
  3883. Assert( IsX360() );
  3884. if ( IsPC() )
  3885. {
  3886. return DC_INVALID_HANDLE;
  3887. }
  3888. DataCacheHandle_t hColorMesh = DC_INVALID_HANDLE;
  3889. {
  3890. AUTO_LOCK( m_CachedStaticPropMutex );
  3891. int iIndex = m_CachedStaticPropColorData.Find( pName );
  3892. if ( m_CachedStaticPropColorData.IsValidIndex( iIndex ) )
  3893. {
  3894. hColorMesh = m_CachedStaticPropColorData[iIndex];
  3895. }
  3896. }
  3897. return hColorMesh;
  3898. }
  3899. void CModelRender::SetupColorMeshes( int nTotalVerts )
  3900. {
  3901. Assert( IsX360() );
  3902. if ( IsPC() )
  3903. {
  3904. return;
  3905. }
  3906. if ( !g_pQueuedLoader->IsMapLoading() )
  3907. {
  3908. // oops, the queued loader didn't run which does the pre-purge cleanup
  3909. // do the cleanup now
  3910. PurgeCachedStaticPropColorData();
  3911. }
  3912. // Set up the appropriate default value for color mesh pooling
  3913. if ( r_proplightingpooling.GetInt() == -1 )
  3914. {
  3915. // This is useful on X360 because VBs are 4-KB aligned, so using a shared VB saves tons of memory
  3916. r_proplightingpooling.SetValue( true );
  3917. }
  3918. if ( r_proplightingpooling.GetInt() == 1 )
  3919. {
  3920. if ( m_colorMeshVBAllocator.GetNumVertsAllocated() == 0 )
  3921. {
  3922. if ( nTotalVerts )
  3923. {
  3924. // Allocate a mesh (vertex buffer) big enough to accommodate all static prop color meshes
  3925. // (which are allocated inside CModelRender::FindOrCreateStaticPropColorData() ):
  3926. m_colorMeshVBAllocator.Init( VERTEX_SPECULAR, nTotalVerts );
  3927. }
  3928. }
  3929. else
  3930. {
  3931. // already allocated
  3932. // 360 keeps the color meshes during same map loads
  3933. // vb allocator already allocated, needs to match
  3934. Assert( m_colorMeshVBAllocator.GetNumVertsAllocated() == nTotalVerts );
  3935. }
  3936. }
  3937. }
  3938. void CModelRender::DestroyInstance( ModelInstanceHandle_t handle )
  3939. {
  3940. if ( handle == MODEL_INSTANCE_INVALID )
  3941. return;
  3942. g_pStudioRender->DestroyDecalList( m_ModelInstances[handle].m_DecalHandle );
  3943. #ifndef SWDS
  3944. g_pShadowMgr->RemoveAllShadowsFromModel( handle );
  3945. #endif
  3946. // 360 holds onto static prop disk color data only, to avoid redundant work during same map load
  3947. // can only persist props with disk based lighting
  3948. // check for dvd mode as a reasonable assurance that the queued loader will be responsible for a possible purge
  3949. // if the queued loader doesn't run, the purge will get caught later than intended
  3950. bool bPersistLighting = IsX360() &&
  3951. ( m_ModelInstances[handle].m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) &&
  3952. ( g_pFullFileSystem->GetDVDMode() == DVDMODE_STRICT );
  3953. if ( !bPersistLighting )
  3954. {
  3955. DestroyStaticPropColorData( handle );
  3956. }
  3957. m_ModelInstances.Remove( handle );
  3958. }
  3959. bool CModelRender::ChangeInstance( ModelInstanceHandle_t handle, IClientRenderable *pRenderable )
  3960. {
  3961. if ( handle == MODEL_INSTANCE_INVALID || !pRenderable )
  3962. return false;
  3963. ModelInstance_t& instance = m_ModelInstances[handle];
  3964. if ( instance.m_pModel != pRenderable->GetModel() )
  3965. {
  3966. DevMsg("MoveInstanceHandle: models are different!\n");
  3967. return false;
  3968. }
  3969. // ok, models are the same, change renderable pointer
  3970. instance.m_pRenderable = pRenderable;
  3971. return true;
  3972. }
  3973. //-----------------------------------------------------------------------------
  3974. // It's not valid if the model index changed + we have non-zero instance data
  3975. //-----------------------------------------------------------------------------
  3976. bool CModelRender::IsModelInstanceValid( ModelInstanceHandle_t handle )
  3977. {
  3978. if ( handle == MODEL_INSTANCE_INVALID )
  3979. return false;
  3980. ModelInstance_t& inst = m_ModelInstances[handle];
  3981. if ( inst.m_DecalHandle == STUDIORENDER_DECAL_INVALID )
  3982. return false;
  3983. model_t const* pModel = inst.m_pRenderable->GetModel();
  3984. return inst.m_pModel == pModel;
  3985. }
  3986. //-----------------------------------------------------------------------------
  3987. // Creates a decal on a model instance by doing a planar projection
  3988. //-----------------------------------------------------------------------------
  3989. void CModelRender::AddDecal( ModelInstanceHandle_t handle, Ray_t const& ray,
  3990. const Vector& decalUp, int decalIndex, int body, bool noPokeThru, int maxLODToDecal )
  3991. {
  3992. Color cColorTemp;
  3993. AddDecalInternal( handle, ray, decalUp, decalIndex, body, false, cColorTemp, noPokeThru, maxLODToDecal );
  3994. }
  3995. //-----------------------------------------------------------------------------
  3996. void CModelRender::AddColoredDecal( ModelInstanceHandle_t handle, Ray_t const& ray,
  3997. const Vector& decalUp, int decalIndex, int body, Color cColor, bool noPokeThru, int maxLODToDecal )
  3998. {
  3999. AddDecalInternal( handle, ray, decalUp, decalIndex, body, true, cColor, noPokeThru, maxLODToDecal );
  4000. }
  4001. //-----------------------------------------------------------------------------
  4002. void CModelRender::GetMaterialOverride( IMaterial** ppOutForcedMaterial, OverrideType_t* pOutOverrideType )
  4003. {
  4004. g_pStudioRender->GetMaterialOverride( ppOutForcedMaterial, pOutOverrideType );
  4005. }
  4006. //-----------------------------------------------------------------------------
  4007. void CModelRender::AddDecalInternal( ModelInstanceHandle_t handle, Ray_t const& ray,
  4008. const Vector& decalUp, int decalIndex, int body, bool bUseColor, Color cColor, bool noPokeThru, int maxLODToDecal)
  4009. {
  4010. if (handle == MODEL_INSTANCE_INVALID)
  4011. return;
  4012. // Get the decal material + radius
  4013. IMaterial* pDecalMaterial;
  4014. float w, h;
  4015. R_DecalGetMaterialAndSize( decalIndex, pDecalMaterial, w, h );
  4016. if ( !pDecalMaterial )
  4017. {
  4018. DevWarning("Bad decal index %d\n", decalIndex );
  4019. return;
  4020. }
  4021. w *= 0.5f;
  4022. h *= 0.5f;
  4023. // FIXME: For now, don't render fading decals on props...
  4024. bool found = false;
  4025. pDecalMaterial->FindVar( "$decalFadeDuration", &found, false );
  4026. if ( found )
  4027. return;
  4028. if ( bUseColor )
  4029. {
  4030. IMaterialVar *pColor = pDecalMaterial->FindVar( "$color2", &found, false );
  4031. if ( found )
  4032. {
  4033. // expects a 0..1 value. Input is 0 to 255
  4034. pColor->SetVecValue( cColor.r() / 255.0f, cColor.g() / 255.0f, cColor.b() / 255.0f );
  4035. }
  4036. }
  4037. // FIXME: Pass w and h into AddDecal
  4038. float radius = (w > h) ? w : h;
  4039. ModelInstance_t& inst = m_ModelInstances[handle];
  4040. if (!IsModelInstanceValid(handle))
  4041. {
  4042. g_pStudioRender->DestroyDecalList(inst.m_DecalHandle);
  4043. inst.m_DecalHandle = STUDIORENDER_DECAL_INVALID;
  4044. }
  4045. Assert( modelloader->IsLoaded( inst.m_pModel ) && ( inst.m_pModel->type == mod_studio ) );
  4046. if ( inst.m_DecalHandle == STUDIORENDER_DECAL_INVALID )
  4047. {
  4048. studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( inst.m_pModel->studio );
  4049. inst.m_DecalHandle = g_pStudioRender->CreateDecalList( pStudioHWData );
  4050. }
  4051. matrix3x4_t *pBoneToWorld = SetupModelState( inst.m_pRenderable );
  4052. g_pStudioRender->AddDecal( inst.m_DecalHandle, g_pMDLCache->GetStudioHdr( inst.m_pModel->studio ),
  4053. pBoneToWorld, ray, decalUp, pDecalMaterial, radius, body, noPokeThru, maxLODToDecal );
  4054. }
  4055. //-----------------------------------------------------------------------------
  4056. // Purpose: Removes all the decals on a model instance
  4057. //-----------------------------------------------------------------------------
  4058. void CModelRender::RemoveAllDecals( ModelInstanceHandle_t handle )
  4059. {
  4060. if (handle == MODEL_INSTANCE_INVALID)
  4061. return;
  4062. ModelInstance_t& inst = m_ModelInstances[handle];
  4063. if (!IsModelInstanceValid(handle))
  4064. return;
  4065. g_pStudioRender->DestroyDecalList( inst.m_DecalHandle );
  4066. inst.m_DecalHandle = STUDIORENDER_DECAL_INVALID;
  4067. }
  4068. //-----------------------------------------------------------------------------
  4069. // Purpose:
  4070. //-----------------------------------------------------------------------------
  4071. void CModelRender::RemoveAllDecalsFromAllModels()
  4072. {
  4073. for ( ModelInstanceHandle_t i = m_ModelInstances.Head();
  4074. i != m_ModelInstances.InvalidIndex();
  4075. i = m_ModelInstances.Next( i ) )
  4076. {
  4077. RemoveAllDecals( i );
  4078. }
  4079. }
  4080. const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
  4081. {
  4082. // make requested data resident
  4083. Assert( pModelData == NULL );
  4084. return s_ModelRender.CacheVertexData();
  4085. }
  4086. bool CheckVarRange_r_rootlod()
  4087. {
  4088. return CheckVarRange_Generic( &r_rootlod, 0, 2 );
  4089. }
  4090. bool CheckVarRange_r_lod()
  4091. {
  4092. return CheckVarRange_Generic( &r_lod, -1, 2 );
  4093. }
  4094. // Convar callback to change lod
  4095. //-----------------------------------------------------------------------------
  4096. void r_lod_f( IConVar *var, const char *pOldValue, float flOldValue )
  4097. {
  4098. CheckVarRange_r_lod();
  4099. }
  4100. //-----------------------------------------------------------------------------
  4101. // Convar callback to change root lod
  4102. //-----------------------------------------------------------------------------
  4103. void SetRootLOD_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  4104. {
  4105. // Make sure the variable is in range.
  4106. if ( CheckVarRange_r_rootlod() )
  4107. return; // was called recursively.
  4108. ConVarRef var( pConVar );
  4109. UpdateStudioRenderConfig();
  4110. if ( !g_LostVideoMemory && Q_strcmp( var.GetString(), pOldString ) )
  4111. {
  4112. // reload only the necessary models to desired lod
  4113. modelloader->Studio_ReloadModels( IModelLoader::RELOAD_LOD_CHANGED );
  4114. }
  4115. }
  4116. //-----------------------------------------------------------------------------
  4117. // Discard and reload (rebuild, rebake, etc) models to the current lod
  4118. //-----------------------------------------------------------------------------
  4119. void FlushLOD_f()
  4120. {
  4121. UpdateStudioRenderConfig();
  4122. if ( !g_LostVideoMemory )
  4123. {
  4124. // force a full discard and rebuild of all loaded models
  4125. modelloader->Studio_ReloadModels( IModelLoader::RELOAD_EVERYTHING );
  4126. }
  4127. }
  4128. //-----------------------------------------------------------------------------
  4129. //
  4130. // CPooledVBAllocator_ColorMesh implementation
  4131. //
  4132. //-----------------------------------------------------------------------------
  4133. //-----------------------------------------------------------------------------
  4134. // CPooledVBAllocator_ColorMesh constructor
  4135. //-----------------------------------------------------------------------------
  4136. CPooledVBAllocator_ColorMesh::CPooledVBAllocator_ColorMesh()
  4137. : m_pMesh( NULL )
  4138. {
  4139. Clear();
  4140. }
  4141. //-----------------------------------------------------------------------------
  4142. // CPooledVBAllocator_ColorMesh destructor
  4143. // - Clear should have been called
  4144. //-----------------------------------------------------------------------------
  4145. CPooledVBAllocator_ColorMesh::~CPooledVBAllocator_ColorMesh()
  4146. {
  4147. CheckIsClear();
  4148. // Clean up, if it hadn't been done already
  4149. Clear();
  4150. }
  4151. //-----------------------------------------------------------------------------
  4152. // Init
  4153. // - Allocate the internal shared mesh (vertex buffer)
  4154. //-----------------------------------------------------------------------------
  4155. bool CPooledVBAllocator_ColorMesh::Init( VertexFormat_t format, int numVerts )
  4156. {
  4157. if ( !CheckIsClear() )
  4158. return false;
  4159. if ( g_VBAllocTracker )
  4160. g_VBAllocTracker->TrackMeshAllocations( "CPooledVBAllocator_ColorMesh::Init" );
  4161. CMatRenderContextPtr pRenderContext( materials );
  4162. m_pMesh = pRenderContext->CreateStaticMesh( format, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_COLOR );
  4163. if ( m_pMesh )
  4164. {
  4165. // Build out the underlying vertex buffer
  4166. CMeshBuilder meshBuilder;
  4167. int numIndices = 0;
  4168. meshBuilder.Begin( m_pMesh, MATERIAL_HETEROGENOUS, numVerts, numIndices );
  4169. {
  4170. m_pVertexBufferBase = meshBuilder.Specular();
  4171. m_totalVerts = numVerts;
  4172. m_vertexSize = meshBuilder.VertexSize();
  4173. // Probably good to catch any change to vertex size... there may be assumptions based on it:
  4174. Assert( m_vertexSize == 4 );
  4175. // Start at the bottom of the VB and work your way up like a simple stack
  4176. m_nextFreeOffset = 0;
  4177. }
  4178. meshBuilder.End();
  4179. }
  4180. if ( g_VBAllocTracker )
  4181. g_VBAllocTracker->TrackMeshAllocations( NULL );
  4182. return ( m_pMesh != NULL );
  4183. }
  4184. //-----------------------------------------------------------------------------
  4185. // Clear
  4186. // - frees the shared mesh (vertex buffer), resets member variables
  4187. //-----------------------------------------------------------------------------
  4188. void CPooledVBAllocator_ColorMesh::Clear( void )
  4189. {
  4190. if ( m_pMesh != NULL )
  4191. {
  4192. if ( m_numAllocations > 0 )
  4193. {
  4194. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Clear should not be called until all allocations released!" );
  4195. Assert( m_numAllocations == 0 );
  4196. }
  4197. CMatRenderContextPtr pRenderContext( materials );
  4198. pRenderContext->DestroyStaticMesh( m_pMesh );
  4199. m_pMesh = NULL;
  4200. }
  4201. m_pVertexBufferBase = NULL;
  4202. m_totalVerts = 0;
  4203. m_vertexSize = 0;
  4204. m_numAllocations = 0;
  4205. m_numVertsAllocated = 0;
  4206. m_nextFreeOffset = -1;
  4207. m_bStartedDeallocation = false;
  4208. }
  4209. //-----------------------------------------------------------------------------
  4210. // CheckIsClear
  4211. // - assert/warn if the allocator isn't in a clear state
  4212. // (no extant allocations, no internal mesh)
  4213. //-----------------------------------------------------------------------------
  4214. bool CPooledVBAllocator_ColorMesh::CheckIsClear( void )
  4215. {
  4216. if ( m_pMesh )
  4217. {
  4218. Warning( "ERROR: CPooledVBAllocator_ColorMesh's internal mesh (vertex buffer) should have been freed!" );
  4219. Assert( m_pMesh == NULL );
  4220. return false;
  4221. }
  4222. if ( m_numAllocations > 0 )
  4223. {
  4224. Warning( "ERROR: CPooledVBAllocator_ColorMesh has unfreed allocations!" );
  4225. Assert( m_numAllocations == 0 );
  4226. return false;
  4227. }
  4228. return true;
  4229. }
  4230. //-----------------------------------------------------------------------------
  4231. // Allocate
  4232. // - Allocate a sub-range of 'numVerts' from free space in the shared vertex buffer
  4233. // (returns the byte offset from the start of the VB to the new allocation)
  4234. // - returns -1 on failure
  4235. //-----------------------------------------------------------------------------
  4236. int CPooledVBAllocator_ColorMesh::Allocate( int numVerts )
  4237. {
  4238. if ( m_pMesh == NULL )
  4239. {
  4240. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Allocate cannot be called before Init (expect a crash)" );
  4241. Assert( m_pMesh );
  4242. return -1;
  4243. }
  4244. // Once we start deallocating, we have to keep going until everything has been freed
  4245. if ( m_bStartedDeallocation )
  4246. {
  4247. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Allocate being called after some (but not all) calls to Deallocate have been called - invalid! (expect visual artifacts)" );
  4248. Assert( !m_bStartedDeallocation );
  4249. return -1;
  4250. }
  4251. if ( numVerts > ( m_totalVerts - m_numVertsAllocated ) )
  4252. {
  4253. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Allocate failing - not enough space left in the vertex buffer!" );
  4254. Assert( numVerts <= ( m_totalVerts - m_numVertsAllocated ) );
  4255. return -1;
  4256. }
  4257. int result = m_nextFreeOffset;
  4258. m_numAllocations += 1;
  4259. m_numVertsAllocated += numVerts;
  4260. m_nextFreeOffset += numVerts*m_vertexSize;
  4261. return result;
  4262. }
  4263. //-----------------------------------------------------------------------------
  4264. // Deallocate
  4265. // - Deallocate an existing allocation
  4266. //-----------------------------------------------------------------------------
  4267. void CPooledVBAllocator_ColorMesh::Deallocate( int offset, int numVerts )
  4268. {
  4269. if ( m_pMesh == NULL )
  4270. {
  4271. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Deallocate cannot be called before Init" );
  4272. Assert( m_pMesh != NULL );
  4273. return;
  4274. }
  4275. if ( m_numAllocations == 0 )
  4276. {
  4277. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Deallocate called too many times! (bug in calling code)" );
  4278. Assert( m_numAllocations > 0 );
  4279. return;
  4280. }
  4281. if ( numVerts > m_numVertsAllocated )
  4282. {
  4283. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Deallocate called with too many verts, trying to free more than were allocated (bug in calling code)" );
  4284. Assert( numVerts <= m_numVertsAllocated );
  4285. numVerts = m_numVertsAllocated; // Hack (avoid counters ever going below zero)
  4286. }
  4287. // Now all extant allocations must be freed before we make any new allocations
  4288. m_bStartedDeallocation = true;
  4289. m_numAllocations -= 1;
  4290. m_numVertsAllocated -= numVerts;
  4291. m_nextFreeOffset = 0; // (we shouldn't be returning this until everything's free, at which point 0 is valid)
  4292. // Are we empty?
  4293. if ( m_numAllocations == 0 )
  4294. {
  4295. if ( m_numVertsAllocated != 0 )
  4296. {
  4297. Warning( "ERROR: CPooledVBAllocator_ColorMesh::Deallocate, after all allocations have been freed too few verts total have been deallocated (bug in calling code)" );
  4298. Assert( m_numVertsAllocated == 0 );
  4299. }
  4300. // We can start allocating again, now
  4301. m_bStartedDeallocation = false;
  4302. }
  4303. }
  4304. //-----------------------------------------------------------------------------
  4305. // CreateLightmapsFromData
  4306. // - Creates Lightmap Textures from data that was squirreled away during ASYNC load.
  4307. // This is necessary because the material system doesn't like us creating things from ASYNC loaders.
  4308. //-----------------------------------------------------------------------------
  4309. static void CreateLightmapsFromData(CColorMeshData* _colorMeshData)
  4310. {
  4311. Assert(_colorMeshData->m_bColorTextureValid);
  4312. Assert(!_colorMeshData->m_bColorTextureCreated);
  4313. for (int mesh = 0; mesh < _colorMeshData->m_nMeshes; ++mesh)
  4314. {
  4315. ColorMeshInfo_t* meshInfo = &_colorMeshData->m_pMeshInfos[mesh];
  4316. // Ensure that we haven't somehow already messed with these.
  4317. Assert(meshInfo->m_pLightmapData);
  4318. Assert(!meshInfo->m_pLightmap);
  4319. ColorTexelsInfo_t* cti = meshInfo->m_pLightmapData;
  4320. Assert(cti->m_pTexelData);
  4321. meshInfo->m_pLightmap = g_pMaterialSystem->CreateTextureFromBits(cti->m_nWidth, cti->m_nHeight, cti->m_nMipmapCount, cti->m_ImageFormat, cti->m_nByteCount, cti->m_pTexelData);
  4322. // If this triggers, we need to figure out if it's reasonable to fail. If it is, then we should figure out how to signal back
  4323. // that we shouldn't try to create this again (probably by clearing _colorMeshData->m_bColoTextureValid)
  4324. Assert(meshInfo->m_pLightmap);
  4325. // Cleanup after ourselves.
  4326. delete [] cti->m_pTexelData;
  4327. delete cti;
  4328. meshInfo->m_pLightmapData = NULL;
  4329. }
  4330. _colorMeshData->m_bColorTextureCreated = true;
  4331. }