Hi folks, it’s me again, with a progress report on elm-physics. You can read the first part here and the second part here.
A lot happened since the previous post: performance improvements, simpler API and more features!
Performance
The current performance seems to have plateaued at 64 boxes running at 60 FPS on my MacBook Pro, which is 3 times more boxes than before! Below are some tricks that helped with improvements.
While elm-explorations/benchmark is nice for micro-benchmarks, it doesn’t work for macro benchmarks. It was more helpful to set up the Boxes example, then look at FPS and profile JavaScript in Chrome (although profiling is not easy, because most functions are listed as anonymous).
- The slowest code isn’t calculating collisions, it is the solver of physical equations (highlighted on the screenshot), a part that is responsible for calculating the final velocities. This means that focusing on improving collisions using spacial indexing would not help much in the current situation.
- The solver needs to go over a list of equations and update a pair of related bodies. While equations are simply iterated over, bodies need to be updated for each equation. Switching from
Dict
toArray
for bodies have improved performance quite a bit, butArray.set
still took more than 15% of the total time! This is where I realized I could group equations by a pair of bodies, because this is how they are created initially (each collision point generates three equations). This let meArray.get
andArray.set
only twice per equation group and reduce the Array usage a little bit, although it is still the bottleneck. The Elm compiler was really helpful in this restructuring. - I did a lot of micro optimizations too: started using record based linear-algebra, inlined vector operations, replaced record update syntax with a new record creation, used
a - b > 0
instead ofa > b
, expanded folds of records into tail optimized recursions, got rid ofArray
in theConvexPolyhedron
type, replaced functions that work onMaybe
with pattern matching. Please let know in comments if anything needs further explanation or you have more ideas!
API
The main problem of the old API was the necessity of keeping and passing around BodyId
. This made elm-physics responsible for generating unique ids: World.addBody
returned a tuple of a new world and the id of an added body, so it could not be simply chained. I addressed this by allowing to store user-defined data
type inside each Body data
: now simple examples can store a WebGL Mesh
inside a body, while more complex examples can tag each body with a custom user-defined id.
For simplicity, I provided basic constructors for bodies with a single shape. For advanced usage I implemented a function to create a compound body from a list of shapes. As a result, the Physics.Shape
module is not even required for simple cases.
There have been a lot of smaller improvements here and there. I am really grateful to Ian Mackenzie and Matthew Griffith for helping me!
You can check the new API here. Make sure to start with Physics.World
and Physics.Body
. By the way, it would be nice if the order of exposed modules from elm.json
was preserved on the package website. Not sure what is the best practice here, maybe I need to add my own table of contents.
New Features
The way I went about adding new features is through evolving the examples.
- When I initially built the Dominoes example, the dominoes were not sliding along each other, so I had to introduce materials to control friction and bounciness.
- I really wanted to interact with objects, so I figured out that I needed to add raycast support to be able to pick them. And then I added a point to point constraint to connect a mouse body with the selected body in the Drag example.
- Having added basic support for constraints, I wanted to see what else can be done with them. Using the hinge constraint the Car example has been built.
You can find the examples code in the elm-physics repository. Everything that is related to rendering has been extracted into common modules, so that the main modules are really focused on showing the API of elm-physics.
Future
I want to add API for applying forces to bodies, to e.g. allow controlling the car with a keyboard. I also want to improve the stability, because right now everything is being constantly recalculated and scenes with many touching bodies don’t calm down. Adding support for more shapes would be nice and some shapes would be quite simple, because a box is already implemented using a generic convex polyhedron. Collision events are still missing too.
Currently I’m preparing for Elm Europe, so please don’t expect new features soon. However I’m open to questions and suggestions. Please let me know if you want to contribute to the project or use elm-physics to build something cool!