XR_ANDROID_spatial_entity_bound_anchor
Chaîne de nom
XR_ANDROID_spatial_entity_bound_anchor
Type d'extension
Extension d'instance
Numéro d'extension enregistré
791
Révision
2
État de ratification
Non ratifié
Dépendances d'extension et de version
XR_EXT_spatial_anchor
Date de dernière modification
2025-08-18
État de la propriété intellectuelle
Aucune réclamation connue concernant la propriété intellectuelle.
Participants
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
Présentation
Cette extension permet aux applications de créer et d'associer des ancres à des entités spatiales, appelées "ancres liées à une entité" dans cette extension.
Une ancre liée à une entité est représentée comme une entité spatiale avec (ou "qui comporte") le composant XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT et le composant XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. Le composant XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT stocke le XrSpatialEntityIdEXT de l'entité parente à laquelle l'ancre est associée.
La pose d'une ancre liée à une entité est toujours représentée par un décalage fixe par rapport à son entité parente, considérée comme la "base" de l'ancre. Par exemple, imaginez qu'un utilisateur a un cadre photo virtuel fixé à un mur. Il utilise une ancre liée à une entité pour représenter le cadre photo virtuel et l'associe à l'entité mur. Ainsi, la position relative entre le mur et le cadre photo est toujours cohérente à mesure que les estimations de suivi du mur physique s'améliorent.
Compatibilité des environnements d'exécution
Un environnement d'exécution doit être compatible avec au moins une extension de suivi spatial, par exemple XR_EXT_spatial_plane_tracking. Si l'environnement d'exécution est compatible avec l'ancre liée à une entité spatiale, il doit fournir au moins un composant associable en énumérant la fonction xrEnumerateSpatialAnchorAttachableComponentsANDROID. L'application peut énumérer les composants associables à l'aide de xrEnumerateSpatialAnchorAttachableComponentsANDROID .
La fonction xrEnumerateSpatialAnchorAttachableComponentsANDROID est définie comme suit :
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
Descriptions des paramètres
instanceest un handle vers un XrInstance .systemIdest leXrSystemIddont les magasins de persistance spatiale seront énumérés.attachableComponentCapacityInputest la capacité du tableauattachableComponents, ou 0 pour indiquer une requête visant à récupérer la capacité requise.attachableComponentCountOutputest le nombre de composants associables ou la capacité requise siattachableComponentCapacityInputest insuffisant.attachableComponentsest un tableau de XrSpatialComponentTypeEXT . Il peut êtreNULLsiattachableComponentCapacityInputest égal à 0.- Pour obtenir une description détaillée de la récupération de la taille
attachableComponentsrequise, consultez le chapitre Paramètres de taille du tampon.
Les environnements d'exécution doivent toujours renvoyer des contenus de tampon identiques à partir de cette énumération pour le systemId donné pendant la durée de vie de l'instance.
Utilisation valide (implicite)
- L'extension
XR_ANDROID_spatial_entity_bound_anchordoit être activée avant d'appeler xrEnumerateSpatialAnchorAttachableComponentsANDROID. -
instancedoit être un handle XrInstance valide. -
attachableComponentCountOutputdoit être un pointeur vers une valeuruint32_t. - Si
attachableComponentCapacityInputn'est pas0,attachableComponentsdoit être un pointeur vers un tableau de valeursattachableComponentCapacityInputXrSpatialComponentTypeEXT.
Codes renvoyés
XR_SUCCESS
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_RUNTIME_FAILUREXR_ERROR_SIZE_INSUFFICIENTXR_ERROR_SYSTEM_INVALIDXR_ERROR_VALIDATION_FAILURE
Créer une ancre liée à une entité spatiale
Les applications utilisent la fonction xrCreateSpatialAnchorEXT pour créer une ancre. Si une application souhaite créer une ancre liée à une entité associée à une entité spatiale, elle peut enchaîner une structure XrSpatialAnchorParentANDROID au pointeur suivant de la structure XrSpatialAnchorCreateInfoEXT lorsqu'elle appelle la fonction xrCreateSpatialAnchorEXT.
La structure XrSpatialAnchorParentANDROID est définie comme suit :
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
Descriptions des membres
typeest le XrStructureType de cette structure.nextestNULLou un pointeur vers la structure suivante dans une chaîne de structures.parentIdest leXrSpatialEntityIdEXTde l'entité à laquelle l'ancre sera associée.
L'environnement d'exécution doit garantir que la distance entre l'entité parente et l'ancre est toujours cohérente, où la distance correspond à la pose de l'ancre par rapport à la surface la plus proche de l'entité parente le long de la normale de la surface. La pose de l'ancre est mise à jour en fonction de la position de l'entité parente et de la distance par rapport à la surface de l'entité parente, quel que soit le nombre de composants associables dont dispose l'entité parente.
L'environnement d'exécution doit renvoyer XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT à partir de xrCreateSpatialAnchorEXT si XrSpatialAnchorParentANDROID ::parentId n'est pas un ID valide pour xrCreateSpatialAnchorEXT :: spatialContext .
L'environnement d'exécution doit renvoyer XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID à partir de xrCreateSpatialAnchorEXT si aucun des composants énumérés par xrEnumerateSpatialAnchorAttachableComponentsANDROID ne se trouve sur l'entité parente.
Utilisation valide (implicite)
- L'extension
XR_ANDROID_spatial_entity_bound_anchordoit être activée avant d'utiliser XrSpatialAnchorParentANDROID -
typedoit êtreXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextdoit êtreNULLou un pointeur valide vers la structure suivante dans une chaîne de structures
Exemple de code
Créer une ancre liée à une entité spatiale
L'exemple de code suivant montre comment créer une ancre liée à une entité et l'associer à une entité de suivi de plan spatial.
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
// ...
}
Obtenir la pose de l'ancre liée à une entité et l'ID de l'entité parente
L'exemple de code suivant montre comment obtenir la pose d'une ancre liée à une entité et l'ID de son entité parente.
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
// ...
}
Énumérer les composants associables et vérifier la capacité de l'ancre liée à une entité
L'exemple de code suivant montre comment énumérer les composants associables et vérifier si l'environnement d'exécution est compatible avec la capacité de l'ancre liée à une entité.
// 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.
// ...
Nouvelles commandes
Nouvelles structures
Extension de XrSpatialAnchorCreateInfoEXT :
Nouvelles constantes d'énumération
XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAMEXR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSIONExtension de XrResult :
XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
Extension de XrStructureType :
XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID
Problèmes
Historique des versions
Révision 1, 2025-08-18 (YuSheng Chang)
- Description initiale de l'extension.
Révision 2, 2025-12-16 (Kyle Chen)
- Ajout d'un langage plus prescriptif à certains comportements de l'API.
- Ajout de l'exemple de code de xrEnumerateSpatialAnchorAttachableComponentsANDROID .