FPS Camera from perspective of a moving parent node

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
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

FPS Camera from perspective of a moving parent node

Post by cheshirekow »

I wrote a modified version of CSceneNodeAnimatorCameraFPS that works the same for a normal CCameraSceneNode, but with a slight modification can be used to operate a FPS camera from within the reference frame of any node (as in, not the reference frame of the root node). Take a look at the screen shot. The FPS camera operates with respect to the set of axes in the forground. However that set happens to be moving around in a circle and rotating, so the camera stays fixed relative to the parent node and the rest of the world moves around it.

Image

I tried to upload a youtube video but it pretty much looks like crap. Maybe you can get a better sense for what this is by looking here.

http://www.youtube.com/watch?v=GiT7XSKwxOs

In the video the camera starts off as a normal FPS camera. At 0:21 I switch the parent node of the camera to the rotating set of axes. Now the camera acts like that node is the root node. This may be useful for having a character that moves around on a moving platform, and it doesn't make sense to have that platform attached to the root of the scene-graph.

I'll post the code in a reply to this initial post to make things a little more managable. CRelativeCameraSceneNode extends CCameraSceneNode and adds two members: m_localTarget and m_localUp. The OnAnimate() method uses these to set the true Target and Up vector, after transforming the local target and up vector into it's parent node's reference frame.

The modified CSceneNodeAnimatorCameraFPS assumes small angles of rotation. Rather than rotating the target vector specifically, I add to the target vector in the direction of the up vector for virtical tilt, and I add to the target vector in the direction orthogonal to both the target vector and the up vector for horizontal movement. Then I normalize the new target vector and scale it to match the original magnitude (just in case I ever want to use that for something, though for my purpose I could probably just leave it normalized).
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Here's the code for the modified camera

Code: Select all

/**
 *  \file       CRelativeCameraSceneNode.h
 *  \author:    cheshirekow
 *  \date:      Dec 15, 2009
 *  \brief:
 *
 *  detail:
 */

#ifndef CRELATIVECAMERASCENENODE_H_
#define CRELATIVECAMERASCENENODE_H_

#include <irrlicht.h>
#include "CCameraSceneNode.h"

namespace irr
{

namespace scene
{

/**
 *  \brief  works like a regular camera but everything is relative to the
 *          coordinate system of the camera's parent, not of the inertial
 *          system
 */
class CRelativeCameraSceneNode :
    public irr::scene::CCameraSceneNode
{
    protected:
        irr::core::vector3df    m_localUp;
        irr::core::vector3df    m_localTarget;

    public:
        /**
         *  \brief  create a new camera scene node with the parent as it's
         *          reference frame
         */
        CRelativeCameraSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,
            const core::vector3df& position = core::vector3df(0,0,0),
            const core::vector3df& lookat = core::vector3df(0,0,100));


        /**
         *  \brief  ...
         */
        virtual ~CRelativeCameraSceneNode();


        /**
         *  \brief  set's the camera's target in the local coordinate system of
         *          it's parent node
         */
        virtual void setTarget(const core::vector3df& pos);


        /**
         *  \brief  returns the camera's target in the local coordinate system
         */
        virtual const core::vector3df& getTarget() const;


        /**
         *  \brief  sets the camera's up vector in the local coordinate system
         *          of it's parent node
         */
        virtual void setUpVector(const core::vector3df& pos);


        /**
         *  \brief  returns the camera's up vector in the local coordinate
         *          system of it's parent node
         */
        virtual const core::vector3df& getUpVector() const;



        /**
         *  \brief  overrides base class definition to set the target and the
         *          up vector based on the current pose of the parent node
         *  \see    irr::scene::ISceneNode::OnAnimate()
         */
        virtual void OnAnimate(u32 timeMs);



};

}

}

#endif /* CRELATIVECAMERASCENENODE_H_ */

Code: Select all

/**
 *  \file       CRelativeCameraSceneNode.cpp
 *  \author:    cheshirekow
 *  \date:      Dec 15, 2009
 *  \brief:
 *
 *  detail:
 */

#include "CRelativeCameraSceneNode.h"
#include <iostream>

