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 & 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 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( &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; zCamera.GetViewVector(); 
				D3DXVec3Normalize(&vViewDir, &vViewDir); 
				D3DXVECTOR3 vPos(fX, fY, fZ); 
				D3DXVECTOR3 vObject = vPos - Game->Camera.Eye; 
				D3DXVECTOR3 vObjectDir = vObject; 
				D3DXVec3Normalize(&vObjectDir, &vObjectDir); 
				float fDot = D3DXVec3Dot( &vViewDir, &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(&vObjectDir, &vObjectDir); 
					fDot = D3DXVec3Dot( &vViewDir, &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(&vObjectDir, &vObjectDir); 
						fDot = D3DXVec3Dot( &vViewDir, &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(&vObjectDir, &vObjectDir); 
							fDot = D3DXVec3Dot( &vViewDir, &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(&vObjectDir, &vObjectDir); 
								fDot = D3DXVec3Dot( &vViewDir, &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, & 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(&norm,&axeY,&axeX); 
				D3DXVec3Normalize(&norm,&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 = &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 = &m_pMap[0]; 
	//int index = 0; 
	//while( !feof( pFile ) ) 
	//{ 
	//	UINT8 buffer; 
	//	fread( &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( &m_matWorld ); 
 
	CalcNormales(); 
} 
 
void CTerrain::Activate ( bool _Animate , bool _Render )   
{ 
	Animated  = _Animate ;  
	Rendered  = _Render ;  
}