How would I make "IrrlichtDevice" universal?

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.
Post Reply
Alon
Posts: 7
Joined: Fri May 16, 2014 9:42 pm

How would I make "IrrlichtDevice" universal?

Post by Alon »

Hey. I'm not exactly new to irrlicht, and I wasn't sure if this was a "beginner" question or not... but, I'm making a program in C++ where my classes regularly all access the same IrrlichtDevice. So far I've done this by passing the pointer to each class, but I'm not sure if this is the best method. Is there an easier/cleaner way, perhaps with some kind of pointer that can be accessed from all my classes?

Also, I have a "Game Manager" class with a bunch of other classes under it. (Like a list of gameobjects to be updated each tick) Is there a way to make it so that the lower objects can communicate with their parent? (Like, say, a game object issuing a command to the game manager)

Any help would be greatly appreciated.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: How would I make "IrrlichtDevice" universal?

Post by Seven »

1) you are talking about a global variable : typically frowned upon by the programming gurus.

Code: Select all

 
 
#include "irrlicht.h"
 
// declared outside of any class or function scope
irrlichtDevice* gDevice = 0;
 
class myClass
{
  public
           void doSomething() 
           { 
              if (gDevice) gDevice->someFunction(); 
           }
};
 
void main
{
      myClass game;
 
      gDevice = createirrlichtdevice();
      while (game.doSomething())
      {
           do more stuff
      }
}
 
you could also use a singleton object, which is a fancy global variable. search for it on google.
Last edited by Seven on Fri May 16, 2014 10:31 pm, edited 1 time in total.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: How would I make "IrrlichtDevice" universal?

Post by Seven »

object to object communication is pretty easy. you need :

1) objects with some way to look them up
2) message structure
3) message Manager

for example :

Code: Select all

 
    // a simple message class
    class CSObjectMessage
    {
    public:
        ADD_VARIABLE_SETGET(CS_MESSAGE_TYPE,Type);  // message type
        ADD_VARIABLE_SETGET(CS_MESSAGE_TYPE,Type2); // message type
        ADD_VARIABLE_SETGET(int,Source);            // who initiated this message
        ADD_VARIABLE_SETGET(int,Dest);              // who is the message going to
        ADD_VARIABLE_SETGET(float,RTime);           // what time was the message sent   
        ADD_VARIABLE_SETGET(float,DTime);           // what time to deliver the message
        ADD_VARIABLE_SETGET(stringc,Data);          // the message
        
        CSObjectMessage();                  // class constructor
        virtual ~CSObjectMessage();         // class destructor
    
        // dual creation allows for better error handling
        virtual bool create(CS_MESSAGE_TYPE type, CS_MESSAGE_TYPE type2, int source,int dest, float rtime, float dtime, stringc data);  
    };
 
 
    class MessageList
    {
    public:
        core::list<CSObjectMessage*> m_List;                // list of messages
        core::list<CSObjectMessage*>::Iterator m_Iterator;  // message list iterator
 
        MessageList();
        ~MessageList();
        void initialize();
        bool cleanup();
        bool create();
        bool add(CSObjectMessage* data);
        void logInfo();
    };
 
    // callback function for the messages
    typedef void (*MESSAGECALLBACKFUNC)(CSObjectMessage* message);
 
    // simple message manager class
    // messages are time based and are stored until the appropriate time.
    // a good example of using messages is for a missile to send itself a timed message
    // to explode in 10 seconds when it is fired. the message hangs around and gets delivered 
    // when the time is right. if the missle already exploded then the message will get 
    // dropped (since the reciever is not a valid object anymore), otherwise the missile will 
    // receive the message to explode and will react accordingly.
    class CSMessageManager  
    {
    public:
 
        ADD_VARIABLE_SETGET(MessageList,List);                  // the list of Message pointers
        ADD_VARIABLE_SETGET(CSObjectManager*, ObjectManager);   // list of created objects
        ADD_VARIABLE_SETGET(CSApplication*, Application);       // the app
        ADD_VARIABLE_SETGET(CSLevel*, Level);                   // the level
 
        CSMessageManager();                         // class constructor
        virtual ~CSMessageManager();                // class destructor
        virtual void initialize();                  // set all variables to a known value
 
        // dual creation allows for better error handling
        virtual bool create(CSObjectManager* m, CSApplication* app, CSLevel* level);
 
        virtual bool cleanup();                     // cleanup whatever memory mess we made
        virtual void frame(float elaspedtime);      // do what this class does each frame
 
        // create and add a message to the list
        virtual void addMessage(CS_MESSAGE_TYPE type, CS_MESSAGE_TYPE type2, int source,int dest, float rtime, float dtime, stringc data); 
 
        // send the message 
        virtual void dispatchMessage(CSObjectMessage* message);
 
        // clear out existing messages
        virtual void clear();
 
        // use a callback if you like.
        MESSAGECALLBACKFUNC MessageCallBack;
        ADD_VARIABLE_SETGET(int,MessageCallBackId);
        void setMessageCallBack(MESSAGECALLBACKFUNC NewCallBack, int id);
 
    };
 

