Beginnen met DirectX

n dit artikel ga ik de basisbeginselen van DirectX uitleggen. Het mooie van DirectX is dat je er veel helpfuncties voor helpt, dus het werkt op zich heel snel.

Allereerst wil ik voorstellen dat je alvast begint met het downloaden van de Microsoft® DirectX SDK. Wat staat voor Software Development Kit. Dit heb je nodig om gebruik te kunnen maken van DirectX. Surf hiervoor naar http://msdn.microsft.com/directx/. De 8.1 versie is zo’n 165 MB. Dus hopelijk heb je een snelle verbinding ?. Het is trouwens ook verstandig om er iets te drinken erbij te pakken, het liefst iets oppeppend en met veel cafeïne. Een Red Bull is zeker aan te raden. Tijdens het schrijven van dit, zal het bij mij ook wel erg hard gaan ;)

DirectX is gebaseerd op het COM idee. COM staat voor Component Object Model. Met dit, hoef je niet met iedere update alles te hercompileren. Dit wil dus zeggen dat als jij een spel hebt, met als onderdelen. Spel logica & Netwerk informatie. Jij wilt nu dat Netwerk gedeelte updaten, maar je wilt niet dat je gebruikers een heel nieuw spel moeten kopen of uploaden. Dat hoeft met COM ook niet meer, je laat ze gewoon een aantal files/patches downloaden, installeer ze in de goede map en het programma gebruikt automatisch de nieuwe gegevens, zonder dat het programma opnieuw gecompileerd is!

DirectX bestaat uit een ruim aantal interfaces. Dit zijn de meest gebruikte.

• DirectDraw
Direct3D
• DirectSound
• DirectMusic
• DirectInput
• DirectPlay

De meeste spreken wel voor zich. DirectDraw is voor 2d animaties en DirectPlay voor netwerken. De rest lijkt me wel duidelijk.

Alle interfaces zijn gebaseerd op de interface IUnknown. Die interface bestaat uit:

Functie: Beschrijving:

QueryInterface Voor gebruik van pointers, zodat ze de afgeleide interfaces kunnen bijhouden.

AddRef Verhoogt de referentietelling van de interface Dit leg ik straks uit

Release Dit geeft een interface vrij. Verlaagt ook de referentie telling.

Goed, Referentietelling. Wat moet je daar weer bij voorstellen. Goede vraag, maar het is niet moeilijk te begrijpen. Kijk het betekent gewoon dat de interface Iunkown bijhoudt hoeveel interfaces er op dat moment gebruikt worden. Wordt er geen 1 gebruik heeft het geen zin voor IUnkown om zich zelf te behouden en kan hij zichzelf vernietigen.

Meer hoef je er niet van te weten.

Goed, nu gaan we ons echt in het diepe gooien en beginnen met DirectDraw. Ben je er klaar voor? Okay, laten we dan maar beginnen met het verkrijgen van de juiste interface!

Om DirectDraw te kunnen gebruiken heb je toegang nodig tot de interface. Voor DirectX7 is die IDirectDraw7. Voor DirectX8 weet ik het niet, het hoeft namelijk niet per se gelijk IDirectDraw8 te zijn. Kijk hiervoor in de source van een van de examples.

Om de interface te kunnen gebruiken, gebruik je DirectDrawCreateEx();

HRESULT WINAPI DirectDrawCreateEx(
FAR *lpGUID,
RECTDRAW7 FAR *lplpDD,
ID iid,
Iunknown FAR *pUnkOuter
);

Parameters:

lpGUID Globally Unique Identifier, GUID, kan je gweoon op NULL zetten. Heeft te maken met het videostuurprogramma. Met NULL gebruik je de standaardversie.

lplpDD Adres van een variabel die de pointer ontvangt naar de interface.

iid GUID die het type interface specifieert. Stel in op IID_IDirectDraw

pUnkOuter Voor toekomstig gebruik, altijd NULL.

Nog een kleine opmerking bij de iid parameter. Voor DirectX7 is dit dus IID_IDirectDraw7, DirectX8 is dit dus IID_IDirectDraw8 enzovoort.

