Howto: Flying 3rd Person camera

A forum to store posts deemed exceptionally wise and useful
the_viking
Posts: 23
Joined: Fri Aug 06, 2004 12:28 pm

Howto: Flying 3rd Person camera

Post by the_viking »

Hi,

for my recent project i build a flying 3rd Person camera. It supports interpolation between positions, so this will be a very smooth cam :)
It bases on some code i found in the forum, but that was very buggy and that cam wasn't too beautyful to fit my high expectation ;)
So this camera would be always behind or in front of or wherever you want it :) of a scene node you can it attach to. So here is the code:

camera.h:

Code: Select all

#ifndef CAMERA_H
#define CAMERA_H

#include <irrlicht.h>

/*
    Custom 3rd person following camera for irrlicht
    Made by the_viking in 2004 
*/

class cFollowingCamera 
{ 
    irr::scene::ISceneNode* m_pTargetNode; 
    irr::scene::ICameraSceneNode* m_cam; 
	irr::scene::IBillboardSceneNode* pCamPosNode,*pCamDirNode;

    irr::core::vector3df currentCameraPos; 
	irr::core::vector3df currentTargetPos;
	irr::f32 fSpeed;
    
   public: 
      // class constructor 
      cFollowingCamera(irr::scene::ISceneNode* targetNode, irr::scene::ISceneManager* smgr, 
										  irr::core::vector3df& vRelativePos = irr::core::vector3df(0,200,-200), irr::f32 speed = 15.f );
      // class destructor 
      ~cFollowingCamera(); 
       
      irr::scene::ICameraSceneNode* getCam() {  return m_cam; } 
       
	  // fDT = delta time in seconds.
      void Update(f32 fDT); 
}; 

// class constructor 
inline cFollowingCamera::cFollowingCamera(irr::scene::ISceneNode* targetNode, irr::scene::ISceneManager* smgr, 
										  irr::core::vector3df& vRelativePos, irr::f32 speed) 
{ 
	// how fast the camera moves
	fSpeed = speed;

	//create a camera 
	m_cam = smgr->addCameraSceneNode(0); // = new cFloatingCamera(targetNode,smgr,-1);

	smgr->setActiveCamera(m_cam);
	m_pTargetNode = targetNode; 

	m_pTargetNode->grab(); //make sure the node wont dissappear on us 

	// I am using billboard scenen nodes with a zero size, 
	// maybe you want to use the fake translation node
	pCamPosNode = smgr->addBillboardSceneNode(targetNode);
	pCamDirNode = smgr->addBillboardSceneNode(targetNode);

	pCamPosNode->setSize(core::dimension2d<f32>(0,0));
	pCamDirNode->setSize(core::dimension2d<f32>(0,0));
	
	pCamPosNode->setPosition(vRelativePos);
	pCamDirNode->setPosition(irr::core::vector3df(0,100.f,0));

	// Update absolute positions... these calls are
	// removing most of the "jitteryness" 
	m_pTargetNode->updateAbsolutePosition();
	pCamPosNode->updateAbsolutePosition();
	pCamDirNode->updateAbsolutePosition();

	// get first position for interpolation
	currentTargetPos = pCamDirNode->getAbsolutePosition();
	currentCameraPos = pCamPosNode->getAbsolutePosition();

	// you can change this, default value is 2000.f
	m_cam->setFarValue(15000.f);
} 

// class destructor 
inline cFollowingCamera::~cFollowingCamera() 
{ 
	m_pTargetNode->drop(); //make sure we let go of the node resource 
	pCamDirNode->drop();
	pCamPosNode->drop();
} 

