Jump to content
  • Advertisement
Sign in to follow this  
Quinnie

Third person camera

This topic is 3671 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey, I'm aware that there are loads of resources on the internet covering this topic but I'm still unable to get my code working correctly. The aspect I'm having the most trouble with is rotation. The camera should be able to rotate around the character on the horizontal plane (around the y-axis) and it should rotate around the vertical plane. At the moment the camera works fine for rotating around the character on the horizontal plane. However when rotating on the vertical plane all kind of strang e things happen. I'll include the source code for the rotating below.
// m_origin is the position the camera is at
// m_lookat is the position the camera is looking at

void Camera::RotateCamera(float deltaX, float deltaY)
{
	D3DXQUATERNION quaternionX;
	D3DXQUATERNION quaternionY;

	D3DXQuaternionRotationAxis(&quaternionX, &D3DXVECTOR3(1, 0, 0), deltaX);
	D3DXQuaternionRotationAxis(&quaternionY, &D3DXVECTOR3(0, 1, 0), deltaY);

	D3DXQUATERNION qRotation = D3DXQUATERNION(0,0,0,1);
	D3DXQUATERNION qRotationConjugate;

	D3DXQuaternionMultiply(&qRotation, &qRotation, &quaternionX);
	D3DXQuaternionMultiply(&qRotation, &quaternionY, &qRotation);

	D3DXQuaternionNormalize(&qRotation, &qRotation);
	D3DXQuaternionConjugate(&qRotationConjugate, &qRotation);

	D3DXQUATERNION qLookAtToOrigin = m_origin - m_lookat;
	qLookAtToOrigin.w = 0;

	D3DXQuaternionMultiply(&qLookAtToOrigin, &qLookAtToOrigin, &qRotationConjugate);
	D3DXQuaternionMultiply(&qLookAtToOrigin, &qRotation, &qLookAtToOrigin);

	m_origin = qLookAtToOrigin + m_lookat;

	return;
}

Any help is greatly appreciated, thanks in advance, quinnie

Share this post


Link to post
Share on other sites
Advertisement
Do a search for "orbit camera" in this forum and you will find info and multiple pieces of source code. 3rd Person cameras require 1) orbit calculation with fixes for induced roll, camera flipping upside down etc. 2) clipping, often just sweeping a box or volume shape back away from the character through the world to prevent looking through walls or obstructions 3) high level logic to change view or help player in an intuitive manner to minimize the need for manual camera control.

Share this post


Link to post
Share on other sites
Hey,

Thanks for your response, searching for "orbit camera" instead of "third person camera" returned some more helpfull results. The rotations are working perfectly now but I have a new problem.

When manually moving the eye or the target I have to recalculate the heading and pitch. I have this problem in the functions MoveEye(), MoveTarget(), SetEyePosition() and SetTargetPosition(). How can I calculate the heading and pitch if I know the position of the target and the eye?

I've included the entire source of the camera and it is free to use for anyone interested.

Camera.h

// Third person orbit camera based on quaternions

#pragma once

class Camera
{
public:
// Constructor
Camera();

public:
// Interface methods
void MoveCamera(D3DXVECTOR3 direction, float distance);
void MoveCamera(D3DXVECTOR3 distance);
void MoveEye(D3DXVECTOR3 direction, float distance);
void MoveEye(D3DXVECTOR3 distance);
void MoveTarget(D3DXVECTOR3 direction, float distance);
void MoveTarget(D3DXVECTOR3 distance);
void RotateCamera(float headingRelative, float pitchRelative);
void Zoom(float distance);

public:
// Set methods
void SetMaximumVerticalAngles(float maxRadians, float minRadians);
void SetMaximumZoomDistances(float minZoom, float maxZoom);
void SetFieldOfView(float fovRadians);
void SetAspectRatio(float ratio);
void SetAspectRatio(float width, float height);
void SetClippingDistances(float nearDistance, float farDistance);

void SetRotation(float heading, float pitch);
void SetCameraPosition(D3DXVECTOR3 position);
void SetEyePosition(D3DXVECTOR3 position);
void SetTargetPosition(D3DXVECTOR3 position);

public:
// Get methods
D3DXVECTOR3 GetEyePosition();
D3DXVECTOR3 GetTargetPosition();
D3DXMATRIX GetViewMatrix();
D3DXMATRIX GetProjectionMatrix();
D3DXMATRIX GetViewProjectionMatrix();

private:
// Eye and target position
D3DXVECTOR3 m_eye;
D3DXVECTOR3 m_target;

// Orientation represented by heading and pitch
float m_heading;
float m_pitch;

// Extreme vertical rotation angles in radians
float m_maxVerticalAngle;
float m_minVerticalAngle;

// Extreme zoom distances
float m_maxZoom;
float m_minZoom;

// Field of view in radians
float m_FieldOfView;

// Ratio between the view space width and height
float m_aspectRatio;

// Clipping distances
float m_minDistance;
float m_maxDistance;
};



