TABLE OF CONTENTS (HIDE)

Yet Another Tutorial on JOGL 2.1

Including Nehe JOGL Port

Introduction to JOGL

OpenGL is a cross-platform, language-independent, industrial standard API for producing 3D (and 2D) computer graphics. Graphics cards that claim OpenGL-compliance make use of the hardware acceleration when possible to speed up the graphics rendering process. OpenGL competes with Direct3D on Microsoft Windows platform. The OpenGL mother site is at http://www.opengl.org.

The JOGL (Java Bindings for the OpenGL) allows Java applications to access the OpenGL API for graphics programming. In other words, it is simply a wrapper library for Java application to use OpenGL API. JOGL is open-source and currently maintained by "JogAmp" (Java on Graphics, Audio, Media and Processing) @ http://jogamp.org. JogAMP provides JOGL (3D graphics), JOAL (Java Bindings for OpenAL for 3D Audio) and JOCL (Java Bindings for OpenCL - a Common Language for Graphics Processors).

Alternatives to JOGL include open-source LWJGL (Light-Weight Java Game Library) @ http://lwjgl.org.

This tutorial assumes that you have sufficient knowledge on OpenGL, but new to JOGL. To learn OpenGL, find a good OpenGL book (e.g., the Red book "OpenGL Programming Guide" or Blue Book "OpenGL Superbible"). Nehe production maintains an excellent OpenGL Tutorials (@ http://nehe.gamedev.net). You may also read my OpenGL tutorials.

JOGL 2 supports OpenGL 1.3 to 4.0 and OpenGL ES 1.x and 2.x. JOGL integrates with the AWT, Swing and SWT. It also provides its own native windowing toolkit called NEWT.

This guide is meant for JOGL 2.0 (tested on rc8), which is not compatible with the older (and obsoleted) JOGL 1.x. For JOGL 1.x, read "A Tutorial on JOGL 1.1".

Setting Up JOGL 2.0_rc8

Reference: "Downloading and installing JOGL" @ http://jogamp.org/wiki/index.php/Downloading_and_installing_JOGL.

Step 0: Install JDK

Install JDK, an IDE such as Eclipse/NetBeans or a programming text editor. You need a working Java programming environment to write JOGL programs.

Step 1: Download JOGL

Download the latest "stable" release from JogAMP @ http://jogamp.org/deployment/jogamp-current/archive (or from JogAMP @ http://jogamp.org ⇒ Builds/Downloads ⇒ Current ⇒ zip). Select "jogamp-all-platforms.7z", which contains the JOGL and gluegen JAR-files, Java Native Library (JNI) for all the platforms (e.g., Win32, Win64, Linux, Mac OS), and source-files.

You are also recommended to download the "javadocs" (jogl-javadoc.7z and gluegen-javadoc.7z), "demos" (jogl-demos.7z) and sources.

Step 2: Setup JOGL
  1. Unzip "jogamp-all-platforms.7z" (you can unzip ".7z" file using WinRAR, WinZip, or http://www.7-zip.org/).
    • The jogl's and gluegen's jar-files are kept in the "jar" sub-directory.
    • The Java Native JNI Libraries (".dll" for Windows, ".so" for Linux, or ".jnilib" for MacOS) are kept in the "lib" sub-directories.
  2. Create a JOGL binary directory, says "jogl-2.1" - I shall denote the binary directory as $JOGL_HOME. Create sub-directories "jar", "lib", "src" (optional), "javadoc" (optional) under the $JOGL_HOME.
    Copy the necessary jar-file, native libraries of your operating platform, and source-files into the appropriate sub-directories. Read "http://jogamp.org/wiki/index.php/Downloading_and_installing_JOGL".
    For example, for Win64, copy "jar\gluegen-rt.jar", "jar\jogl.all.jar", "jar\gluegen-rt-natives-windows-amd64.jar" and "jar\jogl-all-natives-windows-amd64.jar" into "jar"; "lib\windows-amd64\gluegen-rt.dll", "lib\windows-amd64\jogl_desktop.dll", "lib\windows-amd64\nativewindow_awt.dll", "lib\windows-amd64\nativewindow_win32.dll", "lib\windows-amd64\newt.dll" into "lib"; and "gluegen-java-src.zip", "jogl-java-src.zip" into "src". Unzip the javadocs downloaded into "javadoc".

Read the "jogl.README.txt".

This step is optional, but it is good to organize the JOGL JAR-files and Java Native Libraries for your operating platform in a single directory.

Step 3a: Customize for Eclipse 4.3
  1. Create a User Library: We shall first create a Eclipse's User Library called "jogl-2.1", which specifies the jar-files, native libraries, javadoc, and source files for the JOGL API. All the JOGL projects can then include this user library in its build path.
    1. From "Window" menu ⇒ Preferences ⇒ Java ⇒ Build Path ⇒ User Libraries ⇒ New ⇒ In "User library name", enter "jogl-2.1".
    2. In "User Library" dialog ⇒ Select "jogl-2.1" ⇒ "Add External JAR..." ⇒ Navigate to "$JOGL_HOME\jar", and select "gluegen-rt.jar" and "jogl.all.jar".
    3. Expand the "jogl.all.jar" node, select "Native library location: (none)" ⇒ "Edit..." ⇒ External Folder... ⇒ select "$JOGL_HOME\lib" to provide the path for the native library code (such as "jogl_desktop.dll" for Windows).
      Repeat for "gluegen-rt.jar" (for "gluegen-rt.dll").
    4. (Optional But Recommended) Expand the "jogl.all.jar" node again ⇒ Select "Javadoc location" ⇒ "Edit..."
      • Specify the javadoc's path (either file: or http:) in "Javadoc URL" if you use an unzip version of the javadoc.
      • Specify the javadoc's archive file (either zip or jar) in "Javadoc in archive" if you use a zip file.
      Choose "Validate", which search for an "index.html" file.
      This is needed for Eclipse to display javadoc information about classes and methods.
    5. (Optional But Recommended) You may provide the source files by editing "Source attachment" ⇒ "Edit..." ⇒ "External File..." ⇒ Select the source file in zip form. Source is needed only if you are interested to debug into the JOGL source codes.
  2. Include the User Library: For EACH JAVA PROJECT created that uses JOGL, right-click on the project ⇒ "Build Path" ⇒ "Add Libraries" ⇒ Select "User Library" ⇒ Check "jogl-2.1".

Read "Java Native Library (JNI) Error" if you encounter error "SEVERE: java.lang.UnsatisfiedLinkError: no xxx in java.library.path".

Step 3b: Customize for NetBeans 7.0 (To Check!)

There was a so-called "NetBeans OpenGL Pack", but it seems to be out-dated and does not support JOGL 2 (?!).

We shall create our own JOGL Library as follows:

  1. Create a JOGL Library:
    1. From "Tool" ⇒ "Library" ⇒ Click "New Libraries..." ⇒ Enter "jogl2.0".
    2. Click "Add JAR/Folder..." ⇒ Select "jogl.all.jar" and "gluegen-rt.jar".
    3. Under the "JavaDoc" tab ⇒ Select the JOGL's javadoc. You could use the zip version for better performance.
    4. Under the "Source" tab ⇒ Select the JOGL's source. You could use the zip version for better performance.
  2. Include the JOGL Library:
    1. For EACH of the JOGL project, include the JOGL library. Right-click on the project ⇒ "Properties" ⇒ "Library" ⇒ Under "Compile" tab ⇒ "Add Libraries..." ⇒ Choose the library "jogl2.0" created earlier.
    2. You also need to include the native library path for each of the project. Right-click the project ⇒ "Set Configuration" ⇒ "Customize..." ⇒ "Run" ⇒ In "VM options", enter "-Djava.library.path=xxx", where xxx is directory path (e.g., d:\bin\jogl2.0\lib), that contains the Java Native JNI Libraries ("*.dll" for Windows, "*.so" for Linux or "*.jnilib" for MacOS).

Read "Java Native Library (JNI) Error" if you encounter error "SEVERE: java.lang.UnsatisfiedLinkError: no xxx in java.library.path".

Step 3c: Customize for JDK/Editor

You need to modify two environment variables - CLASSPATH and PATH. Read "Environment Variables For Java Applications" on how to set these environment variables.

Modify the CLASSPATH environment variable to include the full-path filenames of "jogl.all.jar" and "gluegen-rt.jar", for example,

shell> set classpath=.;$JOGL_HOME\lib\jogl.all.jar;$JOGL_HOME\lib\gluegen-rt.jar

where $JOGL_HOME denotes the JOGL installed directory. Take note that you should include the current working directory '.'.

Modified the PATH environment variable to include the full path to the JOGL's "lib" directory for accessing the native libraries (e.g., "jogl_xxx.dll", "gluegen-rt.dll"), for example,

shell> set path=$JOGL_HOME\lib;......

Alternatively, you could include the directory path of the native libraries in Java system's property "java.library.path", via the VM command-line option -Djava.library.path=pathname, for example,

shell> java -Djava.library.path=d:\bin\jogl2.0\lib myjoglapp

Read "Java Native Library (JNI) Error" if you encounter error "SEVERE: java.lang.UnsatisfiedLinkError: no xxx in java.library.path".

Getting Started with JOGL 2.1

OpenGL Drawable: GLCanvas and GLJPanel

An OpenGL drawable is a surface (or canvas) for graphics rendering. JOGL provides two drawables in package javax.media.opengl.awt: GLCanvas and GLJPanel.

  1. GLCanvas: A heavyweight AWT component which is a subclass of java.awt.Canvas.
    You can create a GLCanvas via the default constructor GLCanvas(), which construct a new GLCanvas component with a default set of OpenGL capabilities, using the default OpenGL capabilities selection mechanism, on the default screen device. For example,
    GLCanvas canvas = new GLCanvas();
    JFrame frame = new JFrame();         // or AWT's Frame
    frame.getContentPane().add(canvas);  // add Component
    canvas.addGLEventListener(.....);
  2. GLJPanel: A lightweight Swing component which is a subclass of javax.swing.JPanel.
    Again, You can create a GLJPanel via the default constructor GLJPanel(), which construct a new GLJPanel component with a default set of OpenGL capabilities, using the default OpenGL capabilities selection mechanism. For example,
    GLJPanel canvas = new GLJPanel();
    JFrame frame = new JFrame();
    frame.getContentPane().add(canvas);  // or frame.setContentPane(canvas);
    canvas.addGLEventListener(.....);

The GLCanvas is a heavyweight AWT component which supports hardware acceleration. It is designed as the primary widget for JOGL applications. On the other hand, GLJPanel is a swing-compatible lightweight component, which supports hardware acceleration but is not as fast as GLCanvas. GLJPanel is intended to provide 100% swing integration when the heavyweight GLCanvas cannot be used. Both the GLCanvas and GLJPanel implement a common interface GLAutoDrawable (which in turn implements the interface GLDrawable). These interfaces define the common behaviors expected on GLCanvas and GLJPanel, so that applications can switch between them with minimal code changes.

Beside supporting AWT, Swing and SWT, JOGL 2 also provide its own Native Window Toolkit called NEWT via drawable GLWindow (in package com.jogamp.newt.opengl). I shall discuss NEWT later.

GLEventListener and GLAutoDrawable

The interface GLEventListener (in package javax.media.opengl) declares the following 4 OpenGL event handlers:

  1. init(GLAutoDrawable drawable): called by the drawable immediately after the OpenGL context is initialized. It can be used to perform one-time initialization tasks such as setting up of lights and display lists. init() runs only once.
  2. dispose(GLAutoDrawable drawable): called by the drawable before the OpenGL context is destroyed. It can be used to release all OpenGL resources, such as memory buffers.
  3. display(GLAutoDrawable drawable): called by the drawable to render OpenGL graphics. It is the most important method.
  4. reshape(GLAutoDrawable drawable, int x, int y, int width, int height): called by the drawable when it is first set to visible, and during the first repaint after the it has been resized. It is used to set the view port and projection mode, and view volume.

All these methods are call-back methods. When an OpenGL event is posted on the event-queue, the graphics sub-system calls back the corresponding handler. An OpenGL renderer shall implement the GLEventListener interface.

Recall that GLCanvas and GLJPanel are GLAutoDrawable. The interface GLAutoDrawable defines these abstract methods to add or remove a GLEventListener:

public void addGLEventListener(GLEventListener listener)
public void removeGLEventListener(GLEventListener listener)

For example,

GLCanvas canvas = new GLCanvas();
canvas.addGLEventListener(renderer);  // add a GLEventListener called renderer
   // renderer shall provides these handlers
   //   init(GLAutoDrawable drawable)
   //   destroy(GLAutoDrawable drawable)
   //   display(GLAutoDrawable drawable)
   //   reshape(GLAutoDrawable drawable, int x, int y, int width, int height)

Whenever an OpenGL event is fired (e.g., init, display), GLCanvas or GLJPanel invokes the corresponding handler of all its registered GLEventListener(s) (such as init() and display()) with itself as the argument for GLAutoDrawable, as illustrated.

Animator

To perform animation, we need an animator to drive the drawable's display() method in a loop to refresh the display regularly. JOGL provides two animator classes: Animator and FPSAnimator (in package com.jogamp.opengl.util). The commonly-used FPSAnimator can drive the display() of the given drawable at the specified number of frame per seconds (fps). For example,

// Construct a drawable
GLCanvas canvas = new GLCanvas();   // or GLJPanel
// Construct an FPS animator, which drives drawable's display() at the specified frames per second
FPSAnimator animator = new FPSAnimator(canvas, 60);
 
animator.start();       // start the animator
animator.pause();       // pause the animator if started
animator.resume();      // resume the animator if paused
animator.stop();        // stop the animator
animator.isStarted();   // started?
animator.isAnimating(); // started and not pause?
animator.isPause();     // started and pause?

The commonly-used constructors are:

FPSAnimator(GLAutoDrawable drawable, int fps)
      // Creates an FPSAnimator with a given target frames-per-second value 
      //  and an initial drawable to animate.
FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate)
      // Creates an FPSAnimator with a given target frames-per-second value, 
      //  an initial drawable to animate, 
      //  and a flag indicating whether to use fixed-rate scheduling.
Animator(GLAutoDrawable drawable)
      // Creates a new Animator for a particular drawable.

OpenGL Graphics Context

In order to perform rendering, an so-called OpenGL rendering context is required. You can retrieve the graphics context from a drawable as follow:

javax.media.opengl.GL  gl = drawable.getGL();
javax.media.opengl.GL2 gl = drawable.getGL().getGL2();  // up to OpenGL 3.0
javax.media.opengl.GL3 gl = drawable.getGL().getGL3();  // up to OpenGL 3.1
javax.media.opengl.GL4 gl = drawable.getGL().getGL4();  // up to OpenGL 4
......
// Others: GL2GL3, GL2bc, GL4bc, GLES1, GLES2, GL2ES1, GL2ES2

[TODO] To Revise.

JOGL 2.0/2.1 Program Templates

GLCanvas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;
import static javax.media.opengl.GL.*;  // GL constants
import static javax.media.opengl.GL2.*; // GL2 constants
 
/**
 * JOGL 2.0 Program Template (GLCanvas)
 * This is a "Component" which can be added into a top-level "Container".
 * It also handles the OpenGL events to render graphics.
 */
@SuppressWarnings("serial")
public class JOGL2Setup_GLCanvas extends GLCanvas implements GLEventListener {
   // Define constants for the top-level container
   private static String TITLE = "JOGL 2.0 Setup (GLCanvas)";  // window's title
   private static final int CANVAS_WIDTH = 640;  // width of the drawable
   private static final int CANVAS_HEIGHT = 480; // height of the drawable
   private static final int FPS = 60; // animator's target frames per second
 
   /** The entry main() method to setup the top-level container and animator */
   public static void main(String[] args) {
      // Run the GUI codes in the event-dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            // Create the OpenGL rendering canvas
            GLCanvas canvas = new JOGL2Setup_GLCanvas();
            canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
 
            // Create a animator that drives canvas' display() at the specified FPS.
            final FPSAnimator animator = new FPSAnimator(canvas, FPS, true);
 
            // Create the top-level container
            final JFrame frame = new JFrame(); // Swing's JFrame or AWT's Frame
            frame.getContentPane().add(canvas);
            frame.addWindowListener(new WindowAdapter() {
               @Override
               public void windowClosing(WindowEvent e) {
                  // Use a dedicate thread to run the stop() to ensure that the
                  // animator stops before program exits.
                  new Thread() {
                     @Override
                     public void run() {
                        if (animator.isStarted()) animator.stop();
                        System.exit(0);
                     }
                  }.start();
               }
            });
            frame.setTitle(TITLE);
            frame.pack();
            frame.setVisible(true);
            animator.start(); // start the animation loop
         }
      });
   }
 
   // Setup OpenGL Graphics Renderer
 
   private GLU glu;  // for the GL Utility
 
   /** Constructor to setup the GUI for this Component */
   public JOGL2Setup_GLCanvas() {
      this.addGLEventListener(this);
   }
 
   // ------ Implement methods declared in GLEventListener ------
 
   /**
    * Called back immediately after the OpenGL context is initialized. Can be used
    * to perform one-time initialization. Run only once.
    */
   @Override
   public void init(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();      // get the OpenGL graphics context
      glu = new GLU();                         // get GL Utilities
      gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
      gl.glClearDepth(1.0f);      // set clear depth value to farthest
      gl.glEnable(GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL_LEQUAL);  // the type of depth test to do
      gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction
      gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smooths out lighting
 
      // ----- Your OpenGL initialization code here -----
   }
 
   /**
    * Call-back handler for window re-size event. Also called when the drawable is
    * first set to visible.
    */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
 
      if (height == 0) height = 1;   // prevent divide by zero
      float aspect = (float)width / height;
 
      // Set the view port (display area) to cover the entire window
      gl.glViewport(0, 0, width, height);
 
      // Setup perspective projection, with aspect ratio matches viewport
      gl.glMatrixMode(GL_PROJECTION);  // choose projection matrix
      gl.glLoadIdentity();             // reset projection matrix
      glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
 
      // Enable the model-view transform
      gl.glMatrixMode(GL_MODELVIEW);
      gl.glLoadIdentity(); // reset
   }
 
   /**
    * Called back by the animator to perform rendering.
    */
   @Override
   public void display(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
      gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
      gl.glLoadIdentity();  // reset the model-view matrix
 
      // ----- Your OpenGL rendering code here (Render a white triangle for testing) -----
      gl.glTranslatef(0.0f, 0.0f, -6.0f); // translate into the screen
      gl.glBegin(GL_TRIANGLES); // draw using triangles
         gl.glVertex3f(0.0f, 1.0f, 0.0f);
         gl.glVertex3f(-1.0f, -1.0f, 0.0f);
         gl.glVertex3f(1.0f, -1.0f, 0.0f);
      gl.glEnd();
   }
 
   /**
    * Called back before the OpenGL context is destroyed. Release resource such as buffers.
    */
   @Override
   public void dispose(GLAutoDrawable drawable) { }
}

