Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Must know for Game developers.<-Part 3

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
benlue
Moderator
Reputation: 0

Joined: 09 Oct 2006
Posts: 2142

PostPosted: Tue Apr 03, 2007 2:35 am    Post subject: Must know for Game developers.<-Part 3 Reply with quote

Direct Draw
DirectX should be known by everyone so i'm not explaining what it is .

To set up DirectDraw for use in your program, you need to do a minimum of four things in the initialization section of your program. They are:

Create a DirectDraw object.
Set the cooperation level.
Set the screen resolution and color depth (fullscreen applications).
Create at least one DirectDraw surface.
Before looking at how these steps are accomplished, let's go over a brief explanation of what each one means. First, you need to create a DirectDraw object, meaning we want to retrieve a pointer to the IDirectDraw7 interface. That's pretty simple, right? There are actually three ways to do this. You can make a direct COM call, or use one of two DirectDraw wrapper function. Each has its merits, so we'll be looking at all three of them in a bit.

Second, you need to set the cooperation level. This is probably new to you. Cooperation is a concept that's introduced because Windows is a multitasking operating system. The idea is that all programs running at any given time have to inform Windows what resources they're going to be using, and in what way. This is to make sure Windows doesn't try to take resources essential to your program and allocate them to something else, and so it has the details of how your program will be operating. Don't worry, it's just a simple function call.

The third item on the list is familiar, right? If you're writing a fullscreen application, like a game will usually be, you need to set the screen resolution and color depth to where you want them. Doing this in a windowed application is usually not a good idea, because it can cause problems with other programs that are running at the same time. You'd also have to make certain to restore it when you are finished. In fullscreen mode, setting the resolution is just a single call to DirectDraw, and when your program ends, the Windows desktop settings are restored.

Lastly, and most importantly, is the concept of a DirectDraw surface. Manipulating surfaces is what DirectDraw is all about. Basically, a surface is an area in memory reserved for graphics and graphical operations. The size of a DirectDraw surface is defined by its width and height, in pixels, so you can think of it as a rectangular area for drawing graphics. It has its own interface, called IDirectDrawSurface7. There are three main kinds of surfaces, each of which we'll be using between this article and the next.

Primary surfaces: Every DirectDraw application must have a primary surface in order to accomplish anything. The primary surface is the surface that represents the user's display. Its contents are always visible. As such, a primary surface is automatically set to the width and height of the screen mode.

Back buffers: Back buffers are surfaces that are attached to the primary surface, but not visible. This is how animation is created without flicker. Normally, you draw each frame on a back buffer, and then copy the contents of the back buffer to the primary urface, causing it to appear instantaneously. Since they are attached to the primary surface, they are also the same size as the display.

Offscreen buffers: These are very much like back buffers, only they are not attached to the primary surface. They are most often used for storing bitmaps, although you can do anything you want with them. Offscreen buffers can be any size you want. The only limitation is the amount of memory the system has.

DirectDraw surfaces can either be created in system memory, or directly on the video card in VRAM. If you have all of your surfaces in video memory, the speed is going to be fantastic. System memory is slower. Also, if you have one surface stored in video memory, and one stored in system memory, performance will suffer quite a bit, especially if the video card in question has lousy memory bandwidth. Anyway, the moral is that if you can get away with creating all of your surfaces in video memory, it's probably worth doing so.

All right, now that we have some idea for what we have to do, let's see how we go about doing it. Here's the plan. We're going to create a fullscreen DirectX application that runs in 640x480x16bpp. I'll go through all the DirectX stuff you need to do that, but before you can go setting up DirectX, you need to have a window to operate on. That much is up to you! We went through it in my first article, so you should be pretty familiar with creating windows by now. Since this is going to be a fullscreen application, you're going to want a window that doesn't have any Windows controls on it, so for the window style, use WS_POUP | WS_VISIBLE. Got it? All right, here we go.

Creating a DirectDraw Object

As I said before, there are three ways to do this. We could either use one of two DirectDraw wrapper functions, or make a call directly to the COM object. Let's look at all of them, just to get ourselves used to this stuff. The last method I'll show you is by far the easiest, so you'll probably want to use that. As for the other two, they give you a first look at a few different things you'll come across again. First, have a look at DirectDrawCreate():

Code:
HRESULT WINAPI DirectDrawCreate(
    GUID FAR *lpGUID,       
    LPDIRECTDRAW FAR *lplpDD,
    IUnknown FAR *pUnkOuter
);


