A detailed look at implementing graphically demanding games in OpenGL with Java, using the JOGL library

Tuesday, December 23, 2008

MultiTexturing and shaders

Textures are nice and all, but one at a time is a bit boring. There are lots of situations that call for multiple textures on a triangle, or just having multiple textures available on separate units to reduce the amount of swapping.
There are many multitexturing effects you can achieve with the fixed function pipeline, but things start getting really cool when you add shaders. These are programs that take over the tasks of transforming vertices and calculating pixel colours in arbitrary ways, allowing techniques such as bump mapping, environment mapping, blurs, bloom and more. Shaders and multitexturing go hand in hand so I'll tackle them both in one post. Source is available as Tutorial 4

Following on from the simple texturing tutorial, we'll make some minor changes. Starting at the beginning:
public void init(GL gl) {
GLHelper glh = new GLHelper();
try {
shader = glh.LoadShaderProgram(gl, "/tutorial4/vertex.shader", "/tutorial4/fragment.shader");
} catch (IOException ex) {
Logger.getLogger(TutorialObject.class.getName()).log(Level.SEVERE, null, ex);
} catch (GLHelperException ex) {
Logger.getLogger(TutorialObject.class.getName()).log(Level.SEVERE, null, ex);
}
gl.glGenTextures(2, textures, 0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
ByteBuffer b = genTexture(32);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, 32, 32, 0, GL.GL_RGB, GL.GL_BYTE, b);

gl.glBindTexture(GL.GL_TEXTURE_2D, textures[1]);
b = genTexture(32);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, 32, 32, 0, GL.GL_RGB, GL.GL_BYTE, b);
}
GLHelper is a utility class to hide some of the complexity of loading a shader. Then we ask for 2 texture handles. We then bind them in turn and fill them with data. This all goes on in texture unit zero, as we don't need to render them in parallel yet.

Then when we draw -

public void display(GL gl, float time) {
int[] i = new int[1];
gl.glGetIntegerv(GL.GL_CURRENT_PROGRAM, i, 0);
if (i[0] != shader) {
gl.glUseProgram(shader);
}

gl.glEnable(GL.GL_TEXTURE_2D);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[1]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

gl.glUniform2f(gl.glGetUniformLocation(shader, "offset"), time, time);
gl.glUniform1i(gl.glGetUniformLocation(shader, "tex1"), 0);
gl.glUniform1i(gl.glGetUniformLocation(shader, "tex2"), 1);
gl.glUniform1f(gl.glGetUniformLocation(shader, "r"), time);

gl.glBegin(GL.GL_TRIANGLES);
{
gl.glVertex3f(0, 1, -3);
gl.glVertex3f(-1, -1, -3);
gl.glVertex3f(1, -1, -3);
}
gl.glEnd();
}
The first block checks to see if the shader program is active, and if not, activates it.

The second block enables texturing and binds the textures to the first two units. It also sets up bilinear filtering.

The third block sets some variables that are used by the shaders themselves. These are all uniform variables - they cannot be changed between a glBegin/glEnd pair, but there are also attribute variables that can be applied per vertex. Lighthouse 3d has some good background info.

The actual drawing is actually simpler than the last tutorial - just three vertices. That's possible because the vertex shader calculates the texture coordinates from the vertex coordinates, so they don't have to be specified. Obviously this won't work for every situation, but it's a good example of how the whole fixed function pipeline has been replaced. Those shaders in full:

Vertex:
uniform float r;

void main(void)
{
vec4 v = gl_Vertex;
v.y += 0.3*cos(r+v.x);
gl_Position = gl_ModelViewProjectionMatrix * v;
gl_TexCoord[0].xy = gl_Vertex.xy;
gl_TexCoord[1].xy = gl_Vertex.xy;
}
Fragment:
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform vec2 offset;
void main(void)
{
vec4 c;
c = texture2D(tex1, gl_TexCoord[0].st);
c+= texture2D(tex2, offset+gl_TexCoord[1].st);
gl_FragColor = c / 2.0 ;
}
The vertex shader applies a cosine wave to the y coordinate of each vertex - and the fragment shader blends two textures and applies an offset. The overall effect is a wavy, shimmering triangle. You'll have to run this one to see it in motion, I think!

