Getting Started with OpenGL using lwjgl

You are viewing an old version of this entry, click here to see latest version.

While swing is an easy approach to Haxe Java graphics, it does seem quite slow, and the buzz word these days for graphics is OpenGL. OpenGL is hardware accelerated graphics processed on the graphics card, essentially Java can make use of C/C++ hardware/platform specific code, and provide fast and rich graphics all wrapped up in managed Java code designed to run on most platforms.

There are several approaches to OpenGL in Java, but i have chosen to explore lwjgl because it is more popular than jogl. I got the impression jogl was dying but it always depends who you talk with!

Libgx is one approach to using lwjgl which from a Java point of view is simpler or more user friendly, but after briefly exploring, I was unsure from a Haxe perspective if it was wise. Perhaps we want to make less Java style abstractions, with more generic Haxe code, for a start I was struggling with getting the correct types to set up some aspects maybe the complexity of the Library does not suit use from Haxe and it seemed to add more complexity when used from Haxe, and after finding some powerful tutorials on lwjgl I decided that this was the best place to start.

I am on a mac, so I will assume you are too, linux should be similar and we can adjust tutorial to be more cross platform later. lwjgl stands for Light Weight Java Graphics Library. I will also be using the Slick2D library to help with rendering images to screen.

Creating a LWGJL window.

First we need to download the jar files for LWGJL. At the time of writing the latest version is lwjgl-2.9.1, if we unzip that we can drag that locally into a new directory, for clarity we can create an src folder for our code. Due to the way Haxe Java works it helps if we create a package for the code, for instance test_lwjgl. So we should now have a directory structure.

test_lwjgl
    lwjgl-2.9.1
    src
        lwjgl_test

Let's create a main class class Test_lwjgl with some minimal setup code to create a window. So we need to create a Display window with a title, this has to be done within a try and is normally done with some error handling. Next we setup the perspective and initialization of the drawing code, in this case we are not going to draw anything so here I just create a local short cut to the G11 context which is normally used extensively when we come to drawing to screen. Next we create our update loop using a while, this keeps the OpenGL window open. Lastly we add code to cleanly close the window when the user presses the close button.

package lwjgl_test;

// import lwjgl specific libraries
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

class Test_lwjgl {

    public static function main() { new Test_lwjgl(); } public function new()
    {
        // create OpenGL window with title.
        try {
            Display.setDisplayMode(new DisplayMode(640, 480));
            Display.setTitle("Basic test of lwjgl and OpenGL in Java");
            Display.create();
        } catch ( e: LWJGLException ) {
            e.printStackTrace();
            Display.destroy();
            Sys.exit(1);
        }
        
        // short cut to the GL11 context for setting up drawing code, and for drawing with.
        var gl = GL11;
        
        // update window until user closes
        while (!Display.isCloseRequested()) {
            Display.update();
            Display.sync(60);
        }
        
        // close nicely
        Display.destroy();
        Sys.exit(0);
    }
}

