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

5656 lines
191 KiB

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