A warning before we start: this tutorial will not teach you OpenGL ES. There’s a lot of sources out there that do a much better job than i’d be capable of. So let me give you a couple of links before we start.

  • OpenGL from the ground up: an extremely well written tutorial series on OpenGL ES 1.x. Covers all the basics you need to get started with OpenGL. Note that the tutorial is written for the IPhone and uses Objective C/C++. This shouldn’t be a big problem though as the API is the same.
  • OpenGL ES at Khronos: Khronos is the consortium responsible for all the OpenGL APIs out there as well as other things like OpenCL or the Collada format. The page linked includes the specifications for OpenGL ES 1.x as well as 2.0 as well as online manuals. It is a must to read this stuff if you really want a full understanding of what you do.
  • OpenGL ES 2.0 Programming Guide: the definite guide to OpenGL ES 2.0. It is more than worth the money and should be on any OpenGL ES programmer’s book shelf

Do not ever NEVER check out the Nehe tutorial series. It is outdated, does many things completely wrong and boils down to code dumps that don’t really explain anything. If you think you can do graphics programming with just copy & pasting codes, then go ahead and try them. I strongly advice against it. You were warned.

So now that you’ve read and understood all things OpenGL ES let’s look at how you can harness its power with libgdx. The first thing you will do is to create an instance of Application, either a JoglApplication or an AndroidApplication. In either case you must specify if you want to work with OpenGL ES 1.x or 2.0. The problem is that the two versions are incompatible so you have to decide what you want to use. This does not mean that you can’t write a game that has a rendering path for OpenGL ES 1.x and a second path for OpenGL ES 2.0. If you have an OpenGL ES 2.0 rendering path that you want to use you have to tell the Jogl and Android application to initialize a compatible surface and context. Here’s how you do that with the JoglApplication:

public static void main( String[] argv )
{
   JoglApplication app = new JoglApplication( "My app", 480, 320, true );
   ... funky stuff ...
}

The last parameter of the constructor tells the JoglApplication to setup an OpenGL ES 2.0 context if available. On Android you do something similar:

public class MyApp extends AndroidApplication
{
   public void onCreate( Bundle bundle )
   {
      super.onCreate( bundle );
      initialize( true );
      ... funky stuff ...
   }
}

Here we have the method AndroidApplication.initialize() which has a single parameter specifying whether to use OpenGL ES 2.0 if available.

To check whether OpenGL ES 2.0 could be initialized you do the same in both scenarios. You ask the Graphics instance whether OpenGL ES 2.0 is available or not:

if( app.getGraphics().isGL20Available() )
   app.log( "my app", "yay, we can do shaders!" );

If this returns false you have to fallback to OpenGL ES 1.1 or even 1.0. To determine which 1.x version is available you use similar methods:

if( app.getGraphics().isGL11Available() )
   app.log( "my app", "yay, we can use vertex buffer objects" )
if( app.getGraphics().isGL10Available() )
   app.log( "my app", "oh crap, stone age time" );

OpenGL ES 1.1 is backward compatible with OpenGL 1.0, so if 1.1 is available so will 1.0. NOTE: if OpenGL ES 2.0 is available neither OpenGL ES 1.1 nor 1.0 will be available and vice versa as 1.x and 2.0 are not compatible!

So we now know how to initialize an application with OpenGL ES 2.0 support and how to check whether that was successful. The next thing to get going is to get a hold of the actual OpenGL API. On the desktop you’d normally use things like Jogl or LWJGL in Java, on Android you get an instance of GL10 or GL11 passed to your Renderer.onDrawFrame() method. Note that there’s no Java OpenGL ES 2.0 support on Android yet! Another thing to note is that desktop OpenGL is quiet a bit different to OpenGL ES. OpenGL ES is a streamlined version of desktop OpenGL with a lot of unnecessary features removed. It also adds functionality, the possibility to use fixed point math instead of floats being the most prominent one.

Now, with libgdx i want to offer a cross-plattform way to develop and prototype your games on the desktop and deploy them on Android without changing any code. This means that with respect to OpenGL we have to go for the least common denominator which is OpenGL ES. This is of course a problem as a couple of the OpenGL ES features are not available on the desktop. I therefor wrote a wrapper around Jogl implementing the OpenGL ES API (yes i’m that crazy…). It even supports fixed point, except for vertex buffer objects. The feature is working but i’d advice against using it. In my benchmarks i couldn’t find significant benefits from using fixed point on my Android devices (HTC Hero, Motorola Droid). On newer devices fixed point will make even less sense as they will most probably sport proper FPUs. The Droid already has an FPU.

Ok, we still have no idea how to get our hands on the OpenGL ES API. Here’s how that works:

GL10 gl = app.getGraphics().getGL10();
GL11 gl = app.getGraphics().getGL11();
GL20 gl = app.getGraphics().getGL20();

Note again: when OpenGL ES 2.0 is available then Graphics.getGL10() and Graphics.getGL11() will return null and vice versa. If OpenGL ES 1.1 is available then you can either get a GL10 or a GL11 interface. If only OpenGL ES 1.0 is available you can only get a GL10 interface.

The interface GL10, GL11 and GL20 implement the full OpenGL ES specifications. The Java bindings for OpenGL ES 2.0 where done by my and can be downloaded seperately if you want from http://code.google.com/p/gl2-android/. It’s a tiny JNI bridge to the native OpenGL ES API.

Once you get a hold of a GL interface instance you can store it somewhere instead of always querying the Graphics instance. It will stay valid over the complete life cycle of the application.

All that is left is to use the GL instance in your rendering method, maybe like this:

public void render( Application app )
{
   GL11 gl = app.getGraphics().getGL11();
   gl.glClearColor( 1, 0, 0, 1 );
   gl.glClear( GL11.GL_COLOR_BUFFER_BIT );
}

That should be more than enough to get you started. As stated in the introduction, read all the material about OpenGL ES you can get a hold off and experiment with it. Next time we will have a look at the rest of the graphics related classes that will hopefully make you forget about OpenGL :)

  • Share/Bookmark
 

It’s been a while since the last post on libgdx. Let’s see with how files get handled by libgdx.

As you might already know file handling on Android is a bit different as compared to a desktop environment. Each application can have resources and assets which are packaged within the apk of an Android application. Apart from these files you can also access external storage paths like the SD-card.

Libgdx aims to be a cross platform library so i had to map this resources and assets paradigmn of an Android application to also work on the desktop. The first thing i did was throwing out support for resources, they just can’t be mapped in a sane way to the desktop. So if you package files with your application on Android you will store them as assets.

In libgdx there’s 3 types of files: internal, external and absolut files. Internal files refer to assets on Android and to any file relative to the root directory of the application on the desktop. External files refer to files on the SD-card on Android and to any file in the home directory of the current user on the desktop. Finally, absolute files are just that, fully qualified file names.

Internal files can only be read as there’s no way to create new assets in an APK. To minimize confusion it’s also not allowed to write internal files on the desktop. External files can be read and written to, the same is true for absolute files.

So how do you get access to the various files? Via the Files interface that you can get from an Application via a call to Application.getFiles(). Here’s the interface in short:

public interface Files 
{
	public enum FileType
	{
		Internal,
		External,
		Absolut
	}
 
 
	public InputStream readFile( String fileName, FileType type );	
 
	public OutputStream writeFile( String filename, FileType type );
 
	public boolean makeDirectory( String directory, FileType type );
 
	public String[] listDirectory( String directory, FileType type );
 
	public FileHandle getFileHandle( String filename, FileType type );		
}

The enum FileType specifies the three different file types, internal, external and absolute. It is used with all the methods of the Files interface to specify which file type the filename or directory name is pointing to. Depending on the FileType the given filename/directory will then be relative to asset directory/application root directory for internal files, relative to the SD-card/home directory for external files and absolut files will be just that, absolut.

Files.readFile() and Files.writeFile() go the old way of the InputStream and OutputStream. They do what you’d expect. Note that none of the methods throws an Exception, instead null is returned in case something bad happened.

Files.makeDirectory() and Files.listDirectory should be easily understandable too.

Finally there’s Files.getFileHandle which returns an instance of FileHandle. A FileHandle is a plattform specific wrapper for a file descriptor. This is used with some of the other classes in libgdx, namely the sound module and the graphics module. The reason is that many APIs on Android don’t directly work with InputStreams and OutputStreams as their implementation is done in native code which can’t handle streams. Thus i had to introduce this FileHandle wrapper which will get passed to those APIs instead of an InputStream.

That’s it for now.

  • Share/Bookmark
 

Apart from the event based InputListener concept libgdx also allows you to poll for input. Polling means that you check the current state of the input devices. To poll the touch screen, keyboard and accelerometer we again use the Input instance provided by the Application. Let’s have a look at the corresponding methods of the Input interface:

public interface Input 
{
       ...
 
	public boolean isAccelerometerAvailable( );	
 
