Android GL and JBox2D 01 Aug 2013

Disinterested cat

EDIT: I wrote a follow-up, showing how to port the results of this tutorial to GL ES 2.0, check it out here

I had to develop a minimal graphics engine recently, and tie it into Box2D. I’ve never used the JBox2D physics engine, so I reached out to Google, and didn’t find anything on using it with a native GL context. After a bit of trial and error, I came up with a decently straightforward game-ey engine, and want to share it here in the form of a tutorial. So! Without furthur ado

The Ultimate Android GL 1.1 + JBox2D Tutorial

The android docs provide a helpful Displaying Graphics with OpenGL ES section, which utilizes the GL ES 2.0 API. Since I’m comfortable with the fixed function pipline present in ES 1.1, I decided to roll with it. We’ll begin by setting up our project in Android Studio.

I won’t go into detail on this step, just make a new project with a blank activity. You can delete the default layout and menu that is created, as we won’t be using either. To begin with, we need to make a new GLSurfaceView, and set that as our content view. Once we have done that, we need to create a renderer and attach it to the surface view.

A renderer is a class implementing GLSurfaceView.Renderer, in which all of the drawing takes place. I wouldn’t keep any actual scene logic in the renderer, but for the purposes of this tutorial it’ll work. We need to implement three methods in the renderer:

As per the API docs:

  • onSurfaceCreated() - Called when the surface is created or recreated
  • onSurfaceChanged() - Called when the surface changes size
  • onDrawFrame() - Called to draw the current frame

Go ahead and make a new Renderer class implementing GLSurfaceView.Renderer. We’ll also create an instance of the Renderer and attach it to our view.

Our Activity up to this point:

 1 package;
 3 import android.opengl.GLSurfaceView;
 4 import android.os.Bundle;
 5 import;
 7 public class MainActivity extends Activity {
 9   @Override
10   protected void onCreate(Bundle savedInstanceState) {
11     super.onCreate(savedInstanceState);
13     GLSurfaceView view = new GLSurfaceView(this);
14     Renderer renderer = new Renderer();
15     view.setRenderer(renderer);
17     setContentView(view);
18   }
19 }

Empty Renderer:

 1 package;
 3 import android.opengl.GLSurfaceView;
 5 import javax.microedition.khronos.egl.EGLConfig;
 6 import javax.microedition.khronos.opengles.GL10;
 8 public class Renderer implements GLSurfaceView.Renderer {
10   @Override
11   public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
13   }
15   @Override
16   public void onSurfaceChanged(GL10 gl, int width, int height) {
18   }
20   @Override
21   public void onDrawFrame(GL10 gl) {
23   }
24 }

Premature Theory Break

Theory Cat

Right, well, that was fast wasn’t it? Let’s take a break and plan out what we’re going to do. As outlined above, the meat of everything is the onDrawFrame() function. That’s where the magic happens, but we want to make a generalized engine-like system, don’t we? I classify an engine as anything that allows for open-ended useage. I was going to keep things simple, but drawing a triangle by itself is quite boring, isn’t it? That’s what most tutorials do as well, so we’ll do something different.

At the end of all of this, we’ll be able to define objects with syntax like this:

 1 BoxObject bob = new BoxObject(50);
 3 // Resizeable!
 4 bob.setSize(100);
 5 bob.setSize(150);
 7 bob.setPosition(100, 100);
 8 bob.setRotation(90);
 9 bob.setColor(125, 0, 253);
11 bob.density = 1.0f;
12 bob.friction = 0.2f;
13 bob.restitution = 1.1f; // Should be interesting
15 // Toggle-able physics simulation
16 bob.enablePhysics();
17 bob.disablePhysics();
18 bob.enablePhysics();

….and have our engine handle the rest. As stated in the Android docs, the renderer runs in a thread seperate from the UI, but we’ll move our physics sim onto a seperate thread to keep things nice, and allow for actor creation from just about anywhere.

The renderer will manage drawing everything by itself, with no concept of layers. They’d be easy to add, depending on the unused z-coordinate (we’re going 2D only on this one, sorry to burst your bubble), but we have too much ground to cover to really warrant throwing that on top. Maybe later? Probably later.

They say that speaking to readers as if they are next to you keeps them engaged, but really it just makes me feel like I should have slept more. I’ll continue to use “We” throughout this. I hope you appreciate me sacrificing mental comfort for your literary engagement. </minirant>