Try running the above program, which will show a white triangle on a black screen.

In this template:

  • I follow the design of "Component-based Architecture for Rich Internet Applications (RIA)", where the GUI "Component" (GLCanvas or GLJPanel) and "Container" (JFrame, Frame, JApplet, Applet) are clearly separated. The "Component" can be easily plugged into any of the "Container".
  • The main class extends GLCanvas (Component) to provide the OpenGL graphics rendering canvas. It also implements GLEventListener and provides handlers for init(), display(), dispose() and reshape().
  • The main() method:
    • Allocates a GLCanvas component.
    • Allocates an Animator to drive the display() method of the GLCanvas in a loop to refresh the display.
    • Allocates a top-level container (Swing's JFrame or AWT's Frame), and adds the GLCanvas component into the container..
Using AWT's Frame as Top-Level Container

The above template uses Swing's JFrame as the top-level container. To use AWT's Frame as the top-level window, modify the main() method as follows:

/** The entry main() method to setup the top-level container and animator */
public static void main(String[] args) {
   GLCanvas canvas = new GLCanvas();
   ......
   final Frame frame = new Frame(); // instead of Swing's JFrame
   frame.add(canvas);               // instead of Swing's frame.setContentPane()
   .......
}

[TODO] Check whether GLCanvas is better to use with AWT's Frame or Swing's JFrame if there is no other light-weight components.

