dx9.0地形
-
照相机
这里照相机有
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)与世界坐标系的轴对齐
合并视图矩阵=平移矩阵*旋转矩阵 -
地形
1.从raw文件加载高度图readRawFile
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 // vectors on the quad. 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); }
-
原书代码修改部分
这里sprintf_s(_fpsString, 8, “%f”, _fps)的_fps的长度会超过8位,改为stringstream就不用担心这个问题
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;
}
- 运行效果
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
//
// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#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;
ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = (int)m_dwTexWidth;
bmi.bmiHeader.biHeight = -(int)m_dwTexHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biBitCount = 32;
// 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->SetPixelShader(NULL);
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;
// Adjust for character spacing
sx -= m_dwSpacing * (fXScale * vp.Height) / fLineHeight;
FLOAT fStartX = sx;
// Fill vertex buffer
FONT2DVERTEX* pVertices;
DWORD dwNumTriangles = 0L;
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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);
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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->SetPixelShader(NULL);
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;
}
// Adjust for character spacing
sx -= m_dwSpacing;
FLOAT fStartX = sx;
// Fill vertex buffer
FONT2DVERTEX* pVertices = NULL;
DWORD dwNumTriangles = 0;
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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;
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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->SetPixelShader(NULL);
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);
// Adjust for character spacing
x -= m_dwSpacing / 10.0f;
FLOAT fStartX = x;
TCHAR c;
// Fill vertex buffer
FONT3DVERTEX* pVertices;
DWORD dwNumTriangles = 0L;
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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);
m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
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,
FLOAT fTrackBallRadius = 1.0f);
//-----------------------------------------------------------------------------
// Name: D3DUtil_SetDeviceCursor
// Desc: Builds and sets a cursor for the D3D device based on hCursor.
//-----------------------------------------------------------------------------
HRESULT D3DUtil_SetDeviceCursor(LPDIRECT3DDEVICE9 pd3dDevice, HCURSOR hCursor,
BOOL bAddWatermark);
//-----------------------------------------------------------------------------
// 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
FLOAT m_fRadius; // ArcBall's radius in screen coords
FLOAT m_fRadiusTranslation; // ArcBall's radius for translating the target
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 SetRadius(FLOAT fRadius);
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,
FLOAT fTrackBallRadius)
{
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
sz = sqrtf(fTrackBallRadius * fTrackBallRadius - d2 * d2);
else // On hyperbola
sz = (fTrackBallRadius * fTrackBallRadius) / (2.0f * d2);
// Get two points on trackball's sphere
D3DXVECTOR3 p1(sx, sy, sz);
D3DXVECTOR3 p2(0.0f, 0.0f, fTrackBallRadius);
// 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,
BOOL bAddWatermark)
{
HRESULT hr = E_FAIL;
ICONINFO iconinfo;
BOOL bBWCursor;
LPDIRECT3DSURFACE9 pCursorSurface = NULL;
HDC hdcColor = NULL;
HDC hdcMask = NULL;
HDC hdcScreen = NULL;
BITMAP bm;
DWORD dwWidth;
DWORD dwHeightSrc;
DWORD dwHeightDest;
COLORREF crColor;
COLORREF crMask;
UINT x;
UINT y;
BITMAPINFO bmi;
COLORREF* pcrArrayColor = NULL;
COLORREF* pcrArrayMask = 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));
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = dwWidth;
bmi.bmiHeader.biHeight = dwHeightSrc;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
hdcScreen = GetDC(NULL);
hdcMask = CreateCompatibleDC(hdcScreen);
if (hdcMask == NULL)
{
hr = E_FAIL;
goto End;
}
hgdiobjOld = SelectObject(hdcMask, iconinfo.hbmMask);
GetDIBits(hdcMask, iconinfo.hbmMask, 0, dwHeightSrc,
pcrArrayMask, &bmi, DIB_RGB_COLORS);
SelectObject(hdcMask, hgdiobjOld);
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];
crMask = pcrArrayMask[dwWidth * (dwHeightSrc - 1 - y) + x];
}
else
{
crColor = pcrArrayColor[dwWidth * (dwHeightDest - 1 - y) + x];
crMask = pcrArrayMask[dwWidth * (dwHeightDest - 1 - y) + x];
}
if (crMask == 0)
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
// bAddWatermark is TRUE, the following code adds some
// 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.hbmMask != NULL)
DeleteObject(iconinfo.hbmMask);
if (iconinfo.hbmColor != NULL)
DeleteObject(iconinfo.hbmColor);
if (hdcScreen != NULL)
ReleaseDC(NULL, hdcScreen);
if (hdcColor != NULL)
DeleteDC(hdcColor);
if (hdcMask != NULL)
DeleteDC(hdcMask);
SAFE_DELETE_ARRAY(pcrArrayColor);
SAFE_DELETE_ARRAY(pcrArrayMask);
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_fRadiusTranslation = 1.0f;
m_bRightHanded = FALSE;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
VOID CD3DArcBall::SetWindow(int iWidth, int iHeight, float fRadius)
{
// Set ArcBall info
m_iWidth = iWidth;
m_iHeight = iHeight;
m_fRadius = fRadius;
}
//-----------------------------------------------------------------------------
// 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:
//-----------------------------------------------------------------------------
VOID CD3DArcBall::SetRadius(FLOAT fRadius)
{
m_fRadiusTranslation = fRadius;
}
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
// Name: DXUtil_Read*RegKey() and DXUtil_Write*RegKey()
// 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_ADVANCE - to advance the timer by 0.1 seconds
// 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_RESET, TIMER_START, TIMER_STOP, TIMER_ADVANCE,
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);
//-----------------------------------------------------------------------------
// Readme functions
//-----------------------------------------------------------------------------
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);
HRESULT Add(void* pEntry);
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);
si.fMask = SIF_POS;
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
//
// Copyright (c) Microsoft Corporation. All rights reserved
//-----------------------------------------------------------------------------
#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"),
0, KEY_READ, &hKey);
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
file = CreateFile(strDestPath, GENERIC_READ, FILE_SHARE_READ, NULL,
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)
file = CreateFile(strShortName, GENERIC_READ, FILE_SHARE_READ, NULL,
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;
file = CreateFile(strDestPath, GENERIC_READ, FILE_SHARE_READ, NULL,
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
//-----------------------------------------------------------------------------
// Name: DXUtil_ReadStringRegKeyCch()
// 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;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ReadIntRegKey()
// 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;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ReadBoolRegKey()
// 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;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ReadGuidRegKey()
// 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_ADVANCE - to advance the timer by 0.1 seconds
// 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)
m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
}
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)
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter(&qwTime);
// Return the elapsed time
if (command == TIMER_GETELAPSEDTIME)
{
fElapsedTime = (double)(qwTime.QuadPart - m_llLastElapsedTime) / (double)m_llQPFTicksPerSec;
m_llLastElapsedTime = qwTime.QuadPart;
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_llBaseTime = qwTime.QuadPart;
m_llLastElapsedTime = qwTime.QuadPart;
m_llStopTime = 0;
m_bTimerStopped = FALSE;
return 0.0f;
}
// Start the timer
if (command == TIMER_START)
{
if (m_bTimerStopped)
m_llBaseTime += qwTime.QuadPart - m_llStopTime;
m_llStopTime = 0;
m_llLastElapsedTime = qwTime.QuadPart;
m_bTimerStopped = FALSE;
return 0.0f;
}
// Stop the timer
if (command == TIMER_STOP)
{
if (!m_bTimerStopped)
{
m_llStopTime = qwTime.QuadPart;
m_llLastElapsedTime = qwTime.QuadPart;
m_bTimerStopped = TRUE;
}
return 0.0f;
}
// Advance the timer by 1/10th second
if (command == TIMER_ADVANCE)
{
m_llStopTime += m_llQPFTicksPerSec / 10;
return 0.0f;
}
if (command == TIMER_GETABSOLUTETIME)
{
fTime = qwTime.QuadPart / (double)m_llQPFTicksPerSec;
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
if (command == TIMER_ADVANCE)
{
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
}
//-----------------------------------------------------------------------------
// Name: DXUtil_LaunchReadme()
// Desc: Finds and opens the readme.txt for this sample
//-----------------------------------------------------------------------------
VOID DXUtil_LaunchReadme(HWND hWnd, TCHAR* strLoc)
{
#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 strReadmePath[1024];
TCHAR strExeName[MAX_PATH];
TCHAR strExePath[MAX_PATH];
TCHAR strSamplePath[MAX_PATH];
TCHAR* strLastSlash = NULL;
lstrcpy(strReadmePath, TEXT(""));
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"),
0, KEY_READ, &hKey);
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)
{
_sntprintf_s(strReadmePath, 1023, TEXT("%s\\C++\\%s\\readme.txt"),
strSamplePath, strLoc);
strReadmePath[1023] = 0;
if (GetFileAttributes(strReadmePath) != 0xFFFFFFFF)
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
_tcscpy_s(strReadmePath, strExePath);
strLastSlash = _tcsrchr(strReadmePath, TEXT('\\'));
if (strLastSlash)
*strLastSlash = 0;
lstrcat(strReadmePath, TEXT("\\"));
lstrcat(strReadmePath, strExeName);
lstrcat(strReadmePath, TEXT("\\readme.txt"));
if (GetFileAttributes(strReadmePath) != 0xFFFFFFFF)
bFound = TRUE;
}
if (!bFound)
{
// Search in "%EXE_DIR%\"
_tcscpy_s(strReadmePath, strExePath);
lstrcat(strReadmePath, TEXT("\\readme.txt"));
if (GetFileAttributes(strReadmePath) != 0xFFFFFFFF)
bFound = TRUE;
}
if (!bFound)
{
// Search in "%EXE_DIR%\.."
_tcscpy_s(strReadmePath, strExePath);
strLastSlash = _tcsrchr(strReadmePath, TEXT('\\'));
if (strLastSlash)
*strLastSlash = 0;
lstrcat(strReadmePath, TEXT("\\readme.txt"));
if (GetFileAttributes(strReadmePath) != 0xFFFFFFFF)
bFound = TRUE;
}
if (!bFound)
{
// Search in "%EXE_DIR%\..\.."
_tcscpy_s(strReadmePath, strExePath);
strLastSlash = _tcsrchr(strReadmePath, TEXT('\\'));
if (strLastSlash)
*strLastSlash = 0;
strLastSlash = _tcsrchr(strReadmePath, TEXT('\\'));
if (strLastSlash)
*strLastSlash = 0;
lstrcat(strReadmePath, TEXT("\\readme.txt"));
if (GetFileAttributes(strReadmePath) != 0xFFFFFFFF)
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;
HINSTANCE hInstShell32 = LoadLibrary(TEXT("shell32.dll"));
if (hInstShell32 != NULL)
{
#ifdef UNICODE
pShellExecute = (LPShellExecute)GetProcAddress(hInstShell32, _TWINCE("ShellExecuteW"));
#else
pShellExecute = (LPShellExecute)GetProcAddress(hInstShell32, _TWINCE("ShellExecuteA"));
#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
MessageBox(hWnd, TEXT("Could not find readme.txt"),
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;
}
//-----------------------------------------------------------------------------
// Name: CArrayList::Add
// Desc: Adds pEntry to the list.
//-----------------------------------------------------------------------------
HRESULT CArrayList::Add(void* pEntry)
{
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--;
// Find the entry address
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 loadTexture(std::string fileName);
bool genTexture(D3DXVECTOR3* directionToLight);
bool draw(D3DXMATRIX* world, bool drawTris);
private:
IDirect3DDevice9* _device;
IDirect3DTexture9* _tex;
IDirect3DVertexBuffer9* _vb;
IDirect3DIndexBuffer9* _ib;
int _numVertsPerRow;
int _numVertsPerCol;
int _cellSpacing;
int _numCellsPerRow;
int _numCellsPerCol;
int _width;
int _depth;
int _numVertices;
int _numTriangles;
float _heightScale;
std::vector<int> _heightmap;
// helper methods
bool readRawFile(std::string fileName);
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;
// load heightmap
if (!readRawFile(heightmapFileName))
{
::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;
// next quad
baseIndex += 6;
}
}
_ib->Unlock();
return true;
}
bool Terrain::loadTexture(std::string fileName)
{
HRESULT hr = 0;
hr = D3DXCreateTextureFromFile(
_device,
fileName.c_str(),
&_tex);
if (FAILED(hr))
return false;
return true;
}
bool Terrain::genTexture(D3DXVECTOR3* directionToLight)
{
// 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]);
// shade current quad
c *= computeShade(i, j, directionToLight);;
// save shaded color
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
// vectors on the quad.
D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);
float cosine = D3DXVec3Dot(&n, directionToLight);
if (cosine < 0.0f)
cosine = 0.0f;
return cosine;
}
bool Terrain::readRawFile(std::string fileName)
{
// 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;*/
inFile.read(
(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.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = nullptr;
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;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);
int vp = 0;
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件顶点处理
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持
D3DDISPLAYMODE d3ddm;
if (FAILED(d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &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.SwapEffect = D3DSWAPEFFECT_DISCARD;
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;
}