OgreOde Camera
From Ogre Wiki
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.
Contents |
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);
}

