From d3d4be1db3a09084eb2ff329bb7a530a87481480 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 5 Aug 2021 13:14:26 +0900 Subject: XR: Action Binding Improvements Provides several important improvements to the runtime action bindings operation and internal API. Moves input-specific action data (input thresholds, input regions, pose offsets/spaces) from actions to more granular action bindings. This allows a single action to be mapped to a variety of inputs, without having to share a single input threshold, region, or space. Also removes the need for action space creation API, as spaces for pose actions will be automatically created with the bindings. The correct action data for the current inputs is set by calling xrGetCurrentInteractionProfile() to get the current profile and then retrieving the corresponding mapped data. Does not bring about any changes for users since only internal runtime functionality is currently affected. Reviewed By: Julian Eisel Differential Revision: http://developer.blender.org/D12077 --- intern/ghost/GHOST_C-api.h | 19 +- intern/ghost/GHOST_Types.h | 24 +- intern/ghost/intern/GHOST_C-api.cpp | 29 +- intern/ghost/intern/GHOST_XrAction.cpp | 188 ++++---- intern/ghost/intern/GHOST_XrAction.h | 47 +- intern/ghost/intern/GHOST_XrSession.cpp | 87 +--- intern/ghost/intern/GHOST_XrSession.h | 9 +- source/blender/makesdna/DNA_xr_types.h | 9 + source/blender/windowmanager/CMakeLists.txt | 2 +- source/blender/windowmanager/WM_api.h | 26 +- .../blender/windowmanager/xr/intern/wm_xr_action.c | 454 +++++++++++++++++++ .../windowmanager/xr/intern/wm_xr_actions.c | 480 --------------------- .../blender/windowmanager/xr/intern/wm_xr_intern.h | 5 +- 13 files changed, 654 insertions(+), 725 deletions(-) create mode 100644 source/blender/windowmanager/xr/intern/wm_xr_action.c delete mode 100644 source/blender/windowmanager/xr/intern/wm_xr_actions.c diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 46e3888a367..fea5a545807 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1066,22 +1066,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context, uint32_t count, const char *const *action_names); -/** - * Create spaces for pose-based OpenXR actions. - */ -int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - -/** - * Destroy previously created spaces for OpenXR actions. - */ -void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - /** * Create input/output path bindings for OpenXR actions. */ @@ -1096,7 +1080,8 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context, void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context, const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos); + const char *const *action_names, + const char *const *profile_paths); /** * Attach all created action sets to the current OpenXR session. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 94a3fd86b73..fb19b9535ad 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -719,29 +719,27 @@ typedef struct GHOST_XrActionInfo { const char **subaction_paths; /** States for each subaction path. */ void *states; + /** Input thresholds/regions for each subaction path. */ + float *float_thresholds; + int16_t *axis_flags; GHOST_XrCustomdataFreeFn customdata_free_fn; void *customdata; /* wmXrAction */ } GHOST_XrActionInfo; -typedef struct GHOST_XrActionSpaceInfo { - const char *action_name; - uint32_t count_subaction_paths; - const char **subaction_paths; - /** Poses for each subaction path. */ - const GHOST_XrPose *poses; -} GHOST_XrActionSpaceInfo; - typedef struct GHOST_XrActionBindingInfo { - const char *action_name; - uint32_t count_interaction_paths; - /** Interaction path: User (sub-action) path + component path. */ - const char **interaction_paths; + const char *component_path; + float float_threshold; + int16_t axis_flag; + GHOST_XrPose pose; } GHOST_XrActionBindingInfo; typedef struct GHOST_XrActionProfileInfo { + const char *action_name; const char *profile_path; - uint32_t count_bindings; + uint32_t count_subaction_paths; + const char **subaction_paths; + /* Bindings for each subaction path. */ const GHOST_XrActionBindingInfo *bindings; } GHOST_XrActionProfileInfo; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index cb409595e50..0bc9be26eb1 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -961,28 +961,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle, GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context); } -int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) -{ - GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; - GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos), - xr_context); - return 0; -} - -void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) -{ - GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; - GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context); -} - int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, uint32_t count, @@ -998,11 +976,14 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle, void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos) + const char *const *action_names, + const char *const *profile_paths) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context); + GHOST_XR_CAPI_CALL( + xr_session->destroyActionBindings(action_set_name, count, action_names, profile_paths), + xr_context); } int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle) diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 9c4f7fbc7d8..676a3367ee1 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -33,24 +33,22 @@ * * \{ */ -GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance, - XrSession session, +GHOST_XrActionSpace::GHOST_XrActionSpace(XrSession session, XrAction action, - const GHOST_XrActionSpaceInfo &info, - uint32_t subaction_idx) + const char *action_name, + const char *profile_path, + XrPath subaction_path, + const char *subaction_path_str, + const GHOST_XrPose &pose) { - const char *subaction_path = info.subaction_paths[subaction_idx]; - CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path), - (std::string("Failed to get user path \"") + subaction_path + "\".").data()); - XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO}; action_space_info.action = action; - action_space_info.subactionPath = m_subaction_path; - copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace); + action_space_info.subactionPath = subaction_path; + copy_ghost_pose_to_openxr_pose(pose, action_space_info.poseInActionSpace); CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space), - (std::string("Failed to create space \"") + subaction_path + "\" for action \"" + - info.action_name + "\".") + (std::string("Failed to create space \"") + subaction_path_str + "\" for action \"" + + action_name + "\" and profile \"" + profile_path + "\".") .data()); } @@ -66,11 +64,6 @@ XrSpace GHOST_XrActionSpace::getSpace() const return m_space; } -const XrPath &GHOST_XrActionSpace::getSubactionPath() const -{ - return m_subaction_path; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -79,13 +72,19 @@ const XrPath &GHOST_XrActionSpace::getSubactionPath() const * \{ */ GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance, + XrSession session, XrAction action, - const char *profile_path, - const GHOST_XrActionBindingInfo &info) + GHOST_XrActionType type, + const GHOST_XrActionProfileInfo &info) { - CHECK_XR( - xrStringToPath(instance, profile_path, &m_profile), - (std::string("Failed to get interaction profile path \"") + profile_path + "\".").data()); + CHECK_XR(xrStringToPath(instance, info.profile_path, &m_profile), + (std::string("Failed to get interaction profile path \"") + info.profile_path + "\".") + .data()); + + const bool is_float_action = (type == GHOST_kXrActionTypeFloatInput || + type == GHOST_kXrActionTypeVector2fInput); + const bool is_button_action = (is_float_action || type == GHOST_kXrActionTypeBooleanInput); + const bool is_pose_action = (type == GHOST_kXrActionTypePoseInput); /* Create bindings. */ XrInteractionProfileSuggestedBinding bindings_info{ @@ -93,31 +92,80 @@ GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance, bindings_info.interactionProfile = m_profile; bindings_info.countSuggestedBindings = 1; - for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths; - ++interaction_idx) { - const char *interaction_path = info.interaction_paths[interaction_idx]; + for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { + const char *subaction_path_str = info.subaction_paths[subaction_idx]; + const GHOST_XrActionBindingInfo &binding_info = info.bindings[subaction_idx]; + + const std::string interaction_path = std::string(subaction_path_str) + + binding_info.component_path; if (m_bindings.find(interaction_path) != m_bindings.end()) { continue; } XrActionSuggestedBinding sbinding; sbinding.action = action; - CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding), + CHECK_XR(xrStringToPath(instance, interaction_path.data(), &sbinding.binding), (std::string("Failed to get interaction path \"") + interaction_path + "\".").data()); bindings_info.suggestedBindings = &sbinding; /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it * greatly improves error checking to suggest them here first. */ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info), - (std::string("Failed to create binding for profile \"") + profile_path + - "\" and action \"" + info.action_name + - "\". Are the profile and action paths correct?") + (std::string("Failed to create binding for action \"") + info.action_name + + "\" and profile \"" + info.profile_path + + "\". Are the action and profile paths correct?") .data()); m_bindings.insert({interaction_path, sbinding.binding}); + + if (m_subaction_data.find(subaction_path_str) == m_subaction_data.end()) { + std::map::iterator it = + m_subaction_data + .emplace( + std::piecewise_construct, std::make_tuple(subaction_path_str), std::make_tuple()) + .first; + GHOST_XrSubactionData &subaction = it->second; + + CHECK_XR(xrStringToPath(instance, subaction_path_str, &subaction.subaction_path), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); + + if (is_float_action || is_button_action) { + if (is_float_action) { + subaction.float_threshold = binding_info.float_threshold; + } + if (is_button_action) { + subaction.axis_flag = binding_info.axis_flag; + } + } + else if (is_pose_action) { + /* Create action space for pose bindings. */ + subaction.space = std::make_unique(session, + action, + info.action_name, + info.profile_path, + subaction.subaction_path, + subaction_path_str, + binding_info.pose); + } + } } } +XrPath GHOST_XrActionProfile::getProfile() const +{ + return m_profile; +} + +const GHOST_XrSubactionData *GHOST_XrActionProfile::getSubaction(XrPath subaction_path) const +{ + for (auto &[subaction_path_str, subaction] : m_subaction_data) { + if (subaction.subaction_path == subaction_path) { + return &subaction; + } + } + return nullptr; +} + void GHOST_XrActionProfile::getBindings( XrAction action, std::map> &r_bindings) const { @@ -152,6 +200,8 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance, const GHOST_XrActionInfo &info) : m_type(info.type), m_states(info.states), + m_float_thresholds(info.float_thresholds), + m_axis_flags(info.axis_flags), m_custom_data_( std::make_unique(info.customdata, info.customdata_free_fn)) { @@ -201,52 +251,25 @@ GHOST_XrAction::~GHOST_XrAction() } } -bool GHOST_XrAction::createSpace(XrInstance instance, - XrSession session, - const GHOST_XrActionSpaceInfo &info) -{ - uint32_t subaction_idx = 0; - for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) { - return false; - } - } - - for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - m_spaces.emplace(std::piecewise_construct, - std::make_tuple(info.subaction_paths[subaction_idx]), - std::make_tuple(instance, session, m_action, info, subaction_idx)); - } - - return true; -} - -void GHOST_XrAction::destroySpace(const char *subaction_path) -{ - if (m_spaces.find(subaction_path) != m_spaces.end()) { - m_spaces.erase(subaction_path); - } -} - bool GHOST_XrAction::createBinding(XrInstance instance, - const char *profile_path, - const GHOST_XrActionBindingInfo &info) + XrSession session, + const GHOST_XrActionProfileInfo &info) { - if (m_profiles.find(profile_path) != m_profiles.end()) { + if (m_profiles.find(info.profile_path) != m_profiles.end()) { return false; } m_profiles.emplace(std::piecewise_construct, - std::make_tuple(profile_path), - std::make_tuple(instance, m_action, profile_path, info)); + std::make_tuple(info.profile_path), + std::make_tuple(instance, session, m_action, m_type, info)); return true; } -void GHOST_XrAction::destroyBinding(const char *interaction_profile_path) +void GHOST_XrAction::destroyBinding(const char *profile_path) { - if (m_profiles.find(interaction_profile_path) != m_profiles.end()) { - m_profiles.erase(interaction_profile_path); + if (m_profiles.find(profile_path) != m_profiles.end()) { + m_profiles.erase(profile_path); } } @@ -255,6 +278,10 @@ void GHOST_XrAction::updateState(XrSession session, XrSpace reference_space, const XrTime &predicted_display_time) { + const bool is_float_action = (m_type == GHOST_kXrActionTypeFloatInput || + m_type == GHOST_kXrActionTypeVector2fInput); + const bool is_button_action = (is_float_action || m_type == GHOST_kXrActionTypeBooleanInput); + XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO}; state_info.action = m_action; @@ -262,6 +289,28 @@ void GHOST_XrAction::updateState(XrSession session, for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) { state_info.subactionPath = m_subaction_paths[subaction_idx]; + /* Set subaction data based on current interaction profile. */ + XrInteractionProfileState profile_state{XR_TYPE_INTERACTION_PROFILE_STATE}; + CHECK_XR(xrGetCurrentInteractionProfile(session, state_info.subactionPath, &profile_state), + "Failed to get current interaction profile."); + + const GHOST_XrSubactionData *subaction = nullptr; + for (auto &[profile_path, profile] : m_profiles) { + if (profile.getProfile() == profile_state.interactionProfile) { + subaction = profile.getSubaction(state_info.subactionPath); + break; + } + } + + if (subaction != nullptr) { + if (is_float_action) { + m_float_thresholds[subaction_idx] = subaction->float_threshold; + } + if (is_button_action) { + m_axis_flags[subaction_idx] = subaction->axis_flag; + } + } + switch (m_type) { case GHOST_kXrActionTypeBooleanInput: { XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; @@ -299,14 +348,9 @@ void GHOST_XrAction::updateState(XrSession session, xrGetActionStatePose(session, &state_info, &state), (std::string("Failed to get state for pose action \"") + action_name + "\".").data()); if (state.isActive) { - XrSpace pose_space = XR_NULL_HANDLE; - for (auto &[path, space] : m_spaces) { - if (space.getSubactionPath() == state_info.subactionPath) { - pose_space = space.getSpace(); - break; - } - } - + XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ? + subaction->space->getSpace() : + XR_NULL_HANDLE; if (pose_space != XR_NULL_HANDLE) { XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION}; CHECK_XR( diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index 32445c616bd..e2a89e87278 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -34,38 +34,53 @@ class GHOST_XrActionSpace { public: GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */ - GHOST_XrActionSpace(XrInstance instance, - XrSession session, + GHOST_XrActionSpace(XrSession session, XrAction action, - const GHOST_XrActionSpaceInfo &info, - uint32_t subaction_idx); + const char *action_name, + const char *profile_path, + XrPath subaction_path, + const char *subaction_path_str, + const GHOST_XrPose &pose); ~GHOST_XrActionSpace(); XrSpace getSpace() const; - const XrPath &getSubactionPath() const; private: XrSpace m_space = XR_NULL_HANDLE; - XrPath m_subaction_path = XR_NULL_PATH; }; /* -------------------------------------------------------------------- */ +typedef struct GHOST_XrSubactionData { + XrPath subaction_path = XR_NULL_PATH; + float float_threshold; + int16_t axis_flag; + std::unique_ptr space = nullptr; +} GHOST_XrSubactionData; + +/* -------------------------------------------------------------------- */ + class GHOST_XrActionProfile { public: GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */ GHOST_XrActionProfile(XrInstance instance, + XrSession session, XrAction action, - const char *profile_path, - const GHOST_XrActionBindingInfo &info); + GHOST_XrActionType type, + const GHOST_XrActionProfileInfo &info); ~GHOST_XrActionProfile() = default; + XrPath getProfile() const; + const GHOST_XrSubactionData *getSubaction(XrPath subaction_path) const; void getBindings(XrAction action, std::map> &r_bindings) const; private: XrPath m_profile = XR_NULL_PATH; - /* Bindings identified by interaction (user (subaction) + component) path. */ + + /** Subaction data identified by user (subaction) path. */ + std::map m_subaction_data; + /** Bindings identified by interaction (user (subaction) + component) path. */ std::map m_bindings; }; @@ -77,12 +92,9 @@ class GHOST_XrAction { GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info); ~GHOST_XrAction(); - bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info); - void destroySpace(const char *subaction_path); - bool createBinding(XrInstance instance, - const char *profile_path, - const GHOST_XrActionBindingInfo &info); + XrSession session, + const GHOST_XrActionProfileInfo &info); void destroyBinding(const char *profile_path); void updateState(XrSession session, @@ -105,12 +117,13 @@ class GHOST_XrAction { std::vector m_subaction_paths; /** States for each subaction path. */ void *m_states; + /** Input thresholds/regions for each subaction path. */ + float *m_float_thresholds; + int16_t *m_axis_flags; std::unique_ptr m_custom_data_ = nullptr; /* wmXrAction */ - /* Spaces identified by user (subaction) path. */ - std::map m_spaces; - /* Profiles identified by interaction profile path. */ + /** Profiles identified by interaction profile path. */ std::map m_profiles; }; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 919d11d22a9..a29ec1cc560 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -610,9 +610,9 @@ void GHOST_XrSession::destroyActions(const char *action_set_name, } } -bool GHOST_XrSession::createActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) +bool GHOST_XrSession::createActionBindings(const char *action_set_name, + uint32_t count, + const GHOST_XrActionProfileInfo *infos) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { @@ -622,98 +622,37 @@ bool GHOST_XrSession::createActionSpaces(const char *action_set_name, XrInstance instance = m_context->getInstance(); XrSession session = m_oxr->session; - for (uint32_t action_idx = 0; action_idx < count; ++action_idx) { - const GHOST_XrActionSpaceInfo &info = infos[action_idx]; + for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { + const GHOST_XrActionProfileInfo &info = infos[profile_idx]; GHOST_XrAction *action = action_set->findAction(info.action_name); if (action == nullptr) { continue; } - if (!action->createSpace(instance, session, info)) { - return false; - } + action->createBinding(instance, session, info); } return true; } -void GHOST_XrSession::destroyActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) +void GHOST_XrSession::destroyActionBindings(const char *action_set_name, + uint32_t count, + const char *const *action_names, + const char *const *profile_paths) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { return; } - for (uint32_t action_idx = 0; action_idx < count; ++action_idx) { - const GHOST_XrActionSpaceInfo &info = infos[action_idx]; - - GHOST_XrAction *action = action_set->findAction(info.action_name); + for (uint32_t i = 0; i < count; ++i) { + GHOST_XrAction *action = action_set->findAction(action_names[i]); if (action == nullptr) { continue; } - for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - action->destroySpace(info.subaction_paths[subaction_idx]); - } - } -} - -bool GHOST_XrSession::createActionBindings(const char *action_set_name, - uint32_t count, - const GHOST_XrActionProfileInfo *infos) -{ - GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); - if (action_set == nullptr) { - return false; - } - - XrInstance instance = m_context->getInstance(); - - for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { - const GHOST_XrActionProfileInfo &info = infos[profile_idx]; - const char *profile_path = info.profile_path; - - for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) { - const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx]; - - GHOST_XrAction *action = action_set->findAction(binding.action_name); - if (action == nullptr) { - continue; - } - - action->createBinding(instance, profile_path, binding); - } - } - - return true; -} - -void GHOST_XrSession::destroyActionBindings(const char *action_set_name, - uint32_t count, - const GHOST_XrActionProfileInfo *infos) -{ - GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); - if (action_set == nullptr) { - return; - } - - for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { - const GHOST_XrActionProfileInfo &info = infos[profile_idx]; - const char *profile_path = info.profile_path; - - for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) { - const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx]; - - GHOST_XrAction *action = action_set->findAction(binding.action_name); - if (action == nullptr) { - continue; - } - - action->destroyBinding(profile_path); - } + action->destroyBinding(profile_paths[i]); } } diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index d448585d14c..cdeef153fb1 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -60,18 +60,13 @@ class GHOST_XrSession { void destroyActions(const char *action_set_name, uint32_t count, const char *const *action_names); - bool createActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - void destroyActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); bool createActionBindings(const char *action_set_name, uint32_t count, const GHOST_XrActionProfileInfo *infos); void destroyActionBindings(const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos); + const char *const *action_names, + const char *const *profile_paths); bool attachActionSets(); /** diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index fc00d5eb839..c0928e1519f 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -74,6 +74,15 @@ typedef enum eXrOpFlag { XR_OP_MODAL = 2, } eXrOpFlag; +typedef enum eXrAxisFlag { + /** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution + (mutually exclusive per axis). */ + XR_AXIS0_POS = (1 << 0), + XR_AXIS0_NEG = (1 << 1), + XR_AXIS1_POS = (1 << 2), + XR_AXIS1_NEG = (1 << 3), +} eXrAxisFlag; + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index e513c49c11b..8b0ceb02b5f 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -203,7 +203,7 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c - xr/intern/wm_xr_actions.c + xr/intern/wm_xr_action.c xr/intern/wm_xr_draw.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 136c639caea..fb973592a57 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -978,34 +978,24 @@ bool WM_xr_action_create(wmXrData *xr, eXrActionType type, unsigned int count_subaction_paths, const char **subaction_paths, - const float *float_threshold, struct wmOperatorType *ot, struct IDProperty *op_properties, eXrOpFlag op_flag); void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const struct wmXrPose *poses); -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths); bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses); void WM_xr_action_binding_destroy(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path); /* If action_set_name is NULL, then all action sets will be treated as active. */ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c new file mode 100644 index 00000000000..ee4cfcccaa7 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -0,0 +1,454 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Actions + * + * Uses the Ghost-XR API to manage OpenXR actions. + * All functions are designed to be usable by RNA / the Python API. + */ + +#include "BLI_math.h" + +#include "GHOST_C-api.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name XR-Action API + * + * API functions for managing OpenXR actions. + * + * \{ */ + +static wmXrActionSet *action_set_create(const char *action_set_name) +{ + wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__); + action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name"); + strcpy(action_set->name, action_set_name); + + return action_set; +} + +static void action_set_destroy(void *val) +{ + wmXrActionSet *action_set = val; + + MEM_SAFE_FREE(action_set->name); + + MEM_freeN(action_set); +} + +static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) +{ + return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name); +} + +static wmXrAction *action_create(const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + wmXrAction *action = MEM_callocN(sizeof(*action), __func__); + action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); + strcpy(action->name, action_name); + action->type = type; + + const unsigned int count = count_subaction_paths; + action->count_subaction_paths = count; + + action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, + "XrAction_SubactionPaths"); + for (unsigned int i = 0; i < count; ++i) { + action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, + "XrAction_SubactionPath"); + strcpy(action->subaction_paths[i], subaction_paths[i]); + } + + size_t size; + switch (type) { + case XR_BOOLEAN_INPUT: + size = sizeof(bool); + break; + case XR_FLOAT_INPUT: + size = sizeof(float); + break; + case XR_VECTOR2F_INPUT: + size = sizeof(float) * 2; + break; + case XR_POSE_INPUT: + size = sizeof(GHOST_XrPose); + break; + case XR_VIBRATION_OUTPUT: + return action; + } + action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); + action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); + + const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); + if (is_float_action) { + action->float_thresholds = MEM_calloc_arrayN( + count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"); + } + if (is_button_action) { + action->axis_flags = MEM_calloc_arrayN( + count, sizeof(*action->axis_flags), "XrAction_AxisFlags"); + } + + action->ot = ot; + action->op_properties = op_properties; + action->op_flag = op_flag; + + return action; +} + +static void action_destroy(void *val) +{ + wmXrAction *action = val; + + MEM_SAFE_FREE(action->name); + + const unsigned int count = action->count_subaction_paths; + char **subaction_paths = action->subaction_paths; + if (subaction_paths) { + for (unsigned int i = 0; i < count; ++i) { + MEM_SAFE_FREE(subaction_paths[i]); + } + MEM_freeN(subaction_paths); + } + + MEM_SAFE_FREE(action->states); + MEM_SAFE_FREE(action->states_prev); + + MEM_SAFE_FREE(action->float_thresholds); + MEM_SAFE_FREE(action->axis_flags); + + MEM_freeN(action); +} + +static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name); +} + +bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name) +{ + if (action_set_find(xr, action_set_name)) { + return false; + } + + wmXrActionSet *action_set = action_set_create(action_set_name); + + GHOST_XrActionSetInfo info = { + .name = action_set_name, + .customdata_free_fn = action_set_destroy, + .customdata = action_set, + }; + + if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + wmXrSessionState *session_state = &xr->runtime->session_state; + + if (action_set == session_state->active_action_set) { + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_clear(session_state); + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action) { + action_set->active_modal_action = NULL; + } + session_state->active_action_set = NULL; + } + + GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name); +} + +bool WM_xr_action_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + if (action_find(xr, action_set_name, action_name)) { + return false; + } + + wmXrAction *action = action_create(action_name, + type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + op_flag); + + GHOST_XrActionInfo info = { + .name = action_name, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .states = action->states, + .float_thresholds = action->float_thresholds, + .axis_flags = (int16_t *)action->axis_flags, + .customdata_free_fn = action_destroy, + .customdata = action, + }; + + switch (type) { + case XR_BOOLEAN_INPUT: + info.type = GHOST_kXrActionTypeBooleanInput; + break; + case XR_FLOAT_INPUT: + info.type = GHOST_kXrActionTypeFloatInput; + break; + case XR_VECTOR2F_INPUT: + info.type = GHOST_kXrActionTypeVector2fInput; + break; + case XR_POSE_INPUT: + info.type = GHOST_kXrActionTypePoseInput; + break; + case XR_VIBRATION_OUTPUT: + info.type = GHOST_kXrActionTypeVibrationOutput; + break; + } + + if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return; + } + + if (action_set->controller_pose_action && + STREQ(action_set->controller_pose_action->name, action_name)) { + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); + } + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action && + STREQ(action_set->active_modal_action->name, action_name)) { + action_set->active_modal_action = NULL; + } + + GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); +} + + +bool WM_xr_action_binding_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses) +{ + GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( + count_subaction_paths, sizeof(*binding_infos), __func__); + + for (unsigned int i = 0; i < count_subaction_paths; ++i) { + GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; + binding_info->component_path = component_paths[i]; + if (float_thresholds) { + binding_info->float_threshold = float_thresholds[i]; + } + if (axis_flags) { + binding_info->axis_flag = axis_flags[i]; + } + if (poses) { + copy_v3_v3(binding_info->pose.position, poses[i].position); + copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat); + } + } + + GHOST_XrActionProfileInfo profile_info = { + .action_name = action_name, + .profile_path = profile_path, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .bindings = binding_infos, + }; + + bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + + MEM_freeN(binding_infos); + return ret; +} + +void WM_xr_action_binding_destroy(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *profile_path) +{ + GHOST_XrDestroyActionBindings( + xr->runtime->context, action_set_name, 1, &action_name, &profile_path); +} + +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + { + /* Unset active modal action (if any). */ + wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; + if (active_action_set) { + wmXrAction *active_modal_action = active_action_set->active_modal_action; + if (active_modal_action) { + if (active_modal_action->active_modal_path) { + active_modal_action->active_modal_path = NULL; + } + active_action_set->active_modal_action = NULL; + } + } + } + + xr->runtime->session_state.active_action_set = action_set; + + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); + } + + return true; +} + +bool WM_xr_controller_pose_action_set(wmXrData *xr, + const char *action_set_name, + const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return false; + } + + action_set->controller_pose_action = action; + + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_populate(action, xr); + } + + return true; +} + +bool WM_xr_action_state_get(const wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path, + wmXrActionState *r_state) +{ + const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name); + if (!action) { + return false; + } + + r_state->type = (int)action->type; + + /* Find the action state corresponding to the subaction path. */ + for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { + if (STREQ(subaction_path, action->subaction_paths[i])) { + switch (action->type) { + case XR_BOOLEAN_INPUT: + r_state->state_boolean = ((bool *)action->states)[i]; + break; + case XR_FLOAT_INPUT: + r_state->state_float = ((float *)action->states)[i]; + break; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]); + break; + case XR_POSE_INPUT: { + const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i]; + copy_v3_v3(r_state->state_pose.position, pose->position); + copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat); + break; + } + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + return true; + } + } + + return false; +} + +bool WM_xr_haptic_action_apply(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const int64_t *duration, + const float *frequency, + const float *amplitude) +{ + return GHOST_XrApplyHapticAction( + xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + true : + false; +} + +void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); +} + +/** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c deleted file mode 100644 index 7eabd29baa0..00000000000 --- a/source/blender/windowmanager/xr/intern/wm_xr_actions.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup wm - * - * \name Window-Manager XR Actions - * - * Uses the Ghost-XR API to manage OpenXR actions. - * All functions are designed to be usable by RNA / the Python API. - */ - -#include "BLI_math.h" - -#include "GHOST_C-api.h" - -#include "MEM_guardedalloc.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "wm_xr_intern.h" - -/* -------------------------------------------------------------------- */ -/** \name XR-Action API - * - * API functions for managing OpenXR actions. - * - * \{ */ - -static wmXrActionSet *action_set_create(const char *action_set_name) -{ - wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__); - action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name"); - strcpy(action_set->name, action_set_name); - - return action_set; -} - -static void action_set_destroy(void *val) -{ - wmXrActionSet *action_set = val; - - MEM_SAFE_FREE(action_set->name); - - MEM_freeN(action_set); -} - -static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) -{ - return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name); -} - -static wmXrAction *action_create(const char *action_name, - eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, - const float *float_threshold, - wmOperatorType *ot, - IDProperty *op_properties, - eXrOpFlag op_flag) -{ - wmXrAction *action = MEM_callocN(sizeof(*action), __func__); - action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); - strcpy(action->name, action_name); - action->type = type; - - const unsigned int count = count_subaction_paths; - action->count_subaction_paths = count; - - action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, - "XrAction_SubactionPaths"); - for (unsigned int i = 0; i < count; ++i) { - action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, - "XrAction_SubactionPath"); - strcpy(action->subaction_paths[i], subaction_paths[i]); - } - - size_t size; - switch (type) { - case XR_BOOLEAN_INPUT: - size = sizeof(bool); - break; - case XR_FLOAT_INPUT: - size = sizeof(float); - break; - case XR_VECTOR2F_INPUT: - size = sizeof(float) * 2; - break; - case XR_POSE_INPUT: - size = sizeof(GHOST_XrPose); - break; - case XR_VIBRATION_OUTPUT: - return action; - } - action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); - action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); - - if (float_threshold) { - BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); - action->float_threshold = *float_threshold; - CLAMP(action->float_threshold, 0.0f, 1.0f); - } - - action->ot = ot; - action->op_properties = op_properties; - action->op_flag = op_flag; - - return action; -} - -static void action_destroy(void *val) -{ - wmXrAction *action = val; - - MEM_SAFE_FREE(action->name); - - const unsigned int count = action->count_subaction_paths; - char **subaction_paths = action->subaction_paths; - if (subaction_paths) { - for (unsigned int i = 0; i < count; ++i) { - MEM_SAFE_FREE(subaction_paths[i]); - } - MEM_freeN(subaction_paths); - } - - MEM_SAFE_FREE(action->states); - MEM_SAFE_FREE(action->states_prev); - - MEM_freeN(action); -} - -static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name); -} - -bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name) -{ - if (action_set_find(xr, action_set_name)) { - return false; - } - - wmXrActionSet *action_set = action_set_create(action_set_name); - - GHOST_XrActionSetInfo info = { - .name = action_set_name, - .customdata_free_fn = action_set_destroy, - .customdata = action_set, - }; - - if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) { - return false; - } - - return true; -} - -void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return; - } - - wmXrSessionState *session_state = &xr->runtime->session_state; - - if (action_set == session_state->active_action_set) { - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_clear(session_state); - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action) { - action_set->active_modal_action = NULL; - } - session_state->active_action_set = NULL; - } - - GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name); -} - -bool WM_xr_action_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, - const float *float_threshold, - wmOperatorType *ot, - IDProperty *op_properties, - eXrOpFlag op_flag) -{ - if (action_find(xr, action_set_name, action_name)) { - return false; - } - - wmXrAction *action = action_create(action_name, - type, - count_subaction_paths, - subaction_paths, - float_threshold, - ot, - op_properties, - op_flag); - - GHOST_XrActionInfo info = { - .name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - .states = action->states, - .customdata_free_fn = action_destroy, - .customdata = action, - }; - - switch (type) { - case XR_BOOLEAN_INPUT: - info.type = GHOST_kXrActionTypeBooleanInput; - break; - case XR_FLOAT_INPUT: - info.type = GHOST_kXrActionTypeFloatInput; - break; - case XR_VECTOR2F_INPUT: - info.type = GHOST_kXrActionTypeVector2fInput; - break; - case XR_POSE_INPUT: - info.type = GHOST_kXrActionTypePoseInput; - break; - case XR_VIBRATION_OUTPUT: - info.type = GHOST_kXrActionTypeVibrationOutput; - break; - } - - if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { - return false; - } - - return true; -} - -void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return; - } - - if (action_set->controller_pose_action && - STREQ(action_set->controller_pose_action->name, action_name)) { - if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_clear(&xr->runtime->session_state); - } - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action && - STREQ(action_set->active_modal_action->name, action_name)) { - action_set->active_modal_action = NULL; - } - - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { - return; - } -} - -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const wmXrPose *poses) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; - - GHOST_XrPose *ghost_poses = MEM_malloc_arrayN( - count_subaction_paths, sizeof(*ghost_poses), __func__); - for (unsigned int i = 0; i < count_subaction_paths; ++i) { - const wmXrPose *pose = &poses[i]; - GHOST_XrPose *ghost_pose = &ghost_poses[i]; - copy_v3_v3(ghost_pose->position, pose->position); - copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat); - } - info.poses = ghost_poses; - - bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true : - false; - MEM_freeN(ghost_poses); - return ret; -} - -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; - - GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info); -} - -bool WM_xr_action_binding_create(wmXrData *xr, - const char *action_set_name, - const char *profile_path, - const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) -{ - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; - - GHOST_XrActionProfileInfo profile_info = { - .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, - }; - - return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); -} - -void WM_xr_action_binding_destroy(wmXrData *xr, - const char *action_set_name, - const char *profile_path, - const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) -{ - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; - - GHOST_XrActionProfileInfo profile_info = { - .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, - }; - - GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); -} - -bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return false; - } - - { - /* Unset active modal action (if any). */ - wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; - if (active_action_set) { - wmXrAction *active_modal_action = active_action_set->active_modal_action; - if (active_modal_action) { - if (active_modal_action->active_modal_path) { - active_modal_action->active_modal_path = NULL; - } - active_action_set->active_modal_action = NULL; - } - } - } - - xr->runtime->session_state.active_action_set = action_set; - - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); - } - - return true; -} - -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return false; - } - - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { - return false; - } - - action_set->controller_pose_action = action; - - if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_populate(action, xr); - } - - return true; -} - -bool WM_xr_action_state_get(const wmXrData *xr, - const char *action_set_name, - const char *action_name, - const char *subaction_path, - wmXrActionState *r_state) -{ - const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name); - if (!action) { - return false; - } - - BLI_assert(action->type == (eXrActionType)r_state->type); - - /* Find the action state corresponding to the subaction path. */ - for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { - if (STREQ(subaction_path, action->subaction_paths[i])) { - switch ((eXrActionType)r_state->type) { - case XR_BOOLEAN_INPUT: - r_state->state_boolean = ((bool *)action->states)[i]; - break; - case XR_FLOAT_INPUT: - r_state->state_float = ((float *)action->states)[i]; - break; - case XR_VECTOR2F_INPUT: - copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]); - break; - case XR_POSE_INPUT: { - const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i]; - copy_v3_v3(r_state->state_pose.position, pose->position); - copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat); - break; - } - case XR_VIBRATION_OUTPUT: - BLI_assert_unreachable(); - break; - } - return true; - } - } - - return false; -} - -bool WM_xr_haptic_action_apply(wmXrData *xr, - const char *action_set_name, - const char *action_name, - const int64_t *duration, - const float *frequency, - const float *amplitude) -{ - return GHOST_XrApplyHapticAction( - xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? - true : - false; -} - -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); -} - -/** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 6415f96e322..4530aeaa428 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -122,8 +122,9 @@ typedef struct wmXrAction { /** Previous states, stored to determine XR events. */ void *states_prev; - /** Input threshold for float/vector2f actions. */ - float float_threshold; + /** Input thresholds/regions for each subaction path. */ + float *float_thresholds; + eXrAxisFlag *axis_flags; /** The currently active subaction path (if any) for modal actions. */ char **active_modal_path; -- cgit v1.2.3