Texture mapping

Sooner or later everyone gets bored of plain coloured triangles. Texturing in OpenGL is easy enough once you've mastered a few concepts:
  • Texture names
  • Filtering
This post (and the accompanying source code) also builds slightly on the previous tutorial - the rendering we're interested in is now in TutorialObject.java. The Canvas object holds a list of all objects and calls each one to render itself.

Texture names are actually numbers. The numbers are generated by glGenTextures and returned into an array like so:
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
The numbers returned in textures[] are effectively handles to textures. At the moment, they are not attached to any data - they are not even declared as 1 or 2 dimensional. To attach actual data, we must bind the texture:
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
then we can load our image data in with
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, 32, 32, 0, GL.GL_RGB, GL.GL_BYTE, b);
Hang on a minute, where do those parameters come from? The full details are at the OpenGL man pages, but the key ones are the 32s and b. This means we are defining a 32 square texture, and the data defining it is in the buffer b. In this example, b is just full of random data, so the texture looks like disco lights.

If we tried to render a triangle now, it still wouldn't be textured. We need to enable texturing and choose a filtering mode. So at the beginning of our object rendering code, we make these calls:
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
Note that I re-bind the texture. This is because in a realistic program, different objects will need different textures and you won't know which texture is active at the beginning of an object's display method.
Now we can render vertices and expect to see our texture.

gl.glBegin(GL.GL_TRIANGLES);
{
gl.glTexCoord2f(0.0f,0.0f);
gl.glVertex3f(0.0f, 1.0f, -3.0f);

gl.glTexCoord2f(1.0f,0.0f);
gl.glVertex3f(-1.0f, -1.0f, -3.0f);

gl.glTexCoord2f(0.0f,1.0f);
gl.glVertex3f(1.0f, -1.0f, -3.0f);
}
gl.glEnd();
We need to pass the texture coordinates before the vertex coordinates - when a vertex is defined it picks up the last specified texture coordinates, color, normal etc.

Friday, December 19, 2008

Basic OpenGL set up

So you have a skeleton application compiling and running, you've got a subversion repository to commit your code to, and you're ready to start rendering some 3D graphics to see what your game could look like. Great! Let's start at the beginning.

The init method

The first opportunity to execute any OpenGL calls is your canvas's init method (technical note - the method is actually an implementation of the GLEventListener interface, not the GLCanvas class). This is called when the underlying OpenGL context is created (and could conceivably be called again if the native context was lost and re-created, but it's generally OK to consider it a one-time deal). You can use this method to set up anything that will stay constant over the lifetime of the application. Tutorial 2 calls gl.setSwapInterval(1) which makes GL wait for the vertical blank interval before swapping the front and back buffers. This reduces shearing and allows for silky-smooth animation, and also prevents your game from trying to render frames more frequently than the monitor can physically display them.
init is also a good place to load textures, set up display lists and vertex arrays, and set global lighting parameters like the ambient light level.

The reshape method

The second opportunity to execute GL calls is in reshape. This is called whenever the viewport changes, and once before the first call to display. This is where you set up the viewport and the GL_PERSPECTIVE matrix, and it's as good a place as any to set the default GL_MODELVIEW matrix. Combined, these three settings are like defining the camera in your scene, but it can do things that no physical camera can.

The viewport is the 2d window that OpenGL will draw to. display is called with height and width parameters, and in most cases I can think of these are just passed straight through to glViewport. The only exception I can think of to that is if you are managing multiple views in the same context, and you wish to render to several different viewports. However I would look into multiple GLCanvas instances first.

