win32/Opengl....

Discuss programming topics that involve the OpenGL API.
Post Reply
Zylyx_
Posts: 111
Joined: Wed Dec 05, 2007 6:52 pm
Location: scotland, uk

win32/Opengl....

Post by Zylyx_ »

Hi all. My head is hurting from trying to modularize window creation code using OpenGL and Win32 API.

Before anyone accuses of careless cut & paste, I wrote all of this from scratch while currently reading the OpenGL Superbible, Nehe tutorials and Game Tutorials LLC. The problem isnt that I dotn understand the code. The problem is that the compiler doesnt understand me (how great would that be?). Anyways, excuse my arrogance.

Here is my code:



gl_win32_core.h

Code: Select all


//This file contains all of the core Win32/OpenGL function prototypes, system includes and global variables required for setting up a basic
//windowed rendering environment for OpenGL application using Win32. 

#ifndef _GL_WIN32_CORE_H_
#define _GL_WIN32_CORE_H_

//Library includes
//#pragma comment(lib, "opengl32.lib")
//#pragma comment(lib, "glu32.lib")

//System includes
#include <windows.h>		// Must have for Windows platform builds
#include <tchar.h>			// Requires to convert each character in each string argument into a 2 byte character. Win32 treats all string characters as UNICODE.
#include <gl\gl.h>			// Microsoft OpenGL headers (version 1.1 by themselves)
#include <gl\glu.h>			// OpenGL Utilities

//OpenGL hRC specific 
GLfloat windowWidth;
GLfloat windowHeight;						

//Win32 specific global variables
static LPCTSTR lpszAppName = L"Swogle";
static HWND  g_hWnd;		//Window handle	
static RECT  g_rRect;		//Window rendering dimensions					
static HDC   g_hDC;			//GDI hardware device context				
static HGLRC g_hRC;			//Hardware renedeing contect for OpenGL	
static HINSTANCE g_hInstance; //App instance (id)

///////////////////////////////////
//Core Win32 function prototypes
///////////////////////////////////

//WinMain
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

//WindowProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam);

//Prog main loop
WPARAM MainLoop();

//Resiter and create new window
HWND CreateMyWindow(LPSTR strWindowName, int nWidth, int nHeight);

/////////////////////////////////////
//Core OpenGL function prototypes
////////////////////////////////////

//Set display format
void SetDCPixelFormat();

//Change window size
void ChangeSize(GLsizei w, GLsizei h);

//This draws everything to the screen
void RenderScene();

//Free all memeory associated with the program
void DeInit();

#endif

gl_win32_core.cpp

Code: Select all

//This file contains all of the core Win32/OpenGL function implementations required for setting up a basic
//windowed rendering environment for OpenGL application using Win32. 

#include "gl_win32_core.h"

//=======================================================================================================================================================
//=======================================================================================================================================================
//Core Win32 function implementations
//=======================================================================================================================================================
//=======================================================================================================================================================

/////////////////////////////////////
//WinMain function implementation
////////////////////////////////////
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	//HWND hWnd; //Local window handle

	//Create new window
	g_hWnd = CreateMyWindow("SWOGLE 1.0", 800, 600);

	//If window handle is invalid, quit the program
	if(g_hWnd == NULL)
	{
		return TRUE;
	}

	g_hInstance = hInstance;

	//Initialize the program here
														
	//Run message loop and return the result
	return MainLoop();						
}

//////////////////////////////////////////////
//CreateMyWindow() function implementation
//////////////////////////////////////////////
HWND CreateMyWindow(LPCTSTR strWindowName, int nWidth, int nHeight)
{
	HWND hWnd; //Local window handle
	WNDCLASS wc; //Window class that will be registered

	//clear the memory first
    memset(&wc, 0, sizeof(wc));
    
    //Set all of the attributes up for the window class & register it
    wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; //CS_OWNDC: Tells Windows to create a device context just for this window
    wc.lpfnWndProc = WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
    wc.hInstance = g_hInstance;
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = NULL; //No need for background brush when using OpenGL
	wc.lpszMenuName = NULL; //No menus either
    wc.lpszClassName = lpszAppName;
	
	//Now, register the windows class with Windows
    RegisterClass(&wc);							

	//Create the main application window
	hWnd = CreateWindow(
							lpszAppName, 
							strWindowName, 
							WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, //WS_CLIPCHILDREN & WS_CLIPSIBLINGS are used to prevent 
							CW_USEDEFAULT,											 //the OpenGL rendering context from rendering into other  
							CW_USEDEFAULT,											 //windows. An OpenGL rendering context must be associated
							nWidth,													 //with only one active window at a time. 
							nHeight, 
							NULL, 
							NULL, 
							g_hInstance, 
							NULL
						);

	//If there is no window handle...
	if(!hWnd)
	{
		return NULL;
	}

	// Show the window
	ShowWindow(hWnd, SW_SHOWNORMAL);	

	// Draw the window
	UpdateWindow(hWnd);									

	//Sets Keyboard Focus To The Window
	SetFocus(hWnd);											

	return hWnd;
}

