[ Next Section | Chapter Index | Main Index ]

Subsections
A Basic Jogl App
Color
Geometric Transforms and Animation
Hierarchical Modeling
Introduction to Scene Graphs
Compiling and Running Jogl Apps

Section 2.1

Basic OpenGL 2D Programs


The first version of OpenGL was introduced in 1992. A sequence of new versions has gradually added features to the API, with the latest version being OpenGL 3.2, in 2009. In 2004, the power of OpenGL was greatly enhanced in version 2.0 with the addition of GLSL, the OpenGL Shading Language, which makes it possible to replace parts of the standard OpenGL processing with custom programs written in a C-like programming language. This course will cover nothing beyond OpenGL 2.1. (Indeed, I have no graphics card available to me that supports a later version.)

OpenGL is an API for creating images. It does not have support for writing complete applications. You can't use it to open a window on a computer screen or to support input devices such as a keyboard and mouse. To do all that, you need to combine OpenGL with a general-purpose programming language, such as Java. While OpenGL is not part of standard Java, libraries are available that add support for OpenGL to Java. The libraries are referred to as Jogl or as the Java Binding for OpenGL. We will be using Jogl version 1.1.1a (although Jogl 2.0 will be available soon).

In this section, we'll see how to to create basic two-dimensional scenes using Jogl and OpenGL. We will draw some simple shapes, change the drawing color, and apply some geometric transforms. This will already give us enough capabilities to do some hierarchical modeling and animation.


2.1.1  A Basic Jogl App

To use OpenGL to display an image in a Java program, you need a GUI component in which you can draw. Java's standard AWT and Swing components don't support OpenGL drawing directly, but the Jogl API defines two new component classes that you can use, GLCanvas and GLJPanel. These classes, like most of the Jogl classes that we will use in this chapter, are defined in the package javax.media.opengl. GLCanvas is a subclass of the standard AWT Canvas class, while GLJPanel is a subclass of Swing's JPanel class. Both of these classes implement an interface, GLAutoDrawable, that represents the ability to support OpenGL drawing.

While GLCanvas might offer better performance, it doesn't fit as easily into Swing applications as does GLJPanel. For now, we will use GLJPanel.

OpenGL drawing is not done in the paintComponent method of a GLJPanel. Instead, you should write an event listener object that implements the GLEventListener interface, and you should register that object to listen for events from the panel. It's the GLEventListener that will actually do the drawing, in the GLEventListener method

public void display(GLAutoDrawable drawable)

The GLAutoDrawable will be the GLJPanel (or other OpenGL drawing surface) that needs to be drawn. This method plays the same role as the paintComponent method of a JPanel. The GLEventListener class defines three other methods:

public void init(GLAutoDrawable drawable)
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
                                                     boolean deviceChanged)

The init method is called once, when the drawable is first created. It is analogous to a constructor. The reshape method is called when the drawable changes shape. The third method can always be left empty; it has to be there to satisfy the definition of a GLEventListener, but it will not be called (and in fact will not even exist in Jogl 2.0).

To actually do anything with OpenGL, you need an OpenGL context, which is represented in Jogl by an object of type GL. You can get a context from a GLAutoDrawable by saying

GL gl = drawable.getGL()

Generally, this statement is the first line of the init method, of the display method, and of the reshape method in a GLEventListener. An object of type GL includes a huge number of methods, and the GL class defines a huge number of constants for use as parameters in those methods. We will spend much of our time in the rest of this course investigating the GL class. In fact, most of the OpenGL API is contained in this class.


We are just about ready to look at our first program! Here is a very simple but functional Jogl Application:

import java.awt.*;
import javax.swing.*;
import javax.media.opengl.*;

public class BasicJoglApp2D extends JPanel implements GLEventListener {

    public static void main(String[] args) {
        JFrame window = new JFrame("Basic JOGL App 2D");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setContentPane(new BasicJoglApp2D());
        window.pack();
        window.setVisible(true);
    }
    