Looks a bit strange, doesn't it? The HRESULT return type is standard for DirectDraw functions. If successful, the return value is DD_OK. Each function has several constants it can return describing various errors, but in the interest of keeping the length down a bit, I won't list the values for every function. You can always look them up. One thing I will mention is that there are two useful macros you can use to determine the result of a DirectDraw function call: SUCCEEDED() and FAILED(). Pretty self-explanatory, wouldn't you say? Just put the function call inside the macro to see what's happening.

LPDIRECTDRAW FAR *lplpDD: This is the address of a pointer to a DirectDraw object; it will be initialized as such if the function succeeds. You must declare a variable of type LPDIRECTDRAW that will hold your interface pointer, then pass the address to that pointer.

IUnknown FAR *pUnkOuter: This parameter is reserved for advanced COM features that are not yet implemented. Always pass NULL.

That wasn't so bad, but there is a problem. This function gives you a pointer to an IDirectDraw interface, but we want a pointer to the IDirectDraw7 interface! What do we do? The answer is to call the QueryInterface() method of the DirectDraw object we have obtained, and request the IDirectDraw7 interface. Here's what the call looks like:

Code:
HRESULT QueryInterface(
    REFIID iid,        // Identifier of the requested interface
    void **ppvObject   // Address of output variable that receives the
);                     // interface pointer requested in iid


The return value is the resulting reference count, which you would only need to worry about for testing or debugging purposes. Also, it's recommended that for safety, you should set a pointer to a released interface to NULL. It's also usually a good idea to set such pointers to NULL when you declare them in the first place. Are you following me? It can be a bit much to remember in the beginning, but it'll become second nature to you in no time. Let's bring it all together and look at an example of getting an IDirectDraw7 interface pointer:

Code:
LPDIRECTDRAW lpdd = NULL;   // pointer to IDirectDraw (temporary)
LPDIRECTDRAW7 lpdd7 = NULL; // pointer to IDirectDraw7 (what we want)

// get the IDirectDraw interface pointer
if (FAILED(DirectDrawCreate(NULL, &lpdd, NULL)))
{
  // error-handling code here
}

// query for IDirectDraw7 pointer
if (FAILED(lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7)))

{
  // error-handling code here
}
else
{
  // success! release IDirectDraw since we don't need it anymore
  lpdd->Release();
  lpdd = NULL;
}


Now, if you're a C programmer, you may be a little confused by the way I called QueryInterface() and Release(). You've probably seen the -> operator before. It's used to dereference members of a struct when using a pointer to the struct rather than the variable itself. It's the same thing with objects, except that in this case, the member you're making reference to is a function instead of a variable. While we're on the topic, I'll introduce another bit of C++ notation as well, the scope resolution operator (:Smile. It's used to show the class (or interface, in our case) of which a certain function or variable is a member. In this case, QueryInterface() is a method of the base interface IUnknown, so we would refer to it as IUnknown::QueryInterface(). I'll be using this often in the future, so remember it!

To be honest, that's only useful for demonstrating how to use the QueryInterface() method, which is a part of all DirectX interfaces, so let's move on. Next, just to show you what it looks like, let's use the COM method. The nice thing about doing it this way is that you can get an IDirectDraw7 interface pointer immediately, instead of getting the older pointer and querying for the new one. First, you have to initialize COM, like this:

Code:
HRESULT CoInitialize(LPVOID pvReserved);


I usually call CoInitialize() in the very beginning of my DirectX programs, and call CoUninitialize() at the very end, after I've released all my DirectX objects. Once COM is initialized, you can get the pointer you're after by calling CoCreateInstance(), which can get a little ugly:

Code:
STDAPI CoCreateInstance(
    REFCLSID rclsid,     // Class identifier (CLSID) of the object
    LPUNKNOWN pUnkOuter, // Pointer to whether object is or isn't part
                         //  of an aggregate
    DWORD dwClsContext,  // Context for running executable code
    REFIID riid,         // Reference to the identifier of the interface
    LPVOID *ppv          // Address of output variable that receives
);                       //  the interface pointer requested in riid


Use the same GUID you would have used in the call to DirectDrawCreate(). In other words, NULL. So before moving on, let me show you the example of using COM to create a DirectDraw object:

Code:
LPDIRECTDRAW7 lpdd7; // interface pointer

// initialize COM
CoInitialize(NULL);

// create the object
CoCreateInstance(CLSID_DirectDraw, NULL, CLSCTX_ALL, IID_IDirectDraw7, (void**)&lpdd7);

// initialize the object
lpdd7->Initialize(NULL);