On to the matrices. Despite the name, GL_PERSPECTIVE doesn't have to be a perspective transformation at all. Though a perspective view makes sense in a lot of games, if you're planning an RTS or other top-down view, or even a totally 2D game (OpenGL turns out to be great for 2d too -), then an orthogonal transform is what you want. There are two helper functions to create matrices, as otherwise the math can get tricky:
  • glFrustum. A Frustum is a shape like an Incan pyramid, and defines the viewing volume for a perspective view, i.e. things further away are smaller. Suitable for first person shooters, many third person games, and anything where realism is key. 
  • glOrtho. Ortho is short for Orthogonal, meaning that vectors that were at right angles before the tranformation will still be at right angles afterwards. Take a moment to think why perspective transforms are not orthogonal. This function sets up the camera for a parallel projection, i.e. things further do not get smaller. This is handy for 2d games, or top down god games, RTSs and surprisingly more gameplay styles.
Both functions produce a matrix that defines the viewing volume. This is the volume of space that is visible to the camera. Note that it has a front and back - like a real camera, if something is too close to the lens you can't see it very well, and unlike a real camera there is a maximum distance beyond which OpenGL will ignore objects. There is a problem with setting the near plane to 0.00001 and the far plane to 9999999 though - depth buffer precision. Depth buffering is a fantastically useful technique we'll cover in detail later, but for now let's just note that we don't want the ratio of near:far to get too large, or you'll see rendering artifacts.

glFrustum takes six parameters. The first five define the position of the front face of the viewing volume - left, right, bottom, top and near. The sixth sets the far distance. The shape of the front face should be the same as the shape of your viewport - if the user drags out a tall, skinny window, then to prevent distorting your graphics, the view volume should be tall and skinny too, like in the following code:
float aspect = (1.0f) * height / width;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustum(.5f, -.5f, -.5f * aspect, .5f * aspect, 1.f, 500.f);
This sets up a pretty standard perspective view, where a square of side 1 would fill the screen if it was centered one unit in front of the camera - as long as the screen is square.

glOrtho takes six parameters too - in fact, the same ones. But the near face of this viewing volume will be the same size as the far face - so it probably needs to be much larger to be able the view the same objects - e.g:
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(-width / 64.0f, width / 64.0f, -height / 64.0f, height / 64.0f, 1, 100);
This view would show each unit square as a square 64 pixels across, no matter how large the screen is. Resizing the window would show more of the world, rather than scaling up the same view. It's the camera matrix that JavaPop uses.

Now as for GL_MODELVIEW, all we want to do is set it to the identity matrix, to make sure it's initialised:
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
Now the OpenGL context is set up and ready for you to draw your geometry in the display method. We'll get to that very soon.

Image editor

Most games need some graphics. Unless you plan to draw absolutely every element in your game as flat shaded 3d primitives, you'll need textures, custom mouse cursors, and who knows what else.

The best paint package I've ever used was Cloanto's Personal Paint. But that was on the Amiga more than a decade ago, so unless you have one of those lying around (or UAE) there are a few more modern options.
  1. Photoshop. The grand daddy of image editors, but expensive and burdened with what I find to be too many features. But then again maybe I just sucjk at photoshop...
  2. The Gimp. Apart from the scary name that got me a few funny looks from my wife when she saw it on my desktop, a pretty good editor for programmers.
  3. Online editors. These are getting better and better. No installation, so if you find yourself at the in-laws over thanksgiving or Christmas you can still knock out a few icons or textures. I like pixlr and splashup
I would recommend spending only as much time drawing your graphics as you find fun. It's really easy to go back and improve them - and it's also really easy to change the design or display method of your game so that it needs different graphics, so don't commit too much time up front. For this reason I recommend starting with pixlr or splashup just to creat some stand in graphics, then revisiting then later with the Gimp. If a guy in a suit with a briefcase full of cash turns up and wants to publish your opus, then it's time to splash out on photoshop.

In any case, your goal will be to produce, in most cases, a 24-bit .png file with an 8-bit alpha channel. This is a really flexible format, well supported by both image editors and java. That's why PNG stands for Portable Network Graphics!

Wednesday, December 17, 2008

Version control

