#

## Using OpenGL with ClanLib

ClanLib's primary render target is OpenGL. It is handled by the module clanGL, a target for clanDisplay.

ClanDisplay is an abstraction, working to allow ClanLib applications to do stuff in a general matter that can be applied to any display target supported by ClanLib. In order to use clanDisplay, one or more display targets must also be setup. For example, a GL-based ClanLib app must call both CL_SetupDisplay::init() and CL_SetupGL::init() when starting.

Because clanDisplay is an abstraction, its API in itself will not directly offer you access to the objects the implementation is using. Instead, each of the display targets offer their own set of subclasses for clanDisplay. An example of this the CL_OpenGLSurface class. This is the OpenGL version of CL_Surface - you can use it anywhere in ClanLib where it takes a CL_Surface object, and you can cast between the two types of objects:

            CL_Surface some_surface("fish.png");
            CL_OpenGLSurface gl_version = some_surface;
            gl_version.bind();

The display target specific versions of the clanDisplay classes offer ways to integrate directly with target specific code. The bind() function on CL_OpenGLSurface binds the texture used by the surface, for apps wanting to do 3D OpenGL stuff, but use ClanLib to load the textures. Sometimes it's also possible to get the internal handles used by the target.

### OpenGL states

OpenGL as a library is very hardware oriented. It uses a state model that reflect how hardware is, more than how things are ideally from a software developers point of view. This is both the strength and weakness of the OpenGL API. It makes OpenGL very simple and fast, but also makes it hard to write component oriented rendering.

If a state is set in OpenGL (ie glEnable(GL_TEXTURE_2D) or glBindTexture(textureid)), it remains in this state until its changed. This makes it problematic for different rendering code to share the same OpenGL context. If the first piece of render code (ie ClanLib) enables GL_TEXTURE_2D, and the next render code expects it to be disabled, then the output produced may be wrong. To make things even worse, for each graphic context in ClanLib, there is a new OpenGL context. Each of those OpenGL contexts maintain their own set of states.

To address these issues, clanGL introduces a concept called OpenGL state, maintained by the class CL_OpenGLState. CL_OpenGLState acts like there is a seperate OpenGL context (state setup) for each CL_OpenGLState object contstructed. It setups and keeps OpenGL states in a known configuration for the user of CL_OpenGLState.

Currently it tracks the following states:

- glEnable/glDisable GL_TEXTURE_2D
- glBindTexture GL_TEXTURE_2D
- glEnable/glDisable GL_BLEND
- glEnable/glDisable GL_FOG
- glEnable/glDisable GL_DEPTH_TEST
- glEnable/glDisable GL_SCISSOR_TEST
- glEnable/glDisable GL_ALPHA_TEST
- glEnable/glDisable GL_STENCIL_TEST
- glViewport
- glScissor
- glClearColor
- glBlendFunc
- glColor
- glVertexPointer
- glColorPointer
- glTexCoordPointer
- glEnableClientState GL_VERTEX_ARRAY
- glEnableClientState GL_COLOR_ARRAY
- glEnableClientState GL_TEXTURE_COORD_ARRAY
- glMatrixMode
- matrix GL_TEXTURE
- matrix GL_PROJECTION
- matrix GL_MODELVIEW
- glTexEnvf GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE
- glPolygonMode
- glAlphaFunc
- glStencilFunc
- glStencilOp

Only one CL_OpenGLState is active in clanlib at a time. When its made active, it will setup OpenGL to the state that the instance had last time it was active.

To illustrate with an example, look at the following code:

    CL_OpenGLState state_a(window1.get_gc());
    CL_OpenGLState state_b(window1.get_gc());
    CL_OpenGLState state_c(window2.get_gc());

    state_a.set_active();
    glColor3d(1.0,0.0,0.0);

    state_b.set_active();
    glEnable(GL_TEXTURE_2D);
    glBindTexture(texture_handle);

    state_c.set_active();
    glColor3d(0.0,0.0,1.0);

    state_a.set_active();
    glBegin(GL_QUADS);
    glVertex3d(...); glVertex3d(...); glVertex3d(...); glVertex3d(...);
    glEnd();

    state_b.set_active();
    glBegin(GL_QUADS);
    glVertex3d(...); glVertex3d(...); glVertex3d(...); glVertex3d(...);
    glEnd();

In the above example, the first quad drawn will be rendered with GL_TEXTURE_2D disabled, but with color red color (1.0, 0.0, 0.0). The second quad will be with GL_TEXTURE_2D enabled, texture bound to texture_handle and with white color (OpenGL default color).

### Doing custom OpenGL with ClanLib