Separating the Component and Container Classes

Alternatively, you could separate the renderer Component and the top-level Container into two classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import static javax.media.opengl.GL.*;  // GL constants
import static javax.media.opengl.GL2.*; // GL2 constants
 
/**
 * JOGL 2.0 Program Template (GLCanvas)
 * This is a "Component" which can be added into a top-level "Container".
 * It also handles the OpenGL events to render graphics.
 */
@SuppressWarnings("serial")
public class JOGL2Setup_Renderer extends GLCanvas implements GLEventListener {
   private GLU glu;  // for the GL Utility
 
   /** Constructor to setup the GUI for this Component */
   public JOGL2Setup_Renderer() {
      this.addGLEventListener(this);
   }
 
   // ------ Implement methods declared in GLEventListener ------
 
   /**
    * Called back immediately after the OpenGL context is initialized. Can be used
    * to perform one-time initialization. Run only once.
    */
   @Override
   public void init(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();      // get the OpenGL graphics context
      glu = new GLU();                         // get GL Utilities
      gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
      gl.glClearDepth(1.0f);      // set clear depth value to farthest
      gl.glEnable(GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL_LEQUAL);  // the type of depth test to do
      gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction
      gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smooths out lighting
 
      // ----- Your OpenGL initialization code here -----
   }
 
   /**
    * Call-back handler for window re-size event. Also called when the drawable is
    * first set to visible.
    */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
 
      if (height == 0) height = 1;   // prevent divide by zero
      float aspect = (float)width / height;
 
      // Set the view port (display area) to cover the entire window
      gl.glViewport(0, 0, width, height);
 
      // Setup perspective projection, with aspect ratio matches viewport
      gl.glMatrixMode(GL_PROJECTION);  // choose projection matrix
      gl.glLoadIdentity();             // reset projection matrix
      glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
 
      // Enable the model-view transform
      gl.glMatrixMode(GL_MODELVIEW);
      gl.glLoadIdentity(); // reset
   }
 
   /**
    * Called back by the animator to perform rendering.
    */
   @Override
   public void display(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
      gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
      gl.glLoadIdentity();  // reset the model-view matrix
 
      // ----- Your OpenGL rendering code here (render a white triangle for testing) -----
      gl.glTranslatef(0.0f, 0.0f, -6.0f); // translate into the screen
      gl.glBegin(GL_TRIANGLES); // draw using triangles
         gl.glVertex3f(0.0f, 1.0f, 0.0f);
         gl.glVertex3f(-1.0f, -1.0f, 0.0f);
         gl.glVertex3f(1.0f, -1.0f, 0.0f);
      gl.glEnd();
   }
 
   /**
    * Called back before the OpenGL context is destroyed. Release resource such as buffers.
    */
   @Override
   public void dispose(GLAutoDrawable drawable) { }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;
 
