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
276 lines
7.7 KiB
//========= 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(); |
|
}
|
|
|