So, you've got Netbeans set up and you're ready to get coding. Hold those horses! Consider setting up some version control first.

Version control means saving your code (and images, models, and everything else) in a database. If you drop your hard drive or your PC gets hit by lightning and you're using remote version control, then no drama - just get a new PC, check out the latest version of your code and carry on. Or if you delete a big chunk of code you don't need any more, then remember that little bit in the middle that took hours to get right - no problem, just step back to an older revision and look at last months code.

Version control used to be a bit tricky, requiring a local install of cvs and some serious unix skills to keep it running, but these days several companies offer free, simple subversion services. Chief among these are Sourceforge and Google Code - both are open source. which means when you save your work there, the rest of the world can see, learn from and use that code. If you're uneasy about that, spend some time here then come back.

I'm going to go through the steps required to get your project hosted on Google Code. Not only is this really useful for development, but it can also act as a community hub and download site for your game when it's released!

Go to http://code.google.com/hosting/ and click on the Create a new project link. Type in your projects name (it will have to be unique across the google code site) and a brief description and click Create Project - and guess what - you're done. I said it was simple. Click on the Settings link in the top right and make a note of your googlecode password. This isn't your normal google password, it's your passport to the subversion repository.

Now to import your work into the new repository. There's a decision to be made here, unfortunately. By convention, svn repositories are laid out a certain way, but Netbeans rides roughshod over that and just plunks the whole project directory into the root. I recommend a middle ground, laying out the repository like so:
  • /svn (root)
    • branches
    • tags
    • trunk
      • Project1
        • src, test, etc
      • Project2
        • src, test, etc
This will work just fine and let you store multiple projects in one google code project. Have a look at the code for this blog at http://code.google.com/p/javagamedev/source/browse/ to get the idea. I've put a project in there called Tutorial 0 which is simply the default Netbeans project, in the right place. Follow the checkout instructions at the end of this post to get it - though you'll only be able to use http, not https.

Go back to NetBeans, right click on your project in the project tree, and choose Versioning->Import into Subversion Repository...
In the space for the repository URL, type "https://myprojectname.googlecode.com/svn", type your google account email address in the username field and in the password field, enter the googlecode password on your settings page from earlier.
On the next page, change the directory from "myproject" to "trunk/myproject" and enter a simple message - something like "New project" is plenty. Now hit finish and the project will be imported into subversion.

If you want to work on the project on a different computer, install netbeans there too and use the Versioning->Subversion->Checkout command. Enter the repository, user name and password info as above (remembering httpS - or you won't be able to write back your changes) and hit next. On the next screen, use the browse button to select the project in trunk/ or just type trunk/projectname, and select your net beans project dir as the local directory.
Leave the skip... tickbox unticked, and the scan... tickbox checked, and hit finish. Netbeans will check out the project and ask if you want to open the project it found - answer yes and hey presto, we're there.

Now we can go coding off into the sunset without worrying about losing our data.

Fullscreen or windowed?

Quick answer - both! I think that Java provides the most transparent way of handling fullscreen graphics that I've come across. Whether we're fullscreen or windowed, we need to set up an application window (JFrame) and put an OpenGL canvas (GLCanvas) in it. To go fullscreen, we use the GraphicsDevice class, which can be used with OpenGL or without (just in case you want a fullscreen accounting package?)

There are a few steps to turn your app into a fullscreen one:
  1. Remove decorations from your window. If you don't do this, you'll still see a title bar, and users will be able to move the window about on a black background. Not very pro.
  2. Set the full screen window to your JFrame.
  3. Choose a display mode - ideally you'll provide a way for the user to choose this, but to start with there's nothing wrong with hard-coding a resolution you know your machine can handle. Note - you can't change the display mode before setting the full screen window.
  4. Provide a way to get of of fullscreen mode! This should really be zero on the list. As a last resort, you should be able to Alt-F4 or ⌘-Q out of it, but debugging in fullscreen mode can be a pain unless you have a second computer. Fortunately you can debug in windowed mode, and just switch to fullscreen for serious playing.
Lets implement it. The full code can be found at
svn checkout "http://javagamedev.googlecode.com/svn/trunk/Tutorial 2"
and we'll end up with this: More exciting than a grey screen, no? Lets look at the important details. There are two classes this time - TutorialFrame and TutorialCanvas. The Frame class is the top level window. It controls whether the application runs full screen or not -
private void init(boolean bFullScreen) {
fullscreen = bFullScreen;
setUndecorated(bFullScreen);
setSize(800, 600);
setVisible(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(bFullScreen ? this : null);
canvas.requestFocus();
}
By default the frame starts up windowed, but a quick call to
public void setFullscreen(boolean bFullscreen) {
if (fullscreen != bFullscreen) {
this.dispose();
init(bFullscreen);
}
}

switches between windowed or fullscreen. Note the call to dispose() - without this, the frame is "displayable", and any call to setUndecorated() will fail.

The Canvas class actually performs the rendering, and apart from reacting to a reshape, carries on blissfully ignorant of whether it's full screen or windowed. The Canvas implements KeyListener so that it can call the Frame's setFullscreen method:
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_F:
tFrame.setFullscreen(!tFrame.fullscreen);
break;
case KeyEvent.VK_ESCAPE:
tFrame.setFullscreen(false);
}
}
Nice and easy. Practise checking it out, and try to change the spinning shape being rendered - maybe a cube? Hint: you might need a depth buffer!

