OgreOde Camera        

OgreOde 3rd Person Camera


Introduction

I spent some time to make my Camera collide correctly with the environment in OgreOde, like its known from any commercial computer game. My first thought was to give the camera a geometry and a body in OgreOde, so it collides with any object. After I made it not to move other objects but instead glide along them, I spent really a lot of time finding the right parameters for the cameras body and movement, and after all I gave it up. If anyone of you has this idea, I suggest you drop it :-)

The conclusion came, when I began to play World of Warcraft. We are about to implement a camera, that behaves just like this one, except some minor changes. Its pretty much of the GuildWars camera, too.

As I developed this camera while I was working on a bigger project, you will not be able to adapt it completely, but I expect you to work around that on your own ;) It is developed with OgreOde, but it should be no problem to use it with ode or another physics engine. I expect you have worked with OgreOde and cameras in Ogre before, so I will focus the new elements.

Concept

Its pretty easy.

- Take a ray, throw it from the point of the camera to the center, the players head or similiar.
         - Check for collisions between the ray and every object that is to move the camera
         -If there is a collision, move the camera along the ray to the point of collision + some distance

Additional Features: Load and save Settings, chasing if wanted, x rotation bounds

Class Declaration

Declare your cameras class and derive it from OgreOde::CollisionListener as its going to handle the collisions of the ray. You can not work with CollidingObject, because its part of my project. I use it to derive all objects that will have a collision callback by OgreOde.

class Camera : public virtual CollidingObject, public OgreOde::CollisionListener
         {

We will need the following members:

private:
         
         typedef CollidingObject inherited;

         protected:
         
         bool CollisionInThisFrame;
         
         Ogre::Camera* MyCamera; //the ogre camera
         
         Ogre::SceneNode* LookAtNode; //the node we are really looking at
         
         Ogre::SceneNode* RotationCenterNode; //the center of rotation, this node will be given by the user on creation
         Ogre::SceneNode* TargetPositionNode; //the position we want to be at, but maybe are not yet (chasing)
         Ogre::SceneNode* TargetLookAtNode; //the point we look at, but maybe dont yet
         
         
         double TightnessPos; //how fast does the camera reach target position
         double TightnessLook; //how fast does the camera look at the target position
         
         double MaxDistance; //how much can you zoom out
         double MinDistance; //how much can you zoom in
         double Distance; //whats the current distance between rotation center and camera
         double TargetDistance; //whats the target distance (chasing)
         
         double MinCamRot; //the bounds for x rotation
         double MaxCamRot;
         
         OgreOde::RayGeometry* Ray; //the ray we use for the collision detection
         
         ObjectManager* ObjManager; //this is part of my engine, you cant use it, more on that later

         Ogre::Vector3 Scale; //the scale vector of the parent node, so the camera will work same on all sizes you set for your player

and we have these functions:

public:
         Camera(Ogre::SceneNode* CenterParentNode, OgreOde::Space* Space, ObjectManager* Objects);
         ~Camera(void);
         
         bool virtual FrameStarted(double TimePassed);
         bool virtual FrameEnded(double TimePassed);
         void virtual Zoom(const double Value); //Move the camera relative along the ray
         
         bool virtual WriteToIni(IniFile& Ini)    const {return true;} //save the camera to a file
         bool virtual LoadFromIni(const std::basic_string<wchar_t>& ObjectID, IniFile& Ini); //load from a file
         
         
         bool virtual collision(OgreOde::Contact *Contact); //collision callback
         
         void RotateX(double Value); //rotate the camera along x axis, use this instead of rotating the
         //rotation center if you dont want the player to rotate
         
         inline Ogre::Camera* GetCamera(void) {return MyCamera;} //maybe you need the original Ogre object sometime
         
         
         private:
         bool MoveChase(const double TimePassed);
         bool MoveFixed(const double TimePassed);
         
         void InitBase(void);
         void InitNodes(Ogre::Vector3 CamCenter, Ogre::Vector3 LookAtPosition);
         bool InitCamera(void);
         bool InitViewport(void);
         
         void CollideRay(void);
         };

Constructor

Initialize your members. The constructor gets the node of the parent object, like your player, the space including the ode objects that the camera is going to collide with and a pointer to my ObjectManager class. As I said you cant use it, you need to find a way to let the camera access your objects.

