The Source for Java Technology Collaboration
User: Password:



   

Juggling JOGL Juggling JOGL

by Chris Adamson
03/18/2004


Contents
Recap
Coming Clean
Get Up and Move
Transformation
Game on!

The 1980s were a time of simple-but-intense video games. There were 2D shooters like Galaga and Robotron: 2084. Their 2D graphics belie their age, but the gameplay was elegant: throw dozens or hundreds of moving objects at the player and see what he or she can deal with. The simple movement patterns were straightforward to implement and easy for aspiring game programmers to grasp with just a little knowledge of high-school trigonometry. Working in two dimensions is a good way to begin to understand animation and affine transformations.

This article introduces the concepts in JOGL, the Java bindings to OpenGL, that are applicable to 2D gaming. We start with the handling of coordinate spaces and how they're scaled from the OpenGL world to the screen. Then we integrated JOGL's built-in Animator class to provide motion to our objects. Finally, we introduce three critical affine transformations that allow us to draw individual objects at arbitrary locations, sizes, and rotations.

If you're following along with Prof. F. S. Hill Jr.'s Computer Graphics Using OpenGL, the theoretical basis for this article can be found in chapters 3 and 5.

Recap

We need to begin with a recap of some of the installation and configuration issues that were at the heart of the previous java.net JOGL article, "Jumping Into JOGL." Chief among these is the fact that as JOGL approaches a 1.0 release, milestone builds have fallen away in favor of nightly builds. If you follow the "Precompiled binaries and documentation" link on the JOGL home page, you'll find that the project's "Documents and files" list is empty, with just a link pointing you to "Use nightly build."

JOGL's Builds Download Page shows no release or milestone builds, just nightly builds by platform. These will generally work for everyone except those on Mac OS X. The nightly Mac build seems to want its native libraries to be in a Java 1.4.1 folder, a folder that gets deleted by Apple's Java 1.4.2 installer. Assuming you're running 1.4.2 on Mac OS X 10.3 ("Panther"), the file you want is a special build contributed by Gregory Pierce and available on the old project downloads page.

In any case, the download should simply contain a jogl.jar file, which can go anywhere in your classpath, and one or two native library files (name and extension vary by platform) that need to be along the java.library.path (see the previous article for more on this).

Coming Clean

Back in the first article, a few calls to set up a JOGL drawing space were taken on faith. It's time to explain what was really going on with those calls, because this article will begin using non-trivial values for them.

Recall that a JOGL drawing surface, a GLCanvas, is retrieved via a factory method that passes in an object representing the characteristics of the display. This GLCanvas is not drawn to directly. Instead, we register for events via a GLEventListener interface. This interface requires we handle four calls:

  • init()
  • reshape()
  • display()
  • displayChanged()

Each of these passes in, as a GLDrawable, the surface we are to draw on, by way of getting the GL and GLU pseudo-objects. ("Pseudo" because they're meaningless as objects and are instead a very simple mapping to the thousands of functions in the gl.h and glu.h native header files.)

In the previous article, our very simple reshape() implementation obscured two very important facts about the OpenGL world as it relates to the screen:

  1. OpenGL coordinates don't necessarily map one-to-one with on-screen pixel coordinates.
  2. OpenGL calls are passed through a rendering pipeline that, through a series of affine transformations, allow us to rotate, scale, and translate (move) the entire co-ordinate system.

In OpenGL, the "world" is viewed in Cartesian coordinates, with X values increasing as we go right, and Y values increasing as we go up (there is also a Z axis in 3D, but that's for next time). We define a "world window" as the portion of this world that we want to render to the screen. This is done with the call glOrtho2D() in the GLU class. In the previous article, we forced this to be a size appropriate to counting by on-screen pixels, but this is neither required nor desirable: OpenGL will scale the contents of the world window to our on-screen display, called the viewport. The viewport is set with the glViewport() method in the GL class. Note that, curiously, glOrtho2D()'s arguments are left, right, bottom, and top, while glViewport() takes left, right, width, and height.

To exercise this mapping, please check out the ballbounce.tar.gz sample code that accompanies this article. This application provides a "bouncing ball" demo to show off animation, scaling, and affine transforms. The application consists of three classes:

  1. BouncingBall, which represents the location and movement of a ball.
  2. BallBouncer, which manages the movement of the balls inside of a 2D box.
  3. BallBouncerFrame, which presents the BallBouncer in a AWT Frame, with some widgets to control its behavior.

When run via java BallBouncerFrame, the application looks like Figure 1.

Figure 1
Figure 1. Default BallBouncerFrame

There's nothing particularly interesting unless you notice the black box that has been drawn just inside the panel. The BallBouncer creates a box that is just a bit smaller than the original world window size passed to it, and this box is always drawn as the first step of our display():

// load identity matrix
gl.glMatrixMode (GL.GL_MODELVIEW);
gl.glLoadIdentity(); 

// clear screen
gl.glClear (GL.GL_COLOR_BUFFER_BIT);

// draw the wallInterior
gl.glColor3f( 0.0f, 0.0f, 0.0f ); 
gl.glBegin (GL.GL_LINE_LOOP);
gl.glVertex2i (wallInterior.x,
               wallInterior.y);
gl.glVertex2i (wallInterior.x + wallInterior.width,
               wallInterior.y);
gl.glVertex2i (wallInterior.x + wallInterior.width,
               wallInterior.y + wallInterior.height);
gl.glVertex2i (wallInterior.x,
               wallInterior.y + wallInterior.height);
gl.glEnd();

Notice the bit about load identity matrix. That will be important later.

You might be saying, "Wow, it's a box. Big deal." Well, it's bigger than you might think. That box has a lower left corner at (5, 5) and the sides are 2950 long. But the on-screen panel comes up at 300 by 300. What you're seeing is OpenGL scaling at work. The world window is set to a rectangle at (0, 0) with sides 3000 long, but the viewport is always set to be the size of the panel (initially forced to 300 by 300). OpenGL scales the contents of world window to fit in the viewport.

To exercise this, try using the "View" pop-up. Changing the view causes us to use a different world window. Smaller values aren't interesting at this point, but Figure 2 shows what happens when we expand to 4000 by 4000 or 5000 by 5000.

Figure 2
Figure 2. Zooming out BallBouncerFrame

Remember, the box is still being drawn at the same coordinates: (5,5) to (2995, 5) to (2995, 2995) to (5, 2995) It's just that using the "View" choice results in a new call to gluOrtho2d to reset the world window. And now that our world window is looking at a larger area, the on-screen box is smaller.

This happens in our resetWorldWindow() method, called either on a reshape() or when the user changes his or her preferred view. Our implementation changes the world window but keeps the viewport as the size of the on-screen component:

gl.glMatrixMode( GL.GL_PROJECTION );  
gl.glLoadIdentity(); 
glu.gluOrtho2D (worldWindowRect.x,
                worldWindowRect.x + worldWindowRect.width,
                worldWindowRect.y,
                worldWindowRect.y + worldWindowRect.height);
gl.glViewport( 0, 0, viewportWidth, viewportHeight ); 

Now we've mostly dispelled the mystery of the "trust me" code from the first article, since we've covered the difference between gluOrtho2D(), which establishes the world window, and glViewport(), which sets up the viewport. But what's glLoadIdentity()? An explanation for that will come later.

Pages: 1, 2

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds