Arbitrary Scene Node FPS Animator

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

Arbitrary Scene Node FPS Animator

Post by cheshirekow »

This is a class I wrote to control an arbitrary scene node using FPS-style controls. It's basically a rip-off of the animator for the FPS camera.

Here's a screenshot to help you understand my motivation:

Image

Look in the bottom right split screen. Basically the little red-green-blue thing has three orthogonal cameras attached to it. I wanted to control that object with the FPS style controls, not any particular one of the cameras. The other three parts of the split screen are the view from the three cameras on the character.

Anyway, the code is below, comments are appreciated.

CSceneNodeAnimatorFPS.h

Code: Select all

#ifndef __C_SCENE_NODE_ANIMATOR_FPS_H_INCLUDED__
#define __C_SCENE_NODE_ANIMATOR_FPS_H_INCLUDED__

#include "ISceneNodeAnimatorFPS.h"
#include "vector2d.h"
#include "SKeyMap.h"
#include "irrArray.h"

namespace irr
{
namespace gui
{
    class ICursorControl;
}

namespace scene
{
    /**
     *  \brief  Provides first-person like controls for an arbitrary scene
     *          node
     */
    class CSceneNodeAnimatorFPS : public ISceneNodeAnimatorFPS
    {
        public:

            /**
             *  \brief  Creates a new animator
             *  \param  smgr                the scene manager managing this
             *                              animator's  node
             *  \param  cursorControl       the cursor control object
             *  \param  rotateSpeed         responsiveness of the mouse
             *  \param  moveSpeed           how far to move per millisecond
             *  \param  jumpSpeed
             *  \param  keyMapArray         array of key inputs to listen for
             *  \param  keyMapSize          size of the above
             *  \param  noVerticalMovement  boolean indicating vertical movement
             *                              is not allowed
             * @return
             */
            CSceneNodeAnimatorFPS( ISceneManager *smgr,
                gui::ICursorControl* cursorControl,
                f32         rotateSpeed     = 100.0f,
                f32         moveSpeed       = 0.01f,
                f32         jumpSpeed       = 0.f,
                SKeyMap*    keyMapArray     = 0,
                u32         keyMapSize      = 0,
                bool        noVerticalMovement  = false);


            /**
             *  \brief
             */
            virtual ~CSceneNodeAnimatorFPS();


            /**
             *  \brief  updates the indicated scene node based on registered
             *          mouse and keyboard inputs
             *  \param  node    the node to update
             *  \param  timeMs  the time segment over which the update occured
             */
            virtual void animateNode(ISceneNode* node, u32 timeMs);


            /**
             *  \brief  is called by the event handler when an input event
             *          occurs
             *  \param  event   the event that occured
             *  \return true if the function handles the event and it should not
             *          be propagted to other handlers
             */
            virtual bool OnEvent(const SEvent& event);


            /**
             *  \brief
             */
            virtual f32 getMoveSpeed() const;


            /**
             *  \brief  Sets the speed of movement in units per second
             *  \param  moveSpeed   units per second
             */
            virtual void setMoveSpeed(f32 moveSpeed);


            /**
             *  \brief  Returns the rotation speed
             */
            virtual f32 getRotateSpeed() const;


            /**
             *  \brief  Set the rotation speed
             */
            virtual void setRotateSpeed(f32 rotateSpeed);

            /**
             *  \brief  Sets the keyboard mapping for this animator
             *  \param  keymap  an array of keyboard mappings, see SKeyMap
             *  \param  count   the size of the keyboard map array
             */
            virtual void setKeyMap(SKeyMap *map, u32 count);



            /**
             *  \brief  Sets whether vertical movement should be allowed.
             */
            virtual void setVerticalMovement(bool allow);


            /**
             *  \brief  This animator will receive events when attached to the
             *          active camera
             *  \return true if this animator will receive events
             */
            virtual bool isEventReceiverEnabled() const
            {
                return true;
            }


