Last modified: 2/4/2002. Current gleem version: 1.2.1
gleem is a small, self-contained library of 3D widgets that support direct user interaction with a 3D scene. Both Java and C++ programming language implementations are included and have similar APIs. The user interfaces are roughly based on the manipulators in Silicon Graphics' Open Inventor; the names are borrowed from Inventor.
The currently implemented manipulators are:
The most recent version of gleem is available from http://www.media.mit.edu/~kbrussel/gleem/.
gleem uses OpenGL for rendering of its manipulators. The Java programming language version of gleem is built on top of Jausoft's OpenGL for Java and uses the Java platform's window system support to collect mouse events; the C++ version uses GLUT for window system interaction. All of the data structures for storing shapes and computing mouse pointer-object intersections are done within the library, not using the OpenGL picking mechanisms.
gleem currently does not implement Inventor-style fields, field-to-field connections, reference counting, run-time type checking, instantiation by type ID, or any other of the basic mechanisms which made Inventor possible. It is implemented entirely using virtual functions; there is no casting down the hierarchy based on run-time type checks.
The most fundamental limitation of gleem is that, in the interest of portability, it has no notion of a scene graph. It defines what little such support it needs internally. All manipulators live conceptually in world coordinates. If you want to attach a manipulator to the end of a kinematic chain, for example, you will need to manually keep the manipulator in place as your application moves the links.
The Java programming language version of gleem was developed with JDK 1.4 and OpenGL for Java 2.8. The sources
make use of the new assert
keyword and therefore will not
compile with earlier JDKs.
Rather than dropping the OpenGL for Java jar and DLL/DSO into the JDK, it is recommended to set up CLASSPATH and either PATH or LD_LIBRARY_PATH entries to point at an installation elsewhere on the local disk. This should avoid conflicts when launching applications with Java Web Start that attempt to download a particular version of OpenGL for Java.
To use gleem's manipulators in a Java program:
GLEventListener
programming model in OpenGL
for Java rather than subclassing GLCanvas
or any of
the other widget classes. gleem's ExaminerViewer, in particular,
relies on being able to add a GLEventListener
to the
GLDrawable
to which it is attached.
GLDrawable
with the ManipManager by
calling ManipManager.registerWindow()
from within
your GLEventListener's init
method.
ManipManager.removeMouseListeners()
and then send
mouse motion, drag, and button events to gleem using
ManipManager
's mouseMoved
,
mouseDragged
, mousePressed
, and
mouseReleased
methods.
ManipManager.showManipInWindow()
.
ManipManager.updateCameraParameters()
to your GLEventListener
's display
method with parameters of your viewing frustum (forward and up
vectors, vertical FOV, image plane aspect ratio, horizontal and
vertical size of window). gleem currently only handles frusta
corresponding to symmetric truncated pyramids.
ManipManager.render()
to your
display
method.
Note that gleem's Java programming language APIs are more strict than the C++ versions about explicitly registering and unregistering windows. The Java platform's window system interface is inherently multithreaded and the added strictness should help ensure the correctness of applications using the library.
You can then call methods such as getTranslation()
and
getRotation()
on the above manipulator classes in, for
example, your display
method, and feed those values
elsewhere. You can also register a motion listener via
Manip.addMotionListener()
which will be called each time
the manipulator is moved.
See the sources TestTranslate1.java, TestTranslate2.java, and TestHandleBox.java for specific, small examples of instantiating manipulators.
The ExaminerViewer class implements trackball-style rotation, translation, and dollying of the scene. Its controls are similar to those of Open Inventor's Examiner Viewer; see the user interface section below for details.
To use the ExaminerViewer in conjunction with the ManipManager:
GLDrawable
with the ManipManager. This
should generally be done within the init
method of
your GLEventListener
.
viewAll
method on the viewer
to set it up for initial interaction.
See TestExaminerViewer.java for an example which creates a HandleBoxManip inside of an ExaminerViewer.
As the Java platform's window system is inherently multithreaded, the
Java version of gleem is necessarily mostly thread-safe. The
ManipManager class is thread-safe, but individual manipulator
instances are not. Multiple window support is handled through the
ManipManager's registerWindow
,
unregisterWindow
, showManipInWindow
, and
removeManipFromWindow
methods.
The C++ version of gleem was originally developed under SGI's Irix 6.5 and later ported to Windows. The Unix Makefile is currently set up for SGI's CC and include directories. Type "make" in the gleem subdirectory to build the library and tests. You will need to set your LD_LIBRARY_PATH to, for example, /users/yourname/gleem-1.2/c++-impl/lib/iris before running any of the tests.
Project files for Microsoft's Visual C++ are located in the subdirectory c++-impl/gleem/visualc .
The sources should be quite portable and compilable with any recent C++ compiler. STL support is required and SGI's version of the STL is included in the distribution. By default gleem puts all of its datatypes into the namespace "gleem", but if your compiler doesn't support namespaces you can define the preprocessor variable GLEEM_NO_NAMESPACES (Namespace.h). If you port gleem to another platform, please consider making a cross-platform Makefile for gmake and submitting your work. The "machtype" script in the top-level directory may be useful for this purpose.
To use gleem's manipulators in a C++ program using OpenGL and GLUT:
ManipManager::init()
to your
main()
. There must be an open window at this point
if you want the ManipManager to install its callbacks. (To
disable callback installation, call
ManipManager::init(false)
.)
mouseFunc
,
motionFunc
, and passiveMotionFunc
methods at the end of your callbacks for events that should be
processed by gleem.
ManipManager::updateCameraParameters()
with
parameters of your viewing frustum (forward and up vectors,
vertical FOV, image plane aspect ratio, horizontal and vertical
size of window). gleem currently only handles frusta
corresponding to symmetric truncated pyramids.
ManipManager::render()
to your display
callback.
You can then call methods such as getTranslation()
and
getRotation()
on the above manipulator classes in, for
example, your GLUT render callback, and feed those values elsewhere.
You can also register a motion callback via
Manip::addMotionCallback()
which will be called each time
the manipulator is moved.
See the sources TestTranslate1.cpp, TestTranslate2.cpp, and TestHandleBox.cpp for specific, small examples of instantiating manipulators.
The ExaminerViewer class implements trackball-style rotation, translation, and dollying of the scene. Its controls are similar to those of Open Inventor's Examiner Viewer; see the user interface section below for details.
To use the ExaminerViewer in conjunction with the ManipManager:
ExaminerViewer *viewer =
new ExaminerViewer();
). This creates a new window internally and
overrides mouse and motion callbacks.
ManipManager::init(false);
. It isn't possible to
re-install the ExaminerViewer's callbacks if they're overridden, so it's
important not to let the ManipManager do so.
viewer->setMouseDelegate(ManipManager::mouseFunc); viewer->setMotionDelegate(ManipManager::motionFunc);
viewer->makeCurrent(); glutPassiveMotionFunc(ManipManager::passiveMotionFunc);
viewer->update();
before you begin drawing
your geometry. This recomputes the ModelView and Projection matrices for
the current rendering context. You should then be able to push matrices on
top of the one loaded by the ExaminerViewer to effect a camera moving about
the scene.
See TestExaminerViewer.cpp for an example which creates a HandleBoxManip inside of an ExaminerViewer.
gleem supports rendering manipulators in multiple windows. See
ManipManager::windowCreated()
,
ManipManager::windowDestroyed()
,
ManipManager::addManipToWindow()
, and
ManipManager::removeManipFromWindow
. By default, manipulators
are rendered in the window which was current at the time of their
instantiation (determined via glutGetWindow()
.) See
TestMultiWin.cpp
for an example of rendering the same
manipulator into two windows.
The C++ version of gleem is not thread-safe. You should not attempt to instantiate or use gleem objects in more than one thread. Further, gleem assumes it is valid to make OpenGL calls, so the library will probably not work properly in a multiprocessing environment like SGI's Performer without modification.
Click and drag to cause motion along a line or in a plane.
The HandleBoxManipulator contains a box with six faces. Clicking and dragging on any of these six faces causes translational motion in the plane parallel to the face.
Each of the six rotation handles, which lie above the cube's faces, can be used to rotate the manipulator about two possible axes. At the start of a drag motion, one of the two rotation axes is chosen: the one most parallel to the viewing direction. If you visualize the two perpendicular planes which go through the handle, the center of the cube, and the three other rotation handles, the plane in which rotation occurs is the one which is facing the camera the most when the drag is initiated.
Scaling is performed with the eight corner handles. Clicking and dragging these handles causes aspect ratio-preserving scaling about the origin of the cube. Shift-clicking and dragging allows stretching of the cube about one of two axes. The two possible axes are chosen when the handle is first shift-clicked, and are chosen to be the principal axes of the face adjacent to the handle which most faces the camera. Scaling along either of these two axes may be the result of the drag; which of the two is chosen is determined using a "snap-to" paradigm, and can change during the drag.
The ExaminerViewer behaves similarly to Inventor's Examiner Viewer. It grabs all drag motions when the Alt or Meta keys are depressed. The ExaminerViewer has a conceptual "focal point" which is always defined as being the focal distance directly in front of the camera.
Drag motions with Alt/Meta key depressed:
Calling the viewAll() method re-positions (but does not re-orient) the ExaminerViewer so the bounding sphere given by the BSphereProvider is contained entirely within the viewing frustum. The focal distance is reset to be the distance between the camera and the center of the bounding sphere.
gleem's javadoc documentation can be browsed on-line.
Manipulators contain Manipulator Parts. The ManipPart hierarchy is divided into two sections, one containing triangle-based pieces of manipulators and one which contains grouping nodes (analogues to Group and Transform nodes). This is gleem's scene graph mechanism. A warning: in the C++ version, shared instancing of "nodes" (ManipParts) in this scene graph is not allowed (since reference counting is not implemented). The ManipManager keeps track of all instantiated manipulators and handles the picking mechanism as well as rendering.
The Manip base class only implements a minimal interface; it is intended that you refer to manipulators through a pointer to the concrete type rather than the abstract Manip type. The simple manipulators (Translate1Manip, Translate2Manip) contain replaceGeometry() methods so you can customize the draggers with whatever geometry you like. It is not so simple to customize a more complex manipulator like the HandleBoxManip, so it contains no such method.
Each manipulator understands its constraints and implements its drag() method from scratch. Clearly there is some commonality between the Translate2Dragger and the translation functionality of the HandleBoxManip. However, this commonality has not been abstracted into a set of classes like Inventor's projectors. Instead there are a few geometrical classes (Line, Plane, PlaneUV) which encapsulate such functionality as ray casting and point projection, and the manipulators determine how to use this information based on what manipulator part was selected and the current configuration of the manipulator.
The linear algebra gleem uses is quite simple and should be accessible to anyone with anyone who has taken an undergraduate linear algebra course. I encourage you to experiment with more sophisticated algorithms for the drag mechanisms.
Recommended texts:
gleem is distributed under the GNU Lesser General Public License, version 2.1, or any later version.
gleem includes a copy of SGI's STL implementation. Here is the copyright notice from this library:
/* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Hewlett-Packard Company makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * * Copyright (c) 1996 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. */
Q: Why don't the manipulators show up in the right place in my application? (Or, why can't I drag them?)
A: gleem expects the following from the rendering state at the time
you call ManipManager's render
method:
TestExaminerViewer.java
or
TestExaminerViewer.cpp
for an example of how an
application using gleem might be structured.
The event handling code is inefficient in the case of multiple events per
render, which typically occurs when there are many manipulators in the
scene and rendering is slow. The mouse motion and passive motion event
handlers in ManipManager
should be restructured to compress
the event input stream into salient events like "highlighted here" (after a
stream of passive motion events) and "dragged here" (after multiple mouse
drag events), the actual work being done in render(). This will probably
require tricky state management in ManipManager
. Perhaps
render() should be renamed update() since it will be doing more work.