XR_ANDROID_spatial_entity_bound_anchor

سلسلة الاسم

XR_ANDROID_spatial_entity_bound_anchor

نوع الإضافة

إضافة مثيل

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

791

المراجعة

2

حالة التصديق

لم تتم المصادقة

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

XR_EXT_spatial_anchor

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

2025-08-18

حالة عنوان IP

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

المساهمون

"يوشينغ تشانغ"، 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

رموز الإرجاع

تم بنجاح

  • 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، 2018-08-18 (YuSheng Chang)

    • الوصف الأوّلي للإضافة
  • المراجعة 2، 16-12-2025 (Kyle Chen)