In order to acheive the above syntax, we need a few things. A class dedicated to handling our physics engine, object creation and destruction, all on a seperate thread. A slight annoyance of Box2D is that you cannot create bodies (and fixtures?) while the world is being stepped. Since the world will be simulated in a thread seperate from object instantiation, this is a real issue. Objects will add a body definition to a queue in the physics thread, and have a callback to which the created body will be returned.

To be slightly more efficient, we can also destroy the physics world and prevent stepping if there are no bodies in use. Pfft, really dragged you into this one didn’t I? I did say it’d be the “Ultimate” tutorial. I really only said that to come up with a catchy title, but I might as well live up to it.

Also, actors need to be managed by the renderer. Everything in GL is made up of vertices, which we need to create, manage, and pass to the renderer for rendering. To make it easy to extend, we’ll create a BaseObject class, which we will then extend to implement a BoxObject, TriangleObject, and so on.

So, let’s begin with the physics class.

JBox2D - Really simple but difficult the first time

You can download the JBox2D jar to include with your project here. Get the zip, and find the jar in the jbox2d-library\target\ folder. Move that to the libs folder in your project, find it in Android Studio, right click -> Add as Library (towards the bottom).

Now open up your build.gradle file (this step is necessary for the new Android Studio), and add “compile files(‘libs/jbox2d-library-’)” to your dependencies {} section. Of course, rename the jar to match your own.

Well. That was it. Full JBox2D integration.

Ha. Ha. Right. That was actually probably the easiest part, so go ahead and relish it for a moment. Done? Right. Make a new class, call it “Physics”, or whatever else strikes your fancy.

In order to simulate physics, a JBox2D World has to be created, and a method named ”step” has to be called. This method is what actually steps the world through time, and takes 3 parameters.

The first is the time to simulate for, in seconds. The next two are velocity and position iterations, which we don’t really need to worry about. These essentially define how accurate JBox2D is when resolving collisions (objects actually overlap, and have to be seperated). We’ll set these to 6 each, since that seems to work just fine, but feel free to experiment.

Also, we will need to keep track of the owning object in the body creation queue, so it can be notified when the body is created. For this, we will make a tiny container class, containing an actor ID and a body definition. We’ll call this BodyQueueDef:

 1 package;
 3 import org.jbox2d.dynamics.BodyDef;
 5 public class BodyQueueDef {
 7   private int actorID;
 8   private BodyDef bd;
10   public BodyQueueDef(int _actorID, BodyDef _bd) {
11     bd = _bd;
12     actorID = _actorID;
13   }
15   public int getActorID() { return actorID; }
16   public BodyDef getBd() { return bd; }
17 }

Our Physics class contains a few members, along with the world thread. I’ve commented it instead of breaking it up into chunks and explaining it, a tad easier this way:

 1 package;
 3 import org.jbox2d.common.Vec2;
 4 import org.jbox2d.dynamics.Body;
 5 import org.jbox2d.dynamics.World;
 7 import java.util.Vector;
 9 public class Physics {
11   // Defined public, should we need to modify them from elsewhere
12   public static int velIterations = 6;
13   public static int posIterations = 6;
15   // We need to keep track of how many bodies exist, so we can stop the thread
16   // when none are present, and start it up again when necessary
17   private static int bodyCount = 0;
19   // This is private, since we need to set it in the physics world, so directly
20   // modifying it from outside the class would bypass that. Why not set it
21   // in the world directly? The world is in another thread :) It might also
22   // stop and start back up again, so we need to have it saved.
23   private static Vec2 gravity = new Vec2(0, 0);
25   // Our queues. Wonderful? I concur.
26   private static final Vector<Body> bodyDestroyQ = new Vector<Body>();
27   private static final Vector<BodyQueueDef> bodyCreateQ = new Vector<BodyQueueDef>();
29   // Threads!
30   private static PhysicsThread pThread = null;
32   // Thread definition, this is where the physics magic happens
33   private static class PhysicsThread extends Thread {
35     // Setting this to true exits the internal update loop, and ends the thread
36     public boolean stop = false;
38     // We need to know if the thread is still running or not, just in case we try to create it
39     // after telling it to stop, but before it can finish.
40     private boolean running = false;
42     // The world itself
43     private World physicsWorld = null;
45     public boolean isRunning() { return running; }
47     @Override
48     public void run() {
50       running = true;
52       // Create world with saved gravity
53       physicsWorld = new World(gravity);
54       physicsWorld.setAllowSleep(true);
56       // Step!
57       while(!stop) {
59         // Record the start time, so we know how long it took to sim everything
60         long startTime = System.currentTimeMillis();
62         // Step for 1/60th of a second
63         physicsWorld.step(0.016666666f, velIterations, posIterations);
65         // Figure out how long it took
66         long simTime = System.currentTimeMillis() - startTime;
68         // Sleep for the excess
69         if(simTime < 16) {
70           try {
71             Thread.sleep(16 - simTime);
72           } catch (Exception e) {
73             e.printStackTrace();
74           }
75         }
76       }
78       running = false;
79     }
80   }
81 }