Camera.cpp

// Camera class header
#include "Camera.h"

// Initializes the camera with default values
#pragma region Camera()
Camera::Camera()
{
// Set default values
SetEyePosition(D3DXVECTOR3(0,0,0));
SetTargetPosition(D3DXVECTOR3(0,0,0));

SetMaximumVerticalAngles(D3DX_PI / 2.0f - 0.5f, -D3DX_PI / 2.0f + 0.5f);
SetMaximumZoomDistances(1.0f, 100.0f);

SetFieldOfView(D3DX_PI / 4.0f);
SetAspectRatio(16.0f / 10.0f);
SetClippingDistances(1.0f, 1000.0f);

SetRotation(0.0f, 0.0f);

return;
}
#pragma endregion

// Sets the maximum/minimum vertical angles
#pragma region SetMaximumVerticalAngles(float maxRadians, float minRadians)
void Camera::SetMaximumVerticalAngles(float maxRadians, float minRadians)
{
// Wrap the angles into the [0, 2 pi] interval
maxRadians = fmod(maxRadians, 2 * D3DX_PI);
minRadians = fmod(minRadians, 2 * D3DX_PI);

// Save the new angles
m_maxVerticalAngle = maxRadians;
m_minVerticalAngle = minRadians;

// Check if current angles exceed the new max angles
m_pitch = __max(m_pitch, m_minVerticalAngle);
m_pitch = __min(m_pitch, m_maxVerticalAngle);

return;
}
#pragma endregion

// Sets the maximum/minimum zooming distances
#pragma region SetMaximumZoomDistances(float minZoom, float maxZoom)
void Camera::SetMaximumZoomDistances(float minZoom, float maxZoom)
{
// Clamp the min zoom distance to the [0, ->] interval
if (minZoom < 0) minZoom = 0.00f;

// Max zoom distance must be greater then the min zoom distance
if (maxZoom <= minZoom) maxZoom = minZoom * 10.0f;

// Save the new zoom distances
m_minZoom = minZoom;
m_maxZoom = maxZoom;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

return;
}
#pragma endregion

// Set the field of view
#pragma region SetFieldOfView(float fovRadians)
void Camera::SetFieldOfView(float fovRadians)
{
// Wrap the field of view into the [0, 2 pi] interval
fovRadians = fmod(fovRadians, 2 * D3DX_PI);

// Save the new field of view
m_FieldOfView = fovRadians;

return;
}
#pragma endregion

// Set the aspect ratio
#pragma region SetAspectRatio(float ratio)
void Camera::SetAspectRatio(float ratio)
{
// Clamp the aspect ratio the [0, ->] interval
if (ratio < 0) ratio = 0;

// Save the new aspect ratio
m_aspectRatio = ratio;

return;
}
#pragma endregion

#pragma region SetAspectRatio(float width, float height)
void Camera::SetAspectRatio(float width, float height)
{
// Call overloaded function
SetAspectRatio(width / height);

return;
}
#pragma endregion

// Set the clipping distances
#pragma region SetClippingDistances(float nearDistance, float farDistance)
void Camera::SetClippingDistances(float nearDistance, float farDistance)
{
// Clamp the near distance to the [0.01, ->] interval
if (nearDistance <= 0) nearDistance = 0.01f;

// Far clipping distance must be greater then the near clipping distance
if (farDistance <= nearDistance) farDistance = nearDistance * 10000.0f;

// Save the new clipping distances
m_minDistance = nearDistance;
m_maxDistance = farDistance;

return;
}
#pragma endregion

// Sets the camera rotation
#pragma region SetRotation(float heading, float pitch)
void Camera::SetRotation(float heading, float pitch)
{
// Set the heading and pitch
m_heading = heading;
m_pitch = pitch;

// Check if the pitch doesn't exceed the set boundaries
m_pitch = __max(m_pitch, m_minVerticalAngle);
m_pitch = __min(m_pitch, m_maxVerticalAngle);

return;
}
#pragma endregion

// Sets the camera position
#pragma region SetCameraPosition(D3DXVECTOR3 position)
void Camera::SetCameraPosition(D3DXVECTOR3 position)
{
// Calculate a vector from the eye to the target
D3DXVECTOR3 vEyeToTarget = m_target - m_eye;

// Set the new eye position
m_eye = position;

// Restore the lookat position relative to the origin
m_target = m_eye + vEyeToTarget;

return;
}
#pragma endregion

// Sets the eye position
#pragma region SetEyePosition(D3DXVECTOR3 position)
void Camera::SetEyePosition(D3DXVECTOR3 position)
{
// Set the new eye position
m_eye = position;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

// Todo: recalculate the heading and pitch

return;
}
#pragma endregion

