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 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:
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).
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.
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(); }
Todo: write about CL_OpenGLState::setup_2d()
Todo: Write about changing states or texture forces ClanLib's Batch Render engine to break current batch.
Todo: Write that people should group renderings by texture (surface/sprite) and states.