            /**
             *  \brief
             *  \return
             */
            virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
            {
                return ESNAT_CAMERA_FPS;
            }


            /**
             *  \brief  Creates a clone of this animator.
             *
             *  Please note that you will have to drop
             *  (IReferenceCounted::drop()) the returned pointer after calling
             *  this.
             *
             *  \return a pointer to the newly created objec that is a clone of
             *          this one
             */
            virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0);


            /**
             *  \brief  a container for a pairing of an action with a key code
             */
            struct SCamKeyMap
            {
                SCamKeyMap() {};
                SCamKeyMap(s32 a, EKEY_CODE k) : action(a), keycode(k) {}

                s32 action;
                EKEY_CODE keycode;
            };


            /**
             *  \brief  Sets the keyboard mapping for this animator
             *
             *  Helper function for the clone method.
             *
             *  \param  keymap the new keymap array
             */
            void setKeyMap(const core::array<SCamKeyMap>& keymap);


        private:
            /**
             *  \brief  sets the state of all keys to "up"
             */
            void allKeysUp();

            scene::ISceneManager*   smgr;           ///< the scene manager
            gui::ICursorControl*    CursorControl;  ///< the mouse controller

            f32 MaxVerticalAngle;   ///< the maximimum angle for looking up

            f32 MoveSpeed;          ///< number of units to move while key press
            f32 RotateSpeed;        ///< how far to turn when mouse is moved
            f32 JumpSpeed;          ///< how far to move up when jump is pressed

            s32 LastAnimationTime;  ///< the timestamp of the last step through
                                    ///  the animate function

            core::vector3df         TargetVector;
            core::array<SCamKeyMap> KeyMap;
            core::position2d<f32>   CenterCursor, CursorPos;

            bool CursorKeys[6];         ///< true if the key at index [i] is dn
            bool firstUpdate;           ///< true if this is the furst run
            bool NoVerticalMovement;    ///< if true, no vertical movement is
                                        ///  allowed
    };

} // end namespace scene
} // end namespace irr

#endif // __C_SCENE_NODE_ANIMATOR_FPS_H_INCLUDED__


CSceneNodeAnimator.cpp

Code: Select all

// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "CSceneNodeAnimatorFPS.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "Keycodes.h"
#include "ICursorControl.h"
#include "ICameraSceneNode.h"
#include "ISceneNodeAnimatorCollisionResponse.h"

#include <iostream>

namespace irr
{
namespace scene
{

CSceneNodeAnimatorFPS::CSceneNodeAnimatorFPS( ISceneManager *smgr,
        gui::ICursorControl* cursorControl,
		f32 rotateSpeed, f32 moveSpeed, f32 jumpSpeed,
		SKeyMap* keyMapArray, u32 keyMapSize, bool noVerticalMovement)
: smgr(smgr), CursorControl(cursorControl), MaxVerticalAngle(88.0f),
	MoveSpeed(moveSpeed), RotateSpeed(rotateSpeed), JumpSpeed(jumpSpeed),
	LastAnimationTime(0), firstUpdate(true), NoVerticalMovement(noVerticalMovement)
{
	#ifdef _DEBUG
	setDebugName("CSceneNodeAnimatorFPS");
	#endif

	if (CursorControl)
		CursorControl->grab();

	if (smgr)
	    smgr->grab();

	allKeysUp();

	// create key map
	if (!keyMapArray || !keyMapSize)
	{
		// create default key map
		KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, irr::KEY_KEY_W));
		KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, irr::KEY_KEY_S));
		KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, irr::KEY_KEY_A));
		KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, irr::KEY_KEY_D));
		KeyMap.push_back(SCamKeyMap(EKA_JUMP_UP, irr::KEY_SPACE));
	}
	else
	{
		// create custom key map
		setKeyMap(keyMapArray, keyMapSize);
	}
}


CSceneNodeAnimatorFPS::~CSceneNodeAnimatorFPS()
{
	if (CursorControl)
		CursorControl->drop();
	if(smgr)
	    smgr->drop();
}


