Posterous theme by Cory Watilo

Loading and Unloading SceneJS Content

Today a SceneJS user asked the mailing list how content can be pulled into a scene graph on-demand, for an application in which the user browses a map view that loads and displays geometry, material and texture assets as they fall into view. 

Early SceneJS: The "instance" Node

Old versions of SceneJS had the instance node, which instantiated a target subgraph as a child. Really early versions of that were even able to pull the JSON for the target from disk and into the scene graph, on demand. The instance node was however removed in V2.0 because its implementation was so complicated, with the multiple parent paths it would create making engine optimisations impractical. It was replaced by shareable node cores in V2.0.

Latest SceneJS: Staging In-Core

On-demand loading of geometries can be done with a GeoLoaderService (the SceneJS plugin interface for custom mesh loading strategies), but there's nothing in V2.0 that loads whole subgraphs on demand, the way the instance node did.

Aside from implimentation simplicity, the reason is that SceneJS encourages staging content in-core. This means staging content in the scene graph, setting "enabled" flags false to cull it from the view until you want to render it,

For example:

{     
    type: "flags",     
    id: "my-teapot-content",     
    flags: { enabled: false },     
    nodes: [         
        { type: "teapot" }     
    ]
}

myScene.findNode("my-teapot-content").set("flags", { enabled: true });

Geometries, textures and shaders will be allocated on the GPU as the nodes are created in the scene graph. They will be bound on the GPU for rendering only when the geometries that use them are enabled.

You can stage as much content in your scene graph as your GPU has memory for, which these days is often as much as 1-2GB.

Internally, SceneJS optimises for in-core staging by maintaining a "visible list" that caches enabled (ie. visible) objects. Enabling an object causes the main draw list to be iterated on the next frame while saving the enabled objects into the visible list, which will then be the list that's iterated for each subsequent frame as long as more objects are not enabled. Then as objects are disabled, they are just plucked out of the visible list.

Creating and destroying Nodes

If your scene is too big to stage everything in the scene graph, then out-of-core content needs to swapped in and out by creating and destroying nodes:

myScene.findNode("some-node").add("node", { type: "teapot", id: "my-teapot" });
myScene.findNode("my-teapot").destroy();

Bear in mind that adding and destroying nodes cause scene-to-draw list recompilation (an internal optimisation) each time, so these add/destroys are best done in batches, and will cause a moment's delay when they happen.

If you're adding large numbers of nodes, you may want to pause the render loop on the scene graph while loading, just to ensure that the render interval doesn't keep kicking in and recompiling the draw list while you're still in the process of adding them:

myScene.stop();

// ..add many nodes..

myScene.start({
    // ...
});

So you can see how it's up to your application code to do this swapping, possibly under the direction of some sort of bounding volume visibility system. 

How to do this? In BioDigital Human we have our own application logic for swapping nodes and out of the scene, but that's driven by the user explicitly selecting what anatomy and conditions they want to view (Eg. switching gender, loading a beating heart etc).

A view-driven solution is a different kettle of fish. Way back in early SceneJS versions (around V0.7) I had "spheres of locality" centered about the viewpoint: a large outer sphere of say, 100000 units containing an inner sphere of 10000 units. The scene graph could have bounding volumes nodes which enclosed subgraphs: these nodes would load their subgraph when they intersected the inner boundary, then unload it as soon as they fell outside the outer boundary. SceneJS also performed frustum culling with the bounding volumes, enabling (making visible) the subgraphs whenever they intersected the view frustum.

All this was chopped out of V0.8 onwards to keep the engine lean and fast. As long as you are able to track the boundaries of things in application code, you should be able to implement this in third-party code using libraries like jsBVH

To be continued..

 

Objectswap