www.pudn.com > sxdl.zip > terrain.cpp
#include "terrain.h"
//
// The Terrain renderer is contributed by :
// Christian Kaspar aka General Rolf Diensten
//
//
// Please note, this is still an early beta
// important improvement &amt; changes WILL be made soon !!!
//--------------------------------------------------------------
// Name: CGEOMIPMAPPING::Init - public
// Description: Initiate the geomipmapping system
// Arguments: - iPatchSize: the size of the patch (in vertices)
// a good size is usually around 17 (17x17 verts)
// Return Value: A boolean value: -true: successful initiation
// -false: unsuccessful initiation
//--------------------------------------------------------------
bool CTerrain::Init( int iPatchSize )
{
int x, z;
int iLOD;
int iDivisor;
int iPatch;
if( m_pPatches )
Shutdown( );
//initiate the patch information
m_iPatchSize= iPatchSize;
m_iNumPatchesPerSide= (m_iXSize - 1)/(m_iPatchSize-1);
m_pPatches= new SGEOMM_PATCH [m_iNumPatchesPerSide* m_iNumPatchesPerSide];
if( m_pPatches==NULL )
{
Shutdown( );
return false;
}
//figure out the maximum level of detail for a patch
iDivisor= m_iPatchSize-1;
iLOD= 0;
while( iDivisor>2 )
{
iDivisor= iDivisor>>1;
iLOD++;
}
//the max amount of detail
m_iMaxLOD= iLOD;
//initialize the patch values
for( z=0; z<m_iNumPatchesPerSide; z++ )
{
for( x=0; x<m_iNumPatchesPerSide; x++ )
{
iPatch= GetPatchNumber( x, z );
//initialize the patches to the lowest level of detail
m_pPatches[iPatch].m_iLOD= m_iMaxLOD;
m_pPatches[iPatch].m_bVisible= true;
}
}
return true;
}
void CTerrain::Shutdown( void )
{
//delete the patch buffer
if( m_pPatches )
delete[] m_pPatches;
//reset patch values
m_iPatchSize= 0;
m_iNumPatchesPerSide= 0;
}
void CTerrain::SetRenderMode(int iMode)
{
if (iMode == BRUTEFORCE)
{
Shutdown();
m_RenderMode = iMode;
return;
}
if (iMode == LOD)
{
if (m_iXSize != m_iYSize)
{
MessageBox(NULL,"Attention, LOD seulement pour les cartes carrées","Erreur",MB_OK);
return;
}
if (((m_iXSize-1) > 2) != 0)
{
MessageBox(NULL,"Attention, LOD seulement pour les cartes avec coté = 2^n + 1","Erreur",MB_OK);
m_RenderMode = BRUTEFORCE;
return;
}
Init(17);
m_RenderMode = iMode;
return;
}
}
CTerrain::CTerrain ( int Tex1 , int Tex2 , int Data , int iXSize, int iYSize ) :
CCustomRenderer
(
CVertex::PosNormalColorTex0Tex1 ,
iXSize * iYSize ,
0L , // defaults to nothing special
false , // build a static VB
( iXSize - 1 ) * ( iYSize - 1 ) * 4 , // does not create a IB if zero
false , // build a dynamic IB
Tex1 , // no texturing if zero
Tex2 ,
0 ,
0 ,
0 , // no vertex shader if zero
0 // no pixel shader if zero
)
{
DataResourceId = Data ;
// En création, état chaotique !!!!!
// Ne pas oublier d'utiliser Initialise() avant de dessiner
assert( iXSize > 0 );
assert( iYSize > 0 );
m_iXSize = iXSize;
m_iYSize = iYSize;
Animated = false ;
Rendered = false ;
m_pMap = NULL;
m_pNormales = NULL;
m_vectWorldZeroPosition = D3DXVECTOR3( 0.0f , 0.0f , 0.0f );
m_vectWorldScaling = D3DXVECTOR3( 1.0f , 1.0f , 1.0f );
D3DXMatrixIdentity( &amt;m_matWorld );
m_pPatches = NULL;
m_iPatchSize = 0;
m_iNumPatchesPerSide = 0;
m_iMaxLOD = 0;
m_iPatchesPerFrame = 0;
m_RenderMode = LOD;
// The terrain has a fake entity so that it is always rendered
Fake = new CEntity ( ) ;
Fake->Activate ( false , true ) ;
Fake->Visible = true ;
Entities.AddHead ( Fake ) ;
//
Layer = SxDL::Models ;
}
CTerrain::~CTerrain(void)
{
// on vide les données précédentes (si existantes)
if ( m_pMap )
{
delete [ ] m_pMap ;
m_pMap = NULL ;
} ;
if ( m_pNormales )
{
delete [ ] m_pNormales ;
m_pNormales = NULL ;
} ;
Shutdown();
}
void CTerrain::Geometry ( Vector3 _Position , Vector3 _Scale , Vector3 _Angles )
{
Fake->Position = _Position ;
Fake->Scale = _Scale ;
// make sure we can inverse the world matrix
if ( Fake->Scale.x == 0.0f ) Fake->Scale.x = 1.0f ;
if ( Fake->Scale.y == 0.0f ) Fake->Scale.y = 1.0f ;
if ( Fake->Scale.z == 0.0f ) Fake->Scale.z = 1.0f ;
Fake->Angles = _Angles ;
Fake->DoWorldMatrix ( ) ;
}
bool CTerrain::InitialiseDeRawfile2 ( float fHauteurMin, float fHauteurMax, int iRenderMode)
{
// test des paramètres (debug uniquement)
assert( fHauteurMin < fHauteurMax);
// on vide les données précédentes (si existantes)
if ( m_pMap )
{
delete [ ] m_pMap ;
m_pMap = NULL ;
} ;
if ( m_pNormales )
{
delete [ ] m_pNormales ;
m_pNormales = NULL ;
} ;
HRESULT hr = Game->Media.LoadDataFile ( DataResourceId ) ;
if ( SUCCEEDED ( hr ) )
{
unsigned char * ByteMap = ( unsigned char * ) Game->Media.Instance ( DataResourceId ) ;
int iTaille = ( int ) Game->Media.InstanceData ( DataResourceId ) ;
int iLargeur = ( int ) sqrt ( ( double ) iTaille ) ; // dangerous cast ? add 0.5 ???
// Copy and convert to float
m_pMap = new float [ iTaille ] ;
float * pF = m_pMap ;
UINT8 * pB = ByteMap ;
for ( int i = 0 ; i < iTaille ; ++ i )
* pF ++ = ( float ) ( * pB ++ ) ;
// drop bytes
Game->Media.UnloadDataFile ( DataResourceId ) ;
// affectation
m_fHauteurMin = fHauteurMin;
m_fHauteurMax = fHauteurMax;
m_iXSize = iLargeur;
m_iYSize = iLargeur;
m_pNormales = new Vector3 [ m_iXSize * m_iYSize ] ;
}
else
{
// what the fuck ??
return false ;
} ;
// Mise a l'échelle
Normalise();
HauteursMinMax();
// Calculer les normales en chaque point
//CalcNormales(); -> dans setworldscaling
SetWorldPosition(0.0f, 0.0f , 0.0f);
SetWorldScaling(1.0f, 1.0f, 1.0f); // inclu CalcNormales()
// pour LOD
SetRenderMode(iRenderMode);
// life is good
return true;
}
HRESULT CTerrain::OnPopulateIndexBuffer ( void * Indices )
{
if (m_RenderMode == LOD)
{
piIndices = ( unsigned int * ) Indices ;
Update( NULL , Indices);
}
return S_OK;
}
HRESULT CTerrain::OnPopulateBuffers ( void * Vertices , void * Indices )
{
static bool bFirstTime = true;
if (m_RenderMode == LOD)
{
// compute the VB for the terrain only once, only the IB is recomputed every frame
if (bFirstTime)
{
bFirstTime = false;
float* pCarte = m_pMap;
D3DXVECTOR3* pNormales = m_pNormales;
VertexPosNormalColorTex0Tex1 * VerticesTerrain = ( VertexPosNormalColorTex0Tex1 * ) Vertices ;
float DeltaHeight = GetHauteurMax ( ) - GetHauteurMin ( ) ;
float MinHeight = GetHauteurMin ( ) ;
// verts
for ( int z = m_iYSize ; z > 0 ; -- z )
{
for ( int x = 0; x < m_iXSize; ++ x )
{
float Height = * pCarte ;
// Normalize these fucking coordinates
float ny = ( Height - MinHeight ) / ( float ) DeltaHeight ;
float nx = ( x - m_iXSize / 2 ) / ( float ) m_iXSize ;
float nz = ( z - m_iYSize / 2 ) / ( float ) m_iYSize ;
VerticesTerrain->p = D3DXVECTOR3 ( nx, ny , nz ) ;
unsigned int ColorFactor = 219 + ( int ) ( 0.5f + 35.0f * ny ) ;
VerticesTerrain->n = * pNormales;
VerticesTerrain->color = 0xff000000 | ( ColorFactor << 16 ) | ( ColorFactor << 8 ) | ColorFactor ;
VerticesTerrain->tu1 = ( float ) x / ( float ) m_iXSize ;
VerticesTerrain->tv1 = ( float ) ( m_iYSize - z ) / (float) m_iYSize;
VerticesTerrain->tu2 = ( ( float ) x / ( float ) m_iXSize ) * 20.0f ;
VerticesTerrain->tv2 = ( ( float ) z / ( float ) m_iYSize ) * 20.0f;
// next
++ pCarte ;
++ pNormales ;
++ VerticesTerrain ;
}
} ;
};
if (Animated)
{
piIndices = ( unsigned int * ) Indices ;
Update( Vertices , Indices);
}
}
else
{
float* pCarte = m_pMap;
D3DXVECTOR3* pNormales = m_pNormales;
VertexPosNormalColorTex0Tex1 * VerticesTerrain = ( VertexPosNormalColorTex0Tex1 * ) Vertices ;
float DeltaHeight = GetHauteurMax ( ) - GetHauteurMin ( ) ;
float MinHeight = GetHauteurMin ( ) ;
// verts
for ( int z = m_iYSize ; z > 0 ; -- z )
{
for ( int x = 0; x < m_iXSize; ++ x )
{
float Height = * pCarte ;
if ( ( x == 0 ) || ( z == 1 ) || ( x == m_iXSize - 1 ) || ( z == m_iYSize ) )
Height = MinHeight ;
// Normalize these fucking coordinates
float ny = ( Height - MinHeight ) / ( float ) DeltaHeight ;
float nx = ( x - m_iXSize / 2 ) / ( float ) m_iXSize ;
float nz = ( z - m_iYSize / 2 ) / ( float ) m_iYSize ;
VerticesTerrain->p = D3DXVECTOR3 ( nx, ny , nz ) ;
unsigned int ColorFactor = 219 + ( int ) ( 0.5f + 35.0f * ny ) ;
VerticesTerrain->n = * pNormales;
VerticesTerrain->color = 0xff000000 | ( ColorFactor << 16 ) | ( ColorFactor << 8 ) | ColorFactor ;
VerticesTerrain->tu1 = ( float ) x / ( float ) m_iXSize;
VerticesTerrain->tv1 = ( float ) ( m_iYSize - z ) / (float) m_iYSize;
VerticesTerrain->tu2 = ( ( float ) x / ( float ) m_iXSize ) * 20.0f ;
VerticesTerrain->tv2 = ( ( float ) z / ( float ) m_iYSize ) * 20.0f;
// next
++ pCarte ;
++ pNormales ;
++ VerticesTerrain ;
}
} ;
// inds
unsigned int * IndicesTerrain = ( unsigned int * ) Indices ;
for (int k = 0; k < m_iYSize-1 ; k++)
{
for (int l = 0; l < m_iXSize+1 ; l++)
{
int wValeur = k*(m_iXSize)+l;
*IndicesTerrain++ = wValeur;
*IndicesTerrain++ = wValeur+(m_iXSize);
}
}
}
//
return S_OK ;
}
HRESULT CTerrain::OnSetRenderStates ( LPDIRECT3DDEVICE9 Device )
{
HRESULT hr ;
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0);
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1);
// fix that
hr = Game->m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
return S_OK ;
}
void CTerrain::Update( void * Vertices , void * Indices, bool bCullPatches )
{
float fX, fY, fZ;
float fScaledSize;
int x, z;
int iPatch;
fScaledSize= m_iPatchSize * m_vectWorldScaling.x;
float fFOV = cosf(D3DX_PI / 4.0f) ;
float fHalfSize = ( m_iPatchSize/2.0f );
m_iCompteur = 0;
m_iPatchesPerFrame = 0;
for( z=0; z<m_iNumPatchesPerSide; z++ )
{
for( x=0; x<m_iNumPatchesPerSide; x++ )
{
iPatch= GetPatchNumber( x, z );
//compute patch center (used for distance determination
fX= ( x*m_iPatchSize )+ fHalfSize;
fZ= ( z*m_iPatchSize )+ fHalfSize;
fX += m_vectWorldZeroPosition.x;
fZ += m_vectWorldZeroPosition.z;
fY= HauteurPosition( fX, fZ );
//only scale the X and Z values, the Y value has already been scaled
fX *= m_vectWorldScaling.x;
fZ *= m_vectWorldScaling.z;
//check to see if the user wanted to cull the non-visible patches
if( bCullPatches )
{
D3DXVECTOR3 vViewDir = Game->Camera.GetViewVector();
D3DXVec3Normalize(&amt;vViewDir, &amt;vViewDir);
D3DXVECTOR3 vPos(fX, fY, fZ);
D3DXVECTOR3 vObject = vPos - Game->Camera.Eye;
D3DXVECTOR3 vObjectDir = vObject;
D3DXVec3Normalize(&amt;vObjectDir, &amt;vObjectDir);
float fDot = D3DXVec3Dot( &amt;vViewDir, &amt;vObjectDir);
if (fDot >= fFOV)
m_pPatches[iPatch].m_bVisible= true;
else
{
// test du point upper-left
vPos.x = fX - fHalfSize;
vPos.z = fZ + fHalfSize;
vPos.y = HauteurPosition( vPos.x , vPos.z );
vObject = vPos - Game->Camera.Eye;
vObjectDir = vObject;
D3DXVec3Normalize(&amt;vObjectDir, &amt;vObjectDir);
fDot = D3DXVec3Dot( &amt;vViewDir, &amt;vObjectDir);
if (fDot >= fFOV)
m_pPatches[iPatch].m_bVisible= true;
else
{
// test du point upper-right
vPos.x = fX + fHalfSize;
vPos.z = fZ + fHalfSize;
vPos.y = HauteurPosition( vPos.x , vPos.z );
vObject = vPos - Game->Camera.Eye;
vObjectDir = vObject;
D3DXVec3Normalize(&amt;vObjectDir, &amt;vObjectDir);
fDot = D3DXVec3Dot( &amt;vViewDir, &amt;vObjectDir);
if (fDot >= fFOV)
m_pPatches[iPatch].m_bVisible= true;
else
{
// test du point lower-right
vPos.x = fX + fHalfSize;
vPos.z = fZ - fHalfSize;
vPos.y = HauteurPosition( vPos.x , vPos.z );
vObject = vPos - Game->Camera.Eye;
vObjectDir = vObject;
D3DXVec3Normalize(&amt;vObjectDir, &amt;vObjectDir);
fDot = D3DXVec3Dot( &amt;vViewDir, &amt;vObjectDir);
if (fDot >= fFOV)
m_pPatches[iPatch].m_bVisible= true;
else
{
// test du point lower-right
vPos.x = fX - fHalfSize;
vPos.z = fZ - fHalfSize;
vPos.y = HauteurPosition( vPos.x , vPos.z );
vObject = vPos - Game->Camera.Eye;
vObjectDir = vObject;
D3DXVec3Normalize(&amt;vObjectDir, &amt;vObjectDir);
fDot = D3DXVec3Dot( &amt;vViewDir, &amt;vObjectDir);
if (fDot >= fFOV)
m_pPatches[iPatch].m_bVisible= true;
else
{
D3DXVECTOR3 toto = Game->Camera.Eye;
m_pPatches[iPatch].m_fDistance= ( ( ( fX-Game->Camera.Eye.x ) * ( fX-Game->Camera.Eye.x ) )+
( ( fY-Game->Camera.Eye.y ) * ( fY-Game->Camera.Eye.y ) )+
( ( fZ-Game->Camera.Eye.z ) * ( fZ-Game->Camera.Eye.z )) );
//BAD way to determine patch LOD, we will be fixing this code a bit later in the chapter
if( m_pPatches[iPatch].m_fDistance<1200 )
m_pPatches[iPatch].m_bVisible= true;
else
m_pPatches[iPatch].m_bVisible= false;
}
}
}
}
}
}
//make all patches visible
else
m_pPatches[iPatch].m_bVisible= true;
//only finish updating if the patch is visible
if( m_pPatches[iPatch].m_bVisible )
{
//get the distance from the camera to the patch
D3DXVECTOR3 toto = Game->Camera.Eye;
m_pPatches[iPatch].m_fDistance= sqrtf( ( ( fX-Game->Camera.Eye.x ) * ( fX-Game->Camera.Eye.x ) )+
( ( fY-Game->Camera.Eye.y ) * ( fY-Game->Camera.Eye.y ) )+
( ( fZ-Game->Camera.Eye.z ) * ( fZ-Game->Camera.Eye.z )) );
//BAD way to determine patch LOD, we will be fixing this code a bit later in the chapter
if( m_pPatches[iPatch].m_fDistance<20 )
m_pPatches[iPatch].m_iLOD= 0;
else if( m_pPatches[iPatch].m_fDistance<50 )
m_pPatches[iPatch].m_iLOD= 1;
else if( m_pPatches[iPatch].m_fDistance<100 )
m_pPatches[iPatch].m_iLOD= 2;
else if( m_pPatches[iPatch].m_fDistance>=100 )
m_pPatches[iPatch].m_iLOD= 3;
ComputePatch(x, z, Vertices , Indices );
}
}
}
}
HRESULT CTerrain::OnRestoreRenderStates ( LPDIRECT3DDEVICE9 Device )
{
HRESULT hr ;
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
hr = Game->m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0);
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
hr = Game->m_pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0);
hr = Game->m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW);
return S_OK ;
}
HRESULT CTerrain::OnRender ( LPDIRECT3DDEVICE9 Device , CEntity * Entity )
{
HRESULT hr = NULL;
if ( Rendered == false )
return S_OK;
Device->SetTransform ( D3DTS_WORLD, &amt; Fake->GetWorldMatrix ( ) ) ;
if (m_RenderMode == BRUTEFORCE)
{
for (int i = 0; i < m_iYSize-1 ; i++)
{
hr = Device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, (m_iXSize)*(m_iYSize),
(2 * (m_iXSize+1))*i, (m_iXSize-1)*2 );
} ;
return hr ;
}
if (m_RenderMode == LOD)
{
long lTotal = 0;
for (int i = 0; i < m_iPatchesPerFrame; i++)
{
hr = Device->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, lTotal, m_iTrisPerFrame[i], lTotal, m_iTrisPerFrame[i] );
lTotal += m_iTrisPerFrame[i]+2;
}
return hr ;
}
return S_OK ;
}
void CTerrain::CalcNormales( void )
{
for (int y = 0; y < m_iYSize; y++)
{
for (int x = 0; x < m_iXSize; x++)
{
if ((y == 0) || (y == m_iYSize-1) || (x == 0) || (x == m_iXSize -1))
{
SetNormale(x,y,D3DXVECTOR3(0.0f,1.0f,0.0f));
}
else
{
// could do better
D3DXVECTOR3 axeX = D3DXVECTOR3(2.0f,GetCase(x-1,y) - GetCase(x+1,y),0.0f);
D3DXVECTOR3 axeY = D3DXVECTOR3(0.0f,GetCase(x,y-1) - GetCase(x,y+1),2.0f);
D3DXVECTOR3 norm;
D3DXVec3Cross(&amt;norm,&amt;axeY,&amt;axeX);
D3DXVec3Normalize(&amt;norm,&amt;norm);
SetNormale(x,y,norm);
}
}
}
}
void CTerrain::Vider( void )
{
// On présuppose que m_pMap, m_iXSize et m_iYSize sont valides
// On calcule le nombre de cases totale que contient le terrain
// et on boucle pour mettre toutes les valeurs a 0;
int iNbCase = m_iXSize * m_iYSize;
float* pfTemp = m_pMap;
while (iNbCase > 0)
{
*pfTemp++ = 0.0f;
iNbCase--;
}
pfTemp = NULL;
}
inline float CTerrain::HasardIntervalle( float min, float max )
{
return( ( rand() * ( max - min ) / RAND_MAX ) + min );
}
void CTerrain::AjouterMontagne( void )
{
// pick a size for the hill
float fRadius = HasardIntervalle( m_fRayonMin, m_fRayonMax );
// pick a centerpoint for the hill
float x = 0.0f;
float y = 0.0f;
if( m_bIle )
{
// island code:
float fTheta = HasardIntervalle( 0.0f , 6.28f );
// this determines in which direction from the center of the map the
// hill will be placed.
float fDistance = HasardIntervalle( fRadius/2, m_iXSize/2 - fRadius );
// this is how far from the center of the map the hill be placed. note
// that the radius of the hill is subtracted from the range to prevent
// any part of a hill from reaching the very edge of the map.
x = m_iXSize/2.0f + cosf( fTheta ) * fDistance;
y = m_iYSize/2.0f + sinf( fTheta ) * fDistance;
// converts theta and a distance into x and y coordinates.
}
else
{
// non-island code:
x = HasardIntervalle( -fRadius, m_iXSize + fRadius );
y = HasardIntervalle( -fRadius, m_iYSize + fRadius );
// note that the range of the hill is used to determine the
// centerpoint. this allows hills to have their centerpoint off the
// edge of the terrain as long as part of the hill is in bounds. this
// makes the terrains appear continuous all the way to the edge of the
// map.
}
// square the hill radius so we don't have to square root the distance
float fRadiusSq = fRadius * fRadius;
float fDistSq = 0.0f;
float fHeight = 0.0f;
// find the range of cells affected by this hill
int xMin = ( int ) ( x - fRadius - 1 ) ; // add .5 ???
int xMax = ( int ) ( x + fRadius + 1 ) ; // add .5 ???
// don't affect cell outside of bounds
if( xMin < 0 ) xMin = 0;
if( xMax >= m_iXSize ) xMax = m_iXSize - 1;
int yMin = ( int ) ( y - fRadius - 1 ) ; // add .5 ??? ;
int yMax = ( int ) ( y + fRadius + 1 ) ; // add .5 ??? ;
// don't affect cell outside of bounds
if( yMin < 0 ) yMin = 0;
if( yMax >= m_iYSize ) yMax = m_iYSize - 1;
// for each affected cell, determine the height of the hill at that point
// and add it to that cell
for( int h = xMin; h <= xMax; ++h )
{
for( int v = yMin; v <= yMax; ++v )
{
// determine how far from the center of the hill this point is
fDistSq = ( x - h ) * ( x - h ) + ( y - v ) * ( y - v );
// determine the height of the hill at this point
fHeight = fRadiusSq - fDistSq;
// don't add negative hill values (i.e. outside the hill's radius)
if( fHeight > 0 )
{
// add the height of this hill to the cell
AjouteCase( h, v, fHeight );
}
}
}
}
void CTerrain::Normalise( void )
{
float fMin = m_pMap[0]; // stocker la valeur du premier point dans fMin
float fMax = m_pMap[0]; // stocker la valeur du premier point dans fMax
// Parcourir tous les points pour trouver les vraies valeurs de fMin et fMax
int iNbCase = m_iXSize * m_iYSize - 1;
float* pfTemp = &amt;m_pMap[1];
while (iNbCase > 0)
{
if (fMin > *pfTemp) // si le point actuel est plus petit que fMin, changer fMin
fMin = *pfTemp;
if (fMax < *pfTemp) // si le point actuel est plus grand que fMax, changer fMax
fMax = *pfTemp;
pfTemp++; // déplacer le pointeur vers valeur suivante
iNbCase--; // décrémenter compteur de la boucle
}
// On teste si fMin est bien différent de fMax avant de normaliser pour ne pas diviser par zéro
// Si les deux valeurs sont identiques, on a affaire a un terrain plat et on le remet a zéro
// (ce cas ne devrait pas se présenter)
if( fMax != fMin )
{
iNbCase = m_iXSize * m_iYSize;
float* pfTemp = m_pMap;
float fDiff = fMax - fMin;
while (iNbCase > 0)
{
*pfTemp = ((*pfTemp - fMin) / fDiff); // recalculer la valeur comprise entre 0.0 et 1.0
pfTemp++; // déplacer le pointeur vers valeur suivante
iNbCase--; // décrémenter compteur de la boucle
}
}
else
{
Vider();
}
}
void CTerrain::Lisser( void )
{
if( m_uLissage > 1 )
{
int iNbCase = m_iXSize * m_iYSize;
float* pfTemp = m_pMap;
while (iNbCase > 0)
{
float fLisse = 1.0;
float fOriginal = *pfTemp;
for( int i = 0; i < m_uLissage; i++ )
{
fLisse *= fOriginal;
}
*pfTemp++ = fLisse;
iNbCase--;
}
pfTemp = NULL;
}
}
void CTerrain::HauteursMinMax()
{
float ftemp = m_fHauteurMax - m_fHauteurMin;
int iNbCase = m_iXSize * m_iYSize;
float* pfTemp = m_pMap;
while (iNbCase > 0)
{
*pfTemp *= ftemp;
*pfTemp += m_fHauteurMin;
pfTemp++;
iNbCase--;
}
pfTemp = NULL;
}
int CTerrain::GetXSize()
{
return m_iXSize;
}
int CTerrain::GetYSize()
{
return m_iYSize;
}
float* CTerrain::GetTerrain()
{
return m_pMap;
}
D3DXVECTOR3* CTerrain::GetNormales()
{
return m_pNormales;
}
void CTerrain::AjouteCase( int x, int y, float valeur )
{
m_pMap[ x + ( (m_iYSize - y) * m_iXSize ) ] += valeur;
}
float CTerrain::HauteurCase( int x, int y )
{
return m_pMap[ x + ( (m_iYSize - y) * m_iXSize ) ];
}
float CTerrain::HauteurPosition( float x, float y )
{
x -= m_vectWorldZeroPosition.x;
y -= m_vectWorldZeroPosition.z;
int x1 = int (x);
int x2 = x1 + 1;
int y1 = int (y);
int y2 = y1 + 1;
if ((x1 < m_vectWorldZeroPosition.x) || (y1 < m_vectWorldZeroPosition.z))
return m_fHauteurMin;
if ((x2 > m_iXSize) || (y2 > m_iYSize))
return m_fHauteurMin;
float h1 = y - (float)y1;
float h2 = 1.0f - h1;
float l1 = x - (float)x1;
float l2 = 1.0f - l1;
if ( ((l1 * l1) + (h1 * h1)) < ((l2 * l2) + (h2 * h2)))
{
float difx = HauteurCase(x2,y1) - HauteurCase(x1,y1);
difx *= l1;
float dify = HauteurCase(x1,y2) - HauteurCase(x1,y1);
dify *= h1;
return difx+dify+HauteurCase(x1,y1); //+ m_fHauteurMin;
}
else
{
float difx = HauteurCase(x1,y2) - HauteurCase(x2,y2);
difx *= l2;
float dify = HauteurCase(x2,y1) - HauteurCase(x2,y2);
dify *= h2;
return difx+dify+HauteurCase(x2,y2) + m_fHauteurMin;
}
}
float CTerrain::GetCase( int x, int y )
{
return( m_pMap[ x + ( (m_iYSize - y -1) * m_iXSize ) ] );
}
void CTerrain::SetCase( int x, int y, float valeur)
{
m_pMap[ x + ( (m_iYSize - y -1) * m_iXSize ) ] = valeur;
}
void CTerrain::SetNormale( int x, int y, D3DXVECTOR3 valeur)
{
m_pNormales[ x + ( (m_iYSize-1 - y) * m_iXSize ) ] = valeur;
}
void CTerrain::SetHauteurMax(float f)
{
m_fHauteurMax = f;
}
float CTerrain::GetHauteurMax()
{
return m_fHauteurMax;
}
void CTerrain::SetHauteurMin(float f)
{
m_fHauteurMin = f;
}
float CTerrain::GetHauteurMin()
{
return m_fHauteurMin;
}
void CTerrain::SetWorldPosition(float x, float y, float z)
{
m_vectWorldZeroPosition.x = x;
m_vectWorldZeroPosition.y = y;
m_vectWorldZeroPosition.z = z;
}
bool CTerrain::Initialise( float fRayonMin,
float fRayonMax,
float fHauteurMin,
float fHauteurMax,
unsigned short uNbMontagnes,
unsigned short uLissage,
bool bIle,
unsigned int uGenerateur)
{
// Unclear about this one is doing
// --------------------------------
//// test des paramètres (debug uniquement)
//assert( fRayonMin >= 0.0 );
//assert( fRayonMin < fRayonMax );
//assert( fRayonMax >= 0.0 );
//assert( uLissage > 0 );
//assert( fHauteurMin < fHauteurMax);
//// affectation
//m_fRayonMin = fRayonMin;
//m_fRayonMax = fRayonMax;
//m_fHauteurMin = fHauteurMin;
//m_fHauteurMax = fHauteurMax,
//m_uNbMontagnes = uNbMontagnes;
//m_uLissage = uLissage;
//m_bIle = bIle;
//m_uGenerateur = uGenerateur;
//// On crée les tableaux qui vont servir a stocker les valeurs.
//// Mais d'abord on pense a les vider au cas ou il y aurait déjà eu
//// un appel a Initialise()
//m_pMap = (float*)malloc( m_iXSize * m_iYSize * sizeof(float));
//m_pNormales = (D3DXVECTOR3*)malloc( m_iXSize * m_iYSize * sizeof(D3DXVECTOR3));
//if ((m_pMap == NULL) || (m_pNormales == NULL))
//{
// MessageBox(NULL,"Impossible d'initialiser la carte du terrain","Erreur",MB_OK );
// return false; // erreur, probablement pas assez de mémoire, ne pas essayer d'utiliser l'objet !!!
//}
return false ;
}
bool CTerrain::InitialiseDeRawfile( LPCTSTR sFileName,
int iXYSize,
float fHauteurMin,
float fHauteurMax )
{
// unfuck this one too
//
//// test des paramètres (debug uniquement)
//assert( fHauteurMin < fHauteurMax);
//assert( iXYSize > 0);
//// affectation
//m_fHauteurMin = fHauteurMin;
//m_fHauteurMax = fHauteurMax;
//m_iXSize = iXYSize;
//m_iYSize = iXYSize;
//// on vide les données précédentes (si existantes)
//if (m_pMap)
// free(m_pMap);
//if (m_pNormales)
// free(m_pNormales);
//SAFE_RELEASE( m_pVBTerrain );
//SAFE_RELEASE( m_pIBTerrain );
//SAFE_RELEASE( m_pTextureTerrain );
//SAFE_RELEASE( m_pTextureDetail );
//// on alloue les tableaux
//m_pMap = (float*)malloc( m_iXSize * m_iYSize * sizeof(float));
//
//m_pNormales = (D3DXVECTOR3*)malloc( m_iXSize * m_iYSize * sizeof(D3DXVECTOR3));
//if ((m_pMap == NULL) || (m_pNormales == NULL))
//{
// MessageBox(NULL,"Impossible d'initialiser la carte du terrain","Erreur",MB_OK );
// return false; // erreur, probablement pas assez de mémoire, ne pas essayer d'utiliser l'objet !!!
//}
//FILE *pFile;
//// Open the file. "rb" = Read Binary
////
//pFile = fopen( sFileName, "rb" );
//if ( pFile == NULL )
//{
// char strTemp[256];
// // Oops! Couldn't find the file.
// // Do something about it here..
// sprintf( strTemp, "Impossible de trouver le fichier de terrain! \">s\"", sFileName );
// MessageBox( NULL, strTemp, "Error", MB_OK );
// if (m_pMap)
// free(m_pMap);
// if (m_pNormales)
// free(m_pNormales);
// return false;
//}
//// Read the whole file into the buffer
//// The buffer should have been allocated already.
//float* ptr = &amt;m_pMap[0];
//int index = 0;
//while( !feof( pFile ) )
//{
// UINT8 buffer;
// fread( &amt;buffer, 1, 1, pFile );
// if( ferror( pFile ) )
// {
// perror( "Read error" );
// return false;
// }
// *ptr = (float)buffer;
// ptr++;
// index++;
// if (index == (m_iXSize*m_iYSize))
// {
// break; // sortir si la taille du fichier est plus grande que prévu
// }
//}
//ptr = NULL;
//// Fermer le fichier
//fclose(pFile);
//// Mise a l'échelle
//Normalise();
//HauteursMinMax();
//// Calculer les normales en chaque point
//CalcNormales();
return false ;
}
void CTerrain::ComputePatch(int PX, int PZ, void * Vertices , void * Indices )
{
SGEOMM_NEIGHBOR patchNeighbor;
SGEOMM_NEIGHBOR fanNeighbor;
float fSize;
float fHalfSize;
float x, z;
int iPatch= GetPatchNumber( PX, PZ );
int iDivisor;
int iLOD;
//find out information about the patch to the current patch's left, if the patch is of a
//greater detail or there is no patch to the left, we can render the mid-left vertex
if( m_pPatches[GetPatchNumber( PX-1, PZ )].m_iLOD<=m_pPatches[iPatch].m_iLOD || PX==0 )
patchNeighbor.m_bLeft= true;
else
patchNeighbor.m_bLeft= false;
//find out about the upper patch
if( m_pPatches[GetPatchNumber( PX, PZ+1 )].m_iLOD<=m_pPatches[iPatch].m_iLOD || PZ==m_iNumPatchesPerSide )
patchNeighbor.m_bUp= true;
else
patchNeighbor.m_bUp= false;
//find out about the right patch
if( m_pPatches[GetPatchNumber( PX+1, PZ )].m_iLOD<=m_pPatches[iPatch].m_iLOD || PX==m_iNumPatchesPerSide )
patchNeighbor.m_bRight= true;
else
patchNeighbor.m_bRight= false;
//find out about the lower patch
if( m_pPatches[GetPatchNumber( PX, PZ-1 )].m_iLOD<=m_pPatches[iPatch].m_iLOD || PZ==0 )
patchNeighbor.m_bDown= true;
else
patchNeighbor.m_bDown= false;
//we need to determine the distance between each triangle-fan that
//we will be rendering
iLOD = m_pPatches[GetPatchNumber( PX, PZ )].m_iLOD+1;
fSize = ( float )m_iPatchSize;
iDivisor= m_iPatchSize-1;
//find out how many fan divisions we are going to have
while( --iLOD>-1 )
iDivisor= iDivisor>>1;
//the size between the center of each triangle fan
fSize/= iDivisor;
//half the size between the center of each triangle fan (this will be
//the size between each vertex)
fHalfSize= fSize/2.0f;
for( z = 0; z < iDivisor; z++)
{
for ( x = 0; x < iDivisor; x++)
{
if( x==0 )
fanNeighbor.m_bLeft= patchNeighbor.m_bLeft;
else
fanNeighbor.m_bLeft= true;
//if this fan is in the bottom row, we may need to adjust it's rendering to
//prevent cracks
if( z==0 )
fanNeighbor.m_bDown= patchNeighbor.m_bDown;
else
fanNeighbor.m_bDown= true;
//if this fan is in the right row, we may need to adjust it's rendering to
//prevent cracks
if( x == (iDivisor-1) )
fanNeighbor.m_bRight= patchNeighbor.m_bRight;
else
fanNeighbor.m_bRight= true;
//if this fan is in the top row, we may need to adjust it's rendering to
//prevent cracks
if( z == (iDivisor-1) )
fanNeighbor.m_bUp= patchNeighbor.m_bUp;
else
fanNeighbor.m_bUp= true;
ComputeFan( ( PX*(m_iPatchSize-1) )+x*(int)(fSize)+(int)(fSize/2), ( PZ*(m_iPatchSize-1) )+z*(int)(fSize)+(int)(fSize/2) ,
(int)(fSize/2), fanNeighbor, Vertices , Indices );
}
}
}
void CTerrain::ComputeFan( float cX, float cZ, float fSize, SGEOMM_NEIGHBOR neighbor, void * Vertices , void * Indices )
{
// offset du centre du fan
int iTrisPerFrame = 4;
//return m_pMap[ x + ( (m_iYSize - y) * m_iXSize )
int wValeur = (m_iYSize-1 -(int)cZ)*(m_iXSize)+(int)cX;
//int* piIndices = (int*)Indices;
*piIndices = wValeur;
piIndices++;
// offset du lower left
*piIndices++ = wValeur - (((int)(fSize)) *(m_iXSize)) - (int)(fSize) ;
//only render the next vertex if the left patch is NOT of a lower LOD
if( neighbor.m_bLeft )
{
*piIndices++ = wValeur - (int)(fSize) ;
iTrisPerFrame++;
}
// offset du upper left
*piIndices++ = wValeur + (((int)(fSize)) *(m_iXSize)) - (int)(fSize) ;
//only render the next vertex if the upper patch is NOT of a lower LOD
if( neighbor.m_bDown )
{
*piIndices++ = wValeur + (((int)(fSize)) *(m_iXSize));
iTrisPerFrame++;
}
// offset du upper right
*piIndices++ = wValeur + (((int)(fSize)) *(m_iXSize)) + (fSize) ;
//only render the next vertex if the right patch is NOT of a lower LOD
if( neighbor.m_bRight )
{
//render the MID-RIGHT vertex
*piIndices++ = wValeur + (int)(fSize) ;
iTrisPerFrame++;
}
//render the LOWER-RIGHT vertex
*piIndices++ = wValeur - (((int)(fSize)) *(m_iXSize)) + (int)(fSize) ;
//only render the next vertex if the bottom patch is NOT of a lower LOD
if( neighbor.m_bUp )
{
//render the LOWER-MID vertex
*piIndices++ = wValeur - (((int)(fSize)) *(m_iXSize));
iTrisPerFrame++;
}
//render the LOWER-LEFT vertex
*piIndices++ = wValeur - (((int)(fSize)) *(m_iXSize)) - (fSize) ;
m_iTrisPerFrame[m_iCompteur++] = iTrisPerFrame;
m_iPatchesPerFrame++;
//hr = pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, 0, m_iTrisPerFrame, 0, m_iTrisPerFrame );
}
void CTerrain::SetWorldScaling(float x, float y, float z)
{
m_vectWorldScaling.x = x;
m_vectWorldScaling.y = y;
m_vectWorldScaling.z = z;
D3DXMatrixIdentity( &amt;m_matWorld );
CalcNormales();
}
void CTerrain::Activate ( bool _Animate , bool _Render )
{
Animated = _Animate ;
Rendered = _Render ;
}