2 Rendering

2.1 Overview

This chapter describes the maspack rendering package (maspack.render), which provides a general interface for the graphical rendering of objects and allows them to implement their own rendering methods. An object makes itself renderable by implementing the IsRenderable interface, and renderable objects can then be displayed by a Viewer, which typically provides features such as viewpoint control, lighting arrangements, fixtures such as coordinate axes, grids and clipping planes, and component selection. The viewer also implements a Renderer interface which provides the actual graphics functionality which renderable objects use to draw themselves.

2.1.1 Renderables and Viewers

Any object to be rendered should implement the IsRenderable interface, which defines the following four methods,

   void prerender (RenderList list);
   void render (Renderer renderer, int flags);
   void updateBounds (Vector3d pmin, Vector3d pmax);
   int getRenderHints();

prerender() is called prior to rendering and allows the object to update internal rendering information and possibly give the viewer additional objects to render by placing them on the RenderList (Section 2.2.2). render() is the main method by which an object renders itself, using the functionality provided by the supplied Renderer. updateBounds() provides bounds information for the renderable’s spatial extent (which the viewer can use to auto-size the rendering volume); Vector3d describes a 3-vector and is defined in the package maspack.matrix. getRenderHints() returns flags giving additional information about rendering requirements, including whether the renderable is transparent (IsRenderable.TRANSPARENT) or two dimensional (IsRenderable.TWO_DIMENSIONAL).

Figure 2.1: General relationship between viewers, renderers, and renderables. A viewer is associated with a number of renderables, and provides the renderer interface with which renderables use to draw themselves via their render() methods.

A Viewer provides the machinery needed to display renderable objects, and implements the Renderer interface (Section 2.3) with which renderables draw themselves within their render() methods. Renderer includes methods for maintaining graphics state, drawing primitives such as points, lines, and triangles, and drawing simple solid shapes. The general relationship between viewers, renderables, and renderers is shown in Figure 2.1. Rendering is triggered within a viewer by calling its rerender() method, which causes the prerender() and render() methods to be called for every renderable, as discussed in detail in Section 2.2.

Figure 2.2: Viewer display of the example of Listing 1.

2.1.2 A Complete Example

Listing 1 shows a complete example of a renderable object being declared and displayed in a viewer.

Listing 1: Declaration and display of a renderable object.
package maspack.render;
import java.awt.Color;
import maspack.matrix.Vector3d;
import maspack.matrix.AxisAlignedRotation;
import maspack.render.GL.GLViewerFrame;
public class RenderableExample implements IsRenderable {
   public void prerender (RenderList list) {
      // can be used to cache rendering data or add extra renderables;
      // not used in this example
   }
   public void render (Renderer renderer, int flags) {
      // draw two spheres and a connecting cylinder
      Vector3d pnt0 = new Vector3d (-1, 0, 0);
      Vector3d pnt1 = new Vector3d ( 1, 0.5, 0);
      renderer.setColor (Color.GRAY);
      renderer.drawSphere (pnt0, 0.5);
      renderer.drawSphere (pnt1, 0.5);
      renderer.setColor (Color.BLUE);
      renderer.drawCylinder (pnt0, pnt1, 0.1, /*capped=*/false);
   }
   public void updateBounds (Vector3d pmin, Vector3d pmax) {
      // extend pmin and pmax to accommodate bounds of (-1, -1, 0) and (1, 1, 0)
      (new Vector3d (-1, -1, 0)).updateBoundsz (pmin, pmax);
      (new Vector3d ( 1,  1, 0)).updateBounds (pmin, pmax);
   }
   public int getRenderHints() {
      // no specific hints
      return 0;
   }
   public static void main (String[] args) {
      IsRenderable renderable = new RenderableExample();
      // create a glviewer in a 640 x 480 frame
      GLViewerFrame frame = new GLViewerFrame (
         "rendererExample", 640, 480);
      Viewer viewer = frame.getViewer();
      viewer.addRenderable (renderable);
      viewer.setAxialView (AxisAlignedRotation.X_Y); // set view transform
      frame.setVisible (true);
   }
}

The example creates a class called RenderableExample which implements isRenderable and the associated methods: prerender(), which does nothing; render(), which draws two gray spheres connected by a blue cylinder; updateBounds(), which sets the maximum and minimum bounds to be the points (-1,-1,0) and (1,1,0), and getRenderHints() which returns no flags. The example then defines a main() method which creates an instance of RenderableExample, along with a GLViewerFrame which contains a viewer with which to display it. The renderable is added to the viewer, the viewpoint is set so that the x and y axes of the viewing plane are aligned with the world x and y axes, and the frame is set to be visible, with the result shown in Figure 2.2.