/**
 * JOGL 2.0 Program Template (GLCanvas)
 * This is the top-level "Container", which allocates and add GLCanvas ("Component")
 * and animator.
 */
@SuppressWarnings("serial")
public class JOGL2Setup_RendererMain extends JFrame {
   // Define constants for the top-level container
   private static String TITLE = "JOGL 2.0 Setup (GLCanvas)";  // window's title
   private static final int CANVAS_WIDTH = 640;  // width of the drawable
   private static final int CANVAS_HEIGHT = 480; // height of the drawable
   private static final int FPS = 60; // animator's target frames per second
 
   /** Constructor to setup the top-level container and animator */
   public JOGL2Setup_RendererMain() {
      // Create the OpenGL rendering canvas
      GLCanvas canvas = new JOGL2Setup_Renderer();
      canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
 
      // Create a animator that drives canvas' display() at the specified FPS.
      final FPSAnimator animator = new FPSAnimator(canvas, FPS, true);
 
      // Create the top-level container frame
      this.getContentPane().add(canvas);
      this.addWindowListener(new WindowAdapter() {
         @Override
         public void windowClosing(WindowEvent e) {
            // Use a dedicate thread to run the stop() to ensure that the
            // animator stops before program exits.
            new Thread() {
               @Override
               public void run() {
                  if (animator.isStarted()) animator.stop();
                  System.exit(0);
               }
            }.start();
         }
      });
      this.setTitle(TITLE);
      this.pack();
      this.setVisible(true);
      animator.start(); // start the animation loop
   }
 
   /** The entry main() method */
   public static void main(String[] args) {
      // Run the GUI codes in the event-dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            new JOGL2Setup_RendererMain();  // run the constructor
         }
      });
   }
}

In this approach, all the graphics rendering codes are kept in the renderer (Component) class. The main (Container) class can be left untouched.

GLJPanel

To use the light-weight GLJPanel instead of the heavy-weight GLCanvas, simply change all reference of GLCanvas to GLJPanel.

public class JOGL2Setup_GLJPanel extends GLJPanel implements GLEventListener {
   ......
   /** The entry main() method to setup the top-level container and animator */
   public static void main(String[] args) {
      // Run the GUI codes in the event-dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            // Create the OpenGL rendering canvas
            GLJPanel canvas = new JOGL2Setup_GLJPanel();
            ......
         }
      }
   }
   ......
}
GLJPanel canvas = new GLJPanel();  // instead of GLCanvas
Running as an Applet

As we followed the Component-based Architecture in our class design, we can plug the GLCanvas Component into other top-level Container, such as JApplet or Applet easily.

To run the program as an applet, you can simply add an applet launching class (as follow) to launch the main renderer class. Recall that an applet extends javax.swing.JApplet (or java.awt.Frame) and is managed via methods init() (instead of main() of an application), start(), stop() and destroy().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import java.lang.reflect.InvocationTargetException;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import com.jogamp.opengl.util.FPSAnimator;
 
/**
 * JOGL 2.0 Applet Template (with GLCanvas and Swing JApplet)
 * This is the top-level "Container", which allocates and add GLCanvas ("Component")
 * and animator.
 */
@SuppressWarnings("serial")
public class JOGL2Setup_Applet extends JApplet {
   // Define constants for top-level container
   private static final int FPS = 60; // animator's target frames per second
 
   FPSAnimator animator;
 
   @Override
   public void init() {
      // Run the GUI codes in the event-dispatching thread for thread safety
      try {
         SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
               // Create the OpenGL rendering canvas
               GLCanvas canvas = new JOGL2Setup_GLCanvas();
               getContentPane().add(canvas);
 
               // Create a animator that drives canvas' display() at the specified FPS.
               animator = new FPSAnimator(canvas, FPS, true);
               animator.start(); // start the animation loop
            }
         });
      } catch (InvocationTargetException e) {
         e.printStackTrace();
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
 
   @Override
   public void destroy() {
      if (animator.isStarted()) animator.stop();
   }
 
   @Override
   public void start() {
      animator.start();
   }
 
   @Override
   public void stop() {
      if (animator.isStarted()) animator.stop();
   }
}

The main() method in the renderer class is ignored by applet.

Running in Full-Screen Mode

Games are often run in full-screen mode without decoration (title, status, scroll bars). The JOGL 2.0 program template for full screen mode operation is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;
import static javax.media.opengl.GL.*;  // GL constants
import static javax.media.opengl.GL2.*; // GL2 constants
 
/**
 * JOGL 2.0 Program Template (GLCanvas) in Full Screen Mode with ESC to quit
 * This is a "Component" which can be added into a top-level "Container".
 * It also handles the OpenGL events to render graphics.
 */
@SuppressWarnings("serial")
public class JOGL2Setup_GLCanvasFullScreen extends GLCanvas
         implements GLEventListener, KeyListener {
   // Define constants for top-level container
   private static final int FPS = 60; // animator's target frames per second
 
   /** The entry main() method to setup the top-level container and animator */
   public static void main(String[] args) {
      // Run the GUI codes in the event-dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            // Create the OpenGL rendering canvas
            GLCanvas canvas = new JOGL2Setup_GLCanvasFullScreen();
 
            // Create a animator that drives canvas' display() at the specified FPS.
            FPSAnimator animator = new FPSAnimator(canvas, FPS, true);
 
            // Create the top-level container frame
            JFrame frame = new JFrame(); // Swing's JFrame or AWT's Frame
            frame.getContentPane().add(canvas);
            frame.setUndecorated(true);     // no decoration such as title bar
            frame.setExtendedState(Frame.MAXIMIZED_BOTH);  // full screen mode
            frame.setVisible(true);
            animator.start(); // start the animation loop
         }
      });
   }
 
   // Setup OpenGL Graphics Renderer
 
   private GLU glu;  // for the GL Utility
 
   /** Constructor to setup the GUI for this Component */
   public JOGL2Setup_GLCanvasFullScreen() {
      this.addGLEventListener(this);   // for handling GLEvents
      this.addKeyListener(this); // for Handling KeyEvents
      this.setFocusable(true);
      this.requestFocus();
   }
 
   // ------ Implement methods declared in GLEventListener ------
 
   /**
    * Called back immediately after the OpenGL context is initialized. Can be used
    * to perform one-time initialization. Run only once.
    */
   @Override
   public void init(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();      // get the OpenGL graphics context
      glu = new GLU();                         // get GL Utilities
      gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
      gl.glClearDepth(1.0f);      // set clear depth value to farthest
      gl.glEnable(GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL_LEQUAL);  // the type of depth test to do
      gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction
      gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smooths out lighting
 
      // ----- Your OpenGL initialization code here -----
   }
 
   /**
    * Call-back handler for window re-size event. Also called when the drawable is
    * first set to visible.
    */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
 
      if (height == 0) height = 1;   // prevent divide by zero
      float aspect = (float)width / height;
 
      // Set the view port (display area) to cover the entire window
      gl.glViewport(0, 0, width, height);
 
      // Setup perspective projection, with aspect ratio matches viewport
      gl.glMatrixMode(GL_PROJECTION);  // choose projection matrix
      gl.glLoadIdentity();             // reset projection matrix
      glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
 
      // Enable the model-view transform
      gl.glMatrixMode(GL_MODELVIEW);
      gl.glLoadIdentity(); // reset
   }
 
   /**
    * Called back by the animator to perform rendering.
    */
   @Override
   public void display(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
      gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
      gl.glLoadIdentity();  // reset the model-view matrix
 
      // ----- Your OpenGL rendering code here (render a white triangle for testing) -----
      gl.glTranslatef(0.0f, 0.0f, -6.0f); // translate into the screen
      gl.glBegin(GL_TRIANGLES); // draw using triangles
         gl.glVertex3f(0.0f, 1.0f, 0.0f);
         gl.glVertex3f(-1.0f, -1.0f, 0.0f);
         gl.glVertex3f(1.0f, -1.0f, 0.0f);
      gl.glEnd();
   }
 
   /**
    * Called back before the OpenGL context is destroyed. Release resource such as buffers.
    */
   @Override
   public void dispose(GLAutoDrawable drawable) { }
 
   // ------ Implement methods declared in KeyListener ------
 
   @Override
   public void keyTyped(KeyEvent e) {}
 
   @Override
   public void keyPressed(KeyEvent e) {
      int keyCode = e.getKeyCode();
      switch (keyCode) {
         case KeyEvent.VK_ESCAPE: // quit
            // Use a dedicate thread to run the stop() to ensure that the
            // animator stops before program exits.
            new Thread() {
               @Override
               public void run() {
                  GLAnimatorControl animator = getAnimator();
                  if (animator.isStarted()) animator.stop();
                  System.exit(0);
               }
            }.start();
            break;
      }
   }
 
   @Override
   public void keyReleased(KeyEvent e) {}
}