Goed, nu gaan we vaststellen in welke rol DirectDraw optreed. Dat doen we met SetCoorperativeLevel(). Nu kunnen we dus vaststellen of DirectDraw met een volledig scherm of maar met een window werkt. Dit is de prototype:

HRESULT SetCoorperativeLevel(
D hwnd,
D dwFlags

PARAMETERS:
Handle voor het bovenste venster van het programma.

dwFlags Vlaggen die het samenwerkingsniveau voor DirectDraw bepalen. De vlaggen kunnen gecombineerd worden met behulp van de bitgewijze OR, dit is een |

Voor dwFlags zijn de volgende flags mogelijk.

DDSCL_NORMAL Het programma gedraagt zich als een normaal Windows programma. Je kan nu niet gebruik maken van DDSCL_EXCLUSIVE en DDSCL_FULLSCREEN.

DDSCL_EXCLUSIVE Het programma maakt exclusief gebruik van de videohardware, kan alleen in combinatie met DDSCL_FULLSCREEN.

DDSCL_FULLSCREEN Nu gebruikt het programma het gehele beeld. Moet gebruikt worden met DDSCL_EXCLUSIVE.

DDSCL_ALLOWREBOOT. Nu is het mogelijk om de computer opnieuw op te starten door middel van CTRL + ALT + DEL. Anders niet.

Als deze functie lukt word DD_OK tereuggestuurd.

Surfaces

Om te kunnen tekenen in DirectDraw heb je een zo genoemde surface nodig. Met andere woorden een ondergrond. Tekenen doe je ook niet in de lucht, maar op een papier of op je buurmans net nieuwe schutting ;).

Ook hiervoor is een aparte helpfunctie, namelijk CreateSurface. Hier is weer de prototype.

HRESULT CreateSurface(
LPDDSURFACEDESC2 lpDDSurfaceDesc2,
LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface
Iunknown FAR *pUnkOuter
);

// LET OP!
// De pointer lplpDDSurface is geen tik fout! Voor degene die mijn artikel “Windows” hebben gelezen, konden daar ook een stukje lezen over de Hungarian Notation. Daarin stond dat lp voor Long Pointer stond. Daarnaast staat er sowiesow al voor iedere DirectDraw pointer lp. Dit hoort dus is en is geen tik fout van mij!

Parameters:

lpDDSurfaceDesc2 Pointer naar ene structuur DDSURFACESDESC2 met een beschrijving van het nieuw te maken vlak.

lplpDDSurface Adres van een variabele die een pointer ontvangt naar een IDirectDrawSurface7 interface.

pUnkOuter Voor toekomstig gebruik, altijd NULL.

Goed, nu moet er heel goed opgelet worden. Neem nog gauw even een cafeïne stoot en lees weer gauw verder.

Alle ongebruikte waarden in de DDSURFACEDESC2 structuur verwijdert worden. Geef hem daarna weer zijn structuur grootte met behulp van sizeof(DDSURFACEDESC2). Doe je dit niet dan hoe je er programmafouten en waarschijnlijk ook geheugenproblemen, de welbekende Memory Leaks, aan over.

We gaan nu een clipper aan de surface, de Primary surfcace toevoegen. Deze clipper snijd de tekeningen bij. Voorbeeld. Als ik een window van 600X400 pixels heb, maar ik heb een afbeelding van 640*480 pixels. Als ik dit wel prima vind en ik doe er niks aan, maak ik onnodig gebruik van geheugen. Kijk, in principe heb ik maar het geheugen nodig voor het gedeelte van de afbeelding die 600*400 is. Dat kost me dan, als de kleurenresolutie 8 bits (= 256 kleuren) is, dus 1 byte. 600*400*1 byte= 240.000 bytes.

Maar omdat ik de tekening niet bijsnijd, heb ik ook nog een, voor de gebruiker, onzichtbaar gedeelte van 40*80*1 byte = 320 bytes. Ik gebruik dus 320 bytes voor niets!

Nu lijkt het nog niet zoveel, maar als je de formules met 16 bit kleurendieptes gaat uitvoeren, gaat het al aardig hard.

