301 lines
7.3 KiB
C++
301 lines
7.3 KiB
C++
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CCameraFPSSceneNode.h"
|
|
#include "IVideoDriver.h"
|
|
#include "ISceneManager.h"
|
|
#include "os.h"
|
|
#include "Keycodes.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
const f32 MAX_VERTICAL_ANGLE = 88.0f;
|
|
|
|
//! constructor
|
|
CCameraFPSSceneNode::CCameraFPSSceneNode(ISceneNode* parent, ISceneManager* mgr,
|
|
gui::ICursorControl* cursorControl, s32 id, f32 rotateSpeed , f32 moveSpeed,f32 jumpSpeed,
|
|
SKeyMap* keyMapArray, s32 keyMapSize, bool noVerticalMovement)
|
|
: CCameraSceneNode(parent, mgr, id), CursorControl(cursorControl),
|
|
MoveSpeed(moveSpeed), RotateSpeed(rotateSpeed), JumpSpeed(jumpSpeed),
|
|
firstUpdate(true), LastAnimationTime(0), NoVerticalMovement(noVerticalMovement)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CCameraFPSSceneNode");
|
|
#endif
|
|
|
|
if (CursorControl)
|
|
CursorControl->grab();
|
|
|
|
MoveSpeed /= 1000.0f;
|
|
|
|
recalculateViewArea();
|
|
|
|
allKeysUp();
|
|
|
|
// create key map
|
|
if (!keyMapArray || !keyMapSize)
|
|
{
|
|
// create default key map
|
|
KeyMap.push_back(SCamKeyMap(0, irr::KEY_UP));
|
|
KeyMap.push_back(SCamKeyMap(1, irr::KEY_DOWN));
|
|
KeyMap.push_back(SCamKeyMap(2, irr::KEY_LEFT));
|
|
KeyMap.push_back(SCamKeyMap(3, irr::KEY_RIGHT));
|
|
KeyMap.push_back(SCamKeyMap(4, irr::KEY_KEY_J));
|
|
}
|
|
else
|
|
{
|
|
// create custom key map
|
|
|
|
for (s32 i=0; i<keyMapSize; ++i)
|
|
{
|
|
switch(keyMapArray[i].Action)
|
|
{
|
|
case EKA_MOVE_FORWARD: KeyMap.push_back(SCamKeyMap(0, keyMapArray[i].KeyCode));
|
|
break;
|
|
case EKA_MOVE_BACKWARD: KeyMap.push_back(SCamKeyMap(1, keyMapArray[i].KeyCode));
|
|
break;
|
|
case EKA_STRAFE_LEFT: KeyMap.push_back(SCamKeyMap(2, keyMapArray[i].KeyCode));
|
|
break;
|
|
case EKA_STRAFE_RIGHT: KeyMap.push_back(SCamKeyMap(3, keyMapArray[i].KeyCode));
|
|
break;
|
|
case EKA_JUMP_UP: KeyMap.push_back(SCamKeyMap(4, keyMapArray[i].KeyCode));
|
|
break;
|
|
default:
|
|
break;
|
|
} // end switch
|
|
} // end for
|
|
}// end if
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CCameraFPSSceneNode::~CCameraFPSSceneNode()
|
|
{
|
|
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 CCameraFPSSceneNode::OnEvent(const SEvent& event)
|
|
{
|
|
if (event.EventType == EET_KEY_INPUT_EVENT)
|
|
{
|
|
const u32 cnt = KeyMap.size();
|
|
for (u32 i=0; i<cnt; ++i)
|
|
if (KeyMap[i].keycode == event.KeyInput.Key)
|
|
{
|
|
CursorKeys[KeyMap[i].action] = event.KeyInput.PressedDown;
|
|
|
|
if ( InputReceiverEnabled )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//! OnAnimate() is called just before rendering the whole scene.
|
|
//! nodes may calculate or store animations here, and may do other useful things,
|
|
//! dependent on what they are.
|
|
void CCameraFPSSceneNode::OnAnimate(u32 timeMs)
|
|
{
|
|
animate( timeMs );
|
|
|
|
core::list<ISceneNodeAnimator*>::Iterator ait = Animators.begin();
|
|
for (; ait != Animators.end(); ++ait)
|
|
(*ait)->animateNode(this, timeMs);
|
|
|
|
updateAbsolutePosition();
|
|
Target = getPosition() + TargetVector;
|
|
|
|
core::list<ISceneNode*>::Iterator it = Children.begin();
|
|
for (; it != Children.end(); ++it)
|
|
(*it)->OnAnimate(timeMs);
|
|
}
|
|
|
|
|
|
void CCameraFPSSceneNode::animate( u32 timeMs )
|
|
{
|
|
const u32 camIsMe = SceneManager->getActiveCamera() == this;
|
|
|
|
if (firstUpdate)
|
|
{
|
|
if (CursorControl && camIsMe)
|
|
{
|
|
CursorControl->setPosition(0.5f, 0.5f);
|
|
CenterCursor = CursorControl->getRelativePosition();
|
|
}
|
|
|
|
LastAnimationTime = os::Timer::getTime();
|
|
|
|
firstUpdate = false;
|
|
}
|
|
|
|
// get time. only operate on valid camera
|
|
f32 timeDiff = 0.f;
|
|
|
|
if ( camIsMe )
|
|
{
|
|
timeDiff = (f32) ( timeMs - LastAnimationTime );
|
|
LastAnimationTime = timeMs;
|
|
}
|
|
|
|
// update position
|
|
core::vector3df pos = getPosition();
|
|
|
|
// Update rotation
|
|
// if (InputReceiverEnabled)
|
|
{
|
|
Target.set(0,0,1);
|
|
|
|
|
|
if (CursorControl && InputReceiverEnabled && camIsMe )
|
|
{
|
|
core::position2d<f32> cursorpos = CursorControl->getRelativePosition();
|
|
|
|
if (!core::equals(cursorpos.X, CenterCursor.X) ||
|
|
!core::equals(cursorpos.Y, CenterCursor.Y))
|
|
{
|
|
RelativeRotation.X *= -1.0f;
|
|
RelativeRotation.Y *= -1.0f;
|
|
|
|
RelativeRotation.Y += (0.5f - cursorpos.X) * RotateSpeed;
|
|
RelativeRotation.X = core::clamp(
|
|
RelativeRotation.X + (0.5f - cursorpos.Y) * RotateSpeed,
|
|
-MAX_VERTICAL_ANGLE,
|
|
+MAX_VERTICAL_ANGLE);
|
|
|
|
RelativeRotation.X *= -1.0f;
|
|
RelativeRotation.Y *= -1.0f;
|
|
|
|
CursorControl->setPosition(0.5f, 0.5f);
|
|
CenterCursor = CursorControl->getRelativePosition();
|
|
}
|
|
}
|
|
|
|
// set target
|
|
|
|
core::matrix4 mat;
|
|
mat.setRotationDegrees(core::vector3df( RelativeRotation.X, RelativeRotation.Y, 0));
|
|
mat.transformVect(Target);
|
|
|
|
core::vector3df movedir = Target;
|
|
|
|
if (NoVerticalMovement)
|
|
movedir.Y = 0.f;
|
|
|
|
movedir.normalize();
|
|
|
|
if (InputReceiverEnabled && camIsMe)
|
|
{
|
|
if (CursorKeys[0])
|
|
pos += movedir * timeDiff * MoveSpeed;
|
|
|
|
if (CursorKeys[1])
|
|
pos -= movedir * timeDiff * MoveSpeed;
|
|
|
|
// strafing
|
|
|
|
core::vector3df strafevect = Target;
|
|
strafevect = strafevect.crossProduct(UpVector);
|
|
|
|
if (NoVerticalMovement)
|
|
strafevect.Y = 0.0f;
|
|
|
|
strafevect.normalize();
|
|
|
|
if (CursorKeys[2])
|
|
pos += strafevect * timeDiff * MoveSpeed;
|
|
|
|
if (CursorKeys[3])
|
|
pos -= strafevect * timeDiff * MoveSpeed;
|
|
|
|
// jumping ( need's a gravity , else it's a fly to the World-UpVector )
|
|
if (CursorKeys[4])
|
|
{
|
|
pos += UpVector * timeDiff * JumpSpeed;
|
|
}
|
|
}
|
|
|
|
// write translation
|
|
setPosition(pos);
|
|
}
|
|
|
|
// write right target
|
|
|
|
TargetVector = Target;
|
|
Target += pos;
|
|
}
|
|
|
|
|
|
void CCameraFPSSceneNode::allKeysUp()
|
|
{
|
|
for (s32 i=0; i<6; ++i)
|
|
CursorKeys[i] = false;
|
|
}
|
|
|
|
|
|
//! sets the look at target of the camera
|
|
//! \param pos: Look at target of the camera.
|
|
void CCameraFPSSceneNode::setTarget(const core::vector3df& tgt)
|
|
{
|
|
updateAbsolutePosition();
|
|
core::vector3df vect = tgt - getAbsolutePosition();
|
|
vect = vect.getHorizontalAngle();
|
|
RelativeRotation.X = vect.X;
|
|
RelativeRotation.Y = vect.Y;
|
|
|
|
if (RelativeRotation.X > MAX_VERTICAL_ANGLE)
|
|
RelativeRotation.X -= 360.0f;
|
|
}
|
|
|
|
//! Disables or enables the camera to get key or mouse inputs.
|
|
void CCameraFPSSceneNode::setInputReceiverEnabled(bool enabled)
|
|
{
|
|
// So we don't skip when we return from a non-enabled mode and the
|
|
// mouse cursor is now not in the middle of the screen
|
|
if( !InputReceiverEnabled && enabled )
|
|
firstUpdate = true;
|
|
|
|
InputReceiverEnabled = enabled;
|
|
}
|
|
|
|
//! Sets the rotation speed
|
|
void CCameraFPSSceneNode::setRotateSpeed(const f32 speed)
|
|
{
|
|
RotateSpeed = speed;
|
|
}
|
|
|
|
//! Sets the movement speed
|
|
void CCameraFPSSceneNode::setMoveSpeed(const f32 speed)
|
|
{
|
|
MoveSpeed = speed;
|
|
}
|
|
|
|
//! Gets the rotation speed
|
|
f32 CCameraFPSSceneNode::getRotateSpeed()
|
|
{
|
|
return RotateSpeed;
|
|
}
|
|
|
|
// Gets the movement speed
|
|
f32 CCameraFPSSceneNode::getMoveSpeed()
|
|
{
|
|
return MoveSpeed;
|
|
}
|
|
|
|
} // end namespace
|
|
} // end namespace
|
|
|