BoxWrap2d

From Processing

Jump to: navigation, search

This page is about BoxWrap2d, a thin layer on top of JBox2D enabling simple integration of JBox2D with Processing. This is a great library!

This documentation has been built from different sources, including ewjordan's jbox2d javadoc, examples and jbox2d source code.

Contents

Getting started

The following code will set up a simple 2d world where physical laws apply. Nothing amazing but that's where it all starts. The library will run its own method and renderer, so you don't need to call any special function in draw(). Notice that the framerate has been set to 60, which is appropriate for physics simulation in our case.

import org.jbox2d.util.nonconvex.*;
import org.jbox2d.dynamics.contacts.*;
import org.jbox2d.testbed.*;
import org.jbox2d.collision.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.p5.*;
import org.jbox2d.dynamics.*;
 
Physics physics;
 
void setup() {
  size(300, 200);
  frameRate(60);
  physics = new Physics(this, width, height);
}
 
void draw() {
  background(255);
}

Creating bodies

A Body is an object to which physics laws apply.

Shapes

Bodies are built with helper methods like createRect(), createCircle() or createPolygon() which takes screen dimensions as arguments.

Rectangles

// create a rectangle
physics.createRect(140, 140, 160, 150);

Circles

// create a circle
physics.createCircle(200, 40, 10);

Polygons

