# dx9.0地形

### dx9.0地形

1. 照相机
这里照相机有
3种移动方式:
1.x轴:left/right
2.y轴:up/down
3.z轴:forward/backward
3种旋转方式:
1.x轴:rotate on right vector
2.y轴:rotate on up vector
3.z轴:rotate on look vector
计算视图矩阵
1.平移:将摄像机的位置移动到原点

2.旋转:将摄像机的方向向量(x、y、z)与世界坐标系的轴对齐

合并视图矩阵=平移矩阵*旋转矩阵

2. 地形

2.生成地形几何数据:
顶点缓存

v[index] = TerrainVertex(
(float)x,
(float)_heightmap[index],
(float)z,
(float)j * uCoordIncrementSize,
(float)i * vCoordIncrementSize);

索引缓存

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;

3.根据高度生成纹理

DWORD* imageData = (DWORD*)lockedRect.pBits;
for (int i = 0; i < texHeight; i++)
{
for (int j = 0; j < texWidth; j++)
{
D3DXCOLOR c;

// get height of upper left vertex of quad.
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;

// fill locked data, note we divide the pitch by four because the
// pitch is given in bytes and there are 4 bytes per DWORD.
imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;
}
}

4.根据光线和平面法线的照射角度生成明暗属性

// build two vectors on the quad
D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
D3DXVECTOR3 v(0.0f, heightC - heightA, -_cellSpacing);

// find the normal by taking the cross product of two
D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);

float cosine = D3DXVec3Dot(&n, directionToLight);

if (cosine < 0.0f)
cosine = 0.0f;

5.根据位置判断上下三角形获取位置高度

//  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);

//
// Find the triangle we are in:
//

// Translate by the transformation that takes the upper-left
// corner of the cell we are in to the origin.  Recall that our
// cellspacing was nomalized to 1.  Thus we have a unit square
// at the origin of our +x -> 'right' and +z -> 'down' system.
float dx = x - col;
float dz = z - row;

// Note the below compuations of u and v are unneccessary, we really
// only need the height, but we compute the entire vector to emphasis
// the books discussion.
float height = 0.0f;
if (dz < 1.0f - dx)  // upper triangle ABC
{
float uy = B - A; // A->B
float vy = C - A; // A->C

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {A}, plus the heights
// found by interpolating on each vector u and v.
height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
}
else // lower triangle DCB
{
float uy = C - D; // D->C
float vy = B - D; // D->B

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {D}, plus the heights
// found by interpolating on each vector u and v.
height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);
}

3. 原书代码修改部分

bool FPSCounter::render(D3DCOLOR color, float timeDelta)
{
if (_font)
{
_frameCnt++;

_timeElapsed += timeDelta;

if (_timeElapsed >= 1.0f)
{
_fps = (float)_frameCnt / _timeElapsed;

std::stringstream ss;
//std::string fpsStr;
ss << _fps;
ss >> _fpsString;
//strncpy_s(_fpsString, 9, fpsStr.c_str(), 9);
//sprintf_s(_fpsString, 8, "%f", _fps);
_fpsString[8] = '\0'; // mark end of string

_timeElapsed = 0.0f;
_frameCnt = 0;
}

_font->DrawText(20, 20, color, _fpsString);
}
return true;
}
1. 运行效果

5.原书代码
d3dfont.h
#pragma once
#include <tchar.h>
#include <D3D9.h>

// Font creation flags
#define D3DFONT_BOLD        0x0001
#define D3DFONT_ITALIC      0x0002
#define D3DFONT_ZENABLE     0x0004

// Font rendering flags
#define D3DFONT_CENTERED_X  0x0001
#define D3DFONT_CENTERED_Y  0x0002
#define D3DFONT_TWOSIDED    0x0004
#define D3DFONT_FILTERED    0x0008

//-----------------------------------------------------------------------------
// Name: class CD3DFont
// Desc: Texture-based font class for doing text in a 3D scene.
//-----------------------------------------------------------------------------
class CD3DFont
{
TCHAR   m_strFontName[80];            // Font properties
DWORD   m_dwFontHeight;
DWORD   m_dwFontFlags;

LPDIRECT3DDEVICE9       m_pd3dDevice; // A D3DDevice used for rendering
LPDIRECT3DTEXTURE9      m_pTexture;   // The d3d texture for this font
LPDIRECT3DVERTEXBUFFER9 m_pVB;        // VertexBuffer for rendering text
DWORD   m_dwTexWidth;                 // Texture dimensions
DWORD   m_dwTexHeight;
FLOAT   m_fTextScale;
FLOAT   m_fTexCoords[128 - 32][4];
DWORD   m_dwSpacing;                  // Character pixel spacing per side

// Stateblocks for setting and restoring render states
LPDIRECT3DSTATEBLOCK9 m_pStateBlockSaved;
LPDIRECT3DSTATEBLOCK9 m_pStateBlockDrawText;

public:
// 2D and 3D text drawing functions
HRESULT DrawText(FLOAT x, FLOAT y, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags = 0L);
HRESULT DrawTextScaled(FLOAT x, FLOAT y, FLOAT z,
FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags = 0L);
HRESULT Render3DText(const TCHAR* strText, DWORD dwFlags = 0L);

// Function to get extent of text
HRESULT GetTextExtent(const TCHAR* strText, SIZE* pSize);

// Initializing and destroying device-dependent objects
HRESULT InitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice);
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
HRESULT DeleteDeviceObjects();

// Constructor / destructor
CD3DFont(const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags = 0L);
~CD3DFont();
};

d3dfont.cpp

//-----------------------------------------------------------------------------
// File: D3DFont.cpp
//
// Desc: Texture-based font class
//
//-----------------------------------------------------------------------------
#define STRICT
#include <stdio.h>
#include <tchar.h>
#include <D3DX9.h>
#include "D3DFont.h"
#include "D3DUtil.h"
#include "DXUtil.h"

//-----------------------------------------------------------------------------
// Custom vertex types for rendering text
//-----------------------------------------------------------------------------
#define MAX_NUM_VERTICES 50*6

struct FONT2DVERTEX { D3DXVECTOR4 p;   DWORD color;     FLOAT tu, tv; };
struct FONT3DVERTEX { D3DXVECTOR3 p;   D3DXVECTOR3 n;   FLOAT tu, tv; };

#define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
#define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)

inline FONT2DVERTEX InitFont2DVertex(const D3DXVECTOR4& p, D3DCOLOR color,
FLOAT tu, FLOAT tv)
{
FONT2DVERTEX v;   v.p = p;   v.color = color;   v.tu = tu;   v.tv = tv;
return v;
}

inline FONT3DVERTEX InitFont3DVertex(const D3DXVECTOR3& p, const D3DXVECTOR3& n,
FLOAT tu, FLOAT tv)
{
FONT3DVERTEX v;   v.p = p;   v.n = n;   v.tu = tu;   v.tv = tv;
return v;
}

//-----------------------------------------------------------------------------
// Name: CD3DFont()
// Desc: Font class constructor
//-----------------------------------------------------------------------------
CD3DFont::CD3DFont(const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags)
{
_tcsncpy_s(m_strFontName, strFontName, sizeof(m_strFontName) / sizeof(TCHAR));
m_strFontName[sizeof(m_strFontName) / sizeof(TCHAR) - 1] = _T('\0');
m_dwFontHeight = dwHeight;
m_dwFontFlags = dwFlags;
m_dwSpacing = 0;

m_pd3dDevice = NULL;
m_pTexture = NULL;
m_pVB = NULL;

m_pStateBlockSaved = NULL;
m_pStateBlockDrawText = NULL;
}

//-----------------------------------------------------------------------------
// Name: ~CD3DFont()
// Desc: Font class destructor
//-----------------------------------------------------------------------------
CD3DFont::~CD3DFont()
{
InvalidateDeviceObjects();
DeleteDeviceObjects();
}

//-----------------------------------------------------------------------------
// Name: InitDeviceObjects()
// Desc: Initializes device-dependent objects, including the vertex buffer used
//       for rendering text and the texture map which stores the font image.
//-----------------------------------------------------------------------------
HRESULT CD3DFont::InitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice)
{
HRESULT hr;

// Keep a local copy of the device
m_pd3dDevice = pd3dDevice;

// Establish the font and texture size
m_fTextScale = 1.0f; // Draw fonts into texture without scaling

// Large fonts need larger textures
if (m_dwFontHeight > 60)
m_dwTexWidth = m_dwTexHeight = 2048;
else if (m_dwFontHeight > 30)
m_dwTexWidth = m_dwTexHeight = 1024;
else if (m_dwFontHeight > 15)
m_dwTexWidth = m_dwTexHeight = 512;
else
m_dwTexWidth = m_dwTexHeight = 256;

// If requested texture is too big, use a smaller texture and smaller font,
// and scale up when rendering.
D3DCAPS9 d3dCaps;
m_pd3dDevice->GetDeviceCaps(&d3dCaps);

if (m_dwTexWidth > d3dCaps.MaxTextureWidth)
{
m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth;
m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth;
}

// Create a new texture for the font
hr = m_pd3dDevice->CreateTexture(m_dwTexWidth, m_dwTexHeight, 1,
0, D3DFMT_A4R4G4B4,
D3DPOOL_MANAGED, &m_pTexture, NULL);
if (FAILED(hr))
return hr;

// Prepare to create a bitmap
DWORD* pBitmapBits;
BITMAPINFO bmi;

// Create a DC and a bitmap for the font
HDC     hDC = CreateCompatibleDC(NULL);
HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS,
(void**)&pBitmapBits, NULL, 0);
SetMapMode(hDC, MM_TEXT);

// Create a font.  By specifying ANTIALIASED_QUALITY, we might get an
// antialiased font, but this is not guaranteed.
INT nHeight = -MulDiv(m_dwFontHeight,
(INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72);
DWORD dwBold = (m_dwFontFlags & D3DFONT_BOLD) ? FW_BOLD : FW_NORMAL;
DWORD dwItalic = (m_dwFontFlags & D3DFONT_ITALIC) ? TRUE : FALSE;
HFONT hFont = CreateFont(nHeight, 0, 0, 0, dwBold, dwItalic,
FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
VARIABLE_PITCH, m_strFontName);
if (NULL == hFont)
return E_FAIL;

HGDIOBJ hbmOld = SelectObject(hDC, hbmBitmap);
HGDIOBJ hFontOld = SelectObject(hDC, hFont);

// Set text properties
SetTextColor(hDC, RGB(255, 255, 255));
SetBkColor(hDC, 0x00000000);
SetTextAlign(hDC, TA_TOP);

// Loop through all printable character and output them to the bitmap..
// Meanwhile, keep track of the corresponding tex coords for each character.
DWORD x = 0;
DWORD y = 0;
TCHAR str[2] = _T("x");
SIZE size;

// Calculate the spacing between characters based on line height
GetTextExtentPoint32(hDC, TEXT(" "), 1, &size);
x = m_dwSpacing = (DWORD)ceil(size.cy * 0.3f);

for (TCHAR c = 32; c < 127; c++)
{
str[0] = c;
GetTextExtentPoint32(hDC, str, 1, &size);

if ((DWORD)(x + size.cx + m_dwSpacing) > m_dwTexWidth)
{
x = m_dwSpacing;
y += size.cy + 1;
}

ExtTextOut(hDC, x + 0, y + 0, ETO_OPAQUE, NULL, str, 1, NULL);

m_fTexCoords[c - 32][0] = ((FLOAT)(x + 0 - m_dwSpacing)) / m_dwTexWidth;
m_fTexCoords[c - 32][1] = ((FLOAT)(y + 0 + 0)) / m_dwTexHeight;
m_fTexCoords[c - 32][2] = ((FLOAT)(x + size.cx + m_dwSpacing)) / m_dwTexWidth;
m_fTexCoords[c - 32][3] = ((FLOAT)(y + size.cy + 0)) / m_dwTexHeight;

x += size.cx + (2 * m_dwSpacing);
}

// Lock the surface and write the alpha values for the set pixels
D3DLOCKED_RECT d3dlr;
m_pTexture->LockRect(0, &d3dlr, 0, 0);
BYTE* pDstRow = (BYTE*)d3dlr.pBits;
WORD* pDst16;
BYTE bAlpha; // 4-bit measure of pixel intensity

for (y = 0; y < m_dwTexHeight; y++)
{
pDst16 = (WORD*)pDstRow;
for (x = 0; x < m_dwTexWidth; x++)
{
bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth * y + x] & 0xff) >> 4);
if (bAlpha > 0)
{
*pDst16++ = (WORD)((bAlpha << 12) | 0x0fff);
}
else
{
*pDst16++ = 0x0000;
}
}
pDstRow += d3dlr.Pitch;
}

