This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1662
views
04. Simple Font
We'll learn how to display simple font with Direct3D's ID3DX10Font Interface!
Here we will learn how to display some font onto the screen, this is a shorter lessons, but I thought it was pretty basic and suitable for an earlier lesson. This line adds a link to another library, this library is needed to use D3DX10CreateFontIndirect #pragma comment(lib, "d3dx10d.lib") We will add a new function into this lesson. The InitScene function. What this function will do is initialize everything in our scene. The next line is declaring Font as ID3DX10Font type. We are making this global because we need to use it to create the font in the InitScene function, and then we need to use it to display the font in the message loop. bool InitScene(); ID3DX10Font* Font; Here we are making a call to the InitScene function to set up our scene. We check to see if it's successfull and you know what happens if its not. if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } Here is our InitScene function! bool InitScene() { This is where we describe the properties of our font. We create a new font description with the D3DX10_FONT_DESC structure. The first two lines are easy to figure out. Weight is the weight of the text, from 0 to 1000. The next line is "Number of mipmap levels requested. If this value is zero or D3DX_DEFAULT, a complete mipmap chain is created. If the value is 1, the texture space is mapped identically to the screen space.", quoted from msdn, hehe. after that we have a true or false value for if we want the text to be Italics or not. Next is Charset, which is the character set. Quality is just the quality of the text, and pitch and family control exactly what they say, pitch and family. D3DX10_FONT_DESC fd; fd.Height = 175; fd.Width = 0; fd.Weight = 0; fd.MipLevels = 1; fd.Italic = false; fd.CharSet = OUT_DEFAULT_PRECIS; fd.Quality = DEFAULT_QUALITY; fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; This next line specifies the name of the font. If the value is set to NULL then direct3d will look for the first font that matches the other properties of our font. It must be passed a NULL-terminating string no bigger than 32 characters including the terminating NULL. What wcscpy does is just copy our string (font name) to the location of fd.FaceName. No overflow checking is done so there will not be errors wcscpy(fd.FaceName, L"Impact"); The last thing in our InitScene function creates the font from the font description we just filled out, and puts it in our "Font" structure, next we will use our "Font" to print to the screen! D3DX10CreateFontIndirect(d3dDevice, &fd, &Font); return true; } Now we are in our message loop, where we will have the actual game or whatever we want to display onto the screen. The first line creates a color. We talked about this in the last lesson. The next line creates the rectangle which will tell direct3d where our text should be drawn relative to our window. D3DXCOLOR fontColor(0.0f, 0.0f, 0.0f, 1.0f); RECT rectangle = {35, 50, 0, 0}; Here we finally draw our text. we use the DrawText method of our Font structure. The Structure of the method is as so: INT DrawText( [in] LPD3DX10SPRITE pSprite, [in] LPCTSTR pString, [in] INT Count, [in] LPRECT pRect, [in] UINT Format, [in] D3DXCOLOR Color ); pSprite is a pointer to a ID3DX10Sprite object. This can be NULL so direct3d will use its own sprite object, but to improve efficiency, you should create your own sprite object if you will be calling this method more than once in a row. pString is a pointer to the string which we want to draw. Count is the number of characters in the string. We can put -1 if the string is NULL-terminating. pRect is a pointer to the rectangle describing where our text will be drawn relative to our window. Format is optional flags which decide how we draw the text on the rectangle we specified. We put DT_NOCLIP which lets the text go outside of the rectangle without losing clipping any of the text. Here are the flags we can use: **DT_BOTTOM **Justify the text to the bottom of the rectangle. This value must be combined with DT_SINGLELINE. **DT_CALCRECT **Tell DrawText to automatically calculate the width and height of the rectangle based on the length of the string you tell it to draw. If there are multiple lines of text, **ID3DX10Font::DrawText** uses the width of the rectangle pointed to by the pRect parameter and extends the base of the rectangle to bound the last line of text. If there is only one line of text, **ID3DX10Font::DrawText** modifies the right side of the rectangle so that it bounds the last character in the line. In either case, ID3DX10Font::DrawText returns the height of the formatted text but does not draw the text. **DT_CENTER **Center text horizontally in the rectangle. **DT_EXPANDTABS **Expand tab characters. The default number of characters per tab is eight. **DT_LEFT **Align text to the left. **DT_NOCLIP **Draw without clipping. ID3DX10Font::DrawText is somewhat faster when DT_NOCLIP is used. **DT_RIGHT **Align text to the right. **DT_RTLREADING **Display text in right-to-left reading order for bidirectional text when a Hebrew or Arabic font is selected. The default reading order for all text is left-to-right. **DT_SINGLELINE **Display text on a single line only. Carriage returns and line feeds do not break the line. **DT_TOP **Top-justify text. **DT_VCENTER **Center text vertically (single line only). **DT_WORDBREAK **Break words. Lines are automatically broken between words if a word would extend past the edge of the rectangle specified by the pRect parameter. A carriage return/line feed sequence also breaks the line. The next is the text color. Font->DrawText(0, L"Hello World!", -1, &rectangle, DT_NOCLIP, fontColor); Don't forget to add this line. Without this, nothing we just put onto the back buffer will be displayed onto the screen. This line swaps the back buffer with the front buffer, displaying the contents of the back buffer to the screen. SwapChain->Present(0, 0); There, that wasn't too long of a lesson! Remember if you have any problems or comments or anything, PLEASE come visit the forums or contact us! If you are having problems displaying the font on your screen, and you are getting a blank or black screen, It is most likely because the DrawText function of the ID3DX10Font interface CHANGES the states, geometry shader in the effect file, vertex layout, and topology. You must reset all of these after each call to the drawtext function. If you do not have a geometry shader in your effect file, then add this line between the setvertexshader and setpixelshader calls in the technique: SetGeometryShader( NULL ); And remember to reset all the states you are using, such as depth/stencil state, blending state, vertex layout, primitive topology. An example of this is calling the drawtext function at the end of your drawscene function, and setting the states at the beginning of your drawscene function. Here's the final code: main.cpp #include <Windows.h> #include <d3dx10.h> #include <string> #pragma comment(lib, "D3D10.lib") #pragma comment(lib, "d3dx10d.lib") LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 800; const int Height = 600; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; bool InitializeDirect3dApp(HINSTANCE hInstance); //////////////////new//////////////////// bool InitScene(); ID3DX10Font* Font; //////////////////new//////////////////// int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } //////////////////new//////////////////// if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } //////////////////new//////////////////// messageloop(); return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Window Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3dApp(HINSTANCE hInstance) { HRESULT hr; DXGI_SWAP_CHAIN_DESC scd; scd.BufferDesc.Width = Width; scd.BufferDesc.Height = Height; scd.BufferDesc.RefreshRate.Numerator = 60; scd.BufferDesc.RefreshRate.Denominator = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //no multisampling scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 1; scd.OutputWindow = hwnd; scd.Windowed = true; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; D3D10CreateDeviceAndSwapChain(0, D3D10_DRIVER_TYPE_HARDWARE, 0, 0, D3D10_SDK_VERSION, &scd, &SwapChain, &d3dDevice); ID3D10Texture2D* backBuffer; SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer)); d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); D3D10_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D10_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; ID3D10Texture2D* DepthStencilBuffer; ID3D10DepthStencilView* DepthStencilView; hr = (d3dDevice->CreateTexture2D(&depthStencilDesc, 0, &DepthStencilBuffer)); if(FAILED(hr)) { MessageBox(NULL, L"Error creating Texture2D", L"Error", MB_OK | MB_ICONERROR); return 1; } hr = (d3dDevice->CreateDepthStencilView(DepthStencilBuffer, 0, &DepthStencilView)); if(FAILED(hr)) { MessageBox(NULL, L"Error creating Depth Stencil Buffer", L"Error", MB_OK | MB_ICONERROR); return 1; } d3dDevice->OMSetRenderTargets(1, &RenderTargetView, DepthStencilView); D3D10_VIEWPORT vp; vp.TopLeftX = 0; vp.TopLeftY = 0; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; d3dDevice->RSSetViewports(1, &vp); D3DXCOLOR bgColor( 1.0f, 1.0f, 1.0f, 1.0f ); d3dDevice->ClearRenderTargetView(RenderTargetView, bgColor); SwapChain->Present(0, 0); return true; } //////////////////new//////////////////// bool InitScene() { D3DX10_FONT_DESC fd; fd.Height = 175; fd.Width = 0; fd.Weight = 0; fd.MipLevels = 1; fd.Italic = false; fd.CharSet = OUT_DEFAULT_PRECIS; fd.Quality = DEFAULT_QUALITY; fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; wcscpy(fd.FaceName, L"Impact"); D3DX10CreateFontIndirect(d3dDevice, &fd, &Font); return true; } //////////////////new//////////////////// int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code //////////////////new//////////////////// D3DXCOLOR fontColor(0.0f, 0.0f, 0.0f, 1.0f); RECT rectangle = {35, 50, 0, 0}; Font->DrawText(0, L"Hello World!", -1, &rectangle, DT_NOCLIP, fontColor); SwapChain->Present(0, 0); //////////////////new//////////////////// } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ if(MessageBox(0, L"Are you sure you want to exit?", L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); }
Sign in to comment