Code: Select all

 
    // send the message 
    void CSMessageManager::dispatchMessage(CSObjectMessage* message)
    {
        // if we have a callback, send it there too
        if (MessageCallBack)
            if (message->getDest() == getMessageCallBackId()) 
                MessageCallBack(message);
 
        // if this is meant for the app level strcuture, send it there
        if (message->getDest() == ID_APP_MESSAGE)
        {
            getApplication()->receiveMessage(message);
        }
        else
            // if this is meant for the level strcuture, send it there
        if (message->getDest() == ID_LEVEL_MESSAGE)
        {
            getLevel()->receiveMessage(message);
        }
        else
        {
            // it must be for an object, so get a pointer to the object
            CSObject* obj = getObjectManager()->getObjectPointer(message->getDest());
            if (obj)
            {
                // if the object exists, send the message
                obj->receiveMessage(message);
            }
        }
    }
 
and each object has a function called receivemessage() that 'knows' what to do with each message.

Code: Select all

 
    bool CSObject::receiveMessage(CSObjectMessage* m)
    {
        if (m_Children)
        {
            CSObject* obj = m_Children->getNextObject(true);
            while (obj)
            {
                obj->receiveMessage(m);
                obj = m_Children->getNextObject(false);
            }
        }
 
        // we didnt want the message
        return false;
    }
 

here is an example object sending a message
this is a 'fire' object. when the physx simulation detects collision between this and another object, the physx world will send
this object a MESSAGE_TRIGGER_ENTER message. this object then sends a message to the other object
saying that it has been FIREDAMAGED

Code: Select all

 
        virtual bool receiveMessage(CSObjectMessage* m)
        {
            switch (m->getType())
            {
                case MESSAGE_TRIGGER_ENTER:
                {
                    getLevel()->getObjectFactory()->getMessageManager()->addMessage(CS_MESSAGE_TYPE::DAMAGE_FIRE, CS_MESSAGE_TYPE::MESSAGE_TRIGGER_ENTER, getId(), m->getSource(), 0, 0, "10");
                } break;
                case MESSAGE_TRIGGER_CONTACT: 
                {
                } break;
                case MESSAGE_TRIGGER_EXIT: 
                {
                    getLevel()->getObjectFactory()->getMessageManager()->addMessage(CS_MESSAGE_TYPE::DAMAGE_FIRE, CS_MESSAGE_TYPE::MESSAGE_TRIGGER_ENTER, getId(), m->getSource(), 0, 0, "0");
                } break;
            }
            return false;
        }
 
and one receiving a message

this is a character obejct. it receives a firedamage message and displays a hurtful text ont he screen

Code: Select all

 
        virtual bool receiveMessage(CSObjectMessage* m)
        {
            switch (m->getType())
            {
                case CS_MESSAGE_TYPE::DAMAGE_FIRE:
                {
                        int damage = stringcToInt(m->getData());
                        createTextAnim(getLevel(), getPrimarySceneNode(), stringw( stringc("OUCH!! FIRE DAMAGE -") + stringc(damage)),SColor(255, 240, 0, 0), 3000, dimension2d<f32>(160, 30));
                } break;
etc...etc......etc......
 
mant
Posts: 125
Joined: Sun Jan 27, 2013 3:38 pm

Re: How would I make "IrrlichtDevice" universal?

Post by mant »

You can do something like putting it into a namespace. For example:

Code: Select all

//System.h:
#ifndef SYSTEM_H
#define SYSTEM_H
 
//forward declaration
namespace irr{
class IrrlichtDevice;
}
 
namespace SYSTEM {
extern irr::IrrlichtDevice* irrDevice;
inline irr::IrrlichtDevice* getIrrDevice() { return irrDevice; }
}
 
#endif

Code: Select all

//System.cpp
#include "Sytem.h"
 
namespace SYSTEM {
irr::IrrlichtDevice* irrDevice = NULL;
}
And after creating the Device you must call:
SYSTEM::irrDevice = yourIrrlichtDevice;

And when you want to use, include System.h and use function SYSTEM::getIrrDevice();
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How would I make "IrrlichtDevice" universal?

Post by CuteAlien »

I like just passing around the pointer. That way anyone reading your code can immediately see which of your classes needs an IrrlichtDevice. Also consider if all those classes really need access to the full device - often they might only need IVideoDriver and ISceneManager for example. Or only IGUIEnvioronment etc. In those cases it's sufficient to pass on those pointers.

Samy way with gamemanager - every game-object you have a pointer to the gamemanager.

(also I have to admit I didn't do it that way in older projects - there I had usually 1 global for the application class which is still fine for smaller applications I guess).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: How would I make "IrrlichtDevice" universal?

Post by Seven »

I also prefer passing the pointer around. One reason is that I use multiple scenemanagers in my apps. Most of the time that I borrow code off of the forums, the scenenodes assume that the user will be using the getSmgr() functions for irrlicht main structures and I have to rewrite them to accept a smgr pointer so that I can pass mine in. not a big deal, but I try not to assume what the end user will be doing in his app. Sometimes i have a null device that i use for file access and dont pass in the main device, as another example.

also, and someone can correct me since I am not an expert, passing a pointer around is not a big deal in today's compilers as far as speed and whatnot.
Post Reply