XR_ANDROID_spatial_entity_bound_anchor
מחרוזת שם
XR_ANDROID_spatial_entity_bound_anchor
סוג התוסף
תוסף למכונה
מספר השלוחה הרשום
791
גרסה
2
סטטוס האישור
לא אושר
תלויות של תוספים וגרסאות
XR_EXT_spatial_anchor
תאריך השינוי האחרון
2025-08-18
סטטוס IP
אין תלונות ידועות על הפרת קניין רוחני.
שותפים ביצירת התוכן
YuSheng Chang, Google
Kyle Chen, Google
Nihav Jain, Google
Levana Chen, Google
Spencer Quin, Google
סקירה כללית
התוסף הזה מאפשר לאפליקציות ליצור עוגנים ולצרף אותם לישויות מרחביות, שנקראות "עוגנים שקשורים לישויות" בתוסף הזה.
עוגן שקשור לישות מיוצג כישות מרחבית עם הרכיב 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 . הערך שלNULLיכול להיותattachableComponentCapacityInputאם הוא 0. - בפרק Buffer Size Parameters יש תיאור מפורט של אחזור הגודל הנדרש של
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 כדי ליצור נקודת עיגון. אם אפליקציה רוצה ליצור עוגן שמקושר לישות מרחבית, היא יכולה לשרשר מבנה XrSpatialAnchorParentANDROID למצביע הבא של מבנה XrSpatialAnchorCreateInfoEXT כשקוראים לפונקציה xrCreateSpatialAnchorEXT.
המבנה של XrSpatialAnchorParentANDROID מוגדר כך:
typedef struct XrSpatialAnchorParentANDROID {
XrStructureType type;
const void* next;
XrSpatialEntityIdEXT parentId;
} XrSpatialAnchorParentANDROID;
תיאורי חברים
-
typeהוא ה-XrStructureType של המבנה הזה. -
nextהואNULLאו מצביע למבנה הבא בשרשרת מבנים. -
parentIdהוא ה-XrSpatialEntityIdEXTשל הישות שאליה העוגן יצורף.
זמן הריצה חייב להבטיח שהמרחק בין ישות האם לבין העוגן תמיד עקבי, כאשר המרחק הוא התנוחה של העוגן למשטח הקרוב ביותר של ישות האם לאורך הנורמל של המשטח. התנוחה של נקודת העיגון מתעדכנת על סמך המיקום של ישות האם והמרחק למשטח של ישות האם, בלי קשר למספר הרכיבים שניתן לצרף לישות האם.
זמן הריצה חייב להחזיר את הערך 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או מצביע תקין למבנה הבא בשרשרת מבנים
קוד לדוגמה
יצירת עוגן שקשור לישות מרחבית
הקוד לדוגמה הבא מדגים איך ליצור עוגן שמקושר לישות ולצרף אותו לישות מעקב של מישור מרחבי.
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
// ...
}
קבלת תנוחת עוגן שקשורה לישות ומזהה ישות אם
הקוד לדוגמה הבא מדגים איך לקבל את התנוחה של עוגן שקשור לישות ואת המזהה של ישות האם שלו.
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.
// ...
פקודות חדשות
מבנים חדשים
קבועי 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, 18.08.2025 (YuSheng Chang)
- תיאור התוסף הראשוני.
גרסה 2, 16 בדצמבר 2025 (קייל צ'ן)
- הוספת שפה יותר נורמטיבית להתנהגויות מסוימות של ה-API.
- מוסיפים את הקוד לדוגמה של xrEnumerateSpatialAnchorAttachableComponentsANDROID .