Fullscreen
Aangezien een spel in een window wel werkt is een spel in fullscreen natuurlijk veel realistischer. De volgende stap is dan ook een fullscreen OpenGL window waar we het window van de vorige handleiding als basis gebruiken.

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>

HDC g_HDC;
bool fullscreen = TRUE;

////////////////////////////////////////////////////////////////////////////
//Pixelformatdescriptor
////////////////////////////////////////////////////////////////////////////
void setuppfd(HDC hDC)
{
	int nPixelFormat;

	static PIXELFORMATDESCRIPTOR pfd={
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW|
		PFD_SUPPORT_OPENGL|
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,
		0,0,0,0,0,0,
		0,
		0,
		0,
		0,0,0,0,
		16,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0,0,0};

		nPixelFormat = ChoosePixelFormat(hDC, &pfd);
		SetPixelFormat(hDC,nPixelFormat, &pfd);
}

///////////////////////////////////////////////////////////////////////////
//Windows Procedure
///////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM	lParam)	
{
	static HGLRC hRC;
	static HDC hDC;
	int width, height;

	switch (message)
	{
		case WM_CREATE:
			hDC = GetDC(hwnd);
			g_HDC = hDC;
			setuppfd(hDC);

			hRC = wglCreateContext(hDC);
			wglMakeCurrent(hDC,hRC);
			break;

		case WM_CLOSE:
			wglMakeCurrent(hDC,NULL);
			wglDeleteContext(hRC);

			PostQuitMessage(0);
			break;
			
		case WM_SIZE:
			height = HIWORD(lParam);
			width = LOWORD(lParam);

			if (height==0)
			{
				height=1;
			}

			glViewport(0,0,width,height);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f,
   				       1000.0f);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			break;
			
		case WM_KEYDOWN:
			switch (wParam)
			{
				case VK_ESCAPE:
					wglMakeCurrent(hDC,NULL);
					wglDeleteContext(hRC);
					PostQuitMessage(0);
					break;
			}

	}
	return DefWindowProc(hwnd,message,wParam,lParam);
}

//////////////////////////////////////////////////////////////////////////////
//Main Functie
//////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,	HINSTANCE hPrevInstance,
		   LPSTR lpCmdLine, int	nShowCmd)
{
	WNDCLASSEX	wc;	
	HWND hwnd;
	MSG		msg;
	DWORD dwExStyle, dwStyle;
	int width=800,height=600,bits=32;
	bool	done=FALSE;
	RECT windowRect;
					
	windowRect.top = 0;
	windowRect.left = 0;
	windowRect.bottom = height;
	windowRect.right = width;

	wc.cbSize		= sizeof(WNDCLASSEX);	
	wc.style		= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;	
	wc.hIcon		= LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);	
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "OpenGL";
	wc.hIconSm		= LoadIcon(NULL,IDI_WINLOGO);

	RegisterClassEx(&wc);

	if(fullscreen)
	{
	DEVMODE devModeScreen;
	memset(&devModeScreen, 0,sizeof(devModeScreen));
	devModeScreen.dmSize = sizeof(devModeScreen);
	devModeScreen.dmPelsWidth = width;
	devModeScreen.dmPelsHeight = height;
	devModeScreen.dmBitsPerPel = bits;
	devModeScreen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;

	ChangeDisplaySettings(&devModeScreen, CDS_FULLSCREEN);

	dwExStyle = WS_EX_APPWINDOW;
	dwStyle = WS_POPUP;
	ShowCursor(FALSE);
	}

	else
	{
		dwExStyle = NULL;
		dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU;
	}

	
	AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

	hwnd=CreateWindowEx(dwExStyle,
			    "OpenGL",	
			    "glwindow",
			    dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
			    0,0,
			    windowRect.right - windowRect.left,
			    windowRect.bottom - windowRect.top,
			    NULL,
			    NULL,
			    hInstance,	
			    NULL);
	
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);
	
	while(!done)
	{
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);	
			
			if (msg.message==WM_QUIT)
			{
				done=TRUE;
			}
			else
			{
				TranslateMessage(&msg);	
				DispatchMessage(&msg);
			}
	}
	return (msg.wParam);
}
Het eerste nieuwe deel komen we tegen in de window procedure, namelijk de WM_SIZE message.

