www.pudn.com > QuadTreeLOD4cs.rar > Terrain.cpp, change:2003-09-26,size:15944b


 
#include "Terrain.h" 
#include "gamehead.h"	 
#include "Frustum.h" 
#include "text.h" 
#include <time.h> 
#include <LIMITS.H> 
 
CTerrain::CTerrain() 
{ 
	m_pHeightMap = 0; 
	m_pFrustum = 0; 
	m_nMapSize = 0; 
	m_pText = 0; 
	m_pCamera = 0; 
	m_nScale = 16; 
} 
 
CTerrain::~CTerrain() 
{ 
	if(m_pHeightMap) 
		delete []m_pHeightMap; 
} 
#define EQ(x,y) (fabs(x-y)<0.0001?true:false) 
void CTerrain::SetTextureCoord(float x, float z) 
{ 
//	glTexCoord2f(   (float)(x/m_nScale) / (float)(m_nMapSize/m_nScale),	 
//				  - (float)(z/m_nScale) / (float)(m_nMapSize/m_nScale)	); 
	// Find the (u, v) coordinate for the current vertex 
	float u =  (float)x / (float)m_nMapSize; 
	float v = -(float)z / (float)m_nMapSize; 
	 
	// Give OpenGL the current terrain texture coordinate for our height map 
	glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u, v); 
 
	// Give OpenGL the current detail texture coordinate for our height map 
	glMultiTexCoord2fARB(GL_TEXTURE1_ARB, u, v); 
} 
void CTerrain::LoadRawFile(LPSTR strName) 
{ 
	FILE *pFile = NULL; 
	pFile = fopen( strName, "rb" ); 
	fseek(pFile,0,SEEK_END); 
	long nLen = ftell(pFile); 
	rewind(pFile); 
	m_nMapSize = sqrt(nLen); 
	if ( pFile == NULL )	 
	{ 
		MessageBox(NULL, "Can't find the height map!", "Error", MB_OK); 
		return; 
	} 
	if(m_pHeightMap) 
		delete []m_pHeightMap; 
	m_pHeightMap = new BYTE[m_nMapSize*m_nMapSize]; 
//	memset(m_pHeightMap,0,m_nMapSize*m_nMapSize); 
	if(!m_pHeightMap) 
	{ 
		MessageBox(NULL, "Alloc memory error!", "Error", MB_OK); 
		return; 
	} 
	int nRead  = fread( m_pHeightMap, 1, m_nMapSize*m_nMapSize, pFile ); 
	int result = ferror( pFile ); 
	if (result) 
	{ 
		MessageBox(NULL, "Can't get data!", "Error", MB_OK); 
	} 
	fclose(pFile); 
 
	m_cFlag.Create(m_nMapSize,m_nMapSize); 
	m_nMapSize = m_nMapSize*m_nScale - 1; 
	m_cFlag.m_nSclae = m_nScale; 
/*	m_cFlag.Set((m_nMapSize-1)/2,(m_nMapSize-1)/2,true); 
	m_cFlag.Set((m_nMapSize-1)/4,(m_nMapSize-1)/4,true); 
	m_cFlag.Set((m_nMapSize-1)/8,(m_nMapSize-1)/8,true); 
	m_cFlag.Set((m_nMapSize-1)/2-(m_nMapSize-1)/8,(m_nMapSize-1)/2-(m_nMapSize-1)/8,true); 
	m_cFlag.Set((m_nMapSize-1)/2-(m_nMapSize-1)/16,(m_nMapSize-1)/2-(m_nMapSize-1)/16,true); 
*/ 
} 
 
