Skip to main content
GameDev.net gamedev.net
🔒 Locked

Scene Graph and Base Node Class Design

Started by Raeldor Aug 23, 2006 at 8:02 PM 5 replies 700+ views
Original Post
Raeldor
Raeldor
Hi Everyone, I am doing a code review on my code so far and am looking at my base node class (base class from which the scene hierarchy is built). It is getting a little larger than I imagined and I am wondering if my class design is as good as it could be. For example, I have a m_renderPass member which can be values such as LIGHT, TERRAIN, UI etc. Does this really belong in the base node class though, or should it be in some kind of 'RenderableNode' sub-class? I guess the fundamental question is how far do I go with class hierarchy to make the classes space efficient without making them performance and maintenance inefficient? Thanks for any advice! Rael
swordfish
swordfish
Remember that it all depends on your specific implementation. One person's route might be to do it the way you're doing it. Another person might do it another way, depending on their individual needs. Personally, I'm quite fond of the way WM3 approaches it, as it is the only way that handles it in a way that I am familiar with:

Object (naming and RTTI)
|
Spatial (geometric state), etc.
||
(Various functioning nodes and node-types)
(Everything below this level has a proprietary interest in rendering)
Node (hierarchy, parent-child relationship, grouping, etc)
Geometry ('geode', cannot have children)
Light (self-explanatory)
etc..

Geometry
|||
TriMesh
etc.
||||
ScreenPolygon
etc.

Node
|||
Terrain
etc.
http://blog.protonovus.com/
JasonBlochowiak
JasonBlochowiak
Your scene graph nodes should not also be renderable things - renderable things should have their own inheritance heirarchy that are bound to scene graph nodes.
Raeldor
Raeldor
Thanks for the replys guys.

So if your implementation of renderable objects is seperate from the node, would you still have some kind of 'node type' such as NODE_LIGHT, NODE_MESH etc. as well as the reference to the renderable class, or would you just have a NODE_RENDERABLE and let the class handle the details?

Also, would the 'NodeType' property be what people refer to as run-time typing, or is that something more complicated?

Thanks again
Rael
JasonBlochowiak
JasonBlochowiak
Quote:
Original post by Raeldor
Thanks for the replys guys.

So if your implementation of renderable objects is seperate from the node, would you still have some kind of 'node type' such as NODE_LIGHT, NODE_MESH etc. as well as the reference to the renderable class, or would you just have a NODE_RENDERABLE and let the class handle the details?

Also, would the 'NodeType' property be what people refer to as run-time typing, or is that something more complicated?

Thanks again
Rael


I've done it a few different ways. Last time around, I had my lights inherit from my scene graph node, on the basis that a light IsA positionable thing. This time around, lights will not be inherited from nodes, they will be bound to them, the same as meshes, etc.

If I can manage it, there will be no references in the nodes to what clients have bound to them. So, no, the node wouldn't have any type beyond "I'm a position and orientation node, and I have these nodes as children."

From the standpoint of a renderable thing, like a mesh, all you care about the node is that it will end up providing you with one (or more, perhaps, in the case of a skinned mesh) transformation matrix for you to feed to DX or GL.

You might want to have the clients hint the node as to group membership or plug in spatial subdivision schemes for clustering, but I'm pretty sure you'd be able to do that without the nodes needing to know about clients.

As to your other question, yes, anytime you query the type of a thing at run-time, then you're making use of run-time typing. Storing an enum is one way that's done. Using C++'s built in RTTI is another. Generally, though, doing something like the following is frowned on:

void foo(thing *x){ thingType t = x->type(); if (t == type_a)   ; // do something here else if (t == type_b)   ; // do something else here}


Why? Well, it's moving responsibility for doing something with a "thing" out of thing's code, which is pretty much the opposite of what you want to be doing with object oriented code.


To get back to the point of your original question, I would recommend you put your m_renderPass member inside your Renderable base class, and that you then provide covered access. So if you have your RenderDevice with lists for each render pass (or, one list, with elements sorted first by render pass), a call to renderable->SetRenderPass() would not only adjust its m_renderPass member, but do the appropriate thing in terms of list removal and addition.
Raeldor
Raeldor
Some great tips in there Jason, thank you. I'm going to do a UML model of my class design too which will hopefully make things a little clearer. It's getting big enough now where it's hard to visualize mentally.

Thanks!
JasonBlochowiak
JasonBlochowiak
Quote:
Original post by Raeldor
Some great tips in there Jason, thank you. I'm going to do a UML model of my class design too which will hopefully make things a little clearer. It's getting big enough now where it's hard to visualize mentally.

Thanks!


Glad to be of help.

FWIW, I've not ever done an actual formal UML model, but doing some UML-ish rough sketches has definitely been useful to lay out my thoughts.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.