Box2D C++ tutorials - Introduction

Last edited: July 24 2013


Halt! You should have a good understanding of
the basic tutorials before venturing further.


Physics-driven particles


In the anatomy of a collision topic we looked at how to get information about collisions. One piece of information was the contact point, where the impulse will be applied to separate the two fixtures that collided. This topic will look at how we can use the relative velocity of the two fixtures at the contact point, combined with the friction between them, to generate particles like smoke, dust, sparks etc. Physics-driven particles

The basics


We need to keep track of different sets of contacts, for each pair of material types. For this example we will have four material types: steel, rubber, concrete and dirt. The combinations of these rubbing on each other will generate three particle types: smoke, sparks and dirt. Even with only four material types, there are 10 possible combinations of material pairs, so this type of management can grow large very quickly. In this topic we will only generate particles in four cases:

steel rubber concrete dirt
dirt dirt dirt - -
concrete sparks smoke -
rubber - -
steel -

The general procedure for keeping track of particle-generating contacts can be done like this:
  • have a set of contacts for each particle type (smoke, sparks, dirt)
  • when BeginContact occurs, add the contact to the appropriate set
  • when EndContact occurs, remove the contact from the set
  • every time step, look at each contact in the set and check the relative velocity of the fixtures at the contact point to see if particles should be generated
Obviously that last point is the main focus of this topic, but we'll also look at how you could organize the contact listener to keep track of the contacting material pairs.


Sets of contacts


We need some type of list to store all the active contacts, for which I find std::set to be quite useful.
1
2
3
4
5
  //references to currently active contacts
  set<b2Contact*> m_steelToConcreteContacts;
  set<b2Contact*> m_rubberToConcreteContacts;
  set<b2Contact*> m_steelToDirtContacts;
  set<b2Contact*> m_rubberToDirtContacts;
In this example, both the steelToDirt and rubberToDirt contacts will generate dirt particles using the same conditions (relative velocity, friction etc) and the same characteristics (speed, direction, quantity etc), so we don't really need to keep separate lists of contacts for them. However, if you wanted to do something different depending on what the two materials were, you would need to keep separate lists like this.

Next we need to check in BeginContact/EndContact to see if each contact is one that we're interested in. For example if one fixture is steel and the other is concrete, we'll add/remove that contact in m_steelToConcreteContacts. In the contact listener, either one of fixtureA or fixtureB could be the steel, so we have to make checks like:

        if (( A is steel and B is concrete ) or ( A is concrete and B is steel ))

For a large number of material combinations this can become quite messy and tedious, especially when the "is steel" part of the check also involves a lot of code, and checking user data etc. There are various ways you could tackle this problem, although none of them are very pleasant. You might like to read some discussion about this on Stack Overflow: How can I track all of my Box2D collisions in a clean, manageable manner?

For this example I used a nasty pre-processor macro to help out, so I will mention that here. It is a handy solution, but don't take it as a recommendation (it's only applicable for C/C++ anyway). First, a function is made for each material type, to decide whether a fixture is made from that material:
1
2
3
4
  bool fixtureIsSteel(b2Fixture* f)    { ... }
  bool fixtureIsConcrete(b2Fixture* f) { ... }
  bool fixtureIsDirt(b2Fixture* f)     { ... }
  bool fixtureIsRubber(b2Fixture* f)   { ... }
Of course, you would replace the ... with whatever you need to decide which material the fixture is, for example checking the user data etc.

Now we can write a function to decide whether a contact is steelVsConcrete:
1
2
3
4
5
6
7
8
9
  bool contactIsSteelVsConcrete(b2Contact* contact) {
      b2Fixture* fA = contact->GetFixtureA();
      b2Fixture* fB = contact->GetFixtureB();
      if ( fixtureIsSteel(fA) && fixtureIsConcrete(fB) )
          return true;
      if ( fixtureIsSteel(fB) && fixtureIsConcrete(fA) )
          return true;
      return false;
  }
The problem is, we need a function like this for every combination of material pairs, which is a lot of similar functions. Here's where we can make use of the nasty pre-processor macro to do this for us. The original function can be made into a #define with just the material names being substituted, and then that macro is used for each of the pairs we need:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  #define CHECK_MAT_VS_MAT(mat1, mat2)\
      bool contactIs##mat1##Vs##mat2(b2Contact* contact) {\
          b2Fixture* fA = contact->GetFixtureA();\
          b2Fixture* fB = contact->GetFixtureB();\
          if ( fixtureIs##mat1(fA) && fixtureIs##mat2(fB) )\
              return true;\
          if ( fixtureIs##mat1(fB) && fixtureIs##mat2(fA) )\
              return true;\
          return false;\
      }
      
  CHECK_MAT_VS_MAT(Steel, Concrete)
  CHECK_MAT_VS_MAT(Rubber, Concrete)
  CHECK_MAT_VS_MAT(Steel, Dirt)
  CHECK_MAT_VS_MAT(Rubber, Dirt)
