DirectX 3D 基本框架(四)

时间:2023-02-09 13:29:35

继续扩展原来的D3D基本框架。这次将添加地形类库。做出如下修改:

1.增加一个terrain头/库文件。

代码清单:

  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. // 
  3. // File: terrain.h
  4. // 
  5. // by tianzhihen
  6. //
  7. // 2008.10.27, MSVC++ 8.0 
  8. //          
  9. //////////////////////////////////////////////////////////////////////////////////////////////////
  10. #ifndef __terrainH__
  11. #define __terrainH__
  12. #include "d3dUtility.h"
  13. #include <string>
  14. #include <vector>
  15. class Terrain
  16. {
  17. public:
  18.     Terrain(
  19.         IDirect3DDevice9* device,
  20.         std::string heightmapFileName, 
  21.         int numVertsPerRow,  
  22.         int numVertsPerCol, 
  23.         int cellSpacing,    // space between cells
  24.         float heightScale);   
  25.     ~Terrain();
  26.     int  getHeightmapEntry(int row, int col);
  27.     void setHeightmapEntry(int row, int col, int value);
  28.     float getHeight(float x, float z);
  29.     bool  loadTexture(std::string fileName);
  30.     bool  genTexture(D3DXVECTOR3* directionToLight);
  31.     bool  draw(D3DXMATRIX* world, bool drawTris);
  32. private:
  33.     IDirect3DDevice9*       _device;
  34.     IDirect3DTexture9*      _tex;
  35.     IDirect3DVertexBuffer9* _vb;
  36.     IDirect3DIndexBuffer9*  _ib;
  37.     int _numVertsPerRow;
  38.     int _numVertsPerCol;
  39.     int _cellSpacing;
  40.     int _numCellsPerRow;
  41.     int _numCellsPerCol;
  42.     int _width;
  43.     int _depth;
  44.     int _numVertices;
  45.     int _numTriangles;
  46.     float _heightScale;
  47.     std::vector<int> _heightmap;
  48.     // 帮助器函数
  49.     bool  readRawFile(std::string fileName);
  50.     bool  computeVertices();
  51.     bool  computeIndices();
  52.     float computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight);
  53.     struct TerrainVertex
  54.     {
  55.         TerrainVertex(){}
  56.         TerrainVertex(float x, float y, float z, float u, float v)
  57.         {
  58.             _x = x; _y = y; _z = z; _u = u; _v = v;
  59.         }
  60.         float _x, _y, _z;
  61.         float _u, _v;
  62.         static const DWORD FVF;
  63.     };
  64. };
  65. #endif // __terrainH__
  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. // 
  3. // File: terrain.cpp
  4. // 
  5. // by tianzhihen
  6. //
  7. // 2008.10.27, MSVC++ 8.0 
  8. //          
  9. //////////////////////////////////////////////////////////////////////////////////////////////////
  10. #include "terrain.h"
  11. #include <fstream>
  12. #include <cmath>
  13. const DWORD Terrain::TerrainVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX1;
  14. Terrain::Terrain(IDirect3DDevice9* device,
  15.                  std::string heightmapFileName,
  16.                  int numVertsPerRow,
  17.                  int numVertsPerCol,
  18.                  int cellSpacing,
  19.                  float heightScale)
  20. {
  21.     _device         = device;
  22.     _numVertsPerRow = numVertsPerRow;
  23.     _numVertsPerCol = numVertsPerCol;
  24.     _cellSpacing    = cellSpacing;
  25.     _numCellsPerRow = _numVertsPerRow - 1;
  26.     _numCellsPerCol = _numVertsPerCol - 1;
  27.     _width = _numCellsPerRow * _cellSpacing;
  28.     _depth = _numCellsPerCol * _cellSpacing;
  29.     _numVertices  = _numVertsPerRow * _numVertsPerCol;
  30.     _numTriangles = _numCellsPerRow * _numCellsPerCol * 2;
  31.     _heightScale = heightScale;
  32.     // 加载高度图
  33.     if( !readRawFile(heightmapFileName) )
  34.     {
  35.         ::MessageBox(0, "readRawFile - FAILED", 0, 0);
  36.         ::PostQuitMessage(0);
  37.     }
  38.     // 缩放高度
  39.     for(int i = 0; i < _heightmap.size(); i++)
  40.         _heightmap[i] *= heightScale;
  41.     // 计算顶点坐标
  42.     if( !computeVertices() )
  43.     {
  44.         ::MessageBox(0, "computeVertices - FAILED", 0, 0);
  45.         ::PostQuitMessage(0);
  46.     }
  47.     // 计算索引坐标
  48.     if( !computeIndices() )
  49.     {
  50.         ::MessageBox(0, "computeIndices - FAILED", 0, 0);
  51.         ::PostQuitMessage(0);
  52.     }
  53. }
  54. Terrain::~Terrain()
  55. {
  56.     d3d::Release<IDirect3DVertexBuffer9*>(_vb);
  57.     d3d::Release<IDirect3DIndexBuffer9*>(_ib);
  58.     d3d::Release<IDirect3DTexture9*>(_tex);
  59. }
  60. int Terrain::getHeightmapEntry(int row, int col)
  61. {
  62.     return _heightmap[row * _numVertsPerRow + col];
  63. }
  64. void Terrain::setHeightmapEntry(int row, int col, int value)
  65. {
  66.     _heightmap[row * _numVertsPerRow + col] = value;
  67. }
  68. bool Terrain::computeVertices()
  69. {
  70.     HRESULT hr = 0;
  71.     hr = _device->CreateVertexBuffer(
  72.         _numVertices * sizeof(TerrainVertex),
  73.         D3DUSAGE_WRITEONLY,
  74.         TerrainVertex::FVF,
  75.         D3DPOOL_MANAGED,
  76.         &_vb,
  77.         0);
  78.     if(FAILED(hr))
  79.         return false;
  80.     // 起始顶点坐标
  81.     int startX = -_width / 2;
  82.     int startZ =  _depth / 2;
  83.     // 终点顶点坐标
  84.     int endX =  _width / 2;
  85.     int endZ = -_depth / 2;
  86.     //计算纹理坐标([0,1]之间)的增量
  87.     float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;
  88.     float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;
  89.     TerrainVertex* v = 0;
  90.     _vb->Lock(0, 0, (void**)&v, 0);
  91.     int i = 0;
  92.     for(int z = startZ; z >= endZ; z -= _cellSpacing)
  93.     {
  94.         int j = 0;
  95.         for(int x = startX; x <= endX; x += _cellSpacing)
  96.         {
  97.             // 设置空间坐标及纹理坐标
  98.             
  99.             int index = i * _numVertsPerRow + j;
  100.             v[index] = TerrainVertex(
  101.                 (float)x,
  102.                 (float)_heightmap[index],
  103.                 (float)z,
  104.                 (float)j * uCoordIncrementSize,
  105.                 (float)i * vCoordIncrementSize);
  106.             j++; // next column
  107.         }
  108.         i++; // next row
  109.     }
  110.     _vb->Unlock();
  111.     return true;
  112. }
  113. bool Terrain::computeIndices()
  114. {
  115.     HRESULT hr = 0;
  116.     hr = _device->CreateIndexBuffer(
  117.         _numTriangles * 3 * sizeof(WORD), // 3 indices per triangle
  118.         D3DUSAGE_WRITEONLY,
  119.         D3DFMT_INDEX16,
  120.         D3DPOOL_MANAGED,
  121.         &_ib,
  122.         0);
  123.     if(FAILED(hr))
  124.         return false;
  125.     WORD* indices = 0;
  126.     _ib->Lock(0, 0, (void**)&indices, 0);
  127.     int baseIndex = 0;
  128.     // 循环计算每个四边形的索引值
  129.     for(int i = 0; i < _numCellsPerCol; i++)
  130.     {
  131.         for(int j = 0; j < _numCellsPerRow; j++)
  132.         {
  133.             indices[baseIndex]     =   i   * _numVertsPerRow + j;
  134.             indices[baseIndex + 1] =   i   * _numVertsPerRow + j + 1;
  135.             indices[baseIndex + 2] = (i+1) * _numVertsPerRow + j;
  136.             indices[baseIndex + 3] = (i+1) * _numVertsPerRow + j;
  137.             indices[baseIndex + 4] =   i   * _numVertsPerRow + j + 1;
  138.             indices[baseIndex + 5] = (i+1) * _numVertsPerRow + j + 1;
  139.             // next quad
  140.             baseIndex += 6;
  141.         }
  142.     }
  143.     _ib->Unlock();
  144.     return true;
  145. }
  146. bool Terrain::loadTexture(std::string fileName)
  147. {
  148.     // 加载已创建好的纹理文件
  149.     HRESULT hr = 0;
  150.     hr = D3DXCreateTextureFromFile(
  151.         _device,
  152.         fileName.c_str(),
  153.         &_tex);
  154.     if(FAILED(hr))
  155.         return false;
  156.     return true;
  157. }
  158. bool Terrain::genTexture(D3DXVECTOR3* directionToLight)
  159. {
  160.     // 一种过程化方法 —— 首先创建一个空纹理,然后基于地形的高度图逐个计算纹理元的颜色
  161.     HRESULT hr = 0;
  162.     // 纹理元宽/高
  163.     int texWidth  = _numCellsPerRow;
  164.     int texHeight = _numCellsPerCol;
  165.     // 创建空纹理
  166.     hr = D3DXCreateTexture(
  167.         _device,
  168.         texWidth, texHeight,
  169.         0, // create a complete mipmap chain
  170.         0, // usage
  171.         D3DFMT_X8R8G8B8,// 32 bit XRGB format
  172.         D3DPOOL_MANAGED, &_tex);
  173.     if(FAILED(hr))
  174.         return false;
  175.     D3DSURFACE_DESC textureDesc; 
  176.     _tex->GetLevelDesc(0 /*level*/, &textureDesc);
  177.     // 验证纹理元格式
  178.     if( textureDesc.Format != D3DFMT_X8R8G8B8 )
  179.         return false;
  180.         
  181.     D3DLOCKED_RECT lockedRect;
  182.     _tex->LockRect(0/*lock top surface*/, &lockedRect, 
  183.         0 /* lock entire tex*/, 0/*flags*/);         
  184.     DWORD* imageData = (DWORD*)lockedRect.pBits;
  185.     for(int i = 0; i < texHeight; i++)
  186.     {
  187.         for(int j = 0; j < texWidth; j++)
  188.         {
  189.             D3DXCOLOR c;
  190.             // 取得顶点高度
  191.             float height = (float)getHeightmapEntry(i, j) / _heightScale;
  192.             // 确定顶点颜色
  193.             if( (height) < 42.5f )       c = d3d::BEACH_SAND;
  194.             else if( (height) < 85.0f )  c = d3d::LIGHT_YELLOW_GREEN;
  195.             else if( (height) < 127.5f ) c = d3d::PUREGREEN;
  196.             else if( (height) < 170.0f ) c = d3d::DARK_YELLOW_GREEN;
  197.             else if( (height) < 212.5f ) c = d3d::DARKBROWN;
  198.             else                         c = d3d::WHITE;
  199.     
  200.             // 确定单位纹理元明暗度
  201.             c *=  computeShade(i, j, directionToLight);
  202.         
  203.             imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;//float->DWORD
  204.         }
  205.     }
  206.     _tex->UnlockRect(0);
  207.     //纹理过滤  
  208.     hr = D3DXFilterTexture(
  209.         _tex,
  210.         0, // default palette
  211.         0, // use top level as source level
  212.         D3DX_DEFAULT); // default filter
  213.     if(FAILED(hr))
  214.     {
  215.         ::MessageBox(0, "D3DXFilterTexture() - FAILED", 0, 0);
  216.         return false;
  217.     }
  218.     return true;
  219. }
  220. float Terrain::computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight)
  221. {
  222.     // 计算三角形平面顶点高度值
  223.     float heightA = getHeightmapEntry(cellRow,   cellCol);
  224.     float heightB = getHeightmapEntry(cellRow,   cellCol+1);
  225.     float heightC = getHeightmapEntry(cellRow+1, cellCol);
  226.     // 创建 u/v 向量
  227.     D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
  228.     D3DXVECTOR3 v(0.0f,         heightC - heightA, -_cellSpacing);
  229.     // 计算平面法向量
  230.     D3DXVECTOR3 n;
  231.     D3DXVec3Cross(&n, &u, &v);
  232.     D3DXVec3Normalize(&n, &n);
  233.     // 计算法向量与方向光的cos值
  234.     float cosine = D3DXVec3Dot(&n, directionToLight);
  235.     if(cosine < 0.0f)
  236.         cosine = 0.0f;
  237.     return cosine;
  238. }
  239. bool Terrain::readRawFile(std::string fileName)
  240. {
  241.     // 读取RAW文件(128x128) 最多128x128个顶点 
  242.     // 每个顶点对应一个高度值
  243.     std::vector<BYTE> in( _numVertices );
  244.     std::ifstream inFile(fileName.c_str(), std::ios_base::binary);
  245.     if( inFile == 0 )
  246.         return false;
  247.     inFile.read(
  248.         (char*)&in[0], // buffer
  249.         in.size());// number of bytes to read into buffer
  250.     inFile.close();
  251.     // 重置高度图vector大小
  252.     _heightmap.resize( _numVertices );
  253.     for(int i = 0; i < in.size(); i++)
  254.         _heightmap[i] = in[i];
  255.     return true;
  256. }
  257. float Terrain::getHeight(float x, float z)
  258. {
  259.     // 求得点所在高度(计算视点矩阵用)
  260.     // 变换坐标系——使原点位于左上角,而不是原来的中心
  261.     x = ((float)_width / 2.0f) + x;
  262.     z = ((float)_depth / 2.0f) - z;
  263.     // 变换坐标系单位长度——不是原来的_cellSpacing,而是1
  264.     x /= (float)_cellSpacing;
  265.     z /= (float)_cellSpacing;
  266.     // 取不大于 x/z 的最大整数
  267.     float col = ::floorf(x);
  268.     float row = ::floorf(z);
  269.     // 取得所求点所在方格的4个顶点高度
  270.     // 
  271.     //  A   B
  272.     //  *---*
  273.     //  | / |
  274.     //  *---*  
  275.     //  C   D
  276.     float A = getHeightmapEntry(row,   col);
  277.     float B = getHeightmapEntry(row,   col+1);
  278.     float C = getHeightmapEntry(row+1, col);
  279.     float D = getHeightmapEntry(row+1, col+1);
  280.     // 继续变换坐标——使dx/dz位于左上第一个方格内
  281.     float dx = x - col;
  282.     float dz = z - row;
  283.     float height = 0.0f;
  284.     if(dz < 1.0f - dx)  //点位于上三角ABC
  285.     {
  286.         float uy = B - A; // A->B
  287.         float vy = C - A; // A->C
  288.         // (A + dxu + dzv)的y分量就是该点所处高度
  289.         height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
  290.     }
  291.     else // 点位于下三角DCB
  292.     {
  293.         float uy = C - D; // D->C
  294.         float vy = B - D; // D->B
  295.         height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);
  296.     }
  297.     return height;
  298. }
  299. bool Terrain::draw(D3DXMATRIX* world, bool drawTris)
  300. {
  301.     HRESULT hr = 0;
  302.     if( _device )
  303.     {
  304.         _device->SetTransform(D3DTS_WORLD, world);
  305.         _device->SetStreamSource(0, _vb, 0, sizeof(TerrainVertex));
  306.         _device->SetFVF(TerrainVertex::FVF);
  307.         _device->SetIndices(_ib);
  308.         
  309.         _device->SetTexture(0, _tex);
  310.         // 不使用光照直到我们开启
  311.         _device->SetRenderState(D3DRS_LIGHTING, false);
  312.         hr =_device->DrawIndexedPrimitive(
  313.             D3DPT_TRIANGLELIST,
  314.             0,
  315.             0,
  316.             _numVertices,
  317.             0,
  318.             _numTriangles);
  319.         _device->SetRenderState(D3DRS_LIGHTING, true);
  320.         if( drawTris )
  321.         {
  322.             _device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
  323.             hr =_device->DrawIndexedPrimitive(
  324.                 D3DPT_TRIANGLELIST,
  325.                 0,
  326.                 0,
  327.                 _numVertices,
  328.                 0,
  329.                 _numTriangles);
  330.             _device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
  331.         }
  332.         if(FAILED(hr))
  333.             return false;
  334.     }
  335.     return true;
  336. }