/**
 *  Records the key state and mouse position at the time of the event
 */
bool CSceneNodeAnimatorFPS::OnEvent(const SEvent& evt)
{
	switch(evt.EventType)
	{
	case EET_KEY_INPUT_EVENT:
		for (u32 i=0; i<KeyMap.size(); ++i)
		{
			if (KeyMap[i].keycode == evt.KeyInput.Key)
			{
				CursorKeys[KeyMap[i].action] = evt.KeyInput.PressedDown;
				return true;
			}
		}
		break;

	case EET_MOUSE_INPUT_EVENT:
		if (evt.MouseInput.Event == EMIE_MOUSE_MOVED)
		{
			CursorPos = CursorControl->getRelativePosition();
			return true;
		}
		break;

	default:
		break;
	}

	return false;
}


void CSceneNodeAnimatorFPS::animateNode(ISceneNode* node, u32 timeMs)
{
	if (firstUpdate)
	{
		node->updateAbsolutePosition();

		if (CursorControl && node)
		{
			CursorControl->setPosition(0.5f, 0.5f);
			CursorPos = CenterCursor = CursorControl->getRelativePosition();
		}

		LastAnimationTime = timeMs;

		firstUpdate = false;
	}

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

	// get current position and rotation relative to the parent node
	core::vector3df currentPos = node->getPosition();
	core::vector3df relativeRotation = node->getRotation();

	// calculate the incremental rotation as proportional to the distance the
	// mouse cursor was away from the center of the screen
	if (CursorControl)
	{
		if (CursorPos != CenterCursor)
		{
			relativeRotation.Y -= (0.5f - CursorPos.X) * RotateSpeed;
			relativeRotation.X -= (0.5f - CursorPos.Y) * RotateSpeed;

			// X < MaxVerticalAngle or X > 360-MaxVerticalAngle
			if (relativeRotation.X > MaxVerticalAngle*2 &&
				relativeRotation.X < 360.0f-MaxVerticalAngle)
			{
				relativeRotation.X = 360.0f-MaxVerticalAngle;
			}
			else
			if (relativeRotation.X > MaxVerticalAngle &&
				relativeRotation.X < 360.0f-MaxVerticalAngle)
			{
				relativeRotation.X = MaxVerticalAngle;
			}

			// reset cursor position
			CursorControl->setPosition(0.5f, 0.5f);

			CenterCursor = CursorControl->getRelativePosition();

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

	// Since a FPS control works by changing the position of the character
	// with respect to his local frame, we calculate here the change in position
	// of the node in his local frame
	float deltaZ = 0;
	if( CursorKeys[EKA_MOVE_FORWARD] )
	    deltaZ += timeDiff * MoveSpeed;
	if( CursorKeys[EKA_MOVE_BACKWARD])
	    deltaZ -= timeDiff * MoveSpeed;

	float deltaX = 0;
	if( CursorKeys[EKA_STRAFE_RIGHT])
	    deltaX += timeDiff * MoveSpeed;
	if( CursorKeys[EKA_STRAFE_LEFT])
	    deltaX -= timeDiff * MoveSpeed;

	// and then we put a new empty node in that position, by making it a child
	// of the node we are animating, and putting it at the relative position
	// we just calculated
	scene::ISceneNode *target = smgr->addEmptySceneNode(node);
    target->setPosition(core::vector3df(deltaX,0,deltaZ));

    // then we calculate the global position of this empty node
	target->updateAbsolutePosition();

	// then we can calculate the incremental positino change of the node we
	// are animating by subtracting the global position of the node we are
	// animating from the global position of that empty node we just created
	core::vector3df deltaPos = target->getAbsolutePosition() -
                                    node->getAbsolutePosition();

	// then we can tell the animated node exactly how far to move
	node->setPosition(node->getPosition() + deltaPos);

	// and the rotation is just set with the new relative rotation
	node->setRotation(relativeRotation);

	// and we can now remove that empty scene node since it was just a temporary
	// way for us to easily do the linear algebra
	target->remove();
}


void CSceneNodeAnimatorFPS::allKeysUp()
{
	for (u32 i=0; i<6; ++i)
		CursorKeys[i] = false;
}


void CSceneNodeAnimatorFPS::setRotateSpeed(f32 speed)
{
	RotateSpeed = speed;
}


void CSceneNodeAnimatorFPS::setMoveSpeed(f32 speed)
{
	MoveSpeed = speed;
}


f32 CSceneNodeAnimatorFPS::getRotateSpeed() const
{
	return RotateSpeed;
}


f32 CSceneNodeAnimatorFPS::getMoveSpeed() const
{
	return MoveSpeed;
}


void CSceneNodeAnimatorFPS::setKeyMap(SKeyMap *map, u32 count)
{
	// clear the keymap
	KeyMap.clear();

	// add actions
	for (u32 i=0; i<count; ++i)
	{
		switch(map[i].Action)
		{
		case EKA_MOVE_FORWARD: KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, map[i].KeyCode));
			break;
		case EKA_MOVE_BACKWARD: KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, map[i].KeyCode));
			break;
		case EKA_STRAFE_LEFT: KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, map[i].KeyCode));
			break;
		case EKA_STRAFE_RIGHT: KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, map[i].KeyCode));
			break;
		case EKA_JUMP_UP: KeyMap.push_back(SCamKeyMap(EKA_JUMP_UP, map[i].KeyCode));
			break;
		default:
			break;
		}
	}
}