inline void cFollowingCamera::Update(f32 fDT) { 
    if(!m_cam || !m_pTargetNode) return; 

	m_pTargetNode->updateAbsolutePosition();
	pCamPosNode->updateAbsolutePosition();
	pCamDirNode->updateAbsolutePosition();

    irr::core::vector3df currTargetPos = pCamPosNode->getAbsolutePosition();
	irr::core::vector3df currDirPos = pCamDirNode->getAbsolutePosition();
	irr::core::vector3df currTargetRot = m_pTargetNode->getRotation();
	irr::core::vector3df targetMoveRate = currTargetPos - currentCameraPos;
	irr::core::vector3df directionMoveRate = currDirPos - currentTargetPos;

	// Build matrix
	irr::core::matrix4 rotMatrix;
	rotMatrix.setRotationDegrees( currTargetRot );

	// Calculate new direction
	irr::core::vector3df Target(0,0,1);	
	irr::core::vector3df vUpVec(0,1,0);	

	rotMatrix.transformVect(Target);
	rotMatrix.transformVect(vUpVec);
	
	
	currentCameraPos += targetMoveRate * MIN(1.f,fDT * fSpeed);
	currentTargetPos += directionMoveRate * MIN(1.f,fDT * fSpeed);
	
	//Update camera
	
	m_cam->setPosition(currentCameraPos); 
	m_cam->setUpVector(vUpVec);
	m_cam->setTarget(currentTargetPos);
	
	m_cam->updateAbsolutePosition();    
} 

#endif
It's not very well commented, but for example, you can use it like this (i expect there is a scenenode pPlayerNode that represents your player and a pointer to a cFollowingCamera as a member in a class, pSMGR is the scenemanager):

Code: Select all

mpCam = new cFollowingCamera( pPlayerNode, pSMGR, irr::core::vector3df(10,-30, 10.f );
In your update function you then write:

Code: Select all

mpCam->Update( fDeltaTime );
To round it up, here the last part (when the game is over):
Write this to you cleanupGame() function or whatever you name it:

Code: Select all

delete mpCam;
Sometimes people forget this ;)

Maybe you have to define the MIN() macro or you have to place your own min() function there.. my macros look like these:

Code: Select all

#define MIN(a,b) ( (a) < (b) ? (a) : (b))
#define MAX(a,b) ( (a) > (b) ? (a) : (b))
I dont't know if irrlicht has it's own min,max routines, and i'm too lazy to check ;)

I testet this class well with my Project ( a Spaceshooter ), but if you can catch some bugs, just report :)

Well then, i would show you a screenshot, but my project is commercial and i am not allowed to give you any media. Maybe i will make a example app when i have some free time here :) But you're free to do ;)
Last edited by the_viking on Sat Aug 21, 2004 1:44 pm, edited 2 times in total.
spongebob

Post by spongebob »

nice! :D
Cleves
Posts: 224
Joined: Mon Sep 08, 2003 6:40 pm

Post by Cleves »

I've encounterd a few problems.
When I compile I get:

C:\Program Files\irrlicht\The scout\main.cpp(316) : error C2039: 'update' : is not a member of 'cFollowingCamera'
c:\program files\irrlicht\the scout\camera.h(15) : see declaration of 'cFollowingCamera'
C:\Program Files\irrlicht\The scout\main.cpp(316) : error C2065: 'fDeltaTime' : undeclared identifier

Any ideas?

Thanks
the_viking
Posts: 23
Joined: Fri Aug 06, 2004 12:28 pm

Post by the_viking »

That was a example of how to implement it, sorry for typing bug.. it means "Update( .. )" not "update( .. )". So the fDeltaTime var you have to calculate by yourself.. could be done like this in you main loop:

Code: Select all


u32 nLastTime = irrDevice->getTimer()->getTime();

while(irrDevice->run())
{
  u32 nNow = irrDevice->getTimer()->getTime();
  f32 fDeltaTime = ( nNow - nLastTime ) / 1000.f;
  nLastTime = nNow;

   pCam->Update( fDeltaTime );
}

Cleves
Posts: 224
Joined: Mon Sep 08, 2003 6:40 pm

Post by Cleves »

Yeah, that seemd to work.
But i have another problem, when I create the camera:

Code: Select all

