Using Geometry Generation

The geometry generation tools are used to create raw pieces of geometry that you can then incorporate into your own application. For the basic primitive types, there is not much to explain. For the more complex subdivision and implicit surfaces, then we have plenty to talk about!



Class Structure

All geometry generators rely on a very simple set of interfaces. Two classes form the basis of all the interactions: A data holder in GeometryData and an abstract representation of all generators in GeometryGenerator. Both classes can be found in the org.j3d.geom package.

Data Handling

When generating geometry - particularly lots of repetitive lots of the same item, a huge performance boost can be gained due to the optimisation of memory allocation and usage patterns. A typically approach to this is to let the generator internally cache as much data as possible so that geometry can be reused and re-created using pre-calculated data. This approach is taken by the Sun primitives utility code.

There are a number of problems with this method - what if we have a number of different items of the same geometry in the scene, each with a different lot of settings (say different sphere radiuses). If you have these constantly changing then the cached data is thrown away possibly each time and there is no benefit to be gained - particularly in multithreaded applications.

This code takes a different approach to the normally accepted process. Instead of caching data inside the generation routines, it asks the client to cache the memory instead. What it ends up doing is allowing the user to control when items should be cached and when they should not be. The benefit of this approach is seen when dealing with multiple different instances of the one shape type. Instead of needing to reallocate memory for each call, the user provides the data for the coordinates, normals and any other data locally and can use it to cache other areas. This approach is particularly important for building geometry by reference shapes in Java3D where you want to reuse your own data arrays rather than copy the internal ones of the generator, that may be trashed when you least expect it.

Why no individual get/set methods?

My preference is for minimal methods in a file. While I don't debate the merits of having get and set methods, I have a personal dislike of a get/set for every single variable or value. Another thing to consider when dealing with this is how you expect the methods to be used.

In the case of the geometry generators I rarely expect people to be using getter methods for detailed information of the internal state. When people get information it is usally to check all of the state information, not just one at a time. If any one of a collection of variables changes then it effects the entire internal state and therefore it is better to request the user to provide everything that changes the internal state to be passed at once. I cover this more in a second. So, for fetching value, while it may seem convenient to ask for just the radius or just the height, typically it leads to the following situation:

  if(gen.getHeight() != newHeight)

  if(gen.getRadius() != newRadius)
The end user thinks they are making things more efficient, but the reality is far different. In the first place, why is the user checking the generator for it's internal values. The class that is handling the generator should already know if the value has changed and should not even bother checking with the generator. If it doesn't know, what difference does it make anyway? Internally the generator does another check to see if the values are different and only recalculates if necessary. The above code has just wasted a lot of valuable clock cycles, not only in the actual checks but also in all the method calls. It is far more efficient to just pass everything to the generators and let them sort out the mess.

Now, onto why it is more efficient to pass everything at once. Internally we know that if someone changes a parameter - face count, radius, height or whatever, we are going to need to recalculate the values. Now we have two options:

  1. to recalculate values only at the time that the user asks for a new generation
  2. to recalculate at the time the values are set so that a later request for new coordinates is much faster.
This is an optimisation problem. If we provide separate methods for each variable that could be set, then this implies at least something about the internal optimisation capabilities. If I were to code option 1, then having separate methods does not matter. If I were to code option 2 then the performance implications are horrendous. In the above code snippet I must now regenerate the internal data twice, not just once. This is silly and would force me into option 1 as the only reasonable implementation (note that the entire design philosophy for these classes is speed). So, having separate methods for each variable is a bad idea as it does not allow me to take whatever steps are necessary for the optimisation techniques of interest.



Each of the primitives follows a standard pattern internally. If an object requires a basic shape "template" that is generated and cached for later use. Then, each time a new set of vertices is required, the system uses these points and constructs normals and texture coordinates to suit.

For example, a sphere starts by generating a unit sphere sized shape in the cached coordinate array. If the user changes the radius, coordinates do not need to be recalculated. Instead, the sphere just takes the values and scales the values by the appropriate amount. The only time the sphere data needs to be regenerated is if the user asks for a different number of facets.


Subdivision Surfaces


Parametric Surfaces

Parametric surfaces operate using the same regime of the base GeometryGenerator interface. Facets in each direction can be set and the cached array only changes when the number of tesselations changes. If only a new set of control points changes then there is no need to re-allocate the array, just the points in it.

Generation of the surfaces relies on calling an external class that provides 2D functions. The idea here is that the user can take the 2D math functions to build other structures such as a 2D window or path interpolators.