OpenGL Programming/Modern OpenGL Tutorial Navigation
- glutGetModifiers() == GLUT_ACTIVE_SHIFT or GLUT_ACTIVE_CTRL or GLUT_ACTIVE_ALT
Beware: GLUT's special() callback is called repetitively when the press is hold down.
There are several ways to position the (X,Y,Z) axis triplet.
- Z-is-up : used in Blender, this means (X,Y) is the ground, and Z is the altitude
- Y-is-up : used in OpenGL, this means (X,Y) is like a wall, so Y is the altitude, and Z is going closer/farther to that wall
- right-handed / left-hand : OpenGL and Blender use a right-handed coordinates system, which means you can represent it using your right hand, with X on the thumb, Y on the index, and Z on the middle finger. A left-handed coordinates system, as used by DirectX, can be represented similarly using the left hand.
Using the Blender Z-is-up coordinates system in OpenGL can be tempting, but introduces issues:
- .obj files are usually in Y-is-up, requiring changes when loading them (or when exporting them from Blender)
- the camera is in Y-is-up, so if you use a Z-is-up coordinates system, by default the camera is at (0,0,0) and it faces the bottom. This is quite confusing when rotating the camera, because, for instance, if your camera is looking on the horizon towards the Y axis, and you rotate on the Z axis to look left or right, you'll actually rotate the view on the Y axis (barrel roll). When transforming the camera, you'd have to imagine that you shoot the video from your feet.
- other engines such as Irrlicht and Ogre also use Y-is-up
Consequently we'll stick with Y-is-up in this book.
Positioning the camera
There's no built-in concept of camera in OpenGL. We have to somewhat cheat to implement it.
Our technique is the following: instead of thinking how to move our camera in a fixed world, we're thinking how to move the entire world around our camera.
For instance, moving the camera for 3 units on the X axis is the same as moving the whole world for -3 units on the X axis. Same for all other axis and for rotations.
Keep this in mind when working with the camera. The end-result will feel completely intuitive, but whenever you work with the camera, if you forget that we've been cheating in the first place, you'll get the wrong transformations.
We'll start with an intuitive implementation:
- left/right rotates on the camera Y axis
- up/down moves forwards/backwards
- page_up/page_down rotates on the camera X axis
/* code here */
To go forwards/backwards, we use
glm::translate. There are two forms:
glm::translate(transformation_matrix, glm::vec3(dx, dy, dz)): applying
transformation_matrix, then move in (dx,dy,dz) expressed in the new coordinates system
glm::translate(glm::mat4(1), glm::vec3(dx, dy, dz)) * transformation_matrix: applying
transformation_matrix, then move in (dx,dy,dz) expressed in the coordinates system as it was before the translation
- in the case of a camera translation, don't forget that we cheat and move the whole world, not the camera; consequently the above meaning is reversed for the world2camera matrix.
For instance, if your existing
transformation_matrix rotates your camera 90° to the right, and then translate along Z:
- with the first form you will translate the camera's new left (along world's Z axis),
- while with the second form your will translate the camera forwards (along world's X axis).
This implementation has usability issues: when rotating the camera left/right while it's bent, we're not used to rotate our entire body on this bended axis. Instead, we're used to rotate just our head on the world straight Y axis. So we need to translate the world Y axis to the camera's local coordinates:
glm::vec3 y_axis_world = glm::mat3(transforms[MODE_CAMERA]) * glm::vec3(0.0, 1.0, 0.0); transforms[MODE_CAMERA] = glm::rotate(glm::mat4(1.0), -delta_rotY, y_axis_world) * transforms[MODE_CAMERA];
Last, let's implementing strafing:
/* code here */