XR_ANDROID_spatial_entity_bound_anchor

Строка имени

XR_ANDROID_spatial_entity_bound_anchor

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

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

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

791

Пересмотр

2

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

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

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

XR_EXT_spatial_anchor

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

2025-08-18

Статус IP

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

Авторы

ЮШэн Чанг, Google
Кайл Чен, Google
Нихав Джайн, Google
Левана Чен, Google
Спенсер Куин, 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 сущности, к которой будет прикреплена привязка.

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

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

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

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

Пример кода

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

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

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.12.2025 (Кайл Чен)