home

Tutorial Index

DirectX tutorial 3: Organizing data and creating UI components

            So far we saw how to initialize Direct3D and set up the project for displaying an image. In this tutorial we will see a way to wrap our image and the data associated with it in an object, and we will to that by creating the UI component class, since most the 2D items drawn will typically be UI elements. The tutorials aim for 3D games, and in the next tutorials we will start touching 3D.

            Before we start: you will have noticed by now that the program does not completely exit. This is because we have an infinite loop, that even though the window has been destroyed, is still running. To fix that, we declare a global bool isRunning and set it to true, and down in the message handler, in IDM_EXIT and WM_DESTROY case we add the command to turn it to false. Finally, we place the variable in the while(..) loop’s condition. This should fix it entirely.

            Now, to begin with, a class to encapsulate 2D images should contain their coordinates and size, essentially the RECT struct we saw in the previous tutorial. This is used not only to draw the image, but to check if the user has clicked on it. After that we will have to add some functionality to it, this is best done with a delegate, implemented here as a function pointer. Since some UI elements do not act, and are in fact decorative or informative, we will also need a boolean variable to indicate whether or not this element does something, i.e. if the pointer points to anything.

            Essentially we have written this code before, now we just have to see how se can isolate it. Here is the class code, note that I used the Add->Class option from the project’s context menu, which just created the .h and .cpp files and the very basic declarations, so generally most of the code is mine:

UIcomponent.h:

#pragma once
#include <d3d9.h>
#include <d3dx9.h>
#include <d3dx9tex.h>
#include <iostream>

using namespace std;

#pragma once
class UIcomponent
{
public:
bool hasAction;
IDirect3DSurface9 *surface;
IDirect3DDevice9 *device;
RECT rect;

            UIcomponent* next;

            void render(void);

            UIcomponent(void);

            UIcomponent(string path, int x, int y, int w, int h, IDirect3DDevice9 *device);

            ~UIcomponent(void);
};

 

As we have seen in the past, “#pragma once” indicates that the following should be declared only once, and thus saves us from redeclaration of #include statements. After that we include anything we included previously. There is a stray #pragma once, it does not affect the code. Inside the class declaration we have a boolean that shows if we have a function associated with the delegate we’ll make (not here yet), the surface where we’ll store the image, the DirectX device, the rectangle where the image is rendered (used both for rendering and associating a click with it), a pointer to another component (we’ll make a linked list to store all components in a dynamic way), and two constructors, as well as the render function. Let’s take a look at those two, as they exist in UIcomponent.cpp:

UIcomponent::UIcomponent(string path, int x, int y, int w, int h, IDirect3DDevice9 *device)
{
this->device = device;
RECT rect;
rect.top = y;
rect.left = x;
rect.right = x+w;
rect.bottom = y+h;
this->rect = rect;
this->surface = LoadSurfaceFromFile(path);
next = NULL;
}

The code is fairly straightforward, and I generally just added things as I saw that I needed them when the program crashed. Of course the device has to be assigned, that is obvious. Then you set the RECT where the image is to be drawn, I created a new one and then set the actual RECT’s value to that. I could work with the original RECT’s value (of course that also depends on the compiler), but this is more standard as an approach. In the .cpp file I declare LoadSurfaceFromFile(string) using the extern keyword, so I use from the other file to create the surface, sparing me the effort of rewriting it. Finally you have to set next to NULL, which is a flat zero. The default value next gets is 0xffffffff or similar, which just leads to access violation.