/////////////////////////////////////////
//WindowProc() function implementation
////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hWnd,UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	switch(nMsg)
	{
		//Setup the window for OpenGL here
	case WM_CREATE:
		{
			//Store device context
			g_hDC = GetDC(hWnd);

			//Select the pixel format
			SetDCPixelFormat();

			//Create the rendering context and make it current
			g_hRC = wglCreateContext(g_hDC);
			wglMakeCurrent(g_hDC, g_hRC);

			//Create a timer that calls specific functions 30 times a second
			SetTimer(hWnd, 30, 1, NULL);

			return 0;
		}
		//When the window is destroyed, clean up after OpenGL
	case WM_DESTROY:
		{
			//Kill timer
			KillTimer(hWnd, 101);

			//Delete any OpenGL-allocated memory here

			//Deselect current rendering context and delete it
			wglMakeCurrent(g_hDC, NULL);
			wglDeleteContext(g_hRC);

			//Terminate program after window is destroyed
			PostQuitMessage(0);

			return 0;
		}
		//Change window size appropriately (keeping all clipping bounds for the viewport and rendering volume in order)
	case WM_SIZE:
		{
			ChangeSize(LOWORD(lParam), HIWORD(lParam));
			return 0;
		}
		//Call the timer function here. The MoveSquare() function goes here, and after it is called, the window is
		//invalidated so that it will be drawn again. This happens 30 times a second.
	case WM_TIMER:
		{
			//Anim code goes here

			//Invalidate the rect
			InvalidateRect(hWnd, NULL, FALSE);

			return 0;
		}
		//The WM_PAINT message is sent by Windows everytime the screen needs to be updated. And that's what we do here
	case WM_PAINT:
		{
			//Render current scene
			RenderScene();

			//Swap buffers
			SwapBuffers(g_hDC);

			//Validate the newly painted client area
			ValidateRect(hWnd, NULL);

			return 0;
		}
	default:
		{
			return DefWindowProc(hWnd, nMsg, wParam, lParam);
		}
	}
	return 0;
}

///////////////////////////////////////
//MainLoop() function implementation
//////////////////////////////////////
WPARAM MainLoop()
{
	MSG msg;
	
	while(1)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) //Peek to see if there are any messages in the que...
		{
			if (!GetMessage(&msg, NULL, 0, 0)) //Same as above, except the message get's removed from the message queue
			{
				return msg.wParam; //If there is no message to get, return wParam, which returns a 0 upon normal program termination
			}

			//do the rest
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
		else
		{
			//call game logic code here
		}
	}
	
	//Window cleanup code goes here...
	DeInit();
    
	//Return the exit code for the application.
	return msg.wParam;
}

//=======================================================================================================================================================
//=======================================================================================================================================================
//Core OpenGL function implementations
//=======================================================================================================================================================
//=======================================================================================================================================================

//////////////////////////////////////////////////////////////////////////
//Hardware device context pixel format setting function implementation
/////////////////////////////////////////////////////////////////////////
void SetDCPixelFormat()
{
	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,
		0,
		0,
		0,0,0
	};

	//Choose a best matched pixel format fitting the above description
	nPixelFormat = ChoosePixelFormat(g_hDC, &pfd);

	//Set the pixel format for the HDC
	SetPixelFormat(g_hDC, nPixelFormat, &pfd);
}

///////////////////////////////////////////
//RenderScene() function implementation  
//////////////////////////////////////////
void RenderScene()
{
	//Set the background clearing color to gray
	glClearColor(0.25f, 0.25f, 0.25f, 1.0f);

	// Clear the window with current clearing color
	glClear(GL_COLOR_BUFFER_BIT);
}

