The Modelzilla Graphics Layer - API Reference

The ViewerCore package (under the package name harmonic.viewer3D) is concerned with presenting a three-dimensional models to the user. It has facilities for rendering three- dimensional models to a window, giving feedback when the user moves the mouse cursor over geometry, and handling basic mouse interactions with the view-port.

The ViewerCore is an embeddable library. It does not include any kind of advanced data model, or any user interface other than the view-port window itself. It can be used apart from the user input data layer above. For example it is now serving as the graphics library for the Metallo-Protein Database and Browser (the MDB) at the Scripps Research Institute.

Note a Modelzilla application can mostly ignore the ViewerCore, aside from setting up PaintStruct and PaintAttrib objects. The Modelzilla framework handles initialization, control and feedback of the ViewerCore.


class ViewerCore

The very first thing to do when using this package is to instantiate a ViewerCore object. The Modelzilla framework uses the ViewerCore and it does this. Instantiating the ViewerCore creates the manager objects described below. Because all functions are static, there is no need to keep a reference. One ViewerCore supports multiple view-ports. Just put this line in your code:
	
	new ViewerCore(); 
Note, if you are writing a Modelzilla application you don?t have worry about this.

class PaintStruct

Geometries are given to the a view-port to render by wrapping the geometry in a view- port.PaintStruct object, and telling the system to render the PaintStruct in any number of view- ports with ObjectManager.addObject().

A PaintStruct is simply a holder for a single geometry, rendering attributes, and a coordinate system. It is a total description of a draw-able object.

Key Public Fields

PaintStruct.geom (type Geom)

The geometry to draw. A geometry can be shared between multiple painters. The rendering code (a SceneRenderer) should try to use the same rendering cache if it uses one (a display list in OpenGL) for each geometry. Share geometries if you can.

The geometry reference may be null. This means that the PaintStruct will simply be skipped by any methods that require a geometry, such as picking and rendering methods. We allow the reference to be null because it is a convenience for the application developer to be able to keep a PaintStruct in a view-port at all times, whether it's ready to render or not.

PaintStruct.attrib (type PaintAttrib)

Rendering attributes used when drawing the geometry. A PaintAttrib can be shared between multiple painters. This results in many objects having the same general look. A rendering system may optimize the drawing by discovering PaintStruct objects that share common PaintAttrib objects, and drawing these as a group. This could be significant speed up in the case of drawing hundreds of small geometries.

PaintStruct.coordSystem (type CoordSystem)

Describes coordinate system geometry is drawn in. A CoordSystem can be shared between multiple painters.

PaintStruct.localMatrix (type TMat)

If this is non null it is multiplied on the local side of the globalPerLocal matrix returned by coordSystem.getGlobalPerLocal() function. It enables many PaintStruct's to all share exactly the same CoordSystem object, and actually have different coordinate systems. The total coordinate system of a PaintStruct is returned by PaintStruct.getGlobalPerGeom(). This oddness is intended to support applications (such as the Modelzilla framework) that require objects of many geometries (identical or not) that are rigid with respect to one another. The CoordSystem object can be modified frequently, and all PaintStructs that share it will move as a rigid body.

class Geom

This class lives in the Geometry package, but it is important to our mission here as well. This is a common base class for all renderable geometry objects.

class PaintAttrib

This class specifies parameters for drawing methods. The parameters in this class are admittedly geared towards drawing with the OpenGL library.

class CoordSystem

This class defines the coordinate system a geometry lives in. The complete transformation the global coordinate system is defined by multiplying the local coordinate system of this object and the total coordinate system of the parent CoordSystem object. There is an additional complication, because it is possible to specify a separate parent for translation, rotation, and scaling factors. This can be a convenience sometimes. The CoordSystem class is keeps the local to global transformation cached. Because all access to the transformation matrices through access functions, it is possible for this class to keep invalidation flags and propagate transformations changes to child coordinate systems. This ensures the caching is reliable.

