GLUT Tutorials 16: game mode

Blog from: http://www.lighthouse3d.com/tutorials/glut-tutorial/game-mode/

Glut's game mode is to enable high-performance full screen rendering. However, there are always some glut functions that drag down high-performance requirements, such as menu pop ups, sub windows, etc. In this section, the game mode of GLUT will be introduced. My materials for this tutorial. I didn't find any official documents or other tutorials that covered this part. So I'm not sure this tutorial is 100% correct. My glut game mode experience comes from the normal working routines created by myself before, but since it has only been tested in limited hardware configuration, there may be some inaccurate or incorrect statements. If you encounter problems in conceptual design in the tutorial, please give me a description of the problems so that I can analyze and investigate the reasons. If you are familiar with the game mode of glut and find the wrong understanding of the game mode again, I would be very grateful if you could give me feedback on the problems and help me solve those problems.

Now that I have completed my statement, we can start this part of the tutorial. First, declare the game mode, for example, full screen. These settings may include screen resolution, pixel depth, and refresh rate. All in all, we can set any resolution we want (within the range allowed by the hardware).

These settings for full screen mode are specified on a string. The format is as follows

"WxH:Bpp@Rr"

Parameters:

W – the screen width in pixels
H – the screen height in pixels
Bpp – the number of bits per pixel
Rr – the vertical refresh rate in hertz

Until the next step, these settings are just a request for hardware. If the declared pattern is not feasible, these settings are ignored.

Examples:

  • "800×600:32@100" – screen size 800×600; true color (32 bits); 100Hz vertical
  • "640×480:16@75" – screen size 640×480; high color (16 bits); 75 hertz

Specifying all the components is a little bit stressfull. Although we usually have a clear idea of the screen resolution, and sometimes we may require a particular color mode, the refresh rate may be trickier. Fortunately we don't have to specify everything. We can leave some bits out and let GLUT fill in the blanks. The following template strings for partially specifying the desired full screen settings are allowed:

It is difficult to declare all configuration information. Although we usually have a clear idea of screen resolution, sometimes we may need a special color mode, and the setting of refresh rate also needs skill. Fortunately, we don't need to declare everything. We can set some content and let GLUT fill in other parts automatically. Here are some templates for declaring full screen settings.

  • "WxH"
  • "WxH:Bpp"
  • "WxH@Rr"
  • "@Rr"
  • ":Bpp"
  • "Bpp:@Rr"

Basically, GLUT can handle all combinations as long as their combinations are in pre stored setup mode. For example, it is not possible to declare a refresh rate before the pixel depth. If we only care about the resolution but not the pixel depth and refresh rate, we can set "800 × 600" in this way. On the other hand, if we only want to set the full screen mode at the current resolution, and the pixel depth is 32, we can set ": 32" in this way. These examples do not completely cover the full screen setting, we can use any of the above listed.

Ready to move on? OK. First we must provide GLUT with the requested settings for the full screen mode. The GLUT's function to set the game mode is glutGameModeString. The syntax is as follows:

void glutGameModeString(const char *string);

Parameters:
string – a string containing the desired settings as specified above

Although the function glutGameModeString does not return an error code, GLUT will check whether the parameters are set correctly. Glut provides a function to check whether the declared mode is compliant.

int glutGameModeGet(GLenum info);

Parameters:
info – the requested information

In order to check if the supplied mode is valid, info takes the value of a GLUTs pre defined constant: GLUT_GAME_MODE_POSSIBLE.

Return a non-zero value to indicate that the set mode is OK. But GLUT's operating instructions warn that although the declared mode is OK, there is no guarantee that the settings for the screen will be successfully updated.

Imagine that we get a non-zero return value, and then we can enter or at least try to enter game mode. If it is set to OK, the following function can put the screen into the set state.

void glutEnterGameMode(void);

The main function to initialize a GLUT application in game mode at 800 by 600 could be something like this:

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
 

