I have a Camera
attached to a SceneNode
and movement works fine as long as the SceneNode
\'s rotati
This is not meant to be a direct answer but as reference upon the request of the OP.
OpenGL v1.0 using old API calls: Implementation of a Camera Class Object while using it in a Scene Class outside of the Scene Class's Scene Graph. This is written in C++
Camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include "Core.h"
class Camera {
private:
Vector3 _v3EyePosition;
Vector3 _v3LookCenter;
Vector3 _v3Up;
public:
Camera();
~Camera();
void Get3rdPersonLocation( Vector3 &v3Position, float &fAngle );
void Set( Vector3 v3EyePosition, Vector3 v3LookCenter, Vector3 v3Up = Vector3( 0.0f, 1.0f, 0.0f ) );
void Render();
};
#endif
Camera.cpp
#include "stdafx.h"
#include "Camera.h"
Camera::Camera() {
_v3EyePosition = Vector3( 0.0f, 0.0f, 0.0f );
_v3LookCenter = Vector3( 0.0f, 0.0f, -1.0f );
_v3Up = Vector3( 0.0f, 1.0f, 0.0f );
}
Camera::~Camera() {
}
void Camera::Get3rdPersonLocation( Vector3 &v3Position, float &fAngle ) {
v3Position._fX = _v3LookCenter._fX;
v3Position._fY = _v3EyePosition._fY;
v3Position._fZ = _v3LookCenter._fZ;
// Find Angle
float fX = _v3LookCenter._fX - _v3EyePosition._fX;
float fZ = _v3LookCenter._fZ - _v3EyePosition._fZ;
// Angle In Degrees
fAngle = Math::Radian2Degree( atan2( fX, fZ ) );
}
void Camera::Set( Vector3 v3EyePosition, Vector3 v3LookCenter, Vector3 v3Up ) {
_v3EyePosition = v3EyePosition;
_v3LookCenter = v3LookCenter;
_v3Up = v3Up;
}
void Camera::Render() {
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( _v3EyePosition._fX, _v3EyePosition._fY, _v3EyePosition._fZ,
_v3LookCenter._fX, _v3LookCenter._fY, _v3LookCenter._fZ,
_v3Up._fX, _v3Up._fY, _v3Up._fZ );
}
In the Camera
's Render
function using the old OpenGL API calls we first load in the Modelview matrix, then we load the identity matrix; then we finally use glu's gluLookAt(...) method to set the positions of the needed vectors.
Scene.h - Has many members and functions; but as in regards with the Camera
Object it has a Camera as a member and not a pointer to a Camera.
Scene.cpp - Render()
void Scene::Render() {
// Update Camera
_Camera.Set( _Player.GetPosition(), _Player.GetLookCenter() );
// Position Camera
_Camera.Render();
if ( UserSettings::Get()->_bQuit ) {
return;
}
if ( _vpNodes.size() < 1 ) {
// No SceneGraph To Render
return;
}
EnableLights();
// Send Items To Be Rendered
// Clear 2nd Render Pass Container
DeleteAllAlphaObjects();
// Render All Opaque Objects (1st Pass) & Store 2nd Pass Objects
_vpNodes[0]->RenderOGL( false, true );
// Render All Objects With Alpha Values (2nd Pass)
glEnable( GL_BLEND );
glMatrixMode( GL_MODELVIEW );
for ( std::vector<AlphaObject*>::iterator it = _vpAlphaObjects.begin(); it != _vpAlphaObjects.end(); ++it ) {
// Set Model View Matrix
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadMatrixf( &(*it)->f16Matrix[0] );
(*it)->pShape->RenderOGL( true, false );
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
}
// Show Selected Weapon
_Player.RenderWeapon();
glDisable( GL_BLEND );
DisableLights();
return;
}
Here the Camera
is independent of the Player
class as well as the Scene's Scene Graph Hierarchy and we use the Camera
in the Scene's Render
Call. Here we set the Camera
by getting the Player
's current Position, and the Player's
LookCenter
direction.
EDIT - Adding Player Class And Related Code For Movement Calculations
enum Action {
NO_ACTION = -1,
MOVING_FORWARD = 0,
MOVING_BACK,
MOVING_LEFT,
MOVING_RIGHT,
LOOKING_LEFT,
LOOKING_RIGHT,
LOOKING_UP,
LOOKING_DOWN,
}; // Action
Player.h
#ifndef PLAYER_H
#define PLAYER_H
#include "Core.h"
class Weapon;
class NodeTransform;
class Player {
private:
enum MouseLook {
ML_NORMAL = 1,
ML_INVERT = -1,
} _MouseLookState; // MouseLook
Vector3 _v3Position;
Vector3 _v3LookCenter;
float _fLookDistance;
float _fMaxUp;
float _fMaxDown;
float _fLinearSpeed;
float _fAngularSpeed;
public:
Player( float fLookDistance );
~Player();
void SetSpeed( float fLinear, float fAngular );
void SetMouseY( bool bInvert );
void SetLocation( Vector3 v3Position, Vector3 v3Direction = Vector3( 0.0f, 0.0f, -1.0f ) );
void Move( Action action, float fDeltaTime );
bool Update();
inline void SetPosition( Vector3 v3Position );
inline Vector3 GetPosition();
inline Vector3 GetLookCenter();
inline Vector3 GetLookDirection();
};
inline void Player::SetPosition( Vector3 v3Position ) {
Vector3 v3LookDirection;
v3LookDirection = _v3LookCenter - _v3Position;
_v3Position = v3Position;
_v3LookCenter = v3Position + v3LookDirection;
}
inline Vector3 Player::GetPosition() {
return _v3Position;
}
inline Vector3 Player::GetLookCenter() {
return _v3LookCenter;
}
inline Vector3 Player::GetLookDirection() {
Vector3 v3LookDirection;
v3LookDirection = _v3LookCenter - _v3Position;
v3LookDirection.Normalize();
return v3LookDirection;
}
#endif
Player.cpp
#include "stdafx.h"
#include "Player.h"
#include "UserSettings.h"
#include "NodeTransform.h"
Player::Player( float fLookDistance ) {
_fLookDistance = fLookDistance;
// Calculate Maximum Limits For Looking Up And Down
_fMaxUp = _fLookDistance * tan( Math::Degree2Radian( 50 ) );
_fMaxDown = _fLookDistance * tan( Math::Degree2Radian( 40 ) );
_v3Position = Vector3( 0.0f, 0.5f, 0.0f );
_v3LookCenter = Vector3( 0.0f, 0.5f, -fLookDistance );
_fLinearSpeed = 15.0f; // Units Per Second
_fAngularSpeed = 3.0f; // Radians Per Second
SetMouseY( UserSettings::Get()->GetMouseInvert() );
}
Player::~Player() {
} // ~Player
void Player::SetMouseY( bool bInvert ) {
if ( bInvert ) {
_MouseLookState = ML_INVERT;
} else {
_MouseLookState = ML_NORMAL;
}
}
void Player::SetLocation( Vector3 v3Position, Vector3 v3Direction ) {
_v3Position = v3Position;
_v3LookCenter = v3Position + _fLookDistance*v3Direction;
}
void Player::Move( Action action, float fDeltaTime ) {
Vector3 v3LookDirection;
v3LookDirection = _v3LookCenter - _v3Position;
switch ( action ) {
case MOVING_FORWARD: {
// Prevent Vertical Motion
v3LookDirection._fY = 0.0f;
_v3Position += v3LookDirection*fDeltaTime*_fLinearSpeed;
_v3LookCenter += v3LookDirection*fDeltaTime*_fLinearSpeed;
break;
}
case MOVING_BACK: {
// Prevent Vertical Motion
v3LookDirection._fY = 0.0f;
_v3Position -= v3LookDirection*fDeltaTime*_fLinearSpeed;
_v3LookCenter -= v3LookDirection*fDeltaTime*_fLinearSpeed;
break;
}
case MOVING_LEFT: {
// Get "Side" Direction & Prevent Vertical Motion
v3LookDirection._fY = v3LookDirection._fX;
v3LookDirection._fX = -v3LookDirection._fZ;
v3LookDirection._fZ = v3LookDirection._fY;
v3LookDirection._fY = 0.0f;
_v3Position -= v3LookDirection*fDeltaTime*_fLinearSpeed;
_v3LookCenter -= v3LookDirection*fDeltaTime*_fLinearSpeed;
break;
}
case MOVING_RIGHT: {
// Get "Side" Direction & Prevent Vertical Motion
v3LookDirection._fY = v3LookDirection._fX;
v3LookDirection._fX = -v3LookDirection._fZ;
v3LookDirection._fZ = v3LookDirection._fY;
v3LookDirection._fY = 0.0f;
_v3Position += v3LookDirection*fDeltaTime*_fLinearSpeed;
_v3LookCenter += v3LookDirection*fDeltaTime*_fLinearSpeed;
break;
}
case LOOKING_LEFT: {
/*float fSin = -sin( fDeltaTime*_fAngularSpeed );
float fCos = cos( fDeltaTime*_fAngularSpeed );
_v3LookCenter._fX = _v3Position._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
_v3LookCenter._fZ = _v3Position._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
break;*/
// Third Person
float fSin = sin( fDeltaTime*_fAngularSpeed );
float fCos = -cos( fDeltaTime*_fAngularSpeed );
_v3Position._fX = _v3LookCenter._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
_v3Position._fZ = _v3LookCenter._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
break;
}
case LOOKING_RIGHT: {
/*float fSin = sin( fDeltaTime*_fAngularSpeed );
float fCos = cos( fDeltaTime*_fAngularSpeed );
_v3LookCenter._fX = _v3Position._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
_v3LookCenter._fZ = _v3Position._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
break;*/
// Third Person
float fSin = -sin( fDeltaTime*_fAngularSpeed );
float fCos = -cos( fDeltaTime*_fAngularSpeed );
_v3Position._fX = _v3LookCenter._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
_v3Position._fZ = _v3LookCenter._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
break;
}
case LOOKING_UP: {
_v3LookCenter._fY -= fDeltaTime*_fAngularSpeed*_MouseLookState;
// Check Maximum Values
if ( _v3LookCenter._fY > (_v3Position._fY + _fMaxUp ) ) {
_v3LookCenter._fY = _v3Position._fY + _fMaxUp;
} else if ( _v3LookCenter._fY < (_v3Position._fY - _fMaxDown) ) {
_v3LookCenter._fY = _v3Position._fY - _fMaxDown;
}
break;
}
}
}
bool Player::Update() {
// Stripped Down This Deals With Player's Weapons
}
void Player::SetSpeed( float fLinear, float fAngular ) {
_fLinearSpeed = fLinear;
_fAngularSpeed = fAngular;
}
Scene.h - Same here as for the Camera; there is a Player Object and not a pointer to a player object. However there is a pointer to a playerTransform which is a NodeTransform. There are too many functions to list here because of the interaction of the Player with the Scene since this is a working 3D Game. I can provide a few functions that may be of interest.
Scene.cpp Scene::Update()
// -----------------------------------------------------------------------
// Update
// Animate Objects, Pickup Checks Etc. This All Happens At The
// Physics Refresh Rate
void Scene::Update() {
UserSettings* pUserSettings = UserSettings::Get();
AudioManager* pAudio = AudioManager::GetAudio();
bool bPlayerMoving = false;
// Movement
if ( pUserSettings->IsAction( MOVING_FORWARD ) ) {
_Player.Move( MOVING_FORWARD, GameOGL::GetPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->IsAction( MOVING_BACK ) ) {
_Player.Move( MOVING_BACK, GameOGL::GetPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->IsAction( MOVING_LEFT ) ) {
_Player.Move( MOVING_LEFT, GameOGL::GetPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( pUserSettings->IsAction( MOVING_RIGHT ) ) {
_Player.Move( MOVING_RIGHT, GameOGL::GetPhysicsTimeStep() );
bPlayerMoving = true;
}
if ( bPlayerMoving && !_bPlayerWalking ) {
pAudio->SetLooping( AUDIO_FOOTSTEPS, true );
pAudio->Play( AUDIO_FOOTSTEPS );
_bPlayerWalking = true;
}
else if ( !bPlayerMoving && _bPlayerWalking ) {
pAudio->Stop( AUDIO_FOOTSTEPS );
_bPlayerWalking = false;
}
// ... Other Code Here
}
EDIT - Adding NodeTransform::Render() - Show the order of operations for MVP
// Move Model View Matrix M = (T C R S C^)
void NodeTransform::RenderOGL( bool bSecondPass, bool bRenderNext ) {
if ( _pIn && _bVisible ) {
// Put Matrix Onto Stack For Later Retrieval
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
if ( _bHaveMatrix ) {
// Use Transformation Matrix
glMultMatrixf( &_f16Matrix[0] );
}
// Transalate
glTranslatef( _v3Translate._fX, _v3Translate._fY, _v3Translate._fZ );
// Move Back To Center
glTranslatef( _v3Center._fX, _v3Center._fY, _v3Center._fZ );
// Rotate
glRotatef( _fRotateAngle, _v3RotateAxis._fX, _v3RotateAxis._fY, _v3RotateAxis._fZ );
// Scale
glScalef( _v3Scale._fX, _v3Scale._fY, _v3Scale._fZ );
// Offset By -ve Center Value
glTranslatef( -_v3Center._fX, -_v3Center._fY, -_v3Center._fZ );
// Move Down The Tree
_pIn->RenderOGL( bSecondPass, true );
// Get Old Matrix
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
}
if ( _pNext && bRenderNext ) {
_pNext->RenderOGL( bSecondPass, true );
}
} // RenderOGL