It's good to see an example of calling COM directly, since there may come a time when you want to -- or have to -- do this instead of calling one of the high-level wrapper functions. Finally, now that you've seen the hard ways to do this, let's use the easy way. There is another wrapper function that takes care of everything we want to do, in a single call. No querying for higher interfaces, no setting up COM, no nothing. Here's the function:

Code:
DirectDrawCreateEx(
    GUID FAR *lpGuid,
    LPVOID *lplpDD,
    REFIID iid,
    IUnknown FAR *pUnkOuter
);


All these parameters should look familiar, because we've just seen them. The first, second, and fourth parameters are the same ones we passed to DirectDrawCreate(), only in this case we need to cast the address of our interface pointer to void** -- don't ask me why; it wasn't my idea. The third parameter, riid, is the interface ID that we passed to CoCreateInstance, so just use IID_IDirectDraw7 and we're good to go. That's it! Now that we've got our DirectDraw object, we can start doing things with it. The first two things we want to do are setting the cooperation level and screen resolution, so let's take a look.

Creating Surfaces

It's time for something that requires a little more than just a function call! Creating surfaces isn't too tough. Actually, it is accomplished with a single function call, but first you need to fill out a structure that describes the surface you want to create. Before I show you this, I just want to say that you don't have to fill out the whole thing, so never fear. Smile Here it is, the DDSURFACEDESC2:

Code:
typedef struct _DDSURFACEDESC2 {
    DWORD          dwSize;
    DWORD          dwFlags;
    DWORD          dwHeight;
    DWORD          dwWidth;
    union
    {
        LONG       lPitch;
        DWORD      dwLinearSize;
    } DUMMYUNIONNAMEN(1);
    DWORD          dwBackBufferCount;
    union
    {
        DWORD      dwMipMapCount;
        DWORD      dwRefreshRate;
    } DUMMYUNIONNAMEN(2);
    DWORD          dwAlphaBitDepth;
    DWORD          dwReserved;
    LPVOID         lpSurface;
    DDCOLORKEY     ddckCKDestOverlay;
    DDCOLORKEY     ddckCKDestBlt;
    DDCOLORKEY     ddckCKSrcOverlay;
    DDCOLORKEY     ddckCKSrcBlt;
    DDPIXELFORMAT  ddpfPixelFormat;
    DDSCAPS2       ddsCaps;
    DWORD          dwTextureStage;
} DDSURFACEDESC2, FAR *LPDDSURFACEDESC2;


Let's run through an example. Suppose that for our program, we want a primary surface with one back buffer attached, and one offscreen buffer to use for bitmap storage. Assuming we already have our IDirectDraw7 interface pointer, the following code would create the primary surface:

Code:
DDSURFACEDESC2 ddsd;  // surface description structure
LPDIRECTDRAWSURFACE7 lpddsPrimary = NULL;  // primary surface

// set up primary drawing surface
INIT_DXSTRUCT(ddsd);                              // initialize ddsd
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;  // valid flags
ddsd.dwBackBufferCount = 1;                       // one back buffer
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |    // primary surface
                      DDSCAPS_COMPLEX |           // back buffer is chained
                      DDSCAPS_FLIP |              // allow page flipping
                      DDSCAPS_VIDEOMEMORY;        // create in video memory
       
// create primary surface
if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsPrimary, NULL)))
{
    // error-handling code here
}


With that relatively painless function, we can get the back buffer which was created along with the primary surface. The following code does the job:

Code:
LPDIRECTDRAWSURFACE7 lpddsBack = NULL;  // back buffer

// get the attached surface
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
if (FAILED(lpddsPrimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsBack)))

{
  // error-handling code here
}


Are you starting to get the hang of this stuff? If you're still having trouble remembering all these steps, just give it time. And nobody remembers every member of every structure there is, so don't worry about those huge lists of members and flags. That's why you have your MSDN Library CD. Smile Anyway, the last step is to create the offscreen buffer. Suppose we wanted it to be 400 pixels wide and 300 pixels high. We would go about creating it like this:

Code:
LPDIRECTDRAWSURFACE7 lpddsOffscreen = NULL;  // offscreen buffer

// set up offscreen surface
INIT_DXSTRUCT(ddsd);                                  // initialize ddsd
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;  // valid flags
ddsd.dwWidth = 400;                                   // set width
ddsd.dwHeight = 300;                                  // set height
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN |        // offscreen buffer
                      DDSCAPS_VIDEOMEMORY;            // video memory

// create offscreen buffer
if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsOffscreen, NULL)))
{
  // error-handling code here
}


Done this bit .

PLEASE GO TO PART 4 .
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites