Chapter 2: Using What We’ve Seen – Learning Java Bindings for OpenGL (JOGL)

This book and the excerpts on this blogĀ are from 2004. Obviously, the API has changed a bit since then. I provide the older information and book in the hope that it will be useful to some hobbyists.

Purchase Printed Book Learning Java Bindings for OpenGL (JOGL)

 

Table of Contents: Learning Java Bindings for OpenGL (JOGL)

This book describes JOGL which was approved as JSR 231 and will become the javax.media.opengl package.

-T. Gene Davis

 

 

copyright 2004 by Gene Davis of genedavissoftware.com

Chapter 2: Using What We’ve Seen

Coordinate Systems

There is no sense in waiting until you know the best way to do things before using OpenGL. Sure you’re going to find out a few months later that there was a better way to do this or that, but you’ll never get to that point if you don’t use what you’ve learned. We’re going to take that principle to heart and start off showing you that you’ve already learned a lot.

Coordinate systems may be something that you’ve banished to the darker regions of your memory along with memories of Junior High School. It’s time to dust off those memories.

Do you remember the Cartesian coordinate system? It consists of two perpendicular axes [lines]. One is the y axis and the other is the x axis.

Here is a typical coordinate system with a point mapped out on it.

ch2_coordinate_system

 

You’ll notice that there are numbers all over the place. Usually, they’re not all written out, but they’re always there. All points on the coordinate system correspond to distances along the y axis or along the x axis away from the origin. The origin is where the two axes meet. We’ve marked the origin with an O.

So we’ve indicated the position of one point on the graph. It is (6, 10). The 6 corresponds to 6 on the x axis away from the origin. The 10 indicates the point is 10 from the origin on the y axis.

Notice some positions are negative and some are positive. It would get confusing if 2 left of the origin looked like 2 right of the origin when we mapped out points. If there wasn’t any negatives how would we be able to tell where the point (1, 2) was? It could be in four different places, however if negatives are used (-1, 2) is very easy to spot. Can you find it?

If you’re lost at this point, your in trouble. Find a good introduction to geometry or introduction to graphs and functions book and plan on reading some of it. I’d recommend checking out a college book store, or maybe a big general book store. If money’s an issue, check out the internet for tutorials. There are plenty out there.

You can continue reading either way, but plan on spending time on learning about graphs.

During this chapter we will be using the coordinate system extensively. We will also cover drawing lines and the old Trig. standbys — Sine, Cosine and Tangent. Eventually in your pursuit of graphics and JOGL, you will want to become thoroughly familiar with these, but that is then and now is now. I’ll explain briefly anything that matters, and you can always use this book for reference.

glViewport

You may remember seeing a line of code similar to:


gl.glViewport(0, 0, 500, 300);

This is using the GL Object to set the area of the screen to be seen. This is a slight simplification, but will be a good enough definition for our purposes. So remember, glViewport() clips the area to be viewed.

Imagine you have a big piece of paper. Now imagine that you cut a rectangle out of it. If you placed the paper over your monitor, you would only see the portion that wasn’t covered by the paper. In other words, you would see through the rectangular hole in the paper and that’s all.

The rectangular hole in the paper acts the same as the glViewport().

gluOrtho2D

The gluOrtho2D() method is used to set the coordinate system that appears in your GLCanvas. This is sort of like the numbers we have on the Cartesian coordinate system in the first section of this chapter. You can choose any numbers that you find useful for what you are doing. For now you will set the glViewport() and the gluOrtho2D() to the same values. Remeber, the gluOrtho2D() sets the coordinates the GLCanvas maps to.

glClearColor, GL_COLOR_BUFFER_BIT and glClear

When you erase a black board what color does it become? The correct answer is black, but I’m sure someone out there is thinking, “Well that depend on the color of chalk you used.” To that someone, I’m not talking to you anymore.

When you clean a white board what color does it become? I’m sure everyone said white, since we’re not talking to the naysayers anymore.

The GLCanvas that we’re doing our drawing on is a color too. What color is it when we clear it? I just know someone said, “Canvas color.” You there, … Go join the naysayers! Canvas color indeed….

The truth is that you, the programmer choose the color. The method that chooses the color looks like this:


glClearColor(0.0f, 0.0f, 0.0f, 1.0f)

The first three floating point numbers represent red, green and blue. The forth number is for the alpha layer. If you know what that is, great. Any graphic designers reading this, probably have dealt with alpha layers before. I’ll spare the rest of you the details for now.

