Taking advantage of the new Quake compatible OpenGL drivers for Voodoo1/2
opengl driver dll icd opengl32 card full call functions
With the happy release of the new glsetup program, as well as full drivers for Voodoo1 and Voodoo2 chipset cards, it is a most fabulous day for would-be OpenGL game developers. There's only one hitch - notice I said "full drivers", not "a full ICD". At the time I write this Matrox has just released its final ICD for G200 and G400 cards, and as far as I can tell every single 3D card of any consequence now has full OpenGL support. And I do mean of ANY consequence, in the course of supporting my Azteroidz OGL rendered game, I've yet to find a 3D card I can't get the game running on.
Supporting hardware acceleration with OpenGL is normally as easy as writing an OpenGL program; if the user has 3d hardware, then it will be used to accelerate OpenGL programs via its OpenGL ICD. Enter the problem: the ICD driver was designed long before the introduction of the Voodoo1 and it really wasn't meant for supporting more than one graphics card (ie a separate 3D card and 2D card for example). Therefore it is *IMPOSSIBLE* to have an ICD driver for cards that meet this criteria. Enter the Voodoo1/2 cards, they do have a full OpenGL driver, named 3dfxvgl.dll, but it is not an ICD and as such will not be supported by linking your application to opengl32.lib.
So how does an ICD work?
Not so fast my eager reader - first some fundamentals. OpenGL drivers on windows machines are implemented as a DLL named (appropriately enough) OpenGL32.dll. Every 32-bit windows OS comes with this DLL (except the very original Win95 retail edition, for which it is available as a free download from Microsoft). This is the MS software OpenGL renderer, which is a full implementation of OpenGL 1.1. Now, when you clunk in that favorite 3D card and install the drivers for it, it also installs another OpenGL driver which is named differently depending on your card, (as am example - in the case of my RivaTNT its something like nvogl.dll) it also updates the registry to tell the system that an ICD has been installed and what the name of the ICD dll is. So enter an OpenGL program, it loads OpenGL32.dll when it starts (automagically because it was linked to opengl32.lib), and when it makes OpenGL function calls they are directed at the OpenGL32.dll, which in turn forwards them on to the ICD driver.
The solution to this problem is *NOT* to bind your program to OpenGL32.lib. Instead you must manually load the correct driver - either 3dfxvgl.dll in the case of a Voodoo1 or Voodoo2 card or opengl32.dll in the case of everything else.
When I sat down to make this whole process work for my Azteroidz game, there were a few things that I wanted:
- I wanted to continue to use the standard gl.h include file thus guaranteeing that I will not fall victim to stupid typos or other ills of that nature, and at the same time ensuring that updating to support a new OGL revision would be as easy as adding a few function calls without worrying about header files.
- I wanted to change my existing OpenGL application (Azteroidz3D) as little as possible..
- I wanted to support both MiniGL (the subset of OpenGL that Quake1/2 based games use) and full ICD's, software OGL and everything in between. Admittedly theres pretty much zero reason to be using MiniGL anymore (unless you're really hung up on supporting PowerVR cards like the Matrox M3D) - but its still lurking in my code for legacy reasons.
For the latest code, go here.
Using the OpenGL module
Using the module is really simple, just stick to the following steps:
- Do not include gl.h, include OpenGL.h instead
- Do not link to OpenGL32.lib
- Do not call wgl functions, call the exact same function with a prefix of OpenGL
- Do not call OpenGL related functions like SwapBuffers, call the same function with a prefix of OpenGL instead
- At run-time identify the OpenGL driver to use by using OpenGLFindDriver(), Init it using OpenGLInit()
- At exit, call OpenGLUnInit() to unload the opengl driver.
- Whatever you do, dont use glu*() routines.
Comments on the sample code
GLDemo2.c actually demonstrates quite a few different things in addition to dynamic-loading of the OpenGL driver, starting with full-screen mode (which judging by how often this question is asked in the OpenGL newsgroup, oughtta make it worth a look-see for most people right there). Hopefully the code itself is fairly self-explanatory, I've tried to comment it fairly well.
What it doesn't demonstrate well however, is how to setup a proper camera. Those of you more familiar with OpenGL are doubtless gasping in horror at the fact that I put the camera transform in the projection matrix. And yes I know that this is technically a no-no, as it invalidates OpenGL's fogging and lighting operations. However, in self-defense I'll just point out that this code was originally written a long time ago to run on a MiniDriver which has neither fogging nor lighting (so there). Still readers should beware that this is wrong, evil and just plain bad. Do not imitate my laziness, put the camera in the modelview matrix where it belongs - something I will leave to a future article.
Is all this renaming stuff really necessary
Actually you could probably get by without it by compiling the opengl.c module with a few changes as a lib file. However, because you would be dealing with GDI functions etc, link order would become important, complexity would begin spiraling upward, life as we know it would end, and I decided it wasn't worth the bother.
I'd like to give a special thanks to Dan Mintz for his help in adding support for the full list of OpenGL functions to the module, as well as help with testing on various card/driver combinations.
by Ryan Haksi