Thanks @ Maksym for report. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@6152 dfc29bdd-3216-0410-991c-e03cc46cb475
		
			
				
	
	
		
			426 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // 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 "CSceneNodeAnimatorCameraFPS.h"
 | |
| #include "IVideoDriver.h"
 | |
| #include "ISceneManager.h"
 | |
| #include "Keycodes.h"
 | |
| #include "ICursorControl.h"
 | |
| #include "ICameraSceneNode.h"
 | |
| #include "ISceneNodeAnimatorCollisionResponse.h"
 | |
| 
 | |
| namespace irr
 | |
| {
 | |
| namespace scene
 | |
| {
 | |
| 
 | |
| //! constructor
 | |
| CSceneNodeAnimatorCameraFPS::CSceneNodeAnimatorCameraFPS(gui::ICursorControl* cursorControl,
 | |
| 		f32 rotateSpeed, f32 moveSpeed, f32 jumpSpeed,
 | |
| 		SKeyMap* keyMapArray, u32 keyMapSize, bool noVerticalMovement, bool invertY, float rotateSpeedKeyboard)
 | |
| : CursorControl(cursorControl),
 | |
| // On X11 we get events even when mouse is not inside the Irrlicht window, on Windows we don't.
 | |
| // It might be possible to add grabbing on Windows as well in which case this has to be somewhat changed.
 | |
| // TODO: I don't know about OSX, but in theory it should be like old Irrlicht 1.8 behavior whatever that was there.
 | |
| #ifdef _IRR_COMPILE_WITH_X11_DEVICE_
 | |
| 	GrabMouse(false),
 | |
| #else
 | |
| 	GrabMouse(true),
 | |
| #endif
 | |
| 	MaxVerticalAngle(88.0f), NoVerticalMovement(noVerticalMovement),
 | |
| 	MoveSpeed(moveSpeed),
 | |
| 	RotateSpeedKeyboard(rotateSpeedKeyboard), RotateSpeed(rotateSpeed),
 | |
| 	JumpSpeed(jumpSpeed),
 | |
| 	MouseYDirection(invertY ? -1.0f : 1.0f),
 | |
| 	LastAnimationTime(0), HadMouseEvent(false), firstUpdate(true), firstInput(true)
 | |
| {
 | |
| 	#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
 | |
| CSceneNodeAnimatorCameraFPS::~CSceneNodeAnimatorCameraFPS()
 | |
| {
 | |
| 	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 CSceneNodeAnimatorCameraFPS::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:
 | |
| 		HadMouseEvent = true;
 | |
| 		if ( evt.MouseInput.Event == EMIE_MOUSE_ENTER_CANVAS && CursorControl)
 | |
| 		{
 | |
| 			CursorControl->setPosition(0.5f, 0.5f);
 | |
| 			CenterCursor = CursorControl->getRelativePosition(false);
 | |
| 			CursorPos = CenterCursor;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| void CSceneNodeAnimatorCameraFPS::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(0.5f, 0.5f);
 | |
| 			CursorPos = CenterCursor = CursorControl->getRelativePosition(false);
 | |
| 		}
 | |
| 
 | |
| 		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 rotation
 | |
| 	core::vector3df target = (camera->getTarget() - camera->getAbsolutePosition());
 | |
| 	core::vector3df relativeRotation = target.getHorizontalAngle();
 | |
| 
 | |
| 	if (CursorControl)
 | |
| 	{
 | |
| 		bool reset = false;
 | |
| 
 | |
| 		if ( HadMouseEvent || GrabMouse)
 | |
| 			CursorPos = CursorControl->getRelativePosition();
 | |
| 
 | |
| 		if (CursorPos != CenterCursor)
 | |
| 		{
 | |
| 			relativeRotation.Y -= (CenterCursor.X - CursorPos.X) * RotateSpeed;
 | |
| 			relativeRotation.X -= (CenterCursor.Y - CursorPos.Y) * RotateSpeed * MouseYDirection;
 | |
| 
 | |
| 			reset = true;
 | |
| 		}
 | |
| 
 | |
| 		if ( GrabMouse && !reset)
 | |
| 		{
 | |
| 			// Special case, mouse is whipped outside of window before it can update.
 | |
| 			video::IVideoDriver* driver = smgr->getVideoDriver();
 | |
| 			core::vector2d<u32> mousepos(u32(CursorPos.X), u32(CursorPos.Y));
 | |
| 			core::rect<u32> screenRect(0, 0, driver->getScreenSize().Width, driver->getScreenSize().Height);
 | |
| 
 | |
| 			// Only if we are moving outside quickly.
 | |
| 			reset = !screenRect.isPointInside(mousepos);
 | |
| 		}
 | |
| 
 | |
| 		if(reset)
 | |
| 		{
 | |
| 			CursorControl->setPosition(0.5f, 0.5f);
 | |
| 			CenterCursor = CursorControl->getRelativePosition(false);	// often no longer 0.5 due to int/float conversions
 | |
| 			CursorPos = CenterCursor;
 | |
|  		}
 | |
| 	}
 | |
| 	HadMouseEvent = false;
 | |
| 
 | |
| 	// keyboard rotation
 | |
| 	if (CursorKeys[EKA_ROTATE_LEFT])
 | |
| 		relativeRotation.Y -= timeDiff * RotateSpeedKeyboard;
 | |
| 
 | |
| 	if (CursorKeys[EKA_ROTATE_RIGHT])
 | |
| 		relativeRotation.Y += timeDiff * RotateSpeedKeyboard;
 | |
| 
 | |
| 	// 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;
 | |
| 	}
 | |
| 
 | |
| 	// set target
 | |
| 	core::vector3df pos = camera->getPosition();
 | |
| 	target.set(0,0, core::max_(1.f, pos.getLength()));	// better float precision than (0,0,1) in target-pos calculation in camera
 | |
| 	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 CSceneNodeAnimatorCameraFPS::allKeysUp()
 | |
| {
 | |
| 	for (u32 i=0; i<EKA_COUNT; ++i)
 | |
| 		CursorKeys[i] = false;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! Sets the rotation speed
 | |
| void CSceneNodeAnimatorCameraFPS::setRotateSpeed(f32 speed)
 | |
| {
 | |
| 	RotateSpeed = speed;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! Sets the movement speed
 | |
| void CSceneNodeAnimatorCameraFPS::setMoveSpeed(f32 speed)
 | |
| {
 | |
| 	MoveSpeed = speed;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! Gets the rotation speed
 | |
| f32 CSceneNodeAnimatorCameraFPS::getRotateSpeed() const
 | |
| {
 | |
| 	return RotateSpeed;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Gets the movement speed
 | |
| f32 CSceneNodeAnimatorCameraFPS::getMoveSpeed() const
 | |
| {
 | |
| 	return MoveSpeed;
 | |
| }
 | |
| 
 | |
| //! Sets the keyboard mapping for this animator
 | |
| void CSceneNodeAnimatorCameraFPS::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 CSceneNodeAnimatorCameraFPS::setKeyMap(const core::array<SKeyMap>& keymap)
 | |
| {
 | |
| 	KeyMap=keymap;
 | |
| }
 | |
| 
 | |
| const core::array<SKeyMap>& CSceneNodeAnimatorCameraFPS::getKeyMap() const
 | |
| {
 | |
| 	return KeyMap;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! Sets whether vertical movement should be allowed.
 | |
| void CSceneNodeAnimatorCameraFPS::setVerticalMovement(bool allow)
 | |
| {
 | |
| 	NoVerticalMovement = !allow;
 | |
| }
 | |
| 
 | |
| 
 | |
| //! Sets whether the Y axis of the mouse should be inverted.
 | |
| void CSceneNodeAnimatorCameraFPS::setInvertMouse(bool invert)
 | |
| {
 | |
| 	if (invert)
 | |
| 		MouseYDirection = -1.0f;
 | |
| 	else
 | |
| 		MouseYDirection = 1.0f;
 | |
| }
 | |
| 
 | |
| 
 | |
| ISceneNodeAnimator* CSceneNodeAnimatorCameraFPS::createClone(ISceneNode* node, ISceneManager* newManager)
 | |
| {
 | |
| 	CSceneNodeAnimatorCameraFPS * newAnimator =
 | |
| 		new CSceneNodeAnimatorCameraFPS(CursorControl,	RotateSpeed, MoveSpeed, JumpSpeed,
 | |
| 											0, 0, NoVerticalMovement);
 | |
| 	newAnimator->cloneMembers(this);
 | |
| 	newAnimator->setKeyMap(KeyMap);
 | |
| 	return newAnimator;
 | |
| }
 | |
| 
 | |
| void CSceneNodeAnimatorCameraFPS::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
 | |
| {
 | |
| 	ISceneNodeAnimator::serializeAttributes(out, options);
 | |
| 
 | |
| 	out->addFloat("MaxVerticalAngle", MaxVerticalAngle);
 | |
| 	out->addBool("NoVerticalMovement", NoVerticalMovement);
 | |
| 	out->addFloat("MoveSpeed", MoveSpeed);
 | |
| 	out->addFloat("RotateSpeedKeyboard", RotateSpeedKeyboard);
 | |
| 	out->addFloat("RotateSpeed", RotateSpeed);
 | |
| 	out->addFloat("JumpSpeed", JumpSpeed);
 | |
| 	out->addFloat("MouseYDirection", MouseYDirection);
 | |
| 
 | |
| 	out->addInt("KeyMapSize", (s32)KeyMap.size());
 | |
| 	for ( u32 i=0; i < KeyMap.size(); ++i )
 | |
| 	{
 | |
| 		core::stringc name("Action");
 | |
| 		name += core::stringc(i);
 | |
| 		out->addInt(name.c_str(), (int)KeyMap[i].Action);
 | |
| 		name = core::stringc("KeyCode") + core::stringc(i);
 | |
| 		out->addInt(name.c_str(), (int)KeyMap[i].KeyCode);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void CSceneNodeAnimatorCameraFPS::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
 | |
| {
 | |
| 	ISceneNodeAnimator::deserializeAttributes(in, options);
 | |
| 
 | |
| 	MaxVerticalAngle = in->getAttributeAsFloat("MaxVerticalAngle", MaxVerticalAngle);
 | |
| 	NoVerticalMovement = in->getAttributeAsBool("NoVerticalMovement", NoVerticalMovement);
 | |
| 	MoveSpeed = in->getAttributeAsFloat("MoveSpeed", MoveSpeed);
 | |
| 	RotateSpeedKeyboard = in->getAttributeAsFloat("RotateSpeedKeyboard", RotateSpeedKeyboard);
 | |
| 	RotateSpeed = in->getAttributeAsFloat("RotateSpeed", RotateSpeed);
 | |
| 	JumpSpeed = in->getAttributeAsFloat("JumpSpeed", JumpSpeed);
 | |
| 	MouseYDirection = in->getAttributeAsFloat("MouseYDirection", MouseYDirection);
 | |
| 
 | |
| 	if ( in->findAttribute("KeyMapSize") )
 | |
| 	{
 | |
| 		KeyMap.clear();
 | |
| 		s32 keyMapSize = in->getAttributeAsInt("KeyMapSize");
 | |
| 		for ( u32 i=0; i < (u32)keyMapSize; ++i )
 | |
| 		{
 | |
| 			SKeyMap keyMapEntry;
 | |
| 			core::stringc name("Action");
 | |
| 			name += core::stringc(i);
 | |
| 			keyMapEntry.Action = static_cast<EKEY_ACTION>(in->getAttributeAsInt(name.c_str()));
 | |
| 			name = core::stringc("KeyCode") + core::stringc(i);
 | |
| 			keyMapEntry.KeyCode = static_cast<EKEY_CODE>(in->getAttributeAsInt(name.c_str()));
 | |
| 			KeyMap.push_back(keyMapEntry);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| } // namespace scene
 | |
| } // namespace irr
 | |
| 
 |