Scenegraph : alternatives to virtual methods and visitor pattern ?

Started by
8 comments, last by paic 15 years, 8 months ago
Hi, I need some advice with my scenegraph. I'm implement an engine, and I decided to use a scenegraph for my scene. Basically, my engine works in 2 steps : 1-Update and 2-Render. So, my first idea was to have 2 virtual methods, Update() and Render() in my base node interface. It's simple, its easy to use. But then I created an editor, and I needed to be able to select objects by clicking on them. So, I needed to add a virtual method CheckRayCollision() (or something like that) in my base node interface. And you see my point : each time I need a new functionality, I need to create a new virtual method. And that's really bad. So, I found the visitor pattern. I recoded my Update and Render methods using this pattern, and it worked fine. I could then create as many visitors I wanted, without having to touch my base node interface. Great. But then, I wanted to add a new node's type in the editor. And the problem is : if I want this node to be handled by the visitors, I need to modify the visitors classes. To sum up : - virtual methods allow creation of new node types really easily and without having to touch anything, but adding new functionalities is a pain in the ... - visitor pattern is great for adding new functionalities without have to touch the manipulated nodes, but adding new nodes' types is a pain in the ... Question : is there any pattern or method that would have both advantages ?
Advertisement
Hi,

adding a new type while using the visitor pattern should not be a pain in the ...
You add one virtual function call to your visitor class and thats it, isn't it?
Or if you subclass from for example a group node to be a switch node. And now you want a new visitor that does special things with the switch node, in the method that handles the group you would check if the group is a switch (e.g. with dynamic_cast in c++).

The other question is, do you really add that many new types?

Hope I could help.
“Always programm as if the person who will be maintaining your program is a violent psychopath that knows where you live”
Hi,
Thx for the reply.

Well... there's a few things I probably should have mentioned :

- my visitors all inherit from an interface IVisitor. It's part of the pattern, and needed if I want to be able to create a lot of different visitor classes. So when I add a new node's type, I need to update the IVisitor interface (which leads to a recompile of all the derived visitor classes ...) and the visitors that will be applied to the new node's type. That's for the "pain in the ..." part :)

- my engine is in a project and compile as a .lib, and I have an other project for the scene editor which uses this .lib. In the engine, I have only the stuff I will need in the final product (no debugging stuff, no helper interfaces, editor only nodes, etc.) The main visitors (render, update, visibility, etc.) and the IVisitor interface are in the engine's project. And all the visitors used only in the editor (one for selecting an object when clicking on it, one for selecting nodes which uses a resources, etc.) as well as nodes types only used in the editor (a 2D grid, path drawing, all the helper nodes, etc.) are ... in the editor's project.

The result of the 2d point is : when I add a new node's type to the editor, I need to update the IVisitor interface of the engine, and that leads to the recompiling of all the visitors of engine and editor ...

And I have a lot of stuff to add to the editor, since I'm just currently starting it :)


I remember having read something about "component design" or something like that, and I think it was applied to scenegraphs, and was apparently a good way to decouple data and processing without the limitation of virtual methods and visitor pattern ... but I can't find the article.

If anyone have an idea ...
Thx in advance.
I think you'll be hard pressed to find a good solution to the problem as (a bit generalized)
- Virtual methods decouple your data from your algorithms. Forcing algorithms to work though a common data interface.
- Visitor pattern decouple your algorithms from your data. Forcing data to work though a common algorithm interface.

If you fully decouple both sides, neither will know what the other can do.
Perhaps a compromise on both parts might be more suiting. Both provide restricted interfaces which the other side can use. Node types providing similar data can be grouped up and hidden behind a common interface as dragongame pointed out. The visitor pattern can still provide a good way of handling how your algorithms handle different groups of data, though the smaller details are hidden by virtual methods in the group's base classes.
The very first part of your first post gave me some shivers. You seem to be updating and rendering the same objects, which is fine for small programs but creates issues on large programs.

