Shared Spatial Anchors
Health & Safety Recommendation
While building mixed reality experiences, we highly recommend evaluating your content to offer your users a comfortable and safe experience. Please refer to the Health and Safety and Mixed Reality Design guidelines before designing and developing your app.Important: Co-location increases the number of individuals in a shared physical space with restricted visibility of their surroundings. Crowded experiences create safety risks. It is important to be mindful of the occupancy of the playspace for the shared experience being built. Reference
Shared Spatial Anchors Design Guidelines for more guidance.
Shared Spatial Anchors enable users in the same physical space to share Spatial Anchors, facilitating local multiplayer experiences. By creating a shared world-locked frame of reference, multiple users can interact in a common virtual environment. For instance, several individuals can gather around a table and engage in a virtual board game.
- XR_FB_spatial_entity
- XR_FB_spatial_entity_storage
- XR_FB_spatial_entity_query
- XR_FB_spatial_entity_storage_batch
- XR_FB_spatial_entity_user
- XR_FB_spatial_entity_sharing
Our Spatial Anchors follow an entity-component system architecture pattern. Each Spatial Anchor is an entity represented by a unique identifier (UUID) that is assigned upon creation and remains constant throughout the life of the Spatial Anchor, including after a Share-Download cycle. The UUID of the Anchor is not unique per localization map, rather, it is unique per Anchor. If the same Anchor gets distributed across multiple devices as a Shared Spatial Anchor, it would have the same UUID.
Shared Spatial Anchor functionality is offered through the following component defined in the
XrSpaceComponentTypeFB enum:
- Sharable: You can use the persistence operation save to persist the Spatial Anchor to the cloud, and use the share operation to share it with users that you specify.
For the complete API reference as part of the
OpenXR spec, read:
Set Up Shared Spatial Anchors
To add Shared Spatial Anchors to your project, do the following.
Add Permission to Android Manifest To enable Shared Spatial Anchors, add the following to the Android manifest :
<uses-permission android:name="com.oculus.permission.IMPORT_EXPORT_IOT_MAP_DATA" />
In your source code, add the following lines to include the essential extension headers:
#include <openxr/openxr.h>
Check System Compatibility For example:
const char* deviceQuestProName = "Meta Quest Pro";
bool isSharedSpatialAnchorsSupported = strcmp(systemProperties.systemName, deviceQuestProName) == 0;
Enumerate OpenXR Extensions Enumerate the OpenXR extensions by using the following:
const char* const requiredExtensionNames[] = {
XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME,
XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME,
XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_USER_EXTENSION_NAME,
XR_FB_SPATIAL_ENTITY_SHARING_EXTENSION_NAME};
const uint32_t numRequiredExtensions =
sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]);
Function Pointers Hook Up To use the functions defined in the extension headers, you must hook up the function pointers to their implementation by using xrGetInstanceProcAddr
. The following is an example of what to add to your project to do this:
xrGetInstanceProcAddr(
instance, "xrSaveSpaceListFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrSaveSpaceListFB));
xrGetInstanceProcAddr(
instance, "xrCreateSpaceUserFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreateSpaceUserFB));
xrGetInstanceProcAddr(
instance, "xrGetSpaceUserIdFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceUserIdFB));
xrGetInstanceProcAddr(
instance,
"xrDestroySpaceUserFB",
(PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroySpaceUserFB));
xrGetInstanceProcAddr(
instance, "xrShareSpacesFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrShareSpacesFB));
Spatial Anchors Sample App
The XrSpatialAnchor sample project for OpenXR is included in the
Oculus Mobile OpenXR SDK. It demonstrates the capabilities of our Spatial Anchor system and provides example code for handling, maintaining, and sharing Spatial Anchors that you can use in your own project. You can find the project in the folder
XrSamples\XrSpatialAnchor
.
XrSpatialAnchor App User Experience The user is expected to:
- Press A on the right Meta Quest Touch controller to place a persistent Spatial Anchor that mirrors the pose of the right controller. When you do that, the system will place a rectangular object at the origin of the spatial anchor aligned to its frame of reference.
- Press B on the right Meta Quest Touch controller to delete the most recent persistent Spatial Anchor and remove the digital objects attached to it.
- Press X on the left Meta Quest Touch controller to retrieve previously persisted Spatial Anchors.
- Press Y on the left Meta Quest Touch controller to upload and share the currently available Spatial Anchors.
The persistent Spatial Anchors you create in the sample will persist between sessions and headset reboots.
Additionally, the sample uses text files on your device to control the download and sharing behavior of the sample for Spatial Anchors. These files may be added or found on the /sdcard/Android/data/com.oculus.sdk.spatialanchor/files/
path on your headset.
The text files are the following:
- shareUserList.txt - This file should contain one user ID per line, in decimal integer format. The user IDs specified in this file should match the list of users with whom you want to share Spatial Anchors.
- inboundSpatialAnchorList.txt - This file contains one UUID per line, in uppercase hexadecimal format with no spaces or dashes. These UUIDs represent Spatial Anchors that have been shared with the current user, and will be downloaded on app load.
- sharedSpatialAnchorList.txt - This file is emitted by the app when you press the Y button, as described previously, and contains the list of UUIDs of Spatial Anchors that were shared. The format is the same as in inboundSpatialAnchorList.txt.
Make sure these text files do not contain leading or trailing whitespace.
Known Issue: The XrSpatialAnchor app tries to upload and share every loaded anchor when the Y button is pressed, including anchors from the Scene Model. The issue will be resolved in a future release.
To make the XrSpatialAnchor app share Spatial Anchors when there are Scene Anchors present, replace the code in the function uploadAndShareAnchors()
of the SpatialAnchorXr.cpp file with the following:
static void UploadAndShareAnchors(ovrApp& app) {
ALOGV("UploadAndShareAnchors");
if (!app.IsLocalMultiplayerSupported) {
ALOGW("UploadAndShareAnchors: Local multiplayer is not supported. Skipping upload.");
return;
}
std::vector<XrSpace> spaceList;
for (XrSpace space : app.AppRenderer.Scene.SpaceList) {
// We will only upload Anchors that are Storable and Sharable.
XrSpaceComponentStatusFB storableStatus;
XrSpaceComponentStatusFB sharableStatus;
OXR(app.FunPtrs.xrGetSpaceComponentStatusFB(
space, XR_SPACE_COMPONENT_TYPE_STORABLE_FB, &storableStatus));
OXR(app.FunPtrs.xrGetSpaceComponentStatusFB(
space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB, &sharableStatus));
if (storableStatus.enabled && sharableStatus.enabled) {
spaceList.push_back(space);
}
}
XrAsyncRequestIdFB saveRequest;
XrSpaceListSaveInfoFB saveInfo = {
XR_TYPE_SPACE_LIST_SAVE_INFO_FB,
nullptr,
static_cast<uint32_t>(spaceList.size()),
spaceList.data(),
XR_SPACE_STORAGE_LOCATION_CLOUD_FB};
OXR(app.FunPtrs.xrSaveSpaceListFB(app.Session, &saveInfo, &saveRequest));
// The rest will continue on the completion of xrSaveSpaceListFB.
// Add the current spaces to the map. This step needs to be a copy here because we're taking a
// snapshot of the Spatial Anchors in the app state.
app.SaveSpaceListCloudEventMap[saveRequest] = spaceList;
}