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 חייב להיות מצביע למערך של ערכי attachableComponentCapacityInput XrSpatialComponentTypeEXT

קודי החזרה

הצלחה

  • XR_SUCCESS

נכשל

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SIZE_INSUFFICIENT
  • XR_ERROR_SYSTEM_INVALID
  • XR_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 לא נמצא בישות האם.

שימוש תקין (משתמע)

קוד לדוגמה

יצירת עוגן שקשור לישות מרחבית

הקוד לדוגמה הבא מדגים איך ליצור עוגן שמקושר לישות ולצרף אותו לישות מעקב של מישור מרחבי.

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_NAME
  • XR_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 (קייל צ'ן)