Staging Area battis.net

SimplerJOGL is a wrapper for the JOGL API, designed for use in a high school classroom. The wrapper shields new programmers from some of the intricacies of OpenGL and JOGL (and Swing and AWT), while providing an entry point for delving deeper into all these subjects by exploring the wrapper itself.

This was originally written as a group of C++ classes when I was teaching at the Baylor School. I have since ported the idea (if not the exact specifics) of those classes to Java for my classes at St. Mark’s. This past semester, I started using SimplerJOGL by introducing the Logo-esque SimplerJOGL Drawing environment.

November 23rd, 2012

Posted In: Projects

Tags: , , , ,

There are three basic transformations in OpenGL:

  • Scale (changing the size of an object)
  • Translate (moving an object to another location relative to where it started)
  • Rotate (rotating an object around a particular axis through the origin of the model)

The actual method calls for each of these transformations is fairly straightforward:

gl.glScaled(double xScale, double yScale, double zScale);
gl.glTranslated(double xTranslation, double yTranslation, double zTranslation);
gl.glRotated(double degrees, double x, double y, double z);

It is worth mentioning that in each of these method calls, the method ends in a D not because it is in the past tense, but because there are several variations of each of these methods, and the one we will use expects doubles as parameters (as opposed to glRotatef, which expects floats). Presumably glScaled() and glTranslated() are fairly self-evident: the object is either stretched by a particular factor or shifted by a particular factor along each of the axes — and it could be a different factor for each axis.

glRotated() requires further explanation. glRotated() operates on what is colloquially referred to as the “Right Hand Rule”.

http://www.flickr.com/photos/globetrotter1937/1557776175/

Imagine that your hand is placed with the base of the palm at the origin. The (x, y, z) coordinates specified by glRotated() are at the tip of your thumb. The line from the origin (your palm) to that point (your thumb) defines the axis of rotation — the pole about which the rotation will be performed — nota bene that the axis of rotation will always pass through the origin. Your fingers then define the direction of rotation around that axis. And the number of degrees is, well, the number of degrees that you will rotate the object.

The order of transformations is critical. Applying transformations in different orders will have noticeably different effects. The best way to think of the situation that the objects being modeled are passing through a filter, the transformation matrix. Each time a new transformation is applied, the filter is adjusted slightly. A rotation followed by a translation will first rotate the object and then translate it into a new position. A translation followed by a rotation will first move the object into its new position and then rotate the object — from its new position — around the axis of rotation at the origin — causing a planet-like orbit about the origin.

Because this filter is adjusted by each transformation applied, all transformations apply to any objects drawn after the transformations apply — the objects always pass through the transformation matrix on their way to being drawn on-screen, and each transformation we apply adjusts the transformation matrix. There is only one transformation matrix. All objects pass through it. The only question is: when do they pass through it? Before or after a particular adjustment?

gl.glRotated(90, 0, 1, 0); // rotate 90 degrees around the positive y-axis
glut.glutWireTeapot(1.0); // the teapot is rotated
gl.glTranslated(10, 0, 0); // translate 10 units along the positive x-axis
glut.glutWireTetrahedron; // the tetrahedron is translated and then rotated
gl.glScaled(1, 1, 3); // scale, leaving x- and y-axes unchanged, but triple z
glut.glutWireIcosahedron(); // the icosahedron is scaled, translated and then rotated

Note that something strange happens to the order of the transformations — the objects are passed through the most recent transformations first, moving backward in through all of the transformations applied. A quick way to figure out what order the transformations will be applied to a particular object is start at that object and run a finger up the lines of code. The order in which the transformations are encountered is the order in which they will be applied to the object.

Suppose we don’t want every transformation to apply to every object? It would be nice to apply a transformation and then unapply it. We can do this: we save the transformation matrix in its pre-alteration state, apply a new transformation (or two or three…), draw our object, and then revert back to the version that we saved earlier.

gl.glPushMatrix(); // save current state of transformation matrix
{
    gl.glRotated(90, 0, 1, 0); // rotate 90 degrees around the positive y-axis
    glut.glutWireTeapot(1.0); // the teapot is rotated
}
gl.glPopMatrix(); // revert to saved version of transformation matrix
 
gl.glPushMatrix(); // save transformation matrix (again)
{
    gl.glTranslated(10, 0, 0); // translate 10 units along the positive x-axis
    glut.glutWireTetrahedron; // the tetrahedron is ONLY translated
}
gl.glPopMatrix(); // revert to saved (again)
 
gl.glPushMatrix(); // save the transformation matrix a last time
{
    gl.glScaled(1, 1, 3); // scale, leaving x- and y-axes unchanged, but triple z
    glut.glutWireIcosahedron(); // the icosahedron is ONLY scaled
}
gl.glPopMatrix(); // reverto saved a last time
 
// anything that follows will be unaffected by our transformations!

January 5th, 2011

Posted In: Handouts

Tags: , , ,

Each OpenGL primitive (well, each OpenGL GLUT primitive) can be drawn either as a solid (filled) shape or as a wireframe shape. The two method calls are identical in parameters by differ in their names (as listed below). Each of the methods listed below is a method of the GLUT object, so a primitive would be drawn as a method call of that object:

glut.glutSolidCube(1.0);
glutSolidCube(float sideLength)
glutWireCube(float sideLength)

Usually a cube is drawn with unit (1) side length and then scaled to the size desired. This make the cube ideal as the “base shape” for any rectangular shape (since the scaling can be different along each of the x-, y-, and z-axes).

glutSolidSphere(float radius,
    int slices, int stacks)
glutWireSphere(float radius,
    int slices, int stacks)

Spheres are not actually spherical, they are approximated with polyhedrons. The slices parameter defines how many slices (like orange wedges — the longitude lines) the sphere is broken into, while stacks defines how many latitude divisions (rings) the sphere is divided into. Obviously more slices and stacks results in a polyhedron more closely approximating a sphere… but also in a larger number of polygons and thus greater load on the computer. (And, remember, the number of polygons is equal to slices × stacks: 10 slices and 10 stacks gets you 100 polygons.)

glutSolidCylinder(float radius, float length,
    int slices, int stacks)
glutWireCylinder(float radius, float length,
    int slices, int stacks)

The cylinder is (surprisingly, at least to this author) drawn along the z-axis — that is, with the eye staring down the barrel of it from a “normal” default viewpoint). It too is divided into slices and stacks, and in the same manner. Often one needs only a single stack (thereby allowing for the maximizing of slices and thus cylindricality).

glutSolidCone(float radius, float height,
    int slices, int stacks)
glutWireCone(float radius, float height,
    int slices, int stacks)

The cone too is drawn along the z-axis (like the cylinder). It too is broken into slices and stacks.

glutSolidTorus(float innerRadius, float outerRadius,
    int sides, int rings)
glutWireTorus(float innerRadius, float outerRadius,
    int sides, int rings)

The torus has two radii because the inner radius is the radius of the rings circling the “donut”, while the outer radius is the radius of the donut itself. The number of sides refers to the number of sides on the inner radius, while the number of rings is the number of inner radius-sized rings.

glutSolidTeapot(float scale)
glutWireTeapot(float scale)

It pretty much does what it says. Why a teapot? This is no ordinary teapot: this is the Utah Teapot.

Also available:

glutSolidIcosahedron()
glutWireIcosahedron()
 
glutSolidOctahedron()
glutWireOctahedron()
 
glutSolidTetrahedron()
glutWireTetrahedron()
 
glutSolidDodecahedron()
glutWireDodecahedron()

These all do pretty much what they sound like.

January 5th, 2011

Posted In: Handouts

Tags: , , ,

Question 1

October 27th, 2010

Posted In: Handouts

Tags: ,

Below are the code fragments referenced by the reading quiz.

Walker

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
// This class represents a walker with two feet.
 
import java.awt.Image;
import java.awt.Graphics;
 
public class Walker
{
	public static final int PIXELS_PER_INCH = 6;
	private Foot leftFoot, rightFoot;
	private int stepLength;
	private int stepsCount;
 
	// Constructor
	public Walker (int x, int y, Image leftPic, Image rightPic)
	{ . . . }
 
	// Returns the left foot
	public Foot getLeftFoot ()
	{ . . . }
 
	// Returns the right foot
	public Foot getRightFoot ()
	{ . . . }
 
	// Makes first step, starting with the left foot
	public void firstStep ()
	{ . . . }
 
	// Makes next step
	public void nextStep ()
	{ . . . }
 
	// Stops this walker (brings its feet together)
	public void stop ()
	{ . . . }
 
	// Returns the distance walked
	public int distanceTraveled ()
	{ . . . }
 
	// Draws this walker
	public void draw (Graphics g)
	{ . . . }
}

Pacer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// A subclass of Walker that adds the turnAround method.
 
import java.awt.Image;
 
public class Pacer extends Walker
{
	// Constructor
	public Pacer (int x, int y, Image leftPic, Image rightPic)
	{ . . . }
 
	// Turns this Pacer 180 degrees
	// Precondition: the left and right feet are side by side
	public void turnAround ()
	{ . . . }
 
	public void turnRight ()
	{ . . . }
 
	public void turnLeft ()
	{ . . . }
}

PacerTest

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
. . . imports omitted to save space . . .
 
public class PacerTest extends JPanel
{
	// Called automatically when the panel needs repainting
	public void paintComponent (Graphics g)
	{
		super.paintComponent (g);
 
		int x = 100;
		int y = 300;
 
		Pacer p = new Pacer (x, y, lshoe, rshoe);
 
		p.draw (g);
		p.firstStep ();
		p.nextStep ();
		p.stop ();
		p.turnLeft ();
		p.draw (g);
		p.firstStep ();
		p.nextStep ();
		p.stop ();
		p.turnRight ();
		p.draw (g);
 
		// Draw a cursor at the expected center of the first "shoe":
		g.drawLine (x - 50, y, x + 50, y);
		g.drawLine (x, y - 50, x, y + 50);
	}
 
	. . . other methods omitted to save space . . .
}

October 22nd, 2010

Posted In: Handouts

Tags: ,

css.php