XR_ANDROID_spatial_entity_bound_anchor
String do nome
XR_ANDROID_spatial_entity_bound_anchor
Tipo de extensão
Extensão de instância
Número de extensão registrada
791
Revisão
2
Status de ratificação
Não ratificado
Dependências de extensão e versão
XR_EXT_spatial_anchor
Data da última modificação
2025-08-18
Status de IP
Nenhuma reivindicação de IP conhecida.
Colaboradores
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
Visão geral
Essa extensão permite que os aplicativos criem e anexem âncoras a entidades espaciais, que são chamadas de "âncoras vinculadas a entidades" nesta extensão.
Uma âncora vinculada a uma entidade é representada como uma entidade espacial com (ou "que tem") o componente XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT e o componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. O componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT armazena o XrSpatialEntityIdEXT da entidade pai à qual a âncora está anexada.
A pose de uma âncora vinculada a uma entidade é sempre representada por um deslocamento fixo da entidade pai, que é considerada a "base" da âncora. Por exemplo, imagine que um usuário tenha um porta-retrato virtual anexado a uma parede. O usuário usa uma âncora vinculada a uma entidade para representar o porta-retrato virtual e o anexa à entidade de parede. Com isso, a posição relativa entre a parede e o porta-retrato é sempre consistente à medida que as estimativas de rastreamento da parede física melhoram.
Suporte ao ambiente de execução
Um ambiente de execução precisa oferecer suporte a pelo menos uma extensão de rastreamento espacial, por exemplo, XR_EXT_spatial_plane_tracking. Se o ambiente de execução oferecer suporte à âncora vinculada a uma entidade espacial, ele precisa fornecer pelo menos um componente anexável enumerando a função xrEnumerateSpatialAnchorAttachableComponentsANDROID. O aplicativo pode enumerar os componentes anexáveis usando xrEnumerateSpatialAnchorAttachableComponentsANDROID .
A função xrEnumerateSpatialAnchorAttachableComponentsANDROID é definida como:
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
Descrições dos parâmetros
instanceé um identificador para um XrInstance .systemIdé oXrSystemIdcujos armazenamentos de persistência espacial serão enumerados.attachableComponentCapacityInputé a capacidade da matrizattachableComponentsou 0 para indicar uma solicitação para recuperar a capacidade necessária.attachableComponentCountOutputé o número de componentes anexáveis ou a capacidade necessária no caso deattachableComponentCapacityInputser insuficiente.attachableComponentsé uma matriz de XrSpatialComponentTypeEXT . Ela pode serNULLseattachableComponentCapacityInputfor 0.- Consulte o capítulo Parâmetros de tamanho do buffer para uma descrição detalhada de como recuperar o tamanho
attachableComponentsnecessário.
Os ambientes de execução precisam sempre retornar conteúdos de buffer idênticos dessa enumeração para o systemId fornecido durante a vida útil da instância.
Uso válido (implícito)
- A extensão
XR_ANDROID_spatial_entity_bound_anchorprecisa ser ativada antes de chamar xrEnumerateSpatialAnchorAttachableComponentsANDROID. -
instanceprecisa ser um identificador XrInstance válido. -
attachableComponentCountOutputprecisa ser um ponteiro para umuint32_tvalor. - Se
attachableComponentCapacityInputnão for0,attachableComponentsprecisa ser um ponteiro para uma matriz de valoresattachableComponentCapacityInputXrSpatialComponentTypeEXT.
Códigos de retorno
XR_SUCCESS
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_RUNTIME_FAILUREXR_ERROR_SIZE_INSUFFICIENTXR_ERROR_SYSTEM_INVALIDXR_ERROR_VALIDATION_FAILURE
Como criar uma âncora vinculada a uma entidade espacial
Os aplicativos usam a função xrCreateSpatialAnchorEXT para criar uma âncora. Se um aplicativo quiser criar uma âncora vinculada a uma entidade anexada a uma entidade espacial, ele poderá encadear uma estrutura XrSpatialAnchorParentANDROID ao próximo ponteiro da estrutura XrSpatialAnchorCreateInfoEXT ao chamar a função xrCreateSpatialAnchorEXT.
A estrutura XrSpatialAnchorParentANDROID é definida como:
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
Descrições de membros
typeé o XrStructureType dessa estrutura.nextéNULLou um ponteiro para a próxima estrutura em uma cadeia de estruturas.parentIdé oXrSpatialEntityIdEXTda entidade à qual a âncora será anexada.
O ambiente de execução precisa garantir que a distância entre a entidade pai e a âncora seja sempre consistente, em que a distância é a pose da âncora até a superfície mais próxima da entidade pai ao longo da normal da superfície. A pose da âncora é atualizada com base na posição da entidade pai e na distância até a superfície da entidade pai, independentemente de quantos componentes anexáveis a entidade pai tem.
O ambiente de execução precisa retornar XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT de xrCreateSpatialAnchorEXT se XrSpatialAnchorParentANDROID ::parentId não for um ID válido para xrCreateSpatialAnchorEXT :: spatialContext .
O ambiente de execução precisa retornar XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID de xrCreateSpatialAnchorEXT se nenhum dos componentes enumerados por xrEnumerateSpatialAnchorAttachableComponentsANDROID estiver na entidade pai.
Uso válido (implícito)
- A extensão
XR_ANDROID_spatial_entity_bound_anchorprecisa ser ativada antes de usar XrSpatialAnchorParentANDROID. -
typeprecisa serXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextprecisa serNULLou um ponteiro válido para a próxima estrutura em uma cadeia de estruturas
Exemplo de código
Criar uma âncora vinculada a uma entidade espacial
O exemplo de código a seguir demonstra como criar uma âncora vinculada a uma entidade e anexá-la a uma entidade de rastreamento de plano espacial.
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
// ...
}
Receber a pose da âncora vinculada a uma entidade e o ID da entidade pai
O exemplo de código a seguir demonstra como receber a pose de uma âncora vinculada a uma entidade e o ID da entidade pai.
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
// ...
}
Enumerar componentes anexáveis e verificar a capacidade da âncora vinculada a uma entidade
O exemplo de código a seguir demonstra como enumerar os componentes anexáveis e verificar se o ambiente de execução oferece suporte à capacidade da âncora vinculada a uma entidade.
// 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.
// ...
Novos comandos
Novas estruturas
Extensão de XrSpatialAnchorCreateInfoEXT :
Novas constantes de tipo enumerado
XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAMEXR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSIONExtensão de XrResult :
XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
Extensão de XrStructureType :
XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID
Problemas
Histórico de versões
Revisão 1, 2025-08-18 (YuSheng Chang)
- Descrição inicial da extensão.
Revisão 2, 2025-12-16 (Kyle Chen)
- Adicione uma linguagem mais prescritiva a determinados comportamentos da API.
- Adicione o exemplo de código de xrEnumerateSpatialAnchorAttachableComponentsANDROID .