Computer graphics -- 2007-2008 -- info.uvt.ro/Laboratory 2

From Wikiversity
Important! These pages are somehow outdated and it is recommended to consult the newer version at Computer graphics -- 2008-2009 -- info.uvt.ro (by Marc Frâncu).

Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, evaluation, tools, repository.



OpenGL / JOGL Basics[edit]

What is JOGL?[edit]

JOGL is a wrapper library (a binding) that allows OpenGL to be used from the Java language. This is achieved by issuing calls to the OpenGL C API through Java Native Interface (JNI).

The JOGL uses as method names the same names as for functions in OpenGL C API, as a result, if one is familiar with OpenGL for C he should have no problem with JOGL. Also, any tutorial on drawing 2D / 3D scenes in OpenGL is good for JOGL, as the program logic is the same, except minor differences (such as keyboard and mouse control, network and sound issues). And this is why when talking about OpenGL we can apply our knowledge to JOGL -- or viceversa.

Links:

Function / method naming rules[edit]

All the OpenGL functions (in case of C API) and methods (in case of Java API) follow some strict naming rules, and by doing so it eases the programmer's life by helping him identifying:

  • if a function is part of the OpenGL, of an auxiliary OpenGL library, or is an user defined function;
  • if it describes the required parameters;

The rules are the following:

  • each function / method starts with gl;
  • all other words are written using camel case convention;
  • for some functions there are defined suffixes -- like 3f -- that denote the number of arguments it takes (3) and the type (f); (this allows us to have the same function but with different argument count and type; this could not be necessary in Java, as we have method overloading, but in C is mandatory;) for example (character -- Java type -- comments):
    • b -- byte -- 8 bit integer;
    • s -- short -- 16 bit integer;
    • i -- int -- 32 bit integer;
    • f -- float -- 32 bit float;
    • d -- double -- 64 bit float;
    • ub, us, ui -- unsigned byte, short, int;
  • for some functions there is a final v appended and it denotes that it takes an array of that many values;

Example functions:

  • glBegin -- it starts a primitive given by the list of verices;
  • glEnd -- it ends a primitive;
  • glVertex2i -- it designates a vertex by giving its coordinates as integers (the z coordinate is taken to be 0);
  • glVertex3f -- it designates a vertex by giving its coordinates as floats;
  • glColor3b -- it designates a RGB color given by bytes from 0 to 255 -- we should be careful in Java because byte is from -128 to 127;
  • glColor4f -- it designates a RGBA color given by floats from 0.0 to 1.0; (A stands for Alpha -- transparency factor;)
  • glColor4fv -- the same as above, but there is only one argument -- a float array;

There are also rules for constants:

  • each constant starts with GL_;
  • the words are in capital letters;
  • the words are separated by underscores _;

Example constants:

  • GL_COLOR_BUFFER_BIT

Links:

State[edit]

When programming using OpenGL we must be aware of the exact state the OpenGL library is in. It is important to know this because:

  • certain operations are available only in certain states;
  • the state includes also values controlling the way the scene is drawn; the most important states:
    • transformations;
    • color;

The OpenGL state is composed of named values which are called variables. All variables have default values, and thus we can ignore the majority of them and change only the ones we are interested in.

Usually the state variables remain the same until you change them explicitly by calling either glEnable, glDisable. You can query the current values by using glGetBooleanv, and so on for Integer, Float and Double. (There are also query functions specific for particular state variables.)

Links:

View-volume, projection and view-port[edit]

The viewport is a rectangular area -- inside the OpenGL window -- where the drawing will take place. It is measured in pixels, and it is positioned relative to the left upper corner of the component.

By default the viewport is setup to be the entire canvas component, but when the component changes its size, we must reset the viewport accordingly -- by calling the glViewport method.

But when displaying we also need to establish a viewing volume -- that a rectangular parallelepiped -- that will be displayed inside the viewport, and every object outside this volume will not be displayed.

In general the aspect ration (the ratio between the width and the height) of the viewport and the volume should be the same, if not the resulting image would look distorted.

In OpenGL we use matrices to manage the view -- projection and modelview. The projection matrix is used to setup the viewing volume, while modelview is used to apply geometric transformations to the objects (translation, rotation, scalation, etc.) They are manipulated by first selecting one as the current matrix by calling glMatrixMode(mode), where mode is either GL_PROJECTION or GL_MODELVIEW. These matrices are modified each time you rotate, translate, scale, modify or perform other operation on the scene, but you can reset by loading the identity matrix in the current matrix by using glLoadIdentity().

We have at our disposal two projections:

  • orthographic -- there is no perspective -- good for 2D scenes; we select it by using the glOrtho function;
  • perspective -- the view is like a distorted pyramid -- good for 3D; we select it by usig glFrustum function;

Links:

Coordinates[edit]

In the simple form the coordinate system is Cartesian on 3 axis (X, Y, Z), where the positive Z points towards us. (In reality OpenGL has a forth coordinate that works just like a scaling factor -- homogeneous coordinates.)

But we should remember that the origin of the world is located on the bottom left corner of the canvas, just as we were taught in geometry classes, and in oposition with the coordinates for a normal AWT or Swing widget -- upper left corner.

Links:

Colors[edit]

OpenGL -- like almost any other software -- uses 24 bit RGB colors, and sometimes RGBA (A stands for Alpha).

Each time the scene is drawn it is filled with a clear color, which can be set by using glClearColor -- the default is black. Every other object drawn is done with another default color, which can be set by using glColor3f.

Links:

Primitives[edit]

OpenGL allows us to use only the following primitives:

  • points;
  • lines;
  • triangles;

Any other surface / object / shape must be created by using these primitives.

In order to draw any of these primitives we need to issue calls to:

  • glBegin -- we start the object;
  • glVertex family -- we add a new point -- vertex -- to the shape;
  • glEnd -- we end the object;

The way in which the vertex list is transformed into a shape depends on what we use as the glBegin argument. Some available options are:

  • GL_POINTS -- treats the vertices as points;
  • GL_LINES -- treats the vertices as a list of pairs -- each value in the triplet being a line's coordinates;
  • GL_LINE_STRIP -- defines a (broken) line that passes through all the vertices;
  • GL_LINE_LOOP -- defines a (broken) closed line that passes through all the vertices;
  • GL_TRIANGLES -- treats the vertices as a list of triplets -- each value in the triplet being a triangle's coordinates;
  • GL_TRIANGLE_FAN -- treats the first vertex as a center, and every other two vertices define a triangle;
  • GL_TRIANGLE_STRIP;
  • GL_QUADS -- treats the vertices as a list of quadruplets -- each value in the quadruplet being the corners of a polygon;
  • GL_QUAD_STRIP;
  • GL_POLYGON -- defines a polygon having the vertices as corners;

Links:

Surface filling[edit]

When drawing surfaces we have a few options about how they are drawn:

  • filling the entire surface;
  • drawing only the edges;
  • drawing only the corners;
  • drawing the front or the back of the shape -- this is determined by the way in which you specified the vertices;

These options are controlled by glPolygonMode which has two arguments:

  • drawn face:
    • GL_FRONT;
    • GL_BACK;
    • GL_FRONT_AND_BACK;
  • drawing mode:
    • GL_FILL -- the default;
    • GL_LINE;
    • GL_POINT;

We can have different drawing modes for the front and the back;

Links:

Installing JOGL[edit]

