![]() | ![]() | ![]() |
|
Scene Graph Basics© Justin Couch 1999Once you have the basic window established, you need to put something in that window. There are objects to place, sound to listen to, and above all, putting your view into the world so that you can see it. There are many different ways of doing this in general 3D graphics techniques. On the low level end, you just put in lines, points and polygons. On the high end, you create just objects and let the system take care of it. Java3D uses the scene graph approach. A scene graph is a hierarchical approach to describing objects and their relationship to each other. For example, you would describe the connection of your hand relative to your arm. That way, when moving your arm, the hand moves with it. However, you can describe the angle of rotation of the hand in an angle that is relative to the arm. This description of information relative to the parent object is termed a local coordinate system, and is the heart of the scene graph approach to 3D graphics. As you descend each level, there is a grouping structure. Typically this grouping structure contains objects of similar characteristics and always has something useful from the parent. Our hand/arm example is typical. Usually at each group there is the ability to move the object relative to the parent.
Describing the UniverseJava3D operates on a number of levels of progressive refinement of the scene graph. In the conceptual model of the world, the scene graph is a standalone entity. We can create a scene graph without the need to have anything else from Java3D present - not even a canvas.Despite having the sexiest scene graph, what's the point if we can't see or play with it? Apart from the scene graph, we also need to have an on screen place to render it. We also need to put a camera in there to connect the scene graph with the screen. How about mouse input where a user might want to click on an object? Doing a little reading between the lines, this also suggests that it is possible to share the one scene graph between multiple windows. If we have all of these parts floating around, it also suggests a collection requirement to pull all of these bits together. At the top level, we use a universe descriptor. A universe describes everything that we see and do within a particular world. The universe can be a pretty darn big place. An object a couple of billion units away may be pretty hard to see. If we're stuck in the land of floating point numbers, it's pretty tricky to exactly specify the position. To take care of these problems, we introduce the concept of a locale. This provides a basic frame of reference to increase the amount of precision that we can specify a point in 3d space. A universe may have many locales as needed to describe it. For example, if we were modelling the real universe, then there probably would be one locale for each galaxy. From the Locale level, you may now start building a scene graph. You do this by creating various groups and pieces of geometry in whatever order meets your needs. In a typical application, you probably have a collection of pre-built classes that represent objects. Other times these objects are built on the fly in response to your data source(s) like a file or database. You then assemble these objects into groups as needed by the application in each locale.
In the Java3D model of the world, cameras are also placed inside this scene
graph. There is a series of transforms that locate a view in the world, and
then a couple of connecting classes used to represent you with the world and
finally to glue that to the canvas. Within the 3D world, the camera is called
a Building a Basic UniverseNow we come to building that universe in Java3D. A number of different approaches are available to building this universe. Because this tutorial is focused around learning the lowest level implementation details of Java 3D, we will extend theVirtualUniverse class itself. Then, within your
own universe, you can create methods to add items exactly how you want to
classify them.
Because our basic world is very small, there is no need to have multiple
locales. Creating the universe will create a single locale and place two
After creating a universe (remember that we're extending the
Constructing a basic universe takes almost no code at all. For illustrative purposes, we'll create everything in code rather than extending various objects: VirtualUniverse universe = new VirtualUniverse(); Locale locale = new Locale(universe); BranchGroup view_group = new BranchGroup(); BranchGroup world_object_group = new BranchGroup(); // now add them to the locale locale.addBranchGraph(view_group); locale.addBranchGraph(world_object_group);Once you've added the branch graph to the locale, that branch graph, and anything you add to it, is considered live (we'll discuss the difference between live and dead scene graphs shortly). A live scene graph will place restrictions on what you can add, so it is always best to leave this step to the last line of code in your setup routines. Locales have a lot more use than just presenting a bunch of geometry. As you will see in later chapters, it is used for many of the interactions with the scene graph by the display devices. You may want to use more branch groups than this. It is common that top-level branch groups may be assigned to non-3D objects like lights and sound. How you divide up the top level groups is really up to the application that you are writing and also personal style. Viewing the SceneWith the basic scene layout in place, we now need to concentrate on making sure that the user can see what you want to show them. To do this, we need two mechanisms - one to represent the view position in the world and another to map a particular view to the screen.
As explained earlier, a view exists as part of the standard scene graph using
the ViewPlatform camera1 = new ViewPlatform();For a standard application, there is nothing more to do. When dealing with more unusual displays, there are a range of options. These options are based on dealing with HMD's and stereo projections. Using the setViewAttachPolicy() method, you can place the virtual head of
the user in different positions. The different positions are used to take
into account rendering differences for stereo views.
Coming from the other side, a particular canvas needs to know what to render.
On inspection of the View view = new View(); view.addCanvas3D(canvas); view.attachViewPlatform(camera1);The relationship between views and canvases allows multiple renderings of the same view. In normal programming you probably won't be needing to use more than one canvas because of the limitations of the flat screen. What you might use though, is the collection of different policy options available as part of the view class. Options are based around the viewing parameters that need to be projected to the camera. One that you are most likely to use is the setProjectionPolicy() for controlling orthographic visualisation
or standard perspective. You can set it to these two options using the following
code:
view.setProjectionPolicy(View.PARALLEL_PROJECTION); view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);Dealing with view objects can be a trifle kludgy. Not only do they need to know about both the canvas and view platform, but typically there is a lot of other associated baggage like camera specific displays (HUD type objects or modal requirements). There are many different ways of organising your code:
Placing ObjectsUntil this point all you've probably seen is a blank black square when running any of the code. Now it is time to quickly look at different ways of describing and organising your objects.
Java3D organises all scene graph elements into one of two categories:
Leafs cover a very broad spectrum of objects from primitives to sound, lights,
behaviours and more. For the moment, we'll concentrate on one specific leaf -
the
These may be changed at will; changing one will not change the other.
On the organisational side of the house are the grouping nodes. The idea of
the grouping node is to collect together a bunch of the leaf nodes into
common parts. With grouping nodes you can move objects about
( All groups may contain either other groups or leaf nodes, or collections of both. Because you can arrange these groups in this hierarchical order, it leads to term scene graph. In a scene graph you have many objects arranged in a (typically) tree fashion starting from some root group and branching out into sub groups and objects, until you reach the ends of each branch - the individual leaf nodes. We won't look much more at these topics, because they'll be covered in more depth shortly.
Scene Graph AttributesObjects in the scene graph have a number of attributes. The scene graph itself also may have a number of attributes that cover all or large parts of objects. These attributes are used to control internal optimisation of the scene graph. A lot of this is tied in with the existance of theBranchGroup
class.
When you first create a collection of group and leaf nodes, they exist exactly in the order that you create them. Typically when you create a scene graph, you don't construct it from a top down approach. That is, start at the locale, add branch graphs, and then children nodes from there. Instead, you'll probably have a collection of pre-built parts that describe say a box or piece of terrain (elevation grid). From these, you then assemble then with the appropriate transforms to make a complete scene. Some of these objects may even come from files that you've loaded from disk, like a VRML world or DXF mesh. Because of these arrangements, it is possible that you have not created the most efficient structure when it comes to rendering it at the hardware level. A complex scene may contain thousands of groups and primitive objects, so it is also quite possible that most of them you won't be directly changing. That leaves us a lot of room for doing some optimisations under the hood. For example, in our virtual world, we have a car. The car doesn't change colour or size, but it can change location. Being logical thinkers, we've placed a single transform that contains all of the car parts as the root of that piece of scene graph. All of the objects below it remain in fixed positions, so that allows us to optimise away any lower level transforms that might exist. Capability BitsIf you've already gone for a wander through the javadoc for Java3D, you will note that the base class for both group and leaf classes is theSceneGraphObject . In this, you will find the
setCapability() method. Capability bits are the Java3D way of
defining exactly what can and cannot be optimised away by the library.
Although the base class does not define any itself, you will note that every
derived class will.
By default, all capabilities are set to the negative. If you want to do
something to an object in the scene graph, you need to set the capability bit
to allow it. Capabilities are also very fine grained. For example, in the
Capability bits do not transfer to the children of groups. If you have a scene graph that represents an arm with two transform groups, one each for elbow and wrist, setting the capability to write to the shoulder transform does not automatically allow you to write to the wrist or elbow transforms. These must be set individually.
One potential area of confusion is the difference between object attributes
and capabilities. A capability can only ever be set through the
Live and Dead Scene GraphsAt the point where you add your objects to the current locale, they now become eligible for rendering. Any renderable part of a scene graph is termed live. It is now possible that the low level rendering system will pass through the contents of that object and turn them into something visible on screen. The corollary of this is that any scene graph part that is not currently added to a locale can be classified as dead.
In the previous section, we quickly looked at capability bits. These bits
define what can and cannot be done. When the object becomes live, those
capabilities are used to determine what a user may do on the active scene. The
specification is very loose about what this means though when it comes to dead
scene graphs. Under some implementations, the capability bits may be ignored
when dealing with a dead scene graph, while for others they will have full
effect. For any node in the scene graph, you can determine its status by calling
the
If the capability is not set to allow you make a modification to an object, it
will generate a
Having capabilities is only part of the optimisation story. You actually need
to make use of them as well by telling Java3D that it is OK to start the
optimisation process. To do this, you compile the scene graph. The
isCompiled() method of SceneGraphObject . A
true result means that it is compiled. Like a live scene graph,
if you attempt to modify something that you have not set the capability for,
it will result in an exception.
A compiled scene graph is not necessarily the same as a live one. They are
independent states and either could be the result of your
Code ImplementationWith the scene graph well described, we can create a representative class that will be used as part of the demonstration code. For this, we will create a class calledUniverseManager that extends
VirtualUniverse . The idea is that this will perform a basic
management role for looking after the geometry and associated information.
public class UniverseManager extends VirtualUniverse { private Locale locale; private BranchGroup view_group; private BranchGroup world_object_group; public UniverseManager() { locale = new Locale(this); view_group = new BranchGroup(); view_group.setCapability(Group.ALLOW_CHILDREN_READ); view_group.setCapability(Group.ALLOW_CHILDREN_WRITE); view_group.setCapability(Group.ALLOW_CHILDREN_EXTEND); world_object_group = new BranchGroup(); world_object_group.setCapability(Group.ALLOW_CHILDREN_READ); world_object_group.setCapability(Group.ALLOW_CHILDREN_WRITE); world_object_group.setCapability(Group.ALLOW_CHILDREN_EXTEND); } public void addWorldObject() { world_object_group.addChild(node); } public void makeLive() { view_group.compile(); world_object_group.compile(); locale.addBranchGraph(view_group); locale.addBranchGraph(world_object_group); } }Convenience methods will be added as we go along. However, you should also note the capability bits that have been set. In order to be able to add and remove objects from the scene graph, we need to have all these capabilities specified. |
|