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
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;
}
Screenshot:
Egon