XR_ANDROID_spatial_entity_bound_anchor

Name String

XR_ANDROID_spatial_entity_bound_anchor

نوع الإضافة

إضافة مثيل

رقم الإضافة المسجَّل

791

المراجعة

2

حالة التصديق

لم تتم المصادقة عليها

الاعتماد على الإضافات والإصدارات

XR_EXT_spatial_anchor

تاريخ آخر تعديل

2025-08-18

حالة الملكية الفكرية

ما مِن مطالبات معروفة بشأن الملكية الفكرية.

المساهمون

يو شينغ تشانغ، Google
كايل تشن، Google
نيهاف جاين، Google
ليفانا تشن، Google
سبنسر كوين، 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.
  • يُرجى الاطّلاع على فصل مَعلمات حجم ذاكرة التخزين المؤقت للحصول على وصف تفصيلي لاسترداد حجم attachableComponents المطلوب.

يجب أن تعرض بيئات التشغيل دائمًا محتوى مخزن مؤقت متطابق من هذا التعداد لـ systemId المحدّد طوال مدة عمل المثيل.

الاستخدام الصالح (ضمني)

  • يجب تفعيل الإضافة XR_ANDROID_spatial_entity_bound_anchor قبل استدعاء xrEnumerateSpatialAnchorAttachableComponentsANDROID
  • يجب أن يكون instance مقبض XrInstance صالحًا
  • يجب أن يكون attachableComponentCountOutput مؤشرًا إلى قيمة uint32_t
  • إذا لم تكن قيمة attachableComponentCapacityInput هي 0، يجب أن تكون قيمة attachableComponents مؤشرًا إلى مصفوفة من قيم attachableComponentCapacityInput XrSpatialComponentTypeEXT

رموز الإرجاع

Success

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

الأوامر الجديدة

منشآت جديدة

ثوابت تعداد جديدة

  • 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 أغسطس 2025 (يو شينغ تشانغ)

    • وصف الإضافة الأوّلي
  • الإصدار 2، ‏16 ديسمبر 2025 (كايل تشن)