	public float getAccelerometerX( );
 
	public float getAccelerometerY( );
 
	public float getAccelerometerZ( );
 
	public int getX( );
 
	public int getY( );
 
	public boolean isTouched( );
 
	public boolean isKeyPressed( int key );
}

The first four methods allow use to poll the state of the accelerometer. The first method Input.isAccelerometerAvailable() tells us whether there’s an accelerometer installed on the device. The next three methods return the current values for the 3 axis of the accelerometer. When you hold your device in portrait mode then the x-axis goes from left to right, the y-axis from bottom to top and the z-axis points out of the device towards you. The values these 3 methods return are in the range [-10,10] and indicate how much acceleration occurs on a given axis. Note that if you hold your device in landscape mode the axis stay the same!

The next three methods tell you the x and y coordinate of a touch on the screen as well as whether the screen is actually touched. The coordinate system for the touch coordinates has it’s origin in the top left corner of the screen with the x-axis pointing to the right and the y-axis pointing downwards.

Finally there’s Input.isKeyPressed() which allows you to poll the state of the keyboard. You can use any constant in the Input.Keys class here which are the same as the corresponding Android constants.

Note that on the desktop the accelerometer is of course not available. For polling keyboard input the same rules apply as with the event based input method discussed last time. Not all keys are mapped.

That’s all there is to know about the Input module. Next time we’ll see how we manage files with libgdx. After we have understood that we can finally talk about the interesting stuff, graphics and audio programming.

  • Share/Bookmark
 

In the last couple of articles we have seen the listener pattern being used quiet a lot. For the application life cycle we register an ApplicationListener, for rendering related stuff we register a RenderListener. Today i’m introducing the last sort of listener, the InputListener. Together with an Input instance which you receive from an Application analogous to the Graphics instance you can receive touch input and keyboard input in an event based manner.

The InputListener interface has a few methods. One set is used to process touch events, the other methods are used to process keyboard events. Let’s have a look at the touch event methods first:

public interface InputListener
{
	public boolean touchDown( int x, int y, int pointer );
	public boolean touchUp( int x, int y, int pointer );	
	public boolean touchDragged( int x,  int y, int pointer );
 
        ...

Pretty straight forward. InputListener.touchDown() is called when a user put a finger on the touch screen. You get the x and y coordinate in screen coordinates along with the id of the pointer. The pointer will be 0 for the time being as multi-touch on all available Android devices is borked. The screen coordinate system has it’s origin in the top left corner with the y axis pointing downwards, just so you know :) . InputListener.touchUp() is called when the user removes the previously put down finger again. InputListener.touchDragged() is called when the user is swiping his finger across the screen. Not so hard is it?

As already stated there’s also methods that report key pressed. Let’s check them out

 
	public boolean keyDown( int keycode );
	public boolean keyUp( int keycode );	
	public boolean keyTyped( char character );

No surprises here either. InputListener.keyDown() is called when a key was pressed. The parameter is the key code which is one of the constants in Input.Keys. Note that the keycodes are equal to the ones you’d get on Android if you overwrite the onKey method of your View or set an OnKeyListener. Finally, the InputListener.keyTyped() method is called when a key press is finished and a character was generated. The parameter is the actual unicode character generated by the last key sequence, e.g. shift + a would generate ‘A’.

Here’s a catch: all 6 methods must return a boolean. This boolean indicates whether the InputListener processed the input event or not. In case it has processed it the event will not be handed over to other registered InputListeners. Yes, you can register more than one InputListener :) . Usually you’ll only register a single listener, but there are situations where more than one listener can come in handy.

As with the other listeners we register an InputListener with a module of the Application. In this case we register the listener with the Input module like this:

app.getInput().addInputListener( listener );

To unregister an InputListener we simply call the following method:

app.getInput().removeInputListener( listener );

Input events will get passed to all registered Listeners in the order they where added to the Input instance.

Many aspiring game developers on Android struggle with synchronizing the GUI thread where the input is passed to with the game or rendering thread. In libgdx this is handled to you. The Input module guarantees that input events will be delivered to the rendering thread, that is the same thread any set RenderListener will be called from. This way you don’t have to worry about synchronizing. This of course assumes that you implement your input logic in the renderer thread by putting it in your RenderListener. I know there’s a couple of people out there that advocate a seperate game thread to keep things “responsive”. From my experience this is more than unneccessary ond comes with a shitload of overhead. Your mileage may vary.

