home

Tutorial Index

DirectX tutorial 2: Displaying sprites

 

            In this tutorial we will be displaying sprites. Sprites are still images, pictures if you will. However the term refers to an image that may or may not be composed of multiple images evenly and distinctly placed within the image, which are typically individual frames of an animation. Another use of a sprite is to store, for example, all the terrain types in one image, and then you can render the most appropriate one.

            To handle sprites in DirectX you need to load them to a surface. Surfaces are like sheets of paper, they store image information, or simple put, an image. The images are copied between surfaces and the back buffer, which itself is a surface.

            Though we can directly load a surface from a file, we will make a function that will wrap the process and prevent crashed by checking if it succeeded. By this time it is also a good idea to create our own header and place all the functions in there, the header I’ll call D3DLoader.h, due to it’s content. Move the declarations of the DirectX object and the device, along with the function declarations to our new header. Add the #include instruction below the #include instruction for <d3d9.h>. Alternatively, to be independent of order, you can write “pragma once” (without the quotes) and also include <d3d9.h> in the new header.

            One more dependency (or two): to use some of the structs for the surface you will need to #include <d3dx9.h>, and to make it visible you will again have to go to project->properties->C/C++->General and add to the Additional Include Libraries the path “:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include” . As always, if you detect that something is different, fix it appropriately, for example if it is not the June 2010 sdk, put the right version. Do the same with the Additional Library Directories under Linker->General, only instead of \Include, replace it with \Lib\86, and if you are running a 64bit Windows version, create a second entry ending in \Lib\64. Finally, under Visual C++->Additional Library Directories, add an entry “C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Developer Runtime\x64”.

            ATTENTION: make sure your Platform (show at the top of the properties window and the editor itself) is corresponding to your machine, i.e. 64 bit windows should build in x64. If x64 is not available in the list, select new and create it (it is straightforward). In the properties window you may need to use the Configuration Manager, it’s the same process after that.

            Now that we have a functional header to contain our data, we can write our LoadSurfaceFromFile() function:

IDirect3DSurface9* LoadSurfaceFromFile(string FileName)
//#include <iostream> and also add the “using namespace std” instruction at the top
//of the file, so you can use strings
{
HRESULT hResult;
IDirect3DSurface9* surface = NULL;
D3DXIMAGE_INFO imageInfo;
wstring  ws(FileName.begin(), FileName.end()); //we may need an LPCWSTR
hResult = D3DXGetImageInfoFromFile(str, &ImageInfo);
if(FAILED(hResult))
{
return NULL;
}
hResult = pd3dDevice->CreateOffscreenPlainSurface(imageInfo.Width,
imageInfo.Height,
D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&surface,
NULL)
If(FAILED(hResult))
{
return NULL;
}

            hResult = D3DXLoadSurfaceFromFile(surface, NULL, NULL, str, NULL, D3DX_DEFAULT, 0, NULL);

if(FAILED(hResult))
{
return NULL;
}

return surface;
}

            Notice how an HRESULT is used throughout the code. This stores information on the success of the operations, and is a Win32 type, it could be substituted by a bool, but on the one hand the functions return HRESULT, on the other it keeps some more information.
I also create a WSTRING, which is a Wide character STRING. Though the code should work fine, if you receive compiler errors about str, try using ws instead, or ws.str(). I just added it so you have it ready.
Next, as is apparent, we get the image information. As you see a bit further below, it stores info such as the width and height of the image. We use the HRESULT to see if we successfully got the info.
Now we create the actual surface. We give it the width of the image, the height, we set the format to XRGB, as we did in the previous tutorial for the DirectX object, the next parameter is the color pool, which we just use the default one, we give it the address where it should store the surface, and the last parameter is a handle, which is a reserved parameter which remains NULL.
Again we check if that failed, and if not, we load the image to the surface. As per usual, the NULL parameters refer to which part to load, masks, and so on. We just use their default values. With respect to the order of the parameters, we pass the surface, the color palette (8, 16 or 32 bit) to be used, the area where the picture is to be loaded, the source file, the area of the picture to be loaded, the filter to be used, and a color key (used by any filter we apply, here we do not apply any).
After a final check, we return the surface.

            In this tutorial we will render a background image (I have included a high-res logo of DirectX in the project file). To render it though, we need a RECT to tell DirectX where we want it rendered. Along with that, we’ll do some good programming practice and add a couple more things to D3DLoader.h. First, add two integers, xRes and yRes, and a RECT backgroundRect. Make sure you set xRes and yRes to have the default values you want. Then add the following code:

void initGlobalVars()
{
backgroundRect.top = 0;
backgroundRect.bottom = yRes;
backgroundRect.left = 0;
backgroundRect.right = xRes;
}

void initGlobalVars(int x, int y)
{
xRes = x;
yRes = y;
initGlobalVars();
}

We will call either one of these functions, both the window resolution and the back buffer’s are set so it’s up to date. Of course, to change resolution during runtime will also require a little more code, but for now we’re satisfied that we can’t mess up.
Now would also be a good time to go into initDirect3D() and change the numbers we used for our resolution to the variables we just declared, this way they’re consistent and we can use the variables anywhere we refer to resolution. Also call initGlobalVars() right before the call to initDirect3D().

Now that we have the rectangle set up, we declare a surface:

IDirect3DSurface9* backgroundSurface;

For now we will load it in yet another function initPostD3D(), but future surfaces and content will be loaded with more tidy methods. We need to add another function because we need to already have the Direct3D object to load a surface. To load the image use the function we wrote above:

backgroundSurface = LoadSurfaceFromFile(“C:\\MicrosoftDirectX.JPG”);

Note that I gave the path explicitly. Though Visual Studio should handle the path finding part, do not rely on it. Sometimes it works for me, sometimes it doesn’t, if not, just create a folder in a convenient place and use absolute paths. Also be wary of upper or lowercase letters, including those in the extension, you never know what might be wrong.
Now go into render(). Remember how I said that between the Clear(..) and Present(..) commands you put all the rendering code? Time to do that! We use the method StretchRect(..) to do that, which also does the scaling for us:

pd3dDevoce->StretchRect(backgroundSurface, NULL, backBuffer, &backgroundRect, D3DTEXF_NONE);

And the parameters are, with respect to the order: surface to copy, region to copy (NULL so that we copy the entire surface), surface to copy to, region to copy to (this time explicit so the image does not pour our of the screen or does not leave any gaps), and finally, the effect we want to apply, which, in this case, we don’t.

Finally, this tutorial ended. It took me a long while to get all this working, three days, and generally it takes a while for people to get the setup right, so don’t give up without a fight. In the next tutorial we will organize things a bit so we don’t end up with a big mess in the end. If you feel confident now you can try to move the image around according to the Win32 tutorials for input, or you can do it after the next tutorial which will tidy things up a bit.

The project files are here, in the folder where the source code resides you will find the image I used, you can also just replace it with one of your own, don't forget to make sure you give a correct name and path.