600*400*2 = 480.000 bytes.
40*80*2 = 640 bytes.

Als je dan ook nog gebruik maakt van een Back Buffer, dit leg ik zo uit, gaat het al helemaal hard.

480.000 * 2 = 960.000 bytes
640 * 2 = 1280 bytes.

Als je dan een 1mb videokaard hebt, zit je al bijna aan je maximum.

Met de clipper bespaar je dus ruimte in je videokaard geheugen. Je zet de clipper als volgt.

HRESULT CreateSurface(
DWORD dwFlags,
LPDIRECTDRAWCLIPPER7 FAR *lplpDDClipper,
Iunknown FAR *pUnkOuter
};

Parameters:

dwFlags Gebruiken we nog niet. Stel maar in op NULL

lplpDDClipper Adres van een variabele die de pointer ontvangt voor de IDirectDrawClipper.

pUnkOuter Voor toekomstig gebruik, stel weer in op NULL.

We kunnen de clipper koppelen aan de primary surface, maar dit leg ik uit bij ons voorbeeld.

Voorbeeld

We gaan een voorbeeld programma maken. Ik hoop dat de DirectX SDK inmiddels gedownload is en geïnstalleerd. Zo niet, dan nog even geduld.

We beginnen met de developer ruimte in te stellen. Ik gebruik Microsoft Visual C++ 6. Voor Borland en andere compilers weet ik niet hoe het werkt. Vraag dit anders op de diverse fora.

We beginnen met een nieuw project. Vul hem zo in.

Application -> Win32 Application
Name -> BmpLoader

Vervolgens kies je An Empty Project. Druk op OK en je bent klaar om te beginnen. Voor het gemak doe ik even alles in een file. Open een nieuw CPP bestand. File -> New -> C++ Source File. Noem hem BmpLoader.cpp.

Vervolgens gaan we de compiler in de goede directory’s laten zoeken. Zoek het onderstaande scherm op. Hiervoor doe je Tools -> Options. Het Tabblad Directories, vervolgens voeg je de directory toen waar de includes van de SDK instaat. De pad is zo:

%SCHIJF%/%SDK DIRECTORY%/INCLUDE
Bij mij -> c:\MSSDK\INCLUDE.

P.S. Hij moet bovenaan staan!.

P.S.2 Dit hoeft maar 1 keer!

Het volgende moet iedere keer.

Project -> Settings.

Zet Settings For: op All Configurations.

Bij het tabblad Link, vul je bij Object/laibrary modules het volgende in:

ddraw.lib dxguid.lib

Klik op Ok en we zijn klaar! Nu kun je beginnen met het typen!

O ja. Je moet wel telkens includen. Maar dit zie je straks in de source wel terug.

Neem de window source van mijn artikel, Windows, over. Dit is onze basis voor ons voorbeeldprogramma.

We beginnen met het maken van een bitmap loader. Begin onder de commentaar regel

LPDIRECTDRAWSURFACE7 bitmap_surface(LPCTSTR file_name)
{
	HDC hdc;
	HBITMAP bit;
	LPDIRECTDRAWSURFACE7 surf;

	// load the interface bitmap

	bit=(HBITMAP) LoadImage(NULL,file_name,IMAGE_BITMAP,0,0,
	LR_DEFAULTSIZE|LR_LOADFROMFILE);
	if (!bit)

		// failed to load, return failure to caller

		return NULL;

	// get bitmap dimensions

	BITMAP bitmap;
    GetObject( bit, sizeof(BITMAP), &bitmap );
	int surf_width=bitmap.bmWidth;
	int surf_height=bitmap.bmHeight;

	// create surface

	HRESULT ddrval;
	DDSURFACEDESC2 ddsd;
	ZeroMemory(&ddsd,sizeof(ddsd));
	ddsd.dwSize = sizeof(DDSURFACEDESC2);
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
	ddsd.dwWidth = surf_width;
	ddsd.dwHeight = surf_height;

	// attempt to create surface

	ddrval=lpdd->CreateSurface(&ddsd,&surf,NULL);

	// created ok?

	if (ddrval!=DD_OK) {

		// no, release the bitmap and return failure to caller

		DeleteObject(bit);
		return NULL;

	} else {

		// yes, get a DC for the surface

		surf->GetDC(&hdc);

		// generate a compatible DC

		HDC bit_dc=CreateCompatibleDC(hdc);

		// blit the interface to the surface

		SelectObject(bit_dc,bit);
		BitBlt(hdc,0,0,surf_width,surf_height,bit_dc,0,0,SRCCOPY);

		// release the DCs

		surf->ReleaseDC(hdc);
		DeleteDC(bit_dc);
	}

	// clear bitmap

	DeleteObject(bit);

	// return pointer to caller

	return surf;
}

