XR_ANDROID_spatial_entity_bound_anchor

Name String

XR_ANDROID_spatial_entity_bound_anchor

Erweiterungstyp

Instanzerweiterung

Registrierte Erweiterungsnummer

791

Revision

2

Ratifizierungsstatus

Nicht ratifiziert

Abhängigkeiten von Erweiterungen und Versionen

XR_EXT_spatial_anchor

Datum der letzten Änderung

2025-08-18

IP-Status

Keine bekannten IP-Ansprüche.

Mitwirkende

YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google

Übersicht

Mit dieser Erweiterung können Anwendungen Anker für räumliche Entitäten erstellen und anhängen. Diese werden in dieser Erweiterung als „entitätsgebundene Anker“ bezeichnet.

Ein entitätsgebundener Anker wird als räumliche Entität mit der Komponente XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT und der Komponente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT dargestellt. In der Komponente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT wird die XrSpatialEntityIdEXT der übergeordneten Entität gespeichert, an die der Anker angehängt ist.

Die Pose eines entitätsgebundenen Ankers wird immer durch einen festen Offset von der übergeordneten Entität dargestellt, die als „Basis“ des Ankers betrachtet wird. Stellen Sie sich beispielsweise vor, ein Nutzer hat einen virtuellen Bilderrahmen an einer Wand angebracht. Der Nutzer verwendet einen entitätsgebundenen Anker, um den virtuellen Bilderrahmen darzustellen, und hängt ihn an die Wandentität an. So bleibt die relative Position zwischen der Wand und dem Bilderrahmen immer gleich, da sich die Schätzungen der physischen Wand durch die Tracking-Funktion verbessern.

Laufzeitunterstützung

Eine Laufzeit muss mindestens eine räumliche Tracking-Erweiterung unterstützen, z.B. XR_EXT_spatial_plane_tracking . Wenn die Laufzeit entitätsgebundene Anker unterstützt, muss sie mindestens eine anhängbare Komponente bereitstellen, indem sie die Funktion xrEnumerateSpatialAnchorAttachableComponentsANDROID aufzählt. Die Anwendung kann die anhängbaren Komponenten mit xrEnumerateSpatialAnchorAttachableComponentsANDROID aufzählen .

Die Funktion xrEnumerateSpatialAnchorAttachableComponentsANDROID ist so definiert:

XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
    XrInstance                                  instance,
    XrSystemId                                  systemId,
    uint32_t                                    attachableComponentCapacityInput,
    uint32_t*                                   attachableComponentCountOutput,
    XrSpatialComponentTypeEXT*                  attachableComponents);

Parameterbeschreibungen

  • instance ist ein Handle für eine XrInstance .
  • systemId ist die XrSystemId, deren räumliche Persistenzspeicher aufgezählt werden.
  • attachableComponentCapacityInput ist die Kapazität des Arrays attachableComponents oder 0, um eine Anfrage zum Abrufen der erforderlichen Kapazität anzugeben.
  • attachableComponentCountOutput ist die Anzahl der anhängbaren Komponenten oder die erforderliche Kapazität, falls attachableComponentCapacityInput nicht ausreicht.
  • attachableComponents ist ein Array von XrSpatialComponentTypeEXT . Es kann NULL sein, wenn attachableComponentCapacityInput 0 ist.
  • Eine detaillierte Beschreibung des Abrufens der erforderlichen attachableComponents Größe finden Sie im Kapitel Parameter für die Puffergröße.

Laufzeiten müssen für die angegebene systemId während der Lebensdauer der Instanz immer identische Pufferinhalte aus dieser Aufzählung zurückgeben.

Gültige Verwendung (implizit)

  • Die Erweiterung XR_ANDROID_spatial_entity_bound_anchor muss aktiviert sein, bevor xrEnumerateSpatialAnchorAttachableComponentsANDROID aufgerufen wird.
  • instance muss ein gültiges XrInstance-Handle sein.
  • attachableComponentCountOutput muss ein Zeiger auf einen uint32_t-Wert sein.
  • Wenn attachableComponentCapacityInput nicht 0 ist , attachableComponents muss ein Zeiger auf ein Array von attachableComponentCapacityInput XrSpatialComponentTypeEXT Werten sein.

Rückgabecodes

Erfolg

  • XR_SUCCESS

Fehler

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SIZE_INSUFFICIENT
  • XR_ERROR_SYSTEM_INVALID
  • XR_ERROR_VALIDATION_FAILURE

Entitätsgebundenen räumlichen Anker erstellen