// Done updating texture, so clean up used objects
m_pTexture->UnlockRect(0);
SelectObject(hDC, hbmOld);
SelectObject(hDC, hFontOld);
DeleteObject(hbmBitmap);
DeleteObject(hFont);
DeleteDC(hDC);

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CD3DFont::RestoreDeviceObjects()
{
HRESULT hr;

// Create vertex buffer for the letters
int vertexSize = max(sizeof(FONT2DVERTEX), sizeof(FONT3DVERTEX));
if (FAILED(hr = m_pd3dDevice->CreateVertexBuffer(MAX_NUM_VERTICES * vertexSize,
D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
D3DPOOL_DEFAULT, &m_pVB, NULL)))
{
return hr;
}

// Create the state blocks for rendering text
for (UINT which = 0; which < 2; which++)
{
m_pd3dDevice->BeginStateBlock();
m_pd3dDevice->SetTexture(0, m_pTexture);

if (D3DFONT_ZENABLE & m_dwFontFlags)
m_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
else
m_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);

m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
m_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_ALPHAREF, 0x08);
m_pd3dDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
m_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
m_pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_CLIPPLANEENABLE, FALSE);
m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
m_pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
m_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
m_pd3dDevice->SetRenderState(D3DRS_COLORWRITEENABLE,
D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN |
D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

if (which == 0)
m_pd3dDevice->EndStateBlock(&m_pStateBlockSaved);
else
m_pd3dDevice->EndStateBlock(&m_pStateBlockDrawText);
}

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc: Destroys all device-dependent objects
//-----------------------------------------------------------------------------
HRESULT CD3DFont::InvalidateDeviceObjects()
{
SAFE_RELEASE(m_pVB);
SAFE_RELEASE(m_pStateBlockSaved);
SAFE_RELEASE(m_pStateBlockDrawText);

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DeleteDeviceObjects()
// Desc: Destroys all device-dependent objects
//-----------------------------------------------------------------------------
HRESULT CD3DFont::DeleteDeviceObjects()
{
SAFE_RELEASE(m_pTexture);
m_pd3dDevice = NULL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: GetTextExtent()
// Desc: Get the dimensions of a text string
//-----------------------------------------------------------------------------
HRESULT CD3DFont::GetTextExtent(const TCHAR* strText, SIZE* pSize)
{
if (NULL == strText || NULL == pSize)
return E_FAIL;

FLOAT fRowWidth = 0.0f;
FLOAT fRowHeight = (m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight;
FLOAT fWidth = 0.0f;
FLOAT fHeight = fRowHeight;

while (*strText)
{
TCHAR c = *strText++;

if (c == _T('\n'))
{
fRowWidth = 0.0f;
fHeight += fRowHeight;
}

if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT tx2 = m_fTexCoords[c - 32][2];

fRowWidth += (tx2 - tx1) * m_dwTexWidth - 2 * m_dwSpacing;

if (fRowWidth > fWidth)
fWidth = fRowWidth;
}

pSize->cx = (int)fWidth;
pSize->cy = (int)fHeight;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DrawTextScaled()
// Desc: Draws scaled 2D text.  Note that x and y are in viewport coordinates
//       (ranging from -1 to +1).  fXScale and fYScale are the size fraction
//       relative to the entire viewport.  For example, a fXScale of 0.25 is
//       1/8th of the screen width.  This allows you to output text at a fixed
//       fraction of the viewport, even if the screen or window size changes.
//-----------------------------------------------------------------------------
HRESULT CD3DFont::DrawTextScaled(FLOAT x, FLOAT y, FLOAT z,
FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags)
{
if (m_pd3dDevice == NULL)
return E_FAIL;

// Set up renderstate
m_pStateBlockSaved->Capture();
m_pStateBlockDrawText->Apply();
m_pd3dDevice->SetFVF(D3DFVF_FONT2DVERTEX);
m_pd3dDevice->SetStreamSource(0, m_pVB, 0, sizeof(FONT2DVERTEX));

// Set filter states
if (dwFlags & D3DFONT_FILTERED)
{
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
}

D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport(&vp);
FLOAT fLineHeight = (m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight;

// Center the text block in the viewport
if (dwFlags & D3DFONT_CENTERED_X)
{
const TCHAR* strTextTmp = strText;
float xFinal = 0.0f;

while (*strTextTmp)
{
TCHAR c = *strTextTmp++;

if (c == _T('\n'))
break;  // Isn't supported.
if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT tx2 = m_fTexCoords[c - 32][2];

FLOAT w = (tx2 - tx1) * m_dwTexWidth;

w *= (fXScale * vp.Height) / fLineHeight;

xFinal += w - (2 * m_dwSpacing) * (fXScale * vp.Height) / fLineHeight;
}

x = -xFinal / vp.Width;
}
if (dwFlags & D3DFONT_CENTERED_Y)
{
y = -fLineHeight / vp.Height;
}

FLOAT sx = (x + 1.0f) * vp.Width / 2;
FLOAT sy = (y + 1.0f) * vp.Height / 2;
FLOAT sz = z;
FLOAT rhw = 1.0f;

sx -= m_dwSpacing * (fXScale * vp.Height) / fLineHeight;
FLOAT fStartX = sx;

// Fill vertex buffer
FONT2DVERTEX* pVertices;
DWORD         dwNumTriangles = 0L;

while (*strText)
{
TCHAR c = *strText++;

if (c == _T('\n'))
{
sx = fStartX;
sy += fYScale * vp.Height;
}

if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT ty1 = m_fTexCoords[c - 32][1];
FLOAT tx2 = m_fTexCoords[c - 32][2];
FLOAT ty2 = m_fTexCoords[c - 32][3];

FLOAT w = (tx2 - tx1) * m_dwTexWidth;
FLOAT h = (ty2 - ty1) * m_dwTexHeight;

w *= (fXScale * vp.Height) / fLineHeight;
h *= (fYScale * vp.Height) / fLineHeight;

if (c != _T(' '))
{
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + h - 0.5f, sz, rhw), dwColor, tx1, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + 0 - 0.5f, sz, rhw), dwColor, tx1, ty1);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + h - 0.5f, sz, rhw), dwColor, tx2, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + 0 - 0.5f, sz, rhw), dwColor, tx2, ty1);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + h - 0.5f, sz, rhw), dwColor, tx2, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + 0 - 0.5f, sz, rhw), dwColor, tx1, ty1);
dwNumTriangles += 2;

if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6))
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);
dwNumTriangles = 0L;
}
}

sx += w - (2 * m_dwSpacing) * (fXScale * vp.Height) / fLineHeight;
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if (dwNumTriangles > 0)
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);

// Restore the modified renderstates
m_pStateBlockSaved->Apply();

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DrawText()
// Desc: Draws 2D text. Note that sx and sy are in pixels
//-----------------------------------------------------------------------------
HRESULT CD3DFont::DrawText(FLOAT sx, FLOAT sy, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags)
{
if (m_pd3dDevice == NULL)
return E_FAIL;

// Setup renderstate
m_pStateBlockSaved->Capture();
m_pStateBlockDrawText->Apply();
m_pd3dDevice->SetFVF(D3DFVF_FONT2DVERTEX);
m_pd3dDevice->SetStreamSource(0, m_pVB, 0, sizeof(FONT2DVERTEX));

// Set filter states
if (dwFlags & D3DFONT_FILTERED)
{
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
}

// Center the text block in the viewport
if (dwFlags & D3DFONT_CENTERED_X)
{
D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport(&vp);
const TCHAR* strTextTmp = strText;
float xFinal = 0.0f;

while (*strTextTmp)
{
TCHAR c = *strTextTmp++;

if (c == _T('\n'))
break;  // Isn't supported.
if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT tx2 = m_fTexCoords[c - 32][2];

FLOAT w = (tx2 - tx1) * m_dwTexWidth / m_fTextScale;

xFinal += w - (2 * m_dwSpacing);
}

sx = (vp.Width - xFinal) / 2.0f;
}
if (dwFlags & D3DFONT_CENTERED_Y)
{
D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport(&vp);
float fLineHeight = ((m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight);
sy = (vp.Height - fLineHeight) / 2;
}

sx -= m_dwSpacing;
FLOAT fStartX = sx;

// Fill vertex buffer
FONT2DVERTEX* pVertices = NULL;
DWORD         dwNumTriangles = 0;

while (*strText)
{
TCHAR c = *strText++;

if (c == _T('\n'))
{
sx = fStartX;
sy += (m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight;
}

if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT ty1 = m_fTexCoords[c - 32][1];
FLOAT tx2 = m_fTexCoords[c - 32][2];
FLOAT ty2 = m_fTexCoords[c - 32][3];

FLOAT w = (tx2 - tx1) * m_dwTexWidth / m_fTextScale;
FLOAT h = (ty2 - ty1) * m_dwTexHeight / m_fTextScale;

if (c != _T(' '))
{
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + h - 0.5f, 0.9f, 1.0f), dwColor, tx1, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f), dwColor, tx1, ty1);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + h - 0.5f, 0.9f, 1.0f), dwColor, tx2, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f), dwColor, tx2, ty1);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + w - 0.5f, sy + h - 0.5f, 0.9f, 1.0f), dwColor, tx2, ty2);
*pVertices++ = InitFont2DVertex(D3DXVECTOR4(sx + 0 - 0.5f, sy + 0 - 0.5f, 0.9f, 1.0f), dwColor, tx1, ty1);
dwNumTriangles += 2;

if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6))
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);
pVertices = NULL;
dwNumTriangles = 0L;
}
}

sx += w - (2 * m_dwSpacing);
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if (dwNumTriangles > 0)
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);

// Restore the modified renderstates
m_pStateBlockSaved->Apply();

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: Render3DText()
// Desc: Renders 3D text
//-----------------------------------------------------------------------------
HRESULT CD3DFont::Render3DText(const TCHAR* strText, DWORD dwFlags)
{
if (m_pd3dDevice == NULL)
return E_FAIL;

// Setup renderstate
m_pStateBlockSaved->Capture();
m_pStateBlockDrawText->Apply();
m_pd3dDevice->SetFVF(D3DFVF_FONT3DVERTEX);
m_pd3dDevice->SetStreamSource(0, m_pVB, 0, sizeof(FONT3DVERTEX));

// Set filter states
if (dwFlags & D3DFONT_FILTERED)
{
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
}

// Position for each text element
FLOAT x = 0.0f;
FLOAT y = 0.0f;

// Center the text block at the origin (not the viewport)
if (dwFlags & D3DFONT_CENTERED_X)
{
SIZE sz;
GetTextExtent(strText, &sz);
x = -(((FLOAT)sz.cx) / 10.0f) / 2.0f;
}
if (dwFlags & D3DFONT_CENTERED_Y)
{
SIZE sz;
GetTextExtent(strText, &sz);
y = -(((FLOAT)sz.cy) / 10.0f) / 2.0f;
}

// Turn off culling for two-sided text
if (dwFlags & D3DFONT_TWOSIDED)
m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

x -= m_dwSpacing / 10.0f;
FLOAT fStartX = x;
TCHAR c;

// Fill vertex buffer
FONT3DVERTEX* pVertices;
DWORD         dwNumTriangles = 0L;

while ((c = *strText++) != 0)
{
if (c == '\n')
{
x = fStartX;
y -= (m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight / 10.0f;
}

if ((c - 32) < 0 || (c - 32) >= 128 - 32)
continue;

FLOAT tx1 = m_fTexCoords[c - 32][0];
FLOAT ty1 = m_fTexCoords[c - 32][1];
FLOAT tx2 = m_fTexCoords[c - 32][2];
FLOAT ty2 = m_fTexCoords[c - 32][3];

FLOAT w = (tx2 - tx1) * m_dwTexWidth / (10.0f * m_fTextScale);
FLOAT h = (ty2 - ty1) * m_dwTexHeight / (10.0f * m_fTextScale);

if (c != _T(' '))
{
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + 0, y + 0, 0), D3DXVECTOR3(0, 0, -1), tx1, ty2);
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + 0, y + h, 0), D3DXVECTOR3(0, 0, -1), tx1, ty1);
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + w, y + 0, 0), D3DXVECTOR3(0, 0, -1), tx2, ty2);
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + w, y + h, 0), D3DXVECTOR3(0, 0, -1), tx2, ty1);
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + w, y + 0, 0), D3DXVECTOR3(0, 0, -1), tx2, ty2);
*pVertices++ = InitFont3DVertex(D3DXVECTOR3(x + 0, y + h, 0), D3DXVECTOR3(0, 0, -1), tx1, ty1);
dwNumTriangles += 2;

if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6))
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);
dwNumTriangles = 0L;
}
}

x += w - (2 * m_dwSpacing) / 10.0f;
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if (dwNumTriangles > 0)
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);

// Restore the modified renderstates
m_pStateBlockSaved->Apply();

return S_OK;
}

d3dutil.h

#pragma once
#include <D3D9.h>
#include <D3DX9Math.h>

//-----------------------------------------------------------------------------
// Name: D3DUtil_InitMaterial()
// Desc: Initializes a D3DMATERIAL9 structure, setting the diffuse and ambient
//       colors. It does not set emissive or specular colors.
//-----------------------------------------------------------------------------
VOID D3DUtil_InitMaterial(D3DMATERIAL9& mtrl, FLOAT r = 0.0f, FLOAT g = 0.0f,
FLOAT b = 0.0f, FLOAT a = 1.0f);

//-----------------------------------------------------------------------------
// Name: D3DUtil_InitLight()
// Desc: Initializes a D3DLIGHT structure, setting the light position. The
//       diffuse color is set to white, specular and ambient left as black.
//-----------------------------------------------------------------------------
VOID D3DUtil_InitLight(D3DLIGHT9& light, D3DLIGHTTYPE ltType,
FLOAT x = 0.0f, FLOAT y = 0.0f, FLOAT z = 0.0f);

//-----------------------------------------------------------------------------
// Name: D3DUtil_CreateTexture()
// Desc: Helper function to create a texture. It checks the root path first,
//       then tries the DXSDK media path (as specified in the system registry).
//-----------------------------------------------------------------------------
HRESULT D3DUtil_CreateTexture(LPDIRECT3DDEVICE9 pd3dDevice, TCHAR* strTexture,
LPDIRECT3DTEXTURE9* ppTexture,
D3DFORMAT d3dFormat = D3DFMT_UNKNOWN);

//-----------------------------------------------------------------------------
// Name: D3DUtil_GetCubeMapViewMatrix()
// Desc: Returns a view matrix for rendering to a face of a cubemap.
//-----------------------------------------------------------------------------
D3DXMATRIX D3DUtil_GetCubeMapViewMatrix(DWORD dwFace);

