diff options
Diffstat (limited to 'app/legion/heightfield.cpp')
| -rw-r--r-- | app/legion/heightfield.cpp | 276 |
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(); +} |