In short it implies:

  • installing -- or checking if is installed (usually this is the case) -- the OpenGL runtime;
  • downloading the JOGL distribution from:
  • jogl.dev.java.net:
  • it is highly recommended that you read the Userguide.html file inside the distribution archive;
  • creating a folder named lib inside the your project;
  • copying the entire contents of the lib folder inside the distribution to the lib folder inside your project;
  • adding the jogl.jar and glugen-rt.jar to the application classpath -- this depends on the IDE:
    • from Eclipse expand the lib folder, right-click the jogl.jar and choose Add to build path option;
    • if you use command line tools you could set the CLASSPATH environment variable to include the lib folder;
  • adding the lib folder to the java.library.path environment variable when running the application:
    • from Eclipse select Run -> Open Run Dialog... -> Java Application -> New Configuration (or the name given to the run configuration) -> Arguments tab -> add the line -Djava.library.path=lib (or the path where the DLL's reside) into the VM arguments textbox.
    • if you use command line tools you could set the LD_LIBRARY_PATH environment variable to include the lib folder;

The following paragraph is quoted from the Userguide.html file inside the distribution archive (it applies to Windows):

The JOGL distribution for developers comes in the form of a zip archive which contains the Java classes to call OpenGL from Java, as well as the associated JNI native libraries. JOGL depends on some run-time support classes and native code provided by the GlueGen project; these classes and native code are also provided in the zip bundles.
If you are developing a new application which uses JOGL, download the zip archive for your platform (for example., jogl-[version]-windows-i586.zip) and unzip it. Modify your CLASSPATH environment variable to include the full paths to jogl.jar and gluegen-rt.jar; for example, ".;C:\Some\Other\Package\foo.jar;C:\Users\myhome\jogl-[version]-windows-i586\lib\jogl.jar;C:\Users\myhome\jogl-[version]-windows-i586\lib\gluegen-rt.jar". (If you did not previously set the CLASSPATH environment variable, you may want to make sure that ".", the current directory, is on your new CLASSPATH.) Modify your PATH environment variable (Windows), LD_LIBRARY_PATH environment variable (Solaris and Linux), or DYLD_LIBRARY_PATH environment variable (Mac OS X) to contain the full path to the "lib" directory; for example, on Windows, add "C:\Users\myhome\jogl-[version]-windows-i586\lib" to your PATH using the System control panel, Advanced tab, Environment Variables button. At this point your Java installation should be able to see the JOGL class files. Users of IDEs such as NetBeans and Eclipse should consult the IDE's documentation to see how to add jar files and native libraries to their current project.
Dropping the JOGL jar and native library into the extension directory of the JRE is strongly discouraged. Doing so can cause conflicts with third-party applications launched via Java Web Start, and causes confusion later when upgrading the distribution.

For installation details you should consult -- please note that these tutorials might refer to an older version of JOGL:

Application window[edit]

Using Swing or AWT[edit]

JOGL puts at our disposal two classes for displaying scenes:

  • GLCanvas -- an AWT component;
  • GLJpannel -- an Swing component;

Both classes implement the GLAutoDrawable interface, so they can be used interchangeably; but the JOGL user's guide recommends using the AWT component -- GLCanvas -- as it provides better performance.

Thus in what follows we shall use AWT; but you could as easily use Swing.

Links:

Creating an AWT window[edit]

We shall create two classes:

  • MainFrame -- the application frame;
  • Main -- the application entry point;

MainFrame[edit]

[...]

public class MainFrame
		extends Frame
{
	public MainFrame()
	{
		super("Java OpenGL");
		
		this.setLayout(new BorderLayout());
		
		// Registering a window event listener to handle the closing event.
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		
		this.setSize(800, 600);
		
		// this.initializeJogl();
		
		this.setVisible(true);
	}
}

Please note the call to setVisible(true) after initializeJogl; this is mandatory, otherwise the internal state of the library will be messed-up.

Main[edit]

public class Main
{
	public static void main(String[] arguments)
	{
		new MainFrame();
	}
}

Initializing JOGL[edit]

Once we have our window we can proceed to the JOGL setup:

  • setting some JOGL parameters -- capabilities as they are called;
  • creating the GLCanvas object, and adding it to the window;
  • registering the GLEventListener;

Thus we create the initializJogl method which will be concerned with:

  • setting up various variables -- this is achieved through the GLCapabilities instance; for example we can tune:
    • hardware acceleration on / off;
    • double buffering on / off;
    • rendering mode;
    • rendering only a color channel;
    • etc.;
  • creating the GLCanvas instance that we shall use for rendering;
  • adding the GLCanvas instance to the center of the frame;
  • registering the GLEventListener to the newly created canvas: in our case the MainFrame class will also implement the needed GLEventListener interface;
[...]

public class MainFrame
		extends Frame
		implements GLEventListener
{
	
	[...]
	
	private void initializeJogl()
	{
		// Creating an object to manipulate OpenGL parameters.
		GLCapabilities capabilities = new GLCapabilities();
		
		// Setting some OpenGL parameters.
		capabilities.setHardwareAccelerated(true);
		capabilities.setDoubleBuffered(true);
		
		// Creating an OpenGL display widget -- canvas.
		this.canvas = new GLCanvas();
		
		// Adding the canvas in the center of the frame.
		this.add(this.canvas, BorderLayout.CENTER);
		
		// Adding an OpenGL event listener to the canvas.
		this.canvas.addGLEventListener(this);
	}
	
	[...]
	
	private GLCanvas canvas;
}

Links:

Writting JOGL callbacks[edit]

Because we can make calls to the JOGL library only during one of the callbacks defined by GLEventListener -- otherwise an exception will be generated, the next step is to implement the four methods declared inside the GLEventListener interface (all the methods are called automatically by the library):

  • init -- called when the canvas was initialized;
  • rehape -- called when the canvas has changed position or size;
  • displayChanged -- called when the rendering mode has been changed;
  • display -- called when we are required to describe the scene;
[...]

public class MainFrame
		extends Frame
		implements GLEventListener
{
	[...]
	
	public void init(GLAutoDrawable canvas)
	{
		return;
	}
	
	public void display(GLAutoDrawable canvas)
	{
		return;
	}
	
	public void reshape(GLAutoDrawable canvas, int left, int top, int width, int height)
	{
		return;
	}
	
	public void displayChanged(GLAutoDrawable canvas, boolean modeChanged, boolean deviceChanged)
	{
		return;
	}
	
	[...]
}

Links:

Drawing with JOGL[edit]

The next step is starting to draw shapes on our canvas. And here is where classical OpenGL C API diverges from JOGL API: in C we just call the functions (like glVertex3f(...)), but in Java we can not call them unless they are defined in the current class -- or in a super class. But the solution is quite simple: all OpenGL functions are wrapped inside a huge interface named GL (with more than 1000 methods).

For this laboratory all that interests us from the GLEventListener interface is the display method. In this method we shall add all the code that will generate our scene. This method will be called every time OpenGL decides that you need to repaint your graphics, and it gives you a reference to a GLAutoDrawable instance which is the canvas instance where you registered the listener inside the initializeJogl method.

Let's start by drawing a few colored triangles:

[...]

public class MainFrame
		extends Frame
		implements GLEventListener
{
	[...]
	
	public void display(GLAutoDrawable canvas)
	{
		GL gl = canvas.getGL();
		
		// Erasing the canvas -- filling it with the clear color.
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		
		// Selecting filling for both the front and back.
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
		
		// Beginning a triangle list.
		gl.glBegin(GL.GL_TRIANGLES);
		
		// Selecting red as the current drawing color -- the color of the first corner.
		gl.glColor3f(1, 0, 0);
		
		// Defining the first corner.
		gl.glVertex2f(0.5f, 0.5f);
		
		gl.glColor3f(0, 1, 0);
		gl.glVertex2d(0.5 + Math.cos(this.angle) * 0.5, 0.5 + Math.sin(this.angle) * 0.5);
		
		gl.glColor3f(0, 0, 1);
		gl.glVertex2d(0.5 + Math.cos(this.angle + Math.PI / 2) * 0.5, 0.5 + Math.sin(this.angle + Math.PI / 2) * 0.5);
		
		// Ending the triangle list.
		gl.glEnd();
		
		// Forcing the scene to be rendered.
		gl.glFlush();
		
		this.angle += (2 * Math.PI) / 90;
	}
	
	[...]
}

We could also be interested in the init method where we should place the code that initializes:

  • the view;
  • background color;
  • lights;
  • etc;

Also the reshape method is a good place to assure that the aspect ratio of the widget is the same one as the view volume.

[...]

public class MainFrame
		extends Frame
		implements GLEventListener
{
	[...]
	
	public void init(GLAutoDrawable canvas)
	{
		// Obtaining the GL instance associated with the canvas.
		GL gl = canvas.getGL();
		
		// Setting the clear color -- the color which will be used to erase the canvas.
		gl.glClearColor(0, 0, 0, 0);
		
		// Selecting the projection matrix.
		gl.glMatrixMode(GL.GL_PROJECTION);
		
		// Initializing the projection matrix with the identity matrix.
		gl.glLoadIdentity();
		
		// Setting the projection to be orthographic.
		// Selecting the view volume to be x from 0 to 1, y from 0 to 1, z from -1 to 1. 
		gl.glOrtho(0, 1, 0, 1, -1, 1);
	}
	
	[...]
	
	public void reshape(GLAutoDrawable canvas, int left, int top, int width, int height)
	{
		GL gl = canvas.getGL();
		
		// Selecting the viewport -- the display area -- to be the entire widget.
		gl.glViewport(0, 0, width, height);
		
		// Determining the width to height ratio of the widget.
		double ratio = (double) width / (double) height;
		
		// Selecting the projection matrix.
		gl.glMatrixMode(GL.GL_PROJECTION);
		
		gl.glLoadIdentity();
		
		// Selecting the view volume to be x from 0 to 1, y from 0 to 1, z from -1 to 1.
		// But we are careful to keep the aspect ratio and enlarging the width or the height.
		if (ratio < 1)
			gl.glOrtho(0, 1, 0, 1 / ratio, -1, 1);
		else
			gl.glOrtho(0, 1 * ratio, 0, 1, -1, 1);
	}
	
	[...]
}

Links:

Animation[edit]

As we might have noted the display method is called only when there is a need to redraw the scene; but if we want to create animations we should have the possibility to call this method a couple of times per second. We could do this by creating a background thread that forces the repaint, but the easiest -- and most recommended -- solution is to use the Animator class (behind the scenes the animator does create it's own thread).

For this we shall create a new member of the Animator class, and update the initializeJogl method as to:

  • create the instance;
  • add the canvas to the animator;
  • start the animator;
	private void initializeJogl()
	{
		[...]
		
		this.animator = new Animator();
		
		this.animator.add(this.canvas);
		
		this.animator.start();
	}

Links:

Drawing a circle[edit]

Because we don't have a circle primitive, we shall use GL_TRIANGLE_FAN to build one, like in the following code.

[...]

public class MainFrame
		extends Frame
		implements GLEventListener
{
	[...]
	
	private void drawCircle(GL gl, float x, float y, float z, float r, int steps)
	{
		// Starting a list of triangles.
		gl.glBegin(GL.GL_TRIANGLE_FAN);
		
		// Seting the center vertex of the fan.
		gl.glVertex3f(x, y, z);
		
		// For each step draw a triangle from the fan.
		for (int step = 0; step < steps; step++) {
			double angle = 2 * Math.PI / steps * step;
			gl.glVertex3d(x + Math.cos(angle) * r, y + Math.sin(angle) * r, z);
		}
		
		// Close the fan.
		gl.glVertex3f(x + r, y, z);
		
		// End the list of triangles.
		gl.glEnd();
	}
	
	[...]
}

API[edit]

References[edit]

Assignment[edit]

This is the second assignment, so please commit it to the folder assignment-02.

Write an application that creates a window capable of displaying OpenGL scenes.

The scene should be composed of:

  • a rectangle which represents a bounding box -- the bounding box should be centered in the view port and should occupy 75% of the view port;
  • 2 circles which travel inside the bounding box, bouncing at each of the four edges;
  • the rectangle edges are colored, and the rectangle is also filled with a different color than its edges;
  • the circles are initially filled with a random color; but each time they hit an edge the fill color is changed with a new random one -- this color should persist until the next edge hit;
  • you should pay attention to the aspect ration, and assure that a circle is seen as a circle -- not as an elipse;

The circle initial position and direction is chosen randomly. The direction can be only diagonal, horizontal or vertical (in total 8 possible directions). It is assumed that when bouncing on an edge they change their direction only in one of the 8 possible directions. (For example if the ball is moving in the direction North-West, when hitting the edge it will change to North-East -- thus the two directions forming a 90 degree angle. But if it goes in the direction North, when hitting the edge it will change to South -- 180 degrees.)

For bonus points also implement -- one or more of -- the following enhancements:

  • at random moments, circles appear and disappear -- but at no time the scene will be left empty;
  • if two circles, collide then they will become only one (keeping the direction of any of the two), and a new circle will be randomly created;
  • the circles could change their radius by using the sin or cos function;

Constraints:

  • it is forbidden to use glTranslate... function family; each coordinate will have to be manually computed;

Hints:

  • in order not to be bothered with what the size of the widget is, always assume that the view volume is constant;

Complete example[edit]

Note that this example is available on the SVN repository in the folder examples/example-01.

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.FPSAnimator;


public class MainFrame
		extends Frame
		implements GLEventListener
{
	public MainFrame()
	{
		super("Java OpenGL");
		
		this.setLayout(new BorderLayout());
		
		// Registering a window event listener to handle the closing event.
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		
		this.setSize(800, 600);
		
		this.initializeJogl();
		
		this.setVisible(true);
	}
	
	private void initializeJogl()
	{
		// Creating an object to manipulate OpenGL parameters.
		GLCapabilities capabilities = new GLCapabilities();
		
		// Setting some OpenGL parameters.
		capabilities.setHardwareAccelerated(true);
		capabilities.setDoubleBuffered(true);
		
		// Creating an OpenGL display widget -- canvas.
		this.canvas = new GLCanvas();
		
		// Adding the canvas in the center of the frame.
		this.add(this.canvas, BorderLayout.CENTER);
		
		// Adding an OpenGL event listener to the canvas.
		this.canvas.addGLEventListener(this);
		
		// Creating an animator that will redraw the scene 40 times per second.
		this.animator = new FPSAnimator(40);
		
		// Registering the canvas to the animator.
		this.animator.add(this.canvas);
		
		// Starting the animator.
		this.animator.start();
	}
	
	public void init(GLAutoDrawable canvas)
	{
		// Obtaining the GL instance associated with the canvas.
		GL gl = canvas.getGL();
		
		// Setting the clear color -- the color which will be used to erase the canvas.
		gl.glClearColor(0, 0, 0, 0);
		
		// Selecting the projection matrix.
		gl.glMatrixMode(GL.GL_PROJECTION);
		
		// Initializing the projection matrix with the identity matrix.
		gl.glLoadIdentity();
		
		// Setting the projection to be orthographic.
		// Selecting the view volume to be x from 0 to 1, y from 0 to 1, z from -1 to 1. 
		gl.glOrtho(0, 1, 0, 1, -1, 1);
	}
	
	public void display(GLAutoDrawable canvas)
	{
		GL gl = canvas.getGL();
		
		// Erasing the canvas -- filling it with the clear color.
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		
		// Selecting white as the current drawing color.
		gl.glColor3f(1, 1, 1);
		
		// Selecting lines only to be drawn for both front and back.
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
		
		// Drawing a circle.
		this.drawCircle(gl, 0.5f, 0.5f, 0f, 0.4f, 64);
		
		// Selecting filling for both the front and back.
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
		
		// Beginning a triangle list.
		gl.glBegin(GL.GL_TRIANGLES);
		
		// Selecting red as the current drawing color -- the color of the first corner.
		gl.glColor3f(1, 0, 0);
		
		// Defining the first corner.
		gl.glVertex2f(0.5f, 0.5f);
		
		gl.glColor3f(0, 1, 0);
		gl.glVertex2d(0.5 + Math.cos(this.angle) * 0.5, 0.5 + Math.sin(this.angle) * 0.5);
		
		gl.glColor3f(0, 0, 1);
		gl.glVertex2d(0.5 + Math.cos(this.angle + Math.PI / 2) * 0.5, 0.5 + Math.sin(this.angle + Math.PI / 2) * 0.5);
		
		// Ending the triangle list.
		gl.glEnd();
		
		// Forcing the scene to be rendered.
		gl.glFlush();
		
		this.angle += (2 * Math.PI) / 90;
	}
	
	private float angle = 0;
	
	public void reshape(GLAutoDrawable canvas, int left, int top, int width, int height)
	{
		GL gl = canvas.getGL();
		
		// Selecting the viewport -- the display area -- to be the entire widget.
		gl.glViewport(0, 0, width, height);
		
		// Determining the width to height ratio of the widget.
		double ratio = (double) width / (double) height;
		
		// Selecting the projection matrix.
		gl.glMatrixMode(GL.GL_PROJECTION);
		
		gl.glLoadIdentity();
		
		// Selecting the view volume to be x from 0 to 1, y from 0 to 1, z from -1 to 1.
		// But we are careful to keep the aspect ratio and enlarging the width or the height.
		if (ratio < 1)
			gl.glOrtho(0, 1, 0, 1 / ratio, -1, 1);
		else
			gl.glOrtho(0, 1 * ratio, 0, 1, -1, 1);
	}
	
	public void displayChanged(GLAutoDrawable canvas, boolean modeChanged, boolean deviceChanged)
	{
		return;
	}
	
	private GLCanvas canvas;
	private Animator animator;
	
	private void drawCircle(GL gl, float x, float y, float z, float r, int steps)
	{
		// Starting a list of triangles.
		gl.glBegin(GL.GL_TRIANGLE_FAN);
		
		// Seting the center vertex of the fan.
		gl.glVertex3f(x, y, z);
		
		// For each step draw a triangle from the fan.
		for (int step = 0; step < steps; step++) {
			double angle = 2 * Math.PI / steps * step;
			gl.glVertex3d(x + Math.cos(angle) * r, y + Math.sin(angle) * r, z);
		}
		
		// Close the fan.
		gl.glVertex3f(x + r, y, z);
		
		// End the list of triangles.
		gl.glEnd();
	}
}