XR_ANDROID_spatial_entity_bound_anchor
Name String
XR_ANDROID_spatial_entity_bound_anchor
Extension Type
Instance extension
Registered Extension Number
791
Revision
2
Ratification Status
Not ratified
Extension and Version Dependencies
XR_EXT_spatial_anchor
Last Modified Date
2025-08-18
IP Status
No known IP claims.
Contributors
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
ภาพรวม
ส่วนขยายนี้ช่วยให้แอปพลิเคชันสร้างและแนบ Anchor กับเอนทิตีเชิงพื้นที่ ซึ่งในส่วนขยายนี้เรียกว่า "Anchor ที่ผูกกับเอนทิตี"
Anchor ที่ผูกกับเอนทิตีจะแสดงเป็นเอนทิตีเชิงพื้นที่ที่มี (หรือ "ซึ่งมี") คอมโพเนนต์ XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT และคอมโพเนนต์ XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT คอมโพเนนต์ XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT จะจัดเก็บ XrSpatialEntityIdEXT ของเอนทิตีหลักที่แนบ Anchor ไว้
ท่าทางของ Anchor ที่ผูกกับเอนทิตีจะแสดงด้วยออฟเซ็ตคงที่จากเอนทิตีระดับบนเสมอ ซึ่งถือเป็น "ฐาน" ของ Anchor ตัวอย่างเช่น ลองนึกภาพว่าผู้ใช้มีกรอบรูปเสมือนที่ติดอยู่กับผนัง ผู้ใช้ใช้ Anchor ที่ผูกกับเอนทิตีเพื่อแสดงกรอบรูปเสมือนและแนบไว้กับเอนทิตีผนัง ด้วยวิธีนี้ ตำแหน่งสัมพัทธ์ระหว่างผนังกับกรอบรูปจะสอดคล้องกันเสมอเมื่อการประมาณการติดตามของผนังจริงดีขึ้น
การรองรับรันไทม์
รันไทม์ต้อง รองรับส่วนขยายการติดตามเชิงพื้นที่อย่างน้อย 1 รายการ เช่น XR_EXT_spatial_plane_tracking หากรันไทม์รองรับ Anchor ที่ผูกกับเอนทิตีเชิงพื้นที่ ต้อง ระบุคอมโพเนนต์ที่แนบได้ อย่างน้อย 1 รายการโดยการแจกแจงฟังก์ชัน xrEnumerateSpatialAnchorAttachableComponentsANDROID แอปพลิเคชันสามารถ แจกแจงคอมโพเนนต์ที่แนบได้โดยใช้ xrEnumerateSpatialAnchorAttachableComponentsANDROID
ฟังก์ชัน xrEnumerateSpatialAnchorAttachableComponentsANDROID มีคำจำกัดความดังนี้
XrResult xrEnumerateSpatialAnchorAttachableComponentsANDROID(
XrInstance instance,
XrSystemId systemId,
uint32_t attachableComponentCapacityInput,
uint32_t* attachableComponentCountOutput,
XrSpatialComponentTypeEXT* attachableComponents);
คำอธิบายพารามิเตอร์
instanceคือแฮนเดิลไปยัง XrInstancesystemIdคือXrSystemIdที่จะมีการแจกแจงที่เก็บข้อมูลความคงอยู่เชิงพื้นที่attachableComponentCapacityInputคือความจุของอาร์เรย์attachableComponentsหรือ 0 เพื่อระบุคำขอให้ดึงข้อมูลความจุที่จำเป็นattachableComponentCountOutputคือจำนวนคอมโพเนนต์ที่แนบได้ หรือความจุที่จำเป็นในกรณีที่attachableComponentCapacityInputไม่เพียงพอattachableComponentsคืออาร์เรย์ของ XrSpatialComponentTypeEXT ซึ่งอาจ เป็นNULLหากattachableComponentCapacityInputเป็น 0- ดูคำอธิบายโดยละเอียดเกี่ยวกับการดึงข้อมูลขนาด
attachableComponentsที่จำเป็นได้ในบทพารามิเตอร์ขนาดบัฟเฟอร์
รันไทม์ต้อง แสดงผลเนื้อหาบัฟเฟอร์ที่เหมือนกันจากการแจกแจงนี้เสมอสำหรับ systemId ที่ระบุตลอดอายุการใช้งานของอินสแตนซ์
การใช้งานที่ถูกต้อง (โดยนัย)
- ต้องเปิดใช้ส่วนขยาย
XR_ANDROID_spatial_entity_bound_anchorก่อน เรียก xrEnumerateSpatialAnchorAttachableComponentsANDROID -
instanceต้อง เป็นแฮนเดิล XrInstance ที่ถูกต้อง -
attachableComponentCountOutputต้อง เป็น Pointer ไปยังค่า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
การสร้าง Anchor ที่ผูกกับเอนทิตีเชิงพื้นที่
แอปพลิเคชันใช้ฟังก์ชัน xrCreateSpatialAnchorEXT เพื่อสร้าง Anchor หากแอปพลิเคชันต้องการสร้าง Anchor ที่ผูกกับเอนทิตีซึ่งแนบอยู่กับเอนทิตีเชิงพื้นที่ สามารถ เชื่อมโยงโครงสร้าง XrSpatialAnchorParentANDROID กับตัวชี้ถัดไปของโครงสร้าง XrSpatialAnchorCreateInfoEXT เมื่อเรียกฟังก์ชัน xrCreateSpatialAnchorEXT
โครงสร้าง XrSpatialAnchorParentANDROID มีคำจำกัดความดังนี้
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
คำอธิบายสมาชิก
typeคือ XrStructureType ของโครงสร้างนี้nextคือNULLหรือ Pointer ไปยังโครงสร้างถัดไปในเชนโครงสร้างparentIdคือXrSpatialEntityIdEXTของเอนทิตีที่จะแนบ Anchor
รันไทม์ต้อง รับประกันว่าระยะห่างระหว่างเอนทิตีระดับบนกับ Anchor จะสอดคล้องกันเสมอ โดยระยะห่างคือท่าทางของ Anchor ไปยังพื้นผิวที่ใกล้ที่สุดของเอนทิตีระดับบนตามปกติของพื้นผิว ท่าทางของ Anchor จะอัปเดตตามตำแหน่งของเอนทิตีระดับบนและระยะห่างจากพื้นผิวของเอนทิตีระดับบน ไม่ว่าเอนทิตีระดับบนจะมีคอมโพเนนต์ที่แนบได้กี่รายการก็ตาม
รันไทม์ต้อง แสดงผล XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT จาก xrCreateSpatialAnchorEXT หาก XrSpatialAnchorParentANDROID ::parentId ไม่ใช่รหัสที่ถูกต้องสำหรับ xrCreateSpatialAnchorEXT :: spatialContext
รันไทม์ต้อง แสดงผล XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID จาก xrCreateSpatialAnchorEXT หากไม่มีคอมโพเนนต์ที่แจกแจงโดย xrEnumerateSpatialAnchorAttachableComponentsANDROID อยู่ในเอนทิตีระดับบน
การใช้งานที่ถูกต้อง (โดยนัย)
- ต้องเปิดใช้ส่วนขยาย
XR_ANDROID_spatial_entity_bound_anchor**ก่อน** ใช้ XrSpatialAnchorParentANDROID -
typeต้อง เป็นXR_TYPE_SPATIAL_ANCHOR_PARENT_ANDROID -
nextต้อง เป็นNULLหรือตัวชี้ที่ถูกต้องไปยังโครงสร้างถัดไปในห่วงโซ่โครงสร้าง
โค้ดตัวอย่าง
สร้าง Anchor ที่ผูกกับเอนทิตีเชิงพื้นที่
โค้ดตัวอย่างต่อไปนี้แสดงวิธีสร้าง Anchor ที่ผูกกับเอนทิตีและแนบกับเอนทิตีการติดตามระนาบเชิงพื้นที่
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
// ...
}
รับท่าทางของ Anchor ที่ผูกกับเอนทิตีและรหัสเอนทิตีหลัก
โค้ดตัวอย่างต่อไปนี้แสดงวิธีรับท่าทางของ Anchor ที่ผูกกับเอนทิตีและรหัสของเอนทิตีหลัก
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
// ...
}
แจกแจงคอมโพเนนต์ที่แนบได้และตรวจสอบความสามารถของ Anchor ที่ผูกกับเอนทิตี
โค้ดตัวอย่างต่อไปนี้แสดงวิธีแจกแจงคอมโพเนนต์ที่แนบได้และตรวจสอบว่ารันไทม์รองรับความสามารถของ Anchor ที่ผูกกับเอนทิตีหรือไม่
// 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.
// ...
คำสั่งใหม่
โครงสร้างใหม่
ค่าคงที่ Enum ใหม่
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