Repository

Topics

j3d.org

Using Geometry Generation

The geometry generation tools are used to create raw pieces of geometry that you can then incorporate into your own application. The items are standalone and do not require the use of Java 3D specific classes - ie all data is created as arrays of primitive values that may be used wherever you need.

Philosophy

The aim of the geometry generation code is to provide an optimal solution oriented around speed and memory consumption. That is, you create a generator, feed it some configuration information and then let it calculate the values and place them in the data structures you provide.

Internally the generators create minimal structures - only those that are needed to create speedy generation - such as precalculating geometry points. All other data structures such as the calculated coordinates and arrays are your responsibility. So, you get to choose if you re-use data structures each time a new piece of geometry is created or not. Internally the code will make sure that you act sanely and alert to any errors if possible.

 

Setting up

To use a geometry generation, you need to first decide on the generator to use (ie the piece of geometry that you want to create) and what data to create. For example, you may only want coordinate information and other times you want the works - coordinates, normals and texture coordinates.

Defining required data

Before using the generator, you need to create an instance of the data holder class and do all of the setup of the required configuration. This data holder class is what you pass to the generator when asking it to create the geometry. This class that you start with is called GeometryData. There are three fields that you are interested in:
  • geometryType The type of geometry array that you want to have created. There is no default value set and therefore you must select one.
  • geometryComponents The list of components that you want created in addition to the coordinate data. A list of values OR'd together.
  • geometrySubType An optional flag indicating sub-types of the geometry to be created. Currently not used, but will be in subdivision and implicit surfaces.
Thse values are filled in with one of the various constants defined in the class. If you provide dud values, when you attempt to generate geometry an exception will be generated.

     Note
    Generators are not required to support every single geometry type. The individual class documentation should tell you what is currently available.

There are a number of other fields that the GeometryData class contains. These are filled in after you have request the geometry to be created. The fields mainly are the arrays of generated data. If these are not allocated when generation is requested, we'll create them for you. Alternatively, you can provide your own instances of the arrays - if you call the generator a second time with the array, that's exactly what you'll do anyway.

  • vertexCount The number of vertices generated for the type of geometry requested. The number corresponds to the vertexCount parameter that you must pass to a geometry array object when creating it and is adjusted according to the geometry type created.
  • coordinates The array of coordinates for the geometry. It always starts at index zero. The array may be longer than the number of vertices generated. The length must be at least 3 * vertexCount as the coordinates are stored in a linear fashion as [Xn, Yn, Zn, Xn+1, Yn+1, Zn+1, ....]
  • normals The array of normals for the geometry. It always starts at index zero. The array may be longer than the number of vertices generated. The length must be at least 3 * vertexCount as the coordinates are stored in a linear fashion as [Un, Vn, Wn, Un+1, Vn+1, Wn+1, ....]. There is one normal for each vertex.
  • numStrips The number of items stored in the stripCounts field during the last generation run.
  • stripCounts The array of strip counts for strip/fan geometry. Not used or created if the geometry type is not a triangle fan or strip.
  • indexesCount The number of items stored in the indexes field during the last generation run.
  • indexes The list of index information for indexed geometry. Not used or created if the geometry type is not one of the indexed types.

Initialising the generator

The usage of the generator is dependent on the type of geometry that you want created. For illustrative purposes, we will show how to set up a sphere to be created.

The generator will take everything from no arguments to a list of all the available customisation options. You may supply whatever you want. All generators will allow you to change the basic dimensions and control the number of polygons to be generated. Polygon count is set by the facet count argument and is treated according to the local documentation. For the sphere, the facet count is the number of faces around the diameter. As the diameter can run on any plane, this makes the total number of faces (and hence polygons) generated equal to the facetCount squared. If you are generating triangle based information, you can double that number.

If you want to read the current values back to make sure everything is fine, then call the getDimensions() method of each generator. This will contain all the relevant information according to the geometry type.

Another level of control that most generators allow is the specification of partial geometry. For example the cylinder can be specified without its end caps. These flags will also effect the vertex count.

 

Creating Geometry

So, let's set everything up to create a default sphere.
   SphereGenerator gen = new SphereGenerator();

   GeometryData data = new GeometryData();
   data.geometryType = GeometryData.INDEXED_TRIANGLE_STRIP;
   data.geometryComponents = GeometryData.NORMAL_DATA |
                             GeometryData.TEXTURE_2D_DATA;
This will create you a sphere that is uses and indexed triangle strip and contains coordinate, normal and 2D texture coordinates.

The next step is to ask the generator to create data for us. This requires calling the generate() method and is common to all generator implementations. With this, you pass in your instance of GeometryData that you've just set up. If it understands and supports what you have requested then it will return with the information filled in the various arrays that we outlined above.

     Note
    For speed purposes, indexed geometry always uses the same index array for coordinates, normals and texture coordinates. Although Java 3D allows the supply of separate index counts, we found a marked speed increase at the generation stage by only creating one set of indices, and no rendering speed decrease. Java 3D 1.3 is expect to allow the definition of this style of information, but 1.2 does not.

After creating the geometry data, the final step is to create the Java 3D specific structures - in this case an IndexedTriangleStripArray.

    try
    {
        generator.generate(data);
        int j3d_format = GeometryArray.COORDINATES |
                         GeometryArray.NORMALS |
                         GeometryArray.TEXTURE_COORDINATE_2;

        IndexedTriangleStripArray array =
            new IndexedTriangleStripArray(data.vertexCount,
                                          j3d_format,
                                          data.numIndexes,
                                          data.stripCounts);


        array.setCoordinates(0, data.coordinates);
        array.setCoordinateIndices(0, data.indexes);
        array.setNormals(0, data.normals);
        array.setNormalIndices(0, data.indexes);
        array.setTextureCoordinates(0, data.texCoords);
        array.setTextureCoordinateIndices(0, data.indices);
    }
    catch(UnsupportedTypeException ute)
    {
        System.out.println("Triangle strip arrays not supported);
    }
    catch(InvalidArraySizeException iase)
    {
        System.out.println("You passed an array size too small");
    }
Now you have a piece of Java 3D geometry ready to use in your scene graph.

 

Reusing Generators

One of the advantages of using these geometry generators is to allow fast generation of the same object many times, or to modify the existing object (eg change the radius of the sphere). The way the data structures are set up allow for this to be done very efficiently.

Let's try to change the sphere's radius now to a new value. This time the radius will be 5.3. Firstly we call the setDimension() method on the generator to set the new value. Next we ask the generator to generate a new set of coordinates. Note that we can re-use the same instance of the GeometryData that we originally created the geometry with. This makes the regeneration process faster because we don't need to re-allocate the memory for the new values - we simply write over the top of the existing ones.


    generator.setDimension(5.3f);

    try
    {
        generator.generate(data);
        array.setCoordinates(0, data.coordinates);
    }
    catch(UnsupportedTypeException ute)
    {
        System.out.println("Triangle strip arrays not supported);
    }
    catch(InvalidArraySizeException iase)
    {
        System.out.println("You passed an array size too small");
    }
The one catch that you need to deal with is handling the array size issues. We will re-use your data array if we find the reference is not null. However, the generator will barf if the array is not big enough. We are not nasty and automatically allocate you a new array big enough. We assume that you might be using this array for something else to (geometry by reference?) and that trashing it on you unknowingly would not be a good idea. If it is too small, you'll see that second exception.

Summary

That is all there is to know about using the geometry generators. There are plenty to play with and more to come. These techniques apply to any generator type regardless of whether it is a primitive, subdivision or implicit surface.