So how does all this translate to the desktop? Touch events are simply mapped to mouse events. Press a mouse button and InputListener.touchDown() is called. Move the mouse while holding down a button and InputListener.touchDragged() is called. Release a mouse button and InputListener.touchUp() is called. The same thing applies to keyboard events. I simply mapped the keycodes of Swing/AWT to the keycodes of Android. The left, right, up and down cursor keys map to Input.Keys.KEYCODE_DPAD_LEFT and so on. The keys a-z map to Input.Keys.KEYCODE_A to Input.Keys.KEYCODE_Z. The same is true for the keys 0 to 9. Space, Enter and so on are also mapped. Some keys i could not map. I suggest a look at the code in JoglInputMultiplexer.translateKeyCode() to check which keys map to what key codes.

And that’s all there is to say about getting event based input. Next time we’ll have a look at the polling methods for checking the current touch screen, keyboard and accelerometer state. You can find a simple demo application at http://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/tests/InputTest.java which logs any input event to logcat or the console respectively.

  • Share/Bookmark
 

Last time we learned how to instantiate a desktop and an Android application which share the source of an ApplicationListener. The ApplicationListener is great when you want to get life cycle related events like pause and resume but other than that you can’t really do anything interesting.

Today we will have our first excourse into the realms of the Graphics interface. Each Application implementation offers you an instance of that interface via its Application.getGraphics() method. From the Graphics implementation you can ask for an instance of GL10, GL11 or GL20. You can also make it create certain graphics resources like fonts or textures for you. Today however we want to concern ourselves with another piece of the puzzle. The RenderListener.

A RenderListener is an interface that you have to implement much like the ApplicationListener interface. Similarily you also have to register it somewhere, in this case with the Graphics instance of the Application. The Graphics instance will then call the methods of the RenderListener. Let’s see how we do that:

public class MyApp implements RenderListener
{
   @Override
   public void surfaceCreated( Application app )
   {
   }
 
   @Override
   public void surfaceChanged( Application app, int width, int height )
   {
   }
 
   @Override
   public void render( Application app )
   {
   }
 
   @Override
   public void dispose( Application app )
   {
   }
 
   public static void main( String[] argv )
   {
      JoglApplication app = new JoglApplication( "My Renderer App", 480, 320, false );
      app.getGraphics().setRenderListener( new MyApp( ) );
   }
}
public class MyAndroidApp extends AndroidApplication
{
   public void onCreate( Bundle bundle )
   {
      super.onCreate( bundle );
      initialize( false );
      getGraphics().setRenderListener( new MyApp() );
   }
}

Again we have a main class in the desktop project which implements the RenderListener interface and has a static main to get a JoglApplication running and an Activity derived from AndroidApplication which just uses the desktop class as its RenderListener.

Now, the Graphics instance on the desktop and on Android each have their own thread within which they continuously make the OpenGL surface rerender itself. When we register a RenderListener with a Graphics instance we get the oportunity to render to that surface ourselves via the RenderListener.render method. Apart from that method there are a couple of other methods in the RenderListener interface that get called on special occasions.

The RenderListener.surfaceCreated() method gets called each time the OpenGL surface is created. This happens at application startup and when an Application is resumed. The RenderListener.surfaceChanged() method gets called each time the OpenGL surface changed its dimensions. Finally, the RenderListener.dispose() method is called when a new RenderListener is registered or when the Application is shut down. Not that hard and very similar to what the Renderer interface and the GLSurfaceView do.

What you should take away from this is: rendering is done in a different thread than the GUI thread. Also, when an OpenGL surface is recreated after a resume event all the textures and other OpenGL resources your previously created are invalidated and have to be reloaded. In an earlier article i told you about managed resources informally. We’ll have a closer look at those in a future article.

Finally let us do something graphicy and clear the surface (which i’ll also refer to as framebuffer from time to time) with a color. We do this the old fashioned OpenGL way. For brevity i only post the render() method of the MyApp class here:

public void render( Application app )
{
   GL10 gl = app.getGraphics().getGL10();
   gl.glClearColor( 1, 0, 0, 1 );
   gl.glClear( GL10.GL_COLOR_BUFFER_BIT );
}

And we get a red window on the desktop as well as on Android! You could start playing around with OpenGL in there a little more. The Graphics instance can return you GL10, GL11 and GL20 interface instances. Note however that GL20 is only supported when you pass true as the last parameter to the JoglApplication constructor and to the AndroidApplication.initialize() method. GL10 and GL11 will not be available in that case as they are incompatible to GL20. We’ll have a closer look at those classes in a later article.

  • Share/Bookmark
 

