(C++) Free Camera Rotation around target

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
egrath
Posts: 28
Joined: Wed May 13, 2009 8:15 am
Location: Austria
Contact:

(C++) Free Camera Rotation around target

Post by egrath »

Hello,

for a project of mine i've developed a simple camera system which can be rotated around a target point. I used the following approach:

1.) Imagine a sphere with a given radius around your target object
2.) According to the current Pitch/Yaw Angles, do the following:
2.1.) Calculate the position on the Y/Z Plane using the Radius from (1) and the Pitch Angle (Polar to Cartesian Coordinates)
2.2.) Create a Rotation Matrix which rotates around the Y Axis with the Yaw Angle. Use this matrix to transform the vector from 2.1 (which previously had it's X component set to 0)
3.) The vector now contains the position on the sphere where your camera is located.4
4.) Set the position of the camera

The code below contains the following functionality:

1.) Camera Rotation code
2.) Mouse input for getting the desired rotation
3.) Drawing of a loaded Model
4.) Drawing of a Sphere

Engine.h:

Code: Select all

#ifndef ENGINE_H
#define ENGINE_H

#include <iostream>
#include <irrlicht.h>

using std::cout;
using std::endl;

using namespace irr;
using namespace irr::core;
using namespace irr::video;
using namespace irr::scene;

class Engine : public IEventReceiver
{
    private:
    IrrlichtDevice *m_Device;
    ISceneManager *m_Scene;
    IVideoDriver *m_Driver;

    ICameraSceneNode *m_Camera;
    IMeshSceneNode *m_Mesh;

    vector3df m_Rot;                   // H/V Position of camera on sphere (only X/Y used)
    f32 m_Rad;                         // Radius of sphere

    bool m_Dragging;                   // Is currently dragging?
    vector2df m_DragStart;             // 2D Position on screen where the drag started
    vector3df m_DragStartRotation;     // Rotation when drag started

    vector3df getPositionOnSphere( f32 angleH, f32 angleV, f32 radius );
    void updateCameraPosition();

    void drawGimbalCircles();
    void dumpVector( const vector3df &vec );

    public:
    Engine();
    ~Engine();

    void run();

    // IEventReceiver
    public:
    virtual bool OnEvent( const SEvent &event );
};

#endif // ENGINE_H
Engine.cpp:

Code: Select all

#include "Engine.h"

// -----------------------------------------------------------------------
// PRIVATE
// -----------------------------------------------------------------------

//! Calculates the position of a point on a sphere
/** Based on a initial horizontal and vertical angle (vertical=X Rotation,
    horizontal=Y) and the radius of the sphere the position is calculated
    \param angleH Horizontal angle in Radians
    \param angleV Vertical angle in Radians
    \param radius Radius of the sphere
*/
vector3df Engine::getPositionOnSphere( f32 angleH, f32 angleV, f32 radius )
{
    // Get position on Z/Y Plane using conversion from polar
    // to cartesian coordinates
    f32 posZ = radius * cos( angleV );
    f32 posY = radius * sin( angleV );

    // Create a positional vector with X=0
    vector3df camPos( 0, posY, posZ );

    // Create a transformation matrix to rotate the vector 'camPos'
    // around the Y Axis
    matrix4 yawMat;
    yawMat.setRotationRadians( vector3df( 0, angleH, 0 ));
    yawMat.transformVect( camPos );

    dumpVector( camPos );

    return camPos;
}

void Engine::updateCameraPosition()
{
    // Set position of camera on Sphere
    m_Camera->setPosition( getPositionOnSphere( m_Rot.Y, m_Rot.X, m_Rad ));
}

void Engine::drawGimbalCircles()
{
    // Draw coordinate System
    SMaterial *mat = new SMaterial();
    mat->Lighting = false;

    m_Driver->setTransform( ETS_WORLD, matrix4() );
    m_Driver->setMaterial( *mat );
    m_Driver->draw3DLine( vector3df( -5, 0, 0 ), vector3df( 5, 0, 0 ), SColor( 255, 255, 0, 0));
    m_Driver->draw3DLine( vector3df( 0, -5, 0 ), vector3df( 0, 5, 0 ), SColor( 255, 255, 0, 0));
    m_Driver->draw3DLine( vector3df( 0, 0, -5 ), vector3df( 0, 0, 5 ), SColor( 255, 255, 0, 0));

    // Draw circle on X/Y
    vector3df pV( 3, 0, 0 );
    for( f32 r = 0; r <= 2 * PI; r += 0.1f )
    {
        f32 px = 3 * cos( r );
        f32 py = 3 * sin( r );

        m_Driver->draw3DLine( pV, vector3df( px, py, 0 ), SColor( 255, 0, 255, 0 ));
        pV = vector3df( px, py, 0 );
    }
    m_Driver->draw3DLine( pV, vector3df( 3, 0, 0 ), SColor( 255, 0, 255, 0 ));

    // Draw circle on Z/Y
    pV = vector3df( 0, 0, 3 );
    for( f32 r = 0; r <= 2 * PI; r += 0.1f )
    {
        f32 pz = 3 * cos( r );
        f32 py = 3 * sin( r );

        m_Driver->draw3DLine( pV, vector3df( 0, py,pz ), SColor( 255, 0, 255, 255 ));
        pV = vector3df( 0, py, pz );
    }
    m_Driver->draw3DLine( pV, vector3df( 0, 0, 3 ), SColor( 255, 0, 255, 255 ));

    // Draw circle on X/Z
    pV = vector3df( 3, 0, 0 );
    for( f32 r = 0; r <= 2 * PI; r += 0.1f )
    {
        f32 px = 3 * cos( r );
        f32 pz = 3 * sin( r );

        m_Driver->draw3DLine( pV, vector3df( px, 0 ,pz ), SColor( 255, 255, 255, 0 ));
        pV = vector3df( px, 0, pz );
    }
    m_Driver->draw3DLine( pV, vector3df( 3, 0, 0 ), SColor( 255, 255, 255, 0 ));
}

