Populating a scenegraph and Dynamism

Posts: 153
Joined: 2004.12
Post: #1
Iv come to a point in my project where a significant amount of time is going into creating levels. These levels consist of bezier patches, vertex data, height maps, lights, physics objects, and emitters Blink

Im loading all the information for each type of object from a text file in a manner similar to:

step 1.)Define my object in my application (HeightMap *myHeightMap = [[HeightMap alloc] init])
step 2.)load a text file that describes each object ([myHeightMap load:@"someFile.LC"])
step 3.)add the object to my scene graph ([sceneGraph addObject:heightMap])

This obviously makes for some really long initialization code. And anytime i want to add/remove or fundamentally change my level i have to recompile my app (which isn't a big deal but annoying.)

Its also becoming harder and harder to refine or add new features to my different objects. Im seriously thinking about creating a new loading system that relies on dynamism. I was wondering if someone has attempted something like this already? And if so wether or not revamping my entire engine to rely on such technology is really going to help things out.

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Posts: 1,066
Joined: 2004.07
Post: #2
Couldn't you just have a text file that lists the objects, their types, and the files it should reference? Then it takes each object, dynamically allocates memory for it, initializes it with the correct type, and initialization file. That seems like it would work and prevent you from needed to compile when changing the level.
Quote this message in a reply
Posts: 153
Joined: 2004.12
Post: #3
Seems like that would pretty much be exactly what i would do except instead of doing something like:

Class namedClass = NSClassFromString(className);

I would have to use some type of switch. Which *almost* goes against the principle of it. Just one more place where i would have to keep track of some integer based class identifier Cry

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Posts: 5,143
Joined: 2002.04
Post: #4
why couldn't you use a string-based class identifier?
Quote this message in a reply
Posts: 153
Joined: 2004.12
Post: #5
I could. I don't see how that improves the situation though. It just seems easier to use NSClassFromString instead of

    case LC_LIGHT:
        class = [[LCLight alloc] init];

     case LC_HEIGHT_MAP:
          class = [[LCHeightMap alloc] init];



There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Posts: 153
Joined: 2004.12
Post: #6
This is what iv come up with so far:

My scene graph is populated by LCNode's. Each LCNode can have many children and one parent. Every object (Light, Camera, etc..) that can go on my scene graph is a child class of LCNode.

Given a scenegraph like:
Node: LCCamera:1.LCCamera
Name: Camera
Position: 0 0 0
Rotation: 0 0 0 0
Scale: 1 1 1
Children: 2
    Node: LCLight:1.LCLight
    Name: Light1
    Position: 0 0 0
    Rotation: 0 0 0
    Scale: 1 1 1
    Children: 0
    Node: LCCurvedSurface:1.LCCurvedSurface
    Name: Surface0
    Position: 0 0 0
    Rotation: 0 0 0 0
    Scale: 1 1 1
    Children: 0

My loading code is:
- (void)loadSceneGraph:(NSString *)string

    NSString    *name;
    int            nNodes,i;

    NSString *file = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], string];
    NSLog(@"Loading: %@", file);
    NSString *contents = [NSString stringWithContentsOfFile:file];
    NSScanner *scan = [[NSScanner alloc] initWithString:contents];
    [scan scanString:@"SceneGraph:" intoString:nil];
    [scan scanUpToString:@"\n" intoString:&name];
    [scan scanInt:&nNodes];
    NSLog(@"%@ %d Nodes", name, nNodes);
    LCNode *node;
    for(i = 0; i < nNodes; i++)
        node = [self scanNode:scan];
        [self addObject:node];

