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

  • instance est un handle vers un XrInstance .
  • systemId est le XrSystemId dont les magasins de persistance spatiale seront énumérés.
  • attachableComponentCapacityInput est la capacité du tableau attachableComponents, ou 0 pour indiquer une requête visant à récupérer la capacité requise.
  • attachableComponentCountOutput est le nombre de composants associables ou la capacité requise si attachableComponentCapacityInput est insuffisant.
  • attachableComponents est un tableau de XrSpatialComponentTypeEXT . Il peut être NULL si attachableComponentCapacityInput est égal à 0.
  • Pour obtenir une description détaillée de la récupération de la taille attachableComponents requise, 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_anchor doit être activée avant d'appeler xrEnumerateSpatialAnchorAttachableComponentsANDROID.
  • instance doit être un handle XrInstance valide.
  • attachableComponentCountOutput doit être un pointeur vers une valeur uint32_t.
  • Si attachableComponentCapacityInput n'est pas 0 , attachableComponents doit être un pointeur vers un tableau de valeurs attachableComponentCapacityInput XrSpatialComponentTypeEXT.

Codes renvoyés

Opération réussie

  • XR_SUCCESS

Échec

  • 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

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

  • type est le XrStructureType de cette structure.
  • next est NULL ou un pointeur vers la structure suivante dans une chaîne de structures.
  • parentId est le XrSpatialEntityIdEXT de 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)

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

Nouvelles constantes d'énumération

  • XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAME
  • XR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION
  • Extension 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)