The color of the cleared GLCanvas is chosen by mixing the red, green and blue elements using numbers between 0.0 and 1.0. Red would be:


glClearColor(1.0f, 0.0f, 0.0f, 1.0f)

Blue would be:


glClearColor(0.0f, 0.0f, 1.0f, 1.0f)

Green would be:


glClearColor(0.0f, 1.0f, 0.0f, 1.0f)

A valid question is how do I choose some other color? Find a simple painting program, or any program that lets you choose an RGB color. Photoshop displays a nice color picker. Pick a color that you like. Let’s choose purple.

RGB colors usually assign the values for red, green and blue to numbers between 0 and 255. Purple, in this case, is assigned the values of 124, 11 and 183, for red, green and blue respectively. These are hardly values between 0.0 and 1.0.

No problem. If you’re a little fuzzy on your math, here’s what you need to do. Take each of the numbers and divide by 255. Now you have a number between 0.0 and 1.0. In this case the numbers are:


glClearColor(0.486f, 0.043f, 0.718f, 1.0f)

Math is your friend. It’s also not too boring when it is being useful.

Now that you’ve chosen a color, you can clear the GLCanvas to your color any time you want. Just use the GL command:


glClear(GL.GL_COLOR_BUFFER_BIT)

So what color what color is a GLCanvas when it’s cleared? Canvas color of course.

glColor3f

It’s time to choose your own colors. You need to choose a color to draw in it. Remember that you can use any RGB color picker to pick pretty color to draw in, and then convert the base 256 numbers for red, green and blue to the proper float value by dividing it by 255.

To set your current drawing color, use your GL object to call the command glColor3f(). It looks something like this:


gl.glColor3f(.9f, .12f, .15f);

Personally, I always like to predefine my red, green and blue values in float variables, so the actual line looks like this:


gl.glColor3f(red, green, blue);

That just seems to be easier on the eyes, and trust me you will want to make anything you can easy on your eyes when you have a 30,000 line Java program in front of you.

Making a graph

Now let’s apply some of the knowledge we’ve gained. We’re going to make a Cartesian coordinate system similar to the one we showed earlier in the chapter. Don’t stress. It’s actually not much work.

Here’s the code for the main class:

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.games.jogl.*;


/**
 * This is a basic JOGL app. Feel free to
 * reuse this code or modify it.
 */
public class OurGraphApp extends JFrame {

    public static void main(String[] args) {
        final OurGraphApp app = new OurGraphApp();

        // show what we've done
        SwingUtilities.invokeLater (
            new Runnable() {
                public void run() {
                    app.setVisible(true);
                }
            }
        );
    }

    public OurGraphApp() {
        //set the JFrame title
        super("The Cartesian Coordinate System");

        //kill the process when the JFrame is closed
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //only three JOGL lines of code ... and here they are
        GLCapabilities glcaps = new GLCapabilities();

        GLCanvas glcanvas =
        GLDrawableFactory.getFactory().createGLCanvas(glcaps);

        glcanvas.addGLEventListener(
           new OurGraphGLEventListener()
        );

        //add the GLCanvas just like we would any Component
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        //center the JFrame on the screen
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize =
           Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize  = frame.getSize();

        if (frameSize.width  > screenSize.width )
           frameSize.width  = screenSize.width;
        if (frameSize.height > screenSize.height)
           frameSize.height = screenSize.height;

        frame.setLocation (
            (screenSize.width  - frameSize.width ) >> 1,
            (screenSize.height - frameSize.height) >> 1
        );
    }
}

 

 

This is the code for the GLEventListener:

 

import java.awt.*;
import java.awt.event.*;
import net.java.games.jogl.*;

/**
 * For our purposes only two of the GLEventListeners matter. Those would be
 * init() and display().
 */
public class OurGraphGLEventListener implements GLEventListener {

	///////////////////////////////////////////////////
	// GLEventListener implementation
	//

    /**
     * Take care of initialization here.
     */
    public void init(GLDrawable gld) {
        GL gl = gld.getGL();
		GLU glu = gld.getGLU();

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

		gl.glLineWidth(2.0f);

		gl.glViewport(-250, -150, 250, 150);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluOrtho2D(-250.0, 250.0, -150.0, 150.0);
    }

    /**
     * Take care of drawing here.
     */
    public void display(GLDrawable gld) {
        drawGraph( gld.getGL() );
    }


