Code:fullscreen main.h

From the change wiki

C header for easy OpenGL programming.

Gets rid of most boilerplate code. With this, you don't even implement main() - you implement 3 functions init(), draw(), and done(). For more info and examples of how to use it, check out https://github.com/Elie9001/opengl-starter-kit

Code

/***
 Elie's OpenGL wrapper
 Creates a fullscreen vsync'd context with game-style access to keyboard & mouse.
 Version 1.1
***/
#pragma once

// This header file implements main().
// Here are the functions you must define in your own code:
void init();
void draw();
void done();


/** Notes:
 *
 * The init() function is called once at startup.  This is the place to set up your shaders, textures, display lists, etc.
 * The draw() function is called at every frame.   This is the place for your OpenGL drawing code, such as calling your lists etc.
 * The done() function is called once at shutdown. This is the place to delete your shaders, textures, display lists, etc.
 *
 * The program keeps running until the user presses ESC.    (This can be overridden; see below)
 *
 * To access keyboard and mouse within your draw() function, read the following globals:
 *  keymap[] : ascii keys - For example, to check if the 'W' key is held down: code:  if (keymap['W']) { ... }  Note that you must use uppercase letters here.  Also, if you want to respond to a keypress only once when it's pressed (instead of on every frame the key is held down): code:  if (keymap['W']==KEY_FRESHLY_PRESSED) { ... }
 *  special_keymap[] : non-ascii keys, as defined by GLUT constants.
 *  _mouse_dx: mouse pointer motion (as fraction of window size) per frame
 *  _mouse_dy: mouse pointer motion (as fraction of window size) per frame
 * Or,
 * if you like standard controls (W,A,S,D + arrow keys), you can use these macros:
 *  keyboard_dz() : forward / backward
 *  keyboard_dy() :    jump / duck
 *  keyboard_dx() :      strafe
 * 
 * Other globals:
 *  _screen_x, _screen_y      : The screen width & height, in pixels
 *  _screen_size              : Geometric average of width & height. Useful as a general "screen size" no matter the aspect ratio. Think of it as "if all the screen's pixels were rearranged in a square, it would be how many pixels across" = sqrt(_screen_x * _screen_y)
 *  _global_argc, _global_argv: Same as argc & argv from main().
 *
 * Compiler flags needed when you use this header file:
 *      -ffast-math -O -lGL -lglut -lm
 *
 * defines available:
 *  #define NO_ESCAPE           : Disable the default 'ESC to quit' functionality. And then you'll need the line '_exit_the_program = GL_TRUE;' somewhere in your draw() code. Otherwise your program will never quit, and you'll be stuck in fullscreen mode forever.
 *                                Proceed with caution. Beware of accidentally making a program that never exits!
 *
 *  #define VIEWPORT_SCALE 2.0  : This will effectively double your "viewport" size. In other words, the edges of your screen will be at coordinates -0.5 to 0.5, instead of -1.0 to 1.0.
 *                                You might need this feature is you're using a lot of GL_POINTS with point sprites, to prevent them from popping (appearing/disappearing when they are near the screen edges). This is a workaround for OpenGL's badly-designed standard for point-sprites.
 *                                Hopefully this won't actually render extra pixels that you never see. I benchmarked it on an old Radeon graphics card and there was no performance loss. No wasted computations as far as I know. But it should probably be tested on more hardware just to make sure.
 *                                P.S. You can also use other values for VIEWPORT_SCALE, such as 1.2 or 1.5. Just beware of scales too big - you don't want your viewport to exceed GL_MAX_VIEWPORT_DIMS.
 *
 *  #define USE_PRE_INIT        : You get to define an extra function 'pre_init()' which gets called BEFORE the fullscreen window/context gets created. This gives you a chance to quit before the program starts, by setting _exit_the_program = GL_TRUE;
 *                                One use-case might be: Your program requires command-line arguments, and if the args are missing, you want to only display a message in the terminal (no OpenGL).
 *                                                       argc & argv are available as globals: _global_argc, _global_argv
 *
 *  #define USE_MULTISAMPLING   : Basic anti-aliasing mode. Good for triangles/quads/polygons, but useless for GL_POINTS sprites, usually.
 *
 *  #define SHOW_ERROR_LOG      : Report OpenGL errors in the terminal.
 *
 *  #define SHOW_FRAME_RATE     : Report the number of frames-per-second in the terminal.
 *
 *
 * Author: Elie Goldman Smith
 * This file is too trivial to copyright (since it's mostly boilerplate code),
 * so it's in the public domain.
 *
 * Feel free to copy this file into any project.
**/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#ifdef USE_GLEW
 #include <GL/glew.h>
