Open source Java projects: Java Binding for OpenGL (JOGL)

A low-level Java API for 3D graphics

1 2 3 4 5 6 7 8 9 Page 7
Page 7 of 9

Animate a scene

If you need to animate a scene (such as JOGLDemo1's triangle and square), you could create a thread that repeatedly invokes GLAutoDrawable's public void display() method. This method, which you can safely call on any thread, invokes the display() event method of every registered GLEventListener.

However, JOGL provides an easier way to perform animation, by providing the com.sun.opengl.util.Animator class and its com.sun.opengl.util.FPSAnimator subclass. Each class creates a background thread -- via a timer, in the case of FPSAnimator -- that repeatedly invokes the display() methods of all registered drawables.

Animator's public Animator() constructor lets you create an instance with no registered drawables. Alternatively, you can use the public Animator(GLAutoDrawable drawable) constructor to create an instance with the specified drawable. Regardless of constructor, additional drawables can be registered via Animator's public void add(GLAutoDrawable drawable) method.

After registering one or more drawables, start the animation by invoking Animator's public void start() method. To terminate the animation, invoke the public void stop() method. These methods (and their FPSAnimator counterparts) throw the unchecked javax.media.opengl.GLException when you try to start a started or stop a stopped animation.

Animator repeatedly invokes a drawable's display() method and then briefly pauses -- via Thread.yield() -- to avoid swamping the CPU. However, you can avoid this pause and run the animation as fast as possible by invoking Animator's public final void setRunAsFastAsPossible(boolean runFast) method, passing true to runFast.

Listing 3 presents the source code to a JOGLDemo3 application that demonstrates the Animator class, by using this class to rotate JOGLDemo1's scene around the Y axis.

Listing 3. JOGLDemo3.java

// JOGLDemo3.java

// This demo renders a smoothly shaded triangle and a flat shaded square. The
// scene is animated with the triangle and square rotating around the Y axis.
// Because polygon antialiasing is used to minimize jagged edges, depth buffer 
// testing must not be enabled -- it's performed manually by determining which
// geometric object to draw first based on the angle of rotation.

import java.awt.*;
import java.awt.event.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

import javax.swing.*;

import com.sun.opengl.util.*;

public class JOGLDemo3 extends JFrame

...

Download the complete Listing 3.

You might be wondering why the registered window listener's windowClosing() method delegates its animation-stop and application-exit tasks to a background thread. In answer, Animator's Javadoc states that its stop() method might block until completion, which would cause the window to remain displayed until stop() completes if this method was invoked on the event-dispatching thread.

You might also be wondering why JOGLDemo3 assigns an alpha channel to its drawable. The reason has to do with SceneRenderer's use of polygon antialiasing to smooth the edges of the triangular polygons that comprise the triangle and square shapes. Polygon antialiasing cannot be carried out without an alpha channel.

Finally, you might want to know why I perform depth buffer testing manually, rather than take advantage of OpenGL's depth buffer testing, which I used previously in JOGLDemo1 and JOGLDemo2. It turns out that polygon antialiasing doesn't work properly with depth buffer testing. In addition to introducing artifacts, objects aren't properly sorted when both polygon antialiasing and depth buffer testing are enabled.

After compiling its source code, run JOGLDemo3. Figure 6 reveals one frame of the animation.

The triangle and square rotate around a common center.
Figure 6. The triangle and square rotate around a common center.

Running an animation at an arbitrary rate is problematic because the animation runs too fast on some machines, making it hard to watch. Also, if the animation is interrupted by a garbage collection or other lengthy task, the animation may fall behind. For example, if each animation frame is responsible for updating a clock's second hand, a delay will result in the clock losing time because the delay is never corrected.

If your animation must run at a uniform rate, and must also not be affected by delays (games usually have these requirements), you'll want to work with FPSAnimator. This class lets you run an animation at a specified frame rate (also known as frames per second). Also, it lets you take advantage of fixed-rate scheduling to execute an animation the correct number of times over a lengthy period, regardless of delays.

FPSAnimator provides four constructors. The public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) constructor is the most flexible, letting you register a drawable, choose a desired frame rate via fps, and select fixed-rate scheduling by passing true to scheduleAtFixedRate.

As an exercise, modify Listing 3 to use FPSAnimator instead of Animator. Introduce an FPS integer constant that specifies the desired frame rate, and replace the Animator animator; declaration with FPSAnimator animator;. Also replace animator = new Animator (sr); with animator = new FPSAnimator (sr, FPS, true);.

1 2 3 4 5 6 7 8 9 Page 7
Page 7 of 9