New camera type without changing Irrlicht

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

New camera type without changing Irrlicht

Post by ElectroDruid »

Hello,

The FPS and the Maya cameras are kinda useful for debugging, but not particularly good as game cameras, so I want to make my own. Thing is, I want to do it with as little editing of the Irrlicht files as possible, preferably no editing at all. Every solution I think of is causing problems though, and I wonder if I'm just approaching the Irrlicht API in the wrong way.

The first idea was to create my cGameCamera class, and have it contain an ICameraSceneNode*, to wrap up the Irrlicht stuff.

Code: Select all

// This is just example code to show what I mean
class cGameCamera
{
public:
    ICameraSceneNode* mpMyCamNode;

   void Init()
    {
        mpMyCamNode = scenemgr->createCameraSceneNode(blah);
    }

   void Update()
   {
        //  Get user input
        // Do some maths to generate new position and lookat coordinates
        // Apply the position/lookat/whatever to mpMyCamNode
    }
};
This is all fine, except I can't get any input events. What the scene manager creates is a CCameraSceneNode, which ignores all input events by doing nothing in its OnEvent() function, and I don't know any way to pass the input events on to my cGameCamera class. Does anyone know how I can get hold of input events in this way?

The next thing I tried was to have cGameCamera derive from ICameraSceneNode. This turned out to be pretty wasteful because I would have to re-implement most of CCameraSceneNode, and copy/pasting chunks of code is never, ever a good idea.

So I decided to have it derive straight from CCameraSceneNode, and only change the bits I need to, which basically means just filling out the OnEvent function. This approach makes a mockery of Irrlicht having interfaces at all, and just felt wrong. I had to add new include paths just so my code could find CCameraSceneNode.h. Furthermore, in order to create my camera this way, I'd have to change the scene manager to give it a new function called something like createCameraSceneNodeNewGameCam(), and I don't want to change Irrlicht if I there is any way of avoiding it.

So, what's the best way for me to do this? I would rather have my camera class wrap an ICameraSceneNode* than derive from one, but I can't get input events. And if the only way is for me to derive from ICameraSceneNode or from CCameraSceneNode, how can I create my camera when the game initialise without changing the scene manager to add a new function?
Saturn
Posts: 418
Joined: Mon Sep 25, 2006 5:58 pm

Post by Saturn »

Wrapping the camera instead of inheriting is the right choice and personally I'd find it more useful, if FPS and Maya controls were wrapper than doing it by inheritence.

Your problem is easy to solve: cGameCamera needs to inherit from IEventReceiver and you then can override the OnEvent-function in it and register it with the IrrlichtDevice as current event receiver using IrrlichtDevice::setEventReceiver.
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

but by doing that only the camera can get any events.
i would go the way of inharitance and inheritate from ICameraSceneNode
and simply do stuff on the OnEvent function way easier and u don't have to make an extra update call.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

What about doing something like this...

Code: Select all

class MyCameraSceneNode : public ICameraSceneNode
{
public:
   MyCameraSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id = -1)
      : ICameraSceneNode(parent, mgr, id)
   {
     Camera = smgr->addCameraSceneNode(this, mgr, 0);
   }

   virtual ~MyCameraSceneNode()
   {
   }

   virtual void setProjectionMatrix(const core::matrix4& projection)
   {
      Camera->setProjectionMatrix(projection);
   }

   virtual const core::matrix4& getProjectionMatrix()
   {
      return Camera->getProjectionMatrix();
   }

   virtual const core::matrix4& getViewMatrix()
   {
      return Camera->getViewMatrix();
   }

   virtual bool OnEvent(const SEvent &event)
   {
      // do your own thing

      if (!handled)
        Camera->OnEvent(event);
   }

   virtual void setTarget(const core::vector3df& pos)
   {
      Camera->setTarget(pos);
   }

   virtual core::vector3df getTarget() const
   {
      return Camera->getTarget();
   }

   virtual void setUpVector(const core::vector3df& pos)
   {
      Camera->setUpVector(pos);
   }

   virtual core::vector3df getUpVector() const
   {
      return Camera->getUpVector();
   }

   virtual f32 getNearValue()
   {
      return Camera->getNearValue();
   }

   virtual f32 getFarValue()
   {
      return Camera->getFarValue();
   }

   virtual f32 getAspectRatio()
   {
      return Camera->getAspectRatio();
   }

   virtual f32 getFOV()
   {
      return Camera->getFOV()
   }

   virtual void setNearValue(f32 zn)
   {
      Camera->setNearValue(zn);
   }

   virtual void setFarValue(f32 zf)
   {
      Camera->setFarValue(zf);
   }

   virtual void setAspectRatio(f32 aspect)
   {
      Camera->setAspectRatio(aspect);
   }

   virtual void setFOV(f32 fovy)
   {
      Camera->setFOV(fovy);
   }

   virtual const SViewFrustrum* getViewFrustrum()
   {
      return Camera->getViewFrustrum();
   }

   virtual void setInputReceiverEnabled(bool enabled)
   {
      Camera->setInputReceiverEnabled(enabled);
   }

   virtual bool isInputReceiverEnabled()
   {
      return Camera->isInputReceiverEnabled();
   }

   // whoever made these methods non-abstract in the base class is a turd.
   virtual bool isOrthogonal() 
   {
      return Camera->isOrthogonal();
   } 

   void setIsOrthogonal(bool orthogonal)
   {
      Camera->setIsOrthogonal(orthogonal);
   }

private:
   ICameraSceneNode* Camera;
};

// create your camera, add to scene manager
ICameraSceneNode* myCamera = new MyCameraSceneNode(parent, smgr);

// make sure your camera is active
smgr->setActiveCamera(myCamera);
All of the camera functions that you need to override in ICameraSceneNode will just call through to the internal camera. You can override OnEvent() to handle camera specific messages as you please.

Travis
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

vitek, ICameraSceneNode already inherits from IEventReceiver, so by inherting from ICameraSceneNode, you get the IEventReceiver as well.

I think you're trying to determine if the game wants to handle the event, before the camera gets it. If so, best way, IMO, is to setup states for your game, and then for certain states, say for example when a GUI window is up, disable the InputReceiver for the camera and then your camera should not consume those events, and your own EventReceiver will get them.

Or am I way off on what's trying to be done here?
Image
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Yeah, I know ICameraSceneNode inherits from IEventReceiver.

I'm trying to side-step the problem that comes about when the user event receiver handles keyboard and mouse input. When this happens, it can prevent the other parts of the app [gui and camera] from getting their keyboard/mouse input.

This takes advantage of the fact that the scene manager already delegates camera control to the camera. This keeps the camera control code where it belongs [in the camera] and keeps it out of the user event receiver where these problems can crop up. It also avoids cluttering up the user event receiver with camera management code.

There are other ways to do this. One would be to forward keyboard/mouse input to the gui before handling it in the user event receiver. Another way would be to maintain a set of states as you suggested.

Travis
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

Post by ElectroDruid »

I think Saturn may have come up with the answer for what I want. Inheriting from IEventReceiver means I only need to deal with OnEvent(), which I can then pass on to a wrapped camera. Admittedly, as Sudi said it means that the CCameraSceneNode I'd be wrapping will get the OnEvent() call, as well as my cGameCamera getting one, but CCameraSceneNode does nothing except return false anyway, so will probably get optimised out by any decent compiler. Even if it doesn't it's hardly going to be a major performance issue, and it keeps my game code nice and clean.

Thanks for all the suggestions though :) Good to look at the problem from a few angles before working out the best way to tackle it.
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

Post by ElectroDruid »

vitek wrote:Yeah, I know ICameraSceneNode inherits from IEventReceiver.