Goed met deze functie laden we dus een bitmap. Hij maakt gebruik van GDI functies omdat dit makkelijker is dan alle functies zelf schrijven, maar dit is wel langzamer. Dit is geen probleem, want bitmaps laden doe je vaak in gecontroleerde momenten.

Eerst laden we de bitmap en slaan die op in bitmap. Dan gaan we de afmetingen opslaan van de afbeelding. Dan creëren we de surface. Als dat goed gegaan is, tekenen we de bitmap op de surface. Vervolgens releasen we de Device Context (DC).

Vervolgens maken we een teken functie die de afbeelding tekent.

void DrawImage()
{
	if(!lpbmp|!bInit)
		return;

	lpddsprimary->Blt(NULL,lpbmp,NULL,DDBLT_WAIT,NULL);

}

Deze kijkt of de initialisatie is uitgevoerd en of de bitmap geladen is. als dat gebeurt is tekent hij de bitmap.

Dan gaan we de initialisatie functie maken.

int Game_Init(int ncmdshow, HINSTANCE hinstance)
{
	if(!bInit)
	{
		int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
		int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
		WNDCLASSEX winclass; // this will hold the class we create
		HWND hwnd; // generic window handle

		// first fill in the window class stucture
		winclass.cbSize = sizeof(WNDCLASSEX);
		winclass.style = CS_DBLCLKS | CS_OWNDC |
						CS_HREDRAW | CS_VREDRAW;
		winclass.lpfnWndProc = WindowProc;
		winclass.cbClsExtra = 0;
		winclass.cbWndExtra = 0;
		winclass.hInstance = hinstance;
		winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
		winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
		winclass.lpszMenuName = NULL;
		winclass.lpszClassName = WINDOW_CLASS_NAME;
		winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

		// register the window class
		if (!RegisterClassEx(&winclass))
			return(0);

		// create the window
		if (!(hwnd = CreateWindowEx(NULL, // extended style
					    WINDOW_CLASS_NAME, // class
					    "Basic Window", // title
					    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
					    0,0, // initial x,y
					    ScreenWidth,// width
					    ScreenHeight,// height
					    NULL, // handle to parent
					    NULL, // handle to menu
					    hinstance,// app instance
					    NULL))) // extra creation parms
		return(0);

		// save the handles
		main_window_handle = hwnd;
		main_instance = hinstance;

		// we gaan nu naar de init functie
		// maak het hoofdobject van DirectDraw
		if(FAILED(DirectDrawCreateEx(NULL,(LPVOID*)&lpdd,
		   IID_IDirectDraw7,NULL)))
		{
			return(0);
		}

		// Stel samenwerking niveau in

		if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, 
		   DDSCL_NORMAL)))
		{
			return(0);
		}

		// maak de clipper

		if(FAILED(lpdd->CreateClipper(NULL,&lpclip,NULL)))
		{
			return(0);
		}

		lpclip->SetHWnd(0,main_window_handle);
		DDSURFACEDESC2	ddsd;

		ZeroMemory(&ddsd, sizeof(ddsd));
		ddsd.dwSize = sizeof(ddsd);
		ddsd.dwFlags = DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
		if(FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary,NULL)))
		{
			return(0);
		}

		// hier koppelen we nu de clipper aan de primary surface
		lpddsprimary->SetClipper(lpclip); 

		bInit = TRUE;

		lpbmp = bitmap_surface("dctutorial.bmp");

		// geef afbeelding weer

		DrawImage();

		return TRUE;
	}
 return 0;
}

