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.

456 lines
16 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #ifndef COLORSPACE_H
  10. #define COLORSPACE_H
  11. #ifdef _WIN32
  12. #pragma once
  13. #endif
  14. #include "mathlib/mathlib.h"
  15. #include "mathlib/ssemath.h"
  16. #include "mathlib/bumpvects.h"
  17. #include "tier0/dbg.h"
  18. extern float g_LinearToVertex[4096]; // linear (0..4) to screen corrected vertex space (0..1?)
  19. // FIXME!!! Get rid of this. . all of this should be in mathlib
  20. namespace ColorSpace
  21. {
  22. void SetGamma( float screenGamma, float texGamma,
  23. float overbright, bool allowCheats, bool linearFrameBuffer );
  24. // convert texture to linear 0..1 value
  25. float TextureToLinear( int c );
  26. // convert texture to linear 0..1 value
  27. int LinearToTexture( float f );
  28. float TexLightToLinear( int c, int exponent );
  29. // assume 0..4 range
  30. void LinearToLightmap( unsigned char *pDstRGB, const float *pSrcRGB );
  31. // assume 0..4 range
  32. void LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  33. const float *linearBumpColor2, const float *linearBumpColor3,
  34. unsigned char *ret, unsigned char *retBump1,
  35. unsigned char *retBump2, unsigned char *retBump3 );
  36. //
  37. void LinearToBumpedLightmapAlpha( const float *linearAlpha, const float *linearBumpAlpha1,
  38. const float *linearBumpAlpha2, const float *linearBumpAlpha3,
  39. unsigned char *ret, unsigned char *retBump1,
  40. unsigned char *retBump2, unsigned char *retBump3 );
  41. // converts 0..1 linear value to screen gamma (0..255)
  42. int LinearToScreenGamma( float f );
  43. FORCEINLINE void LinearToLightmap( unsigned char *pDstRGB, const float *pSrcRGB )
  44. {
  45. Vector tmpVect;
  46. #if 1
  47. int i, j;
  48. for( j = 0; j < 3; j++ )
  49. {
  50. i = RoundFloatToInt( pSrcRGB[j] * 1024 ); // assume 0..4 range
  51. if (i < 0)
  52. {
  53. i = 0;
  54. }
  55. if (i > 4091)
  56. {
  57. i = 4091;
  58. }
  59. tmpVect[j] = g_LinearToVertex[i];
  60. }
  61. #else
  62. tmpVect[0] = LinearToVertexLight( pSrcRGB[0] );
  63. tmpVect[1] = LinearToVertexLight( pSrcRGB[1] );
  64. tmpVect[2] = LinearToVertexLight( pSrcRGB[2] );
  65. #endif
  66. ColorClamp( tmpVect );
  67. pDstRGB[0] = RoundFloatToByte( tmpVect[0] * 255.0f );
  68. pDstRGB[1] = RoundFloatToByte( tmpVect[1] * 255.0f );
  69. pDstRGB[2] = RoundFloatToByte( tmpVect[2] * 255.0f );
  70. }
  71. // Clamp the three values for bumped lighting such that we trade off directionality for brightness.
  72. FORCEINLINE void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
  73. {
  74. Vector maxs;
  75. Vector *colors[3] = { &color1, &color2, &color3 };
  76. maxs[0] = VectorMaximum( color1 );
  77. maxs[1] = VectorMaximum( color2 );
  78. maxs[2] = VectorMaximum( color3 );
  79. // HACK! Clean this up, and add some else statements
  80. #define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
  81. int order[3];
  82. CONDITION(0,1,2);
  83. CONDITION(0,2,1);
  84. CONDITION(1,0,2);
  85. CONDITION(1,2,0);
  86. CONDITION(2,0,1);
  87. CONDITION(2,1,0);
  88. int i;
  89. for( i = 0; i < 3; i++ )
  90. {
  91. float max = VectorMaximum( *colors[order[i]] );
  92. if( max <= 1.0f )
  93. {
  94. continue;
  95. }
  96. // This channel is too bright. . take half of the amount that we are over and
  97. // add it to the other two channel.
  98. float factorToRedist = ( max - 1.0f ) / max;
  99. Vector colorToRedist = factorToRedist * *colors[order[i]];
  100. *colors[order[i]] -= colorToRedist;
  101. colorToRedist *= 0.5f;
  102. *colors[order[(i+1)%3]] += colorToRedist;
  103. *colors[order[(i+2)%3]] += colorToRedist;
  104. }
  105. ColorClamp( color1 );
  106. ColorClamp( color2 );
  107. ColorClamp( color3 );
  108. if( color1[0] < 0.f ) color1[0] = 0.f;
  109. if( color1[1] < 0.f ) color1[1] = 0.f;
  110. if( color1[2] < 0.f ) color1[2] = 0.f;
  111. if( color2[0] < 0.f ) color2[0] = 0.f;
  112. if( color2[1] < 0.f ) color2[1] = 0.f;
  113. if( color2[2] < 0.f ) color2[2] = 0.f;
  114. if( color3[0] < 0.f ) color3[0] = 0.f;
  115. if( color3[1] < 0.f ) color3[1] = 0.f;
  116. if( color3[2] < 0.f ) color3[2] = 0.f;
  117. }
  118. FORCEINLINE void LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  119. const float *linearBumpColor2, const float *linearBumpColor3,
  120. unsigned char *ret, unsigned char *retBump1,
  121. unsigned char *retBump2, unsigned char *retBump3 )
  122. {
  123. const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
  124. const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
  125. const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
  126. Vector gammaGoal;
  127. // gammaGoal is premultiplied by 1/overbright, which we want
  128. gammaGoal[0] = LinearToVertexLight( linearColor[0] );
  129. gammaGoal[1] = LinearToVertexLight( linearColor[1] );
  130. gammaGoal[2] = LinearToVertexLight( linearColor[2] );
  131. Vector bumpAverage = linearBump1;
  132. bumpAverage += linearBump2;
  133. bumpAverage += linearBump3;
  134. bumpAverage *= ( 1.0f / 3.0f );
  135. Vector correctionScale;
  136. if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
  137. {
  138. // fast path when we know that we don't have to worry about divide by zero.
  139. VectorDivide( gammaGoal, bumpAverage, correctionScale );
  140. // correctionScale = gammaGoal / bumpSum;
  141. }
  142. else
  143. {
  144. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  145. if( bumpAverage[0] != 0.0f )
  146. {
  147. correctionScale[0] = gammaGoal[0] / bumpAverage[0];
  148. }
  149. if( bumpAverage[1] != 0.0f )
  150. {
  151. correctionScale[1] = gammaGoal[1] / bumpAverage[1];
  152. }
  153. if( bumpAverage[2] != 0.0f )
  154. {
  155. correctionScale[2] = gammaGoal[2] / bumpAverage[2];
  156. }
  157. }
  158. Vector correctedBumpColor1;
  159. Vector correctedBumpColor2;
  160. Vector correctedBumpColor3;
  161. VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
  162. VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
  163. VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
  164. Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
  165. ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
  166. ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
  167. ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
  168. ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
  169. retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
  170. retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
  171. retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
  172. retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
  173. retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
  174. retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
  175. retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
  176. retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
  177. retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
  178. }
  179. // For use with CSM correct blending output in alpha channel from vrad
  180. // we can go out of 0..1 range if we normalize the bumped alpha data
  181. // so we 'squash' the range to compensate.
  182. // Note - We shouldn't be taking the simple average in the bump case when normalizing anyway
  183. // (i.e. add together and multiply by 0.575 comments below).
  184. // Keeping the averaging as is for consistency with RGB's, etc
  185. FORCEINLINE void LinearToBumpedLightmapAlpha( const float *linearAlpha, const float *linearBumpAlpha1,
  186. const float *linearBumpAlpha2, const float *linearBumpAlpha3,
  187. unsigned char *ret, unsigned char *retBump1,
  188. unsigned char *retBump2, unsigned char *retBump3 )
  189. {
  190. // divide by three to ensure we stay in a 0..1 range here, scale up result later (via scaling in CSM light color)
  191. const float &linearUnbumped = *((const float *)linearAlpha) / 3.0f;
  192. const float &linearBump1 = *((const float *)linearBumpAlpha1) / 3.0f;
  193. const float &linearBump2 = *((const float *)linearBumpAlpha2) / 3.0f;
  194. const float &linearBump3 = *((const float *)linearBumpAlpha3) / 3.0f;
  195. float bumpAverage = linearBump1;
  196. bumpAverage += linearBump2;
  197. bumpAverage += linearBump3;
  198. bumpAverage *= (1.0f / 3.0f);
  199. float correctionScale;
  200. if ( *(int *)&bumpAverage != 0 )
  201. {
  202. // fast path when we know that we don't have to worry about divide by zero.
  203. correctionScale = linearUnbumped / bumpAverage;
  204. }
  205. else
  206. {
  207. correctionScale = 0.0f;
  208. if ( bumpAverage != 0.0f )
  209. {
  210. correctionScale = linearUnbumped / bumpAverage;
  211. }
  212. }
  213. float correctedBumpAlpha1;
  214. float correctedBumpAlpha2;
  215. float correctedBumpAlpha3;
  216. correctedBumpAlpha1 = linearBump1 * correctionScale;
  217. correctedBumpAlpha2 = linearBump2 * correctionScale;
  218. correctedBumpAlpha3 = linearBump3 * correctionScale;
  219. ret[0] = RoundFloatToByte( linearUnbumped * 255.0f );
  220. retBump1[0] = RoundFloatToByte( correctedBumpAlpha1 * 255.0f );
  221. retBump2[0] = RoundFloatToByte( correctedBumpAlpha2 * 255.0f );
  222. retBump3[0] = RoundFloatToByte( correctedBumpAlpha3 * 255.0f );
  223. }
  224. uint16 LinearFloatToCorrectedShort( float in );
  225. inline unsigned short LinearToUnsignedShort( float in, int nFractionalBits )
  226. {
  227. unsigned short out;
  228. in = in * ( 1 << nFractionalBits );
  229. in = MIN( in, 65535 );
  230. out = ( unsigned short ) MAX( in, 0.0f );
  231. return out;
  232. }
  233. inline void ClampToHDR( const Vector &in, unsigned short out[3] )
  234. {
  235. Vector tmp = in;
  236. out[0] = LinearFloatToCorrectedShort( in.x );
  237. out[1] = LinearFloatToCorrectedShort( in.y );
  238. out[2] = LinearFloatToCorrectedShort( in.z );
  239. }
  240. // divide by 3 to ensure we stay in the same range as bumped alpha data
  241. // this is important for overlays, since a bumped overlay on a non-bumped
  242. // lightmap surface is still drawn with the bumped lightmap shader, with 3x the base lightmap
  243. // and vice versa. If we're not consistent, we break the CSM blending
  244. FORCEINLINE void LinearToLightmapAlpha( unsigned char *pDstA, const float srcA )
  245. {
  246. pDstA[0] = RoundFloatToByte( (srcA * 255.0f) / 3.0f );
  247. }
  248. FORCEINLINE void LinearToLightmapAlpha( int *pDstA, const float srcA )
  249. {
  250. pDstA[0] = LinearToUnsignedShort( srcA / 3.0f, 16 );
  251. }
  252. FORCEINLINE void LinearToLightmapAlpha( float *pA )
  253. {
  254. pA[0] = pA[0] / 3.0f;
  255. }
  256. FORCEINLINE void
  257. LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  258. const float *linearBumpColor2, const float *linearBumpColor3,
  259. float *ret, float *retBump1,
  260. float *retBump2, float *retBump3 )
  261. {
  262. const Vector &linearUnbumped = *( ( const Vector * )linearColor );
  263. Vector linearBump1 = *( ( const Vector * )linearBumpColor1 );
  264. Vector linearBump2 = *( ( const Vector * )linearBumpColor2 );
  265. Vector linearBump3 = *( ( const Vector * )linearBumpColor3 );
  266. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  267. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  268. // areas.
  269. // Note: According to Alex, this code is completely wrong.
  270. // Because the bump vectors constitute a orthonormal basis, one does not simply average
  271. // them to get the straight-up result. In fact they are added together then multiplied
  272. // by 0.575
  273. Vector bumpAverage = linearBump1;
  274. bumpAverage += linearBump2;
  275. bumpAverage += linearBump3;
  276. bumpAverage *= ( 1.0f / 3.0f );
  277. Vector correctionScale;
  278. if( *( int * )&bumpAverage[0] != 0 &&
  279. *( int * )&bumpAverage[1] != 0 &&
  280. *( int * )&bumpAverage[2] != 0 )
  281. {
  282. // fast path when we know that we don't have to worry about divide by zero.
  283. VectorDivide( linearUnbumped, bumpAverage, correctionScale );
  284. }
  285. else
  286. {
  287. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  288. if( bumpAverage[0] != 0.0f )
  289. {
  290. correctionScale[0] = linearUnbumped[0] / bumpAverage[0];
  291. }
  292. if( bumpAverage[1] != 0.0f )
  293. {
  294. correctionScale[1] = linearUnbumped[1] / bumpAverage[1];
  295. }
  296. if( bumpAverage[2] != 0.0f )
  297. {
  298. correctionScale[2] = linearUnbumped[2] / bumpAverage[2];
  299. }
  300. }
  301. linearBump1 *= correctionScale;
  302. linearBump2 *= correctionScale;
  303. linearBump3 *= correctionScale;
  304. *((Vector *)ret) = linearUnbumped;
  305. *((Vector *)retBump1) = linearBump1;
  306. *((Vector *)retBump2) = linearBump2;
  307. *((Vector *)retBump3) = linearBump3;
  308. }
  309. // For use with CSM correct blending output in alpha channel from vrad
  310. // we can go out of 0..1 range if we normalize the bumped alpha data
  311. // so we 'squash' the range to compensate.
  312. // Note - We shouldn't be taking the simple average in the bump case when normalizing anyway
  313. // (i.e. add together and multiply by 0.575 comments below).
  314. // Keeping the averaging as is for consistency with RGB's, etc
  315. FORCEINLINE void LinearToBumpedLightmapAlpha( const float *linearAlpha, const float *linearBumpAlpha1,
  316. const float *linearBumpAlpha2, const float *linearBumpAlpha3,
  317. float *ret, float *retBump1,
  318. float *retBump2, float *retBump3 )
  319. {
  320. // divide by three to ensure we stay in a 0..1 range here, scale up result later (via scaling in CSM light color)
  321. const float &linearUnbumped = *((const float *)linearAlpha) / 3.0f;
  322. const float &linearBump1 = *((const float *)linearBumpAlpha1) / 3.0f;
  323. const float &linearBump2 = *((const float *)linearBumpAlpha2) / 3.0f;
  324. const float &linearBump3 = *((const float *)linearBumpAlpha3) / 3.0f;
  325. float bumpAverage = linearBump1;
  326. bumpAverage += linearBump2;
  327. bumpAverage += linearBump3;
  328. bumpAverage *= (1.0f / 3.0f);
  329. float correctionScale;
  330. if ( *(int *)&bumpAverage != 0 )
  331. {
  332. // fast path when we know that we don't have to worry about divide by zero.
  333. correctionScale = linearUnbumped / bumpAverage;
  334. }
  335. else
  336. {
  337. correctionScale = 0.0f;
  338. if ( bumpAverage != 0.0f )
  339. {
  340. correctionScale = linearUnbumped / bumpAverage;
  341. }
  342. }
  343. float correctedBumpAlpha1;
  344. float correctedBumpAlpha2;
  345. float correctedBumpAlpha3;
  346. correctedBumpAlpha1 = linearBump1 * correctionScale;
  347. correctedBumpAlpha2 = linearBump2 * correctionScale;
  348. correctedBumpAlpha3 = linearBump3 * correctionScale;
  349. ret[0] = linearUnbumped;
  350. retBump1[0] = correctedBumpAlpha1;
  351. retBump2[0] = correctedBumpAlpha2;
  352. retBump3[0] = correctedBumpAlpha3;
  353. }
  354. // The domain of the inputs is floats [0 .. 16.0f]
  355. // the output range is also floats [0 .. 16.0f] (eg, compression to short does not happen)
  356. FORCEINLINE void
  357. LinearToBumpedLightmap( FLTX4 linearColor, FLTX4 linearBumpColor1,
  358. FLTX4 linearBumpColor2, FLTX4 linearBumpColor3,
  359. fltx4 &ret, fltx4 &retBump1, // I pray that with inlining
  360. fltx4 &retBump2, fltx4 &retBump3 ) // these will be returned on registers
  361. {
  362. // preload 3.0f onto the returns so that we don't need to multiply the bumpAverage by it
  363. // straight away (eg, reschedule this dependent op)
  364. static const fltx4 vThree = { 3.0f, 3.0f, 3.0f, 0.0f };
  365. fltx4 retValBump1 = MulSIMD( vThree, linearBumpColor1);
  366. fltx4 retValBump2 = MulSIMD( vThree, linearBumpColor2);
  367. fltx4 retValBump3 = MulSIMD( vThree, linearBumpColor3);
  368. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  369. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  370. // areas.
  371. fltx4 bumpAverage = AddSIMD(AddSIMD(linearBumpColor1, linearBumpColor2), linearBumpColor3); // actually average * 3
  372. // find the zero terms so that we can quash their channels in the output
  373. bi32x4 zeroTerms = CmpEqSIMD(bumpAverage, Four_Zeros);
  374. fltx4 correctionScale = ReciprocalEstSIMD(bumpAverage); // each channel is now 1.0f / (average[x] * 3.0f)
  375. // divide unbumped linear by the average to get the correction scale
  376. correctionScale = MulSIMD( AndNotSIMD(zeroTerms, linearColor), // crush values that were zero in bumpAverage. (saves on dep latency)
  377. correctionScale); // still has an extra 1/3 factor multiplied in
  378. // multiply this against three to get the return values
  379. ret = linearColor;
  380. retBump1 = MulSIMD(retValBump1, correctionScale);
  381. retBump2 = MulSIMD(retValBump2, correctionScale);
  382. retBump3 = MulSIMD(retValBump3, correctionScale);
  383. }
  384. // input: floats [0 .. 16.0f]
  385. // output: shorts [0 .. 65535]
  386. FORCEINLINE void
  387. LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  388. const float *linearBumpColor2, const float *linearBumpColor3,
  389. unsigned short *ret, unsigned short *retBump1,
  390. unsigned short *retBump2, unsigned short *retBump3 )
  391. {
  392. Vector linearUnbumped, linearBump1, linearBump2, linearBump3;
  393. LinearToBumpedLightmap( linearColor, linearBumpColor1, linearBumpColor2, linearBumpColor3, &linearUnbumped.x, &linearBump1.x, &linearBump2.x, &linearBump3.x );
  394. ClampToHDR( linearUnbumped, ret );
  395. ClampToHDR( linearBump1, retBump1 );
  396. ClampToHDR( linearBump2, retBump2 );
  397. ClampToHDR( linearBump3, retBump3 );
  398. }
  399. };
  400. #endif // COLORSPACE_H