You might want to read Part 1 and Part 2 first

We left off after I had completed the Broadphase transplant. Broadphase collision detection was now fast, but we were still spending lots of time in the narrowphase, as well as in other various parts of the physics engine. It seemed like a good time to go back to stage 1.

Turning Knobs Again

Our new Spatial Hashing Broadphase can handle as many small static bodies as we could ever throw at it, but it doesn’t do as well with larger bodies. Back in Part 1 I had consolidated the shapes of walls so that I had a few large bodies with lots of shapes on them. This worked well for p2’s SAPBroadphase, but it’s exactly the opposite of what our new Broadphase likes. I reverted those changes so we once again had a bunch of small bodies for walls, and voilá, collision detection time dropped to almost untraceable levels.

Speeding up the rest

At this point, performance was actually pretty good. Physics were taking only about twice as long as rendering, down from the 10 times as long that I started at. I could have stopped here, but I was on a roll. There were two functions left where it seemed like a lot of time was being spent: World.internalStep and Solver.solve.

World Transplant

There were 5 places during the step that the world was iterating through all the bodies:

  • Applying gravity
  • Applying damping
  • Waking up sleeping bodies
  • Updating positions based on velocity and timestep (called integrating)
  • Resetting forces

What’s one thing all of those have in common? None of them need to happen for static bodies.

The solution was pretty straightforward: keep track of dynamic and kinematic bodies separately. When we add or remove a body from the world, don’t just add it to this.bodies, also add it to this.dynamicBodies or this.kinematicBodies if its type matches.

Now instead of applying gravity to each body in this.bodies, we can apply it to only those in this.dynamicBodies. Instead of integrating all bodies, we can integrate only those in this.dynamicBodies and those in this.kinematicBodies. You get the picture.

Solver Transplant

The solver is the thing that goes and does linear algebra and frankly is a bit above my head. What I can tell you though, is that it deals with things like moving bodies that are overlapping, and applying friction to bodies that are rubbing, and just like before, none of those things apply to static bodies.

This time making the change was even easier. I just replaced the three places where we iterated through world.bodies with world.dynamicBodies instead, and we were good to go.

The Results

With all these changes in place it was time for the final profiling.

Final Profiling results </figcaption>

Physics now takes less time than rendering, when it used to take more than 10 times as long! All that was left to do was to play my pinball game in peace without being disturbed by my laptop’s fans screaming at me.