Jump to content
  • Advertisement
Sign in to follow this  
Pirosan

Camera question (Space Sim)

This topic is 4862 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

Hello, I am working on a space simulator where the player can be in any orientation with the space ship he/she is piloting. there is no up in this game, which makes it rather hard for me to program in the rotations. I am pretty sure quaternions come in there somewhere, but i am not quite sure how to approach this. the player can rotate on any axis, he or she can change the roll tilt and yaw of the ship at will. i will also need to be able to orient the camera in a similar way (1st person, the camera will be spinning around with the ship. i would eventually like to do 3rd person but i will stick with the simple answer for now) Anyways, i just need some hints or clues as to what i should do to appropriatly handle this. any comments welcome, i need all the help i can get. thank you for your time Chris EDIT: Wrong term... at first i said quadratics when i meant to say Quaternions... silly mistake [Edited by - Pirosan on July 25, 2005 1:33:01 PM]

Share this post


Link to post
Share on other sites
Advertisement
I'm sure someone around here has already got to have a camera class you can use. But there are some tuts out there that can help.

Nehe - http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=Quaternion_Camera_Class
Looks good but I think there is no strafe (yaw).

Share this post


Link to post
Share on other sites
Quote:
I am working on a space simulator where the player can be in any orientation with the space ship he/she is piloting. there is no up in this game, which makes it rather hard for me to program in the rotations.

I am pretty sure quaternions come in there somewhere, but i am not quite sure how to approach this. the player can rotate on any axis, he or she can change the roll tilt and yaw of the ship at will. i will also need to be able to orient the camera in a similar way (1st person, the camera will be spinning around with the ship. i would eventually like to do 3rd person but i will stick with the simple answer for now)

Anyways, i just need some hints or clues as to what i should do to appropriatly handle this. any comments welcome, i need all the help i can get.
Here's something that you may find useful. I hope to turn this into a tutorial at some point and make it available online, but I'll just post it for now. It's an OpenGL 'object' class which should do exactly what you need. Feel free to use it as-is, or derive your own code from it. The comments in the files should explain everything, but I'll be glad to answer any questions as well.

(Make sure to read the disclaimer in the comments. I just wrote this tonight, and although it's been tested, it hasn't been tested thoroughly....)


// --------------------------------------------------------------------------------------
// FILE: jykOpenGLObject.h
//
// This is a class, intended to be both functional and a tutorial of sorts, for
// representing objects (such as the camera) in OpenGL. The class is intended to be
// a plug-and-play solution. I've kept the code as simple as possible, and the only
// dependency is jyk::Vector3<>, which you can replace with any vector class which
// supports the usual operations. (You may have to replace the function name
// NormalizeSelf() with Normalize(), which seems to be what most people use.)
//
// Three types of movement commonly encountered in games are supported:
//
// 1. FPS
//
// Forward and backward and side to side (strafing) motion is confined to the xz plane.
// Pitch is always about the local side axis and is restricted to [-90,90]. Yaw is always
// about the world up axis.
//
// 2. Spectator
//
// Same as FPS, except that forward motion is along the view vector, rather than being
// restricted to the xz plane. This allows the camera to 'fly', as in Quake spectator
// mode.
//
// 3. 6DOF
//
// Gimbal-lock free motion with yaw, pitch and roll about the object's local axes.
//
// Most of the class should be fairly self-explanatory, but here are a few notes.
//
// First, it is the +z rather than -z axis that is considered to be forward. When setting
// the view matrix, a 180-degree rotation is applied as the very last transformation
// to align with the -z axis, as per OpenGL convention. This is why the first and third
// rows of the view matrix are negated in GetViewMatrix().
//
// Also, the Move() function is provided as a (rather crude) convenience, so that you
// can use the class 'right out of the box'. Just send the appropriate boolean values
// (for example, derived from user input), the desired speeds, and the time step.
//
// There are some things I will add, such as a lookat function that works in FPS and
// spectator mode, smoother transition between modes, and loading from the OpenGL
// modelview matrix.
//
// If you have comments, suggestions, or other features that you would like to see
// included, please feel free to contact me through gamedev.net, or at jyk@sunflower.com.
//
// IMPORTANT DISCLAIMER:
//
// At the time of this writing (7-25-05) the class has not been thoroughly tested. I've
// tested it in camera mode, but haven't tried the 'model matrix' functions yet. I also
// haven't tested the lookat function. Please report any problems or bugs you encounter;
// once everything has been tested and has checked out, I'll remove this disclaimer.
// --------------------------------------------------------------------------------------