void CSceneNodeAnimatorFPS::setVerticalMovement(bool allow)
{
	NoVerticalMovement = !allow;
}


ISceneNodeAnimator* CSceneNodeAnimatorFPS::createClone(ISceneNode* node, ISceneManager* newManager)
{
	CSceneNodeAnimatorFPS * newAnimator =
		new CSceneNodeAnimatorFPS(smgr, CursorControl, RotateSpeed, MoveSpeed,
                                    JumpSpeed, 0, 0, NoVerticalMovement);
	newAnimator->setKeyMap(KeyMap);
	return newAnimator;
}


void CSceneNodeAnimatorFPS::setKeyMap(const core::array<SCamKeyMap>& keymap)
{
	KeyMap=keymap;
}



} // namespace scene
} // namespace irr


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

Post by cheshirekow »

And here's a simple demo of how to use it. The animator does not have to be the event receiver for the device, but it needs to be registered as an event receiver somewhere

Code: Select all

#include <irrlicht.h>
#include "scene/ISceneNodeAnimatorFPS.h"
#include "scene/CSceneNodeAnimatorFPS.h"

using namespace irr;

using namespace video;
using namespace core;
using namespace scene;


int main()
{
    //Initialise the engine
    IrrlichtDevice *device = createDevice(
      EDT_OPENGL,
      dimension2d<s32>(800,600),
      32,
      false,
      false,
      false,
      0);

    ISceneManager *smgr = device->getSceneManager();

    IVideoDriver *driver = device->getVideoDriver();


    //Load model
    IAnimatedMesh*          axes        = smgr->getMesh("models/axes/axes.3ds");
    IAnimatedMeshSceneNode* axes_node   = smgr->addAnimatedMeshSceneNode(axes);

    //Disable lighting (we've got no light)
    axes_node->setMaterialFlag(EMF_LIGHTING,false);
    axes_node->setScale(vector3df(0.05, 0.05, 0.05));

    ISceneNodeAnimator* fpsAnimator =
                new CSceneNodeAnimatorFPS(smgr, device->getCursorControl() );

    axes_node->addAnimator(fpsAnimator);
    device->setEventReceiver(fpsAnimator);

    // add a camera in the corner that looks at the center
    ICameraSceneNode* camera =
            smgr->addCameraSceneNode( 0, vector3df(5,5,-5), vector3df(0,0,0) );

    //Hide mouse
    device->getCursorControl()->setVisible(false);

    while(device->run())
    {
        driver->beginScene(true,true,SColor(0,100,100,100));
        smgr->drawAll();
        driver->endScene();
    }

    device->drop();
    delete fpsAnimator;

    return 0;
}

Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Seems interesting. But i don't get how do you control the objects like FPS an camera. I mean, how do they move, When you have an FPS camera, you have implicit the directions, up is "forward" down is "backwards" and so on. So, what happens when you press up for a node, for example? i hope this question doesn't sound too silly :)