///////////////////////////////////////
//ChangeSize function implementation 
//////////////////////////////////////
void ChangeSize(GLsizei w, GLsizei h)
{
	GLfloat aspectRatio; //Aspect artio for drawing the window

	//Avoid dividing by zero
	if(h == 0)
	{ 
		h = 1;
	}

	//Set viewport to window dimenstions (0, 0 is the clipping region; w, h are the width and height of the viewport in pixels
	glViewport(0, 0, w, h);

	//Reset coordinate system
	glMatrixMode(GL_PROJECTION);//Define projection matrix
	glLoadIdentity();//Load identity matrix for GL_PROJECTION (reset matrix)

	aspectRatio = (GLfloat)w / (GLfloat)h;

	if(w <= h)
	{
		windowWidth = 100;
		windowHeight = 100 / aspectRatio;
		glOrtho(-100.0, 100.0, -windowHeight, windowHeight, 1.0, -1.0);
	}
	else
	{
		windowWidth = 100 * aspectRatio;
		windowHeight = 100;
		glOrtho(-windowWidth, windowWidth, -100.0, 100.0, 1.0, -1.0);
	}

	glMatrixMode(GL_MODELVIEW);//Set the matrix mode to effect all of the current geometry that is drawn
	glLoadIdentity();//Reset the matrix
}

/////////////////////////////////////////////////
//Free all memeory associated with the program
////////////////////////////////////////////////
void DeInit()
{
	//Add all cleanup code associated with the window here

	//Unregister the window class
	UnregisterClass(lpszAppName, g_hInstance);
}

The error is this:

Code: Select all

1>Compiling...
1>gl_win32_core.cpp
1>Linking...
1>gl_win32_core.obj : error LNK2019: unresolved external symbol "struct HWND__ * __cdecl CreateMyWindow(char *,int,int)" (?CreateMyWindow@@YAPAUHWND__@@PADHH@Z) referenced in function _WinMain@16
1>C:\Users\Vladeta\Desktop\game development\opengl_progs\progs\gl_win32_template\Debug\gl_win32_template.exe : fatal error LNK1120: 1 unresolved externals

My guess is that the cuplrit is my idea of making all of the window and hardware contexts, as well as the application instance, global.

However, seeing as they are declared as static, they should not loose their assigned value. If I declare them as extern, I still get the same error.

Any help is much appreciated. :)
....noodle...
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

your prototype of CreateMyWindow doesn't match
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

I think your unicode thing might be causing you trouble. I'm guessing that you're probably using a more recent version of Visual C++ (possibly 2008 Express?) which defaults projects to unicode. If you go to Configuration Properties | General | Project Defaults | Character Set and set it to Multi-Byte you'll get char * compatibility back.

Totally unrelated, but maybe request a 24 bit depth buffer instead of a 16 bit one? A 24 bit depth buffer is actually really 32 bits with 8 unused, meaning that it's measurably faster on most reasonably modern - and even most older - hardware. The great thing about ChoosePixelFormat is that if 24 bits isn't available it'll just give you 16, so you've nothing to lose. There's no such thing as a 32 bit depth buffer on most hardware by the way, the options are always either 16 bit, 24 bit with 8 unused or 24 bit with 8 stencil.