Everything in libgdx revolves around a so called Application. An application offers you several modules for various tasks. There’s a Graphics module which provides you with all things graphicy. There’s an Audio module which will allow you to play sound effects and music as well as accessing the audio hardware directly (to be re-implemented, the last thing that’s missing). The Input module gives you information about what the user is doing with his keyboard, touchscreen/mouse and so on. Finally there’s a Files module which allows you to read and write files in a system indepedant manner. We will discuss all of this modules in forthcoming articles. Today we want to focus on the Application interface, its implementations and an Application’s life cycle.

The first thing we need to know is how to setup an Application. This setup step is the only thing that will differ between the desktop and Android version of your application. You will have to create a static main method for the desktop version and instantiate a JoglApplication in there. In your Android project you will have to create a class that derives from AndroidApplication which itself is an Activity. Here’s two short snippets for doing just that:

public class MyAppDesktop
{
   public static void main( String[] argv )
   {
      JoglApplication app = new JoglApplication( "My App Title", 480, 320, false );
   }     
}
public class MyAppAndroid extends AndroidApplication
{
   public void onCreate( Bundle bundle )
   {
      super.onCreate( bundle );
      initialize( false );
   }
}

Now that was easy. The desktop version instantiates a JoglApplication. It’s constructor takes the window title, the window dimensions in pixels and wheter OpenGL ES 2.0 should be used or not. That’s it, now you have an OpenGL based application running on your desktop. The Android version is similar. We simply derive from AndroidApplication and overwrite the onCreate method. We first call the onCreate method of the super class and then call initialize which is a special method provided by the AndroidApplication. This method will setup the GLSurfaceView internally, the argument of the method determines wheter OpenGL ES 2.0 should be used or not (in this case we won’t use it). The AndroidApplication will switch to fullscreen. The orientation of the screen will be what it was before you started the application. You could hardcode the orientation before the call to initialize of course (and you probably should do that so you only have to layout your game in landscape mode for example).

Now, there’s not a lot happening. Both applications show a black screen and up until now we haven’t seen how we can do something usefull. What we have to understand first is what those applications do in the background as well as the life cycle of a libgdx application on the desktop and on Android.

On the desktop what happens is that a Swing JFrame is created along with a GLCanvas provided by Jogl which does the heavy lifting. To have continuous rendering a seperate thread is started which permanently tells the GLCanvas to redraw itself. The redrawing happens in the GUI thread on the desktop. On Android a new Activity is started and a GLSurfaceView is set as it’s content view. Additionally the GLSurfaceView starts a new thread which continuously renders the GLSurfaceView anew. The GUI thread is a different thread than the rendering thread on Android.

On Android we have become acustomed to the fact that an Activity can be paused and resumed at any time. An application is paused when an incoming call arrives or the user presses the home button. The application is not active during the pause period but it’s VM is put to sleep (more or less). The user can decide to resume the application later on and expects to find the application in the state it was left in when he paused the application. An application can also be destroyed completely when the user presses the back button while being in the application or when the application is in a paused state and the system needs the resources it occupies. We thus normally overwrite the methods onPause, onResume and onDestroy of an Activity on Android to get notified when any of these events happens. Note that before onDestroy is invoked the onPause is first called on Android. Note also that onResume is also called after the onCreate method was called after the application started the first time.

On the desktop there is no such things as pausing and resuming. A desktop application can only be created and destroyed. However, as Android is the final deployment target libgdx is emulating the onPause, onResume and onDestroy callbacks to some extends. For this we need to register an ApplicationListener with the Application instance. Here’s how we do that:

public class MyApp implements ApplicationListener
{
   public static void main( String[] argv )  
   {
      JoglApplication app = new JoglApplication( "My App title", 480, 320, false );
      app.setApplicationListener( new MyApp() );
   }
 
   public void onResumed( Application app )
   {
      app.log( "My App", "application resumed" );
   }
 
   public void onPaused( Application app )
   {
      app.log( "My App", "application paused" );
   }
 
   public void onDestroyed( Application app )
   {
      app.log( "My App", "application destroyed" );
   }
}
public class MyAppAndroid extends AndroidApplication
{
   public void onCreate( Bundle bundle )
   {
      super.onCreate( bundle );
      initialize( false );
      setApplicationListener( new MyApp() );
   }
}

Note how we use the MyApp from the desktop project class in the Android activity. This is the common pattern all libgdx applications will employ. Write your app on the desktop and simply deploy it to Android by just implementing the above AndroidApplication activity.