Anwendungen verwenden die Funktion xrCreateSpatialAnchorEXT, um einen Anker zu erstellen. Wenn eine Anwendung einen entitätsgebundenen Anker erstellen möchte, der an eine räumliche Entität angehängt ist, kann sie beim Aufrufen der Funktion xrCreateSpatialAnchorEXT eine XrSpatialAnchorParentANDROID-Struktur an den nächsten Zeiger der XrSpatialAnchorCreateInfoEXT-Struktur anhängen.

Die XrSpatialAnchorParentANDROID-Struktur ist so definiert:

typedef struct XrSpatialAnchorParentANDROID {
    XrStructureType         type;
    const void*             next;
    XrSpatialEntityIdEXT    parentId;
} XrSpatialAnchorParentANDROID;

Mitgliederbeschreibungen

  • type ist der XrStructureType dieser Struktur.
  • next ist NULL oder ein Zeiger auf die nächste Struktur in einer Strukturkette.
  • parentId ist die XrSpatialEntityIdEXT der Entität, an die der Anker angehängt wird.

Die Laufzeit muss garantieren, dass der Abstand zwischen der übergeordneten Entität und dem Anker immer gleich ist. Der Abstand ist die Pose des Ankers zur nächstgelegenen Oberfläche der übergeordneten Entität entlang der Normalen der Oberfläche. Die Pose des Ankers wird basierend auf der Position der übergeordneten Entität und dem Abstand zur Oberfläche der übergeordneten Entität aktualisiert, unabhängig davon, wie viele anhängbare Komponenten die übergeordnete Entität hat.

Die Laufzeit muss XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT von xrCreateSpatialAnchorEXT zurückgeben, wenn XrSpatialAnchorParentANDROID ::parentId keine gültige ID für xrCreateSpatialAnchorEXT :: spatialContext ist .

Die Laufzeit muss XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID von xrCreateSpatialAnchorEXT zurückgeben, wenn sich keine der von xrEnumerateSpatialAnchorAttachableComponentsANDROID aufgezählten Komponenten auf der übergeordneten Entität befinden.

Gültige Verwendung (implizit)

Beispielcode

Entitätsgebundenen räumlichen Anker erstellen

Im folgenden Beispielcode wird gezeigt, wie Sie einen entitätsgebundenen Anker erstellen und an eine räumliche Entität für die Ebenenverfolgung anhängen.

XrFutureEXT future {XR_NULL_FUTURE_EXT};

std::vector<XrSpatialEntityEXT> entityBoundAnchorEntities;

// We want to look for entities that have the plane tracking components.
std::vector<XrSpatialComponentTypeEXT> snapshotComponents = {
  XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT,
  XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT,
};

auto discoverSpatialEntities = [&](XrSpatialContextEXT spatialContext, XrTime time) {
  XrSpatialDiscoverySnapshotCreateInfoEXT snapshotCreateInfo{
    .type = XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT,
    .componentTypeCount = static_cast<uint32_t>(snapshotComponents.size()),
    .componentTypes = snapshotComponents.data(),
  };
  CHK_XR(xrCreateSpatialDiscoverySnapshotAsyncEXT(spatialContext, &snapshotCreateInfo, &future));

  waitUntilReady(future);

  XrCreateSpatialDiscoverySnapshotCompletionInfoEXT completionInfo{
    .type = XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT,
    .baseSpace = localSpace,
    .time = time,
    .future = future,
  };

  XrCreateSpatialDiscoverySnapshotCompletionEXT completion{
    .type = XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT,
  };
  CHK_XR(xrCreateSpatialDiscoverySnapshotCompleteEXT(spatialContext, &completionInfo, &completion));
  if (completion.futureResult == XR_SUCCESS) {

    XrSpatialComponentDataQueryConditionEXT queryCond{
      .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT,
      .componentTypeCount = static_cast<uint32_t>(snapshotComponents.size()),
      .componentTypes = snapshotComponents.data(),
    };

    XrSpatialComponentDataQueryResultEXT queryResult{
      .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT,
    };
    CHK_XR(xrQuerySpatialComponentDataEXT(completion.snapshot, &queryCond, &queryResult));

    std::vector<XrSpatialEntityIdEXT> entityIds(queryResult.entityIdCountOutput);
    std::vector<XrSpatialEntityTrackingStateEXT> entityStates(queryResult.entityIdCountOutput);
    queryResult.entityIdCapacityInput = entityIds.size();
    queryResult.entityIds = entityIds.data();
    queryResult.entityStateCapacityInput = entityStates.size();
    queryResult.entityStates = entityStates.data();

    std::vector<XrSpatialBounded2DDataEXT> bounded2D(queryResult.entityIdCountOutput);
    XrSpatialComponentBounded2DListEXT bounded2DList{
      .type = XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT,
      .boundCount = static_cast<uint32_t>(bounded2D.size()),
      .bounds = bounded2D.data(),
    };
    queryResult.next = &bounded2DList;

    CHK_XR(xrQuerySpatialComponentDataEXT(completion.snapshot, &queryCond, &queryResult));

    entityBoundAnchorEntities.reserve(queryResult.entityIdCountOutput);

    // Create anchors attached to the plane entities
    for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
      if (entityStates[i] != XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
        continue;
      }

      // Parent ID info the chained to spatial anchor create info next
      XrSpatialAnchorParentANDROID parentIdCreateInfo{
        .type = XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID,
        .parentId = entityIds[i],
      };

      // spatial anchor create info
      XrSpatialAnchorCreateInfoEXT createInfo{
        .type = XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT,
        // assign Parent ID to anchor create info next, and the pose to the bounded2D center
        .next = &parentIdCreateInfo,
        .baseSpace = localSpace,
        .time = time,
        .pose = bounded2D[i].center,
      };

      XrSpatialEntityIdEXT entityBoundAnchorEntityId {XR_NULL_SPATIAL_ENTITY_ID_EXT};
      XrSpatialEntityEXT entityBoundAnchorEntity {XR_NULL_HANDLE};
      CHK_XR(xrCreateSpatialAnchorEXT(spatialContext, &createInfo, &entityBoundAnchorEntityId, &entityBoundAnchorEntity));

      entityBoundAnchorEntities.push_back(entityBoundAnchorEntity);
    }

    CHK_XR(xrDestroySpatialSnapshotEXT(completion.snapshot));
  }
};

