XR_ANDROID_spatial_entity_bound_anchor

이름 문자열

XR_ANDROID_spatial_entity_bound_anchor

확장 프로그램 유형

인스턴스 확장 프로그램

등록된 확장 프로그램 번호

791

버전

2

비준 상태

비준되지 않음

확장 프로그램 및 버전 종속 항목

XR_EXT_spatial_anchor

최종 수정일

2025-08-18

IP 상태

알려진 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);

매개변수 설명

  • instanceXrInstance 의 핸들입니다 .
  • systemId는 공간 지속성 저장소가 열거될 XrSystemId입니다.
  • attachableComponentCapacityInputattachableComponents 배열의 용량이거나 필요한 용량을 가져오기 위한 요청을 나타내는 0입니다.
  • attachableComponentCountOutput은 연결 가능한 구성요소의 수 또는 attachableComponentCapacityInput이 부족한 경우 필요한 용량입니다.
  • attachableComponentsXrSpatialComponentTypeEXT 배열입니다 . attachableComponentCapacityInput이 0인 경우 NULL일 수 있습니다.
  • 필요한 attachableComponents 크기를 가져오는 방법에 관한 자세한 설명은 버퍼 크기 매개변수 장을 참고하세요.

런타임은 인스턴스의 수명 동안 지정된 systemId에 대해 이 열거에서 항상 동일한 버퍼 콘텐츠를 반환해야 합니다.

유효한 사용 (암시적)

  • XR_ANDROID_spatial_entity_bound_anchor 확장 프로그램을 xrEnumerateSpatialAnchorAttachableComponentsANDROID를 호출하기 전에 사용 설정해야 합니다.
  • instance 는 유효한 XrInstance 핸들이어야 합니다
  • attachableComponentCountOutput uint32_t 값의 포인터여야 합니다.
  • attachableComponentCapacityInput0이 아닌 경우 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 함수를 사용하여 앵커를 만듭니다. 애플리케이션에서 공간 항목에 연결된 항목 결합 앵커를 만들려면 xrCreateSpatialAnchorEXT 함수를 호출할 때 XrSpatialAnchorParentANDROID 구조를 XrSpatialAnchorCreateInfoEXT 구조의 다음 포인터에 연결할 수 있습니다.

XrSpatialAnchorParentANDROID 구조는 다음과 같이 정의됩니다.

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

구성원 설명

  • type은 이 구조의 XrStructureType입니다.
  • nextNULL이거나 구조 체인의 다음 구조를 가리키는 포인터입니다.
  • parentId는 앵커가 연결될 항목의 XrSpatialEntityIdEXT입니다.

런타임은 상위 항목과 앵커 간의 거리가 항상 일관되도록 보장해야 합니다. 여기서 거리는 표면의 법선을 따라 상위 항목의 가장 가까운 표면에 대한 앵커의 포즈입니다. 앵커의 포즈는 상위 항목에 연결 가능한 구성요소가 몇 개인지에 관계없이 상위 항목의 위치와 상위 항목의 표면까지의 거리를 기반으로 업데이트됩니다.

XrSpatialAnchorParentANDROID ::parentId가 xrCreateSpatialAnchorEXT :: spatialContext의 유효한 ID가 아닌 경우 런타임은 xrCreateSpatialAnchorEXT에서 XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT를 반환해야 합니다 .

xrEnumerateSpatialAnchorAttachableComponentsANDROID로 열거된 구성요소가 상위 항목에 없는 경우 런타임은 xrCreateSpatialAnchorEXT에서 XR_ERROR_SPATIAL_ANCHOR_ATTACHABLE_COMPONENT_NOT_FOUND_ANDROID를 반환해야 합니다.

유효한 사용 (암시적)

예시 코드

공간 항목 결합 앵커 만들기

다음 예시 코드는 항목 결합 앵커를 만들고 이를 공간 평면 추적 항목에 연결하는 방법을 보여줍니다.

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

항목 결합 앵커 포즈 및 상위 항목 ID 가져오기

다음 예시 코드는 항목 결합 앵커의 포즈와 상위 항목의 ID를 가져오는 방법을 보여줍니다.

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, 2025-08-18 (YuSheng Chang)

    • 초기 확장 프로그램 설명입니다.
  • 버전 2, 2025-12-16 (Kyle Chen)