So what are you looking at? Well
here we have a Makefile that will work on Linux and on Mac OS X.
The key thing here is that we take advantage of some shell scripts that
will give us some clue as to what platform we are on. That being
the uname script. The
first line takes the result of the script with a -s argument which will return
either "Linux", or "Darwin" for the case of the Mac.
Now OS X has a
slightly different way of accessing the OpenGL libraries because they
use Frameworks. There's plenty of documentation at the Apple Developer's site for how
to use frameworks, make frameworks, etc. Just remember that on
the Mac, if a library is in a framework you need to use a -F<Library Name Here>
approach to getting the include path setup right and a -framework <Library Name Here>
approach to getting the library path setup right.
Next you'll notice
is how we setup the SDL include and library paths. Basically in
the bin directory where SDL is installed has a script called sdl-config, which will retain all
the information needed about where SDL was installed (i.e. the prefix
), what the include path is, what the library path is, compiler
arguements for both and more.
STEP 3: Write SDL + OpenGL code
Now on to the good stuff. What we'll do is take a top down
approach and figure out what we need to do and then break them down
into functions as necessary. First let's make sure we have the
right header files included and below is code that will take care of
some slight differences between Linux and OS X:
#include
<stdlib.h>
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#ifdef __APPLE__
#include
<OpenGL/glu.h>
#include
<OpenGL/glext.h>
#else
#include
<GL/glu.h>
#include
<GL/glext.h>
#include
<GL/glx.h>
#include
<GL/glxext.h>
#define
glXGetProcAddress(x) (*glXGetProcAddressARB)((const GLubyte*)x)
#endif
#include <math.h>
#include <time.h>
#include <unistd.h>
Now one thing in there that seems
strange is the glXGetProcAddress definition. For the sake of
keeping the text down I've placed that there now, but we'll get to it
later. So now let's write our main function. In it what
we'll need to do is setup our SDL video output to use OpenGL hardware
acceleration, and then go into our infinite loop. So far that's
three functions. When setting up SDL with OpenGL we need to do
the following:
- Initialize the video system
- Tell the system to call a
specific SDL function atexit()
- Get optimal video settings
- Set some OpenGL attributes
- Get the
framebuffer/drawing surface
- Setup our OpenGL viewport
and projection mode ( Orthographic or Perspective)
Here's
the source code to do that:
//-----------------------------------
// some globals
#define DESIRED_FPS 60.0
SDL_Surface* gDrawSurface = NULL;
int width = 800;
int height = 600;
//-----------------------------------
// Function prototypes
void InfLoop();
void SetupSDL();
//-----------------------------------
int main(int argc,
char** argv)
{
SetupSDL();
InfLoop();
return 0;
}
//-----------------------------------
// Setup SDL and OpenGL
void SetupSDL(void)
{
// init video
system
const
SDL_VideoInfo* vidinfo;
if(
SDL_Init(SDL_INIT_VIDEO) < 0 )
{
fprintf(stderr,"Failed to initialize SDL Video!\n");
exit(1);
}
// tell
system which funciton to process when exit() call is made
atexit(SDL_Quit);
// get
optimal video settings
vidinfo =
SDL_GetVideoInfo();
if(!vidinfo)
{
fprintf(stderr,"Coudn't get video information!\n%s\n", SDL_GetError());
exit(1);
}
// set opengl
attributes
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,
5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,
5);
#ifdef __APPLE__
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,
32);
#else
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,
16);
#endif
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// get a
framebuffer
gDrawSurface
= SDL_SetVideoMode(width,height,vidinfo->vfmt->BitsPerPixel,
SDL_OPENGL);
if(
!gDrawSurface )
{
fprintf(stderr,"Couldn't set video mode!\n%s\n", SDL_GetError());
exit(1);
}
// set opengl
viewport and perspective view
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( 120, 4.0f / 3.0f, .00001, 100);
glMatrixMode(GL_MODELVIEW);
}
//-----------------------------------
// Infinite Loop
void InfLoop()
{
//---
infinite loop with event queue processing
SDL_Event
event;
while(1)
{
while( SDL_PollEvent( &event ))
{
switch( event.type )
{
case SDL_QUIT:
exit(0);
break;
}
} // -- while event in queue
} // --
infinite loop
}
One little quirky thing that's been
happening to me, but may not for you is that the SDL_GL_DEPTH_SIZE attribute doesn't
work on my Mac when I set the value to 16, but works just fine when I
set it to 32. This is great, but we won't be seeing anything
because nothing is being drawn! It's up to you now to make a new
function and then incorporate it into the main loop. Assuming you
make a function called DrawScene(),
then you would just add it to the main loop like so:
//-----------------------------------
// Infinite Loop
void InfLoop()
{
//---
infinite loop with event queue processing
SDL_Event
event;
while(1)
{
while( SDL_PollEvent( &event ))
{
switch( event.type )
{
case SDL_QUIT:
exit(0);
break;
}
} // -- while event in queue
// Add call to drawing function here!!!!
DrawScene()
} // -- infinite loop
}
Now we need to setup GLSL.
STEP 4: Setting up GLSL
Mac
users, wave your hands in the air because you can just use GLSL
now. No need to get any function pointers or anything, assuming
you have OS X 10.4.3 or later. Linux users, you have a bit more
work cut out for you. This is the step where we end up coming
back to our glXGetProcAddress
macro. Our overall plan is as follows
- Declare global function pointers that will be used to access GLSL
related functions
- Implement a function to acquire GLSL related functions, assuming
they are there
So
now how do we do this?
STEP
4-1:
Since OS X has the functions readily
available we have to first setup a preprocess step to ignore our global
function pointers that we are going to declare when we compile for the
Mac.
#if
!defined(__APPLE__)
#endif
Now we need to populate it with our
globals. But what types are they supposed to be? In OpenGL,
if you want to get a function pointer you typically have a typedef that
follows this pattern:
PFNGL<INSERT
FUNCTION NAME HERE>PROC
Make note that the function name has
to be in all capitals. As an example to create a function pointer
to the glCreateProgramObjectARB()
function you would declare the variable as such:
PFNGLCREATEPROGRAMOBJECTARBPROC
glCreateProgramObjectARB = NULL;
Below is a set of OpenGL functions
you would need for GLSL (NOTE: there are more):
#if
!defined(__APPLE__) && !defined(_WIN32)
PFNGLCREATEPROGRAMOBJECTARBPROC
glCreateProgramObjectARB = NULL;
PFNGLCREATESHADEROBJECTARBPROC
glCreateShaderObjectARB = NULL;
PFNGLSHADERSOURCEARBPROC
glShaderSourceARB = NULL;
PFNGLCOMPILESHADERARBPROC
glCompileShaderARB = NULL;
PFNGLGETOBJECTPARAMETERIVARBPROC
glGetObjectParameterivARB = NULL;
PFNGLATTACHOBJECTARBPROC
glAttachObjectARB = NULL;
PFNGLGETINFOLOGARBPROC
glGetInfoLogARB = NULL;
PFNGLLINKPROGRAMARBPROC
glLinkProgramARB = NULL;
PFNGLUSEPROGRAMOBJECTARBPROC
glUseProgramObjectARB = NULL;
PFNGLGETUNIFORMLOCATIONARBPROC
glGetUniformLocationARB = NULL;
PFNGLUNIFORM1FARBPROC
glUniform1f = NULL;
#endif
STEP
4-2:
Now we need to make a function to
access those funcitons. Remember the following line from above:
#define
glXGetProcAddress(x) (*glXGetProcAddressARB)((const GLubyte*)x)
Now we are going to use the macro and
here is our function:
#ifdef
__APPLE__
void SetupGLSLProcs()
{
//do nothing
}
#elif !defined(_WIN32)
void SetupGLSLProcs()
{
glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)
glXGetProcAddress("glCreateProgramObjectARB");
glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)
glXGetProcAddress("glCreateShaderObjectARB");
glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)
glXGetProcAddress("glShaderSourceARB");
glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)
glXGetProcAddress("glCompileShaderARB");
glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)
glXGetProcAddress("glGetObjectParameterivARB");
glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)
glXGetProcAddress("glAttachObjectARB");
glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)
glXGetProcAddress("glGetInfoLogARB");
glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)
glXGetProcAddress("glLinkProgramARB");
glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)
glXGetProcAddress("glUseProgramObjectARB");
glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)
glXGetProcAddress("glGetUniformLocationARB");
glUniform1f
= (PFNGLUNIFORM1FARBPROC)
glXGetProcAddress("glUniform1fARB");
}
#endif
All you have to do is add a call to
this function after we setup SDL.
//-----------------------------------
int main(int argc, char** argv)
{
SetupSDL();
SetupGLSLProcs();
InfLoop();
return 0;
}
STEP 5: Have Fun!
So
now we've got everything settled and it's up to you to learn how to use
GLSL. At this point you can do some cool things, and don't worry
if it looks awesome. Just mess around with shaders as much as you
can. Follow Andy's notes
on how to set the shaders, compile, link, use, etc.