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.

276 lines
7.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Heightfield class
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "heightfield.h"
  9. #include "materialsystem/imaterial.h"
  10. #include "legion.h"
  11. #include "materialsystem/imaterialsystem.h"
  12. #include "materialsystem/imesh.h"
  13. #include "tier2/tier2.h"
  14. #include "tier2/utlstreambuffer.h"
  15. #include "bitmap/bitmap.h"
  16. #include "bitmap/psd.h"
  17. #include "tier1/KeyValues.h"
  18. //-----------------------------------------------------------------------------
  19. // Utility macro
  20. //-----------------------------------------------------------------------------
  21. #define HEIGHT( _x, _y ) m_pHeightField[ ( (_y) << m_nPowX ) + (_x) ]
  22. #define ROW( _y ) &m_pHeightField[ (_y) << m_nPowX ]
  23. //-----------------------------------------------------------------------------
  24. // Constructor, destructor
  25. //-----------------------------------------------------------------------------
  26. CHeightField::CHeightField( int nPowX, int nPowY, int nPowScale )
  27. {
  28. m_nPowX = nPowX;
  29. m_nPowY = nPowY;
  30. m_nPowScale = nPowScale;
  31. m_nWidth = ( 1 << nPowX );
  32. m_nHeight = ( 1 << nPowY );
  33. m_nScale = ( 1 << nPowScale );
  34. m_flOOScale = 1.0f / m_nScale;
  35. m_pHeightField = (float*)malloc( m_nWidth * m_nHeight * sizeof(float) );
  36. memset( m_pHeightField, 0, m_nWidth * m_nHeight * sizeof(float) );
  37. KeyValues *pKeyValues = new KeyValues( "Wireframe" );
  38. pKeyValues->SetInt( "$nocull", 1 );
  39. m_Material.Init( "__Temp", pKeyValues );
  40. }
  41. CHeightField::~CHeightField()
  42. {
  43. free( m_pHeightField );
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Bilinearly filters a sample out of a bitmap at a particular (x,y)
  47. // NOTE: x,y are not normalized and are expected to go in the range of (0->w-1, 0->h-1)
  48. //-----------------------------------------------------------------------------
  49. float BilerpBitmap( Bitmap_t &bitmap, float x, float y )
  50. {
  51. Assert( bitmap.m_ImageFormat == IMAGE_FORMAT_RGBA8888 );
  52. float w = (float)bitmap.m_nWidth;
  53. float h = (float)bitmap.m_nHeight;
  54. // Clamp to a valid range
  55. x = clamp( x, 0, w - 1.0f );
  56. y = clamp( y, 0, h - 1.0f );
  57. // pick bilerp coordinates
  58. int i0 = (int)floor( x );
  59. int i1 = i0 + 1;
  60. int j0 = (int)floor( y );
  61. int j1 = j0 + 1;
  62. if ( i1 >= bitmap.m_nWidth )
  63. {
  64. i1 = bitmap.m_nWidth - 1;
  65. }
  66. if ( j1 >= bitmap.m_nHeight )
  67. {
  68. j1 = bitmap.m_nHeight - 1;
  69. }
  70. float fx = x - i0;
  71. float fy = y - j0;
  72. RGBA8888_t* pPixel00 = (RGBA8888_t*)bitmap.GetPixel( i0, j0 );
  73. RGBA8888_t* pPixel10 = (RGBA8888_t*)bitmap.GetPixel( i1, j0 );
  74. RGBA8888_t* pPixel01 = (RGBA8888_t*)bitmap.GetPixel( i0, j1 );
  75. RGBA8888_t* pPixel11 = (RGBA8888_t*)bitmap.GetPixel( i1, j1 );
  76. float v00 = pPixel00->r / 255.0f;
  77. float v10 = pPixel10->r / 255.0f;
  78. float v01 = pPixel01->r / 255.0f;
  79. float v11 = pPixel11->r / 255.0f;
  80. // do the bilerp
  81. return (1-fx)*(1-fy)*v00 + fx*(1-fy)*v10 + (1-fx)*fy*v01 + fx*fy*v11;
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Loads the heightfield from a file
  85. //-----------------------------------------------------------------------------
  86. bool CHeightField::LoadHeightFromFile( const char *pFileName )
  87. {
  88. Bitmap_t bitmap;
  89. CUtlStreamBuffer buf( pFileName, "GAME", CUtlBuffer::READ_ONLY );
  90. if ( IsPSDFile( buf ) )
  91. {
  92. if ( !PSDReadFileRGBA8888( buf, bitmap ) )
  93. return false;
  94. }
  95. // map from height field into map, ensuring corner pixel centers line up
  96. // hfx -> mapx: 0 -> 0.5, hfw-1 -> mapw-0.5
  97. // x (mapw - 1)/(hfw - 1) + 0.5
  98. // mapx -> worldx: 0 -> 0, mapw -> worldw
  99. float fx = (float)( bitmap.m_nWidth - 1) / (float)( m_nWidth - 1 );
  100. float fy = (float)( bitmap.m_nHeight - 1) / (float)( m_nHeight - 1 );
  101. for( int i = 0; i < m_nHeight; ++i )
  102. {
  103. float *pRow = ROW( i );
  104. for( int j = 0; j < m_nWidth; ++j, ++pRow )
  105. {
  106. *pRow = 50.0f * BilerpBitmap( bitmap, i * fx, j * fy );
  107. }
  108. }
  109. return true;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Returns the height at a particular point
  113. //-----------------------------------------------------------------------------
  114. float CHeightField::GetHeight( float x, float y )
  115. {
  116. x *= m_flOOScale;
  117. y *= m_flOOScale;
  118. int gx = (int)floor( x );
  119. int gy = (int)floor( y );
  120. x -= gx;
  121. y -= gy;
  122. // Check for out of range
  123. if ( gx < -1 || gy < -1 || gx >= m_nWidth || gy >= m_nHeight )
  124. return 0.0f;
  125. float h00 = ( gx >= 0 && gy >= 0 ) ? HEIGHT(gx , gy ) : 0.0f;
  126. float h01 = ( gx < (m_nWidth-1) && gy >= 0 ) ? HEIGHT(gx+1, gy ) : 0.0f;
  127. float h10 = ( gx >= 0 && gy < (m_nHeight-1) ) ? HEIGHT(gx , gy+1) : 0.0f;
  128. float h11 = ( gx<(m_nWidth-1) && gy<(m_nHeight-1) ) ? HEIGHT(gx+1, gy+1) : 0.0f;
  129. // This fixup accounts for the triangularization of the mesh
  130. if (x > y)
  131. {
  132. h10 = h00 + h11 - h01;
  133. }
  134. else
  135. {
  136. h01 = h00 + h11 - h10;
  137. }
  138. // Bilinear filter
  139. float h0 = h00 + ( h01 - h00 ) * x;
  140. float h1 = h10 + ( h11 - h10 ) * x;
  141. float h = h0 + (h1-h0)*y;
  142. return h;
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Returns the height + slope at a particular point
  146. //-----------------------------------------------------------------------------
  147. float CHeightField::GetHeightAndSlope( float x, float y, float *dx, float *dy )
  148. {
  149. x *= m_flOOScale;
  150. y *= m_flOOScale;
  151. int gx = (int)floor(x);
  152. int gy = (int)floor(y);
  153. x -= gx;
  154. y -= gy;
  155. if ( gx < -1 || gy < -1 || gx >= m_nWidth || gy >= m_nHeight )
  156. {
  157. *dx = 0;
  158. *dy = 0;
  159. return 0.0f;
  160. }
  161. float h00 = ( gx >= 0 && gy >= 0 ) ? HEIGHT(gx , gy ) : 0.0f;
  162. float h01 = ( gx < (m_nWidth-1) && gy >= 0 ) ? HEIGHT(gx+1, gy ) : 0.0f;
  163. float h10 = ( gx >= 0 && gy < (m_nHeight-1) ) ? HEIGHT(gx , gy+1) : 0.0f;
  164. float h11 = ( gx<(m_nWidth-1) && gy<(m_nHeight-1) ) ? HEIGHT(gx+1, gy+1) : 0.0f;
  165. if (x > y)
  166. {
  167. h10 = h00 + h11 - h01;
  168. }
  169. else
  170. {
  171. h01 = h00 + h11 - h10;
  172. }
  173. *dx = ( h01 - h00 ) * m_flOOScale;
  174. *dy = ( h10 - h00 ) * m_flOOScale;
  175. // Bilinear filter
  176. float h0 = h00 + ( h01 - h00 ) * x;
  177. float h1 = h10 + ( h11 - h10 ) * x;
  178. float h = h0 + ( h1 - h0 )* y;
  179. return h;
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Draws the height field
  183. //-----------------------------------------------------------------------------
  184. void CHeightField::Draw( )
  185. {
  186. int nVertexCount = m_nWidth * m_nHeight;
  187. int nIndexCount = 6 * ( m_nWidth - 1 ) * ( m_nHeight - 1 );
  188. float flOOTexWidth = 1.0f / m_Material->GetMappingWidth();
  189. float flOOTexHeight = 1.0f / m_Material->GetMappingHeight();
  190. float iu = 0.5f * flOOTexWidth;
  191. float iv = 1.0f - ( 0.5f * flOOTexHeight );
  192. float du = ( 1.0f - flOOTexWidth ) / ( m_nWidth - 1 );
  193. float dv = -( 1.0f - flOOTexHeight ) / ( m_nHeight - 1 );
  194. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  195. pRenderContext->Bind( m_Material );
  196. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  197. CMeshBuilder meshBuilder;
  198. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, nIndexCount );
  199. // Deal with vertices
  200. float v = iv;
  201. float y = 0.0f;
  202. for ( int i = 0; i < m_nHeight; ++i, y += m_nScale, v += dv )
  203. {
  204. float u = iu;
  205. float x = 0.0f;
  206. for ( int j = 0; j < m_nWidth; ++j, x += m_nScale, u += du )
  207. {
  208. meshBuilder.Position3f( x, y, HEIGHT( j, i ) );
  209. meshBuilder.TexCoord2f( 0, u, v );
  210. meshBuilder.AdvanceVertex();
  211. }
  212. }
  213. // Deal with indices
  214. for ( int i = 0; i < (m_nHeight - 1); ++i )
  215. {
  216. int nRow0 = m_nWidth * i;
  217. int nRow1 = nRow0 + m_nWidth;
  218. for ( int j = 0; j < (m_nWidth - 1); ++j )
  219. {
  220. meshBuilder.FastIndex( nRow0+j );
  221. meshBuilder.FastIndex( nRow0+j+1 );
  222. meshBuilder.FastIndex( nRow1+j+1 );
  223. meshBuilder.FastIndex( nRow0+j );
  224. meshBuilder.FastIndex( nRow1+j+1 );
  225. meshBuilder.FastIndex( nRow1+j );
  226. }
  227. }
  228. meshBuilder.End();
  229. pMesh->Draw();
  230. }