XR_ANDROID_spatial_entity_bound_anchor
Nombre de cadena
XR_ANDROID_spatial_entity_bound_anchor
Tipo de extensión
Extensión de instancia
Número de extensión registrada
791
Revisión
2
Estado de ratificación
No ratificado
Dependencias de extensión y versión
XR_EXT_spatial_anchor
Fecha de última modificación
18/8/2025
Estado de IP
No se conocen reclamos de IP.
Colaboradores
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
Descripción general
Esta extensión permite que las aplicaciones creen y adjunten anclajes a entidades espaciales, que se denominan "anclajes vinculados a entidades" en esta extensión.
Un anclaje vinculado a entidades se representa como una entidad espacial con el componente XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT y el componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. El componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT almacena el XrSpatialEntityIdEXT de la entidad principal a la que está adjunto el anclaje.
La postura de un anclaje vinculado a entidades siempre se representa mediante un desplazamiento fijo de su entidad superior, que se considera la "base" del anclaje. Por ejemplo, imagina que un usuario tiene un marco de fotos virtual adjunto a una pared, el usuario usa un anclaje vinculado a entidades para representar el marco de fotos virtual y lo adjunta a la entidad de la pared. Con eso, la posición relativa entre la pared y el marco de fotos siempre es coherente a medida que mejoran las estimaciones de seguimiento de la pared física.
Asistencia del entorno de ejecución
Un entorno de ejecución debe admitir al menos una extensión de seguimiento espacial, por ejemplo, XR_EXT_spatial_plane_tracking. Si el entorno de ejecución admite el anclaje vinculado a entidades espaciales, debe proporcionar al menos un componente adjuntable enumerando la función xrEnumerateSpatialAnchorAttachableComponentsANDROID. La aplicación puede enumerar los componentes adjuntables con xrEnumerateSpatialAnchorAttachableComponentsANDROID .
La función xrEnumerateSpatialAnchorAttachableComponentsANDROID se define de la siguiente manera:
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
Descripciones de parámetros
instancees un controlador para un XrInstance .systemIdes elXrSystemIdcuyos almacenes de persistencia espacial se enumerarán.attachableComponentCapacityInputes la capacidad del arrayattachableComponentso 0 para indicar una solicitud para recuperar la capacidad requerida.attachableComponentCountOutputes la cantidad de componentes adjuntables o la capacidad requerida en caso de queattachableComponentCapacityInputsea insuficiente.attachableComponentses un array de XrSpatialComponentTypeEXT . Puede serNULLsiattachableComponentCapacityInputes 0.- Consulta el capítulo Parámetros de tamaño del búfer para obtener una descripción detallada de la recuperación del tamaño
attachableComponentsrequerido.
Los entornos de ejecución deben siempre mostrar contenido de búfer idéntico de esta enumeración para el systemId determinado durante la vida útil de la instancia.
Uso válido (implícito)
- La extensión
XR_ANDROID_spatial_entity_bound_anchordebe habilitarse antes de llamar a xrEnumerateSpatialAnchorAttachableComponentsANDROID. -
instancedebe ser un controlador XrInstance válido. -
attachableComponentCountOutputdebe ser un puntero a un valoruint32_t. - Si
attachableComponentCapacityInputno es0,attachableComponentsdebe ser un puntero a un array 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
Crea un anclaje vinculado a entidades espaciales
Las aplicaciones usan la función xrCreateSpatialAnchorEXT para crear un anclaje. Si una aplicación desea crear un anclaje vinculado a entidades que esté adjunto a una entidad espacial, puede encadenar una estructura XrSpatialAnchorParentANDROID al siguiente puntero de la estructura XrSpatialAnchorCreateInfoEXT cuando llama a la función xrCreateSpatialAnchorEXT.
La estructura XrSpatialAnchorParentANDROID se define de la siguiente manera:
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
Descripciones de miembros
typees el XrStructureType de esta estructura.nextesNULLo un puntero a la siguiente estructura en una cadena de estructura.parentIdes elXrSpatialEntityIdEXTde la entidad a la que se adjuntará el anclaje.
El entorno de ejecución debe garantizar que la distancia entre la entidad principal y el anclaje siempre sea coherente, en la que la distancia es la postura del anclaje a la superficie más cercana de la entidad principal a lo largo de la normal de la superficie. La postura del anclaje se actualiza en función de la posición de la entidad principal y la distancia a la superficie de la entidad principal, independientemente de la cantidad de componentes adjuntables que tenga la entidad principal.
El entorno de ejecución debe mostrar XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT desde xrCreateSpatialAnchorEXT si XrSpatialAnchorParentANDROID ::parentId no es un ID válido para xrCreateSpatialAnchorEXT :: spatialContext .
El entorno de ejecución debe mostrar XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID desde xrCreateSpatialAnchorEXT si ninguno de los componentes enumerados por xrEnumerateSpatialAnchorAttachableComponentsANDROID está en la entidad principal.
Uso válido (implícito)
- La extensión
XR_ANDROID_spatial_entity_bound_anchordebe habilitarse antes de usar XrSpatialAnchorParentANDROID -
typedebe serXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextdebe serNULLo un puntero válido a la siguiente estructura en una cadena de estructura
Ejemplo de código
Crea un anclaje vinculado a entidades espaciales
En el siguiente ejemplo de código, se muestra cómo crear un anclaje vinculado a entidades y adjuntarlo a una entidad de seguimiento de planos espaciales.
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
// ...
}
Obtén la postura del anclaje vinculado a entidades y el ID de la entidad principal
En el siguiente ejemplo de código, se muestra cómo obtener la postura de un anclaje vinculado a entidades y el ID de su entidad principal.
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
// ...
}
Enumera los componentes adjuntables y verifica la capacidad de anclaje vinculada a entidades
En el siguiente ejemplo de código, se muestra cómo enumerar los componentes adjuntables y verificar si el entorno de ejecución admite la capacidad de anclaje vinculada a entidades.
// 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.
// ...
Comandos nuevos
Estructuras nuevas
Extensión de XrSpatialAnchorCreateInfoEXT :
Nuevas constantes de enumeración
XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAMEXR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSIONExtensión de XrResult :
XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
Extensión de XrStructureType :
XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID
Problemas
Historial de versiones
Revisión 1, 18/8/2025 (YuSheng Chang)
- Descripción inicial de la extensión.
Revisión 2, 16/12/2025 (Kyle Chen)
- Agrega un lenguaje más prescriptivo a ciertos comportamientos de la API.
- Agrega el código de ejemplo de xrEnumerateSpatialAnchorAttachableComponentsANDROID .