2.增加一个FPS类(计算帧频)

代码清单:

  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. // 
  3. // File: fps.h
  4. // 
  5. // by tianzhihen
  6. //
  7. // 2008.10.27, MSVC++ 8.0 
  8. //          
  9. //////////////////////////////////////////////////////////////////////////////////////////////////
  10. #ifndef __fpsH__
  11. #define __fpsH__
  12. #include "d3dfont.h"
  13. class FPSCounter
  14. {
  15. public:
  16.     FPSCounter(IDirect3DDevice9* device);
  17.     ~FPSCounter();
  18.     bool render(D3DCOLOR color, float timeDelta);
  19. private:
  20.     IDirect3DDevice9* _device;
  21.     
  22.     CD3DFont* _font;
  23.     DWORD     _frameCnt;
  24.     float     _timeElapsed;
  25.     float     _fps;
  26.     char      _fpsString[9];
  27.     
  28. };
  29. #endif // __fpsH__
  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. // 
  3. // File: fps.cpp
  4. // 
  5. // by tianzhihen
  6. //
  7. // 2008.10.27, MSVC++ 8.0 
  8. //          
  9. //////////////////////////////////////////////////////////////////////////////////////////////////
  10. #include "fps.h"
  11. #include <cstdio>
  12. FPSCounter::FPSCounter(IDirect3DDevice9* device)
  13. {
  14.     _device = device;
  15.     _font = new CD3DFont("Times New Roman", 24, 0);
  16.     _font->InitDeviceObjects( _device );
  17.     _font->RestoreDeviceObjects();
  18.     _frameCnt = 0;
  19.     _timeElapsed = 0.0f;
  20.     _fps         = 0.0f;
  21. }
  22. FPSCounter::~FPSCounter()
  23. {
  24.     if( _font )
  25.     {
  26.         _font->InvalidateDeviceObjects();
  27.         _font->DeleteDeviceObjects();
  28.         delete _font;
  29.     }
  30. }
  31. bool FPSCounter::render(D3DCOLOR color, float timeDelta)
  32. {
  33.     if( _font )
  34.     {
  35.         _frameCnt++;
  36.         _timeElapsed += timeDelta;
  37.         if(_timeElapsed >= 1.0f)
  38.         {
  39.             _fps = (float)_frameCnt / _timeElapsed;
  40.             sprintf(_fpsString, "%f", _fps);
  41.             _fpsString[8] = '/0'// mark end of string
  42.             _timeElapsed = 0.0f;
  43.             _frameCnt    = 0;
  44.         }
  45.         _font->DrawText(20, 20, color, _fpsString); 
  46.     }
  47.     return true;
  48. }

