Scene Graph Resources

Started by
14 comments, last by jon723 18 years, 5 months ago
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.
  • Scenegraph Design Notes
  • Scene Graph/Renderer interface
  • Scene Graph. DAG or Tree With Mesh List?
  • Design Patterns used in the OpenSceneGraph
  • API-agnostic vertex storage and conversion to API-usable storage
  • Some UML class diagrams from me [embarrass]
  • SG State Sorting
  • Shader/Material System/Integration
  • SG Optimizations
  • Portal Integration
    • Portals (Old but described/relevant in the context of Scene Graphs)
  • Spatialization
  • 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]
    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
    Maybe this one? Official OpenInventor site @ SGI

    Regards,
    Hi,

    very good initiative ^^

    I would add this really good thread http://www.gamedev.net/community/forums/topic.asp?topic_id=110342
    Quote:Original post by paic
    Hi,

    very good initiative ^^

    I would add this really good thread http://www.gamedev.net/community/forums/topic.asp?topic_id=110342


    Thanks i'm in the process of adding links to gamdev forum threads (including Yann's stuff) into relevant sections.
    Very cool! You guys make my day! [lol]
    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].
    Thumbs up to the #graphicsdev logs! (except for the Iain one simply because I was such a newbie back then :-P)
    I'll sticky this until I get around to integrating it into the FAQ. Nice work.

    Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
    "Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

    Hey, not directly scenegraph-related, but perhaps you'll make another thread about shaders: Generating Shaders From HLSL Fragments, a very interesting read!

    This topic is closed to new replies.

    Advertisement