I am also a bit newbie in this, but let me describe the way I do it.
I can convert angle (Euler) rotation to quaternion like this:
Quaternion qHeading(Vector3(0.0f, 0.0f, 1.0f), headingRadians); // my "up" is Z
Quaternion qPitch(Vector3(1.0f, 0.0f, 0.0f), pitchRadians); // my "up down" rotates around X
Quaternion qRoll(Vector3(0.0f, 1.0f, 0.0f), rollRadians); // my "roll" around Y
auto rot = qHeading * qPitch * qRoll;
I can convert quaternion rotation to analogical matrix transformation with my math lib:
Matrix4 rotMatrix;
rot.to4x4Matrix(&rotMatrix);
I can convert my position to another matrix transformation:
Vector3 pos(15, 0, 0); // example position
Matrix4 posMatrix;
pos.to4x4Matrix(&posMatrix);
Now, I can get a single matrix which represents BOTH transformations by multiplying them:
Matrix4 finalMatrix = rotMatrix * posMatrix;
And I can use this matrix in OpenGL to do both transformations in one call:
glMultMatrixf(finalMatrix.element); // "element" here is matrix in OpenGL compatible 4x4 array
Now, back to rotations: Note how Quaternion rotation is just composition of different rotations about arbitrary vectors.
You can easily rotate Quaternion around any axis just like so:
Quaternion newRot(Vector3(1.0f, 0.0f, 0.0f), PI / 2); // 90 degrees around X
rot *= newRot;
If you read more about matrices you can do a lot of optimizations to these operations.
You can get back the Euler angles from Quaternion if needed. I find Euler angles still required for objects such as camera.
Now, if my object is actually a collection of other objects rotated and positioned (transformed) with this matrix, this is the way to render it:
glPushMatrix(); // save current OpenGL matrix to stack
glMultMatrixf(objectPosAndRot); // move into object's position and rotation
// do the same for each object in collection! (push, transform, pop)
glPopMatrix(); // restore previous matrix
And for camera's positioning and rotation, you do the same with inverted matrix at the beginning of the scene.
Hopefully I covered it right.
So i thought that it'd be easier doing the above around the (local object axes, and then simply transforming them to world space representation.
That is exactly what happens. But think of it like this:
glPushMatrix(); // saved current world transformation matrix
glTranslate(); // moved into object's position
glRotate(); // rotated in local space
glPopMatrix(); // cancel all transformations, move back to world space
And, ugh, to avoid any confusion, I am doing exactly the same with math lib (Matrix4 finalMatrix = rotMatrix * posMatrix). Well, order of operations happens to be reverse there.