Page 1 of 1

Introduction to Vertices Using DirectX Drawing A Triangle With Vertices and FPS Stats

#1 KYA   User is offline

  • Wubba lubba dub dub!
  • member icon

Reputation: 3202
  • View blog
  • Posts: 19,234
  • Joined: 14-September 07

Posted 08 March 2008 - 04:37 AM

Alright, let's check out some cool stuff using DirectX. Today we will use vertices to learn how to draw primitive shapes. In this tutorial's case--a triangle. Also, we will display the current frames per second, milisecond per frame and how many triangles and vertices are on the screen. Reading the previous tutorials is recommended, but not necessary. There is a lot of code again--6 files in total, 3 headers and 3 cpp files. Let's look at the headers first:

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:

Posted Image


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


Is This A Good Question/Topic? 0
  • +

Page 1 of 1