If you want to do your own OpenGL (or use external OpenGL specific libraries) with clanGL, you need to create your own CL_OpenGLState object. Internally ClanLib works with a set of CL_OpenGLState objects, one for each of the following steps, but also other places. This is just to describe the general idea.

- Texture uploads and downloads (CL_Surface)
- ClanDisplay 2D Batch Rendering (lines, fills, surface rendering)
- Copying from back buffer to front (CL_DisplayWindow::update())
- Flipping front and back buffers (CL_DisplayWindow::flip())
- Copying texture to pbuffer (CL_Surface::get_gc())
- Copying from pbuffer to texture (CL_Surface::draw())

Each of those commands act as stand alone components that do not know terrible much about each other. They each have their own CL_OpenGLState object that keep OpenGL's states like they set them up. If you call CL_DisplayWindow::flip(), then ClanLib makes a CL_OpenGLState::set_active() call, causing the current active OpenGL state to be saved, and the new one loaded.

This can happen in any clanDisplay/clanGL call you might make, except those specifically not designed to do so, like CL_OpenGLSurface::bind(). Common error is when loading surfaces/textures - make sure to reactivate your state after doing this.

By creating one (or more) CL_OpenGLState objects yourself, ClanLib is able to store your OpenGL setup and restore it when you need to do OpenGL calls next time. Simply call set_active() before every time you are going to do OpenGL. If the state object is already active, no actions will be performed.

    CL_DisplayWindow window("Hello OpenGL!", 640, 480);
    CL_GraphicContext *gc = window.get_gc();
    CL_OpenGLSurface surface("hello.png");

    // Setup my OpenGL state:
    CL_OpenGLState my_opengl(gc);
    my_opengl.set_active();
    glEnable(GL_TEXTURE_2D);
    surface.bind();
    glMatrixMode(GL_PROJECTION);
    glProject(...);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(...);

    while (CL_Keyboard::get_keycode(CL_KEY_ESCAPE) == false)
    {
    	// draw my opengl world:
    	my_opengl.set_active();
    	glBegin(GL_TRIANGLES);
    	glVertex4d(...);
    	glVertex4d(...);
    	glVertex4d(...);
    	glVertex4d(...);
    	glEnd();

    	// draw HUD (with clanlib 2D stuff):
    	gc->draw_line(...);

    	// Make things visible for user:
    	window.flip();
    	CL_System::keep_alive();
    }

### Accessing OpenGL 2.0

One of the problems you will face when using OpenGL is that nobody really got the OpenGL 2.0 API (at least at the time this was written). If you use the standard OpenGL headers and libraries delivered by your operating system, you will get:

Operating System | OpenGL version | Extensions
--- | --- | ---
Microsoft Windows | OpenGL 1.2 | No extensions in glext.h added since 1995 when Microsoft abandoned OpenGL.
Linux | OpenGL 1.2 | Each vendor got their own set of OpenGL headers. Nvidia got theirs, ATI got theirs, DRI too. Depending on the age of the headers, they can be anywhere between OpenGL 1.3's extensions to OpenGL 1.5.
Apple Mac OS X | OpenGL 1.4 (for Jaguar, OS X 10.3.5) | Extensions? Apple didnt even bother make an aglGetProcAddress function to at least get you started!

ClanLib offers the complete OpenGL 2.0 API via its own set of macros. All you need to do is to include ClanLib/GL/opengl_wrap.h, and then change the gl prefix to cl. It's that simple.

    // Setup my OpenGL state:
    CL_OpenGLState my_opengl(gc);
    my_opengl.set_active();

    // Do some OpenGL 2.0 specific stuff:
    CLInt shader_handle = clCreateShader(CL_FRAGMENT_SHADER);
    clShaderSource(shader_handle, ..);
    clCompileShader(shader_handle);

    CLInt program_handle = clCreateProgram();
    clAttachShader(program_handle);
    clLinkProgram(program_handle);

    clUseProgram(program_handle);

### Custom OpenGL 2D

If you intend to do 2D OpenGL rendering, you can get ClanLib to manage your projection matrix, view matrix and clipping (glScissor). To enable that, simply call CL_OpenGLState::setup_2d() when setting up your OpenGL state. Changes to the CL_GraphicContext's view matrix or clipping will be applied automatically.

    // Setup my OpenGL state:
    CL_OpenGLState my_opengl(gc);
    my_opengl.set_active();

    // We want to work in 2d, doing our own fancy OpenGL effects:
    my_opengl.setup_2d();

    // Ok so our fancy effect is just a boring triangle here :)
    clBegin(CL_TRIANGLES);
    clVertex2i(x, y);
    clVertex2i(x + 10, y);
    clVertex2i(x, y + 10);
    clEnd();

Questions or comments, write to the ClanLib mailing list.