An overview on Body Tracking for Meta Quest and Meta Quest Pro,
A usage guide for the Movement Body Tracking OpenXR extension,
And links to related topics.
What Is Body Tracking?
Body Tracking for Meta Quest and Meta Quest Pro uses hands and/or controller and headset movements to infer a user’s body poses. These body poses are represented as positions in a 3D space and are composed into a body tracking skeleton. Just like a a video can be composed from multiple still shots per second, repeatedly calling this API can be used to track the movements of the person wearing the headset. By mapping the joints of this skeleton onto a character rig the character can then be animated to reflect the human motions. Likewise, the position of the body can be used in game play to hit targets or to detect if the person has dodged a projectile. Note that while body poses are typically mapped to a humanoid rig, one can also map them to fantastical characters or animals.
XrBody Sample App
Build XrBody Sample App
Download the Oculus Mobile OpenXR SDK (v47 or later) and then build the XrBody sample application with:
adb uninstall com.oculus.sdk.xrbody
cd XrSamples/XrBody/Projects/Android
../../../../gradlew installDebug
Using XrBody Sample App
As a user, when you open the sample app in your ego view, you will see the skeleton joints on your body, arms, and hands drawn with the corresponding joint coordinate frames overlayed at that joint. The skeleton joints are the raw output of body tracking without any retargeting or modifications.
You can move the different joints of your body and notice from an ego view the skeleton output articulating the same way.
The Body Tracking OpenXR Extension
XR_FB_body_tracking introduces an extension to provide the output of body tracking which reconstructs the body skeleton from the three input points: the headset and both the hands/controllers. Because hand tracking and controller tracking is available on Meta Quest headsets, this extension works on these older headsets as well.
Note: That’s the “install-time” permission, so it will be granted automatically. For more details about permissions, read Install-time permissions.
OpenXR Initialization
Before the app gets access to functions of a specific OpenXR extension, you must create the OpenXR session and enable the required OpenXR extension. That part of the application is common for all extensions.
During initialization, you can create the following set of objects, which will be shared between all OpenXR extensions of the app:
The following sections provide instructions on the setting up needed to use the extension.
Include Headers
In your source code, include the following header for body tracking.
#include <openxr/fb_body_tracking.h>
Initialize OpenXR
Prior to using body tracking, you must initialize an OpenXR session and enable the extension. For details about session initialization, read Creating Instances and Sessions.
You must initialize the OpenXR extension once and share it between all calls to the OpenXR API. If you do it successfully, you will have the following data:
XrSession Session;
XrSpace StageSpace;
For details, see the SampleXrFramework\Src\XrApp.h header.
It is recommended to use the constant XR_FB_BODY_TRACKING_EXTENSION_NAME as an extension name.
Check Compatibility
You must check if the user’s headset supports body tracking. For a given XrInstance, you must receive the system properties through calling the xrGetSystemProperties function to validate this. To do so, use the XrSystemBodyTrackingPropertiesFB struct that describes if a system supports body tracking. Its definition follows.
If the bodyTrackingSystemProperties field of the XrSystemBodyTrackingPropertiesFB struct returns true, body tracking is supported.
Acquire Function Pointers
To create the body tracker, you must retrieve links to all the functions in the extension before usage. For details, read GetInstance ProcTor in the OpenXR spec. Here is an example on how to achieve this.
You can create a body tracker by using the xrCreateBodyTrackerFB function. This function creates and obtains a handle to a body tracker. Only one body tracker is allowed and multiple calls to this function will return the same handle. The handle is unique per process and cannot be shared across processes. For this call to succeed, your app must request the com.oculus.permission.BODY_TRACKING permission in the manifest, but a user is not requested to or required to grant this permission. The definition of the xrCreateBodyTrackerFB function is the following:
For details, see xrCreateBodyTrackerFB. To call this function you must use an XrBodyTrackerCreateInfoFB struct which describes the requested functionality of the body tracker. The struct’s definition is as follows:
To allocate space for retrieving joint location data, you must create an XrBodyJointLocationFB struct. This is a struct that contains the estimated position, inferred orientation, and simulated tracking status of a specific body joint. It is defined as:
XR_BODY_JOINT_COUNT_FB is an enum that represents the number of joints in the body skeleton including the hand joints.
To retrieve joint locations each time you need them, you must use the xrLocateBodyJointsFB function. This function is used to obtain the 70 body joint locations (18 core body joints + 52 hand joints) that are tracked by the body tracker at a given point in time. Its definition follows.
To use the data, make sure that body tracking is active and each joint location is valid, for example:
if (locations.isActive) {
for (int i = 0; i < XR_BODY_JOINT_COUNT_FB; ++i) {
if ((jointLocations_[i].locationFlags &
(XR_SPACE_LOCATION_ORIENTATION_VALID_BIT |
XR_SPACE_LOCATION_POSITION_VALID_BIT))) {
// the next joint location is in
// skeleton.joints[i].pose
.....
}
}
}
Retrieve T-pose Skeleton
The body skeleton is updated based on internal body skeleton calibration. When the skeleton size/proportions change, updates are indicated by incrementing the skeletonChangedCount counter, for example:
if (locations.isActive) {
if (locations.skeletonChangedCount != skeletonChangeCount_) {
skeletonChangeCount_ = locations.skeletonChangedCount;
// retrieve the updated skeleton
}
}
For allocating space to retrieve the skeleton in T-pose, use the XrBodySkeletonJointFB struct, which is a container that represents a joint in the body skeleton. An array of these structures is used to represent the joint hierarchy of the skeleton in T-pose, for example:
To retrieve the skeleton, call the xrGetSkeletonFB function which obtains the body skeleton in T-pose. By calling xrGetSkeletonFB, you query the skeleton scale and proportions in conjunction with skeletonChangedCount. The function definition follows:
The function returns a pointer to an XrBodySkeletonFB struct. The XrBodySkeletonFB struct is a container that represents the body skeleton in T-pose including the joint hierarchy. Its definition follows:
Note: In the returned skeleton, the joint location and orientation are relative to the parent joint and the location and orientation in space are not guaranteed. So, the skeleton is not intended for rendering and is meant for enabling retargeting. However, to visualize the skeleton by rendering it in the app, change the following flag and the T-pose skeleton will be rendered instead of tracking data.
bool displaySkeleton_ = false;
Destroy Tracker
It is a good practice to release resources before finishing the application. To do so, use the xrDestroyBodyTrackerFB function.