cameranode = new cFollowingCamera(razornode, smgr, vector3df(0, 10,-30, 10.f ));
It seems that vector3df can't take the 4th arguement,
error C2661: 'vector3d<float>::vector3d<float>' : no overloaded function takes 4 parameters.
the_viking
Posts: 23
Joined: Fri Aug 06, 2004 12:28 pm

Post by the_viking »

lol, sorry, another typing bug ^^
Surely vector3d<T> can just take 3 params in their constructor. So just use 3 and everything is ok ;)

Corrected it in the code above. Thanks ;)
Cleves
Posts: 224
Joined: Mon Sep 08, 2003 6:40 pm

Post by Cleves »

Thanks, it seems to work :D
Peter Müller
Posts: 292
Joined: Sun Mar 14, 2004 5:28 pm
Location: Germany
Contact:

Post by Peter Müller »

This camera is (will be) implemented in IrrlichtNX natively.
http://www.games-forge.de - Die Community für Nachwuchsprogrammierer
Guest

Post by Guest »

nice to hear this :)
the_viking
Posts: 23
Joined: Fri Aug 06, 2004 12:28 pm

Post by the_viking »

^
| -> My Post, forgot to login ;)
Peter Müller
Posts: 292
Joined: Sun Mar 14, 2004 5:28 pm
Location: Germany
Contact:

Post by Peter Müller »

the_viking wrote:^
| -> My Post, forgot to login ;)
i++ ;)
http://www.games-forge.de - Die Community für Nachwuchsprogrammierer
eeheeehe
Posts: 22
Joined: Tue Sep 28, 2004 3:33 am

Problems with Code

Post by eeheeehe »

I have tried to use this in irrlicht-0.7 but I keep getting an error message from camera.h when it is included in main.cpp I am using the Dev-CPP compiler, and it keeps throwing me this error message:

Code: Select all

Compiler: Default compiler
Building Makefile: "C:\Dev-Cpp\New Folder\Makefile.win"
Executing  make...
make.exe -f "C:\Dev-Cpp\New Folder\Makefile.win" all
g++.exe -c main.cpp -o main.o -I"C:/DEV-CPP/include/c++/3.3.1"  -I"C:/DEV-CPP/include/c++/3.3.1/mingw32"  -I"C:/DEV-CPP/include/c++/3.3.1/backward"  -I"C:/DEV-CPP/lib/gcc-lib/mingw32/3.3.1/include"  -I"C:/DEV-CPP/include"  -I"C:/DirectX9 SDK/Include"  -I"C:/DirectX9 SDK/Samples/C++/Common/Include"  -I"C:/irrlicht-0.7/include"   

In file included from main.cpp:7:
C:/irrlicht-0.7/include/camera.h:34: error: `f32' was not declared in this 

   scope
C:/irrlicht-0.7/include/camera.h:34: error: syntax error before `)' token
C:/irrlicht-0.7/include/camera.h:27: error: invalid type `irr::core::vector3df' 
   for default argument to `irr::core::vector3df&'
C:/irrlicht-0.7/include/camera.h: In constructor `
   cFollowingCamera::cFollowingCamera(irr::scene::ISceneNode*, 

   irr::scene::ISceneManager*, irr::core::vector3df&, float)':

C:/irrlicht-0.7/include/camera.h:57: error: `core' undeclared (first use this 

   function)
C:/irrlicht-0.7/include/camera.h:57: error: (Each undeclared identifier is 
   reported only once for each function it appears in.)
C:/irrlicht-0.7/include/camera.h:57: error: syntax error before `::' token

C:/irrlicht-0.7/include/camera.h:58: error: syntax error before `::' token

C:/irrlicht-0.7/include/camera.h: At global scope:
C:/irrlicht-0.7/include/camera.h:85: error: `f32' was not declared in this 
   scope
C:/irrlicht-0.7/include/camera.h:85: error: syntax error before `)' token
C:/irrlicht-0.7/include/camera.h: In member function `void 
   cFollowingCamera::Update(...)':
C:/irrlicht-0.7/include/camera.h:110: error: `fDT' undeclared (first use this 
   function)
