1

This previous question & answer explain how to move a dynamic body in A-Frame physics system, by using the syncToPhysics() method call after making changes to position. How to translate A-Frame dynamic body

I've been using this technique, and it works well for me when using the CANNON.js driver.

See e.g. https://unmarred-ambiguous-shad.glitch.me/

However, I would like to use the Ammo driver, as described here. https://github.com/n5ro/aframe-physics-system/blob/master/AmmoDriver.md

(one key reason for wanting to use the Ammo driver is that at this time, CANNON.js does not work with A-Frame 1.2+ https://github.com/n5ro/aframe-physics-system/issues/187 ).

My hope was that I could do the same thing with syncToPhysics() on the ammo-body component:

this.el.components["ammo-body"].syncToPhysics();

But I tried to do so in this glitch, and it's not working - the body is just stuck in one place. https://roasted-snow-replace.glitch.me/

Is there any way to get this function working with Ammo, or is it impossible to directly manipulate the position of dynamic bodies when using the Ammo driver?

  • in the future, probably better to share glitch links to the code, not just to the demo, if you are asking questions about the code written itself. Easier to work with than opening the sources tab or manually manipulating the URL for those that happen to know how. – Kyle Baker Sep 04 '21 at 11:57

3 Answers3

2

One possible solution is to directly set a Linear Velocity in Ammo. This eliminates the need to update the physics engine with the new position data.

Due to friction the block will slow down, so you need to keep setting it for a constant velocity (also probably possible to configure materials to eliminate friction).

Also the tick function starts firing before Ammo WASM is fully initialized, and if you try to create a new Ammo.btVector3 too soon, this hits an error.

After some experimentation, it looks like checking for the existence of Ammo.asm.$ is a good way of determining when the WASM stuff is ready. But there may be a better solution here.

        tick: function(time, timeDelta) {

          // Ammo WASM takes a moment to initialize.
          // Don't try to do anything that involves creating Ammo objects until
          // Ammo.asm.$ exists.

          if (Ammo.asm.$) {       
            const velocity = new Ammo.btVector3(1, 0, 0);
            this.el.body.setLinearVelocity(velocity);
            Ammo.destroy(velocity);
          }
        }

New glitch including this solution...

https://pinto-big-pecorino.glitch.me/

A couple of references I used to help with this answer: BulletPhysics (ammo.js) - How would you go about applying force to an object?

https://github.com/n5ro/aframe-physics-system/blob/master/AmmoDriver.md#using-the-ammojs-api

  • 1
    As per another answer, better to use the "body-loaded" event on the element, to determine when Ammo is ready (rather than checking for Ammo.asm.$). – Diarmid Mackenzie Mar 01 '21 at 16:00
2

This isn't elegant, but the only solution I found to "teleport" an object is to:

  • set the body collision flag to KINEMATIC (2) - otherwise bullet just ignores the motionstate update
  • apply the new position (via syncToPhysics() when using aframe physics)
  • reset the body velocity, so the object won't bounce right after the teleport
  • restore the original flags (via updateCollisionFlags() when using aframe physics)

Something like here:

<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://mixedreality.mozilla.org/ammo.js/builds/ammo.wasm.js"></script>
<script src="https://cdn.jsdelivr.net/gh/n5ro/aframe-physics-system@v4.0.1/dist/aframe-physics-system.js"></script>

<script>
  AFRAME.registerComponent("moving-dynamic-body", {
    init: function() {
        // wait until the physics engine is ready
        this.el.addEventListener("body-loaded", e => {
          // cache the ammo-body component
          this.ammoComponent = this.el.components["ammo-body"];
          // use this vector to zero the velocity
          // keep in mind this needs to be deleted manually from the memory with Ammo.destroy(this.zeroSpeed)
          this.zeroSpeed = new Ammo.btVector3(0, 0, 0);
        });
      }

      ,
    tick: function() {
      // wait for the physics 
      if (!this.ammoComponent) return;
      
      // restore stuff if the "teleport magic" has been done in the last renderloop.
      // this should probably be done in steps instead of tick
      if (this.collisionFlag === 2) {
        // this just tells us that we reverted to normal
        this.collisionFlag = 0;
        // restore the original collision flags
        this.ammoComponent.updateCollisionFlags();
        // reset the speed, or the body will bounce away
        this.ammoComponent.body.setLinearVelocity(this.zeroSpeed);
      }
   
      // if the body is below 1m
      if (this.el.object3D.position.y < 1) {
        // set the THREEJS position.y
        this.el.object3D.position.y = 2;
        // change the collision flag to the KINEMATIC_BODY
        this.collisionFlag = 2;
        // apply the flag
        this.ammoComponent.body.setCollisionFlags(this.collisionFlag);
        // sync the physisc transforms to the THREEJS transform
        this.ammoComponent.syncToPhysics();
      }
    }
  });
</script>

<a-scene physics="driver: ammo; debug: true; debugDrawMode: 1;" renderer="colorManagement:true">
  <a-box id="test1" ammo-body="type: dynamic" ammo-shape="type: box" material="color:#dd1111" height="0.1" width="0.1" depth="0.1" position="0 2 -1" moving-dynamic-body>
  </a-box>
</a-scene>
Piotr Adam Milewski
  • 14,150
  • 3
  • 21
  • 42
  • Thanks. Good to know there's not a straightforward way to do this, as there is with CANNON.js. I had wondered if I was missing something simple, but it seems not... I think moving things using real physics like velocity & forces is probably a better route to go where possible... – Diarmid Mackenzie Mar 01 '21 at 15:54
  • Thanks also for posting a better way to determine when the physics engine is ready! this.el.addEventListener("body-loaded", e => { – Diarmid Mackenzie Mar 01 '21 at 15:55
  • @DiarmidMackenzie If you just want to "move" the object, then using velocity and forces (tho forces may be unpredictable) is good. On the other hand if you want to completely change a position (reset, teleport - the linked question was about syncing cannon physics to threejs so I went that route) I'm afraid It may not be so simple. – Piotr Adam Milewski Mar 01 '21 at 16:03
  • "forces may be unpredictable"... yes, had a horrible time with forces - managed to make the object spin, but not move, even when I applied the force right at the center... The origial question was ambiguous and didn't properly distinguish frame-by-frame movements from teleports. But I now understand the possible solutions for both cases. – Diarmid Mackenzie Mar 01 '21 at 16:13
1

I created a fork of the aframe-physics-system package to make it at least partially compatible with A-Frame 1.2. Here is the updated file:

https://github.com/gearcoded/aframe-physics-system/blob/master/dist/aframe-physics-system.js

I didn't test everything, but the scene editor, gravity, and collision work.

gearcoded
  • 561
  • 5
  • 11
  • 1
    I can vouch for this. Using it in a client project right now, built out a demo of super-hands that works with this. Hopefully this work gets upstreamed, though the changes look pretty drastic and it would be interesting to have more insight into what changes were made and how this work was done. I hope you contribute a pull request if you haven't yet and lobby for it, the lack of this kept me stuck in A-Frame 1.0.4 for quite a while. – Kyle Baker Sep 04 '21 at 11:31