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должен быть указателем на массив значенийattachableComponentCapacityInputXrSpatialComponentTypeEXT.
Коды возврата
-
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 .
Допустимое использование (неявное)
- Расширение
XR_ANDROID_spatial_entity_bound_anchorнеобходимо включить перед использованием XrSpatialAnchorParentANDROID. -
typeдолжен бытьXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextдолжно бытьNULLили допустимым указателем на следующую структуру в цепочке структур.
Пример кода
Создайте пространственный якорь, привязанный к объекту.
Приведенный ниже пример кода демонстрирует, как создать привязку к объекту и прикрепить ее к объекту, отслеживающему пространственную плоскость.
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.
// ...
Новые команды
Новые сооружения
Расширение XrSpatialAnchorCreateInfoEXT :
Новые константы перечислений
-
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 (Кайл Чен)
- Добавить более подробные инструкции к определенным функциям API.
- Добавьте пример кода xrEnumerateSpatialAnchorAttachableComponentsANDROID .