Using AmmoJS with Babylon

I’ve been trying to figure out how to properly use Cannon or Ammo for physics in BabylonJS but the documentation on this is very sparse.

Starting with AmmoJS I’m using the npm package “ammo.js”, there also seems to be one called “ammojs” so I’m not sure which is correct but I believe the former is what I’ve seen used. I’m using Typescript and this is what I have written:

import * as Ammo from "ammo.js";
  async CreatePhysics(): Promise<void> {
    await  Ammo();
    this.scene.enablePhysics(
      new Vector3(0, -9.81, 0),
      new AmmoJSPlugin(true, Ammo)
    );
   }

I get an error about type definitions for ammo.js and it looks like none exist when I try to run
npm i --save-dev @types/ammo.js

I did find these topics in the forum:

Unfortunately, even after following what was mentioned there I still don’t get around this issue. I assume the definitions are preventing me from using this at all. Is there a way around this?

I’ve also tried using Cannon, but from what I’ve read it’s currently outdated. I don’t know if this would cause any issues with it being outdated. I started using “cannon-es” which is the updated version, but I also get an error when using that:
TypeError: this.world.add is not a function

According to this Github Issue:

It looks like this might have been resolved but I still get the issue using this code:
(using the cannon-es NPM package)
`import * as CANNON from “cannon-es”;

async CreatePhysics(): Promise {
//tried using this from the example in the Github issue
// window.CANNON = CANNON;

this.scene.enablePhysics(

  new Vector3(0, -9.81, 0),

  new CannonJSPlugin(null, null, CANNON)

);

}

`

I’m going to try using the outdated cannon NPM package, but if someone can tell me what I’m doing wrong here and how to use an updated physics engine I would greatly appreciate it. Thanks.

1 Like

For me, I am using Ammo.js typed package to get the typescript types properly, package.json:

"ammojs-typed": "^1.0.6"

Then I initialize it in my async function:

import Ammo from 'ammojs-typed'
const ammo = await Ammo()
let physics: AmmoJSPlugin = new AmmoJSPlugin(true, ammo)
scene.enablePhysics(new Vector3(0, -10, 0), physics)
5 Likes

Thank you, I wasn’t aware of the typed version of ammojs. I just tried it myself and it works, marked solution.

2 Likes

Hi!

I did the same thing what @Panuchka has written above. Unfortunately, I receive the following error:

Uncaught TypeError: Cannot set properties of undefined (setting 'Ammo')

I am using Vite as the bundler.

Any idea?

@RaananW might have an idea but at a first glance it looks like your import does not return anything for some reasons :frowning:

2 Likes

Would you be able to (minimally) reproduce that? a project, or some shared code?

I reproduced it here:

Thank you for your time and help! Appreciate it!

Seems like a module scoping issue. try this (instead of the line you have):

const ammo = await Ammo.call(this);
3 Likes

Oh wow thanks! It works! :smiley:

2 Likes

I’ve tried both const ammo = await Ammo(); and const ammo = await Ammo.call(this); directly inside an async function that runs my entire BabylonJS app, and I keep getting an Uncaught (in promise) TypeError: this is undefined message in my console. I even tried this as specified in the README for ammojs-typed:

const App = async function() {
    // ...

    await Ammo(Ammo).then(() => {
        scene01.enablePhysics(new Vector3(0, -9.81, 0), new AmmoJSPlugin(true, this));
    });

    // ...
}
App();

Still the same error. How do I call ammojs-typed in TypeScript for my case? I’m also using Vite

thats kind of an incomplete example, but arrow functions do not have a “this” context, which you may not be expecting? also await Ammo(Ammo) is wrong. If anything, it’d be
await Ammo().then((ammo) => { ammo maybe available here } . Idk if the ammo module returns itself, but either way, you’re probably better off just assigning ammo to the window instead of expecting it to be in the return value of the promise.

so like:
async funciton App() {

window.Ammo = await Ammo()

scene01.enablePhysics(new Vector3(0, -9.81, 0), new AmmoJSPlugin(true, window.Ammo));

}

@wavetro a repro could help @RaananW to do his magic a lot

@jeremy-coleman You’re right, I should probably provide more context.

@sebavan @RaananW You got it!

Here’s a copy of my dev environment: debug/houseDev at main · wavetro/debug · GitHub

Oh, I just posted a quick code snippet in the tutorial section on this topic. Getting ammo setup with webpack - #3 by bigrig

1 Like

@bigrig Legendary timing! Will look into how to apply this same method in Vite

UPDATE: This Webpack solution doesn’t seem to be as plug-and-play with Vite. I tried porting that Webpack method into a vite.config.js file like this, even after running npm install on kripken/ammo.js and fs:

export default {
    resolve: {
      alias: {
        fs: false,
        'path': false,
      },
    },
  }

Doesn’t seem to work…

change your index.html to:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>houseDev</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="./style.css">
  </head>
  <body>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script type="module" src="./src/app.ts"></script>
  </body>
</html>

then in app.ts

//you can can remove this
//import “ammojs-typed”;

window.Ammo = await Ammo()
scene01.enablePhysics(new Vector3(0, -9.81, 0), new AmmoJSPlugin(true));

(no errors)

maybe eventually you want to find a wasm copy of ammo and use that, but thats fine for now

1 Like

This is an emscripten issue that I hope they will resolve.
Short explanation - when running as es module, you don’t have the global context (when running functions in a module. i.e. - this is undefined). hence - it is not possible to assign anything to the global namespace. but emscripten is still trying to do it. and it fails.

fun fun fun!

The solution is simple! :slight_smile:

First import ammo:

import ammo from 'ammojs-typed';

then, in your scene init function use this:

const ImportedAmmo = await ammo.call({});
scene01.enablePhysics(new Vector3(0, -9.81, 0), new AmmoJSPlugin(true, ImportedAmmo));

Setting the context in which the ammo function is called is enough. And - it works:

Avoid polluting the global namespace, avoid importing using different mechanisms. Stick with vite and es modules - this is a wonderful way to get your app performing wonderfully, loading fast, and compiling even faster :slight_smile:

1 Like

It will actually load faster and compile faster by using the script tag. It will load faster because it will be parsed in a different thread by chrome and compile faster because it wont be compiled at all. Doesnt really matter tho i guess.

1 Like

Not sure what you mean exactly with

but i am not going to argue :slight_smile:
My very personal opinion is that if your system works one way, and your build system works one way, and the framework you have decided to use works one way, use this way.
I also will not use someone else’s CDN when serving production code, because then you trust a system you don’t control. But that’s a different issue.

Anyhow - there are 100 different ways to solve this. I can only provide my 2 cents, and the way I personally think will be the cleanest.

1 Like