summaryrefslogtreecommitdiff
path: root/app/legion/heightfield.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/legion/heightfield.cpp')
-rw-r--r--app/legion/heightfield.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/app/legion/heightfield.cpp b/app/legion/heightfield.cpp
new file mode 100644
index 0000000..9daf046
--- /dev/null
+++ b/app/legion/heightfield.cpp
@@ -0,0 +1,276 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Heightfield class
+//
+// $Revision: $
+// $NoKeywords: $
+//===========================================================================//
+
+#include "heightfield.h"
+#include "materialsystem/imaterial.h"
+#include "legion.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "tier2/tier2.h"
+#include "tier2/utlstreambuffer.h"
+#include "bitmap/bitmap.h"
+#include "bitmap/psd.h"
+#include "tier1/KeyValues.h"
+
+
+//-----------------------------------------------------------------------------
+// Utility macro
+//-----------------------------------------------------------------------------
+#define HEIGHT( _x, _y ) m_pHeightField[ ( (_y) << m_nPowX ) + (_x) ]
+#define ROW( _y ) &m_pHeightField[ (_y) << m_nPowX ]
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CHeightField::CHeightField( int nPowX, int nPowY, int nPowScale )
+{
+ m_nPowX = nPowX;
+ m_nPowY = nPowY;
+ m_nPowScale = nPowScale;
+ m_nWidth = ( 1 << nPowX );
+ m_nHeight = ( 1 << nPowY );
+ m_nScale = ( 1 << nPowScale );
+ m_flOOScale = 1.0f / m_nScale;
+ m_pHeightField = (float*)malloc( m_nWidth * m_nHeight * sizeof(float) );
+ memset( m_pHeightField, 0, m_nWidth * m_nHeight * sizeof(float) );
+
+ KeyValues *pKeyValues = new KeyValues( "Wireframe" );
+ pKeyValues->SetInt( "$nocull", 1 );
+ m_Material.Init( "__Temp", pKeyValues );
+}
+
+CHeightField::~CHeightField()
+{
+ free( m_pHeightField );
+}
+
+
+//-----------------------------------------------------------------------------
+// Bilinearly filters a sample out of a bitmap at a particular (x,y)
+// NOTE: x,y are not normalized and are expected to go in the range of (0->w-1, 0->h-1)
+//-----------------------------------------------------------------------------
+float BilerpBitmap( Bitmap_t &bitmap, float x, float y )
+{
+ Assert( bitmap.m_ImageFormat == IMAGE_FORMAT_RGBA8888 );
+
+ float w = (float)bitmap.m_nWidth;
+ float h = (float)bitmap.m_nHeight;
+
+ // Clamp to a valid range
+ x = clamp( x, 0, w - 1.0f );
+ y = clamp( y, 0, h - 1.0f );
+
+ // pick bilerp coordinates
+ int i0 = (int)floor( x );
+ int i1 = i0 + 1;
+ int j0 = (int)floor( y );
+ int j1 = j0 + 1;
+ if ( i1 >= bitmap.m_nWidth )
+ {
+ i1 = bitmap.m_nWidth - 1;
+ }
+ if ( j1 >= bitmap.m_nHeight )
+ {
+ j1 = bitmap.m_nHeight - 1;
+ }
+
+ float fx = x - i0;
+ float fy = y - j0;
+
+ RGBA8888_t* pPixel00 = (RGBA8888_t*)bitmap.GetPixel( i0, j0 );
+ RGBA8888_t* pPixel10 = (RGBA8888_t*)bitmap.GetPixel( i1, j0 );
+ RGBA8888_t* pPixel01 = (RGBA8888_t*)bitmap.GetPixel( i0, j1 );
+ RGBA8888_t* pPixel11 = (RGBA8888_t*)bitmap.GetPixel( i1, j1 );
+
+ float v00 = pPixel00->r / 255.0f;
+ float v10 = pPixel10->r / 255.0f;
+ float v01 = pPixel01->r / 255.0f;
+ float v11 = pPixel11->r / 255.0f;
+
+ // do the bilerp
+ return (1-fx)*(1-fy)*v00 + fx*(1-fy)*v10 + (1-fx)*fy*v01 + fx*fy*v11;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the heightfield from a file
+//-----------------------------------------------------------------------------
+bool CHeightField::LoadHeightFromFile( const char *pFileName )
+{
+ Bitmap_t bitmap;
+ CUtlStreamBuffer buf( pFileName, "GAME", CUtlBuffer::READ_ONLY );
+ if ( IsPSDFile( buf ) )
+ {
+ if ( !PSDReadFileRGBA8888( buf, bitmap ) )
+ return false;
+ }
+
+ // map from height field into map, ensuring corner pixel centers line up
+ // hfx -> mapx: 0 -> 0.5, hfw-1 -> mapw-0.5
+ // x (mapw - 1)/(hfw - 1) + 0.5
+ // mapx -> worldx: 0 -> 0, mapw -> worldw
+ float fx = (float)( bitmap.m_nWidth - 1) / (float)( m_nWidth - 1 );
+ float fy = (float)( bitmap.m_nHeight - 1) / (float)( m_nHeight - 1 );
+
+ for( int i = 0; i < m_nHeight; ++i )
+ {
+ float *pRow = ROW( i );
+ for( int j = 0; j < m_nWidth; ++j, ++pRow )
+ {
+ *pRow = 50.0f * BilerpBitmap( bitmap, i * fx, j * fy );
+ }
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the height at a particular point
+//-----------------------------------------------------------------------------
+float CHeightField::GetHeight( float x, float y )
+{
+ x *= m_flOOScale;
+ y *= m_flOOScale;
+
+ int gx = (int)floor( x );
+ int gy = (int)floor( y );
+ x -= gx;
+ y -= gy;
+
+ // Check for out of range
+ if ( gx < -1 || gy < -1 || gx >= m_nWidth || gy >= m_nHeight )
+ return 0.0f;
+
+ float h00 = ( gx >= 0 && gy >= 0 ) ? HEIGHT(gx , gy ) : 0.0f;
+ float h01 = ( gx < (m_nWidth-1) && gy >= 0 ) ? HEIGHT(gx+1, gy ) : 0.0f;
+ float h10 = ( gx >= 0 && gy < (m_nHeight-1) ) ? HEIGHT(gx , gy+1) : 0.0f;
+ float h11 = ( gx<(m_nWidth-1) && gy<(m_nHeight-1) ) ? HEIGHT(gx+1, gy+1) : 0.0f;
+
+ // This fixup accounts for the triangularization of the mesh
+ if (x > y)
+ {
+ h10 = h00 + h11 - h01;
+ }
+ else
+ {
+ h01 = h00 + h11 - h10;
+ }
+
+ // Bilinear filter
+ float h0 = h00 + ( h01 - h00 ) * x;
+ float h1 = h10 + ( h11 - h10 ) * x;
+ float h = h0 + (h1-h0)*y;
+
+ return h;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the height + slope at a particular point
+//-----------------------------------------------------------------------------
+float CHeightField::GetHeightAndSlope( float x, float y, float *dx, float *dy )
+{
+ x *= m_flOOScale;
+ y *= m_flOOScale;
+
+ int gx = (int)floor(x);
+ int gy = (int)floor(y);
+ x -= gx;
+ y -= gy;
+
+ if ( gx < -1 || gy < -1 || gx >= m_nWidth || gy >= m_nHeight )
+ {
+ *dx = 0;
+ *dy = 0;
+ return 0.0f;
+ }
+
+ float h00 = ( gx >= 0 && gy >= 0 ) ? HEIGHT(gx , gy ) : 0.0f;
+ float h01 = ( gx < (m_nWidth-1) && gy >= 0 ) ? HEIGHT(gx+1, gy ) : 0.0f;
+ float h10 = ( gx >= 0 && gy < (m_nHeight-1) ) ? HEIGHT(gx , gy+1) : 0.0f;
+ float h11 = ( gx<(m_nWidth-1) && gy<(m_nHeight-1) ) ? HEIGHT(gx+1, gy+1) : 0.0f;
+
+ if (x > y)
+ {
+ h10 = h00 + h11 - h01;
+ }
+ else
+ {
+ h01 = h00 + h11 - h10;
+ }
+
+ *dx = ( h01 - h00 ) * m_flOOScale;
+ *dy = ( h10 - h00 ) * m_flOOScale;
+
+ // Bilinear filter
+ float h0 = h00 + ( h01 - h00 ) * x;
+ float h1 = h10 + ( h11 - h10 ) * x;
+ float h = h0 + ( h1 - h0 )* y;
+
+ return h;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the height field
+//-----------------------------------------------------------------------------
+void CHeightField::Draw( )
+{
+ int nVertexCount = m_nWidth * m_nHeight;
+ int nIndexCount = 6 * ( m_nWidth - 1 ) * ( m_nHeight - 1 );
+
+ float flOOTexWidth = 1.0f / m_Material->GetMappingWidth();
+ float flOOTexHeight = 1.0f / m_Material->GetMappingHeight();
+ float iu = 0.5f * flOOTexWidth;
+ float iv = 1.0f - ( 0.5f * flOOTexHeight );
+ float du = ( 1.0f - flOOTexWidth ) / ( m_nWidth - 1 );
+ float dv = -( 1.0f - flOOTexHeight ) / ( m_nHeight - 1 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( m_Material );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, nIndexCount );
+
+ // Deal with vertices
+ float v = iv;
+ float y = 0.0f;
+ for ( int i = 0; i < m_nHeight; ++i, y += m_nScale, v += dv )
+ {
+ float u = iu;
+ float x = 0.0f;
+ for ( int j = 0; j < m_nWidth; ++j, x += m_nScale, u += du )
+ {
+ meshBuilder.Position3f( x, y, HEIGHT( j, i ) );
+ meshBuilder.TexCoord2f( 0, u, v );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ // Deal with indices
+ for ( int i = 0; i < (m_nHeight - 1); ++i )
+ {
+ int nRow0 = m_nWidth * i;
+ int nRow1 = nRow0 + m_nWidth;
+ for ( int j = 0; j < (m_nWidth - 1); ++j )
+ {
+ meshBuilder.FastIndex( nRow0+j );
+ meshBuilder.FastIndex( nRow0+j+1 );
+ meshBuilder.FastIndex( nRow1+j+1 );
+
+ meshBuilder.FastIndex( nRow0+j );
+ meshBuilder.FastIndex( nRow1+j+1 );
+ meshBuilder.FastIndex( nRow1+j );
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}