    public void reshape(
                        GLDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height
                      ) {}
     public void displayChanged(
                              GLDrawable drawable,
                              boolean modeChanged,
                              boolean deviceChanged
                            ) {}

	///////////////////////////////////////////////////
	// Other methods
	//

	/**
	 * In here we draw a Cartesian Coordinate System.
	 */
	private void drawGraph(GL gl) {
		float red;
		float green;
		float blue;

		gl.glClear(GL.GL_COLOR_BUFFER_BIT);

		////////////////////
		//drawing the grid
		red = 0.2f;
		green = 0.2f;
		blue = 0.2f;

		gl.glColor3f(red, green, blue);

		//You may notice I'm using GL_LINES here.
		//Details of glBegin() will be discussed later.
		gl.glBegin(GL.GL_LINES);

		//draw the vertical lines
		for (int x=-250; x<=250; x+=10) {
			gl.glVertex2d(x, -150);
			gl.glVertex2d(x, 150);
		}

		//draw the horizontal lines
		for (int y=-150; y&lt=150; y+=10) {
			gl.glVertex2d(-250, y);
			gl.glVertex2d(250, y);
		}

		gl.glEnd();

		//////////////////////////////
		// draw the x-axis and y-axis
		red = 0.0f;
		green = 0.2f;
		blue = 0.4f;

		gl.glColor3f(red, green, blue);

		gl.glBegin(GL.GL_LINES);

		//line for y-axis
		gl.glVertex2d(0, 140);
		gl.glVertex2d(0, -140);

		//line for x-axis
		gl.glVertex2d(240, 0);
		gl.glVertex2d(-240, 0);

		gl.glEnd();

		/////////////////////
		// draw arrow heads
		gl.glBegin(GL.GL_TRIANGLES);

		gl.glVertex2d( 0, 150);
		gl.glVertex2d(-5, 140);
		gl.glVertex2d( 5, 140);

		gl.glVertex2d( 0, -150);
		gl.glVertex2d(-5, -140);
		gl.glVertex2d( 5, -140);

		gl.glVertex2d(250, 0);
		gl.glVertex2d(240,-5);
		gl.glVertex2d(240, 5);

		gl.glVertex2d(-250, 0);
		gl.glVertex2d(-240,-5);
		gl.glVertex2d(-240, 5);

		gl.glEnd();

	}

}

 

 

 

ch2_graph

A description of what we’ve done is in order. We’ve created this program using two classes. They are the OurGraphGLEventListener and the OurGraphApp classes. OurGraphApp contains the main() method. Mostly we use the OurGraphApp class to start up the non JOGL specific code. The only JOGL specific code in OurGraphApp initializes the GLCapabilities class and retrieves a GLCanvas, then sets up the GLEventListener which does all the actual JOGL work.

OurGraphGLEventListener is a bit more JOGL intensive. It is our GLEventListener implementation. As I’m sure you recall, GLEventListeners draw pretty pictures using Java Bindings for OpenGL. The only two methods we care about for now are init() and display().

The init() method retrieves a copy of the current GL object and current GLU object. Many frugal programmers are in the habit of saving objects off, so they don’t have to be retrieved later. Don’t do this here. The GL and GLU objects may change while you’re not looking, so get a new instance each time you can, just in case.

You should recognize some of the calls in the init() method. glClearColor(), glViewport() and gluOrtho2D() have been discussed earlier in this chapter.

Look at the glLineWidth(). We are drawing lines all over the place in the coordinate system. This is where we set it’s width.

Don’t be fooled by the 2.0f for line width. This does not refer to the thickness of the line in the gluOrtho2D coordinate system. It refers to the thickness of the line in our monitor’s resolution. Notice that if you make the window that the GLCanvas is in larger or smaller, the gluOrtho2D coordinate system is being stretch or shrunk, but the line widths remain the same.

In the display() method we just call the drawGraph() method. This will be useful later, because we will want to draw graph in addition to what we are doing.

Look at the drawGraph() method. Examine where colors are set. Find the gl.glBegin(GL.GL_LINES) and the gl.glEnd() calls. Identify where the triangle arrow heads are drawn. We haven’t discussed all of this yet, but you know enough now to spot where each part of the graph is drawn. You need to read through the code as well as type it in, if you intend to learn JOGL.

Now mess with colors. Pick an RGB color of your own for the lines. Mess around with the glVertex2d() methods and learn how they work.

Point-Slope Equation

