Java 3D Programming by Daniel Selman - HTML preview

PLEASE NOTE: This is an HTML preview only and some elements such as links or page numbers may be incorrect.
Download the book in PDF, ePub, Kindle for a complete version.

CHAPTER 15

Geometry utility classes and object loaders

15.1 Introduction

15.2 Triangulator, normal vector generator, stripifier

15.3 Object loaders

15.4 Summary

This chapter explains some of the utility classes that Java 3D supplies to help you import or generate geometry for your scenes.

15.1 Introduction

Java 3D comes with three utility classes that facilitate runtime geometry creation:

  • Triangulator converts a list of points and connectivity information into a GeometryArray composed of triangles.
  • NormalGenerator generates Normal vectors for a list of triangles.
  • Stripifier redefines an existing triangle list such that the triangles are specified in longs strips, with adjacent triangles sharing as many vertices as possible. Triangle lists are stripified to speed rendering by graphics hardware and software underlying Java 3D.

The geometry compression classes allow Java 3D to create an internal compressed representation of geometry information. If the graphics hardware supports using compressed geometry, the result may be faster rendering time and lower memory usage. Geometry compression can also be useful for applications that need to send Java 3D geometry across network connections using serialization or Java RMI. Refer to the API documentation for further details.

The Java 3D object loaders define an architecture for writing files to import geometry data into Java 3D. Sun supplies two loaders as part of the Java 3D utilities package:

  • Lightwave 3D
  • Object file

In addition, the Java 3D/VRML working groups maintain a VRML loader to import VRML (WRL) files into Java 3D. The VRML loader must be downloaded separately (VRML97.JAR). The example VrmlPickingTest uses the VRML97 loader. The VRML97 loader is no longer be ing developed and is being replaced by the loaders being developed for the X3D standard, hosted at http://www.web3d.org.

15.2 Triangulator, normal vector generator, stripifier

Triangulation is a mechanism to convert arbitrary polygons to triangular surfaces for rendering. The com.sun.j3d.utils.geometry.Triangulator class can be used not only to convert an arbitrary n-sided polygon (which does not have to be planar) to triangul ar surfaces, but also to create holes in the generated composite surface. To use the Triangulator, put your vertex coordinates into a double or float array. This coordinate array should first define the outer boundary of the polygon, using counterclockwise winding.

Then define any polygons that are to be excluded from the generated composite triangular surface. This simple example in figure 15.1 defines two contours: the outer polygon and one hole. The stripCountArray is an array of integers that delineates one con tour from the next. In figure 15.1, the stripCountArray would be

int[] stripCountArray = {4,3};

img200.png

Figure 15.1 Counterclockwise winding for defining a polygon and a hole for Triangulation

img201.png

Figure 15.2 Output from TriangulatorTest: The surface generated rendered both as a solid (left) and as a wireframe (right)

The first contour (A,B,C,D) contains four vertices, and the hole (F,G,H) contains three vertices.

The Triangulator class is not very well documented, so the following example should illustrate the concepts of polygon triangulation using the Triangulator class. In particular, the contourCountArray element is misleadingly documented and should be set t o the number of contours (1 + the number of holes). This is always the same as the length of the stripCountArray. Why the contourCountArray is necessary is not clear.

 From TriangulatorTest.java

//Generate a surface from 10 vertices and

//create a hole in the surface by removing

//a hole defined using 5 vertices. Note that the hole

//must be entirely within the outer polygon.

private double[]  m_VertexArray = {1,1,0, //0

        0,3,0, //1

        1,5,0, //2

        2,4,0, //3

        4,5,0, //4

        3,3,0, //5

        4,2,0, //6

        4,0,0, //7

        3,0,0, //8

        2,1,0, //9

//these are vertices for the hole

        1,3,0, //10

        2,3,0, //11

        3,2,0, //12

        3,1,0, //13

        2,2,0};//14

//triangulate the polygon

GeometryInfo gi = new GeometryInfo( GeometryInfo.POLYGON_ARRAY );

gi.setCoordinates( m_VertexArray );

//the first 10 points make up the outer edge of the polygon,

//the next five make up the hole

int[] stripCountArray = {10,5};

int[] countourCountArray = {stripCountArray.length};

gi.setContourCounts( countourCountArray );

gi.setStripCounts( stripCountArray );

Triangulator triangulator = new Triangulator();

triangulator.triangulate( gi );

//also generate normal vectors so that the surface can be light

NormalGenerator normalGenerator = new NormalGenerator();

normalGenerator.generateNormals( gi );

//create an appearance

Appearance ap = new Appearance();

//render as a wireframe

PolygonAttributes polyAttrbutes = new PolygonAttributes();

polyAttrbutes.setPolygonMode( PolygonAttributes.POLYGON_LINE );

polyAttrbutes.setCullFace( PolygonAttributes.CULL_NONE ) ;

ap.setPolygonAttributes( polyAttrbutes );

//add both a wireframe and a solid version

//of the triangulated surface

Shape3D shape1 = new Shape3D( gi.getGeometryArray(), ap );

Shape3D shape2 = new Shape3D( gi.getGeometryArray() );

_________________________________________________________________

After the geometry has been triangulated and normal vectors have been calculated, the geometry can be very easily stripified:

//invoke the Stripifier on the GeometryInfo

Stripifier st = new Stripifier()

st.stripify(gi);

//extract the stripified GeometryArray