There are several pre defined coordinate systems that have special properties, kept as static references in the CoordSystems class. Do not instantiate more of these special coordinate system types. There is to be one of each.

GlobalCoordSystem

This is the underlying parent of all CoordSystems. It is also the default CoordSystem object assigned by the PaintStruct constructor. It should be thought of as the fixed coordinate system that does not move. It does however, appear to move when the user changes the viewpoint of a view-port. This is to be thought of as the change of viewpoint into an unmoving world.

ViewCoordSystem

This coordinate system is fixed within the view-port the user is interacting with. Its x axis is oriented directly across the horizontal of the computer screen, its y axis points up along the vertical axis of the computer screen, and its Z axis points out of the computer screen. It is centered at the center of the view-port. It is scaled such that there are two units going across the horizontal direction of the window. The single ViewCoordSystem object is a child of the single GlobalCordedSystem. When the user changes the view point in view-port, they are changing the transformation matrix looked at by the ViewCoordSystem. The ViewCoordSystem computes its local to parent transform as the inverse of the viewpoint into the global coordinate system of the active view-port. Objects that are parented to the ViewCoordSystem are drawn in the same position and orientation in the view-port regardless of the viewpoint used to view objects parented to the global coordinate system.

There is only one ViewCoordSystem object. There are usually multiple view-ports. The ViewerCore package is designed to shield developers from the complications of multiple view- ports. An object that wishes to parent to the ViewCoordSystem should not have to change its coordinate system parent whenever a new active view-port is selected. Because of this, the ViewCoordSystem transformation is always calculated from the active view-port, which means the view-port that is currently redrawing or is currently receiving mouse input. A geometry parented to the ViewCoordSystem will be stationary in every view-port.

DeviceCoordSystem

This coordinate system is similar to the view coordinate system, except the axes are different. The DeviceCoordSystem uses the coordinate system convention from two dimensional graphics. It is centered in the upper left-hand corner of the window. The y axis goes down the left edge of the window. The x axis goes across the top edge of the window. It is left handed. It is scaled to match the current pixel resolution of the view-port, so that there is one unit in this coordinate system per pixel in the view-port.

class Viewport

The Viewport class is the JAVA component where geometries are drawn. Actually, it the direct parent of the component where geometries are drawn. This component is produced by the SceneRenderer that is "plugged in" to do the rendering. To acquire a reference to the actual Java component where geometries are drawn, use Viewport.getCanvas(). Application code may talk to the view-port directly to set rendering attributes such as the background color and the depth cue, and to set and acquire transformation matrices.

To add a new view-port to the running application, just call the Viewport constructor. The default constructor will acquire a SceneRenderer reference from ViewportManager.getDefaultSceneRenderer(). The SceneRenderer can also be explicitly specified in the constructor. It should in theory be possible to have a different SceneRenderer in each view-port, but we have actually never tested this.

Be sure to call Viewport.destroyViewport() when the view-port should go away forever. This method does some cleanup with the SceneRenderer and may be overridden (IS overridden in the Modelzilla "ModelViewport") to do additional clean in the larger application.

PaintStructs are given to the Viewport to do render using the ObjectManager class. Because it is possible to have a PaintStruct render in any or all Viewports, the PaintStruct visibility manager the ObjectManager class, which acts as a third party PaintStruct distributor.

Methods of special importance

destroyViewport()

Call when view-port should go away. setSceneRenderer(): Sets the SceneRenderer, swaps out the old canvas for the one returned by the SceneRenderer.

getCanvas()

Returns the actual rendering canvas. Don't use this for registering mouse event listeners. Use the ViewportManager.

format()

Lays out inner windows. A view-port subclass could override this to add new windows other than the rendering canvas.

setViewPerGlobal()

Sets the transformation matrix that takes the global coordinate system to the coordinate system the observer sees things in.

getObjects()

Returns PaintStructs rendered here.

getTransientObjects()

Returns PaintStructs rendered here, that are changing in response to user's interactive input.

