2 Rendering

2.6 Mesh Renderers

The package maspack.geometry defines utility classes for the rendering of its mesh objects PointMesh, PolylineMesh, and PolygonalMesh. These include PointMeshRenderer, PolylineMeshRenderer, and PolygonalMeshRenderer. Each of these provides the following methods:

void prerender (XXXMesh mesh, RenderProps props);
void render (Renderer renderer, XXXMesh mesh, RenderProps props, int flags);

where XXXMesh is PointMesh, PolylineMesh, or PolygonalMesh, as appropriate. The prerender() method creates (or updates) a RenderObject (Section 2.4) for the specified mesh, and the render() method then uses this to draw the mesh in accordance with the render properties props and rendering flags. If Renderer.HIGHLIGHT is set in flags, then the mesh is rendered using highlighting (Section 2.3.3.1), although exactly how this is done depends on the mesh type and the rendering properties specified. Because mesh renderers utilize render objects, for efficiency reasons they should be allowed to persist between rendering operations.

The basic pattern for using a mesh renderer within a renderable is to call its prerender and render methods within the corresponding methods for the renderable, as illustrated by the following example in which a renderable contains a PolygonalMesh that needs to be drawn:

protected PolygonalMesh myMesh;
protected RenderProps myRenderProps;
protected PolygonalMeshRenderer myMeshRenderer;
public void prerender (RenderProps props) {
   if (myMeshRenderer == null) {
      myMeshRenderer = new PolygonalMeshRenderer();
   }
   myMeshRenderer.prerender (myMesh, myRenderProps);
}
public void render (Renderer renderer, RenderProps props, int flags) {
   myMeshRenderer.render (renderer, myMesh, myRenderProps, flags);
}

In this example, the PolygonalMeshRenderer is created on demand inside prerender(), but it could be created elsewhere.

When drawing a mesh, a mesh renderer takes into account its mesh-to-world transform (as returned by getMeshToWorld()), and multiplies the current model matrix by this transform value. As indicated above, it also uses the render properties specified by the render() method’s props argument to control how the mesh is drawn. If meshes have vertex normals defined (as returned by getNormals()), then these will be used to support the shading style specified by the shading property. Mesh renderers will also render meshes with vertex-based coloring, for those that have colors defined (as returned by getColors()), with the color interpolation and mixing controlled by the mesh’s getColorInterpolation() and getVertexColorMixing() methods.

For polygonal meshes, PolygonalMeshRenderer will draw the edges separately if the render property drawEdges is true, using the color specified by edgeColor (or lineColor if the former is null). The face style is controlled by faceStyle. If shading is specified as FLAT, then PolygonalMeshRenderer will shaded the faces using the face normals instead of the vertex-based normals returned by getNormals().

PolygonalMeshRenderer will also apply any color, normal, or bump mappings that are specified in the render properties to meshes that have texture coordinates defined. For example, the following listing implements the mappings shown in Listing 10 and Figure 2.28 by creating a mesh to represent the plane and using a RenderProps object to store the required rendering properties:

import maspack.render.*;
import maspack.geometry.*;
...
   PolygonalMesh myMesh;
   PolygonalMesh myRenderer;
   RenderProps myRenderProps;
   void setupMeshAndProps() {
      // create a 6 x 2 rectangular mesh with texture coordinates
      myMesh = MeshFactory.createRectangle (6, 2, /*textureCoords=*/true);
      float[] greenGold = new float[] {0.61f, 0.77f, 0.12f};
      float[] yellowGold = new float[] {1f, 0.44f, 0f};
      RenderProps props = new RenderProps();
      props.setShininess (10);                       // increase shininess
      props.setFaceStyle (FaceStyle.FRONT_AND_BACK); // see both sides
      props.setFaceColor (greenGold);                // base color
      props.setSpecular (yellowGold);                // reflected color
      // create mapping properties and set them in the render properties
      createMaps();
      if (myColorMapEnabled) {
         props.setColorMap (myColorMap);
      }
      if (myNormalMapEnabled) {
         props.setNormalMap (myNormalMap);
      }
      if (myBumpMapEnabled) {
         props.setBumpMap (myBumpMap);
      }
      myRenderProps = props;
   }
   public void prerender (RenderList list) {
      if (myRenderer == null) {
         myRenderer = new PolygonalMeshRenderer();
      }
      myRenderer.prerender (myMesh, myRenderProps);
   }
   public void render (Renderer renderer, int flags) {
      myRenderer.render (renderer, myMesh, myRenderProps, flags);
   }

In this example, the mesh is created using createRectangle(width,height,addTextureCoords) which generates a rectangular triangular mesh with a specified size, and if addTextureCoords is true, assigns texture coordinates to the vertices with (0,0) and (1,1) corresponding to the lower left and upper right corners.

The mesh classes themselves, PointMesh, PolylineMesh, and PolygonalMesh, implement the Renderable interface, providing their own render properties and using mesh renderers to implement their prerender() and render() methods. They also provide versions of these methods in which the render properties are specified explicitly, bypassing the internal properties, as in

void prerender (RenderList list, RenderProps props);
void render (Renderer renderer, RenderProps props, int flags);

so that the mapping example above could also have been implemented as

   public void prerender (RenderList list) {
      myMesh.prerender (list, myRenderProps);
   }
   public void render (Renderer renderer, int flags) {
      myMesh.render (renderer, myRenderProps, flags);
   }