www.pudn.com > Game_11.rar > Terrain.cpp


// Terrain.cpp: implementation of the CTerrain class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "Terrain.h" 
#include "windows.h" 
#include "stdio.h" 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
CTerrain::CTerrain(LPDIRECT3DDEVICE9 pDevice,D3DVECTOR position) 
{ 
		m_pDevice=pDevice; 
		m_vPos=position; 
		nCol=0; 
		nRow=0; 
 
		m_CellWidth=0; 
		m_pIB=NULL; 
		m_pVB=NULL; 
		m_pTexture=NULL; 
		m_maxHeight=800;  
		 
 
} 
#define SAFE_RELEASE(p) if(p) {p->Release();p=NULL;} 
CTerrain::~CTerrain() 
{ 
	SAFE_RELEASE(m_pIB) 
	SAFE_RELEASE(m_pVB); 
	SAFE_RELEASE(m_pTexture); 
	free(m_pHeightData);  
} 
 
CTerrain::CreateTerrain(char* strTerrain,char* strTexture, 
				float cellWidth,float height) 
{ 
		m_CellWidth=cellWidth; 
		m_maxHeight=height;  
		D3DXCreateTextureFromFile(m_pDevice, 
			strTexture,	&m_pTexture); 
		 
		//前436bytes存放文件头, 
		//灰度图的调色板一般为0x000000~0xffffff且 rr=gg==bb 
		m_pHeightData=(BYTE*)malloc(0x436*sizeof(BYTE));	 
		FILE *fBmp=fopen(strTerrain,"r");  
		fread(m_pHeightData,sizeof(BYTE),0x436,fBmp);	//读取文件头 
    	nCol=*(DWORD*)((BYTE*)m_pHeightData+0x12);	//获取图像宽 
		nRow=*(DWORD*)((BYTE*)m_pHeightData+0x16);	//获取图像高度 
 
		DWORD nSize=*(DWORD*)((BYTE*)m_pHeightData+0x22); 
		m_pHeightData=(BYTE*)realloc(m_pHeightData,nSize*sizeof(BYTE));	//BmpData 
		fseek(fBmp,0x436,SEEK_SET);					//偏移到BMP data的起始点 
		fread(m_pHeightData,sizeof(BYTE),nSize,fBmp); 
		fclose(fBmp);  
 
/*		另一种方法:用GDI对象来读取位图,但是会转换成DIB位图 
		HBITMAP	hBmp; 
		hBmp=(HBITMAP)LoadImage(NULL,strTerrain, 
			IMAGE_BITMAP,nCol,nRow,LR_LOADFROMFILE); 
		pBmpData=(DWORD*)malloc(nCol*nRow*sizeof(DWORD)); 
		GetBitmapBits(hBmp,nCol*nRow*sizeof(DWORD),pBmpData); 
*/ 
		long i=0,j=0; 
		float tu=0,tv=-0.5; 
//				char strInf[256]; 
 
 
		m_pDevice->CreateVertexBuffer(nCol*nRow*sizeof(D3DVERTEX), 
			D3DUSAGE_SOFTWAREPROCESSING,D3DFVF_TERRAIN,  
			D3DPOOL_DEFAULT, &m_pVB,NULL); 
		D3DVERTEX* pVertices; 
 
		m_pVB->Lock(0,nCol*nRow*sizeof(D3DVERTEX),(void**)&pVertices,D3DLOCK_DISCARD); 
			for(i=0;i=1.0f)?(-1.0f):(tu+0.1f));		//tu=-1~1,-1~1,...... 
				if(i%nCol==0)  
				{ 
					tv=((tv+0.1f>=1.0f)?(-1.0f):(tv+0.1f)); 
					tu=0; 
				} 
 
				pVertices[i].tu=tu>0?tu:-tu; 
				pVertices[i].tv=tv>0?tv:-tv; 
 
//				sprintf(strInf,"%.2f,%.2f,%.2f,%.2f,%.2f\n",pVertices[i].x, 
//				 
//				pVertices[i].y,pVertices[i].z,pVertices[i].tu,pVertices[i].tv); 
//					OutputDebugString(strInf); 
 
			} 
		m_pVB->Unlock(); 
 
 
		long nBufferSize=(nCol-1)*(nRow-1)*6*sizeof(WORD); 
		m_pDevice->CreateIndexBuffer(nBufferSize,D3DUSAGE_WRITEONLY, 
			D3DFMT_INDEX16,D3DPOOL_MANAGED,&m_pIB,NULL); 
 
 
		WORD* pIndex; 
		m_pIB->Lock(0,0,(void**)&pIndex,0); 
			for(i=0,j=0;iUnlock(); 
		 
} 
 
	void CTerrain::Render() 
	{ 
 
		m_pDevice->SetTexture(0,m_pTexture); 
		m_pDevice->SetFVF(D3DFVF_TERRAIN); 
		m_pDevice->SetStreamSource(0,m_pVB,0,sizeof(D3DVERTEX)); 
		m_pDevice->SetIndices(m_pIB); 
		this->SetMatrix();  
		m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 
			0,0,nCol*nRow,0,(nCol-1)*(nRow-1)*2); 
 
	} 
 
	void CTerrain::SetMatrix() 
	{	D3DXMATRIX	matWorld; 
		D3DXMatrixTranslation(&matWorld,m_vPos.x-m_CellWidth*nCol/2.0f , 
			m_vPos.y,m_vPos.z-m_CellWidth*nRow/2); 
		m_pDevice->SetTransform(D3DTS_WORLD,&matWorld); 
	}; 
 
	float CTerrain::GetHeight(float x,float z) 
	{ 
		long col,row; 
		col=x/m_CellWidth+nCol/2; 
		row=z/m_CellWidth+nRow/2; 
		if(col> nCol || col<0 ||row>nRow || row<0) return 0; 
		else  
		{ 
			float f=(m_pHeightData[row*nCol+col]*m_maxHeight/256.0f);	 
			return (f); 
		} 
	} 
 
 
 