The graph method that we just wrote is going to come in useful now. We don’t need it, but it will make the next two example programs more interesting, so we’ll leave it in. Below is the screen capture of the Point-Slope Calculation program.

You may wonder why we care to revisit these nasty equations from high school. These are the uses for math that your teacher never explained. I’m guessing none of your math teachers said, “Learn this stuff, because it will help you write really cool Java programs.” That’s what they should have said.

Take for instance the point-slope equation of a line. If you’re writing a Pong clone, point-slope gives you a simplistic equation for figuring out when the ball hit the wall or paddle. Let’s make a graph of user inputted lines.

ch2_line

The main class is:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.games.jogl.*;


/**
 * This is a basic JOGL app. Feel free to
 * reuse this code or modify it.
 */
public class LineGraphApp extends JFrame implements ActionListener {
	//Notice we've given these two objects a larger scope.
	//Local scope to the constructor was no longer sufficient.
	LineGLEventListener listener = new LineGLEventListener();
	GLCanvas glcanvas;

	JTextField ajtf = new JTextField("3", 3);
	JTextField bjtf = new JTextField("2", 3);
	JTextField mjtf = new JTextField("-1", 6);


    public static void main(String[] args) {
        final LineGraphApp app = new LineGraphApp();

        // show what we've done
        SwingUtilities.invokeLater (
            new Runnable() {
                public void run() {
                    app.setVisible(true);
                }
            }
        );
    }

    public LineGraphApp() {
        //set the JFrame title
        super("Point-Slope Calculation");

        //kill the process when the JFrame is closed
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Setting up our southern JPanel
        JPanel jp = new JPanel();

        //adding the JTextFields and JLabels
        jp.add(new JLabel("x:"));
        jp.add(ajtf);
        jp.add(new JLabel("   y:"));
        jp.add(bjtf);
        jp.add(new JLabel("   slope: "));
        jp.add(mjtf);

        //adding the JButton
        JButton jb = new JButton("Redraw");
        jb.addActionListener(this);
        jp.add(jb);

        getContentPane().add("South", jp);

        //only three JOGL lines of code ... and here they are
        GLCapabilities glcaps = new GLCapabilities();

        glcanvas =
        GLDrawableFactory.getFactory().createGLCanvas(glcaps);

        glcanvas.addGLEventListener(listener);

        //add the GLCanvas just like we would any Component
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        //center the JFrame on the screen
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize =
           Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize  = frame.getSize();

        if (frameSize.width  > screenSize.width )
           frameSize.width  = screenSize.width;
        if (frameSize.height > screenSize.height)
           frameSize.height = screenSize.height;

        frame.setLocation (
            (screenSize.width  - frameSize.width ) >> 1,
            (screenSize.height - frameSize.height) >> 1
        );
    }

	/**
	 * Implementation of our ActionListener. This allows the
	 * buttons to perform an action. In this case they set
	 * the "whatToDraw" String and ask for a repaint of the
	 * GLCanvas.
	 */
	public void actionPerformed(ActionEvent ae) {
		listener.a = Double.parseDouble( ajtf.getText() );
		listener.b = Double.parseDouble( bjtf.getText() );
		listener.m = Double.parseDouble( mjtf.getText() );
		glcanvas.repaint();
	}
}

 

 

The GLEventListener follows. Once again we’ve limited the program to an object with a main() method and a GLEventListener implementation. I hope you’re seeing a pattern.

 

import java.awt.*;
import java.awt.event.*;
import net.java.games.jogl.*;

/**
 * For our purposes only two of the GLEventListeners matter. Those would be
 * init() and display().
 */
public class LineGLEventListener implements GLEventListener {

	//public slope and (a,b) for setting up the line
	public double m = -1;
	public double a = 3;
	public double b = 2;

	//floats used for color selection
	float red;
	float green;
	float blue;


	///////////////////////////////////////////////////
	// GLEventListener implementation
	//

    /**
     * Take care of initialization here.
     */
    public void init(GLDrawable gld) {
        GL gl = gld.getGL();
		GLU glu = gld.getGLU();

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

		gl.glLineWidth(2.0f);
		gl.glPointSize(2.0f);

		gl.glViewport(-250, -150, 250, 150);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluOrtho2D(-250.0, 250.0, -150.0, 150.0);
    }

