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.

333 lines
12 KiB

  1. //========= Copyright 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. // converts 0..1 linear value to screen gamma (0..255)
  37. int LinearToScreenGamma( float f );
  38. FORCEINLINE void LinearToLightmap( unsigned char *pDstRGB, const float *pSrcRGB )
  39. {
  40. Vector tmpVect;
  41. #if 1
  42. int i, j;
  43. for( j = 0; j < 3; j++ )
  44. {
  45. i = RoundFloatToInt( pSrcRGB[j] * 1024 ); // assume 0..4 range
  46. if (i < 0)
  47. {
  48. i = 0;
  49. }
  50. if (i > 4091)
  51. {
  52. i = 4091;
  53. }
  54. tmpVect[j] = g_LinearToVertex[i];
  55. }
  56. #else
  57. tmpVect[0] = LinearToVertexLight( pSrcRGB[0] );
  58. tmpVect[1] = LinearToVertexLight( pSrcRGB[1] );
  59. tmpVect[2] = LinearToVertexLight( pSrcRGB[2] );
  60. #endif
  61. ColorClamp( tmpVect );
  62. pDstRGB[0] = RoundFloatToByte( tmpVect[0] * 255.0f );
  63. pDstRGB[1] = RoundFloatToByte( tmpVect[1] * 255.0f );
  64. pDstRGB[2] = RoundFloatToByte( tmpVect[2] * 255.0f );
  65. }
  66. // Clamp the three values for bumped lighting such that we trade off directionality for brightness.
  67. FORCEINLINE void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
  68. {
  69. Vector maxs;
  70. Vector *colors[3] = { &color1, &color2, &color3 };
  71. maxs[0] = VectorMaximum( color1 );
  72. maxs[1] = VectorMaximum( color2 );
  73. maxs[2] = VectorMaximum( color3 );
  74. // HACK! Clean this up, and add some else statements
  75. #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 )
  76. int order[3] = {};
  77. CONDITION(0,1,2);
  78. CONDITION(0,2,1);
  79. CONDITION(1,0,2);
  80. CONDITION(1,2,0);
  81. CONDITION(2,0,1);
  82. CONDITION(2,1,0);
  83. int i;
  84. for( i = 0; i < 3; i++ )
  85. {
  86. float max = VectorMaximum( *colors[order[i]] );
  87. if( max <= 1.0f )
  88. {
  89. continue;
  90. }
  91. // This channel is too bright. . take half of the amount that we are over and
  92. // add it to the other two channel.
  93. float factorToRedist = ( max - 1.0f ) / max;
  94. Vector colorToRedist = factorToRedist * *colors[order[i]];
  95. *colors[order[i]] -= colorToRedist;
  96. colorToRedist *= 0.5f;
  97. *colors[order[(i+1)%3]] += colorToRedist;
  98. *colors[order[(i+2)%3]] += colorToRedist;
  99. }
  100. ColorClamp( color1 );
  101. ColorClamp( color2 );
  102. ColorClamp( color3 );
  103. if( color1[0] < 0.f ) color1[0] = 0.f;
  104. if( color1[1] < 0.f ) color1[1] = 0.f;
  105. if( color1[2] < 0.f ) color1[2] = 0.f;
  106. if( color2[0] < 0.f ) color2[0] = 0.f;
  107. if( color2[1] < 0.f ) color2[1] = 0.f;
  108. if( color2[2] < 0.f ) color2[2] = 0.f;
  109. if( color3[0] < 0.f ) color3[0] = 0.f;
  110. if( color3[1] < 0.f ) color3[1] = 0.f;
  111. if( color3[2] < 0.f ) color3[2] = 0.f;
  112. }
  113. FORCEINLINE void LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  114. const float *linearBumpColor2, const float *linearBumpColor3,
  115. unsigned char *ret, unsigned char *retBump1,
  116. unsigned char *retBump2, unsigned char *retBump3 )
  117. {
  118. const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
  119. const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
  120. const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
  121. Vector gammaGoal;
  122. // gammaGoal is premultiplied by 1/overbright, which we want
  123. gammaGoal[0] = LinearToVertexLight( linearColor[0] );
  124. gammaGoal[1] = LinearToVertexLight( linearColor[1] );
  125. gammaGoal[2] = LinearToVertexLight( linearColor[2] );
  126. Vector bumpAverage = linearBump1;
  127. bumpAverage += linearBump2;
  128. bumpAverage += linearBump3;
  129. bumpAverage *= ( 1.0f / 3.0f );
  130. Vector correctionScale;
  131. if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
  132. {
  133. // fast path when we know that we don't have to worry about divide by zero.
  134. VectorDivide( gammaGoal, bumpAverage, correctionScale );
  135. // correctionScale = gammaGoal / bumpSum;
  136. }
  137. else
  138. {
  139. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  140. if( bumpAverage[0] != 0.0f )
  141. {
  142. correctionScale[0] = gammaGoal[0] / bumpAverage[0];
  143. }
  144. if( bumpAverage[1] != 0.0f )
  145. {
  146. correctionScale[1] = gammaGoal[1] / bumpAverage[1];
  147. }
  148. if( bumpAverage[2] != 0.0f )
  149. {
  150. correctionScale[2] = gammaGoal[2] / bumpAverage[2];
  151. }
  152. }
  153. Vector correctedBumpColor1;
  154. Vector correctedBumpColor2;
  155. Vector correctedBumpColor3;
  156. VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
  157. VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
  158. VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
  159. Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
  160. ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
  161. ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
  162. ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
  163. ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
  164. retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
  165. retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
  166. retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
  167. retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
  168. retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
  169. retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
  170. retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
  171. retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
  172. retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
  173. }
  174. uint16 LinearFloatToCorrectedShort( float in );
  175. inline unsigned short LinearToUnsignedShort( float in, int nFractionalBits )
  176. {
  177. unsigned short out;
  178. in = in * ( 1 << nFractionalBits );
  179. in = min( in, 65535.f );
  180. out = max( in, 0.0f );
  181. return out;
  182. }
  183. inline void ClampToHDR( const Vector &in, unsigned short out[3] )
  184. {
  185. Vector tmp = in;
  186. out[0] = LinearFloatToCorrectedShort( in.x );
  187. out[1] = LinearFloatToCorrectedShort( in.y );
  188. out[2] = LinearFloatToCorrectedShort( in.z );
  189. }
  190. FORCEINLINE void
  191. LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  192. const float *linearBumpColor2, const float *linearBumpColor3,
  193. float *ret, float *retBump1,
  194. float *retBump2, float *retBump3 )
  195. {
  196. const Vector &linearUnbumped = *( ( const Vector * )linearColor );
  197. Vector linearBump1 = *( ( const Vector * )linearBumpColor1 );
  198. Vector linearBump2 = *( ( const Vector * )linearBumpColor2 );
  199. Vector linearBump3 = *( ( const Vector * )linearBumpColor3 );
  200. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  201. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  202. // areas.
  203. // Note: According to Alex, this code is completely wrong.
  204. // Because the bump vectors constitute a orthonormal basis, one does not simply average
  205. // them to get the straight-up result. In fact they are added together then multiplied
  206. // by 0.575
  207. Vector bumpAverage = linearBump1;
  208. bumpAverage += linearBump2;
  209. bumpAverage += linearBump3;
  210. bumpAverage *= ( 1.0f / 3.0f );
  211. Vector correctionScale;
  212. if( *( int * )&bumpAverage[0] != 0 &&
  213. *( int * )&bumpAverage[1] != 0 &&
  214. *( int * )&bumpAverage[2] != 0 )
  215. {
  216. // fast path when we know that we don't have to worry about divide by zero.
  217. VectorDivide( linearUnbumped, bumpAverage, correctionScale );
  218. }
  219. else
  220. {
  221. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  222. if( bumpAverage[0] != 0.0f )
  223. {
  224. correctionScale[0] = linearUnbumped[0] / bumpAverage[0];
  225. }
  226. if( bumpAverage[1] != 0.0f )
  227. {
  228. correctionScale[1] = linearUnbumped[1] / bumpAverage[1];
  229. }
  230. if( bumpAverage[2] != 0.0f )
  231. {
  232. correctionScale[2] = linearUnbumped[2] / bumpAverage[2];
  233. }
  234. }
  235. linearBump1 *= correctionScale;
  236. linearBump2 *= correctionScale;
  237. linearBump3 *= correctionScale;
  238. *((Vector *)ret) = linearUnbumped;
  239. *((Vector *)retBump1) = linearBump1;
  240. *((Vector *)retBump2) = linearBump2;
  241. *((Vector *)retBump3) = linearBump3;
  242. }
  243. // The domain of the inputs is floats [0 .. 16.0f]
  244. // the output range is also floats [0 .. 16.0f] (eg, compression to short does not happen)
  245. FORCEINLINE void
  246. LinearToBumpedLightmap( FLTX4 linearColor, FLTX4 linearBumpColor1,
  247. FLTX4 linearBumpColor2, FLTX4 linearBumpColor3,
  248. fltx4 &ret, fltx4 &retBump1, // I pray that with inlining
  249. fltx4 &retBump2, fltx4 &retBump3 ) // these will be returned on registers
  250. {
  251. // preload 3.0f onto the returns so that we don't need to multiply the bumpAverage by it
  252. // straight away (eg, reschedule this dependent op)
  253. static const fltx4 vThree = { 3.0f, 3.0f, 3.0f, 0.0f };
  254. fltx4 retValBump1 = MulSIMD( vThree, linearBumpColor1);
  255. fltx4 retValBump2 = MulSIMD( vThree, linearBumpColor2);
  256. fltx4 retValBump3 = MulSIMD( vThree, linearBumpColor3);
  257. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  258. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  259. // areas.
  260. fltx4 bumpAverage = AddSIMD(AddSIMD(linearBumpColor1, linearBumpColor2), linearBumpColor3); // actually average * 3
  261. // find the zero terms so that we can quash their channels in the output
  262. fltx4 zeroTerms = CmpEqSIMD(bumpAverage, Four_Zeros);
  263. fltx4 correctionScale = ReciprocalEstSIMD(bumpAverage); // each channel is now 1.0f / (average[x] * 3.0f)
  264. // divide unbumped linear by the average to get the correction scale
  265. correctionScale = MulSIMD( AndNotSIMD(zeroTerms, linearColor), // crush values that were zero in bumpAverage. (saves on dep latency)
  266. correctionScale); // still has an extra 1/3 factor multiplied in
  267. // multiply this against three to get the return values
  268. ret = linearColor;
  269. retBump1 = MulSIMD(retValBump1, correctionScale);
  270. retBump2 = MulSIMD(retValBump2, correctionScale);
  271. retBump3 = MulSIMD(retValBump3, correctionScale);
  272. }
  273. // input: floats [0 .. 16.0f]
  274. // output: shorts [0 .. 65535]
  275. FORCEINLINE void
  276. LinearToBumpedLightmap( const float *linearColor, const float *linearBumpColor1,
  277. const float *linearBumpColor2, const float *linearBumpColor3,
  278. unsigned short *ret, unsigned short *retBump1,
  279. unsigned short *retBump2, unsigned short *retBump3 )
  280. {
  281. Vector linearUnbumped, linearBump1, linearBump2, linearBump3;
  282. LinearToBumpedLightmap( linearColor, linearBumpColor1, linearBumpColor2, linearBumpColor3, &linearUnbumped.x, &linearBump1.x, &linearBump2.x, &linearBump3.x );
  283. ClampToHDR( linearUnbumped, ret );
  284. ClampToHDR( linearBump1, retBump1 );
  285. ClampToHDR( linearBump2, retBump2 );
  286. ClampToHDR( linearBump3, retBump3 );
  287. }
  288. };
  289. #endif // COLORSPACE_H