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.

935 lines
30 KiB

  1. //===== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "studio.h"
  10. #include "studiorendercontext.h"
  11. #include "bitmap/imageformat.h"
  12. #include "materialsystem/imaterialsystem.h"
  13. #include "materialsystem/imaterial.h"
  14. #include "materialsystem/imaterialvar.h"
  15. #include "materialsystem/itexture.h"
  16. #include "materialsystem/imesh.h"
  17. #include "mathlib/mathlib.h"
  18. #include "studiorender.h"
  19. #include "pixelwriter.h"
  20. #include "vtf/vtf.h"
  21. #include "tier1/convar.h"
  22. #include "tier1/keyvalues.h"
  23. #include "tier0/vprof.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define sign( a ) (((a) < 0) ? -1 : (((a) > 0) ? 1 : 0 ))
  27. void CStudioRender::R_StudioEyeballPosition( const mstudioeyeball_t *peyeball, eyeballstate_t *pstate )
  28. {
  29. // Vector forward;
  30. // Vector org, right, up;
  31. pstate->peyeball = peyeball;
  32. Vector tmp;
  33. // move eyeball into worldspace
  34. {
  35. // ConDMsg("%.2f %.2f %.2f\n", peyeball->org[0], peyeball->org[1], peyeball->org[2] );
  36. VectorCopy( peyeball->org, tmp );
  37. tmp[0] += m_pRC->m_Config.fEyeShiftX * sign( tmp[0] );
  38. tmp[1] += m_pRC->m_Config.fEyeShiftY * sign( tmp[1] );
  39. tmp[2] += m_pRC->m_Config.fEyeShiftZ * sign( tmp[2] );
  40. }
  41. VectorTransform( tmp, m_pBoneToWorld[peyeball->bone], pstate->org );
  42. VectorRotate( peyeball->up, m_pBoneToWorld[peyeball->bone], pstate->up );
  43. // look directly at target
  44. VectorSubtract( m_pRC->m_ViewTarget, pstate->org, pstate->forward );
  45. VectorNormalize( pstate->forward );
  46. if ( !m_pRC->m_Config.bEyeMove )
  47. {
  48. VectorRotate( peyeball->forward, m_pBoneToWorld[peyeball->bone], pstate->forward );
  49. VectorScale( pstate->forward, -1 ,pstate->forward ); // ???
  50. }
  51. CrossProduct( pstate->forward, pstate->up, pstate->right );
  52. VectorNormalize( pstate->right );
  53. // shift N degrees off of the target
  54. float dz;
  55. dz = peyeball->zoffset;
  56. VectorMA( pstate->forward, peyeball->zoffset + dz, pstate->right, pstate->forward );
  57. #if 0
  58. // add random jitter
  59. VectorMA( forward, RandomFloat( -0.02, 0.02 ), right, forward );
  60. VectorMA( forward, RandomFloat( -0.02, 0.02 ), up, forward );
  61. #endif
  62. VectorNormalize( pstate->forward );
  63. // re-aim eyes
  64. CrossProduct( pstate->forward, pstate->up, pstate->right );
  65. VectorNormalize( pstate->right );
  66. CrossProduct( pstate->right, pstate->forward, pstate->up );
  67. VectorNormalize( pstate->up );
  68. float scale = (1.0 / peyeball->iris_scale) + m_pRC->m_Config.fEyeSize;
  69. if (scale > 0)
  70. scale = 1.0 / scale;
  71. VectorScale( &pstate->right[0], -scale, pstate->mat[0] );
  72. VectorScale( &pstate->up[0], -scale, pstate->mat[1] );
  73. pstate->mat[0][3] = -DotProduct( &pstate->org[0], pstate->mat[0] ) + 0.5f;
  74. pstate->mat[1][3] = -DotProduct( &pstate->org[0], pstate->mat[1] ) + 0.5f;
  75. // FIXME: push out vertices for cornea
  76. }
  77. //-----------------------------------------------------------------------------
  78. //
  79. //-----------------------------------------------------------------------------
  80. void CStudioRender::R_StudioEyelidFACS( const mstudioeyeball_t *peyeball, const eyeballstate_t *pstate )
  81. {
  82. if ( peyeball->m_bNonFACS )
  83. return;
  84. Vector pos, headup, headforward;
  85. float upperlid = DEG2RAD( 9.5 );
  86. float lowerlid = DEG2RAD( -26.4 );
  87. // FIXME: Crash workaround
  88. Vector vecNormTarget;
  89. vecNormTarget.Init( peyeball->uppertarget[0], peyeball->uppertarget[1], peyeball->uppertarget[2] );
  90. vecNormTarget /= peyeball->radius;
  91. vecNormTarget.x = clamp( vecNormTarget.x, -1.0f, 1.0f );
  92. vecNormTarget.y = clamp( vecNormTarget.y, -1.0f, 1.0f );
  93. vecNormTarget.z = clamp( vecNormTarget.z, -1.0f, 1.0f );
  94. // get weighted position of eyeball angles based on the "raiser", "neutral", and "lowerer" controls
  95. upperlid = m_pFlexWeights[peyeball->upperflexdesc[0]] * asin( vecNormTarget.x );
  96. upperlid += m_pFlexWeights[peyeball->upperflexdesc[1]] * asin( vecNormTarget.y );
  97. upperlid += m_pFlexWeights[peyeball->upperflexdesc[2]] * asin( vecNormTarget.z );
  98. vecNormTarget.Init( peyeball->lowertarget[0], peyeball->lowertarget[1], peyeball->lowertarget[2] );
  99. vecNormTarget /= peyeball->radius;
  100. vecNormTarget.x = clamp( vecNormTarget.x, -1.0f, 1.0f );
  101. vecNormTarget.y = clamp( vecNormTarget.y, -1.0f, 1.0f );
  102. vecNormTarget.z = clamp( vecNormTarget.z, -1.0f, 1.0f );
  103. lowerlid = m_pFlexWeights[peyeball->lowerflexdesc[0]] * asin( vecNormTarget.x );
  104. lowerlid += m_pFlexWeights[peyeball->lowerflexdesc[1]] * asin( vecNormTarget.y );
  105. lowerlid += m_pFlexWeights[peyeball->lowerflexdesc[2]] * asin( vecNormTarget.z );
  106. // ConDMsg("%.1f %.1f\n", RAD2DEG( upperlid ), RAD2DEG( lowerlid ) );
  107. float sinupper, cosupper, sinlower, coslower;
  108. SinCos( upperlid, &sinupper, &cosupper );
  109. SinCos( lowerlid, &sinlower, &coslower );
  110. // convert to head relative space
  111. VectorIRotate( pstate->up, m_pBoneToWorld[peyeball->bone], headup );
  112. VectorIRotate( pstate->forward, m_pBoneToWorld[peyeball->bone], headforward );
  113. // upper lid
  114. VectorScale( headup, sinupper * peyeball->radius, pos );
  115. VectorMA( pos, cosupper * peyeball->radius, headforward, pos );
  116. m_pFlexWeights[peyeball->upperlidflexdesc] = DotProduct( pos, peyeball->up );
  117. // lower lid
  118. VectorScale( headup, sinlower * peyeball->radius, pos );
  119. VectorMA( pos, coslower * peyeball->radius, headforward, pos );
  120. m_pFlexWeights[peyeball->lowerlidflexdesc] = DotProduct( pos, peyeball->up );
  121. // ConDMsg("%.4f %.4f\n", m_pRC->m_FlexWeights[peyeball->upperlidflex], m_pRC->m_FlexWeights[peyeball->lowerlidflex] );
  122. }
  123. void CStudioRender::MaterialPlanerProjection( const matrix3x4_t& mat, int count, const Vector *psrcverts, Vector2D *pdesttexcoords )
  124. {
  125. for (int i = 0; i < count; i++)
  126. {
  127. pdesttexcoords[i][0] = DotProduct( &psrcverts[i].x, mat[0] ) + mat[0][3];
  128. pdesttexcoords[i][1] = DotProduct( &psrcverts[i].x, mat[1] ) + mat[1][3];
  129. }
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Ramp and clamp the flex weight
  133. //-----------------------------------------------------------------------------
  134. float CStudioRender::RampFlexWeight( mstudioflex_t &flex, float w )
  135. {
  136. if ( w <= flex.target0 || w >= flex.target3 )
  137. {
  138. w = 0.0; // value outside of range
  139. }
  140. else if ( w < flex.target1 )
  141. {
  142. w = (w - flex.target0) / (flex.target1 - flex.target0); // 0 to 1 ramp
  143. }
  144. else if ( w > flex.target2 )
  145. {
  146. w = (flex.target3 - w) / (flex.target3 - flex.target2); // 1 to 0 ramp
  147. }
  148. else
  149. {
  150. w = 1.0; // plat
  151. }
  152. return w;
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Setup the flex verts for this rendering
  156. //-----------------------------------------------------------------------------
  157. void CStudioRender::R_StudioFlexVerts( mstudiomesh_t *pmesh, int lod, bool bQuadList )
  158. {
  159. VPROF_BUDGET( "CStudioRender::R_StudioFlexVerts", VPROF_BUDGETGROUP_MODEL_RENDERING );
  160. Assert( pmesh );
  161. const float flVertAnimFixedPointScale = m_pStudioHdr->VertAnimFixedPointScale();
  162. // There's a chance we can actually do the flex twice on a single mesh
  163. // since there's flexed HW + SW portions of the mesh.
  164. if ( m_VertexCache.IsFlexComputationDone() )
  165. return;
  166. // Get pointers to geometry
  167. if ( !pmesh->pModel()->CacheVertexData( m_pStudioHdr ) )
  168. {
  169. // not available yet
  170. return;
  171. }
  172. const mstudio_meshvertexdata_t *vertData = pmesh->GetVertexData( m_pStudioHdr );
  173. Assert( vertData );
  174. if ( !vertData )
  175. {
  176. static unsigned int warnCount = 0;
  177. if ( warnCount++ < 20 )
  178. Warning( "ERROR: model verts have been compressed, cannot render! (use \"-no_compressed_vvds\")" );
  179. return;
  180. }
  181. // The flex data should have been converted to the new (fixed-point) format on load:
  182. Assert( m_pStudioHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED );
  183. if ( ( m_pStudioHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED ) == 0 )
  184. {
  185. static unsigned int flexConversionTimesWarned = 0;
  186. if ( flexConversionTimesWarned++ < 6 )
  187. Warning( "ERROR: flex verts have not been converted (queued loader refcount bug?) - expect to see 'exploded' faces" );
  188. }
  189. mstudiovertex_t *pVertices = vertData->Vertex( 0 );
  190. Vector4D *pStudioTangentS = vertData->HasTangentData() ? vertData->TangentS( 0 ) : NULL;
  191. mstudioflex_t *pflex = pmesh->pFlex( 0 );
  192. m_VertexCache.SetupComputation( pmesh, true );
  193. // Apply flex weights
  194. int i, j, n;
  195. for ( i = 0; i < pmesh->numflexes; i++ )
  196. {
  197. float w1 = RampFlexWeight( pflex[i], m_pFlexWeights[ pflex[i].flexdesc ] );
  198. float w2 = RampFlexWeight( pflex[i], m_pFlexDelayedWeights[ pflex[i].flexdesc ] );
  199. float w3, w4;
  200. if ( pflex[i].flexpair != 0)
  201. {
  202. w3 = RampFlexWeight( pflex[i], m_pFlexWeights[ pflex[i].flexpair ] );
  203. w4 = RampFlexWeight( pflex[i], m_pFlexDelayedWeights[ pflex[i].flexpair ] );
  204. }
  205. else
  206. {
  207. w3 = w1;
  208. w4 = w2;
  209. }
  210. if ( w1 > -0.001 && w1 < 0.001 && w2 > -0.001 && w2 < 0.001 )
  211. {
  212. if ( w3 > -0.001 && w3 < 0.001 && w4 > -0.001 && w4 < 0.001 )
  213. {
  214. continue;
  215. }
  216. }
  217. byte *pvanim = pflex[i].pBaseVertanim();
  218. int nVAnimSizeBytes = pflex[i].VertAnimSizeBytes();
  219. bool bWrinkleFlex = pflex[i].vertanimtype == STUDIO_VERT_ANIM_WRINKLE;
  220. for ( j = 0; j < pflex[i].numverts; j++ )
  221. {
  222. mstudiovertanim_t *pAnim = (mstudiovertanim_t*)( pvanim + j * nVAnimSizeBytes );
  223. n = pAnim->index;
  224. // Only flex the indices that are (still) part of this mesh need lod restriction here
  225. if ( n < pmesh->vertexdata.numLODVertexes[lod] )
  226. {
  227. mstudiovertex_t &vert = pVertices[n];
  228. CachedPosNormTan_t* pFlexedVertex;
  229. if ( !m_VertexCache.IsVertexFlexed(n) )
  230. {
  231. pFlexedVertex = m_VertexCache.CreateFlexVertex(n); // Add a new flexed vert to the list
  232. if ( pFlexedVertex == NULL ) // Skip processing if no more can be allocated
  233. continue;
  234. VectorCopy( vert.m_vecPosition, pFlexedVertex->m_Position.AsVector3D() );
  235. pFlexedVertex->m_Position.w = 0.0f;
  236. VectorCopy( vert.m_vecNormal, pFlexedVertex->m_Normal.AsVector3D() );
  237. if ( pStudioTangentS )
  238. {
  239. Vector4DCopy( pStudioTangentS[n], pFlexedVertex->m_TangentS );
  240. Assert( pFlexedVertex->m_TangentS.w == -1.0f || pFlexedVertex->m_TangentS.w == 1.0f );
  241. }
  242. }
  243. else
  244. {
  245. pFlexedVertex = m_VertexCache.GetFlexVertex(n);
  246. }
  247. float s = pAnim->speed * ( 1.0f/255.0f );
  248. float b = pAnim->side * ( 1.0f/255.0f );
  249. float w = (w1 * s + (1.0f - s) * w2) * (1.0f - b) + b * (w3 * s + (1.0f - s) * w4);
  250. // Accumulate weighted deltas
  251. pFlexedVertex->m_Position.AsVector3D() += pAnim->GetDeltaFixed( flVertAnimFixedPointScale ) * w;
  252. if ( bWrinkleFlex )
  253. {
  254. float delta = ((mstudiovertanim_wrinkle_t *)pAnim)->GetWrinkleDeltaFixed( flVertAnimFixedPointScale );
  255. pFlexedVertex->m_Position.w += w * delta;
  256. }
  257. if ( !bQuadList )
  258. {
  259. pFlexedVertex->m_Normal.AsVector3D() += pAnim->GetNDeltaFixed( flVertAnimFixedPointScale ) * w;
  260. }
  261. if ( pStudioTangentS )
  262. {
  263. pFlexedVertex->m_TangentS.AsVector3D() += pAnim->GetNDeltaFixed( flVertAnimFixedPointScale ) * w;
  264. Assert( pFlexedVertex->m_TangentS.w == -1.0f || pFlexedVertex->m_TangentS.w == 1.0f );
  265. }
  266. }
  267. }
  268. }
  269. m_VertexCache.RenormalizeFlexVertices( vertData->HasTangentData(), bQuadList );
  270. }
  271. // REMOVED!! Look in version 32 if you need it.
  272. //static void R_StudioEyeballNormals( const mstudioeyeball_t *peyeball, int count, const Vector *psrcverts, Vector *pdestnorms )
  273. #define KERNEL_DIAMETER 2
  274. #define KERNEL_TEXELS (KERNEL_DIAMETER)
  275. #define KERNEL_TEXEL_RADIUS (KERNEL_TEXELS / 2)
  276. inline float GlintGaussSpotCoefficient( float dx, float dy /*, float *table */ )
  277. {
  278. const float radius = KERNEL_DIAMETER / 2;
  279. const float rsq = 1.0f / (radius * radius);
  280. float r2 = (dx * dx + dy * dy) * rsq;
  281. if (r2 <= 1.0f)
  282. {
  283. return exp( -25.0 * r2 );
  284. // NOTE: This optimization doesn't make much of a difference
  285. //int index = r2 * (GLINT_TABLE_ENTRIES-1);
  286. //return table[index];
  287. }
  288. return 0;
  289. }
  290. void CStudioRender::AddGlint( CPixelWriter &pixelWriter, float x, float y, const Vector& color )
  291. {
  292. x = (x + 0.5f) * m_GlintWidth;
  293. y = (y + 0.5f) * m_GlintHeight;
  294. const float texelRadius = KERNEL_DIAMETER / 2;
  295. int x0 = (int)x;
  296. int y0 = (int)y;
  297. int x1 = x0 + texelRadius;
  298. int y1 = y0 + texelRadius;
  299. x0 -= texelRadius;
  300. y0 -= texelRadius;
  301. // clip light to texture
  302. if ( (x0 >= m_GlintWidth) || (x1 < 0) || (y0 >= m_GlintHeight) || (y1 < 0) )
  303. return;
  304. // clamp coordinates
  305. if ( x0 < 0 )
  306. {
  307. x0 = 0;
  308. }
  309. if ( y0 < 0 )
  310. {
  311. y0 = 0;
  312. }
  313. if ( x1 >= m_GlintWidth )
  314. {
  315. x1 = m_GlintWidth-1;
  316. }
  317. if ( y1 >= m_GlintHeight )
  318. {
  319. y1 = m_GlintHeight-1;
  320. }
  321. for (int v = y0; v <= y1; ++v )
  322. {
  323. pixelWriter.Seek( x0, v );
  324. for (int u = x0; u <= x1; ++u )
  325. {
  326. float fu = ((float)u) - x;
  327. float fv = ((float)v) - y;
  328. const float offset = 0.25;
  329. float intensity = GlintGaussSpotCoefficient( fu-offset, fv-offset ) +
  330. GlintGaussSpotCoefficient( fu+offset, fv-offset ) +
  331. 5 * GlintGaussSpotCoefficient( fu, fv ) +
  332. GlintGaussSpotCoefficient( fu-offset, fv+offset ) +
  333. GlintGaussSpotCoefficient( fu+offset, fv+offset );
  334. // NOTE: Old filter code multiplies the signal by 8X, so we will too
  335. intensity *= (4.0f/9.0f);
  336. // NOTE: It's much faster to do the work in the dest texture than to touch the memory more
  337. // or make more buffers
  338. Vector outColor = intensity * color;
  339. int r, g, b, a;
  340. pixelWriter.ReadPixelNoAdvance( r, g, b, a );
  341. outColor.x += TextureToLinear(r);
  342. outColor.y += TextureToLinear(g);
  343. outColor.z += TextureToLinear(b);
  344. pixelWriter.WritePixel( LinearToTexture(outColor.x), LinearToTexture(outColor.y), LinearToTexture(outColor.z) );
  345. }
  346. }
  347. }
  348. //-----------------------------------------------------------------------------
  349. // glint
  350. //-----------------------------------------------------------------------------
  351. // test/stub code
  352. #if 0
  353. class CEmptyTextureRegen : public ITextureRegenerator
  354. {
  355. public:
  356. virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  357. {
  358. // get the texture
  359. unsigned char *pTextureData = pVTFTexture->ImageData( 0, 0, 0 );
  360. int nImageSize = pVTFTexture->ComputeMipSize( 0 );
  361. memset( pTextureData, 0, nImageSize );
  362. }
  363. // We've got a global instance, no need to delete it
  364. virtual void Release() {}
  365. };
  366. static CEmptyTextureRegen s_GlintTextureRegen;
  367. #endif
  368. class CGlintTextureRegenerator : public ITextureRegenerator
  369. {
  370. public:
  371. virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
  372. {
  373. // We don't need to reconstitute the bits after a task switch
  374. // since we reconstitute them every frame they are used anyways
  375. if ( !m_pStudioRender )
  376. return;
  377. if ( ( m_pStudioRender->m_GlintWidth != pVTFTexture->Width() ) ||
  378. ( m_pStudioRender->m_GlintHeight != pVTFTexture->Height() ) )
  379. {
  380. m_pStudioRender->m_GlintWidth = pVTFTexture->Width();
  381. m_pStudioRender->m_GlintHeight = pVTFTexture->Height();
  382. }
  383. CStudioRender::GlintRenderData_t pRenderData[16];
  384. int nGlintCount = m_pStudioRender->BuildGlintRenderData( pRenderData,
  385. ARRAYSIZE(pRenderData), m_pState, *m_pVRight, *m_pVUp, *m_pROrigin );
  386. // setup glint texture
  387. unsigned char *pTextureData = pVTFTexture->ImageData( 0, 0, 0 );
  388. CPixelWriter pixelWriter;
  389. pixelWriter.SetPixelMemory( pVTFTexture->Format(), pTextureData, pVTFTexture->RowSizeInBytes( 0 ) );
  390. int nImageSize = pVTFTexture->ComputeMipSize( 0 );
  391. memset( pTextureData, 0, nImageSize );
  392. // Put in glints due to the lights in the scene
  393. for ( int i = 0; i < nGlintCount; ++i )
  394. {
  395. // NOTE: AddGlint is a more expensive solution but it looks better close-up
  396. m_pStudioRender->AddGlint( pixelWriter, pRenderData[i].m_vecPosition[0],
  397. pRenderData[i].m_vecPosition[1], pRenderData[i].m_vecIntensity );
  398. }
  399. }
  400. // We've got a global instance, no need to delete it
  401. virtual void Release() {}
  402. const eyeballstate_t *m_pState;
  403. const Vector *m_pVRight;
  404. const Vector *m_pVUp;
  405. const Vector *m_pROrigin;
  406. CStudioRender *m_pStudioRender;
  407. };
  408. static CGlintTextureRegenerator s_GlintTextureRegen;
  409. static ITexture *s_pProcGlint = NULL;
  410. void CStudioRender::PrecacheGlint()
  411. {
  412. if ( ! m_pGlintTexture )
  413. {
  414. // Begin block in which all render targets should be allocated
  415. // Get the texture that we are going to be updating procedurally.
  416. m_pGlintTexture = materials->FindTexture( "_rt_eyeglint", TEXTURE_GROUP_RENDER_TARGET );
  417. if ( IsErrorTexture( m_pGlintTexture ) )
  418. {
  419. g_pMaterialSystem->BeginRenderTargetAllocation();
  420. m_pGlintTexture = g_pMaterialSystem->CreateNamedRenderTargetTextureEx2(
  421. "_rt_eyeglint", 32, 32, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_BGRA8888, MATERIAL_RT_DEPTH_NONE );
  422. g_pMaterialSystem->EndRenderTargetAllocation();
  423. }
  424. m_pGlintTexture->IncrementReferenceCount();
  425. if ( !IsX360() )
  426. {
  427. // Get the texture that we are going to be updating procedurally.
  428. s_pProcGlint = g_pMaterialSystem->CreateProceduralTexture(
  429. "proc_eyeglint", TEXTURE_GROUP_MODEL, 32, 32, IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_NOMIP|TEXTUREFLAGS_NOLOD );
  430. s_pProcGlint->SetTextureRegenerator( &s_GlintTextureRegen );
  431. }
  432. // JAY: I don't see this pattern in the code often. It looks like the material system
  433. // would rather than I deal exclusively with IMaterials instead.
  434. // So maybe we should bake the LOD texture into the eyes shader.
  435. // For now, just hardcode one
  436. // UNDONE: Add a $lodtexture to the eyes shader. Maybe add a $lodsize too.
  437. // UNDONE: Make eyes texture load $lodtexture and switch to that here instead of black
  438. m_pGlintLODTexture = g_pMaterialSystem->FindTexture( IsX360() ? "black" : "vgui/black", NULL, false );
  439. m_pGlintLODTexture->IncrementReferenceCount();
  440. }
  441. }
  442. void CStudioRender::UncacheGlint()
  443. {
  444. if ( m_pGlintTexture )
  445. {
  446. if ( s_pProcGlint )
  447. {
  448. s_pProcGlint->SetTextureRegenerator( NULL );
  449. s_pProcGlint->DecrementReferenceCount();
  450. s_pProcGlint = NULL;
  451. }
  452. m_pGlintTexture->DecrementReferenceCount();
  453. m_pGlintTexture = NULL;
  454. m_pGlintLODTexture->DecrementReferenceCount();
  455. m_pGlintLODTexture = NULL;
  456. }
  457. }
  458. int CStudioRender::BuildGlintRenderData( GlintRenderData_t *pData, int nMaxGlints,
  459. const eyeballstate_t *pState, const Vector& vright, const Vector& vup, const Vector& r_origin )
  460. {
  461. // NOTE: See version 25 for lots of #if 0ed out stuff I removed
  462. Vector viewdelta;
  463. VectorSubtract( r_origin, pState->org, viewdelta );
  464. VectorNormalize( viewdelta );
  465. // hack cornea position
  466. float iris_radius = pState->peyeball->radius * (6.0 / 12.0);
  467. float cornea_radius = pState->peyeball->radius * (8.0 / 12.0);
  468. Vector cornea;
  469. // position on eyeball that matches iris radius
  470. float er = ( iris_radius / pState->peyeball->radius );
  471. er = FastSqrt( 1 - er * er );
  472. // position on cornea sphere that matches iris radius
  473. float cr = ( iris_radius / cornea_radius );
  474. cr = FastSqrt( 1 - cr * cr );
  475. float r = ( er * pState->peyeball->radius - cr * cornea_radius );
  476. VectorScale( pState->forward, r, cornea );
  477. // get offset for center of cornea
  478. float dx, dy;
  479. dx = DotProduct( vright, cornea );
  480. dy = DotProduct( vup, cornea );
  481. // move cornea to world space
  482. VectorAdd( cornea, pState->org, cornea );
  483. Vector delta, intensity, reflection, coord;
  484. // Put in glints due to the lights in the scene
  485. int nGlintCount = 0;
  486. for ( int i = 0; R_LightGlintPosition( i, cornea, delta, intensity ); ++i )
  487. {
  488. VectorNormalize( delta );
  489. if ( DotProduct( delta, pState->forward ) <= 0 )
  490. continue;
  491. VectorAdd( delta, viewdelta, reflection );
  492. VectorNormalize( reflection );
  493. pData[nGlintCount].m_vecPosition[0] = dx + cornea_radius * DotProduct( vright, reflection );
  494. pData[nGlintCount].m_vecPosition[1] = dy + cornea_radius * DotProduct( vup, reflection );
  495. pData[nGlintCount].m_vecIntensity = intensity;
  496. if ( ++nGlintCount >= nMaxGlints )
  497. return nMaxGlints;
  498. if ( !R_LightGlintPosition( i, pState->org, delta, intensity ) )
  499. continue;
  500. VectorNormalize( delta );
  501. if ( DotProduct( delta, pState->forward ) >= er )
  502. continue;
  503. pData[nGlintCount].m_vecPosition[0] = pState->peyeball->radius * DotProduct( vright, reflection );
  504. pData[nGlintCount].m_vecPosition[1] = pState->peyeball->radius * DotProduct( vup, reflection );
  505. pData[nGlintCount].m_vecIntensity = intensity;
  506. if ( ++nGlintCount >= nMaxGlints )
  507. return nMaxGlints;
  508. }
  509. return nGlintCount;
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Renders a glint texture procedurally
  513. //-----------------------------------------------------------------------------
  514. ITexture* CStudioRender::RenderGlintTexture( const eyeballstate_t *pState,
  515. const Vector& vright, const Vector& vup, const Vector& r_origin )
  516. {
  517. GlintRenderData_t pRenderData[16];
  518. int nGlintCount = BuildGlintRenderData( pRenderData, ARRAYSIZE(pRenderData),
  519. pState, vright, vup, r_origin );
  520. if ( nGlintCount == 0 )
  521. return m_pGlintLODTexture;
  522. // This could be done during the context of a flashlight rendering,
  523. // which could be setting the scissor rectangle. We need to save/restore this state
  524. // if ( m_pCurrentFlashlight )
  525. // {
  526. // DisableScissor();
  527. // }
  528. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  529. pRenderContext->PushRenderTargetAndViewport( m_pGlintTexture );
  530. IMaterial *pPrevMaterial = pRenderContext->GetCurrentMaterial();
  531. void *pPrevProxy = pRenderContext->GetCurrentProxy();
  532. int nPrevBoneCount = pRenderContext->GetCurrentNumBones();
  533. MaterialHeightClipMode_t nPrevClipMode = pRenderContext->GetHeightClipMode( );
  534. bool bPrevClippingEnabled = pRenderContext->EnableClipping( false );
  535. bool bInFlashlightMode = pRenderContext->GetFlashlightMode();
  536. // if ( bInFlashlightMode )
  537. // {
  538. // DisableScissor();
  539. // }
  540. pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
  541. pRenderContext->ClearBuffers( true, false, false );
  542. pRenderContext->SetFlashlightMode( false );
  543. pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
  544. pRenderContext->SetNumBoneWeights( 0 );
  545. pRenderContext->Bind( m_pGlintBuildMaterial );
  546. pRenderContext->MatrixMode( MATERIAL_MODEL );
  547. pRenderContext->PushMatrix();
  548. pRenderContext->LoadIdentity();
  549. pRenderContext->MatrixMode( MATERIAL_VIEW );
  550. pRenderContext->PushMatrix();
  551. pRenderContext->LoadIdentity();
  552. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  553. pRenderContext->PushMatrix();
  554. pRenderContext->LoadIdentity();
  555. CMeshBuilder meshBuilder;
  556. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  557. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nGlintCount * 4, nGlintCount * 6 );
  558. Vector4D white( 1.0f, 1.0f, 1.0f, 1.0f );
  559. const float epsilon = 0.5f / 32.0f;
  560. int nIndex = 0;
  561. for ( int i = 0; i < nGlintCount; ++i )
  562. {
  563. const GlintRenderData_t &glint = pRenderData[i];
  564. // Position of glint 0..31 range
  565. float x = (glint.m_vecPosition.x + 0.5f) * m_GlintWidth;
  566. float y = (glint.m_vecPosition.y + 0.5f) * m_GlintHeight;
  567. Vector vGlintCenter = Vector( x, y, 0.0f );
  568. float ooWidth = 1.0f / (float)m_GlintWidth;
  569. float ooHeight = 1.0f / (float)m_GlintHeight;
  570. int x0 = floor(x);
  571. int y0 = floor(y);
  572. int x1 = x0 + 1.0f;
  573. int y1 = y0 + 1.0f;
  574. x0 -= 2.0f; // Fill rules make us pad this out more than the procedural version
  575. y0 -= 2.0f;
  576. float screenX0 = x0 * 2 * ooWidth + epsilon - 1;
  577. float screenX1 = x1 * 2 * ooWidth + epsilon - 1;
  578. float screenY0 = -(y0 * 2 * ooHeight + epsilon - 1);
  579. float screenY1 = -(y1 * 2 * ooHeight + epsilon - 1);
  580. meshBuilder.Position3f( screenX0, screenY0, 0.0f );
  581. meshBuilder.TexCoord2f( 0, x0, y0 );
  582. meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
  583. meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
  584. meshBuilder.AdvanceVertex();
  585. meshBuilder.Position3f( screenX1, screenY0, 0.0f );
  586. meshBuilder.TexCoord2f( 0, x1, y0 );
  587. meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
  588. meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
  589. meshBuilder.AdvanceVertex();
  590. meshBuilder.Position3f( screenX1, screenY1, 0.0f );
  591. meshBuilder.TexCoord2f( 0, x1, y1 );
  592. meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
  593. meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
  594. meshBuilder.AdvanceVertex();
  595. meshBuilder.Position3f( screenX0, screenY1, 0.0f );
  596. meshBuilder.TexCoord2f( 0, x0, y1 );
  597. meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
  598. meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
  599. meshBuilder.AdvanceVertex();
  600. meshBuilder.FastIndex( nIndex );
  601. meshBuilder.FastIndex( nIndex+1 );
  602. meshBuilder.FastIndex( nIndex+2 );
  603. meshBuilder.FastIndex( nIndex );
  604. meshBuilder.FastIndex( nIndex+2 );
  605. meshBuilder.FastIndex( nIndex+3 );
  606. nIndex += 4;
  607. }
  608. meshBuilder.End();
  609. pMesh->DrawModulated( white );
  610. pRenderContext->MatrixMode( MATERIAL_MODEL );
  611. pRenderContext->PopMatrix();
  612. pRenderContext->MatrixMode( MATERIAL_VIEW );
  613. pRenderContext->PopMatrix();
  614. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  615. pRenderContext->PopMatrix();
  616. if ( IsX360() )
  617. {
  618. pRenderContext->CopyRenderTargetToTextureEx( m_pGlintTexture, 0, NULL, NULL );
  619. }
  620. pRenderContext->PopRenderTargetAndViewport( );
  621. pRenderContext->Bind( pPrevMaterial, pPrevProxy );
  622. pRenderContext->SetNumBoneWeights( nPrevBoneCount );
  623. pRenderContext->SetHeightClipMode( nPrevClipMode );
  624. pRenderContext->EnableClipping( bPrevClippingEnabled );
  625. pRenderContext->SetFlashlightMode( bInFlashlightMode );
  626. // if ( m_pCurrentFlashlight )
  627. // {
  628. // EnableScissor( m_pCurrentFlashlight );
  629. // }
  630. return m_pGlintTexture;
  631. }
  632. static ConVar r_glint_procedural( "r_glint_procedural", "0" );
  633. static ConVar r_glint_alwaysdraw( "r_glint_alwaysdraw", "0" );
  634. void CStudioRender::R_StudioEyeballGlint( const eyeballstate_t *pstate, IMaterialVar *pGlintVar,
  635. const Vector& vright, const Vector& vup, const Vector& r_origin )
  636. {
  637. // Kick off a PIX event, since this process encompasses a bunch of locks etc...
  638. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  639. PIXEVENT( pRenderContext, "GenerateEyeballGlint" );
  640. // Don't do a procedural glint texture if there are enough pixels covered by the eyeball onscreen,
  641. // and the eye isn't backfaced.
  642. if ( m_pGlintLODTexture && r_glint_alwaysdraw.GetInt() == 0 )
  643. {
  644. // backfaced or too small to bother?
  645. float pixelArea = pRenderContext->ComputePixelWidthOfSphere( pstate->org, pstate->peyeball->radius );
  646. if(
  647. // FIXME: this backface doesn't work for something that isn't a plane.
  648. // DotProduct( pstate->forward, m_ViewPlaneNormal ) > 0.0f ||
  649. pixelArea < m_pRC->m_Config.fEyeGlintPixelWidthLODThreshold )
  650. {
  651. // use black glint texture
  652. pGlintVar->SetTextureValue( m_pGlintLODTexture );
  653. return;
  654. }
  655. }
  656. // Legacy method for DX8
  657. if ( !IsX360() && r_glint_procedural.GetInt() )
  658. {
  659. // Set up the texture regenerator
  660. s_GlintTextureRegen.m_pVRight = &vright;
  661. s_GlintTextureRegen.m_pVUp = &vup;
  662. s_GlintTextureRegen.m_pROrigin = &r_origin;
  663. s_GlintTextureRegen.m_pState = pstate;
  664. s_GlintTextureRegen.m_pStudioRender = this;
  665. // This will cause the glint texture to be re-generated and then downloaded
  666. s_pProcGlint->Download( );
  667. // This is necessary to make sure we don't reconstitute the bits
  668. // after coming back from a task switch
  669. s_GlintTextureRegen.m_pStudioRender = NULL;
  670. // Use the normal glint instead of the black glint
  671. pGlintVar->SetTextureValue( s_pProcGlint );
  672. }
  673. else // Queued hardware version
  674. {
  675. // Make sure we know the correct size of the glint texture
  676. m_GlintWidth = m_pGlintTexture->GetActualWidth();
  677. m_GlintHeight = m_pGlintTexture->GetActualHeight();
  678. // Render glint render target
  679. ITexture *pUseGlintTexture = RenderGlintTexture( pstate, vright, vup, r_origin );
  680. // Use the normal glint instead of the black glint
  681. pGlintVar->SetTextureValue( pUseGlintTexture );
  682. }
  683. }
  684. void CStudioRender::ComputeGlintTextureProjection( eyeballstate_t const* pState,
  685. const Vector& vright, const Vector& vup, matrix3x4_t& mat )
  686. {
  687. // project eyeball into screenspace texture
  688. float scale = 1.0 / (pState->peyeball->radius * 2);
  689. VectorScale( &vright.x, scale, mat[0] );
  690. VectorScale( &vup.x, scale, mat[1] );
  691. mat[0][3] = -DotProduct( pState->org.Base(), mat[0] ) + 0.5;
  692. mat[1][3] = -DotProduct( pState->org.Base(), mat[1] ) + 0.5;
  693. }
  694. /*
  695. void R_MouthLighting( int count, const Vector *psrcverts, const Vector *psrcnorms, Vector4D *pdestlightvalues )
  696. {
  697. Vector forward;
  698. if (m_pStudioHdr->nummouths < 1) return;
  699. mstudiomouth_t *pMouth = r_pstudiohdr->pMouth( 0 ); // FIXME: this needs to get the mouth index from the shader
  700. float fIllum = m_FlexWeights[pMouth->flexdesc];
  701. if (fIllum < 0) fIllum = 0;
  702. if (fIllum > 1) fIllum = 1;
  703. fIllum = LinearToTexture( fIllum ) / 255.0;
  704. VectorRotate( pMouth->forward, g_StudioInternalState.boneToWorld[ pMouth->bone ], forward );
  705. for (int i = 0; i < count; i++)
  706. {
  707. float dot = -DotProduct( psrcnorms[i], forward );
  708. if (dot > 0)
  709. {
  710. dot = LinearToTexture( dot ) / 255.0; // FIXME: this isn't robust
  711. VectorScale( pdestlightvalues[i], dot, pdestlightvalues[i] );
  712. }
  713. else
  714. VectorFill( pdestlightvalues[i], 0 );
  715. VectorScale( pdestlightvalues[i], fIllum, pdestlightvalues[i] );
  716. }
  717. }
  718. */
  719. void CStudioRender::R_MouthComputeLightingValues( float& fIllum, Vector& forward )
  720. {
  721. // FIXME: this needs to get the mouth index from the shader
  722. mstudiomouth_t *pMouth = m_pStudioHdr->pMouth( 0 );
  723. fIllum = m_pFlexWeights[pMouth->flexdesc];
  724. if (fIllum < 0) fIllum = 0;
  725. if (fIllum > 1) fIllum = 1;
  726. fIllum = LinearToTexture( fIllum ) / 255.0;
  727. VectorRotate( pMouth->forward, m_pBoneToWorld[ pMouth->bone ], forward );
  728. }
  729. void CStudioRender::R_MouthLighting( float fIllum, const Vector& normal, const Vector& forward, Vector &light )
  730. {
  731. float dot = -DotProduct( normal, forward );
  732. if (dot > 0)
  733. {
  734. VectorScale( light, dot * fIllum, light );
  735. }
  736. else
  737. {
  738. VectorFill( light, 0 );
  739. }
  740. }
  741. static unsigned int illumVarCache = 0;
  742. static unsigned int forwardVarCache = 0;
  743. void CStudioRender::R_MouthSetupVertexShader( IMaterial* pMaterial )
  744. {
  745. if (!pMaterial)
  746. return;
  747. // FIXME: this needs to get the mouth index from the shader
  748. mstudiomouth_t *pMouth = m_pStudioHdr->pMouth( 0 );
  749. // Don't deal with illum gamma, we apply it at a different point for vertex shaders
  750. float fIllum = m_pFlexWeights[pMouth->flexdesc];
  751. if (fIllum < 0) fIllum = 0;
  752. if (fIllum > 1) fIllum = 1;
  753. Vector forward;
  754. VectorRotate( pMouth->forward, m_pBoneToWorld[ pMouth->bone ], forward );
  755. forward *= -1;
  756. IMaterialVar* pIllumVar = pMaterial->FindVarFast( "$illumfactor", &illumVarCache );
  757. if (pIllumVar)
  758. {
  759. pIllumVar->SetFloatValue( fIllum );
  760. }
  761. IMaterialVar* pFowardVar = pMaterial->FindVarFast( "$forward", &forwardVarCache );
  762. if (pFowardVar)
  763. {
  764. pFowardVar->SetVecValue( forward.Base(), 3 );
  765. }
  766. }