To operate in full-screen mode, set the frame to:

final JFrame frame = new JFrame();
frame.setUndecorated(true);  // no decoration such as title and scroll bars
frame.setExtendedState(Frame.MAXIMIZED_BOTH);  // full screen mode
......

The "window-close" button is no longer available in the full-screen mode. Hence, we use the ESC key to exit the program. The renderer, hence, also implements the KeyListener interface and provide KeyEvent handlers, such as keyPressed() for processing the ESC key..

Note: Read the "Graphics Programming" section on how to switch between full-screen mode and windowed mode, if desired.

Example 1: Rotating 2D Shapes

Modify the GLEventListener handlers init(), display() and reshape() of the template (GLCanvas or GLJPanel) as follows:

public class JOGL2Ex1Rotate2D extends GLJCanvas implements GLEventListener {
   ......
   ......
   private float angle = 0.0f;  // rotation angle of the triangle

   /** Called back by the drawable to render OpenGL graphics */
   @Override
   public void display(GLAutoDrawable drawable) {
      render(drawable);
      update();
   }
   
   // Render a triangle
   private void render(GLAutoDrawable drawable) {
      // Get the OpenGL graphics context
      GL2 gl = drawable.getGL().getGL2();
      // Clear the color and the depth buffers
      gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      // Reset the view (x, y, z axes back to normal)
      gl.glLoadIdentity();   

      // Draw a triangle
      float sin = (float)Math.sin(angle);
      float cos = (float)Math.cos(angle);
      gl.glBegin(GL_TRIANGLES);
         gl.glColor3f(1.0f, 0.0f, 0.0f);   // Red
         gl.glVertex2d(-cos, -cos);
         gl.glColor3f(0.0f, 1.0f, 0.0f);   // Green
         gl.glVertex2d(0.0f, cos);
         gl.glColor3f(0.0f, 0.0f, 1.0f);   // Blue
         gl.glVertex2d(sin, -sin);
      gl.glEnd();
   }
   
   // Update the angle of the triangle after each frame
   private void update() {
      angle += 0.01f;
   }
   
   @Override
   public void init(GLAutoDrawable drawable) { }  // set to empty

   @Override
   public void dispose(GLAutoDrawable drawable) { }  // set to empty

   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }  // set to empty

   ......
   ......
}

TRY:

  1. Using GLJPanel on Swing's JFrame.
  2. Use GLCanvas on AWT's Frame.
  3. Use GLCanvas on AWT's Applet.
  4. Using GLJPanel on Swing's JApplet.

Observe and compare the results.

GLEventListener Handlers

init(): called by the drawable immediately after the OpenGL graphics context is initialized. It can be used to perform one-time initialization tasks such as setting up of lights and display lists. The init() runs only once.

   /** Called after context is initialized, for one-time initialization tasks */
   @Override
   public void init(GLAutoDrawable drawable) {
      // Get the OpenGL graphics context
      GL2 gl = drawable.getGL().getGL2();
      // Get GL Utilities after the GL context created.
      // glu = GLU.createGLU();
      // Enable smooth shading, which blends colors nicely, and smooths out lighting.
      gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
      // Set background color in RGBA. Alpha: 0 (transparent) 1 (opaque) 
      gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      // Setup the depth buffer and enable the depth testing
      gl.glClearDepth(1.0f);         // clear z-buffer to the farthest
      gl.glEnable(GL.GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL.GL_LEQUAL);  // the type of depth test to do
      // Do the best perspective correction
      gl.glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
      
      // ----- Your OpenGL initialization code here -----
      // ......
   }

reshape(): called by the drawable when the drawable is first set to visible, and during the first repaint after the canvas has been resized.

   /** Called for the first-time display and after display's size changes. 
       To set up the view port, projection mode and view volume. */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
      // Get the OpenGL graphics context
      GL2 gl = drawable.getGL().getGL2();
      
      height = (height == 0) ? 1 : height;  // Prevent divide by zero
      float aspect = (float)width / height; // Compute aspect ratio
      
      // Set view port to cover full screen 
      gl.glViewport(0, 0, width, height);
      
      // Set up the projection matrix - choose perspective view
      gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);  
      gl.glLoadIdentity();    // reset
      // Angle of view (fovy) is 45 degrees (in the up y-direction). Based on this
      // canvas's aspect ratio. Clipping z-near is 0.1f and z-near is 100.0f.
      glu.gluPerspective(45.0f, aspect, 0.1f, 100.0f); // fovy, aspect, zNear, zFar
      
      // Switch to the model-view transform
      gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
      gl.glLoadIdentity();    // reset
   }

display(): called by the drawable to perform OpenGL graphics rendering.

   /** Called to render graphics */
   @Override
   public void display(GLAutoDrawable drawable) {
      // Get the OpenGL graphics context
      GL2 gl = drawable.getGL().getGL2();
      // Clear the color and the depth buffers
      gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
      // Reset the view (x, y, z axes back to normal)
      gl.glLoadIdentity();
      
      // ----- Your OpenGL rendering code here -----
      // ......
   }

disposed(): called by the drawbale before the OpenGL context is destroyed. It can be used to release all OpenGL resources, such as memory buffers.

@Override
public void disposed(GLAutoDrawable drawable) { 
   // Hardly used.
}

Example 2: Rotating 3D Shapes