namespace irr
{

namespace scene
{
using namespace core;

CRelativeCameraSceneNode::CRelativeCameraSceneNode(
        ISceneNode* parent, ISceneManager* mgr, s32 id,
            const core::vector3df& position,
            const core::vector3df& lookat)
:
    CCameraSceneNode(parent, mgr, id, position, lookat),
    m_localUp(0,1,0),
    m_localTarget(lookat),
{

}

CRelativeCameraSceneNode::~CRelativeCameraSceneNode()
{


}

void CRelativeCameraSceneNode::setTarget(
                                                const core::vector3df& pos)
{
    m_localTarget = pos;
}

const core::vector3df& CRelativeCameraSceneNode::getTarget() const
{
    return m_localTarget;
}

void CRelativeCameraSceneNode::setUpVector(     const core::vector3df& pos)
{
    m_localUp = pos;
}

const core::vector3df& CRelativeCameraSceneNode::getUpVector() const
{
    return m_localUp;
}

void CRelativeCameraSceneNode::OnAnimate(u32 timeMs)
{
    Parent->getAbsoluteTransformation().rotateVect(UpVector, m_localUp);
    Parent->getAbsoluteTransformation().rotateVect(Target, m_localTarget);
    Target += Parent->getAbsolutePosition();

    
    // now call the overriden method to update all animators
    ISceneNode::OnAnimate(timeMs);

}

}

}
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Here's the code for the modified CSceneNodeAnimatorCameraFPS. The only differences are in AnimateNode so I'll only post that method:

Code: Select all

---------SNIP---------

	scene::ISceneManager * smgr = camera->getSceneManager();
	if(smgr && smgr->getActiveCamera() != camera)
		return;

	// get time
	f32 timeDiff = (f32) ( timeMs - LastAnimationTime );
	LastAnimationTime = timeMs;

	core::vector3df oldTargetVec   = (camera->getTarget() -
                                        camera->getPosition());
	core::vector3df upVector       =    camera->getUpVector();
	core::vector3df rightVector    = upVector.crossProduct(oldTargetVec);

	upVector.normalize();
	rightVector.normalize();

	core::vector3df newPosition    = camera->getPosition();
	core::vector3df newTargetVec   = oldTargetVec;

	if (CursorControl)
	{
		if (CursorPos != CenterCursor)
		{
		    f32 deltaX = (CursorPos.X - 0.5f) * RotateSpeed * MouseYDirection;
		    f32 deltaY = (CursorPos.Y - 0.5f) * RotateSpeed * MouseYDirection;

		    newTargetVec.normalize();

		    // if the upVector and the new target vector are within the max
		    // vertical angle (assuming the angle is small and sin(a) ~= a)
		    // then don't update the vertical angle
		    if( (newTargetVec - upVector).getLength() >
                        core::PI*(90 - MaxVerticalAngle)/360 )
		    {
		        newTargetVec -= upVector * deltaY;
		    }

		    newTargetVec += rightVector * deltaX;

		    // keep the target the same distance away from the camera
		    // this won't really be necessary for most applications, consider
		    // making this something that's only done if a flag is set
		    newTargetVec = newTargetVec.normalize() * oldTargetVec.getLength();

			// Do the fix as normal, special case below
			// reset cursor position to the centre of the window.
			CursorControl->setPosition(0.5f, 0.5f);
			CenterCursor = CursorControl->getRelativePosition();

			// needed to avoid problems when the event receiver is disabled
			CursorPos = CenterCursor;
		}

		// Special case, mouse is whipped outside of window before it can update.
		video::IVideoDriver* driver = smgr->getVideoDriver();
		core::vector2d<u32> mousepos(u32(CursorControl->getPosition().X), u32(CursorControl->getPosition().Y));
		core::rect<u32> screenRect(0, 0, driver->getScreenSize().Width, driver->getScreenSize().Height);

		// Only if we are moving outside quickly.
		bool reset = !screenRect.isPointInside(mousepos);

		if(reset)
		{
			// Force a reset.
			CursorControl->setPosition(0.5f, 0.5f);
			CenterCursor = CursorControl->getRelativePosition();
			CursorPos = CenterCursor;
 		}
	}

	// if we assume that the old target vector and the new target vector are
	// close, we wont even need to use the old target vector here
	oldTargetVec.normalize();

	if (CursorKeys[EKA_MOVE_FORWARD])
		newPosition += oldTargetVec * timeDiff * MoveSpeed;

	if (CursorKeys[EKA_MOVE_BACKWARD])
		newPosition -= oldTargetVec * timeDiff * MoveSpeed;

	// strafing
	if (CursorKeys[EKA_STRAFE_LEFT])
		newPosition -= rightVector * timeDiff * MoveSpeed;

	if (CursorKeys[EKA_STRAFE_RIGHT])
		newPosition += rightVector * timeDiff * MoveSpeed;

	// write translation
	camera->setPosition(newPosition);

	// write right target
	camera->setTarget(camera->getPosition() + newTargetVec);

---------SNIP---------
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Humm..

This look interesting. Your parenting seem to work well. Can you still apply the collision response animator on the camera?

When I'll have more time, I will be checking this code. Thanks for posting!
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

I have no idea about the collision response animator, since I've never used it :/ (outside of the tutorials). It would be cool if it could be correctly associated with the parent though.
Post Reply