Camera Animator

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Camera Animator

Post by freezzo »

Hey guys.

I am working on a custom camera animator that will basically handle first and third person, and the transition between them. Does anyone know of any good resources that will help with how to go about this?

I have seen some of the other 1st and 3rd person cameras, but they don't use an animator, which is the route I want to go.

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

Post by cheshirekow »

I just posted this, it may help you figure out your problem. I created an animator for a scene node that had a camera attached to it. You could just as well use the animator with the camera itself.

http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=34567
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

This is awesome. Should help me out greatly...thanks!
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

And, for the record, the 1st person camera that is generated with createCameraSceneNodeFPS() does have an animator (thats where I got most of that code from). That animator can be found in the source in CSceneNodeAnimatorCameraFPS.h/.cpp
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

Oh even more nuggets of information! Thanks man. Helps tons!
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

Do you happen to know how to rotate camera based on current mouse position and not resetting it to the middle of the screen?

Alway, why does my cursor not hide when I set it to visible. I was some things about a bug in irrlicht and MS about it, is that true?
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

Do you happen to know how to rotate camera based on current mouse position and not resetting it to the middle of the screen?
Sure. You can still calculate the rotation using

Code: Select all

device->getCursorControl()->getRelativePosition();
but just don't do this afterward

Code: Select all

device->getCursorControl()->setPosition(0.5f, 0.5f);
Also, when you calculate the rotation, don't add it to the old rotation, just setRotation() with the one you calculated.

why does my cursor not hide when I set it to visible
If you want to hide it you should set visible to "false"; is that what you're doing?

Code: Select all

device->getCursorControl()->setVisible(false);
I was some things about a bug in irrlicht and MS about it, is that true
I'm working on windows and I don't have a problem with the cursor.
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

I can set it to false using the method you shown, but it doesnt disappear.

Here is my current implementation:


Code: Select all

#ifndef __CSCENENODECAMERAANIMATOR_H__
#define __CSCENENODECAMERAANIMATOR_H__

#include <iostream>
#include <irrlicht.h>

using namespace std;

namespace irr
{
	namespace gui
	{
		class ICursorControl;
	}

	namespace scene
	{
		class CSceneNodeCameraAnimator : public ISceneNodeAnimator
		{
		public:
			CSceneNodeCameraAnimator(gui::ICursorControl* cursorControl, 
				f32 rotateSpeed = 100.0f, f32 moveSpeed = .5f, f32 jumpSpeed=0.f,
				SKeyMap* keyMapArray=0, u32 keyMapSize=0, bool noVerticalMovement=false);

			virtual ~CSceneNodeCameraAnimator(void);
			virtual void animateNode(ISceneNode *node, u32 timeMs); 
			virtual bool OnEvent(const SEvent &); 

			virtual ISceneNodeAnimator *createClone (ISceneNode *node, ISceneManager *newManager=0)
			{
				return new CSceneNodeCameraAnimator(CursorControl);
			}

			virtual void setKeyMap(SKeyMap *map, u32 count);

			struct SCamKeyMap
			{
				SCamKeyMap() {};
				SCamKeyMap(s32 a, EKEY_CODE k) : action(a), keycode(k) {}

				s32 action;
				EKEY_CODE keycode;
			};

			virtual bool isEventReceiverEnabled() const
			{
				return true;
			}

			virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
			{
				return ESNAT_CAMERA_FPS;
			}

			void setKeyMap(const core::array<SCamKeyMap>& keymap);

		private:
			void allKeysUp();

			gui::ICursorControl *CursorControl;

			f32 MaxVerticalAngle;

			f32 MoveSpeed;
			f32 RotateSpeed;
			f32 JumpSpeed;

			s32 LastAnimationTime;

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

			bool CursorKeys[6];

			bool firstUpdate;
			bool NoVerticalMovement;

			bool leftMouseDown;
			bool rightMouseDown;
		};
	}
}

#endif
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

Code: Select all

#include "CSceneNodeCameraAnimator.h"

namespace irr
{
	namespace scene
	{