Even though you're doing OpenGL I think it's well worth your while to download and study the reference topics in the DirectX SDK. Direct3D is a little bit "closer to the metal" than OpenGL is, and as such you get to learn a great deal more about format sizes and structures and suchlike (and also topics such as why writing to the front buffer is not a good idea on a lot of 3D cards - because Direct3D doesn't support it, and some drivers are written to the D3D spec with OpenGL tacked on afterwards). Learning more about the environment you're coding for is always a good thing, eh?
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Zylyx_
Posts: 111
Joined: Wed Dec 05, 2007 6:52 pm
Location: scotland, uk

Post by Zylyx_ »

LOL! I totaly missed that. Man, that hungarian notation is hard on the eyes.

Yeah, I also made all the global variables static, otherwise I ended up getting more linker errors (still need to fully understand why though, too tired to read about it atm, probably check it out tomorrow).

Anyways, my basic framework is up adn running now.

I named it SWOGLE (Simple Windowing for OpenGL - Easy).

Now I'm going to add the following features:

1. TTF rendering
2. Keyboard and mouse input support
3. .WAV file playback


My main goal is to build a simple game ofPpong, and create basic re-usable windowing code with support for OpenGL.

The reason for this is that I'm starting my second year of my university course in computer games technology. Up until last year, we only really did graphics programming with the GBA (2D only), and we are going to start learning OpenGL and Windows programming this year.

I spent a good amount of my summer holiday learning the basics of Win32 API, and I just started learning OpenGL. When I get back to uni, my first assigment after re-learning windows programming for the first six weeks will be to make a simple GDI based 2D game. After that we start going into basic OpenGL programming.

We are also going to be learning basic PS2 programming this year, so it should be fun. But yeah, half the people last year couldnt figure out how todo the basic stuff like draw a line or a circle, but I managed to learn the ins and outs of that (bresenhams line & circle algorithm). We also did sprite programming and such, most of which I forgot by now, lol. I made a basic pong game for the GBA.

Anyways, again, to cut my life story short, thank you for your help.

I'm only starting to learn 3D graphics programming, but I'll look into the DirectX SDK docs in due time. Only after this year (in 3rd year), do we get to work with DirectX and do proper 3D graphics programming (shaders and all the bells & whistles). This year we are only doing the basic fixed function pipleline graphics programming with OpenGL. I'll definately read more into the different display formats and the effects of using different buffer sizes, although I think I read about it recently, but it kinda slipped my mind, again, lol. Guess it's always good to stay ahead.

So yeah...long post...bed time. Night!

P.S. Yeah, the MS Unicode thing very annoying. Read about it in the Windows Programming guide, something about each character being 2 bytes and not 8 bits, in order to accomodate most of the written languages. I had a funny thing happen to me recently, I set the window style to default(I thinkt to 0) after getting errors in my window creation routine, and I ended up with Chinese characters in the title bar and the message box! Fun times...
....noodle...
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

1. TTF rendering
two ways. the ms ones convert a font into a display list, which can be draw with drawlists easily enough.
a more modern way to do it is to prerender each glyph into a texture and draw that way using pixel accuracy. freetype2 can do the prerendering.

2. Keyboard and mouse input support
quake uses getcursorpos/setcursorpos to grab the cursor pos and reset it, calculating how much it moved by, thus giving a mouse delta.
good luck disabling mouse acceleration.
google for ToUnicode and WM_KEYDOWN. and figure out some sane way to map virtual keys and logical chars properly.

3. .WAV file playback
sndPlaySound for the lazy.
directsound is emulated now anyway. look into using openal or something I guess. its as fast as any other sound api nowadays, and reasonably portable.
Zylyx_
Posts: 111
Joined: Wed Dec 05, 2007 6:52 pm
Location: scotland, uk

Post by Zylyx_ »

Thanx for the info!

Yeah, the tutorial I'm currently reading uses display lists for TTF rendering, so I might try that first, but I'll check out the method you mentioned as well.

As for the sound and input, I'll read up on what you said. I'm not really interested in using any fancy api for the sound like openal or fmod, I just need the bare basics to play a .wav file.
....noodle...
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

PlaySound will do the WAV playback thing but it's not going to be high performance and you'll lose a lot of flexibility in terms of mixing, panning, etc. If you just want to make a noise and you're not concerned about effects or latency I guess it's fine.

DirectInput might be good for mouse input. It's a nice API that's incredibly simple to set up and use (create an interface, create a device, acquire it for use and read events). It's officially deprecated and is really just a wrapper around the standard Windows messaging functions, and doesn't work too well with some touchpads, but it does have the advantages of disabling mouse acceleration for you, you don't need to worry about stuff like re-centering the mouse after each move or showing/hiding the cursor, and you can consolidate all of your mouse code in the one place.

Don't use it for keyboard input though, that can get real messy as it uses it's own key code table which needs to be translated, and it can frequently send multiple key events with one press which will need to be filtered.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Well, it's a bit off-topic (since it's not OpenGL), but I stumbled at this DirectX wrapper engine another day. Even if you're not going to do 2D gaming and use Direct3D, it has a really good support to input and sound API's. Also, it supports bitmap fonts created from TTF (even has a small utility to conversion).
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Zylyx_
Posts: 111
Joined: Wed Dec 05, 2007 6:52 pm
Location: scotland, uk

Post by Zylyx_ »

Hmm, that HGE engine does look interesting.

Like I said, all of this is a learning experiance for me, so I dont mind doing the bare basics (infact, i find it neccessary). But all the libs you mentioned are really good for building a full game/demo without rebuidling the wheel.

Unfortunately, for my university work we are restircted to using GDI for 2D windows graphics programming and OpenGL for 3D. We are also doing 2D graphics programming on the PS2, which I'm guessing is going to be similart to the GBA programming we did last year, probably a bit more complex.

However, coming from a minor SDL programming background, I do appreciate the easy of using straight off the shelfs libs and wrappers like the ones you mentioned. Makes life so much more easier.

Anywho, I'm gonna start implementing the TTF rendering for my Swogle lib now. Results and headaches coming soon.
....noodle...
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

I read in their forums there's at least one non-official HGE port to OpenGL. Don't know the current status, but you may want to check it out.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Post Reply