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.

284 lines
8.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #ifndef _3D_UNITVEC_H
  9. #define _3D_UNITVEC_H
  10. #define UNITVEC_DECLARE_STATICS \
  11. float cUnitVector::mUVAdjustment[0x2000]; \
  12. Vector cUnitVector::mTmpVec;
  13. // upper 3 bits
  14. #define SIGN_MASK 0xe000
  15. #define XSIGN_MASK 0x8000
  16. #define YSIGN_MASK 0x4000
  17. #define ZSIGN_MASK 0x2000
  18. // middle 6 bits - xbits
  19. #define TOP_MASK 0x1f80
  20. // lower 7 bits - ybits
  21. #define BOTTOM_MASK 0x007f
  22. // unitcomp.cpp : A Unit Vector to 16-bit word conversion
  23. // algorithm based on work of Rafael Baptista ([email protected])
  24. // Accuracy improved by O.D. ([email protected])
  25. // Used with Permission.
  26. // a compressed unit vector. reasonable fidelty for unit
  27. // vectors in a 16 bit package. Good enough for surface normals
  28. // we hope.
  29. class cUnitVector // : public c3dMathObject
  30. {
  31. public:
  32. cUnitVector() { mVec = 0; }
  33. cUnitVector( const Vector& vec )
  34. {
  35. packVector( vec );
  36. }
  37. cUnitVector( unsigned short val ) { mVec = val; }
  38. cUnitVector& operator=( const Vector& vec )
  39. { packVector( vec ); return *this; }
  40. operator Vector()
  41. {
  42. unpackVector( mTmpVec );
  43. return mTmpVec;
  44. }
  45. void packVector( const Vector& vec )
  46. {
  47. // convert from Vector to cUnitVector
  48. Assert( vec.IsValid());
  49. Vector tmp = vec;
  50. // input vector does not have to be unit length
  51. // Assert( tmp.length() <= 1.001f );
  52. mVec = 0;
  53. if ( tmp.x < 0 ) { mVec |= XSIGN_MASK; tmp.x = -tmp.x; }
  54. if ( tmp.y < 0 ) { mVec |= YSIGN_MASK; tmp.y = -tmp.y; }
  55. if ( tmp.z < 0 ) { mVec |= ZSIGN_MASK; tmp.z = -tmp.z; }
  56. // project the normal onto the plane that goes through
  57. // X0=(1,0,0),Y0=(0,1,0),Z0=(0,0,1).
  58. // on that plane we choose an (projective!) coordinate system
  59. // such that X0->(0,0), Y0->(126,0), Z0->(0,126),(0,0,0)->Infinity
  60. // a little slower... old pack was 4 multiplies and 2 adds.
  61. // This is 2 multiplies, 2 adds, and a divide....
  62. float w = 126.0f / ( tmp.x + tmp.y + tmp.z );
  63. long xbits = (long)( tmp.x * w );
  64. long ybits = (long)( tmp.y * w );
  65. Assert( xbits < 127 );
  66. Assert( xbits >= 0 );
  67. Assert( ybits < 127 );
  68. Assert( ybits >= 0 );
  69. // Now we can be sure that 0<=xp<=126, 0<=yp<=126, 0<=xp+yp<=126
  70. // however for the sampling we want to transform this triangle
  71. // into a rectangle.
  72. if ( xbits >= 64 )
  73. {
  74. xbits = 127 - xbits;
  75. ybits = 127 - ybits;
  76. }
  77. // now we that have xp in the range (0,127) and yp in
  78. // the range (0,63), we can pack all the bits together
  79. mVec |= ( xbits << 7 );
  80. mVec |= ybits;
  81. }
  82. void unpackVector( Vector& vec )
  83. {
  84. // if we do a straightforward backward transform
  85. // we will get points on the plane X0,Y0,Z0
  86. // however we need points on a sphere that goes through
  87. // these points. Therefore we need to adjust x,y,z so
  88. // that x^2+y^2+z^2=1 by normalizing the vector. We have
  89. // already precalculated the amount by which we need to
  90. // scale, so all we do is a table lookup and a
  91. // multiplication
  92. // get the x and y bits
  93. long xbits = (( mVec & TOP_MASK ) >> 7 );
  94. long ybits = ( mVec & BOTTOM_MASK );
  95. // map the numbers back to the triangle (0,0)-(0,126)-(126,0)
  96. if (( xbits + ybits ) >= 127 )
  97. {
  98. xbits = 127 - xbits;
  99. ybits = 127 - ybits;
  100. }
  101. // do the inverse transform and normalization
  102. // costs 3 extra multiplies and 2 subtracts. No big deal.
  103. float uvadj = mUVAdjustment[mVec & ~SIGN_MASK];
  104. vec.x = uvadj * (float) xbits;
  105. vec.y = uvadj * (float) ybits;
  106. vec.z = uvadj * (float)( 126 - xbits - ybits );
  107. // set all the sign bits
  108. if ( mVec & XSIGN_MASK ) vec.x = -vec.x;
  109. if ( mVec & YSIGN_MASK ) vec.y = -vec.y;
  110. if ( mVec & ZSIGN_MASK ) vec.z = -vec.z;
  111. Assert( vec.IsValid());
  112. }
  113. static void initializeStatics()
  114. {
  115. for ( int idx = 0; idx < 0x2000; idx++ )
  116. {
  117. long xbits = idx >> 7;
  118. long ybits = idx & BOTTOM_MASK;
  119. // map the numbers back to the triangle (0,0)-(0,127)-(127,0)
  120. if (( xbits + ybits ) >= 127 )
  121. {
  122. xbits = 127 - xbits;
  123. ybits = 127 - ybits;
  124. }
  125. // convert to 3D vectors
  126. float x = (float)xbits;
  127. float y = (float)ybits;
  128. float z = (float)( 126 - xbits - ybits );
  129. // calculate the amount of normalization required
  130. mUVAdjustment[idx] = 1.0f / sqrtf( y*y + z*z + x*x );
  131. Assert( _finite( mUVAdjustment[idx]));
  132. //cerr << mUVAdjustment[idx] << "\t";
  133. //if ( xbits == 0 ) cerr << "\n";
  134. }
  135. }
  136. #if 0
  137. void test()
  138. {
  139. #define TEST_RANGE 4
  140. #define TEST_RANDOM 100
  141. #define TEST_ANGERROR 1.0
  142. float maxError = 0;
  143. float avgError = 0;
  144. int numVecs = 0;
  145. {for ( int x = -TEST_RANGE; x < TEST_RANGE; x++ )
  146. {
  147. for ( int y = -TEST_RANGE; y < TEST_RANGE; y++ )
  148. {
  149. for ( int z = -TEST_RANGE; z < TEST_RANGE; z++ )
  150. {
  151. if (( x + y + z ) == 0 ) continue;
  152. Vector vec( (float)x, (float)y, (float)z );
  153. Vector vec2;
  154. vec.normalize();
  155. packVector( vec );
  156. unpackVector( vec2 );
  157. float ang = vec.dot( vec2 );
  158. ang = (( fabs( ang ) > 0.99999f ) ? 0 : (float)acos(ang));
  159. if (( ang > TEST_ANGERROR ) | ( !_finite( ang )))
  160. {
  161. cerr << "error: " << ang << endl;
  162. cerr << "orig vec: " << vec.x << ",\t"
  163. << vec.y << ",\t" << vec.z << "\tmVec: "
  164. << mVec << endl;
  165. cerr << "quantized vec2: " << vec2.x
  166. << ",\t" << vec2.y << ",\t"
  167. << vec2.z << endl << endl;
  168. }
  169. avgError += ang;
  170. numVecs++;
  171. if ( maxError < ang ) maxError = ang;
  172. }
  173. }
  174. }}
  175. for ( int w = 0; w < TEST_RANDOM; w++ )
  176. {
  177. Vector vec( genRandom(), genRandom(), genRandom());
  178. Vector vec2;
  179. vec.normalize();
  180. packVector( vec );
  181. unpackVector( vec2 );
  182. float ang =vec.dot( vec2 );
  183. ang = (( ang > 0.999f ) ? 0 : (float)acos(ang));
  184. if (( ang > TEST_ANGERROR ) | ( !_finite( ang )))
  185. {
  186. cerr << "error: " << ang << endl;
  187. cerr << "orig vec: " << vec.x << ",\t"
  188. << vec.y << ",\t" << vec.z << "\tmVec: "
  189. << mVec << endl;
  190. cerr << "quantized vec2: " << vec2.x << ",\t"
  191. << vec2.y << ",\t"
  192. << vec2.z << endl << endl;
  193. }
  194. avgError += ang;
  195. numVecs++;
  196. if ( maxError < ang ) maxError = ang;
  197. }
  198. { for ( int x = 0; x < 50; x++ )
  199. {
  200. Vector vec( (float)x, 25.0f, 0.0f );
  201. Vector vec2;
  202. vec.normalize();
  203. packVector( vec );
  204. unpackVector( vec2 );
  205. float ang = vec.dot( vec2 );
  206. ang = (( fabs( ang ) > 0.999f ) ? 0 : (float)acos(ang));
  207. if (( ang > TEST_ANGERROR ) | ( !_finite( ang )))
  208. {
  209. cerr << "error: " << ang << endl;
  210. cerr << "orig vec: " << vec.x << ",\t"
  211. << vec.y << ",\t" << vec.z << "\tmVec: "
  212. << mVec << endl;
  213. cerr << " quantized vec2: " << vec2.x << ",\t"
  214. << vec2.y << ",\t" << vec2.z << endl << endl;
  215. }
  216. avgError += ang;
  217. numVecs++;
  218. if ( maxError < ang ) maxError = ang;
  219. }}
  220. cerr << "max angle error: " << maxError
  221. << ", average error: " << avgError / numVecs
  222. << ", num tested vecs: " << numVecs << endl;
  223. }
  224. friend ostream& operator<< ( ostream& os, const cUnitVector& vec )
  225. { os << vec.mVec; return os; }
  226. #endif
  227. //protected: // !!!!
  228. unsigned short mVec;
  229. static float mUVAdjustment[0x2000];
  230. static Vector mTmpVec;
  231. };
  232. #endif // _3D_VECTOR_H