void Engine::dumpVector( const vector3df &vec )
{
    cout << "vec: [ X = " << vec.X << " Y = " << vec.Y << " Z = " << vec.Z << "]" << endl;
}

// -----------------------------------------------------------------------
// PUBLIC
// -----------------------------------------------------------------------

Engine::Engine()
{
    m_Device = createDevice( EDT_OPENGL, dimension2d<u32>( 1024, 768 ), 32, false, false, false, this );
    m_Scene = m_Device->getSceneManager();
    m_Driver = m_Device->getVideoDriver();

    m_Device->setWindowCaption( L"IrrTest15" );

    // Build Orthogonal Camera
    m_Camera = m_Scene->addCameraSceneNode( 0, vector3df( 0, 0, 7 ), vector3df());
    m_Rad = 7;
    m_Dragging = false;

    f32 width = 10.0f * ( 1024.0f / 768.0f );
    f32 height = 10.0f;

    matrix4 proj;
    proj.buildProjectionMatrixOrthoLH( width, height, 0.1f, 1000.0f );
    m_Camera->setProjectionMatrix( proj, true );

    // Add Light to the Scene
    ILightSceneNode *light = m_Scene->addLightSceneNode( 0, vector3df() );
    light->getLightData().AmbientColor = SColorf( 0.2f, 0.2f, 0.2f );
    light->getLightData().SpecularColor = SColorf( 1.0f, 1.0f, 1.0f );
    light->getLightData().DiffuseColor = SColorf( 0.8f, 0.8f, 0.8f );
    light->setLightType( ELT_DIRECTIONAL );
    light->setRotation( vector3df( 45.0f, 45.0f, 0.0f ));
    m_Scene->setAmbientLight( SColorf( 0.2f, 0.2f, 0.2f ));

    // Load Model
    m_Mesh = m_Scene->addMeshSceneNode( m_Scene->getMesh( "models/teapot.obj" ));
}

Engine::~Engine()
{
    m_Device->drop();
}

void Engine::run()
{
    while( m_Device->run() )
    {
        m_Driver->beginScene();

        m_Scene->drawAll();
        drawGimbalCircles();

        m_Driver->endScene();
    }
}

// IEventReceiver
bool Engine::OnEvent( const SEvent &event )
{    
    if( event.EventType == EET_KEY_INPUT_EVENT )
    {
        const SEvent::SKeyInput *ev = &event.KeyInput;
        if( ev->Key == KEY_LEFT )
            m_Rot.Y -= 0.1f;
        else if( ev->Key == KEY_RIGHT )
            m_Rot.Y += 0.1f;
        else if( ev->Key == KEY_UP )
            m_Rot.X += 0.1f;
        else if( ev->Key == KEY_DOWN )
            m_Rot.X -= 0.1f;

        updateCameraPosition();

        return true;
    }
    else if( event.EventType == EET_MOUSE_INPUT_EVENT )
    {
        const SEvent::SMouseInput *ev = &event.MouseInput;
        if( ev->Event == EMIE_MOUSE_WHEEL )
        {
            cout << "Wheel event: " << ev->Wheel << endl;
            if( ev->Wheel >= 0 )
                m_Rad -= 0.5f;
            else
                m_Rad += 0.5f;

            updateCameraPosition();
        }
        else
        {
            if( ! m_Dragging && ev->isLeftPressed() )
            {
                m_DragStart.X = ev->X;
                m_DragStart.Y = ev->Y;
                m_DragStartRotation.X = m_Rot.X;
                m_DragStartRotation.Y = m_Rot.Y;
                m_Dragging = true;
            }
            else if( m_Dragging && ! ev->isLeftPressed() )
            {
                m_Dragging = false;
            }
            else if( m_Dragging && ev->isLeftPressed() )
            {
                // Calculate a rotational offset in the range of -PI to +PI
                f32 dx = (( ev->X - m_DragStart.X ) / m_Driver->getScreenSize().Width ) * PI;
                f32 dy = (( ev->Y - m_DragStart.Y ) / m_Driver->getScreenSize().Height ) * PI;

                // Calculate the new total rotation
                m_Rot.X = m_DragStartRotation.X + dy;
                m_Rot.Y = m_DragStartRotation.Y + dx;

                updateCameraPosition();
            }
        }
    }

    return false;
}
/edit: The zooming only works if you comment out the parts which set up a orthogonal camera and use the default perspective one.

Screenshot:
Image

Egon
ceyron
Posts: 63
Joined: Tue Mar 03, 2009 5:10 pm
Location: Bucuresti, România

Post by ceyron »

Thank you, i've learned alot by looking over your code...
Image
Distorion
Posts: 12
Joined: Thu Jul 19, 2012 1:58 am
Location: Canberra, Australia

Re: (C++) Free Camera Rotation around target

Post by Distorion »

Hey,
I am trying to change what the camera orbits around during run time. but i am having trouble due to my less then awesome math skills. Everything i have tryd so far just changes the distance at witch the camera orbits the target and not the target its self.

Any help would be greatly apreciated. Thank you!
Post Reply