Once the ApplicationListener is set it will receive notification of pause, resume and destroy events. On the desktop the onResumed method will be called once on startup. When the window is closed the onPause method is first invoked followed by a call to onDestroyed. On Android the callbacks will be called just as their Activity counter parts would be called. When the application first starts onResumed is called. When the application goes into the background due to a press of the home button or another activity taking the foreground the onPaused method will be invoked. When the application is again brought to the foreground the onResumed method is called. Finally, when the application is destroyed first the onPaused method is called followed by an invocation of onDestroyed. We thus have the same life cycle event order on the Desktop and Android with the difference the a desktop application can not be paused.

I might add the possibility to pause and resume a desktop application by minimizing the window.

We still haven’t done anything interesting. However, for the user to be happy we have to implement the life cycle correctly so it doesn’t hurt to understand it. Next time we’ll see how we can actually draw something on the screen via the Graphics instance an Application provides us.

  • Share/Bookmark
 

Before we do anything remotely interesting with libgdx i want to go through the math package you can browse at http://code.google.com/p/libgdx/source/browse/#svn/trunk/gdx/src/com/badlogic/gdx/math. We will go through the most important classes of that package. Let’s start with the vector class(es).

Vector, Vector2D and Quaternion
: vectors come in two flavors in libgdx, three dimensional and two dimensional ones. I did not include 4 dimensional ones for homogenous coordinate systems as i never had a use for them. Vectors can be used to represent a direction or a point within some coordinate system. Their components x, y and z are public members of the class so no time is wasted calling getter or setter functions. Another nice feature is that almost all the methods return a reference to the vector itself allowing you to easily chain commands. Here’s an example:

Vector v = new Vector( 10, 7, 2 ).nor().mul(10);

This instantiates a new vector, normalizes it to have unit length and then scales it to a length of 10. For other methods provided by the two classes refer to the java doc.

The Quaternion class is mostly used by the Matrix class to create a rotation matrix. It features some basic quaternion operations but you will probably never use it directly.

Matrix: the Matrix class represents a 4 by 4 column major matrix. The values of the matrix are stored in a public member called val. This way you can easily load a matrix to OpenGL as it has the right format for such an operation. Matrices can be multiplied with each other, you can build the inverse and so on. As with vectors you can also chain operations. The Matrix class offers a couple of convenience methods that are especially useful in connection to OpenGL. There’s methods to set projection matrices, rotation, scaling and translation matrices and so on. I again refer you to the java doc for more information on what’s available.

BoundingBox, Sphere and Rectangle: these classes are used for bounding volumes in 2D and 3D. A BoundingBox represents an axis aligned 3D bounding box which offers methods to extend it by vectors, retrieve it’s dimensions and corner points and so on. A Sphere represents a 3D sphere having a center and a radius. A Rectangle represents a 2D rectangle having a lower left corner point as well as width and height. The java doc tells you more about each classes capabilities.

Frustum & Ray: these classes are used if you want to do frustum culling and picking, that is selecting object when touching the screen or clicking with your mouse. You will probably never instantiate the Frustum class directly but use the one provided by a camera class like PerspectiveCamera. The same is true for the Ray which is also provided to you by a camera implementation.

Intersector: the intersector class hosts many static methods that allow you to intersect different geometric primitives like rays, spheres, bounding boxes and so on. You will use this class in combination with a Ray obtained from a PerspectiveCamera as well as the bounding boxes of you game objects to check wheter something was clicked for example. There’s also methods for doing intersections in 2D. See the java docs for more information.

CatmullRomSpline & EarCutTriangulator: these classes are byproducts of some of the prototypes i did. A catmull rom spline is a special case of a hermition spline which allows you to generate smooth paths between points in 3D. See this link for more information on those beasts. The EarCutTriangulator class allows you to triangulate 2D concave polygons without self intersection and holes. You can find more information on that algorithm at this link.

And that’s basically the complete math packages. There’s a few classes in there which i didn’t mention as you’ll most likely not need them. Check out the sources at the svn link above and read the java docs!

Next time we’ll take about the general structure of a libgdx application.

  • Share/Bookmark
 

Today i present you libgdx, the framework i wrote in december last year for doing all my android game development. I open sourced it today under the LGPL license. You can find it at http://code.google.com/p/libgdx/. In this article we’ll see how we can easily setup a new project (actually two) so we can prototype and work on the desktop allowing fast iterations and have the same desktop code run on any Android device.