What you would ideally want to do is to separate the rendering from the logic (this is also known as the model/view separation). The model stores the data and implements the elementary update logic (such as physics), while the view displays the data on the screen and allows interaction (for instance, for picking).

So, have a 'ModelNode', which you can inherit from to add new types, and which only has "update" behavior, and a 'ViewNode' which is associated to a 'ModelNode' and serves to render that node.
Problem with exposing a problem here is that you have to simplify it so that explaining your setup won't take 2 pages :)

A little more detail on my implementation : Nodes only contain data. For example, geometry nodes contains an array of little "renderable" structures containing everything needed to draw something on screen (index of a VB, IB, textures, whatever.)
And for rendering, I use a RenderVisitor which walks through my scenegraph and collect those Renderables, and send them to the Renderer.

Now, what you wrote is another way to do that, and it may be good, but I don't really see how it relates to my problem : in my case, let's assume I want to add a new node's type for collision triggers, and after that I want to add a functionality which allows the user to click on something on screen, and get the corresponding node selected in the scenetree ? (the node being a static mesh, an animation curve, a collision trigger, etc.)

What kind of design would allow that with the minimum recompiling ? (keeping in mind that the collision trigger will be something added in the engine since it's used in the final game or simulation, and the functionality will be an editor only stuff)
Here I'm really looking for design choices other than visitors or virtual classes. I'm still learning everyday, I know the drawbacks of the 2 methods, and I don't really need people reminding them. I need to learn something new, new designs that could potentially help me overcome those drawbacks :)

Anyway, thanks for the answers, and don't hesitate to point me to books, articles or whatever learning source about scenegraph, engine design and stuff like that :)
Quote:Original post by paic
I remember having read something about "component design" or something like that, and I think it was applied to scenegraphs, and was apparently a good way to decouple data and processing without the limitation of virtual methods and visitor pattern ... but I can't find the article.


Try searching for component based. clicky
Quote:Original post by paic
Now, what you wrote is another way to do that, and it may be good, but I don't really see how it relates to my problem : in my case, let's assume I want to add a new node's type for collision triggers, and after that I want to add a functionality which allows the user to click on something on screen, and get the corresponding node selected in the scenetree ? (the node being a static mesh, an animation curve, a collision trigger, etc.)


It seems that the problem here is too much abstraction. I see no way that a static mesh, an animation curve and a collision trigger can be manipulated together using a similar interface. I would keep the static mesh in the scene graph, adapt the animation curve as a form of movement, and move the collision trigger to the collision detection system (inside the model and outside the view).

Of course, if you're designing an editor, then the fact that a given object will be, in the engine, a static mesh, animation curve or static trigger, is irrelevant to your editor. All the editor needs to know is that the object you're manipulating is an "engine entity" with a certain simple set of properties, thereby voiding the need for a class hierarchy (and inheritance) and instead using a data-driven solution.

I would personally have an EditorMesh class (that is, a mesh used by the editor to render things and detect clicks on things) and an EngineEntity class as my sole objects. Since the engine entities behave differently in the engine, but have the same behavior in the editor (get/set properties), I would equip the class with a map of named properties, with adapted editors (so, a "text" field is just a text input field in the editor, whereas a "position" field would spawn an EditorMesh to represent that position and allow changing it). Then, what matters is the individual fields, and not the entities. Since a field represents a data type (a position, an orientation, an integer, a boolean, a real, a material, a mesh, the identifier of another entity, a script path) and you don't have that many data types, it should reduce the amount of work necessary.
Your problem is the well understood Expression Problem. Your solution is precisely on the right track. Check this paper to see solutions proffered in various languages. I dont know C++ but Im sure the ones for java or even Ocaml can be backported to C++ - I suspect the solution would involve templates - the magical hammer to all C++ problems.
Thx for the links, I'll have a lot of stuff to read and understand for the WE it seems :)

But you can keep the advices coming :p

This topic is closed to new replies.

Advertisement