/*    glutInitWindowPosition(100,100);
    glutInitWindowSize(640,360);
    glutCreateWindow("SnowMen from Lighthouse3D");
*/
    // setting the game mode replaces the above
    // calls to set the window size and position.
    glutGameModeString("800x600:32");
    // enter full screen
    if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))
        glutEnterGameMode();
    else {
        printf("The select mode is not available\n");
        exit(1);
    }

    // register all callbacks
    init();

    glutMainLoop();
    
    return 1;
}}

The function init should register all the necessary callbacks as well as perform the openGL required initializations, for instance we could write something like this:

void init() {
 

    // register callbacks
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutIdleFunc(renderScene);

    glutIgnoreKeyRepeat(1);
    glutKeyboardFunc(processNormalKeys);
    glutSpecialFunc(pressKey);
    glutSpecialUpFunc(releaseKey);
    glutMouseFunc(mouseButton);
    glutMotionFunc(mouseMove);

    // OpenGL init
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
}

It may be the case that we want to be able to switch between game mode and window mode during the application. The following piece of code assumes that we're starting in window mode. The user can then press F1 to switch to game mode. F6 brings the user back to window mode. In this case the main function must define the window properties, register the callbacks, and enter the main loop.

Before we look at the code here goes the function that tells glut to leave game mode.

void glutLeaveGameMode(void);

The function that will process the special keys is the one that will perform the mode switch. The following function performs the required operations:

void pressKey(int key, int x, int y) {
 

    switch (key) {
        ...
        case GLUT_KEY_F1:  

            // define resolution, color depth
            glutGameModeString("640x480:32");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {

                glutEnterGameMode();

                // register callbacks again
                init();
            }
            break;
        case GLUT_KEY_F6:
            // return to default window
            glutLeaveGameMode();
            break;
    }
}

There is a detail which is very important in the function above, when we enter the game mode with glutEnterGameMode we must register the callbacks again, and redefine the OpenGL context. The game mode is just like a new window, with a different OpenGL and GLUT context. This implies that the callbacks for the window mode will have no effect in game mode. In order to use callback functions we must register them again. Furthermore, the OpenGL context needs to be defined again. For instance display lists created for the window mode need to be defined again when entering the game mode.

GLUT is an excellent API, and as such it also gives the programmer ways to query the current state of affairs. GLUT has a special function for querying the state settings for the game mode, glutGameModeGet. The syntax for this function was already introduced above when we mentioned that one possible value for the argument was GLUT_GAME_MODE_POSSIBLE.

There are several possibilities for the argument of glutGameModeGet that cover all the needs for correct game mode programming. The return values for each case are presented bellow:

  • GLUT_GAME_MODE_ACTIVE – If the app is running in game mode then glutGameModeGet will return a non-zero value, if in window mode it will return zero.
  • GLUT_GAME_MODE_POSSIBLE – As mentioned before this can be used to test the string which specifies the game mode setings. It is good policy to call glutGameModeGet with this value before entering game mode.
  • GLUT_GAME_MODE_DISPLAY_CHANGED – As mentioned before when entering the game mode there is no guarantee that the display mode is indeed changed. This value can be used to test if the game mode was really entered. If we were previously already in game mode then this value can be used to test if the settings were changed.
  • GLUT_GAME_MODE_WIDTH – returns the width of the screen
  • GLUT_GAME_MODE_HEIGHT – returns the height of the screen.
  • GLUT_GAME_MODE_PIXEL_DEPTH – returns the bits per pixel of the current mode.
  • GLUT_GAME_MODE_REFRESH – the actual refresh rate in hertz.

The last four options are meaningful only if we are in game mode. These options will cause glutGameModeGet to return -1 if the latest string specifying the game mode settings is not valid, even if we are already in game mode. So for instance if we're runnig an app in game mode at 640 by 480 and requested a change to 1600 by 1200, and the mode is not valid for the actual hardware configuration, then GLUT does not change the resolution and the game mode stays at 640 by 480. However when asking for the current height we'll get -1 and not 480, although the actual height is 480.

