XR_ANDROID_spatial_entity_bound_anchor
Stringa del nome
XR_ANDROID_spatial_entity_bound_anchor
Tipo di estensione
Estensione dell'istanza
Numero di estensione registrato
791
Revisione
2
Stato di ratifica
Non ratificato
Dipendenze di estensione e versione
XR_EXT_spatial_anchor
Data dell'ultima modifica
2025-08-18
Stato della proprietà intellettuale
Nessuna rivendicazione di proprietà intellettuale nota.
Collaboratori
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
Panoramica
Questa estensione consente alle applicazioni di creare e collegare ancore a entità spaziali, chiamate "ancore associate a entità" in questa estensione.
Un'ancora associata a un'entità è rappresentata come un'entità spaziale con (o "che ha") il componente XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT e il componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT. Il componente XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT memorizza l'elemento XrSpatialEntityIdEXT dell'entità principale a cui è collegata l'ancora.
La posa di un'ancora associata a un'entità è sempre rappresentata da un offset fisso rispetto all'entità principale, considerata la "base" dell'ancora. Ad esempio, supponiamo che un utente abbia una cornice virtuale attaccata a una parete, utilizzi un'ancora associata a un'entità per rappresentare la cornice virtuale e la attacchi all'entità della parete. In questo modo, la posizione relativa tra la parete e la cornice è sempre coerente man mano che le stime di monitoraggio della parete fisica migliorano.
Supporto di runtime
Un runtime deve supportare almeno un'estensione di monitoraggio spaziale, ad es. XR_EXT_spatial_plane_tracking . Se il runtime supporta l'ancora associata a un'entità spaziale, deve fornire almeno un componente collegabile enumerando la funzione xrEnumerateSpatialAnchorAttachableComponentsANDROID. L'applicazione può enumerare i componenti collegabili utilizzando xrEnumerateSpatialAnchorAttachableComponentsANDROID .
La funzione xrEnumerateSpatialAnchorAttachableComponentsANDROID è definita come segue:
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
Descrizioni dei parametri
instanceè un handle per un XrInstance .systemIdè l'elementoXrSystemIddi cui verranno enumerati gli archivi di persistenza spaziale.attachableComponentCapacityInputè la capacità dell'arrayattachableComponentso 0 per indicare una richiesta di recupero della capacità richiesta.attachableComponentCountOutputè il numero di componenti collegabili o la capacità richiesta nel caso in cuiattachableComponentCapacityInputsia insufficiente.attachableComponentsè un array di XrSpatialComponentTypeEXT . Può essereNULLseattachableComponentCapacityInputè 0.- Per una descrizione dettagliata del recupero della dimensione
attachableComponentsrichiesta, consulta il capitolo Parametri della dimensione del buffer.
I runtime devono sempre restituire contenuti del buffer identici da questa enumerazione per il systemId specificato per la durata dell'istanza.
Utilizzo valido (implicito)
- L'estensione
XR_ANDROID_spatial_entity_bound_anchordeve essere abilitata prima di chiamare xrEnumerateSpatialAnchorAttachableComponentsANDROID -
instancedeve essere un handle XrInstance valido -
attachableComponentCountOutputdeve essere un puntatore a un valoreuint32_t - Se
attachableComponentCapacityInputnon è0,attachableComponentsdeve essere un puntatore a un array di valori XrSpatialComponentTypeEXTattachableComponentCapacityInput
Codici restituiti
XR_SUCCESS
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_RUNTIME_FAILUREXR_ERROR_SIZE_INSUFFICIENTXR_ERROR_SYSTEM_INVALIDXR_ERROR_VALIDATION_FAILURE
Creazione di un'ancora associata a un'entità spaziale
Le applicazioni utilizzano la funzione xrCreateSpatialAnchorEXT per creare un'ancora. Se un'applicazione vuole creare un'ancora associata a un'entità collegata a un'entità spaziale, può concatenare una struttura XrSpatialAnchorParentANDROID al puntatore successivo della struttura XrSpatialAnchorCreateInfoEXT quando chiama la funzione xrCreateSpatialAnchorEXT.
La struttura XrSpatialAnchorParentANDROID è definita come segue:
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
Descrizioni dei membri
typeè l'elemento XrStructureType di questa struttura.nextèNULLo un puntatore alla struttura successiva in una catena di strutture.parentIdè l'elementoXrSpatialEntityIdEXTdell'entità a cui verrà collegata l'ancora.
Il runtime deve garantire che la distanza tra l'entità principale e l'ancora sia sempre coerente, dove la distanza è la posa dell'ancora rispetto alla superficie più vicina dell'entità principale lungo la normale della superficie. La posa dell'ancora viene aggiornata in base alla posizione dell'entità principale e alla distanza dalla superficie dell'entità principale, indipendentemente dal numero di componenti collegabili dell'entità principale.
Il runtime deve restituire XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT da xrCreateSpatialAnchorEXT se XrSpatialAnchorParentANDROID ::parentId non è un ID valido per xrCreateSpatialAnchorEXT :: spatialContext .
Il runtime deve restituire XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID da xrCreateSpatialAnchorEXT se nessuno dei componenti enumerati da xrEnumerateSpatialAnchorAttachableComponentsANDROID si trova sull'entità principale.
Utilizzo valido (implicito)
- L'estensione
XR_ANDROID_spatial_entity_bound_anchordeve essere abilitata prima di utilizzare XrSpatialAnchorParentANDROID -
typedeve essereXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextdeve essereNULLo un puntatore valido alla struttura successiva in una catena di strutture
Esempio di codice
Creazione di un'ancora associata a un'entità spaziale
Il seguente esempio di codice mostra come creare un'ancora associata a un'entità e collegarla a un'entità di monitoraggio del piano spaziale.
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
// ...
}
Recupero della posa dell'ancora associata a un'entità e dell'ID dell'entità principale
Il seguente esempio di codice mostra come ottenere la posa di un'ancora associata a un'entità e l'ID della relativa entità principale.
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
// ...
}
Enumerazione dei componenti collegabili e controllo della funzionalità dell'ancora associata a un'entità
Il seguente esempio di codice mostra come enumerare i componenti collegabili e verificare se il runtime supporta la funzionalità dell'ancora associata a un'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.
// ...
Nuovi comandi
Nuove strutture
Estensione di XrSpatialAnchorCreateInfoEXT :
Nuove costanti enum
XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAMEXR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSIONEstensione di XrResult :
XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
Estensione di XrStructureType :
XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID
Problemi
Cronologia delle versioni
Revisione 1, 2025-08-18 (YuSheng Chang)
- Descrizione iniziale dell'estensione.
Revisione 2, 2025-12-16 (Kyle Chen)
- Aggiungi un linguaggio più prescrittivo a determinati comportamenti dell'API.
- Aggiungi l'esempio di codice di xrEnumerateSpatialAnchorAttachableComponentsANDROID .