Writing a OpenGL Program and Rotating Items

Author:

John "Cryect" Rittenhouse


Difficulty:
Pretty Easy Overall

Well here it is at last what several people have bugged me on. I will later release info on drawing textured polygons and stuff without using an API since everyone should do that at one time or another. I've left that out because I wanted to just show people how simple rotating was since this is a commonly asked question and using OpenGL, since if you become a programmer you most likely will be using an API eventually. This will be in Microsoft Visual C++ 5.0 since that is what I will assume most are programming in and it can easily be transferred to 6.0. I would do this in Borland also but I do not use Borland on a regular use but you should have no time transferring the code just need to set up libraries differently.

Okay first go to "File-New" then select Win32 Application. Set location to "C:\" or wherever you wish it to be located. We will call the project "OpenGLTest" then press ok. It now will create a empty project for you. Click back on "File-New" and then select "C++ Source File" and then put for file name "tut2.cpp". Check to make sure add to project is selected and press ok.

Alright now here comes the coding section. You will be adding at the top this code for header files and some function declarations.

	#include <windows.h>
	#include <gl/gl.h>
	#include <malloc.h>
	#include <math.h>
	#include <stdio.h>
	// Function Declarations
	LRESULT CALLBACK 
	WndMsgHandler( HWND hWnd, UINT message,
	WPARAM wParam, LPARAM lParam );
	VOID EnableOpenGL( HWND hWnd, HDC * hDC, HGLRC * hRC );
	VOID DisableOpenGL( HWND hWnd, HDC hDC, HGLRC hRC );

I will describe each of the functions a little later now to start off with the winmain section. Here is how the code will be for that part

	int WINAPI 
	WinMain( HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int iCmdShow )
	{
	WNDCLASS wc;
	HWND hWnd;
	HDC hDC;
	HGLRC hRC;
	MSG msg;
	BOOL bQuit = FALSE;
	float theta 0.0f;

 

Now that isn't much but it will run but we are done yet. Add this code in which register the window class and then creates the window

	// register window class
	wc.style = CS_OWNDC;
	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 = "GLTest";
	RegisterClass( &wc );
	
	// create main window
	hWnd = CreateWindow("GLTest", "QDevels OpenGL Tut2 Test", 
	WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE,
	20,20, 220,220, NULL, NULL, hInstance, NULL );

Now we will have to enable OpenGL and we will use the function we have already declared and will go back later to adding the code for it in to the function.

	// enable OpenGL for the window
	EnableOpenGL( hWnd, &hDC, &hRC );

Now here is the looping section that will handle all the messages and keep up with the drawing. Pretty simple and I will explain it later.

	// main loop
	while ( !bQuit ) {
	// check for messages 
	if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
	
	// handle or dispatch messages and quit if necessary
	if ( msg.message == WM_QUIT ) {
	bQuit = TRUE;
	} else {
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}
		
	} else {
	// must not have been messages so go ahead and draw
	// here is the OpenGL drawing code
	glClearColor( 1.0f, 1.0f, 1.0f, 0.0f );
	glClear( GL_COLOR_BUFFER_BIT );
	// draw the triangle
	glBegin( GL_TRIANGLES );
	glColor3f( 1.0f, 0.0f, 0.0f ); glVertex2d( cos(theta), sin(theta));
	glColor3f( 0.0f, 1.0f, 0.0f ); glVertex2d( cos(theta+2.0944f), sin(theta+2.0944f));
	glColor3f( 0.0f, 0.0f, 1.0f ); glVertex2d( cos(theta+4.1888f), sin(theta+4.1888f));
	glEnd();
	
	// flip the buffers
	SwapBuffers( hDC );
	
	theta += 0.03f;
	if(theta>6.28f)
	 theta-=6.28f;
	}
	
	}

What that did overall is check for messages, if there were messages process them and set the loop to quit if necessary, else if there weren't any messages it would draw a triangle, flip the buffers and increase the angle. Now here is the code to finish that function.

// shutdown OpenGL
DisableOpenGL( hWnd, hDC, hRC );
	
DestroyWindow( hWnd );
	
return msg.wParam;
	
}

