struct SpatialEntity
A SpatialEntity is something the device has discovered or is tracking in the user’s physical environment: a wall, a table, a QR code, or an anchor the app has placed. This is StereoKit’s surface for OpenXR’s spatial entity extensions.
Entities are composed of components, which are chunks of data like a
bounding rectangle, a semantic label, or a mesh. Which components an
entity has depends on the capability that discovered it and what the
device supports, so data is accessed through TryGet methods, and
entities can be filtered by the components you need via With.
Request the capabilities you’re interested in with
Spatial.Request, and StereoKit will keep an up-to-date list of
entities that you can poll each frame. Component data reflects the
entity’s last known state, so check Tracked to know if it’s
currently live.
SpatialEntity is a lightweight identifier, not a reference. The
device owns these entities and controls their lifetimes. Identifiers
are never reused within a session, so a stale one simply stops
resolving once its entity is gone. Valid becomes false, and
accessors return no data.
Instance Fields and Properties
| SpatialComponent Changed | Components whose data meaningfully changed this frame! Continuously updating poses are only flagged when they first arrive, while mesh/polygon/marker data is flagged whenever the system provides new data. Handy for skipping expensive work like mesh extraction when nothing changed. These flags reset every frame, so check them each frame or you may miss an update. |
| SpatialComponent Components | The set of components this entity has valid data for. Each component has a matching TryGet accessor. |
| Byte[] MarkerData | The marker’s raw decoded bytes, for markers with binary data. Null if unavailable. Each read allocates a new array, see TryGetMarkerData to reuse one. |
| string MarkerText | The marker’s decoded string data, for QR family markers that contain text. Null if unavailable. Each read creates a new string, so cache it, and read again when HasChanged flags the Marker component. |
| SpatialEntity Parent | The entity this entity is attached to. This is an invalid entity if there’s no parent, so check Valid. |
| Pose Pose | Where this entity is in the world, from its anchor, or else the center of its 2D or 3D bounds. This is live while Tracked is active, and last known otherwise. It’s Pose.Identity until the entity has any data, like a FindAnchor that hasn’t loaded yet, or something first seen while untracked. For a specific meaning, use the matching TryGet. |
| SpatialStatus Status | Whether the things you’ve asked of this entity have gone through, like creating, loading, or persisting it. Ready means all done, Pending means something’s still in progress, and negative values are failures: Partial if a request like Persist failed, Failed if the entity couldn’t be created or found at all. This is separate from Tracked, so a Pending anchor can still be tracked and usable. |
| BtnState Tracked | Is the system tracking this entity right now? While active, Pose and all component data are live. While inactive, they’re last known and may be stale, or empty if the entity hasn’t been tracked yet. For requests still in progress, like a new anchor, check Status instead. Entities that are permanently lost leave the entity list, except persisted ones, which keep their identifier with Status Pending while storage loads them again. |
| bool Valid | Does this identifier currently resolve to an entity? This becomes false once the entity permanently leaves the entity list, and a default SpatialEntity is never valid. |
Instance Methods
| Destroy | Removes an app-created entity like an anchor from the system entirely. It’s unpersisted if persisted, the system stops tracking it, and it leaves the entity list at the end of the frame. This SpatialEntity stops resolving once that happens. This also works on entities that are still Pending, including ones from FindAnchor that haven’t loaded yet. |
| Equals | Do these identify the same entity? |
| GetHashCode | A hash of the identifier value. |
| Has | Does this entity have data for all of these components? |
| HasChanged | Did any of these components change this frame? Like Changed, this resets every frame, so check it each frame. |
| Persist | Ask the system to persist this entity, giving it a durable identity that survives across sessions! This is asynchronous, and safe to call right away since it waits until the entity is tracking and persistence has started up. On success, TryGetGuid succeeds and Changed flags the Persistence component. On failure, Status becomes Partial and the entity stays without a Guid. If an Unpersist is still in flight, this persists again once it lands. |
| TryGetBounds2D | The 2D rectangular bounds of this entity, such as the extents of a detected plane, or the shape of a marker. The pose faces out of the surface, so its Forward is the surface normal, the same way quads and text face in StereoKit. A floor’s pose faces up, and a wall’s pose faces into the room. The center is usually the same as Pose, but can differ on entities with several pose components, like a table with both a top and a volume. |
| TryGetBounds3D | The oriented 3D bounding volume of this entity. When the entity has a front, like a screen or table top, the center pose’s Forward is the direction it faces. The center is usually the same as Pose, but can differ on entities with several pose components, like a table with both a top and a volume. |
| TryGetGuid | A durable identifier for this entity that stays the same across sessions and device reboots! Entities only have one once they’re persisted, either by the system itself, or by a call to Persist. Store it, and pass it to FindAnchor to get the same physical entity back in a later session. |
| TryGetLabel | A semantic category for this entity, like floor or table. Not all devices provide labels, so check Spatial.ComponentsFor for the capability you’re using. For planes, TryGetPlaneAlign makes a good fallback. |
| TryGetMarker | Marker information, for entities discovered by a marker tracking capability like QR codes or ArUco markers. The marker’s pose and physical size come from TryGetBounds2D. |
| TryGetMarkerData | The marker’s raw decoded bytes, copied into an array you keep, so repeated reads don’t allocate. The array only grows when it’s too small, so use size rather than its length. |
| TryGetMesh | The entity’s 3D mesh! Mesh vertices are relative to the origin pose, which the system keeps aligned with the physical world, so draw the mesh at origin each frame. Filling the Mesh is the expensive path, so pass a null mesh to fetch just the current origin, and refill only when Changed flags the Mesh component. |
| TryGetMesh2D | The entity’s 2D surface mesh, on the XY plane of the origin pose. Works just like TryGetMesh, so pass null to fetch only the origin, and refill when Changed flags the Mesh2D component. |
| TryGetName | The name this anchor was given by CreateAnchor, or by the Anchor class. Persisted anchors get their name back when they load in a later session, however they were found, so this is a good way to tell restored anchors apart. |
| TryGetPlaneAlign | The general orientation of a detected plane, like horizontal or vertical. Plane tracking always provides this, so it’s a reliable fallback on devices that don’t provide labels. |
| TryGetPolygon | The boundary polygon outlining the entity’s surface, on the XY plane of the origin pose. This allocates a new array each call, so for every frame use, prefer the overload that reuses one. |
| Unpersist | Remove this entity from persistent storage. Its name, if it has one, is released right away, and it loses its Guid once the asynchronous operation completes. Status is Pending until then, or Partial if it fails. If a Persist is still in flight, this waits for it to land and then undoes it. An entity from FindAnchor that hasn’t loaded yet stops loading, and once the unpersist lands it leaves the entity list, since there’s nothing left to load. |
Static Fields and Properties
| SpatialEntityCollection All | An enumeration of every spatial entity StereoKit currently knows about. This list is maintained for you, entities appear as the system discovers them, and leave when the system permanently stops tracking them. An entity in Removed is still in this list for its final frame. |
| SpatialEntityCollection New | An enumeration of the spatial entities that appeared for the first time this frame. Entities you create yourself, like with CreateAnchor or FindAnchor, show up here on the following frame, so every entity is seen here exactly once. |
| SpatialEntityCollection Removed | An enumeration of the spatial entities leaving the entity list this frame, because they were lost by the system, destroyed, or Status Failed. Lost persisted entities don’t leave, they wait to load again. These are still Valid with readable data for this one frame, which makes this the place to clean up anything you’ve cached per-entity! |
Static Methods
| CreateAnchor | Create a spatial anchor entity at the given pose, a point the system will keep aligned with the physical world as tracking improves or drifts. You can call this any time after requesting SpatialCapability.Anchor, even while it’s still starting up! Until the system takes over, the anchor sits at this pose with Tracked inactive and Status Pending. If the system can’t create it, the anchor shows up in Removed with Status Failed. |
| FindAnchor | Gets the anchor you created with this name, in this session or an earlier one! This is the easy way to restore anchors across app restarts. Call this once and hold onto the result. Storage loads asynchronously, so the anchor starts out with Status Pending and no data, then fills in when it loads. Nothing loads while SpatialCapability.Anchor isn’t Spatial.Requested. |
| NewWith | An enumeration of the spatial entities that appeared for the first time this frame, and have data for all the given components. |
| RemovedWith | An enumeration of the spatial entities leaving the entity list this frame that have data for all the given components. |
| With | An enumeration of the spatial entities that have data for all the given components. |
Operators
| op_Equality | Do these identify the same entity? |
| op_Inequality | Do these identify different entities? |
Examples
Drawing detected planes
Request plane tracking once, and the device’s walls, floors and tables show up in the entity list as it finds them.
public void StartPlanes()
{
Spatial.Request(SpatialCapability.PlaneTracking);
}
public void StopPlanes()
{
Spatial.Disable(SpatialCapability.PlaneTracking);
}
public void DrawPlanes()
{
foreach (SpatialEntity plane in SpatialEntity.With(SpatialComponent.Bounds2D))
{
// Quads face Forward, same as the plane's center pose
plane.TryGetBounds2D(out Pose center, out Vec2 size);
Mesh.Quad.Draw(Material.Default, center.ToMatrix(new Vec3(size.x, size.y, 1)));
}
}
Found an issue with these docs, or have some additional questions? Create an Issue on Github!