(Work in Progress)
This StereoKit project implements the XR_FB_spatial_entity OpenXR extensions for the StereoKit library. This enables us to track/persist world-locked anchors on Meta Quest devices! This project is built with the sk-multi template to support cross-platform app development on either Android or Quest+Link platforms.
This project implements the following OpenXR extensions:
- XR_FB_spatial_entity
- XR_FB_spatial_entity_storage
- XR_FB_spatial_entity_query
Here are the steps on how to use the Meta spatial anchor extension in your StereoKit project.
-
Copy/paste the SpatialEntity directory from this repo into your StereoKit project. It contains all the code to implement the OpenXR extension for StereoKit.
-
Add the SpatialEntityFBExt to your main program file. Note: This code must be called BEFORE calling
SK.Initialize.SpatialEntityFBExt spatialEntityStepper = SK.AddStepper(new SpatialEntityFBExt());
-
(Optional) Display some UI to interface with the spatial entity system.
// Spatial Anchor Menu UI.WindowBegin("Spatial Anchor Menu", ref window2Pose, new Vec2(30,0) * U.cm); if (spatialEntityStepper.Available) { UI.Label("FB Spatial Entity EXT available!"); if (UI.Button("Create Anchor")) { // We will create the anchor at the location just in front of the window (and we'll adopt the UI window's orientation). Vec3 anchorPosition = window2Pose.position + window2Pose.Forward * .05f + Vec3.Up * 0.1f; Pose pose = new Pose(anchorPosition, window2Pose.orientation); // We can optionally provide some callbacks for when the async operation either completes successfully or fails. spatialEntityStepper.CreateAnchor( pose, (Guid newAnchorUuid) => Log.Info($"Async anchor creation success. New anchor created: Uuid:{newAnchorUuid}"), () => Log.Info("Async anchor creation success failed :(")); } UI.SameLine(); if (UI.Button("Load All")) spatialEntityStepper.LoadAllAnchors(); UI.SameLine(); if (UI.Button("Erase All")) spatialEntityStepper.DeleteAllAnchors(); // List all Anchors UI.HSeparator(); UI.Label($"Anchors Loaded ({spatialEntityStepper.AnchorCount})"); foreach (var anchor in spatialEntityStepper.Anchors) { // Use a PushId to avoid button Id collisions UI.PushId(anchor.Uuid.ToString()); UI.PanelBegin(); if (UI.Button($"{anchor.Uuid.ToString().Substring(0,14)}...")) { // Unselect the anchor (if already selected) or select the anchor (if not already selected) if (selectedAnchorId == anchor.Uuid) selectedAnchorId = null; else selectedAnchorId = anchor.Uuid; } UI.SameLine(); // Button to delete the selected anchor if (UI.Button("Delete")) { spatialEntityStepper.DeleteAnchor(anchor.Uuid); } if (selectedAnchorId == anchor.Uuid) { UI.Label("XrSpace: " + anchor.XrSpace); UI.Label("Located: " + anchor.LocateSuccess); UI.Label(anchor.Pose.ToString()); } UI.PanelEnd(); UI.PopId(); } } else { UI.Label("Spatial Anchor is not available :("); } UI.WindowEnd();
-
(Optional) Visualize the spatial anchors by looping over them and drawing something!
// Visualize all loaded spatial anchor foreach (var anchor in spatialEntityStepper.Anchors) { // Just draw a nice orange cube for the anchor pose Mesh.Cube.Draw(Material.Default, anchor.Pose.ToMatrix(0.1f), new Color(1, 0.5f, 0)); }
I'd like to get these additional OpenXR extensions up and running as well, when I find the time.
- Implement XR_FB_spatial_entity_sharing
- Implement XR_FB_spatial_entity_container
- Implement XR_FB_spatial_entity_storage_batch
- Implement XR_FB_spatial_entity_user
- Implement XR_FB_scene
