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 است، یا ۰ برای نشان دادن درخواست بازیابی ظرفیت موردنیاز.
  • ‫attachableComponentCountOutput تعداد مؤلفه‌های قابل‌پیوست است، یا ظرفیت موردنیاز درصورتی‌که attachableComponentCapacityInput کافی نباشد.
  • ‫attachableComponents آرایه‌ای از XrSpatialComponentTypeEXT است. اگر attachableComponentCapacityInput برابر با ۰ باشد، می‌تواند NULL باشد.
  • برای شرح دقیق بازیابی اندازه 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 نهادی که لنگر به آن متصل خواهد شد.

زمان اجرا باید تضمین کند که فاصله بین موجودیت ولی و لنگر همیشه ثابت است، که در آن فاصله عبارت است از حالت لنگر تا نزدیک‌ترین سطح موجودیت ولی در امتداد سطح نرمال. وضعیت لنگر براساس موقعیت نهاد ولی و فاصله تا سطح نهاد ولی به‌روز می‌شود، صرف‌نظر از اینکه نهاد ولی چند مؤلفه قابل‌اتصال دارد.

اگر XrSpatialAnchorParentANDROID ::parentId شناسه معتبری برای xrCreateSpatialAnchorEXT :: spatialContext نباشد، زمان اجرا باید XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT را از xrCreateSpatialAnchorEXT برگرداند.

اگر هیچ‌کدام از مؤلفه‌های شمارش‌شده توسط xrEnumerateSpatialAnchorAttachableComponentsANDROID در موجودیت والد نباشند، زمان اجرا باید XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID را از xrCreateSpatialAnchorEXT برگرداند.

استفاده معتبر (ضمنی)

کد نمونه

ایجاد لنگر محدود به موجودیت فضایی

کد نمونه زیر نشان می‌دهد که چگونه می‌توان یک لنگر محدود به موجودیت ایجاد کرد و آن را به یک موجودیت ردیابی صفحه فضایی متصل کرد.

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.
// ...

فرمان‌های جدید

سازه‌های جدید

ثابت‌های شمارشی جدید

  • 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

مشکلات

سابقه نسخه

  • بازبینی ۱، ۲۰۲۵-۰۸-۱۸ (YuSheng Chang)

    • توضیحات اولیه افزونه.
  • بازبینی ۲،‏ ۲۰۲۵/۱۲/۱۶ (کایل چن)