I'm trying to side-step the problem that comes about when the user event receiver handles keyboard and mouse input. When this happens, it can prevent the other parts of the app [gui and camera] from getting their keyboard/mouse input.

This takes advantage of the fact that the scene manager already delegates camera control to the camera. This keeps the camera control code where it belongs [in the camera] and keeps it out of the user event receiver where these problems can crop up. It also avoids cluttering up the user event receiver with camera management code.

There are other ways to do this. One would be to forward keyboard/mouse input to the gui before handling it in the user event receiver. Another way would be to maintain a set of states as you suggested.

Travis
Have I missed something about user event receivers? Are there circumstances in which adding my own event receivers will stop other parts of the engine from getting events? Or some constraint that means I can only create one user event receiver? I'm not really following this part of the conversation too well, I haven't had a chance to look at the Irrlicht event system much yet.
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

If an IEventReceiver::OnEvent function returns true, then that is like consuming the event, so it won't be passed on to any other IEventReceiver::OnEvent functions. You return false to not consume the event, which allows other IEventReceiver::OnEvent functions to be called.
Image
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Exactly. The problem comes up that if you have keys that control the camera from a user event receiver [W,A,S,D for example], the GUI edit boxes won't get those key presses.

As I mentioned above, there are ways around this. You just need to pick the one that you feel best solves the problem for your needs.
Saturn
Posts: 418
Joined: Mon Sep 25, 2006 5:58 pm

Post by Saturn »

This problem occurs in the first place because of the way the CameraSceneNodes work. A Camera is a camera and not a controller. So putting control into the camera is not sensible, IMHO. It is too special purpose.
From an OO perspective it violates the Seperation of Concerns rule and I generally dislike "magical" (implicit) behaviour.

I do my game's control more like Spintz proposed it originally.
I have an InputManager, which is the only real input receiver. It sieves out input that is mapped to global actions and distributes the rest to special purpose input controllers. Which Controller has the focus depends on the game state. Those special case controllers have a common GameController super class. Among these are MovementController (for standard 3rd person movement), CombatController for combat, and so on.
These can share parts of their implementation but that's not a applicable for all, so this is not a must.

GUI is handled likewise. If modal gui is open, it gets the input instead.

HTH
Saturn
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I won't argue that managing game state is a smart thing to do. For the projects that most users are doing it isn't really necessary, and most of the guys posting in the beginners forum wouldn't even know where to begin.

Travis
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

You're probably right on that point Vitek, didn't think of that.

You're like Beginners Help forum masta!! :P

^^ I mean that in a good way, BTW. So it's not taken out of context. You're always helpin' out. I take a week or two from even opening this forum section at times, 'cause it can get tiring answering the same questions over and over, but I constantly seeing you helpin' people out in here...kudos to you.
Image
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

Post by ElectroDruid »

So, if I understand this right...

The best approach is to give my game some kind of global input manager, one which inherits from IEventReceiver. This input manager will (if it needs to) check the current game state, and pass on any input messages to the parts of the game which might be interested in Irrlicht input events at that point.

I write (amongst other things) a camera class which derives from nothing but wraps ICameraSceneNode, and takes inputs from this input manager, and uses those inputs to manipulate the camera by changing the position etc of the ICameraSceneNode it wraps.

This way, nothing gets changed in the Irrlicht engine, and the only thing derived from any part of the Irrlicht engine is my event receiver. I was toying with this idea anyway (I have written a nicer (IMHO) event system than Irrlicht uses, so it makes sense to drop it in for the game content side of things).

Am I right? Is this the kind of approach people are suggesting?
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

I'd say that's the best way to go, however, I wouldn't create a class that wraps ICameraSceneNode, I would derive a class, and in the override for OnEvent just always return false.

They both accomplish the same thing, which is important, at least get the concept.

I guess wrapping the class saves the function call to OnEvent whenever there's input, but that's not a big deal.
Image
Post Reply