XR_ANDROID_spatial_entity_bound_anchor

Name String

XR_ANDROID_spatial_entity_bound_anchor

Typ rozszerzenia

Rozszerzenie instancji

Zarejestrowany numer wewnętrzny

791

Wersja

2

Stan ratyfikacji

Nie ratyfikowano

Zależności rozszerzeń i wersji

XR_EXT_spatial_anchor

Data ostatniej modyfikacji

2025-08-18

Stan IP

Brak znanych roszczeń dotyczących własności intelektualnej.

Twórcy

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

Przegląd

To rozszerzenie umożliwia aplikacjom tworzenie i dołączanie kotwic do elementów przestrzennych, które w tym rozszerzeniu są nazywane „kotwicami powiązanymi z elementem”.

Kotwica powiązana z elementem jest reprezentowana jako element przestrzenny z komponentem XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT i komponentem XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. Komponent XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT przechowuje XrSpatialEntityIdEXT encji nadrzędnej, do której jest dołączona kotwica.

Pozycja kotwicy powiązanej z encją jest zawsze reprezentowana przez stałe przesunięcie względem encji nadrzędnej, która jest uważana za „bazę” kotwicy. Załóżmy na przykład, że użytkownik ma wirtualną ramkę na zdjęcia przymocowaną do ściany. Użytkownik używa kotwicy powiązanej z elementem, aby reprezentować wirtualną ramkę na zdjęcia, i dołącza ją do elementu ściany. Dzięki temu względna pozycja między ścianą a ramą obrazu jest zawsze spójna, gdy szacunki śledzenia fizycznej ściany ulegają poprawie.

Obsługa środowiska wykonawczego

Środowisko wykonawcze musi obsługiwać co najmniej 1 rozszerzenie śledzenia przestrzennego, np. XR_EXT_spatial_plane_tracking. Jeśli środowisko wykonawcze obsługuje kotwicę powiązaną z elementem przestrzennym, musi udostępniać co najmniej 1 komponent do dołączenia przez wyliczenie funkcji xrEnumerateSpatialAnchorAttachableComponentsANDROID. Aplikacja może wyliczyć komponenty, które można dołączyć, za pomocą funkcji xrEnumerateSpatialAnchorAttachableComponentsANDROID .

Funkcja xrEnumerateSpatialAnchorAttachableComponentsANDROID jest zdefiniowana w ten sposób:

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

Opisy parametrów

  • instance to uchwyt do XrInstance .
  • systemId to XrSystemId, którego magazyny trwałości przestrzennej zostaną wyliczone.
  • attachableComponentCapacityInput to pojemność tablicy attachableComponents lub 0, co oznacza prośbę o pobranie wymaganej pojemności.
  • attachableComponentCountOutput to liczba komponentów, które można dołączyć, lub wymagana pojemność, jeśli attachableComponentCapacityInput jest niewystarczająca.
  • attachableComponents to tablica XrSpatialComponentTypeEXT . Może mieć wartość NULL, jeśli attachableComponentCapacityInput wynosi 0.
  • Szczegółowy opis pobierania wymaganego rozmiaru attachableComponents znajdziesz w rozdziale Parametry rozmiaru bufora.

Środowiska wykonawcze muszą zawsze zwracać identyczną zawartość bufora z tego wyliczenia dla danego systemId przez cały okres istnienia instancji.

Prawidłowe użycie (pośrednie)

  • Rozszerzenie XR_ANDROID_spatial_entity_bound_anchor musi być włączone przed wywołaniem funkcji xrEnumerateSpatialAnchorAttachableComponentsANDROID.
  • instance musi być prawidłowym uchwytem XrInstance
  • attachableComponentCountOutput musi być wskaźnikiem do wartości uint32_t
  • Jeśli attachableComponentCapacityInput nie jest równe 0 , attachableComponents musi być wskaźnikiem do tablicy wartości attachableComponentCapacityInput XrSpatialComponentTypeEXT

Kody zwrotu

Sukces

  • XR_SUCCESS

Błąd

  • 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

Tworzenie kotwicy powiązanej z elementem przestrzennym

Aplikacje używają funkcji xrCreateSpatialAnchorEXT do tworzenia kotwicy. Jeśli aplikacja chce utworzyć kotwicę powiązaną z elementem, która jest dołączona do elementu przestrzennego, może połączyć strukturę XrSpatialAnchorParentANDROID ze wskaźnikiem next struktury XrSpatialAnchorCreateInfoEXT podczas wywoływania funkcji xrCreateSpatialAnchorEXT.

Struktura XrSpatialAnchorParentANDROID jest zdefiniowana w ten sposób:

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

Opisy członków

  • type to XrStructureType tej struktury.
  • next to NULL lub wskaźnik do następnej struktury w łańcuchu struktur.
  • parentId to XrSpatialEntityIdEXT elementu, do którego zostanie dołączona kotwica.

Środowisko wykonawcze musi gwarantować, że odległość między encją nadrzędną a kotwicą jest zawsze stała, przy czym odległość to pozycja kotwicy względem najbliższej powierzchni encji nadrzędnej wzdłuż wektora normalnego powierzchni. Pozycja kotwicy jest aktualizowana na podstawie pozycji encji nadrzędnej i odległości od jej powierzchni, niezależnie od tego, ile komponentów dołączanych ma encja nadrzędna.

Środowisko wykonawcze musi zwracać XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT z xrCreateSpatialAnchorEXT, jeśli XrSpatialAnchorParentANDROID ::parentId nie jest prawidłowym identyfikatorem dla xrCreateSpatialAnchorEXT :: spatialContext .

Środowisko wykonawcze musi zwracać XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID z xrCreateSpatialAnchorEXT, jeśli żaden z komponentów wymienionych przez xrEnumerateSpatialAnchorAttachableComponentsANDROID nie znajduje się w encji nadrzędnej.

Prawidłowe użycie (pośrednie)

Przykładowy kod

Tworzenie kotwicy powiązanej z elementem przestrzennym

Poniższy przykładowy kod pokazuje, jak utworzyć kotwicę powiązaną z encją i dołączyć ją do encji śledzenia płaszczyzny przestrzennej.

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
  // ...
}

Pobieranie pozycji kotwicy powiązanej z encją i identyfikatora encji nadrzędnej

Poniższy przykładowy kod pokazuje, jak uzyskać pozycję kotwicy powiązanej z encją i identyfikator jej encji nadrzędnej.

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
  // ...
}

Wyliczanie komponentów, które można dołączyć, i sprawdzanie możliwości kotwicy powiązanej z encją

Poniższy przykładowy kod pokazuje, jak wyliczyć komponenty, które można dołączyć, i sprawdzić, czy środowisko wykonawcze obsługuje funkcję kotwicy powiązanej z encją.

// 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.
// ...

Nowe polecenia

Nowe domy

Nowe stałe typu wyliczeniowego

  • XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAME
  • XR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION
  • Rozszerzenie XrResult :

    • XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
  • Rozszerzanie XrStructureType :

    • XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID

Problemy

Historia zmian

  • Wersja 1, 18 sierpnia 2025 r. (YuSheng Chang)

    • Początkowy opis rozszerzenia.
  • Wersja 2, 16 grudnia 2025 r. (Kyle Chen)