case WM_SIZE:
			height = HIWORD(lParam);
			width = LOWORD(lParam);

			if (height==0)
			{
				height=1;
			}

			glViewport(0,0,width,height);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f,
			 	       1000.0f);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			break;
Het eerste wat we hier doen is de hoogte en breedte van het window bepalen waardoor we vervolgens kunnen voorkomen dat het window kleiner dan 0, of negatief wordt. Vervolgens moet de viewport aangepast worden naar de nieuwe window grootte, en moeten we de projection matrix resetten door deze te selecteren door de glMatrixMode(); functie en vervolgens de identity matrix in te stellen. Nu de projection matrix gereset is kunnen we deze opnieuw instellen door de gluPerspective(); functie, of je kunt dit zelf doen. gluPerspective ontvangt 4 parameters waarmee je het perspectief kunt instellen, de eerste stelt de kijk hoek van de y-as in, de volgende parameter stelt de kijk hoek van de x-as in door de breedte te delen door de hoogte, de laatste 2 parameters geven de near en far clipping plane aan. De near en far clipping planes kun je je voorstellen als de afstand die je kunt kijken waar near de monitor is en far een bepaalde afstand in de verte (hier 1000). Als je deze parameters samen met de andere parameters van gluPerspective gebruikt krijg je een afgestompte piramide als kijkveld waar het kleine vlak je monitor is en het grote vlak in de verte is (near en far clipping planes). OpenGL maakt vervolgens van deze piramide een blok, de far clipping plane wordt dus verkleind, waardoor voorwerpen die dicht bij zijn nauwelijks verkleind worden en voorwerpen die ver weg zijn veel verkleind worden en daardoor perspectief ontstaat. Als laatste gaan we dan nog de modelview matrix selecteren en deze resetten waardoor deze gelijk wordt aan de identity matrix. Meer info over matrices, transformations.

Het volgende nieuwe deel dat we tegenkomen is de windowrect class die ervoor zorgt dat in windowed mode de randen van het window de scene niet overlappen.

windowRect.top = 0;
windowRect.left = 0;
windowRect.bottom = height;
windowRect.right = width;

Om deze instellingen vervolgens te gebruiken moeten we de functie AdjustWindowRectEx oproepen die 4 parameters ontvangt. De parameters zijn als eerste natuurlijk de windowRect parameters, de window style parameters, de derde parameter houden we altijd false, en als laatste de extended window style parameters.

AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

En dan zijn we uiteindelijk bij de fullscreen instellingen gekomen waar je eerst gaat kijken of het fullscreen is of niet. Als het programma in windowed mode loopt hoef je alleen maar de dwExStyle en dwStyle instellingen in te vullen die gebruikt worden in CreateWindowEx. Als het fullscreen is dan zul je echter wat meer moeite moeten doen door de DEVMODE class in te stellen. Ik neem aan dat de velden voor zich spreken, het enige waar je voor moet zorgen is dat de breedte en hoogte overeen komen met de grootte van je window. Als je de DEVMODE instellingen hebt gemaakt moet je ze nog doorgeven aan de ChangeDisplaySettings();. De CDS_FULLSCREEN flag die je hier ook aan meegeeft zorgt er voor dat je de taakbalk niet meer ziet en dat windows het beeld verder met rust laat. Als laatste moet je nog even de dwExStyle en dwStyle instellen die weer gebruikt worden in CreateWindowEx, en zorg je ervoor dat je de cursor niet meer ziet.


if(fullscreen)
{
  DEVMODE devModeScreen;
  memset(&devModeScreen, 0,sizeof(devModeScreen));
  devModeScreen.dmSize = sizeof(devModeScreen);
  devModeScreen.dmPelsWidth = width;
  devModeScreen.dmPelsHeight = height;
  devModeScreen.dmBitsPerPel = bits;
  devModeScreen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;

  ChangeDisplaySettings(&devModeScreen, CDS_FULLSCREEN);

  dwExStyle = WS_EX_APPWINDOW;
  dwStyle = WS_POPUP;
  ShowCursor(FALSE);
}

else
{
  dwExStyle = NULL;
  dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU;
}
Voor de rest is het window helemaal gelijk gebleven op de CreateWindowEx(); functie na dan waar dus de instellingen van fullscreen of windowed mode komen maar dat spreekt voor zich. Dit is dan je complete window waarmee je aan de gang kunt om je eigen 3D werelden in te maken.

Succes

Vampire,