XR_ANDROID_spatial_entity_bound_anchor

Name String

XR_ANDROID_spatial_entity_bound_anchor

ประเภทส่วนขยาย

ส่วนขยายอินสแตนซ์

หมายเลขต่อที่ลงทะเบียนไว้

791

การแก้ไข

2

สถานะการให้สัตยาบัน

ไม่ได้รับการให้สัตยาบัน

ทรัพยากร Dependency ของส่วนขยายและเวอร์ชัน

XR_EXT_spatial_anchor

วันที่แก้ไขล่าสุด

2025-08-18

สถานะ IP

ไม่มีการอ้างสิทธิ์ IP ที่ทราบ

ผู้เขียน

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 เป็นแฮนเดิลของ XrInstance
  • systemId คือ XrSystemId ที่จะมีการแจกแจงพื้นที่เก็บข้อมูลถาวรเชิงพื้นที่
  • attachableComponentCapacityInput คือความจุของอาร์เรย์ attachableComponents หรือ 0 เพื่อระบุคำขอให้ดึงข้อมูลความจุที่จำเป็น
  • attachableComponentCountOutput คือจำนวนส่วนประกอบที่ต่อพ่วงได้ หรือความจุที่จำเป็นในกรณีที่ attachableComponentCapacityInput ไม่เพียงพอ
  • attachableComponents คืออาร์เรย์ของ XrSpatialComponentTypeEXT สามารถเป็น NULL ได้หาก attachableComponentCapacityInput เป็น 0
  • ดูคำอธิบายโดยละเอียดเกี่ยวกับการดึงข้อมูลขนาด 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

การสร้าง Anchor ที่เชื่อมโยงกับเอนทิตีเชิงพื้นที่

แอปพลิเคชันจะใช้ฟังก์ชัน xrCreateSpatialAnchorEXT เพื่อสร้าง Anchor หากแอปพลิเคชันต้องการสร้าง Anchor ที่เชื่อมโยงกับเอนทิตีซึ่งแนบอยู่กับเอนทิตีเชิงพื้นที่ แอปพลิเคชันสามารถเชื่อมโยงโครงสร้าง XrSpatialAnchorParentANDROID กับพอยน์เตอร์ถัดไปของโครงสร้าง XrSpatialAnchorCreateInfoEXT เมื่อเรียกใช้ฟังก์ชัน xrCreateSpatialAnchorEXT

โครงสร้าง XrSpatialAnchorParentANDROID มีคำจำกัดความดังนี้

typedef struct XrSpatialAnchorParentANDROID {
    XrStructureType         type;
    const void*             next;
    XrSpatialEntityIdEXT    parentId;
} XrSpatialAnchorParentANDROID;

คำอธิบายสมาชิก

  • type คือ XrStructureType ของโครงสร้างนี้
  • next คือ NULL หรือตัวชี้ไปยังโครงสร้างถัดไปในเชนโครงสร้าง
  • 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 อยู่ในเอนทิตีหลัก

การใช้งานที่ถูกต้อง (โดยนัย)

โค้ดตัวอย่าง

สร้าง 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_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-12-2025 (Kyle Chen)

    • เพิ่มภาษาที่กำหนดมากขึ้นสำหรับลักษณะการทำงานบางอย่างของ API
    • เพิ่มโค้ดตัวอย่างของ xrEnumerateSpatialAnchorAttachableComponentsANDROID