Java OpenSceneGraph

OpenSceneGraph is a high performance Scene Graph for 3D graphic programming. OSG has some documentation, but it's not that much. Unfortunately, the Java Version differs a little in use, too. So while learning JavaOSG, I will try to keep a log about my experience with the library. I would appreciate it, if you help completing this site and help track down bugs. Once again: this is a draft. If you find bugs or something doesn't work, don't damn but mail me, or better post it.

-- MarcBreisinger - 17 Dec 2005

Java Bindings

The bindings from Java to OpenSceneGraph, which is a C++ library can be found at Noodleheaven. Those bindings are created with a tool the guys from NoodleHeaven developed, called NoodleGlue. This tool automatically produces JNI binding libraries. It would be worth another exploration project, as it is very complicated, but extremely useful. So if somebody feels in the mood... smile

Getting Started


To set up your system, you can either go along with the instructions on NoodleHeaven (the hard way) or follow this very good intro without having to compile everything. Here is the HelloWorld file and here an empty project file. On the other hand, you are my hero if you do it the hard way and write a foolproof tutorial how to do this. Why? Scroll down to the Bugs section.

General Issues about (Java) OpeneSceneGraph

Using the viewer with standart settings without modification gives a right handed coordinate system, where the z points upwards, y into the screen and x to the right. If however we do Viewer.setViewByMatrix(Matrix.makeIdentity()); or something similar, the z axis points out of the screen and the y axis upwards.



The following shapes are available:

To be hung into the graph, they need to be encapsulated in a ShapeDrawable as they are no Drawables themselves. Therefore e.g. for a Sphere:
Geode geode = new Geode();
Drawable d = new ShapeDrawable(new Sphere(new Vec3fReference(0, 0, 0),radius));
MatrixTransform m = new MatrixTransform();


To build polygons by programmimg a Geometry object can be created. The following code creates a square polygon:

Geometry geom = new Geometry();
float width = 2;
float height = 1.33;

Vec3Array vertices = new Vec3Array();
vertices.push_back(new Vec3fReference(-width, height, 0f));
vertices.push_back(new Vec3fReference(-width, -height, 0f));
vertices.push_back(new Vec3fReference(width, -height, 0f));
vertices.push_back(new Vec3fReference(width, height, 0f));
short indices[] = { 3, 2, 1, 0 };
ShortPointer indices_ptr = new ShortPointer(indices);
geom.addPrimitiveSet((new DrawElementsUShort(PRIMITIVESETMode.QUADS_Val, indices.length, indices_ptr)));

//for correct shading normals are necessary
Vec3Array normals = new Vec3Array();
normals.push_back(new Vec3fReference(0f, 0f, 1f));
//with the attribute GEOMETRYAttributeBinding.BIND_PER_VERTEX the array normals could also have four elements, one for each vertex
The PRIMITIVESETMode.*_Val offers


You can read for example in the The OpenGL Programming Guide what all these are. These Geometrys inherit from Drawable. They cannot be attached to the scene graph themselves, but need a Geode (Geometry Node) to carry them. Therfore
Geode geode = new Geode();
root.addChild(geode);//root can be any Group, MatrixTransform, PositionAttitudeTransform or (still to find out...)


Text inherits from Drawable like Geometry and is treated similar. It's also hung into the graph carried by a Geode. Example:

Text centerHudText = new Text();
centerHudText.setCharacterSize(fontSize); // if you dont see the text, make sure it's not to small or too big
centerHudText.setPosition(new Vec3fReference( x_pos, y_pos, z_pos ));
centerHudText.setAlignment(TEXTAlignmentType.CENTER_TOP); //where should position point to
centerHudText.setAxisAlignment(TEXTAxisAlignment.SCREEN); //inbuild billboarding function
centerHudText.setColor(new Vec4fReference(red, green, blue, alpha));
Geode textGeode = new Geode();

State Sets

In OpenGL exists a state that determines how the following objects are rendered till it is changed. It is used for different properties like current lighting situation, material, texture, fog, depth testing, line-width and style etc. etc. . In OSG states are encapsulated in StateSets that can be attached to Nodes. StateSets can be used for multiple Nodes which is even recommended for performance issues as switching states is expensive.


Material can be assigned to a node by the following code:

StateSet ss = new StateSet()
Material m = new Material();
m.setAmbient(MATERIALFace.FRONT_AND_BACK, new Vec4fReference(0.9f, 0.8f, 0.6f, 1.0f));
m.setDiffuse(MATERIALFace.FRONT_AND_BACK, new Vec4fReference(0.9f, 0.8f, 0.6f, 1.0f));
m.setEmission(MATERIALFace.FRONT_AND_BACK, new Vec4fReference(1,0,0,1);


To assign transparency to an object all you have to do is assign a StateSet to it that looks somehow like this:
StateSet ss = new StateSet();
Material m = new Material();
m.setTransparency(MATERIALFace.FRONT_AND_BACK, 0.5f);

the higher the last value in setTransparency, the more translucent is the object.


For Texturing a Geometry, you use a openscenegraph.osg.Image, load an image file by osgDBNamespace.readImageFile(fileName) and use a Texture2D to place it on the Geometry. The following code gives the previous example a texture.
Vec2Array texcoords = new Vec2Array();
texcoords.push_back(new Vec2fReference(0f, 1f));
texcoords.push_back(new Vec2fReference(0f, 0f));
texcoords.push_back(new Vec2fReference(1f, 0f));
texcoords.push_back(new Vec2fReference(1f, 1f));
geom.setTexCoordArray(0, texcoords); 

Texture2D texture = new Texture2D();
Image image = osgDBNamespace.readImageFile(imageFile);
   if (image != null)
StateSet ss = new StateSet();
ss.setTextureAttributeAndModes(0, texture, STATEATTRIBUTEValues.ON_Val);

Depth testing

with the following OpenGL attribute you turn off depth testing, which is on by default. You might want to have this for example for HUD elements, which should be always in front.
To make sure they are drawn in front of everything, that means, they are drawn last, give the stateset a high number, as they are drawn in numerical order:
ss.setRenderBinDetails( 11, "RenderBin");


Lights are also hung into the scene graph like nodes. Example:
Light light0 = new Light();
light0.setPosition(new Vec4fReference(1.0f, 1.0f, 1.0f, 0.0f));
light0.setAmbient(new Vec4fReference(0.1f, 0.1f, 0.1f, 1.0f));
light0.setDiffuse(new Vec4fReference(1f, 1f, 1f, 1f));
light0.setSpecular(new Vec4fReference(1f, 1f, 1f, 1f));

LightSource ls0 = new LightSource();

This setup creates a light, that has the characteristic of a sphere. Spots etc. will follow later.

If the fourth value in setPosition is 1, then the three other values are the x,y,z coordinates of the light source. If the value is 0, then the light source is infinitly far away and the other three values describe a vector that points into the direction of the light source.

If you use more than one light there is a nice trap: Light nr. 0 is on by default for the root node, so you dont need to turn it on manually. Nr. 1 and higher are not turned on by default so you need to do this:

Light light1 = new Light();
LightSource ls1 = new LightSource();

[...]//positioning, colors etc.

StateSet rootState = new StateSet();
ls1.setStateSetModes(rootState, STATEATTRIBUTEValues.ON_Val);

So the best thing is to use that StateSet - setting always for all light sources, although it's not necessary for nr. 0.

Another confusing thing about lighting is coming up regarding the OpenSceneGraph Viewer. The Viewer's settings influence the light in that way, that if you don't use any lights, the viewer, set up with standart settings, will provide a head light. If you do use lights, that headlight is not active any more but only your own lights. In non standart settings, light nr. 0 is not activated by default and you have to turn it (and all others) on via a StateSet.

Turning lights on/off

this would turn on the lights for the node:
and using
would of course turn them off. You would like to do this if the object should not be affected by lights, but only appear in its given color (e.g. text)


this is only a draft as this inheritance mechanism is somehow pretty tricky and I still don't understand everything it does. Please feel free to play around with it and post whatever you find out

If the children of a node that has a StateSet don't specify anything on their own, the properties of the parents StateSet are inherited to the children. This is kind of tricky as pretty confusing, wanted or unwanted effects can evolve. Take this example with the nodes A and B, where A is parent of node B. You want node A to be unchanged as modeled so it gets no StateSet at the beginning and you want to assign transparency to it later. B should always be halftransparent and might have a StateSet like

StateSet b_st = new StateSet();
m = new Material();
m.setDiffuse(MATERIALFace.FRONT, new Vec4fReference(1,1,1,0.5f);//white and half transparent

The following StateSet will be assigned to A when it should be invisible:

StateSet a_st = new StateSet();
Material transparentMat = new Material();
transparentMat.setTransparency(MATERIALFace.FRONT_AND_BACK, 1.0f);

The effect is that B is not halftransparent at the start, but turns halftransparent, when A is made fully transparent. The reason is that the alpha value of the color has no effect till B inherits the attribute STATESETRenderingHint.TRANSPARENT_BIN_Val from A, as it is missing in its own StateSet. Also note, that though A's StateSet says transparentMat.setTransparency(MATERIALFace.FRONT_AND_BACK, 1.0f); what means full transparence, B is not fully transparent when the StateSet is assigned, as it has defined an own alphavalue 0.5 that overrides the StateSet's.

The following section is not successfully tested and therefore raw theory. Skip it if you like or try to figure it out and post if you are successful, please.

Furthermore there is the possibility to force children to inherit the parents properties even though they might have defined their own properties. You do that by setting (here e.g. for fog)
so if B says he wants his fog in another way he can forget it,

unless he knows a trick, which is setting its own fog property to the following
what cancels A's OVERRIDE again.

Loading models

OpenSceneGraph can load various file formats from 3D modelers like e.g. .3DS (native export from 3Ds Max), .wrl (VRML), .iv (Inventor) and many more. There is also a .osg format, which is the format that is I used most at the moment, as it is very easy to understand and brings some useful features. If exported with this exporter from 3Ds Max, the structure of the imported file in the scene graph is the same as the structure modeled in 3Ds Max. That is not taken for granted as when you load a .3DS file the relativity between the modeled primitives is lost and all the coordinates are given absolut instead of relative. With that it is almost impossible to do sth. like distance calculation.

(-- Examples and more explanations coming soon --)

So to load a model you use the static method

Node node = osgDBNamespace.readNodeFile(path);
where path is the String representation of the path to the file you want to load, e.g.
String path = "C:\myModels\modelOfAMonster.3DS";
String path = "C:\myModels\modelOfABiggerMonster.osg";
String path = "C:\myModels\modelOfAMonsterKiller.wrl";

Even though I have to add that loading .wrl (VRML) files should be possible (it works without problems on the C++ OSG) but I didn't manage to make it work with JavaOSG.


Collision Detection

I haven't found a working build-in mechanism for collision detection. If somebody knows one, please post it. What I heard so far is, that you best use Shapes for that. The method Drawable.getBounds() gives you a BoundingBox for that Drawable, Node.getBound() gives you a BoundingSphere for the Node. In OSG BoundingBox has a lot of functionality, such as intersection with others, JavaOSG seems to lack that. What's left seems to be contains(Vec3f v), which can check if there is one point enclosed in this BoundingBox. Unfortunately it only checks, as if the BB was still at the origin without any transformation. Kinda useless, right? Seems the only way left is to use the distance method again. Here is some code for it. I know that this is ugly. Its due to that memory leak problem (see Bugs section, and so far I am of the opinion, that you need to do it like that as the RefMatrixds are not freed properly.

Bugs in JavaOSG

The JavaOSG by NoodleGlue is nice, but has a major drawback. One of the most important calls, getMatrix() in MatrixTransform produces a memory leak in allocating memory for a RefMatrixd but not freeing it again. A workaround for the problem is to overwrite MatrixTransform and redefine the getter/setter and mutator methods for the class. The patching class for that can be found here. Further on, as loading .osg files produces trees with MatixTransforms in it, you need something like this method that exchanges those nodes against patched nodes. Then you load the scene with this call:
Node scene = MyHelperClass.patchMatrixTransform(osgDBNamespace.readNodeFile("myScene.osg"));
But there is more. As already mentioned RefMatrixds seem not to be freed properly any more, so you need to construct something that reminds to pointers in C++ . So a method that could begin like this:
public double checkProjectedDistance() {
   RefMatrixd me = new RefMatrixd();
   RefMatrixd act = new RefMatrixd();
   VisuCanvas.getWorldCoords((Node) actor,act);
   RefMatrixd pro = new RefMatrixd(e3d.getProjection());
   RefMatrixd mv = new RefMatrixd(e3d.getModelView());
should better look like this:
private RefMatrixd me = new RefMatrixd();
private RefMatrixd act = new RefMatrixd();
private RefMatrixd pro = new RefMatrixd();
private RefMatrixd mv = new RefMatrixd();
public double checkProjectedDistance() {
   VisuCanvas.getWorldCoords((Node) actor,act);
I had this construct in my code once and it is called very often (around 8000 times per second). In the first version memory keeps on filling up with about 3MB per second. The second version in contrast is fine. Of course this only works for non recursive methods and must be synchronized if multithreaded, etc. It would be better to fix the problem in the JNI, but I haven't had the time to figure out how to recompile the libraries. So please... you get the point.




JavaOSG Bindings

JavaOSG Api

A lecture site from the Lulea University of Technology. Very useful!


Main Project Page

Some good tutorials


Topic attachments
I Attachment Action Size Date Who Comment
Empty.javajava manage 1.7 K 20 Jan 2006 - 00:45 MarcBreisinger  
JOSGHelloWorld.javajava manage 4.6 K 19 Nov 2006 - 11:24 MarcBreisinger  
MBMatrixTransform.javajava manage 0.9 K 14 Dec 2005 - 20:09 MarcBreisinger  
MatrixTransformPatchMethod.txttxt MatrixTransformPatchMethod.txt manage 0.9 K 20 Jan 2006 - 04:38 MarcBreisinger  
colDet.txttxt colDet.txt manage 1.0 K 01 Jan 2006 - 00:46 MarcBreisinger  
Topic revision: r21 - 16 May 2007, RainerFink
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Medieninformatik-Wiki? Send feedback