- (LCNode *)scanNode:(NSScanner *)scan
    NSString *class, *filename, *name;
    float fx,fy,fz;
    float rw,rx,ry,rz;
    float sx,sy,sz;
    int      nChildren, i;
    [scan scanString:@"Node: "    intoString:nil];
    [scan scanUpToString:@":"    intoString:&class];
    [scan scanString:@":"        intoString:nil];
    [scan scanUpToString:@"\n"    intoString:&filename];
    [scan scanString:@"Name: " intoString:nil];
    [scan scanUpToString:@"\n" intoString:&name];
    [scan scanString:@"Position: " intoString:nil];
    [scan scanFloat:&fx];    [scan scanFloat:&fy];    [scan scanFloat:&fz];

    [scan scanString:@"Rotation: " intoString:nil];
    [scan scanFloat:&rw];    [scan scanFloat:&rx];    [scan scanFloat:&ry];     [scan scanFloat:&rz];
    [scan scanString:@"Scale: " intoString:nil];
    [scan scanFloat:&sx];    [scan scanFloat:&sy];    [scan scanFloat:&sz];

    [scan scanString:@"Children: " intoString:nil];
    [scan scanInt:&nChildren];
    NSLog(@"%@ - (%@ : %@)",name, class, filename);
    Class object = NSClassFromString(class);
    LCNode *node = [[object alloc] initFromFile:filename];
    [node setName:name];
    [[node nodePosition] set:fx:fy:fz];
    [[node nodeScale] set:sx:sy:sz];
    LCNode *child;
    for(i = 0; i < nChildren; i++)
        child = [self scanNode:scan];
        [node addChild:child];
    return node;

- (void)addObject:(LCNode *)tModel
    [objects addObject:tModel];

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Posts: 1,199
Joined: 2004.10
Post: #7
I use a mix of classloading and XML-like DOM tree design for loading my scenes.

Here's what one of my "world" files looks like:
[begin:Manifest dtd=org.zakariya.worldengine.world]

    [begin:World name="Test 0"]
            World parameters

                If only one light is specified it will default
                to be the primary light.             
            [begin:Light primary=true]

                #primary defaults to directional anyhow



                primaryColor:reallist=0.3, 0.3, 0.5, 1
                horizonColor:reallist=1, 0.95, 0.9, 1

                [begin:CelestialBody attachToPrimaryLight=true]
                    #attachToPrimaryLight obviates this


                    # one or the other



                [begin:Shader class="NormalModulationTerrainShader"]
                    # primary and secondary colors to be
                    # selected based on modulation
                    # color for low z values
                    # color for high z values
                    # color of light hitting terrain
                    # color of shadows on terrain
                    # thresholding values for modulation; the tighter, the more
                    # severe modulation you'll see between primary & secondary
                        contrast and brightening, applied as a post-processing
                        effect on the mixmap.
                        mixBrighten is an offset, pushing the mix value up or down.
                        valid values are from -1 to +1, will be clamped if out
                        of range. Default value is zero, which has no effect.
                        mixContrast is a power the mix value ( after brightening )
                        will be raised to. Where valid values are from 0 and up.
                        Values from [0, 1) will result in a raising of contrast,
                        with 0 resulting in absolute contrast.
                        Values from (1,infinity) will result in a dampening of
                        contrast, to gray. Default value is 1 which has no effect.                
                        We can add an image to the mixmap, to add some noise,
                        which adds realism.
                        modes are
                            Add            : dst = src + dst
                            AddSigned   : dst = (( src - 128 ) * 2 ) + dst;
                        if true, resulting colormap and mixmap will be written
                        to disk where the app is located as the
                        two files "Colormap_RGB.png" and "Colormap_Alpha.png",
                        where the RGB file is the colormap, and the Alpha file
                        is the mixmap. This is useful for tuning.










            #nothing, for now

            #nothing, for now


I use a classloader to load the objects by name. The objects all implement the interface:

class DOMInitializable
        DOMInitializable( void ){}
        virtual ~DOMInitializable( void ){}

            represents the two states for initialization -- Success and Failure.
        enum status { Failure = 0, Success = 1 };

            initialize this object from a ManifestDOMObject node. @return Success
            on success and Fialure otherwise. Failures should be logged
            to PANSICore::Logger
        virtual status initialize( PANSICore::ManifestDOMObject *node ) = 0;

So as I descend the DOM tree for the file, I create objects by name and pass them the DOM node for initialization. Objects defined *inside* of other objects ( see CelestialBody, which is a child of Sky ) become logical children of the parent, so the parent "owns" it, and acts as it's proxy in the scene graph.

DOn't pass up classloadin and deferral of configuration to the class. It'll keep your code very clean.

One note: The file format above is a custom file format I came up with a few years ago because at the time I didn't know about TinyXML. Had I, I wouldn't have come up with my own... but what's done is done, and my APIs for the file format are pretty easy to work with.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Scenegraph newbie Falcor 2 2,282 Jul 15, 2005 04:34 PM
Last Post: Falcor