Tuesday, December 16, 2008

Starting a new project

So you've reviewed your options for development and chosen Netbeans. Good choice. Once it's installed, fire it up and create a new project from the file menu:

Stick with a standard Java Application and hit next:
Now it's time to choose a name for your project. I always find this a tricky step! Once you've decided, type it in the top box. Stick with the other default settings, and hit Finish.

What do we get?
An empty project that will compile and run, but certainly no flashy spinning OpenGL goodness. For that we need to use the JOGL library that we downloaded in the last post:
  • Right click on Libraries in the project tree up in the top left (ctrl+click, single button mac people) and select Add JAR/Folder...
  • Navigate to where you unzipped the JOGL files, and find lib/gluegen-rt.jar and lib/jogl.jar. You can shift select them to add them both at once. You can also go to Tools->Libraries and set them up as reusable libraries to make it easier to reuse them in other projects, but how much easier can it get?
You'll now be able to write code and even compile it, but if you try and run something that uses gl you'll get an exception: "Exception in thread "main" java.lang.UnsatisfiedLinkError: no jogl in java.library.path"

This is saying in a roundabout way that Java can't find the native implementation files required by the jogl library. To fix it, either follow the instructions on the jogl site to permanently add the libraries to your LD_LIBRARY_PATH or PATH depending on your platform, or set the project configuration to point the JVM in the right place:
  • Go to Build->Set Main Project Configuration->Customize
  • Type -Djava.library.path="/absolute/path/to/jogl/lib" in the VM Options field, including the quotes, but obviously changing the path to the jogl lib directory on your machine.
Now, we can try a very simple OpenGL program. Copy and paste this over the whole of your Main.java:

package tutorial;

import javax.media.opengl.*;
import javax.swing.JFrame;

public class Main {

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.add(new QuickGL());
frame.setVisible(true);
}

private static class QuickGL extends GLCanvas implements GLEventListener {
public QuickGL() {
super();
addGLEventListener(this);
}
public void display(GLAutoDrawable glD) {
GL gl = glD.getGL();
gl.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glFlush();
}
public void init(GLAutoDrawable glD) {
}
public void reshape(GLAutoDrawable glD, int arg1, int arg2, int arg3, int arg4) {
}
public void displayChanged(GLAutoDrawable glD, boolean arg1, boolean arg2) {
}
}
}

There's a lot going on there - but lets look piece by piece. First of all, the package tutorial; line just confirms to the compiler that this file is in the tutorial package. You'll have to change that to reflect the name of the game you chose.

