Enter the third dimension

Use a Swing component to explore three-dimensional computer graphics

While attending university in the mid-1980s, I studied computer graphics from both two-dimensional and three-dimensional perspectives. Although I enjoyed playing with 2D graphics, I became fascinated with 3D graphics. This fascination led me to write this installment of Java Fun and Games, which provides an introduction to 3D graphics from the Java perspective. I'll eventually apply this and future 3D graphics articles to a 3D-based Java game.

This article first introduces you to a Swing component, whose API lets you load/display a 3D model and adjust a 3D model's viewability. Next, the article introduces you to a Swing applet, whose user interface lets you play with this component. This article concludes by presenting you with a component-based 3D graphics tutorial, which introduces you to 3D graphics fundamentals—starting with coordinate systems and transformations.

Note
This article's Swing component does not rely on Java3D or some other high-level 3D API because high-level 3D APIs are extra baggage to consider when deploying applets. Furthermore, having source code access to a low-level 3D API improves your understanding of 3D graphics. I've based this article on Principles of Interactive Computer Graphics (Second Edition) (by William Newman and Robert Sproull, (McGraw-Hill, 1979; ISBN 0070463387)). If you don't have this book, Amazon provides copies for sale.

A component for 3D graphics

A custom Swing component with a simple API is probably the most convenient tool for introducing 3D graphics to Swing-based applets and applications. To this end, I've created a Panel3D class that subclasses javax.swing.JPanel. This 3D panel component provides a four-method API that creates the component, loads a 3D model into the component, displays the model, and adjusts the model's viewability:

  • public Panel3D(int width): creates a 3D panel. Parameter width identifies the number of pixels on each side of the component. The value passed to this parameter establishes the component's preferred and minimum sizes. I set the component's minimum size so that the component is never shown smaller than its preferred size.
  • public void load(String filename): loads a 3D model from the text file identified by parameter filename, parses the model, stores parsed vertices and edges in the component, and repaints the component to present the model. A java.io.IOException object is thrown if an I/O problem occurs. If a parsing problem occurs, a java.text.ParseException object is thrown.
  • public void lookFrom(double x, double y, double z): establishes the viewpoint, or the position from which the 3D model is viewed. The viewpoint's x, y, and z coordinates are specified by the positive/negative values passed to parameters x, y, and z. The viewpoint always looks toward the model's (0, 0, 0) origin. This method does not repaint the component.
  • public void perspective(double ds): establishes the amount of perspective when viewing the 3D model. Parameter ds represents my textbook's D/S zoom ratio. A large ratio zooms into the model (a telephoto view). In contrast, a small ratio zooms out of the model (a wide-angle view). This method must be called after lookFrom(); it repaints the component.

To get comfortable with Panel3D's methods, consider an example that creates a 3D panel for use in an applet context. This component occupies 60 percent of the applet's width, loads the contents of a cube.3d model file (included with this article's code), lets an observer view the model from viewpoint (6.0, 8.0, 7.0), and sets the perspective to halfway between telephoto and wide-angle:

 Panel3D p3d = new Panel3D (getWidth ()*3/5);
p3d.load ("cube.3d");
p3d.lookFrom (6.0, 8.0, 7.0);
p3d.perspective (1.0 / Math.tan (Math.toRadians (45.0)));

Note: You can download this article's source code from Resources.

The load() method's parser requires cube.3d's contents to agree with my custom model file format. This format recognizes C++ // comments, requires vertex definitions to appear first (one definition per line), and requires edge definitions to appear last (also one definition per line). At least one blank line must separate the last vertex definition from the first edge definition.

Each vertex definition must begin with a label that names the vertex and continue with three space-separated real numbers that (left to right) identify the vertex's x, y, and z coordinates. Each edge definition must consist of two space-separated vertex names—the name on the left identifies the start vertex. These definitions and the rest of the model file format are illustrated in Listing 1.

Listing 1. cube.3d

 

// cube.3d

A -1 1 -1 B 1 1 -1 C 1 -1 -1 D -1 -1 -1 E -1 1 1 F 1 1 1 G 1 -1 1 H -1 -1 1

A B B C

C D D A E F F G G H H E A E B F C G D H

Let there be 3D graphics

Before playing with the 3D panel, you must add this component to a Swing-based applet's or application's GUI. To save you the bother, I've created a Demo3D applet. This applet's GUI presents the 3D panel, three slider controls for specifying x, y, and z viewpoint values (each value ranges from -50 to 50), another slider control for specifying the perspective, and three buttons for loading prebuilt 3D models. See Figure 1.

Figure 1. Demo3D lets you load one of three prebuilt 3D models—like the tower. Click on thumbnail to view full-sized image.

Figure 1 reveals that the 3D panel component renders a 3D model as a wire-frame image to simplify the code (including the model file format illustrated in Listing 1) and shorten the article. As a result, the world doesn't look solid and (depending on the viewpoint's current setting), optical illusions can arise—I'll rectify these problems in a future article.

The GUI also reveals that each of the x, y, and z coordinate axes—drawn by Panel3D's public void paintComponent(Graphics g) method—has its own color: the x axis is red, the y axis is green, and the z axis is blue. The tiny black dash that appears on one side of each axis identifies the negative half of the axis.

A third GUI item of interest: "Field Of View." This angle determines how much of a model is viewable and ranges from 0 to 180 degrees. A small angle zooms into the model: you see less model but more detail. In contrast, a large angle zooms out of the model: you see more model but less detail. The field-of-view angle is mapped to ratio D/S, which passes to the perspective() method—I discuss this mapping later.

The Demo3D class's public void init() method constructs this applet's GUI. It first invokes the private JPanel buildGUI() method and then adds the returned JPanel to the applet's content pane. The buildGUI() method, whose code appears below, requires that the applet's width match its height; otherwise null returns:

1 2 3 4 Page 1