419 lines
14 KiB
C++
419 lines
14 KiB
C++
// Copyright (C) 2007 Id Software, Inc.
|
|
//
|
|
|
|
|
|
#include "../precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "HeightMap.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
sdHeightMap
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::sdHeightMap
|
|
==============
|
|
*/
|
|
sdHeightMap::sdHeightMap( void ) {
|
|
Clear();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::~sdHeightMap
|
|
==============
|
|
*/
|
|
sdHeightMap::~sdHeightMap( void ) {
|
|
Clear();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::Clear
|
|
==============
|
|
*/
|
|
void sdHeightMap::Clear( void ) {
|
|
data.Clear();
|
|
|
|
dimensions[ 0 ] = 0;
|
|
dimensions[ 1 ] = 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::Init
|
|
==============
|
|
*/
|
|
void sdHeightMap::Init( int w, int h, byte height ) {
|
|
dimensions[ 0 ] = w;
|
|
dimensions[ 1 ] = h;
|
|
|
|
data.AssureSize( dimensions[ 0 ] * dimensions[ 1 ], 0 );
|
|
for (int i=0; i<w; i++) {
|
|
for (int j=0; j<h; j++) {
|
|
data[ i * h + j ] = height;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::Load
|
|
==============
|
|
*/
|
|
void sdHeightMap::Load( const char* filename ) {
|
|
Clear();
|
|
|
|
if ( !filename || !*filename ) {
|
|
return;
|
|
}
|
|
|
|
byte* pic;
|
|
unsigned timestamp;
|
|
fileSystem->ReadTGA( filename, &pic, &dimensions[ 0 ], &dimensions[ 1 ], ×tamp );
|
|
|
|
if ( dimensions[ 0 ] == 0 || dimensions[ 1 ] == 0 ) {
|
|
return;
|
|
}
|
|
|
|
data.AssureSize( dimensions[ 0 ] * dimensions[ 1 ], 0 );
|
|
|
|
int x;
|
|
for ( x = 0; x < dimensions[ 0 ]; x++ ) {
|
|
int y;
|
|
for ( y = 0; y < dimensions[ 1 ]; y++ ) {
|
|
data[ x + ( y * dimensions[ 0 ] ) ] = pic[ ( x + ( ( dimensions[ 1 ] - 1 - y ) * dimensions[ 0 ] ) ) * 4 ];
|
|
}
|
|
}
|
|
|
|
fileSystem->FreeTGA( pic );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::GetInterpolatedHeight
|
|
==============
|
|
*/
|
|
float sdHeightMap::GetInterpolatedHeight( const idVec3& pos, const sdHeightMapScaleData& scale ) const {
|
|
|
|
// find which points to sample
|
|
int minCoords[ 2 ];
|
|
int maxCoords[ 2 ];
|
|
float blendValues[ 2 ];
|
|
|
|
for ( int i = 0; i < 2; i ++ ) {
|
|
float posCoord = idMath::ClampFloat( 0.f, 1.f, ( pos[ i ] - scale.mins[ i ] ) * scale.invSize[ i ] ) * ( dimensions[ i ] - 1 );
|
|
float intCoord = ( int )posCoord;
|
|
float coordLeftover = posCoord - intCoord;
|
|
|
|
minCoords[ i ] = intCoord;
|
|
maxCoords[ i ] = idMath::ClampInt( 0, dimensions[ i ] - 1, intCoord + 1 );
|
|
blendValues[ i ] = coordLeftover;
|
|
}
|
|
|
|
// sample points
|
|
byte tl = data[ minCoords[ 0 ] + ( maxCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
byte tr = data[ maxCoords[ 0 ] + ( maxCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
byte bl = data[ minCoords[ 0 ] + ( minCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
byte br = data[ maxCoords[ 0 ] + ( minCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
|
|
float combinedTop = Lerp( tl, tr, blendValues[ 0 ] );
|
|
float combinedBottom = Lerp( bl, br, blendValues[ 0 ] );
|
|
float combined = Lerp( combinedBottom, combinedTop, blendValues[ 0 ] );
|
|
|
|
return ( combined * scale.heightScale ) + scale.heightOffset;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::GetHeight
|
|
==============
|
|
*/
|
|
void sdHeightMap::GetHeight( const idBounds& in, idVec2& out, const sdHeightMapScaleData& scale ) const {
|
|
if ( data.Num() == 0 ) {
|
|
gameLocal.Warning( "sdHeightMap::GetHeight() - no heightmap data!" );
|
|
out[ 0 ] = 0.0f;
|
|
out[ 1 ] = 0.0f;
|
|
return;
|
|
}
|
|
|
|
int minCoords[ 2 ];
|
|
int maxCoords[ 2 ];
|
|
|
|
minCoords[ 0 ] = ( idMath::ClampFloat( 0.f, 1.f, ( in.GetMins()[ 0 ] - scale.mins[ 0 ] ) * scale.invSize[ 0 ] ) * ( dimensions[ 0 ] - 1 ) );
|
|
minCoords[ 1 ] = ( idMath::ClampFloat( 0.f, 1.f, ( in.GetMins()[ 1 ] - scale.mins[ 1 ] ) * scale.invSize[ 1 ] ) * ( dimensions[ 1 ] - 1 ) );
|
|
|
|
maxCoords[ 0 ] = ( idMath::ClampFloat( 0.f, 1.f, ( in.GetMaxs()[ 0 ] - scale.mins[ 0 ] ) * scale.invSize[ 0 ] ) * ( dimensions[ 0 ] - 1 ) ) + 1;
|
|
maxCoords[ 1 ] = ( idMath::ClampFloat( 0.f, 1.f, ( in.GetMaxs()[ 1 ] - scale.mins[ 1 ] ) * scale.invSize[ 1 ] ) * ( dimensions[ 1 ] - 1 ) ) + 1;
|
|
|
|
int x;
|
|
int y;
|
|
float value;
|
|
|
|
out[ 0 ] = idMath::INFINITY;
|
|
out[ 1 ] = -idMath::INFINITY;
|
|
|
|
for ( x = minCoords[ 0 ]; x < maxCoords[ 0 ]; x++ ) {
|
|
for ( y = minCoords[ 1 ]; y < maxCoords[ 1 ]; y++ ) {
|
|
value = ( data[ x + ( y * dimensions[ 0 ] ) ] * scale.heightScale ) + scale.heightOffset;
|
|
if ( value < out[ 0 ] ) {
|
|
out[ 0 ] = value;
|
|
}
|
|
if ( value > out[ 1 ] ) {
|
|
out[ 1 ] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::GetHeight
|
|
==============
|
|
*/
|
|
float sdHeightMap::GetHeight( const idVec3& start, const idVec3& end, const sdHeightMapScaleData& scale ) const {
|
|
if ( data.Num() == 0 ) {
|
|
gameLocal.Warning( "sdHeightMap::TracePoint() - no heightmap data!" );
|
|
return 0.0f;
|
|
}
|
|
|
|
// find the start & end points in heightmap coordinates
|
|
idVec2 startCoords;
|
|
idVec2 endCoords;
|
|
startCoords[ 0 ] = ( start[ 0 ] - scale.mins[ 0 ] ) * ( scale.invSize[ 0 ] * ( dimensions[ 0 ] - 1 ) );
|
|
startCoords[ 1 ] = ( start[ 1 ] - scale.mins[ 1 ] ) * ( scale.invSize[ 1 ] * ( dimensions[ 1 ] - 1 ) );
|
|
endCoords[ 0 ] = ( end[ 0 ] - scale.mins[ 0 ] ) * ( scale.invSize[ 0 ] * ( dimensions[ 0 ] - 1 ) );
|
|
endCoords[ 1 ] = ( end[ 1 ] - scale.mins[ 1 ] ) * ( scale.invSize[ 1 ] * ( dimensions[ 1 ] - 1 ) );
|
|
|
|
const float left = 0.0f;
|
|
const float right = dimensions[ 0 ] - 1.0f;
|
|
const float top = 0.0f;
|
|
const float bottom = dimensions[ 1 ] - 1.0f;
|
|
|
|
// find if the trace is entirely outside the map
|
|
if ( ( startCoords[ 0 ] < left && endCoords[ 0 ] < left ) || ( startCoords[ 0 ] > right && endCoords[ 0 ] > right ) ) {
|
|
return 0.0f;
|
|
}
|
|
if ( ( startCoords[ 1 ] < top && endCoords[ 1 ] < top ) || ( startCoords[ 1 ] > bottom && endCoords[ 1 ] > bottom ) ) {
|
|
return 0.0f;
|
|
}
|
|
|
|
idVec2 coordDelta = endCoords - startCoords;
|
|
idVec2 direction = coordDelta;
|
|
direction.Normalize();
|
|
|
|
// clip the start & end coords to the dimensions of the map whilst keeping the same slopes
|
|
if ( startCoords[ 0 ] < left ) startCoords += ( ( left - startCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( startCoords[ 0 ] > right ) startCoords += ( ( right - startCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( startCoords[ 1 ] < top ) startCoords += ( ( top - startCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( startCoords[ 1 ] > bottom ) startCoords += ( ( bottom - startCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
|
|
if ( endCoords[ 0 ] < left ) endCoords += ( ( left - endCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( endCoords[ 0 ] > right ) endCoords += ( ( right - endCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( endCoords[ 1 ] < top ) endCoords += ( ( top - endCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( endCoords[ 1 ] > bottom ) endCoords += ( ( bottom - endCoords[ 1 ] ) / direction[ 1 ] ) * direction;;
|
|
|
|
// update the coorddelta
|
|
coordDelta = endCoords - startCoords;
|
|
|
|
// check if the trace is a vertical one
|
|
if ( idMath::Fabs( coordDelta[ 0 ] ) < 1.0f && idMath::Fabs( coordDelta[ 1 ] ) < 1.0f ) {
|
|
return GetHeight( start, scale );
|
|
}
|
|
|
|
// find the step to go by
|
|
int mostSignificantAxis = 0;
|
|
if ( idMath::Fabs( direction[ 1 ] ) > idMath::Fabs( direction[ 0 ] ) ) {
|
|
mostSignificantAxis = 1;
|
|
}
|
|
|
|
float axisStep = idMath::Fabs( direction[ mostSignificantAxis ] );
|
|
idVec2 step = direction / axisStep;
|
|
if ( step[ mostSignificantAxis ] > 0.0f ) {
|
|
step[ mostSignificantAxis ] = 1.0f; // paranoid
|
|
} else {
|
|
step[ mostSignificantAxis ] = -1.0f; // paranoid
|
|
}
|
|
|
|
int travelled = 0;
|
|
int fullDistance = ( int )endCoords[ mostSignificantAxis ] - ( int )startCoords[ mostSignificantAxis ];
|
|
fullDistance = idMath::Abs( fullDistance );
|
|
byte maxHeight = 0;
|
|
|
|
// TODO: Could optimize this a lot by using ints only in here
|
|
idVec2 testCoords = startCoords;
|
|
for ( int travelled = 0; travelled < fullDistance; travelled++ ) {
|
|
int intTestCoords[ 2 ];
|
|
intTestCoords[ 0 ] = testCoords[ 0 ];
|
|
intTestCoords[ 1 ] = testCoords[ 1 ];
|
|
|
|
byte height = data[ intTestCoords[ 0 ] + ( intTestCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
if ( height > maxHeight ) {
|
|
maxHeight = height;
|
|
}
|
|
|
|
testCoords += step;
|
|
}
|
|
|
|
return ( maxHeight * scale.heightScale ) + scale.heightOffset;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
sdHeightMap::TracePoint
|
|
==============
|
|
*/
|
|
float sdHeightMap::TracePoint( const idVec3& start, const idVec3& end, idVec3& result, float heightOffset, const sdHeightMapScaleData& scale ) const {
|
|
result = end;
|
|
if ( data.Num() == 0 ) {
|
|
gameLocal.Warning( "sdHeightMap::TracePoint() - no heightmap data!" );
|
|
return 1.0f;
|
|
}
|
|
|
|
// find the start & end points in heightmap coordinates
|
|
idVec3 startCoords, unclippedStartCoords;
|
|
idVec3 endCoords, unclippedEndCoords;
|
|
startCoords[ 0 ] = ( start[ 0 ] - scale.mins[ 0 ] ) * ( scale.invSize[ 0 ] * ( dimensions[ 0 ] - 1 ) );
|
|
startCoords[ 1 ] = ( start[ 1 ] - scale.mins[ 1 ] ) * ( scale.invSize[ 1 ] * ( dimensions[ 1 ] - 1 ) );
|
|
startCoords[ 2 ] = ( ( start[ 2 ] - heightOffset ) - scale.heightOffset ) / scale.heightScale;
|
|
endCoords[ 0 ] = ( end[ 0 ] - scale.mins[ 0 ] ) * ( scale.invSize[ 0 ] * ( dimensions[ 0 ] - 1 ) );
|
|
endCoords[ 1 ] = ( end[ 1 ] - scale.mins[ 1 ] ) * ( scale.invSize[ 1 ] * ( dimensions[ 1 ] - 1 ) );
|
|
endCoords[ 2 ] = ( ( end[ 2 ] - heightOffset ) - scale.heightOffset ) / scale.heightScale;
|
|
|
|
unclippedStartCoords = startCoords;
|
|
unclippedEndCoords = endCoords;
|
|
|
|
const float left = 0.0f;
|
|
const float right = dimensions[ 0 ] - 1.0f;
|
|
const float top = 0.0f;
|
|
const float bottom = dimensions[ 1 ] - 1.0f;
|
|
|
|
// find if the trace is entirely outside the map
|
|
if ( ( startCoords[ 0 ] < left && endCoords[ 0 ] < left ) || ( startCoords[ 0 ] > right && endCoords[ 0 ] > right ) ) {
|
|
return 1.0f;
|
|
}
|
|
if ( ( startCoords[ 1 ] < top && endCoords[ 1 ] < top ) || ( startCoords[ 1 ] > bottom && endCoords[ 1 ] > bottom ) ) {
|
|
return 1.0f;
|
|
}
|
|
if ( ( startCoords[ 2 ] < 0.0f && endCoords[ 2 ] < 0.0f ) || ( startCoords[ 2 ] > 255.0f && endCoords[ 2 ] > 255.0f ) ) {
|
|
return 1.0f;
|
|
}
|
|
|
|
idVec3 coordDelta = endCoords - startCoords;
|
|
idVec3 direction = coordDelta;
|
|
direction.Normalize();
|
|
|
|
// clip the start & end coords to the dimensions of the map whilst keeping the same slopes
|
|
// yes, ugly formatting, but it stops it being huuuuge
|
|
if ( startCoords[ 0 ] < left ) startCoords += ( ( left - startCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( startCoords[ 0 ] > right ) startCoords += ( ( right - startCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( startCoords[ 1 ] < top ) startCoords += ( ( top - startCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( startCoords[ 1 ] > bottom ) startCoords += ( ( bottom - startCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( startCoords[ 2 ] < 0.0f ) startCoords += ( ( -startCoords[ 2 ] ) / direction[ 2 ] ) * direction;
|
|
if ( startCoords[ 2 ] > 255.0f ) startCoords += ( ( 255.0f - startCoords[ 2 ] ) / direction[ 2 ] ) * direction;
|
|
|
|
if ( endCoords[ 0 ] < left ) endCoords += ( ( left - endCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( endCoords[ 0 ] > right ) endCoords += ( ( right - endCoords[ 0 ] ) / direction[ 0 ] ) * direction;
|
|
if ( endCoords[ 1 ] < top ) endCoords += ( ( top - endCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( endCoords[ 1 ] > bottom ) endCoords += ( ( bottom - endCoords[ 1 ] ) / direction[ 1 ] ) * direction;
|
|
if ( endCoords[ 2 ] < 0.0f ) endCoords += ( ( -endCoords[ 2 ] ) / direction[ 2 ] ) * direction;
|
|
if ( endCoords[ 2 ] > 255.0f ) endCoords += ( ( 255.0f - endCoords[ 2 ] ) / direction[ 2 ] ) * direction;
|
|
|
|
// update the coorddelta
|
|
coordDelta = endCoords - startCoords;
|
|
|
|
// check if the trace is a vertical one
|
|
if ( idMath::Fabs( coordDelta[ 0 ] ) < 1.0f && idMath::Fabs( coordDelta[ 1 ] ) < 1.0f ) {
|
|
// not tracing far enough to do anything
|
|
if ( idMath::Fabs( coordDelta[ 2 ] ) < 1.0f ) {
|
|
return 1.0f;
|
|
}
|
|
|
|
// tracing up can never return a hit
|
|
if ( endCoords[ 2 ] > startCoords[ 2 ] ) {
|
|
return 1.0f;
|
|
}
|
|
|
|
int intStart[ 2 ];
|
|
intStart[ 0 ] = startCoords[ 0 ];
|
|
intStart[ 1 ] = startCoords[ 1 ];
|
|
float height = data[ intStart[ 0 ] + ( intStart[ 1 ] * dimensions[ 0 ] ) ];
|
|
|
|
if ( height < startCoords[ 2 ] ) {
|
|
float fraction = ( unclippedStartCoords[ 2 ] - height ) / ( unclippedStartCoords[ 2 ] - unclippedEndCoords[ 2 ] );
|
|
if ( fraction > 1.0f ) {
|
|
fraction = 1.0f;
|
|
}
|
|
result = Lerp( start, end, fraction );
|
|
return fraction;
|
|
}
|
|
}
|
|
|
|
// Ok, clipped to the heightmap
|
|
// loop along the path & find where this crosses the map
|
|
int mostSignificantAxis = 0;
|
|
int mostSignificantFractionAxis = 0;
|
|
if ( idMath::Fabs( direction[ 1 ] ) > idMath::Fabs( direction[ 0 ] ) ) {
|
|
mostSignificantAxis = 1;
|
|
mostSignificantFractionAxis = 1;
|
|
}
|
|
if ( idMath::Fabs( direction[ 2 ] ) > idMath::Fabs( direction[ mostSignificantFractionAxis ] ) ) {
|
|
mostSignificantFractionAxis = 2;
|
|
}
|
|
|
|
// find the step to go by
|
|
float axisStep = idMath::Fabs( direction[ mostSignificantAxis ] );
|
|
idVec3 step = direction / axisStep;
|
|
if ( step[ mostSignificantAxis ] > 0.0f ) {
|
|
step[ mostSignificantAxis ] = 1.0f; // paranoid
|
|
} else {
|
|
step[ mostSignificantAxis ] = -1.0f; // paranoid
|
|
}
|
|
|
|
bool first = true;
|
|
bool wasAbove;
|
|
int travelled = 0;
|
|
int fullDistance = ( int )endCoords[ mostSignificantAxis ] - ( int )startCoords[ mostSignificantAxis ];
|
|
fullDistance = idMath::Abs( fullDistance );
|
|
|
|
// TODO: Could optimize this a lot by using ints only in here
|
|
idVec3 testCoords = startCoords;
|
|
for ( int travelled = 0; travelled < fullDistance; travelled++ ) {
|
|
int intTestCoords[ 3 ];
|
|
intTestCoords[ 0 ] = testCoords[ 0 ];
|
|
intTestCoords[ 1 ] = testCoords[ 1 ];
|
|
|
|
float height = data[ intTestCoords[ 0 ] + ( intTestCoords[ 1 ] * dimensions[ 0 ] ) ];
|
|
intTestCoords[ 2 ] = height;
|
|
bool above = testCoords[ 2 ] >= height;
|
|
if ( first ) {
|
|
wasAbove = above;
|
|
first = false;
|
|
} else {
|
|
if ( !above && wasAbove ) {
|
|
// it crossed through the heightmap
|
|
float fraction = intTestCoords[ mostSignificantFractionAxis ] - unclippedStartCoords[ mostSignificantFractionAxis ];
|
|
fraction /= unclippedEndCoords[ mostSignificantFractionAxis ] - unclippedStartCoords[ mostSignificantFractionAxis ];
|
|
result = Lerp( start, end, fraction );
|
|
|
|
return fraction;
|
|
}
|
|
}
|
|
|
|
testCoords += step;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|