#else
 #define GL_GLEXT_PROTOTYPES
 #include <GL/gl.h>
 #include <GL/glext.h>
#endif
#include <GL/glut.h>
#define RND()   (rand()*(2.0f/RAND_MAX)-1.0f) // random number from -1 to 1
#define RND01() (rand()*(1.0f/RAND_MAX))      // random number from  0 to 1


int _screen_x = 8;
int _screen_y = 8;
GLfloat _screen_size = 8;

#define KEY_FRESHLY_PRESSED 3

char keymap[256] = {0};
#ifdef NO_ESCAPE
void gcb_key_down(unsigned char key, int x, int y) { keymap[toupper(key)] = keymap[tolower(key)] = KEY_FRESHLY_PRESSED; }
#else
void gcb_key_down(unsigned char key, int x, int y) { keymap[toupper(key)] = keymap[tolower(key)] = KEY_FRESHLY_PRESSED; if (key == 27) exit(0); }
#endif
void gcb_key_up  (unsigned char key, int x, int y) { keymap[toupper(key)] = keymap[tolower(key)] = 0;} // XXX: toupper only works for letters, not numbers or punctuation. We should really do something more universal like keymap[shiftless(key)] and i would have to define the shiftless() function.

char special_keymap[256] = {0};
void gcb_special_key_down(int key, int x, int y) { special_keymap[(unsigned char)key] = KEY_FRESHLY_PRESSED; }
void gcb_special_key_up  (int key, int x, int y) { special_keymap[(unsigned char)key] = 0; }

#define keyboard_dz()     (!!keymap['W']-!!keymap['S']+!!special_keymap[GLUT_KEY_UP     ]-!!special_keymap[GLUT_KEY_DOWN     ]                            ) // forward/backward
#define keyboard_dy()     (!!keymap['Q']-!!keymap['Z']+!!special_keymap[GLUT_KEY_PAGE_UP]-!!special_keymap[GLUT_KEY_PAGE_DOWN]+!!keymap['E']-!!keymap['C']) // jump/duck
#define keyboard_dx()     (!!keymap['D']-!!keymap['A']+!!special_keymap[GLUT_KEY_RIGHT  ]-!!special_keymap[GLUT_KEY_LEFT     ]                            ) // strafe

int _exit_the_program = GL_FALSE;

GLfloat _mouse_x = 0; // mouse pointer position is
GLfloat _mouse_y = 0; // normalized to _screen_size
GLfloat _mouse_dx = 0;
GLfloat _mouse_dy = 0;
char _mouse_button_map[8] = {0};

void gcb_mouse_motion_with_pointer(int x, int y) {
 _mouse_dx = -_mouse_x;
 _mouse_dy = -_mouse_y;
 _mouse_x = (x - _screen_x/2) * 2.0f/_screen_size;
 _mouse_y = (y - _screen_y/2) *-2.0f/_screen_size;
 _mouse_dx += _mouse_x;
 _mouse_dy += _mouse_y;
}

void gcb_mouse_motion_pointerless(int x, int y) {
 int center_x = _screen_x/2;
 int center_y = _screen_y/2;
 x -= center_x;
 y -= center_y;
 if (x != 0 || y != 0) {
  _mouse_dx = x *-2.0f/_screen_size;
  _mouse_dy = y * 2.0f/_screen_size;
  glutWarpPointer(center_x, center_y);
 }
 _mouse_x = _mouse_y = 0;
}

void gcb_mouse_click(int button, int state, int x, int y) {
 if (button >= 0 && button < 8) _mouse_button_map[button] = (state==GLUT_DOWN)*KEY_FRESHLY_PRESSED;
 _mouse_x = (x - _screen_x/2) * 2.0f/_screen_size;
 _mouse_y = (y - _screen_y/2) *-2.0f/_screen_size;
}