The following example show a color-pyramid and color-cube (Nehe Lesson #5).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;
import static javax.media.opengl.GL.*;  // GL constants
import static javax.media.opengl.GL2.*; // GL2 constants
 
/**
 * JOGL 2.0 Example 2: Rotating 3D Shapes (GLCanvas)
 */
@SuppressWarnings("serial")
public class JOGL2Ex2Rotate3D_GLCanvas extends GLCanvas implements GLEventListener {
   // Define constants for the top-level container
   private static String TITLE = "Rotating 3D Shapes (GLCanvas)";  // window's title
   private static final int CANVAS_WIDTH = 320;  // width of the drawable
   private static final int CANVAS_HEIGHT = 240; // height of the drawable
   private static final int FPS = 60; // animator's target frames per second
 
   /** The entry main() method to setup the top-level container and animator */
   public static void main(String[] args) {
      // Run the GUI codes in the event-dispatching thread for thread safety
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            // Create the OpenGL rendering canvas
            GLCanvas canvas = new JOGL2Ex2Rotate3D_GLCanvas();
            canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
 
            // Create a animator that drives canvas' display() at the specified FPS.
            final FPSAnimator animator = new FPSAnimator(canvas, FPS, true);
 
            // Create the top-level container
            final JFrame frame = new JFrame(); // Swing's JFrame or AWT's Frame
            frame.getContentPane().add(canvas);
            frame.addWindowListener(new WindowAdapter() {
               @Override
               public void windowClosing(WindowEvent e) {
                  // Use a dedicate thread to run the stop() to ensure that the
                  // animator stops before program exits.
                  new Thread() {
                     @Override
                     public void run() {
                        if (animator.isStarted()) animator.stop();
                        System.exit(0);
                     }
                  }.start();
               }
            });
            frame.setTitle(TITLE);
            frame.pack();
            frame.setVisible(true);
            animator.start(); // start the animation loop
         }
      });
   }
 
   // Setup OpenGL Graphics Renderer
 
   private GLU glu;  // for the GL Utility
   private float anglePyramid = 0;    // rotational angle in degree for pyramid
   private float angleCube = 0;       // rotational angle in degree for cube
   private float speedPyramid = 2.0f; // rotational speed for pyramid
   private float speedCube = -1.5f;   // rotational speed for cube
 
   /** Constructor to setup the GUI for this Component */
   public JOGL2Ex2Rotate3D_GLCanvas() {
      this.addGLEventListener(this);
   }
 
   // ------ Implement methods declared in GLEventListener ------
 
   /**
    * Called back immediately after the OpenGL context is initialized. Can be used
    * to perform one-time initialization. Run only once.
    */
   @Override
   public void init(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();      // get the OpenGL graphics context
      glu = new GLU();                         // get GL Utilities
      gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
      gl.glClearDepth(1.0f);      // set clear depth value to farthest
      gl.glEnable(GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL_LEQUAL);  // the type of depth test to do
      gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction
      gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smooths out lighting
   }
 
   /**
    * Call-back handler for window re-size event. Also called when the drawable is
    * first set to visible.
    */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
 
      if (height == 0) height = 1;   // prevent divide by zero
      float aspect = (float)width / height;
 
      // Set the view port (display area) to cover the entire window
      gl.glViewport(0, 0, width, height);
 
      // Setup perspective projection, with aspect ratio matches viewport
      gl.glMatrixMode(GL_PROJECTION);  // choose projection matrix
      gl.glLoadIdentity();             // reset projection matrix
      glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
 
      // Enable the model-view transform
      gl.glMatrixMode(GL_MODELVIEW);
      gl.glLoadIdentity(); // reset
   }
 
   /**
    * Called back by the animator to perform rendering.
    */
   @Override
   public void display(GLAutoDrawable drawable) {
      GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
      gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
 
      // ----- Render the Pyramid -----
      gl.glLoadIdentity();                 // reset the model-view matrix
      gl.glTranslatef(-1.6f, 0.0f, -6.0f); // translate left and into the screen
      gl.glRotatef(anglePyramid, -0.2f, 1.0f, 0.0f); // rotate about the y-axis
 
      gl.glBegin(GL_TRIANGLES); // of the pyramid
 
      // Font-face triangle
      gl.glColor3f(1.0f, 0.0f, 0.0f); // Red
      gl.glVertex3f(0.0f, 1.0f, 0.0f);
      gl.glColor3f(0.0f, 1.0f, 0.0f); // Green
      gl.glVertex3f(-1.0f, -1.0f, 1.0f);
      gl.glColor3f(0.0f, 0.0f, 1.0f); // Blue
      gl.glVertex3f(1.0f, -1.0f, 1.0f);
 
      // Right-face triangle
      gl.glColor3f(1.0f, 0.0f, 0.0f); // Red
      gl.glVertex3f(0.0f, 1.0f, 0.0f);
      gl.glColor3f(0.0f, 0.0f, 1.0f); // Blue
      gl.glVertex3f(1.0f, -1.0f, 1.0f);
      gl.glColor3f(0.0f, 1.0f, 0.0f); // Green
      gl.glVertex3f(1.0f, -1.0f, -1.0f);
 
      // Back-face triangle
      gl.glColor3f(1.0f, 0.0f, 0.0f); // Red
      gl.glVertex3f(0.0f, 1.0f, 0.0f);
      gl.glColor3f(0.0f, 1.0f, 0.0f); // Green
      gl.glVertex3f(1.0f, -1.0f, -1.0f);
      gl.glColor3f(0.0f, 0.0f, 1.0f); // Blue
      gl.glVertex3f(-1.0f, -1.0f, -1.0f);
 
      // Left-face triangle
      gl.glColor3f(1.0f, 0.0f, 0.0f); // Red
      gl.glVertex3f(0.0f, 1.0f, 0.0f);
      gl.glColor3f(0.0f, 0.0f, 1.0f); // Blue
      gl.glVertex3f(-1.0f, -1.0f, -1.0f);
      gl.glColor3f(0.0f, 1.0f, 0.0f); // Green
      gl.glVertex3f(-1.0f, -1.0f, 1.0f);
 
      gl.glEnd(); // of the pyramid
 
      // ----- Render the Color Cube -----
      gl.glLoadIdentity();                // reset the current model-view matrix
      gl.glTranslatef(1.6f, 0.0f, -7.0f); // translate right and into the screen
      gl.glRotatef(angleCube, 1.0f, 1.0f, 1.0f); // rotate about the x, y and z-axes
 
      gl.glBegin(GL_QUADS); // of the color cube
 
      // Top-face
      gl.glColor3f(0.0f, 1.0f, 0.0f); // green
      gl.glVertex3f(1.0f, 1.0f, -1.0f);
      gl.glVertex3f(-1.0f, 1.0f, -1.0f);
      gl.glVertex3f(-1.0f, 1.0f, 1.0f);
      gl.glVertex3f(1.0f, 1.0f, 1.0f);
 
      // Bottom-face
      gl.glColor3f(1.0f, 0.5f, 0.0f); // orange
      gl.glVertex3f(1.0f, -1.0f, 1.0f);
      gl.glVertex3f(-1.0f, -1.0f, 1.0f);
      gl.glVertex3f(-1.0f, -1.0f, -1.0f);
      gl.glVertex3f(1.0f, -1.0f, -1.0f);
 
      // Front-face
      gl.glColor3f(1.0f, 0.0f, 0.0f); // red
      gl.glVertex3f(1.0f, 1.0f, 1.0f);
      gl.glVertex3f(-1.0f, 1.0f, 1.0f);
      gl.glVertex3f(-1.0f, -1.0f, 1.0f);
      gl.glVertex3f(1.0f, -1.0f, 1.0f);
 
      // Back-face
      gl.glColor3f(1.0f, 1.0f, 0.0f); // yellow
      gl.glVertex3f(1.0f, -1.0f, -1.0f);
      gl.glVertex3f(-1.0f, -1.0f, -1.0f);
      gl.glVertex3f(-1.0f, 1.0f, -1.0f);
      gl.glVertex3f(1.0f, 1.0f, -1.0f);
 
      // Left-face
      gl.glColor3f(0.0f, 0.0f, 1.0f); // blue
      gl.glVertex3f(-1.0f, 1.0f, 1.0f);
      gl.glVertex3f(-1.0f, 1.0f, -1.0f);
      gl.glVertex3f(-1.0f, -1.0f, -1.0f);
      gl.glVertex3f(-1.0f, -1.0f, 1.0f);
 
      // Right-face
      gl.glColor3f(1.0f, 0.0f, 1.0f); // magenta
      gl.glVertex3f(1.0f, 1.0f, -1.0f);
      gl.glVertex3f(1.0f, 1.0f, 1.0f);
      gl.glVertex3f(1.0f, -1.0f, 1.0f);
      gl.glVertex3f(1.0f, -1.0f, -1.0f);
 
      gl.glEnd(); // of the color cube
 
      // Update the rotational angle after each refresh.
      anglePyramid += speedPyramid;
      angleCube += speedCube;
   }
 
   /**
    * Called back before the OpenGL context is destroyed. Release resource such as buffers.
    */
   @Override
   public void dispose(GLAutoDrawable drawable) { }
}

TRY:

  1. Modify the program to run as an applet.
  2. Modify the program to run in full screen mode.

