XR_ANDROID_spatial_entity_bound_anchor

Name String

XR_ANDROID_spatial_entity_bound_anchor

Тип расширения

Расширение экземпляра

Зарегистрированный добавочный номер

791

Revision

2

Статус ратификации

Не ратифицировано

Зависимости расширений и версий

XR_EXT_spatial_anchor

Дата последнего изменения

2025-08-18

Статус IP-адреса

Нет известных претензий в отношении интеллектуальной собственности.

Авторы

Юйшэн Чан (YuSheng Chang), Google
Кайл Чэнь (Kyle Chen), Google
Нихав Джайн (Nihav Jain), Google
Леван Чэнь (Levana Chen), Google
Спенсер Куин (Spencer Quin), Google

Обзор

Это расширение позволяет приложениям создавать и прикреплять привязки к пространственным объектам, которые в этом расширении называются "привязками, связанными с объектами".

Якорь, привязанный к объекту, представлен как пространственный объект с компонентами XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT и XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. Компонент XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT хранит XrSpatialEntityIdEXT родительского объекта, к которому прикреплен якорь.

Поза привязанного к объекту якоря всегда представлена фиксированным смещением относительно родительского объекта, который считается "основой" якоря. Например, представьте, что у пользователя есть виртуальная рамка для картины, прикрепленная к стене. Пользователь использует привязку, связанную с объектом, чтобы представить виртуальную рамку для картины, и прикрепляет ее к объекту стены. Благодаря этому относительное позиционирование между стеной и рамкой картины всегда остается неизменным по мере улучшения оценок отслеживания физической стены.

Поддержка среды выполнения

Среда выполнения должна поддерживать хотя бы одно расширение для пространственного отслеживания, например XR_EXT_spatial_plane_tracking. Если среда выполнения поддерживает привязку, связанную с пространственным объектом, она должна предоставлять хотя бы один прикрепляемый компонент, перечисляя функцию xrEnumerateSpatialAnchorAttachableComponentsANDROID. Приложение может перечислить прикрепляемые компоненты с помощью xrEnumerateSpatialAnchorAttachableComponentsANDROID .

Функция xrEnumerateSpatialAnchorAttachableComponentsANDROID определяется следующим образом:

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

Описание параметров

  • instance – дескриптор XrInstance .
  • systemId – это XrSystemId, хранилища пространственного постоянства которого будут перечислены.
  • attachableComponentCapacityInput – емкость массива attachableComponents или 0, если нужно получить требуемую емкость.
  • attachableComponentCountOutput – количество подключаемых компонентов или требуемая емкость, если attachableComponentCapacityInput недостаточно.
  • attachableComponents – массив XrSpatialComponentTypeEXT . Может быть NULL, если attachableComponentCapacityInput равно 0.
  • Подробное описание того, как получить нужный размер attachableComponents, приведено в разделе Параметры размера буфера.

Среды выполнения должны всегда возвращать идентичное содержимое буфера из этого перечисления для заданного systemId в течение всего времени существования экземпляра.

Допустимое использование (неявное)

  • Расширение XR_ANDROID_spatial_entity_bound_anchor должно быть включено до вызова xrEnumerateSpatialAnchorAttachableComponentsANDROID.
  • instance должен быть действительным дескриптором XrInstance.
  • attachableComponentCountOutput должен быть указателем на значение uint32_t.
  • Если attachableComponentCapacityInput не равно 0 , attachableComponents должен быть указателем на массив значений attachableComponentCapacityInput XrSpatialComponentTypeEXT.

Коды возврата

Выполнено

  • XR_SUCCESS

Сбой

  • 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

Как создать привязку, связанную с пространственным объектом

Для создания привязки приложения используют функцию xrCreateSpatialAnchorEXT. Если приложение хочет создать привязку, связанную с пространственным объектом, оно может связать структуру XrSpatialAnchorParentANDROID со следующим указателем структуры XrSpatialAnchorCreateInfoEXT при вызове функции xrCreateSpatialAnchorEXT.

Структура XrSpatialAnchorParentANDROID определяется следующим образом:

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

Описания участников

  • type – это XrStructureType этой структуры.
  • next – это NULL или указатель на следующую структуру в цепочке.
  • parentId – это XrSpatialEntityIdEXT объекта, к которому будет прикреплен якорь.

Среда выполнения должна гарантировать, что расстояние между родительским объектом и якорем всегда постоянно. Расстояние – это положение якоря относительно ближайшей поверхности родительского объекта вдоль нормали поверхности. Поза якоря обновляется на основе положения родительского объекта и расстояния до его поверхности независимо от того, сколько у него прикрепляемых компонентов.

Среда выполнения должна возвращать XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT из xrCreateSpatialAnchorEXT, если XrSpatialAnchorParentANDROID ::parentId не является допустимым идентификатором для xrCreateSpatialAnchorEXT :: spatialContext .

Среда выполнения должна возвращать XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID из xrCreateSpatialAnchorEXT, если ни один из компонентов, перечисленных в xrEnumerateSpatialAnchorAttachableComponentsANDROID, не находится в родительском объекте.

Допустимое использование (неявное)

Пример кода

Создать привязку, связанную с пространственным объектом

В следующем примере кода показано, как создать привязку, связанную с объектом, и прикрепить ее к объекту отслеживания пространственной плоскости.

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

Получение позы привязки, связанной с объектом, и идентификатора родительского объекта

В примере кода ниже показано, как получить позу привязки, связанной с объектом, и идентификатор ее родительского объекта.

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

Перечислить подключаемые компоненты и проверить возможность привязки якоря к объекту

В примере кода ниже показано, как перечислить подключаемые компоненты и проверить, поддерживает ли среда выполнения функцию привязки к объекту.

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

Новые команды

Новые структуры

Новые константы перечисления

  • XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAME
  • XR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION
  • Расширение XrResult :

    • XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
  • Расширение XrStructureType :

    • XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID

Проблемы

История версий

  • Версия 1, 18 августа 2025 г. (Юйшэн Чан)

    • Начальное описание расширения.
  • Версия 2, 16 декабря 2025 г. (Кайл Чен)