Hi,
After some unsuccessful researches (see here), I finally managed to render stuff ingame, with or without the game's zbuffer (your choice), which wasn't covered (maybe even done) yet here so some of you could be interested, even if most of you use directx.
The code (tasty C/C++ mix) I will explain with is written for linux so I give no guarantee for other systems, but it can be ported to Windows probably very easily (see here). The code is given here in its simplest form, for the purpose of the howto. It can obviously be greatly improved and made error-safe that's not the point. Current version is 3.5.5.12340.
First of all, we will have to make our drawings at the right place: we don't want them to be over the interface. glDepthRange is called with (0.0f, 0.94f) when the game is drawing the world, and is called with (0.0f, 1.0f) when the ui is being drawn. Then we will just hook glDepthRange and draw there right before calling the first glDepthRange(0.0f, 1.0f) of each frame.
Code:
bool drawn = false;
void glDepthRange(GLclampd nearVal, GLclampd farVal) {
static void (*real_glDepthRange)(GLclampd, GLclampd) = 0;
if (!real_glDepthRange)
real_glDepthRange = (void (*)(GLclampd, GLclampd))dlsym(RTLD_NEXT, "glDepthRange");
if (!drawn && farVal == 1) {
draw();
drawn = true;
}
real_glDepthRange(nearVal, farVal);
}
void hook_glXSwapBuffers(Display* _dpy, GLXDrawable _drawable) {
static void (*real)(Display*, GLXDrawable) = 0;
if (!real)
real = (void (*)(Display*, GLXDrawable))dlsym(RTLD_NEXT, "glXSwapBuffers");
if (!drawn)
draw();
drawn = false;
real(_dpy, _drawable);
}
void (*glXGetProcAddressARB(const GLubyte* _procname))() {
static void* (*real)(const GLubyte*) = 0;
if (!real)
real = (void* (*)(const GLubyte*))dlsym(RTLD_NEXT, "glXGetProcAddressARB");
void* result = 0;
if (std::string("glXSwapBuffers") == (char*)_procname)
result = (void*)hook_glXSwapBuffers;
if (!result)
result = (void*)real(_procname);
return (void (*)())result;
}

If you use wine, a patch is required to hook glXSwapBuffers like this (explained here).
Now what about the draw function?
The original opengl context is constantly modified, which is a risk for us to attend unexpected results using it. Besides that, the other way is true (altering the original context in a bad way) and restoring every value isn't appealing. Creating our own opengl context seems to be the best solution.
Every time we want to draw, we will make our context current, and switch back on the original when done.
We have to enable the zbuffer, set the viewport and correctly place and orient the camera with the right fov, ratio, znear and zfar. gluPerspective and gluLookAt will do all the job for us.
Most of the needed variables are in the camera structure given by CGWorldFrame__GetActiveCamera (source 1 & source 2, probably partially outdated tho).
Besides that, a glScalef(2.0f, 2.0f, 1.0f) is needed to make our drawings match to the game's perspective and zbuffer. Don't ask how I've found that, just remove it and see by yourselves.
The result:
Code:
#include <iostream>
#include <GL/glx.h>
#include <GL/glu.h>
#include <GL/gl.h>
#include <cmath>
struct WoWCameraVTable {
uint32_t dontcare;
__attribute__((stdcall)) void (*get_forward_vector)(float*); //(1)
};
struct WoWCamera {
WoWCameraVTable* vtable;
uint32_t dontcare0;
float x;
float y;
float z;
uint32_t dontcare1[9];
float znear;
float zfar;
float fov;
float ratio;
};
//void tetrahedron(bool orig_zbuffer, float x, float y, float z);
void draw() {
Display* dpy = glXGetCurrentDisplay();
GLXContext orig_ctx = glXGetCurrentContext();
GLXDrawable drawable = glXGetCurrentDrawable();
static GLXContext my_ctx = 0;
if (!my_ctx) {
//create our own opengl context
//this is the less portable part, but it's still very doable
int screen = -1;
glXQueryContext(dpy, orig_ctx, GLX_SCREEN, &screen);
int attribs[] = { GLX_FBCONFIG_ID, -1, None };
int dummy;
glXQueryContext(dpy, orig_ctx, GLX_FBCONFIG_ID, &attribs[1]);
GLXFBConfig* fb = glXChooseFBConfig(dpy, screen, attribs, &dummy);
XVisualInfo* vis = glXGetVisualFromFBConfig(dpy, *fb);
my_ctx = glXCreateContext(dpy, vis, 0, True);
}
glXMakeCurrent(dpy, drawable, my_ctx);
glEnable(GL_DEPTH_TEST);
uint32_t scr_w = 400, scr_h = 300;
glXQueryDrawable(dpy, drawable, GLX_WIDTH, &scr_w);
glXQueryDrawable(dpy, drawable, GLX_HEIGHT, &scr_h);
glViewport(0, 0, scr_w, scr_h);
WoWCamera* cam = ((WoWCamera* (*)())0x004F5960)(); //CGWorldFrame__GetActiveCamera
if (cam != 0) { //use the camera to draw only if ingame
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(cam->fov * 180 / M_PI, cam->ratio, cam->znear, cam->zfar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glScalef(2.0f, 2.0f, 1.0f);
float forward[3];
asm("movl %0, %%ecx;" : : "m" (cam)); //(2)
cam->vtable->get_forward_vector(forward); //get forward vector
gluLookAt(cam->x, cam->y, cam->z,
cam->x + forward[0], cam->y + forward[1], cam->z + forward[2],
0, 0, 1);
//draw here
//tetrahedrons on the screenshots (Hammerfall camp, Arathi):
//tetrahedron(true, -960.739f, -3483.61f, 52.4689f);
//tetrahedron(false, -955.158f, -3492.94f, 52.3903f);
} else {
//not ingame but you still can draw
//just don't use CGWorldFrame__GetActiveCamera
}
glXMakeCurrent(dpy, drawable, orig_ctx);
}
cam->vtable->get_forward_vector at (1) is actually a thiscall function and not a stdcall function, this (camerabase) being passed in ecx at (2). This is a workaround to respect the windows' thiscall calling convention under linux (explained here).
Before finally drawing, call glDepthRange passing (0.0f, 0.94f) is you want your drawings to be hidden when behind an object of the game. Pass (0.0f, 0.1f) otherwise, then just draw.

Here is the tetrahedron drawing function:
Code:
void tetrahedron(bool orig_zbuffer, float x, float y, float z) {
glDepthRange(0.0f, (orig_zbuffer) ? 0.94f : 0.1f);
glPushMatrix();
glTranslatef(x, y, z);
glScalef(3.0f, 3.0f, 3.0f);
glBegin(GL_TRIANGLE_FAN);
glColor3ub(255,0,255);
glVertex3f(0.0f,0.0f,1.0f);
glColor3ub(255,0,0);
glVertex3f(1.0f,0.0f,0.0f);
glColor3ub(0,255,0);
glVertex3f(cos(2*M_PI/3),sin(2*M_PI/3),0);
glColor3ub(0,0,255);
glVertex3f(cos(-2*M_PI/3),sin(-2*M_PI/3),0);
glColor3ub(255,0,0);
glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glPopMatrix();
}
Code:
-4937.0f -936.0f 504.0f //Ironforge, bridge in front of the AH
5667.0f 675.0f 653.0f //Dalaran, southern bank
-8914.0f 624.0f 100.0f //Stormwind bank
1617.0f -4379.0f 13.0f //Orgrimmar, bank



Feel free to criticize, correct or complete.