in the FPS animator, the cursor position is always rest to 0.5,0.5 (center of the window). in my case, the rendering area may be only a small area let's say rect(30,30,300,300) for example. To map the cursor position correctly, I use the following code which adds two variables (m_CursorOffsetX and m_CursorOffsetY), adds a function to set those variables each frame if needed (void setCursorOffset(float x, float y)) and modifies the void CSSceneNodeAnimatorFPS::animateNode(ISceneNode* node, u32 timeMs) function to utilize the offsets instead of assuming 0.5,0.5 for the center of the screen.
I still need to tweak it (for example it checks the entire window for mouse quick moves) but it works for now.
it is used like this in my case
Code: Select all
virtual bool setLevel(CSLevel* level)
{
CS_INIT(m_Level);
m_Level = level;
if (m_Level)
{
irr::SKeyMap fpsControls[5];
fpsControls[0].Action = EKA_MOVE_FORWARD;
fpsControls[0].KeyCode = KEY_KEY_W;
fpsControls[1].Action = EKA_MOVE_BACKWARD;
fpsControls[1].KeyCode = KEY_KEY_S;
fpsControls[2].Action = EKA_STRAFE_LEFT;
fpsControls[2].KeyCode = KEY_KEY_A;
fpsControls[3].Action = EKA_STRAFE_RIGHT;
fpsControls[3].KeyCode = KEY_KEY_D;
m_Camera = m_Level->getSmgr()->addCameraSceneNode();
m_CameraAnimator = new SSceneNodeAnimatorFPS(m_Device->getCursorControl(),100.0f,0.5f,0.0f,fpsControls,5,false,false);
m_Camera->addAnimator(m_CameraAnimator);
m_Camera->setInputReceiverEnabled(false);
m_Level->getSmgr()->setActiveCamera(m_Camera);
m_Level->setCamera(m_Camera);
return true;
}
return false;
}
Code: Select all
// rendering functions
virtual void drawClientArea()
{
m_isActive = true;
// if the window is large enough to actually see something
if ((m_ClientRect.getWidth() > 20) && (m_ClientRect.getHeight() > 20))
{
// if the level and camera are valid
if ( (getLevel()) && (m_Camera))
{
// the client rectangle of the rendering window
const rect<s32> r = m_ClientRect;
// remember the old viewport so tht we can restore it
rect<s32> oldViewport = getDesktop()->m_Device->getVideoDriver()->getViewPort();
// set the aspect ration to our current rendering area
m_Camera->setAspectRatio((f32)m_ClientRect.getWidth() / (f32)m_ClientRect.getHeight());
// make sure we are using our camera as the active camera
getLevel()->getSmgr()->setActiveCamera(m_Camera);
// set the viewport for our rendering window
getDesktop()->m_Device->getVideoDriver()->setViewPort(r);
// render the smgr or whatever we use for scene rendering (could be XEffects for example)
if (m_Level) m_Level->render();
// restore the old viewport
getDesktop()->m_Device->getVideoDriver()->setViewPort(oldViewport);
// get the center of the window
vector2d<f32> center1;
center1.X = (float)r.getCenter().X / oldViewport.getWidth(); // assume old viewport is entire screen (could use driver screensize instead)
center1.Y = (float)r.getCenter().Y / oldViewport.getHeight(); // assume old viewport is entire screen (could use driver screensize instead)
// let the camera animator know the center of our rendering window for cursor control correctness
if (m_CameraAnimator) m_CameraAnimator->setCursorOffset(center1.X, center1.Y);
}
else
{
CSGUI_Window::drawClientArea();
}
}
}
Code: Select all
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#ifndef __CS_SCENE_NODE_ANIMATOR_CAMERA_FPS_H_INCLUDED__
#define __CS_SCENE_NODE_ANIMATOR_CAMERA_FPS_H_INCLUDED__
#include "ISceneNodeAnimatorCameraFPS.h"
#include "vector2d.h"
#include "position2d.h"
#include "SKeyMap.h"
#include "irrArray.h"
namespace irr
{
namespace gui
{
class ICursorControl;
}
namespace scene
{
//! Special scene node animator for FPS cameras
class CSSceneNodeAnimatorFPS : public ISceneNodeAnimatorCameraFPS
{
public:
//! Constructor
CSSceneNodeAnimatorFPS(gui::ICursorControl* cursorControl,
f32 rotateSpeed = 100.0f, f32 moveSpeed = .5f, f32 jumpSpeed = 0.f,
SKeyMap* keyMapArray = 0, u32 keyMapSize = 0, bool noVerticalMovement = false,
bool invertY = false);
//! Destructor
virtual ~CSSceneNodeAnimatorFPS();
//! Animates the scene node, currently only works on cameras
virtual void animateNode(ISceneNode* node, u32 timeMs);
//! Event receiver
virtual bool OnEvent(const SEvent& event);
//! Returns the speed of movement in units per second
virtual f32 getMoveSpeed() const;
//! Sets the speed of movement in units per second
virtual void setMoveSpeed(f32 moveSpeed);
//! Returns the rotation speed
virtual f32 getRotateSpeed() const;
//! Set the rotation speed
virtual void setRotateSpeed(f32 rotateSpeed);
//! Sets the keyboard mapping for this animator (old style)
//! \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);
//! Sets the keyboard mapping for this animator
//! \param keymap The new keymap array
virtual void setKeyMap(const core::array<SKeyMap>& keymap);
//! Gets the keyboard mapping for this animator
virtual const core::array<SKeyMap>& getKeyMap() const;
//! Sets whether vertical movement should be allowed.
virtual void setVerticalMovement(bool allow);
//! Sets whether the Y axis of the mouse should be inverted.
/** If enabled then moving the mouse down will cause
the camera to look up. It is disabled by default. */
virtual void setInvertMouse(bool invert);
//! This animator will receive events when attached to the active camera
virtual bool isEventReceiverEnabled() const
{
return true;
}
//! Returns the type of this animator
virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
{
return ESNAT_CAMERA_FPS;
}
//! Creates a clone of this animator.
/** Please note that you will have to drop
(IReferenceCounted::drop()) the returned pointer once you're
done with it. */
virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager = 0);
void setCursorOffset(float x, float y)
{
m_CursorOffsetX = x;
m_CursorOffsetY = y;
}
private:
void allKeysUp();
gui::ICursorControl *CursorControl;
float m_CursorOffsetX;
float m_CursorOffsetY;
f32 MaxVerticalAngle;
f32 MoveSpeed;
f32 RotateSpeed;
f32 JumpSpeed;
// -1.0f for inverted mouse, defaults to 1.0f
f32 MouseYDirection;
s32 LastAnimationTime;
core::array<SKeyMap> KeyMap;
core::position2d<f32> CenterCursor, CursorPos;
bool CursorKeys[EKA_COUNT];
bool firstUpdate;
bool firstInput;
bool NoVerticalMovement;
};
} // end namespace scene
} // end namespace irr
#endif // __CS_SCENE_NODE_ANIMATOR_CAMERA_FPS_H_INCLUDED__
Code: Select all
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "stdafx.h"
#include "CSSceneNodeAnimatorFPS.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "Keycodes.h"
#include "ICursorControl.h"
#include "ICameraSceneNode.h"
#include "ISceneNodeAnimatorCollisionResponse.h"
namespace irr
{
namespace scene
{
//! constructor
CSSceneNodeAnimatorFPS::CSSceneNodeAnimatorFPS(gui::ICursorControl* cursorControl,
f32 rotateSpeed, f32 moveSpeed, f32 jumpSpeed,
SKeyMap* keyMapArray, u32 keyMapSize, bool noVerticalMovement, bool invertY)
: CursorControl(cursorControl), MaxVerticalAngle(88.0f),
MoveSpeed(moveSpeed), RotateSpeed(rotateSpeed), JumpSpeed(jumpSpeed),
MouseYDirection(invertY ? -1.0f : 1.0f),
LastAnimationTime(0), firstUpdate(true), firstInput(true), NoVerticalMovement(noVerticalMovement)
{
#ifdef _DEBUG
setDebugName("CCameraSceneNodeAnimatorFPS");
#endif
if (CursorControl)
CursorControl->grab();
allKeysUp();
// create key map
if (!keyMapArray || !keyMapSize)
{
// create default key map
KeyMap.push_back(SKeyMap(EKA_MOVE_FORWARD, irr::KEY_UP));
KeyMap.push_back(SKeyMap(EKA_MOVE_BACKWARD, irr::KEY_DOWN));
KeyMap.push_back(SKeyMap(EKA_STRAFE_LEFT, irr::KEY_LEFT));
KeyMap.push_back(SKeyMap(EKA_STRAFE_RIGHT, irr::KEY_RIGHT));
KeyMap.push_back(SKeyMap(EKA_JUMP_UP, irr::KEY_KEY_J));
}
else
{
// create custom key map
setKeyMap(keyMapArray, keyMapSize);
}
}
//! destructor
CSSceneNodeAnimatorFPS::~CSSceneNodeAnimatorFPS()
{
if (CursorControl)
CursorControl->drop();
}
//! It is possible to send mouse and key events to the camera. Most cameras
//! may ignore this input, but camera scene nodes which are created for
//! example with scene::ISceneManager::addMayaCameraSceneNode or
//! scene::ISceneManager::addFPSCameraSceneNode, may want to get this input
//! for changing their position, look at target or whatever.
bool CSSceneNodeAnimatorFPS::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 CSSceneNodeAnimatorFPS::animateNode(ISceneNode* node, u32 timeMs)
{
if (!node || node->getType() != ESNT_CAMERA)
return;
ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);
if (firstUpdate)
{
camera->updateAbsolutePosition();
if (CursorControl)
{
CursorControl->setPosition(m_CursorOffsetX, m_CursorOffsetY);
CursorPos = CenterCursor = CursorControl->getRelativePosition();
}
LastAnimationTime = timeMs;
firstUpdate = false;
}
// If the camera isn't the active camera, and receiving input, then don't process it.
if (!camera->isInputReceiverEnabled())
{
firstInput = true;
return;
}
if (firstInput)
{
allKeysUp();
firstInput = false;
}
scene::ISceneManager * smgr = camera->getSceneManager();
if (smgr && smgr->getActiveCamera() != camera)
return;
// 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)
{
if (CursorPos != CenterCursor)
{
relativeRotation.Y -= (m_CursorOffsetX - CursorPos.X) * RotateSpeed;
relativeRotation.X -= (m_CursorOffsetY - CursorPos.Y) * RotateSpeed * MouseYDirection;
// 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;
}
// Do the fix as normal, special case below
// reset cursor position to the centre of the window.
CursorControl->setPosition(m_CursorOffsetX, m_CursorOffsetY);
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(m_CursorOffsetX, m_CursorOffsetY);
CenterCursor = CursorControl->getRelativePosition();
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 ISceneNodeAnimatorList& animators = camera->getAnimators();
ISceneNodeAnimatorList::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
target += pos;
camera->setTarget(target);
}
void CSSceneNodeAnimatorFPS::allKeysUp()
{
for (u32 i = 0; i<EKA_COUNT; ++i)
CursorKeys[i] = false;
}
//! Sets the rotation speed
void CSSceneNodeAnimatorFPS::setRotateSpeed(f32 speed)
{
RotateSpeed = speed;
}
//! Sets the movement speed
void CSSceneNodeAnimatorFPS::setMoveSpeed(f32 speed)
{
MoveSpeed = speed;
}
//! Gets the rotation speed
f32 CSSceneNodeAnimatorFPS::getRotateSpeed() const
{
return RotateSpeed;
}
// Gets the movement speed
f32 CSSceneNodeAnimatorFPS::getMoveSpeed() const
{
return MoveSpeed;
}
//! Sets the keyboard mapping for this animator
void CSSceneNodeAnimatorFPS::setKeyMap(SKeyMap *map, u32 count)
{
// clear the keymap
KeyMap.clear();
// add actions
for (u32 i = 0; i<count; ++i)
{
KeyMap.push_back(map[i]);
}
}
void CSSceneNodeAnimatorFPS::setKeyMap(const core::array<SKeyMap>& keymap)
{
KeyMap = keymap;
}
const core::array<SKeyMap>& CSSceneNodeAnimatorFPS::getKeyMap() const
{
return KeyMap;
}
//! Sets whether vertical movement should be allowed.
void CSSceneNodeAnimatorFPS::setVerticalMovement(bool allow)
{
NoVerticalMovement = !allow;
}
//! Sets whether the Y axis of the mouse should be inverted.
void CSSceneNodeAnimatorFPS::setInvertMouse(bool invert)
{
if (invert)
MouseYDirection = -1.0f;
else
MouseYDirection = 1.0f;
}
ISceneNodeAnimator* CSSceneNodeAnimatorFPS::createClone(ISceneNode* node, ISceneManager* newManager)
{
CSSceneNodeAnimatorFPS * newAnimator =
new CSSceneNodeAnimatorFPS(CursorControl, RotateSpeed, MoveSpeed, JumpSpeed,
0, 0, NoVerticalMovement);
newAnimator->setKeyMap(KeyMap);
return newAnimator;
}
} // namespace scene
} // namespace irr