Because you mentioned wanting an “OO” design, I figured I’d describe my approach to that, first. But don’t worry, there’s a bit of math at the end, addressing the original point of how to rotate a piece of geometry around a specific point.
The design I like is one that has three separate components:
The thing deciding what to draw.
The thing deciding how to draw.
The thing doing the drawing.
In reality, “what to draw” would be your scene graph, “how to draw” would be your mesh, and “doing the drawing” would be your renderer abstraction. Your scene graph probably uses some instance meta-data or an instance interface to keep track of things like position and orientation.
Which is a long way of saying:
Your call should end up looking something like:
SceneGraph::renderStuff() {
foreach visible item in allItems {
renderer->drawMesh( item.mesh, item.shader, item.pos, item.ori );
}
}
The actual math of setting up the viewing matrix should probably be inside the renderer, not inside the item, because otherwise each item needs to know about things like what your camera is (or whether you’re rendering for, say, a projected shadow).
Thus, using GL commands only, your drawMesh() might look like this:
Renderer::drawMesh( mesh, material, pos, ori ) {
material->setupState();
glLoadIdentity();
glTranslatef( -camera.pos.x, -camera.pos.y, -camear.pos.z );
glRotatef( -camera.angle, camera.up.x, camera.up.y, camera.up.z );
glTranslatef( pos.x, pos.y, pox.z );
glRotatef( ori.angle, ori.x, ori.y, ori.z );
setupPointers( mesh.pointers() );
drawLists( mesh.lists() );
}
After doing this for a little bit, you’ll realize that keeping “ori” as a quaternion, and doing the position/orientation math using quaternions and vectors is easier, and then you spit out the combined modelview into a matrix and load it with LoadMatrix().
If you have geometry that’s currently centered around some local origin X, then to rotate around this origin, you want to actually perform these operations in squence:
Translate object by -localOrigin
Plain rotation (around 0,0,0)
Translate object by +localOrigin
If you in addition want the object positioned somewhere other than at its local origin in the “world” space, you’d have to add that offset to the last translation in this squence. That would be the camera position in the above pseudo-code.
Typically, the “local origin” is set by a 3d model artist inside the modeling program, as the “pivot” or “node” point for the object. Most often, the exporter will pre-bake the “offset by -localOrigin” into the exported data, so you don’t have to do that yourself.
If you’re getting oval or lopsided rotation, then it’s likely that your notion of “local origin” for the geometry in question isn’t actually the point you want. The local origin usually needs to be the apparent center of mass for the rotation to look good.