Jump to content

Using Body shape for VR controller "touch" detection


photo

Recommended Posts

Hi,

We have a use case where the default VR sample contact detection is not precise enough, ie World BoundingBox and BoundingSphere are not valid option. The goal is to have a precise detection when a VR controller touch an object (always convex though).

I thought using Body shapes could be a good idea, as I couldn't find a way to get more precise intersection with World Intersection. So I simply added a convex hull on the Vive pad and on the objects of concern in my scene (as Rigid Bodies), unaffected by gravity.

(note: I have multiple of these objects in the scene, and some may be interpenetrating: this is due to the fact that I must define convex shapes, and some are intricate, example a tube poking out of a box)

I then added a onContactCallback on the VR controller rigid body in VRPlayerVR::controllers_init(), to detect when the VR controller would touch the other bodies.

  • While I kept the Shapes simple, It looks like sometimes a contact will produce a small stutter.  Is it normal?
  • When I draw the shapes in the call with body->renderShapes(), it looks like during the collision the shapes are vibrating (as if they are repulsing and forced back into position). Is it normal?
  • My idea was to add a Touch Component on the object I want a precise detection with, and call this component "touch" function from the onContact callback of the VR controller. But I can't find a way to do this.

After too many hours of trying, I resort to your help :)

  • Is it the correct way to do this? (shape precise collision with a controller)
  • what am I doing wrong? (probably mis-using the API)

Thanks!

Link to comment

Hello,

The shaking of controllers and objects can be due to the fact that we are changing the transformation of the controller node, and not the transformation of the rigid body. Transformations must be applied when updating physics, for example, using the setVelocityTransform method. To keep the node from falling behind the rigid body, you can use the flushTransform method. In the callback when a contact appears, you can get the object that the body collided with. For example:

void VRPlayerOpenVR::on_contact_enter(BodyPtr body, int num)
{
	int num_contacts = body->getNumContacts();
	if (num < 0 || num_contacts <= num)
		return;

	ObjectPtr obj = body->getContactObject(num);
	if (obj == nullptr)
		return;

	Log::message("frame %d contact enter: %d %s\n", Engine::get()->getFrame(), num, obj->getName());
}

And then you can get a component from this object and call the necessary methods.

The example below is based on a vr template. The main changes in the VRPlayerOpenVR class are in the following functions: controller_update, controller_update_physics and on_contact_enter. There also small changes to the teleport update in VRPlayerVR::teleport_update.

 

vr_test_package.zip

  • Like 2
  • Thanks 1
Link to comment

Hi, thanks a lot for the code sample and the explanation!

Though I notice something peculiar: If the body I assign is a Dummy, then in on_contacts_enter, body->getContactObject(num) will return the object that the controller touched. But if the body I assign is a Rigid, then getContactObject will return the vive pad itself !?!? Why?

As the callback was put on the controller body, I would expect that the callback will be always be called with the contacted body, not the caller's body. Or do I misunderstand something?

Sidenote: body->getName() will return a nullptr if no name was assigned to the body. I found this very unexpected and crash prone, and I think a good practice would have been to always return a valid string, though empty if no name was assigned. This is also the case for other occurrences of getName() in your API (eg plugin).

EDIT: My crashes were actually due to the fact that body->getContactBody1(num) can return a nullptr. Strange, though, that a collision can happen with only one body?

Thanks!

Edited by Amerio.Stephane
Link to comment
6 minutes ago, silent said:

If you have body<->surface contacts you may have similar situation.

Surface Collision and Physics Interaction are disabled for both the vive pad and the objects I want to interact with.

Link to comment

The documentation mentions the following about physical contacts:

Quote

A contact can be handled by any of the bodies that participate in it. To which body a contact is assigned is random. If the contact is assigned to and handled by the body it is called an internal one, otherwise it is called external (handled by another body). The total number of contacts for the body includes all, internal and external ones. Iterating through internal contacts is much faster than through external ones, thus you might want a certain body to handle most of the contacts it participates in. This can be done for a rigid body by raising a priority for it via the setHighPriorityContacts() method.

The callbacks are only called from the BodyRigid, but for both bodies. Due to the random choice of the order of the bodies, we get different objects in the callback.

Example:

(controller_body + object_body) = (contact(controller_body, object_body) or contact(object_body, controller_body))

We get two callback options: on_enter(controller_body, num) and on_enter(object_body, num) for each object.

The contact type can be found using body->isContactInternal(num) in the callback. And for the controller you can set High Priority Contacts.

 

Also I tried to test the case where body->getContactBody1(num) returns a null pointer. This only happens if there is a collision with the surface, not with the shape. Can you try assigning a separate mask for collisions between controller and interactive objects?

Link to comment
×
×
  • Create New...