SDL2 + OpenGL Hello World

Home --> Programming Projects --> FPS Game Code --> SDL Hello World Tutorial
This is a very basic C++ project for using SDL2 + OpenGL with the MinGW compiler on Windows. It uses a makefile.

Although this is intended for MinGW, the directory structure allows for use with other compilers.

SDL2 library and include files should be put in certain sub folders of the project. See later for how to put these files in standard MinGW locations. OpenGL library and include files should already come with MinGW and be in standard locations.

Download

Here is the project.

Installing MinGW + MSYS

First, install MinGW (and MSYS) in the directory c:/MinGW/, or whatever is the default install directory.

There are various .exe files in the bin directory of MinGW. For example, g++.exe. There are also various .exe files in the bin directory of MSYS, such as bash.exe and make.exe.

Add c:/MinGW/bin to the PATH environment variable. You also want to add c:/MinGW/msys/1.0/bin to the PATH environment variable (unless you want to use your own bash and make programs).

At this point, one can compile a hello world c++ program using g++ in cmd. Using cmd on Windows for any longer than a couple seconds induces insanity. Because of this, I recommend using Console2 for a terminal and the program bash.exe as a shell (use c:/MinGW/msys/1.0/bin/bash.exe). You can set Console2 to use whatever program you want as the default shell. With this setup, tab completion should work correctly.

The Directory Structure of the Project

Go on the internet and download a version of SDL2. Open the Include/MinGW folder of this project and find the folder called "SDL2". Move the header files you downloaded into this Include/MinGW/SDL2 folder. Examples of files that are in this SDL2 folder are "SDL.h". Note: if you do not want to move the SDL2 include files into this project, you can instead install them into a standard MinGW location. That is, you can put them in C:/MinGW/include/SDL2.

Remember that OpenGL should be installed in C:/MinGW/include/GL. On my computer this C:/MinGW/include/GL folder contains only the following three files: "gl.h", "glext.h", "glu.h".

Go to your downloaded version of SDL2. Find the lib folder there. Then open the Lib/MinGW/SDL2 folder of THIS project. Put into Lib/MinGW/SDL2 the files "SDL2.lib" and "SDL2main.lib". Again, if you don't want to put these .lib files here you can instead put them in C:/MinGW/lib/SDL2.

Go to your downloaded version of SDL2. Find the bin folder there. Then open the Release/Bin/WindowsMinGW folder of THIS project and copy into it the following files: "SDL2.dll". You also should somehow find the following files on the internet or wherever and copy them into this location: "libstdc++-6.dll" and "libgcc_s_dw2-1.dll".

After installing the files described above, to make the project you cd into Source/ and then run the command "make" from the terminal. This will create "Source/Obj/MinGW/main.o" and "Release/Bin/WindowsMinGW/program.exe". We will explain what the makefile does in the next section.

Compiling and Linking the project

To compile and link the project, go to Source/ and run the command "make" from the terminal.

First, this will cause the only source file main.cpp to be compiled. This is the command that is executed to compile the file:
       g++ -std=c++17 -Wall -O3 -I../Include/MinGW/ -o Obj/MinGW/main.o -c main.cpp
The -std=c++17 says we want to use the C++17 version of the C++ language. The -Wall says we want the compiler to output all warnings. The -O3 means we want the code to be optimized (in a certain way). The "-I../Include/MinGW/" tells the compiler to look in ../Include/MinGW/ for .h files. This is important, otherwise the compiler would not find "SDL.h". The "-o Obj/MinGW/main.o" tells the compiler the name of the file that it should output. The "-c main.cpp" says that main.cpp is the file we want to compile. Note that if we put the SDL2 headers in the standard location (C:/MinGW/include/SDL2), then we would not need the -I option.

This is the command that is run when the project is linked (the command is all in one line, but I displayed it in two lines):
       g++ -std=c++17 -L../Lib/MinGW/SDL2 -o ../Release/Bin/WindowsMinGW/program.exe
	   Obj/MinGW/main.o -lmingw32 -lSDL2main -lSDL2 -lOpengl32 -lglu32
The "-L../Lib/MinGW/SDL2" option tells the compiler to look in ../Lib/MinGW/SDL2 for .lib files. The "-o ..." option specifies the path of the output file. The "Obj/MinGW/main.o" says this object file should be used as input to the linking. The "-lmingw32" says that the compiler should use a certain standard MinGW library. The "-lSDL2main" says that the compiler should look for SDL2main.lib (which it will find in ../Lib/MinGW/SDL2). The "-lSDL2" says that the compiler should look for SDL2.lib (which it will find in ../Lib/MinGW/SDL2). The "-lOpengl32" says that the compiler should look for a certain standard OpenGL library (which should already be installed in a standard location in the MinGW compiler). The "-lglu32" says that the compiler should also look for the OpenGL glu library. Although this glu library is NOT used in this project, I am including this here just to test that it exists.

