www.pudn.com > QuadTreeLOD4cs.rar > Camera.cpp, change:2003-08-14,size:16277b
//***********************************************************************//
// //
// - "Talk to me like I'm a 3 year old!" Programming Lessons - //
// //
// $Author: DigiBen DigiBen@GameTutorials.com //
// //
// $Program: Camera5 (strafing) //
// //
// $Description: Demonstrates camera strafing right and left //
// //
// $Date: 1/1/02 //
// //
//***********************************************************************//
//#include "main.h"
#include <math.h>
#include <windows.h>
#include <gl\glaux.h> // Header File For The Glaux Library
//#include "vector.h"
#define SCREEN_WIDTH 800 // We want our screen width 800 pixels
#define SCREEN_HEIGHT 600 // We want our screen height 600 pixels
#define SCREEN_DEPTH 16 // We want 16 bits per pixel
#include "Camera.h"
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// This is how fast our camera moves (Sped up a bit due to normalizing our vectors)
#define kSpeed 5.5f
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////////// CROSS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns a perpendicular vector from 2 given vectors by taking the cross product.
/////
/////////////////////////////////////// CROSS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
///////////////////////////////// CCAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This is the class constructor
/////
///////////////////////////////// CCAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CCamera::CCamera()
{
CVector3 vZero = CVector3(0.0, 0.0, 0.0); // Init a vVector to 0 0 0 for our position
CVector3 vView = CVector3(0.0, 1.0, 0.5); // Init a starting view vVector (looking up and out the screen)
CVector3 vUp = CVector3(0.0, 0.0, 1.0); // Init a standard up vVector (Rarely ever changes)
m_vPosition = vZero; // Init the position to zero
m_vView = vView; // Init the view to a std starting view
m_vUpVector = vUp; // Init the UpVector
m_pText = 0;
}
///////////////////////////////// POSITION CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function sets the camera's position and view and up vVector.
/////
///////////////////////////////// POSITION CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::PositionCamera(float positionX, float positionY, float positionZ,
float viewX, float viewY, float viewZ,
float upVectorX, float upVectorY, float upVectorZ)
{
CVector3 vPosition = CVector3(positionX, positionY, positionZ);
CVector3 vView = CVector3(viewX, viewY, viewZ);
CVector3 vUpVector = CVector3(upVectorX, upVectorY, upVectorZ);
// The code above just makes it cleaner to set the variables.
// Otherwise we would have to set each variable x y and z.
m_vPosition = vPosition; // Assign the position
m_vView = vView; // Assign the view
m_vUpVector = vUpVector; // Assign the up vector
//
/* char buf[128];
wsprintf(buf,"Position:%d %d %d LookAt:%d %d %d",int(m_vPosition.x),int(m_vPosition.y),int(m_vPosition.z),int(m_vView.x),int(m_vView.y),int(m_vView.z));
if(m_pText)
m_pText->EditItem(m_nTextID,0.0f,1.0f,0.0f,buf);
*/
}
///////////////////////////////// SET VIEW BY MOUSE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This allows us to look around using the mouse, like in most first person games.
/////
///////////////////////////////// SET VIEW BY MOUSE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::SetViewByMouse()
{
POINT mousePos; // This is a window structure that holds an X and Y
int middleX = SCREEN_WIDTH >> 1; // This is a binary shift to get half the width
int middleY = SCREEN_HEIGHT >> 1; // This is a binary shift to get half the height
float angleY = 0.0f; // This is the direction for looking up or down
float angleZ = 0.0f; // This will be the value we need to rotate around the Y axis (Left and Right)
static float currentRotX = 0.0f;
// Get the mouse's current X,Y position
GetCursorPos(&mousePos);
// If our cursor is still in the middle, we never moved... so don't update the screen
if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;
// Set the mouse position to the middle of our window
SetCursorPos(middleX, middleY);
// Get the direction the mouse moved in, but bring the number down to a reasonable amount
angleY = (float)( (middleX - mousePos.x) ) / 1000.0f;
angleZ = (float)( (middleY - mousePos.y) ) / 1000.0f;
// Here we keep track of the current rotation (for up and down) so that
// we can restrict the camera from doing a full 360 loop.
currentRotX -= angleZ;
// If the current rotation (in radians) is greater than 1.0, we want to cap it.
// if(currentRotX > 1.0f)
// currentRotX = 1.0f;
// Check if the rotation is below -1.0, if so we want to make sure it doesn't continue
// else if(currentRotX -1.0f)
// currentRotX = -1.0f;
// Otherwise, we can rotate the view around our position
// else
{
// To find the axis we need to rotate around for up and down
// movements, we need to get a perpendicular vector from the
// camera's view vector and up vector. This will be the axis.
CVector3 vAxis = CrossProduct(m_vView - m_vPosition, m_vUpVector);
vAxis = Normalize(vAxis);
// Rotate around our perpendicular axis and along the y-axis
RotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);
RotateView(angleY, 0, 1, 0);
}
}
///////////////////////////////// ROTATE VIEW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This rotates the view around the position using an axis-angle rotation
/////
///////////////////////////////// ROTATE VIEW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::RotateView(float angle, float x, float y, float z)
{
CVector3 vNewView;
// Get the view vector (The direction we are facing)
CVector3 vView = m_vView - m_vPosition;
// Calculate the sine and cosine of the angle once
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);
// Find the new x position for the new rotated point
vNewView.x = (cosTheta + (1 - cosTheta) * x * x) * vView.x;
vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta) * vView.y;
vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta) * vView.z;
// Find the new y position for the new rotated point
vNewView.y = ((1 - cosTheta) * x * y + z * sinTheta) * vView.x;
vNewView.y += (cosTheta + (1 - cosTheta) * y * y) * vView.y;
vNewView.y += ((1 - cosTheta) * y * z - x * sinTheta) * vView.z;
// Find the new z position for the new rotated point
vNewView.z = ((1 - cosTheta) * x * z - y * sinTheta) * vView.x;
vNewView.z += ((1 - cosTheta) * y * z + x * sinTheta) * vView.y;
vNewView.z += (cosTheta + (1 - cosTheta) * z * z) * vView.z;
// Now we just add the newly rotated vector to our position to set
// our new rotated view of our camera.
m_vView = m_vPosition + vNewView;
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
///////////////////////////////// STRAFE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This strafes the camera left and right depending on the speed (-/+)
/////
///////////////////////////////// STRAFE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::StrafeCamera(float speed)
{
// Strafing is quite simple if you understand what the cross product is.
// If you have 2 vectors (say the up vVector and the view vVector) you can
// use the cross product formula to get a vVector that is 90 degrees from the 2 vectors.
// For a better explanation on how this works, check out the OpenGL "Normals" tutorial at our site.
// In our new Update() function, we set the strafing vector (m_vStrafe). Due
// to the fact that we need this vector for many things including the strafing
// movement and camera rotation (up and down), we just calculate it once.
//
// Like our MoveCamera() function, we add the strafing vector to our current position
// and view. It's as simple as that. It has already been calculated in Update().
// Add the strafe vector to our position
m_vPosition.x += m_vStrafe.x * speed;
m_vPosition.z += m_vStrafe.z * speed;
// Add the strafe vector to our view
m_vView.x += m_vStrafe.x * speed;
m_vView.z += m_vStrafe.z * speed;
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
///////////////////////////////// MOVE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This will move the camera forward or backward depending on the speed
/////
///////////////////////////////// MOVE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::MoveCamera(float speed)
{
// Get the current view vector (the direction we are looking)
CVector3 vVector = m_vView - m_vPosition;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// I snuck this change in here! We now normalize our view vector when
// moving throughout the world. This is a MUST that needs to be done.
// That way you don't move faster than you strafe, since the strafe vector
// is normalized too.
vVector = Normalize(vVector);
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
m_vPosition.x += vVector.x * speed; // Add our acceleration to our position's X
m_vPosition.z += vVector.z * speed; // Add our acceleration to our position's Z
m_vView.x += vVector.x * speed; // Add our acceleration to our view's X
m_vView.z += vVector.z * speed; // Add our acceleration to our view's Z
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// The next 3 functions were added to our camera class. The less code in
// Main.cpp the better.
//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function handles the input faster than in the WinProc()
/////
//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::CheckForMovement()
{
// Check if we hit the Up arrow or the 'w' key
if(GetKeyState(VK_UP) & 0x80 || GetKeyState('W') & 0x80) {
// Move our camera forward by a positive SPEED
MoveCamera(kSpeed);
}
// Check if we hit the Down arrow or the 's' key
if(GetKeyState(VK_DOWN) & 0x80 || GetKeyState('S') & 0x80) {
// Move our camera backward by a negative SPEED
MoveCamera(-kSpeed);
}
// Check if we hit the Left arrow or the 'a' key
if(GetKeyState(VK_LEFT) & 0x80 || GetKeyState('A') & 0x80) {
// Strafe the camera left
StrafeCamera(-kSpeed);
}
// Check if we hit the Right arrow or the 'd' key
if(GetKeyState(VK_RIGHT) & 0x80 || GetKeyState('D') & 0x80) {
// Strafe the camera right
StrafeCamera(kSpeed);
}
}
///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This updates the camera's view and strafe vector
/////
///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::Update()
{
// Below we calculate the strafe vector every time we update
// the camera. This is because many functions use it so we might
// as well calculate it only once.
/* char buf[128];
wsprintf(buf,"Position:%d %d %d LookAt:%d %d %d",int(m_vPosition.x),int(m_vPosition.y),int(m_vPosition.z),int(m_vView.x),int(m_vView.y),int(m_vView.z));
if(m_pText)
m_pText->EditItem(m_nTextID,0.0f,1.0f,0.0f,buf);
*/
// Initialize a variable for the cross product result
CVector3 vCross = CrossProduct(m_vView - m_vPosition, m_vUpVector);
// Normalize the strafe vector
m_vStrafe = Normalize(vCross);
// Move the camera's view by the mouse
SetViewByMouse();
// This checks to see if the keyboard was pressed
CheckForMovement();
}
///////////////////////////////// LOOK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This updates the camera according to the
/////
///////////////////////////////// LOOK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CCamera::Look()
{
// Give openGL our camera position, then camera view, then camera up vector
gluLookAt(m_vPosition.x, m_vPosition.y, m_vPosition.z,
m_vView.x, m_vView.y, m_vView.z,
m_vUpVector.x, m_vUpVector.y, m_vUpVector.z);
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////////////////////////////////////////////////////
//
// * QUICK NOTES *
//
// This file should now be close to complete. Now that we have all the basic camera
// functionality you can use this file in your projects and not have to cut and paste code.
// I created a camera.h for this purpose as well.
//
// We added strafing to this file. 3 other functions were added to our camera class
// so that it would clean up main.cpp a bit more. In Camera.h you will find some
// new changes as well, which include the move of our camera data to private. We now
// will use data access functions to get camera data.
//
// Here are the notes from main.cpp about the concept and application:
//
// This tutorial was taken from the Camera4 tutorial. This is our last camera tutorial.
// We might create a bezier curve camera tutorial though in the future. This tutorial
// shows how to strafe the camera right or left. This might seem easy when you think to
// attempt it, but if you don't know your linear algebra it can be tricky. Strafing the
// camera is moving the camera 90 degrees left or right from the current view. In other
// words, it's as if you were side stepping while you look forward. This is used in most
// first person shooters games, and comes in handy when peering around corners or running
// past a hallway while firing off some rounds. It's also a great way to move diagonal
// while doing a shootout at close ranges to avoid being hit.
//
// Since we understand what strafing is and what it does, let me actually explain how it works.
// We know that we want to walk in the direction that is 90 degrees from the view vVector (with
// the view vVector being m_vView - m_vPosition). So how do we then get the vVector that is 90
// degrees from our view vVector? If you know what the cross product is, you can easily see how
// this would be done. The cross product is a mathematical formula that takes 2 vectors and
// returns the vVector 90 degrees from those 2 vectors. This is how you find the normal to a plane.
// Well, we have a view vVector, but what would the other vVector be? Does the up vVector come to mind?
// That's it! We want to take the cross product between the up vVector and the view vVector. This
// will return the vVector (or direction) that we want to strafe in. In games like Descent, the
// up vVector will change because you can go upside down and spin in crazy directions. The cross
// product will ensure that we will always strafe correctly no matter what orientation the camera is in.
// Once we have the strafe vVector, we need to add it to the position and view points.
// Here are the controls for this tutorial:
//
// w, s, UP_ARROW, DOWN_ARROW - Move the camera forward and backward
// a, d, RIGHT_ARROW, LEFT_ARROW - Strafe the camera left and right
// Mouse movement - Moves the view for first person mode
// ESCAPE - Quits the program
//
// You may notice that we don't use all the methods in the camera class. I decided to leave
// them in there because since this is the last camera tutorial it would be nice to just use
// this in your projects without having to cut and paste code. That's also why I finally
// added a camera.h so you don't have the camera class in main.h.
//
// Enjoy!
//
//
// Ben Humphrey (DigiBen)
// Game Programmer
// DigiBen@GameTutorials.com
// Co-Web Host of www.GameTutorials.com
//
//