UserData in Irrlicht

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
tuXXX
Posts: 6
Joined: Fri May 11, 2007 3:22 pm

UserData in Irrlicht

Post by tuXXX »

Hello,

I'm currently using Irrlicht to do a small game, and I'm using the collision manager to detect the clicked object in the interface. But the collision manager only returns an ISceneNode, and I want to get my own class.

I used Ogre in another project, and it was possible to set and get user data by using the method "setUserObject" on the class "MovableObject" returned by the ray casting.
Irrlicht's collision manager returns an ISceneNode, but there is no way to store a pointer there (and I think that having to create a map between the id of the scene node and my class is not really a good solution).

Maybe I missed something, if so please head me int the good direction.
Actually I patched ISceneNode.h with the following patch and it's working very well :

Code: Select all

--- irrlicht-1.3/include/ISceneNode.h	2007-03-10 10:32:08.000000000 +0100
+++ irrlicht-1.3-mod/include/ISceneNode.h	2007-05-02 14:18:43.000000000 +0200
@@ -40,7 +40,8 @@ namespace scene
 			: RelativeTranslation(position), RelativeRotation(rotation), RelativeScale(scale),
 				Parent(parent), ID(id), SceneManager(mgr), TriangleSelector(0),
 				AutomaticCullingState(EAC_BOX), IsVisible(true),
-				DebugDataVisible(EDS_OFF), IsDebugObject(false)
+				DebugDataVisible(EDS_OFF), IsDebugObject(false),
+				UserData(NULL)
 		{
 			if (Parent)
 				Parent->addChild(this);
@@ -604,6 +605,16 @@ namespace scene
 			updateAbsolutePosition();
 		}
 
+		inline virtual void setUserData(void *data)
+		{
+			UserData = data;
+		}
+
+		inline virtual void *getUserData()
+		{
+			return UserData;
+		}
+
 	protected:
 
 		//! name of the scene node.
@@ -650,6 +661,9 @@ namespace scene
 
 		//! is debug object?
 		bool IsDebugObject;
+		
+		//! User data
+		void *UserData;
 	};
 
 } // end namespace scene
sio2
Competition winner
Posts: 1003
Joined: Thu Sep 21, 2006 5:33 pm
Location: UK

Post by sio2 »

Forget it. This was suggested months ago (void pointer) and was shot down in flames.
:|
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Good idea I might use that patch sometime. Currently Im just either using the node's name, or an array that corresponds to the node's ID (Eg. NetIDs in irrNet) to store node-specific data. In irrEdit there is a button to "Add" User-data but it comes up with the message "The ability to add arbitary user data is not yet available.". So maybe itll be coming soon huh...
tuXXX
Posts: 6
Joined: Fri May 11, 2007 3:22 pm

Post by tuXXX »

I found the previous topic about userdata : http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=17164
(and the patch available here is the really close to the one in this topic)

Currently I'm a little disappointed that something as basic as this won't be added to Irrlicht.
Luben
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Post by Luben »

Are user data pointers really that necesarry? Can't you just design your programs in a way that doesn't require them?
If you don't have anything nice to say, don't say anything at all.
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

Luben wrote:Are user data pointers really that necesarry? Can't you just design your programs in a way that doesn't require them?
Not without making your application slower.
Image
Saturn
Posts: 418
Joined: Mon Sep 25, 2006 5:58 pm

Post by Saturn »

Luben wrote:Are user data pointers really that necesarry? Can't you just design your programs in a way that doesn't require them?
Read the other thread, discussed to death over there. No you can't without double book keeping.
Having user data in the scene node is the cleanest solution by a great margin.
Luben
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Post by Luben »

Nah i meant more like, when do we really need to use the userdata from a scene node?
Ok, irrlicht's collision system, but an application that uses irrlicht's collision system is most probably not an app that will suffer tremendously from double book keeping/making the app a tiny bit of a fraction slower. But of course, i'm not writing apps like that, so i wouldn't know :)
If you don't have anything nice to say, don't say anything at all.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I don't buy either argument.

If you write your code so that some external entity owns the scene node, that same entity can own the physics information for the scene node. There is no double book keeping that I see, the Entity type maintains the physics information and the visual information. If you need to get the Entity from the scene node, like you do with picking, you need a mapping from node back to the entity. When an entity is initialized, it would create an entry and add itself and its scene node to this mapping, and when it is destroyed it would remove that entry.

