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:
-
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.
-
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.
-
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.