Jump to content
  • Advertisement
Sign in to follow this  
snk_kid

OpenGL Scene Graph Resources

This topic is 4613 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

As scene graphs are a such an important and frequently questioned topic here (and else where) i decided to begin compiling a comprehensive listing of resource links about them for those interested in implementing/using them. Hopefully this will become a sticky for all to find of great use. To those who have not done so already and are new to the topic of scene graphs i recommend reading up on some/all design patterns in particular:
  • Composite - reason being that scene graphs are typically implementated with this pattern
  • Decorator - reason being it's related to Composite and is in fact a degenerate form/case of Composite where decorators only have a single or fixed number of children. It is also frequently used with the Composite pattern. In the case of scene graphs implementated via the Composite pattern we have Grouping node types (they are the Composites) typically there will be an instantiable base Group node type who's sole purpose is child containment and child management, this instantiable base is just a plain instance of the composite pattern and the most general grouping node type of all in a scene graph. Then we add more specific kinds of grouping nodes, like transforms, switch nodes, LOD nodes etc these are sub-types of the base group node type thus inherit child containment and child management (code-reuse) but also adding new behaviours/state to internal nodes of a tree/DAG (the reason why i emphasis this is it does not make sense to have something like a Switch/LOD node who's sole purpose is to select/switch between other nodes as terminating/leaf nodes of a tree/DAG). The specific kinds of grouping node types are Composites aswell they are also instances of the decorator pattern too!.
  • Interpreter - reason being it's related to composite but the main point is the idea of a "global" context i.e SG could have global state or rendering context.
  • Observer - Scene Graphs are/use the Observer pattern implicitly without the developer realizing it but it can be used explicitly when scene graphs are DAGs because there maybe occasions where leaf nodes need to notify state changes to all parents (and there parent's parents and so on all the way upto the root/roots if necessary). Therefore leaf node types are subjects and grouping node types are the observers (they are also subjects when viewed in a different context since grouping node types typically share a common base with leaf node types).
  • flyweight - reason being you can permit node sharing in an n-ary tree without resorting to use a directed acyclic graph (DAG) which are harder to deal with but more flexible.
  • Visitor - reason being seperating the operation and data of scene graphs, it will beome more apparent when this pattern is read

Caveat: The main point/advantage of the composite pattern is the distinction between internal (non-terminal) and leaf (terminal) node types (yet they have a common super-type), Composite intances are internal nodes of a tree/DAG, concreate component instances are terminating, leaf nodes of a tree/DAG. The advantage of this there is no need todo a run-time check whether a node is a leaf/terminal, there is no redundant data of child containment in leaf node types (aswell as other state/behaviour that should only ever be in internal nodes of a tree/DAG). Here is the Caveat, Composite pattern is generally applied to OO designs indeed Gang Of Four (GOF) design patterns are concise & general designs/concepts to recurring solutions that arise in OO systems/languages. As such Composite pattern is implementated in-terms/via sub-type polymorphism (the type of polymorphism most widely known). However there is another method to achieve virtually the same (that is making a distinction between leafs and internal nodes) thing without sub-type polymorphism, without the need of a common polymorphic base, even no need for pointers & heap allocation by using something called recursive variants. Virtually all Functional programming languages (i don't mean imperative but some of them do also) natively support variants AKA disjoint disjoint (discriminated) unions, one value - finite set of types, they also can also be recursive (this is the key here). For example in SML we can describe a binary tree as such:
datatype 'a tree = Leaf of 'a | Node of (('a tree) * ('a tree));
This just basically says that a "tree" is either leaf of some type or an internal node. It's what we want. We can do this in C/C++, in C++ the best method is by using boost::variant, we an use boost::variant to achieve what the composite does and implement the basic structure of a typical scene graph interms of it:
#include <deque>
#include <boost/variant.hpp>

typedef boost::variant<
    struct leaf,
    struct leaf2,
    struct leaf3,
    boost::recursive_wrapper<struct group>,
> scene_node;

//...

struct leaf { /*...*/ };
struct leaf2 { /*...*/ };
struct leaf3 { /*...*/ };

struct group {
  
  typedef std::deque<scene_node> child_list; // notice recursive composition

private:  

  child_list kids;

public:

   void add_child(const scene_node& sn) { kids.push_back(sn); }
};

int main() {

    group root;

    root.add_child(leaf());
    root.add_child(leaf2());
    root.add_child(leaf3());
    root.add_child(group());

    group& inner_grp = boost::get<group>(grp.kids.back());

    inner_grp.add_child(leaf());
    inner_grp.add_child(leaf2());

}
You get the idea, read boost::variant docs on how to use it properly, use boost::static/apply_visitor etc. The interesting thing about this method you will still end up using inheritance but not for sub-type polymorphism or interfaces but purely for code reuse, no need for virtual destructors, not only that you will end up replacing sub-type polymorphism with static polymorphism using Curiously Recurring Template Pattern (CRTP). This idea is even possible in standard C (with some extra work) because C/C++ have union user-defined types but it is not a discriminated union (like boost::variant is) though, in C you can simulate discriminated unions using a simple technique called a tagged unions and enforcing state invariants through an interface. All tagged unions is is a union plus a type tag identifier, in pure standard C it will look like this:
typedef enum tag_type { FOO, BAR, .... } tag_type; // enumrated set of types

typedef struct my_variant_ {

    tag_type type_id;

    union { foo f, bar b, ... } var;

} my_variant;
Many C compilers have a non-standard extension that make this slightly less verbose:
typedef enum tag_type { FOO, BAR, .... } tag_type; // enumrated set of types

typedef struct my_variant_ {

    tag_type type_id;

    union { foo f, bar b, ... };

} my_variant;
Now we have variants in C we can apply this again to implement the composite pattern in C with recursive variants:
#include <stddef.h> // size_t
typedef enum tag_type { LEAF, LEAF2, GROUP };

typedef struct scene_node {
    tag_type type_id;
     union {
        struct leaf*   l1;
        struct leaf2*  l2;
        struct group*  grp;
     };
} scene_node;

typedef struct group {
     scene_node* kids; // pointer to an array of scene_node,
                       // notice recursive composition again
     size_t num_of_kids;
} group;

typedef struct leaf {} leaf;
typedef struct leaf2 {} leaf2;
Another advantage of this specifically to C it's slightly more type-safe, you can (almost) completely avoid void pointer syndrome. The obvious disadvantage to using recursive variants in C/C++ is the lack of pattern matching that statically typed functional languages enjoy aswell as the method completely automated, and type-checked. Saying that though its not to bad with boost::variant's static/apply_visitor. Lastly i'm not suggesting this method to be best/better than the traditional OO method of the Composite. i'm just stating it's not the only method. You could probably get the best of both worlds in OCaml.
I'll continuously add/update this, please PM me if you have any more links and i'll stick up here for all to see [smile]. [Edited by - snk_kid on November 4, 2005 6:29:34 AM]

Share this post


Link to post
Share on other sites
Advertisement
Excellent. I'm not a big fan of stickies however and would suggest moving it to the FAQ right away (as in my view it belongs there).

Tom

Share this post


Link to post
Share on other sites
Yay, I wrote most of that Wikipedia entry only the other week! - nice to see that its getting some publicity, the article only just hinges on some of the things that make up a good scene-graph but I think it is quite good as read-first article - thanks snk_kid [smile].

Share this post


Link to post
Share on other sites
Thumbs up to the #graphicsdev logs! (except for the Iain one simply because I was such a newbie back then :-P)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!