What you need:

  • a properly setup JDK, 32-bit (no 64-bit support at the moment i’m afraid. But you can always install several JVMs alongside each other).
  • Eclipse
  • Android SDK and the Eclipse adt plugin correctly setup
  • Windows or Linux (sorry Apple people, don’t have a Mac, can’t test)

If you read this blog you have all that setup already anyways i assume :) . Now there’s 3 ways to get libgdx up and running. The first one is checking out the source code, compile it and put together a proper project with the created libs. We won’t do that as compiling the whole thing for different platforms (win32, linux) is a bit of a hassle at the moment. The second way is to download the current release of libgdx from the google code site. You can find the most up to date version at http://libgdx.googlecode.com/files/gdx-0.2.zip. In there is a README file that explains you how to setup your own projects. For this post we are gonna use the 3rd way of setting up a libgdx project and that is by downloading the template “hello world” zip file from http://libgdx.googlecode.com/files/gdx-helloworld.zip.

Extract that zip file into your workspace and import the two projects contained in there. This is what you’ll see in your package explorer:

The first project we have a look at is the gdx-helloworld project. It has a couple of jars and shared libraries in the libs/ folder. Two of them are easily identifieable as libgdx jars, one containing the class files, the other containing the sources. The sources are in there so we have java docs when we write our libgdx application. The third jar called desktop-dependencies contains all the 3rd party libraries that libgdx uses on the desktop to for example decode ogg files and so on. Additionally there’s shared libraries which come for windows 32-bit and linux 32-bit and contain the native code of libgdx (to date this contains the native mp3 decoder). If you want to setup your own project you simply copy over this libs/ folder to your new project and add the contained jars to your java project. The armeabi/ folder are technically not needed in the Java project but i was to lazy to remove them :)

The second project is called gdx-helloworld-android. It also has a libs/ folder that contains the libgdx jar file as well as a subfolder called armeabi/ containing a shared library libgdx.so. We don’t need the desktop-dependencies.jar here as that won’t work on Android anyways.

Let’s have a look at what the HelloWorld.java file of the Java project contains:

package com.badlogic.gdx.helloworld;
package com.badlogic.gdx.helloworld;
 
import com.badlogic.gdx.Application;
import com.badlogic.gdx.RenderListener;
import com.badlogic.gdx.backends.desktop.JoglApplication;
 
 
public class HelloWorld implements RenderListener 
{
	public static void main( String[] argv )
	{
		JoglApplication app = new JoglApplication( "Hello World", 480, 320, false );
		app.getGraphics().setRenderListener( new HelloWorld() );
	}
 
	@Override
	public void dispose(Application app) 
	{	
 
	}
 
	@Override
	public void render(Application app) 
	{	
 
	}
 
	@Override
	public void surfaceChanged(Application app, int width, int height) 
	{	
 
	}
 
	@Override
	public void surfaceCreated(Application app) 
	{	
 
	}	
}

That’s the most basic libgdx application you can write. We start of by stating that the class HelloWorld implements the interface RenderListener. We’ll get to that in a later article. Next we have a nice little main function that instantiates a JoglApplication with certain properties (title, dimensions, whether to use OpenGL ES 2.0) and register a new instance of HelloWorld with it. The following method stubs are the implementation of the RenderListener interface. To start this application we create a new run configuration. As libgdx has part of its functionality implemented in native code we have to tell the JVM where to find the shared library. For this we go to the arguments tab of the run configuration and enter “-Djava.library.path=libs/” as the VM arguments:

If we run that we get the following output:

Black is beautiful. What we just did was setting up a complete application which initiates OpenGL in the background and creates a rendering thread which invokes the RenderListener.render() method of all registered RenderListeners, in our case that’s the HelloWorld instance.

Now we want to run the same RenderListener on Android. Let’s have a look at the GDXHelloWorld.java file in the Android project:

package com.badlogic.gdx;
 
import android.os.Bundle;
 
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.helloworld.HelloWorld;
 
public class GDXHelloWorld extends AndroidApplication
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initialize( false );
        getGraphics().addRenderListener( new HelloWorld() );
    }
}