#ifndef JYKOPENGLOBJECT_H
#define JYKOPENGLOBJECT_H

#include "jykVector3.h"

namespace jyk {

class OpenGLObject
{
public:

enum {MODE_FPS,
MODE_SPECTATOR,
MODE_6DOF};

OpenGLObject(int mode = MODE_FPS);

int GetMode() const;
Vector3<> GetPos() const;
Vector3<> GetSide() const;
Vector3<> GetUp() const;
Vector3<> GetForward() const;
Vector3<> GetUpMove() const;
Vector3<> GetForwardMove() const;

void SetPos(const Vector3<>& pos);
void SetMode(int mode);

void Identity();

void ApplyYaw(float angle);
void ApplyPitch(float angle);
void ApplyRoll(float angle);

void MoveAlongSideAxis(float t);
void MoveAlongUpAxis(float t);
void MoveAlongForwardAxis(float t);

void LookAt(const Vector3<>& pos, const Vector3<>& target, const Vector3<>& up);

void SetOpenGLModelMatrix();
void SetOpenGLViewMatrix();
void MultOpenGLModelMatrix();
void MultOpenGLViewMatrix();

void GetModelMatrix(float m[16]) const;
void GetViewMatrix(float m[16]) const;

void Move(
bool yawLeft, bool yawRight,
bool pitchDown, bool pitchUp,
bool rollRight, bool rollLeft,
bool moveLeft, bool moveRight,
bool moveUp, bool moveDown,
bool moveForward, bool moveBack,
float yawSpeed, float pitchSpeed, float rollSpeed,
float sideSpeed, float upSpeed, float forwardSpeed,
float dt);

private:

void UpdateFromPitchYaw();
void Orthonormalize();
float DegToRad(float angle);
float RadToDeg(float angle);

int m_mode;

Vector3<> m_pos;

Vector3<> m_side;
Vector3<> m_up;
Vector3<> m_forward;

Vector3<> m_upMove;
Vector3<> m_forwardMove;

float m_pitch;
float m_yaw;
};

} // namespace jyk

#endif

// --------------------------------------------------------------------------------------
// FILE: jykOpenGLObject.cpp
// --------------------------------------------------------------------------------------

#include "jykOpenGLObject.h"
#include "gl.h"
#include <cmath>