void CTerrain::LoadTexture(LPSTR strName) 
{ 
	m_txt.LoadGLTextures(strName); 
} 
int CTerrain::Height(int X, int Y) 
{ 
	// Make sure we don't go past our array size 
	int x = int(X/m_nScale) % int(m_nMapSize/m_nScale);					// Error check our x value 
	int y = int(Y/m_nScale) % int(m_nMapSize/m_nScale);					// Error check our y value 
	if(x<0||y<0||x>=m_nMapSize/m_nScale||y>=m_nMapSize/m_nScale) 
		return 0; 
	if(!m_pHeightMap) return 0;				// Make sure our data is valid 
	 
	// Use the equation: index = (x + (y * arrayWidth) ) to find the current height 
	return m_nScale/2*m_pHeightMap[x + int(y * m_nMapSize/m_nScale)];	// Index into our height array and return the height 
} 
 
void CTerrain::Init() 
{ 
//	m_nScale  = 2; 
	LoadRawFile("terrain\\terrain.raw"); 
	LoadTexture("terrain\\terrain.bmp"); 
	m_cTxtDetail.LoadGLTextures("terrain\\detail.bmp"); 
} 
void CTerrain::Render() 
{ 
/*	CVector3 vNewPos = m_pCamera->GetPos(); 
	CVector3 vPos  = vNewPos; 
//	if(vPos.y<Height((int)vPos.x, (int)vPos.z )+10) 
	{ 
		vNewPos.y = (float)Height((int)vPos.x, (int)vPos.z ) + 70; 
		vNewPos.y = (vPos.y+vNewPos.y)/2; 
		float temp = vNewPos.y - vPos.y; 
	 
		CVector3 vView = m_pCamera->GetView(); 
		vView.y += temp; 
		m_pCamera->PositionCamera(vNewPos.x,  vNewPos.y,  vNewPos.z, 
			vView.x,	vView.y,	vView.z,	0, 1, 0);								 
	}*/ 
//	glEnable(GL_TEXTURE_2D);  
//	glBindTexture(GL_TEXTURE_2D, m_txt.GetTxtID()); 
	glActiveTextureARB(GL_TEXTURE0_ARB); 
	glEnable(GL_TEXTURE_2D); 
	glBindTexture(GL_TEXTURE_2D, m_txt.GetTxtID()); 
	// Activate the second texture ID and bind the fog texture to it 
		glActiveTextureARB(GL_TEXTURE1_ARB); 
		glEnable(GL_TEXTURE_2D); 
		 
		// Here we turn on the COMBINE properties and increase our RGB 
		// gamma for the detail texture.  2 seems to work just right. 
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); 
		glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2); 
		 
		// Bind the detail texture 
		glBindTexture(GL_TEXTURE_2D, m_cTxtDetail.GetTxtID()); 
	 
		// Now we want to enter the texture matrix.  This will allow us 
		// to change the tiling of the detail texture. 
		glMatrixMode(GL_TEXTURE); 
 
			// Reset the current matrix and apply our chosen scale value 
			glLoadIdentity(); 
			glScalef((float)32, (float)32, 1); 
 
		// Leave the texture matrix and set us back in the model view matrix 
		glMatrixMode(GL_MODELVIEW); 
	glEnable(GL_DEPTH_TEST); 
	glCullFace(GL_FRONT); 
	glEnable(GL_CULL_FACE); 
	m_cFlag.Reset();    //细分标记数组清0(这里为了节约内存,一个网格用一个bit表示) 
	Update((m_nMapSize-1)/2,(m_nMapSize-1)/2,(m_nMapSize-1)/2,1);//在细分标记数组记录需要细分的网格 
	//根据细分标记数组递归渲染网格 
	int nCount = RenderQuad((m_nMapSize-1)/2,(m_nMapSize-1)/2,(m_nMapSize-1)/2); 
 
 
	glActiveTextureARB(GL_TEXTURE1_ARB); 
    glDisable(GL_TEXTURE_2D); 
 
	// Turn the first multitexture pass off 
	glActiveTextureARB(GL_TEXTURE0_ARB);		 
    glDisable(GL_TEXTURE_2D); 