Then we have some imports. These are references to libraries and other code that we want to use from within this file. We import the opengl package provided by jogl, and the JFrame class from swing.

Then there are two classes, one within the other. They are nested that way only for convenience, and any non-toy program would have a separate file for QuickGL rather than a static nested class. The Main class has only one method, public static void main(String[] args), which is the 'entry point' for the application. This method creates a JFrame, which is a window, sets the window's size, puts a QuickGL in it, and makes the window visible.

The QuickGL class subclasses GLCanvas and implements GLEventListener. This is about a minimum for an interactive OpenGL program. Subclassing GLCanvas gives us access to all the drawing tools available in OpenGL, and implementing the GLEventListener ensures the correct methods are called to initialise, paint and resize the component. If you are familiar with normal java components, you can think of the display method like the paint method.

In display, we choose a background colour (a medium grey, exciting!) then clear the background with glClear(). Notice the style is very similar to using OpenGL in C - most of the time the only difference in code is the extra gl. object at the beginning of the line. This is because jogl is 99% machine generated from the C OpenGL library.

Finally we call glFlush() to actually perform the drawing. OpenGL has a client/server structure, and most gl commands simply queue up instructions to be executed. glFlush indicates that we're done for the moment and asks the server to do some drawing.

The observant among you may have noticed there's no glSwap call - by default jogl handles this, which is pretty handy given the differences in buffer control between platforms.

The remaining methods satisfy the implementation of the GLEventListener interface. They don't need to do anything yet, so they don't.


So - what do we get at the end of all this?
A grey window! But wait - it's a hardware accelerated, double buffered, 3D capable grey window. Well worth the effort ;o)

You can check out this project with
svn checkout "http://javagamedev.googlecode.com/svn/trunk/Tutorial 1"
or the equivalent for your IDE.

Development Environment

As the title of the blog suggests, I'm going to limit the selection here to Java. This may be controversial, but I think that the language has matured to the point where it's a viable choice for pretty much every style of game.

So, what are our choices?
  1. Plain JDK
  2. Eclipse
  3. Netbeans
#1, while not impossible, is going to get real old, real fast. It's a good thing to know how to compile at the command line, and in general what is going on behind the scenes, but people spent thousands of hours writing IDEs for a reason! You'll still need to download and install this unless you're in an environment where it's provided already (e.g. OS X)

#2 was until recently my choice, but a few longstanding issues (massive memory usage, no profiler under OSX, occasional unresponisveness) have led me to abandon it in favour of ...

#3 Netbeans. Sun's own IDE is great. Easy to use and responsive, it has a built in form designer and subversion support. This blog will assume you've chosen to use Netbeans when giving detailed instructions, although everything is still possible in Eclipse.

Now on to the second word of the blog title: OpenGL. GL implementations in Java have been through a few iterations, but now pretty much stabilised thanks to JOGL.

Download the jogl development package for your platform and unzip it somewhere you'll remember. There are two steps to perform to use JOGL in your Netbeans project - the CLASSPATH and the LIBRARY_PATH. Detailed instructions for each platform are on the jogl site, but as ever there is more than one way to skin a cat. Once we have created a project, we can reference the jogl jar files as a library in either Eclipse or Netbeans to solve the CLASSPATH problem, and pass an argument to the virtual machine to solve the LIBRARY_PATH problem. Let's do that in the next post.

Getting started - what you'll need

You will need:
  • An idea for your game
  • A development environment
  • Hours and hours of free, quiet time
  • Some programming experience (but don't sweat it too much, doing and learning is a reinforcing cycle)
Optional (but really, really useful!)
I'll take these one at a time, and link the posts together as they're written.

Game development in Java using OpenGL

I've spent more time than I should have recently on a little project I'm calling JavaPop, a modern implementation of the classic Bullfrog game Populous. It's a casual, for-fun-not-profit project but none the less I've learned plenty of lessons along the way that I'd like to share.

So welcome. For now, you can keep up with JavaPop at http://code.google.com/p/javapop.