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.

292 lines
8.2 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A little helper class that computes a spline patch
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //-----------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //=============================================================================//
  13. #include "cbase.h"
  14. #include "splinepatch.h"
  15. #include "mathlib/vmatrix.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. //-----------------------------------------------------------------------------
  19. // Catmull rom blend weights
  20. //-----------------------------------------------------------------------------
  21. static VMatrix s_CatmullRom( -0.5, 1.5, -1.5, 0.5,
  22. 1, -2.5, 2, -0.5,
  23. -0.5, 0, 0.5, 0,
  24. 0, 1, 0, 0 );
  25. //-----------------------------------------------------------------------------
  26. // The last argument represents the number of float channels in addition to position
  27. //-----------------------------------------------------------------------------
  28. CSplinePatch::CSplinePatch( ) : m_ChannelCount(0),
  29. m_Width(0), m_Height(0), m_ppPositions(0), m_LinearFactor(1.0f)
  30. {
  31. }
  32. CSplinePatch::~CSplinePatch()
  33. {
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Initialize the spline patch
  37. //-----------------------------------------------------------------------------
  38. void CSplinePatch::Init( int w, int h, int extraChannels )
  39. {
  40. assert( extraChannels < MAX_CHANNELS );
  41. m_ChannelCount = extraChannels;
  42. m_Width = w;
  43. m_Height = h;
  44. m_LinearFactor = 1.0f;
  45. }
  46. //-----------------------------------------------------------------------------
  47. // 0 = linear, 1 = spliney!
  48. //-----------------------------------------------------------------------------
  49. void CSplinePatch::SetLinearBlend( float factor )
  50. {
  51. m_LinearFactor = factor;
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Hooks the patch up to externally controlled data...
  55. //-----------------------------------------------------------------------------
  56. void CSplinePatch::SetControlPositions( Vector const** pPositions )
  57. {
  58. m_ppPositions = pPositions;
  59. }
  60. void CSplinePatch::SetChannelData( int channel, float* pChannel )
  61. {
  62. m_pChannel[channel] = pChannel;
  63. }
  64. static inline void ComputeIndex( int i, int maxval, int* idx )
  65. {
  66. if (i == 0)
  67. {
  68. idx[0] = 0; idx[1] = 0; idx[2] = 1;
  69. idx[3] = (maxval > 2) ? 2 : 1;
  70. }
  71. else
  72. {
  73. idx[0] = i-1; idx[1] = i;
  74. if (i >= maxval - 1)
  75. {
  76. idx[2] = i; idx[3] = i;
  77. }
  78. else
  79. {
  80. idx[2] = i+1;
  81. if (i >= maxval - 2)
  82. idx[3] = i+1;
  83. else
  84. idx[3]= i+2;
  85. }
  86. }
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Computes indices of the samples to read for this interpolation
  90. //-----------------------------------------------------------------------------
  91. void CSplinePatch::ComputeIndices( )
  92. {
  93. int s[4];
  94. int t[4];
  95. ComputeIndex( m_is, m_Width, s );
  96. ComputeIndex( m_it, m_Height, t );
  97. int base = t[0] * m_Width;
  98. m_SampleIndices[0][0] = base + s[0];
  99. m_SampleIndices[1][0] = base + s[1];
  100. m_SampleIndices[2][0] = base + s[2];
  101. m_SampleIndices[3][0] = base + s[3];
  102. base = t[1] * m_Width;
  103. m_SampleIndices[0][1] = base + s[0];
  104. m_SampleIndices[1][1] = base + s[1];
  105. m_SampleIndices[2][1] = base + s[2];
  106. m_SampleIndices[3][1] = base + s[3];
  107. base = t[2] * m_Width;
  108. m_SampleIndices[0][2] = base + s[0];
  109. m_SampleIndices[1][2] = base + s[1];
  110. m_SampleIndices[2][2] = base + s[2];
  111. m_SampleIndices[3][2] = base + s[3];
  112. base = t[3] * m_Width;
  113. m_SampleIndices[0][3] = base + s[0];
  114. m_SampleIndices[1][3] = base + s[1];
  115. m_SampleIndices[2][3] = base + s[2];
  116. m_SampleIndices[3][3] = base + s[3];
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Call this before querying the patch for data at (s,t)
  120. //-----------------------------------------------------------------------------
  121. void CSplinePatch::SetupPatchQuery( float s, float t )
  122. {
  123. m_is = (int)s;
  124. m_it = (int)t;
  125. if( m_is >= m_Width )
  126. {
  127. m_is = m_Width - 1;
  128. m_fs = 1.0f;
  129. }
  130. else
  131. {
  132. m_fs = s - m_is;
  133. }
  134. if( m_it >= m_Height )
  135. {
  136. m_it = m_Height - 1;
  137. m_ft = 1.0f;
  138. }
  139. else
  140. {
  141. m_ft = t - m_it;
  142. }
  143. ComputeIndices( );
  144. // The patch equation is:
  145. // px = S * M * Gx * M^T * T^T
  146. // py = S * M * Gy * M^T * T^T
  147. // pz = S * M * Gz * M^T * T^T
  148. // where S = [s^3 s^2 s 1], T = [t^3 t^2 t 1]
  149. // M is the patch type matrix, in my case I'm using a catmull-rom
  150. // G is the array of control points. rows have constant t
  151. // We're gonna cache off S * M and M^T * T^T...
  152. Vector4D svec, tvec;
  153. float fs2 = m_fs * m_fs;
  154. svec[0] = fs2 * m_fs; svec[1] = fs2; svec[2] = m_fs; svec[3] = 1.0f;
  155. float ft2 = m_ft * m_ft;
  156. tvec[0] = ft2 * m_ft; tvec[1] = ft2; tvec[2] = m_ft; tvec[3] = 1.0f;
  157. // This sets up the catmull rom matrix based on the blend factor!!
  158. // we can go from linear to curvy!
  159. s_CatmullRom.Init( -0.5 * m_LinearFactor, 1.5 * m_LinearFactor, -1.5 * m_LinearFactor, 0.5 * m_LinearFactor,
  160. m_LinearFactor, -2.5 * m_LinearFactor, 2 * m_LinearFactor, -0.5 * m_LinearFactor,
  161. -0.5 * m_LinearFactor, -1 + m_LinearFactor, 1 - 0.5 * m_LinearFactor, 0,
  162. 0, 1, 0, 0 );
  163. Vector4DMultiplyTranspose( s_CatmullRom, svec, m_SVec );
  164. Vector4DMultiplyTranspose( s_CatmullRom, tvec, m_TVec );
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Gets the point and normal at (i,j) specified above
  168. //-----------------------------------------------------------------------------
  169. void CSplinePatch::GetPointAndNormal( Vector& position, Vector& normal ) const
  170. {
  171. // The patch equation is:
  172. // px = S * M * Gx * M^T * T^T
  173. // py = S * M * Gy * M^T * T^T
  174. // pz = S * M * Gz * M^T * T^T
  175. // where S = [s^3 s^2 s 1], T = [t^3 t^2 t 1]
  176. // M is the patch type matrix, in my case I'm using a catmull-rom
  177. // G is the array of control points. rows have constant t
  178. VMatrix controlPointsX, controlPointsY, controlPointsZ;
  179. for (int i = 0; i < 4; ++i)
  180. {
  181. for (int j = 0; j < 4; ++j)
  182. {
  183. int idx = m_SampleIndices[i][j];
  184. controlPointsX[i][j] = m_ppPositions[ idx ]->x;
  185. controlPointsY[i][j] = m_ppPositions[ idx ]->y;
  186. controlPointsZ[i][j] = m_ppPositions[ idx ]->z;
  187. }
  188. }
  189. Vector4D tmp;
  190. Vector4DMultiply( controlPointsX, m_TVec, tmp );
  191. position[0] = DotProduct4D( tmp, m_SVec );
  192. Vector4DMultiply( controlPointsY, m_TVec, tmp );
  193. position[1] = DotProduct4D( tmp, m_SVec );
  194. Vector4DMultiply( controlPointsZ, m_TVec, tmp );
  195. position[2] = DotProduct4D( tmp, m_SVec );
  196. // Normal computation
  197. float fs2 = m_fs * m_fs;
  198. float ft2 = m_ft * m_ft;
  199. Vector4D dsvec( 3.0f * fs2, 2.0f * m_fs, 1.0f, 0.0f );
  200. Vector4D dtvec( 3.0f * ft2, 2.0f * m_ft, 1.0f, 0.0f );
  201. Vector4DMultiplyTranspose( s_CatmullRom, dsvec, dsvec );
  202. Vector4DMultiplyTranspose( s_CatmullRom, dtvec, dtvec );
  203. Vector ds, dt;
  204. Vector4DMultiply( controlPointsX, m_TVec, tmp );
  205. ds[0] = DotProduct4D( tmp, dsvec );
  206. Vector4DMultiply( controlPointsY, m_TVec, tmp );
  207. ds[1] = DotProduct4D( tmp, dsvec );
  208. Vector4DMultiply( controlPointsZ, m_TVec, tmp );
  209. ds[2] = DotProduct4D( tmp, dsvec );
  210. Vector4DMultiply( controlPointsX, dtvec, tmp );
  211. dt[0] = DotProduct4D( tmp, m_SVec );
  212. Vector4DMultiply( controlPointsY, dtvec, tmp );
  213. dt[1] = DotProduct4D( tmp, m_SVec );
  214. Vector4DMultiply( controlPointsZ, dtvec, tmp );
  215. dt[2] = DotProduct4D( tmp, m_SVec );
  216. CrossProduct( ds, dt, normal );
  217. VectorNormalize( normal );
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Gets at other channels
  221. //-----------------------------------------------------------------------------
  222. float CSplinePatch::GetChannel( int channel ) const
  223. {
  224. // The patch equation is:
  225. // px = S * M * Gx * M^T * T^T
  226. // py = S * M * Gy * M^T * T^T
  227. // pz = S * M * Gz * M^T * T^T
  228. // where S = [s^3 s^2 s 1], T = [t^3 t^2 t 1]
  229. // M is the patch type matrix, in my case I'm using a catmull-rom
  230. // G is the array of control points. rows have constant t
  231. assert( m_pChannel[channel] );
  232. VMatrix controlPoints;
  233. for (int i = 0; i < 4; ++i)
  234. {
  235. for (int j = 0; j < 4; ++j)
  236. {
  237. controlPoints[i][j] = m_pChannel[channel][ m_SampleIndices[i][j] ];
  238. }
  239. }
  240. Vector4D tmp;
  241. Vector4DMultiply( controlPoints, m_TVec, tmp );
  242. return DotProduct4D( tmp, m_SVec );
  243. }