    /**
     * Take care of drawing here.
     */
    public void display(GLDrawable gld) {
		GL gl = gld.getGL();
        drawGraph( gl );

		//This is the new code. We find out
		//which trig function is selected,
		//then we draw a scaled up version of
		//the function.

		//Let's make the line red
		red = 1.0f;
		green = 0.2f;
		blue = 0.2f;

		gl.glColor3f(red, green, blue);

		// Point-slope form of a line is:
		// y = m(x -a) + b where (a,b) is the
		// point.
		// Also,
		// y - b = m( x - a )
		// works.
		// m is of course the slope.

		//Let's make the line
		gl.glBegin(GL.GL_POINTS);

		//let's make every grid one point even
		//though it is made by 10 x 10 pixels.
		double a1 = a * 10;
		double b1 = b * 10;

		for (int x=-250; x<=250; x++) {
			gl.glVertex2d(x, (m * (x - a1) + b1) );
		}

		gl.glEnd();


		//Let's make the point now

		//making the point white
		red = 1.0f;
		green = 1.0f;
		blue = 1.0f;
		gl.glColor3f(red, green, blue);

		gl.glPointSize(4.0f);

		gl.glBegin(GL.GL_POINTS);
		gl.glVertex2d( a1, b1 );
		gl.glEnd();

		//resetting the point to 2 pixels.
		gl.glPointSize(2.0f);
    }


    public void reshape(
                        GLDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height
                      ) {}

    public void displayChanged(
                              GLDrawable drawable,
                              boolean modeChanged,
                              boolean deviceChanged
                            ) {}

	///////////////////////////////////////////////////
	// Other methods
	//

	/**
	 * In here we draw a Cartesian Coordinate System.
	 */
	private void drawGraph(GL gl) {

		gl.glClear(GL.GL_COLOR_BUFFER_BIT);

		////////////////////
		//drawing the grid
		red = 0.2f;
		green = 0.2f;
		blue = 0.2f;

		gl.glColor3f(red, green, blue);

		//You may notice I'm using GL_LINES here.
		//Details of glBegin() will be discussed latter.
		gl.glBegin(GL.GL_LINES);

		//draw the vertical lines
		for (int x=-250; x<=250; x+=10) {
			gl.glVertex2d(x, -150);
			gl.glVertex2d(x, 150);
		}

		//draw the horizontal lines
		for (int y=-150; y<=150; y+=10) {
			gl.glVertex2d(-250, y);
			gl.glVertex2d(250, y);
		}

		gl.glEnd();

		//////////////////////////////
		// draw the x-axis and y-axis
		red = 0.0f;
		green = 0.2f;
		blue = 0.4f;

		gl.glColor3f(red, green, blue);

		gl.glBegin(GL.GL_LINES);

		//line for y-axis
		gl.glVertex2d(0, 140);
		gl.glVertex2d(0, -140);

		//line for x-axis
		gl.glVertex2d(240, 0);
		gl.glVertex2d(-240, 0);

		gl.glEnd();

		/////////////////////
		// draw arrow heads
		gl.glBegin(GL.GL_TRIANGLES);

		gl.glVertex2d( 0, 150);
		gl.glVertex2d(-5, 140);
		gl.glVertex2d( 5, 140);

		gl.glVertex2d( 0, -150);
		gl.glVertex2d(-5, -140);
		gl.glVertex2d( 5, -140);

		gl.glVertex2d(250, 0);
		gl.glVertex2d(240,-5);
		gl.glVertex2d(240, 5);

		gl.glVertex2d(-250, 0);
		gl.glVertex2d(-240,-5);
		gl.glVertex2d(-240, 5);

		gl.glEnd();
	}
}

 

 

If you were paying attention, you should have noticed that LineGraphApp implements ActionListener. This is to handle input from the button. You can receive events from the GLCanvas too, but we’ll be discussing that in detail in another chapter. For now we’ll use the more traditional button clicks.

The big change is in the display() method in the LineGLEventListener class. We literally draw the line point by point. We could have drawn it as one line instead of individual points, but where’s the fun in that?

glVertex2d() draws the points, but only when it is surrounded by glBegin(GL.GL_POINTS) and a glEnd(). glPointSize(4.0f) is used to make the selected point’s size large enough to be seen. We also change the color of the selected point to white. White is red of value 1.0f, green of value 1.0f and blue of value 1.0f.

Again, read the code and type it in and compile it. With any luck you will type it wrong and have to track down a bug or two. You’ll never learn so much as when you’re tracking down bugs.

Sine, Cosine and Tangents