getInvalidObjects()

Returns PaintStructs rendered here that need to be redrawn.

getObjectVolume()

Returns the bounding box in view coordinates of objects that should draw here.

invalidate3D()

Invalidates PaintStruct.

class ObjectManager

This class controls which PaintStructs (which we call objects sometimes) appear which Viewports. Because this package supports multiple view-ports, objects are added through this third party distributor rather than adding them to the view-port objects themselves. A single instance of this class exists, returned by ViewerCore.objects().

class ViewportManager

The ViewportManager manages the registration of view-port event listeners. Because this package supports multiple view-ports, event listeners are added to the ViewportManager rather than adding them to the view port objects themselves. This class enables you to add an event listener that will respond to all existing view-ports, and all view-ports yet to come. This way, the application developer does not have to worry about multiple view-port concerns at all for general mouse input needs.

Event registration methods of special importance

addMouseListener(String type, Task task, ViewportMouseListener lst)

This function registers mouse listeners for any viewport. If type is given as null, events from all view-ports are forwarded. Otherwise, use "ModelViewport" for Modelzilla applications, or just "Viewport" if ViewerCore is used independently. If task is non null, task will be required to be active to receive mouse events.

removeMouseListener(String type, Task task, ViewportMouseListener lst)

The reverse of addMouseListener

addSingleViewportListener(Viewport viewport, Task task, ViewportListener listener)

As above, but adds listener to only the given viewport.

removeSingleViewportListener(Viewport viewport, Task task, ViewportListener listener)

The reverse of addSingleViewportListener

[enable,disable]Events()

Viewport events wont be sent if disable has been called more times than enable. In addition to managing the registration of event listeners, ViewportManager manages the invalidation and redraw of the view-ports. Because some public fields are exposed in PaintStruct, etc, it may be necessary to deliberately tell the view port manager to do a redraw after changing PaintAttribs or Geoms. The Modelzilla framework, which uses this package, does do automatic redraws in response to ShapeNode changes, so Modelzilla applications do not have to worry about doing redraws.

Redraw methods of special importance

renderInvalidViewports()

Like is sounds

invalidateAll(boolean bFullRedraw, PaintStruct[] painters)

Mark view ports containing the given PaintStructs as invalid.

class Light

The Light class and its subclasses define the lighting conditions used when rendering a view- port. Lights are specified for each PaintStruct individually, by putting sets of lights in a LightRack object, then assigning PaintStruct.lightRack to each PaintStruct that should use the light rack. A PaintStruct assigns a default light rack to itself in its constructor from the static field PaintStruct.defaultLightRack, which is initialized by the ViewerCore package. A Modelzilla application will generally not have to deal with lights, as these are controlled by the user and applied to the PaintStructs by the framework. However, it is possible to override the framework and give objects different lights deliberately.

In 3D computer graphics, it is primarily because of the effects of lights that we perceive objects in 3D. In real life people also benefit from the stereoscopic effect enabled by having two eyes. However in modern computer graphics we are looking at a 2D screen, so having two eyes does not help. (Unless stereo rendering is used. The ViewerCore package can do a split-screen stereo rendering. Hopefully some more sophisticated techniques will be written in soon.) Therefore in three-dimensional computer graphics we create unrealistically non-uniform lighting to enhance the perception of three dimensions through lighting.

Lighting calculations in 3D computer graphics determine the color and brightness of every point on the rendered surfaces, and sometimes lines and points. The point here is actually a geometry vertex. Points in between vertices are determined by interpolation. A surface will appear darker if its normal points away from the light sources, and brighter if it is pointing towards them. The effects of the lights are additive. The color of the point on the surface is calculating by multiplying the object color with the light color and the light strength based on the angle surface normal and the light's angle of incidence. The points distance to the light source can also be a factor.