-EDIT-
I've seen a possible optimization, which can come in handy, i haven't seen the code thorough, but i think you can save memory, method calls, and make the code safer, in case you lose sight of the empty scene node you create to update the position. Given that you only need a position and a rotation, why not do those transformations only with vectors?. Those structures are simpler, and need less than a whole scene node, even if empty
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Not at all.

So you're right, the concept of a "First Person" control is related to the perspective of the thing that is being controlled. Since you controls are relative to the camera's local coordinate frame. For instance "w" usually makes you go "forward". What I wanted was the ability to control a character using the same conceptual framework but without the Camera actually being the "first person".

To explain further, consider the industry standard reference frame for a camera with the origin at the top left of the image plane and x-axis going from left to right, and y-axis going from top to bottom. I.e. (0,0) means the top left corner, (1,0) is the top right corner, (0,1) is the bottom left corner, and (1,1) is the bottom right corner of the image plane. Using a right-handed coordinate system, this implies that the Z-axis is pointed "outward" from the camera's view.

Ok, then... A FPS style controller using this notation would be as follows:

Code: Select all

Keys
-----------
W:                "Forward" translation   = translation in the +Z direction
A:                "Left" translation      = translation in the -X direction
S:                "Backward" translation  = translation in the -Z direction
D:                "Right" translation     = translation in the +X direction

SPACE:            "Up" translation        = translation in the +Y direction
c:                "Down" translation      = translation in the -Y direction

Mouse
----------
away from user:   "Look up"               = + rotation about the X-axis
toward user:      "Look down"             = - rotation about the X-axis
to users left:    "Look left"             = - rotation about the Y-axis
to users right:   "Look right"            = + rotation about the Y-axis
(the "code" above is tabbed to make columns, if you can't see the columns, widen your browser's window)

Note that in the three columns in the table above, the second column is listed in "first person" nomenclature. The phrases only have meaning from the perspective of the camera. The second column lists their unambiguous mathematical representation, where the axis system is in the nodes local frame.

What I wanted was the ability to achieve the behavior of that third column for a particular node, even if there was no camera on that node for the "first person" idea to exactly make sense. The controlling a character becomes more difficult if the camera "third person", like in the example program, because you kind of have to imagine the view from the node. However, in a case where the kinematics of thing being controlled are constrained (i.e. it cannot rotate around the camera's axes, as is the case if the camera is mounted at the end of a boom) then we need the controller's atomic operations to be relative to an alternative reference frame.

This may also be useful in a game where you have the camera mounted to the front of a car or a spaceship, for example. You dont want an FPS controller centered at the camera, because then when you turn the camera, the whole ship would swing around behind the camera, which is not very realistic. Instead, you want the ship to rotate around it's own center of mass, and the camera to follow.

As you can see in my screenshot example, that was basically my problem. When I push my mouse forward, I want the character to rotate "up" but that will require the "front" camera to both rotate "up" as well as translate "up" and "backwards" because the rotation occurs around the center of the character, not the center of the camera.

I apologize for the esoteric language, this is the best way I know how to explain the reason for this. It may help to just run the sample program with a mesh of your own that has an obvious "front" and "top" and then get a feel for how the controller (animator) works. After that, if you still can't see the usefulness, you'll just have to know there is at least one person who wanted this :).
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

