XR_ANDROID_spatial_entity_bound_anchor
מחרוזת שם
XR_ANDROID_spatial_entity_bound_anchor
סוג התוסף
הרחבת המכונה
מספר שלוחה רשום
791
Revision
2
סטטוס האישור
לא אושר
תלות בתוסף ובגרסה
XR_EXT_spatial_anchor
תאריך השינוי האחרון
2025-08-18
סטטוס כתובת ה-IP
לא ידוע על תלונות בנושא זכויות יוצרים שקשורות לכתובות 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.
// ...
פקודות חדשות
מבנים חדשים
New Enum Constants
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 (יושנג צ'אנג)
- התיאור הראשוני של התוסף.
גרסה 2, 2025-12-16 (Kyle Chen)
- הוספנו שפה יותר מפורטת לגבי התנהגויות מסוימות של ה-API.
- מוסיפים את קוד הדוגמה של xrEnumerateSpatialAnchorAttachableComponentsANDROID .