//	glDisable(GL_TEXTURE_2D);  
	glDisable(GL_DEPTH_TEST); 
	glDisable(GL_CULL_FACE); 
	char sPrint[MAX_PATH]; 
	wsprintf(sPrint,"%d Trigles",nCount); 
	if(m_pText) 
		m_pText->EditItem(m_nTextID,0.0f,3.0f,0.0f,sPrint); 
} 
 
 
int CTerrain::RenderQuad1(int nXCenter,int nZCenter,int nLevel) 
{ 
	int nCount = 0; 
	int nSize = (m_nMapSize-1)/pow(2,nLevel);//由细节级度得到当前渲染矩形的大小 
	int nMax = Height(nXCenter,nZCenter); 
	int nMin = nMax; 
	int nH1 = Height(nXCenter-nSize,nZCenter-nSize); 
	int nH2 = Height(nXCenter+nSize,nZCenter-nSize); 
	int nH3 = Height(nXCenter+nSize,nZCenter+nSize); 
	int nH4 = Height(nXCenter-nSize,nZCenter+nSize); 
	if(nMax<nH1)nMax = nH1; 
	if(nMax<nH2)nMax = nH2; 
	if(nMax<nH3)nMax = nH3; 
	if(nMax<nH4)nMax = nH4; 
	if(nMin>nH1)nMin = nH1; 
	if(nMin>nH2)nMin = nH2; 
	if(nMin>nH3)nMin = nH3; 
	if(nMin>nH4)nMin = nH4; 
	SIZE size; 
	size.cx = nSize;size.cy=(nMax-nMin)/2; 
//	if(!m_pFrustum->CuboidInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,size)) 
//	if(!m_pFrustum->SphereInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(nMax-nMin,nSize))) 
	if(!m_pFrustum->CubeInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(size.cx,size.cy))) 
		return 0; 
	if(nLevel==MAX_LEVEL) 
	{ 
		glBegin( GL_TRIANGLE_FAN ); 
		SetTextureCoord(nXCenter,nZCenter);  // center 
		glVertex3i(nXCenter, Height(nXCenter,nZCenter), nZCenter); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top 
		glVertex3i(nXCenter-nSize,nH1 , nZCenter-nSize); 
 
		SetTextureCoord(nXCenter+nSize,nZCenter-nSize); //right top 
		glVertex3i(nXCenter+nSize, nH2, nZCenter-nSize); 
 
		SetTextureCoord(nXCenter+nSize,nZCenter+nSize); //right bottom 
		glVertex3i(nXCenter+nSize, nH3, nZCenter+nSize); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter+nSize);//left bottom  
		glVertex3i(nXCenter-nSize, nH4, nZCenter+nSize); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top 
		glVertex3i(nXCenter-nSize, nH1, nZCenter-nSize); 
		glEnd(); 
		nCount += 4; 
	} 
	else 
	{ 
		nCount += RenderQuad(nXCenter-nSize/2,nZCenter-nSize/2,nLevel+1); 
		nCount += RenderQuad(nXCenter+nSize/2,nZCenter-nSize/2,nLevel+1); 
		nCount += RenderQuad(nXCenter+nSize/2,nZCenter+nSize/2,nLevel+1); 
		nCount += RenderQuad(nXCenter-nSize/2,nZCenter+nSize/2,nLevel+1); 
	} 
	return nCount; 
} 
bool CTerrain::IsVisible(int nXCenter,int nZCenter,int nSize) 
{ 
	CVector3 v0(nXCenter,Height(nXCenter,nZCenter),nZCenter); 
	CVector3 v1(nXCenter-nSize,Height(nXCenter-nSize,nZCenter-nSize),nZCenter-nSize); 
	CVector3 v2(nXCenter+nSize,Height(nXCenter+nSize,nZCenter-nSize),nZCenter-nSize); 
	CVector3 v3(nXCenter+nSize,Height(nXCenter+nSize,nZCenter+nSize),nZCenter+nSize); 
	CVector3 v4(nXCenter-nSize,Height(nXCenter-nSize,nZCenter+nSize),nZCenter+nSize); 
	CVector3 v1_bak = v1; 
	v1 = CrossProduct(v2-v0,v1-v0); 
	v2 = CrossProduct(v3-v0,v2-v0); 
	v3 = CrossProduct(v4-v0,v3-v0); 
	v4 = CrossProduct(v1_bak-v0,v4-v0); 
	v1 = Normalize(v1); 
	v2 = Normalize(v2); 
	v3 = Normalize(v3); 
	v4 = Normalize(v4); 
	CVector3 vView(v0-m_pCamera->GetPos()); 
	if(DotProduct(v1,vView)>0) 
		if(DotProduct(v2,vView)>0) 
			if(DotProduct(v3,vView)>0) 
				if(DotProduct(v4,vView)>0) 
					return false; 
	return true; 
} 
//渲染网格的递归程序 
//参数: 
//	nXCenter: 网格中心点坐的X值 
//	nZCenter: 网格中心点坐的Y值 
//	nSize:网格大小  
//返回:渲染的三角形数量 
//使用前提:m_cFlag需要细分数组要先调用Update函数记录好. 
int CTerrain::RenderQuad(int nXCenter,int nZCenter,int nSize) 
{ 
	int nCount = 0; 
	int nH0 = Height(nXCenter,nZCenter); 
	int nH1 = Height(nXCenter-nSize,nZCenter-nSize); 
	int nH2 = Height(nXCenter+nSize,nZCenter-nSize); 
	int nH3 = Height(nXCenter+nSize,nZCenter+nSize); 
	int nH4 = Height(nXCenter-nSize,nZCenter+nSize); 
	//求网格5个点中高度最大和最小值 
	int nMax = nH0,nMin = nH0; 
	if(nMax<nH1)nMax = nH1; 
	if(nMax<nH2)nMax = nH2; 
	if(nMax<nH3)nMax = nH3; 
	if(nMax<nH4)nMax = nH4; 
	if(nMin>nH1)nMin = nH1; 
	if(nMin>nH2)nMin = nH2; 
	if(nMin>nH3)nMin = nH3; 
	if(nMin>nH4)nMin = nH4; 
	SIZE size; 
	size.cx = nSize;size.cy=(nMax-nMin)/2; 
	//如果此网格不在视野内,返回 
	if(!m_pFrustum->CubeInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(size.cx,size.cy))) 
		return 0; 
	if(m_cFlag.IsTrue(nXCenter,nZCenter))//需要细分,递归渲染4个子网格 
	{ 
		nCount += RenderQuad(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2); 
		nCount += RenderQuad(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2); 
		nCount += RenderQuad(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2); 
		nCount += RenderQuad(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2); 
	} 
	else//渲染此网格 
	{ 
		glBegin( GL_TRIANGLE_FAN ); 
		SetTextureCoord(nXCenter,nZCenter);  // center 
		glVertex3i(nXCenter, nH0, nZCenter); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top 
		glVertex3i(nXCenter-nSize,nH1 , nZCenter-nSize); 
		//为了避免出现裂缝而多画三角形扇(就是多加那些红线啦) 
		RemedyTop(nXCenter,nZCenter,nSize); 
		 
		SetTextureCoord(nXCenter+nSize,nZCenter-nSize); //right top 
		glVertex3i(nXCenter+nSize, nH2, nZCenter-nSize); 
		//为了避免出现裂缝而多画三角形扇(就是多加那些红线啦) 
		RemedyRight(nXCenter,nZCenter,nSize); 
 
		SetTextureCoord(nXCenter+nSize,nZCenter+nSize); //right bottom 
		glVertex3i(nXCenter+nSize, nH3, nZCenter+nSize); 
		//为了避免出现裂缝而多画三角形扇(就是多加那些红线啦) 
		RemedyBottom(nXCenter,nZCenter,nSize); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter+nSize);//left bottom  
		glVertex3i(nXCenter-nSize, nH4, nZCenter+nSize); 
		//为了避免出现裂缝而多画三角形扇(就是多加那些红线啦) 
		RemedyLeft(nXCenter,nZCenter,nSize); 
 
		SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top 
		glVertex3i(nXCenter-nSize, nH1, nZCenter-nSize); 
		glEnd(); 
		nCount += 4;//这里为求简单,计算渲染的三角形数目并不是十分准确,比实际要小些 
	} 
	return nCount; 
} 
//计算误差(坡度)的函数 
//参数: 
//	nXCenter: 网格中心点坐的X值 
//	nZCenter: 网格中心点坐的Y值 
//	nSize:网格大小  
//返回: 误差大小 
//说明:只是计算某些点细分与不细分的高度值差的和 
inline int CTerrain::CalcError(int nXCenter,int nZCenter,int nSize) 
{ 
	int nError = 0; 
	int nH0 = Height(nXCenter,nZCenter); 
	int nH1 = Height(nXCenter-nSize,nZCenter-nSize); 
	int nH2 = Height(nXCenter+nSize,nZCenter-nSize); 
	int nH3 = Height(nXCenter+nSize,nZCenter+nSize); 
	int nH4 = Height(nXCenter-nSize,nZCenter+nSize); 
	nError += abs(Height(nXCenter,nZCenter-nSize)-(nH1+nH2)/2); 
	nError += abs(Height(nXCenter+nSize,nZCenter)-(nH2+nH3)/2); 
	nError += abs(Height(nXCenter,nZCenter+nSize)-(nH3+nH4)/2); 
	nError += abs(Height(nXCenter-nSize,nZCenter)-(nH4+nH1)/2); 
 
	nError +=abs(Height(nXCenter-nSize/2,nZCenter-nSize/2)-(nH0+nH1)/2); 
	nError +=abs(Height(nXCenter+nSize/2,nZCenter-nSize/2)-(nH0+nH2)/2); 
	nError +=abs(Height(nXCenter+nSize/2,nZCenter+nSize/2)-(nH0+nH3)/2); 
	nError +=abs(Height(nXCenter-nSize/2,nZCenter+nSize/2)-(nH0+nH4)/2); 
	return nError; 
} 
//更新细分记录数组的递归函数 
//参数: 
//	nXCenter: 网格中心点坐的X值 
//	nZCenter: 网格中心点坐的Y值 
//	nSize:网格大小  
//  nLevel:当前的细分等级(其实可由nSize计算出来)	 
void CTerrain::Update(int nXCenter,int nZCenter,int nSize,int nLevel) 
{ 
	CVector3 vPos = m_pCamera->GetPos(); 
//	int nSize = (m_nMapSize-1)/pow(2,nLevel);//由细节级度得到当前渲染矩形的大小 
	//求网格5个点中高度最大和最小值 
	int nMax = Height(nXCenter,nZCenter); 
	int nMin = nMax; 
	int nH1 = Height(nXCenter-nSize,nZCenter-nSize); 
	int nH2 = Height(nXCenter+nSize,nZCenter-nSize); 
	int nH3 = Height(nXCenter+nSize,nZCenter+nSize); 
	int nH4 = Height(nXCenter-nSize,nZCenter+nSize); 
	if(nMax<nH1)nMax = nH1; 
	if(nMax<nH2)nMax = nH2; 
	if(nMax<nH3)nMax = nH3; 
	if(nMax<nH4)nMax = nH4; 
	if(nMin>nH1)nMin = nH1; 
	if(nMin>nH2)nMin = nH2; 
	if(nMin>nH3)nMin = nH3; 
	if(nMin>nH4)nMin = nH4; 
	//网格的中心点坐标 
	CVector3 vDst(nXCenter,(nMax+nMin)/2,nZCenter); 
	SIZE size; 
	size.cx = nSize;size.cy=(nMax-nMin)/2; 
	//如果此网格不在视野内,返回 
	if(!m_pFrustum->CubeInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(size.cx,size.cy))) 
		return ; 
	//视点到网格中心点的距离 
	int nDist = Dist(vPos,vDst); 
	if(nDist>2000)//距离大于2000时才将距离和误差(坡度)2个因素考虑 
	{ 
		//与距离成正比,与误差(坡度)成反比,还与当前细分等级有关 
		if(70.0*Dist(vPos,vDst)/nSize/CalcError(nXCenter,nZCenter,nSize)>nLevel) 
			return;//此网格不需要细分 
	} 
	else//近距离(<2000)时只考虑误差(坡度)因素(因为动态LOD会造成地表呼吸现象,为减弱此现象不考虑距离因素) 
	{ 
		if(CalcError(nXCenter,nZCenter,nSize)<30) 
			return;//此网格不需要细分 
	} 
	if(nSize!=MIN_GRID) 
	{	////此网格需要细分 
		m_cFlag.Set(nXCenter,nZCenter,true);//把细分数组相应位设为true 
		//递归4个子网格 
		Update(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2,nLevel+1); 
		Update(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2,nLevel+1); 
		Update(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2,nLevel+1); 
		Update(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2,nLevel+1); 
	} 
	//此网格不需要细分 
} 
//修补网格顶部裂缝的函数 
//参数: 
//	nXCenter: 网格中心点坐的X值 
//	nZCenter: 网格中心点坐的Y值 
//	nSize:网格大小  
void CTerrain::RemedyTop(int nXCenter,int nZCenter,int nSize) 
{ 
	if(nZCenter-2*nSize>=0)//保证寻址不越界 
	{ 
		if(!m_cFlag.IsTrue(nXCenter,nZCenter-2*nSize))//此网格相邻的顶部网格(同级的)没有细分 
			return; 
	} 
	else 
		return; 
	//此网格相邻的顶部网格(同级的)已经细分 
	//对顶部一分为二递归(跨级精度的情况) 
	RemedyTop(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2); 
	SetTextureCoord(nXCenter,nZCenter-nSize);  
	glVertex3i(nXCenter,Height(nXCenter,nZCenter-nSize),nZCenter-nSize); 
	RemedyTop(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2); 
} 
void CTerrain::RemedyRight(int nXCenter,int nZCenter,int nSize) 
{ 
	if(nXCenter+2*nSize<m_nMapSize) 
	{ 
		if(!m_cFlag.IsTrue(nXCenter+2*nSize,nZCenter)) 
			return; 
	} 
	else 
		return; 
	RemedyRight(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2); 
	SetTextureCoord(nXCenter+nSize,nZCenter);  
	glVertex3i(nXCenter+nSize,Height(nXCenter+nSize,nZCenter),nZCenter); 
	RemedyRight(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2); 
} 
void CTerrain::RemedyBottom(int nXCenter,int nZCenter,int nSize) 
{ 
	if(nZCenter+2*nSize<m_nMapSize) 
	{ 
		if(!m_cFlag.IsTrue(nXCenter,nZCenter+2*nSize)) 
			return; 
	} 
	else 
		return; 
	RemedyBottom(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2); 
	SetTextureCoord(nXCenter,nZCenter+nSize);  
	glVertex3i(nXCenter,Height(nXCenter,nZCenter+nSize),nZCenter+nSize); 
	RemedyBottom(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2); 
} 
void CTerrain::RemedyLeft(int nXCenter,int nZCenter,int nSize) 
{ 
	if(nXCenter-2*nSize>=0) 
	{ 
		if(!m_cFlag.IsTrue(nXCenter-2*nSize,nZCenter)) 
			return; 
	} 
	else 
		return; 
	RemedyLeft(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2); 
	SetTextureCoord(nXCenter-nSize,nZCenter);  
	glVertex3i(nXCenter-nSize,Height(nXCenter-nSize,nZCenter),nZCenter); 
	RemedyLeft(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2); 
}