while (1) {
  // ...
  // For every frame in frame loop
  // ...

  XrFrameState frameState;  // previously returned from xrWaitFrame
  const XrTime time = frameState.predictedDisplayTime;

  // Poll for the XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT event
  XrEventDataBuffer event = {
    .type = XR_TYPE_EVENT_DATA_BUFFER,
  };
  XrResult result = xrPollEvent(instance, &event);
  if (result == XR_SUCCESS) {
      if (event.type == XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) {
              const XrEventDataSpatialDiscoveryRecommendedEXT& eventdata =
                  *reinterpret_cast<XrEventDataSpatialDiscoveryRecommendedEXT*>(&event);
              // Discover spatial entities for the context that we received the "discovery
              // recommended" event for.
              discoverSpatialEntities(eventdata.spatialContext, time);
              break;
      }
  }

  // ...
  // Finish frame loop
  // ...
}

Pose des entitätsgebundenen Ankers und ID der übergeordneten Entität abrufen

Im folgenden Beispielcode wird gezeigt, wie Sie die Pose eines entitätsgebundenen Ankers und die ID der übergeordneten Entität abrufen.

std::vector<XrSpatialEntityEXT> entities;

auto updateEntityBoundAnchorInfo = [&](XrSpatialContextEXT spatialContext, XrTime time) {
    // We want to get updated data for all components of the entities, so skip specifying componentTypes.
    XrSpatialUpdateSnapshotCreateInfoEXT snapshotCreateInfo{
      .type = XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT,
      .entityCount = static_cast<uint32_t>(entities.size()),
      .entities = entities.data(),
      .baseSpace = localSpace,
      .time = time,
    };

    XrSpatialSnapshotEXT snapshot {XR_NULL_HANDLE};
    CHK_XR(xrCreateSpatialUpdateSnapshotEXT(spatialContext, &snapshotCreateInfo, &snapshot));

    // Query for the entities that have the anchor component and parent component on them.
    std::array<XrSpatialComponentTypeEXT, 2> componentsToQuery {XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT, XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT};
    XrSpatialComponentDataQueryConditionEXT queryCond{
      .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT,
      .componentTypeCount = componentsToQuery.size(),
      .componentTypes = componentsToQuery.data(),
    };

    XrSpatialComponentDataQueryResultEXT queryResult{
      .type = XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT,
    };
    CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));

    std::vector<XrSpatialEntityIdEXT> entityIds(queryResult.entityIdCountOutput);
    std::vector<XrSpatialEntityTrackingStateEXT> entityStates(queryResult.entityIdCountOutput);
    queryResult.entityIdCapacityInput = entityIds.size();
    queryResult.entityIds = entityIds.data();
    queryResult.entityStateCapacityInput = entityStates.size();
    queryResult.entityStates = entityStates.data();

    // query for the pose data
    std::vector<XrPosef> locations(queryResult.entityIdCountOutput);
    XrSpatialComponentAnchorListEXT locationList{
      .type = XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT,
      .locationCount = static_cast<uint32_t>(locations.size()),
      .locations = locations.data(),
    };
    queryResult.next = &locationList;

    // query for the parent entity ID data
    std::vector<XrSpatialEntityIdEXT> parentIds(queryResult.entityIdCountOutput);
    XrSpatialComponentParentListEXT parentList{
      .type = XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT,
      .parentCount = static_cast<uint32_t>(parentIds.size()),
      .parents = parentIds.data(),
    };
    queryResult.next = &parentList;

    CHK_XR(xrQuerySpatialComponentDataEXT(snapshot, &queryCond, &queryResult));

    for (int32_t i = 0; i < queryResult.entityIdCountOutput; ++i) {
      if (entityStates[i] == XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT) {
        // Pose for entity entityIds[i] is locations[i].
        // Parent entity ID for entity entityIds[i] is parentIds[i].
      }
    }

    CHK_XR(xrDestroySpatialSnapshotEXT(snapshot));
};