Shape3D shape2 = new Shape3D( gi.getGeometryArray() );

15.3 Object loaders

Sun has defined the com.sun.j3d.loaders.Loader interface that provides a standard set of methods for accessing the information read in from a 3D data file format. Because there is such a wide variety of 3D data file formats available, the Loader interfac e is fairly high level and does not return graphical information directly but encapsulates it in an object implementing the Scene interface.

15.3.1 LoaderBase

java.lang.Object

 |

 +--com.sun.j3d.loaders.LoaderBase

LoaderBase is a convenience class to manage the setting and retrieval of typical Loader information, such as base URL and load flags. Developers implementing their own Loader can populate, return, and interrogate a LoaderBase instance to provide a consis tent API for end-user developers.

15.3.2 SceneBase interface

java.lang.Object

 |

 +--com.sun.j3d.loaders.SceneBase

SceneBase is a convenience class to manage the setting and retrieval of typical Scene information, such as:

  • Background nodes
  • Behavior nodes
  • Description
  • Fog nodes
  • FOV parameters
  • Lights
  • Objects
  • Sound nodes

Developers implementing their own Loader can populate, return, and interrogate a SceneBase instance to provide a consistent API for end-user developers.

15.3.3 Using the ObjectFile loader

The following example loads a simple Wavefront format .obj file (the hand1.obj file from the Sun Morphing demo). Note that it is also necessary to create lights for the scene and assign an Appearance and Material to the loaded Wavefront object. Figure 15 .3 shows rendered output.

img5.png

Figure 15.3 A Wavefront OBJ file loaded using the Sun ObjectFile loader

 From LoaderTest.java

protected BranchGroup createSceneBranchGroup()

{

 BranchGroup objRoot = super.createSceneBranchGroup();

 //create a TransformGroup to flip the hand onto its end

 //and enlarge it.

 TransformGroup objTrans1 = new TransformGroup();

 Transform3D tr = new Transform3D();

 objTrans1.getTransform( tr );

 tr.rotX(90.0 * Math.PI / 180.0);

 tr.setScale( 10.0 );

 objTrans1.setTransform( tr );

 //create a TransformGroup to rotate the hand

 TransformGroup objTrans2 = new TransformGroup();

 objTrans2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

 objTrans2.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

 BoundingSphere bounds = new BoundingSphere(

                         new Point3d(0.0,0.0,0.0), 100.0);

 //create a RotationInterpolator behavior to rotate the hand

 Transform3D yAxis = new Transform3D();

 Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,

 0, 0,

 4000, 0, 0,

 0, 0, 0);

 RotationInterpolator rotator = new RotationInterpolator(

 rotationAlpha, objTrans2, yAxis, 0.0f, (float) Math.PI*2.0f);

 rotator.setSchedulingBounds(bounds);

 objTrans2.addChild(rotator);

 //Set up the global lights

 Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f);

 Vector3f lDir1  = new Vector3f(-1.0f, -1.0f, -1.0f);

 Color3f alColor = new Color3f(0.2f, 0.2f, 0.2f);

 AmbientLight aLgt = new AmbientLight(alColor);

 aLgt.setInfluencingBounds(bounds);

 DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1);

 lgt1.setInfluencingBounds(bounds);

 objRoot.addChild(aLgt);

 objRoot.addChild(lgt1);

 //load the object file

 Scene scene = null;

 Shape3D shape = null;

 //read in the geometry information from the data file

 ObjectFile objFileloader = new ObjectFile( ObjectFile.RESIZE );

 try

 {

  scene = objFileloader.load( "hand1.obj");

 }

 catch (Exception e)

 {

  scene = null;

  System.err.println(e);

 }

 if( scene == null )

  System.exit(1);

 //retrieve the Shape3D object from the scene

 BranchGroup branchGroup = scene.getSceneGroup();

 shape = (Shape3D) branchGroup.getChild(0);

 //create an Appearance and Material

 Appearance app = new Appearance();

 Color3f objColor = new Color3f(1.0f, 0.7f, 0.8f);

 Color3f black = new Color3f(0.0f, 0.0f, 0.0f);

 app.setMaterial(new Material(objColor, black, objColor,

                              black, 80.0f));

 //assign the appearance to the Shape

 shape.setAppearance( app );

 //connect the scenegraph

 objTrans2.addChild( scene.getSceneGroup() );

 objTrans1.addChild( objTrans2 );

 objRoot.addChild( objTrans1 );

 return objRoot;

}

_________________________________________________________________

15.3.4 Third-party object loaders

There are a wide variety of object loaders available for use with Java 3D. The list of available loaders is maintained by Bill Day at http://www.j3d.org. Data file loaders available for most of the common 3D data file formats are listed in table 15.1.

Table 15.1 Data file loaders

img202.png

img203.png

Different loaders have different limitations and bugs. You should experiment with loaders and file formats until you find one that works for the files you need to display.

15.4 Summary

Java 3D includes relatively basic support for processing geometry using the utility classes in the com.sun.j3d.utils.geometry package. The utilities are easy to use, and are useful for simple triangulation. For more powerful geometry manipulation operati ons (such as mesh generation or decimation you will have to convert one of the many utility libraries, usually written in C).

The interfaces and classes defined in the com.sun.j3d.loaders package however have proved to be very useful. They have defined a standard upon which many of the community Java 3D developers can build, and a large variety of loaders have been released—mos t are free.