void UIcomponent::render(void)
{
{
static IDirect3DSurface9* backBuffer;
device->GetBackBuffer(0,NULL, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
if(NULL == device)
{
return;
}

                        device->StretchRect(surface, NULL, backBuffer, &rect, D3DTEXF_NONE);

            }
}

The rendering function has the code essentially copy-pasted, I will not go through it, just don’t forget to change the variable names.

In the .cpp file I have also copied the headers used in the other files, again, #pragma once comes to the rescue.

Now, in the D3DLoader.h file. Notice how in this tutorial the only touch we added to the main file is the refinement of the code for exiting, which means just a line of code that has nothing to do with our overall work! The target is to move our additions as far as possible from the core code, which means we will try to make it so we don’t have to do much more than declarations when we add something. Let’s see the changes made to D3DLoader then:

First of all, we have to #include UIcomponent.h, so our new class is visible.\

After that I declare a pointer to such a component, which I name firstComponent:
UIcomponent* firstComponent;

After that one, I create a new void function called RenderUI(). This one uses the linked list to display the elements. By modifying it, you can create a function to render units, which will use perhaps a more suitable data structure. In any case, the essence remains the same:

 

void RenderUI()
{
if(firstComponent == NULL)
{
return;
}
UIcomponent* ui = firstComponent;
renderUI:
ui->render();
if(ui->next != NULL)
{
ui = ui->next;
goto renderUI;
}

}

So we check if there are any components whatsoever, if so, we start with the first one, using a pointer to the one we are currently working on. Note that a similar function will check which component we clicked. I have used GOTO, which usually causes panic among the masses of programmers who blindly believe what they are told. I use it to create a primitive loop, the translation in socially acceptable code would be:

ui->render();
while(ui->next != NULL)
{
ui = ui->next;
ui->render();
}

My choice of using GOTO is purely to suit my own way of thought. What I though was: Render the component. Is there another one? Point to that one and repeat. The While() trail of thought is: render the first one, while there is a next one, point to the next one and render it. It’s very slight in the difference of thought, choose whichever you wish, but know that they perform and act in an absolutely identical fashion.

Now we have to declare our first component, which I naturally do in initPostD3D(). I did not remove the previous initialization, it simply matters too little. The declaration is straightforward, I give it the parameters I want, so that I replicate the previous effect.

Finally in render(), I replace the StrechRect(..) function with RenderUI(). Thus all UI is rendered with a single function call, and we need not worry about that any longer.

This covers the part of organizing renderable objects. Now to add functionality. We will be adding an exit button. To do that, we will add a function pointer to the class, a function that will assign a function to the pointer, and finally a function to figure out if we clicked the element and thus calls or does not call the function assigned to it.

To begin with, the function we will assign our exit button is simple:

static void exit() //only one instance needed
{
isRunning = false;
}

And this we will write in the UIcomponent.h as void *function(). Don’t forget to declare isRunning as en external variable.

The function pointer is defined in the header. Note that you have to write it (*functionName)(void), requiring the parentheses.

The function to assign the function to the function pointer (try saying that fast!) is declared in the header and defined in the code file:

void assignFunction (void function())
{
this->hasAction = true;
this->function = function;
}

So far so good. Now the tricky part, checking if we should execute it. Now we go back to UIcomponent.h and write our new functions:

void UIrespond(UIcomponent *e, int x, int y)
{
if(isInRect(e, x, y)&& (e->hasAction))
{
e->function();
}
else
{
if(e->next != NULL)
{
return UIrespond(e->next, x, y);
}
else
{
return;
}

}

bool isInRect(UIcomponent *e, int y)
{
if(x < e->rect.right && x > e->rect.left && y < e->rect.bottom && y > e->rect.top)
{
return true;
}
return false;
}

Ok, so let’s take a look at this code. First of all, we outsourced the check which concerns whether or not we clicked on the button to the second function. Keep in mind that the 0 of the Y axis is the top of the screen (otherwise the comparisons don’t make much sense). The function is really simple, nothing further to add. The function we’ll be calling when the mouse is clicked, which is the first function, is also simple. I recursively go through the list of UI elements. We’ll pass it the first element, and it will call itself for each subsequent element. If it finds a matching RECT, or if it runs out of elements, it returns zero. Take note: the function I wrote second has to be declared or defined before the fist one, otherwise it is not visible.
As we have gone deeper down with the recursion, the result is propagated back to the original call. Finally we need to get this called through our message processing function, otherwise there is no point to this. But first, since we handle stuff through the D3DLoad.h, we must make one final function:

void handleMouse(int x, int y)
{
UIrespond(firstElement, x, y);
}

And now the magic moment, go to the message handler and add above WM_PAINT another case, WM_LBUTTONDOWN, which handles the left button press. Under that, you declare an object of the POINT structure, and you give it to GetCursorPos(&POINT) but you also first remove the offset created by the window, and take into account the stretching that has occurred:

 

            case WM_LBUTTONDOWN:
{
POINT p;
GetCursorPos(&p);
RECT w;
GetWindowRect(hWnd, &w);
int x = (p.x - w.left)*(float)((float)xRes/(w.right - w.left));
int y =(p.y - w.top - 50)*(float)((float)yRes/(w.bottom-w.top));
handleMouse(x , y);
break;
}
Let’s take a short look at the math in here, it’s real simple. We get the window rectangle, subtracting the left from the right gives us the width, similarly we can get the height. We divide the resolution DirectX is rendering to, by the one of the actual window, and multiply that times the pixels, after of course having taken into consideration the displacement of the window.
This concludes the part that handles the click. One last thing we did not take care of (I promise, we’re finishing, after all you’ll notice that the scrollbar to the right is not expanding so this is not a joke), is a good, stable way to add components. We will essentially create a new component, assign the current firstCompnent to the next value of the new one, and then make the new on the firstComponent. We essentially expand the linked list by adding in the front.

 

void addUIcomponent(UIcomponent *e)
{
e->next = firstComponent;
firstComponent = e;
}

And finally (I promise, it’s the last one) we add the new UI component. We will in fact add this one first, assign the function, and then add the background. The system we’ve built works like a stack, which means that it will start with the last one added. Thus the code in initPostD3D() is:

void initPostD3D()
{
backgroundSurface = LoadSurfaceFromFile("C:\\MicrosoftDirectX.JPG");
firstComponent = new UIcomponent("C:\\directx-logo.jpg",xRes-100,0,100,100,pd3dDevice);
firstComponent->assignFunction(exit);
addUIcomponent(new UIcomponent("C:\\MicrosoftDirectX.JPG",0,0,xRes,yRes,pd3dDevice));
}

            Notice how we defined our new function to take a pointer to a component, the new keyword actually gives a pointer. Other than that, everything is clear I believe. Now if you build and run the project, you should have the clickable button that exits.
Any further buttons you add will be by using the two last commands. The higher up you add them, the further front they appear.

            Handling multiple layers: Probably the best way to work with having a background behind everything and having the UI above everything, is to create a new pointer to a UIcomponent and render it separately before everything else, and then render the rest of the UI after the models or the gameplay images.

            The next tutorials will cover the basics of rendering 3D models. At first we will create some very basic models in our code, later on we will import models from files, as creating all our models in code is ridiculous as a notion for a modern day project. It probably was so even for the very first 3D games.|

You can download this tutorial’s source and project files here.

Note that naming of the projects may remain the same, regardless, the .rar file has the right name.