bitplane wrote:For Irrlicht 1.5, we plan to have a "camera controller" interface, which is similar to a scene node animator but has an event receiver and deals only with cameras. The FPS and Maya cameras will be a standard camera with a controller attached, and most people won't have to make custom cameras anymore. This doesn't help you at the moment, but you could use a custom animator to do the same job.
There's no need to add your own factory if you don't plan on saving or loading the camera, just make a new class derived from ISceneNodeAnimator and IEventReceiver, and pass events to the animator from your event receiver.
There's no animator tutorial, but this should give you a place to start from-
- Code: Select all
class MyAnimator : public ISceneNodeAnimator, public IEventReceiver
{
public:
// animator methods-
virtual void animateNode (ISceneNode *node, u32 timeMs)
{
if (!node || node->getType() != ESNT_CAMERA)
return; // only works on cameras
ICameraSceneNode *camera = (ICameraSceneNode*)node;
// do stuff with your camera here
}
virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
{
// if anyone asks, this is a 'walk' animator
return (ESCENE_NODE_ANIMATOR_TYPE)MAKE_IRR_ID('w','a','l','k');
}
// methods for event receiver
virtual bool OnEvent (const SEvent &event)
{
// respond to events here, and return true if you acted on the event
return false; // otherwise return false
}
};
I've decided to make a controller that moves a scene node in 3rd space.
Here are 3 binaries that shows its behavior.
1. Free Movement.exe - Shows how a scene node moves with the camera just looking at it from a static position.
2. 3rd Person Camera.exe - Shows an example of a "3rd person camera" using this animator, showing how simple it is.
3. 3rd Person - Time Based (buggy).exe - Same as 2 but the movement is time based.
Now both 1 and 2 aren't time based because I have some buggy/jumpy behavior when trying to make it time based but I can't seem to find how to fix that, maybe someone here can help me with that and so I've added the time based example so you'll see what I mean.
Well, you can download it here
http://rapidshare.com/files/99803531/IS ... r_v0.1.zip
[edit]
I've been asked to add a non-rapidshare link but please, if you can download from rapidshare please do cause I need the points I get from downloads.
So, a Mediafire link: http://www.mediafire.com/download.php?xue95we1dw2
[/edit]
and here's the code.
ISceneNodeControllerAnimator.hpp
- Code: Select all
// Copyright (c) 2007-2008 Tomer Nosrati
// This file is part of the "NUSoftware Game Engine".
// For conditions of distribution and use, see copyright notice in nge.hpp
#pragma once
#ifndef __I_SCENE_NODE_CONTROLLER_ANIMATOR_H_
#define __I_SCENE_NODE_CONTROLLER_ANIMATOR_H_
#include "irrlicht.h"
using namespace irr;
using namespace irr::core;
using namespace irr::gui;
using namespace irr::io;
using namespace irr::scene;
//! Special scene node animator for doing automatic movement in 3D space.
/*! This scene node animator can be attached to any scene node modifying it in that
way, that it can move freely in 3D space, rotate with mouse input and move/strafe with
keyboard input. */
class ISceneNodeControllerAnimator : public ISceneNodeAnimator, public IEventReceiver
{
public:
//! Destructor
virtual ~ISceneNodeControllerAnimator() {}
virtual void animateNode(ISceneNode* node, u32 timeMs) = 0;
virtual bool OnEvent(const SEvent& event) = 0;
//! Returns if the input receiver of the animator is currently enabled.
virtual bool isInputReceiverEnabled() = 0;
//! Disables or enables the animator to get key or mouse inputs.
virtual void setInputReceiverEnabled(bool enabled) = 0;
//! Returns if the scene node could move vertically or not
virtual bool isVerticalMovementEnabled() = 0;
//! Sets if the scene node could move vertically or not
virtual void setVerticalMovementEnabled(bool enabled) = 0;
//! Get the animator's movement speed
virtual f32 getMoveSpeed() const = 0;
//! Get the animator's rotation speed
virtual f32 getRotateSpeed() const = 0;
//! Set the animator's movement speed
virtual void setMoveSpeed(f32 speed) = 0;
//! Set the animator's rotation speed
virtual void setRotateSpeed(f32 speed) = 0;
//! Set should the rotation on both X and Y axes be inverted
virtual void invertRotation(bool X, bool Y) = 0;
//! Set should the rotation on the X axis be inverted
virtual void invertXRotation(bool isInvert) = 0;
//! Set should the rotation on the Y axis be inverted
virtual void invertYRotation(bool isInvert) = 0;
//! Returns whether the rotation on the X axis is inverted or not
virtual bool isXRotationInverted() const = 0;
//! Returns whether the rotation on the Y axis is inverted or not
virtual bool isYRotationInverted() const = 0;
};
#endif // __C_SCENE_NODE_CONTROLLER_ANIMATOR_H_
CSceneNodeControllerAnimator.hpp
- Code: Select all
// Copyright (c) 2007-2008 Tomer Nosrati
// This file is part of the "NUSoftware Game Engine".
// For conditions of distribution and use, see copyright notice in nge.hpp
#pragma once
#ifndef __C_SCENE_NODE_CONTROLLER_ANIMATOR_H_
#define __C_SCENE_NODE_CONTROLLER_ANIMATOR_H_
#include "ISceneNodeControllerAnimator.hpp"
class CSceneNodeControllerAnimator : public ISceneNodeControllerAnimator
{
public:
//! Constructor
CSceneNodeControllerAnimator(ISceneManager* smgr, ICursorControl* cur,
f32 rotateSpeed = 150.f, f32 moveSpeed = 100.f,
SKeyMap* keyMapArray = 0, s32 keyMapSize = 0,
bool verticalMovement = true);
//! Destructor
~CSceneNodeControllerAnimator();
virtual void animateNode(ISceneNode* node, u32 timeMs);
virtual bool OnEvent(const SEvent& event);
virtual bool isInputReceiverEnabled();
virtual void setInputReceiverEnabled(bool enabled);
virtual bool isVerticalMovementEnabled();
virtual void setVerticalMovementEnabled(bool enabled);
f32 getMoveSpeed() const;
f32 getRotateSpeed() const;
void setMoveSpeed(f32 speed);
void setRotateSpeed(f32 speed);
void invertRotation(bool X, bool Y);
void invertXRotation(bool isInvert);
void invertYRotation(bool isInvert);
bool isXRotationInverted() const;
bool isYRotationInverted() const;
private:
void allKeysUp();
//! Binds an action to a key
struct SCamKeyMap
{
SCamKeyMap() {};
SCamKeyMap(EKEY_ACTION a, EKEY_CODE k) : action(a), keycode(k) {}
EKEY_ACTION action;
EKEY_CODE keycode;
};
array<SCamKeyMap> KeyMap;
bool CursorKeys[EKA_COUNT];
ISceneNode* DummySceneNode;
position2df CenterPosition;
ISceneManager* SceneManager;
ICursorControl* CursorControl;
f32 MoveSpeed;
f32 RotateSpeed;
s32 LastAnimationTime;
bool RotUp, RotDown, RotLeft, RotRight;
s32 PrevMouseX, PrevMouseY;
bool IsInvertX, IsInvertY;
bool InputReceiverEnabled;
bool VerticalMovement;
};
#endif // __C_SCENE_NODE_CONTROLLER_ANIMATOR_H_
CSceneNodeControllerAnimator.cpp
- Code: Select all
// Copyright (c) 2007-2008 Tomer Nosrati
// This file is part of the "NUSoftware Game Engine".
// For conditions of distribution and use, see copyright notice in nge.hpp
#include "CSceneNodeControllerAnimator.hpp"
CSceneNodeControllerAnimator::CSceneNodeControllerAnimator(
ISceneManager* smgr, ICursorControl* cur,
f32 rotateSpeed, f32 MoveSpeed, SKeyMap* keyMapArray, s32 keyMapSize,
bool verticalMovement)
: SceneManager(smgr), CursorControl(cur),
MoveSpeed(MoveSpeed), RotateSpeed(rotateSpeed), LastAnimationTime(0),
RotUp(false), RotDown(false), RotLeft(false), RotRight(false),
PrevMouseX(0), PrevMouseY(0),
IsInvertX(false), IsInvertY(false),
InputReceiverEnabled(true), VerticalMovement(verticalMovement)
{
SceneManager->grab();
DummySceneNode = SceneManager->addEmptySceneNode();
CursorControl->setPosition(0.5f,0.5f);
CenterPosition = CursorControl->getRelativePosition();
allKeysUp();
// create key map
if (!keyMapArray || !keyMapSize)
{
// create default key map
KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, KEY_UP));
KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, KEY_DOWN));
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, KEY_LEFT));
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, KEY_RIGHT));
}
else
{
// create custom key map
for (s32 i = 0; i < keyMapSize; i++)
{
switch(keyMapArray[i].Action)
{
case EKA_MOVE_FORWARD:
KeyMap.push_back(SCamKeyMap(EKA_MOVE_FORWARD, keyMapArray[i].KeyCode));
break;
case EKA_MOVE_BACKWARD:
KeyMap.push_back(SCamKeyMap(EKA_MOVE_BACKWARD, keyMapArray[i].KeyCode));
break;
case EKA_STRAFE_LEFT:
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_LEFT, keyMapArray[i].KeyCode));
break;
case EKA_STRAFE_RIGHT:
KeyMap.push_back(SCamKeyMap(EKA_STRAFE_RIGHT, keyMapArray[i].KeyCode));
break;
default:
break;
} // end switch
} // end for
}// end if
}
CSceneNodeControllerAnimator::~CSceneNodeControllerAnimator()
{
if(SceneManager)
SceneManager->drop();
}
void CSceneNodeControllerAnimator::animateNode(ISceneNode* node, u32 timeMs)
{
if (!node)
return;
f32 timeDiff = 0.f;
// for first time
if(LastAnimationTime)
timeDiff = (f32) ( timeMs - LastAnimationTime );
LastAnimationTime = timeMs;
DummySceneNode->setParent(node);
DummySceneNode->setPosition(vector3df(0,0,-8.1f));
vector3df Pos = node->getPosition();
vector3df PrevPos = Pos;
vector3df Rot = node->getRotation();
vector3df PrevRot = Rot;
vector3df Vel(Pos - DummySceneNode->getAbsolutePosition());
f32 MovementSpeed = MoveSpeed/1000.f/* * timeDiff*/;
f32 RotationSpeed = RotateSpeed/100.f;
Vel.normalize();
if(VerticalMovement)
Vel.Y *= MovementSpeed;
else
Vel.Y = 0.f;
Vel.X *= MovementSpeed;
Vel.Z *= MovementSpeed;
if (CursorKeys[EKA_MOVE_FORWARD])
{
Pos += Vel;
}
if (CursorKeys[EKA_MOVE_BACKWARD])
{
Pos -= Vel;
}
if (CursorKeys[EKA_STRAFE_LEFT])
{
Pos.X -= MovementSpeed * cos((Rot.Y) * DEGTORAD);
Pos.Z += MovementSpeed * sin((Rot.Y) * DEGTORAD);
}
if (CursorKeys[EKA_STRAFE_RIGHT])
{
Pos.X += MovementSpeed * cos((Rot.Y) * DEGTORAD);
Pos.Z -= MovementSpeed * sin((Rot.Y) * DEGTORAD);
}
if (RotUp)
{
Rot.X -= RotationSpeed;
RotUp = false;
}
if (RotDown)
{
Rot.X += RotationSpeed;
RotDown = false;
}
if (RotLeft)
{
Rot.Y -= RotationSpeed;
RotLeft = false;
}
if (RotRight)
{
Rot.Y += RotationSpeed;
RotRight = false;
}
node->setPosition(Pos);
node->setRotation(Rot);
if(PrevPos != Pos && CursorControl)
{
CursorControl->setPosition(CenterPosition);
PrevMouseX = CursorControl->getPosition().X;
PrevMouseY = CursorControl->getPosition().Y;
}
}
bool CSceneNodeControllerAnimator::OnEvent(const SEvent& event)
{
if(InputReceiverEnabled)
{
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 (event.EventType == EET_MOUSE_INPUT_EVENT)
{
switch(event.MouseInput.Event)
{
case EMIE_MOUSE_MOVED:
{
s32 CurrentX = event.MouseInput.X;
s32 CurrentY = event.MouseInput.Y;
if((CurrentX - PrevMouseX) > 0)
{
if(IsInvertY)
{
RotLeft = true;
RotRight = false;
}
else
{
RotLeft = false;
RotRight = true;
}
}
else if((CurrentX - PrevMouseX) < 0)
{
if(IsInvertY)
{
RotLeft = false;
RotRight = true;
}
else
{
RotLeft = true;
RotRight = false;
}
}
if((CurrentY - PrevMouseY) > 0)
{
if(IsInvertX)
{
RotUp = true;
RotDown = false;
}
else
{
RotUp = false;
RotDown = true;
}
}
else if((CurrentY - PrevMouseY) < 0)
{
if(IsInvertX)
{
RotUp = false;
RotDown = true;
}
else
{
RotUp = true;
RotDown = false;
}
}
PrevMouseX = CurrentX;
PrevMouseY = CurrentY;
break;
}
}
}
return true;
} // if(InputReceiverEnabled)
return false;
}
bool CSceneNodeControllerAnimator::isInputReceiverEnabled()
{
return InputReceiverEnabled;
}
void CSceneNodeControllerAnimator::setInputReceiverEnabled(bool enabled)
{
InputReceiverEnabled = enabled;
}
bool CSceneNodeControllerAnimator::isVerticalMovementEnabled()
{
return VerticalMovement;
}
void CSceneNodeControllerAnimator::setVerticalMovementEnabled(bool enabled)
{
VerticalMovement = enabled;
}
f32 CSceneNodeControllerAnimator::getMoveSpeed() const
{
return MoveSpeed;
}
f32 CSceneNodeControllerAnimator::getRotateSpeed() const
{
return RotateSpeed;
}
void CSceneNodeControllerAnimator::setMoveSpeed(f32 speed)
{
MoveSpeed = speed;
}
void CSceneNodeControllerAnimator::setRotateSpeed(f32 speed)
{
RotateSpeed = speed;
}
void CSceneNodeControllerAnimator::invertRotation(bool X, bool Y)
{
IsInvertX = X;
IsInvertY = Y;
}
void CSceneNodeControllerAnimator::invertXRotation(bool isInvert)
{
IsInvertX = isInvert;
}
void CSceneNodeControllerAnimator::invertYRotation(bool isInvert)
{
IsInvertY = isInvert;
}
bool CSceneNodeControllerAnimator::isXRotationInverted() const
{
return IsInvertX;
}
bool CSceneNodeControllerAnimator::isYRotationInverted() const
{
return IsInvertY;
}
void CSceneNodeControllerAnimator::allKeysUp()
{
for (s32 i=0; i<EKA_COUNT; i++)
CursorKeys[i] = false;
}
main.cpp
- Code: Select all
#include <irrlicht.h>
#include <iostream>
#include "CSceneNodeControllerAnimator.hpp"
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
scene::ISceneNode* node = 0;
IrrlichtDevice* device = 0;
ISceneNodeControllerAnimator* anim = 0;
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
if(anim)
return anim->OnEvent(event);
return false;
}
};
int main()
{
video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 0;
}
// create device
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice( driverType, core::dimension2d<s32>(640, 480),
32, false, false, false, &receiver);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
device->getFileSystem()->addZipFileArchive("map-20kdm2.pk3");
scene::IAnimatedMesh* lvl_mesh = smgr->getMesh("20kdm2.bsp");
scene::ISceneNode* lvl_node = 0;
if (lvl_mesh)
lvl_node = smgr->addOctTreeSceneNode(lvl_mesh->getMesh(0), 0, -1, 128);
if (lvl_node)
lvl_node->setPosition(core::vector3df(-1300,-144,-1249));
node = smgr->addCubeSceneNode();
node->setPosition(vector3df(15,0,25.f));
if (node)
{
node->setMaterialTexture(0, driver->getTexture("t351sml.jpg"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
SKeyMap keyMap[8];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_UP;
keyMap[1].Action = EKA_MOVE_FORWARD;
keyMap[1].KeyCode = KEY_KEY_W;
keyMap[2].Action = EKA_MOVE_BACKWARD;
keyMap[2].KeyCode = KEY_DOWN;
keyMap[3].Action = EKA_MOVE_BACKWARD;
keyMap[3].KeyCode = KEY_KEY_S;
keyMap[4].Action = EKA_STRAFE_LEFT;
keyMap[4].KeyCode = KEY_LEFT;
keyMap[5].Action = EKA_STRAFE_LEFT;
keyMap[5].KeyCode = KEY_KEY_A;
keyMap[6].Action = EKA_STRAFE_RIGHT;
keyMap[6].KeyCode = KEY_RIGHT;
keyMap[7].Action = EKA_STRAFE_RIGHT;
keyMap[7].KeyCode = KEY_KEY_D;
anim = new CSceneNodeControllerAnimator(smgr, device->getCursorControl(), 150.f,100.f,keyMap,8);
node->addAnimator(anim);
anim->drop();
}
IAnimatedMeshSceneNode* forwardArrow = smgr->addAnimatedMeshSceneNode(smgr->addArrowMesh(
"forward",
video::SColor(255,255,255,0),
video::SColor(255,255,0,0),
4,
8,
8.f,
0.3f,
0.05f,
1.3f));
forwardArrow->setMaterialFlag(video::EMF_LIGHTING, false);
forwardArrow->setParent(node);
forwardArrow->setRotation(vector3df(90.f,0.f,0.f));
forwardArrow->setPosition(vector3df(0.f,0.f,8.f));
scene::ICameraSceneNode * cam = smgr->addCameraSceneNode();
device->getCursorControl()->setVisible(true);
/*
// 3rd Person Camera
cam->setParent(node);
cam->setPosition(vector3df(0.f, 20.f, -40.f));*/
// Add a colorful irrlicht logo
device->getGUIEnvironment()->addImage(
driver->getTexture("irrlichtlogoalpha2.tga"),
core::position2d<s32>(10,10));
int lastFPS = -1;
while(device->run())
{
cam->setTarget(node->getPosition());
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();
device->getGUIEnvironment()->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
tmp += driver->getName();
tmp += L"] fps: ";
tmp += fps;
device->setWindowCaption(tmp.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
Few notes:
- The movement in all examples are with WASD and mouse for rotation.
- All the media files I'm using is in the media folder of the Irrlicht's SDK.
- The license is as Irrlicht's.
Now I've seen something like the following lines that do the same math I've did:
- Code: Select all
void MoveNode(ISceneNode* node, vector3df dest)
{
if (!node)
return;
matrix4 mat = node->getRelativeTransformation();
mat.transformVect(dest);
node->setPosition(dest);
}
So if you ask why don't I use it it's simply because I don't understand it.
Known Bugs:
When setting the animator to more then one scene node you get strange behavior.
(not much of a bug) - not time based.
To use do the following things:
1. Add as an animator to your scene node.
2. Call anim->OnEvent(event) in your event receiver, see main.cpp above for example.
P.S
Is there any single chance it gets into the Irrlicht library or at least start the so called camera controller development?