::Camera::Camera(Ogre::SceneNode* CenterParentNode, OgreOde::Space* Space, ObjectManager* Objects) : inherited(),
         MyCamera(0), LookAtNode(0), TargetLookAtNode(0), TargetPositionNode(0), RotationCenterNode(0),
         TightnessPos(0.0), TightnessLook(0.0), MaxDistance(2.0), MinDistance(0.0), Ray(0), MinCamRot(0.0), MaxCamRot(0.0),
         Distance(1.0), TargetDistance(1.0), CollisionInThisFrame(false), Scale()
         {

Create a child of the parent center node, so it may has its own rotation that does not effect the player

RotationCenterNode = CenterParentNode->createChildSceneNode("Camera Pos");
         
         //Camera never rotates around the z axis:
         RotationCenterNode->setFixedYawAxis(true);

Our Ray. You see that I cast the camera to CollidingObject and then make it user object of the Ray. I do this with every Geometry and its parent colliding class, so you can make your own collision callback behaviour for every object. To use this camera you somehow have to identify your camera in the ray .

Ray = new OgreOde::RayGeometry(0, HardwareManager::getSingleton().GetWorld(), Space);
         Ray->setUserObject(static_cast<CollidingObject*>(this));
         Ray->disable();

Save a pointer to the class that handles all the objects we are going to collide with.

ObjManager = Objects;
         
         Scale = CenterParentNode->getScale();
         }

Destructor

Clean up a little bit:

::Camera::~Camera(void)
         {
             delete Ray;
         }

Create Camera and Viewport

These two functions create a camera and a viewport in ogre, I expect you are familiar with that.

bool ::Camera::InitCamera(void)
         {
                      // Create the camera
             if (MyCamera)
                     HardwareManager::getSingleton().GetScene()->destroyCamera(MyCamera);
         
             MyCamera = HardwareManager::getSingleton().GetScene()->createCamera("PlayerCam");
             if (!MyCamera) return false;
         
             MyCamera->setNearClipDistance(5);
                //note: Position is Ogre::SceneNode* and comes from CollidingObject
             Position->attachObject(MyCamera);
             return true;    
         }
         
         bool ::Camera::InitViewport(void)
         {
             // Create one viewport, entire window
             static Ogre::Viewport* vp =          HardwareManager::getSingleton().GetWindow()->addViewport(MyCamera);
             if (!vp) return false;
         
             //Pink is our background color
             vp->setBackgroundColour(Ogre::ColourValue(255,0,255));
         
             // Alter the camera aspect ratio to match the viewport
             MyCamera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
             return true;
         }

Init the SceneNodes

Now we need several scene nodes for our camera. The RotationCenterNode is what it says and is created within the constructor, so we dont need to handle it here. We will only adjust its position to the given vector, so you can set it to the players head e.g. Then we have a TargetLookAtNode and a TargetPositionNode, these are the positions where the camera should be and look at, but maybe is not yet. LookAtNode and Position are the real ones and will be updated later. You will be able to choose, if you want a static or chasing camera.

void ::Camera::InitNodes(Ogre::Vector3 CamCenter, Ogre::Vector3 LookAtPosition)
         {
             //set position of camera rotation center:
             RotationCenterNode->setPosition(CamCenter);
         
             //Destroy the nodes, if they exist
             if (LookAtNode)    
                           HardwareManager::getSingleton().GetScene()->destroySceneNode(LookAtNode->getName());
             if (TargetLookAtNode) 
                        HardwareManager::getSingleton().GetScene()->destroySceneNode(TargetLookAtNode->getName());
             if (TargetPositionNode) 
                       HardwareManager::getSingleton().GetScene()->destroySceneNode(TargetPositionNode->getName());
         
             //create LookAtNode
             LookAtNode = HardwareManager::getSingleton().GetScene()->getRootSceneNode()->createChildSceneNode();
         
                  //the targets are children of the center node and as such always rotated automatically to the correct positions
             TargetLookAtNode = RotationCenterNode->createChildSceneNode();
             TargetLookAtNode->setPosition(LookAtPosition);
             TargetPositionNode = RotationCenterNode->createChildSceneNode();
                
                //note: Position is Ogre::SceneNode* and comes from CollidingObject         
             //Real PositionNode shall always look at real LookAtNode
             Position->setAutoTracking(true, LookAtNode);
             //and it should never rotate around the z axis.
             Position->setFixedYawAxis(true);
         
         }