From df047b54cc36e22fb7aa22cfc877aa7d52051e61 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Wed, 2 Apr 2025 11:22:16 -0400 Subject: [PATCH] st/oxr: implement XR_EXT_plane_detection Co-Authored-By: Christoph Haag Part-of: --- CMakeLists.txt | 2 + doc/changes/xrt/mr.2439.md | 1 + scripts/generate_oxr_ext_support.py | 1 + .../include/xrt/xrt_config_build.h.cmake_in | 1 + src/xrt/state_trackers/oxr/oxr_api_funcs.h | 29 +- .../state_trackers/oxr/oxr_api_negotiate.c | 9 + src/xrt/state_trackers/oxr/oxr_api_session.c | 376 ++++++++++++++++++ src/xrt/state_trackers/oxr/oxr_api_verify.h | 2 + src/xrt/state_trackers/oxr/oxr_defines.h | 2 + .../oxr/oxr_extension_support.h | 12 + src/xrt/state_trackers/oxr/oxr_objects.h | 56 +++ src/xrt/state_trackers/oxr/oxr_system.c | 14 + 12 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 doc/changes/xrt/mr.2439.md diff --git a/CMakeLists.txt b/CMakeLists.txt index bacc19b0a..39b3aa890 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,7 @@ option(XRT_FEATURE_OPENXR_HAND_TRACKING_EXT "Enable XR_EXT_hand_tracking" ON) option(XRT_FEATURE_OPENXR_HEADLESS "Enable XR_MND_headless" ON) option(XRT_FEATURE_OPENXR_OVERLAY "Enable XR_EXTX_overlay" ON) option(XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS "Enable XR_EXT_performance_settings" OFF) +option(XRT_FEATURE_OPENXR_PLANE_DETECTION "Enable XR_EXT_plane_detection" OFF) option(XRT_FEATURE_OPENXR_VISIBILITY_MASK "Enable XR_KHR_visibility_mask" ON) option(XRT_FEATURE_OPENXR_VULKAN_SWAPCHAIN_FORMAT_LIST "Enable XR_KHR_vulkan_swapchain_format_list" ON) option(XRT_FEATURE_OPENXR_XDEV_SPACE "Enable XR_MNDX_xdev_space" ON) @@ -621,6 +622,7 @@ message(STATUS "# FEATURE_OPENXR_LAYER_FB_SETTINGS: ${XRT_FEATUR message(STATUS "# FEATURE_OPENXR_LAYER_FB_PASSTHROUGH: ${XRT_FEATURE_OPENXR_LAYER_FB_PASSTHROUGH}") message(STATUS "# FEATURE_OPENXR_OVERLAY: ${XRT_FEATURE_OPENXR_OVERLAY}") message(STATUS "# FEATURE_OPENXR_PERFORMANCE_SETTINGS: ${XRT_FEATURE_OPENXR_PERFORMANCE_SETTINGS}") +message(STATUS "# FEATURE_OPENXR_PLANE_DETECTION: ${XRT_FEATURE_OPENXR_PLANE_DETECTION}") message(STATUS "# FEATURE_OPENXR_SPACE_LOCAL_FLOOR: ${XRT_FEATURE_OPENXR_SPACE_LOCAL_FLOOR}") message(STATUS "# FEATURE_OPENXR_SPACE_UNBOUNDED: ${XRT_FEATURE_OPENXR_SPACE_UNBOUNDED}") message(STATUS "# FEATURE_OPENXR_VISIBILITY_MASK: ${XRT_FEATURE_OPENXR_VISIBILITY_MASK}") diff --git a/doc/changes/xrt/mr.2439.md b/doc/changes/xrt/mr.2439.md new file mode 100644 index 000000000..ea5d7fd8b --- /dev/null +++ b/doc/changes/xrt/mr.2439.md @@ -0,0 +1 @@ +Adds support for XR_EXT_plane_detection extension diff --git a/scripts/generate_oxr_ext_support.py b/scripts/generate_oxr_ext_support.py index 56e996e39..207f428ee 100755 --- a/scripts/generate_oxr_ext_support.py +++ b/scripts/generate_oxr_ext_support.py @@ -82,6 +82,7 @@ EXTENSIONS = ( ['XR_HTC_facial_tracking', 'XRT_FEATURE_OPENXR_FACIAL_TRACKING_HTC'], ['XR_META_touch_controller_plus', 'XRT_FEATURE_OPENXR_INTERACTION_TOUCH_PLUS'], ['XR_ML_ml2_controller_interaction', 'XRT_FEATURE_OPENXR_INTERACTION_ML2'], + ['XR_EXT_plane_detection', 'XRT_FEATURE_OPENXR_PLANE_DETECTION'], ['XR_MND_headless', 'XRT_FEATURE_OPENXR_HEADLESS'], ['XR_MND_swapchain_usage_input_attachment_bit'], ['XR_MSFT_hand_interaction', 'XRT_FEATURE_OPENXR_INTERACTION_MSFT_HAND'], diff --git a/src/xrt/include/xrt/xrt_config_build.h.cmake_in b/src/xrt/include/xrt/xrt_config_build.h.cmake_in index 9b1255e89..d6ab13242 100644 --- a/src/xrt/include/xrt/xrt_config_build.h.cmake_in +++ b/src/xrt/include/xrt/xrt_config_build.h.cmake_in @@ -66,6 +66,7 @@ #cmakedefine XRT_FEATURE_OPENXR_LAYER_FB_SETTINGS #cmakedefine XRT_FEATURE_OPENXR_LAYER_FB_PASSTHROUGH #cmakedefine XRT_FEATURE_OPENXR_OVERLAY +#cmakedefine XRT_FEATURE_OPENXR_PLANE_DETECTION #cmakedefine XRT_FEATURE_OPENXR_SPACE_LOCAL_FLOOR #cmakedefine XRT_FEATURE_OPENXR_SPACE_UNBOUNDED #cmakedefine XRT_FEATURE_OPENXR_VISIBILITY_MASK diff --git a/src/xrt/state_trackers/oxr/oxr_api_funcs.h b/src/xrt/state_trackers/oxr/oxr_api_funcs.h index ac7d617e0..ec7544de0 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_funcs.h +++ b/src/xrt/state_trackers/oxr/oxr_api_funcs.h @@ -571,7 +571,6 @@ oxr_xrGetDisplayRefreshRateFB(XrSession session, float *displayRefreshRate); XRAPI_ATTR XrResult XRAPI_CALL oxr_xrRequestDisplayRefreshRateFB(XrSession session, float displayRefreshRate); - //! OpenXR API function @ep{xrLocateSpacesKHR} XRAPI_ATTR XrResult XRAPI_CALL oxr_xrLocateSpacesKHR(XrSession session, const XrSpacesLocateInfoKHR *locateInfo, XrSpaceLocationsKHR *spaceLocations); @@ -580,6 +579,33 @@ oxr_xrLocateSpacesKHR(XrSession session, const XrSpacesLocateInfoKHR *locateInfo XRAPI_ATTR XrResult XRAPI_CALL oxr_xrLocateSpaces(XrSession session, const XrSpacesLocateInfo *locateInfo, XrSpaceLocations *spaceLocations); +#ifdef OXR_HAVE_EXT_plane_detection +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrCreatePlaneDetectorEXT(XrSession session, + const XrPlaneDetectorCreateInfoEXT *createInfo, + XrPlaneDetectorEXT *planeDetector); + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrDestroyPlaneDetectorEXT(XrPlaneDetectorEXT planeDetector); + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrBeginPlaneDetectionEXT(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorBeginInfoEXT *beginInfo); + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlaneDetectionStateEXT(XrPlaneDetectorEXT planeDetector, XrPlaneDetectionStateEXT *state); + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlaneDetectionsEXT(XrPlaneDetectorEXT planeDetector, + const XrPlaneDetectorGetInfoEXT *info, + XrPlaneDetectorLocationsEXT *locations); + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlanePolygonBufferEXT(XrPlaneDetectorEXT planeDetector, + uint64_t planeId, + uint32_t polygonBufferIndex, + XrPlaneDetectorPolygonBufferEXT *polygonBuffer); +#endif // OXR_HAVE_EXT_plane_detection + /* * * oxr_api_passthrough.c @@ -708,7 +734,6 @@ XRAPI_ATTR XrResult XRAPI_CALL oxr_xrCreateXDevSpaceMNDX(XrSession session, const XrCreateXDevSpaceInfoMNDX *createInfo, XrSpace *space); #endif - /*! * @} */ diff --git a/src/xrt/state_trackers/oxr/oxr_api_negotiate.c b/src/xrt/state_trackers/oxr/oxr_api_negotiate.c index dcfcb16b2..bfd5c8b8a 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_negotiate.c +++ b/src/xrt/state_trackers/oxr/oxr_api_negotiate.c @@ -275,6 +275,15 @@ handle_non_null(struct oxr_instance *inst, struct oxr_logger *log, const char *n ENTRY_IF_EXT(xrPassthroughStartFB, FB_passthrough); #endif // OXR_HAVE_FB_passthrough +#ifdef OXR_HAVE_EXT_plane_detection + ENTRY_IF_EXT(xrCreatePlaneDetectorEXT, EXT_plane_detection); + ENTRY_IF_EXT(xrDestroyPlaneDetectorEXT, EXT_plane_detection); + ENTRY_IF_EXT(xrBeginPlaneDetectionEXT, EXT_plane_detection); + ENTRY_IF_EXT(xrGetPlaneDetectionStateEXT, EXT_plane_detection); + ENTRY_IF_EXT(xrGetPlaneDetectionsEXT, EXT_plane_detection); + ENTRY_IF_EXT(xrGetPlanePolygonBufferEXT, EXT_plane_detection); +#endif + #ifdef OXR_HAVE_EXT_debug_utils ENTRY_IF_EXT(xrSetDebugUtilsObjectNameEXT, EXT_debug_utils); ENTRY_IF_EXT(xrCreateDebugUtilsMessengerEXT, EXT_debug_utils); diff --git a/src/xrt/state_trackers/oxr/oxr_api_session.c b/src/xrt/state_trackers/oxr/oxr_api_session.c index c84806bde..90e44a628 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_session.c +++ b/src/xrt/state_trackers/oxr/oxr_api_session.c @@ -12,6 +12,7 @@ #include #include +#include "math/m_space.h" #include "xrt/xrt_compiler.h" #include "util/u_debug.h" @@ -699,3 +700,378 @@ oxr_xrSetAndroidApplicationThreadKHR(XrSession session, XrAndroidThreadTypeKHR t } #endif + +#ifdef OXR_HAVE_EXT_plane_detection + +static XrResult +oxr_plane_detector_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) +{ + struct oxr_plane_detector_ext *pd = (struct oxr_plane_detector_ext *)hb; + + free(pd->xr_locations); + + if (pd->detection_id > 0) { + enum xrt_result xret = xrt_device_destroy_plane_detection_ext(pd->xdev, pd->detection_id); + if (xret != XRT_SUCCESS) { + return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, + "Internal error in xrDestroyPlaneDetectorEXT: %d", xret); + } + } + + xrt_plane_detections_ext_clear(&pd->detections); + + free(pd); + + return XR_SUCCESS; +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrCreatePlaneDetectorEXT(XrSession session, + const XrPlaneDetectorCreateInfoEXT *createInfo, + XrPlaneDetectorEXT *planeDetector) +{ + struct oxr_session *sess; + struct oxr_logger log; + OXR_VERIFY_SESSION_AND_INIT_LOG(&log, session, sess, "xrCreatePlaneDetectorEXT"); + OXR_VERIFY_ARG_TYPE_AND_NOT_NULL(&log, createInfo, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT); + OXR_VERIFY_EXTENSION(&log, sess->sys->inst, EXT_plane_detection); + + //! @todo support planes on other devices + struct xrt_device *xdev = GET_XDEV_BY_ROLE(sess->sys, head); + if (!xdev->planes_supported) { + return XR_ERROR_FEATURE_UNSUPPORTED; + } + + if (createInfo->flags != 0 && createInfo->flags != XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT) { + //! @todo: Disabled to allow Monado forks with internal extensions to have more values. + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Invalid plane detector creation flags: %lx", + createInfo->flags); + } + + struct oxr_plane_detector_ext *out_pd = NULL; + OXR_ALLOCATE_HANDLE_OR_RETURN(&log, out_pd, OXR_XR_DEBUG_PLANEDET, oxr_plane_detector_destroy_cb, + &sess->handle); + + out_pd->sess = sess; + if ((createInfo->flags & XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT) != 0) { + out_pd->flags |= XRT_PLANE_DETECTOR_FLAGS_CONTOUR_EXT; + } + + out_pd->xdev = xdev; + + // no plane detection started on creation + out_pd->state = XR_PLANE_DETECTION_STATE_NONE_EXT; + + out_pd->detection_id = 0; + + out_pd->xr_locations = NULL; + + *planeDetector = oxr_plane_detector_to_openxr(out_pd); + + return XR_SUCCESS; +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrDestroyPlaneDetectorEXT(XrPlaneDetectorEXT planeDetector) +{ + struct oxr_logger log; + struct oxr_plane_detector_ext *pd; + + OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(&log, planeDetector, pd, "xrDestroyPlaneDetectorEXT"); + + return oxr_handle_destroy(&log, &pd->handle); +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrBeginPlaneDetectionEXT(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorBeginInfoEXT *beginInfo) +{ + struct oxr_logger log; + struct oxr_plane_detector_ext *pd; + struct oxr_space *spc; + + OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(&log, planeDetector, pd, "xrBeginPlaneDetectionEXT"); + OXR_VERIFY_SPACE_NOT_NULL(&log, beginInfo->baseSpace, spc); + OXR_VERIFY_ARG_NOT_ZERO(&log, beginInfo->maxPlanes); + OXR_VERIFY_POSE(&log, beginInfo->boundingBoxPose); + if (beginInfo->time < 1) { + return oxr_error(&log, XR_ERROR_TIME_INVALID, "Time %" PRId64 " invalid", beginInfo->time); + } + + if (!pd->xdev->planes_supported) { + return XR_ERROR_FEATURE_UNSUPPORTED; + } + + if (beginInfo->orientationCount > 0) { + OXR_VERIFY_ARG_NOT_NULL(&log, beginInfo->orientations); + } + + + // BoundingBox, pose is relative to baseSpace spc + struct xrt_pose T_base_bb = { + .orientation = + { + .x = beginInfo->boundingBoxPose.orientation.x, + .y = beginInfo->boundingBoxPose.orientation.y, + .z = beginInfo->boundingBoxPose.orientation.z, + .w = beginInfo->boundingBoxPose.orientation.w, + }, + .position = + { + .x = beginInfo->boundingBoxPose.position.x, + .y = beginInfo->boundingBoxPose.position.y, + .z = beginInfo->boundingBoxPose.position.z, + }, + }; + + // Get plane tracker xdev relation in bounding box baseSpc too. The inverse of this relation is the transform + // from baseSpace spc to xdev space and can transform bounding box pose into xdev space. + struct xrt_space_relation T_base_xdev; // What we get, xdev in base space. + XrResult ret = oxr_space_locate_device(&log, pd->xdev, spc, beginInfo->time, &T_base_xdev); + if (ret != XR_SUCCESS) { + return ret; + } + + if (T_base_xdev.relation_flags == 0) { + return XR_ERROR_SPACE_NOT_LOCATABLE_EXT; + } + + struct xrt_space_relation T_xdev_bb; // This is what we want, BoundingBox in xdev space. + struct xrt_relation_chain xrc = {0}; + m_relation_chain_push_pose_if_not_identity(&xrc, &T_base_bb); // T_base_bb + m_relation_chain_push_inverted_relation(&xrc, &T_base_xdev); // T_xdev_base + m_relation_chain_resolve(&xrc, &T_xdev_bb); + + if (T_xdev_bb.relation_flags == 0) { + return XR_ERROR_SPACE_NOT_LOCATABLE_EXT; + } + + struct xrt_plane_detector_begin_info_ext query; + // the plane detector id is the raw handle (in our implementation: pointer to xrt_plane_detector) + query.detector_flags = pd->flags; + + //! @todo be more graceful + if (beginInfo->orientationCount > XRT_MAX_PLANE_ORIENTATIONS_EXT) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Too many plane orientations"); + } + + if (beginInfo->semanticTypeCount > XRT_MAX_PLANE_SEMANTIC_TYPE_EXT) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Too many plane semantic types"); + } + + query.orientation_count = beginInfo->orientationCount; + for (uint32_t i = 0; i < beginInfo->orientationCount; i++) { + // 1:1 mapped + query.orientations[i] = (enum xrt_plane_detector_orientation_ext)beginInfo->orientations[i]; + } + + query.semantic_type_count = beginInfo->semanticTypeCount; + for (uint32_t i = 0; i < beginInfo->semanticTypeCount; i++) { + // 1:1 mapped + query.semantic_types[i] = (enum xrt_plane_detector_semantic_type_ext)beginInfo->semanticTypes[i]; + } + + query.max_planes = beginInfo->maxPlanes; + query.min_area = beginInfo->minArea; + + // extents are invariant under pose transforms + query.bounding_box_extent.x = beginInfo->boundingBoxExtent.width; + query.bounding_box_extent.y = beginInfo->boundingBoxExtent.height; + query.bounding_box_extent.z = beginInfo->boundingBoxExtent.depth; + + query.bounding_box_pose = T_xdev_bb.pose; + + + enum xrt_result xret; + + // The xrt backend tracks plane detections to be able to clean up when the client dies. + // Because it tracks them as standalone objects, it can not know that this plane detection is replacing a + // previous one. Therefore we need to explicitly destroy the previous detection. + if (pd->detection_id > 0) { + xret = xrt_device_destroy_plane_detection_ext(pd->xdev, pd->detection_id); + if (xret != XRT_SUCCESS) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, + "Internal error in xrBeginPlaneDetectionEXT: Failed to destroy previous plane " + "detection: %d", + xret); + } + } + + xret = xrt_device_begin_plane_detection_ext(pd->xdev, &query, pd->detection_id, &pd->detection_id); + if (xret != XRT_SUCCESS) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Internal error in xrBeginPlaneDetectionEXT: %d", + xret); + } + + xrt_plane_detections_ext_clear(&pd->detections); + + // This makes sure a call to xrGetPlaneDetectionsEXT won't see a previous state, in particular a previous DONE. + pd->state = XR_PLANE_DETECTION_STATE_PENDING_EXT; + + return XR_SUCCESS; +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlaneDetectionStateEXT(XrPlaneDetectorEXT planeDetector, XrPlaneDetectionStateEXT *state) +{ + struct oxr_logger log; + struct oxr_plane_detector_ext *pd; + enum xrt_result xret; + + OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(&log, planeDetector, pd, "xrGetPlaneDetectionsEXT"); + + enum xrt_plane_detector_state_ext xstate = 0; + + xret = xrt_device_get_plane_detection_state_ext(pd->xdev, pd->detection_id, &xstate); + if (xret != XRT_SUCCESS) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Internal error in xrGetPlaneDetectionStateEXT: %d", + xret); + } + + *state = (XrPlaneDetectionStateEXT)xstate; // 1:1 mapped + + pd->state = *state; + + return XR_SUCCESS; +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlaneDetectionsEXT(XrPlaneDetectorEXT planeDetector, + const XrPlaneDetectorGetInfoEXT *info, + XrPlaneDetectorLocationsEXT *locations) +{ + struct oxr_logger log; + struct oxr_plane_detector_ext *pd; + struct oxr_space *spc; + enum xrt_result xret; + + OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(&log, planeDetector, pd, "xrGetPlaneDetectionsEXT"); + OXR_VERIFY_SPACE_NOT_NULL(&log, info->baseSpace, spc); + if (info->time < 1) { + return oxr_error(&log, XR_ERROR_TIME_INVALID, "Time %" PRId64 " invalid", info->time); + } + + if (!pd->xdev->planes_supported) { + return XR_ERROR_FEATURE_UNSUPPORTED; + } + + if (pd->state != XR_PLANE_DETECTION_STATE_DONE_EXT) { + locations->planeLocationCountOutput = 0; + return XR_ERROR_CALL_ORDER_INVALID; + } + + xret = xrt_device_get_plane_detections_ext(pd->xdev, pd->detection_id, &pd->detections); + if (xret != XRT_SUCCESS) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Internal error in xrGetPlaneDetectionsEXT: %d", xret); + } + + struct xrt_space_relation T_base_xdev; // What we get, xdev in base space. + XrResult ret = oxr_space_locate_device(&log, pd->xdev, spc, info->time, &T_base_xdev); + if (ret != XR_SUCCESS) { + return ret; + } + + if (T_base_xdev.relation_flags == 0) { + return XR_ERROR_SPACE_NOT_LOCATABLE_EXT; + } + + // create dynamic array for two call idiom + U_ARRAY_REALLOC_OR_FREE(pd->xr_locations, XrPlaneDetectorLocationEXT, pd->detections.location_count); + if (pd->detections.location_count == 0) { + pd->xr_locations = NULL; + } + + // populate pd->xr_locations from pd->detections.locations, also transform plane poses into baseSpace. + for (uint32_t i = 0; i < pd->detections.location_count; i++) { + pd->xr_locations[i].planeId = pd->detections.locations[i].planeId; + pd->xr_locations[i].extents.width = pd->detections.locations[i].extents.x; + pd->xr_locations[i].extents.height = pd->detections.locations[i].extents.y; + // 1:1 mapped + pd->xr_locations[i].orientation = + (XrPlaneDetectorOrientationEXT)pd->detections.locations[i].orientation; + pd->xr_locations[i].semanticType = + (XrPlaneDetectorSemanticTypeEXT)pd->detections.locations[i].semantic_type; + pd->xr_locations[i].polygonBufferCount = pd->detections.locations[i].polygon_buffer_count; + + + // The plane poses are returned in the xdev's space. + struct xrt_space_relation T_xdev_plane = pd->detections.locations[i].relation; + + // Get the plane pose in the base space. + struct xrt_space_relation T_base_plane; + struct xrt_relation_chain xrc = {0}; + m_relation_chain_push_relation(&xrc, &T_xdev_plane); + m_relation_chain_push_relation(&xrc, &T_base_xdev); + m_relation_chain_resolve(&xrc, &T_base_plane); + + OXR_XRT_POSE_TO_XRPOSEF(T_base_plane.pose, pd->xr_locations[i].pose); + + pd->xr_locations[i].locationFlags = 0; + if ((T_base_plane.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0) { + pd->xr_locations[i].locationFlags |= XR_SPACE_LOCATION_ORIENTATION_VALID_BIT; + } + if ((T_base_plane.relation_flags & XRT_SPACE_RELATION_POSITION_VALID_BIT) != 0) { + pd->xr_locations[i].locationFlags |= XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + if ((T_base_plane.relation_flags & XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT) != 0) { + pd->xr_locations[i].locationFlags |= XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT; + } + if ((T_base_plane.relation_flags & XRT_SPACE_RELATION_POSITION_TRACKED_BIT) != 0) { + pd->xr_locations[i].locationFlags |= XR_SPACE_LOCATION_POSITION_TRACKED_BIT; + } + } + + + OXR_TWO_CALL_HELPER(&log, locations->planeLocationCapacityInput, &locations->planeLocationCountOutput, + locations->planeLocations, pd->detections.location_count, pd->xr_locations, XR_SUCCESS); + + return XR_SUCCESS; +} + +XRAPI_ATTR XrResult XRAPI_CALL +oxr_xrGetPlanePolygonBufferEXT(XrPlaneDetectorEXT planeDetector, + uint64_t planeId, + uint32_t polygonBufferIndex, + XrPlaneDetectorPolygonBufferEXT *polygonBuffer) +{ + struct oxr_logger log; + struct oxr_plane_detector_ext *pd; + OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(&log, planeDetector, pd, "xrGetPlaneDetectionsEXT"); + + //! @todo can't reasonably retrieve a polygon without having retrieved plane data first. + if (pd->state != XR_PLANE_DETECTION_STATE_DONE_EXT) { + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, + "xrGetPlanePolygonBufferEXT called but plane detector state is %d", pd->state); + } + + // find the index of the plane in both pd->locations and pd->polygons_ptr arrays. + uint32_t plane_index = UINT32_MAX; + for (uint32_t i = 0; i < pd->detections.location_count; i++) { + if (pd->detections.locations[i].planeId == planeId) { + plane_index = i; + break; + } + } + + if (plane_index == UINT32_MAX) { + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Invalid plane id %" PRId64, planeId); + } + + if (polygonBufferIndex + 1 > pd->detections.locations[plane_index].polygon_buffer_count) { + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Invalid polygon buffer index %u (> %u)", + polygonBufferIndex, pd->detections.locations[plane_index].polygon_buffer_count - 1); + } + + uint32_t polygons_start_index = pd->detections.polygon_info_start_index[plane_index]; + uint32_t polygon_index = polygons_start_index + polygonBufferIndex; + + uint32_t vertices_start_index = pd->detections.polygon_infos[polygon_index].vertices_start_index; + // xrt_vec2 should mapped 1:1 to XrVector2f + XrVector2f *polygon_vertices = (XrVector2f *)&pd->detections.vertices[vertices_start_index]; + + uint32_t vertex_count = pd->detections.polygon_infos[polygon_index].vertex_count; + + OXR_TWO_CALL_HELPER(&log, polygonBuffer->vertexCapacityInput, &polygonBuffer->vertexCountOutput, + polygonBuffer->vertices, vertex_count, polygon_vertices, XR_SUCCESS); +} + +#endif // OXR_HAVE_EXT_plane_detection diff --git a/src/xrt/state_trackers/oxr/oxr_api_verify.h b/src/xrt/state_trackers/oxr/oxr_api_verify.h index c04ccf715..824d0ff19 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_verify.h +++ b/src/xrt/state_trackers/oxr/oxr_api_verify.h @@ -90,6 +90,8 @@ struct oxr_subaction_paths; OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_body_tracker_fb, BTRACKER, name, new_thing->sess->sys->inst) #define OXR_VERIFY_XDEVLIST_AND_INIT_LOG(log, thing, new_thing, name) \ OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_xdev_list, XDEVLIST, name, new_thing->sess->sys->inst) +#define OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(log, thing, new_thing, name) \ + OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_plane_detector_ext, PLANEDET, name, new_thing->sess->sys->inst) // clang-format on #define OXR_VERIFY_INSTANCE_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_instance, INSTANCE); diff --git a/src/xrt/state_trackers/oxr/oxr_defines.h b/src/xrt/state_trackers/oxr/oxr_defines.h index 59af19a42..65be4450b 100644 --- a/src/xrt/state_trackers/oxr/oxr_defines.h +++ b/src/xrt/state_trackers/oxr/oxr_defines.h @@ -32,6 +32,8 @@ // body tracker #define OXR_XR_DEBUG_BTRACKER (*(uint64_t *)"oxrbtra\0") #define OXR_XR_DEBUG_XDEVLIST (*(uint64_t *)"oxrxdli\0") +// plane detection +#define OXR_XR_DEBUG_PLANEDET (*(uint64_t *)"oxrplan\0") // clang-format on /*! diff --git a/src/xrt/state_trackers/oxr/oxr_extension_support.h b/src/xrt/state_trackers/oxr/oxr_extension_support.h index 538717eda..b870ea5f1 100644 --- a/src/xrt/state_trackers/oxr/oxr_extension_support.h +++ b/src/xrt/state_trackers/oxr/oxr_extension_support.h @@ -577,6 +577,17 @@ #endif +/* + * XR_EXT_plane_detection + */ +#if defined(XR_EXT_plane_detection) && defined(XRT_FEATURE_OPENXR_PLANE_DETECTION) +#define OXR_HAVE_EXT_plane_detection +#define OXR_EXTENSION_SUPPORT_EXT_plane_detection(_) _(EXT_plane_detection, EXT_PLANE_DETECTION) +#else +#define OXR_EXTENSION_SUPPORT_EXT_plane_detection(_) +#endif + + /* * XR_MND_headless */ @@ -820,6 +831,7 @@ OXR_EXTENSION_SUPPORT_HTC_facial_tracking(_) \ OXR_EXTENSION_SUPPORT_META_touch_controller_plus(_) \ OXR_EXTENSION_SUPPORT_ML_ml2_controller_interaction(_) \ + OXR_EXTENSION_SUPPORT_EXT_plane_detection(_) \ OXR_EXTENSION_SUPPORT_MND_headless(_) \ OXR_EXTENSION_SUPPORT_MND_swapchain_usage_input_attachment_bit(_) \ OXR_EXTENSION_SUPPORT_MSFT_hand_interaction(_) \ diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h index a32f9e3f9..ea545306e 100644 --- a/src/xrt/state_trackers/oxr/oxr_objects.h +++ b/src/xrt/state_trackers/oxr/oxr_objects.h @@ -130,6 +130,7 @@ struct oxr_facial_tracker_htc; struct oxr_face_tracker2_fb; struct oxr_body_tracker_fb; struct oxr_xdev_list; +struct oxr_plane_detector_ext; #define XRT_MAX_HANDLE_CHILDREN 256 #define OXR_MAX_BINDINGS_PER_ACTION 32 @@ -381,6 +382,19 @@ oxr_hand_tracker_to_openxr(struct oxr_hand_tracker *hand_tracker) return XRT_CAST_PTR_TO_OXR_HANDLE(XrHandTrackerEXT, hand_tracker); } +#ifdef OXR_HAVE_EXT_plane_detection +/*! + * To go back to a OpenXR object. + * + * @relates oxr_plane_detector + */ +static inline XrPlaneDetectorEXT +oxr_plane_detector_to_openxr(struct oxr_plane_detector_ext *plane_detector) +{ + return XRT_CAST_PTR_TO_OXR_HANDLE(XrPlaneDetectorEXT, plane_detector); +} +#endif // OXR_HAVE_EXT_plane_detection + /*! * To go back to a OpenXR object. * @@ -2839,6 +2853,48 @@ struct oxr_xdev_list }; #endif // OXR_HAVE_MNDX_xdev_space +#ifdef OXR_HAVE_EXT_plane_detection +/*! + * A Plane Detector. + * + * Parent type/handle is @ref oxr_session + * + * + * @obj{XrPlaneDetectorEXT} + * @extends oxr_handle_base + */ +struct oxr_plane_detector_ext +{ + //! Common structure for things referred to by OpenXR handles. + struct oxr_handle_base handle; + + //! Owner of this anchor. + struct oxr_session *sess; + + //! Plane detector flags. + enum xrt_plane_detector_flags_ext flags; + + //! The last known state of this plane detector. + XrPlaneDetectionStateEXT state; + + //! Whether the last DONE plane detection has been retrieved from the xdev. + bool retrieved; + + //! The device that this plane detector belongs to. + struct xrt_device *xdev; + + //! Detected planes. xrt_plane_detector_locations_ext::relation is kept in xdev space and not updated. + struct xrt_plane_detections_ext detections; + + //! Corresponds to xrt_plane_detections_ext::locations, but with OpenXR types and transformed into target space. + //! Enables two call idiom. + XrPlaneDetectorLocationEXT *xr_locations; + + //! A globally unique id for the current plane detection or 0, generated by the xrt_device. + uint64_t detection_id; +}; +#endif // OXR_HAVE_EXT_plane_detection + /*! * @} */ diff --git a/src/xrt/state_trackers/oxr/oxr_system.c b/src/xrt/state_trackers/oxr/oxr_system.c index e7248a522..072a532a6 100644 --- a/src/xrt/state_trackers/oxr/oxr_system.c +++ b/src/xrt/state_trackers/oxr/oxr_system.c @@ -508,6 +508,20 @@ oxr_system_get_properties(struct oxr_logger *log, struct oxr_system *sys, XrSyst } #endif // OXR_HAVE_MNDX_xdev_space +#ifdef OXR_HAVE_EXT_plane_detection + XrSystemPlaneDetectionPropertiesEXT *plane_detection_props = NULL; + if (sys->inst->extensions.EXT_plane_detection) { + plane_detection_props = OXR_GET_OUTPUT_FROM_CHAIN( + properties, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, XrSystemPlaneDetectionPropertiesEXT); + } + + if (plane_detection_props) { + // for now these are mapped 1:1 + plane_detection_props->supportedFeatures = + (XrPlaneDetectionCapabilityFlagsEXT)xdev->plane_capability_flags; + } +#endif // OXR_HAVE_EXT_plane_detection + return XR_SUCCESS; }