//-----------------------------------------------------------------------------
// Name: D3DUtil_GetRotationFromCursor()
// Desc: Returns a quaternion for the rotation implied by the window's cursor
//       position.
//-----------------------------------------------------------------------------
D3DXQUATERNION D3DUtil_GetRotationFromCursor(HWND hWnd,

//-----------------------------------------------------------------------------
// Name: D3DUtil_SetDeviceCursor
// Desc: Builds and sets a cursor for the D3D device based on hCursor.
//-----------------------------------------------------------------------------
HRESULT D3DUtil_SetDeviceCursor(LPDIRECT3DDEVICE9 pd3dDevice, HCURSOR hCursor,

//-----------------------------------------------------------------------------
// Name: D3DUtil_D3DFormatToString
// Desc: Returns the string for the given D3DFORMAT.
//       bWithPrefix determines whether the string should include the "D3DFMT_"
//-----------------------------------------------------------------------------
TCHAR* D3DUtil_D3DFormatToString(D3DFORMAT format, bool bWithPrefix = true);

//-----------------------------------------------------------------------------
// Name: class CD3DArcBall
// Desc:
//-----------------------------------------------------------------------------
class CD3DArcBall
{
INT            m_iWidth;   // ArcBall's window width
INT            m_iHeight;  // ArcBall's window height

D3DXQUATERNION m_qDown;               // Quaternion before button down
D3DXQUATERNION m_qNow;                // Composite quaternion for current drag
D3DXMATRIXA16  m_matRotation;         // Matrix for arcball's orientation
D3DXMATRIXA16  m_matRotationDelta;    // Matrix for arcball's orientation
D3DXMATRIXA16  m_matTranslation;      // Matrix for arcball's position
D3DXMATRIXA16  m_matTranslationDelta; // Matrix for arcball's position
BOOL           m_bDrag;               // Whether user is dragging arcball
BOOL           m_bRightHanded;        // Whether to use RH coordinate system

D3DXVECTOR3 ScreenToVector(int sx, int sy);

public:
LRESULT     HandleMouseMessages(HWND, UINT, WPARAM, LPARAM);

D3DXMATRIX* GetRotationMatrix() { return &m_matRotation; }
D3DXMATRIX* GetRotationDeltaMatrix() { return &m_matRotationDelta; }
D3DXMATRIX* GetTranslationMatrix() { return &m_matTranslation; }
D3DXMATRIX* GetTranslationDeltaMatrix() { return &m_matTranslationDelta; }
BOOL        IsBeingDragged() { return m_bDrag; }

VOID        SetWindow(INT w, INT h, FLOAT r = 0.9);
VOID        SetRightHanded(BOOL bRightHanded) { m_bRightHanded = bRightHanded; }

CD3DArcBall();
VOID        Init();
};

//-----------------------------------------------------------------------------
// Name: class CD3DCamera
// Desc:
//-----------------------------------------------------------------------------
class CD3DCamera
{
D3DXVECTOR3 m_vEyePt;       // Attributes for view matrix
D3DXVECTOR3 m_vLookatPt;
D3DXVECTOR3 m_vUpVec;

D3DXVECTOR3 m_vView;
D3DXVECTOR3 m_vCross;

D3DXMATRIXA16  m_matView;
D3DXMATRIXA16  m_matBillboard; // Special matrix for billboarding effects

FLOAT       m_fFOV;         // Attributes for projection matrix
FLOAT       m_fAspect;
FLOAT       m_fNearPlane;
FLOAT       m_fFarPlane;
D3DXMATRIXA16  m_matProj;

public:
// Access functions
D3DXVECTOR3 GetEyePt() { return m_vEyePt; }
D3DXVECTOR3 GetLookatPt() { return m_vLookatPt; }
D3DXVECTOR3 GetUpVec() { return m_vUpVec; }
D3DXVECTOR3 GetViewDir() { return m_vView; }
D3DXVECTOR3 GetCross() { return m_vCross; }

FLOAT       GetFOV() { return m_fFOV; }
FLOAT       GetAspect() { return m_fAspect; }
FLOAT       GetNearPlane() { return m_fNearPlane; }
FLOAT       GetFarPlane() { return m_fFarPlane; }

D3DXMATRIX  GetViewMatrix() { return m_matView; }
D3DXMATRIX  GetBillboardMatrix() { return m_matBillboard; }
D3DXMATRIX  GetProjMatrix() { return m_matProj; }

VOID SetViewParams(D3DXVECTOR3& vEyePt, D3DXVECTOR3& vLookatPt,
D3DXVECTOR3& vUpVec);
VOID SetProjParams(FLOAT fFOV, FLOAT fAspect, FLOAT fNearPlane,
FLOAT fFarPlane);

CD3DCamera();
};

d3dutil.cpp

#define STRICT
#include <Windows.h>
#include <WindowsX.h>
#include <tchar.h>
#include <stdio.h>
#include "D3DUtil.h"
#include "DXUtil.h"
#include "D3DX9.h"

//-----------------------------------------------------------------------------
// Name: D3DUtil_InitMaterial()
// Desc: Initializes a D3DMATERIAL9 structure, setting the diffuse and ambient
//       colors. It does not set emissive or specular colors.
//-----------------------------------------------------------------------------
VOID D3DUtil_InitMaterial(D3DMATERIAL9& mtrl, FLOAT r, FLOAT g, FLOAT b,
FLOAT a)
{
ZeroMemory(&mtrl, sizeof(D3DMATERIAL9));
mtrl.Diffuse.r = mtrl.Ambient.r = r;
mtrl.Diffuse.g = mtrl.Ambient.g = g;
mtrl.Diffuse.b = mtrl.Ambient.b = b;
mtrl.Diffuse.a = mtrl.Ambient.a = a;
}

//-----------------------------------------------------------------------------
// Name: D3DUtil_InitLight()
// Desc: Initializes a D3DLIGHT structure, setting the light position. The
//       diffuse color is set to white; specular and ambient are left as black.
//-----------------------------------------------------------------------------
VOID D3DUtil_InitLight(D3DLIGHT9& light, D3DLIGHTTYPE ltType,
FLOAT x, FLOAT y, FLOAT z)
{
D3DXVECTOR3 vecLightDirUnnormalized(x, y, z);
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = ltType;
light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecLightDirUnnormalized);
light.Position.x = x;
light.Position.y = y;
light.Position.z = z;
light.Range = 1000.0f;
}

//-----------------------------------------------------------------------------
// Name: D3DUtil_CreateTexture()
// Desc: Helper function to create a texture. It checks the root path first,
//       then tries the DXSDK media path (as specified in the system registry).
//-----------------------------------------------------------------------------
HRESULT D3DUtil_CreateTexture(LPDIRECT3DDEVICE9 pd3dDevice, TCHAR* strTexture,
LPDIRECT3DTEXTURE9* ppTexture, D3DFORMAT d3dFormat)
{
HRESULT hr;
TCHAR strPath[MAX_PATH];

// Get the path to the texture
if (FAILED(hr = DXUtil_FindMediaFileCb(strPath, sizeof(strPath), strTexture)))
return hr;

// Create the texture using D3DX
return D3DXCreateTextureFromFileEx(pd3dDevice, strPath,
D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, d3dFormat,
D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE | D3DX_FILTER_MIRROR,
D3DX_FILTER_TRIANGLE | D3DX_FILTER_MIRROR, 0, NULL, NULL, ppTexture);
}

//-----------------------------------------------------------------------------
// Name: D3DUtil_GetCubeMapViewMatrix()
// Desc: Returns a view matrix for rendering to a face of a cubemap.
//-----------------------------------------------------------------------------
D3DXMATRIX D3DUtil_GetCubeMapViewMatrix(DWORD dwFace)
{
D3DXVECTOR3 vEyePt = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vLookDir;
D3DXVECTOR3 vUpDir;

switch (dwFace)
{
case D3DCUBEMAP_FACE_POSITIVE_X:
vLookDir = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
vUpDir = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
break;
case D3DCUBEMAP_FACE_NEGATIVE_X:
vLookDir = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
vUpDir = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
break;
case D3DCUBEMAP_FACE_POSITIVE_Y:
vLookDir = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
vUpDir = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
break;
case D3DCUBEMAP_FACE_NEGATIVE_Y:
vLookDir = D3DXVECTOR3(0.0f, -1.0f, 0.0f);
vUpDir = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
break;
case D3DCUBEMAP_FACE_POSITIVE_Z:
vLookDir = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
vUpDir = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
break;
case D3DCUBEMAP_FACE_NEGATIVE_Z:
vLookDir = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
vUpDir = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
break;
}

// Set the view transform for this cubemap surface
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookDir, &vUpDir);
return matView;
}