// Sets the target position
#pragma region SetTargetPosition(D3DXVECTOR3 position)
void Camera::SetTargetPosition(D3DXVECTOR3 position)
{
// Set the new target position
m_target = position;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

return;
}
#pragma endregion

// Get the eye position
#pragma region GetEyePosition()
D3DXVECTOR3 Camera::GetEyePosition()
{
return m_eye;
}
#pragma endregion

// Gets the target position
#pragma region GetTargetPosition()
D3DXVECTOR3 Camera::GetTargetPosition()
{
return m_target;
}
#pragma endregion

// Creates and returns the view matrix
#pragma region GetViewMatrix()
D3DXMATRIX Camera::GetViewMatrix()
{
D3DXMATRIX viewMatrix;

// Create an orientation quaternion
D3DXQUATERNION qOrientation, qRotation;

D3DXQuaternionIdentity(&qOrientation);
D3DXQuaternionIdentity(&qRotation);

if (m_heading != 0.0f)
{
D3DXQuaternionRotationAxis(&qRotation, &D3DXVECTOR3(0, 1, 0), m_heading);
D3DXQuaternionMultiply(&qOrientation, &qRotation, &qOrientation);
}

if (m_pitch != 0.0f)
{
D3DXQuaternionRotationAxis(&qRotation, &D3DXVECTOR3(1, 0, 0), m_pitch);
D3DXQuaternionMultiply(&qOrientation, &qOrientation, &qRotation);
}

// Normalize the orientation quaternion
D3DXQuaternionNormalize(&qOrientation, &qOrientation);

// Compose a rotation matrix from the orientation quaternion
D3DXMatrixRotationQuaternion(&viewMatrix, &qOrientation);

// Compose the orientations of the axes
D3DXVECTOR3 xAxis = D3DXVECTOR3(viewMatrix(0,0), viewMatrix(1,0), viewMatrix(2,0));
D3DXVECTOR3 yAxis = D3DXVECTOR3(viewMatrix(0,1), viewMatrix(1,1), viewMatrix(2,1));
D3DXVECTOR3 zAxis = D3DXVECTOR3(viewMatrix(0,2), viewMatrix(1,2), viewMatrix(2,2));

// Calculate a vector from the eye to the target
D3DXVECTOR3 vEyeToTarget = m_target - m_eye;

// Calculate the zoom distance
float zoomDistance = D3DXVec3Length(&vEyeToTarget);

// Update the eye position
m_eye = m_target + zAxis * -zoomDistance;

// Adjust the view matrix
viewMatrix(3,0) = -D3DXVec3Dot(&xAxis, &m_eye);
viewMatrix(3,1) = -D3DXVec3Dot(&yAxis, &m_eye);
viewMatrix(3,2) = -D3DXVec3Dot(&zAxis, &m_eye);

return viewMatrix;
}
#pragma endregion

// Creates and returns the projection matrix
#pragma region GetProjectionMatrix()
D3DXMATRIX Camera::GetProjectionMatrix()
{
D3DXMATRIX projectionMatrix;

// Compose a projection matrix
D3DXMatrixPerspectiveFovLH(&projectionMatrix, m_FieldOfView, m_aspectRatio, m_minDistance, m_maxDistance);

return projectionMatrix;
}
#pragma endregion

// Creates and returns the combined view and projection matrix
#pragma region GetViewProjectionMatrix()
D3DXMATRIX Camera::GetViewProjectionMatrix()
{
D3DXMATRIX viewprojectionMatrix;

// Get the view matrix
D3DXMatrixMultiply(&viewprojectionMatrix, &GetViewMatrix(), &GetProjectionMatrix());

return viewprojectionMatrix;
}
#pragma endregion

// Moves the camera
#pragma region MoveCamera(D3DXVECTOR3 direction, float distance)
void Camera::MoveCamera(D3DXVECTOR3 direction, float distance)
{
// Normalize the direction
D3DXVec3Normalize(&direction, &direction);

// Update the eye position
m_eye += direction * distance;

// Update the target position
m_target += direction * distance;

return;
}
#pragma endregion

#pragma region MoveCamera(D3DXVECTOR3 distance)
void Camera::MoveCamera(D3DXVECTOR3 distance)
{
// Update the eye position
m_eye += distance;

// Update the target position
m_target += distance;

return;
}
#pragma endregion

// Moves the eye
#pragma region MoveEye(D3DXVECTOR3 direction, float distance)
void Camera::MoveEye(D3DXVECTOR3 direction, float distance)
{
// Normalize the direction
D3DXVec3Normalize(&direction, &direction);

// Update the eye position
m_eye += direction * distance;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

// Todo: recalculate the heading and pitch

return;
}
#pragma endregion