It is important to light objects from the top of the view-port rather than the bottom. It computer graphics, if you light a surface that is truly concave from the bottom instead of the top, to the minds visual system it will appear exactly like convex surface with lighting from the top. Likewise is possible to make convex surfaces look concave by lighting it from the bottom instead of the top. Because it is extremely important to know when a surface is receding away from you as opposed to coming towards you, it is best to always light from the top.

class AmbientLight

The AmbientLight creates conditions of uniform lighting from everywhere. Surfaces under the influence of ambient lighting are lit regardless of the orientation of the surface. The AmbientLight is used practically to create a minimum brightness.

class DirectionalLight

The DirectionalLight models light from our god the sun with parallel rays that have a constant intensity. The directional light is the most practical of lights. Sometimes it is good to have several directional lights coming from different directions with different colors.

class PointLight

The PointLight represents lighting conditions from a source that has a certain position and radiates light in all directions, with the strength of the light decaying over distance.

class SpotLight

The SpotLight is exactly like a point light except its influence is confined to a limited angle from the source point.

interface SceneRenderer

This interface forms the specification of the replaceable rendering system. An implementation of SceneRender has the responsibility rendering the objects in the view-port to the screen. Normally the application developer would not care about the SceneRenderer, because one of the values of this system is that it enables the developer to not worry about image generation. But never the less, if the need arises it is possible to plug into the SceneRenderer or create a new one to render the scene.

In addition to rendering the scene the SceneRenderer does some supporting things: It produces the three dimensional graphics drawing canvas (a Component subclass), which it likely has to specialize to get the graphics out. It should be able to returning a graphics profile object that breaks down how much time and effort is spend doing what. And it should be able to return image and depth buffer information. The SceneRenderer to use is set with ViewportManager.setDefaultSceneRenderer().

The ViewerCore package has two SceneRenderers at this time. One, harmonic.viewer3D.OpenGL, uses the OpenGL library to render the geometries. The other, harmonic.viewer3D.Java2D, uses the standard Java 2D library that is part of the standard Java distribution. The first SceneRenderer, now discontinued, used Java3D.

Because the ViewerCore uses a pluggable SceneRenderer, it is not dependant on any underlying graphics library. This enables it to run in a web browser without any special plug in, by using the Java2D SceneRenderer. When OpenGL is available (which is most of the time), the ViewerCore can use the OpenGL SceneRenderer. If Java3D ever becomes worth using, the Java3D SceneRenderer can be dusted off. (Java3D also uses OpenGL. If the ViewerCore package used Java3D it would be a double wrapper round OpenGL, which gets cumbersome.)

This package used to have a dependency on Java3D hard wired into its guts. The SceneRender interface was created in the process of changing from a Java3D renderer to an OpenGL renderer. The SceneRender interface allowed us to switch back and forth between the two rendering systems as the OpenGL renderer gradually improved and surpassed the Java3D renderer. If someone is so in love with Java3D we will happily give them our Java3D SceneRenderer code and they can work on making as good as possible to prove that Java3D is better than OpenGL.

It is arguable that the ViewerCore package is simply a replacement for Java3D. However, they have different philosophies. Java3D's intent is to pretend it is the three-dimensional graphics engine itself, when in fact it is not. It is simply handing off all the work to OpenGL. And Java3D creates systems that are so rigid in the name of optimizing the scene that we found using it unworkable. And it was so buggy it seems like half our development time was spent determining which bugs were in our code and which were in theirs. When we switched to OpenGL everything simply worked as advertised and were able to move on to greater things. But this strays from the point. This package does not pretend the OpenGL does not exist. Java3D is constrained to be able to do only the intersection of the abilities of OpenGL and Direct3D, because it will use either. The information from our ViewerCore can be rendered in any way by our pluggable SceneRenderer system. We don't care if the graphics look the same from one SceneRenderer to the next. It allows the developer to calls or any other graphics library they wish. It is possible loop over the individual vertices of your objects and to draw them as needed, and it is also possible to draw without any geometry object at all. This system also has a more informal scene graph. In fact we were never intended on creating any kind of scene graph system other than a linear set of objects to draw. This package was actually created by accident over the course of filling in the practical needs related to graphics in building the Modelzilla framework. Eventually it was pulled out as a graphics subsystem of Modelzilla that is fine library by itself.


