JOGLness
Wednesday, January 4th, 2006Let me repeat some of my general annoyance about JoGL, a lack of backward compatibility, a lack of new tutorials helping people to get a leeway into the actual OpenGL tutorials, or the ability to get on with their life as OpenGL programmers. I have been using tutorials from NeHe to gain an understanding of OpenGL, and I’d be the first to admit they are amazing tutorials, They however use Visual C++ which has different access routines for a lot of non OGL functionality, thus making some sort of a lead in necessary. While NeHe links to JOGL code from Kevin J. Dulling, a lot of it is not very well commented, and most of it is based upon old JoGL releases and hence no longer runs. The JoGL site itself links to another set of NeHe tutorial ports by Eeckhoudt, and to a fairly cool introduction by Gregory Pierce. Both of these however are based upon things before the JSR-231 release, and for reasons best known to the JSR standard and the JOGL team, this release broke backwards compatibility with previous beta releases of JOGL. If or when this happens next is something I am not aware of, and shall hence rely on an implicit belief that I am dealing with the JSR-231 release. There’s a fair chance I’ll be using JOGL for the next few months, so if anything changes while I am still a user, I’ll try and post a note. Also the writing of this entry was to an extent inspired by my roommate, who hasn’t yet gone through JOGL and is currently off to London and the Alps.
Contrary to what I indicated earlier, there is finally a hardware accelerated JoGL swing component, GLJPanel, however this utilizes a frame buffer and draws things using JAVA 2D, and is hence slower than the more heavyweight GLCanvas AWT component.
The JOGL javadocs are strangely hard to find, throwing up all sorts of weird results when looked for. They however can explain more of JOGL than anyone ever can, get it straight up.
A note on classes, this is sort of important:
- javax.media.opengl.GL The big GL class, contains all functions beginning with the phrase “gl” on NeHe’s tutorials, and in general every place else. Essentially provides access to the actual OpenGL library, is usually acquired by calling getGL on a javax.media.opengl.GLDrawable object.
- javax.media.opengl.glu.GLU The big GLU class, gives access to glu functions and hence to all functions prefixed “glu”. Initially at least will be used for gluPerspective and perhaps for MIP maps. Contrary to popular belief and coding convention is not acquired by calling a getGLU function. If someone needs a GLU they should just create a new object by calling on new GLU it sort of takes care of its bindings. Is however thread specific, so be careful with that.
- com.sun.opengl.utils.Animator Somewhere down the line you’ll come across this tutorial asking you to make rotating bodies, even though you’ll follow through on making the tutorial perfectly your objects will not be rotating. This causes some heartburn, you move your window around, notice that the object rotates at each redraw. Tada, JOGL does not put Windows on a refresh loop, there’s little reason to do so. Depending on your perspective this can be a very good or a very bad thing. Good because this lack of redrawing implies a lower CPU and GPU usage, bad because it makes animation gaming and such things a bitch, which is why we have the Animator class. The Animator does little except refreshing the canvas with an amazing frequency, it is supposed to take some time off at the end of each redraw, to prevent the CPU from getting swamped. Created by calling on new Animator(GLAutoDrawable drawable), it helps to remember that javax.media.opengl.GLCanvas implements javax.media.opengl.GLAutoDrawable. The Animator is started with a call to its start() method and stopped with a call to its stop() method.
There are other classes around, GLCanvas for instance, they are mostly utilitarian, use the javadocs to figure out more about thes. Also do note that the java.awt.Frame method show() is deprecated as of Java 5.0. Use the setVisible(boolean visibility) method instead, it works well.
This so far, combined with little peeks at some of the links should allow you to start on the NeHe tutorials, someday, perhaps soon, when I finally go back and edit my old code, I’ll put a tutorial on starting up and how that works, though I am currently sticking to moving ahead in my terms. There’s very little that changes until we get to dealing with textures, which is when NeHe ends up calling on a bunch of Win32 API functions to get his stuff going, and this is clearly not what one would want to do in Java.
The above statements commented because I went through and commented most of my init code, and am going to slowly paste parts of it. I can do this in small bits or in one gigantic blob, let us see how this works
I am working on two classes, one named Test which sort of sets up the frame and stuff, and another named TestRenderer, something that implements GLEventListner and hence does most of the rendering. Most sane people would nest the renderer within the main class, I find this mildly anoying, since keeping them separate allows me to throw all of the renderer away and swap in a new file, without having to edit any of the unecessary gunk, my choice. I am going to start by showing off the Test class, annotating wherever I feel appropriate (may or may not have anything to do with appropriateness).
package joglTut;
import javax.media.opengl.*;
import com.sun.opengl.utils.Animator;
import java.awt.*;
import java.awt.event.*;
The package statement is a sign of Eclipse at work, the class specific import for Animator is something I picked up from being at Brown, one of my CS professors professed the usefulness of these, and every now and then I actually follow some of his coding concession, helps with some of the people I know :D.
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
Frame frame = new Frame("Lesson n");
GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities()); //figure out display capabilities and create
//a canvas to draw on
Animator anim = new Animator(canvas); //attach an animator to the canvas
canvas.addGLEventListener(new TestRenderer()); //Most people like to use an inner class, I like using separate classes
frame.add(canvas); //add canvas
frame.setSize(640, 480); //set size
frame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}); //Exit if windows closed
anim.start(); //start animator
frame.setVisible(true); //make things visible
canvas.requestFocus();
}
}
Seems fairly standard, sorry about the spacing issues, I am not sure how to solve those with TextPattern, besides I’ll be posting the files, hopefully.
That is pretty much the Test class, now comes the renderer. Since I am lazy and yet wish to spare you most of the ugly details of texture mapping, I am going to put my TestRenderer class from making 3D objects and rotating them, this works, trust me…
package joglTut;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import javax.media.opengl.GLEventListener;
public class TestRenderer implements GLEventListener {
// private GL gl;
//private GLAutoDrawable glDrawable;
float rtri = 0; //Triangle rotation
float rtquad = 0; //Quad rotation
Again standard intialization, notice a lack of a class wide field for gl, there’s good reason for this, it is significantly safer to call getGL() from inside every function than to do this on a global level. The floats are variables for rotation…
package joglTut;
public void init(GLAutoDrawable drawable)
{
// this.gl = drawable.getGL();
//this.glDrawable = drawable;
final GL gl = drawable.getGL(); //Repeated sort of everywhere, need something to draw on
//drawable.setGL( new DebugGL(drawable.getGL() )
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //set black for the background
gl.glShadeModel(GL.GL_SMOOTH); //Shade model, GL.GL_FLAT does interesting things when unknown
gl.glClearDepth(1.0f); //Set up depth buffer
gl.glEnable(GL.GL_DEPTH_TEST); //enable depth testing
gl.glDepthFunc(GL.GL_LEQUAL); //Type of depth function
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); //Adds tiny amount of computation, makes things look nicer
//System.out.println("Init GL is " + gl.getClass().getName());
}
The init function is called by the canvas initially during intialization, it’s a good place to set up things of a more permanent nature. This is an exact replica of what’s done on the NeHe tutorial, so I won’t admit to knowing too much about the choices. Uncommenting the println statement produces some interesting output, tells you more about the innards as they stand.
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
{
//final GL gl = drawable.getGL();
//final GLU glu = drawable.getGLU();
GLU glu = new GLU(); //Need a GLU
final GL gl = GLU.getCurrentGL(); //GLUs come with a gl, am pretty sure this is the one returned by drawable, however
//prefer to be safe
drawable.setGL(gl); //setGL allows us to set a new gl object, be safe than sorry
if(height <= 0)
{
height =1;
}
final float h = (float)width/(float)height;
gl.glViewport(0, 0, width, height); //Set viewport area
gl.glMatrixMode(GL.GL_PROJECTION); //Set matrix mode
gl.glLoadIdentity(); //reset everything
glu.gluPerspective(45.0f, h, 1.0, 20.0); //set perspective
gl.glMatrixMode(GL.GL_MODELVIEW); //set matrix mode
gl.glLoadIdentity(); //reset things
}
reshape is called whenever the size of the canvas changes, mostly when windows are resized and the like, called less frequently than draw is useful for doing things which will change with resizing.
public void display(GLAutoDrawable drawable)
{
final GL gl = drawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT ); //Clear the bugger
gl.glLoadIdentity(); //Reset model view matrix
gl.glTranslatef(-1.5f,0.0f,-6.0f); //Translate
gl.glRotatef(rtri, 0.0f,1.0f,0.0f); //Rotate
//gl.glColor3f(1.0f, 0.0f, 0.0f );
gl.glBegin( GL.GL_TRIANGLES );
//.....
gl.glEnd();
gl.glFlush(); //Flush buffer
//....
}
I skipped on most of the gl.glVertex3f statements in there, but they pretty much work as they should. The only real change in this function, from standard C functionality is a call to gl.glFlush() , which as far as I can determine is fairly important, since leaving it out did strange things for my figures yesterday.
OK for reasons of sanity, I am going to stop here, I am going to put texture mapping up later, this involves things like being at home, being about to leave, and other such things. So OK, I’ll write this up soon, once I am in lovely Providence.
Ze Panda
PS: I am aware most of this is slowly filtering through, and I really haven’t written much so far, well I am being slow, I am lazy