Jumping into JOGL handout - Danny Fowler Photography

erectboboSoftware and s/w Development

Dec 14, 2013 (3 years and 6 months ago)

110 views

Jumping into JOGL
http://today.java.net/pub/a/today/2003/09/11/jogl2d.html


JOGL Components and GLEventListener

The first thing we need to do to get JOGL into an AWT app is to
create a
GLCanvas

component:


GLCapabilities capabilities = new GLCapabilities(
);

GLCanvas canvas =


GLDrawableFactory.getFactory().createGLCanvas(capabilities);


The obvious difference between this code and the typical means of
creating AWT components with constructors is that we have to ask this
GLCapabilities object for a compo
nent that is tuned to the
characteristics of the current display, such as its color depth.


It might seem odd that the sample uses AWT instead of the more
-
popular Swing API. While the GLCanvas has a Swing equivalent,
GLJPanel, the Swing version does not cu
rrently enjoy hardware
-
accelerated rendering. That means it's slow, which for many would
defeat the whole purpose of using JOGL.


The next thing we have to do with our canvas is to register it as a
GLEventListener
:

canvas.addGLEventListener(this);


The GLE
ventListener is primarily used to call back to our component
when it needs to repaint itself or deal with various changes. The
interface defines four methods:




init (GLDrawable drawable)
: called when OpenGL is initialized,
and thus useful for any one
-
time
-
only setup work.




display (GLDrawable drawable)
: a request for the component to
draw itself.



reshape (GLDrawable drawable, int i, int x, int width, int
height)
: signals that the component's location or size has been
changed.



displ
ayChanged (GLDrawable drawable, boolean modeChanged,
boolean deviceChanged)
: used to signal that the display mode or
device has changed, such as when the user changes the color depth
from 16
-
bit to 32
-
bit color (i.e., from "thousands" to "millions" of
colo
rs).



In the sample application, init() is used to set the colors for
erasing and drawing to white and black, respectively, and to set the
size in pixels for drawing points:


GL gl = drawable.getGL();

gl.glClearColor( 1.0f, 1.0f, 1.0f, 1.0f );

gl.glColor
3f( 0.0f, 0.0f, 0.0f );

gl.glPointSize(4.0f);


Notice that the GL object is retrieved from the GLDrawable that is
passed into the init() method. All of the GLEventListener methods
provide this object, and the JOGL User's Guide encourages developers
to alw
ays get a fresh reference to the GL object from the GLDrawable
on each callback, rather than caching it in a field and reusing it.
The rationale for this has to do with threading issues within the AWT
and the dangers of making OpenGL calls from the wrong t
hread.


But what is this GL class? The docs define it as "the basic interface
to OpenGL." As mentioned above, its design is almost like a
straightforward dump of the gl.h header file. In a sense, it's better
not to think of it as an object as all, but rath
er as handle for
making method calls. If you're porting from native OpenGL code, then
you would expect functions that start with a gl or constants that
start with a GL to be accessed via this GL instance. In fact, the
code in the init method is a very stra
ightforward port of the
following C code from Hill:


glClearColor (1.0, 1.0, 1.0, 0.0);

glColor3f (0.0f, 0.0f, 0.0f);

glPointSize (4.0
f);


So all we've had to do to port to JOGL is to tack gl. on each of
these calls. When a call to glu.h is necessary, we'l
l get the GLU
object from the GLDrawable and make glu.
-
type calls.


The reshape() method sets or re
-
sets its size and "viewport," which
represents what part of the component is being drawn into; in this
simple case, this is always the entire component. We
also have to
make some calls to indicate that we're working in a simplistic 2D
environment with no rotations, translations, or other matrix
-
based
transformations.


GL gl = drawable.getGL();

GLU glu = drawable.getGLU();

gl.glViewport( 0, 0, width, height
);

gl.glMatrixMode( GL.GL_PROJECTION );

gl.glLoadIdentity();

glu.gluOrtho2D( 0.0, 450.0, 0.0, 375.0);


Finally, there's the display() method, a callback that tells the
component to perform its drawing. This will be called after init()
and reshape() at

startup, and again after any GUI events that could
require a repaint, such as dragging the component's window around,
placing another window over the component, etc. The display() method
could also be called by JOGL's Animator class, which exists to call
display() repeatedly from a loop. This has obvious use for games,
media, and other applications
--

they perform animation by slightly
changing the component on each successive call to display().





Graphics Primitives in JOGL


The first method in the samp
le code is the "three dots" example from
Hill. Having passed in a GL, the code looks like this:

gl.glBegin (GL.GL_POINTS);

gl.glVertex2i (100,50);

gl.glVertex2i (100,130);

gl.glVertex2i (150,130);

gl.glEnd ();


The flow here is straightforward: tell JOGL t
hat we want to draw some
points, and then give their coordinates.


One important fact to be aware of is that in OpenGL (and thus JOGL),
the origin point (0,0) is at the bottom left, instead of the top
left, as in AWT and many other 2D imaging systems. So t
he points in
this example appear as in Figure 1.



Figure 1. Points drawn at (100,50), (100,130), and (150,130)


The next simple graphic primitive is the line. As with the points,
these are defined by just providing coordinates with glVertex2i().
But by u
sing GL.GL_LINES as the argument to glBegin, the behavior
changes from drawing points to drawing lines between each pair of
points:

gl.glBegin (GL.GL_LINES);

gl.glVertex2i (50, 200);

gl.glVertex2i (75, 250);

gl.glVertex2i (60, 200);

gl.glVertex2i (85, 250)
;

gl.glEnd();


Of course, it would be easy to write a library to wrap calls like
this in a more Java
-
friendly fashion. For example, a drawLine()
method would look like this:


public void drawLine (GL gl, Point p1, Point p2) {


gl.glBegin (GL.GL_LINES);





gl.glVertex2i (p1.x, p1.y);




gl.glVertex2i (p2.x, p2.y);


gl.glEnd();

}

Instead of drawing lines between pairs of points, we can use the
GL_LINE_STRIP behavior to connect each point in sequence. A sequence
drawn in this fashion ends at the last poi
nt. Using GL_LINE_LOOP, an
extra line is drawn from the final point back to the first point. The
different behaviors are shown in Figure 2.


Figure 2. Drawing with GL_LINES, GL_LINE_STRIP, and GL_LINE_LOOP


Another set of primitives is the various polygon
s that can be drawn
and filled with OpenGL. The simplest are triangles, which create
triangles from sets of three coordinates:

gl.glBegin (GL.GL_TRIANGLES);

gl.glVertex2i (400, 50);

gl.glVertex2i (400, 100);

gl.glVertex2i (420, 75);

gl.glVertex2i (425, 50)
;

gl.glVertex2i (425, 100);

gl.glVertex2i (445, 75);

gl.glEnd();



There are alternate triangle modes available, such as
GL_TRIANGLE_STRIP, in which the last two points of one triangle are
reused as the first two points of the next, and GL_TRIANGLE_FAN, in

which the first point provided is connected to each subsequent pair
of coordinates.


GL_QUADS provides a similar approach for defining quadrangles as
groups of four points. However, for basic rectangles, which are used
much more often than arbitrary quadr
angles, it's easier to define the
points in a simple one
-
line call:

gl.glRecti (200, 50, 250, 150);


This defines a rectangle stretching from (200,50) to (250,150) and
fills it with the current color.

For shapes with an arbitrary number of points, you call

glBegin with
the GL_POLYGON argument, and then define the points of the polygon:


gl.glBegin (GL.GL_POLYGON);

gl.glVertex2i (300, 50);

gl.glVertex2i (350, 60);

gl.glVertex2i (375, 100);

gl.glVertex2i (325, 115);

gl.glVertex2i (300, 75);

gl.glEnd();