Unable to move physics bodies with setDeltaPosition

I have a map (a collection of gltf meshes that all belong to the same parent root node) that gets loaded and a mesh for the players character. I am able to move the player mesh around as expected, and it seems to be working nicely with the ammo js physics engine / plugin.

The meshes of the map however, are an entirely different matter.

I’ve noted that each of the following map mesh position values are different:

  • mesh.position
  • mesh.physicsImposter.getObjectCenter()
  • mesh.physicsImposter.physicsBody position using this code:
export const getPositionFromPhysicsBody = (physicsBody: PhysicsBody, Ammo) => {
  tmpTrans = new Ammo.btTransform()
  const motionState = physicsBody.getMotionState()
  motionState.getWorldTransform(tmpTrans)
  return tmpTrans.getOrigin()
}

Further still, using mesh.physicsImposter.setDeltaPosition(new Vector3(0, 10, 0)), or any xyz valued vector, has no affect on any of the aforementioned positional values.

The map meshes all have their physics imposter mass set to zero, and are static bodies. I don’t use any physics root on any of the map meshes, and I set them up like so:

export const convertToImposter = (
  {
    meshes,
    scene,
    mass,
    isGround = false
  },
  imposterType = 'BoxImpostor',
) => {

  const rigidBodies: Array<PhysicsBody> = []
  const staticBodies: Array<PhysicsBody> = []

  let rootNodeMass
  if (isGround) {
    rootNodeMass = 0
  } else if (mass !== undefined) {
    rootNodeMass = mass
  } else {
    rootNodeMass = 3.0 
  }

  const childMass = isGround ? 0 : 0.1

  let mesh:Mesh

  for (let i = 0; i < meshes.length; i++) {
    mesh = meshes[i] || new Mesh('appease-typescript')
    if (mesh.id === '__root__') {
      continue
    }

    //@ts-ignore
    const parent = mesh.parent
    //@ts-ignore
    mesh.parent = null

    mesh.physicsImpostor = new PhysicsImpostor(
      mesh,
      PhysicsImpostor[imposterType],
      { mass: childMass },
      scene
    )
    const {x, y, z } = mesh.physicsImpostor.getObjectCenter()
    mesh.physicsImpostor.setDeltaPosition(new Vector3(x, y, z))
    
    if (isGround === false) {
      rigidBodies.push(mesh.physicsImpostor)
    } else {
      staticBodies.push(mesh.physicsImpostor)
      mesh.position.x = x
      mesh.position.y = y
      mesh.position.z = z
    }
    mesh.parent = parent
  }

  return {  rigidBodies, staticBodies }
}

The last puzzling thing I noticed, is that if I scale the map meshes by some scalar value, say 10, then the physics bodies have their position coordinates multiplied by -10???

I’m pretty confused, and would appreciate any help at this point.

Hello @arcman7!

Which Physics engine do you use?

EDIT: Damn I need to put on my glasses next time before reading and asking :smiley: Ammo…

1 Like

Lol no worries.

Btw, I think the multiplication by -10 comes from this bit of code in the ammo js pluggin

                if (impostor.type !== PhysicsImpostor.MeshImpostor && impostor.type !== PhysicsImpostor.NoImpostor) {
                    const boundingInfo = impostor.object.getBoundingInfo();
                    this._tmpVec3.copyFrom(impostor.object.getAbsolutePosition());
                    this._tmpVec3.subtractInPlace(boundingInfo.boundingBox.centerWorld);
                    this._tmpVec3.x /= impostor.object.scaling.x;
                    this._tmpVec3.y /= impostor.object.scaling.y;
                    this._tmpVec3.z /= impostor.object.scaling.z;
                    impostor.setDeltaPosition(this._tmpVec3);
                }

Everytime I get in trouble with the a physics engine in BabylonJS and something is not working as expected I try to use the engine’s native function like this:

    sphere.physicsImpostor.executeNativeFunction((world, physicsBody) => {
        
    })

Maybe you could give it a try.

1 Like

There are two scenarios:

    var physicsPlugin = new BABYLON.AmmoJSPlugin();
    scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), physicsPlugin);

    sphere.physicsImpostor = new BABYLON.PhysicsImpostor(sphere, BABYLON.PhysicsImpostor.SphereImpostor, { mass: 1, restitution: 0.9 }, scene);

    const pos = new BABYLON.Vector3(2, 0, 0)
    sphere.physicsImpostor.setDeltaPosition(pos) // this moves only the impostor

        scene.onReadyObservable.addOnce(() => {
        sphere.physicsImpostor.executeNativeFunction((world, rigidBody) => {
            const worldTransform = rigidBody.getWorldTransform()
            const origin = worldTransform.getOrigin()
            sphere.position.x = origin.x() // sync sphere position with the impostor position
        })
    })
    var physicsPlugin = new BABYLON.AmmoJSPlugin();
    scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), physicsPlugin);

    sphere.physicsImpostor = new BABYLON.PhysicsImpostor(sphere, BABYLON.PhysicsImpostor.SphereImpostor, { mass: 1, restitution: 0.9 }, scene)

    sphere.position.x = 2 // this moves the sphere and the impostor as well

    scene.onReadyObservable.addOnce(() => {
        sphere.physicsImpostor.executeNativeFunction((world, rigidBody) => {
            const worldTransform = rigidBody.getWorldTransform()
            const origin = worldTransform.getOrigin()
            console.log(world, origin.x()) // this outputs 2
        })
    })

Hopefully the code is self explanatory :slight_smile:

If I get your problem correctly tho…

1 Like

I am loading an gltf file as the map (it contains all the meshes being used), and I made a map class that wraps it but otherwise does not alter any babylon js behavior.

map.getMeshPhysicsBodyLocation(7)
// {x: -0.20945030450820923, y: -0.9680235385894775, z: -17.262126922607422}
map.meshes[7].physicsImpostor.setDeltaPosition(new BABYLON.Vector3(0,10,0))
// undefined

map.getMeshPhysicsBodyLocation(7)
{x: -20.94510269165039, y: 1716.213134765625, z: 96.79624938964844}

map.meshes[7].physicsImpostor.setDeltaPosition(new BABYLON.Vector3(0,10,0))
// undefined
map.getMeshPhysicsBodyLocation(7)
//{x: -20.94510269165039, y: 1716.213134765625, z: 96.79624938964844}

I ran this verbatim and I don’t understand this behavior.

EDIT: Okay I just realized executeNativeFunction is an actual function and not a placeholder like I assumed. I’ll try that as well.

@roland Okay, I’ve found that just changing the position of the mesh like you’ve done changes the position of the physics body as well, but only AFTER some time has passed. I don’t know know why or how to tell when that will be - do you know if there’s an event or callback hook I can use?

It’s a direct function call so it should be applied immediately. I am on mobile and feel like shit after getting drunk yesterday at a birthday party so I can’t recall the exact name of the function but there is a method like world.update() . Try to call it after setting the position.

1 Like

maybe @Cedric or @RaananW can help

Some explanation on different transform, center and delta position:

First, a PG :

Ammo (and other physics engine) assume the collision shape has its origin at the center. Box, sphere,… origin at center so the shape extend in every direction. Like this:

image

But, for some reason, you displayed shape (babylon mesh) might not have its origin at the center. It might be anywhere, even outside the volume described by the triangle.

image

There need to be a value that translates from the mesh origin to the physics origin. And it must happen back and forth because user might change the babylon mesh position and physics must be updated and after the physics engine tick, the mesh must be placed at the correct position. This value is the deltaPosition.

When creating a new PhysicsImpostor (let’s say for a sphere but it applies to any other type), the world bouding box is computed from the provided mesh, the world center is computed to be in the middle. The delta position is the difference between this bound center and the mesh origin.

In the PG, you’ll see the difference between mesh position, delta position and physics transform origin. Impostor center will be the same because the collision shape (as seen by Ammo) are at the same place. But meshes are not.

2 Likes

@Cedric Okay, reading through this tomorrow, I’ll have the whole day to code!

But for reference here’s a an example gltf map I’m trying to use to practice with. Going off what you’ve mentioned there must be some cryptic relations between the individual meshes, their respective transform nodes and the root mesh.

m = map.meshes[117]
m.position //  Vector3 {_isDirty: false, _x: 914.8487074799641, _y: 1773.7036397114935, _z: -1285.8860054893194}
m.physicsImposter.getObjectCenter() //  Vector3 {_isDirty: true, _x: 947.9129190444946, _y: 1413.424593925476, _z: -1082.724739074707}
m.physicsImposter._deltaPosition //  Vector3 {_isDirty: true, _x: 0, _y: 0, _z: 0}
map.getPhysicsBodyLocation(117) //  {x: 1053.236572265625, y: 1537.0677490234375, z: -1236.4315185546875}

How is that possible? My understanding is that if the delta position is a zero vector, then the physics body MUST be at the object center.

yes, a 0 delta vector mean the origin of the physics body is at the origin of the mesh. Or, in other words, mesh is centered around 0.

1 Like

So I’ve tried to start over by using a set of meshes I export from blender. I still see the same weird positioning of all meshes at 0,0,0, but their object center -mesh.getBoundingInfo().boundingSphere.centerWorld
- holds their true position.

I think there’s a series of transforms being applied to the each of the meshes that make up the map in a way that it connects them appropriately (like puzzle board with all it’s pieces correctly positioned). Is there a way to re-export them from a Babylon sandbox such that the meshes don’t need any transformations? Like why not just have their object center be 0,0,0 and their position be their actual position after all the transforms would have been applied?

Or is that what’s already happened I’m thinking of this backwards?

You can bake the vertices using the matrices and re-export using inspector to a .glb.
I’m not sure of what you want to understand doing that.

1 Like

If the transformations are baked in, they don’t have to be reapplied to the meshes imposters on the fly every time the page loads. This seems like a more performant way to go about it, right?

It’s also a lot easier for me to go about debugging the collision meshes if I can enable edge rendering on them, and visually confirm that they are in their correct positions. It’s a bit harder to do this when they need various rotations and displacements after scaling.

So when I realized that the meshes origin was outside of the mesh itself, I tried various things to work around that while still using the babylon js ammo plugin. This did not work for me, and I essentially ended up just accessing the ammo js api directly.

I seriously recommend to anyone using meshes that are centered on a coordinate outside of their own volume to use this approach as well. I wasted so much time trying to find a transform scheme that would work. That being said, I still just the babylon ammo js physics imposters for regular meshes with an origin centered on themselves (0,0,0).

1 Like

Hello @arcman7 just checking in, was your question answered? :smiley:

@carolhmj Yes!

Another thing I’ll add is that integrating the babylon inspector into my project seriously aided me in working towards the solution I wanted. Once you are able to toggle the physics wireframe mesh, and just the regular wireframe mesh overlay, it becomes a much more visual problem and solving it becomes a lot easier.

3 Likes