Introduction

Author: Movania Muhammad Mobeen

Hello readers, In this article, we will learn how to port the 10th tutorial on SDL framework and fps calculation in OpenGL 3.3. We saw in the last tutorial how to organize the code into manageable sub units. This article will add a new framework based on SDL a portable framework for interactive application development. For this we would need the SDL library which can be easily downloaded from here The framework contains the same functions as before. The only difference in this case is the addition of SDL functions in place of GLUT functions.

Organization of code - Framework_SDL

We organized our code in the last tutorial for the GLUT framework by putting relevant code into separate code units. This also allows us to isolate the OpenGL and drawing code from the window management code. Note that the rendering code for this tutorial is the same as the last tutorial. Only the window managment is changed with SDL. This code is given in framework_sdl.h/cpp files. FrameworkInit This function initializes our SDL framework. It first calls SDL_Init to initialize the SDL's video and audio sub-systems:

	// First, initialize SDL's video and audio subsystem
	if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO) ==-1 )
	{
	   // Failed, exit
	   fprintf( stdout, "Framework initialization failed: %s\n", SDL_GetError( ) );
	   FrameworkQuit();
	}

After initialization, the video information is retrieved to get the best supported window format:

	const SDL_VideoInfo* info = NULL;
	info = SDL_GetVideoInfo( );
	if( !info ) {
	   // This should probably never happen
	   cerr << "Video query failed:"<< SDL_GetError( ) << endl;
	   FrameworkQuit();
	}

The screen depth is then obtained and then the back buffer's RGB and depth buffer bits are set:

	framework_screen_depth = info->vfmt->BitsPerPixel;
	SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
	SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
	SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 32 );
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

Finally, as before the configuration file is loaded to obtain the screen dimensions and whether the fullscreen flag is on. If on, the fullscreen mode flag is appended to the video flag:

	 // Now we load the video settings from config.ini
	LoadINI("config.ini");
	framework_screen_width=LoadINI_GetInt("config.ini","screen settings","width",640);
	framework_screen_height=LoadINI_GetInt("config.ini","screen settings","height",480);
	framework_fullscreen=LoadINI_GetInt("config.ini","screen settings","fullscreen",0);
	l_video_flags = SDL_OPENGL;
	if (framework_fullscreen)
	   l_video_flags|=SDL_FULLSCREEN;

After this is done, the video mode is set to the current display device. All of the previous settings are passed to this function:

	// Set the video mode
	cout << "Setting video mode"<< endl;
	if( SDL_SetVideoMode( framework_screen_width, framework_screen_height, framework_screen_depth, l_video_flags ) == 0 ) {
	  cerr << "Video mode set failed: "<< SDL_GetError( ) << endl;
	  FrameworkQuit();
	}

Next, the keyboard repeat value is set. This function allows us to poll keboard after a delay of the given amount:

	// Keyboard repeat
	cout << "Enabling key repeat"<< endl;
	if ( SDL_EnableKeyRepeat(10, SDL_DEFAULT_REPEAT_INTERVAL) ) // Key input delay of 10 ms and repeat every 100 msec
	{
	   cout << "Failed enabling key repeat :"<< SDL_GetError() << endl;
	   FrameworkQuit();
	}

Next, the audio playback parameters are set for the audio sub-system and then the audio device is opened for audio playback:

	 // Set the audio format
	framework_audiospec.freq = 22050;
	framework_audiospec.format = AUDIO_S16;
	framework_audiospec.channels = 2;    // 1 = mono, 2 = stereo
	framework_audiospec.samples = 1024;  // Good low-latency value for callback
	framework_audiospec.callback = FrameworkMixAudio;
	framework_audiospec.userdata = NULL;

	// Open the audio device, forcing the desired format
	cout<<"Opening audio device"<< endl;
	if ( SDL_OpenAudio(&framework_audiospec, NULL) < 0 ) {
	   cerr<<"Couldn't open audio:"<< SDL_GetError()<< endl;
	   FrameworkQuit();
	}

To prevent the audio from playing, the audio is explicitly paused:

	SDL_PauseAudio(0);

Finally, the resize handler is called to ensure that the viewport and the projection matrices are setup properly:

	FrameworkResize(framework_screen_width, framework_screen_height);
   	cout << "Framework initialized correctly" << endl;

FrameworkQuit

This function is called when the framework is about to terminate. This allows us to delete all allocated objects. Here we simply quit the application:

	void FrameworkQuit()
	{
	   cout << "Exiting program" << endl;
	   SDL_Quit();
	   exit(0);
	}

FrameworkMainLoop

This function is called after the initialization is over. This function never returns until the application is terminated. The function calls the MainLoop function which intercepts the window events and calls appropriate event handlers:

	void FrameworkMainLoop()
	{
		cout<<"Entering main loop"<< endl;
		while( 1 )
		{
			MainLoop();
		}
	}

FrameworkResize