Running The Program

After compiling and linking the project, open the folder Release/Bin/WindowsMinGW and double click on "program.exe". This will make the screen turn black, and in the upper right there should be a green triangle. Press escape to exit the program.

main.cpp

For completeness, here is the code of main.cpp:
//File: main.cpp

//Note: SDL.h needs to be included before
//the main function, because SDL.h has a macro
//that redefines the main function.
#define SDL_MAIN_HANDLED //Disable the SDL_Main macro.
#include  //for SDL_Quit

//Including OpenGL.
#include 
#include 

//For hiding the console window.
#include 

#include 
#include 
using std::string;
using std::cout;
using std::endl;

//-----------------------------------------------
//
//               EXITING THE PROGRAM
//
//-----------------------------------------------

void PoliteExit() {
    cout << "Exiting program from PoliteExit.\n";
    //Might want to do more here.
    exit(0);
}

//-----------------------------------------------
//
//                     INIT SDL
//
//-----------------------------------------------

//The SDL 2.0 way.
SDL_Window * Helper_SDL_Init() {
    string caption = "SDL2 Hello World";

    //Initializing SDL itself:
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) == -1) {
        cout << "Error: Could not initialize SDL2" << endl;
        PoliteExit();
    }

    //Setting the bit width of the OpenGL depth buffer.
    //Should this be before or after SDL_CreateWindow?
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    SDL_Window * window = 0;

    //Using the option SDL_WINDOW_FULLSCREEN_DESKTOP
    //is perhaps not necessary.  However a good thing about
    //using it is we do not have to ask Windows for the
    //desktop resolution.
    Uint32 flags =
        SDL_WINDOW_OPENGL |
        SDL_WINDOW_FULLSCREEN_DESKTOP;
    window = SDL_CreateWindow(
        caption.c_str(),
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        0, 0, flags);

    if( window == 0 ) {
        cout << "Error: SDL_CreateWindow failed." << endl;
        cout << "Try setting the screen width and height to that"
            " of your desktop" << endl;
        PoliteExit();
    }

    //Setting the window title.
    SDL_SetWindowTitle(window, caption.c_str());

    //Creating the open GL context.
    //Do I need to do anything with this?
    //SDL_GLContext glcontext =
    SDL_GL_CreateContext(window);

    //So mouse does not have min and max x and y values.
    //SDL_ShowCursor(SDL_DISABLE);
    SDL_SetRelativeMouseMode(SDL_TRUE);

    return window;
}

//-----------------------------------------------
//
//                PROCESS INPUT
//
//-----------------------------------------------

void Helper_ProcessInput() {
    SDL_Event e;
    while (SDL_PollEvent(&e)) {
        if( e.type == SDL_KEYDOWN &&
            e.key.keysym.sym == SDLK_ESCAPE )
        {
            cout << "Exiting program because escape key pressed.\n";
            PoliteExit();
        }
    }
}

//-----------------------------------------------
//
//                   RENDERING
//
//-----------------------------------------------

void Helper_Render(
    SDL_Window * sdl_window)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //
    //Drawing a green triangle in the upper
    //right and corner of the screen.
    glBegin(GL_TRIANGLES);
    glColor3f(0.2, 0.7, 0.5);
    glVertex3f(0, 0, 0);
    glVertex3f(1, 0, 0);
    glVertex3f(0, 1, 0);
    glEnd();
    //
    SDL_GL_SwapWindow(sdl_window);
}

//-----------------------------------------------
//
//                MAIN FUNCTION
//
//-----------------------------------------------

int main(int argc, char ** argv) {
    //Hiding the console window.
    HWND windowHandle = GetConsoleWindow();
    ShowWindow(windowHandle,SW_HIDE);

    //Maping stdout to a file.
    std::freopen("../../stdout.txt", "w", stdout);
    
    cout << "SDL2 Hello world!\n";
    cout << "Here in main function.\n";

    //Needed because #defined SDL_MAIN_HANDLED.
    //See https://wiki.libsdl.org/SDL_SetMainReady
    SDL_SetMainReady();

    //Making it so SDL_Quit is called
    //the program exits.
    //Important, because otherwise output
    //to stdout.txt may (sometimes) not appear:
    atexit(SDL_Quit);

    //Initializing SDL.
    SDL_Window * sdl_window = Helper_SDL_Init();
    if( !sdl_window ) {
        cout << "*** Error: std_window is null.\n";
        PoliteExit();
    }

    while(true) {
        Helper_ProcessInput();
        Helper_Render(sdl_window);
    }

    //This line is so there are
    //no compiler warnings.
    return 0;
}