#pragma region MoveEye(D3DXVECTOR3 distance)
void Camera::MoveEye(D3DXVECTOR3 distance)
{
// Update the eye position
m_eye += distance;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

// Todo: recalculate the heading and pitch

return;
}
#pragma endregion

// Moves the target
#pragma region MoveTarget(D3DXVECTOR3 direction, float distance)
void Camera::MoveTarget(D3DXVECTOR3 direction, float distance)
{
// Normalize the direction
D3DXVec3Normalize(&direction, &direction);

// Update the target position
m_target += direction * distance;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

// Todo: recalculate the heading and pitch

return;
}
#pragma endregion

#pragma region MoveTarget(D3DXVECTOR3 distance)
void Camera::MoveTarget(D3DXVECTOR3 distance)
{
// Update the target position
m_target += distance;

// Check if current zoom distance exceeds the new zoom distances by doing a dummy call to zoom
Zoom(0.0f);

// Todo: recalculate the heading and pitch

return;
}
#pragma endregion

// Rotates the camera
#pragma region RotateCamera(float headingRelative, float pitchRelative)
void Camera::RotateCamera(float headingRelative, float pitchRelative)
{
// Increase the heading and pitch
m_heading += headingRelative;
m_pitch += pitchRelative;

// Check if the pitch doesn't exceed the set boundaries
m_pitch = __max(m_pitch, m_minVerticalAngle);
m_pitch = __min(m_pitch, m_maxVerticalAngle);

return;
}
#pragma endregion

// Moves the viewing position in the direction of the lookat position
#pragma region Zoom(float distance)
void Camera::Zoom(float distance)
{
// Calculate a vector from the eye to the target
D3DXVECTOR3 vEyeToTarget = m_target - m_eye;

// Calculate the length of the vector
float currentZoom = D3DXVec3Length(&vEyeToTarget);

// Adjust the zoom level and make sure it doens't cross the boundaries
currentZoom += distance;

currentZoom = __min(currentZoom, m_maxZoom);
currentZoom = __max(currentZoom, m_minZoom);

// Normalize the vector
D3DXVec3Normalize(&vEyeToTarget, &vEyeToTarget);

// Scale the vector to the new zoom level
D3DXVec3Scale(&vEyeToTarget, &vEyeToTarget, currentZoom);

// Calculate the new eye position
m_eye = m_target + vEyeToTarget;

return;
}
#pragma endregion

Share this post


Link to post
Share on other sites
Since no one has responded yet I'll try to clarify the problem and post some code I tried but which (obviously) didn't work.

When manually repositioning the target or the eye, the heading and pitch will have to be updated as well. I tried calculating the heading and pitch by calculating the angle between the eye-to-target vector and the normal vector looking down the z-axis.

I'll post the code that I came up with below. I have no idea whether the problem is a mathematical error, logical error or programming bug.


// Calculate a vector from the eye to the target
D3DXVECTOR3 vEyeToTarget = m_target - m_eye;

// Create a reference vector
D3DXVECTOR3 vReference = D3DXVECTOR3(0,0,-1);

// Project the vector's y-axis and normalize it
vEyeToTarget.y = 0;
D3DXVec3Normalize(&vEyeToTarget, &vEyeToTarget);

// Calculate the angle between EyeToTarget and Reference
m_heading = acos(D3DXVec3Dot(&vReference, &vEyeToTarget));

// Calculate a vector from the eye to the target
vEyeToTarget = m_target - m_eye;

// Project the vector's x and z-axis and normalize it
vEyeToTarget.x = 0;
vEyeToTarget.z = 0;
D3DXVec3Normalize(&vEyeToTarget, &vEyeToTarget);

// Calculate the angle between EyeToTarget and Reference
m_pitch = acos(D3DXVec3Dot(&vReference, &vEyeToTarget));




Any help is greatly appreciated,

Quinnie

Share this post


Link to post
Share on other sites
Hi, i have a similar trouble, i solve it first on papper,

the 3rd person camera its a camera that rotate at the
same time as the "person" and have a distance between both,
if you look the position of the camera in the X Z plane you
should imagine a circle, then, the distance between the camera
and the "person" its the radious of that circle.

For the position you can try with the parametric form:

g_vEYE.x = object.Pos.x + RADIUS*sin(-1*object.spin/5.729577);

g_vEYE.z = object.Pos.z + RADIUS*cos(-1*object.spin/5.729577);

Here:

g_vEYE its the vector camera pEYE
object the structure or class of the "person" with position and spin
RADIUS a constant distance between both
the -1 and /5.72 its for have the proper direction.

The Spin its that, the spin of your object in Y axis,
i hope can help

Share this post


Link to post
Share on other sites
Thanks for your response,

I'm not sure if we actually have the same problem, it seems like you are trying to position the eye based on the target(object) position and a rotation(spin). I'm trying to calculate the rotation from a given eye and target position.

If I misinterpreted your post please let me know.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!