This function handles the resize event by generating a new viewport of the new size and setting the perspective projection matrix. Based on the library currently selected (GLM or spacesimulator.net matrix library), the corresponding function/s are called:

	void FrameworkResize (int p_width, int p_height)
	{
	   if (framework_screen_width==0 && framework_screen_height==0)
	      exit(0);
	   framework_screen_width=p_width; // We obtain the new screen width values and store it
	   framework_screen_height=p_height; // Height value
	   hW = framework_screen_width/2;
	   hH = framework_screen_height/2;
	   glViewport (0, 0, (GLsizei) framework_screen_width, (GLsizei) framework_screen_height);

	   //setup the projection matrix
	   #ifdef USE_GLM
	      P = glm::perspective(45.0f, (GLfloat)framework_screen_width/framework_screen_height, 5.0f, 10000.f);
	   #else
	      Perspective(45.0f, (GLfloat)framework_screen_width/framework_screen_height, 5.0f, 10000.f, P);
	   #endif
	}

FrameworkEvents

This function is called whenever any window event is raised for example a key is down or the window is quit:

	void FrameworkEvents()
	{
	   SDL_Event event;
	   while( SDL_PollEvent(& event) )
	   {
	      switch ( event.type )
	      {
	         case SDL_KEYDOWN : KeyboardHandle(event.key.keysym.sym); break;
	         case SDL_QUIT:     FrameworkQuit();			  break;
	         default:                                                 break;
	      }
	   }
	}

Framework_GetTicks

This function returns the timing information which is useful for doing physics as we will see in a later tutorial:

	long Framework_GetTicks(void)
	{
	   return (long)(SDL_GetTicks());
	}

FrameworkSwapBuffers

This function swaps the back buffer with the front buffer to allow the rendering on the back buffer to be put on the front buffer so that it may be displayed:

	void FrameworkSwapBuffers()
	{
  	   SDL_GL_SwapBuffers( );
	}

FrameworkMixAudio & FrameworkAudioPlayWave

These functions perform audio processing and allow playback of wav audio files with the help of the SDL library. We will look into these function in more detail in a later tutorial:

	void FrameworkMixAudio(void *unused, Uint8 *stream, int len)
	{
	   int i;
	   Uint32 l_amount;

	   for ( i=0; i< FRAMEWORK_MAXSOUNDS; i++)
	   {
	      l_amount = (sounds[i].dlen-sounds[i].dpos);
	      if ( l_amount > (Uint32)len )
	      {
	         l_amount = (Uint32)len;
	      }
	      SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos], l_amount, SDL_MIX_MAXVOLUME);
	      sounds[i].dpos += l_amount;
	   }
	}

	void FrameworkAudioPlayWave(char *file)
	{
	   int index;
	   SDL_AudioSpec wave;
	   Uint8 *data;
	   Uint32 dlen;
	   SDL_AudioCVT cvt;

	   /* Look for an empty (or finished) sound slot */
	   for ( index=0; index< FRAMEWORK_MAXSOUNDS; ++index ) {
	      if ( sounds[index].dpos == sounds[index].dlen ) {
	         break;
	      }
	   }

	   if ( index == FRAMEWORK_MAXSOUNDS )
	      return;

       /* Load sound file and convert it to 16-bit stereo 22kHz */
       if ( SDL_LoadWAV(file, &wave, &data, &dlen) == NULL ) {
          cout<< "Couldn't open "<< file<< ","<< SDL_GetError()<< endl;
          return;
       }
       SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 2, 22050);
       cvt.buf = (Uint8 *)malloc(dlen*cvt.len_mult);
       memcpy(cvt.buf, data, dlen);
       cvt.len = dlen;
       SDL_ConvertAudio(&cvt);
       SDL_FreeWAV(data);

       /* Insert the sound into the slot and play it */
       if ( sounds[index].data ) {
          free(sounds[index].data);
       }
       SDL_LockAudio();
       sounds[index].data = cvt.buf;
       sounds[index].dlen = cvt.len_cvt;
       sounds[index].dpos = 0;
       SDL_UnlockAudio();
    }

The handling of meshes and the rest of the OpenGL code including shaders are identical to the last tutorial so we would not discuss them here.

Choosing between GLUT framework or SDL framework

The code in this and the subsequent tutorials has two frameworks to choose from. The glut framework that we developed in the last tutorial and the SDL framework that we have developed in this tutorial. To use GLUT framework, you need to define the preprocessor FRAMEWORK_GLUT in your compiler setting. In Visual Studio 2008, you can go to Project -> Properties -> Configuration Properties-> C/C++ ->Preprocessor and add FRAMEWORK_GLUT to the preprocessor definitions. To enable SDL framework, define FRAMEWORK_SDL. Note that you can only use one framework at a time and not both. In addition, if you want to use the GLM matrix library, add the preprocessor USE_GLM otherwise the spacesimulator.net's matrix library is used. Running the code gives us the following output. You may press the 'w','a','s','d' keys along with several other keys to transform the camera and see the result:

Tut sdlframework opengl3d3.png

SOURCE CODE

The Source Code of this lesson can be downloaded from the Tutorials Main Page