namespace jyk {

// --------------------------------------------------------------------------------------
OpenGLObject::OpenGLObject(int mode) : m_mode(mode) {Identity();}
// --------------------------------------------------------------------------------------
int OpenGLObject::GetMode() const {return m_mode;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetPos() const {return m_pos;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetSide() const {return m_side;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetUp() const {return m_up;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetForward() const {return m_forward;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetUpMove() const {return m_upMove;}
// --------------------------------------------------------------------------------------
Vector3<> OpenGLObject::GetForwardMove() const {return m_forwardMove;}
// --------------------------------------------------------------------------------------
void OpenGLObject::SetPos(const Vector3<>& pos) {m_pos = pos;}
// --------------------------------------------------------------------------------------
void OpenGLObject::SetMode(int mode)
{
if (mode < 0 || mode > 2)
return;

// If we were in 6dof mode, and we're switching to fps or spectator, our
// old orientation may no longer be valid; that is, it may not be representable
// with pitch and yaw only.

// We could do a little math and preserve as much of our existing orientation as
// possible. But for simplicity, I'm just going to extract the yaw angle when
// going from 6dof to pitch-yaw form.

// If we're switching to 6dof, we can just adopt our axes as they are

if (mode == MODE_6DOF)
{
m_mode = mode;
m_upMove = m_up;
m_forwardMove = m_forward;
}

// Else we're switching to FPS or spectator. If we were in 6dof mode, we'll extract
// our yaw and snap into alignment with the world up vector.

else
{
if (m_mode == MODE_6DOF)
{
float x = m_forward[0];
float z = m_forward[2];

if (std::sqrtf(x * x + z * z) < 0.001f)
m_yaw = 0.0f;
else
m_yaw = RadToDeg(std::atan2f(x, z));

m_pitch = 0.0f;
}

m_mode = mode;
UpdateFromPitchYaw();
}
}
// --------------------------------------------------------------------------------------
void OpenGLObject::Identity()
{
m_pos.Set(0.0f, 0.0f, 0.0f);
m_side.Set(1.0f, 0.0f, 0.0f);
m_up.Set(0.0f, 1.0f, 0.0f);
m_forward.Set(0.0f, 0.0f, 1.0f);
m_upMove = m_up;
m_forwardMove = m_forward;
m_pitch = 0.0f;
m_yaw = 0.0f;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::ApplyYaw(float angle)
{
if (m_mode == MODE_6DOF)
{
// Rotate side and forward about up

float s = std::sinf(DegToRad(angle));
float c = std::cosf(DegToRad(angle));

Vector3<> forward = c * m_forward + s * m_side;
Vector3<> side = c * m_side - s * m_forward;

m_side = side;
m_forward = forward;

m_upMove = m_up;
m_forwardMove = m_forward;

Orthonormalize();
}
else // FPS or spectator
{
// Update yaw angle

m_yaw += angle;
if (m_yaw < 0.0f)
m_yaw += 360.0f;
if (m_yaw >= 360.0f)
m_yaw -= 360.0f;

UpdateFromPitchYaw();
}
}
// --------------------------------------------------------------------------------------
void OpenGLObject::ApplyPitch(float angle)
{
if (m_mode == MODE_6DOF)
{
// Rotate up and forward about side

float s = std::sinf(DegToRad(angle));
float c = std::cosf(DegToRad(angle));

Vector3<> up = c * m_up + s * m_forward;
Vector3<> forward = c * m_forward - s * m_up;

m_up = up;
m_forward = forward;

m_upMove = m_up;
m_forwardMove = m_forward;

Orthonormalize();
}
else // FPS or spectator
{
// Update pitch angle

m_pitch += angle;
if (m_pitch < -90.0f)
m_pitch = -90.0f;
else if (m_pitch > 90.0f)
m_pitch = 90.0f;
UpdateFromPitchYaw();
}
}
// --------------------------------------------------------------------------------------
void OpenGLObject::ApplyRoll(float angle)
{
if (m_mode == MODE_6DOF)
{
// Rotate side and up about forward

float s = std::sinf(DegToRad(angle));
float c = std::cosf(DegToRad(angle));

Vector3<> side = c * m_side + s * m_up;
Vector3<> up = c * m_up - s * m_side;

m_side = side;
m_up = up;

m_upMove = m_up;
m_forwardMove = m_forward;

Orthonormalize();
}

// No roll for FPS or spectator mode
}
// --------------------------------------------------------------------------------------
void OpenGLObject::MoveAlongSideAxis(float t)
{
m_pos += m_side * t;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::MoveAlongUpAxis(float t)
{
m_pos += m_upMove * t;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::MoveAlongForwardAxis(float t)
{
m_pos += m_forwardMove * t;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::LookAt(const Vector3<>& pos, const Vector3<>& target, const Vector3<>& up)
{
// I should (will) add a lookat function that assumes world y as the up vector,
// and therefore doesn't require a switch to 6dof mode.

m_forward = target - pos;
m_forward.NormalizeSelf();
m_side = up.Cross(m_forward);
m_side.NormalizeSelf();
m_up = m_forward.Cross(m_side);
m_pos = pos;

SetMode(MODE_6DOF);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::SetOpenGLModelMatrix()
{
float m[16];
GetModelMatrix(m);
glLoadMatrixf(m);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::SetOpenGLViewMatrix()
{
float m[16];
GetViewMatrix(m);
glLoadMatrixf(m);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::MultOpenGLModelMatrix()
{
float m[16];
GetModelMatrix(m);
glMultMatrixf(m);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::MultOpenGLViewMatrix()
{
float m[16];
GetViewMatrix(m);
glMultMatrixf(m);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::GetModelMatrix(float m[16]) const
{
m[0] = m_side[0];
m[1] = m_side[1];
m[2] = m_side[2];
m[3] = 0.0f;

m[4] = m_up[0];
m[5] = m_up[1];
m[6] = m_up[2];
m[7] = 0.0f;

m[8] = m_forward[0];
m[9] = m_forward[1];
m[10] = m_forward[2];
m[11] = 0.0f;

m[12] = m_pos[0];
m[13] = m_pos[1];
m[14] = m_pos[2];
m[15] = 1.0f;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::GetViewMatrix(float m[16]) const
{
m[0] = -m_side[0];
m[1] = m_up[0];
m[2] = -m_forward[0];
m[3] = 0.0f;

m[4] = -m_side[1];
m[5] = m_up[1];
m[6] = -m_forward[1];
m[7] = 0.0f;

m[8] = -m_side[2];
m[9] = m_up[2];
m[10] = -m_forward[2];
m[11] = 0.0f;

m[12] = m_pos.Dot(m_side);
m[13] = -m_pos.Dot(m_up);
m[14] = m_pos.Dot(m_forward);
m[15] = 1.0f;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::UpdateFromPitchYaw()
{
float sp = std::sinf(DegToRad(m_pitch));
float cp = std::cosf(DegToRad(m_pitch));
float sy = std::sinf(DegToRad(m_yaw));
float cy = std::cosf(DegToRad(m_yaw));

m_side.Set(cy, 0.0f, -sy);
m_forward.Set(sy * cp, -sp, cy * cp);
m_up = m_forward.Cross(m_side);

m_upMove.Set(0.0f, 1.0f, 0.0f);

if (m_mode == MODE_FPS)
m_forwardMove.Set(sy, 0.0f, cy);
else
m_forwardMove = m_forward;
}
// --------------------------------------------------------------------------------------
void OpenGLObject::Orthonormalize()
{
m_forward.NormalizeSelf();
m_side = m_up.Cross(m_forward);
m_side.NormalizeSelf();
m_up = m_forward.Cross(m_side);
}
// --------------------------------------------------------------------------------------
void OpenGLObject::Move(
bool yawLeft, bool yawRight,
bool pitchDown, bool pitchUp,
bool rollRight, bool rollLeft,
bool moveLeft, bool moveRight,
bool moveUp, bool moveDown,
bool moveForward, bool moveBack,
float yawSpeed, float pitchSpeed, float rollSpeed,
float sideSpeed, float upSpeed, float forwardSpeed,
float dt)
{
if (yawLeft)
ApplyYaw(yawSpeed * dt);
if (yawRight)
ApplyYaw(-yawSpeed * dt);
if (pitchDown)
ApplyPitch(pitchSpeed * dt);
if (pitchUp)
ApplyPitch(-pitchSpeed * dt);
if (rollRight)
ApplyRoll(rollSpeed * dt);
if (rollLeft)
ApplyRoll(-rollSpeed * dt);
if (moveLeft)
MoveAlongSideAxis(sideSpeed * dt);
if (moveRight)
MoveAlongSideAxis(-sideSpeed * dt);
if (moveUp)
MoveAlongUpAxis(upSpeed * dt);
if (moveDown)
MoveAlongUpAxis(-upSpeed * dt);
if (moveForward)
MoveAlongForwardAxis(forwardSpeed * dt);
if (moveBack)
MoveAlongForwardAxis(-forwardSpeed * dt);
}
// --------------------------------------------------------------------------------------
float OpenGLObject::DegToRad(float angle)
{
const float DEG_TO_RAD = (std::atanf(1.0f) * 4.0f) / 180.0f;
return angle * DEG_TO_RAD;
}
// --------------------------------------------------------------------------------------
float OpenGLObject::RadToDeg(float angle)
{
const float DEG_TO_RAD = 180.0f / (std::atanf(1.0f) * 4.0f);
return angle * DEG_TO_RAD;
}
// --------------------------------------------------------------------------------------

} // namespace jyk

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!