class OpenGL_SceneRenderer

This package is the OpenGL implementation of the SceneRender specification. The OpenGL SceneRender package makes use of the JOGL JAVA bindings for OpenGL. It used to use the excellent GL4Java by Jausoft in Germany. The OpenGL_SceneRender can usually be treated as a closed box that just does its job without any input on the application developer's part. It will reliably render any geometry with regard to the PaintAttrib and CoordSystem information in a PaintStruct. However, it is possible to write custom drawing functions in three ways:
  1. Replace the entire SceneRenderer. This is a lot of work, and since the SceneRenderer's job is to render every object in the scene, some of which may originate from other applications, so we don't recommend this.
  2. Provide a new rendering method to render selected PaintStructs. PaintStruct has public field objectRenderer. It is declared as an Object because the "object renderer" concept is confined to specific SceneRenderer packages. The harmonic.viewer3D.OpenGL package has an interface within it called OpenGLObjectRenderer. The OpenGL_SceneRenderer loops through the PaintStructs and calls upon the PaintStruct.objectRenderer to do the drawing. When it is invoked, view-port state variables have already been set up, such as the clipping planes and the fog. The Default_OpenGLObjectRenderer sets up OpenGL state variables such as material colors, whether not response to fog, and sets up the display listing. Then invokes the OpenGLGeometryDrawer referenced by the geometry to draw the geometry. An application could for example have a specialized way of drawing spheres that is faster or better than drawing lots of polygons. It could reassign PaintStruct.objectRenderer to draw spheres in its special way.
  3. Provide a new rendering method to render selected Geoms. Geom has a public field geometryDrawer. It should be an implementer of OpenGLGeometryDrawer. An OpenGLGeometryDrawer knows how to loop through the elements of the geometry. For vertex array based geometry, it should transfer the vertices OpenGL. The geometry drawer is invoked within a display list build, so it should not do any operations that are illegal within the display list. If you create a new kind of geometry subclass, you can define the Geom.getGeometryDrawer() to return an instance of a geometry drawer that knows how to draw this kind of geometry. Or you could reassign Geom.geometryDrawer for each object that needs the custom drawer.

To get a handle to the graphics context, use

	GLCanvas canvas = (GLCanvas)paintingView.getCanvas();
	GL gl = canvas.getGL();
in the new OpenGLGeometryDrawer or OpenGLObjectRenderer.

BTW, this re-assignable rendering system is not tested at all. It was written in anticipation of need that we have never needed. If it is not working for you, fix it if you have the source code, or send some e-mail.


class Java2D_SceneRenderer

This is the SceneRender for use on systems that do not have OpenGL installed. It is the default drawer for the Metallo-Protein Database and Browser that runs with a Web browser. This package contains a rendering hook system similar to the OpenGL_SceneRenderer system.

class ViewpointTransformer

This class has some functions for controlling the viewpoint. It is not really intended to be sub- classed.

class Picking

This class is a utility for discovering what objects are under a picking ray or lasso polygon. It actually does this geometrically. It uses a bounding sphere test and a geometric hash table to quickly intersect a ray or polygon with all of the objects in a view-port. Of cource, OpenGL provides such a facility. We may switch to this, but this class was written back when this system used Java3D, and its picking didn?t even work so we had to write our own picker. And this works for any SceneRenderer, as long as it renders real geometries.

class Locater

This class may be used for finding out the bounding boxes of individual objects and the all of objects in a view-port. It has a results caching system that speeds up the case of the same object been queried many times.

class Settings

This class has a table settings are looked at by many pieces of this package.

class GraphicsProfile

This is an abstract class that used to return rendering information.

class MouseViewpointTransformer

This is a class handles the logic for interpreting mouse input into changes in view-point.