void show_mouse() {
 glutMotionFunc       (gcb_mouse_motion_with_pointer);
 glutPassiveMotionFunc(gcb_mouse_motion_with_pointer);
 glutSetCursor(GLUT_CURSOR_INHERIT);
}
void hide_mouse() {
 glutMotionFunc       (gcb_mouse_motion_pointerless);
 glutPassiveMotionFunc(gcb_mouse_motion_pointerless);
 glutSetCursor(GLUT_CURSOR_NONE);
 _mouse_x = _mouse_y = 0; glutWarpPointer(_screen_x/2, _screen_y/2);
}




// btw, my 'gcb' prefix just stands for "glut call-back function"
void gcb_draw_frame()
{
 draw();
 if (_exit_the_program) exit(0);

 // remove the KEY_FRESHLY_PRESSED
 for (int i=0; i<256; i++) {
  keymap[i]         &= 1;
  special_keymap[i] &= 1;
 }
 for (int i=0; i<8; i++) {
  _mouse_button_map[i] &= 1;
 }
 // smooth out the times when mouse dx & dy don't get updated
 _mouse_dx *= 0.875f;
 _mouse_dy *= 0.875f;
 
 #ifdef SHOW_FRAME_RATE
 static int frame=0;
 if (++frame >= 64) {
  static int prev=0;
  int t = glutGet(GLUT_ELAPSED_TIME);
  if (prev) {
   printf(" %d FPS ", (int)(frame*1000.0/(t-prev)));
   fflush(stdout);
  }
  prev=t; frame=0;
 }
 #endif

 glutSwapBuffers();
}


void gcb_reshape_window(int width, int height)
{
 #ifdef VIEWPORT_SCALE
 glViewport((GLint)( width*0.5*(1.0-VIEWPORT_SCALE)),
            (GLint)(height*0.5*(1.0-VIEWPORT_SCALE)),
            (GLint)( width*VIEWPORT_SCALE),
            (GLint)(height*VIEWPORT_SCALE));
 #else
 glViewport(0, 0, (GLint)width, (GLint)height); // default behavior
 #endif
 _screen_x = width;
 _screen_y = height;
 _screen_size = sqrtf((float)_screen_x*_screen_y);
 _mouse_x = _mouse_y = 0; glutWarpPointer(_screen_x/2, _screen_y/2);
}


#ifdef SHOW_ERROR_LOG
void gcb_errors(GLenum source, GLenum type, GLuint id, GLenum severity,
                GLsizei length, const GLchar* message, const void* userParam) {
 fprintf(stderr,"GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
           (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
            type, severity, message);
}
#endif
#ifdef USE_PRE_INIT
void pre_init();
#endif



int    _global_argc;
char **_global_argv;

int main(int argc, char **argv) {
 _global_argc = argc;
 _global_argv = argv;
 #ifdef USE_PRE_INIT
 pre_init();  if (_exit_the_program) return 1;
 #endif
 #ifdef USE_MULTISAMPLING
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
 #else
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
 #endif
 glutInitWindowSize(256,144);
 glutInit(&argc, argv);
 glutCreateWindow("view");
 #ifdef USE_GLEW
 glewInit();
 #endif
 glutReshapeFunc(gcb_reshape_window);
 glutKeyboardFunc(gcb_key_down);
 glutKeyboardUpFunc(gcb_key_up);
 glutSpecialFunc(gcb_special_key_down);
 glutSpecialUpFunc(gcb_special_key_up);
 glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);
 glutMouseFunc(gcb_mouse_click);
 glutMotionFunc(gcb_mouse_motion_pointerless);
 glutPassiveMotionFunc(gcb_mouse_motion_pointerless);
 glutSetCursor(GLUT_CURSOR_NONE);
 glutDisplayFunc(gcb_draw_frame);
 glutIdleFunc(gcb_draw_frame);
 glutFullScreen();

 #ifdef SHOW_ERROR_LOG
 glEnable(GL_DEBUG_OUTPUT);
 glDebugMessageCallback(gcb_errors, 0);
 #endif

 init();
 atexit(done);
 glutMainLoop();
 return 0;
}

/*
 // other input code that might be needed somewhere:
 int mod = glutGetModifiers();
 if (mod & GLUT_ACTIVE_SHIFT) {}
 if (mod & GLUT_ACTIVE_CTRL)  {}
 if (mod & GLUT_ACTIVE_ALT)   {}
*/