Obtaining specializations from a scene graph

Started by
18 comments, last by matt77hias 6 years, 5 months ago

How does or could one obtain specializations from a scene graph?

I currently use a TransformNode that looks something like this:  


 __declspec(align(16)) struct TransformNode final 
        : public AlignedData< TransformNode > {

    private:

        friend class Node;

        SharedPtr< Transform > m_transform;
        Node *m_parent;
        vector< SharedPtr< Node > > m_childs;

        mutable XMMATRIX m_object_to_world;
        mutable XMMATRIX m_world_to_object;
        mutable bool m_dirty_object_to_world;
        mutable bool m_dirty_world_to_object;
    };

The TransformNode encapsulates a local Transform (parent <> object) as well as its positioning in a scene graph (parent and child Nodes). A Node itself is some proxy on top of a TransformNode, allowing to do node->getTransform()->SetTranslation() via an indirection instead of directly calling and polluting the node itself (e.g. node->SetTranslation()). The Node class forms the root of a hierarchy containing abstract models, lights, sprites, cameras which all have their own specializations.

Given some Node, how does one normally convert it to its specialization?

  • Each node has a name, so I can search for a specific node and perform a dynamic_cast based on a template type provided by the user. Though this requires names to be globally unique or at least unique in every subtree of the scene graph.
  • If all the specializations are known in advance, then a static_cast can be used instead of a dynamic_cast if some enum member specifies the specialized type.
  • If all the specializations are known in advance, I can use specialized collections instead of one general collection in TransformNode.
  • The scene graph can support general visitors, but this requires an expensive double dispatch as well as lots of boilerplate code for a single implementation of the visitor.

For rendering purposes, I also have some flattened collections of the scene graph: models, specialized lights, cameras and sprites. But I do not have the need to have a collection for each specialization. For the lights, I explicitly want to know the most specialized types, but for cameras I do not really care for rendering and can just work with the abstract camera interface.

🧙

Advertisement

If you're considering dynamic_casts or need to convert from base to specialization, then you most likely have a fundamental design problem on your hands and are trying to do something you shouldn't be.

Do you even need to build a linked hierarchy this way, or can you give it to a higher-level construct (scene graph itself)? What is the purpose of these specializations? Do they have anything in common, can they share an interface? Is this transform node actually necessary, or are you putting in extra effort for a non-existent problem?

If the goal is to keep transform separate from the node, then just use composition and have each node contain a transform that it can then plug into the scene graph. There's no reason to use inheritance here, IMO.

The hierarchy looks like this:

https://matt77hias.github.io/MAGE-Doc/MAGE-Doc/html/classmage_1_1_node.html

1 hour ago, Styves said:

If the goal is to keep transform separate from the node, then just use composition and have each node contain a transform that it can then plug into the scene graph. There's no reason to use inheritance here, IMO.

But what if you have child nodes? Lets say you have a perspective camera node and add an omni light node as child (or alternatively one can specify this for a transform as well), how do you obtain the omni light node given that perspective camera node?

🧙

You don't, because that's horrid design and asking for a spaghetti mess at the end.

 

Tell your nodes to process themselves, and they can handle any components/children as appropriate. Your main processing can be one line of code: tell the root node to process.

This "process" might be Tick(), or NetworkSync(), or Render(), or any number of other operations.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

8 minutes ago, ApochPiQ said:

tell the root node to process

But then I have a giant number of virtual method calls. Now I have 0 virtual method calls.

Furthermore, the issue remains for scripting. You cannot retrieve any specializations if you do not have a pointer or reference directly to those specializations.

🧙

31 minutes ago, ApochPiQ said:

This "process" might be Tick(), or NetworkSync(), or Render()

My Update()/FixedUpdate() is down in some script instance which needs to store all the needed nodes.

The Render() is down outside the node itself. The node only provides an interface to access the data needed for rendering.

🧙

Sounds like you are way over complicating things.

I liked this article when it first came out: http://lspiroengine.com/?p=566

Really opened my eyes to what it was I was wanting to do.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

37 minutes ago, Mike2343 said:

I liked this article when it first came out: http://lspiroengine.com/?p=566

I read the article, but it still puzzles me. :(

My scene graphs (multiple roots are possible) are not the primary scene data structure, neither do I operate on the scene graph for rendering or frustum culling. I have flattened collections (this works for now but has a linear complexity) of nodes in my scene itself which I iterate for these purposes.

The scene graph is only used for propagation and has some general methods for doing this as well as some specialized transform related methods with dirty flags. This is similar to Unity3D's hierarchies which always contain a transform and thus transform relationships at the very least. My scene graph doesn't do any processing.

So this looks not like violations against the design of a scene graph.

This however still does not specify how that you get some component of some child. The very least a scene graph does is specify the relationships between parents and childs.

 

🧙

I hope L.Spiro reads this one. :)

Why do you need to get "some component of some child" if no processing is occurring?

What exactly is your goal? What problem are you trying to solve? It's really difficult to give you a straight answer without knowing what the problem domain is.

Also, if you want to avoid virtual calls but still process the hierarchy the way ApochPiQ mentioned then just break things up into lists of specialized components (light, camera, etc) and iterate over each list separately, keeping a simple link to the node/transform if necessry. Don't over-complicate it with OOP.

For example you can sort the nodes in order of the hierarchy with parents coming first and then process them in linear order, without even leaving the current function and without the need for any virtual inheritance. Any special behavior can just be linked to the nodes in some way.

14 minutes ago, Styves said:

Also, if you want to avoid virtual calls but still process the hierarchy the way ApochPiQ mentioned then just break things up into lists of specialized components (light, camera, etc) and iterate over each list separately, keeping a simple link to the node/transform if necessry. Don't over-complicate it with OOP.

I have similar collections in my Scene. Furthermore, I still have composition for my scene graph (difference between Node and TransformNode). I know this seems way too complex, but so far it accomplishes the things I wanted it to support.

14 minutes ago, Styves said:

What exactly is your goal? What problem are you trying to solve? It's really difficult to give you a straight answer without knowing what the problem domain is.

If I load hierarchical models with submodels, I cannot use non-root nodes to their full potential, because I cannot access them (unless I use my previous proposals). If a plane has some propeller attached to some other part, I cannot get the propellor.

For the submodel<>model specifically, I don't want to have another hierarchy. The only thing that such submodels share is just a mesh, so special treatment is overkill.

 

16 minutes ago, Styves said:

Why do you need to get "some component of some child" if no processing is occurring?

Scripting.

🧙

This topic is closed to new replies.

Advertisement