XR_ANDROID_spatial_entity_bound_anchor
名稱字串
XR_ANDROID_spatial_entity_bound_anchor
擴充功能類型
執行個體擴充功能
擴充功能註冊編號
791
修訂版本
2
批准狀態
未批准
擴充功能和版本依附元件
XR_EXT_spatial_anchor
上次修改日期
2025-08-18
IP 狀態
沒有已知的智慧財產權聲明。
著作人
Google 的 YuSheng Chang
Google 的 Kyle Chen
Google 的 Nihav Jain
Google 的 Levana Chen
Google 的 Spencer Quin
總覽
這項擴充功能可讓應用程式建立錨點並附加至空間實體,這項擴充功能將這類錨點稱為「實體繫結錨點」。
實體繫結的錨點會以空間實體表示,並具有 XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT 元件和 XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT 元件。XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT 元件會儲存錨點所連結上層實體的 XrSpatialEntityIdEXT。
實體繫結錨點的姿勢一律以與父項實體的固定位移表示,父項實體可視為錨點的「基準」。舉例來說,假設使用者在牆上掛了虛擬相框,並使用實體繫結錨點代表虛擬相框,然後將其附加至牆壁實體。因此,隨著實體牆壁的追蹤估算值提升,牆壁和相框之間的相對位置也會保持一致。
執行階段支援
執行階段必須支援至少一個空間追蹤擴充功能,例如 XR_EXT_spatial_plane_tracking。如果執行階段支援空間實體繫結錨點,則必須透過列舉 xrEnumerateSpatialAnchorAttachableComponentsANDROID 函式,提供至少一個可附加的元件。應用程式可以使用 xrEnumerateSpatialAnchorAttachableComponentsANDROID 列舉可附加的元件。
xrEnumerateSpatialAnchorAttachableComponentsANDROID 函式定義如下:
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
參數說明
instance是 XrInstance 的控制代碼。systemId是要列舉空間持續性儲存空間的XrSystemId。attachableComponentCapacityInput是attachableComponents陣列的容量,或 0 (表示要求擷取必要容量)。attachableComponentCountOutput是可附加的元件數量,或在attachableComponentCapacityInput不足時的必要容量。attachableComponents是 XrSpatialComponentTypeEXT 的陣列。如果attachableComponentCapacityInput為 0,則 可以是NULL。- 如要詳細瞭解如何擷取所需
attachableComponents大小,請參閱「緩衝區大小參數」一章。
在例項的生命週期內,執行階段必須一律從這個列舉傳回指定 systemId 的相同緩衝區內容。
有效使用 (隱含)
- 必須先啟用
XR_ANDROID_spatial_entity_bound_anchor擴充功能,才能呼叫 xrEnumerateSpatialAnchorAttachableComponentsANDROID。 -
instance必須是有效的 XrInstance 控制代碼 -
attachableComponentCountOutput必須是指向uint32_t值的指標 - 如果
attachableComponentCapacityInput不是0,attachableComponents必須是指向attachableComponentCapacityInputXrSpatialComponentTypeEXT 值陣列的指標
傳回代碼
XR_SUCCESS
XR_ERROR_FUNCTION_UNSUPPORTEDXR_ERROR_HANDLE_INVALIDXR_ERROR_INSTANCE_LOSTXR_ERROR_RUNTIME_FAILUREXR_ERROR_SIZE_INSUFFICIENTXR_ERROR_SYSTEM_INVALIDXR_ERROR_VALIDATION_FAILURE
建立空間實體繫結錨點
應用程式會使用 xrCreateSpatialAnchorEXT 函式建立錨點。如果應用程式想建立與空間實體連結的錨點,可以在呼叫 xrCreateSpatialAnchorEXT 函式時,將 XrSpatialAnchorParentANDROID 結構體鏈結至 XrSpatialAnchorCreateInfoEXT 結構體的下一個指標。
XrSpatialAnchorParentANDROID 結構體的定義如下:
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
成員說明
type是這個結構的 XrStructureType。next是NULL,或是結構鏈中下一個結構的指標。parentId是錨點要附加的實體XrSpatialEntityIdEXT。
執行階段必須確保父項實體與錨點之間的距離一律保持一致,其中距離是指錨點相對於父項實體最近表面的姿態,沿著表面的法線。無論上層實體有多少可附加的元件,錨點的姿勢都會根據上層實體的位置和與上層實體表面的距離更新。
如果 XrSpatialAnchorParentANDROID ::parentId 不是 xrCreateSpatialAnchorEXT :: spatialContext 的有效 ID,執行階段必須從 xrCreateSpatialAnchorEXT 傳回 XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT。
如果 xrEnumerateSpatialAnchorAttachableComponentsANDROID 列舉的任何元件都不在上層實體上,執行階段必須從 xrCreateSpatialAnchorEXT 傳回 XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID。
有效使用 (隱含)
-
XR_ANDROID_spatial_entity_bound_anchor擴充功能必須先啟用,才能使用 XrSpatialAnchorParentANDROID -
type必須為XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
next必須是NULL,或是結構體鏈結中下一個結構體的有效指標
範例程式碼
建立空間實體繫結錨點
以下程式碼範例示範如何建立繫結至實體的錨點,並將其附加至空間平面追蹤實體。
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
// ...
}
取得實體繫結的錨點姿勢和上層實體 ID
以下範例程式碼示範如何取得實體繫結錨點的姿勢,以及其上層實體的 ID。
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
// ...
}
列舉可附加的元件,並檢查實體繫結錨點功能
下列程式碼範例說明如何列舉可附加的元件,並檢查執行階段是否支援實體繫結錨點功能。
// 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.
// ...
新指令
新結構
新增列舉常數
XR_ANDROID_SPATIAL_ENTITY_BOUND_ANCHOR_EXTENSION_NAMEXR_ANDROID_spatial_entity_bound_anchor_SPEC_VERSION擴充 XrResult:
XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID
擴充 XrStructureType:
XR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID
問題
版本記錄
修訂版本 1,2025-08-18 (YuSheng Chang)
- 擴充功能說明。
修訂版本 2,2025 年 12 月 16 日 (Kyle Chen)
- 為 API 的特定行為新增更多規範性語言。
- 新增 xrEnumerateSpatialAnchorAttachableComponentsANDROID 的程式碼範例。