This Java tip builds a simple class using the an indexed quadrilateral array. This demonstrates the use of the IndexedQuadArray class. It defines both the vertices and the normals of the shape such that each vertex has only one normal and it appears to have smooth edges.

  import java.awt.BorderLayout;
 import java.awt.Button;
 import java.awt.Frame;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 
 import javax.media.j3d.AmbientLight;
 import javax.media.j3d.Appearance;
 import javax.media.j3d.BoundingSphere;
 import javax.media.j3d.BranchGroup;
 import javax.media.j3d.Canvas3D;
 import javax.media.j3d.DirectionalLight;
 import javax.media.j3d.IndexedQuadArray;
 import javax.media.j3d.Locale;
 import javax.media.j3d.Material;
 import javax.media.j3d.Node;
 import javax.media.j3d.PhysicalBody;
 import javax.media.j3d.PhysicalEnvironment;
 import javax.media.j3d.Shape3D;
 import javax.media.j3d.Transform3D;
 import javax.media.j3d.TransformGroup;
 import javax.media.j3d.View;
 import javax.media.j3d.ViewPlatform;
 import javax.media.j3d.VirtualUniverse;
 import javax.vecmath.AxisAngle4d;
 import javax.vecmath.Color3f;
 import javax.vecmath.Point3d;
 import javax.vecmath.Point3f;
 import javax.vecmath.Vector3f;
 
 /**
  * This builds a simple class using the an indexed quadrilateral array. This
  * demonstrates the use of the IndexedQuadArray class. It defines both the
  * vertices and the normals of the shape such that each vertex has only one
  * normal and it appears to have smooth edges.
  * 
  * @author I.J.Palmer
  * @version 1.0
  */
 public class SimpleIndexedQuadSmooth extends Frame implements ActionListener {
   protected Canvas3D myCanvas3D = new Canvas3D(null);
 
   protected Button myButton = new Button("Exit");
 
   /**
    * This function builds the view branch of the scene graph. It creates a
    * branch group and then creates the necessary view elements to give a
    * useful view of our content.
    * 
    * @param c
    *            Canvas3D that will display the view
    * @return BranchGroup that is the root of the view elements
    */
   protected BranchGroup buildViewBranch(Canvas3D c) {
     BranchGroup viewBranch = new BranchGroup();
     Transform3D viewXfm = new Transform3D();
     viewXfm.set(new Vector3f(0.0f, 0.0f, 5.0f));
     TransformGroup viewXfmGroup = new TransformGroup(viewXfm);
     ViewPlatform myViewPlatform = new ViewPlatform();
     PhysicalBody myBody = new PhysicalBody();
     PhysicalEnvironment myEnvironment = new PhysicalEnvironment();
     viewXfmGroup.addChild(myViewPlatform);
     viewBranch.addChild(viewXfmGroup);
     View myView = new View();
     myView.addCanvas3D(c);
     myView.attachViewPlatform(myViewPlatform);
     myView.setPhysicalBody(myBody);
     myView.setPhysicalEnvironment(myEnvironment);
     return viewBranch;
   }
 
   /**
    * Add some lights so that we can illuminate the scene. This adds one
    * ambient light to bring up the overall lighting level and one directional
    * shape to show the shape of the objects in the scene.
    * 
    * @param b
    *            BranchGroup that the lights are to be added to.
    */
   protected void addLights(BranchGroup b) {
     //Create a bounding sphere to act as the active bounds
     //of the lights
     BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
         100.0);
     //Create the colours and directions
     Color3f lightColour = new Color3f(1.0f, 1.0f, 1.0f);
     Vector3f lightDir = new Vector3f(-1.0f, -1.0f, -1.0f);
     Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f);
     //Create the lights
     AmbientLight ambientLight = new AmbientLight(ambientColour);
     ambientLight.setInfluencingBounds(bounds);
     DirectionalLight directionalLight = new DirectionalLight(lightColour,
         lightDir);
     directionalLight.setInfluencingBounds(bounds);
     //Add the lights to the branch
     b.addChild(ambientLight);
     b.addChild(directionalLight);
   }
 
   /**
    * This builds the content branch of our scene graph. It uses the buildShape
    * function to create the actual shape, adding to to the transform group so
    * that the shape is slightly tilted to reveal its 3D shape. It also uses
    * the addLights function to add some lights to the scene.
    * 
    * @param shape
    *            Node that represents the geometry for the content
    * @return BranchGroup that is the root of the content branch
    */
   protected BranchGroup buildContentBranch(Node shape) {
     BranchGroup contentBranch = new BranchGroup();
     Transform3D rotateCube = new Transform3D();
     rotateCube.set(new AxisAngle4d(1.0, 1.0, 0.0, Math.PI / 4.0));
     TransformGroup rotationGroup = new TransformGroup(rotateCube);
     contentBranch.addChild(rotationGroup);
     rotationGroup.addChild(shape);
     addLights(contentBranch);
     return contentBranch;
   }
 
   /**
    * Build a cube from an IndexedQuadArray. This method creates the vertices
    * as a set of eight points and the normals as a set of six vectors (one for
    * each face). The data is then defined such that each vertex has a
    * different normal associated with it when it is being used for a different
    * face.
    * 
    * @return Node that is the shape.
    */
   protected Node buildShape() {
     //The shape. The constructor specifies 8 vertices, that both
     //vertices and normals are to be defined and that there are
     //24 normals to be specified (4 for each of the 6 faces).
     IndexedQuadArray indexedCube = new IndexedQuadArray(8,
         IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS, 24);
     //The vertex coordinates defined as an array of points.
     Point3f[] cubeCoordinates = { new Point3f(1.0f, 1.0f, 1.0f),
         new Point3f(-1.0f, 1.0f, 1.0f),
         new Point3f(-1.0f, -1.0f, 1.0f),
         new Point3f(1.0f, -1.0f, 1.0f), new Point3f(1.0f, 1.0f, -1.0f),
         new Point3f(-1.0f, 1.0f, -1.0f),
         new Point3f(-1.0f, -1.0f, -1.0f),
         new Point3f(1.0f, -1.0f, -1.0f) };
     //The vertex normals defined as an array of vectors
     Vector3f[] normals = { new Vector3f(1.0f, 1.0f, 1.0f),
         new Vector3f(-1.0f, 1.0f, 1.0f),
         new Vector3f(-1.0f, -1.0f, 1.0f),
         new Vector3f(1.0f, -1.0f, 1.0f),
         new Vector3f(1.0f, 1.0f, -1.0f),
         new Vector3f(-1.0f, 1.0f, -1.0f),
         new Vector3f(-1.0f, -1.0f, -1.0f),
         new Vector3f(1.0f, -1.0f, -1.0f) };
     //Define the indices used to reference vertex array
     int coordIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6, 2, 1,
         0, 4, 5, 1, 6, 7, 3, 2 };
     //Define the indices used to reference normal array
     int normalIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6, 2, 1,
         0, 4, 5, 1, 6, 7, 3, 2 };
     //Set the data
     indexedCube.setCoordinates(0, cubeCoordinates);
     indexedCube.setNormals(0, normals);
     indexedCube.setCoordinateIndices(0, coordIndices);
     indexedCube.setNormalIndices(0, normalIndices);
     //Define an appearance for the shape
     Appearance app = new Appearance();
     Color3f ambientColour = new Color3f(1.0f, 0.0f, 0.0f);
     Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
     Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
     Color3f diffuseColour = new Color3f(1.0f, 0.0f, 0.0f);
     float shininess = 20.0f;
     app.setMaterial(new Material(ambientColour, emissiveColour,
         diffuseColour, specularColour, shininess));
     //Create and return the shape
     return new Shape3D(indexedCube, app);
   }
 
   /**
    * Handles the exit button action to quit the program.
    */
   public void actionPerformed(ActionEvent e) {
     dispose();
     System.exit(0);
   }
 
   public SimpleIndexedQuadSmooth() {
     VirtualUniverse myUniverse = new VirtualUniverse();
     Locale myLocale = new Locale(myUniverse);
     myLocale.addBranchGraph(buildViewBranch(myCanvas3D));
     myLocale.addBranchGraph(buildContentBranch(buildShape()));
     setTitle("SimpleIndexedQuadSmooth");
     setSize(400, 400);
     setLayout(new BorderLayout());
     add("Center", myCanvas3D);
     add("South", myButton);
     myButton.addActionListener(this);
     setVisible(true);
   }
 
   public static void main(String[] args) {
     SimpleIndexedQuadSmooth siqs = new SimpleIndexedQuadSmooth();
   }
 }