As for performance, the method I've suggested above will perform just fine. I went ahead and wrote up a test. I created an array of 10000 scene nodes and their IDs. If I look up _every_ scene node in this array _every_ render I get 52fps. If I just take the ID that is stored in the scene node directly I get 54fps. I personally do not feel that is a significant performance penalty. In most cases you aren't going to need to search for every scene node in the map anyways.

Code: Select all

struct SEntry
{
  scene::ISceneNode* Node;
  s32 Identity;

  friend bool operator<(const SEntry& lhs, const SEntry& rhs)
  {
    return rhs.Node < lhs.Node;
  }

  friend bool operator==(const SEntry& lhs, const SEntry& rhs)
  {
    return rhs.Node == lhs.Node;
  }
};

core::array<SEntry> Entries;

f32 frand(f32 min, f32 max)
{
  f32 r = 1.f * rand() / RAND_MAX;
  return min + r * (max - min);
}

void generateBoxesAboveTerrain(scene::ISceneManager* smgr, scene::ITerrainSceneNode* terrain, f32 heightAboveGround, u32 count)
{
  const core::aabbox3df& bbox = terrain->getBoundingBox();

  u32 n;
  for (n = 0; n < count; ++n)
  {
    core::vector3df pos(frand(bbox.MinEdge.X, bbox.MaxEdge.X),
                        0,
                        frand(bbox.MinEdge.Z, bbox.MaxEdge.Z));
    pos.Y = terrain->getHeight(pos.X, pos.Z) + heightAboveGround;

    scene::ISceneNode* node = smgr->addCubeSceneNode(10.f, terrain, rand(), pos);

    SEntry e;
    e.Node = node;
    e.Identity = node->getID();

    Entries.push_back(e);
  }
}


// in main, inside the render loop

    u32 hash = 0; // calculate hash so that optimizer can't remove loops

//#define USE_ENTRY_MAP
#ifdef USE_ENTRY_MAP
    SEntry entry;
    entry.Identity = 0;

    // access the node specific data via map lookup
    u32 n;
    for (n = 0; n < Entries.size(); ++n)
    {
      entry.Node = Entries[n].Node;

      s32 found = Entries.binary_search(entry);
      if (found != -1)
      {
        s32 id = Entries[found].Identity;
        hash = (hash << 6) | id;
      }
    }
#else
    // access the node specific data directly

    u32 n;
    for (n = 0; n < Entries.size(); ++n)
    {
      s32 id = Entries[n].Node->getID();
      hash = (hash << 6) | id;
    }
#endif

    printf("%u\n", hash);
Travis
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Post by roxaz »

um vitek, on what hardware you did the test?
Saturn
Posts: 418
Joined: Mon Sep 25, 2006 5:58 pm

Post by Saturn »

vitek wrote:There is no double book keeping that I see (...) If you need to get the Entity from the scene node, like you do with picking, you need a mapping from node back to the entity.
That's the double book keeping right there. You need a mapping from ISceneNode to your GameObject type when you want to use the irrlicht collision and it is likely the only thing you need this mapping for. And everyone needs it, if he/she wants to use irrlicht collision. So everyone has to implement it him/herself in one way or another. And any such solution is of greater complexity than a userdata pointer in ISceneNode, as you own code nicely demonstrates.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

on what hardware you did the test?
The test was run on a Lenovo T60p laptop (Intel Core 2 T7600 @ 2.33 GHz, 2GB of RAM). This was also with a release build, using the D3D8 driver.
That's the double book keeping right there.
You may call this double book-keeping, but I don't really see it as that. The user data that would have been in the scene node is kept somewhere else. The only thing that is duplicated is a pointer to the scene node. If an entity system is created, then the insertion and removal from the external array can be made automatic.
And any such solution is of greater complexity than a userdata pointer in ISceneNode
Yes, you have to write a total of 12 lines of code. I don't really see this as being that much more complex than putting the data right in the scene node. The array insert and search can easily be put into functions that are only written once. That is the only part that is more complex. Once those are written, the only difference is the name of the function and how you call it. If you use an entity system, most of this stuff would be hidden in the entity implementation. The only thing that you'd need to provide would be a public static method for getting the entity for a scene node.
So everyone has to implement it him/herself in one way or another.
Yes. If you put the pointer directly in the scene node, there is the issue of ownership that needs to be addressed. Lots of guys are going to want to do things like this... node->setUserData(new Thing). Now they need to be smart about cleaning up the user data. A simple call to node->remove() could result in a memory leak. But you can't remove the user data before you remove the node from the scene graph, because there may be an outstanding reference to the scene node. The scene node can't necessarily just go and delete the user data either, because it might be on the stack, or it could be a shared reference counted object. Only the user application knows what to do with the user data, but the scene node itself knows when it needs to be cleaned up.

