Supplemental Java 3D Documentation

Alpha
Alternate Appearance
Behaviors
DirectX Version Limitations
Clipping Values
Front/Back Clipping Ratio & Z-Buffer
Geometry Info
Geometry by Reference
Memory Leakage
Textures

Alpha

The documentation for setMode() is unclear. It implies that setMode() will clear the previous mode but according to a response by Kelvin the actual behavior appears to be an "OR" with the previous mode.

Alternate Appearance

The original intent of AlternateAppearance is to use it for highlight - such as in response to an user action. Alternate appearance is not designed to dynamically set the appearance at a large group level so as to override the individual appearance on a large number of Shape3Ds. Scoping the alternate appearance at a very high group level (with several Shape3Ds ) and/or detaching/attaching alternate appearance every frame and/or changing the transform above the alternate appearance every frame would result in degradation in performance (as would be the case with lights/fog/modelclip).
(per Uma Sabada - Sun Engineer - February 5, 2001)

Behaviors

Changes made during a behavior will have a delay in frames before they are displayed.

(per Kelvin Chung - Sun Engineer - April 23, 2003)

Direct X Version Limitations

Java 3D 1.2.1 does not support the following under the DirectX implementation:

Clipping Values

Using the default policies, the clip distances are specified in PHYSICAL units (meters) while the scale of the view platform coordinates is such that [-1,1] in VPC maps to [0, screen_width] in PHYSICAL units. Thus, the default scale is physical_screen_width/2 (see the docs for SCALE_SCREEN_SIZE scale policy in the View). The default physical screen size is set assuming a 90 dots per inch, so for a 1024 x 768 screen, the default screen size is 0.2890 x .2167 meters, making the VPC to PHYSICAL scale = 0.1445. Conversely, the PHYSICAL to VPC scale = 6.920 (again assuming a 1024 x 768 screen). So a clip distance of 0.1 in PHYSICAL units will be 0.692 in virtual coordinates.

Kevin Rushforth - Sun Engineer - March 9, 2001

The front and back clip policies have the unfortunate default of PHYSICAL_EYE. Set them to VIRTUAL_EYE and your clip plane problems will go away.

With the default policy, the clip plane distances are measured in meters from the physical eye location. If you don't specify the physical screen width and height explicitly (with Screen3D.setPhysicalScreen{Width,Height}()), then Java 3D assumes a default pixel size of 90 pixels/inch. This makes the scaling from virtual units to physical meters dependent upon the display resolution, so the clip planes will move with respect to the virtual world depending upon the screen resolution.

Mark Hood - Sun Engineer - December 31, 2002

In simple terms this means that for a 1024x768 screen all clipping distances are actually seven times further than what you set them at. - John Wright

Front/Back Clipping Ratio & Z-Buffer

Many users of Java 3D have noticed that the recommended Back to Front Clipping ratio of 1000 to 1 or 3000 to 1 results is a big practical limitation when modeling a virtual world. If you are willing to adjust the default values of your video card (assuming it supports a Z Buffer greater than 16 bits) you can greatly improve the visual results. With a 32 bit Z buffer you can probably have acceptable results with a ratio of about 800,000 to 1.

The problem is that the loss of resolution is really a non-linearity in the transformation of z-values. With a high ratio, the resolution of the z-buffer near the front plane gets higher and but the resolution gets worse near the back plane. More bits of Z will give you more resolution at near the back plane, but the non-linearity will be the same.

Perhaps this will help. The OpenGL reference manual says:

"The greater the ratio of zFar to zNear is, the less effective the depth buffer will be at distinguishing between surfaces that are near each other. If
     r = zFar / zNear
roughly log2(r) bots of depth buffer precision are lost. Because r approaches infinity as zNear approaches 0, zNear must never be 0".

I'm (Doug) not sure exactly what "loosing" bits of precision means or where the log2() equation comes from. Long ago I played with the math involved here and it gets nasty very fast. Perhaps the bits of precision lost refers to the loss of resolution at the back plane. Anyway, let's play with this idea a bit.

If a ratio of 3000 "looses" log2(3000) ~= 11-12 bits of precision (2^11 = 2048), for a 16 bit depth buffer this leaves 4-5 bits of resolution, which would indicate only 16-32 "units" of depth buffering. This seems awfully coarse, I suspect that 3000 is the limit of what a 16-bit z-buffer can handle without being complely useless. The resolution near the front is good and at the back it is pretty bad.

The javadoc for View suggests that a ratio of 100-1000 is better. So let's plug in 128. This "looses" 7 bits of resolution leaving 2^11 or 2048 "units" of resolution. For a 24 bit depth buffer this would leave 17 bits of resolution, or 130K units of resolution. For a 32 bit depth buffer this would leave 21 bits of resolution or 2M units of resolution. Put another way, a ratio of 128 on a 16 bit depth buffer equals a ratio of 32K on a 24 bit depth buffer and a ratio of 8M on a 32 bit depth buffer.

All this makes me (Doug) think that 3000 is probably a pretty good limit for a 24-bit depth buffer, which would make the equivalent ratio on a 32 bit depth buffer be 3000 * 2^8 ~= 800K. This also means that the equivalent ratio on a 16-bit z-buffer would be on 12!

(paraphrased from Doug Gehringer - Sun Engineer - February 5, 2001)

Geometry Info

GeometryInfo does a lot in the background. If you put your geometry into it as POLYGON_ARRAY and then immediately call getGeometryArray(), it will create a default triangulator and triangulate your data, then unindexify it, and then create the GeometryArray and pass it back. There's no other choice because you can't put polygons into a GeometryArray. The word "polygon" is never used anywhere in Java 3D except in GeometryInfo and the triangulator.

