This is how I did it: logically, the cube consists of six faces, each with nine pieces. Each face has a base orientation such that if you have a solved cube and the cube is rotated by that base orientation, you are looking at that face with the texture oriented right side up. From this view, you have four adjacent faces, each of which are called the North, East, South or West neighbor. To minimize special cases, I chose the orientations in such a way that every face is it's northern neighbor's eastern neighbor. This created a (non-unique) face adjacency table of:
const int face_adjacency[6][4] = {
{ 1, 2, 4, 3 },
{ 2, 0, 3, 5 },
{ 0, 1, 5, 4 },
{ 4, 5, 1, 0 },
{ 5, 3, 0, 2 },
{ 3, 4, 2, 1 }
};
Every piece has three pieces of information: which face it's texture was originally on, which part of the texture it is (upper left, upper center, upper right, etc.) and what the current orientation is. Originally each piece has an orientation I called North. Now if you take this face and rotate it right (clockwise) ninety degrees, then the upper right piece will now have the texture the upper left piece had, but it's orientation is now East rather than North. Similarly, the upper center piece now has the texture of the middle right piece, but is also oriented East rather than North, and the center just changes orientation. Basically there are two cycles of pieces that need to be swapped: UR, UL, LL, LR and UC, MR, LC, ML. With an assigned enum values of North = 0, East = 1, South = 2 and West = 3, it means that each of these right rotations needs to increment facing by 1 (mod 4).
However, rotation also requires swapping the piece information for the adjacent pieces on the adjacent faces. There are three cycles to be considered here: N.UR, E.UL, S.LR, W.LL; N.MR, E.UC, S.LC, W.ML and N.LR, E.UR, S.LL, and W.UL. (Where N.UR is the adjacent face on the north's upper right piece, etc.) Like the previous rotations, these also require a facing change, though it's not quite as simple as adding one. Moving from the west face to the north face adds 2, from the south face to the west face adds 1, from the east face to the south face adds 2 and from the north face to the east face adds 3 (all mod 4).
From here rendering each piece is a matter of taking the texture coordinates associated the piece's texture part (upper left, upper center, etc.) and offsetting them according the the orientation. If the piece's orientation is north, then the first texture coordinate should go with the first vertex, second texture coordinate with the second vertex, and so on. If the piece's orientation is east, then the first vertex should go with the second texture coordinate, and so on. Basically add the orientation value and take the modulus by four. (It might be subtract the orientation value depending if you're using clockwise or counter-clockwise winding.)