So now, to fix that issue, you will need to add some sort of notification system for scene node deletion, or you need a user data wrapper interface that knows about the actual user data and how to clean that up. Or, you put your user data into an external array and clean that up periodically.

If you create some sort of entity system, this isn't really a problem because your entity destructor knows how to clean up the application specific user data. But in order to take advantage of that, you have to write the entity system. i.e. to avoid writing code for accessing a user data you add a user data pointer, but to be able to safely use it, you have to write more code. hmmm...

Travis
Saturn
Posts: 418
Joined: Mon Sep 25, 2006 5:58 pm

Post by Saturn »

vitek wrote:
And any such solution is of greater complexity than a userdata pointer in ISceneNode
Yes, you have to write a total of 12 lines of code. I don't really see this as being that much more complex than putting the data right in the scene node. The array insert and search can easily be put into functions that are only written once. That is the only part that is more complex. Once those are written, the only difference is the name of the function and how you call it. If you use an entity system, most of this stuff would be hidden in the entity implementation. The only thing that you'd need to provide would be a public static method for getting the entity for a scene node.
More complex.
vitek wrote:
So everyone has to implement it him/herself in one way or another.
Yes. If you put the pointer directly in the scene node, there is the issue of ownership that needs to be addressed. Lots of guys are going to want to do things like this... node->setUserData(new Thing). Now they need to be smart about cleaning up the user data. A simple call to node->remove() could result in a memory leak. But you can't remove the user data before you remove the node from the scene graph, because there may be an outstanding reference to the scene node. The scene node can't necessarily just go and delete the user data either, because it might be on the stack, or it could be a shared reference counted object. Only the user application knows what to do with the user data, but the scene node itself knows when it needs to be cleaned up.

So now, to fix that issue, you will need to add some sort of notification system for scene node deletion, or you need a user data wrapper interface that knows about the actual user data and how to clean that up. Or, you put your user data into an external array and clean that up periodically.

If you create some sort of entity system, this isn't really a problem because your entity destructor knows how to clean up the application specific user data. But in order to take advantage of that, you have to write the entity system. i.e. to avoid writing code for accessing a user data you add a user data pointer, but to be able to safely use it, you have to write more code. hmmm...
Irrlicht doesn't have to care about ownership of the user data. To a seasoned developer this is obvious, the not so seasoned developer can read it in the API docs to this function. Everything else you write here doesn't matter really as you present a defunct case. Someone who writes node->setUserData(new Thing) either has an idea about the consequences or not. If he/she has, he/she is able to cope with it. If the user hasn't, then there are many other places left to do the same error.

As a summary stays: the use of a user data tag outweights the costs, and even if it is only by 0.5 fps and 12 code lines per application.

Also add to the benefit at large the better feeling in the Irrlicht community if a long standing, much repeated, easy to write addition is finally implemented.
The opposition with which such small obvious improvements are met is quite disconcerting. Over the time such things seem to become more a personal matter than anything else.

Here is a link to a thread that I think is a classic. A humble, non-hurtful request and see how it unfolds.. But this one had a happy end. :)
ACE247
Posts: 704
Joined: Tue Mar 16, 2010 12:31 am

Re: UserData in Irrlicht

Post by ACE247 »

OH LOL not THAT Ogre thread!
Someone even brought up the idea of guns and killing someone! :)

(Yes i was nostalgically browsing the forums :D)
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: UserData in Irrlicht

Post by Cube_ »

well.
I can see several reasons to have user data pointers in a node eg. healt values, other character values (rage, stamina mana, strength. you name it, ammo), use your imagination and the potential uses are endless ^^*
"this is not the bottleneck you are looking for"
Post Reply