To further expand this, the NormalGenerator and Stripifier only work on indexed triangles. Therefore, no matter what format your data is in, it will be converted to indexed triangles if you call the NormalGenerator or Stripfier. This is hidden from the user. If you send in quads and call the stripfier, you will get triangle strips out, and you won't know that your quads were converted to indexed triangles in the interim.

If you send quads (or fans or strips) to the NormalGenerator, they get converted to indexed triangles before Normals are calculated. If you then call getGeometryArray(), the triangles are converted back to the original primitive in the most intelligent way possible. If it was originally fans or strips, it will attempt to stitch them back together, but it may have to break a strip in two if there was a hard edge in the middle of the strip. It does the same thing for quads. (There can't be a hard edge in the middle of a quad because the same facet normal is used by both sub-triangles of the quad).

This probably rarely happens because most people aren't going to generate normals and then call getGeometryArray(), they're going to call the stripifier first. In that case, the data is left as indexed triangles and sent immediately to the stripifier. It's never converted back to the original primitive type.

Keeping all that in mind, POLYGON_ARRAY is treated just like any other primitive by GeometryInfo. Before it can be sent to the NormalGenerator or Stripifier, it must first be converted to triangles. It uses the triangulator utility to do this. Where it differs from the other primitives is when you don't call the Stripifyer. If you call getGeometryArray() on POLYGON_ARRAY data without calling the stripifier, you will always get back individual triangles.

So the answer to your question is yes, you don't need to call the triangulator explicitly (unless you want to specify the earOrder) because it gets called automatically when POLYGON_ARRAY data is used. Does it hurt to call it explicitly? Not much. The triangulator is smart enough to do nothing if the input data is already triangles.

Homework:

1) If I put Quad data into GeometryInfo and call the NormalGenerator followed by getGeometryArray(), what will be the primitive of the returned data?

2) If I put Triangle Fan data into GeometryInfo and call the Stripifier followed by getGeometryArray(), what will be the primitive of the returned data?

3) If I put Polygon data into GeometryInfo and call the NormalGenerator then the Stripifier then getGeometryArray(), what will be the primitive of the returned data?

4) If I put Quad data into GeometryInfo and call the Stripifier then the NormalGenerator then getGeometryArray(), what will be the primitive of the returned data?

Answers:

1) Quad. The quads get taken apart to calculate the normals, then they get put back together.

2) Triangle Strip. The input primitive type doesn't matter because you called the Stripifier.

3) Triangle Strip. Same as #2.

4) Don't do this! When you call the Stripifier, GeometryInfo will convert your data into triangles, and then the Stripifier will turn it into strips. Then GeometryInfo has to turn it back into triangles before it can call the NormalGenerator! Then, after calculating the normals, it will attempt to stitch the strips back together. Don't do this! You are doing tons of extra work and your strips aren't as long as they would have been. Always call the NormalGenerator before calling the Stripifier.

(Paul Pantera- Sun Engineer - February 8, 2001)

Geometry by Reference

Java3D version 1.2.1 uses Vertex Array to render geometry by-reference and therefore the geometry will be sent to the video card every frame. In the future, Sun is contemplating an API by which application can tell Java3D the memory/performance trade-off as well as "very infrequently changed" hint. These hints will help determine whether to use display list or VertexArray.

There are a few things that impact on the amount of computations performed per frame. For example, if any of the Shape3D that use this geometry have autoBoundsCompute set to "true", then boundingBox of this geometry will be computed every time you change the geometry and if you are using non-basic types such as Point3f there is additional work that is done everytime you change the geometry data.

( paraphrased from Uma Sabada - Sun Engineer - June 19, 2001)

Memory Leakage

When adding and removing BranchGroups repeatedly, "memory leakage" has been observed. This is often because not all references to a node have been removed. You may find that an object is being held in memory because it is still being referred to. If so, try to figure out if that is because you haven't really removed the object from the scene graph or because Java3D has an error. As you can tell, this kind of problem can happen easily. Many times a huge subgraph can be held in memory because a tiny piece of it is being referred to and the whole thing is held together with bi-directional references.

Recent releases (Java 3D 1.2.1 beta2) of Java3D have fixed lots of problems like this, so the "hanging" reference could be in either your code or Java3D.

The way to debug this is to use OptimizeIt (www.OptimizeIt.com). You can run your program under the memory profiler, set a "mark" at the after initialization and then look for where the new memory is being allocated as the heap size increases. You may want to force a GC via the tool to clean up the trash. Look for the objects which have increased the most in count or memory size (click on the top of the columns, "Diff" or "Size diff", to sort using the values in that column).

Once you have found objects which are increasing, you need to find out why they are not being released and garbage collected. Double click on the object name to go to explore where the objects are being allocated from. If you find something that looks like it should be getting released, click on the "instances and reference graphs" button to look at specific instances. This will take you to a screen where you can look at the references to a specific object. Trace back the references and look for what is referring to the object. This gets tricky since you need to distinguish between the objects which should still be allocated and the objects which should have been GC'd.

(paraphrased from Doug Gehringer - Sun Engineer - February 5, 2001)

Textures

In Java 3D 1.2, the only way to modify the texture is by creating a new ImageComponent2D object. It is not legal to modify the BufferedImage directly. In Java 3D 1.3, we are adding updateData methods to ImageComponent{2D,3D} and a new ImageComponentUpdater object that will allow you to modify all or part of a BufferedImage in place.

(Kevin Rushforth - Sun Engineer - June 5, 2001)