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;
}