The following code is a display processing of whether to enter the game mode correctly.

if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) == 0)
        sprintf(currentMode,"Current Mode: Window");
    else
        sprintf(currentMode,
            "Current Mode: Game Mode %dx%d at %d hertz, %d bpp",
            glutGameModeGet(GLUT_GAME_MODE_WIDTH),
            glutGameModeGet(GLUT_GAME_MODE_HEIGHT),
            glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE),
            glutGameModeGet(GLUT_GAME_MODE_PIXEL_DEPTH));

The complete code is as follows

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

// angle of rotation for the camera direction
float angle = 0.0f;

// actual vector representing the camera's direction
float lx=0.0f,lz=-1.0f;

// XZ position of the camera
float x=0.0f, z=5.0f;

// the key states. These variables will be zero
// when no key is being pressesed
float deltaAngle = 0.0f;
float deltaMove = 0;
int xOrigin = -1;

// color for the snowman's nose
float red = 1.0f, blue=0.5f, green=0.5f;

// scale of snowman
float scale = 1.0f;

// default font
void *font = GLUT_STROKE_ROMAN;

// width and height of the window
int h,w;

// variables to compute frames per second
int frame;
long time, timebase;
char s[60];
char currentMode[80];

// this string keeps the last good setting
// for the game mode
char gameModeString[40] = "640x480";

void init();

void changeSize(int ww, int hh) {

    h = hh;
    w = ww;
    // Prevent a divide by zero, when window is too short
    // (you cant make a window of zero width).
    if (h == 0)
        h = 1;

    float ratio =  w * 1.0 / h;

    // Use the Projection Matrix
    glMatrixMode(GL_PROJECTION);

    // Reset Matrix
    glLoadIdentity();

    // Set the viewport to be the entire window
    glViewport(0, 0, w, h);

    // Set the correct perspective.
    gluPerspective(45.0f, ratio, 0.1f, 100.0f);

    // Get Back to the Modelview
    glMatrixMode(GL_MODELVIEW);
}

void drawSnowMan() {

    glScalef(scale, scale, scale);
    glColor3f(1.0f, 1.0f, 1.0f);

// Draw Body
    glTranslatef(0.0f ,0.75f, 0.0f);
    glutSolidSphere(0.75f,20,20);

// Draw Head
    glTranslatef(0.0f, 1.0f, 0.0f);
    glutSolidSphere(0.25f,20,20);

// Draw Eyes
    glPushMatrix();
    glColor3f(0.0f,0.0f,0.0f);
    glTranslatef(0.05f, 0.10f, 0.18f);
    glutSolidSphere(0.05f,10,10);
    glTranslatef(-0.1f, 0.0f, 0.0f);
    glutSolidSphere(0.05f,10,10);
    glPopMatrix();

// Draw Nose
    glColor3f(red, green, blue);
    glRotatef(0.0f,1.0f, 0.0f, 0.0f);
    glutSolidCone(0.08f,0.5f,10,2);

    glColor3f(1.0f, 1.0f, 1.0f);
}

void renderBitmapString(
        float x,
        float y,
        float z,
        void *font,
        char *string) {

    char *c;
    glRasterPos3f(x, y,z);
    for (c=string; *c != '\0'; c++) {
        glutBitmapCharacter(font, *c);
    }
}

void renderStrokeFontString(
        float x,
        float y,
        float z,
        void *font,
        char *string) {  

    char *c;
    glPushMatrix();
    glTranslatef(x, y,z);
    glScalef(0.002f, 0.002f, 0.002f);
    for (c=string; *c != '\0'; c++) {
        glutStrokeCharacter(font, *c);
    }
    glPopMatrix();
}

void restorePerspectiveProjection() {

    glMatrixMode(GL_PROJECTION);
    // restore previous projection matrix
    glPopMatrix();

    // get back to modelview mode
    glMatrixMode(GL_MODELVIEW);
}