while (1) {
  // ...
  // For every frame in frame loop
  // ...

  XrFrameState frameState;  // previously returned from xrWaitFrame
  const XrTime time = frameState.predictedDisplayTime;

  updateEntityBoundAnchorInfo(spatialContext, time);

  // ...
  // Finish frame loop
  // ...
}

Anhängbare Komponenten aufzählen und Funktion für entitätsgebundene Anker prüfen

Im folgenden Beispielcode wird gezeigt, wie Sie die anhängbaren Komponenten aufzählen und prüfen, ob die Laufzeit die Funktion für entitätsgebundene Anker unterstützt.

// Check spatial capability
uint32_t capabilityCount = 0;
CHK_XR(xrEnumerateSpatialCapabilitiesEXT(instance, systemId, 0, &capabilityCount, nullptr));
std::vector<XrSpatialCapabilityEXT> capabilities(capabilityCount);
CHK_XR(xrEnumerateSpatialCapabilitiesEXT(instance, systemId, capabilityCount, &capabilityCount, capabilities.data()));

// Check if anchor capability is supported
if (std::find(capabilities.begin(), capabilities.end(), XR_SPATIAL_CAPABILITY_ANCHOR_EXT) == capabilities.end()) {
  return;
}

// Check if plane tracking capability is supported
if (std::find(capabilities.begin(), capabilities.end(), XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT) == capabilities.end()) {
  return;
}

// The supported spatial tracking components
std::vector<XrSpatialComponentTypeEXT> spatialTrackingCapabilityComponents;

// Enumerate supported components for plane tracking capability
XrSpatialCapabilityComponentTypesEXT planeComponents{
  .type = XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT,
};
CHK_XR(xrEnumerateSpatialCapabilityComponentTypesEXT(instance, systemId, XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, &planeComponents));
std::vector<XrSpatialComponentTypeEXT> planeCapabilityComponents(planeComponents.componentTypeCountOutput);
planeComponents.componentTypes = planeCapabilityComponents.data();
CHK_XR(xrEnumerateSpatialCapabilityComponentTypesEXT(instance, systemId, XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, &planeComponents));

// Add plane supported components to spatial tracking supported components
spatialTrackingCapabilityComponents.insert(spatialTrackingCapabilityComponents.end(), planeCapabilityComponents.begin(), planeCapabilityComponents.end());

// Enumerate supported attachable components for anchor
uint32_t attachableComponentCount = 0;
CHK_XR(xrEnumerateSpatialAnchorAttachableComponentsANDROID(instance, systemId, 0, &attachableComponentCount, nullptr));
std::vector<XrSpatialComponentTypeEXT> attachableComponents(attachableComponentCount);
CHK_XR(xrEnumerateSpatialAnchorAttachableComponentsANDROID(instance, systemId, attachableComponentCount, &attachableComponentCount, attachableComponents.data()));

// Check if at least one spatial tracking component is supported
const auto supportsComponent = [&spatialTrackingCapabilityComponents](XrSpatialComponentTypeEXT component) {
  return std::find(spatialTrackingCapabilityComponents.begin(), spatialTrackingCapabilityComponents.end(), component) != spatialTrackingCapabilityComponents.end();
};

bool atLeastOneComponentSupported = false;
for (int32_t i = 0; i < attachableComponentCount; ++i) {
  if(supportsComponent(attachableComponents[i])) {
    atLeastOneComponentSupported = true;
    break;
  }
}

// No spatial tracking component supported for anchor attachment
if(!atLeastOneComponentSupported) return;

// ...
// Create spatial entity anchors and get their latest pose in the frame loop.
// ...

Neue Befehle

Neue Strukturen

Neue Enum-Konstanten

  • XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAME
  • XR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION
  • Erweiterung von XrResult :

    • XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
  • Erweiterung von XrStructureType :

    • XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID

Probleme

Versionsverlauf

  • Revision 1, 18.08.2025 (YuSheng Chang)

    • Erste Beschreibung der Erweiterung.
  • Revision 2, 16.12.2025 (Kyle Chen)