1
// Copyright (C) 2002-2011 Nikolaus Gebhardt
2
// This file is part of the "Irrlicht Engine".
3
// For conditions of distribution and use, see copyright notice in irrlicht.h
5
#include "CSceneNodeAnimatorCameraFPS.h"
6
#include "IVideoDriver.h"
7
#include "ISceneManager.h"
9
#include "ICursorControl.h"
10
#include "ICameraSceneNode.h"
11
#include "ISceneNodeAnimatorCollisionResponse.h"
19
CSceneNodeAnimatorCameraFPS::CSceneNodeAnimatorCameraFPS(gui::ICursorControl* cursorControl,
20
f32 rotateSpeed, f32 moveSpeed, f32 jumpSpeed,
21
SKeyMap* keyMapArray, u32 keyMapSize, bool noVerticalMovement, bool invertY)
22
: CursorControl(cursorControl), MaxVerticalAngle(88.0f),
23
MoveSpeed(moveSpeed), RotateSpeed(rotateSpeed), JumpSpeed(jumpSpeed),
24
MouseYDirection(invertY ? -1.0f : 1.0f),
25
LastAnimationTime(0), firstUpdate(true), NoVerticalMovement(noVerticalMovement)
28
setDebugName("CCameraSceneNodeAnimatorFPS");
32
CursorControl->grab();
37
if (!keyMapArray || !keyMapSize)
39
// create default key map
40
KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, irr::KEY_UP));
41
KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, irr::KEY_DOWN));
42
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, irr::KEY_LEFT));
43
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, irr::KEY_RIGHT));
44
KeyMap.push_back(SCamKeyMap(EKA_JUMP_UP, irr::KEY_KEY_J));
48
// create custom key map
49
setKeyMap(keyMapArray, keyMapSize);
55
CSceneNodeAnimatorCameraFPS::~CSceneNodeAnimatorCameraFPS()
58
CursorControl->drop();
62
//! It is possible to send mouse and key events to the camera. Most cameras
63
//! may ignore this input, but camera scene nodes which are created for
64
//! example with scene::ISceneManager::addMayaCameraSceneNode or
65
//! scene::ISceneManager::addFPSCameraSceneNode, may want to get this input
66
//! for changing their position, look at target or whatever.
67
bool CSceneNodeAnimatorCameraFPS::OnEvent(const SEvent& evt)
71
case EET_KEY_INPUT_EVENT:
72
for (u32 i=0; i<KeyMap.size(); ++i)
74
if (KeyMap[i].keycode == evt.KeyInput.Key)
76
CursorKeys[KeyMap[i].action] = evt.KeyInput.PressedDown;
82
case EET_MOUSE_INPUT_EVENT:
83
if (evt.MouseInput.Event == EMIE_MOUSE_MOVED)
85
CursorPos = CursorControl->getRelativePosition();
98
void CSceneNodeAnimatorCameraFPS::animateNode(ISceneNode* node, u32 timeMs)
100
if (!node || node->getType() != ESNT_CAMERA)
103
ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);
107
camera->updateAbsolutePosition();
108
if (CursorControl && camera)
110
CursorControl->setPosition(0.5f, 0.5f);
111
CursorPos = CenterCursor = CursorControl->getRelativePosition();
114
LastAnimationTime = timeMs;
119
// If the camera isn't the active camera, and receiving input, then don't process it.
120
if(!camera->isInputReceiverEnabled())
123
scene::ISceneManager * smgr = camera->getSceneManager();
124
if(smgr && smgr->getActiveCamera() != camera)
128
f32 timeDiff = (f32) ( timeMs - LastAnimationTime );
129
LastAnimationTime = timeMs;
132
core::vector3df pos = camera->getPosition();
135
core::vector3df target = (camera->getTarget() - camera->getAbsolutePosition());
136
core::vector3df relativeRotation = target.getHorizontalAngle();
140
if (CursorPos != CenterCursor)
142
relativeRotation.Y -= (0.5f - CursorPos.X) * RotateSpeed;
143
relativeRotation.X -= (0.5f - CursorPos.Y) * RotateSpeed * MouseYDirection;
145
// X < MaxVerticalAngle or X > 360-MaxVerticalAngle
147
if (relativeRotation.X > MaxVerticalAngle*2 &&
148
relativeRotation.X < 360.0f-MaxVerticalAngle)
150
relativeRotation.X = 360.0f-MaxVerticalAngle;
153
if (relativeRotation.X > MaxVerticalAngle &&
154
relativeRotation.X < 360.0f-MaxVerticalAngle)
156
relativeRotation.X = MaxVerticalAngle;
159
// Do the fix as normal, special case below
160
// reset cursor position to the centre of the window.
161
CursorControl->setPosition(0.5f, 0.5f);
162
CenterCursor = CursorControl->getRelativePosition();
164
// needed to avoid problems when the event receiver is disabled
165
CursorPos = CenterCursor;
168
// Special case, mouse is whipped outside of window before it can update.
169
video::IVideoDriver* driver = smgr->getVideoDriver();
170
core::vector2d<u32> mousepos(u32(CursorControl->getPosition().X), u32(CursorControl->getPosition().Y));
171
core::rect<u32> screenRect(0, 0, driver->getScreenSize().Width, driver->getScreenSize().Height);
173
// Only if we are moving outside quickly.
174
bool reset = !screenRect.isPointInside(mousepos);
179
CursorControl->setPosition(0.5f, 0.5f);
180
CenterCursor = CursorControl->getRelativePosition();
181
CursorPos = CenterCursor;
187
target.set(0,0, core::max_(1.f, pos.getLength()));
188
core::vector3df movedir = target;
191
mat.setRotationDegrees(core::vector3df(relativeRotation.X, relativeRotation.Y, 0));
192
mat.transformVect(target);
194
if (NoVerticalMovement)
196
mat.setRotationDegrees(core::vector3df(0, relativeRotation.Y, 0));
197
mat.transformVect(movedir);
206
if (CursorKeys[EKA_MOVE_FORWARD])
207
pos += movedir * timeDiff * MoveSpeed;
209
if (CursorKeys[EKA_MOVE_BACKWARD])
210
pos -= movedir * timeDiff * MoveSpeed;
214
core::vector3df strafevect = target;
215
strafevect = strafevect.crossProduct(camera->getUpVector());
217
if (NoVerticalMovement)
220
strafevect.normalize();
222
if (CursorKeys[EKA_STRAFE_LEFT])
223
pos += strafevect * timeDiff * MoveSpeed;
225
if (CursorKeys[EKA_STRAFE_RIGHT])
226
pos -= strafevect * timeDiff * MoveSpeed;
228
// For jumping, we find the collision response animator attached to our camera
229
// and if it's not falling, we tell it to jump.
230
if (CursorKeys[EKA_JUMP_UP])
232
const ISceneNodeAnimatorList& animators = camera->getAnimators();
233
ISceneNodeAnimatorList::ConstIterator it = animators.begin();
234
while(it != animators.end())
236
if(ESNAT_COLLISION_RESPONSE == (*it)->getType())
238
ISceneNodeAnimatorCollisionResponse * collisionResponse =
239
static_cast<ISceneNodeAnimatorCollisionResponse *>(*it);
241
if(!collisionResponse->isFalling())
242
collisionResponse->jump(JumpSpeed);
250
camera->setPosition(pos);
252
// write right target
254
camera->setTarget(target);
258
void CSceneNodeAnimatorCameraFPS::allKeysUp()
260
for (u32 i=0; i<6; ++i)
261
CursorKeys[i] = false;
265
//! Sets the rotation speed
266
void CSceneNodeAnimatorCameraFPS::setRotateSpeed(f32 speed)
272
//! Sets the movement speed
273
void CSceneNodeAnimatorCameraFPS::setMoveSpeed(f32 speed)
279
//! Gets the rotation speed
280
f32 CSceneNodeAnimatorCameraFPS::getRotateSpeed() const
286
// Gets the movement speed
287
f32 CSceneNodeAnimatorCameraFPS::getMoveSpeed() const
293
//! Sets the keyboard mapping for this animator
294
void CSceneNodeAnimatorCameraFPS::setKeyMap(SKeyMap *map, u32 count)
300
for (u32 i=0; i<count; ++i)
302
switch(map[i].Action)
304
case EKA_MOVE_FORWARD: KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, map[i].KeyCode));
306
case EKA_MOVE_BACKWARD: KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, map[i].KeyCode));
308
case EKA_STRAFE_LEFT: KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, map[i].KeyCode));
310
case EKA_STRAFE_RIGHT: KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, map[i].KeyCode));
312
case EKA_JUMP_UP: KeyMap.push_back(SCamKeyMap(EKA_JUMP_UP, map[i].KeyCode));
321
//! Sets whether vertical movement should be allowed.
322
void CSceneNodeAnimatorCameraFPS::setVerticalMovement(bool allow)
324
NoVerticalMovement = !allow;
328
//! Sets whether the Y axis of the mouse should be inverted.
329
void CSceneNodeAnimatorCameraFPS::setInvertMouse(bool invert)
332
MouseYDirection = -1.0f;
334
MouseYDirection = 1.0f;
338
ISceneNodeAnimator* CSceneNodeAnimatorCameraFPS::createClone(ISceneNode* node, ISceneManager* newManager)
340
CSceneNodeAnimatorCameraFPS * newAnimator =
341
new CSceneNodeAnimatorCameraFPS(CursorControl, RotateSpeed, MoveSpeed, JumpSpeed,
342
0, 0, NoVerticalMovement);
343
newAnimator->setKeyMap(KeyMap);
348
void CSceneNodeAnimatorCameraFPS::setKeyMap(const core::array<SCamKeyMap>& keymap)