Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

928 lines
29 KiB

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