HRESULT CTerrain::Pick(POINT ptCursor,D3DXVECTOR3 *pVRet, 
					   float ScreenWidth,float ScreendHeight) 
{ 
	D3DXVECTOR3 vPickRayDir;  
	D3DXVECTOR3 vPickRayOrig; 
	BOOL	m_bClosestOnly=TRUE;	//只计算最近点 
 
	DWORD	m_dwNumIntersections = 0L; 
 
 
 
    // 计算射线 
    { 
        D3DXMATRIXA16 matProj; 
        m_pDevice->GetTransform( D3DTS_PROJECTION, &matProj ); 
 
 
        // Compute the vector of the pick ray in screen space 
        D3DXVECTOR3 v; 
        v.x =  ( ( ( 2.0f * ptCursor.x ) / ScreenWidth  ) /*- 1*/ ) / matProj._11; 
        v.y = ( ( ( 2.0f * ptCursor.y ) / ScreendHeight ) /*- 1*/ ) / matProj._22; 
        v.z =  1.0f; 
 
        // Get the inverse view matrix 
        D3DXMATRIXA16 matView, m; 
        m_pDevice->GetTransform( D3DTS_VIEW, &matView ); 
        D3DXMatrixInverse( &m, NULL, &matView ); 
 
        // Transform the screen space pick ray into 3D space 
        vPickRayDir.x  = v.x*m._11 + v.y*m._21 + v.z*m._31; 
        vPickRayDir.y  = v.x*m._12 + v.y*m._22 + v.z*m._32; 
        vPickRayDir.z  = v.x*m._13 + v.y*m._23 + v.z*m._33; 
        vPickRayOrig.x = m._41; 
        vPickRayOrig.y = m._42; 
        vPickRayOrig.z = m._43; 
    } 
 
    // Get the picked triangle 
     
        WORD*			pIndices; 
        D3DVERTEX*		pVertices; 
 
        m_pIB->Lock( 0, 0, (void**)&pIndices, 0 ); 
        m_pVB->Lock( 0, 0, (void**)&pVertices, 0 ); 
        //{ 
            DWORD dwNumFaces =(nRow-1)*(nCol-1)*2; 
            FLOAT fBary1, fBary2; 
            FLOAT fDist; 
            for( DWORD i=0; i0) 
		{ 
			pVRet->x=pVertices[pIndices[3*m_IntersectionArray[0].dwFace+1]].x+m_vPos.x-m_CellWidth*nCol/2.0f; 
			pVRet->y=pVertices[pIndices[3*m_IntersectionArray[0].dwFace+1]].y+m_vPos.y; 
			pVRet->z=pVertices[pIndices[3*m_IntersectionArray[0].dwFace+1]].z+m_vPos.z-m_CellWidth*nRow/2.0f; 
			m_pVB->Unlock(); 
			m_pIB->Unlock(); 
			return 1; 
		} 
 
        m_pVB->Unlock(); 
        m_pIB->Unlock(); 
     
 
 
    return 0; 
} 
 
 
BOOL CTerrain::IntersectTriangle( const D3DXVECTOR3& orig, 
                                       const D3DXVECTOR3& dir, D3DXVECTOR3& v0, 
                                       D3DXVECTOR3& v1, D3DXVECTOR3& v2, 
                                       FLOAT* t, FLOAT* u, FLOAT* v ) 
{ 
    // Find vectors for two edges sharing vert0 
    D3DXVECTOR3 edge1 = v1 - v0; 
    D3DXVECTOR3 edge2 = v2 - v0; 
 
    // Begin calculating determinant - also used to calculate U parameter 
    D3DXVECTOR3 pvec; 
    D3DXVec3Cross( &pvec, &dir, &edge2 );	//|a×b|=|a||b|sinθ 
 
    // 如果det接近0,表明射线平行于三角形 
    FLOAT det = D3DXVec3Dot( &edge1, &pvec );	//|a dot b|=|a||b|cosθ 
 
    D3DXVECTOR3 tvec; 
    if( det > 0 ) 
    { 
        tvec = orig - v0; 
 
    } 
    else 
    {	 
        tvec = v0 - orig; 
        det = -det; 
    } 
 
    if( det < 0.0001f ) 
        return FALSE; 
 
    // Calculate U parameter and test bounds 
    *u = D3DXVec3Dot( &tvec, &pvec ); 
    if( *u < 0.0f || *u > det ) 
        return FALSE; 
 
    // Prepare to test V parameter 
    D3DXVECTOR3 qvec; 
    D3DXVec3Cross( &qvec, &tvec, &edge1 ); 
 
    // Calculate V parameter and test bounds 
    *v = D3DXVec3Dot( &dir, &qvec ); 
    if( *v < 0.0f || *u + *v > det ) 
        return FALSE; 
 
    // Calculate t, scale parameters, ray intersects triangle 
    *t = D3DXVec3Dot( &edge2, &qvec ); 
    FLOAT fInvDet = 1.0f / det; 
    *t *= fInvDet; 
    *u *= fInvDet; 
    *v *= fInvDet; 
 
    return TRUE; 
}