Box2D C++ tutorials - Using debug draw

Last edited: July 14 2013

Chinese version -> 中文
Chinese version 2 -> 中文、第二

Using debug draw


The testbed makes use of a feature known as "debug draw" to draw the shapes you see. Obviously if you are making a game you will want to have all kinds of eye-popping fancy graphics instead of these boring polygons, but the debug draw feature can be very useful when you are having trouble getting the physics scene to work right. Sometimes the problem can be in the rendering part of your game, for example a sprite may be drawn at the wrong position or rotation, giving the appearance of incorrect physics. I recommend keeping the debug draw ready to hand for times when you want to check exactly what is going on in the Box2D world.

The way it works is quite simple. Box2D tells you all the shapes it sees and where they are eg. "a circle of radius r at x,y", or "an edge from a to b", etc and you draw what you are told. You don't have to do any transforming or worry about where the actual body positions are or which fixtures belong to which bodies, you simply draw the geometry. The idea is that if all you have to do is draw a few lines, you can't mess it up :-)

The default debug draw used by the testbed is done by subclassing the b2DebugDraw class, which has a bunch of virtual functions that can be overridden. Here are the main players:
1
2
3
4
5
6
  virtual void DrawPolygon(b2Vec2* vertices, int32 vertexCount, b2Color& color) = 0;
  virtual void DrawSolidPolygon(b2Vec2* vertices, int32 vertexCount, b2Color& color) = 0;
  virtual void DrawCircle(b2Vec2& center, float32 radius, b2Color& color) = 0;
  virtual void DrawSolidCircle(b2Vec2& center, float32 radius, b2Vec2& axis, b2Color& color) = 0;
  virtual void DrawSegment(b2Vec2& p1, b2Vec2& p2, b2Color& color) = 0;
  virtual void DrawTransform(const b2Transform& xf) = 0;
Separating the rendering code into one location like this makes it easy to implement debug drawing for different APIs like DirectX or OpenGL ES, or switch between different methods easily.

Although the testbed's default debug draw works just fine and we don't really need to customize it, since we're on the topic let's try using our own subclass to alter the appearance a bit. Any scene from the previous topics will be ok to use - I'll start with the same scene as for the moving at constant speed topic. Debug draw To make our own debug draw class, we will need to implement all of the pure virtual functions. For now let's just make them all empty:
1
2
3
4
5
6
7
8
9
10
  class FooDraw : public b2DebugDraw
  {
  public:
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
  };
To tell the Box2D world to use this class instead of the default one, we use the SetDebugDraw function. This function takes a pointer to a b2DebugDraw object, so we'll need to have an instance of the class to point to. This is easily done by just declaring a variable of your new class at global scope.
1
2
3
4
5
6
7
8
  //at global scope
  FooDraw fooDrawInstance;
  
  //in constructor, usually
  m_world->SetDebugDraw( &fooDrawInstance );
  
  //somewhere appropriate
  fooDrawInstance.SetFlags( b2DebugDraw::e_shapeBit );
This will tell the world what class instance it should direct all the drawing instructions to. Notice the last part which selects a certain category of debug information to display. Right now we are interested in seeing the shapes (fixtures) in the world, but you can also set this flag to include the following:
  • e_shapeBit ( draw shapes )
  • e_jointBit ( draw joint connections
  • e_aabbBit ( draw axis aligned bounding boxes )
  • e_pairBit ( draw broad-phase pairs )
  • e_centerOfMassBit ( draw a marker at body CoM )
I say to put this setting 'somewhere appropriate' because it's something you might want to alter at run time, like in the situation I mentioned above when you might occasionally want to confirm that your rendered game entities and Box2D are doing the same thing. In the testbed, you can see these settings as the checkboxes on the right hand panel.

If you use a debug draw class in your own projects, there is one other important point to be aware of: you need to call the DrawDebugData() function of your world, which will in turn cause Box2D to call back to your debug draw class functions for each shape that needs drawing.

Running the testbed now you should see nothing showing in the scene. This is because we still have a completely empty debug draw implementation. From this point, what you fill in the drawing functions depends on what platform and rendering API you are using. As an example, let's implement the DrawSolidPolygon function in OpenGL ES, as used on embedded platforms such as the iPhone. This is a handy example because OpenGL ES is a subset of OpenGL so we can still run it on our PC in the testbed as normal, and also because it's one of those questions that comes up often.

OpenGL ES does not have the glBegin/glEnd/glVertex functions, so rendering is done with vertex arrays instead:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) 
  {
    //set up vertex array
    GLfloat glverts[16]; //allow for polygons up to 8 vertices
    glVertexPointer(2, GL_FLOAT, 0, glverts); //tell OpenGL where to find vertices
    glEnableClientState(GL_VERTEX_ARRAY); //use vertices in subsequent calls to glDrawArrays
    
    //fill in vertex positions as directed by Box2D
    for (int i = 0; i < vertexCount; i++) {
      glverts[i*2]   = vertices[i].x;
      glverts[i*2+1] = vertices[i].y;
    }
    
    //draw solid area
    glColor4f( color.r, color.g, color.b, 1);
    glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount);
  
    //draw lines
    glLineWidth(3); //fat lines
    glColor4f( 1, 0, 1, 1 ); //purple
    glDrawArrays(GL_LINE_LOOP, 0, vertexCount);
  }
Debug draw The other drawing functions can be done in much the same way. Depending on what rendering API you are using, circles might have to be drawn as a polygon with many sides.

Update: I noticed that there is a good implementation for an OpenGL ES debug draw in the Box2D source code iPhone contribution. This is written for Obj-C but it appears to be regular C++ so you can use it without modification:
GLES-Render.mm