GLFW:Tutorials:Basics

From GPWiki
Jump to: navigation, search

The programming interface to GLFW is mercifully small, and setting up a little program with it is a piece of cake. I'll show the basics here. For a more complete description of the library go to http://glfw.sf.net

Installing GLFW

First, you get the source code here and extract it somewhere. Basically you just follow the instructions in readme.html that apply to the compiler you are using, I'm not going to duplicate that information here. If you link your program statically with GLFW (the licence allows this, even for commercial programs), you will not have to distribute any extra DLL's with your program.

If you are using Dev-C++ (MinGW), you can simply download the GLFW Devpack and do not have to compile GLFW yourself

Conventions

All library functions have 'glfw' (lower-case) prefixed to their name. To use the functions you'll have to include the GLFW header file, usually with #include <GL/glfw.h>, this takes care of including <GL/gl.h> for you, and on windows it will also remove the need to include <windows.h> before including gl.h.

GLFW functions that return a status value will use GL_TRUE (a constant from the gl.h header file) for success and GL_FALSE for failure.

Initialization

At the start of your program, you'll have to call glfwInit() to initialize the library. This does not open any windows or do anything, it just does some preparation to allow you to start using GLFW functions. You should check the return value of this function; if it is GL_FALSE, the initialization failed for some reason.

Likewise, when your program terminates, you'll have to call the glfwTerminate(), which will free any resources claimed by GLFW and close any open windows created by GLFW.

Next, you'll want to open a window. Unsurprisingly, the function to do this is glfwOpenWindow:

glfwOpenWindow(int width, int height, int redbits, int greenbits, int bluebits,
               int alphabits, int depthbits, int stencilbits, int mode)

That is an awful lot of parameters. Fortunately, you can set a lot of them to zero. The first two give the resolution for the window, you'll usually want to specify something here (0, 0 gives 640 x 480). Next come the number of bits per color. If you leave these at 0 the color depth of your desktop will be used, if you specify them GLFW will use the color depth that comes closest to what you specified (for example 5, 6, 5 will give you 16 bit color if your OpenGL implementation supports it). Alphabits, depthbits and stencilbits specify will be set to 0, 8, 0 in this example, leave them at that if the words mean nothing to you. The last parameter, mode, is used to indicate whether the window will be full-screen or not. Pass GLFW_FULLSCREEN for a full screen window, and GLFW_WINDOW for a normal one.

if (glfwOpenWindow(800, 600, 5, 6, 5, 0, 8, 0, GLFW_FULLSCREEN) != GL_TRUE)
  Shut_Down(1); // calls glfwTerminate() and exits
glfwSetWindowTitle("The GLFW Window");

This initializes an 800x600, 16-bit color, full screen window, or kills the program if it's initialization failed. It also sets the window title. We're ready to start drawing. Do make sure you only use OpenGL functions when there is an open window (that also goes for creating and destroying texture objects and such things), when there is no window there is no OpenGL context, and you can not use OpenGL.

You can close your window at any time with glfwCloseWindow(), but you can also just let gflwTerminate() handle that when your program closes.

Graphics

GLFW does not do much drawing itself, you'll have to use OpenGL library functions. This tutorial does not concern itself with those. One thing GLFW does help you with is loading textures from TGA files, but usage of that function is so interwoven with OpenGL that you'll have to look it up yourself.

Input

There are two ways of reading input in GLFW, polling for it and registering callback functions. I'll only discuss keyboard input here, but mouse input goes much the same way. One important thing to notice is that GLFW only checks for new input whenever glfwSwapBuffer() or glfwPollEvents() is called, thus if you are not swapping buffers or explicitly polling, no new events will arrive.

if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS)
  quit_the_program = 1;
if (glfwGetKey('A') == GLFW_PRESS)
  printf("A is pressed");