Now that is sleek. GDXHelloWorld is derived from AndroidApplication which is analogous to JoglApplication on the desktop. Both implement the Application interface which essentially hides all the platform specifics for us. The only plattform specific thing we have to code up is the instantiation of a proper Application subclass (either JoglApplication or AndroidApplication) and register a RenderListener with it. Here we see that instead of loading any views in the onCreate() method we first call the super.onCreate() method which does nothing fance. Next we call the AndroidApplication.initialize() method which sets up all the OpenGL and input things for us, much like the constructor of the JoglApplication does. Finally we set a RenderListener, in this case a new HelloWorld instance. This is the same class we defined in our Java project. The android project thus has to reference the Java project for this to work. Now create a run configuration for this activity and try it on the emulator or device. Here’s the result:

What have we done? We setup an OpenGL ES application on Android which runs the same code as the desktop version! From now on we don’t have to touch the Android project anymore as we’ll do all our development in the Java project. Via the link between the java and the Android project the later always has an up to date version of the HelloWorld class which is used as a RenderListener. The only time we modify the Android project is when we add Assets.

A black screen is not really interesting, so let’s draw something. As you might have guessed, the RenderListener interface we implemented is called each time a new frame has to be drawn, much like the Renderer interface of the GLSurfaceView works. The RenderListener.surfaceCreated() method is called once when we first register the RenderListener. In that method we are going to put all our initialization code, like loading textures and meshes. Once everything is setup the rendering thread is repeatedly calling the RenderListener.render() method. This is were we will do all our rendering. If you write a game you can also do your game logic in there, though some advocate to do that in a seperate thread and synchronize it with the rendering thread. I honestly don’t like this approach and have used the “do everything in the rendering thread” approach in all of my games so far. Finally, when the application is closing the RenderListener.dispose() method is called giving you a chance to save any data before exit. In a later article we’ll look into the RendererListener interface in detail as well as into the ApplicationListener interface which both together manage the life cycle of a libgdx application.

To make the hello world example a little bit more interesting let’s add a triangle. For this we simply construct a Mesh as well as a MeshRenderer. A Mesh holds the geometry for a model while the MeshRenderer is responsible for making OpenGL render the Mesh. Here’s a slightly extended version of the HelloWorld class showing you how to create a simple triangle.

package com.badlogic.gdx.helloworld;
 
import com.badlogic.gdx.Application;
import com.badlogic.gdx.RenderListener;
import com.badlogic.gdx.backends.desktop.JoglApplication;
import com.badlogic.gdx.graphics.FloatMesh;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.MeshRenderer;
 
 
public class HelloWorld implements RenderListener 
{
	MeshRenderer mesh;
 
	public static void main( String[] argv )
	{
		JoglApplication app = new JoglApplication( "Hello World", 480, 320, false );
		app.getGraphics().setRenderListener( new HelloWorld() );
	}
 
	@Override
	public void dispose(Application app) 
	{	
 
	}
 
	@Override
	public void render(Application app) 
	{	
		mesh.render( GL10.GL_TRIANGLES );
	}
 
	@Override
	public void surfaceChanged(Application app, int width, int height) 
	{	
 
	}
 
	@Override
	public void surfaceCreated(Application app) 
	{	
		Mesh m = new FloatMesh( 3, 3, false, false, false, 0, 0, false, 0 );
		m.setVertices( new float[] { -0.5f, -0.5f, 0, 
									  0.5f, -0.5f, 0,
									  0.0f,  0.5f, 0 } );
		mesh = new MeshRenderer( app.getGraphics().getGL10(), m, true, true );
	}	
}

I won’t go into the details of the Mesh and MeshRenderer class here as this is the topic of a later article. However, it should be straight forward to figure out what we do. We create a Mesh, set it’s vertices and then create a new MeshRenderer responsible for rendering that Mesh. This happens in the HelloWorld.surfaceCreated() method which get’s called once at start up. In the HelloWorld.render() we then simply call the MeshRenderer.render() method telling it that it should draw its data as triangles. Note that we use OpenGL constants here! Libgdx is tightly integrated with OpenGL ES for better or worse. Many graphics related classes will allow you to use familiar OpenGL constants and so on. We’ll talk more about that in a later article. Here’s the awesome output of the above program:

Yay, a triangle! Now here comes the beauty of libgdx. We don’t have any extra steps to do for that to work on Android. Simply execute the Android run configuration and check the output!

Nice, i like! That’s it for now. In the coming weeks i’ll introduce all the featuers of libgdx one by one. Libgdx encapsulates all necessary things like graphics, audio, user input and file i/o in a platform agnostic way so you can develop on the desktop and deploy to the mobile device seamlessly. So there’s a lot of things to learn. In the meantime you can check out the source code and read the Java doc. Everything is documented and should allow you to get up and running.

Have fun.

  • Share/Bookmark