		CSceneNodeCameraAnimator::CSceneNodeCameraAnimator(gui::ICursorControl* cursorControl,
			f32 rotateSpeed, f32 moveSpeed, f32 jumpSpeed,
			SKeyMap* keyMapArray, u32 keyMapSize, bool noVerticalMovement)
			:	CursorControl(cursorControl), MaxVerticalAngle(88.0f),
				MoveSpeed(moveSpeed), RotateSpeed(rotateSpeed), JumpSpeed(jumpSpeed),
				LastAnimationTime(0), firstUpdate(true), NoVerticalMovement(noVerticalMovement)
		{
			if (CursorControl)
					CursorControl->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_KEY_J));
			}
			else
			{
				// create custom key map
				setKeyMap(keyMapArray, keyMapSize);
			}
		}

		CSceneNodeCameraAnimator::~CSceneNodeCameraAnimator(void)
		{
			if (CursorControl)
				CursorControl->drop();
		}

		void CSceneNodeCameraAnimator::animateNode(ISceneNode *node, u32 timeMs)
		{
			if (node->getType() != ESNT_CAMERA)
				return;

			ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);

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

				LastAnimationTime = timeMs;

				firstUpdate = false;
				leftMouseDown = false;
				rightMouseDown = false;
			}

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

			// update position
			core::vector3df pos = camera->getPosition();

			// Update rotation
			core::vector3df target = (camera->getTarget() - camera->getAbsolutePosition());
			core::vector3df relativeRotation = target.getHorizontalAngle();

			if (CursorControl && rightMouseDown)
			{
				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 ecent receiver is
					// disabled
					CursorPos = CenterCursor;
				}
			}

			// set target
			target.set(0,0, core::max_(1.f, pos.getLength()));
			core::vector3df movedir = target;

			core::matrix4 mat;
			mat.setRotationDegrees(core::vector3df(relativeRotation.X, relativeRotation.Y, 0));
			mat.transformVect(target);

			if (NoVerticalMovement)
			{
				mat.setRotationDegrees(core::vector3df(0, relativeRotation.Y, 0));
				mat.transformVect(movedir);
			}
			else
			{
				movedir = target; 
			}

			movedir.normalize();

			if (CursorKeys[EKA_MOVE_FORWARD])
				pos += movedir * timeDiff * MoveSpeed;

			if (CursorKeys[EKA_MOVE_BACKWARD])
				pos -= movedir * timeDiff * MoveSpeed;

			// strafing
			core::vector3df strafevect = target;
			strafevect = strafevect.crossProduct(camera->getUpVector());

			if (NoVerticalMovement)
				strafevect.Y = 0.0f;

			strafevect.normalize();

			if (CursorKeys[EKA_STRAFE_LEFT])
				pos += strafevect * timeDiff * MoveSpeed;

			if (CursorKeys[EKA_STRAFE_RIGHT])
				pos -= strafevect * timeDiff * MoveSpeed;

			// For jumping, we find the collision response animator attached to our camera
			// and if it's not falling, we tell it to jump.
			if (CursorKeys[EKA_JUMP_UP])
			{
				const core::list<ISceneNodeAnimator*> & animators = camera->getAnimators();
				core::list<ISceneNodeAnimator*>::ConstIterator it = animators.begin();
				while(it != animators.end())
				{
					if(ESNAT_COLLISION_RESPONSE == (*it)->getType())
					{
						ISceneNodeAnimatorCollisionResponse * collisionResponse = 
							static_cast<ISceneNodeAnimatorCollisionResponse *>(*it);

						if(!collisionResponse->isFalling())
							collisionResponse->jump(JumpSpeed);
					}

					it++;
				}
			}

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

			// write right target

			TargetVector = target;
			target += pos;
			camera->setTarget(target);
		}

		bool CSceneNodeCameraAnimator::OnEvent(const SEvent &event)
		{
			if (event.EventType == EET_KEY_INPUT_EVENT)
			{
				for (u32 i=0; i<KeyMap.size(); ++i)
				{
					if (KeyMap[i].keycode == event.KeyInput.Key)
					{
						CursorKeys[KeyMap[i].action] = event.KeyInput.PressedDown;
						return true;
					}
				}
			}
			if (event.EventType == EET_MOUSE_INPUT_EVENT)
			{
				switch (event.MouseInput.Event)
				{
				case EMIE_MOUSE_WHEEL:  // zooming

				break;

				case EMIE_LMOUSE_PRESSED_DOWN:
					leftMouseDown = true;
				break;

				case EMIE_RMOUSE_PRESSED_DOWN:
					//OldCursor = CursorControl->getRelativePosition();
					rightMouseDown = true;
				break;

				case EMIE_MOUSE_MOVED:
					CursorPos = CursorControl->getRelativePosition();
					return true;
				break;

				case EMIE_MMOUSE_PRESSED_DOWN:                           
				break;

				case EMIE_LMOUSE_LEFT_UP:
					leftMouseDown = false;
				break;

				case EMIE_RMOUSE_LEFT_UP:
					//CursorControl->setPosition( OldCursor );
					rightMouseDown = false;
				break;

				}
			}
			return false;
		}

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

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

Post by cheshirekow »

Can you also post your main() that you're using to test it?
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

The main is a normal main. I do not have the code in front of moment but it goes like this (very psuedo-ish):

Code: Select all

camera = new CameraSceneNode(...);
animator = new MyAnimator(...);

camera->addAnimator(animator);
animator->drop();

while(device->run())
{
    driver->beginscene(...);
    smgr->drawAll();
    driver->endscene(...);
}
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

The reason I ask is that you don't have a call to setVisible(false); in the code that you showed, so it's probably in your main(). If there is a problem in your code, I imagine it is somewhere around there.
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

Oh sorry, I took the call out of my code.

Originally I had it here:

Code: Select all

            case EMIE_RMOUSE_LEFT_UP:
               CursorControl->setVisible(true);
               //CursorControl->setPosition( OldCursor );
               rightMouseDown = false;
            break; 


            case EMIE_RMOUSE_PRESSED_DOWN:
               CursorControl->setVisible(false);
               //OldCursor = CursorControl->getRelativePosition();
               rightMouseDown = true;
            break; 
This would have no effect, but if i put an additional call in the main, the wouldn't show up to start. Once I held down the right mouse key though, it would appear, even when telling it not to.
cheshirekow
Posts: 105
Joined: Mon Jul 27, 2009 4:06 pm
Location: Cambridge, MA

Post by cheshirekow »

So it seems the setVisible() method is probably working as expected, and the problem lies somewhere else. What happens if you just remove the "CursorControl->setVisible(true)" from the "case EMIE_RMOUSE_LEFT_UP" block. I imagine it will stay hidden.

Once you try the above, but the statement to make it visible back in, then remove the "CursorConstrol->setVisible(false)" from the "case EMIE_RMOUSE_PRESSED_DOWN" block. Then run your program and pay careful attention to when the cursor appears. When you click down with the right mouse button, does it stay hidden until you lift up? or does it become visible as soon as you click down?
freezzo
Posts: 27
Joined: Thu Mar 08, 2007 6:36 pm
Contact:

Post by freezzo »

Alright, I will have to try that out when I get home tonight. You have been most helpful with everything, much appreciated!
Post Reply