Diarmid Mackenzie
Diarmid Mackenzie

Reputation: 826

How to move a dynamic body with A-Frame physics system, when using Ammo driver?

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?

Upvotes: 1

Views: 2560

Answers (3)

gearcoded
gearcoded

Reputation: 616

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.

Upvotes: 1

Piotr Adam Milewski
Piotr Adam Milewski

Reputation: 14655

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/[email protected]/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>

Upvotes: 3

Diarmid Mackenzie
Diarmid Mackenzie

Reputation: 826

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

Upvotes: 2

Related Questions