Polygons must be :

  • ordered counterclockwise in screen coordinates
  • non self-intersecting
  • convex (if you create concave polygons, don't expect correct behavior!)

File:Tutorials-boxwrap2d_polygon-coords.png

// create a polygon (you can pass as many x,y coordinates as you want)
physics.createPolygon(210, 50, 230, 80, 240, 30);
 
// this one is concave : do not do that!
physics.createPolygon(200, 20, 210, 40, 220, 40, 230, 20, 215, 30);

Creating concave polygons can be achieved using compound shapes and convex decomposition. More on this later!

Compound shapes

Compound shapes are shapes composed of more than one shape. Actually, nothings prevents you to create a body with several shapes bound together. Not be to confused with joints.

Creating compound shapes is a bit tricky at the moment, since boxwrap2d provides no easy-to-use method to do that.

First, let's create a polygon shape:

PolygonDef shape1 = new PolygonDef();
shape1.addVertex(physics.screenToWorld(120, 120));
shape1.addVertex(physics.screenToWorld(120, 100));
shape1.addVertex(physics.screenToWorld(100, 100));
 
// set general shape properties
shape1.density = physics.getDensity();
shape1.friction = physics.getFriction();
shape1.restitution = physics.getRestitution();
shape1.isSensor = physics.getSensor();

Then, let's add a circle shape:

CircleDef shape2 = new CircleDef();
shape2.radius = physics.screenToWorld(10);
shape2.localPosition.set(physics.screenToWorld(120, 120));
 
// set general shape properties
shape2.density = physics.getDensity();
shape2.friction = physics.getFriction();
shape2.restitution = physics.getRestitution();
shape2.isSensor = physics.getSensor();

And here is how you would put all shapes together together to build a body:

BodyDef bd = new BodyDef();
bd.isBullet = physics.getBullet();
 
Body body = physics.getWorld().createBody(bd);
body.createShape(shape1);
body.createShape(shape2);
 
if (physics.getDensity() > 0.0f) {
  body.setMassFromShapes();
}

Static / moving bodies

You can build static (immobile) or normal moving bodies by setting world's density to zero or a non-zero value.

// create a static (immobile) rectangle body
physics.setDensity(0.0f);
physics.createRect(140, 140, 160, 150);
 
// create a regular (moving) rectangle body
physics.setDensity(1.0f);
physics.createRect(100, 40, 170, 50);

Body reference

Sometimes (actually... often) it's useful to reference a body, so you can play with it later. Hopefully, the previous helper methods will return the freshly created body and you'll just need to store the reference in a variable :

Body body = physics.createRect(140, 140, 160, 150);

Attaching user data

An easy way to extend body parameters is to use the handy setUserData() method. It allows you to attach the object of your choice for later use. A simple use is to attach an object containing several parameters, such as the UserData below :

class UserData {
  color c;
  String label;
}
 
data = new UserData();
data.c = color(0, 100, 200);
data.label = "My body";
 
body.setUserData(data);

The user data could be retrieved later using getUserData() :

UserData data = (UserData) body.getUserData();

Interacting with bodies

Forces

Torques

Impulses

Tracking bodies

Sometimes you want to get information about a body you have referenced earlier. Below are a few methods you can use.

Position

To get the location of the body's origin, use getPosition().

Vec2 pos = physics.getPosition(body);

But more generally it's better to get the center of mass position with getCMPosition(), because sometimes the body's origin doesn't actually mean anything.

Vec2 pos = physics.getCMPosition(body);

Angle

To get the angle of the body, use getAngle().

float angle = physics.getAngle(body);

Stability

You can get information about the stability of a body with the isSleeping() method. It returns true when the object is asleep, i.e. it has reached a stability state and is not moving / rotating anymore. Note that some events (colliding with another object, applying a force, a torque or an impulse) will wake up a sleepy body.

boolean asleep = body.isSleeping();

Shapes

A body is composed of shapes. Simple bodies like rectangles have only one, but compound shapes can have several shapes bound together. You can get the first shape attached to a body with Body.getShapeList() and iterate through the next ones with Shape.getNext(). Here is an example :

Shape shape;
for (shape = body.getShapeList(); shape != null; shape = shape.getNext()) {
  // ...
}

Shape types

Use getType() to get the type of the shape.

shape.getType()

Actually, there are only two types of shapes, since rectangles are quadrilateral polygons :

  • ShapeType.POLYGON_SHAPE (rects and polygons)
  • ShapeType.CIRCLE_SHAPE (circles)

Using a custom renderer

We're happy with this nice physics engine, but damned! the default rendered is a bit lo-fi... How about using our own?

Default renderer A custom renderer using rect() and ellipse() Another custom renderer using image()

Set a custom render method

First off, we need to tell our favorite engine that we're tired of its default rendering method and want our own instead. That's what setCustomRenderingMethod() does. Pretty obvious, isn't it?

physics.setCustomRenderingMethod(this, "myCustomRenderer");

You would put that statement in setup(), mostly, but actually you could just decide to change the renderer at any time. You could even have different renderers and swap from one to another.

OK, so our renderer is declared, but what does it look like? This is a simple method which takes a World as an argument :

void myCustomRenderer(World world) {
}

The world variable contains all the bodies we have added to the scene. Pretty useful!

Unset the custom render method

If your renderer looks horrible and you want to swap back to the default one, just call unsetCustomRenderingMethod() :

physics.unsetCustomRenderingMethod();

Draw the bodies

We've defined our new renderer. Fine. But how are we going to render the bodies moving and rotating in the scene? We are just going to use what we've learned above : body tracking!

First of all, we need to retrieve all bodies in the world. Hopefully they are all in a linked list and we can set up a nice loop to retrieve them all with world.getBodyList() and body.getNext().

Then, for each body, we have to get all the shapes it's composed of, find which type they are (polygon or circle?) and draw them. That's what the following code does :

void myCustomRenderer(World world) {
 
  // clear the background
  background(255);
 
  // iterate through the bodies
  Body body;
  for (body = world.getBodyList(); body != null; body = body.getNext()) {
 
    // iterate through the shapes of the body
    org.jbox2d.collision.Shape shape;
    for (shape = body.getShapeList(); shape != null; shape = shape.getNext()) {
 
      // find out the shape type
      ShapeType st = shape.getType();
      if (st == ShapeType.POLYGON_SHAPE) {
 
        // polygon? let's iterate through its vertices while using begin/endShape()
        beginShape();
        PolygonShape poly = (PolygonShape) shape;
        int count = poly.getVertexCount();
        Vec2[] verts = poly.getVertices();
        for(int i = 0; i < count; i++) {
          Vec2 vert = physics.worldToScreen(body.getWorldPoint(verts[i]));
          vertex(vert.x, vert.y);
        }
        Vec2 firstVert = physics.worldToScreen(body.getWorldPoint(verts[0]));
        vertex(firstVert.x, firstVert.y);
        endShape();
 
      }
      else if (st == ShapeType.CIRCLE_SHAPE) {
 
        // circle? let's find its center and radius and draw an ellipse
        CircleShape circle = (CircleShape) shape;
        Vec2 pos = physics.worldToScreen(body.getWorldPoint(circle.getLocalPosition()));
        float radius = physics.worldToScreen(circle.getRadius());
        ellipseMode(CENTER);
        ellipse(pos.x, pos.y, radius*2, radius*2);
        // we'll add one more line to see how it rotates
        line(pos.x, pos.y, pos.x + radius*cos(-body.getAngle()), pos.y + radius*sin(-body.getAngle()));
 
      }
    }
  }
}

Related Links

Personal tools