JOGL's NEWT (Native Windowing Toolkit) - GLWindow

Besides relying on AWT/Swing, JOGL provides its own windowing toolkit called NEWT. The base class for NEWT is GLWindow. To allocate a GLWindow:

// Get the default OpenGL profile, reflecting the best for your running platform
GLProfile glp = GLProfile.getDefault();
// Specifies a set of OpenGL capabilities, based on your profile.
GLCapabilities caps = new GLCapabilities(glp);
// Allocate a GLWindow and add GLEventListener
GLWindow window = GLWindow.create(caps);
window.addGLEventListener(......);

Let's rewrite Example 1 using JOGL's NEWT.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import com.jogamp.newt.event.WindowAdapter;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.FPSAnimator;
 
public class JOGL2NewtDemo implements GLEventListener {  // Renderer
   private static String TITLE = "JOGL 2 with NEWT";  // window's title
   private static final int WINDOW_WIDTH = 640;  // width of the drawable
   private static final int WINDOW_HEIGHT = 480; // height of the drawable
   private static final int FPS = 60; // animator's target frames per second
 
   private double theta = 0.0f;  // rotational angle
 
   /** Constructor */
   public JOGL2NewtDemo() {}
 
   /** The entry main() method */
   public static void main(String[] args) {
       // Get the default OpenGL profile, reflecting the best for your running platform
       GLProfile glp = GLProfile.getDefault();
       // Specifies a set of OpenGL capabilities, based on your profile.
       GLCapabilities caps = new GLCapabilities(glp);
       // Create the OpenGL rendering canvas
       GLWindow window = GLWindow.create(caps);
 
       // Create a animator that drives canvas' display() at the specified FPS.
       final FPSAnimator animator = new FPSAnimator(window, FPS, true);
 
       window.addWindowListener(new WindowAdapter() {
          @Override
          public void windowDestroyNotify(WindowEvent arg0) {
             // Use a dedicate thread to run the stop() to ensure that the
             // animator stops before program exits.
             new Thread() {
                @Override
                public void run() {
                   animator.stop(); // stop the animator loop
                   System.exit(0);
                }
             }.start();
          };
       });
 
       window.addGLEventListener(new JOGL2NewtDemo());
       window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
       window.setTitle(TITLE);
       window.setVisible(true);
       animator.start();
   }
 
   /** Called back by the drawable to render OpenGL graphics */
   @Override
   public void display(GLAutoDrawable drawable) {
       render(drawable);
       update();
   }
 
   /** Render the shape (triangle) */
   private void render(GLAutoDrawable drawable) {
       GL2 gl = drawable.getGL().getGL2();
 
       gl.glClear(GL.GL_COLOR_BUFFER_BIT);
 
       // Draw a triangle
       float sine = (float)Math.sin(theta);
       float cosine = (float)Math.cos(theta);
       gl.glBegin(GL.GL_TRIANGLES);
       gl.glColor3f(1, 0, 0);
       gl.glVertex2d(-cosine, -cosine);
       gl.glColor3f(0, 1, 0);
       gl.glVertex2d(0, cosine);
       gl.glColor3f(0, 0, 1);
       gl.glVertex2d(sine, -sine);
       gl.glEnd();
   }
 
   /** Update the rotation angle after each frame refresh */
   private void update() {
       theta += 0.01;
   }
 
   /** Called back immediately after the OpenGL context is initialized */
   @Override
   public void init(GLAutoDrawable drawable) { }
 
   /** Called back before the OpenGL context is destroyed. */
   @Override
   public void dispose(GLAutoDrawable drawable) { }
 
   /** Called back by the drawable when it is first set to visible,
   and during the first repaint after the it has been resized. */
   @Override
   public void reshape(GLAutoDrawable drawable, int x, int y, int weight, int height) { }
}

OpenGL Profile and Capabilities

[TODO] Revised

You can create drawables to support different OpenGL profiles and capabilities:

  1. First, construct a GLProfile, which specifies the target profile of your application, e.g., OpenGL2, OpenGL3, OpenGL4, OpenGL|ES1, OpenGL|ES2, etc. The profiles are beyond the scope of this tutorial. Read "JOGL 2 - OpenGL Profiles explained" if you are interested. We shall use the following statement to get the default profile, which best reflects your running platform.
    // Get the default OpenGL profile, reflecting the best for your running platform
    GLProfile glp = GLProfile.getDefault();
  2. Next, construct a GLCapabilities, based on your chosen profile, which maintain a set of OpenGL capabilities.
    // Specifies a set of OpenGL capabilities, based on your profile.
    GLCapabilities caps = new GLCapabilities(glp);
  3. Now, you can construct your desired drawing canvas: GLCanvas for AWT, GLJPanel for Swing, and GLWindow for Newt. All these canvas are GLDrawable.
    // Allocate a GLDrawable, based on your OpenGL capabilities.
    // Using GLCanvas for AWT 
    GLCanvas canvas = new GLCanvas(caps);
    this.add(canvas);            // "this" is a subclass of AWT's Frame
       
    // Using GLJPanel for Swing
    GLJPanel panel = new GLJPanel(caps);
    this.setContentPane(panel);  // "this" is a subclass of Swing's JFrame
       
    // Using GLWindow for Newt
    GLWindow window = GLWindow.create(caps);
    window.setVisible(true);

Nehe's JOGL 2 Port

I have ported some of the Nehe's lessons into JOGL. Refer to Nehe for the problem descriptions.

Setting Up

Download Source Codes: "JOGL2Nehe01to08.zip".

  • Nehe's Lesson #1 "Setting Up":
    - Using GLCanvas: JOGL2Setup_GLCanvas.java.
    - Using GLJPanel: JOGL2Setup_GLJPanel.java.
    - Run as an applet with GLCanvas: JOGL2Setup_Applet.java.
    - Run in full-screen mode: JOGL2Setup_GLCanvasFullScreen.

Expected output: blank black screen.

OpenGL Basics

I consider Lessons #2 - #8 as OpenGL basic lessons, that are extremely important! I use GLCanvas on Swing's JFrame for all these exercises, as GLJPanel does not seem to run properly.

Download Source Codes: "JOGL2Nehe01To08.zip".

  • Nehe's Lesson #2 "Basic Shape": JOGL2Nehe02Basic2D.java.
  • Nehe's Lesson #3 "Color": J2Nehe03Color.java.
  • Nehe's Lesson #4 "Rotation": JOGL2Nehe04Rotation.java.
  • Nehe's Lesson #5 "3D Shape": JOGL2Nehe05Shape3D.java.
  • Nehe's Lesson #6 "Texture": JOGL2Nehe06Texture.java.
  • Nehe's Lesson #7 "Texture Filter, Lighting, and key-controlled": JOGL2Nehe07TextureFilterLightKey.java.
  • Nehe's Lesson #8 "Blending": JOGL2Nehe08Blending.java and JOGL2Nehe08Blending_FullScreen.java
      Click image to run the applet (not working - JNLP not supported under my server?!)
OpenGL Intermediates

Download Source Code: "JOGL2NeheIntermediate.zip".

  • Nehe's Lesson #9 "Moving Bitmaps in 3D space": J2NeheEx09Stars.java.
  • Nehe's Lesson #10: Building and moving in a 3D world J2NeheEx10World3D.java.
  • Nehe's Lesson #11 "Waving Effect": J2NeheEx11Flag.java.
  • Nehe's Lesson #12 "Using Display List": J2NeheEx12DisplayList.java.
  • Nehe's Lesson #13 "2D Texts": J2NeheEx13Text2D.java.
  • Nehe's Lesson #14 "3D Texts": J2NeheEx14Text3D.java.
  • Nehe's Lesson #16 "Fog Effect": J2NeheEx16Fog.java.
  • Nehe's Lesson #18 "Quadrics": J2NeheEx18Quadrics.java.
  • Nehe's Lesson #19 "Particle Engine": (a) J2NeheEx19Particle.java, (b) J2NeheEx19Firework.java.
      
  • Nehe's Lesson #26 "Reflection": J2NeheEx26Reflection.java.