but i think you can save memory, method calls, and make the code safer, in case you lose sight of the empty scene node you create to update the position
The reason I did it this way was because there already is a function to do the math using vectors. That function is updateGlobalPosition(). It converts a node's local position, which is defined in its parent's reference frame, to a global position defined in the reference frame of the root scene node. Rather than doing all the math myself and possibly (probably) making an error, I did it this way. If you want to save memory you could save the empty scene node and reuse it later.

You are, of course, right about the optimization though. It would almost certainly be "faster" to do all the math right there in the code, but I cannot tell you how many hours I've spent debugging linear algebra in my codes so my philosophy is: if the math is already done, use it. So in that respect, I'm not sure it would be "safer".
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Hehe... it kind of remind me of the first Resident Evil interface :)

Still, i think there should be a posibility to obtain the absolute position of a node directly to store it within a vector, but i'm not sure. Looks cool! good work.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Hehe... it kind of remind me of the first Resident Evil interface
Yeah! Exactly! Actually this class can be used for a Resident Evil 3 type interface too. Just add this animator to the node for Leon's head with an empty key map, then add a camera to Leon's head node with a position of (0,0,-1) and you're done. Because the animator operates the head node, and since the camera is a child of the head node, when you use the mouse the head node will turn and the camera will swing around behind it!
Still, i think there should be a posibility to obtain the absolute position of a node directly to store it within a vector
I'm not sure what you mean by that. You can use getAbsolutePosition() to return the absolute position of a node as a vector. The problem isn't the absolute position, its finding the delta ( a change ) in the absolute position, expressed in absolute coordinates, given that the input is a change in position, expressed in local coordinates. What I'm avoiding is

Code: Select all

(PSEUDOCODE)
<Rotation Matrix>   R   = getRotationFromLocalToAbsolute()
<Vector>            T   = getPositionInAbsolute()

<Scalar>    strafeMovement  = keyIsDown( 'A' ) ? -moveSpeed : keyIsDown( 'D' ) ? moveSpeed : 0;
<Scalar>    forwardMovement = keyIsDown( 'S' ) ? -moveSpeed : keyIsDown( 'W' ) ? moveSpeed : 0;
<Vector>    deltaT = ( strafeMovement, 0, forwardMovement );

newPosition = T + R * deltaT
since I don't know how the transformations are encoded.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Ah!... i see... it would be great if the absolute rotation could be recovered in the form of a vector the same way as the absolute position, instead of a matrix.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Well there is a conceptual difficulty in doing that. What would that vector represent? The position is a three value quantity representating a translation along each of the three primary axes. Translation is closed under addition so if you add two translation vectors you get a third that is equal to the first translation, followed by the second.

However, the same is not true for rotations. There are two common ways to represent a rotation as a vector of three values. The first is euler angles which are three consecutive rotations about one of the three primary axes (i.e. roll, pitch, yaw). Euler angles aren't closed under any primary operator (addition or multiplication). In other words a (roll1, pitch1, yaw1) followed by a (roll2, pitch2, yaw2) is not equal to (roll1+roll2, pitch1+pitch2, yaw1+yaw2).

The other way to represent a rotation as a vector is as a "rotation vector". Any combination of rotations about any axis can be expressed as a single rotation about some axis (not necessarily the primary axes). A rotation vector is a vector that points in the direction of the axis that the rotation is taken about, and magnitude equal to the magnitude of rotation. Unfortunately, this group is also not closed under addition or multiplication.

Rotation matrices, however, are closed under multiplication. In other words if i have a rotation represented by R1, and another rotation represented by R2, then the matrix R2*R1 = R3 represents a rotation that is the result or the rotation R1 followed by the rotation R2.

Of course, if you work with a scene-graph, you don't have to do all that work usually. If you set up your scene-graph so that you only ever have to work in local coordinates, things get much easier, then you can just let the underlying system do all those rotations for you.
byllgrim
Posts: 9
Joined: Mon May 28, 2012 12:13 pm

Re: Arbitrary Scene Node FPS Animator

Post by byllgrim »

I spent hours trying to make a player character class that works independent of an event receiver, like the fps camera, and I think this helped me find a solution.
Post Reply