3.增加相应DX框架文件。因为FPS类用到了d3dfont.h头文件,因此需添加相应的框架文件。列举如下:

d3dfont.h   d3dfont.cpp   dxutil.h   dxutil.cpp   d3dutil.h   d3dutil.cpp 

 

4.保持camera类不变。d3dUtility.cpp添加Lerp()函数。

  1. float d3d::Lerp(float a, float b, float t)
  2. {
  3.     return a - (a*t) + (b*t);
  4. }

相应的d3dUtility.h须包含此函数声明。

  1.     float Lerp(float a, float b, float t);

5.在d3dUtility.h中添加一些颜色常量。

  1.     const D3DXCOLOR BEACH_SAND( D3DCOLOR_XRGB(255, 249, 157) );
  2.     const D3DXCOLOR DESERT_SAND( D3DCOLOR_XRGB(250, 205, 135) );
  3.     const D3DXCOLOR LIGHTGREEN( D3DCOLOR_XRGB( 60, 184, 120) );
  4.     const D3DXCOLOR  PUREGREEN( D3DCOLOR_XRGB(  0, 166,  81) );
  5.     const D3DXCOLOR  DARKGREEN( D3DCOLOR_XRGB(  0, 114,  54) );
  6.     const D3DXCOLOR LIGHT_YELLOW_GREEN( D3DCOLOR_XRGB(124, 197, 118) );
  7.     const D3DXCOLOR  PURE_YELLOW_GREEN( D3DCOLOR_XRGB( 57, 181,  74) );
  8.     const D3DXCOLOR  DARK_YELLOW_GREEN( D3DCOLOR_XRGB( 25, 123,  48) );
  9.     const D3DXCOLOR LIGHTBROWN(D3DCOLOR_XRGB(198, 156, 109));
  10.     const D3DXCOLOR DARKBROWN( D3DCOLOR_XRGB(115, 100,  87));