    public BasicJoglApp2D() {
        GLJPanel drawable = new GLJPanel();
        drawable.setPreferredSize(new Dimension(600,600));
        setLayout(new BorderLayout());
        add(drawable, BorderLayout.CENTER);
        drawable.addGLEventListener(this); // Set up events for OpenGL drawing!
    }

    public void init(GLAutoDrawable drawable) {
    }

    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);  // Fill with background color.
        
        gl.glBegin(GL.GL_POLYGON);  // Start drawing a polygon.
        gl.glVertex2f(-0.5f,-0.3f); // First vertex of the polygon.
        gl.glVertex2f(0.5f,-0.3f);  // Second vertex
        gl.glVertex2f(0,0.6f);      // Third vertex
        gl.glEnd();                 // Finish the polygon.
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, 
                                             int width, int height) {
    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
            boolean deviceChanged) {
    }
}

The constructor in this class creates a GLJPanel, drawable, and adds it to the main panel. The main panel itself acts as a GLEventListener, and registers itself to listen for OpenGL events from drawable. This is done with the command drawable.addGLEventListener(this). The display method is then the place where the content of the GLJPanel is drawn.

Let's look at the display method, since it's our first example of OpenGL drawing. This method starts, as discussed above, by getting an OpenGL context, gl. The rest of the method uses that context for all OpenGL drawing operations. The goal is to draw a triangle. Default settings are used for the background color (black), the drawing color (white), and the coordinate system (x coordinates ranging from −1 on the left to 1 on the right; y coordinates, from −1 on the bottom to 1 at the top).

The first step is to fill the entire component with the background color. This is done with the command

gl.glClear(GL.GL_COLOR_BUFFER_BIT);

This command "clears" the drawing area to its background color. It's actually clearing the color buffer, where the color value for each pixel is stored. There are several buffers that can be cleared with this command, and the parameter tells which of them should be cleared. We'll see later how to set the background color.

The rest of the display method draws the triangle. Each of the vertices of the triangle is specified by calling the method gl.glVertex2f. This method just specifies points. To say what is done with the points, the calls to gl.glVertex2f have to come between gl.glBegin and gl.glEnd. The parameter to gl.glBegin gives the meaning of the points -- in this case the fact that they are the vertices of a filled polygon. If we were to replace GL.GL_POLYGON with GL.GL_LINE_LOOP in the glBegin method, then the method would draw the outline of the triangle instead of a filled triangle.

You are probably thinking that "gl.glVertex2f" is a pretty strange name for a method. The names of the methods and constants come from the OpenGL API, which was designed with the C programming language in mind. It would have been possible to simplify the names in Java (and in particular to leave off the extra "gl"), but it was thought that programmers would be more comfortable with the more familiar names. The name "glVertex2f" actually has a very specific format. There is a basic name, "glVertex". Then the number 2 tells how many parameters there will be, and the "f" at the end says that the parameters are of type float. There is a version of the basic method that has two parameters of type double, and its name is "glVertex2d". When we move into 3D, the 2 will change to a 3, giving "glVertex3f" and "glVertex3d", since one needs three coordinates to specify a point in three dimensions. The names make sense. A lot of method names in OpenGL work the same way. Unfortunately, you can't always predict which possible variations of a name are actually defined.

Now that we have a sample Jogl application, we can use the same outline for other programs. We just need to change the init, display, and reshape methods.


2.1.2  Color

To set the drawing color in an OpenGL context gl, you can use one of the glColor methods, such as gl.glColor3f(red,green,blue). For this variation, the parameters are three values of type float in the range 0 to 1. For example, gl.glColor3f(1,1,0) will set the color for subsequent drawing operations to yellow. (There is also a version, glColor3d, that takes parameters of type double.)

Suppose that we want to draw a yellow triangle with a black border. This can be done with a display method

public void display(GLAutoDrawable drawable) {
    GL gl = drawable.getGL();
    gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    gl.glColor3f(1.0f, 1.0f, 0.0f);      // Draw with yellow.
    gl.glBegin(GL.GL_POLYGON);           // Draw a filled triangle.
    gl.glVertex2f(-0.5f,-0.3f);
    gl.glVertex2f(0.5f,-0.3f);
    gl.glVertex2f(0,0.6f);
    gl.glEnd();
    gl.glColor3f(0,0,0);                 // Draw with black.
    gl.glBegin(GL.GL_LINE_LOOP);         // Draw the outline of the triangle.
    gl.glVertex2f(-0.5f,-0.3f);
    gl.glVertex2f(0.5f,-0.3f);
    gl.glVertex2f(0,0.6f);
    gl.glEnd();
}

That takes care of changing the drawing color. The background color is a little more complicated. There is no background color as such; there is a "clear color," which is used by the glClear method to fill the color buffer. The clear color can be set by calling gl.glClearColor(red,blue,green,alpha). This method has only one version, and the parameters must be float values in the range 0 to 1. The parameters specify the color components of the color that will be used for clearing the color buffer, including an alpha component, which should ordinarily be set equal to 1. (Alpha components can be used for transparency, but alphas are a more complicated topic in OpenGL than they are in standard Java graphics, and we will avoid that topic for now.)

The clear color is often set once and for all, and then does not change between one call of display and the next. In that case, it is not necessary to set the clear color in display; it can be set in init instead. This illustrates the use of the init method for setting the values of OpenGL state variables that will remain the same throughout the program. Here is an example that sets the clear color to light blue:

public void init(GLAutoDrawable drawable) {
    GL gl = drawable.getGL();
    gl.glClearColor(0.8f, 0.8f, 1, 1);
}

Note that this uses an important fact about OpenGL. An OpenGL context keeps many internal values in state variables. This includes, among many other things, the clear color, the drawing color, the lighting setup, and the current geometric transform. All these state variables retain their values between calls to init, display, and reshape. This is different from Java graphics, where every call to paintComponent starts with a new graphics context, initialized to default values. In OpenGL, if you are drawing in red at the end of one call to display, then you will be drawing in red at the beginning of the next call. If you want to be sure of drawing with some default drawing color at the beginning of display, you have to put in an explicit call to glColor3f to do so.


2.1.3  Geometric Transforms and Animation

Naturally, we don't want to spend too much time looking at still images. We need some action. We can make a simple animation by introducing a frameNumber instance variable to our program and adding Timer to drive the animation. When the timer fires, we will increment the frame number and call the repaint method of the GLJPanel. Calling repaint will in turn trigger a call to the display method where the actual OpenGL drawing is done. If we make that drawing depend on the frame number, we get an animation.

We can use modeling transforms for animation in OpenGL, just as we did with Graphics2D in Section 1.3. The main difference is that in OpenGL, transforms are meant to work in three dimensions rather than two. In three dimensions, there is a z coordinate, in addition to the usual x and y coordinates. We can still use 3D transforms to operate in 2D, as long as we make sure to leave the z coordinate unchanged.

For example, translation in OpenGL is done with gl.glTranslatef(dx,dy,dz) where the parameters specify the amount of displacement in the x, y, and z directions. By setting the third parameter, dz, to 0, we get a 2D translation. The parameters are of type float, as indicated by the "f" at the end of "translatef". If you want to use parameters of type double, you can use gl.translated. The names for the other transform methods work in the same way.

Similarly, scaling can be done with gl.glScalef(sx,sy,sz), where the parameters specify the scaling factor in the x, y, and z directions. To avoid changing the scale in the z direction, the third parameter, sz, should be 1.

Rotation transforms are a little more complicated, since rotation in 3D is more complicated than rotation in 2D. In two dimensions, rotation is rotation about a point, and by default, the rotation is about the origin, (0,0). In three dimensions, rotation is about a line called the axis of rotation. Think of the rotating Earth spinning about its axis. To specify a rotation in OpenGL, you must specify both an angle of rotation and an axis of rotation. This can be done with gl.glRotatef(d,x,y,z), where d is the angle of rotation, measured in degrees, and the axis of rotation is the line through the origin (0,0,0) and the point (x,y,z). For a 2D rotation, you can set (x,y,z) to (0,0,1). That is, for a rotation of d degrees about the 2D origin (0,0), you should call gl.glRotatef(d,0,0,1). The direction of rotation is counterclockwise when d is positive.

As with Graphics2D, transforms apply to drawing that is done after the transform is specified, not to things that have already been drawn. And when there are multiple transforms, then they are applied to shapes in the reverse of the order in which they are specified in the code. For example, if we want to scale a 2D object by a factor of 1.8, rotate it 30 degrees, and then move it 3 units over and 1.5 units up, we could say

gl.glTranslatef( 3, 1.5f, 0 );
gl.glRotatef( 30, 0, 0, 1 );
gl.glScalef( 1.8f, 1.8f, 1 );
  ...  // draw the object

Keep in mind that the transforms apply to all subsequent drawing, not just to the next object drawn, unless you do something to restore the original transform. In fact, since OpenGL state persists across calls to display, the transform will still be in effect the next time display is called, unless you do something to prevent it! To avoid this problem, it's common to set the transform at the beginning of the display method to be the identity transform. This can be done by calling the method gl.glLoadIdentity().

Here, for example, is a display method that draws a rotated triangle:

public void display(GLAutoDrawable drawable) {
    GL gl = drawable.getGL();
    gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    gl.glLoadIdentity();                 // Start from the identity transform!
    gl.glRotated(frameNumber, 0, 0, 1);  // Apply a rotation of frameNumber degrees.
    gl.glColor3f(1.0f, 1.0f, 0.0f);
    gl.glBegin(GL.GL_POLYGON);           // Draw a yellow filled triangle.
    gl.glVertex2f(-0.5f,-0.3f);
    gl.glVertex2f(0.5f,-0.3f);
    gl.glVertex2f(0,0.6f);
    gl.glEnd();
    gl.glColor3f(0,0,0);
    gl.glBegin(GL.GL_LINE_LOOP);         // Outline the triangle with black.
    gl.glVertex2f(-0.5f,-0.3f);
    gl.glVertex2f(0.5f,-0.3f);
    gl.glVertex2f(0,0.6f);
    gl.glEnd();
}

Since the rotation depends on frameNumber, we can use this method in an animation of a spinning triangle. You can find the full source code for this simple animation program in BasicJoglAnimation2D.java Here is an applet version of the animation:


2.1.4  Hierarchical Modeling

When we do geometric modeling, we need a way to limit the effect of a transform to one object. That is, we should save the current transform before drawing the object, and restore that transform after the object has been drawn. OpenGL has methods that are meant to do precisely that. The command gl.glPushMatrix() saves a copy of the current transform. It should be matched by a later call to gl.glPopMatrix(), which restores the transform that was saved by gl.glPushMatrix (thus discarding any changes that were made to the current transform between the two method calls). So, the general scheme for geometric modeling is:

gl.glPushMatrix();
   ...  // Apply the modeling transform of the object.
   ...  // Draw the object.
gl.glPopMatrix();

("Matrix," by the way, is really just another name for "transform." It refers to the way in which transforms are represented internally.)

As you might suspect from the method names, transforms are actually saved on a stack of transforms. (Unfortunately, you cannot assume that this stack can grow to arbitrary size. However, OpenGL implementations must allow at least 32 transforms on the stack, which should be plenty for most purposes.) glPushMatrix adds a copy of the current transform to the stack; glPopMatrix removes the transform from the top of the stack and uses it to replace the current transform in the OpenGL context.

The use of a stack makes it easy to implement hierarchical modeling. You can draw a scene using glPushMatrix and glPopMatrix when placing each object into the scene, to limit transforms that are applied to an object to just that object. But each of the objects could itself be a complex object that uses glPushMatrix and glPopMatrix to draw its sub-objects. As long as each "push" is matched with a "pop," drawing a sub-object will not make any permanent change to the transform stack. In outline, the process would go something like this:

pushMatrix
apply overall object modeling transform
   pushMatrix
   apply first sub-object modeling transform
   draw first sub-object
   popMatrix
   pushMatrix
   apply second sub-object modeling transform
   draw second sub-object
   popMatrix
     .
     .
     .
popMatrix

And keep in mind that the sub-objects could themselves be complex objects, with their own nested calls to glPushMatrix and glPopMatrix.

In HierarchicalModeling2D.java, in Subsection 1.3.2, we did some hierarchical modeling using Java Graphics2D. We're now in a position to do the same thing with Jogl. The sample program JoglHierarchicalModeling2D.java is an almost direct port of the Graphics2D example. It uses similar subroutines to draw a sun, a wheel, a cart, and a windmill. For example, here is the cart-drawing subroutine in OpenGL:

private void drawCart(GL gl) {

    gl.glPushMatrix();                // Draw the first wheel as a sub-object.
    gl.glTranslatef(-1.5f, -0.1f, 0);
    gl.glScalef(0.8f,0.8f,1);
    drawWheel(gl);
    gl.glPopMatrix();
    
    gl.glPushMatrix();                // Draw the second wheel as a sub-object.
    gl.glTranslatef(1.5f, -0.1f, 0);
    gl.glScalef(0.8f,0.8f,1);
    drawWheel(gl);
    gl.glPopMatrix();
    
    gl.glColor3f(1,0,0);             // Draw a rectangle for the body of the cart
    gl.glBegin(GL.GL_POLYGON);
    gl.glVertex2f(-2.5f,0);
    gl.glVertex2f(2.5f,0);
    gl.glVertex2f(2.5f,2);
    gl.glVertex2f(-2.5f,2);
    gl.glEnd();
}

When the display method draws the complete scene, it calls the drawCart method to add a cart to the scene. The cart requires its own modeling transformation to place it into the scene, and that transform requires its own calls to glPushMatrix and glPopMatrix:

gl.glPushMatrix();
gl.glTranslated(-3 + 13*(frameNumber % 300) / 300.0, 0, 0);
gl.glScaled(0.3,0.3,1);
drawCart(gl); // does its own pushes and pops internally!
gl.glPopMatrix();

I encourage you to look through the program, even though it does use a few techniques that we haven't covered yet. Here's an applet version of the program:


2.1.5  Introduction to Scene Graphs

So far, we have been doing hierarchical modeling using subroutines to draw the objects. This works well, but there is another approach that goes well with object-oriented programming. That is, we can represent the graphical objects in a scene with software objects in the program. Instead of having a cart-drawing subroutine, we might have an object of type Cart, where Cart is a class that we have written to represent carts in the scene. (Or, as I will do in the actual example, we could have a cart object belonging to a more general class ComplexObject2D, which represents objects that are made up of sub-objects.) Note that the cart object would include references to the cart's sub-objects, and the scene as a whole would be represented by a linked collection of software objects. This linked data structure is an example of a scene graph. A scene graph is a data structure that represents the contents of a scene. When you need to draw the scene, you can get all the information that you need by traversing the scene graph.

The sample program JoglHMWithSceneGraph2D.java is another version of our hierarchical modeling animation, this one using a scene graph. In this version, an object in the scene is represented in the program by an object of type SceneNode2D. SceneNode2D is an abstract class (defined in scenegraph2D/SceneNode2D.java). It has an abstract method, public void draw(GL gl), which draws the object in an OpenGL context. A SceneNode2D can have a color, and it can have a transformation that is specified by separate scaling, rotation, and translation factors. The color in this case is an example of an attribute of a node in a scene graph. Attributes contain properties of objects beyond the purely geometric properties.

SceneNode2D has a subclass ComplexObject2D that represents an object that is made up of sub-objects. Each sub-object is of type SceneNode2D and can itself be a ComplexObject2D. There is an add method for adding a sub-object to the list of sub-objects in the complex object. The draw method in the ComplexObject2D class simply draws the sub-objects, with their proper colors and transformations. Note that the complex object can have its own transformation, which is applied to the complex object as a whole, on top of the transformations that are applied to the sub-objects. (The treatment of color for complex objects is also interesting. I have decided that the color of an object can be null, which is the default value. If the color of a sub-object is null, then its color will be inherited from the main object in which it is contained. The general question of how to handle attributes in scene graphs is an important design issue.)

SceneNode2D also has several subclasses to represent geometric primitives. For example, there is a class LineNode that represents a single line segment between two points. There is also PolygonNode and DiskNode. These geometric primitive classes represent the "bottom level" of the scene graph. They are objects that are not composed of simpler objects, and they are the fundamental building blocks of complex objects.

In the sample animation program, an instance variable named theWorld of type ComplexObject2D represents the entire content of the scene. This means that in the display method, drawing the content of the image requires only one line of code:

theWorld.draw(gl);

Most of the work has been moved into a method, buildTheWorld(), that constructs the scene graph. This method is called only once, at the beginning of the program. It's interesting to see how objects in the scene are constructed. Here, for example, is the code the creates a wheel for the cart:

ComplexObject2D wheel = new ComplexObject2D();
wheel.setColor(Color.BLACK);   // Color for most of the wheel.
wheel.add( new DiskNode(1) );  // The parameter is the radius of the disk.
DiskNode hubcap = new DiskNode(0.8);
hubcap.setColor(new Color(0.75f, 0.75f, 0.75f)); // Override color of hubcap.
wheel.add(hubcap);
wheel.add( new DiskNode(0.2) );
for (int i = 0; i < 15; i++) { // Add lines representing the wheel's spokes.
    double angle = (2*Math.PI/15) * i;
    wheel.add(new LineNode(0,0,Math.cos(angle),Math.sin(angle)));
}

Once this object is created, we can add it to the cart object. In fact, we can add it twice, with two different transformations. When the scene graph is traversed, the wheel will appear twice. The two occurrences will be in different locations because of the different transformations that are applied. To make things like this easier, I made a subclass TransformedObject of SceneNode2D to represent an object with an extra transform to be applied to that object. Here, then, is how the cart is created:

ComplexObject2D cart = new ComplexObject2D();
cart.setColor(Color.RED); // Overall color; in fact, will apply only to the body.
cart.add( new TransformedObject(wheel, 0.8, 0, -1.5, -0.1) ); // First wheel.
cart.add( new TransformedObject(wheel, 0.8, 0, 1.5, -0.1) );  // Second wheel.
cart.add( new PolygonNode( 
               new double[] { -2.5, 0, 2.5, 0, 2.5, 2, -2.5, 2} ) ); // The body.
cart.setScaling(0.3);  // Suitable scaling for the overall scene.

We can now add the cart to the scene simply by calling theWorld.add(cart). You can read the program to see how the rest of the scene graph is created and used. The classes that are used to build the scene graph can be found in the source directory scenegraph2D.

This leaves open the question of how to do animation when the objects are stored in a scene graph. For the type of animation that is done in this example, we just have to be able to change the transforms that are applied to the object. Before drawing each frame, we will set the correct transforms for that frame.

In the sample program, I store references to the objects representing the wheel, the cart, and the rotating vanes of the windmill in instance variables wheel, cart, and vanes. To animate these objects, the display methods includes commands to set the rotation or translation of the object to a value that depends on the current frame number. This is done just before the world is drawn:

wheel.setRotation(frameNumber*20);
vanes.setRotation(frameNumber * (180.0/46));
cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0);

theWorld.draw(gl);

The scene graph framework that is used in this example is highly simplified and is meant as an example only. More about scene graphs will be coming up later. By the way, a scene graph is called a "graph" because it's generally in the form of a data structure known as a "directed acyclic graph."


2.1.6  Compiling and Running Jogl Apps

Since Jogl is not a standard part of Java, you can't run programs that use it unless you make the Jogl classes available to that program. The classes that you need for Jogl 1.1.1 are distributed in two jar files, jogl.jar and gluegen-rt.jar. These jar files must be on the Java classpath when you compile or run any program that uses Jogl.