Now you need the code for the rest of the functions. Just put them in and then I will discuss what the ones do you need to worry about.

LRESULT CALLBACK 
WndMsgHandler( HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam )
{
	
switch ( message ) {
	
case WM_CREATE:
return 0;
	
case WM_CLOSE:
PostQuitMessage( 0 );
return 0;
	
case WM_DESTROY:
return 0;
	
case WM_KEYDOWN:
switch ( wParam ) {
	
case VK_ESCAPE:
PostQuitMessage( 0 );
return 0;
	
}
return 0;
	
default:
return DefWindowProc( hWnd, 
message, wParam, lParam );
	
}
	
}
	
// Enable OpenGL
	
VOID EnableOpenGL( HWND hWnd, HDC * hDC, HGLRC * hRC )
{
PIXELFORMATDESCRIPTOR pfd;
int iFormat;
	
// get the device context 
*hDC = GetDC( hWnd );
	
// set the pixel format for the device context
ZeroMemory( &pfd, sizeof( pfd ) );
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | 
PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
iFormat = ChoosePixelFormat( *hDC, &pfd );
SetPixelFormat( *hDC, iFormat, &pfd );
	
// create and enable the render context
*hRC = wglCreateContext( *hDC );
wglMakeCurrent( *hDC, *hRC );
	
// set the settings
glOrtho(-1.05,1.05,-1.05,1.05,-1.0,1.0);
glEnable(GL_BLEND);
}
	
// Disable OpenGL
	
VOID DisableOpenGL( HWND hWnd, HDC hDC, HGLRC hRC )
{
wglMakeCurrent( NULL, NULL );
wglDeleteContext( hRC );
ReleaseDC( hWnd, hDC );
}

Oh yes before you can run it you need to add the libs being used. Goto "Project-Settings" and then click on the link tab. Add to the Object/Library modules "opengl32.lib". That is the stuff that is needed. Now you can build and run. You should see a triangle like this picture rotating.

I will now explain what happens in EnableOpenGL. First we will go over this code.

	// set the pixel format for the device context
	  ZeroMemory( &pfd, sizeof( pfd ) );
	  pfd.nSize = sizeof( pfd );
	  pfd.nVersion = 1;
	  pfd.dwFlags = PFD_DRAW_TO_WINDOW | 
	    PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	  pfd.iPixelType = PFD_TYPE_RGBA;
	  pfd.cColorBits = 24;
	  pfd.cDepthBits = 16;
	  pfd.iLayerType = PFD_MAIN_PLANE;
	  iFormat = ChoosePixelFormat( *hDC, &pfd );
	  SetPixelFormat( *hDC, iFormat, &pfd );

 

That code sets up the pixel format. It has set it so it is RGBA mode with 24 color bits and it is double screen buffer. It is pretty simple actually that code you are looking at and you can look elsewhere for more detail. Now to look at this code.

	// set the settings
	  glOrtho(-1.05,1.05,-1.05,1.05,-1.0,1.0);
	  glEnable(GL_BLEND);

Now what that code does is set the boundaries for the window since OpenGL doesn't handle stuff by pixels but by floats and doubles so and resolution supposedly works just as well. I also enabled blending which is useful. Now it appears that I haven't taught you how to rotate but in a way I have. Here is the general formula for rotating on the z-axis.

x=cos(theta)*x-sin(theta)*y
y=cos(theta)*y+sin(theta)*x

This can be applied to all of the other axis's to quite easily. Just rotate in your head the axis's to determine which one should take the place of x and y. Also the computer uses radians instead of degrees 1 degree is equal to pi/180. Hint sin and cos are slow and it is better to create a 1d array for both and load all the values and there and access them as necessary. Also this way you can create your own degree system. 256 values used to work fine but not anymore and it was nice to have it loop around automatically when it overflowed. Well that is about it for now I guess and will discuss more later.

John "Cryect" Rittenhouse

Source Code

This site, and all content and graphics displayed on it,
are ©opyrighted to the Quake DeveLS team. All rights received.
Got a suggestion? Comment? Question? Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for there great help and support with hosting.
Best viewed with Netscape 4 or IE 3