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должен быть указателем на массив значенийattachableComponentCapacityInputXrSpatialComponentTypeEXT.
Коды возврата
XR_SUCCESS
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_RUNTIME_FAILUREXR_ERROR_SIZE_INSUFFICIENTXR_ERROR_SYSTEM_INVALIDXR_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, не находится в родительском объекте.
Допустимое использование (неявное)
- Расширение
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_NAMEXR_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 г. (Кайл Чен)
- Добавлены более строгие формулировки в отношении некоторых аспектов работы API.
- Добавьте пример кода xrEnumerateSpatialAnchorAttachableComponentsANDROID .