XR_ANDROID_spatial_entity_bound_anchor

رشته نام

XR_ANDROID_spatial_entity_bound_anchor

نوع افزونه

افزونه نمونه

شماره داخلی ثبت شده

۷۹۱

بازنگری

۲

وضعیت تصویب

تصویب نشده

وابستگی‌های افزونه و نسخه

XR_EXT_spatial_anchor

آخرین تاریخ اصلاح

۲۰۲۵-۰۸-۱۸

وضعیت آی‌پی

هیچ ادعای مالکیت معنوی شناخته‌شده‌ای وجود ندارد.

مشارکت‌کنندگان

یوشنگ چانگ، گوگل
کایل چن، گوگل
نیهاو جین، گوگل
لوانا چن، گوگل
اسپنسر کوئین، گوگل

نمای کلی

این افزونه به برنامه‌ها اجازه می‌دهد تا لنگرهایی (anchors) ایجاد کرده و به موجودیت‌های مکانی (spatial entity) متصل کنند، که در این افزونه به آنها «لنگرهای وابسته به موجودیت» (entity-bound anchors) گفته می‌شود.

یک لنگرِ وابسته به موجودیت، به عنوان یک موجودیت مکانی با (یا «دارای») کامپوننت 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 است. اگر 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 برای ایجاد یک anchor استفاده می‌کنند. اگر برنامه‌ای بخواهد یک anchor وابسته به entity ایجاد کند که به یک موجودیت مکانی متصل است، می‌تواند هنگام فراخوانی تابع xrCreateSpatialAnchorEXT ، یک ساختار XrSpatialAnchorParentANDROID را به اشاره‌گر بعدی ساختار XrSpatialAnchorCreateInfoEXT زنجیر کند.

ساختار XrSpatialAnchorParentANDROID به صورت زیر تعریف می‌شود:

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

توضیحات اعضا

  • type نوع XrStructureType این ساختار است.
  • next NULL یا اشاره‌گری به ساختار بعدی در یک زنجیره ساختار است.
  • parentId ، XrSpatialEntityIdEXT مربوط به موجودیتی است که anchor به آن متصل خواهد شد.

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

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

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

کاربرد معتبر (ضمنی)

کد مثال

ایجاد لنگر وابسته به موجودیت مکانی

کد مثال زیر نحوه ایجاد یک anchor متصل به entity و اتصال آن به یک موجودیت ردیابی صفحه مکانی را نشان می‌دهد.

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 متصل به entity و شناسه موجودیت والد آن را نشان می‌دهد.

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

شمارش اجزای قابل اتصال و بررسی قابلیت اتصال به موجودیت

کد مثال زیر نحوه شمارش اجزای قابل اتصال و بررسی اینکه آیا زمان اجرا از قابلیت اتصال به entity bound پشتیبانی می‌کند یا خیر را نشان می‌دهد.

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

مسائل

تاریخچه نسخه

  • ویرایش 1، 18-08-2025 (YuSheng Chang)

    • توضیحات اولیه افزونه
  • نسخه ۲، ۲۰۲۵-۱۲-۱۶ (کایل چن)