The trade-off for using the nasty macro, is that now our BeginContact/EndContact functions are quite succinct:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  void BeginContact(b2Contact* contact)
  {
      if ( contactIsSteelVsConcrete(contact) )  m_steelToConcreteContacts.insert(contact);
      if ( contactIsRubberVsConcrete(contact) ) m_rubberToConcreteContacts.insert(contact);
      if ( contactIsSteelVsDirt(contact) )      m_steelToDirtContacts.insert(contact);
      if ( contactIsRubberVsDirt(contact) )     m_rubberToDirtContacts.insert(contact);
  }
  
  void EndContact(b2Contact* contact)
  {
      if ( contactIsSteelVsConcrete(contact) )  m_steelToConcreteContacts.erase(contact);
      if ( contactIsRubberVsConcrete(contact) ) m_rubberToConcreteContacts.erase(contact);
      if ( contactIsSteelVsDirt(contact) )      m_steelToDirtContacts.erase(contact);
      if ( contactIsRubberVsDirt(contact) )     m_rubberToDirtContacts.erase(contact);
  }



Generating particles


Now that we have a list of the current contacts for each pair of material types, we can check those contacts every time step to see if there is enough movement and friction between them to generate some particles. If you're using std::set, the outer part of the loop to do this would look like:
1
2
3
4
5
6
7
8
  // replace "theSet" with eg. m_steelToConcreteContacts etc
  for (set<b2Contact*>::iterator it = theSet.begin(); it != theSet.end(); ++it) {
      b2Contact* contact = *it;
      if ( contact->GetManifold()->pointCount < 1 )
          continue;
          
      // particle generation goes here
  }
The particle generation will rely on having at least one point in the contact manifold, so the pointCount check is just there to be safe.

Now, the main particle generation part goes like this: we get the contact point from the manifold, which is a point in world coordinates. Then we use that point to find the velocity of each of the two fixtures at that point. Comparing those velocities tells us how fast the fixtures are rubbing against each other. We also look at the friction of the two fixtures, to come up with single number to represent a total umm... 'intensity' value. If the intensity is above a certain threshold, we generate a particle, otherwise we don't.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  b2Fixture* fA = contact->GetFixtureA();
  b2Fixture* fB = contact->GetFixtureB();
  b2Body* bA = fA->GetBody();
  b2Body* bB = fB->GetBody();
  
  // get the contact point in world coordinates
  b2WorldManifold worldManifold;
  contact->GetWorldManifold( &worldManifold );
  b2Vec2 worldPoint = worldManifold.points[0];
  
  // find the relative speed of the fixtures at that point
  b2Vec2 velA = bA->GetLinearVelocityFromWorldPoint(worldPoint);
  b2Vec2 velB = bB->GetLinearVelocityFromWorldPoint(worldPoint);
  float relativeSpeed = (velA - velB).Length();
  
  // overall friction of contact
  float totalFriction = fA->GetFriction() * fB->GetFriction();
  
  // check if this speed and friction is enough to generate particles
  float intensity = relativeSpeed * totalFriction;
  if ( intensity > threshold )
      spawnParticle( worldPoint, velA, velB, intensity );
It's important to use the friction of the fixtures as well as their relative movement, because low friction surfaces should have a lower intensity. You could think of this 'intensity' as the heat generated by rubbing two things together - low friction surfaces would require a higher speed to generate the same heat as high friction surfaces.

The creation and management of particles is not really the subject of this tutorial. Much has already been written about particle systems, and there are many ready-made solutions out there to use. The spawnParticle function in the code above is just to show how you might use these values from the Box2D world to tell the particle system what to do.

In the accompanying source code for this topic, you can find a rudimentary particle system using this structure:
1
2
3
4
  struct simpleParticle {
      b2Body* body;
      float life;
  };
The particles are rendered as simple points, and properties such as linear damping, gravity scale, restitution etc are changed for different particle behaviors. This has the benefit that the particles will interact with the physics world, and makes for less coding in the sample code, but please don't take it as a recommendation.


Problems


As you may have noticed from the anatomy of a collision topic, the contact point is actually inside both of the fixtures involved in the collision, like this: Physics-driven particles Depending on what type of particles you are generating, this could be a problem. For example, in the sample source code for this topic the particles are using Box2D bodies, and if the body for the particle starts embedded in another body, it can affect the starting velocity. Or even worse, if one of the fixtures is an edge/chain fixture, the particle can start on the wrong side of the chain and never be able to come back.

I can't think of a nice way to solve this, other than raycasting to find the desired surface, and making sure the particle starts on the outside of it. For example in the case above, if the circle was a car tire you would want the particle to start on the outside of the polygon fixture, so you could raycast from the circle center to the contact point to find an appropriate position: Physics-driven particles (In the source code below, particles are only ever generated on the ground, so a raycast vertically downwards from the sky is used to find the start position.)


Other considerations


This topic and the accompanying source code shows a really basic example of detecting where and how to generate particles. There are so many ways you can tweak and improve things that you could spend all day on it, but this tutorial is mainly to cover the Box2D part of the procedure. So I will just briefly mention some areas where improvements could be made:
  • both manifold points could be checked (for polygon vs polygon)
  • you could consider tangential relative velocity only
  • you could use a better friction mix, eg. b2MixFriction
  • you could use PostSolve to consider the pressure between the fixtures
  • different thresholds for each material pair


Source code


Here is the source code for those who would like to try it out for themselves. This is a 'test' for the testbed, based on Box2D v2.3.0.

Testbed test: iforce2d_physicsDrivenParticles.zip
*** To compile this, you will need to make the b2World::DrawShape function public ***

Linux 32-bit binary
Linux 64-bit binary
Windows binary
MacOSX binary

RUBE file for the test scene: physicsDrivenParticlesRUBEScene.zip

YouTube video