void setOrthographicProjection() {

    // switch to projection mode
    glMatrixMode(GL_PROJECTION);

    // save previous matrix which contains the
    //settings for the perspective projection
    glPushMatrix();

    // reset matrix
    glLoadIdentity();

    // set a 2D orthographic projection
    gluOrtho2D(0, w, h, 0);

    // switch back to modelview mode
    glMatrixMode(GL_MODELVIEW);
}

void computePos(float deltaMove) {

    x += deltaMove * lx * 0.1f;
    z += deltaMove * lz * 0.1f;
}

void renderScene(void) {

    if (deltaMove)
        computePos(deltaMove);

    // Clear Color and Depth Buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Reset transformations
    glLoadIdentity();
    // Set the camera
    gluLookAt(    x, 1.0f, z,
            x+lx, 1.0f,  z+lz,
            0.0f, 1.0f,  0.0f);

// Draw ground

    glColor3f(0.9f, 0.9f, 0.9f);
    glBegin(GL_QUADS);
        glVertex3f(-100.0f, 0.0f, -100.0f);
        glVertex3f(-100.0f, 0.0f,  100.0f);
        glVertex3f( 100.0f, 0.0f,  100.0f);
        glVertex3f( 100.0f, 0.0f, -100.0f);
    glEnd();

// Draw 36 SnowMen
    char number[3];
    for(int i = -3; i < 3; i++)
        for(int j=-3; j < 3; j++) {
            glPushMatrix();
            glTranslatef(i*10.0f, 0.0f, j * 10.0f);
            drawSnowMan();
            sprintf(number,"%d",(i+3)*6+(j+3));
            renderStrokeFontString(0.0f, 0.5f, 0.0f, (void *)font ,number);
            glPopMatrix();
        }

    // Code to compute frames per second
    frame++;

    time=glutGet(GLUT_ELAPSED_TIME);
    if (time - timebase > 1000) {
        sprintf(s,"Lighthouse3D - FPS:%4.2f",
            frame*1000.0/(time-timebase));
        timebase = time;
        frame = 0;
    }

    setOrthographicProjection();
    void *font= GLUT_BITMAP_8_BY_13;
    glPushMatrix();
    glLoadIdentity();
    renderBitmapString(30,15,0,font,(char *)"GLUT Tutorial @ Lighthouse3D");
    renderBitmapString(30,30,0,font,s);
    renderBitmapString(30,45,0,font,(char *)"F1 - Game Mode  640x480 32 bits");
    renderBitmapString(30,60,0,font,(char *)"F2 - Game Mode  800x600 32 bits");
    renderBitmapString(30,75,0,font,(char *)"F3 - Game Mode 1024x768 32 bits");
    renderBitmapString(30,90,0,font,(char *)"F4 - Game Mode 1280x1024 32 bits");
    renderBitmapString(30,105,0,font,(char *)"F5 - Game Mode 1920x1200 32 bits");
    renderBitmapString(30,120,0,font,(char *)"F6 - Window Mode");
    renderBitmapString(30,135,0,font,(char *)"Esc - Quit");
    renderBitmapString(30,150,0,font,currentMode);
    glPopMatrix();

    restorePerspectiveProjection();

    glutSwapBuffers();
}

// -----------------------------------
//             KEYBOARD
// -----------------------------------

void processNormalKeys(unsigned char key, int xx, int yy) {

    switch (key) {
        case 27:
            if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) != 0)
                glutLeaveGameMode();
            exit(0);
            break;
    }
}