Furthermore, Jogl uses "native code libraries." Native code is code that is written in the machine language that is appropriate to a particular kind of computer, rather than in Java. The interface to OpenGL requires some native code, which means that you have to make the appropriate native code libraries available to Java when you run (but not when you compile) any program that uses Jogl. Unfortunately, you need different native code libraries for Linux, for MacOS, and for Windows. That's why there is a different Jogl download for each platform. The download for a given platform includes the appropriate native code libraries for that platform. As I write this, the downloads for Jogl 1.1.1a for various platforms can be found at:

http://download.java.net/media/jogl/builds/archive/jsr-231-1.1.1a/

You will need to get the download for your platform if you want to develop Jogl programs on the command line or in the Eclipse IDE. However, if you use Netbeans (http://netbeans.org) as your development environment, you can avoid the whole issue. There is a Netbeans plugin that adds full support for Jogl/OpenGL development. If you install the plugin, you can develop and run your programs in Netbeans with no further action. As I write this, the current version of the plugin supports Jogl 1.1.1a, and information about it can be found at

http://kenai.com/projects/netbeans-opengl-pack/pages/Home

If Eclipse (http://eclipse.org) is your development environment, it's just a little harder. You need to get the Jogl 1.1.1a download for your platform. Unzip the zip file and put the directory in some convenient place. The directory has a lib subdirectory that contains the jar files and native code libraries that you need. While you are downloading, you might also want to get the Jogl documentation download, jogl-1.1.1a-docs.zip, and unzip it as well.

Then, when you want to use Jogl in an Eclipse project, you have to do the following setup in that project: Right-click the name of the project in the "Project Explorer" pane. In the pop-up menu, go to the "Build Path" submenu and select "Configure Build Path...". A dialog box will open. Click on the "Libraries" tab in the "Java Build Path" pane. Add the two required Jogl jar files to the build path. [Click the button "Add External Jars...". In the file dialog box, browse to the lib directory in the Jogl download. Select jogl.jar and click "OK". Now do the same thing for gluegen-rt.jar.] Finally, set up the jar files to use the Jogl native code libraries. [Click the triangle to the left of jogl.jar in the list of libraries. This will reveal a setting for "Native Library Location". Click it, then click the "Edit" button. Browse to the Jogl lib directory, and click OK. Do the same thing for gluegen-rt.jar.] If you downloaded the Jogl docs, you will also want to set up the "Javadoc Location" under "jogl.jar". Set it to point to the unzipped documentation directory.

If you've gotten all the steps right, you can then compile and run Jogl programs in your Eclipse project.


If you like to work on the command line, you can still do that with Jogl. When using javac to compile a class that uses Jogl, you will need to add the Jogl jar file, jogl.jar, to the classpath. For example:

javac -cp /home/eck/jogl-1.1.1/lib/jogl.jar:. packagename/*.java

Of course, you need to replace /home/eck/jogl-1.1.1/lib with the correct path to the jar file on your computer. (This example assumes a Linux or Mac OS computer; on Windows, paths look a little different.) When using the java command for running a program that uses Jogl, you need to add both jogl.jar and gluegen-rt.jar to the class path. Furthermore, Java must be able to find the native library files that are also in the lib directory of the Jogl download. For Linux, that directory should be added to the LIBRARY_PATH environment variable; for Mac OS, it should be added to DYLD_LIBRARY_PATH; and for Windows, it should be added to PATH. Here for example is a sequence of commands that might be used on Linux to run a class, packagename.MainClass, that uses Jogl:

JOGL_LIB_DIR="/home/eck/jogl-1.1.1/lib"
JOGL_JAR_FILES="$JOGL_LIB_DIR/jogl.jar:$JOGL_LIB_DIR/gluegen-rt.jar"
export LIBRARY_PATH="$JOGL_LIB_DIR"
java -cp $JOGL_JAR_FILES:. packagename.MainClass

All that typing would quickly get annoying, so you will want to use scripts to do the compiling and running. The source code directory for this book contains two simple scripts, gljavac.sh and gljava.sh, that can be used to compile and run Jogl programs on Mac OS or Linux. You will just need to edit the scripts to use the correct path to the Jogl lib directory. (Sorry, but I do not currently have scripts for Windows.)


[ Next Section | Chapter Index | Main Index ]