C:/irrlicht-0.7/include/camera.h:110: error: `MIN' undeclared (first use this 
   function)
main.cpp: In function `int main()':
main.cpp:32: error: syntax error before `::' token
main.cpp:39: error: syntax error before `::' token

main.cpp:79: error: `camera' undeclared (first use this function)

main.cpp:86: error: syntax error before `::' token
main.cpp:87: error: syntax error before `::' token
main.cpp:91: error: `mpCam' undeclared (first use this function)
main.cpp:91: error: syntax error before `;' token

g++.exe main.o  -o "example.exe" -L"C:/DEV-CPP/lib" -L"C:/DirectX9 SDK/Lib" -L"C:/irrlicht-0.7/include" -L"C:/irrlicht-0.7/lib/DevCpp" -L"C:/DXSDK/Lib" ../../irrlicht-0.7/lib/DevCpp/libIrrlicht.a ../../irrlicht-0.7/lib/DevCpp/libjpeg.a ../../irrlicht-0.7/lib/DevCpp/libz.a  

Execution terminated
eeheeehe
Posts: 22
Joined: Tue Sep 28, 2004 3:33 am

Post by eeheeehe »

After staring at the code for a bit, I figured out what I was doing wrong. For any irrlicht newbies who are worse than I am, the code is as follows:

Code: Select all

#ifndef CAMERA_H 
#define CAMERA_H 
#define MIN(a,b) ( (a) < (b) ? (a) : (b)) 
#define MAX(a,b) ( (a) > (b) ? (a) : (b))

#include <irrlicht.h> 

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

/* 
    Custom 3rd person following camera for irrlicht 
    Made by the_viking in 2004 
*/ 

class cFollowingCamera // begin class
{ 
    irr::scene::ISceneNode* m_pTargetNode; // init. variables
    irr::scene::ICameraSceneNode* m_cam; 
   irr::scene::IBillboardSceneNode* pCamPosNode,*pCamDirNode; 

    irr::core::vector3df currentCameraPos; 
   irr::core::vector3df currentTargetPos; 
   irr::f32 fSpeed; 
    
   public: 
      // class constructor 
      cFollowingCamera(irr::scene::ISceneNode* targetNode, irr::scene::ISceneManager* smgr, 
             vector3df vRelativePos = vector3df(0,200,-200), irr::f32 speed = 15.f ); 
      // class destructor 
      ~cFollowingCamera(); 
        
      irr::scene::ICameraSceneNode* getCam() {  return m_cam; } 
        
     // fDT = delta time in seconds. 
      void Update(f32 fDT); 
}; 

// class constructor 
inline cFollowingCamera::cFollowingCamera(irr::scene::ISceneNode* targetNode, irr::scene::ISceneManager* smgr, 
                                irr::core::vector3df vRelativePos, irr::f32 speed) 
{ 
   // how fast the camera moves 
   fSpeed = speed; 

   //create a camera 
   m_cam = smgr->addCameraSceneNode(0); // = new cFloatingCamera(targetNode,smgr,-1); 

   smgr->setActiveCamera(m_cam); 
   m_pTargetNode = targetNode; 

   m_pTargetNode->grab(); //make sure the node wont dissappear on us 

   // I am using billboard scenen nodes with a zero size, 
   // maybe you want to use the fake translation node 
   pCamPosNode = smgr->addBillboardSceneNode(targetNode); 
   pCamDirNode = smgr->addBillboardSceneNode(targetNode); 

   pCamPosNode->setSize(core::dimension2d<f32>(0,0)); 
   pCamDirNode->setSize(core::dimension2d<f32>(0,0)); 
    
   pCamPosNode->setPosition(vRelativePos); 
   pCamDirNode->setPosition(irr::core::vector3df(0,100.f,0)); 

   // Update absolute positions... these calls are 
   // removing most of the "jitteryness" 
   m_pTargetNode->updateAbsolutePosition(); 
   pCamPosNode->updateAbsolutePosition(); 
   pCamDirNode->updateAbsolutePosition(); 

   // get first position for interpolation 
   currentTargetPos = pCamDirNode->getAbsolutePosition(); 
   currentCameraPos = pCamPosNode->getAbsolutePosition(); 

   // you can change this, default value is 2000.f 
   m_cam->setFarValue(15000.f); 
} 

// class destructor 
inline cFollowingCamera::~cFollowingCamera() 
{ 
   m_pTargetNode->drop(); //make sure we let go of the node resource 
   pCamDirNode->drop(); 
   pCamPosNode->drop(); 
} 

inline void cFollowingCamera::Update(f32 fDT) { 
    if(!m_cam || !m_pTargetNode) return; 

   m_pTargetNode->updateAbsolutePosition(); 
   pCamPosNode->updateAbsolutePosition(); 
   pCamDirNode->updateAbsolutePosition(); 

    irr::core::vector3df currTargetPos = pCamPosNode->getAbsolutePosition(); 
   irr::core::vector3df currDirPos = pCamDirNode->getAbsolutePosition(); 
   irr::core::vector3df currTargetRot = m_pTargetNode->getRotation(); 
   irr::core::vector3df targetMoveRate = currTargetPos - currentCameraPos; 
   irr::core::vector3df directionMoveRate = currDirPos - currentTargetPos; 

   // Build matrix 
   irr::core::matrix4 rotMatrix; 
   rotMatrix.setRotationDegrees( currTargetRot ); 

   // Calculate new direction 
   irr::core::vector3df Target(0,0,1);    
   irr::core::vector3df vUpVec(0,1,0);    

   rotMatrix.transformVect(Target); 
   rotMatrix.transformVect(vUpVec); 
    
    
   currentCameraPos += targetMoveRate * MIN(1.f,fDT * fSpeed); 
   currentTargetPos += directionMoveRate * MIN(1.f,fDT * fSpeed); 
    
   //Update camera 
    
   m_cam->setPosition(currentCameraPos); 
   m_cam->setUpVector(vUpVec); 
   m_cam->setTarget(currentTargetPos); 
    
   m_cam->updateAbsolutePosition();    
} 

#endif 
But I still have an orientation problem. The camera keeps being positioned below Sydney, and I am having trouble figuring out how to rotate the camera. IOW: It is always placed upside down.
timeless
Posts: 1
Joined: Sun Feb 13, 2005 2:13 pm

Post by timeless »

really nice code :D

a question, how do i do to place the camera in a different position ? it's always placed under and a little rotated below sydney...please explain how to place the camera behind her etc.

thanx in advance!
ijo coim
Posts: 57
Joined: Fri Mar 25, 2005 1:29 pm
Location: indonesia, jogja-jogja

Post by ijo coim »

hi, i am trying to make a third person camera, then i use your code.
your are right it always under below sydney. but, do you read this thread?
http://irrlicht.sourceforge.net/phpBB2/ ... php?t=1140

it have a lot of the same. but in that thread, there is an event receiver to move the node.

see this in that thread :

Code: Select all

2) moving a node 
if(keys[irr::KEY_LEFT]) { 
faerienode->setRotation( irr::core::vector3df(0, faerienode->getRotation().Y - 10.0f, 0) ); 
} 
if(keys[irr::KEY_RIGHT]) { 
faerienode->setRotation( irr::core::vector3df(0, faerienode->getRotation().Y + 10.0f, 0) ); 
} 
if(keys[irr::KEY_UP]) { 
irr::core::vector3df facing( sin( faerienode->getRotation().Y * PI/180.0f ), 0, cos( faerienode->getRotation().Y * PI/180.0f ) ); 
facing.normalize(); 
irr::core::vector3df newPos = (facing*20.0f) + faerienode->getPosition(); 
faerienode->setPosition( newPos ); 
} 
just put the code into event receiver, than yous will see if that work. but for KEY_UP, i still don'e understan. it seem the camera to slow. eventhough the camera will always follow the faerie node.

try that.
Post Reply