One thing I could never figure out a real use for in High School, was Trig. Wow. I hated trig. Then I started doing computer graphics and realized something. Sine and cosine and all that have useful applications.

Here we have a program that displays graphed values of sine, cosine and tangent. I’ve warped them a bit to make them look nicer, but this should be a good review of the Math class. You’ll also see the graph function again.

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.games.jogl.*;


/**
 * This is a basic JOGL app. Feel free to
 * reuse this code or modify it.
 */
public class TrigGraphApp
   extends JFrame implements ActionListener
{
	//Notice we've given these two objects a larger scope.
	//Local scope to the constructor was no longer sufficient.
	TrigGLEventListener listener = new TrigGLEventListener();
	GLCanvas glcanvas;


    public static void main(String[] args) {
        final TrigGraphApp app = new TrigGraphApp();

        // show what we've done
        SwingUtilities.invokeLater (
            new Runnable() {
                public void run() {
                    app.setVisible(true);
                }
            }
        );
    }

    public TrigGraphApp() {
        //set the JFrame title
        super("Sine, Cosine and Tangent");

        //kill the process when the JFrame is closed
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Setting up our southern JPanel with JRadioButtons
        JPanel jp = new JPanel();
        ButtonGroup bg = new ButtonGroup();

        //setting up the sine JRadioButton
        JRadioButton jrb1 = new JRadioButton("Sine", true);
        jrb1.setActionCommand("sine");
        jrb1.addActionListener(this);

        //setting up the cosine JRadioButton
        JRadioButton jrb2 = new JRadioButton("Cosine");
        jrb2.setActionCommand("cosine");
        jrb2.addActionListener(this);

        //setting up the tangent JRadioButton
        JRadioButton jrb3 = new JRadioButton("Tangent");
        jrb3.setActionCommand("tangent");
        jrb3.addActionListener(this);

        //adding the buttons to the ButtonGroup
        bg.add( jrb1 );
        bg.add( jrb2 );
        bg.add( jrb3 );

        //adding the buttons to the JPanel
        jp.add( jrb1 );
        jp.add( jrb2 );
        jp.add( jrb3 );

        getContentPane().add("South", jp);

        //only three JOGL lines of code ... and here they are
        GLCapabilities glcaps = new GLCapabilities();

        glcanvas =
        GLDrawableFactory.getFactory().createGLCanvas(glcaps);

        glcanvas.addGLEventListener(listener);

        //add the GLCanvas just like we would any Component
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        //center the JFrame on the screen
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize =
           Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize  = frame.getSize();

        if (frameSize.width  > screenSize.width )
           frameSize.width  = screenSize.width;
        if (frameSize.height > screenSize.height)
           frameSize.height = screenSize.height;

        frame.setLocation (
            (screenSize.width  - frameSize.width ) >> 1,
            (screenSize.height - frameSize.height) >> 1
        );
    }

	/**
	 * Implementation of our ActionListener. This allows the
	 * buttons to perform an action. In this case they set
	 * the "whatToDraw" String and ask for a repaint of the
	 * GLCanvas.
	 */
	public void actionPerformed(ActionEvent ae) {
		listener.whatToDraw = ae.getActionCommand();
		glcanvas.repaint();
	}
}

 

 

The GLEventListener ….

 

import java.awt.*;
import java.awt.event.*;
import net.java.games.jogl.*;

/**
 * For our purposes only two of the GLEventListeners matter. Those would be
 * init() and display().
 */
public class TrigGLEventListener implements GLEventListener {

	public String whatToDraw = "sine";

	///////////////////////////////////////////////////
	// GLEventListener implementation
	//

    /**
     * Take care of initialization here.
     */
    public void init(GLDrawable gld) {
        GL gl = gld.getGL();
		GLU glu = gld.getGLU();

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

		gl.glLineWidth(2.0f);
		gl.glPointSize(2.0f);

		gl.glViewport(-250, -150, 250, 150);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluOrtho2D(-250.0, 250.0, -150.0, 150.0);
    }

