![Free-eBooks.net](/resources/img/logo-nfe.png)
![All New Design](/resources/img/allnew.png)
Geometry utility classes and object loaders
15.2 Triangulator, normal vector generator, stripifier
This chapter explains some of the utility classes that Java 3D supplies to help you import or generate geometry for your scenes.
Java 3D comes with three utility classes that facilitate runtime geometry creation:
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:
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};
Figure 15.1 Counterclockwise winding for defining a polygon and a hole for Triangulation
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() );
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:
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.
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
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.
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.