Now, we can’t actually use that thread since we don’t have any logic for managing it or the queues. We can’t do much without knowing what our objects look like, so let’s take a break from the physics stuff for a second and figure that out.

BaseObject - Sprite, but with a more obtuse name

This is where we get to the actual GL part of things. We’ll implement the BaseObject class first, get it rendering, then go back and tie the Physics class into everything.

What does our BaseObject need to do? We should be able to modify the position and rotation of the object, along with its color. Besides that, we need to enable/disable physics simulation of it. Essentially, that syntax we saw above. GL 1.1 handles color components (r, g, b) as values between 0.0 and 1.0. Since the 0-255 system is a bit more familiar to most of us, I’ll go ahead and define a small Color3 class. It’s really tiny, I promise, check it out:

 1 package;
 3 import org.jbox2d.common.Vec3;
 5 public class Color3 {
 7   public int r;
 8   public int g;
 9   public int b;
11   public Color3() {
12     r = g = b = 0;
13   }
14   public Color3(int r, int g, int b) {
15     this.r = r;
16     this.g = g;
17     this.b = b;
18   }
19   public Color3(float r, float g, float b) {
20     this.r = (int)(r * 255.0f);
21     this.g = (int)(g * 255.0f);
22     this.b = (int)(b * 255.0f);
23   }
25   // Makes components GL-compatible, returns them in a vector
26   public Vec3 toFloat() {
27     return new Vec3((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f);
28   }
29 }

Let’s go ahead and sketch out the BaseObject class, and then we’ll fill in the logic one step at a time.

 1 package;
 3 import org.jbox2d.common.Vec2;
 4 import org.jbox2d.dynamics.Body;
 6 import javax.microedition.khronos.opengles.GL10;
 8 public class BaseObject {
10   public Color3 color = new Color3(255, 255, 255);
11   public boolean visible = true;
13   private int id;
14   private Body body = null;
16   protected Vec2 position = new Vec2(0.0f, 0.0f);
17   protected float rotation = 0.0f;
19   public BaseObject() {
20 = Renderer.getNextId();
21   }
23   public void draw(GL10 gl) {
24     if(!visible) { return; }
25   }
27   // Modify the actor or the body
28   public void setPosition(Vec2 position) {
29     this.position = position;
30   }
32   // Modify the actor or the body
33   public void setRotation(float rotation) {
34      this.rotation = rotation;
35   }
37   // Get from the physics body if available
38   public Vec2 getPosition() {
39    return position;
40   }
41   public float getRotation() {
42     return rotation;
43   }
45   public int getId() { return id; }
46 }

Pretty simple, eh? We have the properties I mentioned earlier (position, rotation, color) along with acessors, and a few others. visible just disables rendering by returning early in the render() function, and the ID will be used by the Physics class to find the actor.

In order to keep the IDs unique, we’ll let the Renderer manage them for us. Go ahead and add a new private static int to the Renderer, and a method for returning it. Return the id++ though, so it auto-increments on each request. Illustrated:

In Renderer:

1 ...
2 private static int nextId = 0;
3 ...
4 public static int getNextId() {
5   return nextId++;
6 }
7 ...

We broke out the position and rotation using accessors since we will need to handle them differently if a physics body is present. For now, it looks pointless, but patience grasshopper.

Let’s start filling in the blanks by figuring out how we will handle rendering. In GL, shapes are rendered using arrays of vertices. A square, for example, consists of 4 vertices, one for each corner. We will define everything around the point (0, 0), and then translate to the object position when rendering it. Since the BaseObject has no shape, we’re going to provide a generic method for rendering a set of vertices. For now, work with the image of 4 vertices making up a rectangle. The array is 1-dimensional though, so picture it like:

1 rectangleVertices = {
2   -0.5f, -0.5f, 1.0f,  // Bottom-left corner
3   -0.5f,  0.5f, 1.0f,  // Top-left corner
4    0.5f, -0.5f, 1.0f,  // Bottom-right corner
5    0.5f,  0.5f, 1.0f   // Top-right corner
6 }

Let’s pretend we have our array, and just fill in our render function. GL 1.1 has a fixed function pipeline, and works like a state machine. We set the internal renderer position (keeping things simplified), rotation, and then draw a point, or a set of points. We also enable/disable lighting before drawing things, to effect those specific things. Same with textures and everything else. The beginning of our render function looks like:

 1   public void draw(GL10 gl) {
 3     if(!visible) { return; }
 5     // Save the current state of things
 6     gl.glPushMatrix();
 8     // Enabling this allows us to give GL a pointer to an array containing our vertices
 9     // This is instead of manually drawing every triangle ourselves using glVertex3f() statements
10     // which are missing from GL ES anyways. As far as I know (not that far), this is the only way
11     // to actually draw in 1.1
12     gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
14     // Move to where our object is positioned.
15     gl.glTranslatef(position.x, position.y, 1.0f);
17     // Set the angle on each axis, 0 on x and y, our angle on z
18     gl.glRotatef(0.0f, 1.0f, 0.0f, 0.0f);
19     gl.glRotatef(0.0f, 0.0f, 1.0f, 0.0f);
20     gl.glRotatef(rotation, 0.0f, 0.0f, 1.0f);
22     // Grab our color, convert it to the 0.0 - 1.0 range
23     Vec3 renderCol = color.toFloat();
24     gl.glColor4f(renderCol.x, renderCol.y, renderCol.z, 1.0f);
26     ...

Now that we are ready to go, we need to pass our vertices to GL, and tell it to draw them. We do this using a vertBuffer, and then tell it how many vertices to draw from that buffer. We will store our vertices in a straight array, so we can get the count using vertices.length / 3. The ‘3’ in the call to glVertexPointer specifics the number of coordinates per vertex.

1   ...
2   gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertBuffer);
3   gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
4   ...

GL_TRIANGLE_STRIP is the type of object we are requesting to be drawn (the rectangle we described above can be imagined as two triangles). Now we end the render routine by undoing our state changes:

1   ...
2   gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
3   gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
4   gl.glPopMatrix();

Since we are setting the color on every render call, resetting it to opaque white at the end isn’t necessary, but in case you want to add texture support, you need to set the color to white. Might as well play it safe. (You will add texture support, won’t you?).

Now let’s go ahead and add our vertex containers to BaseObject:

1   ...
2   protected FloatBuffer vertBuffer;
3   protected float[] vertices;
4   ...

Next we’ll create a setVertices() method. Why a seperate method? Well, we need to fill that FloatBuffer, and should we want to change the vertices after creating an object, we will need to re-fill it. We will also need to re-create the physics body (if one exists), so all of that has to be handled in a seperate place. We will make setVertices() a public method, and it will be AWESOME. Actually, short enough to just splat on the screen without further explanation:

 1 public void setVertices(float[] _vertices) {
 3   this.vertices = _vertices;
 5   // Allocate a new byte buffer to move the vertices into a FloatBuffer
 6   ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
 7   byteBuffer.order(ByteOrder.nativeOrder());
 8   vertBuffer = byteBuffer.asFloatBuffer();
 9   vertBuffer.put(vertices);
10   vertBuffer.position(0);
11 }

Very straighforward. We allocate a new bytebuffer, convert it to a FloatBuffer, and fill it with our vertices.

That’s about it! Now we will make a BoxObject to test things out.

BoxObject - So crazy it just might work

Since our BaseObject class is doing all the heavy lifting, creating actual objects is very simple. All we need to do is pass our vertices to our parent class, and it’ll handle the rest. We’ll define our boxes as centered around the origin, so its position will be at its center. We will also expose the width/height for modification, and rebuild the vertices whenever one occurs. Wall of code:

 1 package;
 3 public class BoxObject extends BaseObject {
 5   private float width;
 6   private float height;
 8   public BoxObject(float _width, float _height) {
 9     super(); // Just assigns an ID
11     // 4 points, 3 coords, 12 elements, 9000 problems
12     vertices = new float[12];
14     this.width = _width;
15     this.height = _height;
17     refreshVertices();
18   }
20   private void refreshVertices() {
22     // Modify our own vertex array, and pass it to setVertices
23     // We'll define our box centered around the origin
24     // The z cord could potentially be used to specify a layer to render on. Food for thought.
25     vertices[0] = width * -0.5f;
26     vertices[1] = height * -0.5f;
27     vertices[2] = 1.0f;
29     vertices[3] = width * -0.5f;
30     vertices[4] = height * 0.5f;
31     vertices[5] = 1.0f;
33     vertices[6] = width * 0.5f;
34     vertices[7] = height * -0.5f;
35     vertices[8] = 1.0f;
37     vertices[9] = width * 0.5f;
38     vertices[10] = height * 0.5f;
39     vertices[11] = 1.0f;
41     // Update!
42     setVertices(vertices);
43   }
45   public void setWidth(float _width) {
46     this.width = _width;
47     refreshVertices();
48   }
50   public void setHeight(float _height) {
51     this.height = _height;
52     refreshVertices();
53   }
55   public float getWidth() { return width; }
56   public float getHeight() { return height; }
57 }

Now, that’s great and all, but how do we actually draw the actors? We’ll add an ArrayList to our Renderer, and have each BaseObject register itself. The renderer will then go through and draw every actor on each onDrawFrame() call:

 1   ...
 2   public static ArrayList<BaseObject> actors = new ArrayList<BaseObject>();
 3   ...
 5   @Override
 6   public void onDrawFrame(GL10 gl) {
 8     for(BaseObject obj : actors) {
 9       obj.draw(gl);
10     }
11   }

Now go ahead and add this to the BaseObject constructor:

1   ...
2   Renderer.actors.add(this);
3   ...

We are almost there! Except, not really.

Camera? Wtf is that?

You might not want to hear this, but there is more to life than vertices. GL also has the concept of a Camera, and we need to set it up. Ignoring all the proper view frustum details and whatnot, we’ll just slap on an Orthogonal view and call it a day. This allows us to render in 2D, and have a view of exact width and height. To keep things sane, we’ll make the bottom-left corner be (0, 0), and the rest of the screen the size of our surface. In other words, 1 pixel = 1 unit in GL.

Going back to our Renderer class, we will add our camera setup in the onSurfaceChanged() class. Madness you say! If the surface changes, we need to update the GL world to reflect this. This happens when the device is rotated, or otherwise resized (nawww). So, fill the empty onSurfaceChanged with this little snippet:

1   gl.glViewport(0, 0, width, height);
2   gl.glMatrixMode(GL10.GL_PROJECTION);
3   gl.glOrthof(0, width, 0, height, -10, 10);

glOrthof takes 6 parameters: left, right, bottom, top, near far. These define our viewport, and are self explanatory. We set the width of our display as the right edge, and the height as the top. In other words, (0, 0) is the bottom left, and (ScreenWidth, ScreenHeight) is the top right. Anything outside of this range is offscreen. We are rendering in 2D, so the near/far don’t really matter for us, -10 and 10 just seem nice. We’ll specify all vertices with a z coord of 1.0f, so just roll with it.

Now, we also need to initialize GL when the surface is first created. Since we are sticking to 2D without textures, this part is quite short. We mearly tell GL what color we want the screen to be cleared to (black). Inject this in the onSurfaceCreated() method:

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

Now we are nearly finished. We need to load the identity matrix (essentially reset everything) and clear the screen before each frame. We should also cap our framerate at 60 FPS; this means that each frame can take at most 1.0f / 60.0f seconds, so we will sleep for any extra time at the end of rendering. Go ahead and change the contents of your onDrawFrame() function to this:

 1     // Note when we begin
 2     long startTime = System.currentTimeMillis();
 4     gl.glMatrixMode(GL10.GL_MODELVIEW);
 5     gl.glLoadIdentity();
 8     // Draw everything
 9     for(BaseObject obj : actors) {
10       obj.draw(gl);
11     }
13     // Calculate how much time rendering took
14     long drawTime = System.currentTimeMillis() - startTime;
16     // If we didn't take enough time, sleep for the difference
17     // 1.0f / 60.0f ~= 0.016666666f -> 0.016666666f * 1000 = 16.6666666f
18     // Since currentTimeMillis() returns a ms value, we convert our elapsed to ms
19     //
20     // It's also 1000.0f / 60.0f, but meh
21     if(drawTime < 16) {
22       try {
23         Thread.sleep(16 - drawTime);
24       } catch (Exception e) {
25         e.printStackTrace();
26       }
27     }

It’s alive!

We’re nearly there, that’s it for the GL side of things. All that is left is to integrate JBox2D. First, let’s test it out by playing with a BoxObject and seeing if it actually works. Go ahead and add a BoxObject to the Renderer class, so we can change its position in the onDrawFrame() function:

1   ...
2   private BoxObject bob;
3   ...

Then in the onSurfaceCreated() function, add:

1   ...
2   bob = new BoxObject(50, 100);
3   bob.color = new Color3(255, 0, 0);
4   bob.setPosition(new Vec2(100, 100));

Now we have a red box positioned at (100, 100). Let’s move it on each render, add this to the beginning of onDrawFrame():

1   ...
2   bob.setPosition(new Vec2(bob.getPosition().x + 5.0f, bob.getPosition().y));
3   ...

Works. Kind of boring, eh? Right. Well, let’s add physics and spawn hundreds of boxes. That should be more fun.

Spawning hundreds of boxes

Eh, that isn’t exciting enough.

Spewing boxes and watching them fall realistically

Still not exciting, but descriptive. Counts for something. I feel better anyways. Right. Physics! Let’s start with the BaseObject. We need to add three new methods.

  • createPhysicsBody() - Passing in values for density, friction, and restitution
  • destroyPhysicsBody() - Self explanatory
  • onBodyCreation() - To be called by the Physics engine

In JBox2D, bodies have no actual shape themselves. We will define a shape as a Fixture, using the vertices we are rendering the body with in the first place, and then attach that fixture to the body. We need to notify the Physics class that we want a body created, so we don’t interfere with the call to step(). Once the body is created, the Physics class will call our onBodyCreation() method, passing in the newly created body. Because of this process, we will split up our body creation into those two methods. Without the queue system, these could be combined.

So, to create a body, we need three things. Values for friction, density, and restituion. All three range between 0.0 and 1.0, although you can specify values larger than 1.0 and get some interesting results. No idea what that does to density or friction, but for restitution it causes objects to rebound with a mutually higher velocity than they had when colliding. We can also set the body as either dynamic or static, with static bodies being unmovable. Enviornments should be made up of static bodies, so we will make the ground for our boxes to fall onto out of one.

Right, we will decide if a body is dynamic or static based on its density. If it is 0, the body is static, otherwise it is dynamic. Simplez! Add those three values to our BaseObject:

1   ...
2   // Saved for when body is recreated on a vert refresh
3   private float friction;
4   private float density;
5   private float restitution;
6   ...

Now our createPhysicsBody() method:

 1   public void createPhysicsBody(float _density, float _friction, float _restitution) {
 3     // Cowardly refuse to continue if the body already exists
 4     if(body != null) { return; }
 6     // Save values
 7     friction = _friction;
 8     density = _density;
 9     restitution = _restitution;
11     // Create the body
12     BodyDef bd = new BodyDef();
14     if(density > 0) {
15       bd.type = BodyType.DYNAMIC;
16     } else {
17       bd.type = BodyType.STATIC;
18     }
20     // Oh jeez
21     bd.position = Renderer.screenToWorld(position);
23     // Add to physics world body creation queue, will be finalized when possible
24     Physics.requestBodyCreation(new BodyQueueDef(id, bd));
25   }

Camera Episode 2: Coordinates Strike Back

I know I know, breatthheee, it’s pretty straightforward. This is what tripped me up initially, since I thought I could ignore the JBox2D way of doing things and it’d all work out. Well, it doesn’t, you need to translate between screen-space and world-space. You see, the JBox2D world is tuned to work with real world units, specifically meters, seconds, and kilograms. This means that positioning an object at (100, 100) means placing it at a point a total of ~141.42 meters from the origin. This is all fine and dandy, but imagine an object at (100000, 10000). That’s perfectly fine in pixels, but not optimal for JBox2D. So we need to convert between screen-space and world-space, by offering a ratio between 1 pixel and 1 meter.

The way I prefer to do this is define the ratio somewhere, and expose screenToWorld() and worldToScreen() methods. I’d normally make a seperate camera class to handle this (along with the ortho view), but to keep things simple we’ll handle this in our Renderer. Go ahead and add this at the top:

1   private static float PPM = 128.0f;
3   public static Vec2 screenToWorld(Vec2 cords) { return new Vec2(cords.x / PPM, cords.y / PPM); }
4   public static Vec2 worldToScreen(Vec2 cords) { return new Vec2(cords.x * PPM, cords.y * PPM); }
6   public static float getPPM() { return PPM; }
7   public static float getMPP() { return 1.0f / PPM; }

This states that 1 meter = 128 pixels. You can change this and see how it effects things, but 128 seems to work well on large screens. For smaller screens, lower this value.

Physics queues

Now that we have that out of the way, let’s set up our Queue handling. We need two functions, requestBodyCreation() and destroyBody(). When creation is reqested, the BodyQueueDef object is added to the queue, and the world thread is instantiated if no bodies already exist. Add this to our Physics class:

 1   public static void requestBodyCreation(BodyQueueDef bq) {
 3     // Ship it to our queue
 4     bodyCreateQ.add(bq);
 6     if(bodyCount == 0) {
 8       // If the thread already exists, then wait for it to finish running before re-creating
 9       // Technically one could just restart the thread, but meh
10       if(pThread != null) {
11         while(pThread.isRunning()) { }
12       }
14       pThread = new PhysicsThread();
15       pThread.start();
16     }
18     // Take note of the new body
19     bodyCount++;
20   }

The destroyBody() method is much simpler, as all the bodyCount logic is handled by the thread upon destruction:

1   public static void destroyBody(Body body) {
2     bodyDestroyQ.add(body);
3   }

Now, let’s handle creation and destruction. To create a body, all we need to do is make a call to physicsWorld.createBody(), and then send the body back to the actor that requested it. To destory, we need to make a call to physicsWorld.destroyBody(), decrement the body count, and stop the thread if no more bodies exist. We clear each queue at the end of its section. Then we step the world.

The catch is that we must interact with the queues inside code blocks synchronized on them. What this does is prevent other threads from messing with the queue while read it. So when our Renderer wants to add an object, it will need to wait for this thread to exit the synchronization block. Since the block is short, and should finish before any other Thread has a chance to fill bodyCreateQ/bodyDestroyQ with too many objects, no problem should arrise.

Another issue is that while the block is being hit, even if the queue is empty, no other thread will write to it. So, we will only enter the synchronized block if the queue has at least one element in it. Why is checking the size not an issue? Vector is synchronized internally on every method, and we are only reading the length of it, not modifying it in any way. If either queue contains at least one element, then it is locked for the duration of the block.

So, add this to the beginning of the else {} clause in our thread’s loop, above the world step call:

 1         if(bodyDestroyQ.size() > 0) {
 2           synchronized (bodyDestroyQ) {
 4             for(Body body : bodyDestroyQ) {
 5               physicsWorld.destroyBody(body);
 6               bodyCount--;
 7             }
 9             bodyDestroyQ.clear();
10           }
11         }
13         if(bodyCreateQ.size() > 0) {
14           synchronized (bodyCreateQ) {
16             // Handle creations
17             for (BodyQueueDef bq : bodyCreateQ) {
18               Renderer.actors.get(bq.getActorID()).onBodyCreation(physicsWorld.createBody(bq.getBd()));
19             }
21             bodyCreateQ.clear();
22           }
23         }

Short and sweet. Now, after the step call, add:

1           if(bodyCount == 0) { stop = true; }

We’re nearly done, we also need to add accessors for the world gravity. This entails adding accessors to both the thread and the Physics class, so the gravity is saved across thread deaths. In the thread class:

 1     public void setGravity(Vec2 grav) {
 2       if(physicsWorld != null) {
 3         physicsWorld.setGravity(grav);
 4       }
 5     }
 7     public Vec2 getGravity() {
 8       if(physicsWorld != null) {
 9         return physicsWorld.getGravity();
10       } else {
11         return null;
12       }
13     }

In the Physics class:

 1   public static void setGravity(Vec2 grav) {
 2     gravity = grav;
 3     if(pThread != null) {
 4       pThread.setGravity(grav);
 5     }
 6   }
 8   public static Vec2 getGravity() {
 9     return gravity;
10   }

Grand Finale: BaseObject body handling

Finally, we add the onBodyCreation handler to our BaseObject class, and modify it to be aware of the body when appropriate. The fixture is made using the vertices used to render the body, but they are first transated to world coordinates.

 1   public void onBodyCreation(Body _body) {
 3     body = _body;
 5     // Body has been created, make fixture and finalize it
 6     // Physics world waits for completion before continuing
 8     // Create fixture from vertices
 9     PolygonShape shape = new PolygonShape();
10     Vec2[] verts = new Vec2[vertices.length / 3];
12     int vertIndex = 0;
13     for(int i = 0; i < vertices.length; i += 3) {
14       verts[vertIndex] = new Vec2(vertices[i] / Renderer.getPPM(), vertices[i + 1] / Renderer.getPPM());
15       vertIndex++;
16     }
18     shape.set(verts, verts.length);
20     // Attach fixture
21     FixtureDef fd = new FixtureDef();
22     fd.shape = shape;
23     fd.density = density;
24     fd.friction = friction;
25     fd.restitution = restitution;
27     body.createFixture(fd);
28   }

We also need to add the destroyBody() method, which mearly calls the Physics.destroyBody() method, and nulls the object.

1   public void destroyPhysicsBody() {
3     if(body == null) { return; }
5     Physics.destroyBody(body);
6     body = null;
7   }

Now, we need to take the existence of the body into account in a few places. When the position/rotation of the BaseObject is accessed, we need to pass through the calls to the body, if it exists. We also need to destroy and re-create the body in the setVertices() function, and we need to update the position and rotation of the BaseObject to match that of the body before rendering. Cake.

The new position/rotation accessors:

 1   // Modify the actor or the body
 2   public void setPosition(Vec2 position) {
 3     if(body == null) {
 4       this.position = position;
 5     } else {
 6       body.setTransform(Renderer.screenToWorld(position), body.getAngle());
 7     }
 8   }
10   // Modify the actor or the body
11   public void setRotation(float rotation) {
12     if(body == null) {
13       this.rotation = rotation;
14     } else {
15       body.setTransform(body.getPosition(), rotation * 0.0174532925f); // Convert to radians
16     }
17   }
19   // Get from the physics body if avaliable
20   public Vec2 getPosition() {
21     if(body == null) {
22       return position;
23     } else {
24       return Renderer.worldToScreen(body.getPosition());
25     }
26   }
27   public float getRotation() {
28     if(body == null) {
29       return rotation;
30     } else {
31       return body.getAngle() * 57.2957795786f;
32     }
33   }

Add this inside the draw() method, right after the if(!visible) check:

1     // Update local data from physics engine, if applicable
2     if(body != null) {
3       position = Renderer.worldToScreen(body.getPosition());
4       rotation = body.getAngle() * 57.2957795786f;
5     }

Finally, at the end of our setVertices() method:

1     if(body != null) {
2       destroyPhysicsBody();
3       createPhysicsBody(density, friction, restitution);
4     }

Success: Actual box spewage

Now we’re ready to rumble. At this point, everything is ready to go, and you can go ahead and enable physics simulation on every BoxObject. We’ll go ahead and put together a tiny demo to test things out, and spawn boxes in the middle of the screen, onto a tiny platform below.

We’ll add another variable to the Renderer, to keep track of the delay between spawns, and simply create a new object every 1/10th of a second. We’ll also store the screen width and height in onSurfaceChanged() to position the ground and objects centered horizontally. Add these to Renderer:

1   private static long spawnDelay = 0;
2   private static int screenW;
3   private static int screenH;

We’re going to keep bob, but change his dimensions to (800, 50), and add a call to setGravity(), with -10 on the y axis:

1     bob = new BoxObject(800, 50);
2     bob.color = new Color3(255, 0, 0);
4     Physics.setGravity(new Vec2(0, -10));

We need to create bob’s physics body down in the onSurfaceChanged() function, to set its position once and cleanly. Since createPhysicsBody() will return early if the body is already present, we can just call it immediately after setting the position, and it will only work the first time.

At the bottom of onSurfaceChanged:

1     screenW = width;
2     screenH = height;
4     bob.setPosition(new Vec2(screenW / 2, 100));
5     bob.createPhysicsBody(0, 0.5f, 0.8f);

Now at the top of our onDrawFrame() method, after setting the startTime, we’ll add the delay check and start spewing bodies. We’ll spew 10 per second, so we will make sure 100ms have elapsed since the last spawn, and then create a body of random size and shape:

 1     if(System.currentTimeMillis() - spawnDelay > 100) {
 3       BoxObject obj = new BoxObject((float)Math.random() * 50, (float)Math.random() * 50);
 4       obj.color = new Color3();
 5       obj.color.r = (int)(Math.random() * 255);
 6       obj.color.g = (int)(Math.random() * 255);
 7       obj.color.b = (int)(Math.random() * 255);
 9       obj.setPosition(new Vec2(screenW / 2, screenH / 2));
10       obj.createPhysicsBody(1.0f, 0.2f, 0.5f);
12       spawnDelay = System.currentTimeMillis();
13     }

Awesome, isn’t it? You’ll find that it’ll inevitably slow down, and if you have a low-end device then it might do so rather quickly. Feel free to adjust the spew rate to one box per second.

That’s about it! I won’t go over creating a TriangleObject or CircleObject, since those are straightforward given the code so far. Here is the source if you don’t want to type it all out, feel free to email me if you have any questions, or requests for more tutorials.

EDIT: I wrote a follow-up, showing how to port the results of this tutorial to GL ES 2.0, check it out here