//-----------------------------------------------------------------------------
// Name: D3DUtil_GetRotationFromCursor()
// Desc: Returns a quaternion for the rotation implied by the window's cursor
//       position.
//-----------------------------------------------------------------------------
D3DXQUATERNION D3DUtil_GetRotationFromCursor(HWND hWnd,
{
POINT pt;
RECT  rc;
GetCursorPos(&pt);
GetClientRect(hWnd, &rc);
ScreenToClient(hWnd, &pt);
FLOAT sx = (((2.0f * pt.x) / (rc.right - rc.left)) - 1);
FLOAT sy = (((2.0f * pt.y) / (rc.bottom - rc.top)) - 1);
FLOAT sz;

if (sx == 0.0f && sy == 0.0f)
return D3DXQUATERNION(0.0f, 0.0f, 0.0f, 1.0f);

FLOAT d2 = sqrtf(sx * sx + sy * sy);

if (d2 < fTrackBallRadius * 0.70710678118654752440) // Inside sphere
else                                                 // On hyperbola

// Get two points on trackball's sphere
D3DXVECTOR3 p1(sx, sy, sz);

// Get axis of rotation, which is cross product of p1 and p2
D3DXVECTOR3 vAxis;
D3DXVec3Cross(&vAxis, &p1, &p2);

// Calculate angle for the rotation about that axis
D3DXVECTOR3 vecDiff = p2 - p1;
FLOAT t = D3DXVec3Length(&vecDiff) / (2.0f * fTrackBallRadius);
if (t > +1.0f) t = +1.0f;
if (t < -1.0f) t = -1.0f;
FLOAT fAngle = 2.0f * asinf(t);

// Convert axis to quaternion
D3DXQUATERNION quat;
D3DXQuaternionRotationAxis(&quat, &vAxis, fAngle);
return quat;
}

//-----------------------------------------------------------------------------
// Name: D3DUtil_SetDeviceCursor
// Desc: Gives the D3D device a cursor with image and hotspot from hCursor.
//-----------------------------------------------------------------------------
HRESULT D3DUtil_SetDeviceCursor(LPDIRECT3DDEVICE9 pd3dDevice, HCURSOR hCursor,
{
HRESULT hr = E_FAIL;
ICONINFO iconinfo;
BOOL bBWCursor;
LPDIRECT3DSURFACE9 pCursorSurface = NULL;
HDC hdcColor = NULL;
HDC hdcScreen = NULL;
BITMAP bm;
DWORD dwWidth;
DWORD dwHeightSrc;
DWORD dwHeightDest;
COLORREF crColor;
UINT x;
UINT y;
BITMAPINFO bmi;
COLORREF* pcrArrayColor = NULL;
DWORD* pBitmap;
HGDIOBJ hgdiobjOld;

ZeroMemory(&iconinfo, sizeof(iconinfo));
if (!GetIconInfo(hCursor, &iconinfo))
goto End;

if (0 == GetObject((HGDIOBJ)iconinfo.hbmMask, sizeof(BITMAP), (LPVOID)&bm))
goto End;
dwWidth = bm.bmWidth;
dwHeightSrc = bm.bmHeight;

if (iconinfo.hbmColor == NULL)
{
bBWCursor = TRUE;
dwHeightDest = dwHeightSrc / 2;
}
else
{
bBWCursor = FALSE;
dwHeightDest = dwHeightSrc;
}

// Create a surface for the fullscreen cursor
if (FAILED(hr = pd3dDevice->CreateOffscreenPlainSurface(dwWidth, dwHeightDest,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pCursorSurface, NULL)))
{
goto End;
}

pcrArrayMask = new DWORD[dwWidth * dwHeightSrc];

ZeroMemory(&bmi, sizeof(bmi));

hdcScreen = GetDC(NULL);
{
hr = E_FAIL;
goto End;
}

if (!bBWCursor)
{
pcrArrayColor = new DWORD[dwWidth * dwHeightDest];
hdcColor = CreateCompatibleDC(hdcScreen);
if (hdcColor == NULL)
{
hr = E_FAIL;
goto End;
}
SelectObject(hdcColor, iconinfo.hbmColor);
GetDIBits(hdcColor, iconinfo.hbmColor, 0, dwHeightDest,
pcrArrayColor, &bmi, DIB_RGB_COLORS);
}

// Transfer cursor image into the surface
D3DLOCKED_RECT lr;
pCursorSurface->LockRect(&lr, NULL, 0);
pBitmap = (DWORD*)lr.pBits;
for (y = 0; y < dwHeightDest; y++)
{
for (x = 0; x < dwWidth; x++)
{
if (bBWCursor)
{
crColor = pcrArrayMask[dwWidth * (dwHeightDest - 1 - y) + x];
}
else
{
crColor = pcrArrayColor[dwWidth * (dwHeightDest - 1 - y) + x];
}
pBitmap[dwWidth * y + x] = 0xff000000 | crColor;
else
pBitmap[dwWidth * y + x] = 0x00000000;

// It may be helpful to make the D3D cursor look slightly
// different from the Windows cursor so you can distinguish
// between the two when developing/testing code.  When
// small grey "D3D" characters to the upper-left corner of
// the D3D cursor image.
if (bAddWatermark && x < 12 && y < 5)
{
// 11.. 11.. 11.. .... CCC0
// 1.1. ..1. 1.1. .... A2A0
// 1.1. .1.. 1.1. .... A4A0
// 1.1. ..1. 1.1. .... A2A0
// 11.. 11.. 11.. .... CCC0

const WORD wMask[5] = { 0xccc0, 0xa2a0, 0xa4a0, 0xa2a0, 0xccc0 };
if (wMask[y] & (1 << (15 - x)))
{
pBitmap[dwWidth * y + x] |= 0xff808080;
}
}
}
}
pCursorSurface->UnlockRect();

// Set the device cursor
if (FAILED(hr = pd3dDevice->SetCursorProperties(iconinfo.xHotspot,
iconinfo.yHotspot, pCursorSurface)))
{
goto End;
}

hr = S_OK;

End:
if (iconinfo.hbmColor != NULL)
DeleteObject(iconinfo.hbmColor);
if (hdcScreen != NULL)
ReleaseDC(NULL, hdcScreen);
if (hdcColor != NULL)
DeleteDC(hdcColor);
SAFE_DELETE_ARRAY(pcrArrayColor);
SAFE_RELEASE(pCursorSurface);
return hr;
}

//-----------------------------------------------------------------------------
// Name: D3DFormatToString
// Desc: Returns the string for the given D3DFORMAT.
//-----------------------------------------------------------------------------
TCHAR* D3DUtil_D3DFormatToString(D3DFORMAT format, bool bWithPrefix)
{
TCHAR* pstr = NULL;
switch (format)
{
case D3DFMT_UNKNOWN:         pstr = (TCHAR*)TEXT("D3DFMT_UNKNOWN"); break;
case D3DFMT_R8G8B8:          pstr = (TCHAR*)TEXT("D3DFMT_R8G8B8"); break;
case D3DFMT_A8R8G8B8:        pstr = (TCHAR*)TEXT("D3DFMT_A8R8G8B8"); break;
case D3DFMT_X8R8G8B8:        pstr = (TCHAR*)TEXT("D3DFMT_X8R8G8B8"); break;
case D3DFMT_R5G6B5:          pstr = (TCHAR*)TEXT("D3DFMT_R5G6B5"); break;
case D3DFMT_X1R5G5B5:        pstr = (TCHAR*)TEXT("D3DFMT_X1R5G5B5"); break;
case D3DFMT_A1R5G5B5:        pstr = (TCHAR*)TEXT("D3DFMT_A1R5G5B5"); break;
case D3DFMT_A4R4G4B4:        pstr = (TCHAR*)TEXT("D3DFMT_A4R4G4B4"); break;
case D3DFMT_R3G3B2:          pstr = (TCHAR*)TEXT("D3DFMT_R3G3B2"); break;
case D3DFMT_A8:              pstr = (TCHAR*)TEXT("D3DFMT_A8"); break;
case D3DFMT_A8R3G3B2:        pstr = (TCHAR*)TEXT("D3DFMT_A8R3G3B2"); break;
case D3DFMT_X4R4G4B4:        pstr = (TCHAR*)TEXT("D3DFMT_X4R4G4B4"); break;
case D3DFMT_A2B10G10R10:     pstr = (TCHAR*)TEXT("D3DFMT_A2B10G10R10"); break;
case D3DFMT_A8B8G8R8:        pstr = (TCHAR*)TEXT("D3DFMT_A8B8G8R8"); break;
case D3DFMT_X8B8G8R8:        pstr = (TCHAR*)TEXT("D3DFMT_X8B8G8R8"); break;
case D3DFMT_G16R16:          pstr = (TCHAR*)TEXT("D3DFMT_G16R16"); break;
case D3DFMT_A2R10G10B10:     pstr = (TCHAR*)TEXT("D3DFMT_A2R10G10B10"); break;
case D3DFMT_A16B16G16R16:    pstr = (TCHAR*)TEXT("D3DFMT_A16B16G16R16"); break;
case D3DFMT_A8P8:            pstr = (TCHAR*)TEXT("D3DFMT_A8P8"); break;
case D3DFMT_P8:              pstr = (TCHAR*)TEXT("D3DFMT_P8"); break;
case D3DFMT_L8:              pstr = (TCHAR*)TEXT("D3DFMT_L8"); break;
case D3DFMT_A8L8:            pstr = (TCHAR*)TEXT("D3DFMT_A8L8"); break;
case D3DFMT_A4L4:            pstr = (TCHAR*)TEXT("D3DFMT_A4L4"); break;
case D3DFMT_V8U8:            pstr = (TCHAR*)TEXT("D3DFMT_V8U8"); break;
case D3DFMT_L6V5U5:          pstr = (TCHAR*)TEXT("D3DFMT_L6V5U5"); break;
case D3DFMT_X8L8V8U8:        pstr = (TCHAR*)TEXT("D3DFMT_X8L8V8U8"); break;
case D3DFMT_Q8W8V8U8:        pstr = (TCHAR*)TEXT("D3DFMT_Q8W8V8U8"); break;
case D3DFMT_V16U16:          pstr = (TCHAR*)TEXT("D3DFMT_V16U16"); break;
case D3DFMT_A2W10V10U10:     pstr = (TCHAR*)TEXT("D3DFMT_A2W10V10U10"); break;
case D3DFMT_UYVY:            pstr = (TCHAR*)TEXT("D3DFMT_UYVY"); break;
case D3DFMT_YUY2:            pstr = (TCHAR*)TEXT("D3DFMT_YUY2"); break;
case D3DFMT_DXT1:            pstr = (TCHAR*)TEXT("D3DFMT_DXT1"); break;
case D3DFMT_DXT2:            pstr = (TCHAR*)TEXT("D3DFMT_DXT2"); break;
case D3DFMT_DXT3:            pstr = (TCHAR*)TEXT("D3DFMT_DXT3"); break;
case D3DFMT_DXT4:            pstr = (TCHAR*)TEXT("D3DFMT_DXT4"); break;
case D3DFMT_DXT5:            pstr = (TCHAR*)TEXT("D3DFMT_DXT5"); break;
case D3DFMT_D16_LOCKABLE:    pstr = (TCHAR*)TEXT("D3DFMT_D16_LOCKABLE"); break;
case D3DFMT_D32:             pstr = (TCHAR*)TEXT("D3DFMT_D32"); break;
case D3DFMT_D15S1:           pstr = (TCHAR*)TEXT("D3DFMT_D15S1"); break;
case D3DFMT_D24S8:           pstr = (TCHAR*)TEXT("D3DFMT_D24S8"); break;
case D3DFMT_D24X8:           pstr = (TCHAR*)TEXT("D3DFMT_D24X8"); break;
case D3DFMT_D24X4S4:         pstr = (TCHAR*)TEXT("D3DFMT_D24X4S4"); break;
case D3DFMT_D16:             pstr = (TCHAR*)TEXT("D3DFMT_D16"); break;
case D3DFMT_L16:             pstr = (TCHAR*)TEXT("D3DFMT_L16"); break;
case D3DFMT_VERTEXDATA:      pstr = (TCHAR*)TEXT("D3DFMT_VERTEXDATA"); break;
case D3DFMT_INDEX16:         pstr = (TCHAR*)TEXT("D3DFMT_INDEX16"); break;
case D3DFMT_INDEX32:         pstr = (TCHAR*)TEXT("D3DFMT_INDEX32"); break;
case D3DFMT_Q16W16V16U16:    pstr = (TCHAR*)TEXT("D3DFMT_Q16W16V16U16"); break;
case D3DFMT_MULTI2_ARGB8:    pstr = (TCHAR*)TEXT("D3DFMT_MULTI2_ARGB8"); break;
case D3DFMT_R16F:            pstr = (TCHAR*)TEXT("D3DFMT_R16F"); break;
case D3DFMT_G16R16F:         pstr = (TCHAR*)TEXT("D3DFMT_G16R16F"); break;
case D3DFMT_A16B16G16R16F:   pstr = (TCHAR*)TEXT("D3DFMT_A16B16G16R16F"); break;
case D3DFMT_R32F:            pstr = (TCHAR*)TEXT("D3DFMT_R32F"); break;
case D3DFMT_G32R32F:         pstr = (TCHAR*)TEXT("D3DFMT_G32R32F"); break;
case D3DFMT_A32B32G32R32F:   pstr = (TCHAR*)TEXT("D3DFMT_A32B32G32R32F"); break;
case D3DFMT_CxV8U8:          pstr = (TCHAR*)TEXT("D3DFMT_CxV8U8"); break;
default:                     pstr = (TCHAR*)TEXT("Unknown format"); break;
}
if (bWithPrefix || _tcsstr(pstr, TEXT("D3DFMT_")) == NULL)
return pstr;
else
return pstr + lstrlen(TEXT("D3DFMT_"));
}

//-----------------------------------------------------------------------------
// Name: D3DXQuaternionUnitAxisToUnitAxis2
// Desc: Axis to axis quaternion double angle (no normalization)
//       Takes two points on unit sphere an angle THETA apart, returns
//       quaternion that represents a rotation around cross product by 2*THETA.
//-----------------------------------------------------------------------------
inline D3DXQUATERNION* WINAPI D3DXQuaternionUnitAxisToUnitAxis2
(D3DXQUATERNION* pOut, const D3DXVECTOR3* pvFrom, const D3DXVECTOR3* pvTo)
{
D3DXVECTOR3 vAxis;
D3DXVec3Cross(&vAxis, pvFrom, pvTo);    // proportional to sin(theta)
pOut->x = vAxis.x;
pOut->y = vAxis.y;
pOut->z = vAxis.z;
pOut->w = D3DXVec3Dot(pvFrom, pvTo);
return pOut;
}

//-----------------------------------------------------------------------------
// Name: D3DXQuaternionAxisToAxis
// Desc: Axis to axis quaternion
//       Takes two points on unit sphere an angle THETA apart, returns
//       quaternion that represents a rotation around cross product by theta.
//-----------------------------------------------------------------------------
inline D3DXQUATERNION* WINAPI D3DXQuaternionAxisToAxis
(D3DXQUATERNION* pOut, const D3DXVECTOR3* pvFrom, const D3DXVECTOR3* pvTo)
{
D3DXVECTOR3 vA, vB;
D3DXVec3Normalize(&vA, pvFrom);
D3DXVec3Normalize(&vB, pvTo);
D3DXVECTOR3 vHalf(vA + vB);
D3DXVec3Normalize(&vHalf, &vHalf);
return D3DXQuaternionUnitAxisToUnitAxis2(pOut, &vA, &vHalf);
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CD3DArcBall::CD3DArcBall()
{
Init();
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CD3DArcBall::Init()
{
D3DXQuaternionIdentity(&m_qDown);
D3DXQuaternionIdentity(&m_qNow);
D3DXMatrixIdentity(&m_matRotation);
D3DXMatrixIdentity(&m_matRotationDelta);
D3DXMatrixIdentity(&m_matTranslation);
D3DXMatrixIdentity(&m_matTranslationDelta);
m_bDrag = FALSE;
m_bRightHanded = FALSE;
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
VOID CD3DArcBall::SetWindow(int iWidth, int iHeight, float fRadius)
{
// Set ArcBall info
m_iWidth = iWidth;
m_iHeight = iHeight;
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
D3DXVECTOR3 CD3DArcBall::ScreenToVector(int sx, int sy)
{
// Scale to screen
FLOAT x = -(sx - m_iWidth / 2) / (m_fRadius * m_iWidth / 2);
FLOAT y = (sy - m_iHeight / 2) / (m_fRadius * m_iHeight / 2);

if (m_bRightHanded)
{
x = -x;
y = -y;
}

FLOAT z = 0.0f;
FLOAT mag = x * x + y * y;

if (mag > 1.0f)
{
FLOAT scale = 1.0f / sqrtf(mag);
x *= scale;
y *= scale;
}
else
z = sqrtf(1.0f - mag);

// Return vector
return D3DXVECTOR3(x, y, z);
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
{
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
LRESULT CD3DArcBall::HandleMouseMessages(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
UNREFERENCED_PARAMETER(hWnd);

static int         iCurMouseX;      // Saved mouse position
static int         iCurMouseY;
static D3DXVECTOR3 s_vDown;         // Button down vector

// Current mouse position
int iMouseX = GET_X_LPARAM(lParam);
int iMouseY = GET_Y_LPARAM(lParam);

switch (uMsg)
{
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
// Store off the position of the cursor when the button is pressed
iCurMouseX = iMouseX;
iCurMouseY = iMouseY;
return TRUE;

case WM_LBUTTONDOWN:
// Start drag mode
m_bDrag = TRUE;
s_vDown = ScreenToVector(iMouseX, iMouseY);
m_qDown = m_qNow;
return TRUE;

case WM_LBUTTONUP:
// End drag mode
m_bDrag = FALSE;
return TRUE;

case WM_MOUSEMOVE:
// Drag object
if (MK_LBUTTON & wParam)
{
if (m_bDrag)
{
// recompute m_qNow
D3DXVECTOR3 vCur = ScreenToVector(iMouseX, iMouseY);
D3DXQUATERNION qAxisToAxis;
D3DXQuaternionAxisToAxis(&qAxisToAxis, &s_vDown, &vCur);
m_qNow = m_qDown;
m_qNow *= qAxisToAxis;
D3DXMatrixRotationQuaternion(&m_matRotationDelta, &qAxisToAxis);
}
else
D3DXMatrixIdentity(&m_matRotationDelta);
D3DXMatrixRotationQuaternion(&m_matRotation, &m_qNow);
m_bDrag = TRUE;
}
else if ((MK_RBUTTON & wParam) || (MK_MBUTTON & wParam))
{
// Normalize based on size of window and bounding sphere radius
FLOAT fDeltaX = (iCurMouseX - iMouseX) * m_fRadiusTranslation / m_iWidth;
FLOAT fDeltaY = (iCurMouseY - iMouseY) * m_fRadiusTranslation / m_iHeight;

if (wParam & MK_RBUTTON)
{
D3DXMatrixTranslation(&m_matTranslationDelta, -2 * fDeltaX, 2 * fDeltaY, 0.0f);
D3DXMatrixMultiply(&m_matTranslation, &m_matTranslation, &m_matTranslationDelta);
}
else  // wParam & MK_MBUTTON
{
D3DXMatrixTranslation(&m_matTranslationDelta, 0.0f, 0.0f, 5 * fDeltaY);
D3DXMatrixMultiply(&m_matTranslation, &m_matTranslation, &m_matTranslationDelta);
}

// Store mouse coordinate
iCurMouseX = iMouseX;
iCurMouseY = iMouseY;
}
return TRUE;
}

return FALSE;
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CD3DCamera::CD3DCamera()
{
// Set attributes for the view matrix
D3DXVECTOR3 vEyePt(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 1.0f);
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
SetViewParams(vEyePt, vLookatPt, vUpVec);

// Set attributes for the projection matrix
SetProjParams(D3DX_PI / 4, 1.0f, 1.0f, 1000.0f);
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
VOID CD3DCamera::SetViewParams(D3DXVECTOR3& vEyePt, D3DXVECTOR3& vLookatPt,
D3DXVECTOR3& vUpVec)
{
// Set attributes for the view matrix
m_vEyePt = vEyePt;
m_vLookatPt = vLookatPt;
m_vUpVec = vUpVec;
D3DXVECTOR3 vDir = m_vLookatPt - m_vEyePt;
D3DXVec3Normalize(&m_vView, &vDir);
D3DXVec3Cross(&m_vCross, &m_vView, &m_vUpVec);

D3DXMatrixLookAtLH(&m_matView, &m_vEyePt, &m_vLookatPt, &m_vUpVec);
D3DXMatrixInverse(&m_matBillboard, NULL, &m_matView);
m_matBillboard._41 = 0.0f;
m_matBillboard._42 = 0.0f;
m_matBillboard._43 = 0.0f;
}

//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
VOID CD3DCamera::SetProjParams(FLOAT fFOV, FLOAT fAspect, FLOAT fNearPlane,
FLOAT fFarPlane)
{
// Set attributes for the projection matrix
m_fFOV = fFOV;
m_fAspect = fAspect;
m_fNearPlane = fNearPlane;
m_fFarPlane = fFarPlane;

D3DXMatrixPerspectiveFovLH(&m_matProj, fFOV, fAspect, fNearPlane, fFarPlane);
}

dxutil.h

#pragma once
//-----------------------------------------------------------------------------
// Miscellaneous helper functions
//-----------------------------------------------------------------------------
#define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
#define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }

#ifndef UNDER_CE
//-----------------------------------------------------------------------------
// Name: DXUtil_GetDXSDKMediaPath() and DXUtil_FindMediaFile()
// Desc: Returns the DirectX SDK path, as stored in the system registry
//       during the SDK install.
//-----------------------------------------------------------------------------
HRESULT DXUtil_GetDXSDKMediaPathCch(TCHAR* strDest, int cchDest);
HRESULT DXUtil_GetDXSDKMediaPathCb(TCHAR* szDest, int cbDest);
HRESULT DXUtil_FindMediaFileCch(TCHAR* strDestPath, int cchDest, TCHAR* strFilename);
HRESULT DXUtil_FindMediaFileCb(TCHAR* szDestPath, int cbDest, TCHAR* strFilename);
#endif // !UNDER_CE

//-----------------------------------------------------------------------------
// Desc: Helper functions to read/write a string registry key
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteStringRegKey(HKEY hKey, TCHAR* strRegName, TCHAR* strValue);
HRESULT DXUtil_WriteIntRegKey(HKEY hKey, TCHAR* strRegName, DWORD dwValue);
HRESULT DXUtil_WriteGuidRegKey(HKEY hKey, TCHAR* strRegName, GUID guidValue);
HRESULT DXUtil_WriteBoolRegKey(HKEY hKey, TCHAR* strRegName, BOOL bValue);

HRESULT DXUtil_ReadStringRegKeyCch(HKEY hKey, TCHAR* strRegName, TCHAR* strDest, DWORD cchDest, TCHAR* strDefault);
HRESULT DXUtil_ReadStringRegKeyCb(HKEY hKey, TCHAR* strRegName, TCHAR* strDest, DWORD cbDest, TCHAR* strDefault);
HRESULT DXUtil_ReadIntRegKey(HKEY hKey, TCHAR* strRegName, DWORD* pdwValue, DWORD dwDefault);
HRESULT DXUtil_ReadGuidRegKey(HKEY hKey, TCHAR* strRegName, GUID* pGuidValue, GUID& guidDefault);
HRESULT DXUtil_ReadBoolRegKey(HKEY hKey, TCHAR* strRegName, BOOL* pbValue, BOOL bDefault);

//-----------------------------------------------------------------------------
// Name: DXUtil_Timer()
// Desc: Performs timer opertations. Use the following commands:
//          TIMER_RESET           - to reset the timer
//          TIMER_START           - to start the timer
//          TIMER_STOP            - to stop (or pause) the timer
//          TIMER_GETABSOLUTETIME - to get the absolute system time
//          TIMER_GETAPPTIME      - to get the current time
//          TIMER_GETELAPSEDTIME  - to get the time that elapsed between
//                                  TIMER_GETELAPSEDTIME calls
//-----------------------------------------------------------------------------
enum TIMER_COMMAND {
TIMER_GETABSOLUTETIME, TIMER_GETAPPTIME, TIMER_GETELAPSEDTIME
};
FLOAT __stdcall DXUtil_Timer(TIMER_COMMAND command);

//-----------------------------------------------------------------------------
// UNICODE support for converting between CHAR, TCHAR, and WCHAR strings
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToWideCch(WCHAR* wstrDestination, const CHAR* strSource, int cchDestChar);
HRESULT DXUtil_ConvertWideStringToAnsiCch(CHAR* strDestination, const WCHAR* wstrSource, int cchDestChar);
HRESULT DXUtil_ConvertGenericStringToAnsiCch(CHAR* strDestination, const TCHAR* tstrSource, int cchDestChar);
HRESULT DXUtil_ConvertGenericStringToWideCch(WCHAR* wstrDestination, const TCHAR* tstrSource, int cchDestChar);
HRESULT DXUtil_ConvertAnsiStringToGenericCch(TCHAR* tstrDestination, const CHAR* strSource, int cchDestChar);
HRESULT DXUtil_ConvertWideStringToGenericCch(TCHAR* tstrDestination, const WCHAR* wstrSource, int cchDestChar);
HRESULT DXUtil_ConvertAnsiStringToWideCb(WCHAR* wstrDestination, const CHAR* strSource, int cbDestChar);
HRESULT DXUtil_ConvertWideStringToAnsiCb(CHAR* strDestination, const WCHAR* wstrSource, int cbDestChar);
HRESULT DXUtil_ConvertGenericStringToAnsiCb(CHAR* strDestination, const TCHAR* tstrSource, int cbDestChar);
HRESULT DXUtil_ConvertGenericStringToWideCb(WCHAR* wstrDestination, const TCHAR* tstrSource, int cbDestChar);
HRESULT DXUtil_ConvertAnsiStringToGenericCb(TCHAR* tstrDestination, const CHAR* strSource, int cbDestChar);
HRESULT DXUtil_ConvertWideStringToGenericCb(TCHAR* tstrDestination, const WCHAR* wstrSource, int cbDestChar);

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
VOID DXUtil_LaunchReadme(HWND hWnd, TCHAR* strLoc = NULL);

//-----------------------------------------------------------------------------
// GUID to String converting
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGUIDToStringCch(const GUID* pGuidSrc, TCHAR* strDest, int cchDestChar);
HRESULT DXUtil_ConvertGUIDToStringCb(const GUID* pGuidSrc, TCHAR* strDest, int cbDestChar);
HRESULT DXUtil_ConvertStringToGUID(const TCHAR* strIn, GUID* pGuidOut);

//-----------------------------------------------------------------------------
// Debug printing support
// See dxerr9.h for more debug printing support
//-----------------------------------------------------------------------------
VOID    DXUtil_Trace(TCHAR* strMsg, ...);

#if defined(DEBUG) | defined(_DEBUG)
#define DXTRACE           DXUtil_Trace
#else
#define DXTRACE           sizeof
#endif

//-----------------------------------------------------------------------------
// Name: ArrayListType
// Desc: Indicates how data should be stored in a CArrayList
//-----------------------------------------------------------------------------
enum ArrayListType
{
AL_VALUE,       // entry data is copied into the list
AL_REFERENCE,   // entry pointers are copied into the list
};

//-----------------------------------------------------------------------------
// Name: CArrayList
// Desc: A growable array
//-----------------------------------------------------------------------------
class CArrayList
{
protected:
ArrayListType m_ArrayListType;
void* m_pData;
UINT m_BytesPerEntry;
UINT m_NumEntries;
UINT m_NumEntriesAllocated;

public:
CArrayList(ArrayListType Type, UINT BytesPerEntry = 0);
~CArrayList(void);
void Remove(UINT Entry);
void* GetPtr(UINT Entry);
UINT Count(void) { return m_NumEntries; }
bool Contains(void* pEntryData);
void Clear(void) { m_NumEntries = 0; }
};

//-----------------------------------------------------------------------------
// WinCE build support
//-----------------------------------------------------------------------------

#ifdef UNDER_CE

#define CheckDlgButton(hdialog, id, state) ::SendMessage(::GetDlgItem(hdialog, id), BM_SETCHECK, state, 0)
#define IsDlgButtonChecked(hdialog, id) ::SendMessage(::GetDlgItem(hdialog, id), BM_GETCHECK, 0L, 0L)
#define GETTIMESTAMP GetTickCount
#define _TWINCE(x) _T(x)

__inline int GetScrollPos(HWND hWnd, int nBar)
{
SCROLLINFO si;
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
if (!GetScrollInfo(hWnd, nBar, &si))
{
return 0;
}
else
{
return si.nPos;
}
}

#else // !UNDER_CE

#define GETTIMESTAMP timeGetTime
#define _TWINCE(x) x

#endif // UNDER_CE

dxutil.cpp

//-----------------------------------------------------------------------------
// File: DXUtil.cpp
//
// Desc: Shortcut macros and functions for using DX objects
//
//-----------------------------------------------------------------------------
#ifndef STRICT
#define STRICT
#endif // !STRICT
#include <windows.h>
#include <mmsystem.h>
#include <tchar.h>
#include <stdio.h>
#include <stdarg.h>
#include "DXUtil.h"

#ifdef UNICODE
typedef HINSTANCE(WINAPI* LPShellExecute)(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
#else
typedef HINSTANCE(WINAPI* LPShellExecute)(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
#endif

#ifndef UNDER_CE
//-----------------------------------------------------------------------------
// Name: DXUtil_GetDXSDKMediaPathCch()
// Desc: Returns the DirectX SDK media path
//       cchDest is the size in TCHARs of strDest.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_GetDXSDKMediaPathCch(TCHAR* strDest, int cchDest)
{
if (strDest == NULL || cchDest < 1)
return E_INVALIDARG;

lstrcpy(strDest, TEXT(""));

// Open the appropriate registry key
HKEY  hKey;
LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("Software\\Microsoft\\DirectX SDK"),
if (ERROR_SUCCESS != lResult)
return E_FAIL;

DWORD dwType;
DWORD dwSize = cchDest * sizeof(TCHAR);
lResult = RegQueryValueEx(hKey, _T("DX9SDK Samples Path"), NULL,
&dwType, (BYTE*)strDest, &dwSize);
strDest[cchDest - 1] = 0; // RegQueryValueEx doesn't NULL term if buffer too small
RegCloseKey(hKey);

if (ERROR_SUCCESS != lResult)
return E_FAIL;

const TCHAR* strMedia = _T("\\Media\\");
if (lstrlen(strDest) + lstrlen(strMedia) < cchDest)
_tcscat_s(strDest, cchDest, strMedia);
else
return E_INVALIDARG;

return S_OK;
}
#endif // !UNDER_CE

#ifndef UNDER_CE
//-----------------------------------------------------------------------------
// Name: DXUtil_FindMediaFileCch()
// Desc: Returns a valid path to a DXSDK media file
//       cchDest is the size in TCHARs of strDestPath.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_FindMediaFileCch(TCHAR* strDestPath, int cchDest, TCHAR* strFilename)
{
HRESULT hr;
HANDLE file;
TCHAR* strShortNameTmp = NULL;
TCHAR strShortName[MAX_PATH];
int cchPath;

if (NULL == strFilename || NULL == strDestPath || cchDest < 1)
return E_INVALIDARG;

lstrcpy(strDestPath, TEXT(""));
lstrcpy(strShortName, TEXT(""));

// Build full path name from strFileName (strShortName will be just the leaf filename)
cchPath = GetFullPathName(strFilename, cchDest, strDestPath, &strShortNameTmp);
if ((cchPath == 0) || (cchDest <= cchPath))
return E_FAIL;
if (strShortNameTmp)
lstrcpyn(strShortName, strShortNameTmp, MAX_PATH);

// first try to find the filename given a full path
OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != file)
{
CloseHandle(file);
return S_OK;
}

// next try to find the filename in the current working directory (path stripped)
OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != file)
{
_tcsncpy_s(strDestPath, cchDest, strShortName, cchDest);
strDestPath[cchDest - 1] = 0; // _tcsncpy doesn't NULL term if it runs out of space
CloseHandle(file);
return S_OK;
}

// last, check if the file exists in the media directory
if (FAILED(hr = DXUtil_GetDXSDKMediaPathCch(strDestPath, cchDest)))
return hr;

if (lstrlen(strDestPath) + lstrlen(strShortName) < cchDest)
lstrcat(strDestPath, strShortName);
else
return E_INVALIDARG;

OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != file)
{
CloseHandle(file);
return S_OK;
}

// On failure, just return the file as the path
_tcsncpy_s(strDestPath, cchDest, strFilename, cchDest);
strDestPath[cchDest - 1] = 0; // _tcsncpy doesn't NULL term if it runs out of space
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
#endif // !UNDER_CE

//-----------------------------------------------------------------------------
// Desc: Helper function to read a registry key string
//       cchDest is the size in TCHARs of strDest.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadStringRegKeyCch(HKEY hKey, TCHAR* strRegName, TCHAR* strDest,
DWORD cchDest, TCHAR* strDefault)
{
DWORD dwType;
DWORD cbDest = cchDest * sizeof(TCHAR);

if (ERROR_SUCCESS != RegQueryValueEx(hKey, strRegName, 0, &dwType,
(BYTE*)strDest, &cbDest))
{
_tcsncpy_s(strDest, cchDest, strDefault, cchDest);
strDest[cchDest - 1] = 0;

if (dwType != REG_SZ)
return E_FAIL;

return S_OK;
}

return E_FAIL;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_WriteStringRegKey()
// Desc: Helper function to write a registry key string
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteStringRegKey(HKEY hKey, TCHAR* strRegName,
TCHAR* strValue)
{
if (NULL == strValue)
return E_INVALIDARG;

DWORD cbValue = ((DWORD)_tcslen(strValue) + 1) * sizeof(TCHAR);

if (ERROR_SUCCESS != RegSetValueEx(hKey, strRegName, 0, REG_SZ,
(BYTE*)strValue, cbValue))
return E_FAIL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Desc: Helper function to read a registry key int
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadIntRegKey(HKEY hKey, TCHAR* strRegName, DWORD* pdwDest,
DWORD dwDefault)
{
DWORD dwType;
DWORD dwLength = sizeof(DWORD);

if (ERROR_SUCCESS != RegQueryValueEx(hKey, strRegName, 0, &dwType,
(BYTE*)pdwDest, &dwLength))
{
*pdwDest = dwDefault;
if (dwType != REG_DWORD)
return E_FAIL;

return S_OK;
}

return E_FAIL;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_WriteIntRegKey()
// Desc: Helper function to write a registry key int
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteIntRegKey(HKEY hKey, TCHAR* strRegName, DWORD dwValue)
{
if (ERROR_SUCCESS != RegSetValueEx(hKey, strRegName, 0, REG_DWORD,
(BYTE*)&dwValue, sizeof(DWORD)))
return E_FAIL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Desc: Helper function to read a registry key BOOL
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadBoolRegKey(HKEY hKey, TCHAR* strRegName, BOOL* pbDest,
BOOL bDefault)
{
DWORD dwType;
DWORD dwLength = sizeof(BOOL);

if (ERROR_SUCCESS != RegQueryValueEx(hKey, strRegName, 0, &dwType,
(BYTE*)pbDest, &dwLength))
{
*pbDest = bDefault;
if (dwType != REG_DWORD)
return E_FAIL;

return S_OK;
}

return E_FAIL;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_WriteBoolRegKey()
// Desc: Helper function to write a registry key BOOL
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteBoolRegKey(HKEY hKey, TCHAR* strRegName, BOOL bValue)
{
if (ERROR_SUCCESS != RegSetValueEx(hKey, strRegName, 0, REG_DWORD,
(BYTE*)&bValue, sizeof(BOOL)))
return E_FAIL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Desc: Helper function to read a registry key guid
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadGuidRegKey(HKEY hKey, TCHAR* strRegName, GUID* pGuidDest,
GUID& guidDefault)
{
DWORD dwType;
DWORD dwLength = sizeof(GUID);

if (ERROR_SUCCESS != RegQueryValueEx(hKey, strRegName, 0, &dwType,
(LPBYTE)pGuidDest, &dwLength))
{
*pGuidDest = guidDefault;
if (dwType != REG_BINARY)
return E_FAIL;

return S_OK;
}

return E_FAIL;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_WriteGuidRegKey()
// Desc: Helper function to write a registry key guid
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteGuidRegKey(HKEY hKey, TCHAR* strRegName, GUID guidValue)
{
if (ERROR_SUCCESS != RegSetValueEx(hKey, strRegName, 0, REG_BINARY,
(BYTE*)&guidValue, sizeof(GUID)))
return E_FAIL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_Timer()
// Desc: Performs timer opertations. Use the following commands:
//          TIMER_RESET           - to reset the timer
//          TIMER_START           - to start the timer
//          TIMER_STOP            - to stop (or pause) the timer
//          TIMER_GETABSOLUTETIME - to get the absolute system time
//          TIMER_GETAPPTIME      - to get the current time
//          TIMER_GETELAPSEDTIME  - to get the time that elapsed between
//                                  TIMER_GETELAPSEDTIME calls
//-----------------------------------------------------------------------------
FLOAT __stdcall DXUtil_Timer(TIMER_COMMAND command)
{
static BOOL     m_bTimerInitialized = FALSE;
static BOOL     m_bUsingQPF = FALSE;
static BOOL     m_bTimerStopped = TRUE;
static LONGLONG m_llQPFTicksPerSec = 0;

// Initialize the timer
if (FALSE == m_bTimerInitialized)
{
m_bTimerInitialized = TRUE;

// Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is
// not supported, we will timeGetTime() which returns milliseconds.
LARGE_INTEGER qwTicksPerSec;
m_bUsingQPF = QueryPerformanceFrequency(&qwTicksPerSec);
if (m_bUsingQPF)
}

if (m_bUsingQPF)
{
static LONGLONG m_llStopTime = 0;
static LONGLONG m_llLastElapsedTime = 0;
static LONGLONG m_llBaseTime = 0;
double fTime;
double fElapsedTime;
LARGE_INTEGER qwTime;

// Get either the current time or the stop time, depending
// on whether we're stopped and what command was sent
if (m_llStopTime != 0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
else
QueryPerformanceCounter(&qwTime);

// Return the elapsed time
if (command == TIMER_GETELAPSEDTIME)
{
fElapsedTime = (double)(qwTime.QuadPart - m_llLastElapsedTime) / (double)m_llQPFTicksPerSec;
return (FLOAT)fElapsedTime;
}

// Return the current time
if (command == TIMER_GETAPPTIME)
{
double fAppTime = (double)(qwTime.QuadPart - m_llBaseTime) / (double)m_llQPFTicksPerSec;
return (FLOAT)fAppTime;
}

// Reset the timer
if (command == TIMER_RESET)
{
m_llStopTime = 0;
m_bTimerStopped = FALSE;
return 0.0f;
}

// Start the timer
if (command == TIMER_START)
{
if (m_bTimerStopped)
m_llStopTime = 0;
m_bTimerStopped = FALSE;
return 0.0f;
}

// Stop the timer
if (command == TIMER_STOP)
{
if (!m_bTimerStopped)
{
m_bTimerStopped = TRUE;
}
return 0.0f;
}

// Advance the timer by 1/10th second
{
m_llStopTime += m_llQPFTicksPerSec / 10;
return 0.0f;
}

if (command == TIMER_GETABSOLUTETIME)
{
return (FLOAT)fTime;
}

return -1.0f; // Invalid command specified
}
else
{
// Get the time using timeGetTime()
static double m_fLastElapsedTime = 0.0;
static double m_fBaseTime = 0.0;
static double m_fStopTime = 0.0;
double fTime;
double fElapsedTime;

// Get either the current time or the stop time, depending
// on whether we're stopped and what command was sent
if (m_fStopTime != 0.0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
fTime = m_fStopTime;
else
fTime = GETTIMESTAMP() * 0.001;

// Return the elapsed time
if (command == TIMER_GETELAPSEDTIME)
{
fElapsedTime = (double)(fTime - m_fLastElapsedTime);
m_fLastElapsedTime = fTime;
return (FLOAT)fElapsedTime;
}

// Return the current time
if (command == TIMER_GETAPPTIME)
{
return (FLOAT)(fTime - m_fBaseTime);
}

// Reset the timer
if (command == TIMER_RESET)
{
m_fBaseTime = fTime;
m_fLastElapsedTime = fTime;
m_fStopTime = 0;
m_bTimerStopped = FALSE;
return 0.0f;
}

// Start the timer
if (command == TIMER_START)
{
if (m_bTimerStopped)
m_fBaseTime += fTime - m_fStopTime;
m_fStopTime = 0.0f;
m_fLastElapsedTime = fTime;
m_bTimerStopped = FALSE;
return 0.0f;
}

// Stop the timer
if (command == TIMER_STOP)
{
if (!m_bTimerStopped)
{
m_fStopTime = fTime;
m_fLastElapsedTime = fTime;
m_bTimerStopped = TRUE;
}
return 0.0f;
}

// Advance the timer by 1/10th second
{
m_fStopTime += 0.1f;
return 0.0f;
}

if (command == TIMER_GETABSOLUTETIME)
{
return (FLOAT)fTime;
}

return -1.0f; // Invalid command specified
}
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToWideCch()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       WCHAR string.
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to
//       pass in sizeof(strDest)
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToWideCch(WCHAR* wstrDestination, const CHAR* strSource,
int cchDestChar)
{
if (wstrDestination == NULL || strSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

int nResult = MultiByteToWideChar(CP_ACP, 0, strSource, -1,
wstrDestination, cchDestChar);
wstrDestination[cchDestChar - 1] = 0;

if (nResult == 0)
return E_FAIL;
return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertWideStringToAnsi()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       CHAR string.
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToAnsiCch(CHAR* strDestination, const WCHAR* wstrSource,
int cchDestChar)
{
if (strDestination == NULL || wstrSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

int nResult = WideCharToMultiByte(CP_ACP, 0, wstrSource, -1, strDestination,
cchDestChar * sizeof(CHAR), NULL, NULL);
strDestination[cchDestChar - 1] = 0;

if (nResult == 0)
return E_FAIL;
return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGenericStringToAnsi()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       CHAR string.
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGenericStringToAnsiCch(CHAR* strDestination, const TCHAR* tstrSource,
int cchDestChar)
{
if (strDestination == NULL || tstrSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

#ifdef _UNICODE
return DXUtil_ConvertWideStringToAnsiCch(strDestination, tstrSource, cchDestChar);
#else
strncpy_s(strDestination, cchDestChar, tstrSource, cchDestChar);
strDestination[cchDestChar - 1] = '\0';
return S_OK;
#endif
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGenericStringToWide()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       WCHAR string.
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to
//       pass in sizeof(strDest)
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGenericStringToWideCch(WCHAR* wstrDestination, const TCHAR* tstrSource,
int cchDestChar)
{
if (wstrDestination == NULL || tstrSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

#ifdef _UNICODE
wcsncpy(wstrDestination, tstrSource, cchDestChar);
wstrDestination[cchDestChar - 1] = L'\0';
return S_OK;
#else
return DXUtil_ConvertAnsiStringToWideCch(wstrDestination, tstrSource, cchDestChar);
#endif
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToGeneric()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       TCHAR string.
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToGenericCch(TCHAR* tstrDestination, const CHAR* strSource,
int cchDestChar)
{
if (tstrDestination == NULL || strSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

#ifdef _UNICODE
return DXUtil_ConvertAnsiStringToWideCch(tstrDestination, strSource, cchDestChar);
#else
strncpy_s(tstrDestination, cchDestChar, strSource, cchDestChar);
tstrDestination[cchDestChar - 1] = '\0';
return S_OK;
#endif
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToGeneric()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       TCHAR string.
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToGenericCch(TCHAR* tstrDestination, const WCHAR* wstrSource,
int cchDestChar)
{
if (tstrDestination == NULL || wstrSource == NULL || cchDestChar < 1)
return E_INVALIDARG;

#ifdef _UNICODE
wcsncpy(tstrDestination, wstrSource, cchDestChar);
tstrDestination[cchDestChar - 1] = L'\0';
return S_OK;
#else
return DXUtil_ConvertWideStringToAnsiCch(tstrDestination, wstrSource, cchDestChar);
#endif
}

//-----------------------------------------------------------------------------
// Desc: Finds and opens the readme.txt for this sample
//-----------------------------------------------------------------------------
{

#ifdef UNDER_CE
// This is not available on PocketPC
MessageBox(hWnd, TEXT("For operating instructions, please open the ")
TEXT("readme.txt file included with the project."),
TEXT("DirectX SDK Sample"), MB_ICONWARNING | MB_OK);

return;
#else

bool bSuccess = false;
bool bFound = false;
TCHAR strExeName[MAX_PATH];
TCHAR strExePath[MAX_PATH];
TCHAR strSamplePath[MAX_PATH];
TCHAR* strLastSlash = NULL;

lstrcpy(strExePath, TEXT(""));
lstrcpy(strExeName, TEXT(""));
lstrcpy(strSamplePath, TEXT(""));

// If the user provided a location for the readme, check there first.
if (strLoc)
{
HKEY  hKey;
LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("Software\\Microsoft\\DirectX SDK"),
if (ERROR_SUCCESS == lResult)
{
DWORD dwType;
DWORD dwSize = MAX_PATH * sizeof(TCHAR);
lResult = RegQueryValueEx(hKey, _T("DX9SDK Samples Path"), NULL,
&dwType, (BYTE*)strSamplePath, &dwSize);
strSamplePath[MAX_PATH - 1] = 0; // RegQueryValueEx doesn't NULL term if buffer too small

if (ERROR_SUCCESS == lResult)
{
strSamplePath, strLoc);

bFound = TRUE;
}
}

RegCloseKey(hKey);
}

// Get the exe name, and exe path
GetModuleFileName(NULL, strExePath, MAX_PATH);
strExePath[MAX_PATH - 1] = 0;

strLastSlash = _tcsrchr(strExePath, TEXT('\\'));
if (strLastSlash)
{
_tcsncpy_s(strExeName, &strLastSlash[1], MAX_PATH);
strExeName[MAX_PATH - 1] = 0;

// Chop the exe name from the exe path
*strLastSlash = 0;

// Chop the .exe from the exe name
strLastSlash = _tcsrchr(strExeName, TEXT('.'));
if (strLastSlash)
*strLastSlash = 0;
}

if (!bFound)
{
// Search in "%EXE_DIR%\..\%EXE_NAME%".  This matchs the DirectX SDK layout

if (strLastSlash)
*strLastSlash = 0;
bFound = TRUE;
}

if (!bFound)
{
// Search in "%EXE_DIR%\"
bFound = TRUE;
}

if (!bFound)
{
// Search in "%EXE_DIR%\.."
if (strLastSlash)
*strLastSlash = 0;
bFound = TRUE;
}

if (!bFound)
{
// Search in "%EXE_DIR%\..\.."
if (strLastSlash)
*strLastSlash = 0;
if (strLastSlash)
*strLastSlash = 0;
bFound = TRUE;
}

if (bFound)
{
// GetProcAddress for ShellExecute, so we don't have to include shell32.lib
// in every project that uses dxutil.cpp
LPShellExecute pShellExecute = NULL;
if (hInstShell32 != NULL)
{
#ifdef UNICODE
#else
#endif
if (pShellExecute != NULL)
{
if (pShellExecute(hWnd, TEXT("open"), strReadmePath, NULL, NULL, SW_SHOW) > (HINSTANCE)32)
bSuccess = true;
}

FreeLibrary(hInstShell32);
}
}

if (!bSuccess)
{
// Tell the user that the readme couldn't be opened
TEXT("DirectX SDK Sample"), MB_ICONWARNING | MB_OK);
}

#endif // UNDER_CE
}

//-----------------------------------------------------------------------------
// Name: DXUtil_Trace()
// Desc: Outputs to the debug stream a formatted string with a variable-
//       argument list.
//-----------------------------------------------------------------------------
VOID DXUtil_Trace(TCHAR* strMsg, ...)
{
#if defined(DEBUG) | defined(_DEBUG)
TCHAR strBuffer[512];

va_list args;
va_start(args, strMsg);
_vsntprintf_s(strBuffer, 512, strMsg, args);
va_end(args);

OutputDebugString(strBuffer);
#else
UNREFERENCED_PARAMETER(strMsg);
#endif
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertStringToGUID()
// Desc: Converts a string to a GUID
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertStringToGUID(const TCHAR* strSrc, GUID* pGuidDest)
{
UINT aiTmp[10];

if (_stscanf_s(strSrc, TEXT("{%8X-%4X-%4X-%2X%2X-%2X%2X%2X%2X%2X%2X}"),
&pGuidDest->Data1,
&aiTmp[0], &aiTmp[1],
&aiTmp[2], &aiTmp[3],
&aiTmp[4], &aiTmp[5],
&aiTmp[6], &aiTmp[7],
&aiTmp[8], &aiTmp[9]) != 11)
{
ZeroMemory(pGuidDest, sizeof(GUID));
return E_FAIL;
}
else
{
pGuidDest->Data2 = (USHORT)aiTmp[0];
pGuidDest->Data3 = (USHORT)aiTmp[1];
pGuidDest->Data4[0] = (BYTE)aiTmp[2];
pGuidDest->Data4[1] = (BYTE)aiTmp[3];
pGuidDest->Data4[2] = (BYTE)aiTmp[4];
pGuidDest->Data4[3] = (BYTE)aiTmp[5];
pGuidDest->Data4[4] = (BYTE)aiTmp[6];
pGuidDest->Data4[5] = (BYTE)aiTmp[7];
pGuidDest->Data4[6] = (BYTE)aiTmp[8];
pGuidDest->Data4[7] = (BYTE)aiTmp[9];
return S_OK;
}
}

//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGUIDToStringCch()
// Desc: Converts a GUID to a string
//       cchDestChar is the size in TCHARs of strDest.  Be careful not to
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGUIDToStringCch(const GUID* pGuidSrc, TCHAR* strDest, int cchDestChar)
{
int nResult = _sntprintf_s(strDest, cchDestChar, cchDestChar, TEXT("{%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}"),
pGuidSrc->Data1, pGuidSrc->Data2, pGuidSrc->Data3,
pGuidSrc->Data4[0], pGuidSrc->Data4[1],
pGuidSrc->Data4[2], pGuidSrc->Data4[3],
pGuidSrc->Data4[4], pGuidSrc->Data4[5],
pGuidSrc->Data4[6], pGuidSrc->Data4[7]);

if (nResult < 0)
return E_FAIL;
return S_OK;
}

//-----------------------------------------------------------------------------
// Name: CArrayList constructor
// Desc:
//-----------------------------------------------------------------------------
CArrayList::CArrayList(ArrayListType Type, UINT BytesPerEntry)
{
if (Type == AL_REFERENCE)
BytesPerEntry = sizeof(void*);
m_ArrayListType = Type;
m_pData = NULL;
m_BytesPerEntry = BytesPerEntry;
m_NumEntries = 0;
m_NumEntriesAllocated = 0;
}

//-----------------------------------------------------------------------------
// Name: CArrayList destructor
// Desc:
//-----------------------------------------------------------------------------
CArrayList::~CArrayList(void)
{
if (m_pData != NULL)
delete[] m_pData;
}

//-----------------------------------------------------------------------------
// Desc: Adds pEntry to the list.
//-----------------------------------------------------------------------------
{
if (m_BytesPerEntry == 0)
return E_FAIL;
if (m_pData == NULL || m_NumEntries + 1 > m_NumEntriesAllocated)
{
void* pDataNew;
UINT NumEntriesAllocatedNew;
if (m_NumEntriesAllocated == 0)
NumEntriesAllocatedNew = 16;
else
NumEntriesAllocatedNew = m_NumEntriesAllocated * 2;
pDataNew = new BYTE[NumEntriesAllocatedNew * m_BytesPerEntry];
if (pDataNew == NULL)
return E_OUTOFMEMORY;
if (m_pData != NULL)
{
CopyMemory(pDataNew, m_pData, m_NumEntries * m_BytesPerEntry);
delete[] m_pData;
}
m_pData = pDataNew;
m_NumEntriesAllocated = NumEntriesAllocatedNew;
}

if (m_ArrayListType == AL_VALUE)
CopyMemory((BYTE*)m_pData + (m_NumEntries * m_BytesPerEntry), pEntry, m_BytesPerEntry);
else
*(((void**)m_pData) + m_NumEntries) = pEntry;
m_NumEntries++;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: CArrayList::Remove
// Desc: Remove the item at Entry in the list, and collapse the array.
//-----------------------------------------------------------------------------
void CArrayList::Remove(UINT Entry)
{
// Decrement count
m_NumEntries--;

BYTE* pData = (BYTE*)m_pData + (Entry * m_BytesPerEntry);

// Collapse the array
MoveMemory(pData, pData + m_BytesPerEntry, (m_NumEntries - Entry) * m_BytesPerEntry);
}

//-----------------------------------------------------------------------------
// Name: CArrayList::GetPtr
// Desc: Returns a pointer to the Entry'th entry in the list.
//-----------------------------------------------------------------------------
void* CArrayList::GetPtr(UINT Entry)
{
if (m_ArrayListType == AL_VALUE)
return (BYTE*)m_pData + (Entry * m_BytesPerEntry);
else
return *(((void**)m_pData) + Entry);
}

//-----------------------------------------------------------------------------
// Name: CArrayList::Contains
// Desc: Returns whether the list contains an entry identical to the
//       specified entry data.
//-----------------------------------------------------------------------------
bool CArrayList::Contains(void* pEntryData)
{
for (UINT iEntry = 0; iEntry < m_NumEntries; iEntry++)
{
if (m_ArrayListType == AL_VALUE)
{
if (memcmp(GetPtr(iEntry), pEntryData, m_BytesPerEntry) == 0)
return true;
}
else
{
if (GetPtr(iEntry) == pEntryData)
return true;
}
}
return false;
}

//-----------------------------------------------------------------------------
// Name: BYTE helper functions
// Desc: cchDestChar is the size in BYTEs of strDest.  Be careful not to
//       pass use sizeof() if the strDest is a string pointer.
//       eg.
//       TCHAR* sz = new TCHAR[100]; // sizeof(sz)  == 4
//       TCHAR sz2[100];             // sizeof(sz2) == 200
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToWideCb(WCHAR* wstrDestination, const CHAR* strSource, int cbDestChar)
{
return DXUtil_ConvertAnsiStringToWideCch(wstrDestination, strSource, cbDestChar / sizeof(WCHAR));
}

HRESULT DXUtil_ConvertWideStringToAnsiCb(CHAR* strDestination, const WCHAR* wstrSource, int cbDestChar)
{
return DXUtil_ConvertWideStringToAnsiCch(strDestination, wstrSource, cbDestChar / sizeof(CHAR));
}

HRESULT DXUtil_ConvertGenericStringToAnsiCb(CHAR* strDestination, const TCHAR* tstrSource, int cbDestChar)
{
return DXUtil_ConvertGenericStringToAnsiCch(strDestination, tstrSource, cbDestChar / sizeof(CHAR));
}

HRESULT DXUtil_ConvertGenericStringToWideCb(WCHAR* wstrDestination, const TCHAR* tstrSource, int cbDestChar)
{
return DXUtil_ConvertGenericStringToWideCch(wstrDestination, tstrSource, cbDestChar / sizeof(WCHAR));
}

HRESULT DXUtil_ConvertAnsiStringToGenericCb(TCHAR* tstrDestination, const CHAR* strSource, int cbDestChar)
{
return DXUtil_ConvertAnsiStringToGenericCch(tstrDestination, strSource, cbDestChar / sizeof(TCHAR));
}

HRESULT DXUtil_ConvertWideStringToGenericCb(TCHAR* tstrDestination, const WCHAR* wstrSource, int cbDestChar)
{
return DXUtil_ConvertWideStringToGenericCch(tstrDestination, wstrSource, cbDestChar / sizeof(TCHAR));
}

HRESULT DXUtil_ReadStringRegKeyCb(HKEY hKey, TCHAR* strRegName, TCHAR* strDest, DWORD cbDest, TCHAR* strDefault)
{
return DXUtil_ReadStringRegKeyCch(hKey, strRegName, strDest, cbDest / sizeof(TCHAR), strDefault);
}

HRESULT DXUtil_ConvertGUIDToStringCb(const GUID* pGuidSrc, TCHAR* strDest, int cbDestChar)
{
return DXUtil_ConvertGUIDToStringCch(pGuidSrc, strDest, cbDestChar / sizeof(TCHAR));
}

#ifndef UNDER_CE
HRESULT DXUtil_GetDXSDKMediaPathCb(TCHAR* szDest, int cbDest)
{
return DXUtil_GetDXSDKMediaPathCch(szDest, cbDest / sizeof(TCHAR));
}

HRESULT DXUtil_FindMediaFileCb(TCHAR* szDestPath, int cbDest, TCHAR* strFilename)
{
return DXUtil_FindMediaFileCch(szDestPath, cbDest / sizeof(TCHAR), strFilename);
}
#endif // !UNDER_CE

fps.h

#pragma once
#include "d3dfont.h"
#include <sstream>

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

};

fps.cpp

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

std::stringstream ss;
//std::string fpsStr;
ss << _fps;
ss >> _fpsString;
//strncpy_s(_fpsString, 9, fpsStr.c_str(), 9);
//sprintf_s(_fpsString, 8, "%f", _fps);
_fpsString[8] = '\0'; // mark end of string

_timeElapsed = 0.0f;
_frameCnt = 0;
}

_font->DrawText(20, 20, color, _fpsString);
}
return true;
}

terrain.h

#pragma once
#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  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;

// helper methods
bool  computeVertices();
bool  computeIndices();
bool  lightTerrain(D3DXVECTOR3* directionToLight);
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;
};
};

terrain.cpp

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

{
::MessageBox(0, "readRawFile - FAILED", 0, 0);
::PostQuitMessage(0);
}

// scale heights
for (int i = 0; i < _heightmap.size(); i++)
_heightmap[i] *= heightScale;

// compute the vertices
if (!computeVertices())
{
::MessageBox(0, "computeVertices - FAILED", 0, 0);
::PostQuitMessage(0);
}

// compute the indices
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;

// coordinates to start generating vertices at
int startX = -_width / 2;
int startZ = _depth / 2;

// coordinates to end generating vertices at
int endX = _width / 2;
int endZ = -_depth / 2;

// compute the increment size of the texture coordinates
// from one vertex to the next.
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)
{
// compute the correct index into the vertex buffer and heightmap
// based on where we are in the nested loop.
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);

// index to start of a group of 6 indices that describe the
// two triangles that make up a quad
int baseIndex = 0;

// loop through and compute the triangles of each quad
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;

baseIndex += 6;
}
}

_ib->Unlock();

return true;
}

{
HRESULT hr = 0;

hr = D3DXCreateTextureFromFile(
_device,
fileName.c_str(),
&_tex);

if (FAILED(hr))
return false;

return true;
}

bool Terrain::genTexture(D3DXVECTOR3* directionToLight)
{
// Method fills the top surface of a texture procedurally.  Then
// lights the top surface.  Finally, it fills the other mipmap
// surfaces based on the top surface data using D3DXFilterTexture.

HRESULT hr = 0;

// texel for each quad cell
int texWidth = _numCellsPerRow;
int texHeight = _numCellsPerCol;

// create an empty texture
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);

// make sure we got the requested format because our code
// that fills the texture is hard coded to a 32 bit pixel depth.
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;

// get height of upper left vertex of quad.
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;

// fill locked data, note we divide the pitch by four because the
// pitch is given in bytes and there are 4 bytes per DWORD.
imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;
}
}

_tex->UnlockRect(0);

if (!lightTerrain(directionToLight))
{
::MessageBox(0, "lightTerrain() - FAILED", 0, 0);
return false;
}

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

bool Terrain::lightTerrain(D3DXVECTOR3* directionToLight)
{
HRESULT hr = 0;

D3DSURFACE_DESC textureDesc;
_tex->GetLevelDesc(0 /*level*/, &textureDesc);

// make sure we got the requested format because our code that fills the
// texture is hard coded to a 32 bit pixel depth.
if (textureDesc.Format != D3DFMT_X8R8G8B8)
return false;

D3DLOCKED_RECT lockedRect;
_tex->LockRect(
0,          // lock top surface level in mipmap chain
&lockedRect,// pointer to receive locked data
0,          // lock entire texture image
0);         // no lock flags specified

DWORD* imageData = (DWORD*)lockedRect.pBits;
for (int i = 0; i < textureDesc.Height; i++)
{
for (int j = 0; j < textureDesc.Width; j++)
{
// index into texture, note we use the pitch and divide by
// four since the pitch is given in bytes and there are
// 4 bytes per DWORD.
int index = i * lockedRect.Pitch / 4 + j;

// get current color of quad
D3DXCOLOR c(imageData[index]);

imageData[index] = (D3DCOLOR)c;
}
}

_tex->UnlockRect(0);

return true;
}

float Terrain::computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight)
{
// get heights of three vertices on the quad
float heightA = getHeightmapEntry(cellRow, cellCol);
float heightB = getHeightmapEntry(cellRow, cellCol + 1);
float heightC = getHeightmapEntry(cellRow + 1, cellCol);

// build two vectors on the quad
D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
D3DXVECTOR3 v(0.0f, heightC - heightA, -_cellSpacing);

// find the normal by taking the cross product of two
D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);

float cosine = D3DXVec3Dot(&n, directionToLight);

if (cosine < 0.0f)
cosine = 0.0f;

return cosine;
}

{
// Restriction: RAW file dimensions must be >= to the
// dimensions of the terrain.  That is a 128x128 RAW file
// can only be used with a terrain constructed with at most
// 128x128 vertices.

// A height for each vertex
std::vector<BYTE> in(_numVertices);

std::ifstream inFile(fileName.c_str(), std::ios_base::binary);

/*if (inFile.is_open())
return false;*/

(char*)&in[0], // buffer
in.size());// number of bytes to read into buffer

inFile.close();

// copy BYTE vector to int 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)
{
// Translate on xz-plane by the transformation that takes
// the terrain START point to the origin.
x = ((float)_width / 2.0f) + x;
z = ((float)_depth / 2.0f) - z;

// Scale down by the transformation that makes the
// cellspacing equal to one.  This is given by
// 1 / cellspacing since; cellspacing * 1 / cellspacing = 1.
x /= (float)_cellSpacing;
z /= (float)_cellSpacing;

// From now on, we will interpret our positive z-axis as
// going in the 'down' direction, rather than the 'up' direction.
// This allows to extract the row and column simply by 'flooring'
// x and z:

float col = ::floorf(x);
float row = ::floorf(z);

// get the heights of the quad we're in:
//
//  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);

//
// Find the triangle we are in:
//

// Translate by the transformation that takes the upper-left
// corner of the cell we are in to the origin.  Recall that our
// cellspacing was nomalized to 1.  Thus we have a unit square
// at the origin of our +x -> 'right' and +z -> 'down' system.
float dx = x - col;
float dz = z - row;

// Note the below compuations of u and v are unneccessary, we really
// only need the height, but we compute the entire vector to emphasis
// the books discussion.
float height = 0.0f;
if (dz < 1.0f - dx)  // upper triangle ABC
{
float uy = B - A; // A->B
float vy = C - A; // A->C

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {A}, plus the heights
// found by interpolating on each vector u and v.
height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
}
else // lower triangle DCB
{
float uy = C - D; // D->C
float vy = B - D; // D->B

// Linearly interpolate on each vector.  The height is the vertex
// height the vectors u and v originate from {D}, plus the heights
// found by interpolating on each vector u and v.
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);

// turn off lighting since we're lighting it ourselves
_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;
}

camera.h

#pragma once
#include <d3dx9.h>

class Camera
{
public:
enum CameraType { LANDOBJECT, AIRCRAFT };
Camera();
Camera(CameraType cameraType);
~Camera();

void strafe(float units);	//left/right
void fly(float units);	//up/down
void walk(float units);	//forward/backward

void pitch(float angle);	//rotate on right vector
void yaw(float angle);	//rotate on up vector
void roll(float angle);	//rotate on look vector

void getViewMatrix(D3DXMATRIX* V);
void setCameraType(CameraType cameraType);
void getPosition(D3DXVECTOR3* pos);
void setPosition(D3DXVECTOR3* pos);

void getRight(D3DXVECTOR3* right);
void getUp(D3DXVECTOR3* up);
void getLook(D3DXVECTOR3* look);

private:
CameraType _cameraType;
D3DXVECTOR3 _right;
D3DXVECTOR3 _up;
D3DXVECTOR3 _look;
D3DXVECTOR3 _pos;
};

camera.cpp

#include "camera.h"

Camera::Camera()
{
_cameraType = LANDOBJECT;

_pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
_look = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
}

Camera::Camera(CameraType cameraType)
{
_cameraType = cameraType;

_pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
_look = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
}

Camera::~Camera()
{

}

void Camera::getPosition(D3DXVECTOR3* pos)
{
*pos = _pos;
}

void Camera::setPosition(D3DXVECTOR3* pos)
{
_pos = *pos;
}

void Camera::getRight(D3DXVECTOR3* right)
{
*right = _right;
}

void Camera::getUp(D3DXVECTOR3* up)
{
*up = _up;
}

void Camera::getLook(D3DXVECTOR3* look)
{
*look = _look;
}

void Camera::walk(float units)
{
if (_cameraType == LANDOBJECT)
_pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;
if (_cameraType == AIRCRAFT)
_pos += _look * units;
}

void Camera::strafe(float units)
{
if (_cameraType == LANDOBJECT)
_pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;
if (_cameraType == AIRCRAFT)
_pos += _right * units;
}

void Camera::fly(float units)
{
if (_cameraType == LANDOBJECT)
_pos.y += units;
if (_cameraType == AIRCRAFT)
_pos += _up * units;
}

void Camera::pitch(float angle)
{
D3DXMATRIX T;
D3DXMatrixRotationAxis(&T, &_right, angle);

D3DXVec3TransformCoord(&_up, &_up, &T);
D3DXVec3TransformCoord(&_look, &_look, &T);
}

void Camera::yaw(float angle)
{
D3DXMATRIX T;
if (_cameraType == LANDOBJECT)
D3DXMatrixRotationY(&T, angle);
if (_cameraType == AIRCRAFT)
D3DXMatrixRotationAxis(&T, &_up, angle);

D3DXVec3TransformCoord(&_right, &_right, &T);
D3DXVec3TransformCoord(&_look, &_look, &T);
}

void Camera::roll(float angle)
{
if (_cameraType == AIRCRAFT)
{
D3DXMATRIX T;
D3DXMatrixRotationAxis(&T, &_look, angle);

D3DXVec3TransformCoord(&_up, &_up, &T);
D3DXVec3TransformCoord(&_right, &_right, &T);
}
}

void Camera::getViewMatrix(D3DXMATRIX* V)
{
//保持相机轴彼此正交
D3DXVec3Normalize(&_look, &_look);

D3DXVec3Cross(&_up, &_look, &_right);
D3DXVec3Normalize(&_up, &_up);

D3DXVec3Cross(&_right, &_up, &_look);
D3DXVec3Normalize(&_right, &_right);

//构建视图矩阵
float x = -D3DXVec3Dot(&_right, &_pos);
float y = -D3DXVec3Dot(&_up, &_pos);
float z = -D3DXVec3Dot(&_look, &_pos);

//视图矩阵=平移矩阵*旋转矩阵
(*V)(0, 0) = _right.x;	(*V)(0, 1) = _up.x;	(*V)(0, 2) = _look.x;	(*V)(0, 3) = 0.0f;
(*V)(1, 0) = _right.y;	(*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y;	(*V)(1, 3) = 0.0f;
(*V)(2, 0) = _right.z;	(*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z;	(*V)(2, 3) = 0.0f;
(*V)(3, 0) = x;			(*V)(3, 1) = y;		(*V)(3, 2) = z;			(*V)(3, 3) = 1.0f;
}

void Camera::setCameraType(CameraType cameraType)
{
_cameraType = cameraType;
}

d3dutil.h

#pragma once
#include <d3d9.h>
#include <d3dx9math.h>

namespace d3d
{
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device);
int EnterMsgLoop(bool (*ptr_display)(float timeDelta));
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

template<class T> void Release(T t)
{
if (t)
{
t->Release();
t = nullptr;
}
}

template<class T> void Delete(T t)
{
if (t)
{
delete t;
t = nullptr;
}
}

const D3DXCOLOR WHITE(D3DCOLOR_XRGB(255, 255, 255));
const D3DXCOLOR BLACK(D3DCOLOR_XRGB(0, 0, 0));
const D3DXCOLOR RED(D3DCOLOR_XRGB(255, 0, 0));
const D3DXCOLOR GREEN(D3DCOLOR_XRGB(0, 255, 0));
const D3DXCOLOR BLUE(D3DCOLOR_XRGB(0, 0, 255));
const D3DXCOLOR YELLOW(D3DCOLOR_XRGB(255, 255, 0));
const D3DXCOLOR CYAN(D3DCOLOR_XRGB(0, 255, 255));
const D3DXCOLOR MAGENTA(D3DCOLOR_XRGB(255, 0, 255));

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

//材质
D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);
const D3DMATERIAL9 WHITE_MTRL = InitMtrl(WHITE, WHITE, WHITE, BLACK, 8.0f);
const D3DMATERIAL9 RED_MTRL = InitMtrl(RED, RED, RED, BLACK, 8.0f);
const D3DMATERIAL9 GREEN_MTRL = InitMtrl(GREEN, GREEN, GREEN, BLACK, 8.0f);
const D3DMATERIAL9 BLUE_MTRL = InitMtrl(BLUE, BLUE, BLUE, BLACK, 8.0f);
const D3DMATERIAL9 YELLOW_MTRL = InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 8.0f);

//光源
D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);
//D3DLIGHT9 InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color);
//D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);

// Function references "desert.bmp" internally.  This file must
// be in the working directory.
bool DrawBasicScene(
IDirect3DDevice9* device,// Pass in 0 for cleanup.
float scale);            // uniform scale

struct Vertex
{
Vertex() {}
Vertex(float x, float y, float z,
float nx, float ny, float nz,
float u, float v)
{
_x = x;  _y = y;  _z = z;
_nx = nx; _ny = ny; _nz = nz;
_u = u;  _v = v;
}
float _x, _y, _z;
float _nx, _ny, _nz;
float _u, _v;

static const DWORD FVF;
};

//
// Randomness
//

// Desc: Return random float in [lowBound, highBound] interval.
float GetRandomFloat(float lowBound, float highBound);

// Desc: Returns a random vector in the bounds specified by min and max.
void GetRandomVector(
D3DXVECTOR3* out,
D3DXVECTOR3* min,
D3DXVECTOR3* max);

//
// Conversion
//
DWORD FtoDw(float f);

//
// Interpolation
//

float Lerp(float a, float b, float t);
}

d3dutil.cpp

#include "d3dUtility.h"

// vertex formats
const DWORD d3d::Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;

bool d3d::InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device)
{
//注册wndclass
WNDCLASS wndclass;
wndclass.style = CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszClassName = TEXT("d3d");

if (!RegisterClass(&wndclass))
{
MessageBox(nullptr, TEXT("RegisterClass() - FAILED"), TEXT("ERROR"), MB_ICONERROR);
return false;
}

//创建窗口
HWND hwnd = CreateWindow(TEXT("d3d"), TEXT("d3d window"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height,
nullptr, nullptr, hInstance, nullptr);

//设备列举和创建IDirect3DDevice9对象
IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

//检测硬件顶点处理
D3DCAPS9 caps;
int vp = 0;
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;	//支持硬件顶点处理
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;	//不支持

D3DDISPLAYMODE d3ddm;
return false;

//填充D3DPRESENT_PARAMETERS结构
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
//d3dpp.BackBufferFormat = D3DFMT_A8B8G8R8;//像素格式
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = windowed;//full screen
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;//depth format
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

//创建IDirect3DDevice9对象
HRESULT hr = d3d9->CreateDevice(D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device);
if (FAILED(hr))
{
MessageBox(nullptr, TEXT("CraeteDevice() - FAILED"), TEXT("ERROR"), MB_ICONERROR);
return false;
}

d3d9->Release();

return true;
}

int d3d::EnterMsgLoop(bool(*ptr_display)(float timeDelta))
{
MSG msg;
memset(&msg, 0, sizeof(msg));
static float lastTime = (float)timeGetTime();

while (true)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}

LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

D3DMATERIAL9 d3d::InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p)
{
D3DMATERIAL9 mtrl;

mtrl.Ambient = a;	//环境光
mtrl.Diffuse = d;	//漫反射
mtrl.Specular = s;	//镜面反射
mtrl.Emissive = e;	//表面添加颜色
mtrl.Power = p;		//镜面高光

return mtrl;
}

D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
ZeroMemory(&light, sizeof(light));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse = *color;
light.Ambient = *color * 0.3f;
light.Specular = *color * 0.6f;
light.Direction = *direction;

return light;
}

bool d3d::DrawBasicScene(IDirect3DDevice9* device, float scale)
{
static IDirect3DVertexBuffer9* floor = nullptr;
static IDirect3DTexture9* tex = nullptr;
static ID3DXMesh* pillar = nullptr;

HRESULT hr = 0;
if (device == nullptr)
{
if (floor && tex && pillar)
{
d3d::Release<IDirect3DVertexBuffer9*>(floor);
d3d::Release<IDirect3DTexture9*>(tex);
d3d::Release<ID3DXMesh*>(pillar);
}
}
else if (!floor && !tex && !pillar)
{
device->CreateVertexBuffer(
6 * sizeof(d3d::Vertex),
0,
d3d::Vertex::FVF,
D3DPOOL_MANAGED,
&floor,
0);
Vertex* v = 0;
floor->Lock(0, 0, (void**)&v, 0);

v[0] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[1] = Vertex(-20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
v[2] = Vertex(20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

v[3] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[4] = Vertex(20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[5] = Vertex(20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);

floor->Unlock();

//使用左手坐标系创建包含缸的网格
D3DXCreateCylinder(device, 0.5f, 0.5f, 5.0f, 20, 20, &pillar, nullptr);

D3DXCreateTextureFromFile(device, TEXT("desert.bmp"), &tex);
}
else
{
//设置过滤器
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

//设置光源
D3DXVECTOR3 dir(0.707f, -0.707f, 0.707f);
D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);
D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);

device->SetLight(0, &light);
device->LightEnable(0, true);
device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
device->SetRenderState(D3DRS_SPECULARENABLE, true);

//渲染
D3DXMATRIX T, R, P, S;

//生成沿x、y、z轴缩放的矩阵
D3DXMatrixScaling(&S, scale, scale, scale);

//用于旋转圆柱体以与世界y轴平行
D3DXMatrixRotationX(&R, -D3DX_PI * 0.5f);

//draw floor
D3DXMatrixIdentity(&T);
T = T * S;
device->SetTransform(D3DTS_WORLD, &T);
device->SetMaterial(&d3d::WHITE_MTRL);
device->SetTexture(0, tex);
device->SetStreamSource(0, floor, 0, sizeof(Vertex));
device->SetFVF(Vertex::FVF);
device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

//draw pillars
device->SetMaterial(&d3d::BLUE_MTRL);
device->SetTexture(0, 0);
for (int i = 0; i < 5; i++)
{
D3DXMatrixTranslation(&T, -5.0f, 0.0f, -15.0f + (i * 7.5f));
P = R * T * S;
device->SetTransform(D3DTS_WORLD, &P);
pillar->DrawSubset(0);

D3DXMatrixTranslation(&T, 5.0f, 0.0f, -15.0f + (i * 7.5f));
P = R * T * S;
device->SetTransform(D3DTS_WORLD, &P);
pillar->DrawSubset(0);
}
}
return true;
}

float d3d::GetRandomFloat(float lowBound, float highBound)
{
if (lowBound >= highBound) // bad input
return lowBound;

// get random float in [0, 1] interval
float f = (rand() % 10000) * 0.0001f;

// return float in [lowBound, highBound] interval.
return (f * (highBound - lowBound)) + lowBound;
}

void d3d::GetRandomVector(
D3DXVECTOR3* out,
D3DXVECTOR3* min,
D3DXVECTOR3* max)
{
out->x = GetRandomFloat(min->x, max->x);
out->y = GetRandomFloat(min->y, max->y);
out->z = GetRandomFloat(min->z, max->z);
}

DWORD d3d::FtoDw(float f)
{
return *((DWORD*)&f);
}

float d3d::Lerp(float a, float b, float t)
{
return a - (a * t) + (b * t);
}

terrainDriver.cpp

#include "d3dUtility.h"
#include "terrain.h"
#include "camera.h"
#include "fps.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "winmm.lib")

#define KEY_DOWN(vk_code) (GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0)

//
// Globals
//

IDirect3DDevice9* device = 0;

const int Width = 640;
const int Height = 480;

Terrain* TheTerrain = 0;
Camera   TheCamera(Camera::LANDOBJECT);

FPSCounter* FPS = 0;

//
// Framework Functions
//
bool setup()
{
//
// Create the terrain.
//

D3DXVECTOR3 lightDirection(0.0f, 1.0f, 0.0f);
TheTerrain = new Terrain(device, "coastMountain64.raw", 64, 64, 10, 0.5f);
TheTerrain->genTexture(&lightDirection);

//
// Create the font.
//

FPS = new FPSCounter(device);

//
// Set texture filters.
//

device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

//
// Set projection matrix.
//

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.25f, // 45 - degree
(float)Width / (float)Height,
1.0f,
1000.0f);
device->SetTransform(D3DTS_PROJECTION, &proj);

return true;
}

void cleanup()
{
d3d::Delete<Terrain*>(TheTerrain);
d3d::Delete<FPSCounter*>(FPS);
}

bool display(float timeDelta)
{
//
// Update the scene:
//

if (device)
{
if (KEY_DOWN(VK_UP))
TheCamera.walk(100.0f * timeDelta);

if (KEY_DOWN(VK_DOWN))
TheCamera.walk(-100.0f * timeDelta);

if (KEY_DOWN(VK_LEFT))
TheCamera.yaw(-1.0f * timeDelta);

if (KEY_DOWN(VK_RIGHT))
TheCamera.yaw(1.0f * timeDelta);

if (KEY_DOWN('N'))
TheCamera.strafe(-100.0f * timeDelta);

if (KEY_DOWN('M'))
TheCamera.strafe(100.0f * timeDelta);

if (KEY_DOWN('W'))
TheCamera.pitch(1.0f * timeDelta);

if (KEY_DOWN('S'))
TheCamera.pitch(-1.0f * timeDelta);

D3DXVECTOR3 pos;
TheCamera.getPosition(&pos);
float height = TheTerrain->getHeight(pos.x, pos.z);
pos.y = height + 5.0f; // add height because we're standing up
TheCamera.setPosition(&pos);

D3DXMATRIX V;
TheCamera.getViewMatrix(&V);
device->SetTransform(D3DTS_VIEW, &V);

//
// Draw the scene:
//

device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0);
device->BeginScene();

D3DXMATRIX I;
D3DXMatrixIdentity(&I);

if (TheTerrain)
TheTerrain->draw(&I, false);

if (FPS)
FPS->render(0xffffffff, timeDelta);

device->EndScene();
device->Present(0, 0, 0, 0);
}
return true;
}

//
// WinMain
//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
if (!d3d::InitD3D(hInstance, Width, Height, true, D3DDEVTYPE_HAL, &device))
{
MessageBox(nullptr, TEXT("InitD3D() - FAILED"), TEXT("ERROR"), MB_ICONERROR);
return 0;
}

if (!setup())
{
MessageBox(nullptr, TEXT("setup() - FAILED"), TEXT("ERROR"), MB_ICONERROR);
return 0;
}

d3d::EnterMsgLoop(display);

cleanup();

device->Release();

return 1;
}
06-29
08-13
09-10
11-14
04-04
05-15
03-11
08-02 4256
10-14 5944
04-12 845
04-12 397
04-12 609

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。