继续扩展原来的D3D基本框架。这次将添加地形类库。做出如下修改:
1.增加一个terrain头/库文件。
代码清单:
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // File: terrain.h
- //
- // by tianzhihen
- //
- // 2008.10.27, MSVC++ 8.0
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////
- #ifndef __terrainH__
- #define __terrainH__
- #include "d3dUtility.h"
- #include <string>
- #include <vector>
- class Terrain
- {
- public:
- Terrain(
- IDirect3DDevice9* device,
- std::string heightmapFileName,
- int numVertsPerRow,
- int numVertsPerCol,
- int cellSpacing, // space between cells
- float heightScale);
- ~Terrain();
- int getHeightmapEntry(int row, int col);
- void setHeightmapEntry(int row, int col, int value);
- float getHeight(float x, float z);
- bool loadTexture(std::string fileName);
- bool genTexture(D3DXVECTOR3* directionToLight);
- bool draw(D3DXMATRIX* world, bool drawTris);
- private:
- IDirect3DDevice9* _device;
- IDirect3DTexture9* _tex;
- IDirect3DVertexBuffer9* _vb;
- IDirect3DIndexBuffer9* _ib;
- int _numVertsPerRow;
- int _numVertsPerCol;
- int _cellSpacing;
- int _numCellsPerRow;
- int _numCellsPerCol;
- int _width;
- int _depth;
- int _numVertices;
- int _numTriangles;
- float _heightScale;
- std::vector<int> _heightmap;
- // 帮助器函数
- bool readRawFile(std::string fileName);
- bool computeVertices();
- bool computeIndices();
- float computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight);
- struct TerrainVertex
- {
- TerrainVertex(){}
- TerrainVertex(float x, float y, float z, float u, float v)
- {
- _x = x; _y = y; _z = z; _u = u; _v = v;
- }
- float _x, _y, _z;
- float _u, _v;
- static const DWORD FVF;
- };
- };
- #endif // __terrainH__
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // File: terrain.cpp
- //
- // by tianzhihen
- //
- // 2008.10.27, MSVC++ 8.0
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////
- #include "terrain.h"
- #include <fstream>
- #include <cmath>
- const DWORD Terrain::TerrainVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX1;
- Terrain::Terrain(IDirect3DDevice9* device,
- std::string heightmapFileName,
- int numVertsPerRow,
- int numVertsPerCol,
- int cellSpacing,
- float heightScale)
- {
- _device = device;
- _numVertsPerRow = numVertsPerRow;
- _numVertsPerCol = numVertsPerCol;
- _cellSpacing = cellSpacing;
- _numCellsPerRow = _numVertsPerRow - 1;
- _numCellsPerCol = _numVertsPerCol - 1;
- _width = _numCellsPerRow * _cellSpacing;
- _depth = _numCellsPerCol * _cellSpacing;
- _numVertices = _numVertsPerRow * _numVertsPerCol;
- _numTriangles = _numCellsPerRow * _numCellsPerCol * 2;
- _heightScale = heightScale;
- // 加载高度图
- if( !readRawFile(heightmapFileName) )
- {
- ::MessageBox(0, "readRawFile - FAILED", 0, 0);
- ::PostQuitMessage(0);
- }
- // 缩放高度
- for(int i = 0; i < _heightmap.size(); i++)
- _heightmap[i] *= heightScale;
- // 计算顶点坐标
- if( !computeVertices() )
- {
- ::MessageBox(0, "computeVertices - FAILED", 0, 0);
- ::PostQuitMessage(0);
- }
- // 计算索引坐标
- if( !computeIndices() )
- {
- ::MessageBox(0, "computeIndices - FAILED", 0, 0);
- ::PostQuitMessage(0);
- }
- }
- Terrain::~Terrain()
- {
- d3d::Release<IDirect3DVertexBuffer9*>(_vb);
- d3d::Release<IDirect3DIndexBuffer9*>(_ib);
- d3d::Release<IDirect3DTexture9*>(_tex);
- }
- int Terrain::getHeightmapEntry(int row, int col)
- {
- return _heightmap[row * _numVertsPerRow + col];
- }
- void Terrain::setHeightmapEntry(int row, int col, int value)
- {
- _heightmap[row * _numVertsPerRow + col] = value;
- }
- bool Terrain::computeVertices()
- {
- HRESULT hr = 0;
- hr = _device->CreateVertexBuffer(
- _numVertices * sizeof(TerrainVertex),
- D3DUSAGE_WRITEONLY,
- TerrainVertex::FVF,
- D3DPOOL_MANAGED,
- &_vb,
- 0);
- if(FAILED(hr))
- return false;
- // 起始顶点坐标
- int startX = -_width / 2;
- int startZ = _depth / 2;
- // 终点顶点坐标
- int endX = _width / 2;
- int endZ = -_depth / 2;
- //计算纹理坐标([0,1]之间)的增量
- float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;
- float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;
- TerrainVertex* v = 0;
- _vb->Lock(0, 0, (void**)&v, 0);
- int i = 0;
- for(int z = startZ; z >= endZ; z -= _cellSpacing)
- {
- int j = 0;
- for(int x = startX; x <= endX; x += _cellSpacing)
- {
- // 设置空间坐标及纹理坐标
- int index = i * _numVertsPerRow + j;
- v[index] = TerrainVertex(
- (float)x,
- (float)_heightmap[index],
- (float)z,
- (float)j * uCoordIncrementSize,
- (float)i * vCoordIncrementSize);
- j++; // next column
- }
- i++; // next row
- }
- _vb->Unlock();
- return true;
- }
- bool Terrain::computeIndices()
- {
- HRESULT hr = 0;
- hr = _device->CreateIndexBuffer(
- _numTriangles * 3 * sizeof(WORD), // 3 indices per triangle
- D3DUSAGE_WRITEONLY,
- D3DFMT_INDEX16,
- D3DPOOL_MANAGED,
- &_ib,
- 0);
- if(FAILED(hr))
- return false;
- WORD* indices = 0;
- _ib->Lock(0, 0, (void**)&indices, 0);
- int baseIndex = 0;
- // 循环计算每个四边形的索引值
- for(int i = 0; i < _numCellsPerCol; i++)
- {
- for(int j = 0; j < _numCellsPerRow; j++)
- {
- indices[baseIndex] = i * _numVertsPerRow + j;
- indices[baseIndex + 1] = i * _numVertsPerRow + j + 1;
- indices[baseIndex + 2] = (i+1) * _numVertsPerRow + j;
- indices[baseIndex + 3] = (i+1) * _numVertsPerRow + j;
- indices[baseIndex + 4] = i * _numVertsPerRow + j + 1;
- indices[baseIndex + 5] = (i+1) * _numVertsPerRow + j + 1;
- // next quad
- baseIndex += 6;
- }
- }
- _ib->Unlock();
- return true;
- }
- bool Terrain::loadTexture(std::string fileName)
- {
- // 加载已创建好的纹理文件
- HRESULT hr = 0;
- hr = D3DXCreateTextureFromFile(
- _device,
- fileName.c_str(),
- &_tex);
- if(FAILED(hr))
- return false;
- return true;
- }
- bool Terrain::genTexture(D3DXVECTOR3* directionToLight)
- {
- // 一种过程化方法 —— 首先创建一个空纹理,然后基于地形的高度图逐个计算纹理元的颜色
- HRESULT hr = 0;
- // 纹理元宽/高
- int texWidth = _numCellsPerRow;
- int texHeight = _numCellsPerCol;
- // 创建空纹理
- hr = D3DXCreateTexture(
- _device,
- texWidth, texHeight,
- 0, // create a complete mipmap chain
- 0, // usage
- D3DFMT_X8R8G8B8,// 32 bit XRGB format
- D3DPOOL_MANAGED, &_tex);
- if(FAILED(hr))
- return false;
- D3DSURFACE_DESC textureDesc;
- _tex->GetLevelDesc(0 /*level*/, &textureDesc);
- // 验证纹理元格式
- if( textureDesc.Format != D3DFMT_X8R8G8B8 )
- return false;
- D3DLOCKED_RECT lockedRect;
- _tex->LockRect(0/*lock top surface*/, &lockedRect,
- 0 /* lock entire tex*/, 0/*flags*/);
- DWORD* imageData = (DWORD*)lockedRect.pBits;
- for(int i = 0; i < texHeight; i++)
- {
- for(int j = 0; j < texWidth; j++)
- {
- D3DXCOLOR c;
- // 取得顶点高度
- float height = (float)getHeightmapEntry(i, j) / _heightScale;
- // 确定顶点颜色
- if( (height) < 42.5f ) c = d3d::BEACH_SAND;
- else if( (height) < 85.0f ) c = d3d::LIGHT_YELLOW_GREEN;
- else if( (height) < 127.5f ) c = d3d::PUREGREEN;
- else if( (height) < 170.0f ) c = d3d::DARK_YELLOW_GREEN;
- else if( (height) < 212.5f ) c = d3d::DARKBROWN;
- else c = d3d::WHITE;
- // 确定单位纹理元明暗度
- c *= computeShade(i, j, directionToLight);
- imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;//float->DWORD
- }
- }
- _tex->UnlockRect(0);
- //纹理过滤
- hr = D3DXFilterTexture(
- _tex,
- 0, // default palette
- 0, // use top level as source level
- D3DX_DEFAULT); // default filter
- if(FAILED(hr))
- {
- ::MessageBox(0, "D3DXFilterTexture() - FAILED", 0, 0);
- return false;
- }
- return true;
- }
- float Terrain::computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight)
- {
- // 计算三角形平面顶点高度值
- float heightA = getHeightmapEntry(cellRow, cellCol);
- float heightB = getHeightmapEntry(cellRow, cellCol+1);
- float heightC = getHeightmapEntry(cellRow+1, cellCol);
- // 创建 u/v 向量
- D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
- D3DXVECTOR3 v(0.0f, heightC - heightA, -_cellSpacing);
- // 计算平面法向量
- D3DXVECTOR3 n;
- D3DXVec3Cross(&n, &u, &v);
- D3DXVec3Normalize(&n, &n);
- // 计算法向量与方向光的cos值
- float cosine = D3DXVec3Dot(&n, directionToLight);
- if(cosine < 0.0f)
- cosine = 0.0f;
- return cosine;
- }
- bool Terrain::readRawFile(std::string fileName)
- {
- // 读取RAW文件(128x128) 最多128x128个顶点
- // 每个顶点对应一个高度值
- std::vector<BYTE> in( _numVertices );
- std::ifstream inFile(fileName.c_str(), std::ios_base::binary);
- if( inFile == 0 )
- return false;
- inFile.read(
- (char*)&in[0], // buffer
- in.size());// number of bytes to read into buffer
- inFile.close();
- // 重置高度图vector大小
- _heightmap.resize( _numVertices );
- for(int i = 0; i < in.size(); i++)
- _heightmap[i] = in[i];
- return true;
- }
- float Terrain::getHeight(float x, float z)
- {
- // 求得点所在高度(计算视点矩阵用)
- // 变换坐标系——使原点位于左上角,而不是原来的中心
- x = ((float)_width / 2.0f) + x;
- z = ((float)_depth / 2.0f) - z;
- // 变换坐标系单位长度——不是原来的_cellSpacing,而是1
- x /= (float)_cellSpacing;
- z /= (float)_cellSpacing;
- // 取不大于 x/z 的最大整数
- float col = ::floorf(x);
- float row = ::floorf(z);
- // 取得所求点所在方格的4个顶点高度
- //
- // A B
- // *---*
- // | / |
- // *---*
- // C D
- float A = getHeightmapEntry(row, col);
- float B = getHeightmapEntry(row, col+1);
- float C = getHeightmapEntry(row+1, col);
- float D = getHeightmapEntry(row+1, col+1);
- // 继续变换坐标——使dx/dz位于左上第一个方格内
- float dx = x - col;
- float dz = z - row;
- float height = 0.0f;
- if(dz < 1.0f - dx) //点位于上三角ABC
- {
- float uy = B - A; // A->B
- float vy = C - A; // A->C
- // (A + dxu + dzv)的y分量就是该点所处高度
- height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
- }
- else // 点位于下三角DCB
- {
- float uy = C - D; // D->C
- float vy = B - D; // D->B
- height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);
- }
- return height;
- }
- bool Terrain::draw(D3DXMATRIX* world, bool drawTris)
- {
- HRESULT hr = 0;
- if( _device )
- {
- _device->SetTransform(D3DTS_WORLD, world);
- _device->SetStreamSource(0, _vb, 0, sizeof(TerrainVertex));
- _device->SetFVF(TerrainVertex::FVF);
- _device->SetIndices(_ib);
- _device->SetTexture(0, _tex);
- // 不使用光照直到我们开启
- _device->SetRenderState(D3DRS_LIGHTING, false);
- hr =_device->DrawIndexedPrimitive(
- D3DPT_TRIANGLELIST,
- 0,
- 0,
- _numVertices,
- 0,
- _numTriangles);
- _device->SetRenderState(D3DRS_LIGHTING, true);
- if( drawTris )
- {
- _device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
- hr =_device->DrawIndexedPrimitive(
- D3DPT_TRIANGLELIST,
- 0,
- 0,
- _numVertices,
- 0,
- _numTriangles);
- _device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
- }
- if(FAILED(hr))
- return false;
- }
- return true;
- }
2.增加一个FPS类(计算帧频)
代码清单:
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // File: fps.h
- //
- // by tianzhihen
- //
- // 2008.10.27, MSVC++ 8.0
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////
- #ifndef __fpsH__
- #define __fpsH__
- #include "d3dfont.h"
- class FPSCounter
- {
- public:
- FPSCounter(IDirect3DDevice9* device);
- ~FPSCounter();
- bool render(D3DCOLOR color, float timeDelta);
- private:
- IDirect3DDevice9* _device;
- CD3DFont* _font;
- DWORD _frameCnt;
- float _timeElapsed;
- float _fps;
- char _fpsString[9];
- };
- #endif // __fpsH__
- //////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // File: fps.cpp
- //
- // by tianzhihen
- //
- // 2008.10.27, MSVC++ 8.0
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////
- #include "fps.h"
- #include <cstdio>
- FPSCounter::FPSCounter(IDirect3DDevice9* device)
- {
- _device = device;
- _font = new CD3DFont("Times New Roman", 24, 0);
- _font->InitDeviceObjects( _device );
- _font->RestoreDeviceObjects();
- _frameCnt = 0;
- _timeElapsed = 0.0f;
- _fps = 0.0f;
- }
- FPSCounter::~FPSCounter()
- {
- if( _font )
- {
- _font->InvalidateDeviceObjects();
- _font->DeleteDeviceObjects();
- delete _font;
- }
- }
- bool FPSCounter::render(D3DCOLOR color, float timeDelta)
- {
- if( _font )
- {
- _frameCnt++;
- _timeElapsed += timeDelta;
- if(_timeElapsed >= 1.0f)
- {
- _fps = (float)_frameCnt / _timeElapsed;
- sprintf(_fpsString, "%f", _fps);
- _fpsString[8] = '/0'; // mark end of string
- _timeElapsed = 0.0f;
- _frameCnt = 0;
- }
- _font->DrawText(20, 20, color, _fpsString);
- }
- return true;
- }
3.增加相应DX框架文件。因为FPS类用到了d3dfont.h头文件,因此需添加相应的框架文件。列举如下:
d3dfont.h d3dfont.cpp dxutil.h dxutil.cpp d3dutil.h d3dutil.cpp
4.保持camera类不变。d3dUtility.cpp添加Lerp()函数。
- float d3d::Lerp(float a, float b, float t)
- {
- return a - (a*t) + (b*t);
- }
相应的d3dUtility.h须包含此函数声明。
- float Lerp(float a, float b, float t);
5.在d3dUtility.h中添加一些颜色常量。
- const D3DXCOLOR BEACH_SAND( D3DCOLOR_XRGB(255, 249, 157) );
- const D3DXCOLOR DESERT_SAND( D3DCOLOR_XRGB(250, 205, 135) );
- const D3DXCOLOR LIGHTGREEN( D3DCOLOR_XRGB( 60, 184, 120) );
- const D3DXCOLOR PUREGREEN( D3DCOLOR_XRGB( 0, 166, 81) );
- const D3DXCOLOR DARKGREEN( D3DCOLOR_XRGB( 0, 114, 54) );
- const D3DXCOLOR LIGHT_YELLOW_GREEN( D3DCOLOR_XRGB(124, 197, 118) );
- const D3DXCOLOR PURE_YELLOW_GREEN( D3DCOLOR_XRGB( 57, 181, 74) );
- const D3DXCOLOR DARK_YELLOW_GREEN( D3DCOLOR_XRGB( 25, 123, 48) );
- const D3DXCOLOR LIGHTBROWN(D3DCOLOR_XRGB(198, 156, 109));
- const D3DXCOLOR DARKBROWN( D3DCOLOR_XRGB(115, 100, 87));