Porting Bullet Physics into Ammo.js and improving Physi.js

I thought it will be easy

Alexander Buzin
WhitestormJS Framework
4 min readApr 14, 2016

--

The goal was to update ammo.js in Physi.js plugin that is a complete physics for Three.js

Why Physi.js?

It’s simple — because it supports concave objects. It seems like it’s the only physics for JavaScript that supports it.

Some more features why I chose it as a physics library for WhitestormJS framework:

  • Web Workers. I found it very useful for large calculations like for physics and rendering. It works well, because Web Workers provides mutithreading and it is possible to move your calculations in two different threads that will work independently from each other.
  • It’s very easy. As I said before, it is a physics plugin for Three.js. It means that you can use it only with Three.js and nothing else. But this is also a good part, because it will be much easier in usage than Oimo.js or Cannon.js that both can be used with Three.js too.
  • Fast and powerfull. Physi.js is just a wrapper around Ammo.js and Three.js. It gets all its power from Ammo.js (to be correct — Bullet Physics). Ammo.js (minified version) weight is more than 1.75Mb while other physics libraries are about 100Kb which are ~18 times smaller. Every kilobyte in web development is on balance. Large libraries make your site slow, but not with Web Workers, they will load it in independent thread asynchronously.

What scared me in Physi.js

When I started learning Physi.js structure on GitHub I noticed that Ammo.js library there hasn’t been updated for 3 years. I compared that time with an Ammo.js repository. They updated it 2 months ago. First idea that came up into my head was to replace existing one in Physi.js with the newest.

First troubles. Not as easy as I thought.

That’s not that Ammo.js

I was afraid that Physi.js might not work on a newer Ammo.js after 2 fixed examples (heightfield and basic). The problem was not in Physi.js directly, it couldn’t find some necessary functions that are needed to make vehicle working.

After some attempts and research I understood that I should make my own custom build of Ammo.js.

Installing emscripten

My troubles began there. Installing emscripten to compile c++ libraries to JavaScript isn’t so easy. Even it’s like to look for a needle in a haystack. I followed all instructions on a website, installed & updated. But it did not work. My bash windows showed me several errors in compiling. I found this issue on GitHub.

Attempt #2

I tried to install 3 new packages: clang-tag-e1.36.1–64bit, emscripten-tag-1.36.1–64bit, sdk-tag-1.36.1–64bit like they said. I has gone deeper. It said me that i need to install cmake. I installed cmake. It showed me another error that it can’t be installed(after 2 hours of installing off course)…

Attempt #3

No chances…

I asked Jack Dalton to install it on linux and try to compile… It solved the problem. Happy tux!

Compiling a custom version of Ammo.js

Second challenge

We successfully compiled existing version of Ammo.js. Nothing was changed, but now we are sure that it works!

WebIDL

According to emscripten website:

The WebIDL Binder provides a simple and lightweight approach to binding C++, so that compiled code can be called from JavaScript as if it were a normal JavaScript library.

The WebIDL Binder uses WebIDL to define the bindings, an interface language that wasspecifically designed for gluing together C++ and JavaScript. Not only is this a natural choice for the bindings, but because it is low-level it is relatively easy to optimize.

…but it’s not so simple. Сonversely. It would be simple if we we had a full documentation that describes WebIDL, but we don’t have it yet. First thing that we imported to Ammo.js library with the help of ammo.idl file was btRotationalLimitMotor. That’s how it looks in WebIDL format:

interface btRotationalLimitMotor {
void btRotationalLimitMotor();
void btRotationalLimitMotor([Const, Ref] btRotationalLimitMotor limot);
attribute float m_loLimit;
attribute float m_hiLimit;
attribute float m_targetVelocity;
attribute float m_maxMotorForce;
attribute boolean m_enableMotor;
boolean isLimited();
boolean needApplyTorques();
};

btTypedConstraint:

void setLinearUpperLimit([Const, Ref] btVector3 linearUpper);
void setAngularLowerLimit([Const, Ref] btVector3 angularLower);
void setAngularUpperLimit([Const, Ref] btVector3 angularUpper);
// and here as a method returning this object.
btRotationalLimitMotor getRotationalLimitMotor(long index);
};btGeneric6DofConstraint implements btTypedConstraint;

Correcting Physi.js code

Bullet Physics removed getRigidBodyA() and getRigidBodyB() methods in btGeneric6DofSpringConstraint class. That’s not a problem for Ammo.js, I just set them as constraint.a and constraint.a attributes right in the physijs_worker.js source code. No need to compile Ammo.js again, it solved the problem well.

More changes in ammo.idl

Other changes in ammo.idl

Results

FPS increased from ~50 to ~110

I compared results that i achieved on the same example (Jenga) running both variants of Physi.js library.

OMG!! OMG!! OMG!!!!!! ~110 fps!!!

New one is 2x times faster than old

New one really looks much smoother. Newer physics core does amazing. I also fixed the problem that stops physics processing when you have more than one scene on a web page.

Links

Custom Ammo.js

Whitestorm.js

--

--

Alexander Buzin
WhitestormJS Framework

🚀 Technical founder & startup enthusiast. 10+ years in the IT industry. Featured on hackernoon & TechCrunch