Position Mesh based on position and orientation of WebXR Controller

I have mesh that has some GUI objects attached. I setup a toggle visibility feature that will show and hide this object when pressing the Y button on a VR controller.

When I show this mesh using the Y button, I’d like to position it near to the player, relative to the left controller and based on the direction that the controller is pointing. I can place it in the exact location on the controller by using controller.grip.position. I thought that I might be able to use the forward vector to get a position in front of the controller, so I tried controller.grip.forward but that always positioned the mesh in the same direction in world space, regardless of where the controller was pointing/facing. Or to put it another way, I think the forward vector on the grip does not consider the orientation of the controller.

What is a better way to get the coordinates of a point in space relative to VR motion controller?

cc @RaananW

Yes, there is :slight_smile:

The WebXRInputSource class has the function getWorldPointerRayToRef, which will give you a forward ray from the grip or pointer :slight_smile:
https://doc.babylonjs.com/typedoc/classes/babylon.webxrinputsource#getworldpointerraytoref

The controller variable is an XRInputSource, so you can use this function directly

1 Like

Thanks! It took me a little while to figure out how to use getWorldPointerRayToRef and I’m still not sure I understand it. I found this playground by searching for getWorldPointeRayToRef.

Here is what I did

             const tmpRay = new BABYLON.Ray(
                controller.pointer.absolutePosition,
                controller.pointer.forward,
                0.5
              );
              controller.getWorldPointerRayToRef(tmpRay, true);

              const newPosition = new BABYLON.Vector3(
                tmpRay.origin.x + tmpRay.direction.x,
                tmpRay.origin.y,
                tmpRay.origin.z + tmpRay.direction.z
              );
  1. Create a ray using the controller pointer position and forward vector, with a length of 0.5
  2. Called controller.getWorldPointeRayToRef
  3. Used the ray origin and direction to calculate a position in space

This works for what I’m trying to do.

I am a little confused about step 2 though. What does this do? The name of the function seems to indicate that it would return a ray, but the documentation says it returns void and they in needs to have a ray passed in.

If I comment out controller.getWorldPointerRayToRef(tmpRay, true) my positioning code still works.

I guess I have what I need, but I don’t understand how or why it works and how getWorldPointeRayToRef is involved.

Yes, this is a “ToRef” function. it takes an already-existing ray and updates it. it is done this way to avoid too many garbage-collection calls every time you call this function. So you need to create an empty ray, pass it to the function, and then use the ray you have passed. And the function returns void, because the actual work is done with the variable passed to it.

Interesting :thinking:

From what I can tell, these two bits of code to the same thing, at least for my purpose.

Option 1:

  • Create a ray using the grip position and forward vector
              const tmpRay = new BABYLON.Ray(
                controller.grip.absolutePosition,
                controller.grip.forward,
                0.25
              );

Option 2:

  • Create an empty ray
  • Pass the empty ray to getWorldPointerRayToRef which will update its data (origin, direction, length?)
             const tmpRay = new BABYLON.Ray(
                new BABYLON.Vector3(),
                new BABYLON.Vector3(),
                Infinity
              );

              controller.getWorldPointerRayToRef(tmpRay, true);

After using either of those options, I can calculate a position using the origin and direction on the ray.

              const newPosition = new BABYLON.Vector3(
                tmpRay.origin.x + tmpRay.direction.x,
                tmpRay.origin.y,
                tmpRay.origin.z + tmpRay.direction.z
              );

The only difference seems to be if I need a reference or not.

For this use, I only need to calculate this position once. I don’t want to “attach an object” to the controller. Instead, I want to move an object to this position when pressing a button.

It seems like the reference would be useful for attaching something to the controller, having it updated in real time. Is there a reason to use Option 2 if I’m not doing that?

1 Like

The difference between doing it yourself and calling the function is mainly cosmetic if everything is set “correctly”. For example, some controllers don’t have the pointer. in that case, it will still return a forward ray, but from the pointer. It also deals with RHS if needed. It is just a helper function, you can of course do it yourself :slight_smile:

Got it. I’ll generally do things the recommended way. I wanted to make sure I wasn’t missing anything. Thanks for your help explaining this to me.

1 Like