    /**
     * Take care of drawing here.
     */
    public void display(GLDrawable gld) {
		GL gl = gld.getGL();
        drawGraph( gl );

		//This is the new code. We find out
		//which trig function is selected,
		//then we draw a scaled up version of
		//the function.

		float red;
		float green;
		float blue;

		////////////////////
		//drawing the grid
		red = 1.0f;
		green = 0.2f;
		blue = 0.2f;

		gl.glColor3f(red, green, blue);

		gl.glBegin(GL.GL_POINTS);

		if (whatToDraw.equals("sine")) {
			//draw enlarged sine function
			for (int x=-250; x<250;x++)
				gl.glVertex2d(x, (Math.sin(x/60.0)*100.0));
		} else if (whatToDraw.equals("cosine")) {
			//draw enlarged cosine function
			for (int x=-250; x<250;x++)
				gl.glVertex2d(x, (Math.cos(x/60.0)*100.0));
		} else if (whatToDraw.equals("tangent")) {
			//draw enlarged tangent function
			for (int x=-250; x<250;x++)
				gl.glVertex2d(x, (Math.tan(x/60.0)*30.0));
		}

		gl.glEnd();


    }


    public void reshape(
                        GLDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height
                      ) {}
     public void displayChanged(
                              GLDrawable drawable,
                              boolean modeChanged,
                              boolean deviceChanged
                            ) {}


      ///////////////////////////////////////////////////
	// Other methods
	//

	/**
	 * In here we draw a Cartesian Coordinate System.
	 */
	private void drawGraph(GL gl) {
		float red;
		float green;
		float blue;

		gl.glClear(GL.GL_COLOR_BUFFER_BIT);

		////////////////////
		//drawing the grid
		red = 0.2f;
		green = 0.2f;
		blue = 0.2f;

		gl.glColor3f(red, green, blue);

		//You may notice I'm using GL_LINES here.
		//Details of glBegin() will be discussed latter.
		gl.glBegin(GL.GL_LINES);

		//draw the vertical lines
		for (int x=-250; x<=250; x+=10) {
			gl.glVertex2d(x, -150);
			gl.glVertex2d(x, 150);
		}

		//draw the horizontal lines
		for (int y=-150; y<=150; y+=10) {
			gl.glVertex2d(-250, y);
			gl.glVertex2d(250, y);
		}

		gl.glEnd();

		//////////////////////////////
		// draw the x-axis and y-axis
		red = 0.0f;
		green = 0.2f;
		blue = 0.4f;

		gl.glColor3f(red, green, blue);

		gl.glBegin(GL.GL_LINES);

		//line for y-axis
		gl.glVertex2d(0, 140);
		gl.glVertex2d(0, -140);

		//line for x-axis
		gl.glVertex2d(240, 0);
		gl.glVertex2d(-240, 0);

		gl.glEnd();

		/////////////////////
		// draw arrow heads
		gl.glBegin(GL.GL_TRIANGLES);

		gl.glVertex2d( 0, 150);
		gl.glVertex2d(-5, 140);
		gl.glVertex2d( 5, 140);

		gl.glVertex2d( 0, -150);
		gl.glVertex2d(-5, -140);
		gl.glVertex2d( 5, -140);

		gl.glVertex2d(250, 0);
		gl.glVertex2d(240,-5);
		gl.glVertex2d(240, 5);

		gl.glVertex2d(-250, 0);
		gl.glVertex2d(-240,-5);
		gl.glVertex2d(-240, 5);

		gl.glEnd();

	}

}

 

 

This is what it should look like:

 

ch2_trig

Circles By Hand

Probably the first real intensive piece of graphics I did professionally in Java was a dial or a pie chart. I groaned and wished I had not slept through all those classes. I pulled it off, my boss liked it and I didn’t get fired. There’s your happily-ever-after for the day.

So here is the secret to making circles. There will be a quiz on this later.

You can find ANY point on the edge of a circle by using the radius measurement and the angle of the point on the circle. If you remember, there are 360 degrees in a circle. The diagram below should serve as a reminder as to where some of the angles are located on the edge of a circle.

ch2_circle_degrees

 

The radius is half the distance across the largest part of the circle. In other words, if you start from the center of the circle and draw a line out to the edge, you’ve drawn the radius of the circle.

Suppose you decide you want to draw a circle with radius 15 and 360 degrees. (Yes, circles on are defined as having 360 degrees.) We now need to know the x and y coordinates of each of those 360 degrees. Do a for loop that uses this formula:

x = radius * (cosine of angle measured in radians)

and

y = radius * (sine of angle measured in radians)

Hold up! What’s a radian? Gotcha.

We’re pretty much going to ignore radians, but here is how to define one degree in radians.

 

final double ONE_DEGREE = (Math.PI/180);

 

 

Likewise, 360 degrees would be,

 

final double THREE_SIXTY = 2 * Math.PI;

 

 

