A lot happened since the previous post: performance improvements, simpler API and more features!
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.
- 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
Arrayfor bodies have improved performance quite a bit, but
Array.setstill 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 me
Array.setonly 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 > 0instead of
a > b, expanded folds of records into tail optimized recursions, got rid of
ConvexPolyhedrontype, replaced functions that work on
Maybewith pattern matching. Please let know in comments if anything needs further explanation or you have more ideas!
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.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.
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.
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!