Our framework piece d3dApp.h is the same with a few additions, a custom vertex definition and an additional pointer to hold our vertex buffer (all code here I compiled on MSVS 2005):
/*
* DirectX Tutorial
* KYA
*
*/
#ifndef D3DAPP_H
#define D3DAPP_H
//Our vertex def.
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
#include "d3dUtil.h"
#include <string>
class D3DApp
{
public:
D3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP);
virtual ~D3DApp();
HINSTANCE getAppInst();
HWND getMainWnd();
// Framework methods. Derived client class overrides these methods to
// implement specific application requirements.
virtual bool checkDeviceCaps() { return true; }
virtual void onLostDevice() {}
virtual void onresetDevice() {}
virtual void updateScene(float dt) {}
virtual void drawScene() {}
// Override these methods only if you do not like the default window creation,
// direct3D device creation, window procedure, or message loop. In general,
// for the sample programs of this book, we will not need to modify these.
virtual void initMainWindow();
virtual void initDirect3D();
virtual int run();
virtual LRESULT msgProc(UINT msg, WPARAM wParam, LPARAM lParam);
void enableFullScreenMode(bool enable);
bool isDeviceLost();
protected:
// Derived client class can modify these data members in the constructor to
// customize the application.
std::string mMainWndCaption;
D3DDEVTYPE mDevType;
DWORD mRequestedVP;
// Application, Windows, and Direct3D data members.
HINSTANCE mhAppInst;
HWND mhMainWnd;
IDirect3D9* md3dObject;
bool mAppPaused;
D3DPRESENT_PARAMETERS md3dPP;
};
// Globals for convenient access.
extern D3DApp* gd3dApp;
extern IDirect3DDevice9* gd3dDevice;
extern IDirect3DVertexBuffer9* g_pVB;
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // The transformed position for the vertex.
DWORD color; // The vertex color.
};
#endif // D3DAPP_H
The changes are near the end. Alright easy so far right? We declare our CUSTOMVERTEX to have position (this is important for later on) and color (because black and white would be boring). We have a pointer to reference our vertex buffer (like most DirecxtX things you must have a buffer before things are drawn to the screen.)
Moving right along we have the d3dUtil.h file. another piece of framework from a previous tutorial; nothing has changed. I repaste it here to emphasize the helpfulness of macros in some debugging, but I warn against them at the same time:
/*
* DirectX Tutorial
* KYA
*
*/
#ifndef D3DUTIL_H
#define D3DUTIL_H
// Enable extra D3D debugging in debug builds if using the debug DirectX runtime.
// This makes D3D objects work well in the debugger watch window, but slows down
// performance slightly.
#if defined(DEBUG) | defined(_DEBUG)
#ifndef D3D_DEBUG_INFO
#define D3D_DEBUG_INFO
#endif
#endif
#include <d3d9.h>
#include <d3dx9.h>
#include <dxerr9.h>
#include <string>
#include <sstream>
//===============================================================
// Globals for convenient access.
class D3DApp;
extern D3DApp* gd3dApp;
extern IDirect3DDevice9* gd3dDevice;
//===============================================================
// Clean up
#define ReleaseCOM(x) { if(x){ x->Release();x = 0; } }
//===============================================================
// Debug
#if defined(DEBUG) | defined(_DEBUG)
#ifndef HR
#define HR(x) \
{ \
HRESULT hr = x; \
if(FAILED(hr)) \
{ \
DXTrace(__FILE__, __LINE__, hr, #x, TRUE); \
} \
}
#endif
#else
#ifndef HR
#define HR(x) x;
#endif
#endif
#endif // D3DUTIL_H
This is boilerplate, not necessary to the understanding of vertices, but there for convinence. looking at our last header file GfxStats.h. This name is chosen for the reason that we derive a mGfxStats class from our framework d3dapp like in the previous tutorials. This header file sets up all the information we need for a gfxStats object:
/*
* DirectX Tutorial
* KYA
*
*/
#ifndef GFX_STATS_H
#define GFX_STATS_H
#include <d3dx9.h>
class GfxStats
{
public:
GfxStats();
~GfxStats();
void onLostDevice();
void onresetDevice();
void addVertices(DWORD n);
void subVertices(DWORD n);
void addTriangles(DWORD n);
void subTriangles(DWORD n);
void setTriCount(DWORD n);
void setVertexCount(DWORD n);
void update(float dt);
void display();
private:
// Prevent copying
GfxStats(const GfxStats& rhs);
GfxStats& operator=(const GfxStats& rhs);
private:
ID3DXFont* mFont;
float mFPS;
float mMilliSecPerFrame;
DWORD mNumTris;
DWORD mNumVertices;
};
#endif // GFX_STATS_H
We can set the number of vertices, triangles, etc... Now onto the meat of this program:
d3dApp.cpp, not much has changed in this framework piece either (notice half of this project is simply there to reduce our effort and retyping each time we want to experiment) Here is the code in entirety and I highlight changes later down on:
/*
* DirectX Tutorial
* KYA
*
*/
#include "d3dApp.h"
//initalizing pointers to zero-dangling pointers are horrible!
D3DApp* gd3dApp = 0;
IDirect3DDevice9* gd3dDevice = 0;
IDirect3DVertexBuffer9* g_pVB = 0; //our pointer to the vertex buffer
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Don't start processing messages until the application has been created.
if( gd3dApp != 0 )
return gd3dApp->msgProc(msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
D3DApp::D3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
{
mMainWndCaption = winCaption;
mDevType = devType;
mRequestedVP = requestedVP;
mhAppInst = hInstance;
mhMainWnd = 0;
md3dObject = 0;
mAppPaused = false;
ZeroMemory(&md3dPP, sizeof(md3dPP));
initMainWindow();
initDirect3D();
}
D3DApp::~D3DApp()
{
ReleaseCOM(md3dObject);
ReleaseCOM(gd3dDevice);
}
HINSTANCE D3DApp::getAppInst()
{
return mhAppInst;
}
HWND D3DApp::getMainWnd()
{
return mhMainWnd;
}
void D3DApp::initMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhAppInst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "D3DWndClassName";
if( !RegisterClass(&wc) )
{
MessageBox(0, "RegisterClass FAILED", 0, 0);
PostQuitMessage(0);
}
// Default to a window with a client area rectangle of 800x600.
RECT R = {0, 0, 800, 600};
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
mhMainWnd = CreateWindow("D3DWndClassName", mMainWndCaption.c_str(),
WS_OVERLAPPEDWINDOW, 100, 100, R.right, R.bottom,
0, 0, mhAppInst, 0);
if( !mhMainWnd )
{
MessageBox(0, "CreateWindow FAILED", 0, 0);
PostQuitMessage(0);
}
ShowWindow(mhMainWnd, SW_SHOW);
UpdateWindow(mhMainWnd);
}
void D3DApp::initDirect3D()
{
// Step 1: Create the IDirect3D9 object.
md3dObject = Direct3DCreate9(D3D_SDK_VERSION);
if( !md3dObject )
{
MessageBox(0, "Direct3DCreate9 FAILED", 0, 0);
PostQuitMessage(0);
}
// Step 2: Verify hardware support for specified formats in windowed and full screen modes.
D3DDISPLAYMODE mode;
md3dObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);
HR(md3dObject->CheckDeviceType(D3DADAPTER_DEFAULT, mDevType, mode.Format, mode.Format, true));
HR(md3dObject->CheckDeviceType(D3DADAPTER_DEFAULT, mDevType, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, false));
// Step 3: Check for requested vertex processing and pure device.
D3DCAPS9 caps;
HR(md3dObject->GetDeviceCaps(D3DADAPTER_DEFAULT, mDevType, &caps));
DWORD devbehaviorFlags = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
devbehaviorFlags |= mRequestedVP;
else
devbehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// If pure device and HW T&L supported
if( caps.DevCaps & D3DDEVCAPS_PUREDEVICE &&
devbehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)
devbehaviorFlags |= D3DCREATE_PUREDEVICE;
// Step 4: Fill out the D3DPRESENT_PARAMETERS structure.
md3dPP.BackBufferWidth = 0;
md3dPP.BackBufferHeight = 0;
md3dPP.BackBufferFormat = D3DFMT_UNKNOWN;
md3dPP.BackBufferCount = 1;
md3dPP.MultiSampleType = D3DMULTISAMPLE_NONE;
md3dPP.MultiSampleQuality = 0;
md3dPP.SwapEffect = D3DSWAPEFFECT_DISCARD;
md3dPP.hDeviceWindow = mhMainWnd;
md3dPP.Windowed = true;
md3dPP.EnableAutoDepthStencil = true;
md3dPP.AutoDepthStencilFormat = D3DFMT_D24S8;
md3dPP.Flags = 0;
md3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
md3dPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// Step 5: Create the device.
HR(md3dObject->CreateDevice(
D3DADAPTER_DEFAULT, // primary adapter
mDevType, // device type
mhMainWnd, // window associated with device
devbehaviorFlags, // vertex processing
&md3dPP, // present parameters
&gd3dDevice)); // return created device
}
int D3DApp::run()
{
MSG msg;
msg.message = WM_NULL;
__int64 cntsPerSec = 0;
QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);
float secsPerCnt = 1.0f / (float)cntsPerSec;
__int64 prevTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);
//calling function
if( FAILED( gd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0 /*Usage*/, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) ){
return E_FAIL;
}//end if
//defining our vertices
CUSTOMVERTEX vertices[] =
{
{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
//declare a pointer and lock vertices
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
return E_FAIL;
//copy vertices into memory buffer
memcpy( pVertices, vertices, sizeof(vertices) );
//unlock the vertices for processing
g_pVB->Unlock();
while(msg.message != WM_QUIT)
{
// If there are Window messages then process them.
if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// Otherwise, do animation/game stuff.
else
{
// If the application is paused then free some CPU cycles to other
// applications and then continue on to the next frame.
if( mAppPaused )
{
Sleep(20);
continue;
}
if( !isDeviceLost() )
{
__int64 currTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;
updateScene(dt);
drawScene();
// Prepare for next iteration: The current time stamp becomes
// the previous time stamp for the next iteration.
prevTimeStamp = currTimeStamp;
}
}
}
return (int)msg.wParam;
}
LRESULT D3DApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
// Is the application in a minimized or maximized state?
static bool minOrMaxed = false;
RECT clientRect = {0, 0, 0, 0};
switch( msg )
{
// WM_ACTIVE is sent when the window is activated or deactivated.
// We pause the game when the main window is deactivated and
// unpause it when it becomes active.
case WM_ACTIVATE:
if( LOWORD(wParam) == WA_INACTIVE )
mAppPaused = true;
else
mAppPaused = false;
return 0;
// WM_SIZE is sent when the user resizes the window.
case WM_SIZE:
if( gd3dDevice )
{
md3dPP.BackBufferWidth = LOWORD(lParam);
md3dPP.BackBufferHeight = HIWORD(lParam);
if( wParam == SIZE_MINIMIZED )
{
mAppPaused = true;
minOrMaxed = true;
}
else if( wParam == SIZE_MAXIMIZED )
{
mAppPaused = false;
minOrMaxed = true;
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onresetDevice();
}
// Restored is any resize that is not a minimize or maximize.
// For example, restoring the window to its default size
// after a minimize or maximize, or from dragging the resize
// bars.
else if( wParam == SIZE_RESTORED )
{
mAppPaused = false;
// Are we restoring from a mimimized or maximized state,
// and are in windowed mode? Do not execute this code if
// we are restoring to full screen mode.
if( minOrMaxed && md3dPP.Windowed )
{
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onresetDevice();
}
else
{
// No, which implies the user is resizing by dragging
// the resize bars. However, we do not reset the device
// here because as the user continuously drags the resize
// bars, a stream of WM_SIZE messages is sent to the window,
// and it would be pointless (and slow) to reset for each
// WM_SIZE message received from dragging the resize bars.
// So instead, we reset after the user is done resizing the
// window and releases the resize bars, which sends a
// WM_EXITSIZEMOVE message.
}
minOrMaxed = false;
}
}
return 0;
// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
// Here we reset everything based on the new window dimensions.
case WM_EXITSIZEMOVE:
GetClientRect(mhMainWnd, &clientRect);
md3dPP.BackBufferWidth = clientRect.right;
md3dPP.BackBufferHeight = clientRect.bottom;
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onresetDevice();
return 0;
// WM_CLOSE is sent when the user presses the 'X' button in the
// caption bar menu.
case WM_CLOSE:
DestroyWindow(mhMainWnd);
return 0;
// WM_DESTROY is sent when the window is being destroyed.
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
enableFullScreenMode(false);
else if( wParam == 'F' )
enableFullScreenMode(true);
return 0;
}
return DefWindowProc(mhMainWnd, msg, wParam, lParam);
}
void D3DApp::enableFullScreenMode(bool enable)
{
// Switch to fullscreen mode.
if( enable )
{
// Are we already in fullscreen mode?
if( !md3dPP.Windowed )
return;
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
md3dPP.BackBufferFormat = D3DFMT_X8R8G8B8;
md3dPP.BackBufferWidth = width;
md3dPP.BackBufferHeight = height;
md3dPP.Windowed = false;
// Change the window style to a more fullscreen friendly style.
SetWindowLongPtr(mhMainWnd, GWL_STYLE, WS_POPUP);
// If we call SetWindowLongPtr, MSDN states that we need to call
// SetWindowPos for the change to take effect. In addition, we
// need to call this function anyway to update the window dimensions.
SetWindowPos(mhMainWnd, HWND_TOP, 0, 0, width, height, SWP_NOZORDER | SWP_SHOWWINDOW);
}
// Switch to windowed mode.
else
{
// Are we already in windowed mode?
if( md3dPP.Windowed )
return;
RECT R = {0, 0, 800, 600};
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
md3dPP.BackBufferFormat = D3DFMT_UNKNOWN;
md3dPP.BackBufferWidth = 800;
md3dPP.BackBufferHeight = 600;
md3dPP.Windowed = true;
// Change the window style to a more windowed friendly style.
SetWindowLongPtr(mhMainWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
// If we call SetWindowLongPtr, MSDN states that we need to call
// SetWindowPos for the change to take effect. In addition, we
// need to call this function anyway to update the window dimensions.
SetWindowPos(mhMainWnd, HWND_TOP, 100, 100, R.right, R.bottom, SWP_NOZORDER | SWP_SHOWWINDOW);
}
// Reset the device with the changes.
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onresetDevice();
}
bool D3DApp::isDeviceLost()
{
// Get the state of the graphics device.
HRESULT hr = gd3dDevice->TestCooperativeLevel();
// If the device is lost and cannot be reset yet then
// sleep for a bit and we'll try again on the next
// message loop cycle.
if( hr == D3DERR_DEVICELOST )
{
Sleep(20);
return true;
}
// Driver error, exit.
else if( hr == D3DERR_DRIVERINTERNALERROR )
{
MessageBox(0, "Internal Driver Error...Exiting", 0, 0);
PostQuitMessage(0);
return true;
}
// The device is lost but we can reset and restore it.
else if( hr == D3DERR_DEVICENOTRESET )
{
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onresetDevice();
return false;
}
else
return false;
}
WOW, a lot of code right? Well it is the same except for :
__int64 cntsPerSec = 0;
QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);
float secsPerCnt = 1.0f / (float)cntsPerSec;
__int64 prevTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);
//calling function
if( FAILED( gd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0 /*Usage*/, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) ){
return E_FAIL;
}//end if
//defining our vertices
CUSTOMVERTEX vertices[] =
{
{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
//declare a pointer and lock vertices
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
return E_FAIL;
//copy vertices into memory buffer
memcpy( pVertices, vertices, sizeof(vertices) );
//unlock the vertices for processing
g_pVB->Unlock();
Using system time information we can calculate how much time has elasped and essentially how many frames per second we are getting (which is important for gaming applications as any fragger knows.) The other new addition is the vertex definition and implementation. We create a buffer then create 3 vertices and give them position and color. You can play around with the various coordinates and make a variety of shapes, sizes of triangles, etc...
Also new in this file is:
if( !isDeviceLost() )
{
__int64 currTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;
updateScene(dt);
drawScene();
// Prepare for next iteration: The current time stamp becomes
// the previous time stamp for the next iteration.
prevTimeStamp = currTimeStamp;
}
Calculus fans rejoice. We are tracking the change in time in regards to our application functions. Again this can give us a good idea of our FPS which will be displayed in the upper left hand corner.
Onto the gfxStats.cpp. This sets up the gfxStats object we will be using to draw text and vertices. See here:
/*
* DirectX Tutorial
* KYA
*
*/
#include "d3dUtil.h"
#include "d3dApp.h"
#include "GfxStats.h"
#include <tchar.h>
GfxStats::GfxStats()
: mFont(0), mFPS(0.0f), mMilliSecPerFrame(0.0f), mNumTris(0), mNumVertices(0)
{
LOGFONTA font;
font.lfHeight = 20;
font.lfWidth = 5;
font.lfEscapement = 0;
font.lfOrientation = 0;
font.lfWeight = FW_DONTCARE;
font.lfItalic = false;
font.lfUnderline = false;
font.lfStrikeOut = false;
font.lfCharSet = DEFAULT_CHARSET;
font.lfOutPrecision = OUT_DEFAULT_PRECIS;
font.lfClipPrecision = CLIP_CHARACTER_PRECIS;
font.lfQuality = DEFAULT_QUALITY;
font.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
_tcscpy(font.lfFaceName, _T("Times New Roman"));
HR(D3DXCreateFontIndirect(gd3dDevice, &font, &mFont));
}
GfxStats::~GfxStats()
{
ReleaseCOM(mFont);
}
void GfxStats::onLostDevice()
{
HR(mFont->OnLostDevice());
}
void GfxStats::onresetDevice()
{
HR(mFont->onresetDevice());
}
void GfxStats::addVertices(DWORD n)
{
mNumVertices += n;
}
void GfxStats::subVertices(DWORD n)
{
mNumVertices -= n;
}
void GfxStats::addTriangles(DWORD n)
{
mNumTris += n;
}
void GfxStats::subTriangles(DWORD n)
{
mNumTris -= n;
}
void GfxStats::setTriCount(DWORD n)
{
mNumTris = n;
}
void GfxStats::setVertexCount(DWORD n)
{
mNumVertices = n;
}
void GfxStats::update(float dt)
{
// Make static so that their values persist accross function calls.
static float numFrames = 0.0f;
static float timeElapsed = 0.0f;
// Increment the frame count.
numFrames += 1.0f;
// Accumulate how much time has passed.
timeElapsed += dt;
// Has one second passed?--we compute the frame statistics once
// per second. Note that the time between frames can vary so
// these stats are averages over a second.
if( timeElapsed >= 1.0f )
{
// Frames Per Second = numFrames / timeElapsed,
// but timeElapsed approx. equals 1.0, so
// frames per second = numFrames.
mFPS = numFrames;
// Average time, in miliseconds, it took to render a single frame.
mMilliSecPerFrame = 1000.0f / mFPS;
// Reset time counter and frame count to prepare for computing
// the average stats over the next second.
timeElapsed = 0.0f;
numFrames = 0.0f;
}
}
void GfxStats::display()
{
// Make static so memory is not allocated every frame.
static char buffer[256];
sprintf(buffer, "Frames Per Second = %.2f\n"
"Milliseconds Per Frame = %.4f\n"
"Triangle Count = %d\n"
"Vertex Count = %d", mFPS, mMilliSecPerFrame, mNumTris, mNumVertices);
RECT R = {5, 5, 0, 0};
HR(mFont->DrawText(buffer, -1, &R, DT_NOCLIP, D3DCOLOR_XRGB(0,0,0)));
}
Essentially similar to the last tutorial's object, we create the text we want and display it. The best is yet to come. The actual source file that all of this code has gotten us to at this point. GfxStatsDemo.cpp:
/*
* DirectX Tutorial
* KYA
*
*/
#include "d3dApp.h"
#include <tchar.h>
#include <crtdbg.h>
#include "GfxStats.h"
class GfxStatsDemo : public D3DApp
{
public:
GfxStatsDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP);
~GfxStatsDemo();
bool checkDeviceCaps();
void onLostDevice();
void onresetDevice();
void updateScene(float dt);
void drawScene();
private:
GfxStats* mGfxStats;
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
GfxStatsDemo app(hInstance, "Vertices Demo", D3DDEVTYPE_HAL, D3DCREATE_HARDWARE_VERTEXPROCESSING);
gd3dApp = &app;
return gd3dApp->run();
}
GfxStatsDemo::GfxStatsDemo(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
: D3DApp(hInstance, winCaption, devType, requestedVP)
{
if(!checkDeviceCaps())
{
MessageBox(0, "checkDeviceCaps() Failed", 0, 0);
PostQuitMessage(0);
}
mGfxStats = new GfxStats();
}
GfxStatsDemo::~GfxStatsDemo()
{
delete mGfxStats;
}
bool GfxStatsDemo::checkDeviceCaps()
{
// Nothing to check.
return true;
}
void GfxStatsDemo::onLostDevice()
{
mGfxStats->onLostDevice();
}
void GfxStatsDemo::onresetDevice()
{
mGfxStats->onresetDevice();
}
void GfxStatsDemo::updateScene(float dt)
{
mGfxStats->update(dt);
}
void GfxStatsDemo::drawScene()
{
//All the good stuff happens here:
HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));
//There's a way to dynamically have the program calculate this, just not in at the moment
mGfxStats->setVertexCount(3);
mGfxStats->setTriCount(1);
//Begin
HR(gd3dDevice->BeginScene());
mGfxStats->display();
//drawing some vertices
gd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
gd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
gd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
//ending scene and clean up
HR(gd3dDevice->EndScene());
HR(gd3dDevice->Present(0, 0, 0, 0));
}
YAY! We are here! Let's explore... We create this object with the constructor which takes all of the information provided by the rest of this code. The most important stuff is here (oddly at the end...):
void GfxStatsDemo::drawScene()
{
//All the good stuff happens here:
HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));
//There's a way to dynamically have the program calculate this, just not in at the moment
mGfxStats->setVertexCount(3);
mGfxStats->setTriCount(1);
//Begin
HR(gd3dDevice->BeginScene());
mGfxStats->display();
//drawing some vertices
gd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
gd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
gd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
//ending scene and clean up
HR(gd3dDevice->EndScene());
HR(gd3dDevice->Present(0, 0, 0, 0));
}
First we clean the backbuffer, then I manually set the value of vertices and triangles (which can be done dynamically, but not built into the program at this time). We begin the scene, display the stats, then set up the vertices and instruct them to draw a triangle. We then clean up after ourselves as to prevent memory leaks whenever possible.
You should see a screen that looks something like this:

Your triangle will be different, since I was changing positions of the vertices to see a variety of triangle shapes. There you have it, a beginning look into using vertices with directx. Vertices are 2D for the most part. Matrices will be next and can be manipulated in a 3D realm.
IMPORTANT: Link the following libraries for compilation:
d3d9.lib
d3dx9.lib
dxguid.lib
DxErr9.lib
dinput8.lib
EDIT: DO NOT move the window around or minimize,maximize, etc... that is not accounted for in the infrastructure and will crash the program. I'll make future examples more robust, my apologies.
Happy coding!
--kya
This post has been edited by KYA: 09 March 2008 - 02:16 AM



MultiQuote

|