The Beginnings of a Useful Camera
Sunday, July 31st, 2005By now I am sure some of you are thinking, “Great, I can draw stuff, but how am I supposed to go around and look at it once it is drawn!?!” Maybe not with that much excitement, but I’m sure someone was thinking it. Anyway, the solution to this problem requires two things. A Camera class and an Input class that will let us tell the camera what to do. If you aren’t familiar with trigonometry and polar coordinates, then you should try to brush up on those concepts before attempting to learn much from this and the following tutorial. I wrote a camera class a while back that had an explanation of the math involved here.
I will be implementing this camera a bit differently since it will be both 1st and 3rd person, but the math involved is the same. If you think you know enough trig to get through this, or you have a new understanding of if after having read my article, then here we go!
Step 1: Implementing the Basic Camera Framework
Start out by creating a new class in your 3D engine project and name it something with the word Camera in it. Mine is going to be HMCamera. Since we are going to be using this camera to control all the aspects of our screen display, and possibly for some fun zoom effects or the like later, I figure we should store all of the variables that we used to initialize the camera in our System class earlier so this one class will give us full control over all of those aspects of our games. Here is the basic class with its member variables and a few useful properties:
public class HMCamera { private Vector3 position = new Vector3(0, 0, 0); // These are the most important values in private Vector3 target = new Vector3(0, 0, 1); // the camera class, and they are what private Vector3 upVector = new Vector3(0, 1, 0); // we make our final View matrix with private int screenWidth; private int screenHeight; // These values tell the camera how wide our view angle is and how close/far we can see private float nearClip = 1.0f; private float farClip = 100.0f; private float fov = (float)Math.PI / 4.0f; // We will use these in the math processes that control movement later private float hRotation = (float)Math.PI / 2.0f; private float vRotation = 0.0f; private float radius = 1.0f; // Public matrix properties that our device can use to set its own matrices before rendering public Matrix World { get { return Matrix.Identity; } } public Matrix View { get { return Matrix.LookAtLH(position, target, upVector); } } public Matrix Projection { get { return Matrix.PerspectiveFovLH( fov, (float)screenWidth / (float)screenHeight, nearClip, farClip ); } } public HMCamera(int width, int height) { screenWidth = width; screenHeight = height; } }
Now that we have a basic camera put into place we need to add the code in the main system part of our engine to use it. First we need to add our member variable for the camera and a public property so we can access it outside the main system, and add an initializer into the constructor of the System as well.
private HMCamera myCamera; public HMCamera MyCamera { get { return myCamera; } } // In the constructor if(InitializeGraphics()) { myScene = new HMScene(); myCamera = new HMCamera(this.Width, this.Height); myInput = new HMInput(this); }
The next thing we can do is delete the SetupCamera() function that created our matrices in the system class and replace it with a new one in the camera that looks like this:
private void SetDeviceCamera() { myDevice.Transform.World = myCamera.World; myDevice.Transform.View = myCamera.View; myDevice.Transform.Projection = myCamera.Projection; }
Now add a call to the SetDeviceCamera() function at the beginning of our system’s main Render function.