Drawing a circle using JOGL looks like this.

 

gl.glBegin(GL.GL_POLYGON);
for (double a=0; a<THREE_SIXTY; a+=ONE_DEGREE) {
   x = radius * (Math.cos(a)) + shiftXPosition;
   y = radius * (Math.sin(a)) + shiftYPosition;
   gl.glVertex2d(x, y);
}
gl.glEnd();

 

 

Some readers may recognize this as the formula for converting from polar to rectangular coordinates. Good eye. We’re just using it for circle creation.

That’s about all that is new with the next and final example for this chapter. Again, we’ll start by creating the JFrame and placing a GLCanvas in it….

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.games.jogl.*;


/**
 * This is a basic JOGL app. Feel free to
 * reuse this code or modify it.
 */
public class FirstCircle extends JFrame {

    public static void main(String[] args) {
        final FirstCircle app = new FirstCircle();

        // show what we've done
        SwingUtilities.invokeLater (
            new Runnable() {
                public void run() {
                    app.setVisible(true);
                }
            }
        );
    }

    public FirstCircle() {
        //set the JFrame title
        super("First Circle Using Sine and Cosine");

         //kill the process when the JFrame is closed
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //only three JOGL lines of code ... and here they are
        GLCapabilities glcaps = new GLCapabilities();

        GLCanvas glcanvas =
        GLDrawableFactory.getFactory().createGLCanvas(glcaps);

        glcanvas.addGLEventListener(
           new FirstCircleEventListener()
        );

        //add the GLCanvas just like we would any Component
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        //center the JFrame on the screen
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize =
           Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize  = frame.getSize();

        if (frameSize.width  > screenSize.width )
           frameSize.width  = screenSize.width;
        if (frameSize.height > screenSize.height)
           frameSize.height = screenSize.height;

        frame.setLocation (
            (screenSize.width  - frameSize.width ) >> 1,
            (screenSize.height - frameSize.height) >> 1
        );
    }
}

 

 

Now let’s display the circle using a GLEventListener – FirstCircleEventListener in this case.

 

import net.java.games.jogl.*;

/**
 * We make the center of the GLCanvas the origin of our graph
 * and construct a circle around it using sine and cosine
 * methods from the Math class.
 *
 * We have ignored insets in this and other examples, so this
 * circle may be slightly more of an oval depending on the OS.
 */
public class FirstCircleEventListener
  implements GLEventListener {

    final double ONE_DEGREE = (Math.PI/180);
    final double THREE_SIXTY = 2 * Math.PI;

    /**
     * Take care of initialization here.
     */
    public void init(GLDrawable gld) {
        GL gl = gld.getGL();
        GLU glu = gld.getGLU();

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

        gl.glViewport(-250, -150, 250, 150);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluOrtho2D(-250.0, 250.0, -150.0, 150.0);
    }

    /**
     * Take care of drawing here.
     */
    public void display(GLDrawable drawable) {
        double x,y;
        double radius = 100;

        float red = 0.5f;
        float green = 0.0f;
        float blue = 0.5f;

        GL gl = drawable.getGL();

        gl.glClear(GL.GL_COLOR_BUFFER_BIT);

        gl.glColor3f(red, green, blue);

        gl.glBegin(GL.GL_POLYGON);

        // angle is
        // x = radius * (cosine of angle)
        // y = radius * (sine of angle)
        for (double a=0; a<THREE_SIXTY; a+=ONE_DEGREE) {
            x = radius * (Math.cos(a));
            y = radius * (Math.sin(a));
            gl.glVertex2d(x, y);
        }
        gl.glEnd();
    }

  public void reshape(
                        GLDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height
                      ) {}

  public void displayChanged(
                              GLDrawable drawable,
                              boolean modeChanged,
                              boolean deviceChanged
                            ) {}
}

 

 

It is important to note that we ignore insets of this JFrame, so the circle may appear slightly oval. Other than that, we have just made a perfectly acceptable circle.

ch2_first_circle

 

I’m sure wheels are already turning in your mind thinking about some of the things you can do with the JOGL knowledge you’ve gained so far. But wait! There’s more! The whole next chapter is dedicated to looking with more detail at some simple real world examples you can already do with what we’ve discussed in this chapter. We’ll also take a look at animation.

Purchase Printed Book Learning Java Bindings for OpenGL (JOGL)

 

Table of Contents: Learning Java Bindings for OpenGL (JOGL)