void pressKey(int key, int xx, int yy) {

    switch (key) {
        case GLUT_KEY_UP : deltaMove = 0.5f; break;
        case GLUT_KEY_DOWN : deltaMove = -0.5f; break;
        case GLUT_KEY_F1:
            // define resolution, color depth
            glutGameModeString("640x480:32");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
                glutEnterGameMode();
                sprintf(gameModeString,"640x480:32");
                // register callbacks again
                // and init OpenGL context
                init();
            }
            else
                glutGameModeString(gameModeString);
            break;
        case GLUT_KEY_F2:
            // define resolution, color depth
            glutGameModeString("800x600:32");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
                glutEnterGameMode();
                sprintf(gameModeString,"800x600:32");
                // register callbacks again
                // and init OpenGL context
                init();
            }
            else
                glutGameModeString(gameModeString);
            break;
        case GLUT_KEY_F3:
            // define resolution, color depth
            glutGameModeString("1024x768:32");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
                glutEnterGameMode();
                sprintf(gameModeString,"1024x768:32");
                // register callbacks again
                // and init OpenGL context
                init();
            }
            else
                glutGameModeString(gameModeString);
            break;
        case GLUT_KEY_F4:
            // define resolution, color depth
            glutGameModeString("1280x1024:32");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
                glutEnterGameMode();
                sprintf(gameModeString,"1280x1024:32");
                // register callbacks again
                // and init OpenGL context
                init();
            }
            else
                glutGameModeString(gameModeString);
            break;
        case GLUT_KEY_F5:
            // define resolution, color depth
            glutGameModeString("1920x1200");
            // enter full screen
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
                glutEnterGameMode();
                sprintf(gameModeString,"1920x1200");
                // register callbacks again
                // and init OpenGL context
                init();
            }
            else
                glutGameModeString(gameModeString);
            break;
        case GLUT_KEY_F6:
            // return to default window
            w = 800;h = 600;
            if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) != 0) {
                glutLeaveGameMode();
                //init();
            }
            break;
    }
    if (glutGameModeGet(GLUT_GAME_MODE_ACTIVE) == 0)
        sprintf(currentMode,"Current Mode: Window");
    else
        sprintf(currentMode,
            "Current Mode: Game Mode %dx%d at %d hertz, %d bpp",
            glutGameModeGet(GLUT_GAME_MODE_WIDTH),
            glutGameModeGet(GLUT_GAME_MODE_HEIGHT),
            glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE),
            glutGameModeGet(GLUT_GAME_MODE_PIXEL_DEPTH));
}

void releaseKey(int key, int x, int y) {

    switch (key) {
        case GLUT_KEY_UP :
        case GLUT_KEY_DOWN : deltaMove = 0;break;
    }
}

// -----------------------------------
//             MOUSE
// -----------------------------------

void mouseMove(int x, int y) {

    // this will only be true when the left button is down
    if (xOrigin >= 0) {

        // update deltaAngle
        deltaAngle = (x - xOrigin) * 0.001f;

        // update camera's direction
        lx = sin(angle + deltaAngle);
        lz = -cos(angle + deltaAngle);
    }
}

void mouseButton(int button, int state, int x, int y) {

    // only start motion if the left button is pressed
    if (button == GLUT_LEFT_BUTTON) {

        // when the button is released
        if (state == GLUT_UP) {
            angle += deltaAngle;
            xOrigin = -1;
        }
        else  {// state = GLUT_DOWN
            xOrigin = x;
        }
    }
}

void init() {

    // register callbacks
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutIdleFunc(renderScene);

    glutIgnoreKeyRepeat(1);
    glutKeyboardFunc(processNormalKeys);
    glutSpecialFunc(pressKey);
    glutSpecialUpFunc(releaseKey);
    glutMouseFunc(mouseButton);
    glutMotionFunc(mouseMove);

    // OpenGL init
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
}

// -----------------------------------
//             MAIN
// -----------------------------------

int main(int argc, char **argv) {

    // init GLUT and create window
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(800,600);
    glutCreateWindow("Lighthouse3D - GLUT Tutorial");

    // register callbacks
    init();

    // enter GLUT event processing cycle
    glutMainLoop();

    return 1;
}

The display effect is as follows

My computer will crash when switching between different resolutions when running the game mode.

Tags: Windows Programming

Posted on Sat, 23 May 2020 22:29:20 -0700 by Kilgore Trout