Het makkelijkst is het om hem om de window creatie heen te bouwen. Zorg maar dat hij hier op lijkt. Het lijkt me allemaal wel duidelijk, anders stel je maar vragen op het forum ;).

In principe zijn we dan klaar. Hij moet op deze source lijken. Je moet nog een aantal kleine dingen veranderen. Neem hem dus goed door.


// NO MFC/////////////////////////////////////

#define WIN32_LEAN_AND_MEAN

#define WINDOW_CLASS_NAME "winclass1"
#define SafeRelease(x)	if(x) { x->Release(); x=NULL; }

///////////////////////////////////////////////////////

// INLUDES ////////////////////////////////////////////

#include 
#include 
#include 
#include 

//////////////////////////////////////////////////////

// GLOBALS //////////////////////////////////////////////////////////

HWND main_window_handle = NULL;
HINSTANCE main_instance = NULL;

LPDIRECTDRAW7		lpdd = NULL;
LPDIRECTDRAWSURFACE7	lpddsprimary = NULL;
LPDIRECTDRAWCLIPPER	lpclip = NULL;
LPDIRECTDRAWSURFACE7	lpbmp = NULL;

bool bInit = FALSE;

int Game_Init(int ncmdshow,HINSTANCE hinstance);
LPDIRECTDRAWSURFACE7 bitmap_surface(LPCTSTR file_name);
void DrawImage();
void CleanUp();
//////////////////////////////////////////////////////////////////////

// FUNCTIONS ////////////////////////////////////////////////////////

LPDIRECTDRAWSURFACE7 bitmap_surface(LPCTSTR file_name)
{
	HDC hdc;
	HBITMAP bit;
	LPDIRECTDRAWSURFACE7 surf;

	// load the interface bitmap

	bit=(HBITMAP) LoadImage(NULL,file_name,IMAGE_BITMAP,0,0,
	LR_DEFAULTSIZE|LR_LOADFROMFILE);
	if (!bit)

		// failed to load, return failure to caller

		return NULL;

	// get bitmap dimensions

	BITMAP bitmap;
    GetObject( bit, sizeof(BITMAP), &bitmap );
	int surf_width=bitmap.bmWidth;
	int surf_height=bitmap.bmHeight;

	// create surface

	HRESULT ddrval;
	DDSURFACEDESC2 ddsd;
	ZeroMemory(&ddsd,sizeof(ddsd));
	ddsd.dwSize = sizeof(DDSURFACEDESC2);
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
	ddsd.dwWidth = surf_width;
	ddsd.dwHeight = surf_height;

	// attempt to create surface

	ddrval=lpdd->CreateSurface(&ddsd,&surf,NULL);

	// created ok?

	if (ddrval!=DD_OK) {

		// no, release the bitmap and return failure to caller

		DeleteObject(bit);
		return NULL;

	} else {

		// yes, get a DC for the surface

		surf->GetDC(&hdc);

		// generate a compatible DC

		HDC bit_dc=CreateCompatibleDC(hdc);

		// blit the interface to the surface

		SelectObject(bit_dc,bit);
		BitBlt(hdc,0,0,surf_width,surf_height,bit_dc,0,0,SRCCOPY);

		// release the DCs

		surf->ReleaseDC(hdc);
		DeleteDC(bit_dc);
	}

	// clear bitmap

	DeleteObject(bit);

	// return pointer to caller

	return surf;
}

void DrawImage()
{
	if(!lpbmp|!bInit)
		return;

	lpddsprimary->Blt(NULL,lpbmp,NULL,DDBLT_WAIT,NULL);

}

void CleanUp()
{
	// geef interfaces in omgekeerde volgordevan aanroepen vrij!!!!!!

	SafeRelease(lpbmp);
	SafeRelease(lpddsprimary);
	SafeRelease(lpclip);
	SafeRelease(lpdd);

	return;
}

LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
	// this is the main message handler of the system
	PAINTSTRUCT ps; // used in WM_PAINT
	HDC hdc; // handle to a device context

	// what is the message
	switch(msg)
		{
		case WM_CREATE:
			{
			// do initialization stuff here

				// return success
			return(0);
			} break;

		case WM_PAINT:
			{
			// simply validate the window
			hdc = BeginPaint(hwnd,&ps);
			// you would do all your painting here
			DrawImage();
			EndPaint(hwnd,&ps);
			// return success
			return(0);
			} break;

		case WM_DESTROY:
			{
			CleanUp();
			// kill the application, this sends a WM_QUIT message
			PostQuitMessage(0);

			// return success
			return(0);
			} break;

			default:break;

		} // end switch

	// process any messages that we didn't take care of
	return (DefWindowProc(hwnd, msg, wparam, lparam));

} // end WinProc


////////////////////////////////////////////////////////////

int Game_Init(int ncmdshow, HINSTANCE hinstance)
{
	if(!bInit)
	{
		int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
		int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
		WNDCLASSEX winclass; // this will hold the class we create
		HWND hwnd; // generic window handle

		// first fill in the window class stucture
		winclass.cbSize = sizeof(WNDCLASSEX);
		winclass.style = CS_DBLCLKS | CS_OWNDC |
						CS_HREDRAW | CS_VREDRAW;
		winclass.lpfnWndProc = WindowProc;
		winclass.cbClsExtra = 0;
		winclass.cbWndExtra = 0;
		winclass.hInstance = hinstance;
		winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
		winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
		winclass.lpszMenuName = NULL;
		winclass.lpszClassName = WINDOW_CLASS_NAME;
		winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

		// register the window class
		if (!RegisterClassEx(&winclass))
			return(0);

		// create the window
		if (!(hwnd = CreateWindowEx(NULL, // extended style
					    WINDOW_CLASS_NAME, // class
					    "Basic Window", // title
					    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
					    0,0, // initial x,y
					    ScreenWidth,// width
					    ScreenHeight, // height
					    NULL, // handle to parent
					    NULL, // handle to menu
					    hinstance,// app instance
					    NULL))) // extra creation parms
		return(0);

		// save the handles
		main_window_handle = hwnd;
		main_instance = hinstance;

		// we gaan nu naar de init functie
		// maak het hoofdobject van DirectDraw
		if(FAILED(DirectDrawCreateEx(NULL,(LPVOID*)&lpdd,
		   IID_IDirectDraw7,NULL)))

		{
			return(0);
		}

		// Stel samenwerking niveau in

		if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, 
		   DDSCL_NORMAL)))
		{
			return(0);
		}

		// maak de clipper

		if(FAILED(lpdd->CreateClipper(NULL,&lpclip,NULL)))
		{
			return(0);
		}

		lpclip->SetHWnd(0,main_window_handle);
		DDSURFACEDESC2	ddsd;

		ZeroMemory(&ddsd, sizeof(ddsd));
		ddsd.dwSize = sizeof(ddsd);
		ddsd.dwFlags = DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
		if(FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary,NULL)))
		{
			return(0);
		}

		// hier koppelen we nu de clipper aan de primary surface
		lpddsprimary->SetClipper(lpclip); 

		bInit = TRUE;

		lpbmp = bitmap_surface("dctutorial.bmp");

		// geef afbeelding weer

		DrawImage();

		return TRUE;
	}
 return 0;
}

// WINMAIN /////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
	MSG msg;
 	Game_Init(ncmdshow,hinstance);
	while(TRUE)
	{
		// test if there is a message in queue, if so get it
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
			{
			// test if this is a quit
			if (msg.message == WM_QUIT)
			break;

			// translate any accelerator keys
			TranslateMessage(&msg);

			// send the message to the window proc
			DispatchMessage(&msg);
			} // end if

	} // end while

// return to Windows like this
return(msg.wParam);

} // end WinMain

Goed, download dctutorial.bmp en zet deze in je project map. Compileer het programma en voer het uit. Hij moet het nu doen.

Veel succes,
Paul
pderaaij@kabelfoon.nl voor problemen.