![]()
::[ OPENGL WINDOWS SCREEN SAVER TUTORIAL
]::[ Source
]::
::[ INTRODUCTION ]:: As my interest for OpenGL and Windows Programming have grown I found displaying graphics as a screen saver was one way to display my OpenGL creations. I, personally, have used many tutorials over the internet and this is the first time I have decided to share what I’ve learned. I do not claim to be an expert, I do not have a computer science degree, nor am I a definitive resource for creating screen savers. I do have a interest in programming and computer graphics and hope to help you in creating your first screen saver. It is possible that this document has mistakes so use at your own risk. As a newbie myself to C/C++ programming I am only familiar with VC++.Net compilers so I will weight heavily on my understanding of this particular compiler. I must first explain that I will not teach you how to program in OpenGL. The best source for this is NeHe’s OpenGL tutorial site at: http://nehe.gamedev.net. Nor is this meant to be a tutorial in C programming. Some aspects of this code require you to use a reference. If I were to explain every piece of code in detail this tut would be much longer. I hope you only use this as a backbone and do the required research on your own. I must also give props to Rachel Grey’s paper: "Writing an OpenGL Windows Screensaver” at http://cityintherain.com/howtoscr.html. Actually, if you're interested in creating a screensaver that will save options to the registry then you may want to look at her source code. At this point I will not cover this topic, and it is not completely necessary either to get your screen saver up and running.
Before we start coding let me first explain that three functions are absolutely required when writing a screen saver.
Screen saver executables can run in two modes as specified by the command line argument: configuration mode (-c) or screen saver mode (-s). Windows knows which mode to use when the screen saver is installed but when testing in the VC++ environment this needs to specified. You can specify –s by right clicking on you project in the explorer window, select properties, choose debugging on the left menu, and enter –s in the Command Argument box.
::[
RESOURCES
]:: NOTE: When adding a resource, Visual Studio will automatically add a rc file to your project as well as a resource.h file in the header folder. The resource.h file must be included in you source code for the resources to work correctly. See code below. Next, if you want a custom icon add another string to the string table: ID_APP=100. To add the icon you’ll need a 32*32 pixel .ico file. Next, right click on your already created .rc file, select add resource, choose icon, then click on the import button. Make sure “Files of type:” is set to .ico files. Make sure you change the ID of the icon to ID_APP! Lastly, is the dialog box. This is the dialog box the pops up when you choose settings in the display properties/screen saver window. Since dialog boxes deserve a tutorial all to themselves I will only tell you how to add one that displays the author of the screensaver with an OK and CANCEL button. Right click on the the .rc file, select add resource, select dialog, and click the new button. Well good for us the OK and CANCEL buttons have already been added, and the ScreenSaverConfigureDialog function below is already set up to handle these messages. All you have to do is add ‘static text’ to the dialog box and change the ID in its properties window to DLG_SCRNSAVECONFIGURE=2003. Studio Developer will do the rest. If you want to check the dialog box run the screensaver with –c as the command argument.
#include <windows.h>
#include <scrnsave.h>
#include <commctrl.h>
#include <gl\gl.h> // Header for OpenGL32 Lib
#include <gl\glu.h> // GLu32 Lib
#include <gl\glaux.h> // GLaux Lib
#include "resource.h"
// Include these libraries
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "GLu32.lib")
#pragma comment(lib, "GLaux.lib")
#pragma comment(lib, "ScrnSave.lib")
#pragma comment(lib, "comctl32.lib")
// Screen saver library contains the main function and other
// Startup code required for a scrnsaver user defined vars
#define TIMER 1
// Function Prototypes
// NOTE: In InitGL, hDC and hRC are passed by reference
// because both will be altered by this function.
// At least that is my understanding!
void InitGL(HWND hWnd, HDC &hDC, HGLRC &hRC); // Setup OpenGL pixelformat
void CloseGL(HWND hWnd, HDC hDC, HGLRC hRC); // Clean up and close OpenGL
void SetupAnimation(int Width, int Height); // Setup for OpenGL
void OnTimer(HDC hDC); // Draw OpenGL Scene
// Global variables
int Width, Height; // Size of screen variables
// Screensaver procedure...first when window is created:
// 1) get users window resolution
// 2) call a function that sets up OpenGL
// 3) Setup a TIMER that will be used to drive animations
LRESULT WINAPI ScreenSaverProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
static HDC hDC; // Handle to device context
static HGLRC hRC; // Handle to OpenGL rendering context
static RECT rect; // Instance of structure RECT which defines the
// coords of the upper-left and lower-right
// corners of a rectangle
switch(message)
{
// Set timer and any other initializations
case WM_CREATE:
GetClientRect(hWnd, &rect); // Get window dimensions
Width = rect.right; // Store width
Height = rect.bottom; // Store height
InitGL(hWnd, hDC, hRC); // Initalize OpenGL
// Setup for OpenGL (look at graphics.cpp)
SetupAnimation(Width, Height);
// Create timer with timeout value (10)
SetTimer(hWnd, TIMER, 10, NULL);
return 0;
case WM_DESTROY: // Destroy timer and perform cleanup
KillTimer(hWnd, TIMER); // Destroy timer
CloseGL(hWnd, hDC, hRC); // Clean up and close OpenGL
return 0;
case WM_TIMER: // Perform drawing operations
OnTimer(hDC); // Draw OpenGL scene (graphics.cpp)
return 0;
}
// Unprocessed messages are handled by the screen saver
// library by calling the following:
return DefScreenSaverProc(hWnd, message, wParam, lParam);
}
// The system will call the following function when the user is
// in the control panel setting up the screensaver and they
// press the setting button...
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
// Dialog message handling
switch(message)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hDlg, LOWORD(wParam) == IDOK);
return true;
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam) == IDOK);
return true;
}
}
return false;
}
// The following function registers any nonstandard window
// classes required by the screen saver if the screen saver
// does not require this functionality simply return true
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
return true;
}
// Initialize OpenGL
void InitGL(HWND hWnd, HDC &hDC, HGLRC &hRC)
{
// Next define pixelformat...we use stuct PIXELFORMATDESCRIPTOR
// This is straight out of NeHe’s framework
static PIXELFORMATDESCRIPTOR pfd = // static ensures data
// is stored between calls
{
sizeof(PIXELFORMATDESCRIPTOR),
1, // nVersion should be set to 1
PFD_DRAW_TO_WINDOW | // buffer can draw to window
PFD_SUPPORT_OPENGL | // buffer supports OpenGL drawing
PFD_DOUBLEBUFFER, // buffer is double buffered
PFD_TYPE_RGBA, // rgba pixels
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // look up rest at MSDN, color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accumulation bits
16, // 16 bit z buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main drawing layer
0, // reserved
0, 0, 0 // layer mask ignored
};
hDC = GetDC(hWnd); // Retrieves a handle to a
// display device context
int i = ChoosePixelFormat(hDC, &pfd); // Try and match a pixel format
// supported by DC
SetPixelFormat(hDC, i, &pfd);
hRC = wglCreateContext(hDC); // Create a new OpenGL rendering context
wglMakeCurrent(hDC, hRC); // Makes OpenGL hRC the calling
// threads current RC
}
// Shut down OpenGL
void CloseGL(HWND hWnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent(NULL, NULL); // Makes current RC no longer current
wglDeleteContext(hRC); // Deletes OpenGL rendering context
ReleaseDC(hWnd, hDC); // Releases a DC, freeing it
// for other apps
}
// All graphical functions can go in this file
#include <windows.h>
#include <scrnsave.h>
#include <commctrl.h>
#include <gl\gl.h> // Header for OpenGL32 Lib
#include <gl\glu.h> // GLu32 Lib
#include <gl\glaux.h> // GLaux Lib
#include "resource.h"
// Include these libraries
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "GLu32.lib")
#pragma comment(lib, "GLaux.lib")
#pragma comment(lib, "ScrnSave.lib")
#pragma comment(lib, "comctl32.lib")
// Screen saver library contains the main function and
// other startup code required for a scrnsaver
// setup for OpenGL
void SetupAnimation(int Width, int Height)
{
// Current viewport:x, y, width, height
glViewport(0, 0, (GLsizei)Width, (GLsizei)Height);
// Next we set up for a perspective view
glMatrixMode(GL_PROJECTION); // Select the Projection Matrix
glLoadIdentity(); // Reset(init) the Projection Matrix
gluPerspective(45.0f, (GLfloat)Width/(GLfloat)Height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW); // Select the modelview matrix
glLoadIdentity(); // Reset (init) the modelview matrix
gluLookAt(0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set background to black
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glShadeModel(GL_SMOOTH); // Enable smooth shading
glClearDepth(1.0f); // Clear depth buffer
glEnable(GL_DEPTH_TEST); // Enables depth test
glDepthFunc(GL_LEQUAL); // Type of depth test to perform
// Enhances image quality
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glColor3f(0.5f, 0.5f, 1.0f);
}
// Declare rotation variables
static GLfloat spinx, spiny, spinz;
// OpenGL drawing and animation
void OnTimer(HDC hDC)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Rotation variables
spinx = 0.1f;
spiny = -0.2f;
spinz = 0.3f;
glRotatef(spinx, 1.0f, 0.0f, 0.0f);
glRotatef(spiny, 0.0f, 1.0f, 0.0f);
glRotatef(spinz, 0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
// Front Face
glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom left
glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom right
glVertex3f( 1.0f, 1.0f, 1.0f); // Top right
glVertex3f(-1.0f, 1.0f, 1.0f); // Top left
// Back Face
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
// Top Face
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
// Bottom Face
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
// Right Face
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
// Left Face
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
SwapBuffers(hDC);
}
Well I hope this tutorial was as clear as I could make it. I myself copied and pasted the above code into a new C++ project (make sure you choose empty Window project) and followed the same directions I gave you for the resources and everything worked fine. Now you have a way to distribute your OpenGL projects in a form where others can appreciate your work. If you have any questions or comments let me know at lance@lanceusa.com. |
|