To run the code we need to compile against the relevant libraries ( I am unsure if we need all the jar's below just seemed simpler to include the lot, and then with lwjgl we also need to copy the native files to the directory where we run the code So on a mac we would need to create an hxml file to compile our code, details will differ on linux and windows but template will be similar.

#( please save this file as     compile_lwjgl.hxml   )

# specify external jar files used ( may not need all these ), they are used by haxe compiler for the classes we import.
-java-lib lwjgl-2.9.1/jar/AppleJavaExtensions.jar
-java-lib lwjgl-2.9.1/jar/asm-debug-all.jar
-java-lib lwjgl-2.9.1/jar/jinput.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_test.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_util_applet.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_util.jar
-java-lib lwjgl-2.9.1/jar/lwjgl-debug.jar
-java-lib lwjgl-2.9.1/jar/lwjgl.jar
-java-lib lwjgl-2.9.1/jar/lzma.jar

# tell Haxe where our code is.
-cp src
# Define the main class 
-main lwjgl_test.Test_lwjgl
# Define a new folder to create our executable code
-java lwjgl_test

# after Haxe has compiled into Java lets run the code
--next
-cmd cd lwjgl_test/src/
-cmd cd ../
# copy the native code next to our jar code, notice for this test I have specified mac ones.
-cmd cp '../lwjgl-2.9.1/native/macosx/openal.dylib' 'openal.dylib'
-cmd cp '../lwjgl-2.9.1/native/macosx/libjinput-osx.jnilib' 'libjinput-osx.jnilib'
-cmd cp '../lwjgl-2.9.1/native/macosx/liblwjgl.jnilib' 'liblwjgl.jnilib'
# run our application
-cmd java -jar lwjgl_test.jar

To run the program open Terminal and navigate to our test_lwjgl folder and compile and run the Haxe Java code..

haxe  compile_lwjgl.hxml

When we run this providing we have setup Java properly we should see a black window with a title bar appear, our first Haxe Java OpenGL application, not very exciting but a first step.

Getting Slick, with a Haxe logo

Ok well normally the next stage of any openGL tutorial is to draw a triangle with rainbow colors, but how useful is that really?

So our next step is to grab Slick so we can start working with images easily. If you download the Slick.zip unzip it and drag it into our working folder. Now we also need an image to work with, create a folder res and grab an image to put in it here haxe logo png.

so the basic folder structure now looks like:

lwjgl_test
lwjgl-2.9.1
res
slick
src

first lets update our compile code adding Slick to the external libraries, and we will copy to logo.png directly into our runtime folder.

#( please save this file as     compile_lwjgl.hxml   )

# specify external jar files used ( may not need all these ), they are used by haxe compiler for the classes we import.
-java-lib lwjgl-2.9.1/jar/AppleJavaExtensions.jar
-java-lib lwjgl-2.9.1/jar/asm-debug-all.jar
-java-lib lwjgl-2.9.1/jar/jinput.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_test.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_util_applet.jar
-java-lib lwjgl-2.9.1/jar/lwjgl_util.jar
-java-lib lwjgl-2.9.1/jar/lwjgl-debug.jar
-java-lib lwjgl-2.9.1/jar/lwjgl.jar
-java-lib lwjgl-2.9.1/jar/lzma.jar
# slick library to help us display images.
-java-lib slick/lib/slick.jar

# tell Haxe where our code is.
-cp src
# Define the main class 
-main lwjgl_test.Test_lwjgl
# Define a new folder to create our executable code
-java lwjgl_test

# after Haxe has compiled into Java lets run the code
--next
-cmd cd lwjgl_test/src/
-cmd cd ../
# copy the native code next to our jar code, notice for this test I have specified mac ones.
-cmd cp '../lwjgl-2.9.1/native/macosx/openal.dylib' 'openal.dylib'
-cmd cp '../lwjgl-2.9.1/native/macosx/libjinput-osx.jnilib' 'libjinput-osx.jnilib'
-cmd cp '../lwjgl-2.9.1/native/macosx/liblwjgl.jnilib' 'liblwjgl.jnilib'

# copy image accross
-cmd cp '../res/logo.png' 'logo.png'

# run our application
-cmd java -jar lwjgl_test.jar

Ok to render 2D graphics we need to setup OpenGL, notice the dimensions of our app.

var gl = GL11;
gl.glMatrixMode( gl.GL_PROJECTION );
// we set it up more like flash with coordinates starting top left of our screen.
gl.glOrtho( 0, 640, 480, 0, 1, -1 );
gl.glMatrixMode( gl.GL_MODELVIEW );

To render textures we need to allow it

gl.glEnable( gl.GL_TEXTURE_2D );

To render a transparent png we need to enable blending to allow transparent bits on an image when using OpenGL so also need to add:

gl.glEnable( gl.GL_BLEND); 
gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA );

Now just like in swing when we open a file it could cause an exception so we must wrap any file access in a try and if the code fails we must exit our application cleanly.

var img = null;
try{
    img = new Image("logo.png");
} catch ( e: SlickException ){
    e.printStackTrace();
    Display.destroy();
    Sys.exit(1);
}

For fun to the while loop we can add some simple sine animation.

theta += 0.07;
// lets just move the logo in a circle to prove we have update control.
x = 100 + 30*Math.sin( theta );
y = 100 + 30*Math.cos( theta );
img.draw( x, y );

So the revised code should look something like...

package lwjgl_test;

// import lwjgl specific libraries
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Image;

// import Slick to help us render images easily
import org.newdawn.slick.SlickException;

class Test_lwjgl {

    public static function main() { new Test_lwjgl(); } public function new()
    {
        // create OpenGL window with title.
        try {
            Display.setDisplayMode(new DisplayMode(640, 480));
            Display.setTitle("Animating a Haxe png logo in OpenGL Haxe Java");
            Display.create();
        } catch ( e: LWJGLException ) {
            e.printStackTrace();
            Display.destroy();
            Sys.exit(1);
        }
        
        // short cut to the GL11 context for setting up drawing code, and for drawing with.
        var gl = GL11;
        
        // create image to display
        var img = null;
        try{
            img = new Image("logo.png");
        } catch ( e: SlickException ){
            e.printStackTrace();
            Display.destroy();
            Sys.exit(1);
        }
        
        var x = 100.;
        var y = 100.;
        var theta = 0.;
        
        // setup for 2D
        gl.glMatrixMode( gl.GL_PROJECTION );
        gl.glOrtho( 0, 640, 480, 0, 1, -1 );
        gl.glMatrixMode( gl.GL_MODELVIEW );
        
        // enable texture
        gl.glEnable( gl.GL_TEXTURE_2D );
        
        // enable transparency
        gl.glEnable( gl.GL_BLEND); 
        gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA );
        
        // update window until user closes
        while (!Display.isCloseRequested()) {
            // clear last render
            gl.glClear( gl.GL_COLOR_BUFFER_BIT );
            
            // update angle used to animate logo
            theta += 0.07;
            
            // lets just move the logo in a circle to prove we have update control.
            x = 100 + 30*Math.sin( theta );
            y = 100 + 30*Math.cos( theta );
            
            // draw the Haxe logo to screen
            img.draw( x, y );
            
            Display.update();
            Display.sync(60);
        }
        
        // close nicely
        Display.destroy();
        Sys.exit(0);
    }
}

You should see a black window with an orange Haxe logo animating in a small anti-clockwise circle.

source files

version #20103, modified 2014-05-08 13:52:07 by JLM