Custom UserData for an Entity3D

Jul 10, 2013 at 11:56 PM
Edited Jul 10, 2013 at 11:57 PM
I need additional custom data that can be stored inside an Entity3D object.

Can u implement something like this inside the Entity3D class:
void* m_pUserData;
SetUserData(void* data)
void* GetUserData();
The reason i need this is because i dont want to make a SpecialEntity3D : public Entity3D class only for give the Entity3D data like:
Filter* filer = new Filter(); //example class
for example, so i can make things like:
_entity->SetUserData(filter);
and get it back with:
Filter* filter = (Filter*)_entity->GetUserData();
Box2D for example makes the same for the bodies for storing custom data.

good idea or bad idea?
Coordinator
Jul 11, 2013 at 12:04 PM
This is one possibility, for sure. However, by using a void pointer we would lose all type information on the data - which as you show above would require a blind cast in order to get the Filter object. Normally for the Entity3D classes I would recommend having a Controller to add new functionality. There are a few examples in the engine, and since a controller's update method is called every frame update, this would also let you put some logic into this smaller module without modifying the Entity3D class itself.

If the controller system doesn't work for you, I have also in the past used the Actor class to hold the 'extra' objects, and then just use them for the non-rendering actions. That might also work for you, but I'm not sure (see for example the TextActor).

If neither of those work in your situation, then I can add an API similar to what you describe above. Can you (or anyone else out there) think of a type safe way to do this instead?
Jul 12, 2013 at 1:09 AM
Edited Jul 12, 2013 at 2:02 AM
First thanks for your answer!
void ViewLightIDMap::ExecuteTask( PipelineManagerDX11* pPipelineManager, IParameterManager* pParamManager ) {
    if ( m_pScene ) {
        pPipelineManager->ClearRenderTargets();
        
        //Sets the IdBuffer as renderTarget.
        pPipelineManager->OutputMergerStage.DesiredState.RenderTargetViews.SetState( 0, m_pRenderTarget->m_iResourceRTV );
        pPipelineManager->ApplyRenderTargets();

        //Configure the desired viewports in this pipeline
        ConfigureViewports( pPipelineManager );

        //Clear the G-Buffer targets
        pPipelineManager->ClearBuffers( m_vColor, 1.0f, 0.0f );

        //Set this view's render parameters
        SetRenderParams( pParamManager );

        // Run through the graph and render each of the entities
        std::vector<Entity3D*> set;
        m_pScene->GetRoot()->GetEntities( set );

        for ( auto pEntity : set ) {
            if ( pEntity != this->m_pEntity ) {

                void* userData = pEntity->GetUserData();
                if(userData != 0) {
                    Filter* filter = (Filter*)userData;

                    bool collide = (_maskBits & filter->maskBits) != 0;

                    if(collide) {
                        pEntity->Render( pPipelineManager, pParamManager, VT_LIGHTS );
                    }
                }

            }
        }
    }
}
Here is the example where i need it. It renders for each light an obstacle map of all objects that cast shadows. On this map the shadow will be calculated later.

I give the entity the filter that contains flags for determining if the entity is for example Monster, Player, Light or other SceneObjects. The mask checked with the mask of the view and if the mask matches the object will be drawed as obstacle.

The problem is here, that i have no access to the actor. The Controller is one possibility without modify your engine. But too much i think. Dont need the update each frame of the controller.

Here is a link how Box2D solve it (like me now) -> http://www.iforce2d.net/b2dtut/user-data

"Can you (or anyone else out there) think of a type safe way to do this instead?"

I look inside the Ogre3D engine and i see they solve it like this:
http://www.ogre3d.org/docs/api/html/classOgre_1_1MovableObject.html
virtual void setUserAny (const Any &anything)
virtual const Any& getUserAny (void) const 
I think the Any class is something like a Variant class.

"A Variant is used to represent arbitrary type of value. Any value, such as but not limited to, integer, float, pointer, object, reference, can be stored in a Variant"

... But the source of Ogre3D is open. You can look inside the class.

I think its a good improvement. And makes few things easier to handle.
Coordinator
Jul 12, 2013 at 11:59 AM
Ok - I think this is a good idea to add some extra customized information. I'm not sure if it should be a CustomData base class or if we should just go for void pointers though... I will need to think about that a little longer to understand what would be best...

For your particular situation, I think you could just set your filter ID as a vector parameter and attach it to the entity. Then the value can be read out and checked to see what type it is during the SceneRenderTask evaluation. That might get you up and running quickly. Do you think that could work?
Jul 12, 2013 at 1:48 PM
Edited Jul 12, 2013 at 1:53 PM
Do it as a vector parameter would be one solution i thought about too (if only integer or float datatype). To add more custom data in the future would be great.

Later perhaps i will change Filter (SetUserData(_filter)) to Actor (SetUserData(_actor)), that contains the Filter anyway. Because my Actor knows the Entity ... but sometimes you need the opposite of it, so that a Entity knows his Actor for example.
Coordinator
Jul 15, 2013 at 5:53 PM
I can add this one in - it makes sense. It is just going to be at least a week until I can get to it, as I have been fully booked for a while. It will come in the next commit though!
Coordinator
Jul 29, 2013 at 2:02 AM
This has been added in the most recent commit!