This uses the polling method to check for key input. The return value of glfwGetKey is GLFW_PRESS if the key is currently held down, and GLFW_RELEASE if it is not. Keys that have a character associated with them can be indicated by using that character as argument, other keys have their own constants (see the documentation at http://glfw.sf.net or glfw.h for all the constants).

Using callbacks to get key events goes like this:

void GLFWCALL My_Key_Callback(int key, int action)
{
  if (key == GLFW_KEY_ESC && action == GLFW_PRESS)
    quit_the_program = 1;
  else if (key == 'A' and action == GLFW_PRESS)
    printf("A was pressed");
}
 
// And somewhere in the init code
glfwSetKeyCallback(My_Key_Callback);

Now every time a key is pressed or released, My_Key_Callback is called with information about the event. This allows for more flexible input handling that the other method, but is a bit more work.

Timing

The function glfwGetTime() returns a 'double' (double-precision floating poing number) containing the number of seconds that elapsed since glfwInit() was called. It uses the highest-precision timer available on the system, so precision might vary on different platforms.

An Example

The following code will open a window and display a cute animation until escape is pressed. The code should be very easy to read (if it is not, you'll have to study C and OpenGL some more). To compile it on your system, make sure you link with the OpenGL and GLFW libraries, and on Unix systems you'll also need to link with the X libraries and threading libraries (X11, Xxf86vm and c_r on FreeBSD, X11, pthread and maybe some others on Linux). (For example it can be compiled under GNU/Linux with gcc myprog.c -o myprog -lglfw -lGL -lpthread)

#include <stdlib.h>
#include <GL/glfw.h>
 
void Init(void);
void Shut_Down(int return_code);
void Main_Loop(void);
void Draw_Square(float red, float green, float blue);
void Draw(void);
 
float rotate_y = 0,
      rotate_z = 0;
const float rotations_per_tick = .2;
 
int main(void)
{
  Init();
  Main_Loop();
  Shut_Down(0);
}
 
void Init(void)
{
  const int window_width = 800,
            window_height = 600;
 
  if (glfwInit() != GL_TRUE)
    Shut_Down(1);
  // 800 x 600, 16 bit color, no depth, alpha or stencil buffers, windowed
  if (glfwOpenWindow(window_width, window_height, 5, 6, 5,
                     0, 0, 0, GLFW_WINDOW) != GL_TRUE)
    Shut_Down(1);
  glfwSetWindowTitle("The GLFW Window");
 
  // set the projection matrix to a normal frustum with a max depth of 50
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  float aspect_ratio = ((float)window_height) / window_width;
  glFrustum(.5, -.5, -.5 * aspect_ratio, .5 * aspect_ratio, 1, 50);
  glMatrixMode(GL_MODELVIEW);
}
 
void Shut_Down(int return_code)
{
  glfwTerminate();
  exit(return_code);
}
 
void Main_Loop(void)
{
  // the time of the previous frame
  double old_time = glfwGetTime();
  // this just loops as long as the program runs
  while(1)
  {
    // calculate time elapsed, and the amount by which stuff rotates
    double current_time = glfwGetTime(),
           delta_rotate = (current_time - old_time) * rotations_per_tick * 360;
    old_time = current_time;
    // escape to quit, arrow keys to rotate view
    if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS)
      break;
    if (glfwGetKey(GLFW_KEY_LEFT) == GLFW_PRESS)
      rotate_y += delta_rotate;
    if (glfwGetKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
      rotate_y -= delta_rotate;
    // z axis always rotates
    rotate_z += delta_rotate;
 
    // clear the buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // draw the figure
    Draw();
    // swap back and front buffers
    glfwSwapBuffers();
  }
}
 
void Draw_Square(float red, float green, float blue)
{
  // Draws a square with a gradient color at coordinates 0, 10
  glBegin(GL_QUADS);
  {
    glColor3f(red, green, blue);
    glVertex2i(1, 11);
    glColor3f(red * .8, green * .8, blue * .8);
    glVertex2i(-1, 11);
    glColor3f(red * .5, green * .5, blue * .5);
    glVertex2i(-1, 9);
    glColor3f(red * .8, green * .8, blue * .8);
    glVertex2i(1, 9);
  }
  glEnd();
}
 
void Draw(void)
{
  // reset view matrix
  glLoadIdentity();
  // move view back a bit
  glTranslatef(0, 0, -30);
  // apply the current rotation
  glRotatef(rotate_y, 0, 1, 0);
  glRotatef(rotate_z, 0, 0, 1);
  // by repeatedly rotating the view matrix during drawing, the
  // squares end up in a circle
  int i = 0, squares = 15;
  float red = 0, blue = 1;
  for (; i < squares; ++i){
    glRotatef(360.0/squares, 0, 0, 1);
    // colors change for each square
    red += 1.0/12;
    blue -= 1.0/12;
    Draw_Square(red, .6, blue);
  }
}