More JOGL Ports/Examples

JOGL comes with many excellent demos. You can get the latest demo source codes from https://github.com/sgothel/ ⇒ "jogl demos" ⇒ "src" ⇒ "demos". However, many of these demos are not properly explained (?).

Gears/JGears

You can get the source codes "Gears.java" from JOGL demos; or get the latest code from https://github.com/sgothel/ ⇒ "jogl demos" ⇒ "src" ⇒ "demos" ⇒ "gears" ⇒ "Gears.java".

"Gears.java" run the JOGL's NEWT window. [TODO] more.

There is a variation called "JGears.java", which included 2 images and texts in the display.

[TODO] Photo-Cube

[TODO] Bouncing Balls inside a Cube

[TODO] JOGL Demos

[TODO] Redbook Examples

Deploying JOGL Applications, Applets and Web Start Apps

Read "Java's Rich Internet Applications (RIA) - Applets & Web Start Apps".

Deploying JOGL 2.0 WebStart App

Jarring Up JOGL App

Jar-up your JOGL application's classes and relevant resources.

Preparing JNLP file
<?xml version="1.0" encoding="UTF-8"?>
<jnlp>
   <information>
      <title>My JOGL App</title>
      <vendor>My Company</vendor>
      <offline-allowed />
   </information>
   <resources>
      <j2se version="1.4+" href="http://java.sun.com/products/autodl/j2se" />
      <jar href="my_app.jar" main="true" />
      <extension name="jogl-all-awt" href="http://jogamp.org/deployment/v2.0-rc8/jogl-all-awt.jnlp" />
   </resources>
   <application-desc main-class="mydemo.myAppMain" />
</jnlp>

The above <extension> is meant for JOGL application. The necessary JOGL jar-files are provide by the jogamp server. Choose the appropriate JOGL version that you used to compile your application, e.g., "v2.0-rc8".

Deploying JOGL 2.0 Applet

Reference:

  1. Java Network Launch Protocol (JNLP) Support @ http://www.oracle.com/technetwork/java/javase/jnlp-136707.html
  2. JNLPAppletLauncher @ http://jogamp.org/applet-launcher/www/.
  3. Test page for JOGL Applets @ http://jogamp.org/deployment/jogamp-current/jogl-test-applets.html.
  4. JOGL: Applets @ http://jausoft.com/blog/2009/07/03/jogl-applets-part-1.
JNLP Applet (JDK 6u10)

Starting from JDK 6u10 Standard "JNLP Applets" are supported, in which the Java Plug-In (for web browser) provides support for launching applets directly from JNLP files. To launch an applet from a JNLP file, use the "jnlp_href" parameter in the <applet> tag.

For example, the following HTML file contains an applet which references JNLP file "my_applet.jnlp".

<applet width="300" height="300" >
<param name="jnlp_href" value="my_applet.jnlp">
</applet>

The JNLP file "my_applet.jnlp" is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp href="my_applet.jnlp">
   <information>
      <title>My Applet</title>
      <vendor>My Company</vendor>
      <offline-allowed />
   </information>
   <resources>
      <j2se version="1.6+" />
      <jar href="my_applet.jar" main="true" />
      <extension name="jogl-all-awt" href="http://jogamp.org/deployment/v2.0-rc8/jogl-all-awt.jnlp" />
   </resources>
   <applet-desc
      name="My Applet"
      main-class="mydemo.MyApplet"
      width="300"
      height="300">
   </applet-desc>
</jnlp>

The above extension is meant for JOGL app, where you should choose the appropriate JOGL version that you used to compile your application, e.g., "v2.0-rc8".

Applet Launcher (JDK 6u10)

JDK 1.6u10 has greatly improved the efficiency of Java applet, and it is now feasible and practical to deploy a huge Java program as an applet, via a so-called "Applet Launcher".

JNLP applet is support since JDK 6u14. However, you may need to fall back to the Applet-Launcher to support the older releases.

Deploying JOGL Applet as "Standard JNLP Applet" / "JNLPAppletLauncher"

We shall deploy JOGL applet as "JNLP Applet" if the web browser JRE Plug-in supports it (above JDK 6u14); otherwise, we fall back to JNLPAppletLauncher, as follow:

Prepare a JNLP file as follow:

<?xml version="1.0" encoding="utf-8"?>
<jnlp>
  <information>
    <title>JOGL Test</title>
    <vendor>SomeOne</vendor>
  </information>
  <resources>
      <j2se version="1.6+"/>
      <property name="sun.java2d.noddraw" value="true"/>
      <jar href="YourJoglApp.jar" main="true"/>
      <extension name="jogl-all-awt" href="http://jogamp.org/deployment/v2.0-rc8/jogl-all-awt.jnlp" />
  </resources>
 
  <applet-desc
      name="Test Applet"
      main-class="YourAppletClass"
      width="640"
      height="480">
  </applet-desc>
</jnlp>

In the <extension>'s href, set to the appropriate JOGL version that you used to compile your program, e.g., "v2.0-rc8" in my case.

Use the following <applet> tag with a reference to the JNLP file in parameter "jnlp_href":

<applet code="org.jdesktop.applet.util.JNLPAppletLauncher"
      width=600
      height=400
      archive=
      "http://jogamp.org/deployment/v2.0-rc8/jar/applet-launcher.jar,
       http://jogamp.org/deployment/v2.0-rc8/jar/nativewindow.all.jar,
       http://jogamp.org/deployment/v2.0-rc8/jar/gluegen-rt.jar,
       http://jogamp.org/deployment/v2.0-rc8/jar/jogl.all.jar,
       YourJOGLApp.jar">
   <param name="codebase_lookup" value="false">
   <param name="subapplet.classname" value="YourAppletClass">
   <param name="subapplet.displayname" value="JOGL Applet Test">
   <param name="noddraw.check" value="true">
   <param name="progressbar" value="true">
   <param name="jnlpNumExtensions" value="1">
   <param name="jnlpExtension1" value="http://jogamp.org/deployment/v2.0-rc8/jogl-core.jnlp">
   <param name="java_arguments" value="-Dsun.java2d.noddraw=true">
   <param name="jnlp_href" value="YourJnlpFile.jnlp">
</applet>

where:

  • Attribute "code" is pointing at "JNLPAppletLauncher", which is downloaded from http://jogamp.org/deployment/v2.0-rc8/jar/applet-launcher.jar, instead of your local server.
  • Attribute "archive" includes all the jar files for running JOGL program. Again, they are downloaded from the server. "archive" also include your JOGL JAR file.
  • Attributes "width" and "height" specify the width and height of your applet's display area inside the browser's window.
  • The name of your main applet is specified in the parameter "subapplet.classname".
  • The parameter "codebase_lookup" is set to false, as this applet does not need to fetch other files from your local server's code base path.
  • The parameter "noddraw.check" is set to true, to check if DirectDraw is enabled and, if so, will prompt the user to disable it for all applets. DirectDraw is incompatible with OpenGL. Disabling it is unlikely to slow down other non-3D applets significantly.

REFERENCES & RESOURCES

  1. JOGL mother site @ http://jogamp.org/jogl/www/; JOGL tutorial @ http://jogamp.org/wiki/index.php/Jogl_Tutorial; JOGL Wiki @ http://jogamp.org/wiki/index.php/Main_Page.
  2. JOGL Developer and master repository (including source and demos) @ https://github.com/sgothel/.
  3. OpenGL mother site @ www.opengl.org.
  4. Nehe OpenGL tutorials @ http://nehe.gamedev.net.
  5. OpenGL Programming Guide (Red book); OpenGL Superbible (Blue book)